From: Vincent Vanwaelscappel Date: Thu, 2 Nov 2023 10:00:30 +0000 (+0100) Subject: wip #6447 @0.75 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=05fbc7216e044aa91ebd5c9475f2ce8082152072;p=michelcastel.git wip #6447 @0.75 --- 05fbc7216e044aa91ebd5c9475f2ce8082152072 diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..d25d354 --- /dev/null +++ b/.htaccess @@ -0,0 +1,2 @@ + +AddType x-mapp-php5 .php \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..66fe1a2 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,250 @@ +Dotclear 2.1.6 - 2009-10-01 +=========================================================== +* Install procedure fixes +* Admin: Page managers can now create pages +* Admin: several typos corrected. +* Admin: Widgets now work in IE8. +* Admin: Password protected posts can now be previewed. +* Templates: tpl:Meta* are now tpl:Tags*. +* Templates: now display all posts. +* new behavior: adminPageHTMLHead +* DB schema: new blog_id field in log table +* Media manager: Pubic folder can now be set on a different host. +* WordPress import fixes +* Dailymotion insertion fix +* Upgrade procedure: CRLF removed in files that were bugging the upgrade. +* JQuery updated to 1.3 +* IE7-js update +* security: Full Path Disclosure protection. Thx to Karim Ayad for pointing it out. +* and way too many bugfixes to be listed. + +Dotclear 2.1.5 - 2009-02-05 +=========================================================== +* Security release +* Youtube insertion update + +Dotclear 2.1.4 - 2008-12-21 +=========================================================== +* Security flaw fix +* WordPress import refining +* XML-RPC improvements + +Dotclear 2.1.3 - 2008-11-19 +=========================================================== +* Admin: New upgrade procedure +* Admin: Fixed video insertion bug +* Template: New attributes + * url on EntryIf + * only_category on Blogroll + * no_context on Pagination +* Template: New tag + * BlogID +* Admin: escaped blog_id on authentication page + +Dotclear 2.1.1 - 2008-11-07 +=========================================================== +* Admin: Automatic Update bug fixes +* Admin: Disable Automatic Update if no digests file +* Admin: Javascript fixes in authentication page +* Admin: Fixed errors with categories select boxes +* Template: Added level attribute in tpl:Categories +* Media: Added H.264/MPEG-4 AVC for mp4 files + +Dotclear 2.1 - 2008-11-01 +=========================================================== +* Subcategories +* Admin: Automatic Update +* Admin: Flash 10 support for uploader +* Admin: mailto link in comment details +* Admin: Embedded video size selection +* Admin: Restrict session cookie path to admin +* Media: H.264/MPEG-4 AVC (HD) support with m4v files +* Inherited themes +* WordPress XML-RPC methods support +* True unicode URLs +* Plugin: Widgets as template tags +* Plugin: Filters in entries widgets and Blogroll +* Plugin: Added vimeo.com in external media +* Template: New tags + * LoopPosition + * CommentAuthorDomain + * CommentAuthorMD5 + * EntryFirstImage + * EntryCategoryShortURL + * CategoryIf + * CategoryFirstChildren + * CategoryParents + * EntryCategoriesBreadcrum + * MediaURL + +Dotclear 2.0.2 - 2008-09-05 +=========================================================== +* New installation procedure +* Plugin: WordPress import fixes +* Plugin: Plain text export as downloadable files +* Plugin: Message about URLs in Dotclear 1.2 import +* Public: Display a message if search returns no result +* Admin: Fixed some CSS bugs +* Admin: Batch select/unselect entries +* Admin: In a media item, find entries containing it + +Dotclear 2.0.1 - 2008-08-16 +=========================================================== +* Plugin: Fixed a bug with Dotclear 1.2 URLs import. +* Plugin: Fixed a l10n bug in Pages +* Admin: Enhanced plugins resources loading and cache + +Dotclear 2.0 - 2008-08-01 +=========================================================== +* Public: Atom becomes the default feed format. RSS 2 is always available. +* Admin: design enhancements and new Dotclear logo +* Admin: entries preview in blog context +* L10N: New language manager with zip files support +* Plugin: Import/Export plugin version 2.0 with import from Dotclear 1.2 and WordPress +* Plugin: Pages enhancements (preview, sorting) +* Plugin: support for jamendo and deezer in External Media +* JSMin on JavaScript files instead of JS packing +* SQLite 3 only support (PDO based) +* Many bug fixes and major performances improvements + +Dotclear 2.0 RC2 - 2008-06-21 +=========================================================== +* FairTrackback spam filter +* Language pack infrastructure +* Bug fix on comment search with author "0" +* Javascript fixes +* dcAuth::sessionExists and dcAuth::checkSession new methods +* Right management in dcAuth::sudo +* Media File sorting options in media manager +* CandyUpload, new uploader tool based on SWFUpload +* New search engine robots options +* New image options +* L10N: Japanese and Portugues (Brazil) language packs +* Many bug fixes and enhancements + +Dotclear 2.0 RC1 - 2008-05-01 +=========================================================== +* New: Pages plugin +* New: Theme editor plugin +* Entries: Text and WYSIWYG enhancements +* Entries: Markup validator +* Entries: Insertion of links to other entries from toolbar +* Entries: External media insertion (dailymotion, youtube, google video) +* Tags: Same list for new and existing entries +* Tags: Tags can be removed on all associated entries +* Tags: Tags can be removed on a post selection +* Admin: Ask password for user management tasks, theme upload and plugin upload +* Admin: New contextual help viewer +* Media manager: Recreate thumbnails option +* Media manager: Custom medium thumbnail size (per blog) +* Media manager: Zip files extract support +* Media manager: Zip file download of directory +* Media manager: File exclusion pattern option +* Themes and plugins: Zip as new package format +* Themes and plugins: Upload +* Themes and plugins: Upgrade within administration interface +* Themes and plugins: Deletion +* Public: New default theme: Blowup (fully customizable) +* Public: Changed the way commenter cookie is handled +* Themes: Template files moved to tpl/ directory +* L10N: Polish, Catalan and Spanish translations +* Misc: jQuery upgraded to 1.2.3 +* Misc: Crushed png files +* Fixed many bugs + +Dotclear 2.0 beta 7 - 2007-07-12 +=========================================================== +* New way to display comments and trackbacks on entries in backend +* Dashboard visual improvements +* Default cache dir created by installation process +* Option to limit posts and comments in feeds +* Introduced UDBS for installation and upgrade +* Changed handling of XML-RPC URLs +* New option to force HTTPS redirect if wanted +* Enforced cookies security (directory and ssl support) +* Added Plugin auto-install and auto-upgrade support +* Added trackbacks ttl and moderation preferences +* Added an Internal search engine +* FLV support in backend with Neolao player +* Added nice messages if database is broken or Dotclear not installed +* upgrade jQuery to 1.1.3 +* Fixed many bugs +* Fixed security issues in backend + +Dotclear 2.0 beta 6 - 2007-02-19 +=========================================================== +* New antispam plugin, with a set of filters (rbl, ipblacklist, spamwords, akismet) +* New admin dashboard page +* Fixed unwanted logout bug +* Added settings to disable template caching and allow PHP code +* Blog preferences panel bug fix +* New XML-RPC Client and Server +* Comment posting permissions bug fix + +Dotclear 2.0 beta 5.4 - 2007-01-19 +=========================================================== +* Minor change on spam display in comments.php +* Command line upgrade script and fix in load_plugin_file.php +* Make akismet configurable only by superadmin with DC_AKISMET_SUPER +* SQL optimisations +* New comments view in post + +Dotclear 2.0 beta 5.2 - 2007-01-11 +=========================================================== +* Fixed a bug with imageMeta::getMeta +* Enhanced dynamic file uploader +* Move clearbricks files to their own repository +* Fixed a bug with auto_br in wikiSimpleComment +* Support for language restriction in feeds +* Default theme structure changes +* Fixed a PHP 5.0 compatibility issue +* Installation Wizard + +Dotclear 2.0 beta 4 - 2006-12-26 +=========================================================== +* Performances enhancements. +* Administration UI enhancements. +* More user-friendly Widgets (version 1.5). +* Switch to jQuery . +* Added jQuery in default theme. +* Major changes in HTTP client and Feed Parser based on a + generic socket handler. +* PHP 5.2 compatibility. +* Code documentation (all core and most of clearbricks). +* Many bug fixes. + +Dotclear 2.0 beta 3 - 2006-11-05 +=========================================================== +* Disallow special wrappers for fopen like functions. +* XML/RPC improvements. +* Read IPTC and EXIF metadata in uploaded pictures. +* MySQL 4.1 support only. +* Metadata import from Dotclear 1.2.x. +* Akismet plugin. +* Pings plugin. +* Added a priority setting for plugins. +* Many bug fixes. + +Dotclear 2.0 beta 2 - 2006-08-09 +=========================================================== +* DC_PLUGIN_ROOT can handle more than one path. +* OPML/XBEL import in blogroll plugin. +* Fixed a security issue in html::absoluteURLs(). +* Fixed issues with timezone on scheduled entries. +* Multiple categories selection in tpl:Entries. +* Improved dbLayer. +* Changed category feed URL. +* Feeds for tags (entries and comments). +* Added attachments count on backend and frontend. +* New settings code design. Can now handle wide system settings. +* Memory usage improvements with autoloader. +* Some code cleanup. +* Feed parser improvements. +* Themes can be configured if needed. +* XMP support on JPEG files. +* Media manager improvements. +* Spamplemousse now uses DNSBL (and the guy who left the bug was fired). +* Javascript editor and toolbar improvements. +* RDS support (XML/RPC API discovery). +* Added a theme with user stylesheet. +* Plugins manager \ No newline at end of file diff --git a/admin/auth.php b/admin/auth.php new file mode 100644 index 0000000..e0cae49 --- /dev/null +++ b/admin/auth.php @@ -0,0 +1,288 @@ +auth->allowPassChange() && !empty($_REQUEST['recover']); +$akey = $core->auth->allowPassChange() && !empty($_GET['akey']) ? $_GET['akey'] : null; +$user_id = $user_pwd = $user_key = $user_email = null; +$err = $msg = null; + +# Auto upgrade +if (empty($_GET) && empty($_POST)) { + require dirname(__FILE__).'/../inc/dbschema/upgrade.php'; + try { + if (($changes = dotclearUpgrade($core)) !== false) { + $msg = __('Dotclear has been upgraded.').''; + } + } catch (Exception $e) { + $err = $e->getMessage(); + } +} + +# If we have POST login informations, go throug auth process +if (!empty($_POST['user_id']) && !empty($_POST['user_pwd'])) +{ + $user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null; + $user_pwd = !empty($_POST['user_pwd']) ? $_POST['user_pwd'] : null; +} +# If we have POST login informations, go throug auth process +elseif (isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104) +{ + # If we have a remember cookie, go through auth process with user_key + $user_id = substr($_COOKIE['dc_admin'],40); + $user_id = @unpack('a32',@pack('H*',$user_id)); + if (is_array($user_id)) + { + $user_id = $user_id[1]; + $user_key = substr($_COOKIE['dc_admin'],0,40); + $user_pwd = null; + } + else + { + $user_id = null; + } +} + +# Recover password +if ($recover && !empty($_POST['user_id']) && !empty($_POST['user_email'])) +{ + $user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null; + $user_email = !empty($_POST['user_email']) ? $_POST['user_email'] : ''; + try + { + $recover_key = $core->auth->setRecoverKey($user_id,$user_email); + + $subject = mail::B64Header('DotClear '.__('Password reset')); + $message = + __('Someone has requested to reset the password for the following site and username.')."\n\n". + $page_url."\n".__('Username:').' '.$user_id."\n\n". + __('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.')."\n". + $page_url.'?akey='.$recover_key; + + $headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST']; + $headers[] = 'Content-Type: text/plain; charset=UTF-8;'; + + mail::sendMail($user_email,$subject,$message,$headers); + $msg = sprintf(__('The e-mail was sent successfully to %s.'),$user_email); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} +# Send new password +elseif ($akey) +{ + try + { + $recover_res = $core->auth->recoverUserPassword($akey); + + $subject = mb_encode_mimeheader('DotClear '.__('Your new password'),'UTF-8','B'); + $message = + __('Username:').' '.$recover_res['user_id']."\n". + __('Password:').' '.$recover_res['new_pass']."\n\n". + preg_replace('/\?(.*)$/','',$page_url); + + $headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST']; + $headers[] = 'Content-Type: text/plain; charset=UTF-8;'; + + mail::sendMail($recover_res['user_email'],$subject,$message,$headers); + $msg = __('Your new password is in your mailbox.'); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} +# Try to log +elseif ($user_id !== null && ($user_pwd !== null || $user_key !== null)) +{ + # We check the user + if ($core->auth->checkUser($user_id,$user_pwd,$user_key) === true) + { + $core->session->start(); + $_SESSION['sess_user_id'] = $user_id; + $_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY); + + if (!empty($_POST['blog'])) { + $_SESSION['sess_blog_id'] = $_POST['blog']; + } + + if (!empty($_POST['user_remember'])) + { + $cookie_admin = + http::browserUID(DC_MASTER_KEY.$user_id.crypt::hmac(DC_MASTER_KEY,$user_pwd)). + bin2hex(pack('a32',$user_id)); + + setcookie('dc_admin',$cookie_admin,strtotime('+15 days'),'','',DC_ADMIN_SSL); + } + + http::redirect('index.php'); + } + else + { + if (isset($_COOKIE['dc_admin'])) { + unset($_COOKIE['dc_admin']); + setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL); + } + $err = __('Wrong username or password'); + } +} + +if (isset($_GET['user'])) { + $user_id = $_GET['user']; +} + +header('Content-Type: text/html; charset=UTF-8'); +?> + + + + + + + + + + + <?php echo html::escapeHTML(DC_VENDOR_NAME); ?> + + + + + callBehavior('loginPageHTMLHead'); + ?> + + + + + + +
+

+ +'.$err.''; +} +if ($msg) { + echo '

'.$msg.'

'; +} + +if ($akey) +{ + echo '

'.__('Back to login screen').'

'; +} +elseif ($recover) +{ + echo + '
'.__('Request a new password').''. + '

'. + + '

'. + + '

'. + form::hidden(array('recover'),1).'

'. + '
'. + + '

'.__('Back to login screen').'

'; +} +else +{ + if (is_callable(array($core->auth,'authForm'))) + { + echo $core->auth->authForm($user_id); + } + else + { + echo + '
'. + '

'. + + '

'. + + '

'. + + '

'; + + if (!empty($_REQUEST['blog'])) { + echo form::hidden('blog',html::escapeHTML($_REQUEST['blog'])); + } + + echo + '
'. + + '

'.__('You must accept cookies in order to use the private area.').'

'; + + if ($core->auth->allowPassChange()) { + echo '

'.__('I forgot my password').'

'; + } + } +} +?> +
+ + \ No newline at end of file diff --git a/admin/blog.php b/admin/blog.php new file mode 100644 index 0000000..dab788e --- /dev/null +++ b/admin/blog.php @@ -0,0 +1,98 @@ +con->openCursor($core->prefix.'blog'); + $blog_id = $cur->blog_id = $_POST['blog_id']; + $blog_url = $cur->blog_url = $_POST['blog_url']; + $blog_name = $cur->blog_name = $_POST['blog_name']; + $blog_desc = $cur->blog_desc = $_POST['blog_desc']; + + try + { + # --BEHAVIOR-- adminBeforeBlogCreate + $core->callBehavior('adminBeforeBlogCreate',$cur,$blog_id); + + $core->addBlog($cur); + + # Default settings and override some + $core->blogDefaults($cur->blog_id); + $blog_settings = new dcSettings($core,$cur->blog_id); + $blog_settings->setNameSpace('system'); + $blog_settings->put('lang',$core->auth->getInfo('user_lang')); + $blog_settings->put('blog_timezone',$core->auth->getInfo('user_tz')); + + if (substr($blog_url,-1) == '?') { + $blog_settings->put('url_scan','query_string'); + } else { + $blog_settings->put('url_scan','path_info'); + } + + # --BEHAVIOR-- adminAfterBlogCreate + $core->callBehavior('adminAfterBlogCreate',$cur,$blog_id,$blog_settings); + + http::redirect('blog.php?id='.$cur->blog_id.'&add=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +if (!empty($_REQUEST['id'])) +{ + $edit_blog_mode = true; + include dirname(__FILE__).'/blog_pref.php'; +} +else +{ + dcPage::open(__('New blog'),dcPage::jsConfirmClose('blog-form')); + + echo + '

'.__('Blogs').' › '.__('New blog').'

'. + + '
'. + + '
'.__('Blog details').''. + $core->formNonce(). + '

'. + '

'.__('At least 2 characters using letters, numbers or symbols.').' '. + __('Please note that changing your blog ID may require changes in your public index.php file.').'

'. + + '

'. + + '

'. + + '

'. + form::textarea('blog_desc',60,5,html::escapeHTML($blog_desc)).'

'. + '
'. + + '

'. + '
'; + + dcPage::close(); +} +?> \ No newline at end of file diff --git a/admin/blog_del.php b/admin/blog_del.php new file mode 100644 index 0000000..756d32b --- /dev/null +++ b/admin/blog_del.php @@ -0,0 +1,71 @@ +getBlog($_POST['blog_id']); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if ($rs->isEmpty()) { + $core->error->add(__('No such blog ID')); + } else { + $blog_id = $rs->blog_id; + } +} + +# Delete the blog +if (!$core->error->flag() && $blog_id && !empty($_POST['del'])) +{ + if (!$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['pwd']))) { + $core->error->add(__('Password verification failed')); + } else { + try { + $core->delBlog($blog_id); + http::redirect('blogs.php?del=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } +} + +dcPage::open('Delete a blog'); + +if (!$core->error->flag()) +{ + echo + '

'.__('Delete a blog').'

'. + '

'.__('Warning').'

'. + '

'.sprintf(__('You are about to delete the blog %s. Every entry, comment and category will be deleted.'), + ''.$blog_id.'').'

'. + '

'.__('Please give your password to confirm the blog deletion.').'

'; + + echo + '
'. + '
'.$core->formNonce().'
'. + '

'. + '

'. + form::hidden('blog_id',$blog_id).'

'. + '
'; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/blog_pref.php b/admin/blog_pref.php new file mode 100644 index 0000000..e28b107 --- /dev/null +++ b/admin/blog_pref.php @@ -0,0 +1,528 @@ +blog->id; + $blog_url = $core->blog->url; + $blog_status = $core->blog->status; + $blog_name = $core->blog->name; + $blog_desc = $core->blog->desc; + $blog_settings = $core->blog->settings; + + $action = 'blog_pref.php'; + $redir = 'blog_pref.php?upd=1'; +} +else +{ + dcPage::checkSuper(); + try + { + if (empty($_REQUEST['id'])) { + throw new Exception(__('No given blog id.')); + } + $rs = $core->getBlog($_REQUEST['id']); + + if (!$rs) { + throw new Exception(__('No such blog.')); + } + + $blog_id = $rs->blog_id; + $blog_url = $rs->blog_url; + $blog_status = $rs->blog_status; + $blog_name = $rs->blog_name; + $blog_desc = $rs->blog_desc; + $blog_settings = new dcSettings($core,$blog_id); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + + $action = 'blog.php'; + $redir = 'blog.php?id=%s&upd=1'; +} + +# Language codes +$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' : ''); +} + +# Status combo +foreach ($core->getAllBlogStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# URL scan modes +$url_scan_combo = array( + 'PATH_INFO' => 'path_info', + 'QUERY_STRING' => 'query_string' +); + +# Post URL combo +$post_url_combo = array( + __('year/month/day/title') => '{y}/{m}/{d}/{t}', + __('year/month/title') => '{y}/{m}/{t}', + __('year/title') => '{y}/{t}', + __('title') => '{t}' +); +if (!in_array($blog_settings->post_url_format,$post_url_combo)) { + $post_url_combo[html::escapeHTML($blog_settings->post_url_format)] = html::escapeHTML($blog_settings->post_url_format); +} + +# Image title combo +$img_title_combo = array( + __('Title') => 'Title ;; separator(, )', + __('Title, Date') => 'Title ;; Date(%b %Y) ;; separator(, )', + __('Title, Country, Date') => 'Title ;; Country ;; Date(%b %Y) ;; separator(, )', + __('Title, City, Country, Date') => 'Title ;; City ;; Country ;; Date(%b %Y) ;; separator(, )', +); +if (!in_array($blog_settings->media_img_title_pattern,$img_title_combo)) { + $img_title_combo[html::escapeHTML($blog_settings->media_img_title_pattern)] = html::escapeHTML($blog_settings->media_img_title_pattern); +} + +# Robots policy options +$robots_policy_options = array( + 'INDEX,FOLLOW' => __("I would like search engines and archivers to index and archive my blog's content."), + 'INDEX,FOLLOW,NOARCHIVE' => __("I would like search engines and archivers to index but not archive my blog's content."), + 'NOINDEX,NOFOLLOW,NOARCHIVE' => __("I would like to prevent search engines and archivers from indexing or archiving my blog's content."), +); + +# Update a blog +if ($blog_id && !empty($_POST) && $core->auth->check('admin',$blog_id)) +{ + $cur = $core->con->openCursor($core->prefix.'blog'); + if ($core->auth->isSuperAdmin()) { + $cur->blog_id = $_POST['blog_id']; + $cur->blog_url = $_POST['blog_url']; + if (in_array($_POST['blog_status'],$status_combo)) { + $cur->blog_status = (integer) $_POST['blog_status']; + } + } + $cur->blog_name = $_POST['blog_name']; + $cur->blog_desc = $_POST['blog_desc']; + + $media_img_t_size = abs((integer) $_POST['media_img_t_size']); + if ($media_img_t_size < 0) { $media_img_t_size = 100; } + + $media_img_s_size = abs((integer) $_POST['media_img_s_size']); + if ($media_img_s_size < 0) { $media_img_s_size = 240; } + + $media_img_m_size = abs((integer) $_POST['media_img_m_size']); + if ($media_img_m_size < 0) { $media_img_m_size = 448; } + + $nb_post_per_page = abs((integer) $_POST['nb_post_per_page']); + if ($nb_post_per_page <= 1) { $nb_post_per_page = 1; } + + $nb_post_per_feed = abs((integer) $_POST['nb_post_per_feed']); + if ($nb_post_per_feed <= 1) { $nb_post_per_feed = 1; } + + $nb_comment_per_feed = abs((integer) $_POST['nb_comment_per_feed']); + if ($nb_comment_per_feed <= 1) { $nb_comment_per_feed = 1; } + + try + { + # --BEHAVIOR-- adminBeforeBlogUpdate + $core->callBehavior('adminBeforeBlogUpdate',$cur,$blog_id); + + if (!preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$_POST['lang'])) { + throw new Exception(__('Invalid language code')); + } + + $core->updBlog($blog_id,$cur); + + # --BEHAVIOR-- adminAfterBlogUpdate + $core->callBehavior('adminAfterBlogUpdate',$cur,$blog_id); + + if ($blog_id == $core->blog->id && $cur->blog_id != null && $cur->blog_id != $blog_id) { + $blog_id = $cur->blog_id; + $core->setBlog($cur->blog_id); + $_SESSION['sess_blog_id'] = $cur->blog_id; + $blog_settings = $core->blog->settings; + } + + + $blog_settings->setNameSpace('system'); + + $blog_settings->put('editor',$_POST['editor']); + $blog_settings->put('copyright_notice',$_POST['copyright_notice']); + $blog_settings->put('post_url_format',$_POST['post_url_format']); + $blog_settings->put('lang',$_POST['lang']); + $blog_settings->put('blog_timezone',$_POST['blog_timezone']); + $blog_settings->put('date_format',$_POST['date_format']); + $blog_settings->put('time_format',$_POST['time_format']); + $blog_settings->put('comments_ttl',abs((integer) $_POST['comments_ttl'])); + $blog_settings->put('trackbacks_ttl',abs((integer) $_POST['trackbacks_ttl'])); + $blog_settings->put('allow_comments',!empty($_POST['allow_comments'])); + $blog_settings->put('allow_trackbacks',!empty($_POST['allow_trackbacks'])); + $blog_settings->put('comments_pub',empty($_POST['comments_pub'])); + $blog_settings->put('trackbacks_pub',empty($_POST['trackbacks_pub'])); + $blog_settings->put('comments_nofollow',!empty($_POST['comments_nofollow'])); + $blog_settings->put('wiki_comments',!empty($_POST['wiki_comments'])); + $blog_settings->put('enable_xmlrpc',!empty($_POST['enable_xmlrpc'])); + + $blog_settings->put('nb_post_per_page',$nb_post_per_page); + $blog_settings->put('use_smilies',!empty($_POST['use_smilies'])); + $blog_settings->put('media_img_t_size',$media_img_t_size); + $blog_settings->put('media_img_s_size',$media_img_s_size); + $blog_settings->put('media_img_m_size',$media_img_m_size); + $blog_settings->put('media_img_title_pattern',$_POST['media_img_title_pattern']); + $blog_settings->put('nb_post_per_feed',$nb_post_per_feed); + $blog_settings->put('nb_comment_per_feed',$nb_comment_per_feed); + $blog_settings->put('short_feed_items',!empty($_POST['short_feed_items'])); + + if (isset($_POST['robots_policy'])) { + $blog_settings->put('robots_policy',$_POST['robots_policy']); + } + + # --BEHAVIOR-- adminBeforeBlogSettingsUpdate + $core->callBehavior('adminBeforeBlogSettingsUpdate',$blog_settings); + + if ($core->auth->isSuperAdmin() && in_array($_POST['url_scan'],$url_scan_combo)) { + $blog_settings->put('url_scan',$_POST['url_scan']); + } + + http::redirect(sprintf($redir,$blog_id)); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +dcPage::open(__('Blog settings'), + dcPage::jsConfirmClose('blog-form'). + + # --BEHAVIOR-- adminBlogPreferencesHeaders + $core->callBehavior('adminBlogPreferencesHeaders'). + + dcPage::jsPageTabs() +); + +if ($blog_id) +{ + echo '

'.(!$standalone ? ''.__('Blogs').' › ' : ''). + html::escapeHTML($blog_name).' › '. + __('Blog settings').'

'; + + if (!empty($_GET['add'])) { + echo '

'.__('Blog has been successfully created.').'

'; + } + + if (!empty($_GET['upd'])) { + echo '

'.__('Blog has been successfully updated.').'

'; + } + + echo + '
'. + '

'.__('Parameters').'

'. + '
'; + + echo + '
'.__('Blog details').''. + $core->formNonce(); + + if ($core->auth->isSuperAdmin()) + { + echo + '

'. + '

'.__('At least 2 characters using letters, numbers or symbols.').' '. + __('Please note that changing your blog ID may require changes in your public index.php file.').'

'; + } + + echo + '

'; + + if ($core->auth->isSuperAdmin()) + { + echo + '

'. + + '

'. + + '

'; + } + + echo + '

'. + form::textarea('blog_desc',60,5,html::escapeHTML($blog_desc)).'

'. + '
'; + + + echo + '
'.__('Blog configuration').''. + '
'. + '
'. + '

'. + + '

'. + + '

'. + '
'. + + '
'. + '

'. + + '

'. + + '

'. + ' - '.__('more information').'

'. + '
'. + '
'. + '
'. //Opera sucks + '
'; + + echo + '
'.__('Comments and trackbacks').''. + '
'. + '
'. + '

'. + + '

'. + + '

'. + '

'.__('Leave blank to disable this feature.').'

'. + + '

'. + '
'. + + '
'. + '

'. + + '

'. + + '

'. + '

'.__('Leave blank to disable this feature.').'

'. + + '

'. + '
'. + '
'. + '
'. //Opera sucks + '
'; + + echo + '
'.__('Blog presentation').''. + '
'. + '
'. + '

'. + + '

'. + + '

'. + '
'. + + '
'. + '

'. + + '

'. + + '

'. + + '

'. + '
'. + '
'. + '
'. //Opera sucks + '
'; + + echo + '
'.__('Media and images').''. + '
'. + '
'. + '

'.__('Generated image sizes (in pixels)').'

'. + '

'. + + '

'. + + '

'. + '
'. + + '
'. + '

'. + '

'.__('This defines image tag title when you insert it in a post from the media manager. It is retrieved from the picture\'s metadata.').'

'. + '

'.form::combo('media_img_title_pattern',$img_title_combo,html::escapeHTML($blog_settings->media_img_title_pattern)).'

'. + '
'. + '
'. + '
'; + + echo + '
'.__('Search engines robots policy').''; + + foreach ($robots_policy_options as $k => $v) + { + echo '

'; + } + + echo '
'; + + + # --BEHAVIOR-- adminBlogPreferencesForm + $core->callBehavior('adminBlogPreferencesForm',$core,$blog_settings); + + echo + '

'. + (!$standalone ? form::hidden('id',$blog_id) : ''). + '

'. + '
'; + + if ($core->auth->isSuperAdmin() && $blog_id != $core->blog->id) + { + echo + '
'. + '

'. + form::hidden(array('blog_id'),$blog_id). + $core->formNonce().'

'. + '
'; + } + + # XML/RPC information + echo '

'.__('XML/RPC interface').'

'; + + echo '

'.__('XML/RPC interface allows you to edit your blog with an external client.').'

'; + + if (!$blog_settings->enable_xmlrpc) + { + echo '

'.__('XML/RPC interface is not active. Change settings to enable it.').'

'; + } + else + { + echo + '

'.__('XML/RPC interface is active. You should set the following parameters on your XML/RPC client:').'

'. + '
    '. + '
  • '.__('Server URL:').' '. + sprintf(DC_XMLRPC_URL,$core->blog->url,$core->blog->id). + '
  • '. + '
  • '.__('Blogging system:').' Movable Type
  • '. + '
  • '.__('User name:').' '.$core->auth->userID().'
  • '. + '
  • '.__('Password:').' '.__('your password').'
  • '. + '
  • '.__('Blog ID:').' 1
  • '. + '
'; + } + + echo '
'; + + # + # Users on the blog (with permissions) + + $blog_users = $core->getBlogPermissions($blog_id,$core->auth->isSuperAdmin()); + $perm_types = $core->auth->getPermissionsTypes(); + + echo + '
'. + '

'.__('Users on this blog').'

'; + + if (empty($blog_users)) + { + echo '

'.__('No users').'

'; + } + else + { + if ($core->auth->isSuperAdmin()) { + $user_url_p = '%1$s'; + } else { + $user_url_p = '%1$s'; + } + + foreach ($blog_users as $k => $v) + { + if (count($v['p']) > 0) + { + echo + '

'.sprintf($user_url_p,html::escapeHTML($k)). + ' ('.html::escapeHTML(dcUtils::getUserCN( + $k, $v['name'], $v['firstname'], $v['displayname'] + )).')'; + + if (!$v['super'] && $core->auth->isSuperAdmin()) { + echo + ' - ' + .__('change permissions').''; + } + + echo '

'; + + echo '
    '; + if ($v['super']) { + echo '
  • '.__('Super administrator').'
  • '; + } else { + foreach ($v['p'] as $p => $V) { + echo '
  • '.__($perm_types[$p]).'
  • '; + } + } + echo '
'; + } + } + } + + echo '
'; +} + +dcPage::helpBlock('core_blog_pref'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/blog_theme.php b/admin/blog_theme.php new file mode 100644 index 0000000..62e821f --- /dev/null +++ b/admin/blog_theme.php @@ -0,0 +1,352 @@ +themes = new dcThemes($core); +$core->themes->loadModules($core->blog->themes_path,null); + +# Theme screenshot +if (!empty($_GET['shot']) && $core->themes->moduleExists($_GET['shot'])) +{ + if (empty($_GET['src'])) { + $f = $core->blog->themes_path.'/'.$_GET['shot'].'/screenshot.jpg'; + } else { + $f = $core->blog->themes_path.'/'.$_GET['shot'].'/'.path::clean($_GET['src']); + } + + $f = path::real($f); + + if (!file_exists($f)) { + $f = dirname(__FILE__).'/images/noscreenshot.png'; + } + + http::cache(array_merge(array($f),get_included_files())); + + header('Content-Type: '.files::getMimeType($f)); + header('Content-Length: '.filesize($f)); + readfile($f); + + exit; +} + +$can_install = $core->auth->isSuperAdmin(); +$is_writable = is_dir($core->blog->themes_path) && is_writable($core->blog->themes_path); +$default_tab = 'themes-list'; + +# Selecting theme +if (!empty($_POST['theme']) && !empty($_POST['select']) && empty($_REQUEST['conf'])) +{ + $core->blog->settings->setNameSpace('system'); + $core->blog->settings->put('theme',$_POST['theme']); + $core->blog->triggerBlog(); + http::redirect('blog_theme.php?upd=1'); +} + +if ($can_install && !empty($_POST['theme']) && !empty($_POST['remove']) && empty($_REQUEST['conf'])) +{ + try + { + if ($_POST['theme'] == 'default') { + throw new Exception(__('You can\'t remove default theme.')); + } + + if (!$core->themes->moduleExists($_POST['theme'])) { + throw new Exception(__('Theme does not exist.')); + } + + $theme = $core->themes->getModules($_POST['theme']); + + # --BEHAVIOR-- themeBeforeDelete + $core->callBehavior('themeBeforeDelete',$theme); + + $core->themes->deleteModule($_POST['theme']); + + # --BEHAVIOR-- themeAfterDelete + $core->callBehavior('themeAfterDelete',$theme); + + http::redirect('blog_theme.php?del=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Theme upload +if ($can_install && $is_writable && ((!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])) || + (!empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url'])))) +{ + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + if (!empty($_POST['upload_pkg'])) + { + files::uploadStatus($_FILES['pkg_file']); + + $dest = $core->blog->themes_path.'/'.$_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 = $core->blog->themes_path.'/'.basename($url); + + try + { + $client = netHttp::initClient($url,$path); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + } + catch( Exception $e) + { + throw new Exception(__('An error occurred while downloading the file.')); + } + + unset($client); + } + + $ret_code = dcModules::installPackage($dest,$core->themes); + http::redirect('blog_theme.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + $default_tab = 'add-theme'; + } +} + +$theme_conf_mode = false; +if (!empty($_REQUEST['conf'])) +{ + $theme_conf_file = path::real($core->blog->themes_path.'/'.$core->blog->settings->theme).'/_config.php'; + if (file_exists($theme_conf_file)) { + $theme_conf_mode = true; + } +} + +function display_theme_details($id,$details,$current) +{ + global $core; + + $screenshot = 'images/noscreenshot.png'; + if (file_exists($core->blog->themes_path.'/'.$id.'/screenshot.jpg')) { + $screenshot = 'blog_theme.php?shot='.rawurlencode($id); + } + + $radio_id = 'theme_'.html::escapeHTML($id); + $theme_url = http::concatURL($core->blog->url,$core->blog->settings->themes_url.'/'.$id); + $has_conf = file_exists(path::real($core->blog->themes_path.'/'.$id).'/_config.php'); + $has_css = file_exists(path::real($core->blog->themes_path.'/'.$id).'/style.css'); + $parent = $core->themes->moduleInfo($id,'parent'); + $has_parent = (boolean)$parent; + if ($has_parent) { + $is_parent_present = $core->themes->moduleExists($parent); + } + + $res = + '
'. + '
'. + '
'. + '

'.form::radio(array('theme',$radio_id),html::escapeHTML($id),$current,'','',($has_parent && !$is_parent_present)).' '. + '

'. + '

'.html::escapeHTML($details['desc']).' '. + ''.sprintf(__('by %s'),html::escapeHTML($details['author'])).' '. + ''.sprintf(__('version %s'),html::escapeHTML($details['version'])).' '; + if ($has_parent) { + if ($is_parent_present) { + $res .= ''.sprintf(__('(built on "%s")'),html::escapeHTML($parent)).' '; + } else { + $res .= ''.sprintf(__('(requires "%s")'),html::escapeHTML($parent)).' '; + } + } + if ($has_css) { + $res .= ''.__('Stylesheet').''; + } + $res .= '

'; + $res .= + '
'. + '
'; + if ($current && $has_conf) { + $res .= '

'.__('Theme configuration').'

'; + } + if ($current) { + # --BEHAVIOR-- adminCurrentThemeDetails + $res .= $core->callBehavior('adminCurrentThemeDetails',$core,$id,$details); + } + $res .= + '
'. + '
'; + + return $res; +} + +dcPage::open(__('Blog themes'), + dcPage::jsLoad('js/_blog_theme.js'). + dcPage::jsPageTabs($default_tab). + dcPage::jsColorPicker() +); + +if (!$theme_conf_mode) +{ + echo + '

'.html::escapeHTML($core->blog->name).' › '.__('Blog aspect').'

'; + + if (!empty($_GET['upd'])) { + echo '

'.__('Theme has been successfully changed.').'

'; + } + + if (!empty($_GET['added'])) { + echo '

'. + ($_GET['added'] == 2 ? __('Theme has been successfully upgraded') : __('Theme has been successfully installed.')). + '

'; + } + + if (!empty($_GET['del'])) { + echo '

'.__('Theme has been successfully deleted.').'

'; + } + + if ($can_install) { + echo + '

'.sprintf(__('You can find additional themes for your blog on %s.'), + 'Dotaddict').' '. + __('To install or upgrade a theme you generally just need to upload it '. + 'in "Install or upgrade a theme" section.').'

'; + } + + # Themes list + echo '
'; + + $themes = $core->themes->getModules(); + if (isset($themes[$core->blog->settings->theme])) { + echo '

'.sprintf(__('You are currently using "%s"'),$themes[$core->blog->settings->theme]['name']).'

'; + } + + echo + '
'. + '
'; + + if (isset($themes[$core->blog->settings->theme])) { + echo display_theme_details($core->blog->settings->theme,$themes[$core->blog->settings->theme],true); + } + + foreach ($themes as $k => $v) + { + if ($core->blog->settings->theme == $k) { // Current theme + continue; + } + echo display_theme_details($k,$v,false); + } + + echo '
'; + + echo + '
'. + $core->formNonce(). + '

'; + + if ($can_install) { + echo '

'; + } + + echo + '
'. + '
'. + '
'; + + # Add a new theme + if ($can_install) + { + echo + '
'; + + if ($is_writable) + { + echo '

'.__('You can install themes by uploading or downloading zip files.').'

'; + + # 'Upload theme' form + echo + '
'. + '
'. + ''.__('Upload a zip file').''. + '

'. + '

'. + ''. + $core->formNonce(). + '
'. + '
'; + + # 'Fetch theme' form + echo + '
'. + '
'. + ''.__('Download a zip file').''. + '

'. + '

'. + ''. + $core->formNonce(). + '
'. + '
'; + } + else + { + echo + '

'. + __('To enable this function, please give write access to your themes directory.'). + '

'; + } + echo '
'; + } +} +else +{ + $theme_name = $core->themes->moduleInfo($core->blog->settings->theme,'name'); + echo + '

'.html::escapeHTML($core->blog->name). + ' › '.__('Blog aspect').' › '.__('Theme configuration').'

'. + '

'.__('back').'

'; + + try + { + echo '
'; + + include $theme_conf_file; + + echo + '

'. + $core->formNonce().'

'. + '
'; + } + catch (Exception $e) + { + echo '

'.$e->getMessage().'

'; + } +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/blogs.php b/admin/blogs.php new file mode 100644 index 0000000..de6440f --- /dev/null +++ b/admin/blogs.php @@ -0,0 +1,183 @@ + 'blog_upddt', +__('Blog name') => 'UPPER(blog_name)', +__('Blog ID') => 'B.blog_id' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + +$q = !empty($_GET['q']) ? $_GET['q'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'blog_upddt'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'desc'; + +$page = !empty($_GET['page']) ? $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = $_GET['nb']; +} + +$show_filters = false; + +# - Search filter +if ($q) { + $params['q'] = $q; + $show_filters = true; +} + +# - Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + } + + if ($sortby != 'blog_upddt' || $order != 'desc') { + $show_filters = true; + } +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); + +try { + $counter = $core->getBlogs($params,1); + $rs = $core->getBlogs($params); + $nb_blog = $counter->f(0); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = ''; +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} +dcPage::open(__('List of blogs'),$starting_script); + +if (!empty($_GET['del'])) { + echo '

'.__('Blog has been successfully deleted.').'

'; +} + +echo '

'.__('List of blogs').'

'; + +if (!$core->error->flag()) +{ + if ($core->auth->isSuperAdmin()) { + echo '

'.__('Create a new blog').'

'; + } + + if (!$show_filters) { + echo '

'.__('Filters').'

'; + } + + echo + '
'. + '
'.__('Filters').''. + + '
'. + '

'. + '

'. + '
'. + + '
'. + '

'. + '

'. + '

'. + '
'. + + '
'. //Opera sucks + '
'. + '
'; + + # Show blogs + if ($nb_blog == 0) + { + echo '

'.__('No blog').'

'; + } + else + { + $pager = new pager($page,$nb_blog,$nb_per_page,10); + $pager->var_page = 'page'; + + echo '

'.__('Page(s)').' : '.$pager->getLinks().'

'; + + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + while ($rs->fetch()) { + echo blogLine($rs); + } + + echo '
'.__('Blog name').''.__('Last update').''.__('Entries').''.__('Blog ID').' '.__('Status').'
'; + + echo '

'.__('Page(s)').' : '.$pager->getLinks().'

'; + } +} + +dcPage::close(); + +function blogLine(&$rs) +{ + global $core; + + $blog_id = html::escapeHTML($rs->blog_id); + $edit_link = ''; + + if ($GLOBALS['core']->auth->isSuperAdmin()) { + $edit_link = + ''. + __('edit').''; + } + + $img_status = $rs->blog_status == 1 ? 'check-on' : 'check-off'; + $txt_status = $GLOBALS['core']->getBlogStatus($rs->blog_status); + $img_status = sprintf('%2$s',$img_status,$txt_status); + + return + ''. + ''. + html::escapeHTML($rs->blog_name).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->blog_upddt).''. + ''.$core->countBlogPosts($rs->blog_id).''. + ''.$blog_id.''. + ''.$edit_link.''. + ''.$img_status.''. + ''; +} +?> \ No newline at end of file diff --git a/admin/categories.php b/admin/categories.php new file mode 100644 index 0000000..b832269 --- /dev/null +++ b/admin/categories.php @@ -0,0 +1,188 @@ +blog->getCategory((integer) $_POST['del_cat']); + if ($c->isEmpty()) { + throw new Exception(__('This category does not exist.')); + } + unset($c); + $core->blog->delCategory($_POST['del_cat']); + http::redirect('categories.php?del=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Reset order +if (!empty($_POST['reset'])) +{ + try + { + $core->blog->resetCategoriesOrder(); + http::redirect('categories.php?reord=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +/* Display +-------------------------------------------------------- */ +dcPage::open(__('Categories'), + dcPage::jsToolMan()."\n". + dcPage::jsLoad('js/_categories.js') +); + +if (!empty($_GET['add'])) { + echo '

'.__('The category has been successfully created.').'

'; +} +if (!empty($_GET['del'])) { + echo '

'.__('The category has been successfully removed.').'

'; +} +if (!empty($_GET['reord'])) { + echo '

'.__('Categories have been successfully reordered.').'

'; +} +if (!empty($_GET['moved'])) { + echo '

'.__('The category has been successfully moved.').'

'; +} + +echo '

'.html::escapeHTML($core->blog->name).' › '.__('Categories').'

'; + +$rs = $core->blog->getCategories(array('post_type'=>'post')); + +echo +'
'. +'
'; +if ($rs->isEmpty()) +{ + echo '

'.__('No category yet.').'

'; +} +else +{ + echo + '

'.__('Categories list').'

'. + '
'; + + $ref_level = $level = $rs->level-1; + while ($rs->fetch()) + { + $attr = 'id="cat'.$rs->cat_id.'"'; + if ($rs->nb_total == 0) { + $attr .= ' class="deletable"'; + } + + if ($rs->level > $level) { + echo str_repeat('
  • ',$rs->level - $level); + } elseif ($rs->level < $level) { + echo str_repeat('
',-($rs->level - $level)); + } + + if ($rs->level <= $level) { + echo '
  • '; + } + + echo + '

    '.html::escapeHTML($rs->cat_title).''. + ' ('. + sprintf(($rs->nb_post > 1 ? __('%d entries') : __('%d entry') ),$rs->nb_post).''. + ', '.__('total:').' '.$rs->nb_total.')

    '. + '

    '.__('URL:').' '.html::escapeHTML($rs->cat_url).'

    '; + + $level = $rs->level; + } + + if ($ref_level - $level < 0) { + echo str_repeat('
  • ',-($ref_level - $level)); + } + echo '
    '; +} +echo '
    '; + +echo '
    '. + +'
    '. +'
    '.__('Add a new category').''. +'

    '. +'

    '. +'

    '. +$core->formNonce(). +'
    '. +'
    '; + +if (!$rs->isEmpty()) +{ + $deletable = array(); + $l = $rs->level; + $full_name = array($rs->cat_title); + while ($rs->fetch()) + { + if ($rs->level < $l) { + $full_name = array(); + } elseif ($rs->level == $l) { + array_pop($full_name); + } + $full_name[] = html::escapeHTML($rs->cat_title); + if ($rs->nb_post == 0) { + $deletable[implode(' / ',$full_name)] = $rs->cat_id; + } + $l = $rs->level; + } + + if (count($deletable) > 0) + { + echo + '
    '. + '
    '.__('Remove a category').''. + '

    '. + '

    '. + $core->formNonce(). + '
    '. + '
    '; + } + + echo + '
    '. + '
    '.__('Reorder categories').''. + '

    '.__('This will relocate all categories on the top level').'

    '. + '

    '. + form::hidden(array('reset'),1). + $core->formNonce(). + '
    '. + '
    '; +} +echo '
    '; +echo '
    '; + +dcPage::helpBlock('core_categories'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/category.php b/admin/category.php new file mode 100644 index 0000000..76a26bf --- /dev/null +++ b/admin/category.php @@ -0,0 +1,260 @@ +blog->getCategory($_REQUEST['id']); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if (!$core->error->flag() && !$rs->isEmpty()) + { + $cat_id = (integer) $rs->cat_id; + $cat_title = $rs->cat_title; + $cat_url = $rs->cat_url; + $cat_desc = $rs->cat_desc; + } + unset($rs); + + # Getting hierarchy information + $parents = $core->blog->getCategoryParents($cat_id); + $rs = $core->blog->getCategoryParent($cat_id); + $cat_parent = $rs->isEmpty() ? 0 : (integer) $rs->cat_id; + unset($rs); + + # Allowed parents list + $children = $core->blog->getCategories(array('post_type'=>'post','start'=>$cat_id)); + $allowed_parents = array(__('Top level')=>0); + + $p = array(); + while ($children->fetch()) { + $p[$children->cat_id] = 1; + } + + $rs = $core->blog->getCategories(array('post_type'=>'post')); + while ($rs->fetch()) { + if (!isset($p[$rs->cat_id])) { + $allowed_parents[] = new formSelectOption( + str_repeat('  ',$rs->level-1).'• '.html::escapeHTML($rs->cat_title), + $rs->cat_id + ); + } + } + unset($rs); + + # Allowed siblings list + $siblings = array(); + $rs = $core->blog->getCategoryFirstChildren($cat_parent); + while ($rs->fetch()) { + if ($rs->cat_id != $cat_id) { + $siblings[html::escapeHTML($rs->cat_title)] = $rs->cat_id; + } + } + unset($rs); +} + +# Changing parent +if ($cat_id && isset($_POST['cat_parent'])) +{ + $new_parent = (integer) $_POST['cat_parent']; + if ($cat_parent != $new_parent) + { + try { + $core->blog->setCategoryParent($cat_id,$new_parent); + http::redirect('categories.php?moved=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } +} + +# Changing sibling +if ($cat_id && isset($_POST['cat_sibling'])) +{ + try { + $core->blog->setCategoryPosition($cat_id,(integer) $_POST['cat_sibling'],$_POST['cat_move']); + http::redirect('categories.php?moved=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Create or update a category +if (isset($_POST['cat_title'])) +{ + $cur = $core->con->openCursor($core->prefix.'category'); + + $cur->cat_title = $cat_title = $_POST['cat_title']; + + if (isset($_POST['cat_desc'])) { + $cur->cat_desc = $cat_desc = $_POST['cat_desc']; + } + + if (isset($_POST['cat_url'])) { + $cur->cat_url = $cat_url = $_POST['cat_url']; + } else { + $cur->cat_url = $cat_url; + } + + try + { + # Update category + if ($cat_id) + { + # --BEHAVIOR-- adminBeforeCategoryUpdate + $core->callBehavior('adminBeforeCategoryUpdate',$cur,$cat_id); + + $core->blog->updCategory($_POST['id'],$cur); + + # --BEHAVIOR-- adminAfterCategoryUpdate + $core->callBehavior('adminAfterCategoryUpdate',$cur,$cat_id); + + http::redirect('category.php?id='.$_POST['id'].'&upd=1'); + } + # Create category + else + { + # --BEHAVIOR-- adminBeforeCategoryCreate + $core->callBehavior('adminBeforeCategoryCreate',$cur); + + $id = $core->blog->addCategory($cur,(integer) $_POST['new_cat_parent']); + + # --BEHAVIOR-- adminAfterCategoryCreate + $core->callBehavior('adminAfterCategoryCreate',$cur,$id); + + http::redirect('categories.php?add=1'); + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +$title = $cat_id ? html::escapeHTML($cat_title) : __('New category'); + +dcPage::open($title, + dcPage::jsConfirmClose('category-form'). + dcPage::jsToolBar(). + dcPage::jsLoad('js/_category.js') +); + +if (!empty($_GET['upd'])) { + echo '

    '.__('Category has been successfully updated.').'

    '; +} + +echo +'

    '.html::escapeHTML($core->blog->name).' › '. +__('Categories').' › '; + +if ($cat_id) +{ + while($parents->fetch()) { + echo ''.html::escapeHTML($parents->cat_title).''; + echo " › "; + } +} + +echo $title.'

    '; + +echo +'
    '. +'
    '.__('Category information').''. +'

    '; +if (!$cat_id) +{ + $rs = $core->blog->getCategories(array('post_type'=>'post')); + echo + '

    '; + unset($rs); +} +echo +'
    '. +'

    '. +'

    '. +__('Warning: If you set the URL manually, it may conflict with another category.').'

    '. +'
    '. + +'

    '. +form::textarea('cat_desc',50,8,html::escapeHTML($cat_desc),'',5). +'

    '. + +'

    '. +($cat_id ? form::hidden('id',$cat_id) : ''). +$core->formNonce(). +'

    '. +'
    '. +'
    '; + +if ($cat_id) +{ + echo + '

    '.__('Move this category').'

    '. + '
    '. + '
    '. + + '
    '. + '
    '.__('Category parent').''. + '

    '. + '

    '. + form::hidden(array('id'),$cat_id).$core->formNonce().'

    '. + '
    '. + '
    '. + '
    '; + + if (count($siblings) > 0) { + echo + '
    '. + '
    '. + '
    '.__('Category sibling').''. + '

    '. + form::combo('cat_move',array(__('before')=>'before',__('after')=>'after')).' '. + form::combo('cat_sibling',$siblings).'

    '. + '

    '. + form::hidden(array('id'),$cat_id).$core->formNonce().'

    '. + '
    '. + '
    '. + '
    '; + } + + echo '
    '; +} + +dcPage::helpBlock('core_categories'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/comment.php b/admin/comment.php new file mode 100644 index 0000000..bdb7776 --- /dev/null +++ b/admin/comment.php @@ -0,0 +1,232 @@ +blog->getAllCommentStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# Adding comment +if (!empty($_POST['add']) && !empty($_POST['post_id'])) +{ + try + { + $rs = $core->blog->getPosts(array('post_id' => $_POST['post_id'], 'post_type' => '')); + + if ($rs->isEmpty()) { + throw new Exception(__('Entry does not exist.')); + } + + $cur = $core->con->openCursor($core->prefix.'comment'); + + $cur->comment_author = $_POST['comment_author']; + $cur->comment_email = html::clean($_POST['comment_email']); + $cur->comment_site = html::clean($_POST['comment_site']); + $cur->comment_content = $core->HTMLfilter($_POST['comment_content']); + $cur->post_id = (integer) $_POST['post_id']; + + # --BEHAVIOR-- adminBeforeCommentCreate + $core->callBehavior('adminBeforeCommentCreate',$cur); + + $comment_id = $core->blog->addComment($cur); + + # --BEHAVIOR-- adminAfterCommentCreate + $core->callBehavior('adminAfterCommentCreate',$cur,$comment_id); + + http::redirect($core->getPostAdminURL($rs->post_type,$rs->post_id,false).'&co=1&creaco=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +if (!empty($_REQUEST['id'])) +{ + $params['comment_id'] = $_REQUEST['id']; + + try { + $rs = $core->blog->getComments($params); + if (!$rs->isEmpty()) { + $comment_id = $rs->comment_id; + $post_id = $rs->post_id; + $post_type = $rs->post_type; + $post_title = $rs->post_title; + $comment_dt = $rs->comment_dt; + $comment_author = $rs->comment_author; + $comment_email = $rs->comment_email; + $comment_site = $rs->comment_site; + $comment_content = $rs->comment_content; + $comment_ip = $rs->comment_ip; + $comment_status = $rs->comment_status; + $comment_trackback = (boolean) $rs->comment_trackback; + $comment_spam_status = $rs->comment_spam_status; + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +if (!$comment_id && !$core->error->flag()) { + $core->error->add(__('No comment')); +} + +if (!$core->error->flag() && isset($rs)) +{ + $can_edit = $can_delete = $can_publish = $core->auth->check('contentadmin',$core->blog->id); + + if (!$core->auth->check('contentadmin',$core->blog->id) && $core->auth->userID() == $rs->user_id) { + $can_edit = true; + if ($core->auth->check('delete',$core->blog->id)) { + $can_delete = true; + } + if ($core->auth->check('publish',$core->blog->id)) { + $can_publish = true; + } + } + + # update comment + if (!empty($_POST['update']) && $can_edit) + { + $cur = $core->con->openCursor($core->prefix.'comment'); + + $cur->comment_author = $_POST['comment_author']; + $cur->comment_email = html::clean($_POST['comment_email']); + $cur->comment_site = html::clean($_POST['comment_site']); + $cur->comment_content = $core->HTMLfilter($_POST['comment_content']); + + if (isset($_POST['comment_status'])) { + $cur->comment_status = (integer) $_POST['comment_status']; + } + + try + { + # --BEHAVIOR-- adminBeforeCommentUpdate + $core->callBehavior('adminBeforeCommentUpdate',$cur,$comment_id); + + $core->blog->updComment($comment_id,$cur); + + # --BEHAVIOR-- adminAfterCommentUpdate + $core->callBehavior('adminAfterCommentUpdate',$cur,$comment_id); + + http::redirect('comment.php?id='.$comment_id.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + + if (!empty($_POST['delete']) && $can_delete) + { + try { + $core->blog->delComment($comment_id); + http::redirect($core->getPostAdminURL($rs->post_type,$rs->post_id).'&co=1#c'.$comment_id,false); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$can_edit) { + $core->error->add(__("You can't edit this comment.")); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Edit comment'), + dcPage::jsConfirmClose('comment-form'). + dcPage::jsToolBar(). + dcPage::jsLoad('js/_comment.js') +); + +if ($comment_id) +{ + if (!empty($_GET['upd'])) { + echo '

    '.__('Comment has been successfully updated.').'

    '; + } + + $comment_mailto = ''; + if ($comment_email) + { + $comment_mailto = 'getPostURL())) + .'">'.__('Send an e-mail').''; + } + + echo '

    '.html::escapeHTML($core->blog->name).' › '.__('Edit comment').'

    '; + + echo '

    '. + sprintf(__('Back to "%s"'),$post_title).'

    '; + + echo + '
    '. + '

    '. + ''.$comment_ip.'

    '. + + '

    '. + dt::dt2str(__('%Y-%m-%d %H:%M'),$comment_dt).'

    '. + + '

    '. + + '

    '. + + '

    '. + + '

    '. + + # --BEHAVIOR-- adminAfterCommentDesc + $core->callBehavior('adminAfterCommentDesc', $rs). + + '

    '. + form::textarea('comment_content',50,10,html::escapeHTML($comment_content)). + '

    '. + + '

    '.form::hidden('id',$comment_id). + $core->formNonce(). + ' '; + + if ($can_delete) { + echo ''; + } + echo + '

    '. + '
    '; +} + +dcPage::helpBlock('core_comments'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/comments.php b/admin/comments.php new file mode 100644 index 0000000..cbcc32f --- /dev/null +++ b/admin/comments.php @@ -0,0 +1,233 @@ + '' +); +foreach ($core->blog->getAllCommentStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +$type_combo = array( +'-' => '', +__('comment') => 'co', +__('trackback') => 'tb' +); + +$sortby_combo = array( +__('Date') => 'comment_dt', +__('Entry title') => 'post_title', +__('Author') => 'comment_author', +__('Status') => 'comment_status' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + + +/* Get comments +-------------------------------------------------------- */ +$author = isset($_GET['author']) ? $_GET['author'] : ''; +$status = isset($_GET['status']) ? $_GET['status'] : ''; +$type = !empty($_GET['type']) ? $_GET['type'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'comment_dt'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'desc'; +$ip = !empty($_GET['ip']) ? $_GET['ip'] : ''; + +$with_spam = $author || $status || $type || $sortby != 'comment_dt' || $order != 'desc' || $ip; + +$show_filters = false; + +$page = !empty($_GET['page']) ? (integer) $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + if ($nb_per_page != $_GET['nb']) { + $show_filters = true; + } + $nb_per_page = (integer) $_GET['nb']; +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; + +# Author filter +if ($author !== '') { + $params['q_author'] = $author; + $show_filters = true; +} + +# - Type filter +if ($type == 'tb' || $type == 'co') { + $params['comment_trackback'] = ($type == 'tb'); + $show_filters = true; +} + +# - Status filter +if ($status !== '' && in_array($status,$status_combo)) { + $params['comment_status'] = $status; + $show_filters = true; +} elseif (!$with_spam) { + $params['comment_status_not'] = -2; +} + +# - IP filter +if ($ip) { + $params['comment_ip'] = $ip; + $show_filters = true; +} + +# Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + } + + if ($sortby != 'comment_dt' || $order != 'desc') { + $show_filters = true; + } +} + +# Actions combo box +$combo_action = array(); +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; +} +if ($core->auth->check('delete,contentadmin',$core->blog->id)) +{ + $combo_action[__('delete')] = 'delete'; +} + + +/* Get comments +-------------------------------------------------------- */ +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()); +} + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_comments.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} +# --BEHAVIOR-- adminCommentsHeaders +$starting_script .= $core->callBehavior('adminCommentsHeaders'); + +dcPage::open(__('Comments'),$starting_script); + +echo '

    '.html::escapeHTML($core->blog->name).' › '.__('Comments').'

    '; + +if (!$core->error->flag()) +{ + # Filters + if (!$show_filters) { + echo '

    '. + __('Filters').'

    '; + } + + echo + '
    '. + '
    '.__('Filters').''. + '
    '. + '
    '. + ' '. + ''. + '
    '. + + '
    '. + '

    '. + '

    '. + '

    '. + '
    '. + + '
    '. + '

    '. + '

    '. + '

    '. + '
    '. + + '
    '. + '
    '. //Opera sucks + '
    '. + '
    '; + + if (!$with_spam) { + $spam_count = $core->blog->getComments(array('comment_status'=>-2),true)->f(0); + if ($spam_count == 1) { + echo '

    '.sprintf(__('You have one spam comments.'),''.$spam_count.'').' '. + ''.__('Show it.').'

    '; + } elseif ($spam_count > 1) { + echo '

    '.sprintf(__('You have %s spam comments.'),''.$spam_count.'').' '. + ''.__('Show them.').'

    '; + } + } + + # Show comments + $comment_list->display($page,$nb_per_page, + '
    '. + + '%s'. + + '
    '. + '

    '. + + '

    '.__('Selected comments action:').' '. + form::combo('action',$combo_action). + $core->formNonce(). + '

    '. + form::hidden(array('type'),$type). + form::hidden(array('sortby'),$sortby). + form::hidden(array('order'),$order). + form::hidden(array('author'),preg_replace('/%/','%%',$author)). + form::hidden(array('status'),$status). + form::hidden(array('ip'),preg_replace('/%/','%%',$ip)). + form::hidden(array('page'),$page). + form::hidden(array('nb'),$nb_per_page). + '
    '. + + '
    ' + ); +} + +dcPage::helpBlock('core_comments'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/comments_actions.php b/admin/comments_actions.php new file mode 100644 index 0000000..650ec15 --- /dev/null +++ b/admin/comments_actions.php @@ -0,0 +1,97 @@ + $v) { + $comments[$k] = (integer) $v; + } + + $params['sql'] = 'AND C.comment_id IN('.implode(',',$comments).') '; + $params['no_content'] = true; + + $co = $core->blog->getComments($params); + + if (preg_match('/^(publish|unpublish|pending|junk)$/',$action)) + { + switch ($action) { + case 'unpublish' : $status = 0; break; + case 'pending' : $status = -1; break; + case 'junk' : $status = -2; break; + default : $status = 1; break; + } + + while ($co->fetch()) + { + try { + $core->blog->updCommentStatus($co->comment_id,$status); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect($redir); + } + } + elseif ($action == 'delete') + { + while ($co->fetch()) + { + try { + $core->blog->delComment($co->comment_id); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect($redir); + } + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Comments')); + +echo '

    '.__('back').'

    '; + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/dispatcher.php b/admin/dispatcher.php new file mode 100644 index 0000000..22754e5 --- /dev/null +++ b/admin/dispatcher.php @@ -0,0 +1,34 @@ +What the hell are you doing here?

    '; +exit; +?> \ No newline at end of file diff --git a/admin/images/attach.png b/admin/images/attach.png new file mode 100644 index 0000000..f87bc77 Binary files /dev/null and b/admin/images/attach.png differ diff --git a/admin/images/check-off.png b/admin/images/check-off.png new file mode 100644 index 0000000..fc5f661 Binary files /dev/null and b/admin/images/check-off.png differ diff --git a/admin/images/check-on.png b/admin/images/check-on.png new file mode 100644 index 0000000..c36f26c Binary files /dev/null and b/admin/images/check-on.png differ diff --git a/admin/images/check-wrn.png b/admin/images/check-wrn.png new file mode 100644 index 0000000..fa327f0 Binary files /dev/null and b/admin/images/check-wrn.png differ diff --git a/admin/images/date-picker.png b/admin/images/date-picker.png new file mode 100644 index 0000000..e35e932 Binary files /dev/null and b/admin/images/date-picker.png differ diff --git a/admin/images/dotclear_pw.png b/admin/images/dotclear_pw.png new file mode 100644 index 0000000..593a913 Binary files /dev/null and b/admin/images/dotclear_pw.png differ diff --git a/admin/images/edit-mini.png b/admin/images/edit-mini.png new file mode 100644 index 0000000..890bae6 Binary files /dev/null and b/admin/images/edit-mini.png differ diff --git a/admin/images/help.png b/admin/images/help.png new file mode 100644 index 0000000..57c87c2 Binary files /dev/null and b/admin/images/help.png differ diff --git a/admin/images/junk.png b/admin/images/junk.png new file mode 100644 index 0000000..7f4b667 Binary files /dev/null and b/admin/images/junk.png differ diff --git a/admin/images/locker.png b/admin/images/locker.png new file mode 100644 index 0000000..cec3260 Binary files /dev/null and b/admin/images/locker.png differ diff --git a/admin/images/media/audio.png b/admin/images/media/audio.png new file mode 100644 index 0000000..fec7e22 Binary files /dev/null and b/admin/images/media/audio.png differ diff --git a/admin/images/media/blank.png b/admin/images/media/blank.png new file mode 100644 index 0000000..0f0c2b9 Binary files /dev/null and b/admin/images/media/blank.png differ diff --git a/admin/images/media/document.png b/admin/images/media/document.png new file mode 100644 index 0000000..5af28a8 Binary files /dev/null and b/admin/images/media/document.png differ diff --git a/admin/images/media/executable.png b/admin/images/media/executable.png new file mode 100644 index 0000000..bb830b8 Binary files /dev/null and b/admin/images/media/executable.png differ diff --git a/admin/images/media/folder.png b/admin/images/media/folder.png new file mode 100644 index 0000000..055a9bb Binary files /dev/null and b/admin/images/media/folder.png differ diff --git a/admin/images/media/html.png b/admin/images/media/html.png new file mode 100644 index 0000000..eed6e04 Binary files /dev/null and b/admin/images/media/html.png differ diff --git a/admin/images/media/image.png b/admin/images/media/image.png new file mode 100644 index 0000000..811e0ed Binary files /dev/null and b/admin/images/media/image.png differ diff --git a/admin/images/media/package.png b/admin/images/media/package.png new file mode 100644 index 0000000..82f5cdf Binary files /dev/null and b/admin/images/media/package.png differ diff --git a/admin/images/media/presentation.png b/admin/images/media/presentation.png new file mode 100644 index 0000000..282d350 Binary files /dev/null and b/admin/images/media/presentation.png differ diff --git a/admin/images/media/spreadsheet.png b/admin/images/media/spreadsheet.png new file mode 100644 index 0000000..8427509 Binary files /dev/null and b/admin/images/media/spreadsheet.png differ diff --git a/admin/images/media/text.png b/admin/images/media/text.png new file mode 100644 index 0000000..1e91806 Binary files /dev/null and b/admin/images/media/text.png differ diff --git a/admin/images/media/video.png b/admin/images/media/video.png new file mode 100644 index 0000000..a0ed12c Binary files /dev/null and b/admin/images/media/video.png differ diff --git a/admin/images/menu/blog-pref-b.png b/admin/images/menu/blog-pref-b.png new file mode 100644 index 0000000..ad1e18a Binary files /dev/null and b/admin/images/menu/blog-pref-b.png differ diff --git a/admin/images/menu/blog-theme-b.png b/admin/images/menu/blog-theme-b.png new file mode 100644 index 0000000..5284cb8 Binary files /dev/null and b/admin/images/menu/blog-theme-b.png differ diff --git a/admin/images/menu/blogs.png b/admin/images/menu/blogs.png new file mode 100644 index 0000000..7cbc1f3 Binary files /dev/null and b/admin/images/menu/blogs.png differ diff --git a/admin/images/menu/categories.png b/admin/images/menu/categories.png new file mode 100644 index 0000000..9503b6b Binary files /dev/null and b/admin/images/menu/categories.png differ diff --git a/admin/images/menu/comments-b.png b/admin/images/menu/comments-b.png new file mode 100644 index 0000000..09c9adf Binary files /dev/null and b/admin/images/menu/comments-b.png differ diff --git a/admin/images/menu/comments.png b/admin/images/menu/comments.png new file mode 100644 index 0000000..b2c7568 Binary files /dev/null and b/admin/images/menu/comments.png differ diff --git a/admin/images/menu/dashboard.png b/admin/images/menu/dashboard.png new file mode 100644 index 0000000..73e5a9c Binary files /dev/null and b/admin/images/menu/dashboard.png differ diff --git a/admin/images/menu/edit-b.png b/admin/images/menu/edit-b.png new file mode 100644 index 0000000..d0bfbd5 Binary files /dev/null and b/admin/images/menu/edit-b.png differ diff --git a/admin/images/menu/edit.png b/admin/images/menu/edit.png new file mode 100644 index 0000000..ca59d5f Binary files /dev/null and b/admin/images/menu/edit.png differ diff --git a/admin/images/menu/entries-b.png b/admin/images/menu/entries-b.png new file mode 100644 index 0000000..35d652a Binary files /dev/null and b/admin/images/menu/entries-b.png differ diff --git a/admin/images/menu/entries.png b/admin/images/menu/entries.png new file mode 100644 index 0000000..304e35f Binary files /dev/null and b/admin/images/menu/entries.png differ diff --git a/admin/images/menu/langs.png b/admin/images/menu/langs.png new file mode 100644 index 0000000..71c4b50 Binary files /dev/null and b/admin/images/menu/langs.png differ diff --git a/admin/images/menu/media.png b/admin/images/menu/media.png new file mode 100644 index 0000000..a85894c Binary files /dev/null and b/admin/images/menu/media.png differ diff --git a/admin/images/menu/plugins.png b/admin/images/menu/plugins.png new file mode 100644 index 0000000..21c3dd1 Binary files /dev/null and b/admin/images/menu/plugins.png differ diff --git a/admin/images/menu/search.png b/admin/images/menu/search.png new file mode 100644 index 0000000..1ba1941 Binary files /dev/null and b/admin/images/menu/search.png differ diff --git a/admin/images/menu/update.png b/admin/images/menu/update.png new file mode 100644 index 0000000..dad1429 Binary files /dev/null and b/admin/images/menu/update.png differ diff --git a/admin/images/menu/user-pref-b.png b/admin/images/menu/user-pref-b.png new file mode 100644 index 0000000..ed66544 Binary files /dev/null and b/admin/images/menu/user-pref-b.png differ diff --git a/admin/images/menu/users.png b/admin/images/menu/users.png new file mode 100644 index 0000000..c6d9b2c Binary files /dev/null and b/admin/images/menu/users.png differ diff --git a/admin/images/menu_off.png b/admin/images/menu_off.png new file mode 100644 index 0000000..7bb6523 Binary files /dev/null and b/admin/images/menu_off.png differ diff --git a/admin/images/menu_on.png b/admin/images/menu_on.png new file mode 100644 index 0000000..9b3ccdf Binary files /dev/null and b/admin/images/menu_on.png differ diff --git a/admin/images/minus.png b/admin/images/minus.png new file mode 100644 index 0000000..5baa0f3 Binary files /dev/null and b/admin/images/minus.png differ diff --git a/admin/images/noscreenshot.png b/admin/images/noscreenshot.png new file mode 100644 index 0000000..e9ba66d Binary files /dev/null and b/admin/images/noscreenshot.png differ diff --git a/admin/images/picker.png b/admin/images/picker.png new file mode 100644 index 0000000..d70e6b2 Binary files /dev/null and b/admin/images/picker.png differ diff --git a/admin/images/plus.png b/admin/images/plus.png new file mode 100644 index 0000000..77229ad Binary files /dev/null and b/admin/images/plus.png differ diff --git a/admin/images/scheduled.png b/admin/images/scheduled.png new file mode 100644 index 0000000..f6e2e9c Binary files /dev/null and b/admin/images/scheduled.png differ diff --git a/admin/images/selected.png b/admin/images/selected.png new file mode 100644 index 0000000..b25834b Binary files /dev/null and b/admin/images/selected.png differ diff --git a/admin/images/trash.png b/admin/images/trash.png new file mode 100644 index 0000000..3991899 Binary files /dev/null and b/admin/images/trash.png differ diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..2142eb6 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,257 @@ +setUserDefaultBlog($core->auth->userID(),$core->blog->id); + http::redirect('index.php'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +dcPage::check('usage,contentadmin'); + +# Logout +if (!empty($_GET['logout'])) { + $core->session->destroy(); + if (isset($_COOKIE['dc_admin'])) { + unset($_COOKIE['dc_admin']); + setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL); + } + http::redirect('auth.php'); + exit; +} + +# Plugin install +$plugins_install = $core->plugins->installModules(); + +# Dashboard icons +$__dashboard_icons = new ArrayObject(); + +$post_count = $core->blog->getPosts(array(),true)->f(0); +$str_entries = ($post_count > 1) ? __('%d entries') : __('%d entry'); + +$comment_count = $core->blog->getComments(array(),true)->f(0); +$str_comments = ($comment_count > 1) ? __('%d comments') : __('%d comment'); + +$__dashboard_icons['new_post'] = new ArrayObject(array(__('New entry'),'post.php','images/menu/edit-b.png')); +$__dashboard_icons['posts'] = new ArrayObject(array(sprintf($str_entries,$post_count),'posts.php','images/menu/entries-b.png')); +$__dashboard_icons['comments'] = new ArrayObject(array(sprintf($str_comments,$comment_count),'comments.php','images/menu/comments-b.png')); +$__dashboard_icons['prefs'] = new ArrayObject(array(__('User preferences'),'preferences.php','images/menu/user-pref-b.png')); + +if ($core->auth->check('admin',$core->blog->id)) +{ + $__dashboard_icons['blog_pref'] = new ArrayObject(array(__('Blog settings'),'blog_pref.php','images/menu/blog-pref-b.png')); + $__dashboard_icons['blog_theme'] = new ArrayObject(array(__('Blog aspect'),'blog_theme.php','images/menu/blog-theme-b.png')); +} + +$core->callBehavior('adminDashboardIcons', $core, $__dashboard_icons); + + +# Latest news for dashboard +$__dashboard_items = new ArrayObject(array(new ArrayObject,new ArrayObject)); + +# Documentation links +if (!empty($__resources['doc'])) +{ + $doc_links = '

    '.__('Documentation').'

      '; + + foreach ($__resources['doc'] as $k => $v) { + $doc_links .= '
    • '.$k.'
    • '; + } + + $doc_links .= '
    '; + $__dashboard_items[0][] = $doc_links; +} + +try +{ + if (empty($__resources['rss_news'])) { + throw new Exception(); + } + + $feed_reader = new feedReader; + $feed_reader->setCacheDir(DC_TPL_CACHE); + $feed_reader->setTimeout(2); + $feed_reader->setUserAgent('Dotclear - http://www.dotclear.org/'); + $feed = $feed_reader->parse($__resources['rss_news']); + if ($feed) + { + $latest_news = '

    '.__('Latest news').'

    '; + $i = 1; + foreach ($feed->items as $item) + { + $dt = isset($item->link) ? ''.$item->title.'' : $item->title; + + if ($i < 3) { + $latest_news .= + '
    '.$dt.'
    '. + '

    '.dt::dt2str('%d %B %Y',$item->pubdate,'Europe/Paris').': '. + ''.text::cutString(html::clean($item->content),120).'...

    '; + } else { + $latest_news .= + '
    '.$dt.'
    '. + '
    '.dt::dt2str('%d %B %Y',$item->pubdate,'Europe/Paris').'
    '; + } + $i++; + if ($i > 7) { break; } + } + $latest_news .= '
    '; + $__dashboard_items[1][] = $latest_news; + } +} +catch (Exception $e) {} + +$core->callBehavior('adminDashboardItems', $core, $__dashboard_items); + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Dashboard'), + dcPage::jsToolBar(). + dcPage::jsLoad('js/_index.js'). + # --BEHAVIOR-- adminDashboardHeaders + $core->callBehavior('adminDashboardHeaders') +); + +echo '

    '.html::escapeHTML($core->blog->name).' › '.__('Dashboard'); + +if ($core->auth->getInfo('user_default_blog') != $core->blog->id && $core->auth->blog_count > 1) { + echo + ' - '.__('Make this blog my default blog').''; +} + +echo '

    '; + +if ($core->blog->status == 0) { + echo '

    '.__('This blog is offline').'

    '; +} elseif ($core->blog->status == -1) { + echo '

    '.__('This blog is removed').'

    '; +} + +if (!DC_ADMIN_URL) { + echo + '

    '. + __('DC_ADMIN_URL is not defined, you should edit your configuration file.'). + '

    '; +} + +# 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 '
    '; +} + +# Dashboard icons +echo '
    '; +foreach ($__dashboard_icons as $i) +{ + echo + '

    '. + ''.$i[0].'

    '; +} +echo '
    '; + +if ($core->auth->check('usage,contentadmin',$core->blog->id)) +{ + $categories_combo = array(' ' => ''); + try { + $categories = $core->blog->getCategories(array('post_type'=>'post')); + while ($categories->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$categories->level-1).'• '.html::escapeHTML($categories->cat_title), + $categories->cat_id + ); + } + } catch (Exception $e) { } + + echo + '
    '. + '

    '.__('Quick entry').'

    '. + '
    '. + '
    '. + '

    '. + '

    '. + form::textarea('post_content',50,7,'','',2). + '

    '. + '

    '. + '

    '. + ($core->auth->check('publish',$core->blog->id) + ? '' + : ''). + $core->formNonce(). + form::hidden('post_status',-2). + form::hidden('post_format',$core->auth->getOption('post_format')). + form::hidden('post_excerpt',''). + form::hidden('post_lang',$core->auth->getInfo('user_lang')). + form::hidden('post_notes',''). + '

    '. + '
    '. + '
    '. + '
    '; +} + +echo '
    '; + +# Dashboard columns +echo '
    '; + +# Dotclear updates notifications +if ($core->auth->isSuperAdmin() && is_readable(DC_DIGESTS)) +{ + $updater = new dcUpdate(DC_UPDATE_URL,'dotclear',DC_UPDATE_VERSION,DC_TPL_CACHE.'/versions'); + $new_v = $updater->check(DC_VERSION); + + if ($updater->getNotify() && $new_v) { + echo + '

    '.sprintf(__('Dotclear %s is available!'),$new_v).'

    '. + '
    '; + } +} + +foreach ($__dashboard_items as $i) +{ + echo '
    '; + foreach ($i as $v) { + echo $v; + } + echo '
    '; +} +echo '
    '; + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/install/check.php b/admin/install/check.php new file mode 100644 index 0000000..16edaff --- /dev/null +++ b/admin/install/check.php @@ -0,0 +1,83 @@ +driver() == 'mysql') + { + if (version_compare($con->version(),'4.1','<')) + { + $err[] = sprintf(__('MySQL version is %s (4.1 or earlier needed).'),$con->version()); + } + else + { + $rs = $con->select('SHOW ENGINES'); + $innodb = false; + while ($rs->fetch()) { + if (strtolower($rs->f(0)) == 'innodb' && strtolower($rs->f(1)) != 'disabled' && strtolower($rs->f(1)) != 'no') { + $innodb = true; + break; + } + } + + if (!$innodb) { + $err[] = __('MySQL InnoDB engine is not available.'); + } + } + } + elseif ($con->driver() == 'pgsql') + { + if (version_compare($con->version(),'8.0','<')) + { + $err[] = sprintf(__('PostgreSQL version is %s (8.0 or earlier needed).'),$con->version()); + } + } + + return count($err) == 0; +} +?> \ No newline at end of file diff --git a/admin/install/index.php b/admin/install/index.php new file mode 100644 index 0000000..091a21b --- /dev/null +++ b/admin/install/index.php @@ -0,0 +1,313 @@ +wizard.','wizard.php'); + $code = 0; + include dirname(__FILE__).'/../../inc/core_error.php'; + exit; +} + +require dirname(__FILE__).'/../../inc/prepend.php'; +require dirname(__FILE__).'/check.php'; + +$can_install = true; +$err = ''; + +# Loading locales for detected language +$dlang = http::getAcceptLanguage(); +if ($dlang != 'en') +{ + l10n::init(); + l10n::set(dirname(__FILE__).'/../../locales/'.$dlang.'/main'); +} + +if (!defined('DC_MASTER_KEY') || DC_MASTER_KEY == '') { + $can_install = false; + $err = '

    '.__('Please set a master key (DC_MASTER_KEY) in configuration file.').'

    '; +} + +# Check if dotclear is already installed +$schema = dbSchema::init($core->con); +if (in_array($core->prefix.'post',$schema->getTables())) { + $can_install = false; + $err = '

    '.__('Dotclear is already installed.').'

    '; +} + +# Check system capabilites +if (!dcSystemCheck($core->con,$_e)) { + $can_install = false; + $err = '

    '.__('Dotclear cannot be installed.').'

    • '.implode('
    • ',$_e).'
    '; +} + +# Get information and perform install +$u_email = $u_firstname = $u_name = $u_login = $u_pwd = ''; +$mail_sent = false; +if ($can_install && !empty($_POST)) +{ + $u_email = !empty($_POST['u_email']) ? $_POST['u_email'] : null; + $u_firstname = !empty($_POST['u_firstname']) ? $_POST['u_firstname'] : null; + $u_name = !empty($_POST['u_name']) ? $_POST['u_name'] : null; + $u_login = !empty($_POST['u_login']) ? $_POST['u_login'] : null; + $u_pwd = !empty($_POST['u_pwd']) ? $_POST['u_pwd'] : null; + $u_pwd2 = !empty($_POST['u_pwd2']) ? $_POST['u_pwd2'] : null; + + try + { + # Check user information + if (empty($u_login)) { + throw new Exception(__('No user ID given')); + } + if (!preg_match('/^[A-Za-z0-9@._-]{2,}$/',$u_login)) { + throw new Exception(__('User ID must contain at least 2 characters using letters, numbers or symbols.')); + } + if ($u_email && !text::isEmail($u_email)) { + throw new Exception(__('Invalid email address')); + } + + if (empty($u_pwd)) { + throw new Exception(__('No password given')); + } + if ($u_pwd != $u_pwd2) { + throw new Exception(__("Passwords don't match")); + } + if (strlen($u_pwd) < 6) { + throw new Exception(__('Password must contain at least 6 characters.')); + } + + # Try to guess timezone + $default_tz = 'Europe/London'; + if (!empty($_POST['u_date']) && function_exists('timezone_open')) + { + if (preg_match('/\((.+)\)$/',$_POST['u_date'],$_tz)) { + $_tz = $_tz[1]; + $_tz = @timezone_open($_tz); + if ($_tz instanceof DateTimeZone) { + $_tz = @timezone_name_get($_tz); + if ($_tz) { + $default_tz = $_tz; + } + } + unset($_tz); + } + } + + # Create schema + $_s = new dbStruct($core->con,$core->prefix); + require dirname(__FILE__).'/../../inc/dbschema/db-schema.php'; + + $si = new dbStruct($core->con,$core->prefix); + $changes = $si->synchronize($_s); + + # Create user + $cur = $core->con->openCursor($core->prefix.'user'); + $cur->user_id = $u_login; + $cur->user_super = 1; + $cur->user_pwd = crypt::hmac(DC_MASTER_KEY,$u_pwd); + $cur->user_name = (string) $u_name; + $cur->user_firstname = (string) $u_firstname; + $cur->user_email = (string) $u_email; + $cur->user_lang = $dlang; + $cur->user_tz = $default_tz; + $cur->user_creadt = array('NOW()'); + $cur->user_upddt = array('NOW()'); + $cur->user_options = serialize($core->userDefaults()); + $cur->insert(); + + $core->auth->checkUser($u_login); + + $admin_url = preg_replace('%install/index.php$%','',$_SERVER['REQUEST_URI']); + $root_url = preg_replace('%/admin/install/index.php$%','',$_SERVER['REQUEST_URI']); + + # Create blog + $cur = $core->con->openCursor($core->prefix.'blog'); + $cur->blog_id = 'default'; + $cur->blog_url = http::getHost().$root_url.'/index.php?'; + $cur->blog_name = __('My first blog'); + $core->addBlog($cur); + $core->blogDefaults($cur->blog_id); + + $blog_settings = new dcSettings($core,'default'); + $blog_settings->setNameSpace('system'); + $blog_settings->put('blog_timezone',$default_tz); + $blog_settings->put('lang',$dlang); + $blog_settings->put('public_url',$root_url.'/public'); + $blog_settings->put('themes_url',$root_url.'/themes'); + + # Add Dotclear version + $cur = $core->con->openCursor($core->prefix.'version'); + $cur->module = 'core'; + $cur->version = (string) DC_VERSION; + $cur->insert(); + + # Create first post + $core->setBlog('default'); + + $cur = $core->con->openCursor($core->prefix.'post'); + $cur->user_id = $u_login; + $cur->post_format = 'xhtml'; + $cur->post_lang = $dlang; + $cur->post_title = __('Welcome to Dotclear!'); + $cur->post_content = '

    '.__('This is your first entry. When you\'re ready '. + 'to blog, log in to edit or delete it.').'

    '; + $cur->post_content_xhtml = $cur->post_content; + $cur->post_status = 1; + $cur->post_open_comment = 1; + $cur->post_open_tb = 0; + $post_id = $core->blog->addPost($cur); + + # Add a comment to it + $cur = $core->con->openCursor($core->prefix.'comment'); + $cur->post_id = $post_id; + $cur->comment_tz = $default_tz; + $cur->comment_author = __('Dotclear Team'); + $cur->comment_email = 'contact@dotclear.net'; + $cur->comment_site = 'http://www.dotclear.org/'; + $cur->comment_content = __("

    This is a comment.

    \n

    To delete it, log in and ". + "view your blog's comments. Then you might remove or edit it.

    "); + $core->blog->addComment($cur); + + $step = 1; + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} + +if (!isset($step)) { + $step = 0; +} +header('Content-Type: text/html; charset=UTF-8'); +?> + + + + + + + + + + + Dotclear Install + + + + + + + +
    +'.__('Dotclear installation').''; + +if (!is_writable(DC_TPL_CACHE)) { + echo '

    '.sprintf(__('Cache directory %s is not writable.'),DC_TPL_CACHE).'

    '; +} + +if (!empty($err)) { + echo '

    '.__('Errors:').'

    '.$err.'
    '; +} + +if (!empty($_GET['wiz'])) { + echo '

    '.__('Configuration file has been successfully created.').'

    '; +} + +if ($can_install && $step == 0) +{ + echo + '

    '.__('User information').'

    '. + + '

    '.__('Please provide the following information needed to create the first user.').'

    '. + + '
    '. + '
    '.__('User information').''. + '

    '. + '

    '. + '

    '. + '
    '. + + '
    '.__('Username and password').''. + '

    '. + '

    '. + '

    '. + '
    '. + + '

    '. + '
    '; +} +elseif ($can_install && $step == 1) +{ + echo + '

    '.__('All done!').'

    '. + + '

    '.__('Dotclear has been successfully installed. Here is some useful information you should keep.').'

    '. + + '

    '.__('Your account').'

    '. + '
      '. + '
    • '.__('Username:').' '.html::escapeHTML($u_login).'
    • '. + '
    • '.__('Password:').' '.html::escapeHTML($u_pwd).'
    • '. + '
    '. + + '

    '.__('Your blog').'

    '. + '
      '. + '
    • '.__('Blog address:').' '.html::escapeHTML(http::getHost().$root_url).'/index.php?
    • '. + '
    • '.__('Administration interface:').' '.html::escapeHTML(http::getHost().$admin_url).'
    • '. + '
    '. + + '
    '. + '

    '. + form::hidden(array('user_id'),html::escapeHTML($u_login)). + form::hidden(array('user_pwd'),html::escapeHTML($u_pwd)). + '

    '. + '
    '; +} +?> +
    + + \ No newline at end of file diff --git a/admin/install/wizard.php b/admin/install/wizard.php new file mode 100644 index 0000000..73ab1d5 --- /dev/null +++ b/admin/install/wizard.php @@ -0,0 +1,182 @@ +%s already exists. If you need to reset any of the configuration items in this file, please delete it first or you may continue to install.'), + basename(DC_RC_PATH),'index.php')); +} + +$DBDRIVER = !empty($_POST['DBDRIVER']) ? $_POST['DBDRIVER'] : 'mysql'; +$DBHOST = !empty($_POST['DBHOST']) ? $_POST['DBHOST'] : ''; +$DBNAME = !empty($_POST['DBNAME']) ? $_POST['DBNAME'] : ''; +$DBUSER = !empty($_POST['DBUSER']) ? $_POST['DBUSER'] : ''; +$DBPASSWORD = !empty($_POST['DBPASSWORD']) ? $_POST['DBPASSWORD'] : ''; +$DBPREFIX = !empty($_POST['DBPREFIX']) ? $_POST['DBPREFIX'] : 'dc_'; + +if (!empty($_POST)) +{ + try + { + # Tries to connect to database + try { + $con = dbLayer::init($DBDRIVER,$DBHOST,$DBNAME,$DBUSER,$DBPASSWORD); + } catch (Exception $e) { + throw new Exception('

    ' . __($e->getMessage()) . '

    '); + } + + # Checks system capabilites + require dirname(__FILE__).'/check.php'; + if (!dcSystemCheck($con,$_e)) { + $can_install = false; + throw new Exception('

    '.__('Dotclear cannot be installed.').'

    • '.implode('
    • ',$_e).'
    '); + } + + # Check if dotclear is already installed + $schema = dbSchema::init($con); + if (in_array($DBPREFIX.'version',$schema->getTables())) { + throw new Exception(__('Dotclear is already installed.')); + } + + # Does config.php.in exist? + $config_in = dirname(__FILE__).'/../../inc/config.php.in'; + if (!is_file($config_in)) { + throw new Exception(sprintf(__('File %s does not exist.'),$config_in)); + } + + # Can we write config.php + if (!is_writable(dirname(DC_RC_PATH))) { + throw new Exception(sprintf(__('Cannot write %s file.'),DC_RC_PATH)); + } + + # Creates config.php file + $full_conf = file_get_contents($config_in); + + writeConfigValue('DC_DBDRIVER',$DBDRIVER,$full_conf); + writeConfigValue('DC_DBHOST',$DBHOST,$full_conf); + writeConfigValue('DC_DBUSER',$DBUSER,$full_conf); + writeConfigValue('DC_DBPASSWORD',$DBPASSWORD,$full_conf); + writeConfigValue('DC_DBNAME',$DBNAME,$full_conf); + writeConfigValue('DC_DBPREFIX',$DBPREFIX,$full_conf); + + $admin_url = preg_replace('%install/wizard.php$%','',$_SERVER['REQUEST_URI']); + writeConfigValue('DC_ADMIN_URL',http::getHost().$admin_url,$full_conf); + writeConfigValue('DC_MASTER_KEY',md5(uniqid()),$full_conf); + + $fp = @fopen(DC_RC_PATH,'wb'); + if ($fp === false) { + throw new Exception(sprintf(__('Cannot write %s file.'),DC_RC_PATH)); + } + fwrite($fp,$full_conf); + fclose($fp); + chmod(DC_RC_PATH, 0666); + + $con->close(); + http::redirect('index.php?wiz=1'); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} + +function writeConfigValue($name,$val,&$str) +{ + $val = str_replace("'","\'",$val); + $str = preg_replace('/(\''.$name.'\')(.*?)$/ms','$1,\''.$val.'\');',$str); +} + +header('Content-Type: text/html; charset=UTF-8'); +?> + + + + + + + + + + + Dotclear Install Wizard + + + + + +
    +'.__('Dotclear installation wizard').''; + +if (!empty($err)) { + echo '

    '.__('Errors:').'

    '.$err.'
    '; +} + +echo +'

    '.__('System information').'

    '. + +'

    '.__('Please provide the following information needed to create your configuration file.').'

    '. + +'
    '. +'

    '. +'

    '. +'

    '. +'

    '. +'

    '. +'

    '. + +'

    '. +'
    '; +?> +
    + + \ No newline at end of file diff --git a/admin/js/_blog_theme.js b/admin/js/_blog_theme.js new file mode 100644 index 0000000..325a193 --- /dev/null +++ b/admin/js/_blog_theme.js @@ -0,0 +1,6 @@ + +$(function(){$('#themes-actions').hide();var submit_s=$('#themes-actions input[name=select]');var submit_r=$('#themes-actions input[name=remove]');var details=$('#themes div.theme-details');$('div.theme-actions',details).hide();$('input:radio',details).hide();$('div.theme-info span, div.theme-info a',details).hide();details.removeClass('theme-details').addClass('theme-details-js');var themes_wrapper=$('
    ');var theme_box=$('
    ');$('#themes').wrap(themes_wrapper).before(theme_box);details.each(function(){var box=this;var a=$(document.createElement('a'));a.attr('href','#');a.attr('title',$('>div h3>label',this).text());$(box).wrap(a);$(box).parent().click(function(event){update_box(box);event.preventDefault();return false;});});function update_box(e){theme_box.empty();var img=$('div.theme-shot',e).clone();var info=$('div.theme-info',e).clone();if($(e).hasClass('current-theme')){var actions=$('div.theme-actions',e).clone();actions.show();}else{var actions=$('
    ');if(submit_s.length>0&&!$('input:radio',info).attr('disabled')){var select=$(''+dotclear.msg.use_this_theme+'');select.css('font-weight','bold').click(function(){submit_s.click();return false;});actions.append(select).append('  ');} +if(submit_r.length>0&&$('input:radio',info).attr('id')!='theme_default'){var remove=$(''+dotclear.msg.remove_this_theme+'');remove.click(function(){var t_name=$(this).parents('#theme-box').find('div.theme-info h3:first').text();t_name=$.trim(t_name);if(window.confirm(dotclear.msg.confirm_delete_theme.replace('%s',t_name))){submit_r.click();} +return false;});actions.append(remove);}} +$('input:radio',info).remove();$('span, a',info).show();theme_box.append(img).append(info).append(actions);details.removeClass('theme-selected');$(e).addClass('theme-selected');$('input:radio',e).attr('checked','checked');} +update_box(details[0]);}); \ No newline at end of file diff --git a/admin/js/_categories.js b/admin/js/_categories.js new file mode 100644 index 0000000..8e6929e --- /dev/null +++ b/admin/js/_categories.js @@ -0,0 +1,2 @@ + +$(function(){$('form#delete-category').submit(function(){var c_id=$('#del_cat').val();var c_name=$('#del_cat option[value='+c_id+']').text();return window.confirm(dotclear.msg.confirm_delete_category.replace('%s',c_name));});});$(function(){$('form#reset-order').submit(function(){return window.confirm(dotclear.msg.confirm_reorder_categories);});}); \ No newline at end of file diff --git a/admin/js/_category.js b/admin/js/_category.js new file mode 100644 index 0000000..496a4c4 --- /dev/null +++ b/admin/js/_category.js @@ -0,0 +1,2 @@ + +$(function(){dotclear.hideLockable();var tbCategory=new jsToolBar(document.getElementById('cat_desc'));tbCategory.draw('xhtml');}); \ No newline at end of file diff --git a/admin/js/_comment.js b/admin/js/_comment.js new file mode 100644 index 0000000..a46fa52 --- /dev/null +++ b/admin/js/_comment.js @@ -0,0 +1,3 @@ + +$(function(){if(!document.getElementById){return;} +var tbComment=new jsToolBar(document.getElementById('comment_content'));tbComment.draw('xhtml');$('#comment-form input[name="delete"]').click(function(){return window.confirm(dotclear.msg.confirm_delete_comment);});}); \ No newline at end of file diff --git a/admin/js/_comments.js b/admin/js/_comments.js new file mode 100644 index 0000000..01b7483 --- /dev/null +++ b/admin/js/_comments.js @@ -0,0 +1,6 @@ + +dotclear.commentExpander=function(line){var td=line.firstChild;var img=document.createElement('img');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;img.className='expand';$(img).css('cursor','pointer');img.line=line;img.onclick=function(){dotclear.viewCommentContent(this,this.line);};td.insertBefore(img,td.firstChild);};dotclear.viewCommentContent=function(img,line){var commentId=line.id.substr(1);var tr=document.getElementById('ce'+commentId);if(!tr){tr=document.createElement('tr');tr.id='ce'+commentId;var td=document.createElement('td');td.colSpan=6;td.className='expand';tr.appendChild(td);img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;$.get('services.php',{f:'getCommentById',id:commentId},function(data){var rsp=$(data).children('rsp')[0];if(rsp.attributes[0].value=='ok'){var comment=$(rsp).find('comment_display_content').text();if(comment){$(td).append(comment);var comment_email=$(rsp).find('comment_email').text();var comment_site=$(rsp).find('comment_site').text();var comment_ip=$(rsp).find('comment_ip').text();var comment_spam_disp=$(rsp).find('comment_spam_disp').text();$(td).append('

    '+dotclear.msg.website+' '+comment_site+'
    '+''+dotclear.msg.email+' '+comment_email+'
    '+''+dotclear.msg.ip_address+' '+comment_ip+''+'
    '+comment_spam_disp+'

    ');}}else{alert($(rsp).find('message').text());}});$(line).toggleClass('expand');line.parentNode.insertBefore(tr,line.nextSibling);} +else if(tr.style.display=='none') +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;} +else +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;}};$(function(){$('#form-comments tr.line').each(function(){dotclear.commentExpander(this);});$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});dotclear.commentsActionsHelper();}); \ No newline at end of file diff --git a/admin/js/_index.js b/admin/js/_index.js new file mode 100644 index 0000000..405c4d1 --- /dev/null +++ b/admin/js/_index.js @@ -0,0 +1,9 @@ + +$(function(){var f=$('#quick-entry');if(f.length>0){var contentTb=new jsToolBar($('#post_content',f)[0]);contentTb.switchMode($('#post_format',f).val());$('input[name=save]',f).click(function(){quickPost(f,-2);return false;});if($('input[name=save-publish]',f).length>0){var btn=$('');$('input[name=save-publish]',f).remove();$('input[name=save]',f).after(btn).after(' ');btn.click(function(){quickPost(f,1);return false;});} +function quickPost(f,status){if(contentTb.getMode()=='wysiwyg'){contentTb.syncContents('iframe');} +var params={f:'quickPost',xd_check:dotclear.nonce,post_title:$('#post_title',f).val(),post_content:$('#post_content',f).val(),cat_id:$('#cat_id',f).val(),post_status:status,post_format:$('#post_format',f).val(),post_lang:$('#post_lang',f).val()} +$('p.qinfo',f).remove();$.post('services.php',params,function(data){if($('rsp[status=failed]',data).length>0){var msg='

    '+dotclear.msg.error+' '+$('rsp',data).text()+'

    ';}else{var msg='

    '+dotclear.msg.entry_created+' - '+ +dotclear.msg.edit_entry+'';if($('rsp>post',data).attr('post_status')==1){msg+=' - '+ +dotclear.msg.view_entry+'';} +msg+='

    ';$('#post_title',f).val('');$('#post_content',f).val('');if(contentTb.getMode()=='wysiwyg'){contentTb.syncContents('textarea');}} +$('fieldset',f).prepend(msg);});}}}); \ No newline at end of file diff --git a/admin/js/_langs.js b/admin/js/_langs.js new file mode 100644 index 0000000..8824cff --- /dev/null +++ b/admin/js/_langs.js @@ -0,0 +1,2 @@ + +$(function(){$('table.plugins form input[type=submit][name=delete]').click(function(){var l_name=$(this).parents('tr.line').find('td:first').text();return window.confirm(dotclear.msg.confirm_delete_lang.replace('%s',l_name));});}); \ No newline at end of file diff --git a/admin/js/_media.js b/admin/js/_media.js new file mode 100644 index 0000000..01e486d --- /dev/null +++ b/admin/js/_media.js @@ -0,0 +1,7 @@ + +$(function(){fileRemoveAct();function fileRemoveAct(){$('a.media-remove').click(function(){var m_name=$(this).parents('ul').find('li:first>a').text();if(window.confirm(dotclear.msg.confirm_delete_media.replace('%s',m_name))){var f=$('#media-remove-hide').get(0);f.elements['remove'].value=this.href.replace(/^(.*)&remove=(.*?)(&|$)/,'$2');this.href='';f.submit();} +return false;});} +if(!$.browser.opera){var upldr=$(''+dotclear.msg.activate_enhanced_uploader+'').click(function(){candyUploadInit();return false;});$('#media-upload>fieldset').append($('
    ').append(upldr));if($.cookie('dc_candy_upl')==1){candyUploadInit();}} +function candyUploadInit() +{var candy_upload_success=false;var candy_upload_form_url=$('#media-upload').attr('action')+'&file_sort=date-desc&d='+$('#media-upload input[name=d]').val();var candy_upload_limit=$('#media-upload input[name=MAX_FILE_SIZE]').val();$('#media-upload').candyUpload({upload_url:dotclear.candyUpload.base_url+'/media.php',flash_movie:dotclear.candyUpload.movie_url,file_size_limit:candy_upload_limit+'b',params:'swfupload=1&'+dotclear.candyUpload.params,callbacks:{createControls:function(){var _this=this;var l=$(''+dotclear.msg.disable_enhanced_uploader+'').click(function(){_this.upldr.destroy();_this.ctrl.block.empty().remove();$('#media-upload').show();delete _this;$.cookie('dc_candy_upl','',{expires:-1});return false;});this.ctrl.disable=$('
    ').append(l).appendTo(this.ctrl.block);},flashReady:function(){this.ctrl.btn_browse.addClass('button');this.ctrl.block.append(this.ctrl.disable);},uploadSuccess:function(o,data){if(data=='ok'){candy_upload_success=true;this.fileMsg(o.id,this.locales.file_uploaded);}else{this.fileErrorMsg(o.id,data);} +if(candy_upload_success&&$('div.cu-file:has(span.cu-filecancel a)',this.ctrl.files).length==0){$.cookie('dc_candy_upl','1',{expires:30});$.get(candy_upload_form_url,function(data){var media=$('div.media-list');media.after($('div.media-list',data)).remove();fileRemoveAct();});}}}});}}); \ No newline at end of file diff --git a/admin/js/_media_item.js b/admin/js/_media_item.js new file mode 100644 index 0000000..c9a2eeb --- /dev/null +++ b/admin/js/_media_item.js @@ -0,0 +1,4 @@ + +$(function(){$('#media-details-tab').onetabload(function(){var media_dt=document.getElementById('media_dt');if(media_dt==undefined){return;} +var post_dtPick=new datePicker(media_dt);post_dtPick.img_top='1.5em';post_dtPick.draw();});$('#file-unzip').each(function(){var a=document.createElement('a');var mediaId=$(this).find('input[name=id]').val();var self=$(this);a.href='#';$(a).text(dotclear.msg.zip_file_content);self.before(a);$(a).wrap('

    ');$(a).click(function(){$.get('services.php',{f:'getZipMediaContent',id:mediaId},function(data){var rsp=$(data).children('rsp')[0];if(rsp.attributes[0].value=='ok'){var div=document.createElement('div');var list=document.createElement('ul');var expanded=false;$(div).css({overflow:'auto',border:'1px solid #ccc',margin:'1em 0',padding:'1px 0.5em'});$(div).append(list);self.before(div);$(a).hide();$(div).before('

    '+dotclear.msg.zip_file_content+'

    ');$(rsp).find('file').each(function(){$(list).append('
  • '+$(this).text()+'
  • ');if($(div).height()>200&&!expanded){$(div).css({height:'200px'});expanded=true;}});}else{alert($(rsp).find('message').text());}});return false;});});$('#file-unzip').submit(function(){if($(this).find('#inflate_mode').val()=='current'){return window.confirm(dotclear.msg.confirm_extract_current);} +return true;});}); \ No newline at end of file diff --git a/admin/js/_permissions.js b/admin/js/_permissions.js new file mode 100644 index 0000000..1547911 --- /dev/null +++ b/admin/js/_permissions.js @@ -0,0 +1,6 @@ + +jQuery.fn.updatePermissionsForm=function(){return this.each(function(){var perms={};var re=/^perm\[(.+?)\]\[(.+?)\]$/;var e,prop;for(var i=0;i'+dotclear.msg.website+' '+comment_site+'
    '+''+dotclear.msg.email+' '+ +comment_email+'
    '+comment_spam_disp+'

    ');}}else{alert($(rsp).find('message').text());}});$(line).toggleClass('expand');line.parentNode.insertBefore(tr,line.nextSibling);} +else if(tr.style.display=='none') +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;} +else +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;}};$(function(){if(!document.getElementById){return;} +if(document.getElementById('edit-entry')) +{var formatField=$('#post_format').get(0);$(formatField).change(function(){excerptTb.switchMode(this.value);contentTb.switchMode(this.value);});var excerptTb=new jsToolBar(document.getElementById('post_excerpt'));var contentTb=new jsToolBar(document.getElementById('post_content'));excerptTb.context=contentTb.context='post';} +if(document.getElementById('comment_content')){var commentTb=new jsToolBar(document.getElementById('comment_content'));} +$('#post-preview').modalWeb($(window).width()-40,$(window).height()-40);$('#edit-entry').onetabload(function(){dotclear.hideLockable();var post_dtPick=new datePicker($('#post_dt').get(0));post_dtPick.img_top='1.5em';post_dtPick.draw();$('input[name="delete"]').click(function(){return window.confirm(dotclear.msg.confirm_delete_post);});$('#notes-area label').toggleWithLegend($('#notes-area').children().not('label'),{cookie:'dcx_post_notes',hide:$('#post_notes').val()==''});$('#post_lang').parent().toggleWithLegend($('#post_lang'),{cookie:'dcx_post_lang'});$('#post_password').parent().toggleWithLegend($('#post_password'),{cookie:'dcx_post_password',hide:$('#post_password').val()==''});$('#excerpt-area label').toggleWithLegend($('#excerpt-area').children().not('label'),{fn:function(){excerptTb.switchMode(formatField.value);},cookie:'dcx_post_excerpt',hide:$('#post_excerpt').val()==''});contentTb.switchMode(formatField.value);$('a.attachment-remove').click(function(){this.href='';var m_name=$(this).parents('ul').find('li:first>a').attr('title');if(window.confirm(dotclear.msg.confirm_remove_attachment.replace('%s',m_name))){var f=$('#attachment-remove-hide').get(0);f.elements['media_id'].value=this.id.substring(11);f.submit();} +return false;});var h=document.createElement('h4');var a=document.createElement('a');a.href='#';$(a).click(function(){var params={xd_check:dotclear.nonce,f:'validatePostMarkup',excerpt:$('#post_excerpt').text(),content:$('#post_content').text(),format:$('#post_format').get(0).value,lang:$('#post_lang').get(0).value};$.post('services.php',params,function(data){if($(data).find('rsp').attr('status')!='ok'){alert($(data).find('rsp message').text());return false;} +if($(data).find('valid').text()==1){var p=document.createElement('p');p.id='markup-validator';if($('#markup-validator').length>0){$('#markup-validator').remove();} +$(p).addClass('message');$(p).text(dotclear.msg.xhtml_valid);$(p).insertAfter(h);$(p).backgroundFade({sColor:'#666666',eColor:'#ffcc00',steps:50},function(){$(this).backgroundFade({sColor:'#ffcc00',eColor:'#666666'});});}else{var div=document.createElement('div');div.id='markup-validator';if($('#markup-validator').length>0){$('#markup-validator').remove();} +$(div).addClass('error');$(div).html('

    '+dotclear.msg.xhtml_not_valid+'

    '+$(data).find('errors').text());$(div).insertAfter(h);$(div).backgroundFade({sColor:'#ffffff',eColor:'#ff9999',steps:50},function(){$(this).backgroundFade({sColor:'#ff9999',eColor:'#ffffff'});});} +return false;});return false;});a.appendChild(document.createTextNode(dotclear.msg.xhtml_validator));h.appendChild(a);$(h).appendTo('#entry-content');});$('#comments').onetabload(function(){$('.comments-list tr.line').each(function(){dotclear.commentExpander(this);});$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});dotclear.commentsActionsHelper();});$('#add-comment').onetabload(function(){commentTb.draw('xhtml');});}); \ No newline at end of file diff --git a/admin/js/_posts_list.js b/admin/js/_posts_list.js new file mode 100644 index 0000000..f4ca648 --- /dev/null +++ b/admin/js/_posts_list.js @@ -0,0 +1,7 @@ + +dotclear.postExpander=function(line){var td=line.firstChild;var img=document.createElement('img');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;img.className='expand';$(img).css('cursor','pointer');img.line=line;img.onclick=function(){dotclear.viewPostContent(this,this.line);};td.insertBefore(img,td.firstChild);};dotclear.viewPostContent=function(img,line){var postId=line.id.substr(1);var tr=document.getElementById('pe'+postId);if(!tr){tr=document.createElement('tr');tr.id='pe'+postId;var td=document.createElement('td');td.colSpan=8;td.className='expand';tr.appendChild(td);img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;$.get('services.php',{f:'getPostById',id:postId,post_type:''},function(data){var rsp=$(data).children('rsp')[0];if(rsp.attributes[0].value=='ok'){var post=$(rsp).find('post_display_content').text();var post_excerpt=$(rsp).find('post_display_excerpt').text();var res='';if(post){if(post_excerpt){res+=post_excerpt+'
    ';} +res+=post;$(td).append(res);}}else{alert($(rsp).find('message').text());}});$(line).toggleClass('expand');line.parentNode.insertBefore(tr,line.nextSibling);} +else if(tr.style.display=='none') +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;} +else +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;}};$(function(){$('#form-entries tr.line').each(function(){dotclear.postExpander(this);});$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});dotclear.postsActionsHelper();}); \ No newline at end of file diff --git a/admin/js/_preferences.js b/admin/js/_preferences.js new file mode 100644 index 0000000..d47d010 --- /dev/null +++ b/admin/js/_preferences.js @@ -0,0 +1,5 @@ + +$(function(){if($('#new_pwd').length==0){return;} +var user_email=$('#user_email').val();$('#user-form').submit(function(){var e=this.elements['cur_pwd'];if(e.value!=''){return true;} +if($('#user_email').val()!=user_email||$('#new_pwd').val()!=''){e.focus();$(e).backgroundFade({sColor:'#ffffff',eColor:'#ff9999',steps:50},function(){$(this).backgroundFade({sColor:'#ff9999',eColor:'#ffffff'});});return false;} +return true;});}); \ No newline at end of file diff --git a/admin/js/_trackbacks.js b/admin/js/_trackbacks.js new file mode 100644 index 0000000..b3842d7 --- /dev/null +++ b/admin/js/_trackbacks.js @@ -0,0 +1,2 @@ + +$(function(){$('#tb_excerpt').keypress(function(){if(this.value.length>255){this.value=this.value.substring(0,255);}});}); \ No newline at end of file diff --git a/admin/js/_users.js b/admin/js/_users.js new file mode 100644 index 0000000..fec32c1 --- /dev/null +++ b/admin/js/_users.js @@ -0,0 +1,7 @@ + +$(function(){$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});$('#form-users').submit(function(){var action=$(this).find('select[name="dispatch_action"]').val();var user_ids=new Array();var nb_posts=new Array();var i;var msg_cannot_delete=false;$(this).find('input[name="user_id[]"]').each(function(){user_ids.push(this);});$(this).find('input[name="nb_post[]"]').each(function(){nb_posts.push(this.value);});if(action=='deleteuser'){for(i=0;i0){user_ids[i].checked=false;msg_cannot_delete=true;}} +if(msg_cannot_delete==true){alert(dotclear.msg.cannot_delete_users);}} +var selectfields=0;for(i=0;iThis.height()){This.css('height',$('body').height()+'px');}};var textToggler=function(o){var i=$(''+p.img_on_alt+'');o.css('cursor','pointer');var hide=true;o.prepend(' ').prepend(i);o.click(function(){$(this).nextAll().each(function(){if($(this).is('h3')){return false;} +$(this).toggle();sizeBox();return true;});hide=!hide;var img=$(this).find('img');if(!hide){img.attr('src',p.img_off_src);}else{img.attr('src',p.img_on_src);}});};this.addClass('help-box');this.find('>hr').remove();this.find('h3').each(function(){textToggler($(this));});this.find('h3:first').nextAll('*:not(h3)').hide();sizeBox();var img=$(''+dotclear.msg.help+'');var select=$();img.click(function(){return toggle();});$('#content').append(img);return this;};var dotclear={msg:{},hideLockable:function(){$('div.lockable').each(function(){var current_lockable_div=this;$(this).find('p.form-note').hide();$(this).find('input').each(function(){this.disabled=true;$(this).width(($(this).width()-14)+'px');var imgE=document.createElement('img');imgE.src='images/locker.png';imgE.style.position='absolute';imgE.style.top='1.7em';imgE.style.left=($(this).width()+4)+'px';$(imgE).css('cursor','pointer');$(imgE).click(function(){$(this).hide();$(this).prev('input').each(function(){this.disabled=false;$(this).width(($(this).width()+14)+'px');});$(current_lockable_div).find('p.form-note').show();});$(this).parent().css('position','relative');$(this).after(imgE);});});},checkboxesHelpers:function(e){var a=document.createElement('a');a.href='#';$(a).append(document.createTextNode(dotclear.msg.select_all));a.onclick=function(){$(this).parents('form').find('input[type="checkbox"]').check();return false;};$(e).append(a);$(e).append(document.createTextNode(' - '));a=document.createElement('a');a.href='#';$(a).append(document.createTextNode(dotclear.msg.invert_sel));a.onclick=function(){$(this).parents('form').find('input[type="checkbox"]').toggleCheck();return false;};$(e).append(a);},postsActionsHelper:function(){$('#form-entries').submit(function(){var action=$(this).find('select[name="action"]').val();var checked=false;$(this).find('input[name="entries[]"]').each(function(){if(this.checked){checked=true;}});if(!checked){return false;} +if(action=='delete'){return window.confirm(dotclear.msg.confirm_delete_posts);} +return true;});},commentsActionsHelper:function(){$('#form-comments').submit(function(){var action=$(this).find('select[name="action"]').val();var checked=false;$(this).find('input[name="comments[]"]').each(function(){if(this.checked){checked=true;}});if(!checked){return false;} +if(action=='delete'){return window.confirm(dotclear.msg.confirm_delete_comments);} +return true;});}};$(function(){$('#switchblog').change(function(){this.form.submit();});var menu_settings={img_on_src:dotclear.img_menu_off,img_off_src:dotclear.img_menu_on,legend_click:true,speed:100} +$('#blog-menu h3:first').toggleWithLegend($('#blog-menu ul:first'),$.extend({cookie:'dc_blog_menu',hide:false,reverse_cookie:true},menu_settings));$('#system-menu h3:first').toggleWithLegend($('#system-menu ul:first'),$.extend({cookie:'dc_system_menu'},menu_settings));$('#plugins-menu h3:first').toggleWithLegend($('#plugins-menu ul:first'),$.extend({cookie:'dc_plugins_menu'},menu_settings));$('#help').helpViewer();$('.message').backgroundFade({sColor:'#cccccc',eColor:'#666666',steps:20});$('.error').backgroundFade({sColor:'#f5e5e5',eColor:'#e5bfbf',steps:20});$('form:has(input[type=password][name=your_pwd])').submit(function(){var e=this.elements['your_pwd'];if(e.value==''){e.focus();$(e).backgroundFade({sColor:'#ffffff',eColor:'#ff9999',steps:50},function(){$(this).backgroundFade({sColor:'#ff9999',eColor:'#ffffff'});});return false;} +return true;});}); \ No newline at end of file diff --git a/admin/js/confirm-close.js b/admin/js/confirm-close.js new file mode 100644 index 0000000..41489c9 --- /dev/null +++ b/admin/js/confirm-close.js @@ -0,0 +1,12 @@ + +function confirmClose(){if(arguments.length>0){for(var i=0;i0){var res=new Array();var f;for(var i=0;i23){h=0;} +if(h<10){h='0'+h;} +this.hour=h*1;this.oHour.value=h;},setMinute:function(m){if(m<0){m=59;} +if(m>59){m=0;} +if(m<10){m='0'+m;} +this.minute=m*1;this.oMinute.value=m;},changeMonth:function(dir){var y=this.year;var m=this.month;m=m+dir;if(m>12){this.month=1;this.year++;} +else if(m<1){this.month=12;this.year--;} +else{this.month=m;} +this.setDate();},changeYear:function(dir){this.year=this.year+dir;this.setDate();},changeHour:function(dir){this.setHour(this.hour*1+dir);},changeMinute:function(dir){this.setMinute(this.minute*1+dir);},sendDate:function(d){var m=this.month;var hour=this.oHour.value*1;var minute=this.oMinute.value*1;if(hour<0||hour>23||isNaN(hour)){hour=0;} +if(minute<0||minute>59||isNaN(minute)){minute=0;} +if(m<10){m='0'+m;} +if(d<10){d='0'+d;} +if(hour<10){hour='0'+hour;} +if(minute<10){minute='0'+minute;} +this.target.value=this.year+'-'+m+'-'+d+' '+hour+':'+minute;this.close();},sendNow:function(){var dt=new Date();var y=dt.getFullYear();var m=dt.getMonth()+1;var d=dt.getDate();var h=dt.getHours();var i=dt.getMinutes();if(m<10){m='0'+m;} +if(d<10){d='0'+d;} +if(h<10){h='0'+h;} +if(i<10){i='0'+i;} +this.target.value=y+'-'+m+'-'+d+' '+h+':'+i;this.close();},close:function(){document.body.removeChild(this.oTable);},numberOfDays:function(){var res=31;if(this.month==4||this.month==6||this.month==9||this.month==11){res=30;}else if(this.month==2){res=28;if(this.year%4==0&&(this.year%100!=0||this.year%400==0)){res=29;}} +return res;},firstDay:function(){var dt=new Date(this.year,this.month-1,1);var res=dt.getDay();if(res==0){res=7;} +return res;},show:function(){var re=/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/;var match=re.exec(this.target.value);if(match){this.year=match[1]*1;this.month=match[2]*1;this.day=match[3]*1;this.hour=match[4]*1;this.minute=match[5]*1;}else{var dt=new Date();this.year=dt.getFullYear();this.month=dt.getMonth()+1;this.day=dt.getDate();this.hour=dt.getHours();this.minute=dt.getMinutes();} +this.oTable.appendChild(this.oBody);this.setDate();this.setPosition();document.body.appendChild(this.oTable);this.oHour.focus();},setPosition:function(){var t_x=this.findPosX(this.target);var t_y=this.findPosY(this.target);var o_h=this.oTable.offsetHeight;var o_w=this.oTable.offsetWidth;this.oTable.style.position='absolute';this.oTable.style.zIndex='100';this.oTable.style.top=t_y+'px';this.oTable.style.left=t_x+'px';},findPosX:function(obj){var curleft=0;if(obj.offsetParent){while(1){curleft+=obj.offsetLeft;if(!obj.offsetParent){break;} +obj=obj.offsetParent;}}else if(obj.x){curleft+=obj.x;} +return curleft;},findPosY:function(obj){var curtop=0;if(obj.offsetParent){while(1){curtop+=obj.offsetTop;if(!obj.offsetParent){break;} +obj=obj.offsetParent;}}else if(obj.y){curtop+=obj.y;} +return curtop;},draw:function(){var imgE=document.createElement('img');imgE.src=this.img_src;imgE.style.position='absolute';imgE.style.top=this.img_top;imgE.style.left=(this.target.clientWidth+4)+'px';imgE.obj=this;imgE.fn=this.show;imgE.onclick=function(){this.fn.apply(this.obj);};this.target.parentNode.style.position='relative';this.target.parentNode.insertBefore(imgE,this.target.nextSibling);}}; \ No newline at end of file diff --git a/admin/js/dragsort-tablerows.js b/admin/js/dragsort-tablerows.js new file mode 100644 index 0000000..164b5ec --- /dev/null +++ b/admin/js/dragsort-tablerows.js @@ -0,0 +1,2 @@ + +ToolMan._dragsortFactory.makeTableSortable=function(table){if(table==null)return;var helpers=ToolMan.helpers();var coordinates=ToolMan.coordinates();var items=table.getElementsByTagName("tr");helpers.map(items,function(item){var dragGroup=dragsort.makeSortable(item);dragGroup.setThreshold(4);var min,max;dragGroup.addTransform(function(coordinate,dragEvent){return coordinate.constrainTo(min,max);});dragGroup.register('dragstart',function(){var items=table.getElementsByTagName("tr");min=max=coordinates.topLeftOffset(items[0]);for(var i=1,n=items.length;i":"","\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/":"","@(namespace|import)[^;\\n]+[;\\n]":"","'(\\\\.|[^'\\\\])*'":bJ,'"(\\\\.|[^"\\\\])*"':bJ,"\\s+":" "});function cB(a){return cA.exec(a)};function bI(c){return c.replace(cw,function(a,b){return bp[b-1]})};function bJ(c){return"\x01"+bp.push(c.replace(cz,function(a,b){return eval("'\\u"+"0000".slice(b.length)+b+"'")}).slice(1,-1).replace(cx,"\\'"))};function cC(a){return cy.test(a)?bp[a.slice(1)-1]:a};var cD=new D({Width:"Height",width:"height",Left:"Top",left:"top",Right:"Bottom",right:"bottom",onX:"onY"});function A(a){return cD.exec(a)};var bK=[];function bq(a){cF(a);w(window,"onresize",a)};function w(a,b,c){a.attachEvent(b,c);bK.push(arguments)};function cE(a,b,c){try{a.detachEvent(b,c)}catch(ignore){}};w(window,"onunload",function(){var a;while(a=bK.pop()){cE(a[0],a[1],a[2])}});function R(a,b,c){if(!a.elements)a.elements={};if(c)a.elements[b.uniqueID]=b;else delete a.elements[b.uniqueID];return c};w(window,"onbeforeprint",function(){if(!IE7.CSS.print)new bw("print");IE7.CSS.print.recalc()});var bL=/^\d+(px)?$/i;var J=/^\d+%$/;var E=function(a,b){if(bL.test(b))return parseInt(b);var c=a.style.left;var d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;a.style.left=b||0;b=a.style.pixelLeft;a.style.left=c;a.runtimeStyle.left=d;return b};var br="ie7-";var bM=z.extend({constructor:function(){this.fixes=[];this.recalcs=[]},init:Q});var bs=[];function cF(a){bs.push(a)};IE7.recalc=function(){IE7.HTML.recalc();IE7.CSS.recalc();for(var a=0;a1?2:0;var h=T.exec(f[g])||"if(0){";if(o){h+=i("if(e%1.nodeName!='!'){",m)}var p=S>1?bV:"";h+=i(p+bW,m);h+=Array(I(h,/\{/g).length+1).join("}");d+=h}eval(i(bX,F)+T.unescape(d)+"return s?null:r}");be[a]=_h}return be[a](b||document,c)};var bd=k<6;var bO=/^(href|src)$/;var bu={"class":"className","for":"htmlFor"};IE7._5=1;IE7._e=function(a,b){var c=a.all[b]||null;if(!c||c.id==b)return c;for(var d=0;d+~,]|[^(]\+|^)([#.:\[])/g,cH=/(^|,)([^\s>+~])/g,cI=/\s*([\s>+~(),]|^|$)\s*/g,bQ=/\s\*\s/g;var bR=D.extend({constructor:function(a){this.base(a);this.sorter=new D;this.sorter.add(/:not\([^)]*\)/,D.IGNORE);this.sorter.add(/([ >](\*|[\w-]+))([^: >+~]*)(:\w+-child(\([^)]+\))?)([^: >+~]*)/,"$1$3$6$4")},ignoreCase:true,escape:function(a){return this.optimise(this.format(a))},format:function(a){return a.replace(cI,"$1").replace(cH,"$1 $2").replace(cG,"$1*$2")},optimise:function(a){return this.sorter.exec(a.replace(bQ,">* "))},unescape:function(a){return bI(a)}});var bS={"":"%1!=null","=":"%1=='%2'","~=":/(^| )%1( |$)/,"|=":/^%1(-|$)/,"^=":/^%1/,"$=":/%1$/,"*=":/%1/};var bT={"first-child":"!IE7._b(e%1)","link":"e%1.currentStyle['ie7-link']=='link'","visited":"e%1.currentStyle['ie7-link']=='visited'"};var bv="var p%2=0,i%2,e%2,n%2=e%1.";var bU="e%1.sourceIndex";var bV="var g="+bU+";if(!p[g]){p[g]=1;";var bW="r[r.length]=e%1;if(s)return e%1;";var bX="var _h=function(e0,s){IE7._5++;var r=[],p={},reg=[%1],d=document;";var F;var m;var o;var x;var S;var be={};var T=new bR({" (\\*|[\\w-]+)#([\\w-]+)":function(a,b,c){o=false;var d="var e%2=IE7._e(d,'%4');if(e%2&&";if(b!="*")d+="e%2.nodeName=='%3'&&";d+="(e%1==d||e%1.contains(e%2))){";if(x)d+=i("i%1=n%1.length;",x);return i(d,m++,m,b.toUpperCase(),c)}," (\\*|[\\w-]+)":function(a,b){S++;o=b=="*";var c=bv;c+=(o&&bd)?"all":"getElementsByTagName('%3')";c+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";return i(c,m++,x=m,b.toUpperCase())},">(\\*|[\\w-]+)":function(a,b){var c=x;o=b=="*";var d=bv;d+=c?"children":"childNodes";if(!o&&c)d+=".tags('%3')";d+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";if(o){d+="if(e%2.nodeType==1){";o=bd}else{if(!c)d+="if(e%2.nodeName=='%3'){"}return i(d,m++,x=m,b.toUpperCase())},"\\+(\\*|[\\w-]+)":function(a,b){var c="";if(o)c+="if(e%1.nodeName!='!'){";o=false;c+="e%1=IE7._a(e%1);if(e%1";if(b!="*")c+="&&e%1.nodeName=='%2'";c+="){";return i(c,m,b.toUpperCase())},"~(\\*|[\\w-]+)":function(a,b){var c="";if(o)c+="if(e%1.nodeName!='!'){";o=false;S=2;c+="while(e%1=e%1.nextSibling){if(e%1.ie7_adjacent==IE7._5)break;if(";if(b=="*"){c+="e%1.nodeType==1";if(bd)c+="&&e%1.nodeName!='!'"}else c+="e%1.nodeName=='%2'";c+="){e%1.ie7_adjacent=IE7._5;";return i(c,m,b.toUpperCase())},"#([\\w-]+)":function(a,b){o=false;var c="if(e%1.id=='%2'){";if(x)c+=i("i%1=n%1.length;",x);return i(c,m,b)},"\\.([\\w-]+)":function(a,b){o=false;F.push(new RegExp("(^|\\s)"+bb(b)+"(\\s|$)"));return i("if(e%1.className&®[%2].test(e%1.className)){",m,F.length-1)},"\\[([\\w-]+)\\s*([^=]?=)?\\s*([^\\]]*)\\]":function(a,b,c,d){var f=bu[b]||b;if(c){var g="e%1.getAttribute('%2',2)";if(!bO.test(b)){g="e%1.%3||"+g}b=i("("+g+")",m,b,f)}else{b=i("IE7._f(e%1,'%2')",m,b)}var h=bS[c||""]||"0";if(h&&h.source){F.push(new RegExp(i(h.source,bb(T.unescape(d)))));h="reg[%2].test(%1)";d=F.length-1}return"if("+i(h,b,d)+"){"},":+([\\w-]+)(\\(([^)]+)\\))?":function(a,b,c,d){b=bT[b];return"if("+(b?i(b,m,d||""):"0")+"){"}});var bY=/a(#[\w-]+)?(\.[\w-]+)?:(hover|active)/i;var bZ=/\s*\{\s*/,ca=/\s*\}\s*/,cb=/\s*\,\s*/;var cc=/(.*)(:first-(line|letter))/;var y=document.styleSheets;IE7.CSS=new(bM.extend({parser:new bH,screen:"",print:"",styles:[],rules:[],pseudoClasses:k<7?"first\\-child":"",dynamicPseudoClasses:{toString:function(){var a=[];for(var b in this)a.push(b);return a.join("|")}},init:function(){var a="^\x01$";var b="\\[class=?[^\\]]*\\]";var c=[];if(this.pseudoClasses)c.push(this.pseudoClasses);var d=this.dynamicPseudoClasses.toString();if(d)c.push(d);c=c.join("|");var f=k<7?["[>+~[(]|([:.])\\w+\\1"]:[b];if(c)f.push(":("+c+")");this.UNKNOWN=new RegExp(f.join("|")||a,"i");var g=k<7?["\\[[^\\]]+\\]|[^\\s(\\[]+\\s*[+~]"]:[b];var h=g.concat();if(c)h.push(":("+c+")");n.COMPLEX=new RegExp(h.join("|")||a,"ig");if(this.pseudoClasses)g.push(":("+this.pseudoClasses+")");L.COMPLEX=new RegExp(g.join("|")||a,"i");L.MATCH=new RegExp(d?"(.*):("+d+")(.*)":a,"i");this.createStyleSheet();this.refresh()},addEventHandler:function(){w.apply(null,arguments)},addFix:function(a,b){this.parser.add(a,b)},addRecalc:function(c,d,f,g){d=new RegExp("([{;\\s])"+c+"\\s*:\\s*"+d+"[^;}]*");var h=this.recalcs.length;if(g)g=c+":"+g;this.addFix(d,function(a,b){return(g?b+g:a)+";ie7-"+a.slice(1)+";ie7_recalc"+h+":1"});this.recalcs.push(arguments);return h},apply:function(){this.getInlineStyles();new bw("screen");this.trash()},createStyleSheet:function(){this.styleSheet=document.createStyleSheet();this.styleSheet.ie7=true;this.styleSheet.owningElement.ie7=true;this.styleSheet.cssText=G},getInlineStyles:function(){var a=document.getElementsByTagName("style"),b;for(var c=a.length-1;(b=a[c]);c--){if(!b.disabled&&!b.ie7){this.styles.push(b.innerHTML)}}},getText:function(a,b){try{var c=a.cssText}catch(e){c=""}if(H)c=cl(a.href,b)||c;return c},recalc:function(){this.screen.recalc();var a=/ie7_recalc\d+/g;var b=G.match(/[{,]/g).length;var c=b+(this.screen.cssText.match(/\{/g)||"").length;var d=this.styleSheet.rules,f;var g,h,p,t,q,j,u,l;for(q=b;q0&&n.CLASS.test(b)){b=b.replace(n.CLASS,"");d--}while(c>0&&n.TAG.test(b)){b=b.replace(n.TAG,"$1*");c--}b+="."+this.className;d=Math.min(d,2);c=Math.min(c,2);var f=-10*d-c;if(f>0){b=b+","+n.MAP[f]+" "+b}return b},remove:function(a){a.className=a.className.replace(this.MATCH,"$1")},toString:function(){return i("%1 {%2}",this.selectorText,this.cssText)}},{CHILD:/>/g,CLASS:/\.[\w-]+/,CLASSES:/[.:\[]/g,MULTI:/(\.[\w-]+)+/g,PREFIX:"ie7_class",TAG:/^\w+|([\s>+~])\w+/,TAGS:/^\w|[\s>+~]\w/g,MAP:{1:"html",2:"html body",10:".ie7_html",11:"html.ie7_html",12:"html.ie7_html body",20:".ie7_html .ie7_body",21:"html.ie7_html .ie7_body",22:"html.ie7_html body.ie7_body"}});var L=n.extend({constructor:function(a,b,c,d,f){this.attach=b||"*";this.dynamicPseudoClass=IE7.CSS.dynamicPseudoClasses[c];this.target=d;this.base(a,f)},recalc:function(){var a=B(this.attach),b;for(var c=0;b=a[c];c++){var d=this.target?B(this.target,b):[b];if(d.length)this.dynamicPseudoClass.apply(b,d,this)}}});var cd=z.extend({constructor:function(a,b){this.name=a;this.apply=b;this.instances={};IE7.CSS.dynamicPseudoClasses[a]=this},register:function(a){var b=a[2];a.id=b.id+a[0].uniqueID;if(!this.instances[a.id]){var c=a[1],d;for(d=0;d*:"+(b=="marginTop"?"first":"last")+"-child",a,true);if(d&&d.currentStyle.styleFloat=="none"&&IE7.hasLayout(d)){collapseMargin(d,b);margin=_9(a,a.currentStyle[b]);childMargin=_9(d,d.currentStyle[b]);if(margin<0||childMargin<0){a.runtimeStyle[b]=margin+childMargin}else{a.runtimeStyle[b]=Math.max(childMargin,margin)}d.runtimeStyle[b]="0px"}}};function _9(a,b){return b=="auto"?0:E(a,b)};var UNIT=/^[.\d][\w%]*$/,AUTO=/^(auto|0cm)$/;var applyWidth,applyHeight;IE7.Layout.borderBox=function(a){applyWidth(a);applyHeight(a)};var fixWidth=function(g){applyWidth=function(a){if(!J.test(a.currentStyle.width))h(a);collapseMargins(a)};function h(a,b){if(!a.runtimeStyle.fixedWidth){if(!b)b=a.currentStyle.width;a.runtimeStyle.fixedWidth=(UNIT.test(b))?Math.max(0,q(a,b)):b;K(a,"width",a.runtimeStyle.fixedWidth)}};function p(a){if(!bc(a)){var b=a.offsetParent;while(b&&!IE7.hasLayout(b))b=b.offsetParent}return(b||s).clientWidth};function t(a,b){if(J.test(b))return parseInt(parseFloat(b)/100*p(a));return E(a,b)};var q=function(a,b){var c=a.currentStyle["box-sizing"]=="border-box";var d=0;if(C&&!c)d+=j(a)+u(a,"padding");else if(!C&&c)d-=j(a)+u(a,"padding");return t(a,b)+d};function j(a){return a.offsetWidth-a.clientWidth};function u(a,b){return t(a,a.currentStyle[b+"Left"])+t(a,a.currentStyle[b+"Right"])};G+="*{minWidth:none;maxWidth:none;min-width:none;max-width:none}";layout.minWidth=function(a){if(a.currentStyle["min-width"]!=null){a.style.minWidth=a.currentStyle["min-width"]}if(R(arguments.callee,a,a.currentStyle.minWidth!="none")){layout.boxSizing(a);h(a);l(a)}};eval("IE7.Layout.maxWidth="+String(layout.minWidth).replace(/min/g,"max"));function l(a){var b=a.getBoundingClientRect();var c=b.right-b.left;if(a.currentStyle.minWidth!="none"&&c<=q(a,a.currentStyle.minWidth)){a.runtimeStyle.width=a.currentStyle.minWidth}else if(a.currentStyle.maxWidth!="none"&&c>=q(a,a.currentStyle.maxWidth)){a.runtimeStyle.width=a.currentStyle.maxWidth}else{a.runtimeStyle.width=a.runtimeStyle.fixedWidth}};function r(a){if(R(r,a,/^(fixed|absolute)$/.test(a.currentStyle.position)&&bt(a,"left")!="auto"&&bt(a,"right")!="auto"&&AUTO.test(bt(a,"width")))){N(a);IE7.Layout.boxSizing(a)}};IE7.Layout.fixRight=r;function N(a){var b=t(a,a.runtimeStyle._c||a.currentStyle.left);var c=p(a)-t(a,a.currentStyle.right)-b-u(a,"margin");if(parseInt(a.runtimeStyle.width)==c)return;a.runtimeStyle.width="";if(bc(a)||g||a.offsetWidth=5.5&&k<7){IE7.CSS.addFix(/background(-image)?\s*:\s*([^};]*)?url\(([^\)]+)\)([^;}]*)?/,function(a,b,c,d,f){d=cC(d);return bi.test(d)?"filter:"+i(bx,d,"crop")+";zoom:1;background"+(b||"")+":"+(c||"")+"none"+(f||""):a});IE7.HTML.addRecalc("img,input",function(a){if(a.tagName=="INPUT"&&a.type!="image")return;by(a);w(a,"onpropertychange",function(){if(!bj&&event.propertyName=="src"&&a.src.indexOf(bg)==-1)by(a)})});var bj=false;w(window,"onbeforeprint",function(){bj=true;for(var a=0;a=7)return;IE7.CSS.addRecalc("position","fixed",_6,"absolute");IE7.CSS.addRecalc("background(-attachment)?","[^};]*fixed",_2);var $viewport=C?"body":"documentElement";function _3(){if(v.currentStyle.backgroundAttachment!="fixed"){if(v.currentStyle.backgroundImage=="none"){v.runtimeStyle.backgroundRepeat="no-repeat";v.runtimeStyle.backgroundImage="url("+bg+")"}v.runtimeStyle.backgroundAttachment="fixed"}_3=Q};var _0=bN("img");function _1(a){return a?bc(a)||_1(a.parentElement):false};function _d(a,b,c){setTimeout("document.all."+a.uniqueID+".runtimeStyle.setExpression('"+b+"','"+c+"')",0)};function _2(a){if(R(_2,a,a.currentStyle.backgroundAttachment=="fixed"&&!a.contains(v))){_3();bgLeft(a);bgTop(a);_8(a)}};function _8(a){_0.src=a.currentStyle.backgroundImage.slice(5,-2);var b=a.canHaveChildren?a:a.parentElement;b.appendChild(_0);setOffsetLeft(a);setOffsetTop(a);b.removeChild(_0)};function bgLeft(a){a.style.backgroundPositionX=a.currentStyle.backgroundPositionX;if(!_1(a)){_d(a,"backgroundPositionX","(parseInt(runtimeStyle.offsetLeft)+document."+$viewport+".scrollLeft)||0")}};eval(A(bgLeft));function setOffsetLeft(a){var b=_1(a)?"backgroundPositionX":"offsetLeft";a.runtimeStyle[b]=getOffsetLeft(a,a.style.backgroundPositionX)-a.getBoundingClientRect().left-a.clientLeft+2};eval(A(setOffsetLeft));function getOffsetLeft(a,b){switch(b){case"left":case"top":return 0;case"right":case"bottom":return s.clientWidth-_0.offsetWidth;case"center":return(s.clientWidth-_0.offsetWidth)/2;default:if(J.test(b)){return parseInt((s.clientWidth-_0.offsetWidth)*parseFloat(b)/100)}_0.style.left=b;return _0.offsetLeft}};eval(A(getOffsetLeft));function _6(a){if(R(_6,a,bc(a))){K(a,"position","absolute");K(a,"left",a.currentStyle.left);K(a,"top",a.currentStyle.top);_3();IE7.Layout.fixRight(a);_4(a)}};function _4(a,b){positionTop(a,b);positionLeft(a,b,true);if(!a.runtimeStyle.autoLeft&&a.currentStyle.marginLeft=="auto"&&a.currentStyle.right!="auto"){var c=s.clientWidth-getPixelWidth(a,a.currentStyle.right)-getPixelWidth(a,a.runtimeStyle._c)-a.clientWidth;if(a.currentStyle.marginRight=="auto")c=parseInt(c/2);if(_1(a.offsetParent))a.runtimeStyle.pixelLeft+=c;else a.runtimeStyle.shiftLeft=c}clipWidth(a);clipHeight(a)};function clipWidth(a){var b=a.runtimeStyle.fixWidth;a.runtimeStyle.borderRightWidth="";a.runtimeStyle.width=b?getPixelWidth(a,b):"";if(a.currentStyle.width!="auto"){var c=a.getBoundingClientRect();var d=a.offsetWidth-s.clientWidth+c.left-2;if(d>=0){a.runtimeStyle.borderRightWidth="0px";d=Math.max(E(a,a.currentStyle.width)-d,0);K(a,"width",d);return d}}};eval(A(clipWidth));function positionLeft(a,b){if(!b&&J.test(a.currentStyle.width)){a.runtimeStyle.fixWidth=a.currentStyle.width}if(a.runtimeStyle.fixWidth){a.runtimeStyle.width=getPixelWidth(a,a.runtimeStyle.fixWidth)}a.runtimeStyle.shiftLeft=0;a.runtimeStyle._c=a.currentStyle.left;a.runtimeStyle.autoLeft=a.currentStyle.right!="auto"&&a.currentStyle.left=="auto";a.runtimeStyle.left="";a.runtimeStyle.screenLeft=getScreenLeft(a);a.runtimeStyle.pixelLeft=a.runtimeStyle.screenLeft;if(!b&&!_1(a.offsetParent)){_d(a,"pixelLeft","runtimeStyle.screenLeft+runtimeStyle.shiftLeft+document."+$viewport+".scrollLeft")}};eval(A(positionLeft));function getScreenLeft(a){var b=a.offsetLeft,c=1;if(a.runtimeStyle.autoLeft){b=s.clientWidth-a.offsetWidth-getPixelWidth(a,a.currentStyle.right)}if(a.currentStyle.marginLeft!="auto"){b-=getPixelWidth(a,a.currentStyle.marginLeft)}while(a=a.offsetParent){if(a.currentStyle.position!="static")c=-1;b+=a.offsetLeft*c}return b};eval(A(getScreenLeft));function getPixelWidth(a,b){return J.test(b)?parseInt(parseFloat(b)/100*s.clientWidth):E(a,b)};eval(A(getPixelWidth));function _g(){var a=_2.elements;for(var b in a)_8(a[b]);a=_6.elements;for(b in a){_4(a[b],true);_4(a[b],true)}_7=0};var _7;bq(function(){if(!_7)_7=setTimeout(_g,0)})};var bk={backgroundColor:"transparent",backgroundImage:"none",backgroundPositionX:null,backgroundPositionY:null,backgroundRepeat:null,borderTopWidth:0,borderRightWidth:0,borderBottomWidth:0,borderLeftStyle:"none",borderTopStyle:"none",borderRightStyle:"none",borderBottomStyle:"none",borderLeftWidth:0,height:null,marginTop:0,marginBottom:0,marginRight:0,marginLeft:0,width:"100%"};IE7.CSS.addRecalc("overflow","visible",function(a){if(a.parentNode.ie7_wrapped)return;if(IE7.Layout&&a.currentStyle["max-height"]!="auto"){IE7.Layout.maxHeight(a)}if(a.currentStyle.marginLeft=="auto")a.style.marginLeft=0;if(a.currentStyle.marginRight=="auto")a.style.marginRight=0;var b=document.createElement(bA);b.ie7_wrapped=a;for(var c in bk){b.style[c]=a.currentStyle[c];if(bk[c]!=null){a.runtimeStyle[c]=bk[c]}}b.style.display="block";b.style.position="relative";a.runtimeStyle.position="absolute";a.parentNode.insertBefore(b,a);b.appendChild(a)});function cf(){var f="xx-small,x-small,small,medium,large,x-large,xx-large".split(",");for(var g=0;g":"","\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/":"","@(namespace|import)[^;\\n]+[;\\n]":"","'(\\\\.|[^'\\\\])*'":bW,'"(\\\\.|[^"\\\\])*"':bW,"\\s+":" "});function cS(a){return bV.exec(a)};function bg(c){return c.replace(cO,function(a,b){return bA[b-1]})};function bW(c){return"\x01"+bA.push(c.replace(cR,function(a,b){return eval("'\\u"+"0000".slice(b.length)+b+"'")}).slice(1,-1).replace(cP,"\\'"))};function bB(a){return cQ.test(a)?bA[a.slice(1)-1]:a};var cT=new H({Width:"Height",width:"height",Left:"Top",left:"top",Right:"Bottom",right:"bottom",onX:"onY"});function C(a){return cT.exec(a)};var bX=[];function bC(a){cV(a);v(window,"onresize",a)};function v(a,b,c){a.attachEvent(b,c);bX.push(arguments)};function cU(a,b,c){try{a.detachEvent(b,c)}catch(ignore){}};v(window,"onunload",function(){var a;while(a=bX.pop()){cU(a[0],a[1],a[2])}});function X(a,b,c){if(!a.elements)a.elements={};if(c)a.elements[b.uniqueID]=b;else delete a.elements[b.uniqueID];return c};v(window,"onbeforeprint",function(){if(!IE7.CSS.print)new bJ("print");IE7.CSS.print.recalc()});var bY=/^\d+(px)?$/i;var M=/^\d+%$/;var D=function(a,b){if(bY.test(b))return parseInt(b);var c=a.style.left;var d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;a.style.left=b||0;b=a.style.pixelLeft;a.style.left=c;a.runtimeStyle.left=d;return b};var bD="ie7-";var bZ=B.extend({constructor:function(){this.fixes=[];this.recalcs=[]},init:U});var bE=[];function cV(a){bE.push(a)};IE7.recalc=function(){IE7.HTML.recalc();IE7.CSS.recalc();for(var a=0;a1?2:0;var h=E.exec(f[g])||"if(0){";if(p){h+=i("if(e%1.nodeName!='!'){",l)}var j=Y>1?ch:"";h+=i(j+ci,l);h+=Array(L(h,/\{/g).length+1).join("}");d+=h}eval(i(cj,I)+E.unescape(d)+"return s?null:r}");bj[a]=_k}return bj[a](b||document,c)};var bi=m<6;var cb=/^(href|src)$/;var bG={"class":"className","for":"htmlFor"};IE7._1=1;IE7._e=function(a,b){var c=a.all[b]||null;if(!c||c.id==b)return c;for(var d=0;d+~,]|[^(]\+|^)([#.:\[])/g,cX=/(^|,)([^\s>+~])/g,cY=/\s*([\s>+~(),]|^|$)\s*/g,cd=/\s\*\s/g;var ce=H.extend({constructor:function(a){this.base(a);this.sorter=new H;this.sorter.add(/:not\([^)]*\)/,H.IGNORE);this.sorter.add(/([ >](\*|[\w-]+))([^: >+~]*)(:\w+-child(\([^)]+\))?)([^: >+~]*)/,"$1$3$6$4")},ignoreCase:true,escape:function(a){return this.optimise(this.format(a))},format:function(a){return a.replace(cY,"$1").replace(cX,"$1 $2").replace(cW,"$1*$2")},optimise:function(a){return this.sorter.exec(a.replace(cd,">* "))},unescape:function(a){return bg(a)}});var cf={"":"%1!=null","=":"%1=='%2'","~=":/(^| )%1( |$)/,"|=":/^%1(-|$)/,"^=":/^%1/,"$=":/%1$/,"*=":/%1/};var bH={"first-child":"!IE7._4(e%1)","link":"e%1.currentStyle['ie7-link']=='link'","visited":"e%1.currentStyle['ie7-link']=='visited'"};var bI="var p%2=0,i%2,e%2,n%2=e%1.";var cg="e%1.sourceIndex";var ch="var g="+cg+";if(!p[g]){p[g]=1;";var ci="r[r.length]=e%1;if(s)return e%1;";var cj="var _k=function(e0,s){IE7._1++;var r=[],p={},reg=[%1],d=document;";var I;var l;var p;var y;var Y;var bj={};var E=new ce({" (\\*|[\\w-]+)#([\\w-]+)":function(a,b,c){p=false;var d="var e%2=IE7._e(d,'%4');if(e%2&&";if(b!="*")d+="e%2.nodeName=='%3'&&";d+="(e%1==d||e%1.contains(e%2))){";if(y)d+=i("i%1=n%1.length;",y);return i(d,l++,l,b.toUpperCase(),c)}," (\\*|[\\w-]+)":function(a,b){Y++;p=b=="*";var c=bI;c+=(p&&bi)?"all":"getElementsByTagName('%3')";c+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";return i(c,l++,y=l,b.toUpperCase())},">(\\*|[\\w-]+)":function(a,b){var c=y;p=b=="*";var d=bI;d+=c?"children":"childNodes";if(!p&&c)d+=".tags('%3')";d+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";if(p){d+="if(e%2.nodeType==1){";p=bi}else{if(!c)d+="if(e%2.nodeName=='%3'){"}return i(d,l++,y=l,b.toUpperCase())},"\\+(\\*|[\\w-]+)":function(a,b){var c="";if(p)c+="if(e%1.nodeName!='!'){";p=false;c+="e%1=IE7._3(e%1);if(e%1";if(b!="*")c+="&&e%1.nodeName=='%2'";c+="){";return i(c,l,b.toUpperCase())},"~(\\*|[\\w-]+)":function(a,b){var c="";if(p)c+="if(e%1.nodeName!='!'){";p=false;Y=2;c+="while(e%1=e%1.nextSibling){if(e%1.ie7_adjacent==IE7._1)break;if(";if(b=="*"){c+="e%1.nodeType==1";if(bi)c+="&&e%1.nodeName!='!'"}else c+="e%1.nodeName=='%2'";c+="){e%1.ie7_adjacent=IE7._1;";return i(c,l,b.toUpperCase())},"#([\\w-]+)":function(a,b){p=false;var c="if(e%1.id=='%2'){";if(y)c+=i("i%1=n%1.length;",y);return i(c,l,b)},"\\.([\\w-]+)":function(a,b){p=false;I.push(new RegExp("(^|\\s)"+W(b)+"(\\s|$)"));return i("if(e%1.className&®[%2].test(e%1.className)){",l,I.length-1)},"\\[([\\w-]+)\\s*([^=]?=)?\\s*([^\\]]*)\\]":function(a,b,c,d){var f=bG[b]||b;if(c){var g="e%1.getAttribute('%2',2)";if(!cb.test(b)){g="e%1.%3||"+g}b=i("("+g+")",l,b,f)}else{b=i("IE7._f(e%1,'%2')",l,b)}var h=cf[c||""]||"0";if(h&&h.source){I.push(new RegExp(i(h.source,W(E.unescape(d)))));h="reg[%2].test(%1)";d=I.length-1}return"if("+i(h,b,d)+"){"},":+([\\w-]+)(\\(([^)]+)\\))?":function(a,b,c,d){b=bH[b];return"if("+(b?i(b,l,d||""):"0")+"){"}});var ck=/a(#[\w-]+)?(\.[\w-]+)?:(hover|active)/i;var cl=/\s*\{\s*/,cm=/\s*\}\s*/,cn=/\s*\,\s*/;var co=/(.*)(:first-(line|letter))/;var z=document.styleSheets;IE7.CSS=new(bZ.extend({parser:new bU,screen:"",print:"",styles:[],rules:[],pseudoClasses:m<7?"first\\-child":"",dynamicPseudoClasses:{toString:function(){var a=[];for(var b in this)a.push(b);return a.join("|")}},init:function(){var a="^\x01$";var b="\\[class=?[^\\]]*\\]";var c=[];if(this.pseudoClasses)c.push(this.pseudoClasses);var d=this.dynamicPseudoClasses.toString();if(d)c.push(d);c=c.join("|");var f=m<7?["[>+~[(]|([:.])\\w+\\1"]:[b];if(c)f.push(":("+c+")");this.UNKNOWN=new RegExp(f.join("|")||a,"i");var g=m<7?["\\[[^\\]]+\\]|[^\\s(\\[]+\\s*[+~]"]:[b];var h=g.concat();if(c)h.push(":("+c+")");o.COMPLEX=new RegExp(h.join("|")||a,"ig");if(this.pseudoClasses)g.push(":("+this.pseudoClasses+")");O.COMPLEX=new RegExp(g.join("|")||a,"i");O.MATCH=new RegExp(d?"(.*):("+d+")(.*)":a,"i");this.createStyleSheet();this.refresh()},addEventHandler:function(){v.apply(null,arguments)},addFix:function(a,b){this.parser.add(a,b)},addRecalc:function(c,d,f,g){d=new RegExp("([{;\\s])"+c+"\\s*:\\s*"+d+"[^;}]*");var h=this.recalcs.length;if(g)g=c+":"+g;this.addFix(d,function(a,b){return(g?b+g:a)+";ie7-"+a.slice(1)+";ie7_recalc"+h+":1"});this.recalcs.push(arguments);return h},apply:function(){this.getInlineStyles();new bJ("screen");this.trash()},createStyleSheet:function(){this.styleSheet=document.createStyleSheet();this.styleSheet.ie7=true;this.styleSheet.owningElement.ie7=true;this.styleSheet.cssText=J},getInlineStyles:function(){var a=document.getElementsByTagName("style"),b;for(var c=a.length-1;(b=a[c]);c--){if(!b.disabled&&!b.ie7){this.styles.push(b.innerHTML)}}},getText:function(a,b){try{var c=a.cssText}catch(e){c=""}if(K)c=cD(a.href,b)||c;return c},recalc:function(){this.screen.recalc();var a=/ie7_recalc\d+/g;var b=J.match(/[{,]/g).length;var c=b+(this.screen.cssText.match(/\{/g)||"").length;var d=this.styleSheet.rules,f;var g,h,j,q,r,k,u,n;for(r=b;r0&&o.CLASS.test(b)){b=b.replace(o.CLASS,"");d--}while(c>0&&o.TAG.test(b)){b=b.replace(o.TAG,"$1*");c--}b+="."+this.className;d=Math.min(d,2);c=Math.min(c,2);var f=-10*d-c;if(f>0){b=b+","+o.MAP[f]+" "+b}return b},remove:function(a){a.className=a.className.replace(this.MATCH,"$1")},toString:function(){return i("%1 {%2}",this.selectorText,this.cssText)}},{CHILD:/>/g,CLASS:/\.[\w-]+/,CLASSES:/[.:\[]/g,MULTI:/(\.[\w-]+)+/g,PREFIX:"ie7_class",TAG:/^\w+|([\s>+~])\w+/,TAGS:/^\w|[\s>+~]\w/g,MAP:{1:"html",2:"html body",10:".ie7_html",11:"html.ie7_html",12:"html.ie7_html body",20:".ie7_html .ie7_body",21:"html.ie7_html .ie7_body",22:"html.ie7_html body.ie7_body"}});var O=o.extend({constructor:function(a,b,c,d,f){this.attach=b||"*";this.dynamicPseudoClass=IE7.CSS.dynamicPseudoClasses[c];this.target=d;this.base(a,f)},recalc:function(){var a=x(this.attach),b;for(var c=0;b=a[c];c++){var d=this.target?x(this.target,b):[b];if(d.length)this.dynamicPseudoClass.apply(b,d,this)}}});var A=B.extend({constructor:function(a,b){this.name=a;this.apply=b;this.instances={};IE7.CSS.dynamicPseudoClasses[a]=this},register:function(a){var b=a[2];a.id=b.id+a[0].uniqueID;if(!this.instances[a.id]){var c=a[1],d;for(d=0;d*:"+(b=="marginTop"?"first":"last")+"-child",a,true);if(d&&d.currentStyle.styleFloat=="none"&&IE7.hasLayout(d)){collapseMargin(d,b);margin=_b(a,a.currentStyle[b]);childMargin=_b(d,d.currentStyle[b]);if(margin<0||childMargin<0){a.runtimeStyle[b]=margin+childMargin}else{a.runtimeStyle[b]=Math.max(childMargin,margin)}d.runtimeStyle[b]="0px"}}};function _b(a,b){return b=="auto"?0:D(a,b)};var UNIT=/^[.\d][\w%]*$/,AUTO=/^(auto|0cm)$/;var applyWidth,applyHeight;IE7.Layout.borderBox=function(a){applyWidth(a);applyHeight(a)};var fixWidth=function(g){applyWidth=function(a){if(!M.test(a.currentStyle.width))h(a);collapseMargins(a)};function h(a,b){if(!a.runtimeStyle.fixedWidth){if(!b)b=a.currentStyle.width;a.runtimeStyle.fixedWidth=(UNIT.test(b))?Math.max(0,r(a,b)):b;N(a,"width",a.runtimeStyle.fixedWidth)}};function j(a){if(!bh(a)){var b=a.offsetParent;while(b&&!IE7.hasLayout(b))b=b.offsetParent}return(b||t).clientWidth};function q(a,b){if(M.test(b))return parseInt(parseFloat(b)/100*j(a));return D(a,b)};var r=function(a,b){var c=a.currentStyle["box-sizing"]=="border-box";var d=0;if(G&&!c)d+=k(a)+u(a,"padding");else if(!G&&c)d-=k(a)+u(a,"padding");return q(a,b)+d};function k(a){return a.offsetWidth-a.clientWidth};function u(a,b){return q(a,a.currentStyle[b+"Left"])+q(a,a.currentStyle[b+"Right"])};J+="*{minWidth:none;maxWidth:none;min-width:none;max-width:none}";layout.minWidth=function(a){if(a.currentStyle["min-width"]!=null){a.style.minWidth=a.currentStyle["min-width"]}if(X(arguments.callee,a,a.currentStyle.minWidth!="none")){layout.boxSizing(a);h(a);n(a)}};eval("IE7.Layout.maxWidth="+String(layout.minWidth).replace(/min/g,"max"));function n(a){var b=a.getBoundingClientRect();var c=b.right-b.left;if(a.currentStyle.minWidth!="none"&&c<=r(a,a.currentStyle.minWidth)){a.runtimeStyle.width=a.currentStyle.minWidth}else if(a.currentStyle.maxWidth!="none"&&c>=r(a,a.currentStyle.maxWidth)){a.runtimeStyle.width=a.currentStyle.maxWidth}else{a.runtimeStyle.width=a.runtimeStyle.fixedWidth}};function s(a){if(X(s,a,/^(fixed|absolute)$/.test(a.currentStyle.position)&&bF(a,"left")!="auto"&&bF(a,"right")!="auto"&&AUTO.test(bF(a,"width")))){R(a);IE7.Layout.boxSizing(a)}};IE7.Layout.fixRight=s;function R(a){var b=q(a,a.runtimeStyle._c||a.currentStyle.left);var c=j(a)-q(a,a.currentStyle.right)-b-u(a,"margin");if(parseInt(a.runtimeStyle.width)==c)return;a.runtimeStyle.width="";if(bh(a)||g||a.offsetWidth=5.5&&m<7){IE7.CSS.addFix(/background(-image)?\s*:\s*([^};]*)?url\(([^\)]+)\)([^;}]*)?/,function(a,b,c,d,f){d=bB(d);return bm.test(d)?"filter:"+i(bK,d,"crop")+";zoom:1;background"+(b||"")+":"+(c||"")+"none"+(f||""):a});IE7.HTML.addRecalc("img,input",function(a){if(a.tagName=="INPUT"&&a.type!="image")return;bL(a);v(a,"onpropertychange",function(){if(!bn&&event.propertyName=="src"&&a.src.indexOf(bk)==-1)bL(a)})});var bn=false;v(window,"onbeforeprint",function(){bn=true;for(var a=0;a=7)return;IE7.CSS.addRecalc("position","fixed",_8,"absolute");IE7.CSS.addRecalc("background(-attachment)?","[^};]*fixed",_5);var $viewport=G?"body":"documentElement";function _6(){if(w.currentStyle.backgroundAttachment!="fixed"){if(w.currentStyle.backgroundImage=="none"){w.runtimeStyle.backgroundRepeat="no-repeat";w.runtimeStyle.backgroundImage="url("+bk+")"}w.runtimeStyle.backgroundAttachment="fixed"}_6=U};var _0=ca("img");function _2(a){return a?bh(a)||_2(a.parentElement):false};function _d(a,b,c){setTimeout("document.all."+a.uniqueID+".runtimeStyle.setExpression('"+b+"','"+c+"')",0)};function _5(a){if(X(_5,a,a.currentStyle.backgroundAttachment=="fixed"&&!a.contains(w))){_6();bgLeft(a);bgTop(a);_a(a)}};function _a(a){_0.src=a.currentStyle.backgroundImage.slice(5,-2);var b=a.canHaveChildren?a:a.parentElement;b.appendChild(_0);setOffsetLeft(a);setOffsetTop(a);b.removeChild(_0)};function bgLeft(a){a.style.backgroundPositionX=a.currentStyle.backgroundPositionX;if(!_2(a)){_d(a,"backgroundPositionX","(parseInt(runtimeStyle.offsetLeft)+document."+$viewport+".scrollLeft)||0")}};eval(C(bgLeft));function setOffsetLeft(a){var b=_2(a)?"backgroundPositionX":"offsetLeft";a.runtimeStyle[b]=getOffsetLeft(a,a.style.backgroundPositionX)-a.getBoundingClientRect().left-a.clientLeft+2};eval(C(setOffsetLeft));function getOffsetLeft(a,b){switch(b){case"left":case"top":return 0;case"right":case"bottom":return t.clientWidth-_0.offsetWidth;case"center":return(t.clientWidth-_0.offsetWidth)/2;default:if(M.test(b)){return parseInt((t.clientWidth-_0.offsetWidth)*parseFloat(b)/100)}_0.style.left=b;return _0.offsetLeft}};eval(C(getOffsetLeft));function _8(a){if(X(_8,a,bh(a))){N(a,"position","absolute");N(a,"left",a.currentStyle.left);N(a,"top",a.currentStyle.top);_6();IE7.Layout.fixRight(a);_7(a)}};function _7(a,b){positionTop(a,b);positionLeft(a,b,true);if(!a.runtimeStyle.autoLeft&&a.currentStyle.marginLeft=="auto"&&a.currentStyle.right!="auto"){var c=t.clientWidth-getPixelWidth(a,a.currentStyle.right)-getPixelWidth(a,a.runtimeStyle._c)-a.clientWidth;if(a.currentStyle.marginRight=="auto")c=parseInt(c/2);if(_2(a.offsetParent))a.runtimeStyle.pixelLeft+=c;else a.runtimeStyle.shiftLeft=c}clipWidth(a);clipHeight(a)};function clipWidth(a){var b=a.runtimeStyle.fixWidth;a.runtimeStyle.borderRightWidth="";a.runtimeStyle.width=b?getPixelWidth(a,b):"";if(a.currentStyle.width!="auto"){var c=a.getBoundingClientRect();var d=a.offsetWidth-t.clientWidth+c.left-2;if(d>=0){a.runtimeStyle.borderRightWidth="0px";d=Math.max(D(a,a.currentStyle.width)-d,0);N(a,"width",d);return d}}};eval(C(clipWidth));function positionLeft(a,b){if(!b&&M.test(a.currentStyle.width)){a.runtimeStyle.fixWidth=a.currentStyle.width}if(a.runtimeStyle.fixWidth){a.runtimeStyle.width=getPixelWidth(a,a.runtimeStyle.fixWidth)}a.runtimeStyle.shiftLeft=0;a.runtimeStyle._c=a.currentStyle.left;a.runtimeStyle.autoLeft=a.currentStyle.right!="auto"&&a.currentStyle.left=="auto";a.runtimeStyle.left="";a.runtimeStyle.screenLeft=getScreenLeft(a);a.runtimeStyle.pixelLeft=a.runtimeStyle.screenLeft;if(!b&&!_2(a.offsetParent)){_d(a,"pixelLeft","runtimeStyle.screenLeft+runtimeStyle.shiftLeft+document."+$viewport+".scrollLeft")}};eval(C(positionLeft));function getScreenLeft(a){var b=a.offsetLeft,c=1;if(a.runtimeStyle.autoLeft){b=t.clientWidth-a.offsetWidth-getPixelWidth(a,a.currentStyle.right)}if(a.currentStyle.marginLeft!="auto"){b-=getPixelWidth(a,a.currentStyle.marginLeft)}while(a=a.offsetParent){if(a.currentStyle.position!="static")c=-1;b+=a.offsetLeft*c}return b};eval(C(getScreenLeft));function getPixelWidth(a,b){return M.test(b)?parseInt(parseFloat(b)/100*t.clientWidth):D(a,b)};eval(C(getPixelWidth));function _j(){var a=_5.elements;for(var b in a)_a(a[b]);a=_8.elements;for(b in a){_7(a[b],true);_7(a[b],true)}_9=0};var _9;bC(function(){if(!_9)_9=setTimeout(_j,0)})};var bp={backgroundColor:"transparent",backgroundImage:"none",backgroundPositionX:null,backgroundPositionY:null,backgroundRepeat:null,borderTopWidth:0,borderRightWidth:0,borderBottomWidth:0,borderLeftStyle:"none",borderTopStyle:"none",borderRightStyle:"none",borderBottomStyle:"none",borderLeftWidth:0,height:null,marginTop:0,marginBottom:0,marginRight:0,marginLeft:0,width:"100%"};IE7.CSS.addRecalc("overflow","visible",function(a){if(a.parentNode.ie7_wrapped)return;if(IE7.Layout&&a.currentStyle["max-height"]!="auto"){IE7.Layout.maxHeight(a)}if(a.currentStyle.marginLeft=="auto")a.style.marginLeft=0;if(a.currentStyle.marginRight=="auto")a.style.marginRight=0;var b=document.createElement(bN);b.ie7_wrapped=a;for(var c in bp){b.style[c]=a.currentStyle[c];if(bp[c]!=null){a.runtimeStyle[c]=bp[c]}}b.style.display="block";b.style.position="relative";a.runtimeStyle.position="absolute";a.parentNode.insertBefore(b,a);b.appendChild(a)});function cq(){var f="xx-small,x-small,small,medium,large,x-large,xx-large".split(",");for(var g=0;g=%2",f,g,c,d,"&&","%","==");if(h)j="!("+j+")";return j};bH={"link":"e%1.currentStyle['ie7-link']=='link'","visited":"e%1.currentStyle['ie7-link']=='visited'","checked":"e%1.checked","contains":"e%1.innerText.indexOf('%2')!=-1","disabled":"e%1.isDisabled","empty":"IE7._g(e%1)","enabled":"e%1.disabled===false","first-child":"!IE7._4(e%1)","lang":"IE7._h(e%1,'%2')","last-child":"!IE7._3(e%1)","only-child":"!IE7._4(e%1)&&!IE7._3(e%1)","target":"e%1.id==location.hash.slice(1)","indeterminate":"e%1.indeterminate"};IE7._i=function(a){if(a.rows){a.ie7_length=a.rows.length;a.ie7_lookup="rowIndex"}else if(a.cells){a.ie7_length=a.cells.length;a.ie7_lookup="cellIndex"}else if(a.ie7_indexed!=IE7._1){var b=0;var c=a.firstChild;while(c){if(c.nodeType==1&&c.nodeName!="!"){c.ie7_index=++b}c=c.nextSibling}a.ie7_length=b;a.ie7_lookup="ie7_index"}a.ie7_indexed=IE7._1;return a};var ba=E[V];var cs=ba[ba.length-1];ba.length--;E.merge({":not\\((\\*|[\\w-]+)?([^)]*)\\)":function(a,b,c){var d=(b&&b!="*")?i("if(e%1.nodeName=='%2'){",l,b.toUpperCase()):"";d+=E.exec(c);return"if(!"+d.slice(2,-1).replace(/\)\{if\(/g,"&&")+"){"},":nth(-last)?-child\\(([^)]+)\\)":function(a,b,c){p=false;b=i("e%1.parentNode.ie7_length",l);var d="if(p%1!==e%1.parentNode)p%1=IE7._i(e%1.parentNode);";d+="var i=e%1[p%1.ie7_lookup];if(p%1.ie7_lookup!='ie7_index')i++;if(";return i(d,l)+cr(a,c,"i",b)+"){"}});ba.push(cs);var bM="\\([^)]*\\)";if(IE7.CSS.pseudoClasses)IE7.CSS.pseudoClasses+="|";IE7.CSS.pseudoClasses+="before|after|last\\-child|only\\-child|empty|root|"+"not|nth\\-child|nth\\-last\\-child|contains|lang".split("|").join(bM+"|")+bM;bV.add(/::/,":");var bb=new A("focus",function(a){var b=arguments;IE7.CSS.addEventHandler(a,"onfocus",function(){bb.unregister(b);bb.register(b)});IE7.CSS.addEventHandler(a,"onblur",function(){bb.unregister(b)});if(a==document.activeElement){bb.register(b)}});var bq=new A("active",function(a){var b=arguments;IE7.CSS.addEventHandler(a,"onmousedown",function(){bq.register(b)})});v(document,"onmouseup",function(){var a=bq.instances;for(var b in a)bq.unregister(a[b])});var br=new A("checked",function(a){if(typeof a.checked!="boolean")return;var b=arguments;IE7.CSS.addEventHandler(a,"onpropertychange",function(){if(event.propertyName=="checked"){if(a.checked)br.register(b);else br.unregister(b)}});if(a.checked)br.register(b)});var bs=new A("enabled",function(a){if(typeof a.disabled!="boolean")return;var b=arguments;IE7.CSS.addEventHandler(a,"onpropertychange",function(){if(event.propertyName=="disabled"){if(!a.isDisabled)bs.register(b);else bs.unregister(b)}});if(!a.isDisabled)bs.register(b)});var bt=new A("disabled",function(a){if(typeof a.disabled!="boolean")return;var b=arguments;IE7.CSS.addEventHandler(a,"onpropertychange",function(){if(event.propertyName=="disabled"){if(a.isDisabled)bt.register(b);else bt.unregister(b)}});if(a.isDisabled)bt.register(b)});var bu=new A("indeterminate",function(a){if(typeof a.indeterminate!="boolean")return;var b=arguments;IE7.CSS.addEventHandler(a,"onpropertychange",function(){if(event.propertyName=="indeterminate"){if(a.indeterminate)bu.register(b);else bu.unregister(b)}});IE7.CSS.addEventHandler(a,"onclick",function(){bu.unregister(b)})});var bv=new A("target",function(a){var b=arguments;if(!a.tabIndex)a.tabIndex=0;IE7.CSS.addEventHandler(document,"onpropertychange",function(){if(event.propertyName=="activeElement"){if(a.id&&a.id==location.hash.slice(1))bv.register(b);else bv.unregister(b)}});if(a.id&&a.id==location.hash.slice(1))bv.register(b)});var ct=/^attr/;var cu=/^url\s*\(\s*([^)]*)\)$/;var cv={before0:"beforeBegin",before1:"afterBegin",after0:"afterEnd",after1:"beforeEnd"};var F=IE7.PseudoElement=o.extend({constructor:function(a,b,c){this.position=b;var d=c.match(F.CONTENT),f,g;if(d){d=d[1];f=d.split(/\s+/);for(var h=0;(g=f[h]);h++){f[h]=ct.test(g)?{attr:g.slice(5,-1)}:(g.charAt(0)=="'")?bB(g):bg(g)}d=f}this.content=d;this.base(a,bg(c))},init:function(){this.match=x(this.selector);for(var a=0;a%4",MATCH:/(.*):(before|after).*/,count:0});var cw=/^(submit|reset|button)$/;IE7.HTML.addRecalc("button,input",function(a){if(a.tagName=="BUTTON"){var b=a.outerHTML.match(/ value="([^"]*)"/i);a.runtimeStyle.value=(b)?b[1]:""}if(a.type=="submit"){v(a,"onclick",function(){a.runtimeStyle.clicked=true;setTimeout("document.all."+a.uniqueID+".runtimeStyle.clicked=false",1)})}});IE7.HTML.addRecalc("form",function(c){v(c,"onsubmit",function(){for(var a,b=0;a=c[b];b++){if(cw.test(a.type)&&!a.disabled&&!a.runtimeStyle.clicked){a.disabled=true;setTimeout("document.all."+a.uniqueID+".disabled=false",1)}else if(a.tagName=="BUTTON"&&a.type=="submit"){setTimeout("document.all."+a.uniqueID+".value='"+a.value+"'",1);a.value=a.runtimeStyle.value}}})});IE7.HTML.addRecalc("img",function(a){if(a.alt&&!a.title)a.title=""});IE7.CSS.addRecalc("border-spacing",P,function(a){if(a.currentStyle.borderCollapse!="collapse"){a.cellSpacing=D(a,a.currentStyle["border-spacing"])}});IE7.CSS.addRecalc("box-sizing","content-box",IE7.Layout.boxSizing);IE7.CSS.addRecalc("box-sizing","border-box",IE7.Layout.borderBox);IE7.CSS.addFix(/opacity\s*:\s*([\d.]+)/,function(a,b){return"zoom:1;filter:Alpha(opacity="+((b*100)||1)+")"});var cx=/^image/i;IE7.HTML.addRecalc("object",function(a){if(cx.test(a.type)){a.body.style.cssText="margin:0;padding:0;border:none;overflow:hidden";return a}});IE7.loaded=true;(function(){try{bx.doScroll("left")}catch(e){setTimeout(arguments.callee,1);return}try{eval(bO.innerHTML)}catch(e){}bm=new RegExp(W(typeof IE7_PNG_SUFFIX=="string"?IE7_PNG_SUFFIX:"-trans.png")+"$","i");w=document.body;t=G?w:bx;w.className+=" ie7_body";bx.className+=" ie7_html";if(G)cq();IE7.CSS.init();IE7.HTML.init();IE7.HTML.apply();IE7.CSS.apply();IE7.recalc()})()})(); \ No newline at end of file diff --git a/admin/js/ie7/blank.gif b/admin/js/ie7/blank.gif new file mode 100644 index 0000000..a4fe2e6 Binary files /dev/null and b/admin/js/ie7/blank.gif differ diff --git a/admin/js/ie7/ie7-recalc.js b/admin/js/ie7/ie7-recalc.js new file mode 100644 index 0000000..f07c5a6 --- /dev/null +++ b/admin/js/ie7/ie7-recalc.js @@ -0,0 +1,2 @@ +/* IE7/IE8.js - copyright 2004-2008, Dean Edwards */ +(function(){if(!IE7.loaded)return;CLASSES=/\sie7_class\d+/g;IE7.CSS.extend({elements:{},handlers:[],reset:function(){this.removeEventHandlers();var a=this.elements;for(var b in a)a[b].runtimeStyle.cssText="";this.elements={};var a=IE7.Rule.elements;for(var b in a){with(a[b])className=className.replace(CLASSES,"")}IE7.Rule.elements={}},reload:function(){this.rules=[];this.getInlineStyles();this.screen.load();if(this.print)this.print.load();this.refresh();this.trash()},addRecalc:function(b,c,d,e){this.base(b,c,function(a){d(a);IE7.CSS.elements[a.uniqueID]=a},e)},recalc:function(){this.reset();this.base()},addEventHandler:function(a,b,c){a.attachEvent(b,c);this.handlers.push(arguments)},removeEventHandlers:function(){var a;while(a=this.handlers.pop()){a[0].detachEvent(a[1],a[2])}},getInlineStyles:function(){var a=document.getElementsByTagName("style"),b;for(var c=a.length-1;(b=a[c]);c--){if(!b.disabled&&!b.ie7){var d=b.cssText||b.innerHTML;this.styles.push(d);b.cssText=d}}},trash:function(){var a=document.styleSheets,b,c;for(c=0;c= 6) IE7.CSS.addRecalc("float", "(left|right)", function(element) { + IE7.Layout.boxSizing(element.parentElement); // assing "hasLayout" to parent element + // "doubled margin" bug + element.style.display = "inline"; + }); + + // "unscrollable content" bug + // http://www.positioniseverything.net/explorer/unscrollable.html + IE7.CSS.addRecalc("position", "absolute|fixed", function(element) { + if (element.offsetParent && element.offsetParent.currentStyle.position == "relative") + IE7.Layout.boxSizing(element.offsetParent); // assing "hasLayout" + }); +} + +//# // get rid of Microsoft's pesky image toolbar +//# document.write(''); diff --git a/admin/js/jquery/jquery.bgFade.js b/admin/js/jquery/jquery.bgFade.js new file mode 100644 index 0000000..cdf201a --- /dev/null +++ b/admin/js/jquery/jquery.bgFade.js @@ -0,0 +1,7 @@ + +jQuery.fn.backgroundFade=function(s,callback){var defaults={sColor:[255,0,0],eColor:[255,255,255],fColor:null,steps:200,intervals:5,powr:4},params=jQuery.extend(defaults,s);return this.each(function(){this.bgFade_sColor=jQuery.backgroundFade.parseHexColor(params.sColor);this.bgFade_eColor=jQuery.backgroundFade.parseHexColor(params.eColor);this.bgFade_fColor=params.fColor?params.fColor:jQuery(this).css('backgroundColor');this.bgFade_steps=params.steps;this.bgFade_intervals=params.intervals;this.bgFade_powr=params.powr;this.bgFade_fn=callback;jQuery.backgroundFade.doFade(this);});};jQuery.backgroundFade={parseHexColor:function(c){if(c.constructor==String&&c.substr(0,1)=='#'){return[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16)];} +return c;},easeInOut:function(minValue,maxValue,totalSteps,actualStep,powr){var delta=maxValue-minValue;var stepp=minValue+(Math.pow(((1/totalSteps)*actualStep),powr)*delta);return Math.ceil(stepp);},doFade:function(e){if(e.bgFadeInt)window.clearInterval(e.bgFadeInt);var act_step=0;e.bgFadeInt=window.setInterval(function(){e.style.backgroundColor="rgb("+ +jQuery.backgroundFade.easeInOut(e.bgFade_sColor[0],e.bgFade_eColor[0],e.bgFade_steps,act_step,e.bgFade_powr)+","+ +jQuery.backgroundFade.easeInOut(e.bgFade_sColor[1],e.bgFade_eColor[1],e.bgFade_steps,act_step,e.bgFade_powr)+","+ +jQuery.backgroundFade.easeInOut(e.bgFade_sColor[2],e.bgFade_eColor[2],e.bgFade_steps,act_step,e.bgFade_powr)+")";act_step++;if(act_step>e.bgFade_steps){window.clearInterval(e.bgFadeInt);e.bgFade_sColor=undefined;e.bgFade_eColor=undefined;e.bgFade_fColor=undefined;e.bgFade_steps=undefined;e.bgFade_intervals=undefined;e.bgFade_powr=undefined;if(typeof(e.bgFade_fn)=='function'){e.bgFade_fn.call(e);} +e.style.backgroundColor=e.bgFade_fColor;}},e.bgFade_intervals);}}; \ No newline at end of file diff --git a/admin/js/jquery/jquery.biscuit.js b/admin/js/jquery/jquery.biscuit.js new file mode 100644 index 0000000..8b74194 --- /dev/null +++ b/admin/js/jquery/jquery.biscuit.js @@ -0,0 +1,5 @@ + +jQuery.cookie=function(name,value,options){if(typeof value!='undefined'){options=options||{};var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toGMTString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;} +expires='; expires='+date.toGMTString();} +var path=options.path?'; path='+options.path:'';var domain=options.domain?'; domain='+options.domain:'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');return null;}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i'+''+''+''+''+''+'';this.container=$('');if(this.params.target_element==null){this.container.css({display:'block',position:'absolute',left:$(document).scrollLeft()+'px',top:$(document).scrollTop()+'px',width:'30px',height:'30px',background:'#f00'});$('body').append(this.container);}else{$(this.params.target_element).append(this.container);this.container.css({display:'block',position:'absolute',top:0,left:0,zIndex:1});} +this.container[0].innerHTML=flash;this.movie=document.getElementById(this.params.movie_name);return this;},addFlashVar:function(fv,n,v){if(v!=null){fv.push(n+'='+encodeURIComponent(v));}},flashBind:function(){_this=this;var events={flashReady:function(){if(window[_this.params.movie_name]==undefined){window[_this.params.movie_name]=_this.movie;} +_this.flashBindEvent('flashReady',arguments);},fileDialogComplete:function(){_this.flashBindEvent('fileDialogComplete',arguments);},fileDialogStart:function(){_this.flashBindEvent('fileDialogStart',arguments);},fileQueued:function(file_object){_this.flashBindEvent('fileQueued',arguments);},fileQueueError:function(file_object,error_code,error_msg){_this.flashBindEvent('fileQueueError',arguments);},uploadStart:function(){_this.flashBindEvent('uploadStart',arguments);},uploadProgress:function(){_this.flashBindEvent('uploadProgress',arguments);},uploadError:function(){_this.flashBindEvent('uploadError',arguments);},uploadSuccess:function(){_this.flashBindEvent('uploadSuccess',arguments);},uploadComplete:function(){_this.flashBindEvent('uploadComplete',arguments);},debug:function(){_this.flashBindEvent('debug',arguments);}};window.SWFUpload.instances[this.params.movie_name]=events;window.SWFUpload.movieCount++;},flashEventQueue:[],flashBindEvent:function(evt,a){a=a||new Array();var _this=this;if($.isFunction(this.callbacks[evt])){this.flashEventQueue.push(function(){this.callbacks[evt].apply(this,a);});setTimeout(function(){_this.flashExecuteNextEvent();},0);}else if(this.callbacks[evt]!==null){throw'Event handler '+evt+' is unknown or is not a function';}},flashExecuteNextEvent:function(){var f=this.flashEventQueue?this.flashEventQueue.shift():null;if($.isFunction(f)){f.apply(this);}},destroy:function(){try{this.StopUpload();$(this.movie).remove();SWFUpload.instances[this.params.movie_name]=null;SWFUpload.movieCount--;delete SWFUpload.instances[this.movieName];delete window[this.movieName];return true;}catch(e){return false;}},StartUpload:function(file_id){return this.movie.StartUpload(file_id);},ReturnUploadStart:function(value){return this.movie.ReturnUploadStart(value);},StopUpload:function(){return this.movie.StopUpload();},CancelUpload:function(file_id){return this.movie.CancelUpload(file_id);},GetStats:function(){return this.movie.GetStats();},SetStats:function(stats){return this.movie.SetStats(stats);},GetFile:function(file_id){return this.movie.GetFile(file_id);},GetFileByIndex:function(file_index){return this.movie.GetFileByIndex(file_index);},getFileSizeLimit:function(size){var value=0;var unit='kb';size=$.trim(size.toLowerCase());var values=size.match(/^\d+/);if(values!=null&&values.length>0){value=parseInt(values[0]);} +var units=size.match(/(b|kb|mb|gb)/);if(units!=null&&units.length>0){unit=units[0];} +var multiplier=1024;if(unit==="b"){multiplier=1;}else if(unit==="mb"){multiplier=1048576;}else if(unit==="gb"){multiplier=1073741824;} +return value*multiplier;}};})(jQuery);(function($){$.fn.candyUpload=function(settings,callbacks){new $._candyUpload(this,settings,callbacks);return this;};$._candyUpload=function(target,settings,callbacks){var defaults={debug:false,upload_url:'',params:null,flash_movie:'',file_types:'*.*',file_types_description:'All files',file_size_limit:0,file_upload_limit:0,file_queue_limit:-1,callbacks:{}};this.params=$.extend(defaults,settings);this.params.movie_name='SWFU-'+(window.SWFUpload.instances.length+1);this.target=target;this.createControls();this.target.hide().after(this.ctrl.block).hide();this.params.target_element=this.ctrl.btn_browse.parent().get(0);var _this=this;this.upldr=$.uploader(this.params,{debug:function(msg){$('body').append('
    '+msg+'
    ');_this.bindEvent('debug',arguments);},flashReady:function(){_this.initControls(this);_this.bindEvent('flashReady',arguments);this.movie.style.width=_this.ctrl.btn_browse.width()+'px';this.movie.style.height=_this.ctrl.btn_browse.height()+'px';},fileDialogComplete:function(num_ref_files,num_queue_files){_this.bindEvent('fileDialogComplete',arguments);},fileDialogStart:function(){_this.bindEvent('fileQueued',arguments);},fileQueued:function(o){_this.appendFile(this,o);_this.refreshControls(this);_this.bindEvent('fileQueued',arguments);},fileQueueError:function(o,code,msg){var codes=window.SWFUpload.QUEUE_ERROR;switch(code){case codes.QUEUE_LIMIT_EXCEEDED:_this.queueErrorMsg(_this.locales.limit_exceeded);break;case codes.FILE_EXCEEDS_SIZE_LIMIT:_this.queueErrorMsg(_this.locales.size_limit_exceeded);break;case codes.ZERO_BYTE_FILE:case codes.INVALID_FILETYPE:_this.queueErrorMsg(msg);break;} +_this.bindEvent('fileQueueError',arguments);},uploadStart:function(){this.ReturnUploadStart(true);_this.bindEvent('uploadStart',arguments);},uploadProgress:function(o,bytes,total){_this.fileProgressBar(o.id,bytes,total);_this.bindEvent('uploadProgress',arguments);},uploadError:function(o,code,msg){var codes=window.SWFUpload.UPLOAD_ERROR;switch(code){case codes.FILE_CANCELLED:_this.fileErrorMsg(o.id,_this.locales.canceled);break;case codes.HTTP_ERROR:_this.fileErrorMsg(o.id,_this.locales.http_error+' '+msg);break;case codes.MISSING_UPLOAD_URL:case codes.IO_ERROR:case codes.SECURITY_ERROR:case codes.UPLOAD_LIMIT_EXCEEDED:case codes.UPLOAD_FAILED:case codes.SPECIFIED_FILE_ID_NOT_FOUND:case codes.FILE_VALIDATION_FAILED:case codes.FILE_CANCELLED:case codes.UPLOAD_STOPPED:_this.fileErrorMsg(o.id,_this.locales.error+' '+msg);break;} +_this.refreshControls(this);_this.removeFileCancel(o);_this.bindEvent('uploadError',arguments);},uploadSuccess:function(o,data){_this.fileProgressBar(o.id,1,1);_this.refreshControls(this);_this.removeFileCancel(o);_this.bindEvent('uploadSuccess',arguments);},uploadComplete:function(o){this.StartUpload();_this.refreshControls(this);_this.bindEvent('uploadComplete',arguments);}});};$._candyUpload.prototype={locales:{max_file_size:'Maximum file size allowed:',limit_exceeded:'Limit exceeded.',size_limit_exceeded:'File size exceeds allowed limit.',canceled:'Canceled.',http_error:'HTTP Error:',error:'Error:',choose_file:'Choose file',choose_files:'Choose files',cancel:'Cancel',clean:'Clean',upload:'Upload',no_file_in_queue:'No file in queue.',file_in_queue:'1 file in queue.',files_in_queue:'%d files in queue.',queue_error:'Queue error:'},ctrl:{block:$('
    '),files:null},createControls:function(){this.ctrl.btn_browse=$(' ').click(function(){return false;});this.ctrl.btn_cancel=$(''+this.locales.cancel+'').click(function(){return false;});this.ctrl.btn_clean=$(''+this.locales.clean+'').click(function(){return false;});this.ctrl.btn_upload=$(''+this.locales.upload+'').click(function(){return false;});this.ctrl.msg=$('
    '+this.locales.no_file_in_queue+'
    ').appendTo(this.ctrl.block);var btn=$('
    ').appendTo(this.ctrl.block);var brw=$('').append(this.ctrl.btn_browse).appendTo(btn);$('').append(this.ctrl.btn_upload).appendTo(btn).hide();$('').append(this.ctrl.btn_cancel).appendTo(btn).hide();$('').append(this.ctrl.btn_clean).appendTo(btn).hide();this.bindEvent('createControls');},initControls:function(upldr){if(this.params.file_queue_limit==1){this.ctrl.btn_browse.text(this.locales.choose_file);}else{this.ctrl.btn_browse.text(this.locales.choose_files);} +var _this=this;this.ctrl.btn_cancel.click(function(){_this.cancelQueue(upldr);return false;});this.ctrl.btn_clean.click(function(){_this.cleanQueue(upldr);return false;});this.ctrl.btn_upload.click(function(){_this.uploadQueue(upldr);return false;});var size=this.formatSize(upldr.params.file_size_limit);$('
    '+this.locales.max_file_size+' '+size+'
    ').appendTo(this.ctrl.block);},refreshControls:function(upldr){if(!this.ctrl.files||this.ctrl.files.length==0){return;} +var stats=upldr.GetStats();if(stats.files_queued>0){this.ctrl.btn_cancel.parent().show();this.ctrl.btn_upload.parent().show();if(this.params.file_queue_limit>0&&this.params.file_queue_limit==stats.files_queued){this.ctrl.btn_browse.hide();}else{this.ctrl.btn_browse.show();} +if(stats.files_queued>1){var msg=this.locales.files_in_queue.replace(/%d/,stats.files_queued);}else{var msg=this.locales.file_in_queue;}}else{this.ctrl.btn_browse.show();this.ctrl.btn_cancel.parent().hide();this.ctrl.btn_upload.parent().hide();var msg=this.locales.no_file_in_queue;} +this.ctrl.msg.removeClass('cu-error').text(msg);if(stats.successful_uploads>0||stats.upload_errors>0||stats.upload_cancelled>0){this.ctrl.btn_clean.parent().show();}else{this.ctrl.btn_clean.parent().hide();}},removeFileCancel:function(o){$('#'+o.id+' span.cu-filecancel',this.ctrl.files).remove();},appendFile:function(upldr,o){if(!this.ctrl.files){this.ctrl.files=$('
    ');this.ctrl.msg.after(this.ctrl.files);} +var fileblock=$('
    '+'
    '+o.name+' '+'('+this.formatSize(o.size)+') '+'cancel '+''+'
    ');$('span.cu-filecancel a',fileblock).click(function(){upldr.CancelUpload(o.id);return false;});this.ctrl.files.append(fileblock);},fileProgressBar:function(file_id,bytes,total){var bar=$('#'+file_id+' div.cu-progress>div',this.ctrl.files);if(bar.length==0){$('#'+file_id,this.ctrl.files).append('
     
    ');bar=$('#'+file_id+' div.cu-progress>div',this.ctrl.files);} +var percent=Math.round((bytes*100)/total);bar.css('width',percent+'%').text(percent+'%');},fileMsg:function(file_id,msg,error){error=error||false;var span=$('#'+file_id+' span.cu-filemsg',this.ctrl.files).attr('class','cu-filemsg');if(error){span.addClass('cu-error');} +span.text(msg);},fileErrorMsg:function(file_id,msg){this.fileMsg(file_id,msg,true);},cancelQueue:function(upldr){if(!this.ctrl.files||this.ctrl.files.length==0){return;} +this.ctrl.files.children('div').each(function(){upldr.CancelUpload(this.id);});},uploadQueue:function(upldr){if(!this.ctrl.files||this.ctrl.files.length==0){return;} +upldr.StartUpload();},cleanQueue:function(upldr){var _this=this;var e=$('div.cu-file',this.ctrl.files).not(':has(span.cu-filecancel a)');e.filter(':last').slideUp(200,function(){$(this).remove();if(e.length==1){upldr.SetStats({successful_uploads:0,upload_errors:0,upload_cancelled:0});_this.refreshControls(upldr);}else if(e.length>1){_this.cleanQueue(upldr);}});},queueErrorMsg:function(msg){this.ctrl.msg.addClass('cu-error').text(this.locales.queue_error+' '+msg);},formatSize:function(s){var a_size=Array('B','KB','MB','GB','TB');var i_index=0;while(s>1024){i_index++;s/=1024;} +return(Math.round(s*100)/100)+' '+a_size[i_index];},bindEvent:function(evt,a){if(this.params.callbacks[evt]!=undefined&&$.isFunction(this.params.callbacks[evt])){a=a||new Array();this.params.callbacks[evt].apply(this,a);}}};})(jQuery); \ No newline at end of file diff --git a/admin/js/jquery/jquery.farbtastic.js b/admin/js/jquery/jquery.farbtastic.js new file mode 100644 index 0000000..a758344 --- /dev/null +++ b/admin/js/jquery/jquery.farbtastic.js @@ -0,0 +1,22 @@ + +jQuery.fn.farbtastic=function(callback){$.farbtastic(this,callback);return this;};jQuery.farbtastic=function(container,callback){var container=$(container).get(0);return container.farbtastic||(container.farbtastic=new jQuery._farbtastic(container,callback));};jQuery._farbtastic=function(container,callback){var fb=this;$(container).html('
    ');var e=$('.farbtastic',container);fb.wheel=$('.wheel',container).get(0);fb.radius=84;fb.square=100;fb.width=194;if(navigator.appVersion.match(/MSIE [0-6]\./)){$('*',e).each(function(){if(this.currentStyle.backgroundImage!='none'){var image=this.currentStyle.backgroundImage;image=this.currentStyle.backgroundImage.substring(5,image.length-2);$(this).css({'backgroundImage':'none','filter':"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='"+image+"')"});}});} +fb.linkTo=function(callback){if(typeof fb.callback=='object'){$(fb.callback).unbind('keyup',fb.updateValue);} +fb.color=null;if(typeof callback=='function'){fb.callback=callback;} +else if(typeof callback=='object'||typeof callback=='string'){fb.callback=$(callback);fb.callback.bind('keyup',fb.updateValue);if(fb.callback.get(0).value){fb.setColor(fb.callback.get(0).value);}} +return this;};fb.updateValue=function(event){if(this.value&&this.value!=fb.color){fb.setColor(this.value);}};fb.setColor=function(color){var unpack=fb.unpack(color);if(fb.color!=color&&unpack){fb.color=color;fb.rgb=unpack;fb.hsl=fb.RGBToHSL(fb.rgb);fb.updateDisplay();} +return this;};fb.setHSL=function(hsl){fb.hsl=hsl;fb.rgb=fb.HSLToRGB(hsl);fb.color=fb.pack(fb.rgb);fb.updateDisplay();return this;};fb.widgetCoords=function(event){var x,y;var el=event.target||event.srcElement;var reference=fb.wheel;if(typeof event.offsetX!='undefined'){var pos={x:event.offsetX,y:event.offsetY};var e=el;while(e){e.mouseX=pos.x;e.mouseY=pos.y;pos.x+=e.offsetLeft;pos.y+=e.offsetTop;e=e.offsetParent;} +var e=reference;var offset={x:0,y:0};while(e){if(typeof e.mouseX!='undefined'){x=e.mouseX-offset.x;y=e.mouseY-offset.y;break;} +offset.x+=e.offsetLeft;offset.y+=e.offsetTop;e=e.offsetParent;} +e=el;while(e){e.mouseX=undefined;e.mouseY=undefined;e=e.offsetParent;}} +else{var pos=fb.absolutePosition(reference);x=(event.pageX||0*(event.clientX+$('html').get(0).scrollLeft))-pos.x;y=(event.pageY||0*(event.clientY+$('html').get(0).scrollTop))-pos.y;} +return{x:x-fb.width/2,y:y-fb.width/2};};fb.mousedown=function(event){if(!document.dragging){$(document).bind('mousemove',fb.mousemove).bind('mouseup',fb.mouseup);document.dragging=true;} +var pos=fb.widgetCoords(event);fb.circleDrag=Math.max(Math.abs(pos.x),Math.abs(pos.y))*2>fb.square;fb.mousemove(event);return false;};fb.mousemove=function(event){var pos=fb.widgetCoords(event);if(fb.circleDrag){var hue=Math.atan2(pos.x,-pos.y)/6.28;if(hue<0)hue+=1;fb.setHSL([hue,fb.hsl[1],fb.hsl[2]]);} +else{var sat=Math.max(0,Math.min(1,-(pos.x/fb.square)+.5));var lum=Math.max(0,Math.min(1,-(pos.y/fb.square)+.5));fb.setHSL([fb.hsl[0],sat,lum]);} +return false;};fb.mouseup=function(){$(document).unbind('mousemove',fb.mousemove);$(document).unbind('mouseup',fb.mouseup);document.dragging=false;};fb.updateDisplay=function(){var angle=fb.hsl[0]*6.28;$('.h-marker',e).css({left:Math.round(Math.sin(angle)*fb.radius+fb.width/2)+'px',top:Math.round(-Math.cos(angle)*fb.radius+fb.width/2)+'px'});$('.sl-marker',e).css({left:Math.round(fb.square*(.5-fb.hsl[1])+fb.width/2)+'px',top:Math.round(fb.square*(.5-fb.hsl[2])+fb.width/2)+'px'});$('.color',e).css('backgroundColor',fb.pack(fb.HSLToRGB([fb.hsl[0],1,0.5])));if(typeof fb.callback=='object'){$(fb.callback).css({backgroundColor:fb.color,color:fb.hsl[2]>0.5?'#000':'#fff'});$(fb.callback).each(function(){if(this.value!=fb.color){this.value=fb.color;}});} +else if(typeof fb.callback=='function'){fb.callback.call(fb,fb.color);}};fb.absolutePosition=function(el){var r={x:el.offsetLeft,y:el.offsetTop};if(el.offsetParent){var tmp=fb.absolutePosition(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;} +return r;};fb.pack=function(rgb){var r=Math.round(rgb[0]*255);var g=Math.round(rgb[1]*255);var b=Math.round(rgb[2]*255);return'#'+(r<16?'0':'')+r.toString(16)+ +(g<16?'0':'')+g.toString(16)+ +(b<16?'0':'')+b.toString(16);};fb.unpack=function(color){if(color.length==7){return[parseInt('0x'+color.substring(1,3))/255,parseInt('0x'+color.substring(3,5))/255,parseInt('0x'+color.substring(5,7))/255];} +else if(color.length==4){return[parseInt('0x'+color.substring(1,2))/15,parseInt('0x'+color.substring(2,3))/15,parseInt('0x'+color.substring(3,4))/15];}};fb.HSLToRGB=function(hsl){var m1,m2,r,g,b;var h=hsl[0],s=hsl[1],l=hsl[2];m2=(l<=0.5)?l*(s+1):l+s-l*s;m1=l*2-m2;return[this.hueToRGB(m1,m2,h+0.33333),this.hueToRGB(m1,m2,h),this.hueToRGB(m1,m2,h-0.33333)];};fb.hueToRGB=function(m1,m2,h){h=(h<0)?h+1:((h>1)?h-1:h);if(h*6<1)return m1+(m2-m1)*h*6;if(h*2<1)return m2;if(h*3<2)return m1+(m2-m1)*(0.66666-h)*6;return m1;};fb.RGBToHSL=function(rgb){var min,max,delta,h,s,l;var r=rgb[0],g=rgb[1],b=rgb[2];min=Math.min(r,Math.min(g,b));max=Math.max(r,Math.max(g,b));delta=max-min;l=(min+max)/2;s=0;if(l>0&&l<1){s=delta/(l<0.5?(2*l):(2-2*l));} +h=0;if(delta>0){if(max==r&&max!=g)h+=(g-b)/delta;if(max==g&&max!=b)h+=(2+(b-r)/delta);if(max==b&&max!=r)h+=(4+(r-g)/delta);h/=6;} +return[h,s,l];};$('*',e).mousedown(fb.mousedown);fb.setColor('#000000');if(callback){fb.linkTo(callback);}}; \ No newline at end of file diff --git a/admin/js/jquery/jquery.js b/admin/js/jquery/jquery.js new file mode 100644 index 0000000..4071503 --- /dev/null +++ b/admin/js/jquery/jquery.js @@ -0,0 +1,432 @@ + +(function(){var +window=this,undefined,_jQuery=window.jQuery,_$=window.$,jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);},quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,isSimple=/^.[^:#\[\.,]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;this.context=selector;return this;} +if(typeof selector==="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1]) +selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem&&elem.id!=match[3]) +return jQuery().find(selector);var ret=jQuery(elem||[]);ret.context=document;ret.selector=selector;return ret;}}else +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector)) +return jQuery(document).ready(selector);if(selector.selector&&selector.context){this.selector=selector.selector;this.context=selector.context;} +return this.setArray(jQuery.isArray(selector)?selector:jQuery.makeArray(selector));},selector:"",jquery:"1.3.2",size:function(){return this.length;},get:function(num){return num===undefined?Array.prototype.slice.call(this):this[num];},pushStack:function(elems,name,selector){var ret=jQuery(elems);ret.prevObject=this;ret.context=this.context;if(name==="find") +ret.selector=this.selector+(this.selector?" ":"")+selector;else if(name) +ret.selector=this.selector+"."+name+"("+selector+")";return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(typeof name==="string") +if(value===undefined) +return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;} +return this.each(function(i){for(name in options) +jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0) +value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!=="object"&&text!=null) +return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8) +ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).clone();if(this[0].parentNode) +wrap.insertBefore(this[0]);wrap.map(function(){var elem=this;while(elem.firstChild) +elem=elem.firstChild;return elem;}).append(this);} +return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1) +this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1) +this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},push:[].push,sort:[].sort,splice:[].splice,find:function(selector){if(this.length===1){var ret=this.pushStack([],"find",selector);ret.length=0;jQuery.find(selector,this[0],ret);return ret;}else{return this.pushStack(jQuery.unique(jQuery.map(this,function(elem){return jQuery.find(selector,elem);})),"find",selector);}},clone:function(events){var ret=this.map(function(){if(!jQuery.support.noCloneEvent&&!jQuery.isXMLDoc(this)){var html=this.outerHTML;if(!html){var div=this.ownerDocument.createElement("div");div.appendChild(this.cloneNode(true));html=div.innerHTML;} +return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0];}else +return this.cloneNode(true);});if(events===true){var orig=this.find("*").andSelf(),i=0;ret.find("*").andSelf().each(function(){if(this.nodeName!==orig[i].nodeName) +return;var events=jQuery.data(orig[i],"events");for(var type in events){for(var handler in events[type]){jQuery.event.add(this,type,events[type][handler],events[type][handler].data);}} +i++;});} +return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,jQuery.grep(this,function(elem){return elem.nodeType===1;})),"filter",selector);},closest:function(selector){var pos=jQuery.expr.match.POS.test(selector)?jQuery(selector):null,closer=0;return this.map(function(){var cur=this;while(cur&&cur.ownerDocument){if(pos?pos.index(cur)>-1:jQuery(cur).is(selector)){jQuery.data(cur,"closest",closer);return cur;} +cur=cur.parentNode;closer++;}});},not:function(selector){if(typeof selector==="string") +if(isSimple.test(selector)) +return this.pushStack(jQuery.multiFilter(selector,this,true),"not",selector);else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector==="string"?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return!!selector&&this.is("."+selector);},val:function(value){if(value===undefined){var elem=this[0];if(elem){if(jQuery.nodeName(elem,'option')) +return(elem.attributes.value||{}).specified?elem.value:elem.text;if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0) +return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length) +this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value===undefined?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,+i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},domManip:function(args,table,callback){if(this[0]){var fragment=(this[0].ownerDocument||this[0]).createDocumentFragment(),scripts=jQuery.clean(args,(this[0].ownerDocument||this[0]),fragment),first=fragment.firstChild;if(first) +for(var i=0,l=this.length;i1||i>0?fragment.cloneNode(true):fragment);if(scripts) +jQuery.each(scripts,evalScript);} +return this;function root(elem,cur){return table&&jQuery.nodeName(elem,"table")&&jQuery.nodeName(cur,"tr")?(elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody"))):elem;}}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src) +jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode) +elem.parentNode.removeChild(elem);} +function now(){return+new Date;} +jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(typeof target==="boolean"){deep=target;target=arguments[1]||{};i=2;} +if(typeof target!=="object"&&!jQuery.isFunction(target)) +target={};if(length==i){target=this;--i;} +for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];} +callback.call(elem);for(var name in options) +elem.style[name]=old[name];},css:function(elem,name,force,extra){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;if(extra==="border") +return;jQuery.each(which,function(){if(!extra) +val-=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;if(extra==="margin") +val+=parseFloat(jQuery.curCSS(elem,"margin"+this,true))||0;else +val-=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});} +if(elem.offsetWidth!==0) +getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,Math.round(val));} +return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;if(name=="opacity"&&!jQuery.support.opacity){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;} +if(name.match(/float/i)) +name=styleFloat;if(!force&&style&&style[name]) +ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i)) +name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle) +ret=computedStyle.getPropertyValue(name);if(name=="opacity"&&ret=="") +ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}} +return ret;},clean:function(elems,context,fragment){context=context||document;if(typeof context.createElement==="undefined") +context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;if(!fragment&&elems.length===1&&typeof elems[0]==="string"){var match=/^<(\w+)\s*\/?>$/.exec(elems[0]);if(match) +return[context.createElement(match[1])];} +var ret=[],scripts=[],div=context.createElement("div");jQuery.each(elems,function(i,elem){if(typeof elem==="number") +elem+='';if(!elem) +return;if(typeof elem==="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=elem.replace(/^\s+/,"").substring(0,10).toLowerCase();var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||!jQuery.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--) +div=div.lastChild;if(!jQuery.support.tbody){var hasBody=/"&&!hasBody?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j) +if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length) +tbody[j].parentNode.removeChild(tbody[j]);} +if(!jQuery.support.leadingWhitespace&&/^\s/.test(elem)) +div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);elem=jQuery.makeArray(div.childNodes);} +if(elem.nodeType) +ret.push(elem);else +ret=jQuery.merge(ret,elem);});if(fragment){for(var i=0;ret[i];i++){if(jQuery.nodeName(ret[i],"script")&&(!ret[i].type||ret[i].type.toLowerCase()==="text/javascript")){scripts.push(ret[i].parentNode?ret[i].parentNode.removeChild(ret[i]):ret[i]);}else{if(ret[i].nodeType===1) +ret.splice.apply(ret,[i+1,0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))));fragment.appendChild(ret[i]);}} +return scripts;} +return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8) +return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&elem.parentNode) +elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode) +throw"type property can't be changed";elem[name]=value;} +if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name)) +return elem.getAttributeNode(name).nodeValue;if(name=="tabIndex"){var attributeNode=elem.getAttributeNode("tabIndex");return attributeNode&&attributeNode.specified?attributeNode.value:elem.nodeName.match(/(button|input|object|select|textarea)/i)?0:elem.nodeName.match(/^(a|area)$/i)&&elem.href?0:undefined;} +return elem[name];} +if(!jQuery.support.style&¬xml&&name=="style") +return jQuery.attr(elem.style,"cssText",value);if(set) +elem.setAttribute(name,""+value);var attr=!jQuery.support.hrefNormalized&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;} +if(!jQuery.support.opacity&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+ +(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");} +return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";} +name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set) +elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||typeof array==="string"||jQuery.isFunction(array)||array.setInterval) +ret[0]=array;else +while(i) +ret[--i]=array[i];} +return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i0?this.clone(true):this).get();jQuery.fn[original].apply(jQuery(insert[i]),elems);ret=ret.concat(elems);} +return this.pushStack(ret,name,selector);};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1) +this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames,state){if(typeof state!=="boolean") +state=!jQuery.className.has(this,classNames);jQuery.className[state?"add":"remove"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).length){jQuery("*",this).add([this]).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode) +this.parentNode.removeChild(this);}},empty:function(){jQuery(this).children().remove();while(this.firstChild) +this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;} +var expando="jQuery"+now(),uuid=0,windowData={};jQuery.extend({cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id) +id=elem[expando]=++uuid;if(name&&!jQuery.cache[id]) +jQuery.cache[id]={};if(data!==undefined) +jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id]) +break;if(!name) +jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute) +elem.removeAttribute(expando);} +delete jQuery.cache[id];}},queue:function(elem,type,data){if(elem){type=(type||"fx")+"queue";var q=jQuery.data(elem,type);if(!q||jQuery.isArray(data)) +q=jQuery.data(elem,type,jQuery.makeArray(data));else if(data) +q.push(data);} +return q;},dequeue:function(elem,type){var queue=jQuery.queue(elem,type),fn=queue.shift();if(!type||type==="fx") +fn=queue[0];if(fn!==undefined) +fn.call(elem);}});jQuery.fn.extend({data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length) +data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},queue:function(type,data){if(typeof type!=="string"){data=type;type="fx";} +if(data===undefined) +return jQuery.queue(this[0],type);return this.each(function(){var queue=jQuery.queue(this,type,data);if(type=="fx"&&queue.length==1) +queue[0].call(this);});},dequeue:function(type){return this.each(function(){jQuery.dequeue(this,type);});}});(function(){var chunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,done=0,toString=Object.prototype.toString;var Sizzle=function(selector,context,results,seed){results=results||[];context=context||document;if(context.nodeType!==1&&context.nodeType!==9) +return[];if(!selector||typeof selector!=="string"){return results;} +var parts=[],m,set,checkSet,check,mode,extra,prune=true;chunker.lastIndex=0;while((m=chunker.exec(selector))!==null){parts.push(m[1]);if(m[2]){extra=RegExp.rightContext;break;}} +if(parts.length>1&&origPOS.exec(selector)){if(parts.length===2&&Expr.relative[parts[0]]){set=posProcess(parts[0]+parts[1],context);}else{set=Expr.relative[parts[0]]?[context]:Sizzle(parts.shift(),context);while(parts.length){selector=parts.shift();if(Expr.relative[selector]) +selector+=parts.shift();set=posProcess(selector,set);}}}else{var ret=seed?{expr:parts.pop(),set:makeArray(seed)}:Sizzle.find(parts.pop(),parts.length===1&&context.parentNode?context.parentNode:context,isXML(context));set=Sizzle.filter(ret.expr,ret.set);if(parts.length>0){checkSet=makeArray(set);}else{prune=false;} +while(parts.length){var cur=parts.pop(),pop=cur;if(!Expr.relative[cur]){cur="";}else{pop=parts.pop();} +if(pop==null){pop=context;} +Expr.relative[cur](checkSet,pop,isXML(context));}} +if(!checkSet){checkSet=set;} +if(!checkSet){throw"Syntax error, unrecognized expression: "+(cur||selector);} +if(toString.call(checkSet)==="[object Array]"){if(!prune){results.push.apply(results,checkSet);}else if(context.nodeType===1){for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&(checkSet[i]===true||checkSet[i].nodeType===1&&contains(context,checkSet[i]))){results.push(set[i]);}}}else{for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&checkSet[i].nodeType===1){results.push(set[i]);}}}}else{makeArray(checkSet,results);} +if(extra){Sizzle(extra,context,results,seed);if(sortOrder){hasDuplicate=false;results.sort(sortOrder);if(hasDuplicate){for(var i=1;i":function(checkSet,part,isXML){var isPartStr=typeof part==="string";if(isPartStr&&!/\W/.test(part)){part=isXML?part:part.toUpperCase();for(var i=0,l=checkSet.length;i=0)){if(!inplace) +result.push(elem);}else if(inplace){curLoop[i]=false;}}} +return false;},ID:function(match){return match[1].replace(/\\/g,"");},TAG:function(match,curLoop){for(var i=0;curLoop[i]===false;i++){} +return curLoop[i]&&isXML(curLoop[i])?match[1]:match[1].toUpperCase();},CHILD:function(match){if(match[1]=="nth"){var test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2]=="even"&&"2n"||match[2]=="odd"&&"2n+1"||!/\D/.test(match[2])&&"0n+"+match[2]||match[2]);match[2]=(test[1]+(test[2]||1))-0;match[3]=test[3]-0;} +match[0]=done++;return match;},ATTR:function(match,curLoop,inplace,result,not,isXML){var name=match[1].replace(/\\/g,"");if(!isXML&&Expr.attrMap[name]){match[1]=Expr.attrMap[name];} +if(match[2]==="~="){match[4]=" "+match[4]+" ";} +return match;},PSEUDO:function(match,curLoop,inplace,result,not){if(match[1]==="not"){if(match[3].match(chunker).length>1||/^\w/.test(match[3])){match[3]=Sizzle(match[3],null,null,curLoop);}else{var ret=Sizzle.filter(match[3],curLoop,inplace,true^not);if(!inplace){result.push.apply(result,ret);} +return false;}}else if(Expr.match.POS.test(match[0])||Expr.match.CHILD.test(match[0])){return true;} +return match;},POS:function(match){match.unshift(true);return match;}},filters:{enabled:function(elem){return elem.disabled===false&&elem.type!=="hidden";},disabled:function(elem){return elem.disabled===true;},checked:function(elem){return elem.checked===true;},selected:function(elem){elem.parentNode.selectedIndex;return elem.selected===true;},parent:function(elem){return!!elem.firstChild;},empty:function(elem){return!elem.firstChild;},has:function(elem,i,match){return!!Sizzle(match[3],elem).length;},header:function(elem){return/h\d/i.test(elem.nodeName);},text:function(elem){return"text"===elem.type;},radio:function(elem){return"radio"===elem.type;},checkbox:function(elem){return"checkbox"===elem.type;},file:function(elem){return"file"===elem.type;},password:function(elem){return"password"===elem.type;},submit:function(elem){return"submit"===elem.type;},image:function(elem){return"image"===elem.type;},reset:function(elem){return"reset"===elem.type;},button:function(elem){return"button"===elem.type||elem.nodeName.toUpperCase()==="BUTTON";},input:function(elem){return/input|select|textarea|button/i.test(elem.nodeName);}},setFilters:{first:function(elem,i){return i===0;},last:function(elem,i,match,array){return i===array.length-1;},even:function(elem,i){return i%2===0;},odd:function(elem,i){return i%2===1;},lt:function(elem,i,match){return imatch[3]-0;},nth:function(elem,i,match){return match[3]-0==i;},eq:function(elem,i,match){return match[3]-0==i;}},filter:{PSEUDO:function(elem,match,i,array){var name=match[1],filter=Expr.filters[name];if(filter){return filter(elem,i,match,array);}else if(name==="contains"){return(elem.textContent||elem.innerText||"").indexOf(match[3])>=0;}else if(name==="not"){var not=match[3];for(var i=0,l=not.length;i=0);}}},ID:function(elem,match){return elem.nodeType===1&&elem.getAttribute("id")===match;},TAG:function(elem,match){return(match==="*"&&elem.nodeType===1)||elem.nodeName===match;},CLASS:function(elem,match){return(" "+(elem.className||elem.getAttribute("class"))+" ").indexOf(match)>-1;},ATTR:function(elem,match){var name=match[1],result=Expr.attrHandle[name]?Expr.attrHandle[name](elem):elem[name]!=null?elem[name]:elem.getAttribute(name),value=result+"",type=match[2],check=match[4];return result==null?type==="!=":type==="="?value===check:type==="*="?value.indexOf(check)>=0:type==="~="?(" "+value+" ").indexOf(check)>=0:!check?value&&result!==false:type==="!="?value!=check:type==="^="?value.indexOf(check)===0:type==="$="?value.substr(value.length-check.length)===check:type==="|="?value===check||value.substr(0,check.length+1)===check+"-":false;},POS:function(elem,match,i,array){var name=match[2],filter=Expr.setFilters[name];if(filter){return filter(elem,i,match,array);}}}};var origPOS=Expr.match.POS;for(var type in Expr.match){Expr.match[type]=RegExp(Expr.match[type].source+/(?![^\[]*\])(?![^\(]*\))/.source);} +var makeArray=function(array,results){array=Array.prototype.slice.call(array);if(results){results.push.apply(results,array);return results;} +return array;};try{Array.prototype.slice.call(document.documentElement.childNodes);}catch(e){makeArray=function(array,results){var ret=results||[];if(toString.call(array)==="[object Array]"){Array.prototype.push.apply(ret,array);}else{if(typeof array.length==="number"){for(var i=0,l=array.length;i";var root=document.documentElement;root.insertBefore(form,root.firstChild);if(!!document.getElementById(id)){Expr.find.ID=function(match,context,isXML){if(typeof context.getElementById!=="undefined"&&!isXML){var m=context.getElementById(match[1]);return m?m.id===match[1]||typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id").nodeValue===match[1]?[m]:undefined:[];}};Expr.filter.ID=function(elem,match){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return elem.nodeType===1&&node&&node.nodeValue===match;};} +root.removeChild(form);})();(function(){var div=document.createElement("div");div.appendChild(document.createComment(""));if(div.getElementsByTagName("*").length>0){Expr.find.TAG=function(match,context){var results=context.getElementsByTagName(match[1]);if(match[1]==="*"){var tmp=[];for(var i=0;results[i];i++){if(results[i].nodeType===1){tmp.push(results[i]);}} +results=tmp;} +return results;};} +div.innerHTML="";if(div.firstChild&&typeof div.firstChild.getAttribute!=="undefined"&&div.firstChild.getAttribute("href")!=="#"){Expr.attrHandle.href=function(elem){return elem.getAttribute("href",2);};}})();if(document.querySelectorAll)(function(){var oldSizzle=Sizzle,div=document.createElement("div");div.innerHTML="

    ";if(div.querySelectorAll&&div.querySelectorAll(".TEST").length===0){return;} +Sizzle=function(query,context,extra,seed){context=context||document;if(!seed&&context.nodeType===9&&!isXML(context)){try{return makeArray(context.querySelectorAll(query),extra);}catch(e){}} +return oldSizzle(query,context,extra,seed);};Sizzle.find=oldSizzle.find;Sizzle.filter=oldSizzle.filter;Sizzle.selectors=oldSizzle.selectors;Sizzle.matches=oldSizzle.matches;})();if(document.getElementsByClassName&&document.documentElement.getElementsByClassName)(function(){var div=document.createElement("div");div.innerHTML="
    ";if(div.getElementsByClassName("e").length===0) +return;div.lastChild.className="e";if(div.getElementsByClassName("e").length===1) +return;Expr.order.splice(1,0,"CLASS");Expr.find.CLASS=function(match,context,isXML){if(typeof context.getElementsByClassName!=="undefined"&&!isXML){return context.getElementsByClassName(match[1]);}};})();function dirNodeCheck(dir,cur,doneName,checkSet,nodeCheck,isXML){var sibDir=dir=="previousSibling"&&!isXML;for(var i=0,l=checkSet.length;i0){match=elem;break;}} +elem=elem[dir];} +checkSet[i]=match;}}} +var contains=document.compareDocumentPosition?function(a,b){return a.compareDocumentPosition(b)&16;}:function(a,b){return a!==b&&(a.contains?a.contains(b):true);};var isXML=function(elem){return elem.nodeType===9&&elem.documentElement.nodeName!=="HTML"||!!elem.ownerDocument&&isXML(elem.ownerDocument);};var posProcess=function(selector,context){var tmpSet=[],later="",match,root=context.nodeType?[context]:context;while((match=Expr.match.PSEUDO.exec(selector))){later+=match[0];selector=selector.replace(Expr.match.PSEUDO,"");} +selector=Expr.relative[selector]?selector+"*":selector;for(var i=0,l=root.length;i0||elem.offsetHeight>0;};Sizzle.selectors.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem;}).length;};jQuery.multiFilter=function(expr,elems,not){if(not){expr=":not("+expr+")";} +return Sizzle.matches(expr,elems);};jQuery.dir=function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1) +matched.push(cur);cur=cur[dir];} +return matched;};jQuery.nth=function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir]) +if(cur.nodeType==1&&++num==result) +break;return cur;};jQuery.sibling=function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem) +r.push(n);} +return r;};return;window.Sizzle=Sizzle;})();jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8) +return;if(elem.setInterval&&elem!=window) +elem=window;if(!handler.guid) +handler.guid=this.guid++;if(data!==undefined){var fn=handler;handler=this.proxy(fn);handler.data=data;} +var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){return typeof jQuery!=="undefined"&&!jQuery.event.triggered?jQuery.event.handle.apply(arguments.callee.elem,arguments):undefined;});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();handler.type=namespaces.slice().sort().join(".");var handlers=events[type];if(jQuery.event.specialAll[type]) +jQuery.event.specialAll[type].setup.call(elem,data,namespaces);if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem,data,namespaces)===false){if(elem.addEventListener) +elem.addEventListener(type,handle,false);else if(elem.attachEvent) +elem.attachEvent("on"+type,handle);}} +handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8) +return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types===undefined||(typeof types==="string"&&types.charAt(0)==".")) +for(var type in events) +this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;} +jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");if(events[type]){if(handler) +delete events[type][handler.guid];else +for(var handle in events[type]) +if(namespace.test(events[type][handle].type)) +delete events[type][handle];if(jQuery.event.specialAll[type]) +jQuery.event.specialAll[type].teardown.call(elem,namespaces);for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem,namespaces)===false){if(elem.removeEventListener) +elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent) +elem.detachEvent("on"+type,jQuery.data(elem,"handle"));} +ret=null;delete events[type];}}});} +for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(event,data,elem,bubbling){var type=event.type||event;if(!bubbling){event=typeof event==="object"?event[expando]?event:jQuery.extend(jQuery.Event(type),event):jQuery.Event(type);if(type.indexOf("!")>=0){event.type=type=type.slice(0,-1);event.exclusive=true;} +if(!elem){event.stopPropagation();if(this.global[type]) +jQuery.each(jQuery.cache,function(){if(this.events&&this.events[type]) +jQuery.event.trigger(event,data,this.handle.elem);});} +if(!elem||elem.nodeType==3||elem.nodeType==8) +return undefined;event.result=undefined;event.target=elem;data=jQuery.makeArray(data);data.unshift(event);} +event.currentTarget=elem;var handle=jQuery.data(elem,"handle");if(handle) +handle.apply(elem,data);if((!elem[type]||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false) +event.result=false;if(!bubbling&&elem[type]&&!event.isDefaultPrevented()&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}} +this.triggered=false;if(!event.isPropagationStopped()){var parent=elem.parentNode||elem.ownerDocument;if(parent) +jQuery.event.trigger(event,data,parent,true);}},handle:function(event){var all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);event.currentTarget=this;var namespaces=event.type.split(".");event.type=namespaces.shift();all=!namespaces.length&&!event.exclusive;var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||namespace.test(handler.type)){event.handler=handler;event.data=handler.data;var ret=handler.apply(this,arguments);if(ret!==undefined){event.result=ret;if(ret===false){event.preventDefault();event.stopPropagation();}} +if(event.isImmediatePropagationStopped()) +break;}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(event){if(event[expando]) +return event;var originalEvent=event;event=jQuery.Event(originalEvent);for(var i=this.props.length,prop;i;){prop=this.props[--i];event[prop]=originalEvent[prop];} +if(!event.target) +event.target=event.srcElement||document;if(event.target.nodeType==3) +event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement) +event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);} +if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode)) +event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey) +event.metaKey=event.ctrlKey;if(!event.which&&event.button) +event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy=proxy||function(){return fn.apply(this,arguments);};proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:bindReady,teardown:function(){}}},specialAll:{live:{setup:function(selector,namespaces){jQuery.event.add(this,namespaces[0],liveHandler);},teardown:function(namespaces){if(namespaces.length){var remove=0,name=RegExp("(^|\\.)"+namespaces[0]+"(\\.|$)");jQuery.each((jQuery.data(this,"events").live||{}),function(){if(name.test(this.type)) +remove++;});if(remove<1) +jQuery.event.remove(this,namespaces[0],liveHandler);}}}}};jQuery.Event=function(src){if(!this.preventDefault) +return new jQuery.Event(src);if(src&&src.type){this.originalEvent=src;this.type=src.type;}else +this.type=src;this.timeStamp=now();this[expando]=true;};function returnFalse(){return false;} +function returnTrue(){return true;} +jQuery.Event.prototype={preventDefault:function(){this.isDefaultPrevented=returnTrue;var e=this.originalEvent;if(!e) +return;if(e.preventDefault) +e.preventDefault();e.returnValue=false;},stopPropagation:function(){this.isPropagationStopped=returnTrue;var e=this.originalEvent;if(!e) +return;if(e.stopPropagation) +e.stopPropagation();e.cancelBubble=true;},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=returnTrue;this.stopPropagation();},isDefaultPrevented:returnFalse,isPropagationStopped:returnFalse,isImmediatePropagationStopped:returnFalse};var withinElement=function(event){var parent=event.relatedTarget;while(parent&&parent!=this) +try{parent=parent.parentNode;} +catch(e){parent=this;} +if(parent!=this){event.type=event.data;jQuery.event.handle.apply(this,arguments);}};jQuery.each({mouseover:'mouseenter',mouseout:'mouseleave'},function(orig,fix){jQuery.event.special[fix]={setup:function(){jQuery.event.add(this,orig,withinElement,fix);},teardown:function(){jQuery.event.remove(this,orig,withinElement);}};});jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data){return this.each(function(){jQuery.event.trigger(type,data,this);});},triggerHandler:function(type,data){if(this[0]){var event=jQuery.Event(type);event.preventDefault();event.stopPropagation();jQuery.event.trigger(event,data,this[0]);return event.result;}},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);} +var type="GET";if(params) +if(jQuery.isFunction(params)){callback=params;params=null;}else if(typeof params==="object"){params=jQuery.param(params);type="POST";} +var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified") +self.html(selector?jQuery("
    ").append(res.responseText.replace(//g,"")).find(selector):res.responseText);if(callback) +self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return this.elements?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;} +return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};} +return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!=="string") +s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre)) +s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre)) +s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";} +if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data) +s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){} +if(head) +head.removeChild(script);};} +if(s.dataType=="script"&&s.cache==null) +s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");} +if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;} +if(s.global&&!jQuery.active++) +jQuery.event.trigger("ajaxStart");var parts=/^(\w+:)?\/\/([^\/?#]+)/.exec(s.url);if(s.dataType=="script"&&type=="GET"&&parts&&(parts[1]&&parts[1]!=location.protocol||parts[2]!=location.host)){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset) +script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();script.onload=script.onreadystatechange=null;head.removeChild(script);}};} +head.appendChild(script);return undefined;} +var requestDone=false;var xhr=s.xhr();if(s.username) +xhr.open(type,s.url,s.async,s.username,s.password);else +xhr.open(type,s.url,s.async);try{if(s.data) +xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified) +xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){} +if(s.beforeSend&&s.beforeSend(xhr,s)===false){if(s.global&&!--jQuery.active) +jQuery.event.trigger("ajaxStop");xhr.abort();return false;} +if(s.global) +jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(xhr.readyState==0){if(ival){clearInterval(ival);ival=null;if(s.global&&!--jQuery.active) +jQuery.event.trigger("ajaxStop");}}else if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;} +status=isTimeout=="timeout"?"timeout":!jQuery.httpSuccess(xhr)?"error":s.ifModified&&jQuery.httpNotModified(xhr,s.url)?"notmodified":"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s);}catch(e){status="parsererror";}} +if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){} +if(s.ifModified&&modRes) +jQuery.lastModified[s.url]=modRes;if(!jsonp) +success();}else +jQuery.handleError(s,xhr,status);complete();if(isTimeout) +xhr.abort();if(s.async) +xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0) +setTimeout(function(){if(xhr&&!requestDone) +onreadystatechange("timeout");},s.timeout);} +try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);} +if(!s.async) +onreadystatechange();function success(){if(s.success) +s.success(data,status);if(s.global) +jQuery.event.trigger("ajaxSuccess",[xhr,s]);} +function complete(){if(s.complete) +s.complete(xhr,status);if(s.global) +jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active) +jQuery.event.trigger("ajaxStop");} +return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global) +jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223;}catch(e){} +return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url];}catch(e){} +return false;},httpData:function(xhr,type,s){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror") +throw"parsererror";if(s&&s.dataFilter) +data=s.dataFilter(data,type);if(typeof data==="string"){if(type=="script") +jQuery.globalEval(data);if(type=="json") +data=window["eval"]("("+data+")");} +return data;},param:function(a){var s=[];function add(key,value){s[s.length]=encodeURIComponent(key)+'='+encodeURIComponent(value);};if(jQuery.isArray(a)||a.jquery) +jQuery.each(a,function(){add(this.name,this.value);});else +for(var j in a) +if(jQuery.isArray(a[j])) +jQuery.each(a[j],function(){add(j,this);});else +add(j,jQuery.isFunction(a[j])?a[j]():a[j]);return s.join("&").replace(/%20/g,"+");}});var elemdisplay={},timerId,fxAttrs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function genFx(type,num){var obj={};jQuery.each(fxAttrs.concat.apply([],fxAttrs.slice(0,num)),function(){obj[this]=type;});return obj;} +jQuery.fn.extend({show:function(speed,callback){if(speed){return this.animate(genFx("show",3),speed,callback);}else{for(var i=0,l=this.length;i").appendTo("body");display=elem.css("display");if(display==="none") +display="block";elem.remove();elemdisplay[tagName]=display;} +jQuery.data(this[i],"olddisplay",display);}} +for(var i=0,l=this.length;i=0;i--) +if(timers[i].elem==this){if(gotoEnd) +timers[i](true);timers.splice(i,1);}});if(!gotoEnd) +this.dequeue();return this;}});jQuery.each({slideDown:genFx("show",1),slideUp:genFx("hide",1),slideToggle:genFx("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(name,props){jQuery.fn[name]=function(speed,callback){return this.animate(props,speed,callback);};});jQuery.extend({speed:function(speed,easing,fn){var opt=typeof speed==="object"?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&!jQuery.isFunction(easing)&&easing};opt.duration=jQuery.fx.off?0:typeof opt.duration==="number"?opt.duration:jQuery.fx.speeds[opt.duration]||jQuery.fx.speeds._default;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false) +jQuery(this).dequeue();if(jQuery.isFunction(opt.old)) +opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig) +options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step) +this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style) +this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)) +return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;var self=this;function t(gotoEnd){return self.step(gotoEnd);} +t.elem=this.elem;if(t()&&jQuery.timers.push(t)&&!timerId){timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim) +if(this.options.curAnim[i]!==true) +done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none") +this.elem.style.display="block";} +if(this.options.hide) +jQuery(this.elem).hide();if(this.options.hide||this.options.show) +for(var p in this.options.curAnim) +jQuery.attr(this.elem.style,p,this.options.orig[p]);this.options.complete.call(this.elem);} +return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();} +return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){if(fx.elem.style&&fx.elem.style[fx.prop]!=null) +fx.elem.style[fx.prop]=fx.now+fx.unit;else +fx.elem[fx.prop]=fx.now;}}});if(document.documentElement["getBoundingClientRect"]) +jQuery.fn.offset=function(){if(!this[0])return{top:0,left:0};if(this[0]===this[0].ownerDocument.body)return jQuery.offset.bodyOffset(this[0]);var box=this[0].getBoundingClientRect(),doc=this[0].ownerDocument,body=doc.body,docElem=doc.documentElement,clientTop=docElem.clientTop||body.clientTop||0,clientLeft=docElem.clientLeft||body.clientLeft||0,top=box.top+(self.pageYOffset||jQuery.boxModel&&docElem.scrollTop||body.scrollTop)-clientTop,left=box.left+(self.pageXOffset||jQuery.boxModel&&docElem.scrollLeft||body.scrollLeft)-clientLeft;return{top:top,left:left};};else +jQuery.fn.offset=function(){if(!this[0])return{top:0,left:0};if(this[0]===this[0].ownerDocument.body)return jQuery.offset.bodyOffset(this[0]);jQuery.offset.initialized||jQuery.offset.initialize();var elem=this[0],offsetParent=elem.offsetParent,prevOffsetParent=elem,doc=elem.ownerDocument,computedStyle,docElem=doc.documentElement,body=doc.body,defaultView=doc.defaultView,prevComputedStyle=defaultView.getComputedStyle(elem,null),top=elem.offsetTop,left=elem.offsetLeft;while((elem=elem.parentNode)&&elem!==body&&elem!==docElem){computedStyle=defaultView.getComputedStyle(elem,null);top-=elem.scrollTop,left-=elem.scrollLeft;if(elem===offsetParent){top+=elem.offsetTop,left+=elem.offsetLeft;if(jQuery.offset.doesNotAddBorder&&!(jQuery.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(elem.tagName))) +top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0;prevOffsetParent=offsetParent,offsetParent=elem.offsetParent;} +if(jQuery.offset.subtractsBorderForOverflowNotVisible&&computedStyle.overflow!=="visible") +top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0;prevComputedStyle=computedStyle;} +if(prevComputedStyle.position==="relative"||prevComputedStyle.position==="static") +top+=body.offsetTop,left+=body.offsetLeft;if(prevComputedStyle.position==="fixed") +top+=Math.max(docElem.scrollTop,body.scrollTop),left+=Math.max(docElem.scrollLeft,body.scrollLeft);return{top:top,left:left};};jQuery.offset={initialize:function(){if(this.initialized)return;var body=document.body,container=document.createElement('div'),innerDiv,checkDiv,table,td,rules,prop,bodyMarginTop=body.style.marginTop,html='
    ';rules={position:'absolute',top:0,left:0,margin:0,border:0,width:'1px',height:'1px',visibility:'hidden'};for(prop in rules)container.style[prop]=rules[prop];container.innerHTML=html;body.insertBefore(container,body.firstChild);innerDiv=container.firstChild,checkDiv=innerDiv.firstChild,td=innerDiv.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(checkDiv.offsetTop!==5);this.doesAddBorderForTableAndCells=(td.offsetTop===5);innerDiv.style.overflow='hidden',innerDiv.style.position='relative';this.subtractsBorderForOverflowNotVisible=(checkDiv.offsetTop===-5);body.style.marginTop='1px';this.doesNotIncludeMarginInBodyOffset=(body.offsetTop===0);body.style.marginTop=bodyMarginTop;body.removeChild(container);this.initialized=true;},bodyOffset:function(body){jQuery.offset.initialized||jQuery.offset.initialize();var top=body.offsetTop,left=body.offsetLeft;if(jQuery.offset.doesNotIncludeMarginInBodyOffset) +top+=parseInt(jQuery.curCSS(body,'marginTop',true),10)||0,left+=parseInt(jQuery.curCSS(body,'marginLeft',true),10)||0;return{top:top,left:left};}};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};} +return results;},offsetParent:function(){var offsetParent=this[0].offsetParent||document.body;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static')) +offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return null;return val!==undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom",lower=name.toLowerCase();jQuery.fn["inner"+name]=function(){return this[0]?jQuery.css(this[0],lower,false,"padding"):null;};jQuery.fn["outer"+name]=function(margin){return this[0]?jQuery.css(this[0],lower,false,margin?"margin":"border"):null;};var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(document.documentElement["client"+name],document.body["scroll"+name],document.documentElement["scroll"+name],document.body["offset"+name],document.documentElement["offset"+name]):size===undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,typeof size==="string"?size:size+"px");};});})(); \ No newline at end of file diff --git a/admin/js/jquery/jquery.modal.js b/admin/js/jquery/jquery.modal.js new file mode 100644 index 0000000..03703e9 --- /dev/null +++ b/admin/js/jquery/jquery.modal.js @@ -0,0 +1,17 @@ + +(function($){if(/^1\.(0|1)\./.test($.fn.jquery)||/^1\.2\.(0|1|2|3|4|5)/.test($.fn.jquery)){throw('Modal requieres jQuery v1.2.6 or later. You are using v'+$.fn.jquery);return;} +$.modal=function(data,params){this.params=$.extend(this.params,params);return this.build(data);};$.modal.version='1.0';$.modal.prototype={params:{width:null,height:null,speed:300,opacity:0.9,loader_img:'loader.gif',loader_txt:'loading...',close_img:'close.png',close_txt:'close',on_close:function(){}},ctrl:{box:$(),loader:$(),overlay:$('
    '),hidden:$()},build:function(data){this.ctrl.loader=$('
    '+this.params.loader_txt+'
    ');this.addOverlay();var size=this.getBoxSize(this.ctrl.loading);this.ctrl.box=this.getBox(this.ctrl.loading,{top:Math.round($(window).height()/2+$(window).scrollTop()-size.h/2),left:Math.round($(window).width()/2+$(window).scrollLeft()-size.w/2),visibility:'hidden'});this.ctrl.overlay.after(this.ctrl.box);if(data!=undefined){this.updateBox(data);this.data=data;} +return this;},updateBox:function(data,fn){var This=this;this.hideCloser();fn=$.isFunction(fn)?fn:function(){};var content=$('div.jq-modal-content',this.ctrl.box);content.empty().append(this.ctrl.loader);var size=this.getBoxSize(data,this.params.width,this.params.height);var top=Math.round($(window).height()/2+$(window).scrollTop()-size.h/2);var left=Math.round($(window).width()/2+$(window).scrollLeft()-size.w/2);this.ctrl.box.css('visibility','visible').animate({top:top<0?0:top,left:left<0?0:left,width:size.w,height:size.h},this.params.speed,function(){This.setContentSize(content,This.params.width,This.params.height);content.empty().append(data).css('opacity',0).fadeTo(This.params.speed,1,function(){fn.call(This,content);});This.showCloser();});},getBox:function(data,css,content_w,content_h){var box=$('
    '+'
    '+'
    ').css($.extend({position:'absolute',top:0,left:0,zIndex:100},css));if(data!=undefined){$('div.jq-modal-content',box).append(data);} +this.setContentSize($('div.jq-modal-content',box),content_w,content_h);return box;},getBoxSize:function(data,content_w,content_h){var box=this.getBox(data,{visibility:'hidden'},content_w,content_h);this.ctrl.overlay.after(box);var size={w:box.width(),h:box.height()};box.remove();box=null;return size;},setContentSize:function(content,w,h){content.css({width:w>0?w:'auto',height:h>0?h:'auto'});},showCloser:function(){if($('div.jq-modal-closer',this.ctrl.box).length>0){$('div.jq-modal-closer',this.ctrl.box).show();return;} +$('div.jq-modal-container',this.ctrl.box).append('');var This=this;var close=$('div.jq-modal-closer a',this.ctrl.box) +close.css({background:'transparent url('+this.params.close_img+') no-repeat'}).click(function(){This.removeOverlay();return false;});if(document.all){close[0].runtimeStyle.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+this.params.close_img+'", sizingMethod="crop")';close[0].runtimeStyle.backgroundImage="none"}},hideCloser:function(){$('div.jq-modal-closer',this.ctrl.box).hide();},addOverlay:function(){var This=this;if(document.all){this.ctrl.hidden=$('select:visible, object:visible, embed:visible').css('visibility','hidden');} +this.ctrl.overlay.css({backgroundColor:'#000',position:'absolute',top:0,left:0,zIndex:90,opacity:this.params.opacity}).appendTo('body').dblclick(function(){This.removeOverlay();});this.resizeOverlay({data:this.ctrl});$(window).bind('resize.modal',this.ctrl,this.resizeOverlay);$(document).bind('keypress.modal',this,this.keyRemove);},resizeOverlay:function(e){e.data.overlay.css({width:$(window).width(),height:$(document).height()});if(e.data.box.parents('body').length>0){var top=Math.round($(window).height()/2+$(window).scrollTop()-e.data.box.height()/2);var left=Math.round($(window).width()/2+$(window).scrollLeft()-e.data.box.width()/2);e.data.box.css({top:top<0?0:top,left:left<0?0:left});}},keyRemove:function(e){if(e.keyCode==27){e.data.removeOverlay();} +return true;},removeOverlay:function(){$(window).unbind('resize.modal');$(document).unbind('keypress');this.params.on_close.apply(this);this.ctrl.overlay.remove();this.ctrl.hidden.css('visibility','visible');this.ctrl.box.remove();this.ctrl.box=$();}};})(jQuery);(function($){$.fn.modalImages=function(params){params=$.extend(this.params,params);var links=new Array();this.each(function(){if($(this).attr('href')==''||$(this).attr('href')==undefined||$(this).attr('href')=='#'){return false;} +var index=links.length;links.push($(this));$(this).click(function(){new $.modalImages(index,links,params);return false;});return true;});return this;};$.modalImages=function(index,links,params){this.links=links;this.modal=new $.modal(null,params);this.showImage(index);};$.modalImages.prototype={params:{prev_txt:'previous',next_txt:'next',prev_img:'prev.png',next_img:'next.png',blank_img:'blank.gif'},showImage:function(index){var This=this;$(document).unbind('keypress.modalImage');if(this.links[index]==undefined){this.modal.removeOverlay();} +var link=this.links[index];var modal=this.modal;var res=$('
    ');res.append('');var thumb=$('img:first',link);if(thumb.length>0&&thumb.attr('title')){res.append(''+thumb.attr('title')+'');}else if(link.attr('title')){res.append(''+link.attr('title')+'');} +if(index!=0){$('prev').appendTo(res);} +if(index+1next').appendTo(res);} +var img=new Image();if(this.modal.ctrl.box.css('visibility')=='visible'){$('div.jq-modal-content',this.modal.ctrl.box).empty().append(this.modal.ctrl.loader);}else{this.modal.updateBox(this.modal.ctrl.loader);} +img.onload=function(){modal.updateBox(res,function(){var Img=$('div.jq-modal-content img',this.ctrl.box);This.navBtnStyle($('a.jq-modal-next',this.ctrl.box),true).css('height',Img.height()).bind('click',index+1,navClick);This.navBtnStyle($('a.jq-modal-prev',this.ctrl.box),false).css('height',Img.height()).bind('click',index-1,navClick);Img.click(function(){This.modal.removeOverlay();});$(document).bind('keypress.modalImage',navKey);});this.onload=function(){};};img.src=link.attr('href');var navClick=function(e){This.showImage(e.data);return false;};var navKey=function(e){var key=String.fromCharCode(e.which).toLowerCase();if((key=='n'||e.keyCode==39)&&index+1').css({border:'none',width:w,height:h});return new $.modal(iframe);};$.fn.modalWeb=function(w,h){this.click(function(){if(this.href!=undefined){$.modalWeb(this.href,w,h);} +return false;});};})(jQuery); \ No newline at end of file diff --git a/admin/js/jquery/jquery.pageTabs.js b/admin/js/jquery/jquery.pageTabs.js new file mode 100644 index 0000000..7c79756 --- /dev/null +++ b/admin/js/jquery/jquery.pageTabs.js @@ -0,0 +1,4 @@ + +jQuery.pageTabs=function(start_tab,settings){return new jQuery._pageTabs(start_tab,settings);};jQuery._pageTabs=function(start_tab,settings){var defaults={className:'multi-part',listClassName:'part-tabs',breakerClassName:'clear'};var index=start_tab?start_tab:0;this.params=jQuery.extend(defaults,settings);this.divs=jQuery('div.'+this.params.className);this.createList();this.showDiv(index);};jQuery._pageTabs.prototype={items:new Array(),createList:function(){if(this.divs.length<=0){return;} +this.block=document.createElement('div');this.block.className=this.params.listClassName;this.list=document.createElement('ul');this.block.appendChild(this.list);var li,a;var This=this;var i=0;jQuery('.'+this.params.className).each(function(){if(this.tagName=="DIV"){li=document.createElement('li');a=document.createElement('a');a.appendChild(document.createTextNode(this.title));this.title='';a.href='#';a.fn=This.showDiv;a.index=this.id||i;a.obj=This;jQuery(a).click(function(){this.fn.call(this.obj,this.index);return false;});li.appendChild(a);This.list.appendChild(li);This.items[i]=li;i++;}else{li=document.createElement('li');li.className=This.params.listClassName+'-link';li.appendChild(this);This.list.appendChild(li);}});this.breaker=document.createElement('br');this.breaker.className=this.params.breakerClassName;jQuery(this.divs.get(0)).before(this.block);jQuery(this.block).after(this.breaker);},showDiv:function(index){var This=this;var i=0;var to_trigger=null;this.divs.each(function(){if((this.id!=''&&this.id==index)||i==index){jQuery(this).show();This.items[i].className=This.params.listClassName+'-active';to_trigger=i;}else{jQuery(this).hide();This.items[i].className='';} +i++;});if(to_trigger!=null){jQuery(this.divs[to_trigger]).onetabload();jQuery(this.divs[to_trigger]).tabload();}}};jQuery.fn.tabload=function(f){this.each(function(){if(f){chainHandler(this,'tabload',f)}else{var h=this.tabload;if(h){h.apply(this);}}});return this;};jQuery.fn.onetabload=function(f){this.each(function(){if(f){chainHandler(this,'onetabload',f);}else{var h=this.onetabload;if(h!=null){h.apply(this);this.onetabload=null;}}});return this;}; \ No newline at end of file diff --git a/admin/js/jsToolBar/jsToolBar.dotclear.js b/admin/js/jsToolBar/jsToolBar.dotclear.js new file mode 100644 index 0000000..86ee951 --- /dev/null +++ b/admin/js/jsToolBar/jsToolBar.dotclear.js @@ -0,0 +1,30 @@ + +jsToolBar.prototype.elements.link.data={};jsToolBar.prototype.elements.link.fncall={};jsToolBar.prototype.elements.link.open_url='popup_link.php';jsToolBar.prototype.elements.link.popup=function(args){window.the_toolbar=this;args=args||'';this.elements.link.data={};var url=this.elements.link.open_url+args;var p_win=window.open(url,'dc_popup','alwaysRaised=yes,dependent=yes,toolbar=yes,height=420,width=380,'+'menubar=no,resizable=yes,scrollbars=yes,status=no');};jsToolBar.prototype.elements.link.fn.wiki=function(){this.elements.link.popup.call(this,'?hreflang='+this.elements.link.default_hreflang);};jsToolBar.prototype.elements.link.fncall.wiki=function(){var data=this.elements.link.data;if(data.href==''){return;} +var etag='|'+data.href;if(data.hreflang){etag+='|'+data.hreflang;} +if(data.content){this.encloseSelection('['+data.content,etag+']');}else{this.encloseSelection('[',etag+']');}};jsToolBar.prototype.elements.link.fn.xhtml=function(){this.elements.link.popup.call(this,'?hreflang='+this.elements.link.default_hreflang);};jsToolBar.prototype.elements.link.fncall.xhtml=function(){var data=this.elements.link.data;if(data.href==''){return;} +var stag='','>').replace('<','<').replace('"','"')+'"';} +res+=' />';if(d.link){res=''+res+'';} +return res;});};jsToolBar.prototype.elements.img.fn.wysiwyg=function(){var src=this.elements.img.prompt.call(this);if(!src){return;} +var img=this.iwin.document.createElement('img');img.src=src;img.setAttribute('alt',this.getSelectedText());this.insertNode(img);};jsToolBar.prototype.elements.img_select.fn.wysiwyg=function(){this.elements.img_select.popup.call(this);};jsToolBar.prototype.elements.img_select.fncall.wysiwyg=function(){var d=this.elements.img_select.data;if(d.src==undefined){return;} +var img=this.iwin.document.createElement('img');img.src=d.src;img.setAttribute('alt',this.getSelectedText());if(d.alignment=='left'){if(img.style.styleFloat!=undefined){img.style.styleFloat='left';}else{img.style.cssFloat='left';} +img.style.marginTop=0;img.style.marginRight='1em';img.style.marginBottom='1em';img.style.marginLeft=0;}else if(d.alignment=='right'){if(img.style.styleFloat!=undefined){img.style.styleFloat='right';}else{img.style.cssFloat='right';} +img.style.marginTop=0;img.style.marginRight=0;img.style.marginBottom='1em';img.style.marginLeft='1em';}else if(d.alignment=='center'){img.style.marginTop=0;img.style.marginRight='auto';img.style.marginBottom=0;img.style.marginLeft='auto';img.style.display='block';} +if(d.description){img.setAttribute('title',d.description);} +if(d.link){var a=this.iwin.document.createElement('a');a.href=d.url;a.appendChild(img);this.insertNode(a);}else{this.insertNode(img);}};jsToolBar.prototype.elements.mp3_insert={fncall:{},data:{}};jsToolBar.prototype.elements.mp3_insert.fncall.wiki=function(){var d=this.elements.mp3_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n///html\n'+d.player+'///\n';});};jsToolBar.prototype.elements.mp3_insert.fncall.xhtml=function(){var d=this.elements.mp3_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n'+d.player+'\n';});};jsToolBar.prototype.elements.mp3_insert.fncall.wysiwyg=function(){return;};jsToolBar.prototype.elements.flv_insert={fncall:{},data:{}};jsToolBar.prototype.elements.flv_insert.fncall.wiki=function(){var d=this.elements.flv_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n///html\n'+d.player+'///\n';});};jsToolBar.prototype.elements.flv_insert.fncall.xhtml=function(){var d=this.elements.flv_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n'+d.player+'\n';});};jsToolBar.prototype.elements.flv_insert.fncall.wysiwyg=function(){return;};jsToolBar.prototype.elements.post_link={type:'button',title:'Link to an entry',fn:{},open_url:'popup_posts.php',data:{},popup:function(){window.the_toolbar=this;this.elements.img_select.data={};var p_win=window.open(this.elements.post_link.open_url,'dc_popup','alwaysRaised=yes,dependent=yes,toolbar=yes,height=500,width=760,'+'menubar=no,resizable=yes,scrollbars=yes,status=no');}};jsToolBar.prototype.elements.post_link.fn.wiki=function(){this.elements.post_link.popup.call(this);};jsToolBar.prototype.elements.post_link.fn.xhtml=function(){this.elements.post_link.popup.call(this);};jsToolBar.prototype.elements.post_link.fn.wysiwyg=function(){this.elements.post_link.popup.call(this);};jsToolBar.prototype.elements.space3={type:'space'}; \ No newline at end of file diff --git a/admin/js/jsToolBar/jsToolBar.js b/admin/js/jsToolBar/jsToolBar.js new file mode 100644 index 0000000..9121528 --- /dev/null +++ b/admin/js/jsToolBar/jsToolBar.js @@ -0,0 +1,28 @@ + +function jsToolBar(textarea){if(!document.createElement){return;} +if(!textarea){return;} +if((typeof(document["selection"])=="undefined")&&(typeof(textarea["setSelectionRange"])=="undefined")){return;} +this.textarea=textarea;this.editor=document.createElement('div');this.editor.className='jstEditor';this.textarea.parentNode.insertBefore(this.editor,this.textarea);this.editor.appendChild(this.textarea);this.toolbar=document.createElement("div");this.toolbar.className='jstElements';this.editor.parentNode.insertBefore(this.toolbar,this.editor);if(this.editor.addEventListener) +{this.handle=document.createElement('div');this.handle.className='jstHandle';var dragStart=this.resizeDragStart;var This=this;this.handle.addEventListener('mousedown',function(event){dragStart.call(This,event);},false);window.addEventListener('unload',function(){var del=This.handle.parentNode.removeChild(This.handle);delete(This.handle);},false);this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);} +this.context=null;this.toolNodes={};};function jsButton(title,fn,scope,className){this.title=title||null;this.fn=fn||function(){};this.scope=scope||null;this.className=className||null;};jsButton.prototype.draw=function(){if(!this.scope)return null;var button=document.createElement('button');button.setAttribute('type','button');if(this.className)button.className=this.className;button.title=this.title;var span=document.createElement('span');span.appendChild(document.createTextNode(this.title));button.appendChild(span);if(this.icon!=undefined){button.style.backgroundImage='url('+this.icon+')';} +if(typeof(this.fn)=='function'){var This=this;button.onclick=function(){try{This.fn.apply(This.scope,arguments)}catch(e){}return false;};} +return button;};function jsSpace(id){this.id=id||null;this.width=null;};jsSpace.prototype.draw=function(){var span=document.createElement('span');if(this.id)span.id=this.id;span.appendChild(document.createTextNode(String.fromCharCode(160)));span.className='jstSpacer';if(this.width)span.style.marginRight=this.width+'px';return span;};function jsCombo(title,options,scope,fn,className){this.title=title||null;this.options=options||null;this.scope=scope||null;this.fn=fn||function(){};this.className=className||null;};jsCombo.prototype.draw=function(){if(!this.scope||!this.options)return null;var select=document.createElement('select');if(this.className)select.className=className;select.title=this.title;for(var o in this.options){var option=document.createElement('option');option.value=o;option.appendChild(document.createTextNode(this.options[o]));select.appendChild(option);} +var This=this;select.onchange=function(){try{This.fn.call(This.scope,this.value);}catch(e){alert(e);} +return false;};return select;};jsToolBar.prototype={base_url:'',mode:'xhtml',elements:{},getMode:function(){return this.mode;},setMode:function(mode){this.mode=mode||'xhtml';},switchMode:function(mode){mode=mode||'xhtml';this.draw(mode);},button:function(toolName){var tool=this.elements[toolName];if(typeof tool.fn[this.mode]!='function')return null;var b=new jsButton(tool.title,tool.fn[this.mode],this,'jstb_'+toolName);if(tool.icon!=undefined){b.icon=tool.icon;} +return b;},space:function(toolName){var tool=new jsSpace(toolName);if(this.elements[toolName].width!==undefined){tool.width=this.elements[toolName].width;} +return tool;},combo:function(toolName){var tool=this.elements[toolName];var length=tool[this.mode].list.length;if(typeof tool[this.mode].fn!='function'||length==0){return null;}else{var options={};for(var i=0;i','');}catch(e){};this.toolNodes.blocks.value='nonebis';}},wiki:{list:['nonebis','h2','h3','h4','h5'],fn:function(opt){switch(opt){case'nonebis':this.textarea.focus();break;case'h2':this.encloseSelection('!!!!');break;case'h3':this.encloseSelection('!!!');break;case'h4':this.encloseSelection('!!');break;case'h5':this.encloseSelection('!');break;} +this.toolNodes.blocks.value='nonebis';}}};jsToolBar.prototype.elements.space0={type:'space'};jsToolBar.prototype.elements.strong={type:'button',title:'Strong emphasis',fn:{wiki:function(){this.singleTag('__')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.em={type:'button',title:'Emphasis',fn:{wiki:function(){this.singleTag("''")},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.ins={type:'button',title:'Inserted',fn:{wiki:function(){this.singleTag('++')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.del={type:'button',title:'Deleted',fn:{wiki:function(){this.singleTag('--')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.quote={type:'button',title:'Inline quote',fn:{wiki:function(){this.singleTag('{{','}}')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.code={type:'button',title:'Code',fn:{wiki:function(){this.singleTag('@@')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.space1={type:'space'};jsToolBar.prototype.elements.br={type:'button',title:'Line break',fn:{wiki:function(){this.encloseSelection("%%%\n",'')},xhtml:function(){this.encloseSelection("
    \n",'')}}};jsToolBar.prototype.elements.space2={type:'space'};jsToolBar.prototype.elements.blockquote={type:'button',title:'Blockquote',fn:{xhtml:function(){this.singleTag('
    ','
    ')},wiki:function(){this.encloseSelection("\n",'',function(str){str=str.replace(/\r/g,'');return'> '+str.replace(/\n/g,"\n> ");});}}};jsToolBar.prototype.elements.pre={type:'button',title:'Preformated text',fn:{wiki:function(){this.singleTag("///\n","\n///")},xhtml:function(){this.singleTag('
    ','
    ')}}};jsToolBar.prototype.elements.ul={type:'button',title:'Unordered list',fn:{wiki:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');return'* '+str.replace(/\n/g,"\n* ");});},xhtml:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');str=str.replace(/\n/g,"\n
  • ");return"
      \n
    • "+str+"
    • \n
    ";});}}};jsToolBar.prototype.elements.ol={type:'button',title:'Ordered list',fn:{wiki:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');return'# '+str.replace(/\n/g,"\n# ");});},xhtml:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');str=str.replace(/\n/g,"
  • \n
  • ");return"
      \n
    1. "+str+"
    2. \n
    ";});}}};jsToolBar.prototype.elements.space3={type:'space'};jsToolBar.prototype.elements.link={type:'button',title:'Link',fn:{},href_prompt:'Please give page URL:',hreflang_prompt:'Language of this page:',default_hreflang:'',prompt:function(href,hreflang){href=href||'';hreflang=hreflang||this.elements.link.default_hreflang;href=window.prompt(this.elements.link.href_prompt,href);if(!href){return false;} +hreflang=window.prompt(this.elements.link.hreflang_prompt,hreflang);return{href:this.stripBaseURL(href),hreflang:hreflang};}};jsToolBar.prototype.elements.link.fn.xhtml=function(){var link=this.elements.link.prompt.call(this);if(link){var stag='';var etag='';this.encloseSelection(stag,etag);}};jsToolBar.prototype.elements.link.fn.wiki=function(){var link=this.elements.link.prompt.call(this);if(link){var stag='[';var etag='|'+link.href;if(link.hreflang){etag=etag+'|'+link.hreflang;} +etag=etag+']';this.encloseSelection(stag,etag);}};jsToolBar.prototype.elements.img={type:'button',title:'External image',src_prompt:'Please give image URL:',fn:{},prompt:function(src){src=src||'';return this.stripBaseURL(window.prompt(this.elements.img.src_prompt,src));}};jsToolBar.prototype.elements.img.fn.xhtml=function(){var src=this.elements.img.prompt.call(this);if(src){this.encloseSelection('','',function(str){if(str){return''+str+'';}else{return'';}});}};jsToolBar.prototype.elements.img.fn.wiki=function(){var src=this.elements.img.prompt.call(this);if(src){this.encloseSelection('','',function(str){if(str){return'(('+src+'|'+str+'))';}else{return'(('+src+'))';}});}}; \ No newline at end of file diff --git a/admin/js/jsToolBar/jsToolBar.wysiwyg.js b/admin/js/jsToolBar/jsToolBar.wysiwyg.js new file mode 100644 index 0000000..cb33ad0 --- /dev/null +++ b/admin/js/jsToolBar/jsToolBar.wysiwyg.js @@ -0,0 +1,47 @@ + +jsToolBar.prototype.can_wwg=(document.designMode!=undefined);jsToolBar.prototype.iframe=null;jsToolBar.prototype.iwin=null;jsToolBar.prototype.ibody=null;jsToolBar.prototype.iframe_css=null;jsToolBar.prototype.drawToolBar=jsToolBar.prototype.draw;jsToolBar.prototype.draw=function(mode){mode=mode||'xhtml';if(this.can_wwg){this.mode='wysiwyg';this.drawToolBar('wysiwyg');this.initWindow();}else{this.drawToolBar(mode);}};jsToolBar.prototype.switchMode=function(mode){mode=mode||'xhtml';if(mode=='xhtml'){this.draw(mode);}else{if(this.wwg_mode){this.syncContents('iframe');} +this.removeEditor();this.textarea.style.display='';this.drawToolBar(mode);}};jsToolBar.prototype.syncContents=function(from){from=from||'textarea';var This=this;if(from=='textarea'){initContent();}else{this.validBlockquote();var html=this.applyHtmlFilters(this.ibody.innerHTML);if(html=='
    '){html='

    ';} +this.textarea.value=html;} +function initContent(){if(!This.iframe.contentWindow.document||!This.iframe.contentWindow.document.body){setTimeout(initContent,1);return;} +This.ibody=This.iframe.contentWindow.document.body;if(This.textarea.value!=''&&This.textarea.value!='

    '){This.ibody.innerHTML=This.applyWysiwygFilters(This.textarea.value);if(This.ibody.createTextRange){var IErange=This.ibody.createTextRange();IErange.execCommand("SelectAll");IErange.collapse();IErange.select();}}else if(window.navigator.product!=undefined&&window.navigator.product=='Gecko'){This.ibody.innerHTML='


    ';}else{var idoc=This.iwin.document;var para=idoc.createElement('p');para.appendChild(idoc.createTextNode(''));while(idoc.body.hasChildNodes()){idoc.body.removeChild(idoc.body.lastChild);} +idoc.body.appendChild(para);}}};jsToolBar.prototype.htmlFilters={tagsoup:function(str){return this.tagsoup2xhtml(str);}};jsToolBar.prototype.applyHtmlFilters=function(str){for(var fn in this.htmlFilters){str=this.htmlFilters[fn].call(this,str);} +return str;};jsToolBar.prototype.wysiwygFilters={};jsToolBar.prototype.applyWysiwygFilters=function(str){for(var fn in this.wysiwygFilters){str=this.wysiwygFilters[fn].call(this,str);} +return str;};jsToolBar.prototype.switchEdit=function(){if(this.wwg_mode){this.textarea.style.display='';this.iframe.style.display='none';this.syncContents('iframe');this.drawToolBar('xhtml');this.wwg_mode=false;this.focusEditor();}else{this.iframe.style.display='';this.textarea.style.display='none';this.syncContents('textarea');this.drawToolBar('wysiwyg');this.wwg_mode=true;this.focusEditor();} +this.setSwitcher();};jsToolBar.prototype.initWindow=function(){var This=this;this.iframe=document.createElement('iframe');this.textarea.parentNode.insertBefore(this.iframe,this.textarea.nextSibling);this.switcher=document.createElement('ul');this.switcher.className='jstSwitcher';this.editor.appendChild(this.switcher);this.iframe.height=this.textarea.offsetHeight+0;this.iframe.width=this.textarea.offsetWidth+0;if(this.textarea.tabIndex!=undefined){this.iframe.tabIndex=this.textarea.tabIndex;} +function initIframe(){var doc=This.iframe.contentWindow.document;if(!doc){setTimeout(initIframe,1);return false;} +doc.open();var html='\n'+'\n'+'\n'+ +(This.base_url!=''?'':'')+'\n'+'\n'+'\n'+'';doc.write(html);doc.close();if(document.all){doc.designMode='on';} +This.iwin=This.iframe.contentWindow;This.syncContents('textarea');if(This.wwg_mode==undefined){This.wwg_mode=true;} +if(This.wwg_mode){This.textarea.style.display='none';}else{This.iframe.style.display='none';} +if(This.textarea.form){chainHandler(This.textarea.form,'onsubmit',function(){if(This.wwg_mode){This.syncContents('iframe');}});} +for(var evt in This.iwinEvents){var event=This.iwinEvents[evt];This.addIwinEvent(This.iframe.contentWindow.document,event.type,event.fn,This);} +This.setSwitcher();setTimeout(function(){This.focusEditor();},1);return true;} +initIframe();};jsToolBar.prototype.addIwinEvent=function(target,type,fn,scope){var myFn=function(e){fn.call(scope,e)};addEvent(target,type,myFn,true);addEvent(scope.iwin,'unload',function(){removeEvent(target,type,myFn,true);},true);};jsToolBar.prototype.iwinEvents={block1:{type:'mouseup',fn:function(){this.adjustBlockLevelCombo()}},block2:{type:'keyup',fn:function(){this.adjustBlockLevelCombo()}}};jsToolBar.prototype.switcher_visual_title='visual';jsToolBar.prototype.switcher_source_title='source';jsToolBar.prototype.setSwitcher=function(){while(this.switcher.hasChildNodes()){this.switcher.removeChild(this.switcher.firstChild);} +var This=this;function setLink(title,link){var li=document.createElement('li');if(link){var a=document.createElement('a');a.href='#';a.editor=This;a.onclick=function(){this.editor.switchEdit();return false;};a.appendChild(document.createTextNode(title));}else{li.className='jstSwitcherCurrent';a=document.createTextNode(title);} +li.appendChild(a);This.switcher.appendChild(li);} +setLink(this.switcher_visual_title,!this.wwg_mode);setLink(this.switcher_source_title,this.wwg_mode);};jsToolBar.prototype.removeEditor=function(){if(this.iframe!=null){this.iframe.parentNode.removeChild(this.iframe);this.iframe=null;} +if(this.switcher!=undefined&&this.switcher.parentNode!=undefined){this.switcher.parentNode.removeChild(this.switcher);}};jsToolBar.prototype.focusEditor=function(){if(this.wwg_mode){try{this.iwin.document.designMode='on';}catch(e){};var This=this;setTimeout(function(){This.iframe.contentWindow.focus()},1);}else{this.textarea.focus();}};jsToolBar.prototype.resizeSetStartH=function(){if(this.wwg_mode&&this.iframe!=undefined){this.dragStartH=this.iframe.offsetHeight;return;} +this.dragStartH=this.textarea.offsetHeight+0;};jsToolBar.prototype.resizeDragMove=function(event){var new_height=(this.dragStartH+event.clientY-this.dragStartY)+'px';if(this.iframe!=undefined){this.iframe.style.height=new_height;} +this.textarea.style.height=new_height;};jsToolBar.prototype.insertNode=function(node){var range;if(this.iwin.getSelection){var sel=this.iwin.getSelection();range=sel.getRangeAt(0);sel.removeAllRanges();range.deleteContents();range.insertNode(node);range.selectNodeContents(node);range.setEndAfter(node);if(range.endContainer.childNodes.length>range.endOffset&&range.endContainer.nodeType!=Node.TEXT_NODE){range.setEnd(range.endContainer.childNodes[range.endOffset],0);}else{range.setEnd(range.endContainer.childNodes[0]);} +sel.addRange(range);sel.collapseToEnd();}else{var p=this.iwin.document.createElement('div');p.appendChild(node);range=this.iwin.document.selection.createRange();range.execCommand('delete');range.pasteHTML(p.innerHTML);range.collapse(false);range.select();} +this.iwin.focus();};jsToolBar.prototype.getSelectedNode=function(){if(this.iwin.getSelection){var sel=this.iwin.getSelection();var range=sel.getRangeAt(0);var content=range.cloneContents();}else{var sel=this.iwin.document.selection;var d=this.iwin.document.createElement('div');d.innerHTML=sel.createRange().htmlText;var content=this.iwin.document.createDocumentFragment();for(var i=0;i/gim,''],[/[\w\W]*?<\/style>/gim,''],[/<\/?font[\w\W]*?>/gim,''],[/<(\/?)(B|b|STRONG)([\s>\/])/g,"<$1strong$3"],[/<(\/?)(I|i|EM)([\s>\/])/g,"<$1em$3"],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/<(\/?)U([\s>\/])/gi,"<$1ins$2"],[/<(\/?)STRIKE([\s>\/])/gi,"<$1del$2"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/strong>/gm,"$1"],[/<([a-z]+) style="font-weight: normal;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="font-weight: bold;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="font-style: italic;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="text-decoration: underline;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="text-decoration: line-through;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="text-decoration: underline line-through;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: italic; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: italic; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline; ?){3}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: line-through; ?){3}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline line-through; ?){3}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<\/(strong|em|ins|del|q|code)>(\s*?)<\1>/gim,"$2"],[/<(br|BR)>/g,"
    "],[/([^\s])\/>/g,"$1 />"],[/
    \s*<\/(h1|h2|h3|h4|h5|h6|ul|ol|li|p|blockquote|div)/gi,"([^\n\u000B\r\f])/gi,"\n$2"],[/<(hr|HR)( style="width: 100%; height: 2px;")?>/g,"
    "]);jsToolBar.prototype.tagsoup2xhtml=function(html){for(var reg in this.simpleCleanRegex){html=html.replace(this.simpleCleanRegex[reg][0],this.simpleCleanRegex[reg][1]);} +while(/(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/.test(html)){html=html.replace(/(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/g,"");} +html=html.replace(/<(\/?)([A-Z0-9]+)/g,function(match0,match1,match2){return"<"+match1+match2.toLowerCase();});var myRegexp=/<[^>]+((\s+\w+\s*=\s*)([^"'][\w~@+$,%\/:.#?=&;!*()-]*))[^>]*?>/;while(myRegexp.test(html)){html=html.replace(myRegexp,function(str,val1,val2,val3){var tamponRegex=new RegExp(regexpEscape(val1));return str.replace(tamponRegex,val2+'"'+val3+'"');})} +while(/(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/.test(html)){html=html.replace(/(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/gi,"$1"+"0$4");} +html=html.replace(/\r\n/g,"\n");html=html.replace(/^\s+/gm,'');html=html.replace(/\s+$/gm,'');return html;};jsToolBar.prototype.validBlockquote=function(){var blockElts=['address','blockquote','dl','div','fieldset','form','h1','h2','h3','h4','h5','h6','hr','ol','p','pre','table','ul'];var BQs=this.iwin.document.getElementsByTagName('blockquote');var bqChilds;for(var bq=0;bq=0;i--){if(bqChilds[i].nodeType==1&&arrayIndexOf(blockElts,bqChilds[i].tagName.toLowerCase())>=0) +{if(frag.childNodes.length>0){var p=this.iwin.document.createElement('p');p.appendChild(frag);BQs[bq].replaceChild(p,bqChilds[i+1]);frag=this.iwin.document.createDocumentFragment();}}else{if(frag.childNodes.length>0)BQs[bq].removeChild(bqChilds[i+1]);frag.insertBefore(bqChilds[i].cloneNode(true),frag.firstChild);}} +if(frag.childNodes.length>0){var p=this.iwin.document.createElement('p');p.appendChild(frag);BQs[bq].replaceChild(p,bqChilds[0]);}}};jsToolBar.prototype.removeFormatRegexp=new Array([/(<[a-z][^>]*)margin\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-bottom\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-left\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-right\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-top\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-bottom\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-left\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-right\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-top\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-family\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-size\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-style\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-variant\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-weight\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)color\s*:[^;]*;/mg,"$1"]);jsToolBar.prototype.removeTextFormating=function(html){for(var reg in this.removeFormatRegexp){html=html.replace(this.removeFormatRegexp[reg][0],this.removeFormatRegexp[reg][1]);} +html=this.tagsoup2xhtml(html);html=html.replace(/style="\s*?"/mgi,'');return html;};jsToolBar.prototype.elements.blocks.wysiwyg={list:['none','p','h1','h2','h3','h4','h5','h6'],fn:function(opt){if(opt=='none'){var blockLevel=this.getBlockLevel();if(blockLevel!==null){this.replaceNodeByContent(blockLevel);} +this.iwin.focus();}else{try{this.iwin.document.execCommand('formatblock',false,'<'+opt+'>');}catch(e){};this.iwin.focus();}}};jsToolBar.prototype.elements.strong.fn.wysiwyg=function(){this.iwin.document.execCommand('bold',false,null);this.iwin.focus();};jsToolBar.prototype.elements.em.fn.wysiwyg=function(){this.iwin.document.execCommand('italic',false,null);this.iwin.focus();};jsToolBar.prototype.elements.ins.fn.wysiwyg=function(){this.iwin.document.execCommand('underline',false,null);this.iwin.focus();};jsToolBar.prototype.elements.del.fn.wysiwyg=function(){this.iwin.document.execCommand('strikethrough',false,null);this.iwin.focus();};jsToolBar.prototype.elements.quote.fn.wysiwyg=function(){var n=this.getSelectedNode();var q=this.iwin.document.createElement('q');q.appendChild(n);this.insertNode(q);};jsToolBar.prototype.elements.code.fn.wysiwyg=function(){var n=this.getSelectedNode();var code=this.iwin.document.createElement('code');code.appendChild(n);this.insertNode(code);};jsToolBar.prototype.elements.br.fn.wysiwyg=function(){var n=this.iwin.document.createElement('br');this.insertNode(n);};jsToolBar.prototype.elements.blockquote.fn.wysiwyg=function(){var n=this.getSelectedNode();var q=this.iwin.document.createElement('blockquote');q.appendChild(n);this.insertNode(q);};jsToolBar.prototype.elements.pre.fn.wysiwyg=function(){this.iwin.document.execCommand('formatblock',false,'
    ');this.iwin.focus();};jsToolBar.prototype.elements.ul.fn.wysiwyg=function(){this.iwin.document.execCommand('insertunorderedlist',false,null);this.iwin.focus();};jsToolBar.prototype.elements.ol.fn.wysiwyg=function(){this.iwin.document.execCommand('insertorderedlist',false,null);this.iwin.focus();};jsToolBar.prototype.elements.link.fn.wysiwyg=function(){var href,hreflang;var range,commonAncestorContainer;if(this.iwin.getSelection){var selection=this.iwin.getSelection();range=selection.getRangeAt(0);commonAncestorContainer=range.commonAncestorContainer;while(commonAncestorContainer.nodeType!=1){commonAncestorContainer=commonAncestorContainer.parentNode;}}else{range=this.iwin.document.selection.createRange();commonAncestorContainer=range.parentElement();}
    +var ancestorTagName=commonAncestorContainer.tagName.toLowerCase();while(ancestorTagName!='a'&&ancestorTagName!='body'){commonAncestorContainer=commonAncestorContainer.parentNode;ancestorTagName=commonAncestorContainer.tagName.toLowerCase();}
    +if(ancestorTagName=='a'){href=commonAncestorContainer.href||'';hreflang=commonAncestorContainer.hreflang||'';}
    +href=window.prompt(this.elements.link.href_prompt,href);if(ancestorTagName=='a'&&href==''){this.replaceNodeByContent(commonAncestorContainer);}
    +if(!href)return;hreflang=window.prompt(this.elements.link.hreflang_prompt,hreflang);if(ancestorTagName=='a'&&href){commonAncestorContainer.setAttribute('href',href);if(hreflang){commonAncestorContainer.setAttribute('hreflang',hreflang);}else{commonAncestorContainer.removeAttribute('hreflang');}
    +return;}
    +var n=this.getSelectedNode();var a=this.iwin.document.createElement('a');a.href=href;if(hreflang)a.setAttribute('hreflang',hreflang);a.appendChild(n);this.insertNode(a);};jsToolBar.prototype.elements.removeFormat={type:'button',title:'Remove text formating',fn:{}};jsToolBar.prototype.elements.removeFormat.disabled=!jsToolBar.prototype.can_wwg;jsToolBar.prototype.elements.removeFormat.fn.xhtml=function(){var html=this.textarea.value;html=this.removeTextFormating(html);this.textarea.value=html;};jsToolBar.prototype.elements.removeFormat.fn.wysiwyg=function(){var html=this.iwin.document.body.innerHTML;html=this.removeTextFormating(html);this.iwin.document.body.innerHTML=html;};function arrayIndexOf(aArray,aValue){if(typeof Array.indexOf=='function'){return aArray.indexOf(aValue);}else{var index=-1;var l=aArray.length;for(var i=0;i'+player+'
  • ';} +tb.elements.mp3_insert.data.player=player.replace(/>/g,'>\n');tb.elements.mp3_insert.fncall[tb.mode].call(tb);} +else if(type=='flv') +{var oplayer=$('
    '+$('#public_player').val()+'
    ');var flashvars=$("[name=FlashVars]",oplayer).val();var align=$('input[name="alignment"]:checked',insert_form).val();var title=insert_form.elements.title.value;if(title){flashvars='title='+encodeURI(title)+'&'+flashvars;} +$('object',oplayer).attr('width',$('#video_w').val());$('object',oplayer).attr('height',$('#video_h').val());flashvars=flashvars.replace(/(width=\d*)/,'width='+$('#video_w').val());flashvars=flashvars.replace(/(height=\d*)/,'height='+$('#video_h').val());$("[name=FlashVars]",oplayer).val(flashvars);var player=oplayer.html();if(align!=undefined&&align!='none'){player='
    '+player+'
    ';} +tb.elements.flv_insert.data.player=player.replace(/>/g,'>\n');tb.elements.flv_insert.fncall[tb.mode].call(tb);} +else +{tb.elements.link.data.href=tb.stripBaseURL(insert_form.elements.url.value);tb.elements.link.data.content=insert_form.elements.title.value;tb.elements.link.fncall[tb.mode].call(tb);}};function playerFormat(s){s=s.replace(/</g,'<');s=s.replace(/>/g,'>\n');s=s.replace(/&/g,'&');return s;};}); \ No newline at end of file diff --git a/admin/js/jsToolBar/popup_posts.js b/admin/js/jsToolBar/popup_posts.js new file mode 100644 index 0000000..0fbb77d --- /dev/null +++ b/admin/js/jsToolBar/popup_posts.js @@ -0,0 +1,2 @@ + +$(function(){$('#link-insert-cancel').click(function(){window.close();});$('#form-entries tr>td.maximal>a').click(function(){var tb=window.opener.the_toolbar;var data=tb.elements.link.data;data.href=tb.stripBaseURL($(this).attr('title'));tb.elements.link.fncall[tb.mode].call(tb);window.close();});}); \ No newline at end of file diff --git a/admin/js/tool-man/cookies.js b/admin/js/tool-man/cookies.js new file mode 100644 index 0000000..4e4f03d --- /dev/null +++ b/admin/js/tool-man/cookies.js @@ -0,0 +1,11 @@ + +ToolMan._cookieOven={set:function(name,value,expirationInDays){if(expirationInDays){var date=new Date() +date.setTime(date.getTime()+(expirationInDays*24*60*60*1000)) +var expires="; expires="+date.toGMTString()}else{var expires=""} +document.cookie=name+"="+value+expires+"; path=/"},get:function(name){var namePattern=name+"=" +var cookies=document.cookie.split(';') +for(var i=0,n=cookies.length;i=0){return this.create(document.body.scrollLeft,document.body.scrollTop)}else{return this.create(0,0)}},clientSize:function(){if(window.innerHeight>=0){return this.create(window.innerWidth,window.innerHeight)}else if(document.documentElement){return this.create(document.documentElement.clientWidth,document.documentElement.clientHeight)}else if(document.body.clientHeight>=0){return this.create(document.body.clientWidth,document.body.clientHeight)}else{return this.create(0,0)}},mousePosition:function(event){event=ToolMan.events().fix(event) +return this.create(event.clientX,event.clientY)},mouseOffset:function(event){event=ToolMan.events().fix(event) +if(event.pageX>=0||event.pageX<0){return this.create(event.pageX,event.pageY)}else if(event.clientX>=0||event.clientX<0){return this.mousePosition(event).plus(this.scrollOffset())}},_size:function(element){return this.create(element.offsetWidth,element.offsetHeight)},_offset:function(element){return this.create(element.offsetLeft,element.offsetTop)}} +function _ToolManCoordinate(factory,x,y){this.factory=factory +this.x=isNaN(x)?0:x +this.y=isNaN(y)?0:y} +_ToolManCoordinate.prototype={toString:function(){return"("+this.x+","+this.y+")"},plus:function(that){return this.factory.create(this.x+that.x,this.y+that.y)},minus:function(that){return this.factory.create(this.x-that.x,this.y-that.y)},min:function(that){return this.factory.create(Math.min(this.x,that.x),Math.min(this.y,that.y))},max:function(that){return this.factory.create(Math.max(this.x,that.x),Math.max(this.y,that.y))},constrainTo:function(one,two){var min=one.min(two) +var max=one.max(two) +return this.max(min).min(max)},distance:function(that){return Math.sqrt(Math.pow(this.x-that.x,2)+Math.pow(this.y-that.y,2))},reposition:function(element){element.style["top"]=this.y+"px" +element.style["left"]=this.x+"px"}} \ No newline at end of file diff --git a/admin/js/tool-man/core.js b/admin/js/tool-man/core.js new file mode 100644 index 0000000..7a46341 --- /dev/null +++ b/admin/js/tool-man/core.js @@ -0,0 +1,35 @@ + +var ToolMan={events:function(){if(!ToolMan._eventsFactory)throw"ToolMan Events module isn't loaded";return ToolMan._eventsFactory},css:function(){if(!ToolMan._cssFactory)throw"ToolMan CSS module isn't loaded";return ToolMan._cssFactory},coordinates:function(){if(!ToolMan._coordinatesFactory)throw"ToolMan Coordinates module isn't loaded";return ToolMan._coordinatesFactory},drag:function(){if(!ToolMan._dragFactory)throw"ToolMan Drag module isn't loaded";return ToolMan._dragFactory},dragsort:function(){if(!ToolMan._dragsortFactory)throw"ToolMan DragSort module isn't loaded";return ToolMan._dragsortFactory},helpers:function(){return ToolMan._helpers},cookies:function(){if(!ToolMan._cookieOven)throw"ToolMan Cookie module isn't loaded";return ToolMan._cookieOven},junkdrawer:function(){return ToolMan._junkdrawer}} +ToolMan._helpers={map:function(array,func){for(var i=0,n=array.length;i0)return identifier;identifier=trim(item.getAttribute("itemID")) +if(identifier!=null&&identifier.length>0)return identifier;return trim(item.innerHTML)},_itemsByID:function(list){var array=new Array() +var items=list.getElementsByTagName('li') +for(var i=0,n=items.length;isetCacheDir(DC_TPL_CACHE); +$feed_reader->setTimeout(5); +$feed_reader->setUserAgent('Dotclear - http://www.dotclear.org/'); +try { + $dc_langs = $feed_reader->parse(sprintf(DC_L10N_UPDATE_URL,DC_VERSION)); + if ($dc_langs !== false) { + $dc_langs = $dc_langs->items; + } +} catch (Exception $e) {} + +# Delete a language pack +if ($is_writable && !empty($_POST['delete']) && !empty($_POST['locale_id'])) +{ + try + { + $locale_id = $_POST['locale_id']; + if (!isset($iso_codes[$locale_id]) || !is_dir(DC_L10N_ROOT.'/'.$locale_id)) { + throw new Exception(__('No such installed language')); + } + + if ($locale_id == 'en') { + throw new Exception(__("You can't remove English language.")); + } + + if (!files::deltree(DC_L10N_ROOT.'/'.$locale_id)) { + throw new Exception(__('Permissions to delete language denied.')); + } + + http::redirect('langs.php?removed=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Download a language pack +if ($is_writable && !empty($_POST['pkg_url'])) +{ + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + $url = html::escapeHTML($_POST['pkg_url']); + $dest = DC_L10N_ROOT.'/'.basename($url); + if (!preg_match('#^http://[^.]+\.dotclear\.net/.*\.zip$#',$url)) { + throw new Exception(__('Invalid language file URL.')); + } + + $client = netHttp::initClient($url,$path); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + + try { + $ret_code = dc_lang_install($dest); + } catch (Exception $e) { + @unlink($dest); + throw $e; + } + + @unlink($dest); + http::redirect('langs.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Upload a language pack +if ($is_writable && !empty($_POST['upload_pkg'])) +{ + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + files::uploadStatus($_FILES['pkg_file']); + $dest = DC_L10N_ROOT.'/'.$_FILES['pkg_file']['name']; + if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'],$dest)) { + throw new Exception(__('Unable to move uploaded file.')); + } + + try { + $ret_code = dc_lang_install($dest); + } catch (Exception $e) { + @unlink($dest); + throw $e; + } + + @unlink($dest); + http::redirect('langs.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +dcPage::open(__('Languages management'), + dcPage::jsLoad('js/_langs.js') +); + +echo +'

    '.__('Languages management').'

    '; + +if (!empty($_GET['removed'])) { + echo '

    '.__('Language has been successfully deleted.').'

    '; +} + +if (!empty($_GET['added'])) { + echo '

    '. + ($_GET['added'] == 2 ? __('Language has been successfully upgraded') : __('Language has been successfully installed.')). + '

    '; +} + +echo +'

    '.__('Here you can install, upgrade or remove languages for your Dotclear '. +'installation.').'

    '. +'

    '.sprintf(__('You can change your user language in your preferences or '. +'change your blog\'s main language in your blog settings.'), +'preferences.php','blog_pref.php').'

    '; + +echo +'

    '.__('Installed languages').'

    '; + +$locales_content = scandir(DC_L10N_ROOT); +$tmp = array(); +foreach ($locales_content as $v) { + $c = ($v == '.' || $v == '..' || $v == 'en' || !is_dir(DC_L10N_ROOT.'/'.$v) || !isset($iso_codes[$v])); + + if (!$c) { + $tmp[$v] = DC_L10N_ROOT.'/'.$v; + } +} +$locales_content = $tmp; + +if (empty($locales_content)) +{ + echo '

    '.__('No additional language is installed.').'

    '; +} +else +{ + echo + ''. + ''. + ''. + ''; + + foreach ($locales_content as $k => $v) + { + $is_deletable = $is_writable && is_writable($v); + + echo + ''. + ''. + ''; + } + echo '
    '.__('Language').''.__('Action').'
    ('.$k.') '. + ''.html::escapeHTML($iso_codes[$k]).''; + + if ($is_deletable) + { + echo + '
    '. + '
    '. + $core->formNonce(). + form::hidden(array('locale_id'),html::escapeHTML($k)). + ' '. + '
    '. + '
    '; + } + + echo '
    '; +} + +echo '

    '.__('Install or upgrade languages').'

    '; + +if (!$is_writable) { + echo '

    '.sprintf(__('You can install or remove a language by adding or '. + 'removing the relevant directory in your %s folder.'),'locales').'

    '; +} + +if (!empty($dc_langs) && $is_writable) +{ + $dc_langs_combo = array(); + foreach ($dc_langs as $k => $v) { + if ($v->link && isset($iso_codes[$v->title])) { + $dc_langs_combo[html::escapeHTML('('.$v->title.') '.$iso_codes[$v->title])] = html::escapeHTML($v->link); + } + } + + echo + '
    '. + '
    '. + ''.__('Available languages').''. + '

    '.sprintf(__('You can download and install a additional language directly from Dotclear.net. '. + 'Proposed languages are based on your version: %s.'),''.DC_VERSION.'').'

    '. + '

    '. + '

    '. + ''. + $core->formNonce(). + '
    '. + '
    '; +} + +if ($is_writable) +{ + # 'Upload language pack' form + echo + '
    '. + '
    '. + ''.__('Upload a zip file').''. + '

    '.__('You can install languages by uploading zip files.').'

    '. + '

    '. + '

    '. + ''. + $core->formNonce(). + '
    '. + '
    '; +} + +dcPage::close(); + + +# Language installation function +function dc_lang_install($file) +{ + $zip = new fileUnzip($file); + $zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.DS_Store|Thumbs\.db)(/|$)#'); + + if (!preg_match('/^[a-z]{2,3}(-[a-z]{2})?$/',$zip->getRootDir())) { + throw new Exception(__('Invalid language zip file.')); + } + + if ($zip->isEmpty() || !$zip->hasFile($zip->getRootDir().'/main.po')) { + throw new Exception(__('The zip file does not appear to be a valid Dotclear language pack.')); + } + + + $target = dirname($file); + $destination = $target.'/'.$zip->getRootDir(); + $res = 1; + + if (is_dir($destination)) { + if (!files::deltree($destination)) { + throw new Exception(__('An error occurred during language upgrade.')); + } + $res = 2; + } + + $zip->unzipAll($target); + return $res; +} +?> \ No newline at end of file diff --git a/admin/media.php b/admin/media.php new file mode 100644 index 0000000..906a172 --- /dev/null +++ b/admin/media.php @@ -0,0 +1,449 @@ +auth->check('media,media_admin',$core->blog->id)) { + throw new Exception('Permission denied.'); + } + + $d = isset($_POST['d']) ? $_POST['d'] : null; + $core->media = new dcMedia($core); + $core->media->chdir($d); + $core->media->getDir(); + $dir =& $core->media->dir; + + if (empty($_FILES['Filedata'])) { + throw new Exception('No file to upload.'); + } + + files::uploadStatus($_FILES['Filedata']); + $core->media->uploadFile($_FILES['Filedata']['tmp_name'],$_FILES['Filedata']['name']); + + echo 'ok'; + } + catch (Exception $e) { + echo __('Error:').' '.__($e->getMessage()); + } + exit; +} + + +/* HTML page +-------------------------------------------------------- */ +require dirname(__FILE__).'/../inc/admin/prepend.php'; + +dcPage::check('media,media_admin'); + +$post_id = !empty($_GET['post_id']) ? (integer) $_GET['post_id'] : null; +if ($post_id) { + $post = $core->blog->getPosts(array('post_id'=>$post_id,'post_type'=>'')); + if ($post->isEmpty()) { + $post_id = null; + } + $post_title = $post->post_title; + $post_type = $post->post_type; + unset($post); +} + +$d = isset($_REQUEST['d']) ? $_REQUEST['d'] : null; +$dir = null; + +$page = !empty($_GET['page']) ? $_GET['page'] : 1; +$nb_per_page = 30; + +# We are on home not comming from media manager +if ($d === null && isset($_SESSION['media_manager_dir'])) { + # We get session information + $d = $_SESSION['media_manager_dir']; +} + +if (!isset($_GET['page']) && isset($_SESSION['media_manager_page'])) { + $page = $_SESSION['media_manager_page']; +} + +# We set session information about directory and page +if ($d) { + $_SESSION['media_manager_dir'] = $d; +} else { + unset($_SESSION['media_manager_dir']); +} +if ($page != 1) { + $_SESSION['media_manager_page'] = $page; +} else { + unset($_SESSION['media_manager_page']); +} + +# Sort combo +$sort_combo = array( + __('By names, ascendant') => 'name-asc', + __('By names, descendant') => 'name-desc', + __('By dates, ascendant') => 'date-asc', + __('By dates, descendant') => 'date-desc' +); + +if (!empty($_GET['file_sort']) && in_array($_GET['file_sort'],$sort_combo)) { + $_SESSION['media_file_sort'] = $_GET['file_sort']; +} +$file_sort = !empty($_SESSION['media_file_sort']) ? $_SESSION['media_file_sort'] : null; + +$popup = (integer) !empty($_GET['popup']); + +$page_url = 'media.php?popup='.$popup.'&post_id='.$post_id; + +if ($popup) { + $open_f = array('dcPage','openPopup'); + $close_f = array('dcPage','closePopup'); +} else { + $open_f = array('dcPage','open'); + $close_f = create_function('',"dcPage::helpBlock('core_media'); dcPage::close();"); +} + +$core_media_writable = false; +try { + $core->media = new dcMedia($core); + if ($file_sort) { + $core->media->setFileSort($file_sort); + } + $core->media->chdir($d); + $core->media->getDir(); + $core_media_writable = $core->media->writable(); + $dir =& $core->media->dir; + if (!$core_media_writable) { + throw new Exception('you do not have sufficient permissions to write to this folder: '); + } +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +# Zip download +if (!empty($_GET['zipdl']) && $core->auth->check('media_admin',$core->blog->id)) +{ + try + { + @set_time_limit(300); + $fp = fopen('php://output','wb'); + $zip = new fileZip($fp); + $zip->addExclusion('#(^|/).(.*?)_(m|s|sq|t).jpg$#'); + $zip->addDirectory($core->media->root.'/'.$d,'',true); + + header('Content-Disposition: attachment;filename='.($d ? $d : 'media').'.zip'); + header('Content-Type: application/x-zip'); + $zip->write(); + unset($zip); + exit; + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# New directory +if ($dir && !empty($_POST['newdir'])) +{ + try { + $core->media->makeDir($_POST['newdir']); + http::redirect($page_url.'&d='.rawurlencode($d).'&mkdok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Adding a file +if ($dir && !empty($_FILES['upfile'])) +{ + try + { + files::uploadStatus($_FILES['upfile']); + + $f_title = (isset($_POST['upfiletitle']) ? $_POST['upfiletitle'] : ''); + $f_private = (isset($_POST['upfilepriv']) ? $_POST['upfilepriv'] : false); + + $core->media->uploadFile($_FILES['upfile']['tmp_name'],$_FILES['upfile']['name'],$f_title,$f_private); + http::redirect($page_url.'&d='.rawurlencode($d).'&upok=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +# Removing item +if ($dir && !empty($_POST['rmyes']) && !empty($_POST['remove'])) +{ + $_POST['remove'] = rawurldecode($_POST['remove']); + + try { + $core->media->removeItem($_POST['remove']); + http::redirect($page_url.'&d='.rawurlencode($d).'&rmfok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Rebuild directory +if ($dir && $core->auth->isSuperAdmin() && !empty($_POST['rebuild'])) +{ + try { + $core->media->rebuild($d); + http::redirect($page_url.'&d='.rawurlencode($d).'&rebuildok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + + +# DISPLAY confirm page for rmdir & rmfile +if ($dir && !empty($_GET['remove'])) +{ + call_user_func($open_f,__('Media manager')); + + echo '

    '.html::escapeHTML($core->blog->name).' › '.__('Media manager').' › '.__('confirm removal').'

    '; + + echo + '
    '. + '

    '.sprintf(__('Are you sure you want to remove %s?'), + html::escapeHTML($_GET['remove'])).'

    '. + '

    '. + '   '. + form::hidden('d',$d). + $core->formNonce(). + form::hidden('remove',html::escapeHTML($_GET['remove'])).'

    '. + '
    '; + + call_user_func($close_f); + exit; +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +call_user_func($open_f,__('Media manager'), + dcPage::jsLoad('js/_media.js'). + ($core_media_writable ? dcPage::jsCandyUpload(array('d='.$d)) : '')); + +if (!empty($_GET['mkdok'])) { + echo '

    '.__('Directory has been successfully created.').'

    '; +} + +if (!empty($_GET['upok'])) { + echo '

    '.__('Files have been successfully uploaded.').'

    '; +} + +if (!empty($_GET['rmfok'])) { + echo '

    '.__('File has been successfully removed.').'

    '; +} + +if (!empty($_GET['rmdok'])) { + echo '

    '.__('Directory has been successfully removed.').'

    '; +} + +if (!empty($_GET['rebuildok'])) { + echo '

    '.__('Directory has been successfully rebuilt.').'

    '; +} + +if (!empty($_GET['unzipok'])) { + echo '

    '.__('Zip file has been successfully extracted.').'

    '; +} + +echo '

    '.html::escapeHTML($core->blog->name).' › '.__('Media manager').''. +' / '.$core->media->breadCrumb(html::escapeURL($page_url).'&d=%s').'

    '; + +if (!$dir) { + call_user_func($close_f); + exit; +} + +if ($post_id) { + echo '

    '.sprintf(__('Choose a file to attach to entry %s by clicking on %s.'), + ''.html::escapeHTML($post_title).'', + ''.__('Attach this file to entry').'').'

    '; +} +if ($popup) { + echo '

    '.sprintf(__('Choose a file to insert into entry by clicking on %s.'), + ''.__('Attach this file to entry').'').'

    '; +} + + +$items = array_values(array_merge($dir['dirs'],$dir['files'])); +if (count($items) == 0) +{ + echo '

    '.__('No file.').'

    '; +} +else +{ + $pager = new pager($page,count($items),$nb_per_page,10); + + echo + '
    '. + '

    '. + form::hidden(array('popup'),$popup). + form::hidden(array('post_id'),$post_id). + '

    '. + '
    '. + + '
    '. + '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + + for ($i=$pager->index_start, $j=0; $i<=$pager->index_end; $i++, $j++) + { + echo mediaItemLine($items[$i],$j); + } + + echo + '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '. + '
    '; +} + +if ($core_media_writable) +{ + echo '
    '; + + echo + '

    '.__('Add files').'

    '. + '
    '. + '
    '.form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE). + $core->formNonce().'
    '. + '
    '. + '

    '. + '

    '. + '

    '. + '

    '. + form::hidden(array('d'),$d).'

    '. + '
    '. + '
    '. + '

    '.__('Please take care to publish media that you own and that are not protected by copyright.').'

    '. + '
    '; + + echo + '

    '.__('New directory').'

    '. + '
    '. + '
    '. + $core->formNonce(). + '

    '. + '

    '. + form::hidden(array('d'),html::escapeHTML($d)).'

    '. + '
    '. + '
    '; + + echo '
    '; +} + +# Empty remove form (for javascript actions) +echo +'
    '. +form::hidden('rmyes',1).form::hidden('d',html::escapeHTML($d)). +form::hidden('remove',''). +$core->formNonce(). +'
    '; + +# Get zip directory +if ($core->auth->check('media_admin',$core->blog->id)) +{ + echo + '

    '. + __('Download this directory as a zip file').'

    '; +} + +call_user_func($close_f); + +/* ----------------------------------------------------- */ +function mediaItemLine($f,$i) +{ + global $core, $page_url, $popup, $post_id; + + $fname = $f->basename; + + if ($f->d) { + $link = html::escapeURL($page_url).'&d='.html::sanitizeURL($f->relname); + if ($f->parent) { + $fname = '..'; + } + } else { + $link = + 'media_item.php?id='.$f->media_id.'&popup='.$popup.'&post_id='.$post_id; + } + + $class = 'media-item media-col-'.($i%2); + + $res = + '
    '. + ''. + '
      '. + '
    • '.$fname.'
    • '; + + if (!$f->d) { + $res .= + '
    • '.$f->media_title.'
    • '. + '
    • '. + $f->media_dtstr.' - '. + files::size($f->size).' - '. + ''.__('open').''. + '
    • '; + } + + $res .= '
    •  '; + + if ($post_id && !$f->d) { + $res .= '
      '. + ' '. + form::hidden('media_id',$f->media_id). + form::hidden('post_id',$post_id). + form::hidden('attach',1). + $core->formNonce(). + '
      '; + } + + if ($popup && !$f->d) { + $res .= ''.__('Insert this file into entry').' '; + } + + if ($f->del) { + $res .= ''. + ''.__('delete').''; + } + + $res .= '
    • '; + + if ($f->type == 'audio/mpeg3') { + $res .= '
    • '.dcMedia::mp3player($f->file_url,'index.php?pf=player_mp3.swf').'
    • '; + } + + $res .= '
    '; + + return $res; +} +?> \ No newline at end of file diff --git a/admin/media_item.php b/admin/media_item.php new file mode 100644 index 0000000..1ddbaba --- /dev/null +++ b/admin/media_item.php @@ -0,0 +1,518 @@ +blog->getPosts(array('post_id'=>$post_id)); + if ($post->isEmpty()) { + $post_id = null; + } + $post_title = $post->post_title; + unset($post); +} + +$file = null; +$popup = (integer) !empty($_GET['popup']); +$page_url = 'media_item.php?popup='.$popup.'&post_id='.$post_id; +$media_page_url = 'media.php?popup='.$popup.'&post_id='.$post_id; + +$id = !empty($_REQUEST['id']) ? (integer) $_REQUEST['id'] : ''; + +if ($popup) { + $open_f = array('dcPage','openPopup'); + $close_f = array('dcPage','closePopup'); +} else { + $open_f = array('dcPage','open'); + $close_f = create_function('',"dcPage::helpBlock('core_media'); dcPage::close();"); +} + +$core_media_writable = false; +try +{ + $core->media = new dcMedia($core); + + if ($id) { + $file = $core->media->getFile($id); + } + + if ($file === null) { + throw new Exception(__('Not a valid file')); + } + + $core->media->chdir(dirname($file->relname)); + $core_media_writable = $core->media->writable(); + + # Prepare directories combo box + $dirs_combo = array(); + foreach ($core->media->getRootDirs() as $v) { + if ($v->w) { + $dirs_combo['/'.$v->relname] = $v->relname; + } + } +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} + +# Upload a new file +if ($file && !empty($_FILES['upfile']) && $file->editable && $core_media_writable) +{ + try { + files::uploadStatus($_FILES['upfile']); + $core->media->uploadFile($_FILES['upfile']['tmp_name'],$file->basename,null,false,true); + http::redirect($page_url.'&id='.$id.'&fupl=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Update file +if ($file && !empty($_POST['media_file']) && $file->editable && $core_media_writable) +{ + $newFile = clone $file; + + $newFile->basename = $_POST['media_file']; + + if ($_POST['media_path']) { + $newFile->dir = $_POST['media_path']; + $newFile->relname = $_POST['media_path'].'/'.$newFile->basename; + } else { + $newFile->dir = ''; + $newFile->relname = $newFile->basename; + } + $newFile->media_title = $_POST['media_title']; + $newFile->media_dt = strtotime($_POST['media_dt']); + $newFile->media_dtstr = $_POST['media_dt']; + $newFile->media_priv = !empty($_POST['media_private']); + + try { + $core->media->updateFile($file,$newFile); + http::redirect($page_url.'&id='.$id.'&fupd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Update thumbnails +if (!empty($_POST['thumbs']) && $file->media_type == 'image' && $file->editable && $core_media_writable) +{ + try { + $foo = null; + $core->media->imageThumbCreate($foo,$file->basename); + http::redirect($page_url.'&id='.$id.'&thumbupd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Unzip file +if (!empty($_POST['unzip']) && $file->type == 'application/zip' && $file->editable && $core_media_writable) +{ + try { + $unzip_dir = $core->media->inflateZipFile($file,$_POST['inflate_mode'] == 'new'); + http::redirect($media_page_url.'&d='.$unzip_dir.'&unzipok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Function to get image title based on meta +function dcGetImageTitle($file,$pattern) +{ + $res = array(); + $pattern = preg_split('/\s*;;\s*/',$pattern); + $sep = ', '; + + foreach ($pattern as $v) { + if ($v == 'Title') { + $res[] = $file->media_title; + } elseif ($file->media_meta->{$v}) { + $res[] = (string) $file->media_meta->{$v}; + } elseif (preg_match('/^Date\((.+?)\)$/u',$v,$m)) { + $res[] = dt::str($m[1],$file->media_dt); + } elseif (preg_match('/^DateTimeOriginal\((.+?)\)$/u',$v,$m) && $file->media_meta->DateTimeOriginal) { + $res[] = dt::dt2str($m[1],(string) $file->media_meta->DateTimeOriginal); + } elseif (preg_match('/^separator\((.*?)\)$/u',$v,$m)) { + $sep = $m[1]; + } + } + return implode($sep,$res); +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +$starting_scripts = dcPage::jsLoad('js/_media_item.js'); +if ($popup) { + $starting_scripts .= + dcPage::jsLoad('js/jsToolBar/popup_media.js'); +} +call_user_func($open_f,__('Media manager'), + $starting_scripts. + dcPage::jsDatePicker(). + dcPage::jsPageTabs() +); + +if ($file === null) { + call_user_func($close_f); + exit; +} + +if (!empty($_GET['fupd']) || !empty($_GET['fupl'])) { + echo '

    '.__('File has been successfully updated.').'

    '; +} +if (!empty($_GET['thumbupd'])) { + echo '

    '.__('Thumbnails have been successfully updated.').'

    '; +} + +echo '

    '.__('Media manager').''. +' / '.$core->media->breadCrumb(html::escapeURL($media_page_url).'&d=%s'). +$file->basename.'

    '; + +# Insertion popup +if ($popup) +{ + $media_desc = $file->media_title; + + echo + '
    '. + '
    '; + + if ($file->media_type == 'image') + { + $media_type = 'image'; + $media_desc = dcGetImageTitle($file,$core->blog->settings->media_img_title_pattern); + if ($media_desc == $file->basename) { + $media_desc = ''; + } + + echo + '

    '.__('Image size:').'

    '; + + $s_checked = false; + echo '

    '; + foreach (array_reverse($file->media_thumb) as $s => $v) { + $s_checked = ($s == 'm'); + echo '
    '; + } + $s_checked = (!isset($file->media_thumb['m'])); + echo '
    '; + echo '

    '; + + + echo '

    '.__('Image alignment').'

    '; + $i_align = array( + 'none' => array(__('None'),1), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),0) + ); + + echo '

    '; + foreach ($i_align as $k => $v) { + echo '
    '; + } + echo '

    '; + + echo + '

    '.__('Image insertion').'

    '. + '

    '. + '
    '. + ''. + '

    '; + } + elseif ($file->type == 'audio/mpeg3') + { + $media_type = 'mp3'; + + echo '

    '.__('MP3 disposition').'

    '. + '

    '.__("Please note that you cannot insert mp3 files with visual editor.").'

    '; + + $i_align = array( + 'none' => array(__('None'),0), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),1) + ); + + echo '

    '; + foreach ($i_align as $k => $v) { + echo '
    '; + } + + $public_player = dcMedia::mp3player($file->file_url,$core->blog->getQmarkURL().'pf=player_mp3.swf'); + echo form::hidden('public_player',html::escapeHTML($public_player)); + echo '

    '; + } + elseif ($file->type == 'video/x-flv' || $file->type == 'video/mp4' || $file->type == 'video/x-m4v') + { + $media_type = 'flv'; + + echo + '

    '.__("Please note that you cannot insert video files with visual editor.").'

    '; + + echo + '

    '.__('Video size').'

    '. + '

    '; + + echo '

    '.__('Video disposition').'

    '; + + $i_align = array( + 'none' => array(__('None'),0), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),1) + ); + + echo '

    '; + foreach ($i_align as $k => $v) { + echo '
    '; + } + + $public_player = dcMedia::flvplayer($file->file_url,$core->blog->getQmarkURL().'pf=player_flv.swf'); + echo form::hidden('public_player',html::escapeHTML($public_player)); + echo '

    '; + } + else + { + $media_type = 'default'; + echo '

    '.__('Media item will be inserted as a link.').'

    '; + } + + echo + '

    '.__('Cancel').' - '. + ''.__('Insert').''. + form::hidden(array('type'),html::escapeHTML($media_type)). + form::hidden(array('title'),html::escapeHTML($file->media_title)). + form::hidden(array('description'),html::escapeHTML($media_desc)). + form::hidden(array('url'),$file->file_url). + '

    '; + + echo '
    '; +} + +echo +'
    '. +'

    '; + +echo +'
    '; + +if ($file->media_image) +{ + $thumb_size = !empty($_GET['size']) ? $_GET['size'] : 's'; + + if (!isset($core->media->thumb_sizes[$thumb_size]) && $thumb_size != 'o') { + $thumb_size = 's'; + } + + echo '

    '.__('Available sizes:').' '; + foreach (array_reverse($file->media_thumb) as $s => $v) + { + $strong_link = ($s == $thumb_size) ? '%s' : '%s'; + printf($strong_link,''.$core->media->thumb_sizes[$s][2].' | '); + } + echo ''.__('original').''; + echo '

    '; + + if (isset($file->media_thumb[$thumb_size])) { + echo '

    '; + } elseif ($thumb_size == 'o') { + $S = getimagesize($file->file); + $class = ($S[1] > 500) ? ' class="overheight"' : ''; + unset($S); + echo '

    '; + } +} + +if ($file->type == 'audio/mpeg3') +{ + echo dcMedia::mp3player($file->file_url,'index.php?pf=player_mp3.swf'); +} + +if ($file->type == 'video/x-flv' || $file->type == 'video/mp4' || $file->type == 'video/x-m4v') +{ + echo dcMedia::flvplayer($file->file_url,'index.php?pf=player_flv.swf'); +} + +echo +'

    '.__('Media details').'

    '. +'
      '. + '
    • '.__('File owner:').' '.$file->media_user.'
    • '. + '
    • '.__('File type:').' '.$file->type.'
    • '. + '
    • '.__('File size:').' '.files::size($file->size).'
    • '. + '
    • '.__('File URL:').' '.$file->file_url.'
    • '. +'
    '; + +if (empty($_GET['find_posts'])) +{ + echo + '

    '. + __('Show entries containing this media').'

    '; +} +else +{ + echo '

    '.__('Entries containing this media').'

    '; + $params = array( + 'post_type' => '', + 'from' => 'LEFT OUTER JOIN '.$core->prefix.'post_media PM ON P.post_id = PM.post_id ', + 'sql' => 'AND ('. + 'PM.media_id = '.(integer) $id.' '. + "OR post_content_xhtml LIKE '%".$core->con->escape($file->relname)."%' ". + "OR post_excerpt_xhtml LIKE '%".$core->con->escape($file->relname)."%' " + ); + + if ($file->media_image) + { # We look for thumbnails too + $media_root = $core->blog->host.path::clean($core->blog->settings->public_url).'/'; + foreach ($file->media_thumb as $v) { + $v = preg_replace('/^'.preg_quote($media_root,'/').'/','',$v); + $params['sql'] .= "OR post_content_xhtml LIKE '%".$core->con->escape($v)."%' "; + $params['sql'] .= "OR post_excerpt_xhtml LIKE '%".$core->con->escape($v)."%' "; + } + } + + $params['sql'] .= ') '; + + $rs = $core->blog->getPosts($params); + + if ($rs->isEmpty()) + { + echo '

    '.__('No entry seems contain this media.').'

    '; + } + else + { + echo '
      '; + while ($rs->fetch()) { + echo '
    • '. + $rs->post_title.''. + ($rs->post_type != 'post' ? ' ('.html::escapeHTML($rs->post_type).')' : ''). + ' - '.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->post_dt).'
    • '; + } + echo '
    '; + } +} + +if ($file->type == 'image/jpeg') +{ + echo '

    '.__('Image details').'

    '; + + if (count($file->media_meta) == 0) + { + echo '

    '.__('No detail').'

    '; + } + else + { + echo '
      '; + foreach ($file->media_meta as $k => $v) + { + if ((string) $v) { + echo '
    • '.$k.': '.html::escapeHTML($v).'
    • '; + } + } + echo '
    '; + } +} + +if ($file->editable && $core_media_writable) +{ + if ($file->media_type == 'image') + { + echo + '
    '. + '
    '.__('Update thumbnails').''. + '

    '.__('This will create or update thumbnails for this image.').'

    '. + '

    '. + form::hidden(array('id'),$id). + $core->formNonce().'

    '. + '
    '; + } + + if ($file->type == 'application/zip') + { + $inflate_combo = array( + __('Extract in a new directory') => 'new', + __('Extract in current directory') => 'current' + ); + + echo + '
    '. + '
    '.__('Extract archive').''. + '
      '. + '
    • '.__('Extract in a new directory').' : '. + __('This will extract archive in a new directory that should not exists yet.').'
    • '. + '
    • '.__('Extract in current directory').' : '. + __('This will extract archive in current directory and will overwrite existing files or directory.').'
    • '. + '
    '. + '

    '. + ''. + form::hidden(array('id'),$id). + $core->formNonce().'

    '. + '
    '; + } + + echo + '
    '. + '
    '.__('Change media properties').''. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '. + form::hidden(array('id'),$id). + $core->formNonce().'

    '. + '
    '; + + echo + '
    '. + '
    '.__('Change file').''. + '
    '.form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE).'
    '. + '

    '. + '

    '. + form::hidden(array('id'),$id). + $core->formNonce().'

    '. + '
    '; +} + +echo +'
    '. +'
    '; + +call_user_func($close_f); +?> \ No newline at end of file diff --git a/admin/permissions.php b/admin/permissions.php new file mode 100644 index 0000000..f9094a7 --- /dev/null +++ b/admin/permissions.php @@ -0,0 +1,160 @@ +userExists($u)) { + $users[] = $u; + } + } +} + +# Check blogs +if (!empty($_REQUEST['blog_id']) && is_array($_REQUEST['blog_id'])) +{ + foreach ($_REQUEST['blog_id'] as $b) + { + if ($core->blogExists($b)) { + $blogs[] = $b; + } + } +} + +# Update permissions +if (!empty($_POST['upd_perm']) && !empty($users) && !empty($blogs)) +{ + $redir = 'permissions.php?upd=1'; + + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + foreach ($users as $u) + { + foreach ($blogs as $b) + { + $set_perms = array(); + + 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); + } + + $redir .= '&user_id[]='.$u; + } + + foreach ($blogs as $b) { + $redir .= '&blog_id[]='.$b; + } + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + + +if (empty($blogs) || empty($users)) { + $core->error->add(__('No blog or user given.')); +} + + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('permissions'), + dcPage::jsLoad('js/_permissions.js') +); + +echo '

    '.__('Users').' › '.__('Permissions').'

    '; + +if (!empty($_GET['upd'])) { + echo '

    '.__('The permissions have been successfully updated.').'

    '; +} + +if (!empty($blogs) && !empty($users)) +{ + $perm_form = ''; + + if (count($users) == 1) { + $user_perm = $core->getUserPermissions($users[0]); + } + + foreach ($users as $u) { + $user_list[] = ''.$u.''; + } + + echo '

    '.sprintf(__('You are about to change permissions on the following blogs for users %s.'), + implode(', ',$user_list)); + + echo '

    '; + + foreach ($blogs as $b) + { + echo '

    '.html::escapeHTML($b).''. + form::hidden(array('blog_id[]'),$b).'

    '; + + foreach ($core->auth->getPermissionsTypes() as $perm_id => $perm) + { + $checked = false; + + if (count($users) == 1) { + $checked = isset($user_perm[$b]['p'][$perm_id]) && $user_perm[$b]['p'][$perm_id]; + } + + echo + '

    '; + } + } + + echo + '
    '. + '

    '. + '
    '. + '

    '. + $core->formNonce(); + + foreach ($users as $u) { + echo form::hidden(array('user_id[]'),$u); + } + + echo form::hidden('upd_perm',1).'

    '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/permissions_blog.php b/admin/permissions_blog.php new file mode 100644 index 0000000..9720b7c --- /dev/null +++ b/admin/permissions_blog.php @@ -0,0 +1,196 @@ + 'B.blog_id', +__('Blog name') => 'blog_name' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + +$q = !empty($_GET['q']) ? $_GET['q'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'blog_id'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'asc'; + + +# Check users +if (!empty($_REQUEST['user_id']) && is_array($_REQUEST['user_id'])) +{ + foreach ($_REQUEST['user_id'] as $u) + { + if ($core->userExists($u)) { + $users[] = $u; + } + } +} + +if (empty($users)) +{ + $core->error->add(__('No blog or user given.')); +} +else +{ + $page = !empty($_GET['page']) ? $_GET['page'] : 1; + $nb_per_page = 30; + + if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = $_GET['nb']; + } + + $show_filters = false; + + # - Search filter + if ($q) { + $params['q'] = $q; + $show_filters = true; + } + + # - Sortby and order filter + if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + $show_filters = true; + } + } + + $params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); + + try { + $rs = $core->getBlogs($params); + $counter = $core->getBlogs($params,1); + $nb_blog = $counter->f(0); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_permissions_blog.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} +dcPage::open(__('choose a blog'),$starting_script); + +echo '

    '.__('Users').' › '.__('Choose a blog').'

    '; + +if (!$core->error->flag()) +{ + $hidden_fields = ''; + foreach ($users as $u) { + $hidden_fields .= form::hidden(array('user_id[]'),$u); + } + + if (!$show_filters) { + echo '

    '.__('Filters').'

    '; + } + + echo + '
    '. + '
    '.__('Filters').''. + + '
    '. + '

    '. + '

    '. + '
    '. + + '
    '. + '

    '. + '

    '. + ''. + $hidden_fields.'

    '. + '
    '. + + '
    '. //Opera sucks + '
    '. + '
    '; + + echo + '

    '. + sprintf(__('Choose one or more blogs to which you want to give permissions to users %s.'), + ''.implode(', ',$users).'').'

    '; + + # Show blogs + if ($nb_blog == 0) + { + echo '

    '.__('No blog').'

    '; + } + else + { + $pager = new pager($page,$nb_blog,$nb_per_page,10); + $pager->var_page = 'page'; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + + echo + '
    '. + ''. + ''. + ''. + ''. + ''. + ''; + + while ($rs->fetch()) { + echo blogLine($rs); + } + + echo + '
    '.__('Blog ID').''.__('Blog name').''.__('Entries').''.__('Status').'
    '. + + '

    '. + + '

    '. + $hidden_fields. + $core->formNonce().'

    '. + '
    '; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + } +} + +dcPage::close(); + +function blogLine(&$rs) +{ + global $core; + + $img_status = $rs->blog_status == 1 ? 'check-on' : 'check-off'; + $txt_status = $GLOBALS['core']->getBlogStatus($rs->blog_status); + $img_status = sprintf('%2$s',$img_status,$txt_status); + + return + ''. + ''. + form::checkbox(array('blog_id[]'),$rs->blog_id).''. + ''.$rs->blog_id.''. + ''.html::escapeHTML($rs->blog_name).''. + ''.$core->countBlogPosts($rs->blog_id).''. + ''.$img_status.''. + ''; +} +?> \ No newline at end of file diff --git a/admin/plugin.php b/admin/plugin.php new file mode 100644 index 0000000..0e633a9 --- /dev/null +++ b/admin/plugin.php @@ -0,0 +1,91 @@ +plugins->moduleExists($p)) { + $p_file = $core->plugins->moduleRoot($p).'/index.php'; +} + +if (file_exists($p_file)) +{ + # Loading plugin + $p_info = $core->plugins->getModules($p); + + $p_url = 'plugin.php?p='.$p; + + $p_title = 'no content - plugin'; + $p_head = ''; + $p_content = '

    '.__('No content found on this plugin.').'

    '; + + ob_start(); + include $p_file; + $res = ob_get_contents(); + ob_end_clean(); + + if (preg_match('|(.*?)(.*?)|ms',$m[1],$mt)) { + $p_title = $mt[1]; + } + + if (preg_match_all('|(.*?)|ms',$m[1],$ms)) { + foreach ($ms[1] as $v) { + $p_head .= $v."\n"; + } + } + + if (preg_match_all('|(.*?)|ms',$m[1],$ms)) { + foreach ($ms[1] as $v) { + $p_head .= $v."\n"; + } + } + + if (preg_match_all('|()|ms',$m[1],$ms)) { + foreach ($ms[1] as $v) { + $p_head .= $v."\n"; + } + } + } + + if (preg_match('|(.+)|ms',$res,$m)) { + $p_content = $m[1]; + } + + call_user_func($open_f,$p_title,$p_head); + echo $p_content; + call_user_func($close_f); +} +else +{ + call_user_func($open_f,__('Plugin not found')); + + echo '

    '.__('Plugin not found').'

    '; + + echo '

    '.__('The plugin you reached does not exist or does not have an admin page.').'

    '; + + call_user_func($close_f); +} +?> \ No newline at end of file diff --git a/admin/plugins.php b/admin/plugins.php new file mode 100644 index 0000000..f93a378 --- /dev/null +++ b/admin/plugins.php @@ -0,0 +1,364 @@ +plugins->moduleExists($plugin_id)) { + throw new Exception(__('No such plugin.')); + } + + $plugin = $core->plugins->getModules($plugin_id); + $plugin['id'] = $plugin_id; + + if (!preg_match('!^'.$p_path_pat.'!', $plugin['root'])) { + throw new Exception(__('You don\'t have permissions to delete this plugin.')); + } + + # --BEHAVIOR-- pluginBeforeDelete + $core->callBehavior('pluginsBeforeDelete', $plugin); + + $core->plugins->deleteModule($plugin_id); + + # --BEHAVIOR-- pluginAfterDelete + $core->callBehavior('pluginsAfterDelete', $plugin); + } + else + { + $core->plugins->deleteModule($plugin_id,true); + } + + http::redirect('plugins.php?removed=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + # Deactivate plugin + elseif ($plugin_id && !empty($_POST['deactivate'])) + { + try + { + if (!$core->plugins->moduleExists($plugin_id)) { + throw new Exception(__('No such plugin.')); + } + + $plugin = $core->plugins->getModules($plugin_id); + $plugin['id'] = $plugin_id; + + if (!$plugin['root_writable']) { + throw new Exception(__('You don\'t have permissions to deactivate this plugin.')); + } + + $core->plugins->deactivateModule($plugin_id); + http::redirect('plugins.php'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + # Activate plugin + elseif ($plugin_id && !empty($_POST['activate'])) + { + try + { + $p = $core->plugins->getDisabledModules(); + if (!isset($p[$plugin_id])) { + throw new Exception(__('No such plugin.')); + } + $core->plugins->activateModule($plugin_id); + http::redirect('plugins.php'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + # Plugin upload + elseif ((!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])) || + (!empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url']))) + { + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + if (!empty($_POST['upload_pkg'])) + { + files::uploadStatus($_FILES['pkg_file']); + + $dest = $p_path.'/'.$_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 = $p_path.'/'.basename($url); + + try + { + $client = netHttp::initClient($url,$path); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + } + catch( Exception $e) + { + throw new Exception(__('An error occurred while downloading the file.')); + } + + unset($client); + } + + $ret_code = $core->plugins->installPackage($dest,$core->plugins); + http::redirect('plugins.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + $default_tab = 'addplugin'; + } + } +} + +# Plugin install +$plugins_install = $core->plugins->installModules(); + +/* DISPLAY Main page +-------------------------------------------------------- */ +dcPage::open(__('Plugins management'), + dcPage::jsLoad('js/_plugins.js'). + dcPage::jsPageTabs($default_tab) +); + +echo +'

    '.__('Plugins management').'

    '; + +if (!empty($_GET['removed'])) { + echo + '

    '.__('Plugin has been successfully deleted.').'

    '; +} +if (!empty($_GET['added'])) { + echo '

    '. + ($_GET['added'] == 2 ? __('Plugin has been successfully upgraded') : __('Plugin has been successfully installed.')). + '

    '; +} + +# 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 '
    '; +} + +# List all active plugins +echo '

    '.__('Plugins add new functionalities to Dotclear. '. +'Here you can activate or deactivate installed plugins.').'

    '; + +echo '

    '.sprintf(__('You can find additional plugins for your blog on %s.'), +'Dotaddict').' '; + +if ($is_writable) { + echo __('To install or upgrade a plugin you generally just need to upload it '. + 'in "Install or upgrade a plugin" section.'); +} else { + echo __('To install or upgrade a plugin you just need to extract it in your plugins directory.'); +} +echo '

    '; + +echo +'
    '; + +$p_available = $core->plugins->getModules(); +uasort($p_available,create_function('$a,$b','return strcasecmp($a["name"],$b["name"]);')); +if (!empty($p_available)) +{ + echo + '

    '.__('Activated plugins').'

    '. + ''. + ''. + ''. + ''. + ''. + ''; + + foreach ($p_available as $k => $v) + { + $is_deletable = $is_writable && preg_match('!^'.$p_path_pat.'!',$v['root']); + $is_deactivable = $v['root_writable']; + + echo + ''. + ''. + ''. + ''. + ''. + ''; + } + echo + '
    '.__('Plugin').''.__('Version').''.__('Details').''.__('Action').'
    '.html::escapeHTML($k).''.html::escapeHTML($v['version']).''.html::escapeHTML($v['name']).' '. + '
    '.html::escapeHTML($v['desc']).'
    '; + + if ($is_deletable || $is_deactivable) + { + echo + '
    '. + '
    '. + $core->formNonce(). + form::hidden(array('plugin_id'),html::escapeHTML($k)). + ($is_deactivable ? ' ' : ''). + ($is_deletable ? ' ' : ''). + '
    '. + '
    '; + } + + echo + '
    '; +} + +$p_disabled = $core->plugins->getDisabledModules(); +uksort($p_disabled,create_function('$a,$b','return strcasecmp($a,$b);')); +if (!empty($p_disabled)) +{ + echo + '

    '.__('Deactivated plugins').'

    '. + ''. + ''. + ''. + ''; + + foreach ($p_disabled as $k => $v) + { + $is_deletable = $is_writable && preg_match('!^'.$p_path_pat.'!',$v['root']); + $is_activable = $v['root_writable']; + + echo + ''. + ''. + ''. + ''; + } + echo + '
    '.__('Plugin').''.__('Action').'
    '.html::escapeHTML($k).''; + + if ($is_deletable || $is_activable) + { + echo + '
    '. + '
    '. + $core->formNonce(). + form::hidden(array('plugin_id'),html::escapeHTML($k)). + form::hidden(array('deactivated'),1). + ($is_activable ? ' ' : ''). + ($is_deletable ? ' ' : ''). + '
    '. + '
    '; + } + + echo + '
    '; +} + +echo '
    '; + +# Add a new plugin +echo +'
    '; + +if ($is_writable) +{ + echo '

    '.__('You can install plugins by uploading or downloading zip files.').'

    '; + + # 'Upload plugin' form + echo + '
    '. + '
    '. + ''.__('Upload a zip file').''. + '

    '. + '

    '. + ''. + $core->formNonce(). + '
    '. + '
    '; + + # 'Fetch plugin' form + echo + '
    '. + '
    '. + ''.__('Download a zip file').''. + '

    '. + '

    '. + ''. + $core->formNonce(). + '
    '. + '
    '; +} +else +{ + echo + '

    '. + __('To enable this function, please give write access to your plugins directory.'). + '

    '; +} +echo '
    '; + +# --BEHAVIOR-- pluginsToolsTabs +$core->callBehavior('pluginsToolsTabs',$core); + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/popup_link.php b/admin/popup_link.php new file mode 100644 index 0000000..a75d496 --- /dev/null +++ b/admin/popup_link.php @@ -0,0 +1,58 @@ +'.__('Add a link').''; + +# Languages combo +$rs = $core->blog->getLangs(array('order'=>'asc')); +$all_langs = l10n::getISOcodes(0,1); +$lang_combo = array('' => '', __('Most used') => array(), __('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 +''. + +'

    '.__('cancel').' - '. +''.__('insert').'

    '."\n". + +''."\n"; + +dcPage::closePopup(); +?> \ No newline at end of file diff --git a/admin/popup_posts.php b/admin/popup_posts.php new file mode 100644 index 0000000..9b771a6 --- /dev/null +++ b/admin/popup_posts.php @@ -0,0 +1,57 @@ +'.__('Add a link to an entry').''; + +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(); +?> \ No newline at end of file diff --git a/admin/post.php b/admin/post.php new file mode 100644 index 0000000..796ddc6 --- /dev/null +++ b/admin/post.php @@ -0,0 +1,657 @@ +auth->getOption('post_format'); +$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->allow_comments; +$post_open_tb = $core->blog->settings->allow_trackbacks; + +$post_media = array(); + +$page_title = __('New entry'); + +$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 = array(' ' => ''); +try { + $categories = $core->blog->getCategories(array('post_type'=>'post')); + while ($categories->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$categories->level-1).'• '.html::escapeHTML($categories->cat_title), + $categories->cat_id + ); + } +} catch (Exception $e) { } + +# Status combo +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# Formaters combo +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +# Languages combo +$rs = $core->blog->getLangs(array('order'=>'asc')); +$all_langs = l10n::getISOcodes(0,1); +$lang_combo = array('' => '', __('Most used') => array(), __('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); + + +# Get entry informations +if (!empty($_REQUEST['id'])) +{ + $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; + + $page_title = __('Edit entry'); + + $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); + $post_media = $core->media->getPostMedia($post_id); + } catch (Exception $e) {} + } +} + +# Format excerpt and content +if (!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 { + $post_dt = strtotime($_POST['post_dt']); + $post_dt = date('Y-m-d H:i',$post_dt); + } + + $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 + ); +} + +# Create or update post +if (!empty($_POST) && !empty($_POST['save']) && $can_edit_post) +{ + $cur = $core->con->openCursor($core->prefix.'post'); + + $cur->post_title = $post_title; + $cur->cat_id = ($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); + + http::redirect('post.php?id='.$post_id.'&upd=1'); + } + 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); + + http::redirect('post.php?id='.$return_id.'&crea=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } +} + +if (!empty($_POST['delete']) && $can_delete) +{ + try { + # --BEHAVIOR-- adminBeforePostDelete + $core->callBehavior('adminBeforePostDelete',$post_id); + $core->blog->delPost($post_id); + http::redirect('posts.php'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +$default_tab = 'edit-entry'; +if (!$can_edit_post) { + $default_tab = ''; +} +if (!empty($_GET['co'])) { + $default_tab = 'comments'; +} + +dcPage::open($page_title, + dcPage::jsDatePicker(). + dcPage::jsToolBar(). + dcPage::jsModal(). + 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 +); + +if (!empty($_GET['upd'])) { + echo '

    '.__('Entry has been successfully updated.').'

    '; +} +elseif (!empty($_GET['crea'])) { + echo '

    '.__('Entry has been successfully created.').'

    '; +} +elseif (!empty($_GET['attached'])) { + echo '

    '.__('File has been successfully attached.').'

    '; +} +elseif (!empty($_GET['rmattach'])) { + echo '

    '.__('Attachment has been successfully removed.').'

    '; +} + +if (!empty($_GET['creaco'])) { + echo '

    '.__('Comment has been successfully created.').'

    '; + } + +# XHTML conversion +if (!empty($_GET['xconv'])) +{ + $post_excerpt = $post_excerpt_xhtml; + $post_content = $post_content_xhtml; + $post_format = 'xhtml'; + + echo '

    '.__('Don\'t forget to validate your XHTML conversion by saving your post.').'

    '; +} + +echo '

    '.html::escapeHTML($core->blog->name).' › '.$page_title; + +if ($post_id && $post->post_status == 1) { + echo ' - '.__('View entry').''; +} elseif ($post_id) { + $preview_url = + $core->blog->url.$core->url->getBase('preview').'/'. + $core->auth->userID().'/'. + http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')). + '/'.$post->post_url; + echo ' - '.__('Preview entry').''; +} + +echo '

    '; + +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); + + echo '

    '; +} + +# Exit if we cannot view page +if (!$can_view_page) { + dcPage::helpBlock('core_post'); + dcPage::close(); + exit; +} + +/* Post form if we can edit post +-------------------------------------------------------- */ +if ($can_edit_post) +{ + echo '
    '; + echo '
    '; + echo '
    '; + + echo + '

    '. + + '

    '. + + '

    '. + + '

    '. + + '

    '. + '

    '. + '

    '. + + '

    '. + + '

    '. + + '
    '. + '

    '. + '

    '. + __('Warning: If you set the URL manually, it may conflict with another entry.'). + '

    '. + '
    '; + + if ($post_id) + { + echo + '

    '.__('Attachments').'

    '; + foreach ($post_media as $f) + { + $ftitle = $f->media_title; + if (strlen($ftitle) > 18) { + $ftitle = substr($ftitle,0,16).'...'; + } + echo + '
    '. + ''. + ''. + ''. + '
    '; + } + unset($f); + + if (empty($post_media)) { + echo '

    '.__('No attachment.').'

    '; + } + echo '

    '.__('Add files to this entry').'

    '; + } + + # --BEHAVIOR-- adminPostFormSidebar + $core->callBehavior('adminPostFormSidebar',isset($post) ? $post : null); + + echo '
    '; // End #entry-sidebar + + echo '
    '; + + echo + '

    '. + + '

    '. + form::textarea('post_excerpt',50,5,html::escapeHTML($post_excerpt),'',2). + '

    '. + + '

    '. + form::textarea('post_content',50,$core->auth->getOption('edit_size'),html::escapeHTML($post_content),'',2). + '

    '. + + '

    '. + form::textarea('post_notes',50,5,html::escapeHTML($post_notes),'',2). + '

    '; + + # --BEHAVIOR-- adminPostForm + $core->callBehavior('adminPostForm',isset($post) ? $post : null); + + echo + '

    '. + ($post_id ? form::hidden('id',$post_id) : ''). + ' '. + ($can_delete ? '' : ''). + $core->formNonce(). + '

    '; + + echo '
    '; // End #entry-content + echo '
    '; + echo '
    '; + + if ($post_id && $post->post_status == 1) { + echo '

    '. + __('Ping blogs').'

    '; + } + + if ($post_id && !empty($post_media)) + { + echo + '
    '. + '
    '.form::hidden(array('post_id'),$post_id). + form::hidden(array('media_id'),''). + form::hidden(array('remove'),1). + $core->formNonce().'
    '; + } +} + + +/* Comments and trackbacks +-------------------------------------------------------- */ +if ($post_id) +{ + $params = array('post_id' => $post_id, 'order' => 'comment_dt ASC'); + + $comments = $core->blog->getComments(array_merge($params,array('comment_trackback'=>0))); + $trackbacks = $core->blog->getComments(array_merge($params,array('comment_trackback'=>1))); + + # Actions combo box + $combo_action = array(); + if ($can_edit_post && $core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; + } + + if ($can_edit_post && $core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('delete')] = 'delete'; + } + + $has_action = !empty($combo_action) && (!$trackbacks->isEmpty() || !$comments->isEmpty()); + + echo + '
    '; + + if ($has_action) { + echo '
    '; + } + + echo '

    '.__('Trackbacks').'

    '; + + if (!$trackbacks->isEmpty()) { + showComments($trackbacks,$has_action); + } else { + echo '

    '.__('No trackback').'

    '; + } + + echo '

    '.__('Comments').'

    '; + if (!$comments->isEmpty()) { + showComments($comments,$has_action); + } else { + echo '

    '.__('No comment').'

    '; + } + + if ($has_action) { + echo + '
    '. + '

    '. + + '

    '.__('Selected comments action:').' '. + form::combo('action',$combo_action). + form::hidden('redir','post.php?id='.$post_id.'&co=1'). + $core->formNonce(). + '

    '. + '
    '. + '
    '; + } + + echo '
    '; +} + +/* Add a comment +-------------------------------------------------------- */ +if ($post_id) +{ + echo + '
    '. + '

    '.__('Add a comment').'

    '. + + '
    '. + '
    '. + '

    '. + + '

    '. + + '

    '. + + '

    '. + form::textarea('comment_content',50,8,html::escapeHTML('')). + '

    '. + + '

    '.form::hidden('post_id',$post_id). + $core->formNonce(). + '

    '. + '
    '. + '
    '. + '
    '; +} + + +# Show comments or trackbacks +function showComments(&$rs,$has_action) +{ + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + while($rs->fetch()) + { + $comment_url = 'comment.php?id='.$rs->comment_id; + + $img = '%1$s'; + switch ($rs->comment_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + break; + case -2: + $img_status = sprintf($img,__('junk'),'junk.png'); + break; + } + + echo + ''. + + ''. + ''. + ''. + ''. + ''. + ''. + + ''; + } + + echo '
    '.__('Author').''.__('Date').''.__('IP address').''.__('Status').' 
    '. + ($has_action ? form::checkbox(array('comments[]'),$rs->comment_id,'','','',0) : '').''.html::escapeHTML($rs->comment_author).''.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->comment_dt).''.$rs->comment_ip.''.$img_status.''. + '
    '; +} + +dcPage::helpBlock('core_post','core_wiki'); +dcPage::close(); +?> diff --git a/admin/post_media.php b/admin/post_media.php new file mode 100644 index 0000000..1360dd4 --- /dev/null +++ b/admin/post_media.php @@ -0,0 +1,79 @@ +blog->getPosts(array('post_id' => $post_id,'post_type'=>'')); +if ($rs->isEmpty()) { + exit; +} + +if ($post_id && $media_id && !empty($_POST['attach'])) +{ + $core->media = new dcMedia($core); + $core->media->addPostMedia($post_id,$media_id); + http::redirect($core->getPostAdminURL($rs->post_type,$post_id,false)); +} + +try { + $core->media = new dcMedia($core); + $f = $core->media->getPostMedia($post_id,$media_id); + 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); + http::redirect($core->getPostAdminURL($rs->post_type,$post_id,false).'&rmattach=1'); + } + 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 + '
    '. + '

    '.__('Are you sure you want to remove this attachment?').'

    '. + '

    '. + '   '. + form::hidden('post_id',$post_id). + form::hidden('media_id',$media_id). + $core->formNonce().'

    '. + '
    '; + + dcPage::close(); + exit; + } +} +?> \ No newline at end of file diff --git a/admin/posts.php b/admin/posts.php new file mode 100644 index 0000000..c575a9c --- /dev/null +++ b/admin/posts.php @@ -0,0 +1,298 @@ +blog->getCategories(array('post_type'=>'post')); +} 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(array('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 +if (!$core->error->flag()) +{ + # Filter form we'll put in html_block + $users_combo = $categories_combo = array(); + $users_combo['-'] = $categories_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; + } + + while ($categories->fetch()) { + $categories_combo[str_repeat('  ',$categories->level-1).'• '. + html::escapeHTML($categories->cat_title). + ' ('.$categories->nb_post.')'] = $categories->cat_id; + } + + $status_combo = array( + '-' => '' + ); + foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = (string) $k; + } + + $selected_combo = array( + '-' => '', + __('selected') => '1', + __('not selected') => '0' + ); + + # Months array + $dt_m_combo['-'] = ''; + while ($dates->fetch()) { + $dt_m_combo[dt::str('%B %Y',$dates->ts())] = $dates->year().$dates->month(); + } + + $lang_combo['-'] = ''; + while ($langs->fetch()) { + $lang_combo[$langs->post_lang] = $langs->post_lang; + } + + $sortby_combo = array( + __('Date') => 'post_dt', + __('Title') => 'post_title', + __('Category') => 'cat_title', + __('Author') => 'user_id', + __('Status') => 'post_status', + __('Selected') => 'post_selected' + ); + + $order_combo = array( + __('Descending') => 'desc', + __('Ascending') => 'asc' + ); +} + +# Actions combo box +$combo_action = array(); +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('schedule')] = 'schedule'; + $combo_action[__('mark as pending')] = 'pending'; +} +$combo_action[__('mark as selected')] = 'selected'; +$combo_action[__('mark as unselected')] = 'unselected'; +$combo_action[__('change category')] = 'category'; +if ($core->auth->check('admin',$core->blog->id)) { + $combo_action[__('change author')] = 'author'; +} +if ($core->auth->check('delete,contentadmin',$core->blog->id)) +{ + $combo_action[__('delete')] = 'delete'; +} + +# --BEHAVIOR-- adminPostsActionsCombo +$core->callBehavior('adminPostsActionsCombo',array(&$combo_action)); + +/* 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'] : ''; +$selected = isset($_GET['selected']) ? $_GET['selected'] : ''; +$month = !empty($_GET['month']) ? $_GET['month'] : ''; +$lang = !empty($_GET['lang']) ? $_GET['lang'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'post_dt'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'desc'; + +$show_filters = false; + +$page = !empty($_GET['page']) ? (integer) $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + if ($nb_per_page != $_GET['nb']) { + $show_filters = true; + } + $nb_per_page = (integer) $_GET['nb']; +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; + +# - User filter +if ($user_id !== '' && in_array($user_id,$users_combo)) { + $params['user_id'] = $user_id; + $show_filters = true; +} + +# - Categories filter +if ($cat_id !== '' && in_array($cat_id,$categories_combo)) { + $params['cat_id'] = $cat_id; + $show_filters = true; +} + +# - Status filter +if ($status !== '' && in_array($status,$status_combo)) { + $params['post_status'] = $status; + $show_filters = true; +} + +# - Selected filter +if ($selected !== '' && in_array($selected,$selected_combo)) { + $params['post_selected'] = $selected; + $show_filters = true; +} + +# - 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; +} + +# - Lang filter +if ($lang !== '' && in_array($lang,$lang_combo)) { + $params['post_lang'] = $lang; + $show_filters = true; +} + +# - Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + } + + if ($sortby != 'post_dt' || $order != 'desc') { + $show_filters = true; + } +} + +# 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 +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_posts_list.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} + +dcPage::open(__('Entries'),$starting_script); + +if (!$core->error->flag()) +{ + echo '

    '.html::escapeHTML($core->blog->name).' › '.__('Entries').'

    '; + + if (!$show_filters) { + echo '

    '. + __('Filters').'

    '; + } + + echo + '
    '. + '
    '.__('Filters').''. + '
    '. + '
    '. + ' '. + ' '. + ' '. + '
    '. + + '
    '. + ' '. + ' '. + ' '. + '
    '. + + '
    '. + '

    '. + '

    '. + '

    '. + '

    '. + '
    '. + '
    '. + '
    '. //Opera sucks + '
    '. + '
    '; + + # Show posts + $post_list->display($page,$nb_per_page, + '
    '. + + '%s'. + + '
    '. + '

    '. + + '

    '.__('Selected entries action:').' '. + form::combo('action',$combo_action). + '

    '. + form::hidden(array('user_id'),$user_id). + form::hidden(array('cat_id'),$cat_id). + form::hidden(array('status'),$status). + form::hidden(array('selected'),$selected). + form::hidden(array('month'),$month). + form::hidden(array('lang'),$lang). + form::hidden(array('sortby'),$sortby). + form::hidden(array('order'),$order). + form::hidden(array('page'),$page). + form::hidden(array('nb'),$nb_per_page). + $core->formNonce(). + '
    '. + '
    ' + ); +} + +dcPage::helpBlock('core_posts','core_wiki'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/posts_actions.php b/admin/posts_actions.php new file mode 100644 index 0000000..21594b0 --- /dev/null +++ b/admin/posts_actions.php @@ -0,0 +1,250 @@ + $v) { + $entries[$k] = (integer) $v; + } + + $params['sql'] = 'AND P.post_id IN('.implode(',',$entries).') '; + $params['no_content'] = true; + + if (isset($_POST['post_type'])) { + $params['post_type'] = $_POST['post_type']; + } + + $posts = $core->blog->getPosts($params); + + # --BEHAVIOR-- adminPostsActions + $core->callBehavior('adminPostsActions',$core,$posts,$action,$redir); + + if (preg_match('/^(publish|unpublish|schedule|pending)$/',$action)) + { + switch ($action) { + case 'unpublish' : $status = 0; break; + case 'schedule' : $status = -1; break; + case 'pending' : $status = -2; break; + default : $status = 1; break; + } + + try + { + while ($posts->fetch()) { + $core->blog->updPostStatus($posts->post_id,$status); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'selected' || $action == 'unselected') + { + try + { + while ($posts->fetch()) { + $core->blog->updPostSelected($posts->post_id,$action == 'selected'); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'delete') + { + try + { + while ($posts->fetch()) { + # --BEHAVIOR-- adminBeforePostDelete + $core->callBehavior('adminBeforePostDelete',$posts->post_id); + $core->blog->delPost($posts->post_id); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + + } + elseif ($action == 'category' && isset($_POST['new_cat_id'])) + { + try + { + while ($posts->fetch()) + { + $new_cat_id = (integer) $_POST['new_cat_id']; + $core->blog->updPostCategory($posts->post_id,$new_cat_id); + } + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'author' && isset($_POST['new_auth_id']) + && $core->auth->check('admin',$core->blog->id)) + { + $new_user_id = $_POST['new_auth_id']; + + try + { + if ($core->getUser($new_user_id)->isEmpty()) { + throw new Exception(__('This user does not exist')); + } + + while ($posts->fetch()) + { + $cur = $core->con->openCursor($core->prefix.'post'); + $cur->user_id = $new_user_id; + $cur->update('WHERE post_id = '.(integer) $posts->post_id); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Entries')); + +if (!isset($action)) { + dcPage::close(); + exit; +} + +$hidden_fields = ''; +while ($posts->fetch()) { + $hidden_fields .= form::hidden(array('entries[]'),$posts->post_id); +} + +if (isset($_POST['redir']) && strpos($_POST['redir'],'://') === false) +{ + $hidden_fields .= form::hidden(array('redir'),html::escapeURL($_POST['redir'])); +} +else +{ + $hidden_fields .= + form::hidden(array('user_id'),$_POST['user_id']). + form::hidden(array('cat_id'),$_POST['cat_id']). + form::hidden(array('status'),$_POST['status']). + form::hidden(array('selected'),$_POST['selected']). + form::hidden(array('month'),$_POST['month']). + form::hidden(array('lang'),$_POST['lang']). + form::hidden(array('sortby'),$_POST['sortby']). + form::hidden(array('order'),$_POST['order']). + form::hidden(array('page'),$_POST['page']). + form::hidden(array('nb'),$_POST['nb']); +} + +if (isset($_POST['post_type'])) { + $hidden_fields .= form::hidden(array('post_type'),$_POST['post_type']); +} + +# --BEHAVIOR-- adminPostsActionsContent +$core->callBehavior('adminPostsActionsContent',$core,$action,$hidden_fields); + +if ($action == 'category') +{ + echo '

    '.__('Change category for entries').'

    '; + + # categories list + # Getting categories + $categories_combo = array(' ' => ''); + try { + $categories = $core->blog->getCategories(array('post_type'=>'post')); + while ($categories->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$categories->level-1).'• '.html::escapeHTML($categories->cat_title), + $categories->cat_id + ); + } + } catch (Exception $e) { } + + echo + '
    '. + '

    '; + + echo + $hidden_fields. + $core->formNonce(). + form::hidden(array('action'),'category'). + '

    '. + '
    '; +} +elseif ($action == 'author' && $core->auth->check('admin',$core->blog->id)) +{ + echo '

    '.__('Change author for entries').'

    '; + + echo + '
    '. + '

    '; + + echo + $hidden_fields. + $core->formNonce(). + form::hidden(array('action'),'author'). + '

    '. + '
    '; +} + +echo '

    '.__('back').'

    '; + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/preferences.php b/admin/preferences.php new file mode 100644 index 0000000..13e018a --- /dev/null +++ b/admin/preferences.php @@ -0,0 +1,202 @@ +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(); + +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = $k; +} + +# Language codes +$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' : ''); +} + +# Add or update user +if (isset($_POST['user_name'])) +{ + try + { + $pwd_check = !empty($_POST['cur_pwd']) && $core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_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_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['enable_wysiwyg'] = !empty($_POST['user_wysiwyg']); + + $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('adminBeforeUserUpdate',$cur,$core->auth->userID()); + + # Udate user + $core->updUser($core->auth->userID(),$cur); + + # --BEHAVIOR-- adminAfterUserUpdate + $core->callBehavior('adminAfterUserUpdate',$cur,$core->auth->userID()); + + http::redirect('preferences.php?upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open($page_title, + dcPage::jsLoad('js/_preferences.js'). + dcPage::jsConfirmClose('user-form'). + + # --BEHAVIOR-- adminPreferencesHeaders + $core->callBehavior('adminPreferencesHeaders') +); + +if (!empty($_GET['upd'])) { + echo '

    '.__('Personal information has been successfully updated.').'

    '; +} + +echo '

    '.$page_title.'

    '; + + +echo +'
    '. +'
    '.__('User preferences').''. +'
    '. +'
    '. +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'
    '. + +'
    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '. +'
    '. +'
    '. //Opera sucks +'
    '; + +# --BEHAVIOR-- adminPreferencesForm +$core->callBehavior('adminPreferencesForm',$core); + +if ($core->auth->allowPassChange()) +{ + echo + '
    '. + ''.__('Change your password').''. + + '

    '. + + '

    '. + '
    '. + + '
    '. + '

    '.__('If you want to change your email or password you must provide your current password.').'

    '. + '

    '. + '
    '; +} + +echo +'

    '. +$core->formNonce(). +'

    '. +'
    '; + +dcPage::helpBlock('core_user_pref'); +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/search.php b/admin/search.php new file mode 100644 index 0000000..fd7d899 --- /dev/null +++ b/admin/search.php @@ -0,0 +1,180 @@ +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'] = array((($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()); + } + } +} + + +dcPage::open(__('Search'),$starting_scripts); + +echo +'

    '.html::escapeHTML($core->blog->name).' › '.__('Search').'

    '. +'
    '. +'
    '.__('Search options').''. +'

    '. +' '. +' '. +'

    '. +'
    '. +'
    '; + +if ($q && !$core->error->flag()) +{ + $redir = html::escapeHTML($_SERVER['REQUEST_URI']); + + # Show posts + if ($qtype == 'p') + { + # Actions combo box + $combo_action = array(); + if ($core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('schedule')] = 'schedule'; + $combo_action[__('mark as pending')] = 'pending'; + } + $combo_action[__('change category')] = 'category'; + if ($core->auth->check('admin',$core->blog->id)) { + $combo_action[__('change author')] = 'author'; + } + if ($core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('delete')] = 'delete'; + } + + # --BEHAVIOR-- adminPostsActionsCombo + $core->callBehavior('adminPostsActionsCombo',array(&$combo_action)); + + 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, + '
    '. + + '%s'. + + '
    '. + '

    '. + + '

    '.__('Selected entries action:'). + form::combo('action',$combo_action). + '

    '. + form::hidden('redir',preg_replace('/%/','%%',$redir)). + $core->formNonce(). + '
    '. + '
    ' + ); + } + # Show posts + elseif ($qtype == 'c') + { + # Actions combo box + $combo_action = array(); + if ($core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; + } + if ($core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('delete')] = 'delete'; + } + + 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, + '
    '. + + '%s'. + + '
    '. + '

    '. + + '

    '.__('Selected comments action:').' '. + form::combo('action',$combo_action). + '

    '. + form::hidden('redir',preg_replace('/%/','%%',$redir)). + $core->formNonce(). + '
    '. + '
    ' + ); + } +} + + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/services.php b/admin/services.php new file mode 100644 index 0000000..f19146c --- /dev/null +++ b/admin/services.php @@ -0,0 +1,236 @@ +rest->addFunction('getPostById',array('dcRestMethods','getPostById')); +$core->rest->addFunction('getCommentById',array('dcRestMethods','getCommentById')); +$core->rest->addFunction('quickPost',array('dcRestMethods','quickPost')); +$core->rest->addFunction('validatePostMarkup',array('dcRestMethods','validatePostMarkup')); +$core->rest->addFunction('getZipMediaContent',array('dcRestMethods','getZipMediaContent')); + +$core->rest->serve(); + +/* Common REST methods */ +class dcRestMethods +{ + public static function getPostById(&$core,$get) + { + if (empty($get['id'])) { + throw new Exception('No post ID'); + } + + $params = array('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(array('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); + # --BEHAVIOR-- adminAfterCommentDesc + $rsp->comment_spam_disp($core->callBehavior('adminAfterCommentDesc', $rs)); + } + + return $rsp; + } + + public static function quickPost(&$core,$get,$post) + { + $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->allow_comments; + $cur->post_open_tb = (integer) $core->blog->settings->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(array('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'); + } + + $core->media = new dcMedia($core); + $file = $core->media->getFile($id); + + 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; + } +} +?> \ No newline at end of file diff --git a/admin/style/candyUpload/cancel.png b/admin/style/candyUpload/cancel.png new file mode 100644 index 0000000..b1a5947 Binary files /dev/null and b/admin/style/candyUpload/cancel.png differ diff --git a/admin/style/candyUpload/loader.png b/admin/style/candyUpload/loader.png new file mode 100644 index 0000000..267f541 Binary files /dev/null and b/admin/style/candyUpload/loader.png differ diff --git a/admin/style/candyUpload/style.css b/admin/style/candyUpload/style.css new file mode 100644 index 0000000..7e2fc91 --- /dev/null +++ b/admin/style/candyUpload/style.css @@ -0,0 +1,120 @@ + +div.cu-ctrl { + background: #fbfbfb; + padding: 5px 0 0 0; + margin: 0 0 1em 0; + overflow: hidden; + border-color: #ccc; + border-width: 1px 0; + border-style: solid; +} +div.cu-ctrl a { + border: none; +} + +div.cu-msg { + padding: 0 0.5em; + font-weight: bold; +} +div.cu-msg.cu-error { + color: #c00; +} + +div.cu-files { + padding: 0 0.5em; + margin: 10px 0; +} +div.cu-file { + margin: 0 0 8px 0; + position: relative; +} +div.cu-fileinfo { + margin-left: 16px; +} +div.cu-fileinfo span.cu-filecancel { + display: block; + position: absolute; + top: 2px; + left: 0px !important; + left: -16px; +} +div.cu-files span.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; +} +div.cu-files span.cu-filemsg { + font-weight: bold; + color: green; +} +div.cu-files span.cu-filemsg.cu-error { + color: #c00; +} + +div.cu-progress { + margin-left: 16px; +} +div.cu-progress div { + height: 10px; + width: 0; + font-size: 0.8em; + line-height: 1em; + height: 1em; + padding: 2px 0; + text-align: right; + background: green url(loader.png) repeat-x top left; + color: white; + font-weight: bold; + -moz-border-radius: 2px; +} + +div.cu-btn { + padding: 0 0.5em; + line-height: 1em; + height: 1.6em; + margin-top: 1em; + position: relative; +} +div.cu-btn span { + display: block; + margin: 0 0 0 5px; + float: right; +} +div.cu-btn span a { + display: block; + padding: 2px; + line-height: 127%; + background: #e2dfca; + color: #333; + text-decoration: none; + border: 1px solid #e2dfca !important; + outline: none; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} +div.cu-btn span a:hover, div.cu-btn span a:focus { + border-color: #ccc; + color: #06c; +} +div.cu-btn span.cu-btn-browse { + float: none; + position: absolute; + left: 0.5em; + margin: 0; +} +div.cu-btn span.cu-btn-upload { + font-weight: bold; +} + +div.cu-maxsize { + clear: both; + margin: 3px 0 0 0.5em; +} + +div.cu-disable { + padding: 0 0 0.5em 0.5em; +} \ No newline at end of file diff --git a/admin/style/cat-bg.png b/admin/style/cat-bg.png new file mode 100644 index 0000000..5a8b0c2 Binary files /dev/null and b/admin/style/cat-bg.png differ diff --git a/admin/style/date-picker.css b/admin/style/date-picker.css new file mode 100644 index 0000000..8cb66c7 --- /dev/null +++ b/admin/style/date-picker.css @@ -0,0 +1,35 @@ +.date-picker { + border-collapse : collapse; + background : #fff; + color : #fff; + border: 1px solid #666; + border-width : 1px 2px 2px 1px; +} +.date-picker th { + border : none; + color : #000; + text-align : center; +} +.date-picker td { + border : 1px solid #666; + 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 : #06c; + cursor : pointer; +} +.date-picker-day, .date-picker-today { + color : #000; + background : #eee; + cursor : pointer; +} +.date-picker-today { + background : #ccc; +} \ No newline at end of file diff --git a/admin/style/default-rtl.css b/admin/style/default-rtl.css new file mode 100644 index 0000000..f1217c4 --- /dev/null +++ b/admin/style/default-rtl.css @@ -0,0 +1,11 @@ +body { + direction: rtl; +} + +.right { + text-align: left; +} + +th { + text-align: left; +} \ No newline at end of file diff --git a/admin/style/default.css b/admin/style/default.css new file mode 100644 index 0000000..7d5714a --- /dev/null +++ b/admin/style/default.css @@ -0,0 +1,1074 @@ +/* +# -- BEGIN LICENSE BLOCK ---------------------------------- +# +# This file is part of Dotclear 2. +# +# Copyright (c) 2003-2009 Olivier Meunier and contributors +# Licensed under the GPL version 2.0 license. +# See LICENSE file or +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# +# -- END LICENSE BLOCK ------------------------------------ +*/ +body { + font: 62.5%/1.5em "DejaVu Sans","Lucida Grande","Lucida Sans Unicode",Arial,sans-serif; + color : #000; + background : #fff; + margin : 0; + padding : 0; +} +body.auth { + background-image: none; +} +body.install { + background: transparent url(page-bg.png) repeat-y top left; +} +body.install #content { + margin-top: 1em; + font-size: 1.1em; +} + +a img,:link img,:visited img { border:none } + +a, a:link, a:visited { + color : #06c; + text-decoration : none; + border-bottom : 1px dotted #f90; +} +a:hover, a:active, a:focus { + +} + +h1, h2, h3, h4, h5, h6, p { + margin-top : 0; + margin-bottom: 0.6em; +} + +h2 { + font-family : Arial,Helvetica,sans-serif; + color : #069; + font-size : 1.4em; + padding: 4px 0; +} + +h3 { + font-family : Arial,Helvetica,sans-serif; + color : #333; + font-size : 1.2em; +} + +p, div.p { + margin : 0 0 1em 0; +} + +hr { + height : 1px; + border-width : 1px 0 0 0; + border-color : #999; + border-style : solid; +} + +pre, code { + font: 100% "Andale Mono","Courier New",monospace; +} +pre { + white-space: pre; + white-space: -moz-pre-wrap; + white-space: -hp-pre-wrap; + white-space: -o-pre-wrap; + white-space: -pre-wrap; + white-space: pre-wrap; + white-space: pre-line; + word-wrap: break-word; +} + + +/* LAYOUT +-------------------------------------------------------- */ +/* General font-size */ +#top, #info-box, #main, #main-menu, #footer { + font-size: 1.1em; +} + +#top { + margin : 0; + padding : 0; + background : transparent url(head-bg.png) repeat-x; +} +#top h1 { + padding : 0; + margin : 0; + height : 58px; + text-indent : -1000px; + background : transparent url(head-logo.png) no-repeat 0 0; +} +#top h1 a { + position: absolute; + top: 3px; + left: 0; + width: 130px; + height: 35px; + border: none; + outline: none; +} + +#info-box { + position : absolute; + right : 10px; + top : 3px; + margin : 0; + padding : 3px 3px 4px 15px; + color: #fff; +} +#info-box div { + margin: 0; padding: 0; +} +#info-box div div { + display: inline; +} +#info-box>form>div { + line-height: 18px; +} +#info-box select { + width : 160px; +} +#info-box a.logout { + font-weight : bold; + color: #fff; +} + +#wrapper { + background: transparent url(page-bg.png) repeat-y top left; + width: 100%; +} +#main { + width : 100%; + float : right; + margin-left : -155px; + margin-top : 0; +} + +#content { + margin-left : 155px; + margin-bottom : 10px; + padding-top : 1px; + margin-right : 15px; +} + +#main-menu { + width : 135px; + float : left; + margin-top : 0; + margin-bottom : 10px; +} +#main-menu h3 { + margin : 0 0 0.5em 0; + padding : 0 0 0 5px; + text-transform: uppercase; + color: #7e7d77; +} +#main-menu ul { + margin : 0 0 1em 4px; + padding : 0; + list-style : none; +} +#main-menu li { + display : block; + margin : 0.5em 0 0 5px; + padding : 2px 0 1px 20px; + background-repeat: no-repeat; + background-position: 0 0; +} +#main-menu a { + font-weight : bold; +} +#main-menu .active a { + border-bottom-style: solid; +} +#main-menu #dashboard-menu li { + margin-left: 0; +} +#main-menu #menu-new-post a { + padding : 2px; + background: #7e7d77; + color: #fff; + border: 1px solid #7e7d77; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} +#main-menu #menu-new-post a:hover, #main-menu #menu-new-post a:focus { + background: #666; +} + +#footer { + clear : both; + margin: 0; + padding: 40px 0 0 0; + background: #fff url(footer-bg.png) no-repeat top left; +} +#footer p * { + vertical-align: bottom; +} +#footer p { + margin: 0 15px 0 155px;; + padding: 3px 0 20px 0; + border-top: 1px solid #dedcd1; + color: #93928a; + text-align: right; + font-size: 1.1em; +} + +#debug { + position: absolute; + top: 0; + width: 100%; + height: 4px; + background: #d99; +} +#debug div { + display: none; + padding: 3px 0.5em 2px; +} +#debug p { + margin : 0.5em 0; +} +#debug:hover { + height: auto; +} +#debug:hover div { + display: block; +} + +/* DASHBOARD */ +#dashboard-main { + float: left; + overflow: hidden; + padding-bottom: 1em; + width: 66%; +} +#dashboard-main #icons { + overflow: hidden; + padding-bottom: 1em; +} +#dashboard-main #icons p { + float: left; + width: 32%; + text-align: center; + margin: 2em 0 0 0; +} +#dashboard-main #icons span { + display: block; +} +#dashboard-main #icons a { + border-bottom-width: 0; +} +#dashboard-main #icons span a { + border-bottom-width: 1px; +} + +#dashboard-main #quick { + clear: left; + margin-top: 2em; +} +#dashboard-main #quick h3 { + margin-bottom: 0.2em; + font-size: 1.4em; +} +#dashboard-main #quick p.qinfo { + margin: -1em -1em 1em; + padding: 1em; + background: #ebeadd; +} + +#dashboard-items { + float: left; + width: 31%; + overflow: hidden; + margin-left: 2%; + padding-bottom: 1em; + font-size: 1.2em; +} +#dashboard-items p img { + vertical-align: middle; +} +#dashboard-items ul { + display: block; + padding-left: 1.5em; + list-style: square; +} +#dashboard-items li { + margin: 0.25em 0 0 0; + color: #666; +} +#dashboard-items #news dt { + font-weight: bold; + margin: 0 0 0.4em 0; +} +#dashboard-items #news dd { + font-size: 0.9em; + margin: 0 0 1em 0; +} +#dashboard-items #news dd p { + margin: 0.2em 0 0 0; +} +#upg-notify { + font-size: 0.9em; +} +#upg-notify ul { + padding-left: 15px; +} +#upg-notify li { + color: #fff; +} + +/* POST */ +#entry-sidebar { + width : 200px; + float : right; +} +#entry-content { + margin-right : 220px; +} +#comments { + clear : both; +} + +/* CATEGORIES */ +#categories { + margin: 1em 0 2em; +} +#categories ul { + list-style: none; + margin: 0 0 0 0; + padding: 0 0 0 0; +} +#categories ul li { + margin: 1em 0 0 0; + padding: 5px 10px 1em 30px; + border: 1px solid #ccc; + background: transparent url(cat-bg.png) repeat-y top left; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; +} +#categories ul li h4 { + margin: 0; +} +#categories ul li h4 span { + font-weight: normal; +} +#categories ul li p { + margin: 0; +} +select#del_cat { + width: 100%; +} + +/* MEDIA */ +#media-icon { + float: left; +} +#media-details { + margin-left: 70px; +} +#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; +} + +#add-file-f { + position: relative; +} +#add-file-f .more-file { + position:absolute; + right: 0.5em; + background: #999; + color: #fff; + border: none; +} + +/* Help */ +#help { + margin-top: 2em; + background: #d6dde5; +} +#help-button { + position: absolute; + top: 50px; + right: 0px; + cursor: pointer; + background: #06c; + -moz-border-radius: 8px 0 0 8px; + -webkit-border-top-left-radius: 8px; + -webkit-border-bottom-left-radius: 8px; +} +.help-box { + display: none; +} +.help-box ul { + padding-left: 20px; + margin-left: 0; +} +#content.with-help #help-button { + right: 280px; +} +#content.with-help #help { + display: block; + position: absolute; + top: 40px; + right: 0; + width: 280px; + border-left: 1px solid #ccc; + margin-top: 0; + padding: 10px 0 0 0; + overflow: auto; +} +#content.with-help .help-content { + padding: 0 5px 1em 5px; +} + +/* POPUP */ +body.popup #wrapper { + background-position : -130px 0; +} +body.popup #top h1 { + background-position : -130px 0; +} +body.popup #main { + margin-left : -35px; +} +body.popup #content { + margin-left : 35px; +} +body.popup #footer { + background-position: -130px 0; +} +body.popup #footer p { + margin-left: 35px; + border: none; +} + +/* CLASSES +-------------------------------------------------------- */ +a.button, a.back { + padding : 2px; + background: #e2dfca; + border: 1px solid #e2dfca; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + cursor: pointer; + outline: none; +} +a.button:hover, a.button:focus, a.back:hover, a.back:focus { + border-color: #ccc; +} +h2 a.button { + color: #333; + font-weight: normal; +} +a.back:before { + content: "\ab\a0"; +} + +.help-content dt { + font-weight: bold; + color: #666; + margin: 0; +} +.help-content dd { + margin: 0.3em 0 1.5em 0; +} + +.clear { + clear : both; +} +.lclear { + clear : left; +} +div.clearer { + height : 1px; + font-size : 1px; +} + +.hide { + display : none; +} + +.right { + text-align : right; +} + +.frame-shrink { + border: 1px solid #666; + padding: 0.5em; + margin-bottom: 1em; + height: 120px; + overflow: auto; +} + +.grid { + background: transparent repeat url('grid.png') 0 0; +} + +.line p { + margin : 0; +} + +div.error, p.error, div.message, p.message, div.static-msg, p.static-msg { + padding: 0.5em 0.5em 0.5em 40px; + margin-bottom: 1em; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} +p.error, p.message, p.static-msg { + padding-top: 1em; + padding-bottom: 1em; +} +div.error, p.error { + background: #e5bfbf url(msg-error.png) no-repeat 5px 5px; + color: #600; +} +div.message, p.message, div.static-msg, p.static-msg { + background: #666 url(msg-std.png) no-repeat 5px 5px; + color: #fff; +} +div.message a, p.message a, div.static-msg a, p.static-msg a { + color: #fff; +} + +.offline { + color : #666; +} + +ul.nice { + margin: 1em 0; + padding: 0 0 0 2em; + list-style: square; +} +ul.nice li { + margin:0; + padding: 0; +} + +.three-cols { +} +.three-cols .col { + width : 32.3%; + float : left; + margin-left : 1%; +} +.three-cols .col:first-child { + width : 33.3%; + margin-left : 0; +} + +.two-cols { + position : static; +} +.two-cols .col { + width : 49%; + margin-left : 1%; + float : left; +} +.two-cols .col:first-child { + width : 50%; + margin-left : 0; +} + +.comment { + border-top : 2px solid #ccc; + margin-bottom : 1em; + padding : 2em 0 1em 0; + position : relative; +} +.comment form p { + margin : 0; + position : absolute; + top : 2px; + right : 0; +} + + +.part-tabs { + float: left; + width: 100%; + background: transparent url(tab-bg.png) repeat-x bottom; + margin-bottom: 2em; +} +.part-tabs ul { + margin: 0; + padding: 10px 10px 0; + list-style: none; +} +.part-tabs li { + float: left; + background: transparent url(tab-n-l.png) no-repeat top left; + margin: 0 3px 0 0; + padding: 0 0 0 5px; +} +.part-tabs a { + display: block; + background: transparent url(tab-n-r.png) no-repeat top right; + padding: 1px 10px 1px 5px; + border: none; + outline: none; +} +.part-tabs li.part-tabs-active { + background-image: url(tab-c-l.png); +} +.part-tabs li.part-tabs-active a { + background-image: url(tab-c-r.png); + padding-bottom: 2px; + font-weight : bold; +} +.part-tabs li.part-tabs-link { + background-image: url(tab-l-l.png); +} +.part-tabs li.part-tabs-link a { + background-image: url(tab-l-r.png); +} + +/* Themes list */ +#themes { + border-bottom: 1px solid #ccc; + margin: 1em 0; +} +#themes div.theme-details { + clear: left; + border-top: 1px solid #ccc; + padding: 1em 0; +} +#themes div.theme-details:hover { + background: #eee; +} +#themes div.theme-details div.theme-shot { + float: left; +} +#themes div.theme-details div.theme-shot img { + display: block; + width: 57px; + height: 50px; + border: 1px solid #ccc; +} +#themes div.theme-details div.theme-info { + margin-left: 67px; +} +#themes div.theme-details div.theme-info span.theme-desc { + display: block; +} +#themes div.theme-details div.theme-info span.theme-version { + color: #666; +} +#themes div.theme-details div.theme-actions { + margin-left: 67px; +} +/* Themes list, JS version */ +#themes-wrapper { +} +#theme-box { + border: 1px solid #999; + border-left: none; + padding: 5px; + float: right; + height: 400px; + width: 300px; + overflow: auto; +} +#theme-box div.theme-shot img { + display: block; + margin: 0 0 0 10px; + width: 280px; + height: 245px; + border: 1px solid #ccc; +} +#theme-box div.theme-info { + margin: 1em 0 0 10px; +} +#theme-box h3 { + margin: 0; +} +#theme-box div.theme-info span { + display: block; +} +#theme-box span.theme-version { + color: #666; +} +#theme-box span.theme-parent-ok { + color: #666; +} +#theme-box span.theme-parent-missing { + color: #c00; + font-weight:bold; +} +#theme-box div.theme-actions { + margin-left: 10px; +} +#themes-wrapper #themes { + border: 1px solid #999; + overflow: auto; + height: 400px; + padding: 5px; + margin: 0; +} +#themes div.theme-details-js { + float: left; + width: 120px; + height: 150px; + margin: 0 10px 20px; + padding: 10px 10px 0; + text-align: center; + background: #f3f3f3; + border: 1px solid #f3f3f3; + cursor: pointer; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; +} +#themes div.theme-details-js label { + cursor: pointer; +} +#themes div.theme-details-js.theme-selected { + background: #ebe9dd; + border: 1px solid #999; +} +#themes div.theme-details-js div.theme-shot img { + width: 120px; + height: 105px; + border: 1px solid #fff; +} +#themes div.theme-details-js h3 { + font-size: 90%; + font-family: inherit; + font-weight: normal; + margin: 0; + padding: 0; +} + +/* Plugins list */ +#plugins td.action { + vertical-align: middle; +} +#plugins td form input.activate, table.plugins form input.delete { + padding: 1px; + background: none; + font-weight: normal; + border-color: #f90; +} +#plugins td form input.delete { + font-weight: bold; + color: #c00; +} + +.media-list { + position : static; +} +.media-col-0 { + clear: left; +} +.media-item { + position: relative; + border-top: 1px solid #ccc; + margin-bottom: 1em; + padding: 5px 0; +} +div.media-list .media-item { + width: 49%; + float: left; + margin-right: 1%; +} +a.media-icon { + display: block; + border-bottom: none; + float: left; +} +.media-icon img { + display: block; +} +.media-item ul { + display: block; + list-style: none; + margin: 0 0 0 60px; + padding: 0; +} +li.media-action { + display: block; + position: absolute; + top: 5px; + right: 5px; + height: 16px; +} +li.media-action a { + border: none; +} +li.media-action form { + display: inline; +} +li.media-action input { + border: none; +} + +.zip-dl { + background: transparent url(package.png) no-repeat 0 50%; + padding: 5px 0 5px 20px; +} + +select.l10n option { + padding-left: 16px; +} +option.avail10n { + background: transparent url(../images/check-on.png) no-repeat 0 50%; +} + +/* TABLES +-------------------------------------------------------- */ +table { + font-size : 1em; + border-collapse : collapse; + margin : 0 0 1em 0; +} +tr.line:hover { + background : #ddd; +} +th, td { + border-width : 0 0 1px 0; + border-style : solid; + border-color : #ccc; + padding : 3px 5px; + vertical-align : top; +} +th { + text-align : left; + border-bottom-color : #666; +} + +.noborder td, td.noborder, .noborder th, th.noborder { + border-width : 0; +} + +table .maximal, table.maximal { + width : 100%; +} +table .minimal { + width : 1px; +} + +table .nowrap { + white-space : nowrap; +} + +td.status { + vertical-align: middle; +} +td.status img { + margin-bottom: -2px; +} +td.status a { + border: none; +} + +tr.line img.expand { + margin-right: 10px; + margin-bottom: -2px; +} +tr.line input { + vertical-align: top; +} +tr.expand td { + border-bottom: none; +} +td.expand { + padding: 1em; +} + +.dragable { + border-collapse: separate; +} +.dragable tbody td { + +} +.handle { + padding : 0; +} +.handler { + cursor : move; + background : transparent url(drag.png) no-repeat 0 50%; + padding-left : 15px; +} + +/* FORMS +-------------------------------------------------------- */ +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: #fbfbfb; +} +legend { + font-weight : bold; + padding: 0.2em 0.6em; + border-width: 1px; + border-style: solid; + border-color: #ccc; + background: #fbfbfb; + margin-bottom: 0.5em; +} +optgroup { + font-weight : bold; + font-style : normal; +} +option { + font-weight : normal; +} + +input, textarea, select { + background : #f9f9f9; + border-width : 1px; + border-style : solid; + border-color : #000 #ccc #ccc #000; +} +input, textarea, select, option { + font: 1em "DejaVu Sans","Lucida Grande","Lucida Sans Unicode",Arial,sans-serif; +} +input[type=text], input[type=password], textarea { + padding : 2px 0; +} +input[type=checkbox], input[type=radio] { + border: none; +} +textarea { + padding : 2px 0; +} +/*input[type=text]:focus, input[type=password]:focus, textarea:focus, option { + background : #dfdcc7; +} can't select text with opera */ +input[type=submit], input[type=reset], input[type=button], input.submit { + padding : 2px 2px; + background: #e2dfca; + border: 1px solid #e2dfca; + color: #333; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + cursor: pointer; +} +input[type=submit]:hover, input[type=reset]:hover, input[type=button]:hover, input.submit:hover { + border-color: #ccc; + color: #06c; +} + +input[type=submit], input.submit { + font-weight : bold; +} + +input[type=checkbox], input[type=radio] { + margin : 0; + padding : 0; + background : transparent; +} + +label { + display : block; +} +label input, label select, label span { + display : block; +} +p.form-note { + margin-top : -1em; + color : #f60; +} + +label.classic { + display : inline; +} +label.classic input, label span input, label.classic select, label span select { + display : inline; +} + +label.area, p.area { + width: inherit !important; +} +.area textarea { + display : block; + width : 100%; +} + +label.required { + font-weight : bold; +} +label.required:before { + content : '* '; + color : #c00; +} + +p.field { + position: relative; + +} +p.field label { + display: block; + width: 14em; +} +p.field input, p.field select { + display: inline; + position: absolute; + left: 15em; + top: 0; +} + +label .maximal, textarea.maximal, input.maximal { + width : 100%; +} + +a.form-control { + display : none; + font-weight: bold; + background: url(magnifier.png) no-repeat 0 0; + color: green; + padding-left: 20px; +} + +fieldset.constrained { + margin: 0; + padding: 0; + border: none; + background: transparent; +} + +#login-screen { + display: block; + width : 180px; + margin : 30px auto 0; + font-size: 1.1em; +} +#login-screen h1 { + text-indent: -2000px; + background: transparent url(dotclear-logo.png) no-repeat top left; + height: 46px; +} +#login-screen fieldset { + border: 1px solid #999; + padding: 1em 10px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; +} +#login-screen input[type=text], #login-screen input[type=password] { + width: 100%; +} \ No newline at end of file diff --git a/admin/style/dotclear-logo.png b/admin/style/dotclear-logo.png new file mode 100644 index 0000000..e39a3ea Binary files /dev/null and b/admin/style/dotclear-logo.png differ diff --git a/admin/style/drag.png b/admin/style/drag.png new file mode 100644 index 0000000..c581786 Binary files /dev/null and b/admin/style/drag.png differ diff --git a/admin/style/farbtastic/farbtastic.css b/admin/style/farbtastic/farbtastic.css new file mode 100644 index 0000000..07d953f --- /dev/null +++ b/admin/style/farbtastic/farbtastic.css @@ -0,0 +1,33 @@ +.farbtastic { + position: relative; +} +.farbtastic * { + position: absolute; + cursor: crosshair; +} +.farbtastic, .farbtastic .wheel { + width: 195px; + height: 195px; +} +.farbtastic .color, .farbtastic .overlay { + top: 47px; + left: 47px; + width: 101px; + height: 101px; +} +.farbtastic .wheel { + background: url(wheel.png) no-repeat; + width: 195px; + height: 195px; +} +.farbtastic .overlay { + background: url(mask.png) no-repeat; +} +.farbtastic .marker { + width: 17px; + height: 17px; + margin: -8px 0 0 -8px; + overflow: hidden; + background: url(marker.png) no-repeat; +} + diff --git a/admin/style/farbtastic/marker.png b/admin/style/farbtastic/marker.png new file mode 100644 index 0000000..db7da55 Binary files /dev/null and b/admin/style/farbtastic/marker.png differ diff --git a/admin/style/farbtastic/mask.png b/admin/style/farbtastic/mask.png new file mode 100644 index 0000000..b0a4d40 Binary files /dev/null and b/admin/style/farbtastic/mask.png differ diff --git a/admin/style/farbtastic/wheel.png b/admin/style/farbtastic/wheel.png new file mode 100644 index 0000000..97b343d Binary files /dev/null and b/admin/style/farbtastic/wheel.png differ diff --git a/admin/style/footer-bg.png b/admin/style/footer-bg.png new file mode 100644 index 0000000..45019be Binary files /dev/null and b/admin/style/footer-bg.png differ diff --git a/admin/style/grid.png b/admin/style/grid.png new file mode 100644 index 0000000..cf9402d Binary files /dev/null and b/admin/style/grid.png differ diff --git a/admin/style/head-bg.png b/admin/style/head-bg.png new file mode 100644 index 0000000..d3f91a3 Binary files /dev/null and b/admin/style/head-bg.png differ diff --git a/admin/style/head-logo.png b/admin/style/head-logo.png new file mode 100644 index 0000000..399c67e Binary files /dev/null and b/admin/style/head-logo.png differ diff --git a/admin/style/iesucks.css b/admin/style/iesucks.css new file mode 100644 index 0000000..32faed0 --- /dev/null +++ b/admin/style/iesucks.css @@ -0,0 +1,42 @@ +label .maximal, textarea.maximal, input.maximal { + width : 98% !important; +} +.area textarea { + width : 98% !important; +} +textarea { + padding : 0 !important; +} +legend { + margin-bottom: 1em !important; +} + +p.area { + width: 98% !important; +} +p.area textarea { + width: 100% !important; +} + +tr.line input { + vertical-align: baseline !important; +} + +#dashboard .col { + width: 48% !important; +} + +td.status img, tr.line img.expand { + margin-bottom: 0 !important; +} + +.wysiwygIframe { + width: 98% !important; +} + +div.media-list .media-item { + width: 48% !important; +} +a.media-icon { + border-bottom:none !important; +} \ No newline at end of file diff --git a/admin/style/jsToolBar/bt_bquote.png b/admin/style/jsToolBar/bt_bquote.png new file mode 100644 index 0000000..bb6be88 Binary files /dev/null and b/admin/style/jsToolBar/bt_bquote.png differ diff --git a/admin/style/jsToolBar/bt_br.png b/admin/style/jsToolBar/bt_br.png new file mode 100644 index 0000000..717ff6c Binary files /dev/null and b/admin/style/jsToolBar/bt_br.png differ diff --git a/admin/style/jsToolBar/bt_clean.png b/admin/style/jsToolBar/bt_clean.png new file mode 100644 index 0000000..3973a2c Binary files /dev/null and b/admin/style/jsToolBar/bt_clean.png differ diff --git a/admin/style/jsToolBar/bt_code.png b/admin/style/jsToolBar/bt_code.png new file mode 100644 index 0000000..40a149f Binary files /dev/null and b/admin/style/jsToolBar/bt_code.png differ diff --git a/admin/style/jsToolBar/bt_del.png b/admin/style/jsToolBar/bt_del.png new file mode 100644 index 0000000..87c3c66 Binary files /dev/null and b/admin/style/jsToolBar/bt_del.png differ diff --git a/admin/style/jsToolBar/bt_em.png b/admin/style/jsToolBar/bt_em.png new file mode 100644 index 0000000..cddfe87 Binary files /dev/null and b/admin/style/jsToolBar/bt_em.png differ diff --git a/admin/style/jsToolBar/bt_img.png b/admin/style/jsToolBar/bt_img.png new file mode 100644 index 0000000..b991a52 Binary files /dev/null and b/admin/style/jsToolBar/bt_img.png differ diff --git a/admin/style/jsToolBar/bt_img_select.png b/admin/style/jsToolBar/bt_img_select.png new file mode 100644 index 0000000..b5bc5bf Binary files /dev/null and b/admin/style/jsToolBar/bt_img_select.png differ diff --git a/admin/style/jsToolBar/bt_ins.png b/admin/style/jsToolBar/bt_ins.png new file mode 100644 index 0000000..f690b90 Binary files /dev/null and b/admin/style/jsToolBar/bt_ins.png differ diff --git a/admin/style/jsToolBar/bt_link.png b/admin/style/jsToolBar/bt_link.png new file mode 100644 index 0000000..07a2b0e Binary files /dev/null and b/admin/style/jsToolBar/bt_link.png differ diff --git a/admin/style/jsToolBar/bt_ol.png b/admin/style/jsToolBar/bt_ol.png new file mode 100644 index 0000000..65d26e4 Binary files /dev/null and b/admin/style/jsToolBar/bt_ol.png differ diff --git a/admin/style/jsToolBar/bt_paragraph.png b/admin/style/jsToolBar/bt_paragraph.png new file mode 100644 index 0000000..5de086e Binary files /dev/null and b/admin/style/jsToolBar/bt_paragraph.png differ diff --git a/admin/style/jsToolBar/bt_post.png b/admin/style/jsToolBar/bt_post.png new file mode 100644 index 0000000..69f0c6f Binary files /dev/null and b/admin/style/jsToolBar/bt_post.png differ diff --git a/admin/style/jsToolBar/bt_pre.png b/admin/style/jsToolBar/bt_pre.png new file mode 100644 index 0000000..b765759 Binary files /dev/null and b/admin/style/jsToolBar/bt_pre.png differ diff --git a/admin/style/jsToolBar/bt_quote.png b/admin/style/jsToolBar/bt_quote.png new file mode 100644 index 0000000..6cd98e1 Binary files /dev/null and b/admin/style/jsToolBar/bt_quote.png differ diff --git a/admin/style/jsToolBar/bt_strong.png b/admin/style/jsToolBar/bt_strong.png new file mode 100644 index 0000000..d2ac922 Binary files /dev/null and b/admin/style/jsToolBar/bt_strong.png differ diff --git a/admin/style/jsToolBar/bt_ul.png b/admin/style/jsToolBar/bt_ul.png new file mode 100644 index 0000000..158790a Binary files /dev/null and b/admin/style/jsToolBar/bt_ul.png differ diff --git a/admin/style/jsToolBar/jsToolBar.css b/admin/style/jsToolBar/jsToolBar.css new file mode 100644 index 0000000..d6b8145 --- /dev/null +++ b/admin/style/jsToolBar/jsToolBar.css @@ -0,0 +1,173 @@ +.jstEditor { + border-width : 0 1px 1px 1px; + border-style : solid; + border-color : #000 #ccc #ccc #000; + padding-left: 5px; + background : #f9f9f9; +} +.jstEditor textarea, .jstEditor iframe { + margin: 0; + border: 0; +} + +.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; +} + +.jstElements { + padding: 3px 3px; + border-width: 1px 1px 0 1px; + border-style: solid; + border-color: #000 #ccc #ccc #000; + background: #eceade; +} + +.jstElements select, .jstElements button { + vertical-align: middle; +} +.jstElements select { + width: 12em; +} +.jstElements button { + margin-right : 2px; + width : 20px; + height: 20px; + padding: 0; + border-style: solid; + border-width: 1px; + border-color: #eceade; + background-color : transparent; + background-position : 50% 50%; + background-repeat: no-repeat; +} +.jstElements button:hover { + border-color : #ccc; + background-color: #dfdbc7; +} +.jstElements button span { + display : none; +} +.jstElements span { + display : inline; +} + +.jstSpacer { + width : 0px; + font-size: 1px; + margin-right: 4px; +} + +.jstSwitcher { + display: block; + list-style: none; + margin: 0 0 0 -5px; + padding: 0 0 5px 0; + background: #eee; + border-top: 1px solid #999; + font-size: 90%; + font-weight: bold; +} +.jstSwitcher li { + display: inline; + margin: 0 0 0 5px; + padding: 2px 4px; +} +.jstSwitcher li.jstSwitcherCurrent { + color: #fff; + background: #999; + -moz-border-radius: 0 0 2px 2px; +} +.jstSwitcher a { + font-weight: normal; + border-bottom: none !important; +} + +/* Buttons +-------------------------------------------------------- */ +.jstb_strong { + background-image: url(bt_strong.png); +} +.jstb_em { + background-image: url(bt_em.png); +} +.jstb_ins { + background-image: url(bt_ins.png); +} +.jstb_del { + background-image: url(bt_del.png); +} +.jstb_quote { + background-image: url(bt_quote.png); +} +.jstb_code { + background-image: url(bt_code.png); +} +.jstb_paragraph { + background-image: url(bt_paragraph.png); +} +.jstb_br { + background-image: url(bt_br.png); +} +.jstb_blockquote { + background-image: url(bt_bquote.png); +} +.jstb_pre { + background-image: url(bt_pre.png); +} +.jstb_ul { + background-image: url(bt_ul.png); +} +.jstb_ol { + background-image: url(bt_ol.png); +} +.jstb_link { + background-image: url(bt_link.png); +} +.jstb_img { + background-image: url(bt_img.png); +} +.jstb_img_select { + background-image: url(bt_img_select.png); +} +.jstb_post_link { + background-image: url(bt_post.png); +} +.jstb_removeFormat { + background-image: url(bt_clean.png); +} + + +/* 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; +} \ No newline at end of file diff --git a/admin/style/jsToolBar/resize.png b/admin/style/jsToolBar/resize.png new file mode 100644 index 0000000..1b21270 Binary files /dev/null and b/admin/style/jsToolBar/resize.png differ diff --git a/admin/style/magnifier.png b/admin/style/magnifier.png new file mode 100644 index 0000000..1578a8d Binary files /dev/null and b/admin/style/magnifier.png differ diff --git a/admin/style/modal/close.png b/admin/style/modal/close.png new file mode 100644 index 0000000..5386506 Binary files /dev/null and b/admin/style/modal/close.png differ diff --git a/admin/style/modal/loader.gif b/admin/style/modal/loader.gif new file mode 100644 index 0000000..e2a116c Binary files /dev/null and b/admin/style/modal/loader.gif differ diff --git a/admin/style/modal/modal.css b/admin/style/modal/modal.css new file mode 100644 index 0000000..8524e7c --- /dev/null +++ b/admin/style/modal/modal.css @@ -0,0 +1,52 @@ +div.jq-modal { + line-height: 1; + background: #fff; +} +div.jq-modal-container { + padding: 1px; + position: relative; +} +div.jq-modal-content { + position: relative; + overflow: auto; +} +div.jq-modal-closer { + position: absolute; + top: -12px; + right: -12px; + width: 25px; + height: 25px; +} +div.jq-modal-closer a { + display: block; + text-indent: -5000px; + width: 25px; + height: 25px; + outline: none; + border: none; +} + +/* modalImage */ +div.jq-modal-content img { + display: block; +} +span.jq-modal-legend { + display: block; + padding: 3px 0; +} +a.jq-modal-next, a.jq-modal-prev { + display: block; + position: absolute; + top: 0; + width: 50%; + bottom: 0; + outline: none; + border: none; + text-indent: -5000px; +} +a.jq-modal-next { + right: 0; +} +a.jq-modal-prev { + left: 0; +} \ No newline at end of file diff --git a/admin/style/msg-error.png b/admin/style/msg-error.png new file mode 100644 index 0000000..eb6cedb Binary files /dev/null and b/admin/style/msg-error.png differ diff --git a/admin/style/msg-std.png b/admin/style/msg-std.png new file mode 100644 index 0000000..3a2ccd6 Binary files /dev/null and b/admin/style/msg-std.png differ diff --git a/admin/style/package.png b/admin/style/package.png new file mode 100644 index 0000000..7b5f24e Binary files /dev/null and b/admin/style/package.png differ diff --git a/admin/style/page-bg.png b/admin/style/page-bg.png new file mode 100644 index 0000000..a048e81 Binary files /dev/null and b/admin/style/page-bg.png differ diff --git a/admin/style/tab-bg.png b/admin/style/tab-bg.png new file mode 100644 index 0000000..4c466be Binary files /dev/null and b/admin/style/tab-bg.png differ diff --git a/admin/style/tab-c-l.png b/admin/style/tab-c-l.png new file mode 100644 index 0000000..e449418 Binary files /dev/null and b/admin/style/tab-c-l.png differ diff --git a/admin/style/tab-c-r.png b/admin/style/tab-c-r.png new file mode 100644 index 0000000..200358c Binary files /dev/null and b/admin/style/tab-c-r.png differ diff --git a/admin/style/tab-l-l.png b/admin/style/tab-l-l.png new file mode 100644 index 0000000..3760520 Binary files /dev/null and b/admin/style/tab-l-l.png differ diff --git a/admin/style/tab-l-r.png b/admin/style/tab-l-r.png new file mode 100644 index 0000000..ef538e6 Binary files /dev/null and b/admin/style/tab-l-r.png differ diff --git a/admin/style/tab-n-l.png b/admin/style/tab-n-l.png new file mode 100644 index 0000000..2d9a8bf Binary files /dev/null and b/admin/style/tab-n-l.png differ diff --git a/admin/style/tab-n-r.png b/admin/style/tab-n-r.png new file mode 100644 index 0000000..420bf7f Binary files /dev/null and b/admin/style/tab-n-r.png differ diff --git a/admin/trackbacks.php b/admin/trackbacks.php new file mode 100644 index 0000000..cad9401 --- /dev/null +++ b/admin/trackbacks.php @@ -0,0 +1,150 @@ +blog->getPosts($params); + + if ($post->isEmpty()) { + $core->error->add(__('This entry does not exist or is not published')); + $can_view_page = false; + } else { + $TB = new dcTrackback($core); + $tb_excerpt = $post->post_excerpt_xhtml.' '.$post->post_content_xhtml; + $post_title = $post->post_title; + $post_url = $post->getURL(); + } +} +else +{ + $core->error->add(__('This entry does not exist.')); + $can_view_page = false; +} + +# Change excerpt +if (!empty($_POST['tb_excerpt'])) { + $tb_excerpt = $_POST['tb_excerpt']; +} + +# Sanitize excerpt +$tb_excerpt = html::clean($tb_excerpt); +$tb_excerpt = html::decodeEntities($tb_excerpt); +$tb_excerpt = text::cutString(html::escapeHTML($tb_excerpt),255); +$tb_excerpt = preg_replace('/\s+/ms',' ',$tb_excerpt); + +# Send pings +if ($post && !$post->isEmpty() && !empty($_POST['tb_urls'])) +{ + $tb_urls = $_POST['tb_urls']; + $tb_urls = str_replace("\r",'',$tb_urls); + + $post_title = html::escapeHTML(trim(html::clean($post_title))); + + foreach (explode("\n",$tb_urls) as $tb_url) + { + try { + $TB->ping($tb_url,$id,$post_title,$tb_excerpt,$post_url); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect('trackbacks.php?id='.$id.'&sent=1'); + } +} + +$page_title = __('Ping blogs'); + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open($page_title,dcPage::jsLoad('js/_trackbacks.js')); + +# Exit if we cannot view page +if (!$can_view_page) { + dcPage::close(); + exit; +} + +if (!empty($_GET['sent'])) { + echo '

    '.__('All pings sent.').'

    '; +} + +echo '

    '.html::escapeHTML($core->blog->name).' › '.$page_title.'

    '; + +echo '

    « '. + sprintf(__('Back to "%s"'),html::escapeHTML($post->post_title)).'

    '; + +echo +'

    '. +html::escapeHTML($post->post_title).'

    '. +'
    '. +($post->post_excerpt_xhtml ? $post->post_excerpt_xhtml.'
    ' : ''). +$post->post_content_xhtml. +'
    '; + +if (!empty($_GET['auto'])) { + flush(); + $tb_urls = implode("\n",$TB->discover($post->post_excerpt_xhtml.' '.$post->post_content_xhtml)); +} else { + $auto_link = ''. + __('Auto discover ping URLs').''; +} + +echo +'

    '.__('Ping blogs').'

    '. +'
    '. +'

    '. + +'

    '. + +'

    '.form::hidden('id',$id). +$core->formNonce(). +'  '. +$auto_link.'

    '. +'
    '; + +$pings = $TB->getPostPings($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 '
    '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/update.php b/admin/update.php new file mode 100644 index 0000000..1495fc3 --- /dev/null +++ b/admin/update.php @@ -0,0 +1,212 @@ +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); +$zip_file = $new_v ? DC_ROOT.'/'.basename($updater->getFileURL()) : ''; + +# 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,array('check','download','backup','unzip')) ? $step : ''; + +$archives = array(); +foreach (files::scanDir(DC_ROOT) as $v) { + if (preg_match('/backup-([0-9A-Za-z\.-]+).zip/',$v)) { + $archives[] = $v; + } +} + +# 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_ROOT.'/'.$b_file)) { + throw new Exception(sprintf(__('Unable to delete file %s'),html::escapeHTML($b_file))); + } + http::redirect($p_url); + } + + if (!empty($_POST['b_revert'])) + { + $zip = new fileUnzip(DC_ROOT.'/'.$b_file); + $zip->unzipAll(DC_ROOT.'/'); + @unlink(DC_ROOT.'/'.$b_file); + http::redirect($p_url); + } + } + 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"') + ); + } + http::redirect($p_url.'?step=backup'); + break; + case 'backup': + $updater->backup( + $zip_file, 'dotclear/inc/digests', + DC_ROOT, DC_ROOT.'/inc/digests', + DC_ROOT.'/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); + } +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +dcPage::open(__('Dotclear update')); + +if (!$core->error->flag()) { + echo '

    '.__('Dotclear update').'

    '; +} + +if (!$step) +{ + if (empty($new_v)) + { + echo '

    '.__('No newer Dotclear version available.').'

    '; + } + else + { + echo + '

    '.sprintf(__('Dotclear %s is available.'),$new_v).'

    '. + + '

    '.__('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.').'

    '. + '
    '. + '

    '. + '

    '. + '
    '; + } + + if (!empty($archives)) + { + echo + '

    '.__('Update backup files').'

    '. + '

    '.__('The following files are backups of previously updates. '. + 'You can revert your previous installation or delete theses files.').'

    '; + + echo '
    '; + + foreach ($archives as $v) { + echo + '

    '; + } + + echo + '

    '.__('Please note that reverting your Dotclear version may have some '. + 'unwanted side-effects. Consider reverting only if you experience strong issues with this new version.').' '. + sprintf(__('You should not revert to version prior to last one (%s).'),end($archives)). + '

    '. + '

    '. + ''. + $core->formNonce().'

    '. + '
    '; + } +} +elseif ($step == 'unzip' && !$core->error->flag()) +{ + echo + '

    '. + __("Congratulations, you're one click away from the end of the update."). + ' '.__('Finish the update.').''. + '

    '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/user.php b/admin/user.php new file mode 100644 index 0000000..5e38136 --- /dev/null +++ b/admin/user.php @@ -0,0 +1,301 @@ +auth->getInfo('user_lang'); +$user_tz = $core->auth->getInfo('user_tz'); +$user_post_status = ''; + +$user_options = $core->userDefaults(); + +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = $k; +} + +# Language codes +$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' : ''); +} + +# 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_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(crypt::hmac(DC_MASTER_KEY,$_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 = $_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_post_status = $user_post_status = $_POST['user_post_status']; + + 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'] = $_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(); + } + + http::redirect('user.php?id='.$new_id.'&upd=1'); + } + # Add user + else + { + if ($core->getUsers(array('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); + + http::redirect('user.php?id='.$new_id.'&add=1'); + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open($page_title, + dcPage::jsConfirmClose('user-form'). + + # --BEHAVIOR-- adminUserHeaders + $core->callBehavior('adminUserHeaders') +); + +if (!empty($_GET['upd'])) { + echo '

    '.__('User has been successfully updated.').'

    '; +} + +if (!empty($_GET['add'])) { + echo '

    '.__('User has been successfully created.').'

    '; +} + +echo '

    '.__('Users').' › '.$page_title.'

    '; + +if ($user_id == $core->auth->userID()) { + echo + '

    '.__('Warning:').' '. + __('If you change your username, you will have to log in again.').'

    '; +} + +echo +'
    '. +'
    '.__('User information').''. +'
    '. +'
    '. +'

    '. +'

    '.__('At least 2 characters using letters, numbers or symbols.').'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '. + +'
    '. +'

    '. +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '. +'
    '. +'
    '; + +# --BEHAVIOR-- adminUserForm +$core->callBehavior('adminUserForm',isset($rs) ? $rs : null); + +echo +'
    '. +'

    '. +'
    '. +'

    '. +($user_id != '' ? form::hidden('id',$user_id) : ''). +$core->formNonce(). +'

    '. + +'
    '; + +if ($user_id) +{ + echo '
    '.__('Permissions').''; + + $permissions = $core->getUserPermissions($user_id); + $perm_types = $core->auth->getPermissionsTypes(); + + if (count($permissions) == 0) + { + echo '

    '.__('No permissions.').'

    '; + } + else + { + foreach ($permissions as $k => $v) + { + if (count($v['p']) > 0) + { + echo '

    '. + html::escapeHTML($v['name']).' ('.html::escapeHTML($k).') - '. + '' + .__('change permissions').'

    '; + + echo '
      '; + foreach ($v['p'] as $p => $V) { + if (isset($perm_types[$p])) { + echo '
    • '.__($perm_types[$p]).'
    • '; + } + } + echo '
    '; + } + } + } + + echo + '

    '. + __('Add new permissions').'

    '. + '
    '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/users.php b/admin/users.php new file mode 100644 index 0000000..195cabd --- /dev/null +++ b/admin/users.php @@ -0,0 +1,167 @@ +callBehavior('adminBeforeUserDelete',$u); + + $core->delUser($u); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect('users.php?del=1'); + } +} + + +# Creating filter combo boxes +$sortby_combo = array( +__('Username') => 'U.user_id', +__('Last Name') => 'user_name', +__('First Name') => 'user_firstname', +__('Display name') => 'user_displayname', +__('Number of entries') => 'nb_post' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + + +# Get users +$page = !empty($_GET['page']) ? $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = $_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'] = array((($page-1)*$nb_per_page),$nb_per_page); + +$show_filters = false; + +# - Search filter +if ($q) { + $params['q'] = $q; + $show_filters = true; +} + +# - Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + $show_filters = true; + } +} + +try { + $rs = $core->getUsers($params); + $counter = $core->getUsers($params,1); + $user_list = new adminUserList($core,$rs,$counter->f(0)); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_users.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} + +dcPage::open(__('users'),$starting_script); + +if (!$core->error->flag()) +{ + if (!empty($_GET['del'])) { + echo '

    '.__('User has been successfully removed.').'

    '; + } + + echo '

    '.__('Users').'

    '; + + echo '

    '.__('Create a new user').'

    '; + + if (!$show_filters) { + echo '

    '.__('Filters').'

    '; + } + + echo + '
    '. + '
    '.__('Filters').''. + + '
    '. + '

    '. + '

    '. + '
    '. + + '
    '. + '

    '. + '

    '. + '

    '. + '
    '. + + '
    '. //Opera sucks + '
    '. + '
    '; + + # Show users + $user_list->display($page,$nb_per_page, + '
    '. + + '%s'. + + '
    '. + '

    '. + + '

    '. + ''. + '

    '. + '
    '. + '
    ' + ); +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/admin/xmlrpc.php b/admin/xmlrpc.php new file mode 100644 index 0000000..39b3e0c --- /dev/null +++ b/admin/xmlrpc.php @@ -0,0 +1,35 @@ +plugins->loadModules(DC_PLUGINS_ROOT); + +# Start XML-RPC server +$server = new dcXmlRpc($core,$blog_id); +$server->serve(); +?> \ No newline at end of file diff --git a/cache/.htaccess b/cache/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/cache/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/cache/cbfeed/6a/4f/6a4f55e24111799aaa8e45c1cc4d4583.php b/cache/cbfeed/6a/4f/6a4f55e24111799aaa8e45c1cc4d4583.php new file mode 100644 index 0000000..2830353 --- /dev/null +++ b/cache/cbfeed/6a/4f/6a4f55e24111799aaa8e45c1cc4d4583.php @@ -0,0 +1,304 @@ +O:10:"feedParser":7:{s:9:"feed_type";s:8:"atom 1.0";s:5:"title";s:13:"Blog Dotclear";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:"2023-10-16T10:50:34+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/2023/08/31/Dotclear-2.27.3";s:5:"title";s:15:"Dotclear 2.27.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:276:"

    Une nouvelle version qui corrige quelques problèmes rencontrés avec l'installation, la gestion des dépendances des plugins et thèmes, la procédure de récupération de mot de passe, l'export à plat et lorsque certains réglages de widget étaient incorrects.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-08-31T16:34:00+02:00";s:2:"TS";i:1693492440;}i:1;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/08/22/Dotclear-2.27.2";s:5:"title";s:15:"Dotclear 2.27.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:300:"

    Une nouvelle version de maintenance qui comporte quelques corrections d'erreurs rencontrées avec les versions 2.27 et 2.27.1 précédentes.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-08-22T11:05:00+02:00";s:2:"TS";i:1692695100;}i:2;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/08/14/Dotclear-2.27.1";s:5:"title";s:15:"Dotclear 2.27.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:217:"

    Forcément, on regarde partout, on teste mille fois, et puis la petite erreur de frappe vient s'insinuer dans les rouages bien huilés et donc ce matin une petite version de maintenance sans effet visible.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-08-14T09:47:00+02:00";s:2:"TS";i:1691999220;}i:3;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2023/08/13/Dotclear-2.27";s:5:"title";s:13:"Dotclear 2.27";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1223:"

    Comme souvent on avait prévu des monts et des merveilles et puis chemin faisant on est redevenu raisonnable compte-tenu de nos disponibilités respectives avec toutefois quelques améliorations visibles et surtout beaucoup de travail invisible pour moderniser le code et supprimer les vieilleries.

    + +

    Ce trimestre la version 2.27 apporte la prévisualisation des thèmes avec le contenu actuel du blog, ça peut être utile pour se rendre compte visuellement avant d'activer un thème, et puis on a ajouté et complété (à l'aide de moteurs de traduction) quelques langues ; mais surtout cette version est la dernière à supporter PHP 7.4 et PHP 8.0.

    + +

    Nous vous conseillons donc, si vous souhaitez poursuivre les mises à jour futures, de basculer sur PHP 8.1 ou PHP 8.2. Notez que ne pas le faire ne vous empêchera pas d'utiliser encore longtemps la 2.27, à vous de voir.

    + +

    Pour finir, nous souhaitons un joyeux anniversaire pour les vingt ans de DotClear avec un C majuscule, comme Olivier l'écrivait dans son billet d'annonce !

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-08-13T08:43:00+02:00";s:2:"TS";i:1691908980;}i:4;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/06/04/Dotclear-2.26.1";s:5:"title";s:15:"Dotclear 2.26.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:708:"

    Une petite version de maintenance, habituelle après une version majeure et qui règle quelques problèmes rencontrés depuis.

    + +

    La prochaine version majeure (2.27) est prévue pour mi-août, ça sera d'ailleurs l'occasion de fêter les 20 ans de Dotclear et sachez que cette version sera la dernière à supporter encore PHP 7.4.

    + +

    À partir de la 2.28 qui sera publiée à la fin de l'année (mi-novembre a priori) il faudra avoir a minima PHP 8.1 pour fonctionner.

    + +

    Vous pouvez d'ores et déjà vous y préparer en basculant sur cette version, voire encore mieux sur la 8.2 puisque ça fait quelques versions que Dotclear la supporte bien !

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-06-04T09:15:00+02:00";s:2:"TS";i:1685862900;}i:5;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2023/05/13/Dotclear-2.26";s:5:"title";s:13:"Dotclear 2.26";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1228:"

    Une nouvelle version dont la mise à jour devrait passer sans problème majeur chez la plupart d'entre vous.

    + +

    Rien d'extraordinaire cette fois, surtout des petites améliorations ici et là, et c'est surtout du côté du code que l'essentiel à été fait car pour la première fois depuis la première version de Dotclear 2, la librairie Clearbricks n'en fait plus partie, ou plutôt a été intégrée directement dans le code de Dotclear.

    + +

    C'était également l'occasion de renforcer les tests unitaires et l'analyse statique du code (avec PHPStan, PSalm et Rector) qui continuera d'être progressivement modernisé (il en reste pas mal).

    + +

    Un nouveau thème est en cours de développement et il devrait, si possible, faire partie de la prochaine version majeure — qui sonnera aussi les 20 ans de Dotclear —, mais comme on a dit souvent ici : « Ça sera le cas quand il sera prêt ! ».

    + +

    Par ailleurs, la reprise de l'architecture de l'administration est à l'étude, en particulier pour bénéficier des dernières possibilités de HTML et CSS. Il y aura possiblement un peu de casse, mais on essaiera de limiter les dégâts, comme à notre habitude.

    + +

    Bonne mise à jour !

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-05-13T06:08:00+02:00";s:2:"TS";i:1683950880;}i:6;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/02/25/Dotclear-2.25.3";s:5:"title";s:15:"Dotclear 2.25.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:82:"

    Une mise à jour qui corrige quelques bugs rencontrés avec la 2.25.2.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-02-25T09:16:00+01:00";s:2:"TS";i:1677312960;}i:7;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/02/19/Dotclear-2.25.2";s:5:"title";s:15:"Dotclear 2.25.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:82:"

    Une mise à jour qui corrige quelques bugs rencontrés avec la 2.25.1.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-02-19T09:13:00+01:00";s:2:"TS";i:1676794380;}i:8;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/02/18/Dotclear-2.25.1";s:5:"title";s:15:"Dotclear 2.25.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:80:"

    Une mise à jour qui corrige quelques bugs rencontrés avec la 2.25.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-02-18T13:08:00+01:00";s:2:"TS";i:1676722080;}i:9;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2023/02/13/Dotclear-2.25";s:5:"title";s:13:"Dotclear 2.25";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:3009:"

    Une nouvelle version de Dotclear pour laquelle vous devriez rencontrer moins de soucis, si c'était le cas, qu'au moment de la mise à jour en 2.24.

    + +

    Cette version intègre essentiellement une modernisation du code — modernisation qui devrait encore se poursuivre pendant quelques trimestres.

    + +

    Il est possible que dans un futur proche la version de PHP requise évolue, en particulier pour nous permettre d'accentuer nos efforts sur la modernisation du code. Pour l'instant c'est encore PHP 7.4, mais l'interface d'administration de la 2.25 vous signalera, si vous utilisez encore cette version, qu'il est temps de songer à basculer sur PHP 8.0 ou encore PHP 8.1, voire PHP 8.2 pour laquelle nous nous sommes efforcés d'assurer la compatibilité.

    + +

    Une petite note à l'attention des développeurs de plugins : Il était jusqu'à maintenant optionnel de définir le type de module dans le fichier _define.php ; il est fort probable que la future 2.26 l'impose, alors ajoutez cette ligne dans les propriétés :

    + +
    +'type' => 'plugin',
    +
    + +

    Prenez exemple sur un des plugins distribués avec Dotclear si nécessaire.

    + +

    Le CHANGELOG de la 2.25 :

    + +
      +
    • 🐘 PHP 7.4+ is required, PHP 8.0/8.1 compliance (and as far as we know PHP 8.2)
    • +
    • Core: New plugin/theme code structure (using a brand new autoloader), old plugins/themes remain compatibles
    • +
    • Core: Clearbricks is now included in Dotclear code, not more as a git sub-module
    • +
    • Core: Update last step will not redirect to the safe-mode login page
    • +
    • Core: No more need to add namespace (blog-settings) / workspace (user-preferences) before using them
    • +
    • Core: Add possible using of external db driver (Experimental)
    • +
    • Core: Add support of .mjs ECMAScript module files
    • +
    • Core: Remove unnecessary and weak protection code
    • +
    • Core: Move some legacy and proxy code in dcProxyV2 plugin
    • +
    • a11y: Some aria attributes have been set in backend
    • +
    • Theme: Merge default smilies and blog's smilies (the blog's smilies have a higher priority)
    • +
    • Theme: Merge plugin blowupConfig into theme Blowup
    • +
    • Admin: Some jQuery javascript scripts have been rewritten in pure ECMAScript
    • +
    • Admin: Add Atkinson Hyperlegible font (used by default in admin, may be disabled in user pref)
    • +
    • Admin: Add some shortcuts (CTRL+letter) to dcLegacyEditor toolbar
    • +
    • Admin: Add a specific warning message on update page if necessary (breaking changes)
    • +
    • Admin: Cope with no user TZ defined (use UTC in this case)
    • +
    • Admin: Fix admin permission management for blogroll and pages plugins
    • +
    • Admin: Cope with float/double values in about:config and user:pref
    • +
    • 🐛 → Various bugs, a11y concerns and typos fixed
    • +
    • 🌼 → Some locales and cosmetic adjustments
    • +
    • 📣 Warning: Next major release (2.26) may require PHP 8.0 (announced in backend) or PHP 8.1
    • +
    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-02-13T09:25:00+01:00";s:2:"TS";i:1676276700;}i:10;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2023/01/18/Dotclear-2.24.1";s:5:"title";s:15:"Dotclear 2.24.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:97:"

    Une mise à jour qui corrige quelques bugs rencontrés avec la 2.24 de Noël dernier.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2023-01-18T12:13:00+01:00";s:2:"TS";i:1674040380;}i:11;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2022/12/24/Dotclear-2.24";s:5:"title";s:13:"Dotclear 2.24";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1827:"

    La nouvelle version pour les fêtes de fin d'année. Il est fortement recommandé de faire la mise à jour en mode de secours, ce qui vous permettra ensuite de mettre à jour les plugins qui doivent l'être.

    + +

    Si vous avez un souci pour vous connecter après la mise à jour, supprimez les cookies associés avant de rafraichir la page de connexion.

    + +

    Le CHANGELOG de la 2.24 :

    + +
      +
    • 🐘 PHP 7.4+ is required, PHP 8.0/8.1 compliance
    • +
    • 🗑 Remove XML/RPC system (keep only minimum for Pingbacks)
    • +
    • New blog parameter to close comments/trackbacks after a period of inactivity on the blog
    • +
    • Core: Large code review has been done, may break old code (3rd party plugins and themes)
    • +
    • Admin UI: New default icons for media items
    • +
    • Admin UI: Message look reviewed
    • +
    • Admin UX: Preserve current dir and current view of media manager
    • +
    • Admin UX: Password strength use an entropy indicator
    • +
    • Admin UX: Improve navigation in about:config and user:preferences list
    • +
    • Admin UX: Allow activation and de-activation of plugins in safe mode
    • +
    • Admin UX: Allow update of disabled/activated plugins in safe mode/normal mode
    • +
    • Admin UX: Add folding capability to widgets group
    • +
    • Theme: Cope with theme defined widget container format
    • +
    • Theme: Smilies are available for every theme (Blowup theme not more mandatory)
    • +
    • Lib: Update CKEditor to 4.20.1
    • +
    • Lib: Update Codemirror to 5.65.10
    • +
    • 🐛 → Various bugs, a11y concerns and typos fixed
    • +
    • 🌼 → Some locales and cosmetic adjustments
    • +
    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-12-24T10:00:00+01:00";s:2:"TS";i:1671872400;}i:12;O:8:"stdClass":8:{s:4:"link";s:78:"https://fr.dotclear.org/blog/post/2022/12/13/Faire-la-mise-%C3%A0-jour-en-2.24";s:5:"title";s:29:"Faire la mise à jour en 2.24";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:5541:"

    + +

    On va pas se mentir, la prochaine mise à jour pourra être … sportive1 :-)

    + +

    Je viens de tester plusieurs fois, à partir d'une installation qui tourne en production (mon blog) avec Dotclear 2.23.1 et j'ai noté les choses suivantes :

    + +
      +
    1. Si jamais vous utilisez le plugin de cache statique2, désactivez-le temporairement (il suffit de commenter la constante d'activation DC_SC_CACHE_ENABLE dans le fichier inc/config.php, ou de la mettre à false).

    2. +
    3. Faire la mise à jour vers Dotclear 2.24 en mode de secours est la meilleure façon de faire, une fois que vous savez3 que les plugins que vous utilisez sont disponibles pour la 2.244.

    4. +
    5. Si jamais vous aviez besoin du plugin FakeMeUp, alors reconnectez-vous en mode normal, installez-le, lancez-le, puis reconnectez-vous en mode de secours.

    6. +
    7. Place aux mises à jour :

      + +
        +
      1. Faites la mise à jour de Dotclear en 2.24 (toujours en mode de secours),
      2. +
      3. Reconnectez-vous en mode de secours car l'étape précédente vous ramènera sur la page d'authentification5,
      4. +
      5. Faites la mise à jour des plugins6,
      6. +
      7. Réactivez le cache statique si nécessaire,
      8. +
      9. Reconnectez-vous en mode normal.
      10. +
    8. +
    + +

    Ça devrait être tout bon !

    + +

    Profitez-en pour vider le cache des templates et le cache statique (plugin Entretien).

    + +

    Petit supplément : il peut être utile d'installer le plugin growUp pour faire un peu de ménage7 une fois la mise à jour effectuée8.

    + +

    Quoi qu'il en soit on sera dans les parages si jamais il y avait un problème ; sur le forum en particulier.

    + +
    +
    +
      + +
    1. +

      Pour être honnête ma première tentative s'est heurté à un problème avec le cache statique (voir le point 1 de la liste), sinon j'ai pu faire la mise à jour en mode normal sans souci. 

      +
    2. + +
    3. +

      Surtout si vous l'utilisez, comme moi, de façon agressive. 

      +
    4. + +
    5. +

      Installez le plugin Check store version, il vous dira tout ça. 

      +
    6. + +
    7. +

      Dans le cas contraire, attendez qu'ils le soient, c'est préférable ! 

      +
    8. + +
    9. +

      Il se peut que la page d'authentification ne s'affiche pas, dans ce cas supprimez les cookies associés au site et rafraichissez la page. 

      +
    10. + +
    11. +

      Forcez la vérification de mise à jour pour être sûr·e de ne rien louper. 

      +
    12. + +
    13. +

      C'est normalement effectué pendant la mise à jour, mais parfois il peut se passer des bricoles ;-) 

      +
    14. + +
    15. +

      Il peut être désactivé ou désinstallé ensuite. 

      +
    16. + +
    +
    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-12-13T15:13:00+01:00";s:2:"TS";i:1670940780;}i:13;O:8:"stdClass":8:{s:4:"link";s:77:"https://fr.dotclear.org/blog/post/2022/12/03/Mode-de-secours-de-Dotclear-2.24";s:5:"title";s:32:"Mode de secours de Dotclear 2.24";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:7114:"

    + +

    La version 2.24 de Dotclear — qui devrait être publiée dans quelques semaines — a été l'objet d'une reprise du code assez profonde, pour assurer une meilleure robustesse et pour évacuer quelques vieilleries qu'elle transportait depuis longtemps. Cependant cette refonte a généré quelques incompatibilités avec les versions précédentes des plugins et parfois des thèmes.

    + +

    En conséquence, une fois mis à jour avec la version 2.24 de Dotclear il se pourrait bien que vous ayez du mal à retrouver votre tableau de bord ou autre chose qu'une page blanche du côté public de votre blog préféré.

    + +

    Message d'erreur d'un plugin

    + +

    La raison ?

    + +

    Un ou plusieurs plugins qui nécessitent des mises à jour — celles-ci sont déjà disponibles pour un nombre conséquent de plugins tiers —, ou des désactivations.

    + +

    Or comment faire cette ou ces mises à jour, voire cette ou ces désactivations si vous n'avez pas accès à l'administration de votre installation ?

    + +

    C'est très exactement pour cette raison qu'il y a, depuis plusieurs années, un mode de secours — aussi appelé « mode sans échec » — qui permet de se connecter à l'interface d'administration sans activer aucun plugin. Et quand je dis aucun, c'est vraiment aucun ; même les plugins livrés avec Dotclear sont désactivés dans ce mode particulier.

    + +
    +

    Attention

    +

    Il vous est fortement conseillé, avant de mettre à jour votre installation vers la version 2.24, de lister les plugins tiers installés et actifs (et de mettre cette liste de côté), ils sont les plus susceptibles de poser des problèmes une fois la mise à jour effectuée, pour évaluer ensuite la liste des mises à jour requises, voire les désactivations à prévoir si elles n'étaient pas disponibles

    +

    Il existe d'ailleurs un plugin (pour les versions 2.19 à 2.23.1), nommé CheckStoreVersion, développé par Jean-Christian Denis, et qui permet d'ajouter un onglet supplémentaire sur la page de gestion des plugins (et des thèmes), et qui indique la liste des mises à jours disponibles pour les plugins (et thèmes) actuellement installés. Ce plugin est disponible sur DotAddict.

    +
    + +

    La nouveauté avec la version 2.24 de Dotclear est que vous pouvez, avec ce mode de secours, désactiver, mettre à jour, supprimer ou installer des plugins, ceux-ci restant bien évidemment inactifs tant que vous êtes connecté dans ce mode.

    + +

    En pratique, comment ça se passe ?

    + +

    Premièrement il faut vous connecter en mode de secours. Pour cela, sur la page de connexion vous avez un lien situé en bas et intitulé « Problème de connexion ? » :

    + +

    Page de connexion normale

    + +

    Si vous cliquez sur ce lien vous démasquez alors deux liens. Le premier permet d'engager la procédure à utiliser en cas d'oubli de son mot de passe — mais ce n'est pas l'objet de ce billet —, et le second vous permet de basculer en mode de secours :

    + +

    Page de connexion normale avec les liens de secours

    + +
    +

    Astuce

    +

    Il se peut que même l'accès à la page de connexion soit impossible. Dans ce cas il vous suffira d'ajouter à la fin de l'URL de connexion que vous utilisez habituellement ceci : +

    ?safe_mode=1
    +Safe mode étant l'équivalent anglais de Mode de secours (ou mode sans échec).

    + +

    Si par exemple votre URL de connexion est :

    +
    https://example.com/admin/auth.php
    +

    alors l'URL de connexion en mode de secours sera :

    +
    https://example.com/admin/auth.php?safe_mode=1
    +
    + +

    Il faut donc cliquer sur ce deuxième lien qui vous amènera alors sur une autre page de connexion :

    + +

    Page de connexion en mode de secours

    + +

    Remplissez les champs comme à l'accoutumée et validez. Vous devriez maintenant avoir le tableau de bord affiché avec un beau message vous avertissant que vous êtes en mode de secours :

    + +

    Tableau de bord avec le message concernant le mode de secours

    + +

    Vous remarquerez alors, que comme précisé plus haut, aucun plugin n'est actif ; par contre vous avez la possibilité de basculer sur la page de gestion des plugins qui fonctionne de la même manière qu'en mode normal.

    + +

    Vous retrouverez donc les onglets classiques qui listent les plugins en attente de mise à jour, les plugins installés, activés ou pas, ainsi que les onglets qui permettent l'installation automatique ou manuelle de plugins :

    + +

    Page de gestion des plugins en mode de secours

    + +

    Chaque action dans cette page ne vous sortira pas du mode de secours où aucun plugin n'est actif, donc aucun danger de « casser » votre installation ici.

    + +

    Une fois tous les plugins mis à jour ou désactivés en attendant une prochaine mise à jour — comparez avec la liste que vous avez mis de côté avant de mettre à jour pour vérifier que vous n'avez rien oublié —, vous pouvez revenir à la connexion habituelle.

    + +

    Il suffit pour cela de vous déconnecter et de vous reconnecter comme à l'ordinaire.

    + +
    + +

    Gardez ce billet sous le coude pour le jour où vous ferez la mise à jour vers la 2.24, il se pourrait qu'il vous serve.

    +";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-12-03T15:38:00+01:00";s:2:"TS";i:1670078280;}i:14;O:8:"stdClass":8:{s:4:"link";s:74:"https://fr.dotclear.org/blog/post/2022/10/27/Adapter-son-code-pour-la-2.24";s:5:"title";s:29:"Adapter son code pour la 2.24";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1756:"

    + +

    Bonjour,

    + +

    J'ai entrepris, depuis quelques jours, de publier sur mon blog une série de billets expliquant, pas à pas, comment mettre à niveau le code de vos plugins et thèmes pour assurer la compatibilité avec la future version 2.24 de Dotclear, qui devrait être publiée dans quelques semaines.

    + +

    Un flux Atom est également disponible pour cette série avec ce lien.

    + +

    En effet, cette future version de Dotclear rompt avec le passé récent et la rétro-compatibilité habituelle assurée de version en version depuis quelques années ; c'est le coût de la modernisation et du renforcement de la robustesse du cœur de Dotclear.

    + +

    En conséquence il est possible que certains plugins (et peut-être thèmes) « bloquent » l'accès à votre blog et/ou à l'administration de votre installation après la mise à jour en 2.24. Il faudra alors vérifier si une mise à jour de ceux-ci est disponible ou dans le cas contraire les désactiver.

    + +

    À ce sujet je reviendrai dans un futur billet sur le mode de secours, dont les fonctionnalités ont été améliorées, au moment de la sortie de la version 2.24.

    + +

    Notez par ailleurs qu'il est d'ores et déjà possible de publier ses plugins et thèmes dépendants de la version 2.24 sur DotAddict (via la console).

    +";s:7:"subject";a:1:{i:0;s:14:"Développement";}s:7:"pubdate";s:25:"2022-10-27T08:14:00+02:00";s:2:"TS";i:1666851240;}i:15;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2022/08/13/Dotclear-2.23.1";s:5:"title";s:15:"Dotclear 2.23.1";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:108:"

    Et dans la foulée, une version de maintenance pour corriger un bug avec l'ajout de commentaires.

    ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-08-13T10:01:00+02:00";s:2:"TS";i:1660377660;}i:16;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2022/08/13/Dotclear-2.23";s:5:"title";s:13:"Dotclear 2.23";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:2437:"

    La nouvelle version pour ce trimestre avec quelques améliorations au programme essentiellement dans le cœur du code, mais pas que.

    + + +

    Notez la disparition de la gestion des jeux d'icônes, peu utilisée et potentiellement complexifiée avec l'usage des icônes au format SVG en deux versions (thème clair et sombre).

    + + +

    Le CHANGELOG de la 2.23 :

    + +
      +
    • 🐘 PHP 7.4+ is required, PHP 8.0/8.1 compliance
    • +
    • 🗑 Remove Iconset management
    • +
    • Admin UI: Harmonize font size on different support (laptop, tablet, mobile)
    • +
    • Admin UX: Group more logically buttons on CKEditor toolbar
    • +
    • Core: New constant DC_DEFAULT_THEME, set to 'berlin'
    • +
    • Core: Use predefined constants for post statuses (dcBlog::POST_*)
    • +
    • Core: Use predefined constants for comment statuses (dcBlog::COMMENT_*)
    • +
    • Core: Deprecated global $core (or $GLOBALS'core'), use dcCore::app() instead
    • +
    • Core: Deprecated global $_ctx, use dcCore::app()->ctx instead
    • +
    • Core: Deprecated global $_lang, use dcCore::app()->lang instead
    • +
    • Core: Deprecated global $mod_files, use dcCore::app()->cache'mod_files' instead
    • +
    • Core: Deprecated global $mod_ts, use dcCore::app()->cache'mod_ts' instead
    • +
    • Core: Deprecated global $_menu, use dcCore::app()->menu instead
    • +
    • Core: Deprecated global $__resources, use dcCore::app()->resources instead
    • +
    • Core: REST server now accepts JSON format (experimental)
    • +
    • Fix: Use relative URL for attachments as far as possible
    • +
    • Fix: Remove select hiding mechanism when help is displayed
    • +
    • Fix: Loading of modules (plugins/themes) in safe mode
    • +
    • Fix: Message position on Quick entry submit (dashboard)
    • +
    • Fix: Select appearance on Safari (webkit engine)
    • +
    • Lib: Update CKEditor to 4.19.1
    • +
    • Lib: Update Codemirror to 5.65.7
    • +
    • 🐛 → Various bugs, a11y concerns and typos fixed
    • +
    • 🌼 → Some locales and cosmetic adjustments
    • +
    • 📣 Warning: Internet Explorer is not more officially supported (may still work weirdly)
    • +
    ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-08-13T09:30:00+02:00";s:2:"TS";i:1660375800;}i:17;O:8:"stdClass":8:{s:4:"link";s:58:"https://fr.dotclear.org/blog/post/2022/05/13/Dotclear-2.22";s:5:"title";s:13:"Dotclear 2.22";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:1381:"

    La nouvelle version pour ce trimestre — on tient le rythme, c'est une bonne chose — avec quelques améliorations au programme :

    + +
      +
    • PHP 7.4 minimum et c'est compatible — sauf bug de dernière minute — jusqu'à PHP 8.1 inclus
    • +
    • On a retiré le système de traçage FLoC de Google qui a jeté l'éponge entretemps
    • +
    • Ajout d'un bouton de prévisualisation dans l'éditeur standard de Dotclear ; pratique pour vérifier les modifications d'un billet déjà publié, avant de les sauvegarder
    • +
    • Les thèmes Berlin et Ductile ne requièrent plus jQuery pour fonctionner ; vérifiez toutefois les plugins tiers que vous avez installé, ils peuvent toujours en avoir besoin côté public
    • +
    • Amélioration de la récupération des métadonnées de la source d'un Webmention ou d'un Pingback
    • +
    • Ajout d'un bouton « Réinitialiser à maintenant Â» pour la date de publication d'un billet ; permet de pallier l'absence de cette fonctionnalité sur certains navigateurs
    • +
    • Réduction des seuils de bascule de l'affichage de l'administration (mobile, tablette et laptop)
    • +
    • Le menu d'accès rapide aux sections de about:Config et user:Pref est maintenant en position fixe en haut de l'écran
    • +
    • Léger redesign des icônes de la barre d'outils de l'éditeur standard de Dotclear
    • +
    ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-05-13T08:17:00+02:00";s:2:"TS";i:1652422620;}i:18;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2022/03/07/Dotclear-2.21.3";s:5:"title";s:15:"Dotclear 2.21.3";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:146:"

    Une nouvelle version qui corrige deux bugs concernant la gestion des utilisateurs autres qu'administrateurs (ou super-administrateurs).

    ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-03-07T10:17:00+01:00";s:2:"TS";i:1646644620;}i:19;O:8:"stdClass":8:{s:4:"link";s:60:"https://fr.dotclear.org/blog/post/2022/02/26/Dotclear-2.21.2";s:5:"title";s:15:"Dotclear 2.21.2";s:7:"creator";s:6:"Franck";s:11:"description";s:0:"";s:7:"content";s:391:"

    Une nouvelle version de maintenance qui résout, entre autres, les problèmes de date et de cache rencontrés par certains utilisateurs.

    + + +

    Changements :

    + +
      +
    • Retour en arrière sur certaines modifications faites pour la conformité à PHP 8.1 (strftime)
    • +
    • Nettoyage des dossiers currywurst restants (template currywurst supprimé depuis la 2.20)
    • +
    ";s:7:"subject";a:1:{i:0;s:4:"News";}s:7:"pubdate";s:25:"2022-02-26T09:32:00+01:00";s:2:"TS";i:1645864320;}}} \ No newline at end of file diff --git a/cache/cbtpl/06/16/061624c7d38d3b032e8770d96e17d8da.php b/cache/cbtpl/06/16/061624c7d38d3b032e8770d96e17d8da.php new file mode 100644 index 0000000..73490f4 --- /dev/null +++ b/cache/cbtpl/06/16/061624c7d38d3b032e8770d96e17d8da.php @@ -0,0 +1,61 @@ +xml version="1.0" encoding="utf-8""; ?> + + + <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?><?php if ($_ctx->feed_subtitle !== null) { echo context::global_filter($_ctx->feed_subtitle,1,0,0,0,0,'SysFeedSubtitle');} ?> + - <?php echo __('Comments'); ?> + blog->desc,1,0,0,0,0,'BlogDescription'); ?> + + + blog->upddt,$core->blog->settings->blog_timezone),0,0,0,0,0,'BlogUpdateDate'); ?> + + blog->settings->editor,1,0,0,0,0,'BlogEditor'); ?> + + blog->uid,0,0,0,0,0,'BlogFeedID'); ?> + Dotclear + + exists("meta")) { @$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)."' "; +} ?> +posts !== null) { $params['post_id'] = $_ctx->posts->post_id; $core->blog->withoutPassword(false); +} +if ($_ctx->nb_comment_per_page !== null) { $params['limit'] = $_ctx->nb_comment_per_page; } +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] <?php echo context::global_filter($_ctx->pings->post_title,1,0,0,0,0,'PingEntryTitle'); ?> - <?php echo context::global_filter($_ctx->pings->comment_author,1,0,0,0,0,'PingBlogName'); ?> + + pings->getFeedID(),0,0,0,0,0,'PingFeedID'); ?> + pings->getISO8601Date(),0,0,0,0,0,'PingDate'); ?> + pings->comment_author,1,0,0,0,0,'PingBlogName'); ?> + <p><a href="pings->getAuthorURL(),1,0,0,0,0,'PingAuthorURL'); ?>">pings->getTrackbackTitle(),1,0,0,0,0,'PingTitle'); ?></a></p> pings->getTrackbackContent(),1,0,0,0,0,'PingContent'); ?> + + + + + comments->comment_trackback) : ?> + + <?php echo context::global_filter($_ctx->comments->post_title,1,0,0,0,0,'CommentEntryTitle'); ?> - <?php echo context::global_filter($_ctx->comments->comment_author,1,0,0,0,0,'CommentAuthor'); ?> + + comments->getFeedID(),0,0,0,0,0,'CommentFeedID'); ?> + comments->getISO8601Date(),0,0,0,0,0,'CommentDate'); ?> + comments->comment_author,1,0,0,0,0,'CommentAuthor'); ?> + comments->getContent(1),1,0,0,0,0,'CommentContent'); ?> + + + comments = null; ?> + + \ No newline at end of file diff --git a/cache/cbtpl/1c/4b/1c4b52bb84ebd38be237c1522ddf181d.php b/cache/cbtpl/1c/4b/1c4b52bb84ebd38be237c1522ddf181d.php new file mode 100644 index 0000000..f8871ba --- /dev/null +++ b/cache/cbtpl/1c/4b/1c4b52bb84ebd38be237c1522ddf181d.php @@ -0,0 +1,212 @@ + + + + + + + + <?php echo __('Tag'); ?> - <?php echo context::global_filter($_ctx->meta->meta_id,0,0,0,0,0,'TagID'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?><?php if(!context::PaginationStart()) : ?> - <?php echo __('page'); ?> <?php echo context::global_filter(context::PaginationPosition(0),0,0,0,0,0,'PaginationCurrent'); ?><?php endif; ?> + + + + + + + + + + + + + + + " title="" /> + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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",0,0,0,0,0,'TagFeedURL'); ?>" /> + " /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + +
    +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + +
    +

    - meta->meta_id,0,0,0,0,0,'TagID'); ?>

    + +

    meta->meta_id)."/atom",0,0,0,0,0,'TagFeedURL'); ?>" + title="" class="feed"> + + blog->settings->allow_comments || $core->blog->settings->allow_trackbacks) : ?> + - meta->meta_id)."/atom",0,0,0,0,0,'TagFeedURL'); ?>/comments" + title="" class="feed"> + +

    +
    + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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->firstPostOfDay()) : ?>

    posts->getDate(''),0,0,0,0,0,'EntryDate'); ?>

    + +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + + + meta = $objMeta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?> + meta->isStart()) : ?> + meta = null; unset($objMeta); ?> + + hasBehavior('publicEntryBeforeContent')) { $core->callBehavior('publicEntryBeforeContent',$core,$_ctx);} ?> + + posts->isExtended()) : ?> +
    posts->getExcerpt(0),0,0,0,0,0,'EntryExcerpt'); ?>
    +

    ...

    + + + posts->isExtended()) : ?> +
    posts->getContent(0),0,0,0,0,0,'EntryContent'); ?>
    + + + hasBehavior('publicEntryAfterContent')) { $core->callBehavior('publicEntryAfterContent',$core,$_ctx);} ?> + + posts->countMedia() || ($_ctx->posts->hasComments() || $_ctx->posts->commentsActive()) || ($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())) : ?> + + +
    + + posts->isEnd()) : ?> + post_params; +$_ctx->pagination = $core->blog->getPosts($params,true); unset($params); +?> +pagination->f(0) > $_ctx->posts->count()) : ?> +

    - + + -

    + + + posts = null; $_ctx->post_params = null; ?> +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/3a/b6/3ab69b6036ca46cabbbc3f6f39ceb835.php b/cache/cbtpl/3a/b6/3ab69b6036ca46cabbbc3f6f39ceb835.php new file mode 100644 index 0000000..86c976a --- /dev/null +++ b/cache/cbtpl/3a/b6/3ab69b6036ca46cabbbc3f6f39ceb835.php @@ -0,0 +1,92 @@ + + + + + + + + <?php echo __('Archives'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?> + + + + + + + + + + + + + + + + exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +$_ctx->archives = $core->blog->getDates($params); unset($params); +?> +archives->fetch()) : ?> + + archives = null; ?> + + " /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + +
    +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + +
    +

    +
    + +
    + exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +$_ctx->archives = $core->blog->getDates($params); unset($params); +?> +archives->fetch()) : ?> + archives->yearHeader()) : ?> +

    archives->dt),0,0,0,0,0,'ArchiveDate'); ?>

    + + + archives = null; ?> +
    + +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/3b/b3/3bb3a29ef197db17fdf8e0d1c82e1f9f.php b/cache/cbtpl/3b/b3/3bb3a29ef197db17fdf8e0d1c82e1f9f.php new file mode 100644 index 0000000..d41216b --- /dev/null +++ b/cache/cbtpl/3b/b3/3bb3a29ef197db17fdf8e0d1c82e1f9f.php @@ -0,0 +1,273 @@ + + + + + + + + <?php echo context::global_filter($_ctx->posts->post_title,1,0,0,0,0,'EntryTitle'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?> + posts->getContent(0),1,1,180,0,0,'EntryContent'); ?>" /> + + + + + + + posts->getContent(0),1,1,180,0,0,'EntryContent'); ?>" /> + + + + + + + + + + " title="" /> + + " /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + + + + +
    +posts->trackbacksActive()) { echo $_ctx->posts->getTrackbackData(); } ?> + + +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + +
    +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + hasBehavior('publicEntryBeforeContent')) { $core->callBehavior('publicEntryBeforeContent',$core,$_ctx);} ?> + + posts->isExtended()) : ?> +
    posts->getExcerpt(0),0,0,0,0,0,'EntryExcerpt'); ?>
    + +
    posts->getContent(0),0,0,0,0,0,'EntryContent'); ?>
    + +

    posts->getDate(''),0,0,0,0,0,'EntryDate'); ?> + posts->getAuthorLink(),0,0,0,0,0,'EntryAuthorLink'); ?>

    + + hasBehavior('publicEntryAfterContent')) { $core->callBehavior('publicEntryAfterContent',$core,$_ctx);} ?> +
    + + posts !== null && $core->media) { +$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id)); +?> +attachments as $attach_i => $attach_f) : $GLOBALS['attach_i'] = $attach_i; $GLOBALS['attach_f'] = $attach_f;$_ctx->file_url = $attach_f->file_url; ?> + +
    +

    +
      + +
    • + type == "audio/mpeg3") : ?> + tpl->getData('_mp3_player.html'); } catch (Exception $e) {} ?> - + + type == "video/x-flv" || $attach_f->type == "video/mp4" || $attach_f->type == "video/x-m4v")) : ?> + tpl->getData('_flv_player.html'); } catch (Exception $e) {} ?> + + type == "video/x-flv" || $attach_f->type == "video/mp4" || $attach_f->type == "video/x-m4v")) : ?> + media_title,0,0,0,0,0,'AttachmentTitle'); ?> + +
    • + attachments)) : ?> +
    +
    + + attachments = null; unset($attach_i,$attach_f,$_ctx->file_url); ?> + + + posts->hasComments() || $_ctx->posts->commentsActive())) : ?> + exists("meta")) { @$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)."' "; +} ?> +posts !== null) { $params['post_id'] = $_ctx->posts->post_id; $core->blog->withoutPassword(false); +} +$params['comment_trackback'] = false; +if ($_ctx->nb_comment_per_page !== null) { $params['limit'] = $_ctx->nb_comment_per_page; } +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 asc'; +$_ctx->comments = $core->blog->getComments($params); unset($params); +if ($_ctx->posts !== null) { $core->blog->withoutPassword(true);} +?> +comments->fetch()) : ?> + comments->isStart()) : ?> +
    +

    +
    + +
    comments->index()+1; ?>. + comments->getDate(''),0,0,0,0,0,'CommentDate'); ?>, comments->getTime(''),0,0,0,0,0,'CommentTime'); ?> + comments->getAuthorLink(),0,0,0,0,0,'CommentAuthorLink'); ?>
    + +
    + + hasBehavior('publicCommentBeforeContent')) { $core->callBehavior('publicCommentBeforeContent',$core,$_ctx);} ?> + + comments->getContent(0),0,0,0,0,0,'CommentContent'); ?> + + hasBehavior('publicCommentAfterContent')) { $core->callBehavior('publicCommentAfterContent',$core,$_ctx);} ?> +
    + comments->isEnd()) : ?> +
    +
    + + comments = null; ?> + + + posts->commentsActive()) : ?> + form_error !== null) : ?> +

    form_error !== null) { echo $_ctx->form_error; } ?>

    + + + +

    + + + +

    + + +
    + comment_preview !== null && $_ctx->comment_preview["preview"]) : ?> +
    +

    +
    +
    comment_preview["content"],0,0,0,0,0,'CommentPreviewContent'); ?>
    +
    +

    +
    + + +

    +
    + + hasBehavior('publicCommentFormBeforeContent')) { $core->callBehavior('publicCommentFormBeforeContent',$core,$_ctx);} ?> + +

    + " /> +

    + +

    + " /> +

    + +

    + " /> +

    + +

    + +

    + +

    + +

    + + hasBehavior('publicCommentFormAfterContent')) { $core->callBehavior('publicCommentFormAfterContent',$core,$_ctx);} ?> +
    + +
    +

    + comment_preview !== null && $_ctx->comment_preview["preview"]) : ?>

    +
    +
    + + + posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())) : ?> +
    +

    + posts !== null) { $params['post_id'] = $_ctx->posts->post_id; $core->blog->withoutPassword(false); +} +$params['comment_trackback'] = true; +if ($_ctx->nb_comment_per_page !== null) { $params['limit'] = $_ctx->nb_comment_per_page; } +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 asc'; +$_ctx->pings = $core->blog->getComments($params); unset($params); +if ($_ctx->posts !== null) { $core->blog->withoutPassword(true);} +?> +pings->fetch()) : ?> + pings->isStart()) : ?> +
    + +
    pings->index()+1; ?>. + pings->getDate(''),0,0,0,0,0,'PingDate'); ?>, pings->getTime(''),0,0,0,0,0,'PingTime'); ?> + pings->comment_author,1,0,0,0,0,'PingBlogName'); ?>
    + +
    + + hasBehavior('publicPingBeforeContent')) { $core->callBehavior('publicPingBeforeContent',$core,$_ctx);} ?> + +

    blog->settings->comments_nofollow) { echo ' rel="nofollow"';} ?>>pings->getTrackbackTitle(),1,0,0,0,0,'PingTitle'); ?>

    + pings->getTrackbackContent(),0,0,0,0,0,'PingContent'); ?> + + hasBehavior('publicPingAfterContent')) { $core->callBehavior('publicPingAfterContent',$core,$_ctx);} ?> +
    + pings->isEnd()) : ?> +
    + + pings = null; ?> +
    + + + posts->trackbacksActive()) : ?> +

     : posts->trackbacksActive()) { echo $_ctx->posts->getTrackbackLink(); } ?> +

    + + + posts->commentsActive() || $_ctx->posts->trackbacksActive()) : ?> +

    /comments/posts->post_id,0,0,0,0,0,'EntryID'); ?>" + title="">

    + +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/3d/2a/3d2ab7176958994fb44ae475a0e58648.php b/cache/cbtpl/3d/2a/3d2ab7176958994fb44ae475a0e58648.php new file mode 100644 index 0000000..8ff0822 --- /dev/null +++ b/cache/cbtpl/3d/2a/3d2ab7176958994fb44ae475a0e58648.php @@ -0,0 +1,16 @@ + + + + + + + + + +tpl->getData('user_head.html'); } catch (Exception $e) {} ?> +hasBehavior('publicHeadContent')) { $core->callBehavior('publicHeadContent',$core,$_ctx);} ?> + diff --git a/cache/cbtpl/62/e1/62e139c10241e743060059b9ff916ad8.php b/cache/cbtpl/62/e1/62e139c10241e743060059b9ff916ad8.php new file mode 100644 index 0000000..d82b021 --- /dev/null +++ b/cache/cbtpl/62/e1/62e139c10241e743060059b9ff916ad8.php @@ -0,0 +1,329 @@ + + + + + + + + <?php echo context::global_filter($_ctx->posts->post_title,1,0,0,0,0,'EntryTitle'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?> + posts->getContent(0),1,1,180,0,0,'EntryContent'); ?>" /> + + + + + + + posts->getContent(0),1,1,180,0,0,'EntryContent'); ?>" /> + + + + + + + + + + " title="" /> + + 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; ?> + + + " /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + + + +
    +posts->trackbacksActive()) { echo $_ctx->posts->getTrackbackData(); } ?> + + +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + + + +
    +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + + + hasBehavior('publicEntryBeforeContent')) { $core->callBehavior('publicEntryBeforeContent',$core,$_ctx);} ?> + + posts->isExtended()) : ?> +
    posts->getExcerpt(0),0,0,0,0,0,'EntryExcerpt'); ?>
    + + +
    posts->getContent(0),0,0,0,0,0,'EntryContent'); ?>
    + + meta = $objMeta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?> + meta->isStart()) : ?> + meta = null; unset($objMeta); ?> + + hasBehavior('publicEntryAfterContent')) { $core->callBehavior('publicEntryAfterContent',$core,$_ctx);} ?> + +
     
    +
    + + posts !== null && $core->media) { +$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id)); +?> +attachments as $attach_i => $attach_f) : $GLOBALS['attach_i'] = $attach_i; $GLOBALS['attach_f'] = $attach_f;$_ctx->file_url = $attach_f->file_url; ?> + +
    +

    +
      + +
    • + type == "audio/mpeg3") : ?> + tpl->getData('_mp3_player.html'); } catch (Exception $e) {} ?> - + + type == "video/x-flv" || $attach_f->type == "video/mp4" || $attach_f->type == "video/x-m4v")) : ?> + tpl->getData('_flv_player.html'); } catch (Exception $e) {} ?> + + type == "video/x-flv" || $attach_f->type == "video/mp4" || $attach_f->type == "video/x-m4v")) : ?> + media_title,0,0,0,0,0,'AttachmentTitle'); ?> + +
    • + attachments)) : ?> +
    +
    + + attachments = null; unset($attach_i,$attach_f,$_ctx->file_url); ?> + + + posts->hasComments() || $_ctx->posts->commentsActive())) : ?> + exists("meta")) { @$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)."' "; +} ?> +posts !== null) { $params['post_id'] = $_ctx->posts->post_id; $core->blog->withoutPassword(false); +} +$params['comment_trackback'] = false; +if ($_ctx->nb_comment_per_page !== null) { $params['limit'] = $_ctx->nb_comment_per_page; } +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 asc'; +$_ctx->comments = $core->blog->getComments($params); unset($params); +if ($_ctx->posts !== null) { $core->blog->withoutPassword(true);} +?> +comments->fetch()) : ?> + comments->isStart()) : ?> +
    +

    +
    + +
    comments->index()+1; ?>. + comments->getDate(''),0,0,0,0,0,'CommentDate'); ?>, comments->getTime(''),0,0,0,0,0,'CommentTime'); ?> + comments->getAuthorLink(),0,0,0,0,0,'CommentAuthorLink'); ?>
    + +
    + + hasBehavior('publicCommentBeforeContent')) { $core->callBehavior('publicCommentBeforeContent',$core,$_ctx);} ?> + + comments->getContent(0),0,0,0,0,0,'CommentContent'); ?> + + hasBehavior('publicCommentAfterContent')) { $core->callBehavior('publicCommentAfterContent',$core,$_ctx);} ?> +
    + comments->isEnd()) : ?> +
    +
    + + comments = null; ?> + + + posts->commentsActive()) : ?> + form_error !== null) : ?> +

    form_error !== null) { echo $_ctx->form_error; } ?>

    + + + +

    + + + +

    + + + + +
    + comment_preview !== null && $_ctx->comment_preview["preview"]) : ?> +
    +

    +
    +
    comment_preview["content"],0,0,0,0,0,'CommentPreviewContent'); ?>
    +
    +

    +
    + + +

    +
    +
    + + hasBehavior('publicCommentFormBeforeContent')) { $core->callBehavior('publicCommentFormBeforeContent',$core,$_ctx);} ?> + +

    + " /> +

    + +

    + " /> +

    + +

    + " /> +

    + +

    + +

    + +

    +

    + + hasBehavior('publicCommentFormAfterContent')) { $core->callBehavior('publicCommentFormAfterContent',$core,$_ctx);} ?> +
    + +
    +

    + comment_preview !== null && $_ctx->comment_preview["preview"]) : ?>

    +
    +
    +
    + + + posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())) : ?> +
    +

    + posts !== null) { $params['post_id'] = $_ctx->posts->post_id; $core->blog->withoutPassword(false); +} +$params['comment_trackback'] = true; +if ($_ctx->nb_comment_per_page !== null) { $params['limit'] = $_ctx->nb_comment_per_page; } +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 asc'; +$_ctx->pings = $core->blog->getComments($params); unset($params); +if ($_ctx->posts !== null) { $core->blog->withoutPassword(true);} +?> +pings->fetch()) : ?> + pings->isStart()) : ?> +
    + +
    pings->index()+1; ?>. + pings->getDate(''),0,0,0,0,0,'PingDate'); ?>, pings->getTime(''),0,0,0,0,0,'PingTime'); ?> + pings->comment_author,1,0,0,0,0,'PingBlogName'); ?>
    + +
    + + hasBehavior('publicPingBeforeContent')) { $core->callBehavior('publicPingBeforeContent',$core,$_ctx);} ?> + +

    blog->settings->comments_nofollow) { echo ' rel="nofollow"';} ?>>pings->getTrackbackTitle(),1,0,0,0,0,'PingTitle'); ?>

    + pings->getTrackbackContent(),0,0,0,0,0,'PingContent'); ?> + + hasBehavior('publicPingAfterContent')) { $core->callBehavior('publicPingAfterContent',$core,$_ctx);} ?> +
    + pings->isEnd()) : ?> +
    + + pings = null; ?> +
    + + + posts->trackbacksActive()) : ?> +

     : posts->trackbacksActive()) { echo $_ctx->posts->getTrackbackLink(); } ?> +

    + + + posts->commentsActive() || $_ctx->posts->trackbacksActive()) : ?> +

    /comments/posts->post_id,0,0,0,0,0,'EntryID'); ?>" + title="">

    + +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/68/28/68289f8a2d83100b6bda1a3cccbbc63f.php b/cache/cbtpl/68/28/68289f8a2d83100b6bda1a3cccbbc63f.php new file mode 100644 index 0000000..a2f35de --- /dev/null +++ b/cache/cbtpl/68/28/68289f8a2d83100b6bda1a3cccbbc63f.php @@ -0,0 +1,230 @@ + + + + + + + + <?php echo context::global_filter($_ctx->categories->cat_title,1,0,0,0,0,'CategoryTitle'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?><?php if(!context::PaginationStart()) : ?> - <?php echo __('page'); ?> <?php echo context::global_filter(context::PaginationPosition(0),0,0,0,0,0,'PaginationCurrent'); ?><?php endif; ?> + + + + + + + + + + + + + + + + + " title="" /> + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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",0,0,0,0,0,'CategoryFeedURL'); ?>" /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + +
    +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + +
    +

    categories = $core->blog->getCategoryParents($_ctx->categories->cat_id); +while ($_ctx->categories->fetch()) : ?>categories->cat_url,0,0,0,0,0,'CategoryURL'); ?>">categories->cat_title,1,0,0,0,0,'CategoryTitle'); ?>categories = null; ?> + categories->cat_title,1,0,0,0,0,'CategoryTitle'); ?>

    + categories->cat_desc,0,0,0,0,0,'CategoryDescription'); ?> + + categories->nb_post > 0) : ?> +

    categories->cat_url."/atom",0,0,0,0,0,'CategoryFeedURL'); ?>" + title="" class="feed"> + + blog->settings->allow_comments || $core->blog->settings->allow_trackbacks) : ?> + - categories->cat_url."/atom",0,0,0,0,0,'CategoryFeedURL'); ?>/comments" + title="" class="feed"> + +

    + +
    + + categories = $core->blog->getCategoryFirstChildren($_ctx->categories->cat_id); +while ($_ctx->categories->fetch()) : ?> + categories->isStart()) : ?> + + + categories = null; ?> + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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->firstPostOfDay()) : ?>

    posts->getDate(''),0,0,0,0,0,'EntryDate'); ?>

    + +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + + + meta = $objMeta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?> + meta->isStart()) : ?> + meta = null; unset($objMeta); ?> + + hasBehavior('publicEntryBeforeContent')) { $core->callBehavior('publicEntryBeforeContent',$core,$_ctx);} ?> + + posts->isExtended()) : ?> +
    posts->getExcerpt(0),0,0,0,0,0,'EntryExcerpt'); ?>
    +

    ...

    + + + posts->isExtended()) : ?> +
    posts->getContent(0),0,0,0,0,0,'EntryContent'); ?>
    + + + hasBehavior('publicEntryAfterContent')) { $core->callBehavior('publicEntryAfterContent',$core,$_ctx);} ?> + + posts->countMedia() || ($_ctx->posts->hasComments() || $_ctx->posts->commentsActive()) || ($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())) : ?> + + +
    + + posts->isEnd()) : ?> + post_params; +$_ctx->pagination = $core->blog->getPosts($params,true); unset($params); +?> +pagination->f(0) > $_ctx->posts->count()) : ?> +

    - + + -

    + + + posts = null; $_ctx->post_params = null; ?> +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/8b/ae/8bae1e7eb3f0cd863c169541fdad8d0e.php b/cache/cbtpl/8b/ae/8bae1e7eb3f0cd863c169541fdad8d0e.php new file mode 100644 index 0000000..3cdda34 --- /dev/null +++ b/cache/cbtpl/8b/ae/8bae1e7eb3f0cd863c169541fdad8d0e.php @@ -0,0 +1,7 @@ + + +

    | + | +

    \ No newline at end of file diff --git a/cache/cbtpl/a7/06/a706cb39770a563585de2242a3226981.php b/cache/cbtpl/a7/06/a706cb39770a563585de2242a3226981.php new file mode 100644 index 0000000..cb3007c --- /dev/null +++ b/cache/cbtpl/a7/06/a706cb39770a563585de2242a3226981.php @@ -0,0 +1,73 @@ +xml version="1.0" encoding="utf-8""; ?> + + + <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?><?php if ($_ctx->feed_subtitle !== null) { echo context::global_filter($_ctx->feed_subtitle,1,0,0,0,0,'SysFeedSubtitle');} ?> + blog->desc,1,0,0,0,0,'BlogDescription'); ?> + + + blog->upddt,$core->blog->settings->blog_timezone),0,0,0,0,0,'BlogUpdateDate'); ?> + + blog->settings->editor,1,0,0,0,0,'BlogEditor'); ?> + + blog->uid,0,0,0,0,0,'BlogFeedID'); ?> + Dotclear + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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()) : ?> + + + <?php echo context::global_filter($_ctx->posts->post_title,1,0,0,0,0,'EntryTitle'); ?> + + posts->getFeedID(),0,0,0,0,0,'EntryFeedID'); ?> + posts->getISO8601Date(),0,0,0,0,0,'EntryDate'); ?> + posts->getAuthorCN(),1,0,0,0,0,'EntryAuthorCommonName'); ?> + posts->cat_id) : ?> + posts->cat_title,1,0,0,0,0,'EntryCategory'); ?> + + meta = $objMeta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?>meta->meta_id,0,0,0,0,0,'MetaID'); ?>meta = null; unset($objMeta); ?> + + posts->getExcerpt(1),1,0,0,0,0,'EntryExcerpt'); ?> + posts->getContent(1),1,0,0,0,0,'EntryContent'); ?> + + posts !== null && $core->media) { +$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id)); +?> +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(),0,0,0,0,0,'EntryURL'); ?>#comment-form + blog->url.$core->url->getBase("feed")."/atom",0,0,0,0,0,'BlogFeedURL'); ?>/comments/posts->post_id,0,0,0,0,0,'EntryID'); ?> + + + posts = null; $_ctx->post_params = null; ?> + + \ No newline at end of file diff --git a/cache/cbtpl/b0/5d/b05d35fd541ee9f5da5478faf45bb412.php b/cache/cbtpl/b0/5d/b05d35fd541ee9f5da5478faf45bb412.php new file mode 100644 index 0000000..e7c50c1 --- /dev/null +++ b/cache/cbtpl/b0/5d/b05d35fd541ee9f5da5478faf45bb412.php @@ -0,0 +1,71 @@ + + + + + + + + <?php echo __('Tags'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?> + + + + + + + + + + + + + + + " title="" /> + + " /> + + 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/cache/cbtpl/b0/f4/b0f4418e5a9e7ab1a68ede430ca901e4.php b/cache/cbtpl/b0/f4/b0f4418e5a9e7ab1a68ede430ca901e4.php new file mode 100644 index 0000000..a520b4c --- /dev/null +++ b/cache/cbtpl/b0/f4/b0f4418e5a9e7ab1a68ede430ca901e4.php @@ -0,0 +1,63 @@ + + + + + + + + <?php echo __('Document not found'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?> + + + + + + + + + + + + + " /> + " /> + + 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/cache/cbtpl/b9/d7/b9d701fcfceb205e24dd3c2658797c4e.php b/cache/cbtpl/b9/d7/b9d701fcfceb205e24dd3c2658797c4e.php new file mode 100644 index 0000000..e7f1ac6 --- /dev/null +++ b/cache/cbtpl/b9/d7/b9d701fcfceb205e24dd3c2658797c4e.php @@ -0,0 +1,207 @@ + + + + + + + + <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?><?php if(!context::PaginationStart()) : ?> - <?php echo __('page'); ?> <?php echo context::global_filter(context::PaginationPosition(0),0,0,0,0,0,'PaginationCurrent'); ?><?php endif; ?> + + + + + + + + + + + + + + + + " /> + categories = $core->blog->getCategories($params); +?> +categories->fetch()) : ?> + categories->cat_url,0,0,0,0,0,'CategoryURL'); ?>" title="categories->cat_title,1,0,0,0,0,'CategoryTitle'); ?>" /> + categories = null; unset($params); ?> + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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; ?> + + " /> + + " /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + +
    +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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->firstPostOfDay()) : ?>

    posts->getDate(''),0,0,0,0,0,'EntryDate'); ?>

    + +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + + + meta = $objMeta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?> + meta->isStart()) : ?> + meta = null; unset($objMeta); ?> + + hasBehavior('publicEntryBeforeContent')) { $core->callBehavior('publicEntryBeforeContent',$core,$_ctx);} ?> + + posts->isExtended()) : ?> +
    posts->getExcerpt(0),0,0,0,0,0,'EntryExcerpt'); ?>
    +

    ...

    + + + posts->isExtended()) : ?> +
    posts->getContent(0),0,0,0,0,0,'EntryContent'); ?>
    + + + hasBehavior('publicEntryAfterContent')) { $core->callBehavior('publicEntryAfterContent',$core,$_ctx);} ?> + + posts->countMedia() || ($_ctx->posts->hasComments() || $_ctx->posts->commentsActive()) || ($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())) : ?> + + +
    + + posts->isEnd()) : ?> + post_params; +$_ctx->pagination = $core->blog->getPosts($params,true); unset($params); +?> +pagination->f(0) > $_ctx->posts->count()) : ?> +

    - + + -

    + + + posts = null; $_ctx->post_params = null; ?> +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/c1/cd/c1cd28bd66b4517eec04f3abde1cb6c0.php b/cache/cbtpl/c1/cd/c1cd28bd66b4517eec04f3abde1cb6c0.php new file mode 100644 index 0000000..173e2a1 --- /dev/null +++ b/cache/cbtpl/c1/cd/c1cd28bd66b4517eec04f3abde1cb6c0.php @@ -0,0 +1,211 @@ + + + + + + + + <?php echo __('Search'); ?> - <?php if (isset($_search)) { echo sprintf(__('%1$s'),context::global_filter($_search,1,0,0,0,0,'SysSearchString'),$_search_count);} ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?><?php if(!context::PaginationStart()) : ?> - <?php echo __('page'); ?> <?php echo context::global_filter(context::PaginationPosition(0),0,0,0,0,0,'PaginationCurrent'); ?><?php endif; ?> + + + + + + + + + + + + + + + " title="" /> + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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; ?> + + " /> + + tpl->getData('_head.html'); } catch (Exception $e) {} ?> + + + +
    +tpl->getData('_top.html'); } catch (Exception $e) {} ?> + +
    + +
    +
    + +
    +

    + +

    %1$s returned no result.'),context::global_filter($_search,1,0,0,0,0,'SysSearchString'),$_search_count);} ?>

    + + +

    %1$s returned %2$s result.'),context::global_filter($_search,1,0,0,0,0,'SysSearchString'),$_search_count);} ?>

    + + 1)) : ?> +

    %1$s returned %2$s results.'),context::global_filter($_search,1,0,0,0,0,'SysSearchString'),$_search_count);} ?>

    + +
    + + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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->firstPostOfDay()) : ?>

    posts->getDate(''),0,0,0,0,0,'EntryDate'); ?>

    + +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + + + meta = $objMeta->getMetaRecordset($_ctx->posts->post_meta,'tag'); $_ctx->meta->sort('meta_id_lower','asc'); ?>meta->fetch()) : ?> + meta->isStart()) : ?> + meta = null; unset($objMeta); ?> + + hasBehavior('publicEntryBeforeContent')) { $core->callBehavior('publicEntryBeforeContent',$core,$_ctx);} ?> + + posts->isExtended()) : ?> +
    posts->getExcerpt(0),0,0,0,0,0,'EntryExcerpt'); ?>
    +

    ...

    + + + posts->isExtended()) : ?> +
    posts->getContent(0),0,0,0,0,0,'EntryContent'); ?>
    + + + hasBehavior('publicEntryAfterContent')) { $core->callBehavior('publicEntryAfterContent',$core,$_ctx);} ?> + + posts->countMedia() || ($_ctx->posts->hasComments() || $_ctx->posts->commentsActive()) || ($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())) : ?> + + +
    + + posts->isEnd()) : ?> + post_params; +$_ctx->pagination = $core->blog->getPosts($params,true); unset($params); +?> +pagination->f(0) > $_ctx->posts->count()) : ?> +

    - + + -

    + + + posts = null; $_ctx->post_params = null; ?> +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/d6/6a/d66aceb2839fdeb0b5251550574000be.php b/cache/cbtpl/d6/6a/d66aceb2839fdeb0b5251550574000be.php new file mode 100644 index 0000000..f3216ff --- /dev/null +++ b/cache/cbtpl/d6/6a/d66aceb2839fdeb0b5251550574000be.php @@ -0,0 +1,5 @@ + + +hasBehavior('publicFooterContent')) { $core->callBehavior('publicFooterContent',$core,$_ctx);} ?> \ No newline at end of file diff --git a/cache/cbtpl/e1/53/e153e91980f2acce5c73feac0a840192.php b/cache/cbtpl/e1/53/e153e91980f2acce5c73feac0a840192.php new file mode 100644 index 0000000..24da2b8 --- /dev/null +++ b/cache/cbtpl/e1/53/e153e91980f2acce5c73feac0a840192.php @@ -0,0 +1,173 @@ + + + + + + + + <?php echo __('Archives'); ?> - <?php echo context::global_filter(dt::dt2str('%B %Y',$_ctx->archives->dt),0,0,0,0,0,'ArchiveDate'); ?> - <?php echo context::global_filter($core->blog->name,1,0,0,0,0,'BlogName'); ?> + + + + + + + + + + + + + + + " title="" /> + " 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")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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) {} ?> + +
    + +
    +
    + + + +
    +

    archives->dt),0,0,0,0,0,'ArchiveDate'); ?>

    +
    + +
    + exists("meta")) { @$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_per_page; +$params['limit'] = array((($_page_number-1)*$params['limit']),$params['limit']); +if ($_ctx->exists("users")) { $params['user_id'] = $_ctx->users->user_id; } +if ($_ctx->exists("categories")) { $params['cat_id'] = $_ctx->categories->cat_id; } +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->firstPostOfDay()) : ?>

    posts->getDate(''),0,0,0,0,0,'EntryDate'); ?>

    + +

    posts->post_title,1,0,0,0,0,'EntryTitle'); ?>

    + + + posts = null; $_ctx->post_params = null; ?> +
    +
    +
    + + + +
    + +tpl->getData('_footer.html'); } catch (Exception $e) {} ?> +
    + + \ No newline at end of file diff --git a/cache/cbtpl/f7/f4/f7f41a7bdbfd5f0616a3e4b89660a6a8.php b/cache/cbtpl/f7/f4/f7f41a7bdbfd5f0616a3e4b89660a6a8.php new file mode 100644 index 0000000..e69de29 diff --git a/cache/daorg/68/cb/68cbfef3c40e3d56b4fb94709e2b9321.ser b/cache/daorg/68/cb/68cbfef3c40e3d56b4fb94709e2b9321.ser new file mode 100644 index 0000000..0ce3b4e Binary files /dev/null and b/cache/daorg/68/cb/68cbfef3c40e3d56b4fb94709e2b9321.ser differ diff --git a/cache/daorg/fa/37/fa37bc9589c9a834517f9ddff94f8683.ser b/cache/daorg/fa/37/fa37bc9589c9a834517f9ddff94f8683.ser new file mode 100644 index 0000000..1376901 Binary files /dev/null and b/cache/daorg/fa/37/fa37bc9589c9a834517f9ddff94f8683.ser differ diff --git a/cache/versions/dotclear-stable b/cache/versions/dotclear-stable new file mode 100644 index 0000000..4f9b765 --- /dev/null +++ b/cache/versions/dotclear-stable @@ -0,0 +1 @@ +a:5:{s:7:"version";s:6:"2.27.3";s:4:"href";s:56:"https://download.dotclear.org/latest/dotclear-2.27.3.zip";s:8:"checksum";s:32:"793317b81d814fe42eddc1603e064215";s:4:"info";s:57:"https://services.dotclear.net/redir?about=dotclear-2.27.3";s:6:"notify";b:1;} \ No newline at end of file diff --git a/db/.htaccess b/db/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/db/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/dotclear-loader.php b/dotclear-loader.php new file mode 100644 index 0000000..6c55d50 --- /dev/null +++ b/dotclear-loader.php @@ -0,0 +1,462 @@ +'') { + return $GLOBALS['__l10n'][$str]; + } else { + return $str; + } +} + +function fetchRemote($src,&$dest,$step=0) # Rudimentary HTTP client +{ + if ($step > 3) { + return false; + } + + $src = parse_url($src); + $host = $src['host']; + $path = $src['path']; + + if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false) { + return false; + } + + fwrite($s, + 'GET '.$path." HTTP/1.0\r\n" + .'Host: '.$host."\r\n" + ."User-Agent: Dotclear Net Install\r\n" + ."Accept: text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*\r\n" + ."\r\n" + ); + + $i = 0; + $in_content = false; + while (!feof($s)) + { + $line = fgets($s,4096); + + if (rtrim($line,"\r\n") == '' && !$in_content) { + $in_content = true; + $i++; + continue; + } + + if ($i == 0) { + if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m)) { + fclose($s); + return false; + } + $status = (integer) $m[2]; + if ($status < 200 || $status >= 400) { + fclose($s); + return false; + } + } + + if (!$in_content) + { + if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m)) { + fclose($s); + return fetchRemote(trim($m[1]),$dest,$step+1); + } + $i++; + continue; + } + + if (is_resource($dest)) { + fwrite($dest,$line); + } else { + $dest .= $line; + } + + $i++; + } + + fclose($s); + return true; +} + +function getLanguage($default = 'en') +{ + if (!empty($_REQUEST['lang'])) { + return $_REQUEST['lang']; + } + if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $languages = explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']); + $l = explode(';',$languages[0]); + $dlang = substr(trim($l[0]),0,2); + return strtolower($dlang); + } + return $default; +} + +function getLocation() +{ + $server_name = explode(':',$_SERVER['HTTP_HOST']); + $server_name = $server_name[0]; + if ($_SERVER['SERVER_PORT'] == '443') + { + $scheme = 'https'; + $port = ''; + } + elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') + { + $scheme = 'https'; + $port = ($_SERVER['SERVER_PORT'] != '443') ? ':'.$_SERVER['SERVER_PORT'] : ''; + } + else + { + $scheme = 'http'; + $port = ($_SERVER['SERVER_PORT'] != '80') ? ':'.$_SERVER['SERVER_PORT'] : ''; + } + + $loc = preg_replace('#/$#','',dirname($_SERVER['SCRIPT_NAME'])); + + return $scheme.'://'.$server_name.$port.$loc.'/'; +} + +function openPage() +{ + header('Content-Type: text/html; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + ' '."\n". + ' '.__('Dotclear 2 NetInstall').''."\n". + ' '."\n". + ' '."\n". + ''."\n". + ''."\n". + '
    '."\n". + '

    '.__('Dotclear NetInstall').'

    '."\n". + '
    '."\n"; +} + +function closePage() +{ + echo + '
    '."\n". + '
    '."\n". + ''."\n". + ''; +} + +function initPHP5() +{ + $htaccess = dirname(__FILE__).'/.htaccess'; + if (file_exists($htaccess)) { + if (!is_readable($htaccess) || !is_writable($htaccess)) { + return false; + } + } + $rawdatas = ''; + if (!fetchRemote(DC_LOADER_SERVICE.'hosting.txt',$rawdatas)) { + return false; + } + $rawdatas = explode("\n",$rawdatas); + if (!($my_hostname = @gethostbyaddr($_SERVER['SERVER_ADDR']))) { + return false; + } + $found = false; + foreach ($rawdatas as $line) { + list($name,$hostname,$rule) = explode('|',trim($line)); + if (preg_match('!'.preg_quote($hostname).'$!',$my_hostname)) { + $found = $rule; + break; + } + } + if ($found) { + if (false !== ($fh = @fopen($htaccess,"ab"))) { + fwrite($fh,"\n".$found); + fclose($fh); + return true; + } + } + return false; +} + +function cleanFiles() +{ + @unlink(dirname(__FILE__).'/dcl_files.php'); + @unlink(dirname(__FILE__).'/dcl_unzip.php'); + @unlink(dirname(__FILE__).'/dotclear-install.zip'); +} + +function grabFiles() +{ + $failed = true; + $lib_files = @fopen(dirname(__FILE__).'/dcl_files.php','wb'); + $lib_unzip = @fopen(dirname(__FILE__).'/dcl_unzip.php','wb'); + $dc_zip = @fopen(dirname(__FILE__).'/dotclear-install.zip','wb'); + + if (!$lib_files || !$lib_unzip || !$dc_zip ) { + return false; + } + + if (fetchRemote(DC_LOADER_SERVICE.'lib.files.php',$lib_files)) { + if (fetchRemote(DC_LOADER_SERVICE.'class.unzip.php',$lib_unzip)) { + if (fetchRemote(DC_LOADER_ARCHIVE,$dc_zip)) { + $failed = false; + } + } + } + + fclose($lib_files); + fclose($lib_unzip); + fclose($dc_zip); + + if ($failed) { + cleanFiles(); + return false; + } + return true; +} + +function writeMessage($level,$title,$lines) +{ + if (empty($lines)) { + return; + } + + echo + '
    '. + '

    '.$title.'

    '; + $level = ' class='.$level; + foreach ($lines as $line) { + echo ''.$line.'

    '; + $level = ''; + } + echo '
    '; +} + +function writeMessage1($level,$title,$lines) +{ + if (empty($lines)) { + return; + } + + echo + '
    '. + '

    '.$title.'

    '; + foreach ($lines as $line) { + echo '

    '.$line.'

    '; + } + echo '
    '; +} + +function nextAction($label,$step,$more='') +{ + echo + '
    '. + $more. + '

    '. + ''. + ''. + '

    '; +} + +if (!$can_fetch) { + openPage(); + echo + '

    '.__('NetInstall').'

    '."\n"; + writeMessage('warning',__('Damnit!'), array( + __('Due to restrictions in your PHP configuration, NetInstall cannot get its job done.'), + sprintf(__('Please see the Dotclear documentation to perform a normal installation.'), + __('http://dotclear.org/documentation/2.0/admin/install')), + __('Really sorry for the inconvenience.') + )); + closePage(); + exit; +} + +function initializeL10n() +{ + fetchRemote('http://download.dotclear.org/loader/static/l10n/'.DC_LOADER_LANG.'.php',$l10n); + fetchRemote('http://download.dotclear.org/loader/static/l10n/'.DC_LOADER_LANG.'.md5',$md5); + If (md5($l10n) == trim($md5) || trim($md5) == 'debug') { + eval('?>'.$l10n); + } +} + +initializeL10n(); +switch ($step) { + case 1 : { + openPage(); + echo + '

    '.__('Welcome to NetInstall').'

    '."\n". + '

    '.__('This tool is meant to retrieve the latest Dotclear 2 archive and unzip it in your webspace.').'

    '. + '

    '.__('Right after then, you will be redirect to the Dotclear 2 Setup Wizard.').'

    '; + + if (!$can_write) { + writeMessage('message',__('Write access is needed'), array( + __('It looks like NetInstall wont be able to write in the current directory, and this is required to follow on.'), + __('Please try to change the permissions to allow write access, then reload this page by hitting the Refresh button.') + )); + nextAction(__('Refresh'),1); + } + elseif (!$got_php5) { + writeMessage('message',__('PHP 5 is required'), array( + sprintf(__('It appears your webhost is currently running PHP %s.'), PHP_VERSION), + __('NetInstall may try to switch your configuration to PHP 5 by creating or modifying a .htaccess file.'), + __('Note you can change your configuration by yourself and restart NetInstall after that.') + )); + nextAction(__('Try to activate PHP 5'),2); + } + else { + nextAction(__('Retrieve and unzip Dotclear'),3, + '

    '. + getLocation(). + '

    ' + ); + } + closePage(); + break; + } + case 2 : { + if (!empty($_POST['submit']) && !$got_php5) { + if (($got_php5 = initPHP5())) { + header('Location: '.$_SERVER['SCRIPT_NAME'].'?step=1'); + } + } + elseif ($got_php5) { + header('Location: '.$_SERVER['SCRIPT_NAME'].'?step=1'); + } + else { + openPage(); + writeMessage('message',__('OMG!'),array( + __('NetInstall was not able to activate PHP 5.'), + __('You may referer to your hosting provider\'s support and see how you could switch to PHP 5 by yourself.'), + __('Hope to see you back soon.') + )); + closePage(); + } + break; + } + case 3 : { + $msg = array(__('WTF are you doing here that way?!')); + $level = 'error'; + $text = ''; + if (!empty($_POST['submit']) && isset($_POST['destination'])) + { + $msg = array(); + $dest = preg_replace('/[^A-Za-z0-9_\/-]/','',$_POST['destination']); + $dest = preg_replace('#/+#','/',$dest); + + if (file_exists(dirname(__FILE__).'/./'.$dest.'/inc/config.php') || file_exists(dirname(__FILE__).'/./'.$dest.'/conf/dotclear.ini')) + { + $level = 'message'; + $msg[] = __('It seems like a previous Dotclear installation is still sitting in that space.'); + $msg[] = __('You need to rename or remove it before we can go further...'); + } + elseif (grabFiles()) + { + $lib_files = dirname(__FILE__).'/dcl_files.php'; + $lib_unzip = dirname(__FILE__).'/dcl_unzip.php'; + $dc_zip = dirname(__FILE__).'/dotclear-install.zip'; + if (!file_exists($lib_files) || !file_exists($lib_unzip) || !file_exists($dc_zip)) { + $msg[] = __('Files needed for this automatic installation could not be downloaded.'); + $msg[] = sprintf(__('Please see the Dotclear documentation to perform a normal installation.'), + __('http://dotclear.org/documentation/2.0/admin/install')); + $msg[] = __('Really sorry for the inconvenience.'); + } + + require $lib_files; + require $lib_unzip; + $uz = new fileUnzip($dc_zip); + $files = $uz->getList(); + if (count($files) == 0) { + $msg[] = __('The integrity of the downloaded archive could not be verified.'); + $msg[] = sprintf(__('Please see the Dotclear documentation to perform a normal installation.'), + __('http://dotclear.org/documentation/2.0/admin/install')); + $msg[] = __('Really sorry for the inconvenience.'); + } + + foreach ($files as $k => $v) + { + if ($v['is_dir']) { + continue; + } + $t = preg_replace('#^dotclear/#','./'.$dest.'/',$k); + $uz->unzip($k,$t); + } + + if (!is_dir(dirname(__FILE__).'/./'.$dest)) + { + $msg[] = __('The downloaded archive file could not be extracted.'); + $msg[] = sprintf(__('Please see the Dotclear documentation to perform a normal installation.'), + __('http://dotclear.org/documentation/2.0/admin/install')); + $msg[] = __('Really sorry for the inconvenience.'); + } + else + { + # Remove files, create public directory, and self-destruction + files::makeDir('./'.$dest.'/public'); + cleanFiles(); +// @unlink(__FILE__); + + $redir = preg_replace('#/+#','/',dirname($_SERVER['SCRIPT_NAME']).'/'.$dest.'/admin/install/wizard.php'); + + header('Location: '.$redir); + } + } + else + { + $msg[] = __('An error occurred while grabbing the necessary files to go on.'); + $msg[] = sprintf(__('Please see the Dotclear documentation to perform a normal installation.'), + __('http://dotclear.org/documentation/2.0/admin/install')); + $msg[] = __('Really sorry for the inconvenience.'); + } + } + openPage(); + writeMessage($level,__('Something went wrong ...'),$msg); + echo $text; + closePage(); + break; + } +} +?> \ No newline at end of file diff --git a/inc/.htaccess b/inc/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/inc/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/inc/admin/class.dc.menu.php b/inc/admin/class.dc.menu.php new file mode 100644 index 0000000..b5a4e4f --- /dev/null +++ b/inc/admin/class.dc.menu.php @@ -0,0 +1,86 @@ +id = $id; + $this->title = $title; + $this->itemSpace = $itemSpace; + $this->items = array(); + } + + public function addItem($title,$url,$img,$active,$show=true,$id=null) + { + if($show) { + $this->items[] = $this->itemDef($title,$url,$img,$active,$id); + } + } + + public function prependItem($title,$url,$img,$active,$show=true,$id=null) + { + if ($show) { + array_unshift($this->items,$this->itemDef($title,$url,$img,$active,$id)); + } + } + + public function draw() + { + if (count($this->items) == 0) { + return ''; + } + + $res = + '
    '. + ($this->title ? '

    '.$this->title.'

    ' : ''). + '
      '."\n"; + + for ($i=0; $iitems); $i++) + { + if ($i+1 < count($this->items) && $this->itemSpace != '') { + $res .= preg_replace('|$|',$this->itemSpace.'',$this->items[$i]); + $res .= "\n"; + } else { + $res .= $this->items[$i]."\n"; + } + } + + $res .= '
    '."\n"; + + return $res; + } + + protected function itemDef($title,$url,$img,$active,$id=null) + { + if (is_array($url)) { + $link = $url[0]; + $ahtml = (!empty($url[1])) ? ' '.$url[1] : ''; + } else { + $link = $url; + $ahtml = ''; + } + + return + ''. + + ''.$title.''."\n"; + } +} +?> \ No newline at end of file diff --git a/inc/admin/lib.dc.page.php b/inc/admin/lib.dc.page.php new file mode 100644 index 0000000..6323f6f --- /dev/null +++ b/inc/admin/lib.dc.page.php @@ -0,0 +1,668 @@ +blog && $core->auth->check($permissions,$core->blog->id)) + { + return; + } + + if (session_id()) { + $core->session->destroy(); + } + http::redirect(DC_AUTH_PAGE); + } + + # Check super admin + public static function checkSuper() + { + global $core; + + if (!$core->auth->isSuperAdmin()) + { + if (session_id()) { + $core->session->destroy(); + } + http::redirect(DC_AUTH_PAGE); + } + } + + # Top of admin page + public static function open($title='', $head='') + { + global $core; + + # List of user's blogs + if ($core->auth->blog_count == 1 || $core->auth->blog_count > 20) + { + $blog_box = + __('Blog:').' '. + html::escapeHTML($core->blog->name).''; + + if ($core->auth->blog_count > 20) { + $blog_box .= ' - '.__('Change blog').''; + } + } + else + { + $rs_blogs = $core->getBlogs(array('order'=>'LOWER(blog_name)','limit'=>20)); + $blogs = array(); + while ($rs_blogs->fetch()) { + $blogs[html::escapeHTML($rs_blogs->blog_name.' - '.$rs_blogs->blog_url)] = $rs_blogs->blog_id; + } + $blog_box = + __('Blogs:').' '. + $core->formNonce(). + form::combo('switchblog',$blogs,$core->blog->id, '',1). + ''; + } + + # Display + header('Content-Type: text/html; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + ' '."\n". + ' '.$title.' - '.html::escapeHTML($core->blog->name).' - '.html::escapeHTML(DC_VENDOR_NAME).' - '.DC_VERSION.''."\n". + + ' '."\n". + ' '."\n". + ' '."\n". + + self::jsLoadIE7(). + ' \n"; + if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') { + echo ' \n"; + } + + echo + self::jsCommon(). + $head; + + # --BEHAVIOR-- adminPageHTMLHead + $core->callBehavior('adminPageHTMLHead'); + + echo + "\n". + ''."\n". + + ''."\n"; + + + echo + '
    '. + '
    '. + $blog_box. + ' - '.__('View site').''. + ' - '.__('User:').' '.$core->auth->userID().''. + ' - '.__('Logout').''. + '
    '. + '
    '; + + echo + '
    '."\n". + '
    '."\n". + '
    '."\n"; + + if ($core->error->flag()) { + echo + '
    '.__('Errors:').''. + $core->error->toHTML(). + '
    '; + } + } + + public static function close() + { + $menu =& $GLOBALS['_menu']; + + echo + "
    \n". // End of #content + "
    \n". // End of #main + + ''."\n". // End of #main-menu + ''."\n". + "
    \n"; // End of #wrapper + + if (defined('DC_DEV') && DC_DEV === true) { + echo self::debugInfo(); + } + + echo + ''; + } + + public static function openPopup($title='', $head='') + { + global $core; + + # Display + header('Content-Type: text/html; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + ' '."\n". + ' '.$title.' - '.html::escapeHTML($core->blog->name).' - '.html::escapeHTML(DC_VENDOR_NAME).' - '.DC_VERSION.''."\n". + + ' '."\n". + ' '."\n". + ' '."\n". + + self::jsLoadIE7(). + ' \n"; + if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') { + echo ' \n"; + } + + echo + self::jsCommon(). + $head; + + # --BEHAVIOR-- adminPageHTMLHead + $core->callBehavior('adminPageHTMLHead'); + + echo + "\n". + ''."\n". + + '

    '.DC_VENDOR_NAME.'

    '."\n"; + + echo + '
    '."\n". + '
    '."\n". + '
    '."\n"; + + if ($core->error->flag()) { + echo + '
    '.__('Errors:').''. + $core->error->toHTML(). + '
    '; + } + } + + public static function closePopup() + { + echo + "
    \n". // End of #content + "
    \n". // End of #main + ''."\n". + "
    \n". // End of #wrapper + ''; + } + + private static function debugInfo() + { + $global_vars = implode(', ',array_keys($GLOBALS)); + + $res = + '
    '. + '

    memory usage: '.memory_get_usage().' ('.files::size(memory_get_usage()).')

    '; + + if (function_exists('xdebug_get_profiler_filename')) + { + $res .= '

    Elapsed time: '.xdebug_time_index().' seconds

    '; + + $prof_file = xdebug_get_profiler_filename(); + if ($prof_file) { + $res .= '

    Profiler file : '.xdebug_get_profiler_filename().'

    '; + } else { + $prof_url = http::getSelfURI(); + $prof_url .= (strpos($prof_url,'?') === false) ? '?' : '&'; + $prof_url .= 'XDEBUG_PROFILE'; + $res .= '

    Trigger profiler

    '; + } + + /* xdebug configuration: + zend_extension = /.../xdebug.so + xdebug.auto_trace = On + xdebug.trace_format = 0 + xdebug.trace_options = 1 + xdebug.show_mem_delta = On + xdebug.profiler_enable = 0 + xdebug.profiler_enable_trigger = 1 + xdebug.profiler_output_dir = /tmp + xdebug.profiler_append = 0 + xdebug.profiler_output_name = timestamp + */ + } + + $res .= + '

    Global vars: '.$global_vars.'

    '. + '
    '; + + return $res; + } + + public static function help($page,$index='') + { + # Deprecated but we keep this for plugins. + } + + public static function helpBlock() + { + $args = func_get_args(); + if (empty($args)) { + return; + }; + + global $__resources; + if (empty($__resources['help'])) { + return; + } + + $content = ''; + foreach ($args as $v) + { + if (is_object($v) && isset($v->content)) { + $content .= $v->content; + continue; + } + + if (!isset($__resources['help'][$v])) { + continue; + } + $f = $__resources['help'][$v]; + if (!file_exists($f) || !is_readable($f)) { + continue; + } + + $fc = file_get_contents($f); + if (preg_match('|]*?>(.*?)|ms',$fc,$matches)) { + $content .= $matches[1]; + } else { + $content .= $fc; + } + } + + if (trim($content) == '') { + return; + } + + echo + '

    '.__('Help').'

    '. + $content. + '
    '; + } + + public static function jsLoad($src) + { + return ''."\n"; + } + + public static function jsVar($n,$v) + { + return $n." = '".html::escapeJS($v)."';\n"; + } + + public static function jsCommon() + { + return + self::jsLoad('js/jquery/jquery.js'). + self::jsLoad('js/jquery/jquery.biscuit.js'). + self::jsLoad('js/jquery/jquery.bgFade.js'). + self::jsLoad('js/common.js'). + + '\n"; + } + + public static function jsLoadIE7() + { + return + ''."\n"; + } + + public static function jsConfirmClose() + { + $args = func_get_args(); + if (count($args) > 0) { + foreach ($args as $k => $v) { + $args[$k] = "'".html::escapeJS($v)."'"; + } + $args = implode(',',$args); + } else { + $args = ''; + } + + return + self::jsLoad('js/confirm-close.js'). + '\n"; + } + + public static function jsPageTabs($default=null) + { + if ($default) { + $default = "'".html::escapeJS($default)."'"; + } + + return + self::jsLoad('js/jquery/jquery.pageTabs.js'). + '\n"; + } + + public static function jsModal() + { + return + ''."\n". + self::jsLoad('js/jquery/jquery.modal.js'). + '\n"; + } + + public static function jsColorPicker() + { + return + ''."\n". + self::jsLoad('js/jquery/jquery.farbtastic.js'). + self::jsLoad('js/color-picker.js'); + } + + public static function jsDatePicker() + { + return + ''."\n". + self::jsLoad('js/date-picker.js'). + '\n"; + } + + public static function jsToolBar() + { + $res = + ''. + ''; + + if (isset($GLOBALS['core']->auth) && $GLOBALS['core']->auth->getOption('enable_wysiwyg')) { + $res .= ''; + } + + $res .= + ''. + '\n"; + + return $res; + } + + public static function jsCandyUpload($params=array(),$base_url=null) + { + if (!$base_url) { + $base_url = path::clean(dirname(preg_replace('/(\?.*$)?/','',$_SERVER['REQUEST_URI']))).'/'; + } + + $params = array_merge($params,array( + 'sess_id='.session_id(), + 'sess_uid='.$_SESSION['sess_browser_uid'], + 'xd_check='.$GLOBALS['core']->getNonce() + )); + + return + ''."\n". + self::jsLoad('js/jquery/jquery.candyUpload.js'). + + '\n"; + } + + public static function jsToolMan() + { + return + ''. + ''. + ''. + ''. + ''. + ''. + ''; + } +} +?> \ No newline at end of file diff --git a/inc/admin/lib.pager.php b/inc/admin/lib.pager.php new file mode 100644 index 0000000..aa3b888 --- /dev/null +++ b/inc/admin/lib.pager.php @@ -0,0 +1,408 @@ +core =& $core; + $this->rs =& $rs; + $this->rs_count = $rs_count; + $this->html_prev = __('«prev.'); + $this->html_next = __('next»'); + } +} + +class adminPostList extends adminGenericList +{ + public function display($page,$nb_per_page,$enclose_block='') + { + if ($this->rs->isEmpty()) + { + echo '

    '.__('No entry').'

    '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
    '.__('Title').''.__('Date').''.__('Category').''.__('Author').''.__('Comments').''.__('Trackbacks').''.__('Status').'
    '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->postLine(); + } + + echo $blocks[1]; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + } + } + + private function postLine() + { + 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 = __('None'); + } + + $img = '%1$s'; + switch ($this->rs->post_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('scheduled'),'scheduled.png'); + break; + case -2: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + 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 = ''; + + $res .= + ''. + form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable()).''. + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).''. + ''.$cat_title.''. + ''.$this->rs->user_id.''. + ''.$this->rs->nb_comment.''. + ''.$this->rs->nb_trackback.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''; + + 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 pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + '%s
    '.__('Title').''.__('Date').''.__('Author').''.__('Status').'
    '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->postLine(); + } + + echo $blocks[1]; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + } + } + + private function postLine() + { + $img = '%1$s'; + switch ($this->rs->post_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('scheduled'),'scheduled.png'); + break; + case -2: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + 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 = ''; + + $res .= + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).''. + ''.$this->rs->user_id.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''; + + return $res; + } +} + +class adminCommentList extends adminGenericList +{ + public function display($page,$nb_per_page,$enclose_block='') + { + if ($this->rs->isEmpty()) + { + echo '

    '.__('No comment').'

    '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
    '.__('Title').''.__('Date').''.__('Author').''.__('Type').''.__('Status').' 
    '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->commentLine(); + } + + echo $blocks[1]; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + } + } + + private function commentLine() + { + global $author, $status, $sortby, $order, $nb_per_page; + + $author_url = + 'comments.php?n='.$nb_per_page. + '&status='.$status. + '&sortby='.$sortby. + '&order='.$order. + '&author='.rawurlencode($this->rs->comment_author); + + $post_url = $this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id); + + $comment_url = 'comment.php?id='.$this->rs->comment_id; + + $comment_dt = + dt::dt2str($this->core->blog->settings->date_format.' - '. + $this->core->blog->settings->time_format,$this->rs->comment_dt); + + $img = '%1$s'; + switch ($this->rs->comment_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + break; + case -2: + $img_status = sprintf($img,__('junk'),'junk.png'); + break; + } + + $comment_author = html::escapeHTML($this->rs->comment_author); + if (mb_strlen($comment_author) > 20) { + $comment_author = mb_strcut($comment_author,0,17).'...'; + } + + $res = ''; + + $res .= + ''. + form::checkbox(array('comments[]'),$this->rs->comment_id,'','','',0).''. + ''. + html::escapeHTML($this->rs->post_title).''. + ($this->rs->post_type != 'post' ? ' ('.html::escapeHTML($this->rs->post_type).')' : '').''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->comment_dt).''. + ''.$comment_author.''. + ''.($this->rs->comment_trackback ? __('trackback') : __('comment')).''. + ''.$img_status.''. + ''. + ''; + + $res .= ''; + + return $res; + } +} + +class adminUserList extends adminGenericList +{ + public function display($page,$nb_per_page,$enclose_block='') + { + if ($this->rs->isEmpty()) + { + echo '

    '.__('No user').'

    '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + '%s
    '.__('Username').''.__('First Name').''.__('Last Name').''.__('Display name').''.__('Entries').'
    '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->userLine(); + } + + echo $blocks[1]; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + } + } + + private function userLine() + { + return + ''. + ''.form::hidden(array('nb_post[]'),(integer) $this->rs->nb_post). + form::checkbox(array('user_id[]'),$this->rs->user_id).''. + ''. + $this->rs->user_id.''. + ''.$this->rs->user_firstname.''. + ''.$this->rs->user_name.''. + ''.$this->rs->user_displayname.''. + ''.$this->rs->nb_post.''. + ''; + } +} +?> \ No newline at end of file diff --git a/inc/admin/prepend.php b/inc/admin/prepend.php new file mode 100644 index 0000000..3444cf7 --- /dev/null +++ b/inc/admin/prepend.php @@ -0,0 +1,211 @@ +auth->getInfo('user_lang'); + $_lang = preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$_lang) ? $_lang : 'en'; + + 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.'/plugins'); +} + +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 + 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'); + } + + # 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']); + } + + # 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'); + } +} + +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); + + # Menus creation + $_menu['Dashboard'] = new dcMenu('dashboard-menu',null); + $_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); + + # Set menu titles + + $_menu['System']->title = __('System'); + $_menu['Blog']->title = __('Blog'); + $_menu['Plugins']->title = __('Plugins'); + + $_menu['Dashboard']->prependItem(__('Dashboard'),'index.php','images/menu/dashboard.png', + preg_match('/index.php$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + + $_menu['Blog']->prependItem(__('Media manager'),'media.php','images/menu/media.png', + preg_match('/media(_item)?.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('media,media_admin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Categories'),'categories.php','images/menu/categories.png', + preg_match('/categories.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('categories',$core->blog->id)); + $_menu['Blog']->prependItem(__('Search'),'search.php','images/menu/search.png', + preg_match('/search.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Comments'),'comments.php','images/menu/comments.png', + preg_match('/comments.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Entries'),'posts.php','images/menu/entries.png', + preg_match('/posts.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + $_menu['Blog']->prependItem(__('New entry'),'post.php','images/menu/edit.png', + preg_match('/post.php$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id),'menu-new-post'); + + $_menu['System']->prependItem(__('Updates'),'update.php','images/menu/update.png', + preg_match('/update.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin() && is_readable(DC_DIGESTS)); + $_menu['System']->prependItem(__('Languages'),'langs.php','images/menu/langs.png', + preg_match('/langs.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + $_menu['System']->prependItem(__('Plugins'),'plugins.php','images/menu/plugins.png', + preg_match('/plugins.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + $_menu['System']->prependItem(__('Users'),'users.php','images/menu/users.png', + preg_match('/users.php$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + $_menu['System']->prependItem(__('Blogs'),'blogs.php','images/menu/blogs.png', + preg_match('/blogs.php$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin() || + $core->auth->check('usage,contentadmin',$core->blog->id) && $core->auth->blog_count > 1); +} +?> \ No newline at end of file diff --git a/inc/clearbricks/_common.php b/inc/clearbricks/_common.php new file mode 100644 index 0000000..571ce43 --- /dev/null +++ b/inc/clearbricks/_common.php @@ -0,0 +1,83 @@ + \ No newline at end of file diff --git a/inc/clearbricks/common/_main.php b/inc/clearbricks/common/_main.php new file mode 100644 index 0000000..be193cb --- /dev/null +++ b/inc/clearbricks/common/_main.php @@ -0,0 +1,58 @@ + dirname(__FILE__).'/lib.crypt.php', + 'dt' => dirname(__FILE__).'/lib.date.php', + 'files' => dirname(__FILE__).'/lib.files.php', + 'path' => dirname(__FILE__).'/lib.files.php', + 'form' => dirname(__FILE__).'/lib.form.php', + 'formSelectOption' => dirname(__FILE__).'/lib.form.php', + 'html' => dirname(__FILE__).'/lib.html.php', + 'http' => dirname(__FILE__).'/lib.http.php', + 'text' => dirname(__FILE__).'/lib.text.php' +); + +# autoload for clearbricks +function cb_autoload($name) +{ + global $__autoload; + + if (isset($__autoload[$name])) { + require_once $__autoload[$name]; + } +} + +# if php version >= 5.1.2, we can benefit from spl_autoload_register, +# so other libraries can define their own independent autoload too +if (function_exists("spl_autoload_register")) { + spl_autoload_register("cb_autoload"); +} else { + # otherwise we define a classic autoload function for older php... + function __autoload($name) { + cb_autoload($name); + } +} + +# We only need l10n __() function +require_once dirname(__FILE__).'/lib.l10n.php'; + +# We set default timezone to avoid warning +dt::setTZ('UTC'); +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.crypt.php b/inc/clearbricks/common/lib.crypt.php new file mode 100644 index 0000000..424fb10 --- /dev/null +++ b/inc/clearbricks/common/lib.crypt.php @@ -0,0 +1,77 @@ +$data, using the said $key + * and $hashfunc as hash method (sha1 or md5 are accepted.) + * + * @param string $key Hash key + * @param string $data Data + * @param string $hashfunc Hash function (md5 or sha1) + * @return string + */ + public static function hmac($key,$data,$hashfunc='sha1') + { + $blocksize=64; + if ($hashfunc != 'sha1') { + $hashfunc = 'md5'; + } + + if (strlen($key)>$blocksize) { + $key=pack('H*', $hashfunc($key)); + } + + $key=str_pad($key,$blocksize,chr(0x00)); + $ipad=str_repeat(chr(0x36),$blocksize); + $opad=str_repeat(chr(0x5c),$blocksize); + $hmac = pack('H*',$hashfunc(($key^$opad).pack('H*',$hashfunc(($key^$ipad).$data)))); + return bin2hex($hmac); + } + + /** + * Password generator + * + * Returns an 8 characters random password. + * + * @todo Add a length param + * + * @return string + */ + public static function createPassword() + { + $pwd = array(); + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; + $chars2 = '$!@'; + + foreach (range(0,7) as $i) { + $pwd[] = $chars[rand(0,strlen($chars)-1)]; + } + + $pos1 = array_rand(array(0,1,2,3)); + $pos2 = array_rand(array(4,5,6,7)); + $pwd[$pos1] = $chars2[rand(0,strlen($chars2)-1)]; + $pwd[$pos2] = $chars2[rand(0,strlen($chars2)-1)]; + + return implode('',$pwd); + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.date.php b/inc/clearbricks/common/lib.date.php new file mode 100644 index 0000000..5994b53 --- /dev/null +++ b/inc/clearbricks/common/lib.date.php @@ -0,0 +1,260 @@ + $v) { + $g = explode('/',$k); + $tmp[$g[0]][$k] = $v; + } + $res = $tmp; + } + } + + return $res; + } + + private static function _callback($args) + { + $b = array(1=>'_Jan',2=>'_Feb',3=>'_Mar',4=>'_Apr',5=>'_May',6=>'_Jun', + 7=>'_Jul',8=>'_Aug',9=>'_Sep',10=>'_Oct',11=>'_Nov',12=>'_Dec'); + + $B = array(1=>'January',2=>'February',3=>'March',4=>'April', + 5=>'May',6=>'June',7=>'July',8=>'August',9=>'September', + 10=>'October',11=>'November',12=>'December'); + + $a = array(1=>'_Mon',2=>'_Tue',3=>'_Wed',4=>'_Thu',5=>'_Fri', + 6=>'_Sat',0=>'_Sun'); + + $A = array(1=>'Monday',2=>'Tuesday',3=>'Wednesday',4=>'Thursday', + 5=>'Friday',6=>'Saturday',0=>'Sunday'); + + return __(${$args[1]}[(integer) $args[2]]); + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.files.php b/inc/clearbricks/common/lib.files.php new file mode 100644 index 0000000..6bd3fb1 --- /dev/null +++ b/inc/clearbricks/common/lib.files.php @@ -0,0 +1,667 @@ + 'application/vnd.oasis.opendocument.text', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + + 'sxw' => 'application/vnd.sun.xml.writer', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxi' => 'application/vnd.sun.xml.impress', + + 'ppt' => 'application/mspowerpoint', + 'doc' => 'application/msword', + 'xls' => 'application/msexcel', + 'rtf' => 'application/rtf', + + 'pdf' => 'application/pdf', + 'ps' => 'application/postscript', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'json' => 'application/json', + 'xml' => 'application/xml', + + 'bin' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + + 'bz2' => 'application/x-bzip', + 'deb' => 'application/x-debian-package', + 'gz' => 'application/x-gzip', + 'jar' => 'application/x-java-archive', + 'rar' => 'application/rar', + 'rpm' => 'application/x-redhat-package-manager', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-gtar', + 'zip' => 'application/zip', + + 'aiff' => 'audio/x-aiff', + 'ua' => 'audio/basic', + 'mp3' => 'audio/mpeg3', + 'mid' => 'audio/x-midi', + 'midi' => 'audio/x-midi', + 'ogg' => 'application/ogg', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'wav' => 'audio/x-wav', + 'wma' => 'audio/x-ms-wma', + + 'swf' => 'application/x-shockwave-flash', + 'swfl' => 'application/x-shockwave-flash', + + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'ico' => 'image/vnd.microsoft.icon', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'xbm' => 'image/x-xbitmap', + + 'css' => 'text/css', + 'csv' => 'text/csv', + 'js' => 'text/javascript', + 'html' => 'text/html', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'rtf' => 'text/richtext', + 'rtx' => 'text/richtext', + + 'mpg' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'ogv' => 'video/ogg', + 'viv' => 'video/vnd.vivo', + 'vivo' => 'video/vnd.vivo', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mp4' => 'video/mp4', + 'm4v' => 'video/x-m4v', + 'flv' => 'video/x-flv', + 'avi' => 'video/x-msvideo', + 'wmv' => 'video/x-ms-wmv' + ); + + /** + * Directory scanning + * + * Returns a directory child files and directories. + * + * @param string $d Path to scan + * @param boolean $order Order results + * @return array + */ + public static function scandir($d,$order=false) + { + $res = array(); + $dh = @opendir($d); + + if ($dh === false) { + throw new Exception(__('Unable to open directory.')); + } + + while (($f = readdir($dh)) !== false) { + $res[] = $f; + } + closedir($dh); + + sort($res); + if ($order) { + rsort($res); + } + + return $res; + } + + /** + * File extension + * + * Returns a file extension. + * + * @param string $f File name + * @return string + */ + public static function getExtension($f) + { + $f = explode('.',basename($f)); + + if (count($f) <= 1) { return ''; } + + return strtolower($f[count($f)-1]); + } + + /** + * MIME type + * + * Returns a file MIME type, based on static var {@link $mimeType} + * + * @param string $f File name + * @return string + */ + public static function getMimeType($f) + { + $ext = self::getExtension($f); + $types = self::mimeTypes(); + + if (isset($types[$ext])) { + return $types[$ext]; + } else { + return 'text/plain'; + } + } + + /** + * MIME types + * + * Returns all defined MIME types. + * + * @return array + */ + public static function mimeTypes() + { + return self::$mimeType; + } + + /** + * New MIME types + * + * Append new MIME types to defined MIME types. + * + * @param array $tab New MIME types. + */ + public static function registerMimeTypes($tab) + { + self::$mimeType = array_merge(self::$mimeType, $tab); + } + + /** + * Is a file or directory deletable. + * + * Returns true if $f is a file or directory and is deletable. + * + * @param string $f File or directory + * @return boolean + */ + public static function isDeletable($f) + { + if (is_file($f)) { + return is_writable(dirname($f)); + } elseif (is_dir($f)) { + return (is_writable(dirname($f)) && count(files::scandir($f)) <= 2); + } + } + + /** + * Recursive removal + * + * Remove recursively a directory. + * + * @param string $dir Directory patch + * @return boolean + */ + public static function deltree($dir) + { + $current_dir = opendir($dir); + while($entryname = readdir($current_dir)) + { + if (is_dir($dir.'/'.$entryname) and ($entryname != '.' and $entryname!='..')) + { + if (!files::deltree($dir.'/'.$entryname)) { + return false; + } + } + elseif ($entryname != '.' and $entryname!='..') + { + if (!@unlink($dir.'/'.$entryname)) { + return false; + } + } + } + closedir($current_dir); + return @rmdir($dir); + } + + /** + * Touch file + * + * Set file modification time to now. + * + * @param string $f File to change + */ + public static function touch($f) + { + if (is_writable($f)) { + if (function_exists('touch')) { + @touch($f); + } else { + # Very bad hack + @file_put_contents($f,file_get_contents($f)); + } + } + } + + /** + * Directory creation. + * + * Creates directory $f. If $r is true, attempts to create needed parents + * directories. + * + * @param string $f Directory to create + * @param boolean $r Create parent directories + */ + public static function makeDir($f,$r=false) + { + if (empty($f)) { + return; + } + + if (DIRECTORY_SEPARATOR == '\\') { + $f = str_replace('/','\\',$f); + } + + if (is_dir($f)) { + return; + } + + if ($r) + { + $dir = path::real($f,false); + $dirs = array(); + + while (!is_dir($dir)) { + array_unshift($dirs,basename($dir)); + $dir = dirname($dir); + } + + foreach ($dirs as $d) + { + $dir .= DIRECTORY_SEPARATOR.$d; + if ($d != '' && !is_dir($dir)) { + self::makeDir($dir); + } + } + } + else + { + if (@mkdir($f) === false) { + throw new Exception(__('Unable to create directory.')); + } + self::inheritChmod($f); + } + } + + /** + * Mode inheritage + * + * Sets file or directory mode according to its parent. + * + * @param string $file File to change + */ + public static function inheritChmod($file) + { + if (!function_exists('fileperms') || !function_exists('chmod')) { + return false; + } + + if (self::$dir_mode != null) { + return @chmod($file,self::$dir_mode); + } else { + return @chmod($file,fileperms(dirname($file))); + } + } + + /** + * Changes file content. + * + * Writes $f_content into $f file. + * + * @param string $f File to edit + * @param string $f_content Content to write + */ + public static function putContent($f, $f_content) + { + if (file_exists($f) && !is_writable($f)) { + throw new Exception(__('File is not writable.')); + } + + $fp = @fopen($f, 'w'); + + if ($fp === false) { + throw new Exception(__('Unable to open file.')); + } + + fwrite($fp,$f_content,strlen($f_content)); + fclose($fp); + return true; + } + + /** + * Human readable file size. + * + * @param integer $size Bytes + * @return string + */ + public static function size($size) + { + $kb = 1024; + $mb = 1024 * $kb; + $gb = 1024 * $mb; + $tb = 1024 * $gb; + + if($size < $kb) { + return $size." B"; + } + else if($size < $mb) { + return round($size/$kb,2)." KB"; + } + else if($size < $gb) { + return round($size/$mb,2)." MB"; + } + else if($size < $tb) { + return round($size/$gb,2)." GB"; + } + else { + return round($size/$tb,2)." TB"; + } + } + + /** + * Converts a human readable file size to bytes. + * + * @param string $v Size + * @return integer + */ + public static function str2bytes($v) + { + $v = trim($v); + $last = strtolower(substr($v,-1,1)); + + switch($last) + { + case 'g': + $v *= 1024; + case 'm': + $v *= 1024; + case 'k': + $v *= 1024; + } + + return $v; + } + + /** + * Upload status + * + * Returns true if upload status is ok, throws an exception instead. + * + * @param array $file File array as found in $_FILES + * @return boolean + */ + public static function uploadStatus($file) + { + if (!isset($file['error'])) { + throw new Exception(__('Not an uploaded file.')); + } + + switch ($file['error']) { + case UPLOAD_ERR_OK: + return true; + case UPLOAD_ERR_INI_SIZE: + case UPLOAD_ERR_FORM_SIZE: + throw new Exception(__('The uploaded file exceeds the maximum file size allowed.')); + return false; + case UPLOAD_ERR_PARTIAL: + throw new Exception(__('The uploaded file was only partially uploaded.')); + return false; + case UPLOAD_ERR_NO_FILE: + throw new Exception(__('No file was uploaded.')); + return false; + case UPLOAD_ERR_NO_TMP_DIR: + throw new Exception(__('Missing a temporary folder.')); + return false; + case UPLOAD_ERR_CANT_WRITE: + throw new Exception(__('Failed to write file to disk.')); + return false; + default: + return true; + } + } + + # Packages generation methods + # + /** + * Recursive directory scanning + * + * Returns an array of a given directory's content. The array contains + * two arrays: dirs and files. Directory's content is fetched recursively. + * + * @param string $dirName Directory name + * @param array $contents Contents array. Leave it empty + * @return array + */ + public static function getDirList($dirName, &$contents = null) + { + if (!$contents) { + $contents = array('dirs'=> array(),'files' => array()); + } + + $exclude_list=array('.','..','.svn'); + + if (empty($res)) { + $res = array(); + } + + $dirName = preg_replace('|/$|','',$dirName); + + if (!is_dir($dirName)) { + throw new Exception(sprintf(__('%s is not a directory.'),$dirName)); + } + + $contents['dirs'][] = $dirName; + + $d = @dir($dirName); + + if ($d === false) { + throw new Exception(__('Unable to open directory.')); + } + + while($entry = $d->read()) + { + if (!in_array($entry,$exclude_list)) + { + if (is_dir($dirName.'/'.$entry)) + { + files::getDirList($dirName.'/'.$entry, $contents); + } + else + { + $contents['files'][] = $dirName.'/'.$entry; + } + } + } + $d->close(); + + return $contents; + } + + /** + * Filename cleanup + * + * Removes unwanted characters in a filename. + * + * @param string $n Filename + * @return string + */ + public static function tidyFileName($n) + { + $n = text::deaccent($n); + $n = preg_replace('/^[.]/u','',$n); + return preg_replace('/[^A-Za-z0-9._-]/u','_',$n); + } +} + +/** +* Path manipulation utilities +* +* @package Clearbricks +* @subpackage Common +*/ +class path +{ + /** + * Returns the real path of a file. + * + * If parameter $strict is true, file should exist. Returns false if + * file does not exist. + * + * @param string $p Filename + * @param boolean $strict File should exists + * @return string + */ + public static function real($p,$strict=true) + { + $os = (DIRECTORY_SEPARATOR == '\\') ? 'win' : 'nix'; + + # Absolute path? + if ($os == 'win') { + $_abs = preg_match('/^\w+:/',$p); + } else { + $_abs = substr($p,0,1) == '/'; + } + + # Standard path form + if ($os == 'win') { + $p = str_replace('\\','/',$p); + } + + # Adding root if !$_abs + if (!$_abs) { + $p = dirname($_SERVER['SCRIPT_FILENAME']).'/'.$p; + } + + # Clean up + $p = preg_replace('|/+|','/',$p); + + if (strlen($p) > 1) { + $p = preg_replace('|/$|','',$p); + } + + $_start = ''; + if ($os == 'win') { + list($_start,$p) = explode(':',$p); + $_start .= ':/'; + } else { + $_start = '/'; + } + $p = substr($p,1); + + # Go through + $P = explode('/',$p); + $res = array(); + + for ($i=0;$i 0) { + array_pop($res); + } + } else { + array_push($res,$P[$i]); + } + } + + $p = $_start.implode('/',$res); + + if ($strict && !@file_exists($p)) { + return false; + } + + return $p; + } + + /** + * Returns a clean file path + * + * @param string $p File path + * @return string + */ + public static function clean($p) + { + $p = str_replace('..','',$p); + $p = preg_replace('|/{2,}|','/',$p); + $p = preg_replace('|/$|','',$p); + + return $p; + } + + /** + * Path information + * + * Returns an array of information: + * - dirname + * - basename + * - extension + * - base (basename without extension) + * + * @param string $f File path + */ + public static function info($f) + { + $p = pathinfo($f); + $res = array(); + + $res['dirname'] = $p['dirname']; + $res['basename'] = $p['basename']; + $res['extension'] = isset($p['extension']) ? $p['extension'] : ''; + $res['base'] = preg_replace('/\.'.preg_quote($res['extension'],'/').'$/','',$res['basename']); + + return $res; + } + + /** + * Full path with root + * + * Returns a path with root concatenation unless path begins with a slash + * + * @param string $p File path + * @param string $root Root path + * @return string + */ + public static function fullFromRoot($p,$root) + { + if (substr($p,0,1) == '/') { + return $p; + } + + return $root.'/'.$p; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.form.php b/inc/clearbricks/common/lib.form.php new file mode 100644 index 0000000..effed9d --- /dev/null +++ b/inc/clearbricks/common/lib.form.php @@ -0,0 +1,347 @@ +'."\n"; + + $res .= self::comboOptions($data,$default); + + $res .= ''."\n"; + + return $res; + } + + private static function comboOptions($data,$default) + { + $res = ''; + $option = ''."\n"; + $optgroup = ''."\n".'%2$s'."\n"; + + foreach($data as $k => $v) + { + if (is_array($v)) { + $res .= sprintf($optgroup,$k,self::comboOptions($v,$default)); + } elseif ($v instanceof formSelectOption) { + $res .= $v->render($default); + } else { + $s = ($v == $default) ? ' selected="selected"' : ''; + $res .= sprintf($option,$v,$k,$s); + } + } + + return $res; + } + + /** + * Radio button + * + * Returns HTML code for a radio button. $nid could be a string or an array of + * name and ID. + * + * @param string|array $nid Element ID and name + * @param string $value Element value + * @param boolean $checked True if checked + * @param string $class Element class name + * @param string $tabindex Element tabindex + * @param boolean $disabled True if disabled + * @param string $extra_html Extra HTML attributes + * + * @return string + */ + public static function radio($nid, $value, $checked='', $class='', $tabindex='', + $disabled=false, $extra_html='') + { + self::getNameAndId($nid,$name,$id); + + $res = ''; + $res .= $default; + $res .= ''; + + return $res; + } + + /** + * Hidden field + * + * Returns HTML code for an hidden field. $nid could be a string or an array of + * name and ID. + * + * @param string|array $nid Element ID and name + * @param string $value Element value + * + * @return string + */ + public static function hidden($nid,$value) + { + self::getNameAndId($nid,$name,$id); + + $res = 'name = $name; + $this->value = $value; + $this->class_name = $class_name; + $this->html = $html; + } + + /** + * Option renderer + * + * Returns option HTML code + * + * @param boolean $default Option is selected + * @return string + */ + public function render($default) + { + $attr = $this->html; + $attr .= $this->class_name ? ' class="'.$this->class_name.'"' : ''; + + if ($this->value == $default) { + $attr .= ' selected="selected"'; + } + + return sprintf($this->option,$this->value,$this->name,$attr)."\n"; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.html.php b/inc/clearbricks/common/lib.html.php new file mode 100644 index 0000000..85c676d --- /dev/null +++ b/inc/clearbricks/common/lib.html.php @@ -0,0 +1,185 @@ + "'" + ); + + $str = str_replace(array_keys($extra),array_values($extra),$str); + + return html_entity_decode($str,ENT_QUOTES,'UTF-8'); + } + + /** + * Remove markup + * + * Removes every tags, comments, cdata from string + * + * @param string $str String to clean + * @return string + */ + public static function clean($str) + { + $str = strip_tags($str); + return $str; + } + + /** + * Javascript escape + * + * Returns a protected JavaScript string + * + * @param string $str String to protect + * @return string + */ + public static function escapeJS($str) + { + $str = htmlspecialchars($str,ENT_NOQUOTES,'UTF-8'); + $str = str_replace("'","\'",$str); + $str = str_replace('"','\"',$str); + return $str; + } + + /** + * URL escape + * + * Returns an escaped URL string for HTML content + * + * @param string $str String to escape + * @return string + */ + public static function escapeURL($str) + { + return str_replace('&','&',$str); + } + + /** + * URL sanitize + * + * Encode every parts between / in url + * + * @param string $str String to satinyze + * @return string + */ + public static function sanitizeURL($str) + { + return str_replace('%2F','/',rawurlencode($str)); + } + + /** + * Remove host in URL + * + * Removes host part in URL + * + * @param string $str URL to transform + * @return string + */ + public static function stripHostURL($url) + { + return preg_replace('|^[a-z]{3,}://.*?(/.*$)|','$1',$url); + } + + /** + * Set links to absolute ones + * + * Appends $root URL to URIs attributes in $str. + * + * @param string $str HTML to transform + * @param string $root Base URL + * @return string + */ + public static function absoluteURLs($str,$root) + { + self::$url_root = $root; + $attr = 'action|background|cite|classid|codebase|data|href|longdesc|profile|src|usemap'; + + $str = preg_replace_callback('/((?:'.$attr.')=")(.*?)(")/msu',array('self','absoluteURLHandler'),$str); + + foreach (self::$absolute_regs as $r) { + $str = preg_replace_callback($r,array('self','absoluteURLHandler'),$str); + } + + self::$url_root = null; + return $str; + } + + private static function absoluteURLHandler($m) + { + $url = $m[2]; + + $link = $m[1].'%s'.$m[3]; + $host = preg_replace('|^([a-z]{3,}://)(.*?)/(.*)$|','$1$2',self::$url_root); + + $parse = parse_url($m[2]); + if (empty($parse['scheme'])) + { + if (strpos($url,'/') === 0) { + $url = $host.$url; + } elseif (strpos($url,'#') === 0) { + $url = self::$url_root.$url; + } elseif (preg_match('|/$|',self::$url_root)) { + $url = self::$url_root.$url; + } else { + $url = dirname(self::$url_root).'/'.$url; + } + } + + return sprintf($link,$url); + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.http.php b/inc/clearbricks/common/lib.http.php new file mode 100644 index 0000000..3d1c885 --- /dev/null +++ b/inc/clearbricks/common/lib.http.php @@ -0,0 +1,418 @@ += $ts) + { + self::head(304,'Not Modified'); + foreach ($headers as $v) { + header($v); + } + exit; + } + else + { + header('Date: '.gmdate('D, d M Y H:i:s',$now).' GMT'); + foreach ($headers as $v) { + header($v); + } + } + } + + /** + * HTTP Etag + * + * Sends HTTP cache headers (304) according to a list of etags in client request. + * + * @param string $p_content Response page content + */ + public static function etag() + { + # We create an etag from all arguments + $args = func_get_args(); + if (empty($args)) { + return; + } + + $etag = '"'.md5(implode('',$args)).'"'; + unset($args); + + header('ETag: '.$etag); + + # Do we have a previously sent content? + if (!empty($_SERVER['HTTP_IF_NONE_MATCH'])) + { + foreach (explode(',',$_SERVER['HTTP_IF_NONE_MATCH']) as $i) + { + if (stripslashes(trim($i)) == $etag) { + self::head(304,'Not Modified'); + exit; + } + } + } + } + + /** + * HTTP Header + * + * Sends an HTTP code and message to client. + * + * @param string $code HTTP code + * @param string $msg Message + */ + public static function head($code,$msg=null) + { + $status_mode = preg_match('/cgi/',PHP_SAPI); + + if (!$msg) + { + $msg_codes = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + + $msg = isset($msg_codes[$code]) ? $msg_codes[$code] : '-'; + } + + if ($status_mode) { + header('Status: '.$code.' '.$msg); + } else { + if (version_compare(phpversion(),'4.3.0','>=')) { + header($msg, true, $code); + } else { + header('HTTP/1.x '.$code.' '.$msg); + } + } + } + + /** + * Trim request + * + * Trims every value in GET, POST, REQUEST and COOKIE vars. + * Removes magic quotes if magic_quote_gpc is on. + */ + public static function trimRequest() + { + if(!empty($_GET)) { + array_walk($_GET,array('self','trimRequestHandler')); + } + if(!empty($_POST)) { + array_walk($_POST,array('self','trimRequestHandler')); + } + if(!empty($_REQUEST)) { + array_walk($_REQUEST,array('self','trimRequestHandler')); + } + if(!empty($_COOKIE)) { + array_walk($_COOKIE,array('self','trimRequestHandler')); + } + } + + private static function trimRequestHandler(&$v,$key) + { + $v = self::trimRequestInVar($v); + } + + private static function trimRequestInVar($value) + { + if (is_array($value)) + { + $result = array(); + foreach ($value as $k => $v) + { + if (is_array($v)) { + $result[$k] = self::trimRequestInVar($v); + } else { + if (get_magic_quotes_gpc()) { + $v = stripslashes($v); + } + $result[$k] = trim($v); + } + } + return $result; + } + else + { + if (get_magic_quotes_gpc()) { + $value = stripslashes($value); + } + return trim($value); + } + } + + /** + * Unset global variables + * + * If register_globals is on, removes every GET, POST, COOKIE, REQUEST, SERVER, + * ENV, FILES vars from GLOBALS. + */ + public static function unsetGlobals() + { + if (!ini_get('register_globals')) { + return; + } + + if (isset($_REQUEST['GLOBALS'])) { + throw new Exception('GLOBALS overwrite attempt detected'); + } + + # Variables that shouldn't be unset + $no_unset = array('GLOBALS','_GET','_POST','_COOKIE','_REQUEST', + '_SERVER','_ENV','_FILES'); + + $input = array_merge($_GET,$_POST,$_COOKIE,$_SERVER,$_ENV,$_FILES, + (isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array())); + + foreach ($input as $k => $v) { + if (!in_array($k,$no_unset) && isset($GLOBALS[$k]) ) { + $GLOBALS[$k] = null; + unset($GLOBALS[$k]); + } + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.l10n.php b/inc/clearbricks/common/lib.l10n.php new file mode 100644 index 0000000..087ab41 --- /dev/null +++ b/inc/clearbricks/common/lib.l10n.php @@ -0,0 +1,451 @@ + 'Afaraf', + 'ab' => 'Аҧсуа', + 'ae' => 'avesta', + 'af' => 'Afrikaans', + 'ak' => 'Akan', + 'am' => 'አማርኛ', + 'an' => 'Aragonés', + 'ar' => '‫العربية', + 'as' => 'অসমীয়া', + 'av' => 'авар мацӀ', + 'ay' => 'aymar aru', + 'az' => 'azərbaycan dili', + 'ba' => 'башҡорт теле', + 'be' => 'Беларуская', + 'bg' => 'български език', + 'bh' => 'भोजपुरी', + 'bi' => 'Bislama', + 'bm' => 'bamanankan', + 'bn' => 'বাংলা', + 'bo' => 'བོད་ཡིག', + 'br' => 'brezhoneg', + 'bs' => 'bosanski jezik', + 'ca' => 'Català', + 'ce' => 'нохчийн мотт', + 'ch' => 'Chamoru', + 'co' => 'corsu', + 'cr' => 'ᓀᐦᐃᔭᐍᐏᐣ', + 'cs' => 'česky', + 'cu' => 'ѩзыкъ словѣньскъ', + 'cv' => 'чӑваш чӗлхи', + 'cy' => 'Cymraeg', + 'da' => 'dansk', + 'de' => 'Deutsch', + 'dv' => '‫ދިވެހި', + 'dz' => 'རྫོང་ཁ', + 'ee' => 'Ɛʋɛgbɛ', + 'el' => 'Ελληνικά', + 'en' => 'English', + 'eo' => 'Esperanto', + 'es' => 'español', + 'et' => 'Eesti keel', + 'eu' => 'euskara', + 'fa' => '‫فارسی', + 'ff' => 'Fulfulde', + 'fi' => 'suomen kieli', + 'fj' => 'vosa Vakaviti', + 'fo' => 'Føroyskt', + 'fr' => 'français', + 'fy' => 'Frysk', + 'ga' => 'Gaeilge', + 'gd' => 'Gàidhlig', + 'gl' => 'Galego', + 'gn' => "Avañe'ẽ", + 'gu' => 'ગુજરાતી', + 'gv' => 'Ghaelg', + 'ha' => '‫هَوُسَ', + 'he' => '‫עברית', + 'hi' => 'हिन्दी', + 'ho' => 'Hiri Motu', + 'hr' => 'Hrvatski', + 'ht' => 'Kreyòl ayisyen', + 'hu' => 'Magyar', + 'hy' => 'Հայերեն', + 'hz' => 'Otjiherero', + 'ia' => 'Interlingua', + 'id' => 'Bahasa Indonesia', + 'ie' => 'Interlingue', + 'ig' => 'Igbo', + 'ii' => 'ꆇꉙ', + 'ik' => 'Iñupiaq', + 'io' => 'Ido', + 'is' => 'Íslenska', + 'it' => 'Italiano', + 'iu' => 'ᐃᓄᒃᑎᑐᑦ', + 'ja' => '日本語', + 'jv' => 'basa Jawa', + 'ka' => 'ქართული', + 'kg' => 'KiKongo', + 'ki' => 'Gĩkũyũ', + 'kj' => 'Kuanyama', + 'kk' => 'Қазақ тілі', + 'kl' => 'kalaallisut', + 'km' => 'ភាសាខ្មែរ', + 'kn' => 'ಕನ್ನಡ', + 'ko' => '한국어', + 'kr' => 'Kanuri', + 'ks' => 'कश्मीरी', + 'ku' => 'Kurdî', + 'kv' => 'коми кыв', + 'kw' => 'Kernewek', + 'ky' => 'кыргыз тили', + 'la' => 'latine', + 'lb' => 'Lëtzebuergesch', + 'lg' => 'Luganda', + 'li' => 'Limburgs', + 'ln' => 'Lingála', + 'lo' => 'ພາສາລາວ', + 'lt' => 'lietuvių kalba', + 'lu' => 'Luba-Katanga ', + 'lv' => 'latviešu valoda', + 'mg' => 'Malagasy fiteny', + 'mh' => 'Kajin M̧ajeļ', + 'mi' => 'te reo Māori', + 'mk' => 'македонски јазик', + 'ml' => 'മലയാളം', + 'mn' => 'Монгол', + 'mo' => 'Limba moldovenească', + 'mr' => 'मराठी', + 'ms' => 'bahasa Melayu', + 'mt' => 'Malti', + 'my' => 'ဗမာစာ', + 'na' => 'Ekakairũ Naoero', + 'nb' => 'Norsk bokmål', + 'nd' => 'isiNdebele', + 'ne' => 'नेपाली', + 'ng' => 'Owambo', + 'nl' => 'Nederlands', + 'nl-be' => 'Nederlands (Belgium)', + 'nn' => 'Norsk nynorsk', + 'no' => 'Norsk', + 'nr' => 'Ndébélé', + 'nv' => 'Diné bizaad', + 'ny' => 'chiCheŵa', + 'oc' => 'Occitan', + 'oj' => 'ᐊᓂᔑᓈᐯᒧᐎᓐ', + 'om' => 'Afaan Oromoo', + 'or' => 'ଓଡ଼ିଆ', + 'os' => 'Ирон æвзаг', + 'pa' => 'ਪੰਜਾਬੀ', + 'pi' => 'पाऴि', + 'pl' => 'polski', + 'ps' => '‫پښتو', + 'pt' => 'Português', + 'pt-br' => 'Português (Brasil)', + 'qu' => 'Runa Simi', + 'rm' => 'rumantsch grischun', + 'rn' => 'kiRundi', + 'ro' => 'română', + 'ru' => 'Русский', + 'rw' => 'Ikinyarwanda', + 'sa' => 'संस्कृतम्', + 'sc' => 'sardu', + 'sd' => 'सिन्धी', + 'se' => 'Davvisámegiella', + 'sg' => 'yângâ tî sängö', + 'sh' => 'Srpskohrvatski', + 'si' => 'සිංහල', + 'sk' => 'slovenčina', + 'sl' => 'slovenščina', + 'sm' => "gagana fa'a Samoa", + 'sn' => 'chiShona', + 'so' => 'Soomaaliga', + 'sq' => 'Shqip', + 'sr' => 'српски језик', + 'ss' => 'SiSwati', + 'st' => 'seSotho', + 'su' => 'Basa Sunda', + 'sv' => 'Svenska', + 'sw' => 'Kiswahili', + 'ta' => 'தமிழ்', + 'te' => 'తెలుగు', + 'tg' => 'тоҷикӣ', + 'th' => 'ไทย', + 'ti' => 'ትግርኛ', + 'tk' => 'Türkmen', + 'tl' => 'Tagalog', + 'tn' => 'seTswana', + 'to' => 'faka Tonga', + 'tr' => 'Türkçe', + 'ts' => 'xiTsonga', + 'tt' => 'татарча', + 'tw' => 'Twi', + 'ty' => 'Reo Mā`ohi', + 'ug' => 'Uyƣurqə', + 'uk' => 'Українська', + 'ur' => '‫اردو', + 'uz' => "O'zbek", + 've' => 'tshiVenḓa', + 'vi' => 'Tiếng Việt', + 'vo' => 'Volapük', + 'wa' => 'Walon', + 'wo' => 'Wollof', + 'xh' => 'isiXhosa', + 'yi' => '‫ייִדיש', + 'yo' => 'Yorùbá', + 'za' => 'Saɯ cueŋƅ', + 'zh' => '中文', + 'zh-hk' => '中文 (香港)', + 'zh-tw' => '中文 (臺灣)', + 'zu' => 'isiZulu' + ); + } + + $langs = self::$langs; + if ($name_with_code) { + foreach ($langs as $k => &$v) { + $v = '('.$k.') '.$v; + } + } + + if ($flip) { + return array_flip($langs); + } + + return $langs; + } + + /** + * Text direction + * + * Returns text direction for a given language. + * If text direction was forced with {@link $text_direction}, returns this + * value. + * + * @param string $lang Language code + * @return string + */ + public static function getTextDirection($lang) + { + if (self::$text_direction) { + return self::$text_direction; + } + + if (preg_match('/^(ar|dv|fa|ha|he|ps|ur|yi)$/i',$lang)) { + return 'rtl'; + } + return 'ltr'; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/lib.text.php b/inc/clearbricks/common/lib.text.php new file mode 100644 index 0000000..fabe582 --- /dev/null +++ b/inc/clearbricks/common/lib.text.php @@ -0,0 +1,355 @@ + $p) { + $str = preg_replace('/['.$p.']/u',$r,$str); + } + + return $str; + } + + /** + * String to URL + * + * Transforms a string to a proper URL. + * + * @param string $str String to transform + * @param boolean $with_slashes Keep slashes in URL + * @return string + */ + public static function str2URL($str,$with_slashes=true) + { + $str = self::deaccent($str); + $str = preg_replace('/[^A-Za-z0-9_\s\'\:\/[\]-]/','',$str); + + return self::tidyURL($str,$with_slashes); + } + + /** + * URL cleanup + * + * @param string $str URL to tidy + * @param boolean $keep_slashes Keep slashes in URL + * @param boolean $keep_spaces Keep spaces in URL + * @return string + */ + public static function tidyURL($str,$keep_slashes=true,$keep_spaces=false) + { + $str = strip_tags($str); + $str = str_replace(array('?','&','#','=','+','<','>','"','%'),'',$str); + $str = str_replace("'",' ',$str); + $str = preg_replace('/[\s]+/',' ',trim($str)); + + if (!$keep_slashes) { + $str = str_replace('/','-',$str); + } + + if (!$keep_spaces) { + $str = str_replace(' ','-',$str); + } + + $str = preg_replace('/[-]+/','-',$str); + + # Remove path changes in URL + $str = preg_replace('%^/%','',$str); + $str = preg_replace('%\.+/%','',$str); + + return $str; + } + + /** + * Cut string + * + * Returns a cuted string on spaced at given length $l. + * + * @param string $str String to cut + * @param integer $l Length to keep + * @return string + */ + public static function cutString($str,$l) + { + $s = preg_split('/([\s]+)/u',$str,-1,PREG_SPLIT_DELIM_CAPTURE); + + $res = ''; + $L = 0; + + if (mb_strlen($s[0]) >= $l) { + return mb_substr($s[0],0,$l); + } + + foreach ($s as $v) + { + $L = $L+mb_strlen($v); + + if ($L > $l) { + break; + } else { + $res .= $v; + } + } + + return trim($res); + } + + /** + * Split words + * + * Returns an array of words from a given string. + * + * @param string $str Words to split + * @return array + */ + public static function splitWords($str) + { + $non_word = '\x{0000}-\x{002F}\x{003A}-\x{0040}\x{005b}-\x{0060}\x{007B}-\x{007E}\x{00A0}-\x{00BF}\s'; + if (preg_match_all('/([^'.$non_word.']{3,})/msu',html::clean($str),$match)) { + foreach ($match[1] as $i => $v) { + $match[1][$i] = mb_strtolower($v); + } + return $match[1]; + } + return array(); + } + + /** + * Encoding detection + * + * Returns the encoding (in lowercase) of given $str. + * + * @param string $str String + * @return string + */ + public static function detectEncoding($str) + { + return strtolower(mb_detect_encoding($str.' ', + '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')); + } + + /** + * UTF8 conversions + * + * Returns an UTF-8 converted string. If $encoding is not specified, the + * function will try to detect encoding. + * + * @param string $str String to convert + * @param string $encoding Optionnal "from" encoding + * @return string + */ + public static function toUTF8($str,$encoding=null) + { + if (!$encoding) { + $encoding = self::detectEncoding($str); + } + + if ($encoding != 'utf-8') { + $str = iconv($encoding,'UTF-8',$str); + } + + return $str; + } + + /** + * Find bad UTF8 tokens + * + * Locates the first bad byte in a UTF-8 string returning it's + * byte index in the string + * PCRE Pattern to locate bad bytes in a UTF-8 string + * Comes from W3 FAQ: Multilingual Forms + * Note: modified to include full ASCII range including control chars + * + * @copyright Harry Fuecks + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU LGPL 2.1 + * @link http://phputf8.sourceforge.net + * + * @param string $str String to search + * @return integer|false + */ + public static function utf8badFind($str) + { + $UTF8_BAD = + '([\x00-\x7F]'. # ASCII (including control chars) + '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte + '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs + '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte + '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates + '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 + '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 + '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 + '|(.{1}))'; # invalid byte + $pos = 0; + $badList = array(); + + while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { + $bytes = strlen($matches[0]); + if ( isset($matches[2])) { + return $pos; + } + $pos += $bytes; + $str = substr($str,$bytes); + } + return false; + } + + /** + * UTF8 cleanup + * + * Replaces non utf8 bytes in $str by $repl. + * + * @copyright Harry Fuecks + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU LGPL 2.1 + * @link http://phputf8.sourceforge.net + * + * @param string $str String to clean + * @param string $repl Replacement string + * @return string + */ + public static function cleanUTF8($str,$repl='?') + { + while (($bad_index = self::utf8badFind($str)) !== false) { + $str = substr_replace($str,$repl,$bad_index,1); + } + + return $str; + } + + /** + * BOM removal + * + * Removes BOM from the begining of a string if present. + * + * @param string $str String to clean + * @return string + */ + public static function removeBOM($str) + { + if (substr_count($str,'')) { + return str_replace('','',$str); + } + + return $str; + } + + /** + * Quoted printable conversion + * + * Encodes given str to quoted printable + * + * @param string $str String to encode + * @return string + */ + public static function QPEncode($str) + { + $res = ''; + + foreach (preg_split("/\r?\n/msu", $str) as $line) + { + $l = ''; + preg_match_all('/./',$line,$m); + + foreach ($m[0] as $c) + { + $a = ord($c); + + if ($a < 32 || $a == 61 || $a > 126) { + $c = sprintf('=%02X',$a); + } + + $l .= $c; + } + + $res .= $l."\r\n"; + } + return $res; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/common/tz.dat b/inc/clearbricks/common/tz.dat new file mode 100644 index 0000000..09cc367 --- /dev/null +++ b/inc/clearbricks/common/tz.dat @@ -0,0 +1,407 @@ +Africa/Abidjan +Africa/Accra +Africa/Addis_Ababa +Africa/Algiers +Africa/Asmera +Africa/Bamako +Africa/Bangui +Africa/Banjul +Africa/Bissau +Africa/Blantyre +Africa/Brazzaville +Africa/Bujumbura +Africa/Cairo +Africa/Casablanca +Africa/Ceuta +Africa/Conakry +Africa/Dakar +Africa/Dar_es_Salaam +Africa/Djibouti +Africa/Douala +Africa/El_Aaiun +Africa/Freetown +Africa/Gaborone +Africa/Harare +Africa/Johannesburg +Africa/Kampala +Africa/Khartoum +Africa/Kigali +Africa/Kinshasa +Africa/Lagos +Africa/Libreville +Africa/Lome +Africa/Luanda +Africa/Lubumbashi +Africa/Lusaka +Africa/Malabo +Africa/Maputo +Africa/Maseru +Africa/Mbabane +Africa/Mogadishu +Africa/Monrovia +Africa/Nairobi +Africa/Ndjamena +Africa/Niamey +Africa/Nouakchott +Africa/Ouagadougou +Africa/Porto-Novo +Africa/Sao_Tome +Africa/Timbuktu +Africa/Tripoli +Africa/Tunis +Africa/Windhoek + + +America/Adak +America/Anchorage +America/Anguilla +America/Antigua +America/Araguaina +America/Argentina/Buenos_Aires +America/Argentina/Catamarca +America/Argentina/ComodRivadavia +America/Argentina/Cordoba +America/Argentina/Jujuy +America/Argentina/La_Rioja +America/Argentina/Mendoza +America/Argentina/Rio_Gallegos +America/Argentina/San_Juan +America/Argentina/Tucuman +America/Argentina/Ushuaia +America/Aruba +America/Asuncion +America/Bahia +America/Barbados +America/Belem +America/Belize +America/Boa_Vista +America/Bogota +America/Boise +America/Cambridge_Bay +America/Campo_Grande +America/Cancun +America/Caracas +America/Cayenne +America/Cayman +America/Chicago +America/Chihuahua +America/Costa_Rica +America/Cuiaba +America/Curacao +America/Danmarkshavn +America/Dawson +America/Dawson_Creek +America/Denver +America/Detroit +America/Dominica +America/Edmonton +America/Eirunepe +America/El_Salvador +America/Fortaleza +America/Glace_Bay +America/Godthab +America/Goose_Bay +America/Grand_Turk +America/Grenada +America/Guadeloupe +America/Guatemala +America/Guayaquil +America/Guyana +America/Halifax +America/Havana +America/Hermosillo +America/Indiana/Indianapolis +America/Indiana/Knox +America/Indiana/Marengo +America/Indiana/Vevay +America/Indianapolis +America/Inuvik +America/Iqaluit +America/Jamaica +America/Juneau +America/Kentucky/Louisville +America/Kentucky/Monticello +America/La_Paz +America/Lima +America/Los_Angeles +America/Louisville +America/Maceio +America/Managua +America/Manaus +America/Martinique +America/Mazatlan +America/Menominee +America/Merida +America/Mexico_City +America/Miquelon +America/Monterrey +America/Montevideo +America/Montreal +America/Montserrat +America/Nassau +America/New_York +America/Nipigon +America/Nome +America/Noronha +America/North_Dakota/Center +America/Panama +America/Pangnirtung +America/Paramaribo +America/Phoenix +America/Port-au-Prince +America/Port_of_Spain +America/Porto_Velho +America/Puerto_Rico +America/Rainy_River +America/Rankin_Inlet +America/Recife +America/Regina +America/Rio_Branco +America/Santiago +America/Santo_Domingo +America/Sao_Paulo +America/Scoresbysund +America/Shiprock +America/St_Johns +America/St_Kitts +America/St_Lucia +America/St_Thomas +America/St_Vincent +America/Swift_Current +America/Tegucigalpa +America/Thule +America/Thunder_Bay +America/Tijuana +America/Toronto +America/Tortola +America/Vancouver +America/Whitehorse +America/Winnipeg +America/Yakutat +America/Yellowknife + + +Antarctica/Casey +Antarctica/Davis +Antarctica/DumontDUrville +Antarctica/Mawson +Antarctica/McMurdo +Antarctica/Palmer +Antarctica/Rothera +Antarctica/South_Pole +Antarctica/Syowa +Antarctica/Vostok + + +Arctic/Longyearbyen + + +Asia/Aden +Asia/Almaty +Asia/Amman +Asia/Anadyr +Asia/Aqtau +Asia/Aqtobe +Asia/Ashgabat +Asia/Baghdad +Asia/Bahrain +Asia/Baku +Asia/Bangkok +Asia/Beirut +Asia/Bishkek +Asia/Brunei +Asia/Calcutta +Asia/Choibalsan +Asia/Chongqing +Asia/Colombo +Asia/Damascus +Asia/Dhaka +Asia/Dili +Asia/Dubai +Asia/Dushanbe +Asia/Gaza +Asia/Harbin +Asia/Hong_Kong +Asia/Hovd +Asia/Irkutsk +Asia/Istanbul +Asia/Jakarta +Asia/Jayapura +Asia/Jerusalem +Asia/Kabul +Asia/Kamchatka +Asia/Karachi +Asia/Kashgar +Asia/Katmandu +Asia/Krasnoyarsk +Asia/Kuala_Lumpur +Asia/Kuching +Asia/Kuwait +Asia/Macau +Asia/Magadan +Asia/Makassar +Asia/Manila +Asia/Muscat +Asia/Nicosia +Asia/Novosibirsk +Asia/Omsk +Asia/Oral +Asia/Phnom_Penh +Asia/Pontianak +Asia/Pyongyang +Asia/Qatar +Asia/Qyzylorda +Asia/Rangoon +Asia/Riyadh +Asia/Saigon +Asia/Sakhalin +Asia/Samarkand +Asia/Seoul +Asia/Shanghai +Asia/Singapore +Asia/Taipei +Asia/Tashkent +Asia/Tbilisi +Asia/Tehran +Asia/Thimphu +Asia/Tokyo +Asia/Ulaanbaatar +Asia/Urumqi +Asia/Vientiane +Asia/Vladivostok +Asia/Yakutsk +Asia/Yekaterinburg +Asia/Yerevan + + +Atlantic/Azores +Atlantic/Bermuda +Atlantic/Canary +Atlantic/Cape_Verde +Atlantic/Faeroe +Atlantic/Jan_Mayen +Atlantic/Madeira +Atlantic/Reykjavik +Atlantic/South_Georgia +Atlantic/St_Helena +Atlantic/Stanley + + +Australia/Adelaide +Australia/Brisbane +Australia/Broken_Hill +Australia/Darwin +Australia/Hobart +Australia/Lindeman +Australia/Lord_Howe +Australia/Melbourne +Australia/Perth +Australia/Sydney + + +Europe/Amsterdam +Europe/Andorra +Europe/Athens +Europe/Belfast +Europe/Belgrade +Europe/Berlin +Europe/Bratislava +Europe/Brussels +Europe/Bucharest +Europe/Budapest +Europe/Chisinau +Europe/Copenhagen +Europe/Dublin +Europe/Gibraltar +Europe/Helsinki +Europe/Istanbul +Europe/Kaliningrad +Europe/Kiev +Europe/Lisbon +Europe/Ljubljana +Europe/London +Europe/Luxembourg +Europe/Madrid +Europe/Malta +Europe/Mariehamn +Europe/Minsk +Europe/Monaco +Europe/Moscow +Europe/Nicosia +Europe/Oslo +Europe/Paris +Europe/Prague +Europe/Riga +Europe/Rome +Europe/Samara +Europe/San_Marino +Europe/Sarajevo +Europe/Simferopol +Europe/Skopje +Europe/Sofia +Europe/Stockholm +Europe/Tallinn +Europe/Tirane +Europe/Uzhgorod +Europe/Vaduz +Europe/Vatican +Europe/Vienna +Europe/Vilnius +Europe/Warsaw +Europe/Zagreb +Europe/Zaporozhye +Europe/Zurich + + +Indian/Antananarivo +Indian/Chagos +Indian/Christmas +Indian/Cocos +Indian/Comoro +Indian/Kerguelen +Indian/Mahe +Indian/Maldives +Indian/Mauritius +Indian/Mayotte +Indian/Reunion + + +Pacific/Apia +Pacific/Auckland +Pacific/Chatham +Pacific/Easter +Pacific/Efate +Pacific/Enderbury +Pacific/Fakaofo +Pacific/Fiji +Pacific/Funafuti +Pacific/Galapagos +Pacific/Gambier +Pacific/Guadalcanal +Pacific/Guam +Pacific/Honolulu +Pacific/Johnston +Pacific/Kiritimati +Pacific/Kosrae +Pacific/Kwajalein +Pacific/Majuro +Pacific/Marquesas +Pacific/Midway +Pacific/Nauru +Pacific/Niue +Pacific/Norfolk +Pacific/Noumea +Pacific/Pago_Pago +Pacific/Palau +Pacific/Pitcairn +Pacific/Ponape +Pacific/Port_Moresby +Pacific/Rarotonga +Pacific/Saipan +Pacific/Tahiti +Pacific/Tarawa +Pacific/Tongatapu +Pacific/Truk +Pacific/Wake +Pacific/Wallis +Pacific/Yap \ No newline at end of file diff --git a/inc/clearbricks/dblayer/class.cursor.php b/inc/clearbricks/dblayer/class.cursor.php new file mode 100644 index 0000000..1565894 --- /dev/null +++ b/inc/clearbricks/dblayer/class.cursor.php @@ -0,0 +1,258 @@ + + * openCursor('table'); + * $cur->field1 = 1; + * $cur->field2 = 'foo'; + * $cur->insert(); // Insert field ... + * + * $cur->update('WHERE field3 = 4'); // ... or update field + * ?> + * + * + * @see dbLayer::openCursor() + * @param dbLayer &$con Connection object + * @param string $table Table name + */ + public function __construct($con,$table) + { + $this->__con =& $con; + $this->setTable($table); + } + + /** + * Set table + * + * Changes working table and resets data + * + * @param string $table Table name + */ + public function setTable($table) + { + $this->__table = $table; + $this->__data = array(); + } + + /** + * Set field + * + * Set value $v to a field named $n. Value could be + * an string, an integer, a float, a null value or an array. + * + * If value is an array, its first value will be interpreted as a SQL + * command. String values will be automatically escaped. + * + * @see __set() + * @param string $n Field name + * @param mixed $v Field value + */ + public function setField($n,$v) + { + $this->__data[$n] = $v; + } + + /** + * Unset field + * + * Remove a field from data set. + * + * @param string $n Field name + */ + public function unsetField($n) + { + unset($this->__data[$n]); + } + + /** + * Field exists + * + * @return boolean true if field named $n exists + */ + public function isField($n) + { + return isset($this->__data[$n]); + } + + /** + * Field value + * + * @see __get() + * @return mixed value for a field named $n + */ + public function getField($n) + { + if (isset($this->__data[$n])) { + return $this->__data[$n]; + } + + return null; + } + + /** + * Set Field + * + * Magic alias for {@link setField()} + */ + public function __set($n,$v) + { + $this->setField($n,$v); + } + + /** + * Field value + * + * Magic alias for {@link getField()} + * + * @return mixed value for a field named $n + */ + public function __get($n) + { + return $this->getField($n); + } + + /** + * Empty data set + * + * Removes all data from data set + */ + public function clean() + { + $this->__data = array(); + } + + private function formatFields() + { + $data = array(); + + foreach ($this->__data as $k => $v) + { + $k = $this->__con->escapeSystem($k); + + if (is_null($v)) { + $data[$k] = 'NULL'; + } elseif (is_string($v)) { + $data[$k] = "'".$this->__con->escape($v)."'"; + } elseif (is_array($v)) { + $data[$k] = $v[0]; + } else { + $data[$k] = $v; + } + } + + return $data; + } + + /** + * Get insert query + * + * Returns the generated INSERT query + * + * @return string + */ + public function getInsert() + { + $data = $this->formatFields(); + + $insReq = 'INSERT INTO '.$this->__con->escapeSystem($this->__table)." (\n". + implode(",\n",array_keys($data))."\n) VALUES (\n". + implode(",\n",array_values($data))."\n) "; + + return $insReq; + } + + /** + * Get update query + * + * Returns the generated UPDATE query + * + * @param string $where WHERE condition + * @return string + */ + public function getUpdate($where) + { + $data = $this->formatFields(); + $fields = array(); + + $updReq = 'UPDATE '.$this->__con->escapeSystem($this->__table)." SET \n"; + + foreach ($data as $k => $v) { + $fields[] = $k.' = '.$v.""; + } + + $updReq .= implode(",\n",$fields); + $updReq .= "\n".$where; + + return $updReq; + } + + /** + * Execute insert query + * + * Executes the generated INSERT query + */ + public function insert() + { + if (!$this->__table) { + throw new Exception('No table name.'); + } + + $insReq = $this->getInsert(); + + $this->__con->execute($insReq); + + return true; + } + + /** + * Execute update query + * + * Executes the generated UPDATE query + * + * @param string $where WHERE condition + */ + public function update($where) + { + if (!$this->__table) { + throw new Exception('No table name.'); + } + + $updReq = $this->getUpdate($where); + + $this->__con->execute($updReq); + + return true; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dblayer/class.mysql.php b/inc/clearbricks/dblayer/class.mysql.php new file mode 100644 index 0000000..662d7e8 --- /dev/null +++ b/inc/clearbricks/dblayer/class.mysql.php @@ -0,0 +1,253 @@ +db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + if (!function_exists('mysql_pconnect')) { + throw new Exception('PHP MySQL functions are not available'); + } + + if (($link = @mysql_pconnect($host,$user,$password)) === false) { + throw new Exception('Unable to connect to database'); + } + + $this->db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + private function db_post_connect($link,$database) + { + if (@mysql_select_db($database,$link) === false) { + throw new Exception('Unable to use database '.$database); + } + + if (version_compare($this->db_version($link),'4.1','>=')) + { + $this->db_query($link,'SET NAMES utf8'); + $this->db_query($link,'SET CHARACTER SET utf8'); + $this->db_query($link,"SET COLLATION_CONNECTION = 'utf8_general_ci'"); + $this->db_query($link,"SET COLLATION_SERVER = 'utf8_general_ci'"); + $this->db_query($link,"SET CHARACTER_SET_SERVER = 'utf8'"); + $this->db_query($link,"SET CHARACTER_SET_DATABASE = 'utf8'"); + } + } + + /** @ignore */ + public function db_close($handle) + { + if (is_resource($handle)) { + mysql_close($handle); + } + } + + /** @ignore */ + public function db_version($handle) + { + if (is_resource($handle)) { + return mysql_get_server_info(); + } + return null; + } + + /** @ignore */ + public function db_query($handle,$query) + { + if (is_resource($handle)) + { + $res = @mysql_query($query,$handle); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if (is_resource($res)) { + return mysql_num_fields($res); + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + if (is_resource($res)) { + return mysql_num_rows($res); + } + return 0; + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if (is_resource($res)) { + return mysql_field_name($res,$position); + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if (is_resource($res)) { + return mysql_field_type($res,$position); + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + if (is_resource($res)) { + return mysql_fetch_assoc($res); + } + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + if (is_resource($res)) { + return mysql_data_seek($res,$row); + } + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if (is_resource($handle)) { + return mysql_affected_rows($handle); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if (is_resource($handle)) + { + $e = mysql_error($handle); + if ($e) { + return $e.' ('.mysql_errno($handle).')'; + } + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + if (is_resource($handle)) { + return mysql_real_escape_string($str,$handle); + } else { + return mysql_escape_string($str); + } + } + + /** @ignore */ + public function db_write_lock($table) + { + try { + $this->execute('LOCK TABLES '.$this->escapeSystem($table).' WRITE'); + } catch (Exception $e) { + # As lock is a privilege in MySQL, we can avoid errors with weak_locks static var + if (!self::$weak_locks) { + throw $e; + } + } + } + + /** @ignore */ + public function db_unlock() + { + try { + $this->execute('UNLOCK TABLES'); + } catch (Exception $e) { + if (!self::$weak_locks) { + throw $e; + } + } + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('OPTIMIZE TABLE '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + $pattern = str_replace('%M','%i',$pattern); + + return 'DATE_FORMAT('.$field.','."'".$this->escape($pattern)."') "; + } + + /** @ignore */ + public function concat() + { + $args = func_get_args(); + return 'CONCAT('.implode(',',$args).')'; + } + + /** @ignore */ + public function escapeSystem($str) + { + return '`'.$str.'`'; + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dblayer/class.pgsql.php b/inc/clearbricks/dblayer/class.pgsql.php new file mode 100644 index 0000000..66f8d05 --- /dev/null +++ b/inc/clearbricks/dblayer/class.pgsql.php @@ -0,0 +1,280 @@ +get_connection_string($host,$user,$password,$database); + + if (($link = @pg_connect($str)) === false) { + throw new Exception('Unable to connect to database'); + } + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + if (!function_exists('pg_pconnect')) { + throw new Exception('PHP PostgreSQL functions are not available'); + } + + $str = $this->get_connection_string($host,$user,$password,$database); + + if (($link = @pg_pconnect($str)) === false) { + throw new Exception('Unable to connect to database'); + } + + return $link; + } + + /** @ignore */ + public function db_close($handle) + { + if (is_resource($handle)) { + pg_close($handle); + } + } + + /** @ignore */ + public function db_version($handle) + { + if (is_resource($handle)) + { + return pg_parameter_status($handle,'server_version'); + } + return null; + } + + /** @ignore */ + public function db_query($handle,$query) + { + if (is_resource($handle)) + { + $res = @pg_query($handle,$query); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if (is_resource($res)) { + return pg_num_fields($res); + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + if (is_resource($res)) { + return pg_num_rows($res); + } + return 0; + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if (is_resource($res)) { + return pg_field_name($res,$position); + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if (is_resource($res)) { + return pg_field_type($res,$position); + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + if (is_resource($res)) { + return pg_fetch_assoc($res); + } + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + if (is_resource($res)) { + return pg_result_seek($res,(int) $row); + } + return false; + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if (is_resource($handle) && is_resource($res)) { + return pg_affected_rows($res); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if (is_resource($handle)) { + return pg_last_error($handle); + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + return pg_escape_string($str); + } + + /** @ignore */ + public function db_write_lock($table) + { + $this->execute('BEGIN'); + $this->execute('LOCK TABLE '.$this->escapeSystem($table).' IN EXCLUSIVE MODE'); + } + + /** @ignore */ + public function db_unlock() + { + $this->execute('END'); + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('VACUUM FULL '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + $rep = array( + '%d' => 'DD', + '%H' => 'HH24', + '%M' => 'MI', + '%m' => 'MM', + '%S' => 'SS', + '%Y' => 'YYYY' + ); + + $pattern = str_replace(array_keys($rep),array_values($rep),$pattern); + + return 'TO_CHAR('.$field.','."'".$this->escape($pattern)."') "; + } + + /** + * Function call + * + * Calls a PostgreSQL function an returns the result as a {@link record}. + * After $name, you can add any parameters you want to append + * them to the PostgreSQL function. You don't need to escape string in + * arguments. + * + * @param string $name Function name + * @return record + */ + public function callFunction($name) + { + $data = func_get_args(); + array_shift($data); + + foreach ($data as $k => $v) + { + if (is_null($v)) { + $data[$k] = 'NULL'; + } elseif (is_string($v)) { + $data[$k] = "'".$this->escape($v)."'"; + } elseif (is_array($v)) { + $data[$k] = $v[0]; + } else { + $data[$k] = $v; + } + } + + $req = + 'SELECT '.$name."(\n". + implode(",\n",array_values($data)). + "\n) "; + + return $this->select($req); + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dblayer/class.sqlite.php b/inc/clearbricks/dblayer/class.sqlite.php new file mode 100644 index 0000000..97fbe73 --- /dev/null +++ b/inc/clearbricks/dblayer/class.sqlite.php @@ -0,0 +1,278 @@ +db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + if (!class_exists('PDO') || !in_array('sqlite',PDO::getAvailableDrivers())) { + throw new Exception('PDO SQLite class is not available'); + } + + $link = new PDO('sqlite:'.$database,null,null,array(PDO::ATTR_PERSISTENT => true)); + $this->db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + private function db_post_connect($handle,$database) + { + if ($handle instanceof PDO) { + $this->db_exec($handle,'PRAGMA short_column_names = 1'); + $this->db_exec($handle,'PRAGMA encoding = "UTF-8"'); + $handle->sqliteCreateFunction('now',array($this,'now'),0); + } + } + + /** @ignore */ + public function db_close($handle) + { + if ($handle instanceof PDO) { + $handle = null; + $this->__link = null; + } + } + + /** @ignore */ + public function db_version($handle) + { + if ($handle instanceof PDO) { + return $handle->getAttribute(PDO::ATTR_SERVER_VERSION); + } + } + + # There is no other way than get all selected data in a staticRecord + /** @ignore */ + public function select($sql) + { + $result = $this->db_query($this->__link,$sql); + $this->__last_result =& $result; + + $info = array(); + $info['con'] =& $this; + $info['cols'] = $this->db_num_fields($result); + $info['info'] = array(); + + for ($i=0; $i<$info['cols']; $i++) { + $info['info']['name'][] = $this->db_field_name($result,$i); + $info['info']['type'][] = $this->db_field_type($result,$i); + } + + $data = array(); + while ($r = $result->fetch(PDO::FETCH_ASSOC)) + { + $R = array(); + foreach ($r as $k => $v) { + $k = preg_replace('/^(.*)\./','',$k); + $R[$k] = $v; + $R[] =& $R[$k]; + } + $data[] = $R; + } + + $info['rows'] = count($data); + $result->closeCursor(); + + return new staticRecord($data,$info); + } + + /** @ignore */ + public function db_query($handle,$query) + { + if ($handle instanceof PDO) + { + $res = $handle->query($query); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if ($res instanceof PDOStatement) { + return $res->columnCount(); + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if ($res instanceof PDOStatement) { + $m = $res->getColumnMeta($position); + return preg_replace('/^.+\./','',$m['name']); # we said short_column_names = 1 + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if ($res instanceof PDOStatement) { + $m = $res->getColumnMeta($position); + switch ($m['pdo_type']) { + case PDO::PARAM_BOOL: + return 'boolean'; + case PDO::PARAM_NULL: + return 'null'; + case PDO::PARAM_INT: + return 'integer'; + default: + return 'varchar'; + } + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if ($res instanceof PDOStatement) { + return $res->rowCount(); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if ($handle instanceof PDO) { + $err = $handle->errorInfo(); + return $err[2].' ('.$err[1].')'; + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + if ($handle instanceof PDO) { + return trim($handle->quote($str),"'"); + } + return $str; + } + + /** @ignore */ + public function escapeSystem($str) + { + return "'".$this->escape($str)."'"; + } + + /** @ignore */ + public function begin() + { + if ($this->__link instanceof PDO) { + $this->__link->beginTransaction(); + } + } + + /** @ignore */ + public function commit() + { + if ($this->__link instanceof PDO) { + $this->__link->commit(); + } + } + + /** @ignore */ + public function rollback() + { + if ($this->__link instanceof PDO) { + $this->__link->rollBack(); + } + } + + /** @ignore */ + public function db_write_lock($table) + { + $this->execute('BEGIN EXCLUSIVE TRANSACTION'); + } + + /** @ignore */ + public function db_unlock() + { + $this->execute('END'); + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('VACUUM '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + return "strftime('".$this->escape($pattern)."',".$field.') '; + } + + # Internal SQLite function that adds NOW() SQL function. + /** @ignore */ + public function now() + { + return date('Y-m-d H:i:s'); + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dblayer/dblayer.php b/inc/clearbricks/dblayer/dblayer.php new file mode 100644 index 0000000..ee22b13 --- /dev/null +++ b/inc/clearbricks/dblayer/dblayer.php @@ -0,0 +1,1144 @@ +$position. + * + * @param resource $res Resource result + * @param integer $position Field position + * @return string + */ + function db_field_name($res,$position); + + /** + * Field type + * + * This method should return the field type a the given position + * $position. + * + * @param resource $res Resource result + * @param integer $position Field position + * @return string + */ + function db_field_type($res,$position); + + /** + * Fetch result + * + * This method should fetch one line of result and return an associative array + * with field name as key and field value as value. + * + * @param resource $res Resource result + * @return array + */ + function db_fetch_assoc($res); + + /** + * Move result cursor + * + * This method should move result cursor on given row position $row + * and return true on success. + * + * @param resource $res Resource result + * @param integer $position Row position + * @return boolean + */ + function db_result_seek($res,$row); + + /** + * Affected rows + * + * This method should return number of rows affected by INSERT, UPDATE or + * DELETE queries. + * + * @param resource $handle Resource link + * @param resource $res Resource result + * @return integer + */ + function db_changes($handle,$res); + + /** + * Last error + * + * This method should return the last error string for the current connection. + * + * @param resource $handle Resource link + * @return string + */ + function db_last_error($handle); + + /** + * Escape string + * + * This method should return an escaped string for the current connection. + * + * @param string $str String to escape + * @param resource $handle Resource link + * @return string + */ + function db_escape_string($str,$handle=null); + + /** + * Acquiere Write lock + * + * This method should lock the given table in write access. + * + * @param string $table Table name + */ + function db_write_lock($table); + + /** + * Release lock + * + * This method should releases an acquiered lock. + */ + function db_unlock(); +} + +/** +* Database Abstraction Layer class +* +* Base class for database abstraction. Each driver extends this class and +* implements {@link i_dbLayer} interface. +* +* @package Clearbricks +* @subpackage DBLayer +*/ +class dbLayer +{ + /** @var string Driver name */ + protected $__driver = null; + + /** @var string Database version */ + protected $__version = null; + + /** @var resource Database resource link */ + protected $__link; + + /** @var resource Last result resource link */ + protected $__last_result; + + /** + * Start connection + * + * Static function to use to init database layer. Returns a object extending + * dbLayer. + * + * @param string $driver Driver name + * @param string $host Database hostname + * @param string $database Database name + * @param string $user User ID + * @param string $password Password + * @param string $persistent Persistent connection + * @return object + */ + public static function init($driver,$host,$database,$user='',$password='',$persistent=false) + { + if (file_exists(dirname(__FILE__).'/class.'.$driver.'.php')) { + require_once dirname(__FILE__).'/class.'.$driver.'.php'; + $driver_class = $driver.'Connection'; + } else { + trigger_error('Unable to load DB layer for '.$driver,E_USER_ERROR); + exit(1); + } + + return new $driver_class($host,$database,$user,$password,$persistent); + } + + /** + * @param string $host Database hostname + * @param string $database Database name + * @param string $user User ID + * @param string $password Password + * @param string $persistent Persistent connection + */ + public function __construct($host,$database,$user='',$password='',$persistent=false) + { + if ($persistent) { + $this->__link = $this->db_pconnect($host,$user,$password,$database); + } else { + $this->__link = $this->db_connect($host,$user,$password,$database); + } + + $this->__version = $this->db_version($this->__link); + $this->__database = $database; + } + + /** + * Closes database connection. + */ + public function close() + { + $this->db_close($this->__link); + } + + /** + * Returns database driver name + * + * @return string + */ + public function driver() + { + return $this->__driver; + } + + /** + * Returns database driver version + * + * @return string + */ + public function version() + { + return $this->__version; + } + + /** + * Returns current database name + * + * @return string + */ + public function database() + { + return $this->__database; + } + + /** + * Returns link resource + * + * @return resource + */ + public function link() + { + return $this->__link; + } + + /** + * Run query and get results + * + * Executes a query and return a {@link record} object. + * + * @param string $sql SQL query + * @return record + */ + public function select($sql) + { + $result = $this->db_query($this->__link,$sql); + + $this->__last_result =& $result; + + $info = array(); + $info['con'] =& $this; + $info['cols'] = $this->db_num_fields($result); + $info['rows'] = $this->db_num_rows($result); + $info['info'] = array(); + + for ($i=0; $i<$info['cols']; $i++) { + $info['info']['name'][] = $this->db_field_name($result,$i); + $info['info']['type'][] = $this->db_field_type($result,$i); + } + + return new record($result,$info); + } + + /** + * Run query + * + * Executes a query and return true if succeed + * + * @param string $sql SQL query + * @return true + */ + public function execute($sql) + { + $result = $this->db_exec($this->__link,$sql); + + $this->__last_result =& $result; + + return true; + } + + /** + * Begin transaction + * + * Begins a transaction. Transaction should be {@link commit() commited} + * or {@link rollback() rollbacked}. + */ + public function begin() + { + $this->execute('BEGIN'); + } + + /** + * Commit transaction + * + * Commits a previoulsy started transaction. + */ + public function commit() + { + $this->execute('COMMIT'); + } + + /** + * Rollback transaction + * + * Rollbacks a previously started transaction. + */ + public function rollback() + { + $this->execute('ROLLBACK'); + } + + /** + * Aquiere write lock + * + * This method lock the given table in write access. + * + * @param string $table Table name + */ + public function writeLock($table) + { + $this->db_write_lock($table); + } + + /** + * Release lock + * + * This method releases an acquiered lock. + */ + public function unlock() + { + $this->db_unlock(); + } + + /** + * Vacuum the table given in argument. + * + * @param string $table Table name + */ + public function vacuum($table) + { + } + + /** + * Changed rows + * + * Returns the number of lines affected by the last DELETE, INSERT or UPDATE + * query. + * + * @return integer + */ + public function changes() + { + return $this->db_changes($this->__link,$this->__last_result); + } + + /** + * Last error + * + * Returns the last database error or false if no error. + * + * @return string|false + */ + public function error() + { + $err = $this->db_last_error($this->__link); + + if (!$err) { + return false; + } + + return $err; + } + + /** + * Date formatting + * + * Returns a query fragment with date formater. + * + * The following modifiers are accepted: + * + * - %d : Day of the month, numeric + * - %H : Hour 24 (00..23) + * - %M : Minute (00..59) + * - %m : Month numeric (01..12) + * - %S : Seconds (00..59) + * - %Y : Year, numeric, four digits + * + * @param string $field Field name + * @param string $pattern Date format + * @return string + */ + public function dateFormat($field,$pattern) + { + return + 'TO_CHAR('.$field.','."'".$this->escape($pattern)."') "; + } + + /** + * Query Limit + * + * Returns a LIMIT query fragment. $arg1 could be an array of + * offset and limit or an integer which is only limit. If $arg2 + * is given and $arg1 is an integer, it would become limit. + * + * @param array|integer $arg1 array or integer with limit intervals + * @param array|null $arg2 integer or null + * @return string + */ + public function limit($arg1, $arg2=null) + { + if (is_array($arg1)) + { + $arg1 = array_values($arg1); + $arg2 = isset($arg1[1]) ? $arg1[1] : null; + $arg1 = $arg1[0]; + } + + if ($arg2 === null) { + $sql = ' LIMIT '.(integer) $arg1.' '; + } else { + $sql = ' LIMIT '.(integer) $arg2.' OFFSET '.$arg1.' '; + } + + return $sql; + } + + /** + * IN fragment + * + * Returns a IN query fragment where $in could be an array, a string, + * an integer or null + * + * @param array|string|integer|null $in "IN" values + * @return string + */ + public function in($in) + { + if (is_null($in)) + { + return ' IN (NULL) '; + } + elseif (is_string($in)) + { + return " IN ('".$this->escape($in)."') "; + } + elseif (is_array($in)) + { + foreach ($in as $i => $v) { + if (is_null($v)) { + $in[$i] = 'NULL'; + } elseif (is_string($v)) { + $in[$i] = "'".$this->escape($v)."'"; + } + } + return ' IN ('.implode(',',$in).') '; + } + else + { + return ' IN ( '.(integer) $in.') '; + } + } + + /** + * Concat strings + * + * Returns SQL concatenation of methods arguments. Theses arguments + * should be properly escaped when needed. + * + * @return string + */ + public function concat() + { + $args = func_get_args(); + return implode(' || ',$args); + } + + /** + * Escape string + * + * Returns SQL protected string or array values. + * + * @param string|array $i String or array to protect + * @return string|array + */ + public function escape($i) + { + if (is_array($i)) { + foreach ($i as $k => $s) { + $i[$k] = $this->db_escape_string($s,$this->__link); + } + return $i; + } + + return $this->db_escape_string($i,$this->__link); + } + + /** + * System escape string + * + * Returns SQL system protected string. + * + * @param string $str String to protect + * @return string + */ + public function escapeSystem($str) + { + return '"'.$str.'"'; + } + + /** + * Cursor object + * + * Returns a new instance of {@link cursor} class on $table for + * the current connection. + * + * @param string $table Target table + * @return cursor + */ + public function openCursor($table) + { + return new cursor($this,$table); + } +} + +/** +* Query Result Record Class +* +* This class acts as an iterator over database query result. It does not fetch +* all results on instantiation and thus, depending on database engine, should not +* fill PHP process memory. +* +* @package Clearbricks +* @subpackage DBLayer +*/ +class record +{ + /** @var resource Database resource link */ + protected $__link; + + /** @var resource Query result resource */ + protected $__result; + + /** @var array Result information array */ + protected $__info; + + /** @var array List of static functions that extend record */ + protected $__extend = array(); + + /** @var integer Current result position */ + protected $__index = 0; + + /** @var array Current result row content */ + protected $__row = false; + + private $__fetch = false; + + /** + * Constructor + * + * Creates class instance from result link and some informations. + * $info is an array with the following content: + * + * - con => database object instance + * - cols => number of columns + * - rows => number of rows + * - info[name] => an array with columns names + * - info[type] => an array with columns types + * + * @param resource $result Resource result + * @param array $info Information array + */ + public function __construct($result,$info) + { + $this->__result = $result; + $this->__info = $info; + $this->__link = $info['con']->link(); + $this->index(0); + } + + /** + * To staticRecord + * + * Converts this record to a {@link staticRecord} instance. + */ + public function toStatic() + { + if ($this instanceof staticRecord) { + return $this; + } + return new staticRecord($this->__result,$this->__info); + } + + /** + * Magic call + * + * Magic call function. Calls function added by {@link extend()} if exists, passing it + * self object and arguments. + * + * @return mixed + */ + public function __call($f,$args) + { + if (isset($this->__extend[$f])) + { + array_unshift($args,$this); + return call_user_func_array($this->__extend[$f],$args); + } + + trigger_error('Call to undefined method record::'.$f.'()',E_USER_ERROR); + } + + /** + * Magic get + * + * Alias for {@link field()}. + * + * @param string|integer $n Field name + * @return string + */ + public function __get($n) + { + return $this->field($n); + } + + /** + * Get field + * + * Alias for {@link field()}. + * + * @param string|integer $n Field name + * @return string + */ + public function f($n) + { + return $this->field($n); + } + + /** + * Get field + * + * Retrieve field value by its name or column position. + * + * @param string|integer $n Field name + * @return string + */ + public function field($n) + { + return $this->__row[$n]; + } + + /** + * Field exists + * + * Returns true if a field exists. + * + * @param string $n Field name + * @return string + */ + public function exists($n) + { + return isset($this->__row[$n]); + } + + /** + * Extend record + * + * Extends this instance capabilities by adding all public static methods of + * $class to current instance. Class methods should take at least + * this record as first parameter. + * + * @see __call() + * + * @param string $class Class name + */ + public function extend($class) + { + if (!class_exists($class)) { + return; + } + + $c = new ReflectionClass($class); + foreach ($c->getMethods() as $m) { + if ($m->isStatic() && $m->isPublic()) { + $this->__extend[$m->name] = array($class,$m->name); + } + } + } + + /** + * Returns record extensions. + * + * @return array + */ + public function extensions() + { + return $this->__extend; + } + + private function setRow() + { + $this->__row = $this->__info['con']->db_fetch_assoc($this->__result); + + if ($this->__row !== false) + { + foreach ($this->__row as $k => $v) { + $this->__row[] =& $this->__row[$k]; + } + return true; + } + else + { + return false; + } + } + + /** + * Returns the current index position (0 is first) or move to $row if + * specified. + * + * @param integer $row Row number to move + * @return integer + */ + public function index($row=null) + { + if ($row === null) { + return $this->__index === null ? 0 : $this->__index; + } + + if ($row < 0 || $row+1 > $this->__info['rows']) { + return false; + } + + if ($this->__info['con']->db_result_seek($this->__result,(integer) $row)) + { + $this->__index = $row; + $this->setRow(); + $this->__info['con']->db_result_seek($this->__result,(integer) $row); + return true; + } + return false; + } + + /** + * One step move index + * + * This method moves index forward and return true until index is not + * the last one. You can use it to loop over record. Example: + * + * fetch()) { + * echo $rs->field1; + * } + * ?> + * + * + * @return boolean + */ + public function fetch() + { + if (!$this->__fetch) { + $this->__fetch = true; + $i = -1; + } else { + $i = $this->__index; + } + + if (!$this->index($i+1)) { + $this->__fetch = false; + $this->__index = 0; + return false; + } + + return true; + } + + /** + * Moves index to first position. + * + * @return boolean + */ + public function moveStart() + { + return $this->index(0); + } + + /** + * Moves index to last position. + * + * @return boolean + */ + public function moveEnd() + { + return $this->index($this->__info['rows']-1); + } + + /** + * Moves index to next position. + * + * @return boolean + */ + public function moveNext() + { + return $this->index($this->__index+1); + } + + /** + * Moves index to previous position. + * + * @return boolean + */ + public function movePrev() + { + return $this->index($this->__index-1); + } + + /** + * @return boolean true if index is at last position + */ + public function isEnd() + { + return $this->__index+1 == $this->count(); + } + + /** + * @return boolean true if index is at first position. + */ + public function isStart() + { + return $this->__index <= 0; + } + + /** + * @return boolean true if record contains no result. + */ + public function isEmpty() + { + return $this->count() == 0; + } + + /** + * @return integer number of rows in record + */ + public function count() + { + return $this->__info['rows']; + } + + /** + * @return array array of columns, with name as key and type as value. + */ + public function columns() + { + return $this->__info['info']['name']; + } + + /** + * @return array all rows in record. + */ + public function rows() + { + return $this->getData(); + } + + /** + * All data + * + * Returns an array of all rows in record. This method is called by rows(). + * + * @return array + */ + protected function getData() + { + $res = array(); + + if ($this->count() == 0) { + return $res; + } + + $this->__info['con']->db_result_seek($this->__result,0); + while (($r = $this->__info['con']->db_fetch_assoc($this->__result)) !== false) { + foreach ($r as $k => $v) { + $r[] =& $r[$k]; + } + $res[] = $r; + } + $this->__info['con']->db_result_seek($this->__result,$this->__index); + + return $res; + } +} + +/** +* Query Result Static Record Class +* +* Unlike record class, this one contains all results in an associative array. +* +* @package Clearbricks +* @subpackage DBLayer +*/ +class staticRecord extends record +{ + /** @var array Data array */ + public $__data = array(); + + private $__sortfield; + private $__sortsign; + + /** @ignore */ + public function __construct($result,$info) + { + if (is_array($result)) + { + $this->__info = $info; + $this->__data = $result; + } + else + { + parent::__construct($result,$info); + $this->__data = parent::getData(); + } + + unset($this->__link); + unset($this->__result); + } + + /** + * Static record from array + * + * Returns a new instance of object from an associative array. + * + * @param array $data Data array + * @return staticRecord + */ + public static function newFromArray($data) + { + if (!is_array($data)) { + $data = array(); + } + + $data = array_values($data); + + if (empty($data) || !is_array($data[0])) { + $cols = 0; + } else { + $cols = count($data[0]); + } + + $info = array( + 'con' => null, + 'info' => null, + 'cols' => $cols, + 'rows' => count($data) + ); + + return new self($data,$info); + } + + /** @ignore */ + public function field($n) + { + return $this->__data[$this->__index][$n]; + } + + /** @ignore */ + public function exists($n) + { + return isset($this->__data[$this->__index][$n]); + } + + /** @ignore */ + public function index($row=null) + { + if ($row === null) { + return $this->__index; + } + + if ($row < 0 || $row+1 > $this->__info['rows']) { + return false; + } + + $this->__index = $row; + return true; + } + + /** @ignore */ + public function rows() + { + return $this->__data; + } + + /** + * Changes value of a given field in the current row. + * + * @param string $n Field name + * @param string $v Field value + */ + public function set($n,$v) + { + if ($this->__index === null) { + return false; + } + + $this->__data[$this->__index][$n] = $v; + } + + /** + * Sorts values by a field in a given order. + * + * @param string $field Field name + * @param string $order Sort type (asc or desc) + */ + public function sort($field,$order='asc') + { + if (!isset($this->__data[0][$field])) { + return false; + } + + $this->__sortfield = $field; + $this->__sortsign = strtolower($order) == 'asc' ? 1 : -1; + + usort($this->__data,array($this,'sortCallback')); + + $this->__sortfield = null; + $this->__sortsign = null; + } + + private function sortCallback($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 strcmp($a,$b) * $this->__sortsign; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dbschema/class.dbschema.php b/inc/clearbricks/dbschema/class.dbschema.php new file mode 100644 index 0000000..54ac086 --- /dev/null +++ b/inc/clearbricks/dbschema/class.dbschema.php @@ -0,0 +1,310 @@ +array + */ + function db_get_tables(); + + /** + This method should return an associative array of columns in given table + $table with column names in keys. Each line value is an array + with following values: + + - [type] data type (string) + - [len] data length (integer or null) + - [null] is null? (boolean) + - [default] default value (string) + + @param table string Table name + @return array + */ + function db_get_columns($table); + + /** + This method should return an array of keys in given table + $table. Each line value is an array with following values: + + - [name] index name (string) + - [primary] primary key (boolean) + - [unique] unique key (boolean) + - [cols] columns (array) + + @param table string Table name + @return array + */ + function db_get_keys($table); + + /** + This method should return an array of indexes in given table + $table. Each line value is an array with following values: + + - [name] index name (string) + - [type] index type (string) + - [cols] columns (array) + + @param table string Table name + @return array + */ + function db_get_indexes($table); + + /** + This method should return an array of foreign keys in given table + $table. Each line value is an array with following values: + + - [name] key name (string) + - [c_cols] child columns (array) + - [p_table] parent table (string) + - [p_cols] parent columns (array) + - [update] on update statement (string) + - [delete] on delete statement (string) + + @param table string Table name + @return array + */ + function db_get_references($table); + + function db_create_table($name,$fields); + + function db_create_field($table,$name,$type,$len,$null,$default); + + function db_create_primary($table,$name,$cols); + + function db_create_unique($table,$name,$cols); + + function db_create_index($table,$name,$type,$cols); + + function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + + function db_alter_field($table,$name,$type,$len,$null,$default); + + function db_alter_primary($table,$name,$newname,$cols); + + function db_alter_unique($table,$name,$newname,$cols); + + function db_alter_index($table,$name,$newname,$type,$cols); + + function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + + function db_drop_unique($table,$name); +} + +class dbSchema +{ + protected $con; + + public function __construct($con) + { + $this->con =& $con; + } + + public static function init($con) + { + $driver = $con->driver(); + $driver_class = $driver.'Schema'; + + if (!class_exists($driver_class)) + { + if (file_exists(dirname(__FILE__).'/class.'.$driver.'.dbschema.php')) { + require dirname(__FILE__).'/class.'.$driver.'.dbschema.php'; + } else { + trigger_error('Unable to load DB schema layer for '.$driver,E_USER_ERROR); + exit(1); + } + } + + return new $driver_class($con); + } + + /** + Database data type to universal data type conversion. + + @param type string Type name + @param leng integer Field length (in/out) + @param default string Default field value (in/out) + @return string + */ + public function dbt2udt($type,&$len,&$default) + { + $c = array( + 'bool' => 'boolean', + 'int2' => 'smallint', + 'int' => 'integer', + 'int4' => 'integer', + 'int8' => 'bigint', + 'float4' => 'real', + 'double precision' => 'float', + 'float8' => 'float', + 'decimal' => 'numeric', + 'character varying' => 'varchar', + 'character' => 'char' + ); + + if (isset($c[$type])) { + return $c[$type]; + } + + return $type; + } + + /** + Universal data type to database data tye conversion. + + @param type string Type name + @param leng integer Field length (in/out) + @param default string Default field value (in/out) + @return string + */ + public function udt2dbt($type,&$len,&$default) + { + return $type; + } + + /** + Returns an array of all table names. + + @see i_dbSchema::db_get_tables + @return array + */ + public function getTables() + { + return $this->db_get_tables(); + } + + /** + Returns an array of columns (name and type) of a given table. + + @see i_dbSchema::db_get_columns + @param table string Table name + @return array + */ + public function getColumns($table) + { + return $this->db_get_columns($table); + } + + /** + Returns an array of index of a given table. + + @see i_dbSchema::db_get_keys + @param table string Table name + @return array + */ + public function getKeys($table) + { + return $this->db_get_keys($table); + } + + /** + Returns an array of indexes of a given table. + + @see i_dbSchema::db_get_index + @param table string Table name + @return array + */ + public function getIndexes($table) + { + return $this->db_get_indexes($table); + } + + /** + Returns an array of foreign keys of a given table. + + @see i_dbSchema::db_get_references + @param table string Table name + @return array + */ + public function getReferences($table) + { + return $this->db_get_references($table); + } + + public function createTable($name,$fields) + { + return $this->db_create_table($name,$fields); + } + + public function createField($table,$name,$type,$len,$null,$default) + { + return $this->db_create_field($table,$name,$type,$len,$null,$default); + } + + public function createPrimary($table,$name,$cols) + { + return $this->db_create_primary($table,$name,$cols); + } + + public function createUnique($table,$name,$cols) + { + return $this->db_create_unique($table,$name,$cols); + } + + public function createIndex($table,$name,$type,$cols) + { + return $this->db_create_index($table,$name,$type,$cols); + } + + public function createReference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + return $this->db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function alterField($table,$name,$type,$len,$null,$default) + { + return $this->db_alter_field($table,$name,$type,$len,$null,$default); + } + + public function alterPrimary($table,$name,$newname,$cols) + { + return $this->db_alter_primary($table,$name,$newname,$cols); + } + + public function alterUnique($table,$name,$newname,$cols) + { + return $this->db_alter_unique($table,$name,$newname,$cols); + } + + public function alterIndex($table,$name,$newname,$type,$cols) + { + return $this->db_alter_index($table,$name,$newname,$type,$cols); + } + + public function alterReference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + return $this->db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function dropUnique($table,$name) + { + return $this->db_drop_unique($table,$name); + } + + public function flushStack() + { + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dbschema/class.dbstruct.php b/inc/clearbricks/dbschema/class.dbstruct.php new file mode 100644 index 0000000..20dec7d --- /dev/null +++ b/inc/clearbricks/dbschema/class.dbstruct.php @@ -0,0 +1,639 @@ +con =& $con; + $this->prefix = $prefix; + } + + public function driver() + { + return $this->con->driver(); + } + + public function table($name) + { + $this->tables[$name] = new dbStructTable($name); + return $this->tables[$name]; + } + + public function __get($name) + { + if (!isset($this->tables[$name])) { + return $this->table($name); + } + + return $this->tables[$name]; + } + + public function reverse() + { + $schema = dbSchema::init($this->con); + + # Get tables + $tables = $schema->getTables(); + + foreach ($tables as $t_name) + { + if ($this->prefix && strpos($t_name,$this->prefix) !== 0) { + continue; + } + + $t = $this->table($t_name); + + # Get columns + $cols = $schema->getColumns($t_name); + + foreach ($cols as $c_name => $col) { + $type = $schema->dbt2udt($col['type'],$col['len'],$col['default']); + $t->field($c_name,$type,$col['len'],$col['null'],$col['default'],true); + } + + # Get keys + $keys = $schema->getKeys($t_name); + + foreach ($keys as $k) + { + $args = $k['cols']; + array_unshift($args,$k['name']); + + if ($k['primary']) { + call_user_func_array(array($t,'primary'),$args); + } elseif ($k['unique']) { + call_user_func_array(array($t,'unique'),$args); + } + } + + # Get indexes + $idx = $schema->getIndexes($t_name); + foreach ($idx as $i) + { + $args = array($i['name'],$i['type']); + $args = array_merge($args,$i['cols']); + + call_user_func_array(array($t,'index'),$args); + } + + # Get foreign keys + $ref = $schema->getReferences($t_name); + foreach ($ref as $r) { + $t->reference($r['name'],$r['c_cols'],$r['p_table'],$r['p_cols'],$r['update'],$r['delete']); + } + } + } + + /** + Synchronize this schema taken from database with $schema. + + @param s dbStruct Structure to synchronize with + */ + public function synchronize($s) + { + $this->tables = array(); + $this->reverse(); + + if (!($s instanceof self)) { + throw new Exception('Invalid database schema'); + } + + $tables = $s->getTables(); + + $table_create = array(); + $key_create = array(); + $index_create = array(); + $reference_create = array(); + + $field_create = array(); + $field_update = array(); + $key_update = array(); + $index_update = array(); + $reference_update = array(); + + $got_work = false; + + $schema = dbSchema::init($this->con); + + foreach ($tables as $tname => $t) + { + if (!$this->tableExists($tname)) + { + # Table does not exist, create table + $table_create[$tname] = $t->getFields(); + + # Add keys, indexes and references + $keys = $t->getKeys(); + $indexes = $t->getIndexes(); + $references = $t->getReferences(); + + foreach ($keys as $k => $v) { + $key_create[$tname][$this->prefix.$k] = $v; + } + foreach ($indexes as $k => $v) { + $index_create[$tname][$this->prefix.$k] = $v; + } + foreach ($references as $k => $v) { + $v['p_table'] = $this->prefix.$v['p_table']; + $reference_create[$tname][$this->prefix.$k] = $v; + } + + $got_work = true; + } + else # Table exists + { + # Check new fields to create + $fields = $t->getFields(); + $db_fields = $this->tables[$tname]->getFields(); + foreach ($fields as $fname => $f) + { + if (!$this->tables[$tname]->fieldExists($fname)) + { + # Field doest not exist, create it + $field_create[$tname][$fname] = $f; + $got_work = true; + } + elseif ($this->fieldsDiffer($db_fields[$fname],$f)) + { + # Field exists and differs from db version + $field_update[$tname][$fname] = $f; + $got_work = true; + } + } + + # Check keys to add or upgrade + $keys = $t->getKeys(); + $db_keys = $this->tables[$tname]->getKeys(); + + foreach ($keys as $kname => $k) + { + if ($k['type'] == 'primary' && $this->con->driver() == 'mysql') { + $kname = 'PRIMARY'; + } else { + $kname = $this->prefix.$kname; + } + + $db_kname = $this->tables[$tname]->keyExists($kname,$k['type'],$k['cols']); + if (!$db_kname) + { + # Key does not exist, create it + $key_create[$tname][$kname] = $k; + $got_work = true; + } + elseif ($this->keysDiffer($db_kname,$db_keys[$db_kname]['cols'],$kname,$k['cols'])) + { + # Key exists and differs from db version + $key_update[$tname][$db_kname] = array_merge(array('name'=>$kname),$k); + $got_work = true; + } + } + + # Check index to add or upgrade + $idx = $t->getIndexes(); + $db_idx = $this->tables[$tname]->getIndexes(); + + foreach ($idx as $iname => $i) + { + $iname = $this->prefix.$iname; + $db_iname = $this->tables[$tname]->indexExists($iname,$i['type'],$i['cols']); + + if (!$db_iname) + { + # Index does not exist, create it + $index_create[$tname][$iname] = $i; + $got_work = true; + } + elseif ($this->indexesDiffer($db_iname,$db_idx[$db_iname],$iname,$i)) + { + # Index exists and differs from db version + $index_update[$tname][$db_iname] = array_merge(array('name'=>$iname),$i); + $got_work = true; + } + } + + # Check references to add or upgrade + $ref = $t->getReferences(); + $db_ref = $this->tables[$tname]->getReferences(); + + foreach ($ref as $rname => $r) + { + $rname = $this->prefix.$rname; + $r['p_table'] = $this->prefix.$r['p_table']; + $db_rname = $this->tables[$tname]->referenceExists($rname,$r['c_cols'],$r['p_table'],$r['p_cols']); + + if (!$db_rname) + { + # Reference does not exist, create it + $reference_create[$tname][$rname] = $r; + $got_work = true; + } + elseif ($this->referencesDiffer($db_rname,$db_ref[$db_rname],$rname,$r)) + { + $reference_update[$tname][$db_rname] = array_merge(array('name'=>$rname),$r); + $got_work = true; + } + } + } + } + + if (!$got_work) { + return; + } + + # Create tables + foreach ($table_create as $table => $fields) + { + $schema->createTable($table,$fields); + } + + # Create new fields + foreach ($field_create as $tname => $fields) + { + foreach ($fields as $fname => $f) { + $schema->createField($tname,$fname,$f['type'],$f['len'],$f['null'],$f['default']); + } + } + + # Update fields + foreach ($field_update as $tname => $fields) + { + foreach ($fields as $fname => $f) { + $schema->alterField($tname,$fname,$f['type'],$f['len'],$f['null'],$f['default']); + } + } + + # Create new keys + foreach ($key_create as $tname => $keys) + { + foreach ($keys as $kname => $k) + { + if ($k['type'] == 'primary') { + $schema->createPrimary($tname,$kname,$k['cols']); + } elseif ($k['type'] == 'unique') { + $schema->createUnique($tname,$kname,$k['cols']); + } + } + } + + # Update keys + foreach ($key_update as $tname => $keys) + { + foreach ($keys as $kname => $k) + { + if ($k['type'] == 'primary') { + $schema->alterPrimary($tname,$kname,$k['name'],$k['cols']); + } elseif ($k['type'] == 'unique') { + $schema->alterUnique($tname,$kname,$k['name'],$k['cols']); + } + } + } + + # Create indexes + foreach ($index_create as $tname => $index) + { + foreach ($index as $iname => $i) { + $schema->createIndex($tname,$iname,$i['type'],$i['cols']); + } + } + + # Update indexes + foreach ($index_update as $tname => $index) + { + foreach ($index as $iname => $i) { + $schema->alterIndex($tname,$iname,$i['name'],$i['type'],$i['cols']); + } + } + + # Create references + foreach ($reference_create as $tname => $ref) + { + foreach ($ref as $rname => $r) + { + $schema->createReference($rname,$tname,$r['c_cols'],$r['p_table'],$r['p_cols'],$r['update'],$r['delete']); + } + } + + # Update references + foreach ($reference_update as $tname => $ref) + { + foreach ($ref as $rname => $r) { + $schema->alterReference($rname,$r['name'],$tname,$r['c_cols'],$r['p_table'],$r['p_cols'],$r['update'],$r['delete']); + } + } + + # Flush execution stack + $schema->flushStack(); + + return + count($table_create) + count($key_create) + count($index_create) + + count($reference_create) + count($field_create) + count($field_update) + + count($key_update) + count($index_update) + count($reference_update); + } + + public function getTables() + { + $res = array(); + foreach ($this->tables as $t => $v) + { + $res[$this->prefix.$t] = $v; + } + + return $res; + } + + public function tableExists($name) + { + return isset($this->tables[$name]); + } + + private function fieldsDiffer($db_field,$schema_field) + { + $d_type = $db_field['type']; + $d_len = (integer) $db_field['len']; + $d_default = $db_field['default']; + $d_null = $db_field['null']; + + $s_type = $schema_field['type']; + $s_len = (integer) $schema_field['len']; + $s_default = $schema_field['default']; + $s_null = $schema_field['null']; + + return $d_type != $s_type || $d_len != $s_len || $d_default != $s_default || $d_null != $s_null; + } + + private function keysDiffer($d_name,$d_cols,$s_name,$s_cols) + { + return $d_name != $s_name || $d_cols != $s_cols; + } + + private function indexesDiffer($d_name,$d_i,$s_name,$s_i) + { + return $d_name != $s_name || $d_i['cols'] != $s_i['cols'] || $d_i['type'] != $s_i['type']; + } + + private function referencesDiffer($d_name,$d_r,$s_name,$s_r) + { + return + $d_name != $s_name || $d_r['c_cols'] != $s_r['c_cols'] + || $d_r['p_table'] != $s_r['p_table'] || $d_r['p_cols'] != $s_r['p_cols'] + || $d_r['update'] != $s_r['update'] || $d_r['delete'] != $s_r['delete']; + } +} + +class dbStructTable +{ + protected $name; + protected $has_primary = false; + + protected $fields = array(); + protected $keys = array(); + protected $indexes = array(); + protected $references = array(); + + /** + Universal data types supported by dbSchema + + SMALLINT : signed 2 bytes integer + INTEGER : signed 4 bytes integer + BIGINT : signed 8 bytes integer + REAL : signed 4 bytes floating point number + FLOAT : signed 8 bytes floating point number + NUMERIC : exact numeric type + + DATE : Calendar date (day, month and year) + TIME : Time of day + TIMESTAMP : Date and time + + CHAR : A fixed n-length character string + VARCHAR : A variable length character string + TEXT : A variable length of text + */ + protected $allowed_types = array( + 'smallint','integer','bigint','real','float','numeric', + 'date','time','timestamp', + 'char','varchar','text' + ); + + public function __construct($name) + { + $this->name = $name; + return $this; + } + + public function getFields() + { + return $this->fields; + } + + public function getKeys($primary=null) + { + return $this->keys; + } + + public function getIndexes() + { + return $this->indexes; + } + + public function getReferences() + { + return $this->references; + } + + public function fieldExists($name) + { + return isset($this->fields[$name]); + } + + public function keyExists($name,$type,$cols) + { + # Look for key with the same name + if (isset($this->keys[$name])) { + return $name; + } + + # Look for key with the same columns list and type + foreach ($this->keys as $n => $k) + { + if ($k['cols'] == $cols && $k['type'] == $type) { + # Same columns and type, return new name + return $n; + } + } + + return false; + } + + public function indexExists($name,$type,$cols) + { + # Look for key with the same name + if (isset($this->indexes[$name])) { + return $name; + } + + # Look for index with the same columns list and type + foreach ($this->indexes as $n => $i) + { + if ($i['cols'] == $cols && $i['type'] == $type) { + # Same columns and type, return new name + return $n; + } + } + + return false; + } + + public function referenceExists($name,$c_cols,$p_table,$p_cols) + { + if (isset($this->references[$name])) { + return $name; + } + + # Look for reference with same chil columns, parent table and columns + foreach ($this->references as $n => $r) + { + if ($c_cols == $r['c_cols'] && $p_table == $r['p_table'] && $p_cols == $r['p_cols']) { + # Only name differs, return new name + return $n; + } + } + + return false; + } + + public function field($name,$type,$len,$null=true,$default=false,$to_null=false) + { + $type = strtolower($type); + + if (!in_array($type,$this->allowed_types)) + { + if ($to_null) { + $type = null; + } else { + throw new Exception('Invalid data type '.$type.' in schema'); + } + } + + $this->fields[$name] = array( + 'type' => $type, + 'len' => (integer) $len, + 'default' => $default, + 'null' => (boolean) $null + ); + + return $this; + } + + public function __call($name,$args) + { + array_unshift($args,$name); + return call_user_func_array(array($this,'field'),$args); + } + + public function primary($name,$col) + { + if ($this->has_primary) { + throw new Exception(sprintf('Table %s already has a primary key',$this->name)); + } + + $cols = func_get_args(); + array_shift($cols); + + return $this->newKey('primary',$name,$cols); + } + + public function unique($name,$col) + { + $cols = func_get_args(); + array_shift($cols); + + return $this->newKey('unique',$name,$cols); + } + + public function index($name,$type,$col) + { + $cols = func_get_args(); + array_shift($cols); + array_shift($cols); + + $this->checkCols($cols); + + $this->indexes[$name] = array( + 'type' => strtolower($type), + 'cols' => $cols + ); + + return $this; + } + + public function reference($name,$c_cols,$p_table,$p_cols,$update=false,$delete=false) + { + if (!is_array($p_cols)) { + $p_cols = array($p_cols); + } + if (!is_array($c_cols)) { + $c_cols = array($c_cols); + } + + $this->checkCols($c_cols); + + $this->references[$name] = array( + 'c_cols' => $c_cols, + 'p_table' => $p_table, + 'p_cols' => $p_cols, + 'update' => $update, + 'delete' => $delete + ); + } + + protected function newKey($type,$name,$cols) + { + $this->checkCols($cols); + + $this->keys[$name] = array( + 'type' => $type, + 'cols' => $cols + ); + + if ($type == 'primary') { + $this->has_primary = true; + } + + return $this; + } + + protected function checkCols($cols) + { + foreach ($cols as $v) { + if (!preg_match('/^\(.*?\)$/',$v) && !isset($this->fields[$v])) { + throw new Exception(sprintf('Field %s does not exist in table %s',$v,$this->name)); + } + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dbschema/class.mysql.dbschema.php b/inc/clearbricks/dbschema/class.mysql.dbschema.php new file mode 100644 index 0000000..af1963f --- /dev/null +++ b/inc/clearbricks/dbschema/class.mysql.dbschema.php @@ -0,0 +1,470 @@ +con->select($sql); + + $res = array(); + while ($rs->fetch()) { + $res[] = $rs->f(0); + } + return $res; + } + + public function db_get_columns($table) + { + $sql = 'SHOW COLUMNS FROM '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $field = trim($rs->f('Field')); + $type = trim($rs->f('Type')); + $null = strtolower($rs->f('Null')) == 'yes'; + $default = $rs->f('Default'); + + $len = null; + if (preg_match('/^(.+?)\(([\d,]+)\)$/si',$type,$m)) { + $type = $m[1]; + $len = (integer) $m[2]; + } + + if ($default != '' && !is_numeric($default)) { + $default = "'".$default."'"; + } + + $res[$field] = array( + 'type' => $type, + 'len' => $len, + 'null' => $null, + 'default' => $default + ); + } + return $res; + } + + public function db_get_keys($table) + { + $sql = 'SHOW INDEX FROM '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $t = array(); + $res = array(); + while ($rs->fetch()) + { + $key_name = $rs->f('Key_name'); + $unique = $rs->f('Non_unique') == 0; + $seq = $rs->f('Seq_in_index'); + $col_name = $rs->f('Column_name'); + + if ($key_name == 'PRIMARY' || $unique) { + $t[$key_name]['cols'][$seq] = $col_name; + $t[$key_name]['unique'] = $unique; + } + } + + foreach ($t as $name => $idx) + { + ksort($idx['cols']); + + $res[] = array( + 'name' => $name, + 'primary' => $name == 'PRIMARY', + 'unique' => $idx['unique'], + 'cols' => array_values($idx['cols']) + ); + } + + return $res; + } + + public function db_get_indexes($table) + { + $sql = 'SHOW INDEX FROM '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $t = array(); + $res = array(); + while ($rs->fetch()) + { + $key_name = $rs->f('Key_name'); + $unique = $rs->f('Non_unique') == 0; + $seq = $rs->f('Seq_in_index'); + $col_name = $rs->f('Column_name'); + $type = $rs->f('Index_type'); + + if ($key_name != 'PRIMARY' && !$unique) { + $t[$key_name]['cols'][$seq] = $col_name; + $t[$key_name]['type'] = $type; + } + } + + foreach ($t as $name => $idx) + { + ksort($idx['cols']); + + $res[] = array( + 'name' => $name, + 'type' => $idx['type'], + 'cols' => $idx['cols'] + ); + } + + return $res; + } + + public function db_get_references($table) + { + $sql = 'SHOW CREATE TABLE '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $s = $rs->f(1); + + $res = array(); + + $n = preg_match_all('/^\s*CONSTRAINT\s+`(.+?)`\s+FOREIGN\s+KEY\s+\((.+?)\)\s+REFERENCES\s+`(.+?)`\s+\((.+?)\)(.*?)$/msi',$s,$match); + if ($n > 0) + { + foreach ($match[1] as $i => $name) + { + # Columns transformation + $t_cols = str_replace('`','',$match[2][$i]); + $t_cols = explode(',',$t_cols); + $r_cols = str_replace('`','',$match[4][$i]); + $r_cols = explode(',',$r_cols); + + # ON UPDATE|DELETE + $on = trim($match[5][$i],', '); + $on_delete = null; + $on_update = null; + if ($on != '') { + if (preg_match('/ON DELETE (.+?)(?:\s+ON|$)/msi',$on,$m)) { + $on_delete = strtolower(trim($m[1])); + } + if (preg_match('/ON UPDATE (.+?)(?:\s+ON|$)/msi',$on,$m)) { + $on_update = strtolower(trim($m[1])); + } + } + + $res[] = array ( + 'name' => $name, + 'c_cols' => $t_cols, + 'p_table' => $match[3][$i], + 'p_cols' => $r_cols, + 'update' => $on_update, + 'delete' => $on_delete + ); + } + } + return $res; + } + + public function db_create_table($name,$fields) + { + $a = array(); + + foreach ($fields as $n => $f) + { + $type = $f['type']; + $len = (integer) $f['len']; + $default = $f['default']; + $null = $f['null']; + + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $a[] = + $this->con->escapeSystem($n).' '. + $type.$len.' '.$null.' '.$default; + } + + $sql = + 'CREATE TABLE '.$this->con->escapeSystem($name)." (\n". + implode(",\n",$a). + "\n) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin "; + + $this->con->execute($sql); + } + + public function db_create_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = (integer) $len > 0 ? '('.(integer) $len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD COLUMN '.$this->con->escapeSystem($name).' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_create_primary($table,$name,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD CONSTRAINT PRIMARY KEY ('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_create_unique($table,$name,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD CONSTRAINT UNIQUE KEY '.$this->con->escapeSystem($name).' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_create_index($table,$name,$type,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD INDEX '.$this->con->escapeSystem($name).' USING '.$type.' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $c = array(); + $p = array(); + foreach ($c_cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + foreach ($p_cols as $v) { + $p[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($c_table).' '. + 'ADD CONSTRAINT '.$name.' FOREIGN KEY '. + '('.implode(',',$c).') '. + 'REFERENCES '.$this->con->escapeSystem($p_table).' '. + '('.implode(',',$p).') '; + + if ($update) { + $sql .= 'ON UPDATE '.$update.' '; + } + if ($delete) { + $sql .= 'ON DELETE '.$delete.' '; + } + + $this->con->execute($sql); + } + + public function db_alter_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = (integer) $len > 0 ? '('.(integer) $len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'CHANGE COLUMN '.$this->con->escapeSystem($name).' '.$this->con->escapeSystem($name).' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_alter_primary($table,$name,$newname,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP PRIMARY KEY, ADD PRIMARY KEY '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_alter_unique($table,$name,$newname,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP INDEX '.$this->con->escapeSystem($name).', '. + 'ADD UNIQUE '.$this->con->escapeSystem($newname).' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_alter_index($table,$name,$newname,$type,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP INDEX '.$this->con->escapeSystem($name).', '. + 'ADD INDEX '.$this->con->escapeSystem($newname).' '. + 'USING '.$type.' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($c_table).' '. + 'DROP FOREIGN KEY '.$this->con->escapeSystem($name); + + $this->con->execute($sql); + $this->createReference($newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function db_drop_unique($table,$name) + { + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP INDEX '.$this->con->escapeSystem($name); + $this->con->execute($sql); + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dbschema/class.pgsql.dbschema.php b/inc/clearbricks/dbschema/class.pgsql.dbschema.php new file mode 100644 index 0000000..3745713 --- /dev/null +++ b/inc/clearbricks/dbschema/class.pgsql.dbschema.php @@ -0,0 +1,400 @@ + 'no action', + 'r' => 'restrict', + 'c' => 'cascade', + 'n' => 'set null', + 'd' => 'set default' + ); + + public function dbt2udt($type,&$len,&$default) + { + $type = parent::dbt2udt($type,$len,$default); + + return $type; + } + + public function udt2dbt($type,&$len,&$default) + { + $type = parent::udt2dbt($type,$len,$default); + + return $type; + } + + public function db_get_tables() + { + $sql = + 'SELECT table_name '. + 'FROM information_schema.tables '. + "WHERE table_schema = 'public' "; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) { + $res[] = $rs->f(0); + } + return $res; + } + + public function db_get_columns($table) + { + $sql = + 'SELECT column_name, udt_name, character_maximum_length, '. + 'is_nullable, column_default '. + 'FROM information_schema.columns '. + "WHERE table_name = '".$this->con->escape($table)."' "; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $field = trim($rs->column_name); + $type = trim($rs->udt_name); + $null = strtolower($rs->is_nullable) == 'yes'; + $default = $rs->column_default; + $len = $rs->character_maximum_length; + + if ($len == '') { + $len = null; + } + + $default = preg_replace('/::([\w\d\s]*)$/','',$default); + + $res[$field] = array( + 'type' => $type, + 'len' => $len, + 'null' => $null, + 'default' => $default + ); + } + + return $res; + } + + public function db_get_keys($table) + { + $sql = + 'SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as idxname, indisunique::integer, indisprimary::integer, '. + 'indnatts, tab.relname as tabname, contype, amname '. + 'FROM pg_index idx '. + 'JOIN pg_class cls ON cls.oid=indexrelid '. + 'JOIN pg_class tab ON tab.oid=indrelid '. + 'LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace '. + 'JOIN pg_namespace n ON n.oid=tab.relnamespace '. + 'JOIN pg_am am ON am.oid=cls.relam '. + "LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0') ". + 'LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) '. + 'LEFT OUTER JOIN pg_description des ON des.objoid=con.oid '. + 'LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) '. + "WHERE tab.relname = '".$this->con->escape($table)."' ". + "AND contype IN ('p','u') ". + 'ORDER BY cls.relname '; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $k = array( + 'name' => $rs->idxname, + 'primary' => (boolean) $rs->indisprimary, + 'unique' => (boolean) $rs->indisunique, + 'cols' => array() + ); + + for ($i=1; $i<=$rs->indnatts; $i++) { + $cols = $this->con->select('SELECT pg_get_indexdef('.$rs->oid.'::oid, '.$i.', true);'); + $k['cols'][] = $cols->f(0); + } + + $res[] = $k; + } + + return $res; + } + + public function db_get_indexes($table) + { + $sql = + 'SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as idxname, n.nspname, '. + 'indnatts, tab.relname as tabname, contype, amname '. + 'FROM pg_index idx '. + 'JOIN pg_class cls ON cls.oid=indexrelid '. + 'JOIN pg_class tab ON tab.oid=indrelid '. + 'LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace '. + 'JOIN pg_namespace n ON n.oid=tab.relnamespace '. + 'JOIN pg_am am ON am.oid=cls.relam '. + "LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0') ". + 'LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) '. + 'LEFT OUTER JOIN pg_description des ON des.objoid=con.oid '. + 'LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) '. + "WHERE tab.relname = '".$this->con->escape($table)."' ". + 'AND conname IS NULL '. + 'ORDER BY cls.relname '; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $k = array( + 'name' => $rs->idxname, + 'type' => $rs->amname, + 'cols' => array() + ); + + for ($i=1; $i<=$rs->indnatts; $i++) { + $cols = $this->con->select('SELECT pg_get_indexdef('.$rs->oid.'::oid, '.$i.', true);'); + $k['cols'][] = $cols->f(0); + } + + $res[] = $k; + } + + return $res; + } + + public function db_get_references($table) + { + $sql = + 'SELECT ct.oid, conname, condeferrable, condeferred, confupdtype, '. + 'confdeltype, confmatchtype, conkey, confkey, conrelid, confrelid, cl.relname as fktab, '. + 'cr.relname as reftab '. + 'FROM pg_constraint ct '. + 'JOIN pg_class cl ON cl.oid=conrelid '. + 'JOIN pg_namespace nl ON nl.oid=cl.relnamespace '. + 'JOIN pg_class cr ON cr.oid=confrelid '. + 'JOIN pg_namespace nr ON nr.oid=cr.relnamespace '. + "WHERE contype='f' ". + "AND cl.relname = '".$this->con->escape($table)."' ". + 'ORDER BY conname '; + + $rs = $this->con->select($sql); + + $cols_sql = + 'SELECT a1.attname as conattname, a2.attname as confattname '. + 'FROM pg_attribute a1, pg_attribute a2 '. + 'WHERE a1.attrelid=%1$s::oid AND a1.attnum=%2$s '. + 'AND a2.attrelid=%3$s::oid AND a2.attnum=%4$s '; + + $res = array(); + while ($rs->fetch()) + { + $conkey = preg_replace('/[^\d]/','',$rs->conkey); + $confkey = preg_replace('/[^\d]/','',$rs->confkey); + + $k = array( + 'name' => $rs->conname, + 'c_cols' => array(), + 'p_table' => $rs->reftab, + 'p_cols' => array(), + 'update' => $this->ref_actions_map[$rs->confupdtype], + 'delete' => $this->ref_actions_map[$rs->confdeltype] + ); + + $cols = $this->con->select(sprintf($cols_sql,$rs->conrelid,$conkey,$rs->confrelid,$confkey)); + while ($cols->fetch()) { + $k['c_cols'][] = $cols->conattname; + $k['p_cols'][] = $cols->confattname; + } + + $res[] = $k; + } + + return $res; + } + + public function db_create_table($name,$fields) + { + $a = array(); + + foreach ($fields as $n => $f) + { + $type = $f['type']; + $len = (integer) $f['len']; + $default = $f['default']; + $null = $f['null']; + + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $a[] = + $n.' '. + $type.$len.' '.$null.' '.$default; + } + + $sql = + 'CREATE TABLE '.$name." (\n". + implode(",\n",$a). + "\n)"; + + $this->con->execute($sql); + } + + public function db_create_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$table.' ADD COLUMN '.$name.' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_create_primary($table,$name,$cols) + { + $sql = + 'ALTER TABLE '.$table.' '. + 'ADD CONSTRAINT '.$name.' PRIMARY KEY ('.implode(",",$cols).') '; + + $this->con->execute($sql); + } + + public function db_create_unique($table,$name,$cols) + { + $sql = + 'ALTER TABLE '.$table.' '. + 'ADD CONSTRAINT '.$name.' UNIQUE ('.implode(',',$cols).') '; + + $this->con->execute($sql); + } + + public function db_create_index($table,$name,$type,$cols) + { + $sql = + 'CREATE INDEX '.$name.' ON '.$table.' USING '.$type. + '('.implode(',',$cols).') '; + + $this->con->execute($sql); + } + + public function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $sql = + 'ALTER TABLE '.$c_table.' '. + 'ADD CONSTRAINT '.$name.' FOREIGN KEY '. + '('.implode(',',$c_cols).') '. + 'REFERENCES '.$p_table.' '. + '('.implode(',',$p_cols).') '; + + if ($update) { + $sql .= 'ON UPDATE '.$update.' '; + } + if ($delete) { + $sql .= 'ON DELETE '.$delete.' '; + } + + $this->con->execute($sql); + } + + public function db_alter_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = (integer) $len > 0 ? '('.(integer) $len.')' : ''; + + $sql = 'ALTER TABLE '.$table.' ALTER COLUMN '.$name.' TYPE '.$type.$len; + $this->con->execute($sql); + + if ($default === null) { + $default = 'SET DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'SET DEFAULT '.$default; + } else { + $default = 'DROP DEFAULT'; + } + + $sql = 'ALTER TABLE '.$table.' ALTER COLUMN '.$name.' '.$default; + $this->con->execute($sql); + + $null = $null ? 'DROP NOT NULL' : 'SET NOT NULL'; + $sql = 'ALTER TABLE '.$table.' ALTER COLUMN '.$name.' '.$null; + $this->con->execute($sql); + } + + public function db_alter_primary($table,$name,$newname,$cols) + { + $sql = 'ALTER TABLE '.$table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + + $this->createPrimary($table,$newname,$cols); + } + + public function db_alter_unique($table,$name,$newname,$cols) + { + $sql = 'ALTER TABLE '.$table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + + $this->createUnique($table,$newname,$cols); + } + + public function db_alter_index($table,$name,$newname,$type,$cols) + { + $sql = 'DROP INDEX '.$name; + $this->con->execute($sql); + + $this->createIndex($table,$newname,$type,$cols); + } + + public function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $sql = 'ALTER TABLE '.$c_table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + + $this->createReference($newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function db_drop_unique($table,$name) + { + $sql = 'ALTER TABLE '.$table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/dbschema/class.sqlite.dbschema.php b/inc/clearbricks/dbschema/class.sqlite.dbschema.php new file mode 100644 index 0000000..aa0b336 --- /dev/null +++ b/inc/clearbricks/dbschema/class.sqlite.dbschema.php @@ -0,0 +1,515 @@ +table_stack as $table => $def) + { + $sql = 'CREATE TABLE '.$table." (\n".implode(",\n",$def)."\n)\n "; + $this->con->execute($sql); + } + + foreach ($this->x_stack as $x) + { + $this->con->execute($x); + } + + } + + public function db_get_tables() + { + $res = array(); + $sql = "SELECT * FROM sqlite_master WHERE type = 'table'"; + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) { + $res[] = $rs->tbl_name; + } + + return $res; + } + + public function db_get_columns($table) + { + $sql = 'PRAGMA table_info('.$this->con->escapeSystem($table).')'; + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $field = trim($rs->name); + $type = trim($rs->type); + $null = trim($rs->notnull) == 0; + $default = trim($rs->dflt_value); + + $len = null; + if (preg_match('/^(.+?)\(([\d,]+)\)$/si',$type,$m)) { + $type = $m[1]; + $len = (integer) $m[2]; + } + + $res[$field] = array( + 'type' => $type, + 'len' => $len, + 'null' => $null, + 'default' => $default + ); + } + return $res; + } + + public function db_get_keys($table) + { + $t = array(); + $res = array(); + + # Get primary keys first + $sql = "SELECT sql FROM sqlite_master WHERE type='table' AND name='".$this->con->escape($table)."'"; + $rs = $this->con->select($sql); + + if ($rs->isEmpty()) { + return array(); + } + + # Get primary keys + $n = preg_match_all('/^\s*CONSTRAINT\s+([^,]+?)\s+PRIMARY\s+KEY\s+\((.+?)\)/msi',$rs->sql,$match); + if ($n > 0) + { + foreach ($match[1] as $i => $name) + { + $cols = preg_split('/\s*,\s*/',$match[2][$i]); + $res[] = array( + 'name' => $name, + 'primary' => true, + 'unique' => false, + 'cols' => $cols + ); + } + } + + # Get unique keys + $n = preg_match_all('/^\s*CONSTRAINT\s+([^,]+?)\s+UNIQUE\s+\((.+?)\)/msi',$rs->sql,$match); + if ($n > 0) + { + foreach ($match[1] as $i => $name) + { + $cols = preg_split('/\s*,\s*/',$match[2][$i]); + $res[] = array( + 'name' => $name, + 'primary' => false, + 'unique' => true, + 'cols' => $cols + ); + } + } + + return $res; + } + + public function db_get_indexes($table) + { + $sql = 'PRAGMA index_list('.$this->con->escapeSystem($table).')'; + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + if (preg_match('/^sqlite_/',$rs->name)) { + continue; + } + + $idx = $this->con->select('PRAGMA index_info('.$this->con->escapeSystem($rs->name).')'); + $cols = array(); + while ($idx->fetch()) { + $cols[] = $idx->name; + } + + $res[] = array( + 'name' => $rs->name, + 'type' => 'btree', + 'cols' => $cols + ); + } + + return $res; + } + + public function db_get_references($table) + { + $sql = 'SELECT * FROM sqlite_master WHERE type=\'trigger\' AND tbl_name = \'%1$s\' AND name LIKE \'%2$s_%%\' '; + $res = array(); + + # Find constraints on table + $bir = $this->con->select(sprintf($sql,$this->con->escape($table),'bir')); + $bur = $this->con->select(sprintf($sql,$this->con->escape($table),'bur')); + + if ($bir->isEmpty() || $bur->isempty()) { + return $res; + } + + while ($bir->fetch()) + { + # Find child column and parent table and column + if (!preg_match('/FROM\s+(.+?)\s+WHERE\s+(.+?)\s+=\s+NEW\.(.+?)\s*?\) IS\s+NULL/msi',$bir->sql,$m)) { + continue; + } + + $c_col = $m[3]; + $p_table = $m[1]; + $p_col = $m[2]; + + # Find on update + $on_update = 'restrict'; + $aur = $this->con->select(sprintf($sql,$this->con->escape($p_table),'aur')); + while ($aur->fetch()) + { + if (!preg_match('/AFTER\s+UPDATE/msi',$aur->sql)) { + continue; + } + + if (preg_match('/UPDATE\s+'.$table.'\s+SET\s+'.$c_col.'\s*=\s*NEW.'.$p_col. + '\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$aur->sql)) { + $on_update = 'cascade'; + break; + } + + if (preg_match('/UPDATE\s+'.$table.'\s+SET\s+'.$c_col.'\s*=\s*NULL'. + '\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$aur->sql)) { + $on_update = 'set null'; + break; + } + } + + # Find on delete + $on_delete = 'restrict'; + $bdr = $this->con->select(sprintf($sql,$this->con->escape($p_table),'bdr')); + while ($bdr->fetch()) + { + if (!preg_match('/BEFORE\s+DELETE/msi',$bdr->sql)) { + continue; + } + + if (preg_match('/DELETE\s+FROM\s+'.$table.'\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$bdr->sql)) { + $on_delete = 'cascade'; + break; + } + + if (preg_match('/UPDATE\s+'.$table.'\s+SET\s+'.$c_col.'\s*=\s*NULL'. + '\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$bdr->sql)) { + $on_update = 'set null'; + break; + } + } + + $res[] = array( + 'name' => substr($bir->name,4), + 'c_cols' => array($c_col), + 'p_table' => $p_table, + 'p_cols' => array($p_col), + 'update' => $on_update, + 'delete' => $on_delete + ); + } + + return $res; + } + + public function db_create_table($name,$fields) + { + $a = array(); + + foreach ($fields as $n => $f) + { + $type = $f['type']; + $len = (integer) $f['len']; + $default = $f['default']; + $null = $f['null']; + + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $a[] = $n.' '.$type.$len.' '.$null.' '.$default; + } + + $this->table_stack[$name][] = implode(",\n",$a); + $this->table_hist[$name] = $fields; + } + + public function db_create_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD COLUMN '.$this->con->escapeSystem($name).' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_create_primary($table,$name,$cols) + { + $this->table_stack[$table][] = 'CONSTRAINT '.$name.' PRIMARY KEY ('.implode(',',$cols).') '; + } + + public function db_create_unique($table,$name,$cols) + { + $this->table_stack[$table][] = 'CONSTRAINT '.$name.' UNIQUE ('.implode(',',$cols).') '; + } + + public function db_create_index($table,$name,$type,$cols) + { + $this->x_stack[] = 'CREATE INDEX '.$name.' ON '.$table.' ('.implode(',',$cols).') '; + } + + public function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + if (!isset($this->table_hist[$c_table])) { + return; + } + + if (count($c_cols) > 1 || count($p_cols) > 1) { + throw new Exception('SQLite UDBS does not support multiple columns foreign keys'); + } + + $c_col = $c_cols[0]; + $p_col = $p_cols[0]; + + $update = strtolower($update); + $delete = strtolower($delete); + + $cnull = $this->table_hist[$c_table][$c_col]['null']; + + # Create constraint + $this->x_stack[] = + 'CREATE TRIGGER bir_'.$name."\n". + 'BEFORE INSERT ON '.$c_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE(ROLLBACK,\'insert on table "'.$c_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE '. + ($cnull ? 'NEW.'.$c_col." IS NOT NULL\n AND " : ''). + '(SELECT '.$p_col.' FROM '.$p_table.' WHERE '.$p_col.' = NEW.'.$c_col.") IS NULL;\n". + "END;\n"; + + # Update constraint + $this->x_stack[] = + 'CREATE TRIGGER bur_'.$name."\n". + 'BEFORE UPDATE ON '.$c_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE(ROLLBACK,\'update on table "'.$c_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE '. + ($cnull ? 'NEW.'.$c_col." IS NOT NULL\n AND " : ''). + '(SELECT '.$p_col.' FROM '.$p_table.' WHERE '.$p_col.' = NEW.'.$c_col.") IS NULL;\n". + "END;\n"; + + # ON UPDATE + if ($update == 'cascade') + { + $this->x_stack[] = + 'CREATE TRIGGER aur_'.$name."\n". + 'AFTER UPDATE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' UPDATE '.$c_table.' SET '.$c_col.' = NEW.'.$p_col.' WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + elseif ($update == 'set null') + { + $this->x_stack[] = + 'CREATE TRIGGER aur_'.$name."\n". + 'AFTER UPDATE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' UPDATE '.$c_table.' SET '.$c_col.' = NULL WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + else # default on restrict + { + $this->x_stack[] = + 'CREATE TRIGGER burp_'.$name."\n". + 'BEFORE UPDATE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE (ROLLBACK,\'update on table "'.$p_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE (SELECT '.$c_col.' FROM '.$c_table.' WHERE '.$c_col.' = OLD.'.$p_col.") IS NOT NULL;\n". + "END;\n"; + } + + # ON DELETE + if ($delete == 'cascade') + { + $this->x_stack[] = + 'CREATE TRIGGER bdr_'.$name."\n". + 'BEFORE DELETE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' DELETE FROM '.$c_table.' WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + elseif ($delete == 'set null') + { + $this->x_stack[] = + 'CREATE TRIGGER bdr_'.$name."\n". + 'BEFORE DELETE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' UPDATE '.$c_table.' SET '.$c_col.' = NULL WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + else + { + $this->x_stack[] = + 'CREATE TRIGGER bdr_'.$name."\n". + 'BEFORE DELETE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE (ROLLBACK,\'delete on table "'.$p_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE (SELECT '.$c_col.' FROM '.$c_table.' WHERE '.$c_col.' = OLD.'.$p_col.") IS NOT NULL;\n". + "END;\n"; + } + } + + public function db_alter_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + if ($type != 'integer' && $type != 'text' && $type != 'timestamp') { + throw new Exception('SQLite fields cannot be changed.'); + } + } + + public function db_alter_primary($table,$name,$newname,$cols) + { + throw new Exception('SQLite primary key cannot be changed.'); + } + + public function db_alter_unique($table,$name,$newname,$cols) + { + throw new Exception('SQLite unique index cannot be changed.'); + } + + public function db_alter_index($table,$name,$newname,$type,$cols) + { + $this->con->execute('DROP INDEX IF EXISTS '.$name); + $this->con->execute('CREATE INDEX '.$newname.' ON '.$table.' ('.implode(',',$cols).') '); + } + + public function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $this->con->execute('DROP TRIGGER IF EXISTS bur_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS burp_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS bir_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS aur_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS bdr_'.$name); + + $this->table_hist[$c_table] = $this->db_get_columns($c_table); + $this->db_create_reference($newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function db_drop_unique($table,$name) + { + throw new Exception('SQLite unique index cannot be removed.'); + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/filemanager/class.filemanager.php b/inc/clearbricks/filemanager/class.filemanager.php new file mode 100644 index 0000000..3096ee8 --- /dev/null +++ b/inc/clearbricks/filemanager/class.filemanager.php @@ -0,0 +1,614 @@ +array(),'files'=>array()); + + /** + * Constructor + * + * New filemanage istance. Note that filemanage is a jail in given root + * path. You won't be able to access files outside {@link $root} path with + * the object's methods. + * + * @param string $root Root path + * @param string $root_url Root URL + */ + public function __construct($root,$root_url='') + { + $this->root = $this->pwd = path::real($root); + $this->root_url = $root_url; + + if (!preg_match('#/$#',$this->root_url)) { + $this->root_url = $this->root_url.'/'; + } + + if (!$this->root) { + throw new Exception('Invalid root directory.'); + } + } + + /** + * Change directory + * + * Changes working directory. $dir is relative to instance {@link $root} + * directory. + * + * @param string $dir Directory + */ + public function chdir($dir) + { + $realdir = path::real($this->root.'/'.path::clean($dir)); + if (!$realdir || !is_dir($realdir)) { + throw new Exception('Invalid directory.'); + } + + if ($this->isExclude($realdir)) { + throw new Exception('Directory is excluded.'); + } + + $this->pwd = $realdir; + } + + /** + * Get working directory + * + * Returns working directory path. + * + * @return string + */ + public function getPwd() + { + return $this->pwd; + } + + /** + * Current directory is writable + * + * @return boolean true if working directory is writable + */ + public function writable() + { + if (!$this->pwd) { + return false; + } + + return is_writable($this->pwd); + } + + /** + * Add exclusion + * + * Appends an exclusion to exclusions list. $f should be a regexp. + * + * @see $exclude_list + * @param string $f Exclusion regexp + */ + public function addExclusion($f) + { + if (is_array($f)) + { + foreach ($f as $v) { + if (($V = path::real($v)) !== false) { + $this->exclude_list[] = $V; + } + } + } + elseif (($F = path::real($f)) !== false) + { + $this->exclude_list[] = $F; + } + } + + /** + * Path is excluded + * + * Returns true if path (file or directory) $f is excluded. $f path is + * relative to {@link $root} path. + * + * @see $exclude_list + * @param string $f Path to match + * @return boolean + */ + protected function isExclude($f) + { + foreach ($this->exclude_list as $v) + { + if (strpos($f,$v) === 0) { + return true; + } + } + + return false; + } + + /** + * File is excluded + * + * Returns true if file $f is excluded. $f path is relative to {@link $root} + * path. + * + * @see $exclude_pattern + * @param string $f File to match + * @return boolean + */ + protected function isFileExclude($f) + { + if (!$this->exclude_pattern) { + return false; + } + + return preg_match($this->exclude_pattern,$f); + } + + /** + * Item in jail + * + * Returns true if file or directory $f is in jail (ie. not outside the + * {@link $root} directory). + * + * @param string $f Path to match + * @return boolean + */ + protected function inJail($f) + { + $f = path::real($f); + + if ($f !== false) { + return preg_match('|^'.preg_quote($this->root,'|').'|',$f); + } + + return false; + } + + /** + * File in files + * + * Returns true if file $f is in files array of {@link $dir}. + * + * @param string $f File to match + * @return boolean + */ + public function inFiles($f) + { + foreach ($this->dir['files'] as $v) { + if ($v->relname == $f) { + return true; + } + } + return false; + } + + /** + * Directory list + * + * Creates list of items in working directory and append it to {@link $dir} + * + * @uses sortHandler() + * @uses fileItem + */ + public function getDir() + { + $dir = path::clean($this->pwd); + + $dh = @opendir($dir); + + if ($dh === false) { + throw new Exception('Unable to read directory.'); + } + + $d_res = $f_res = array(); + + while (($file = readdir($dh)) !== false) + { + $fname = $dir.'/'.$file; + + if ($this->inJail($fname) && !$this->isExclude($fname)) + { + if (is_dir($fname) && $file != '.') { + $tmp = new fileItem($fname,$this->root,$this->root_url); + if ($file == '..') { + $tmp->parent = true; + } + $d_res[] = $tmp; + } + + if (is_file($fname) && strpos($file,'.') !== 0 && !$this->isFileExclude($file)) { + $f_res[] = new fileItem($fname,$this->root,$this->root_url); + } + } + } + closedir($dh); + + $this->dir = array('dirs'=>$d_res,'files'=>$f_res); + usort($this->dir['dirs'],array($this,'sortHandler')); + usort($this->dir['files'],array($this,'sortHandler')); + } + + /** + * Root directories + * + * Returns an array of directory under {@link $root} directory. + * + * @uses fileItem + * @return array + */ + public function getRootDirs() + { + $d = files::getDirList($this->root); + + $dir = array(); + + foreach ($d['dirs'] as $v) { + $dir[] = new fileItem($v,$this->root,$this->root_url); + } + + return $dir; + } + + /** + * Upload file + * + * Move $tmp file to its final destination $dest and + * returns the destination file path. + * $dest should be in jail. This method will throw exception + * if the file cannot be written. + * + * You should first verify upload status, with {@link files::uploadStatus()} + * or PHP native functions. + * + * @see files::uploadStatus() + * @param string $tmp Temporary uploaded file path + * @param string $dest Destination file + * @param string $overwrite overwrite mode + * @return string Destination real path + */ + public function uploadFile($tmp,$dest,$overwrite=false) + { + $dest = $this->pwd.'/'.path::clean($dest); + + if ($this->isFileExclude($dest)) { + throw new Exception(__('Uploading this file is not allowed.')); + } + + if (!$this->inJail(dirname($dest))) { + throw new Exception(__('Destination directory is not in jail.')); + } + + if (!$overwrite && file_exists($dest)) { + throw new Exception(__('File already exists.')); + } + + if (!is_writable(dirname($dest))) { + throw new Exception(__('Cannot write in this directory.')); + } + + if (@move_uploaded_file($tmp,$dest) === false) { + throw new Exception(__('An error occurred while writing the file.')); + } + + files::inheritChmod($dest); + return path::real($dest); + } + + /** + * Upload file by bits + * + * Creates a new file $dest with contents of $bits and + * return the destination file path. + * $dest should be in jail. This method will throw exception + * if file cannot be written. + * + * @param string $bits Destination file content + * @param string $dest Destination file + * @return string Destination real path + */ + public function uploadBits($name,$bits) + { + $dest = $this->pwd.'/'.path::clean($name); + + if ($this->isFileExclude($dest)) { + throw new Exception(__('Uploading this file is not allowed.')); + } + + if (!$this->inJail(dirname($dest))) { + throw new Exception(__('Destination directory is not in jail.')); + } + + if (!is_writable(dirname($dest))) { + throw new Exception(__('Cannot write in this directory.')); + } + + $fp = @fopen($dest,'wb'); + if ($fp === false) { + throw new Exception(__('An error occurred while writing the file.')); + } + + fwrite($fp,$bits); + fclose($fp); + files::inheritChmod($dest); + + return path::real($dest); + } + + /** + * New directory + * + * Creates a new directory $d relative to working directory. + * + * @param string $d Directory name + */ + public function makeDir($d) + { + files::makeDir($this->pwd.'/'.path::clean($d)); + } + + /** + * Move file + * + * Moves a file $s to a new destination $d. Both + * $s and $d are relative to {@link $root}. + * + * @param string $s Source file + * @param string $d Destination file + */ + public function moveFile($s,$d) + { + $s = $this->root.'/'.path::clean($s); + $d = $this->root.'/'.path::clean($d); + + if (($s = path::real($s)) === false) { + throw new Exception(__('Source file does not exist.')); + } + + $dest_dir = path::real(dirname($d)); + + if (!$this->inJail($s)) { + throw new Exception(__('File is not in jail.')); + } + if (!$this->inJail($dest_dir)) { + throw new Exception(__('File is not in jail.')); + } + + if (!is_writable($dest_dir)) { + throw new Exception(__('Destination directory is not writable.')); + } + + if (@rename($s,$d) === false) { + throw new Exception(__('Unable to rename file.')); + } + } + + /** + * Remove item + * + * Removes a file or directory $f which is relative to working + * directory. + * + * @param string $f Path to remove + */ + public function removeItem($f) + { + $file = path::real($this->pwd.'/'.path::clean($f)); + + if (is_file($file)) { + $this->removeFile($f); + } elseif (is_dir($file)) { + $this->removeDir($f); + } + } + + /** + * Remove item + * + * Removes a file $f which is relative to working directory. + * + * @param string $f File to remove + */ + public function removeFile($f) + { + $f = path::real($this->pwd.'/'.path::clean($f)); + + if (!$this->inJail($f)) { + throw new Exception(__('File is not in jail.')); + } + + if (!files::isDeletable($f)) { + throw new Exception(__('File cannot be removed.')); + } + + if (@unlink($f) === false) { + throw new Exception(__('File cannot be removed.')); + } + } + + /** + * Remove item + * + * Removes a directory $d which is relative to working directory. + * + * @param string $d Directory to remove + */ + public function removeDir($d) + { + $d = path::real($this->pwd.'/'.path::clean($d)); + + if (!$this->inJail($d)) { + throw new Exception(__('Directory is not in jail.')); + } + + if (!files::isDeletable($d)) { + throw new Exception(__('Directory cannot be removed.')); + } + + if (@rmdir($d) === false) { + throw new Exception(__('Directory cannot be removed.')); + } + } + + /** + * SortHandler + * + * This method is called by {@link getDir()} to sort files. Can be overrided + * in inherited classes. + * + * @param fileItem $a fileItem object + * @param fileItem $b fileItem object + * @return integer + */ + protected function sortHandler($a,$b) + { + if ($a->parent && !$b->parent || !$a->parent && $b->parent) { + return ($a->parent) ? -1 : 1; + } + return strcasecmp($a->basename,$b->basename); + } +} + +/** +* File item +* +* File item class used by {@link filemanager}. In this class {@link $file} could +* be either a file or a directory. +* +* @package Clearbricks +* @subpackage Filemanager +*/ +class fileItem +{ + /** @var string Complete path to file */ + public $file; + + /** @var string File basename */ + public $basename; + + /** @var string File directory name */ + public $dir; + + /** @var string File URL */ + public $file_url; + + /** @var string File directory URL */ + public $dir_url; + + /** @var string File extension */ + public $extension; + + /** @var string File path relative to $root given in constructor */ + public $relname; + + /** @var boolean Parent directory (ie. "..") */ + public $parent = false; + + + /** @var string File MimeType. See {@link files::getMimeType()}. */ + public $type; + + /** @var integer File modification timestamp */ + public $mtime; + + /** @var integer File size */ + public $size; + + /** @var integer File permissions mode */ + public $mode; + + /** @var integer File owner ID */ + public $uid; + + /** @var integer File group ID */ + public $gid; + + /** @var boolean True if file or directory is writable */ + public $w; + + /** @var boolean True if file is a directory */ + public $d; + + /** @var boolean True if file file is executable or directory is traversable */ + public $x; + + /** @var boolean True if file is a file */ + public $f; + + /** @var boolean True if file or directory is deletable */ + public $del; + + /** + * Constructor + * + * Creates an instance of fileItem object. + * + * @param string $file Absolute file or directory path + * @param string $root File root path + * @param string $root_url File root URL + */ + public function __construct($file,$root,$root_url='') + { + $file = path::real($file); + $stat = stat($file); + $path = path::info($file); + + $rel = preg_replace('/^'.preg_quote($root,'/').'\/?/','',$file); + + $this->file = $file; + $this->basename = $path['basename']; + $this->dir = $path['dirname']; + $this->relname = $rel; + + $this->file_url = str_replace('%2F','/',rawurlencode($rel)); + $this->file_url = $root_url.$this->file_url; + + $this->dir_url = dirname($this->file_url); + $this->extension = $path['extension']; + + $this->mtime = $stat[9]; + $this->size = $stat[7]; + $this->mode = $stat[2]; + $this->uid = $stat[4]; + $this->gid = $stat[5]; + $this->w = is_writable($file); + $this->d = is_dir($file); + $this->f = is_file($file); + $this->x = file_exists($file.'/.'); + $this->del = files::isDeletable($file); + + $this->type = $this->d ? null : files::getMimeType($file); + $this->type_prefix = preg_replace('/^(.+?)\/.+$/','$1',$this->type); + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/html.filter/class.html.filter.php b/inc/clearbricks/html.filter/class.html.filter.php new file mode 100644 index 0000000..ba22c4c --- /dev/null +++ b/inc/clearbricks/html.filter/class.html.filter.php @@ -0,0 +1,676 @@ +parser = xml_parser_create('UTF-8'); + xml_set_object($this->parser,$this); + xml_set_element_handler($this->parser, 'tag_open', 'tag_close'); + xml_set_character_data_handler($this->parser, 'cdata'); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); + + $this->removeTags( + 'applet','base','basefont','body','center','dir','font', + 'frame','frameset','head','html','isindex', + 'link','menu','meta','noframes','script','style' + ); + + $this->removeAttributes( + 'onclick','ondblclick','onfocus','onkeydown','onkeypress', + 'onkeyup','onload','onmousedown','onmousemove','onmouseout', + 'onmouseover','onmouseup','onreset','onselect','onsubmit', + 'onunload' + ); + } + + /** + * Append tags + * + * Appends tags to remove. Each method argument is a tag. Example: + * + * + * removeTags('frame','script'); + * ?> + * + */ + public function removeTags() + { + foreach ($this->argsArray(func_get_args()) as $tag) { + $this->removed_tags[] = $tag; + } + } + + /** + * Append attributes + * + * Appends attributes to remove. Each method argument is an attribute. Example: + * + * + * removeAttributes('onclick','onunload'); + * ?> + * + */ + public function removeAttributes() + { + foreach ($this->argsArray(func_get_args()) as $a) { + $this->removed_attrs[] = $a; + } + } + + /** + * Append attributes for tags + * + * Appends attributes to remove from specific tags. Each method argument is + * an array of tags with attributes. Example: + * + * + * removeTagAttributes(array('a' => array('src','title'))); + * ?> + * + */ + public function removeTagAttributes($tag) + { + $args = $this->argsArray(func_get_args()); + array_shift($args); + + foreach ($args as $a) { + $this->removed_tag_attrs[$tag][] = $a; + } + } + + /** + * Known tags + * + * Creates a list of known tags. + * + * @param array $t Tags array + */ + public function setTags($t) + { + if (is_array($t)) { + $this->tags = $t; + } + } + + /** + * Apply filter + * + * This method applies filter on given $str string. It will first + * try to use tidy extension if exists and then apply the filter. + * + * @param string $str String to filter + * @return string Filtered string + */ + public function apply($str) + { + if (extension_loaded('tidy') && class_exists('tidy')) + { + $config = array( + 'doctype' => 'strict', + 'drop-proprietary-attributes' => true, + 'drop-font-tags' => true, + 'escape-cdata' => true, + 'indent' => false, + 'join-classes' => false, + 'join-styles' => true, + 'lower-literals' => true, + 'output-xhtml' => true, + 'show-body-only' => true, + 'wrap' => 80 + ); + + $str = '

    tt

    '.$str; // Fixes a big issue + + $tidy = new tidy; + $tidy->parseString($str, $config, 'utf8'); + $tidy->cleanRepair(); + + $str = (string) $tidy; + + $str = preg_replace('#^

    tt

    \s?#','',$str); + } + else + { + $str = $this->miniTidy($str); + } + + # Removing open comments, open CDATA and processing instructions + $str = preg_replace('%%msu','',$str); + $str = str_replace(' + $fc = preg_replace('/(^\s*)?/ms','',$fc); + + # Compile blocks + foreach ($this->blocks as $b => $f) { + $pattern = sprintf($this->tag_block,preg_quote($b,'#')); + + $fc = preg_replace_callback('#'.$pattern.'#ms', + array($this,'compileBlock'),$fc); + } + + # Compile values + foreach ($this->values as $v => $f) { + $pattern = sprintf($this->tag_value,preg_quote($v,'#')); + + $fc = preg_replace_callback('#'.$pattern.'#ms', + array($this,'compileValue'),$fc); + } + + return $fc; + } + + protected function compileBlock($match) + { + $b = $match[1]; + $content = $match[3]; + $attr = $this->getAttrs($match[2]); + + # Call block function + return call_user_func($this->blocks[$b],$attr,$content); + } + + protected function compileValue($match) + { + $v = $match[1]; + $attr = isset($match[2]) ? $this->getAttrs($match[2]) : array(); + $str_attr = isset($match[2]) ? $match[2] : null; + + return call_user_func($this->values[$v],$attr,ltrim($str_attr)); + } + + protected function getAttrs($str) + { + $res = array(); + if (preg_match_all('|([a-zA-Z0-9_:-]+)="(.+?)"|ms',$str,$m) > 0) { + foreach ($m[1] as $i => $v) { + $res[$v] = $m[2][$i]; + } + } + return $res; + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php b/inc/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php new file mode 100644 index 0000000..d0c4b96 --- /dev/null +++ b/inc/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php @@ -0,0 +1,1267 @@ + Added ``inline html`` support +# +# 3.2.5 +# => Changed longdesc by title in images +# +# 3.2.4 +# => Auto links +# => Code cleanup +# +# 3.2.3 +# Olivier +# => PHP5 Strict +# +# 3.2.2 +# Olivier +# => Changement de la gestion des URL spéciales +# +# 3.2.1 +# Olivier +# => Changement syntaxe des macros +# +# 3.2 +# Olivier +# => Changement de fonctionnement des macros +# => Passage de fonctions externes pour les macros et les mots wiki +# +# 3.1d +# Jérôme Lipowicz +# => antispam +# Olivier +# => centrage d'image +# +# 3.1c +# Olivier +# => Possibilité d'échaper les | dans les marqueurs avec \ +# +# 3.1b +# Nicolas Chachereau +# => Changement de regexp pour la correction syntaxique +# +# 3.1a +# Olivier +# => Bug du Call-time pass-by-reference +# +# 3.1 +# Olivier +# => Ajout des macros «««..»»» +# => Ajout des blocs vides øøø +# => Ajout du niveau de titre paramétrable +# => Option de blocage du parseur dans les
    +#			=> Titres au format setext (experimental, désactivé)
    +#
    +# 3.0
    +# Olivier		=> Récriture du parseur inline, plus d'erreur XHTML
    +#			=> Ajout d'une vérification d'intégrité pour les listes
    +#			=> Les acronymes sont maintenant dans un fichier texte
    +#			=> Ajout d'un tag images ((..)), del --..-- et ins ++..++
    +#			=> Plus possible de faire des liens JS [lien|javascript:...]
    +#			=> Ajout des notes de bas de page §§...§§
    +#			=> Ajout des mots wiki
    +#
    +# 2.5
    +# Olivier		=> Récriture du code, plus besoin du saut de ligne entre blocs !=
    +#
    +# 2.0
    +# Stephanie	=> correction des PCRE et ajout de fonctionnalités
    +# Mathieu 	=> ajout du strip-tags, implementation des options, reconnaissance automatique d'url, etc.
    +# Olivier		=> chagement de active_link en active_urls
    +#			=> ajout des options pour les blocs
    +#			=> intégration de l'aide dans le code, avec les options
    +#			=> début de quelque chose pour la reconnaissance auto d'url (avec Mat)
    +
    +# TODO :
    +# Mathieu	=> active_wiki_urls (modifier wikiParseUrl ?)
    +# 		=> active_auto_urls
    +#
    +# *		=> ajouter des options.
    +# 		=> trouver un meilleur nom pour active_link ? (pour le jour ou ca sera tellement une usine
    +#		   a gaz que on generera des tags  :)
    +#
    +
    +# Wiki2xhtml
    +
    +class wiki2xhtml
    +{
    +	public $__version__ = '3.2.6';
    +	
    +	public $T;
    +	public $opt;
    +	public $line;
    +	public $acro_table;
    +	public $foot_notes;
    +	public $macros;
    +	public $functions;
    +	
    +	public $tags;
    +	public $open_tags;
    +	public $close_tags;
    +	public $custom_tags = array();
    +	public $all_tags;
    +	public $tag_pattern;
    +	public $escape_table;
    +	public $allowed_inline = array();
    +	
    +	function __construct()
    +	{
    +		# Mise en place des options
    +		$this->setOpt('active_title',1);		# Activation des titres !!!
    +		$this->setOpt('active_setext_title',0);	# Activation des titres setext (EXPERIMENTAL)
    +		$this->setOpt('active_hr',1);			# Activation des 
    + $this->setOpt('active_lists',1); # Activation des listes + $this->setOpt('active_quote',1); # Activation du
    + $this->setOpt('active_pre',1); # Activation du
    +		$this->setOpt('active_empty',1);		# Activation du bloc vide øøø
    +		$this->setOpt('active_auto_urls',0);	# Activation de la reconnaissance d'url
    +		$this->setOpt('active_auto_br',0);	# Activation du saut de ligne automatique (dans les paragraphes)
    +		$this->setOpt('active_antispam',1);	# Activation de l'antispam pour les emails
    +		$this->setOpt('active_urls',1);		# Activation des liens []
    +		$this->setOpt('active_auto_img',1);	# Activation des images automatiques dans les liens []
    +		$this->setOpt('active_img',1);		# Activation des images (())
    +		$this->setOpt('active_anchor',1);		# Activation des ancres ~...~
    +		$this->setOpt('active_em',1);			# Activation du  ''...''
    +		$this->setOpt('active_strong',1);		# Activation du  __...__
    +		$this->setOpt('active_br',1);			# Activation du 
    %%% + $this->setOpt('active_q',1); # Activation du {{...}} + $this->setOpt('active_code',1); # Activation du @@...@@ + $this->setOpt('active_acronym',1); # Activation des acronymes + $this->setOpt('active_ins',1); # Activation des ins ++..++ + $this->setOpt('active_del',1); # Activation des del --..-- + $this->setOpt('active_inline_html',1); # Activation du HTML inline ``...`` + $this->setOpt('active_footnotes',1); # Activation des notes de bas de page + $this->setOpt('active_wikiwords',0); # Activation des mots wiki + $this->setOpt('active_macros',1); # Activation des macros /// /// + + $this->setOpt('parse_pre',1); # Parser l'intérieur de blocs
     ?
    +		
    +		$this->setOpt('active_fr_syntax',1);	# Corrections syntaxe FR
    +		
    +		$this->setOpt('first_title_level',3);	# Premier niveau de titre 
    +		
    +		$this->setOpt('note_prefix','wiki-footnote');
    +		$this->setOpt('note_str','

    Notes

    %s
    '); + $this->setOpt('words_pattern', + '((?setOpt('auto_url_pattern', + '%(?setOpt('acronyms_file',dirname(__FILE__).'/acronyms.txt'); + + $this->acro_table = $this->__getAcronyms(); + $this->foot_notes = array(); + $this->functions = array(); + $this->macros = array(); + + $this->registerFunction('macro:html',array($this,'__macroHTML')); + } + + function setOpt($option, $value) + { + $this->opt[$option] = $value; + } + + function setOpts($options) + { + if (!is_array($options)) { + return; + } + + foreach ($options as $k => $v) { + $this->opt[$k] = $v; + } + } + + function getOpt($option) + { + return (!empty($this->opt[$option])) ? $this->opt[$option] : false; + } + + function registerFunction($type,$name) + { + if (is_callable($name)) { + $this->functions[$type] = $name; + } + } + + function transform($in) + { + # Initialisation des tags + $this->__initTags(); + $this->foot_notes = array(); + + # Récupération des macros + if ($this->getOpt('active_macros')) { + $in = preg_replace('#^///(.*?)///($|\r)#mse',"\\\$this->__getMacro('\\1')",$in); + } + + # Vérification du niveau de titre + if ($this->getOpt('first_title_level') > 4) { + $this->setOpt('first_title_level',4); + } + + $res = str_replace("\r", '', $in); + + $escape_pattern = array(); + + # traitement des titres à la setext + if ($this->getOpt('active_setext_title') && $this->getOpt('active_title')) { + $res = preg_replace('/^(.*)\n[=]{5,}$/m','!!!$1',$res); + $res = preg_replace('/^(.*)\n[-]{5,}$/m','!!$1',$res); + } + + # Transformation des mots Wiki + if ($this->getOpt('active_wikiwords') && $this->getOpt('words_pattern')) { + $res = preg_replace('/'.$this->getOpt('words_pattern').'/msu','¶¶¶$1¶¶¶',$res); + } + + # Transformation des URLs automatiques + if ($this->getOpt('active_auto_urls')) + { + $active_urls = $this->getOpt('active_urls'); + + $this->setOpt('active_urls',1); + $this->__initTags(); + + # If urls are not active, escape URLs tags + if (!$active_urls) + { + $res = preg_replace( + '%(?tags['a'])).'])%msU', + '\\\$1',$res + ); + } + + # Transforms urls + $res = preg_replace($this->getOpt('auto_url_pattern'),'[$1$2]',$res); + + } + + $this->T = explode("\n",$res); + $this->T[] = ''; + + # Parse les blocs + $res = $this->__parseBlocks(); + + # Line break + if ($this->getOpt('active_br')) { + $res = preg_replace('/(?', $res); + $escape_pattern[] = '%%%'; + } + + # Nettoyage des \s en trop + $res = preg_replace('/([\s]+)(<\/p>|<\/li>|<\/pre>)/', '$2', $res); + $res = preg_replace('/(
  • )([\s]+)/', '$1', $res); + + # On vire les escapes + if (!empty($escape_pattern)) { + $res = preg_replace('/\\\('.implode('|',$escape_pattern).')/','$1',$res); + } + + # On vire les ¶¶¶MotWiki¶¶¶ qui sont resté (dans les url...) + if ($this->getOpt('active_wikiwords') && $this->getOpt('words_pattern')) { + $res = preg_replace('/¶¶¶'.$this->getOpt('words_pattern').'¶¶¶/msu','$1',$res); + } + + # On remet les macros + if ($this->getOpt('active_macros')) { + $res = preg_replace('/^##########MACRO#([0-9]+)#$/mse','\$this->__putMacro("$1")',$res); + } + + # Auto line break dans les paragraphes + if ($this->getOpt('active_auto_br')) { + $res = preg_replace_callback('%(

    )(.*?)(

    )%msu',array($this,'__autoBR'),$res); + } + + # On ajoute les notes + if (count($this->foot_notes) > 0) + { + $res_notes = ''; + $i = 1; + foreach ($this->foot_notes as $k => $v) { + $res_notes .= "\n".'

    ['.$i.'] '.$v.'

    '; + $i++; + } + $res .= sprintf("\n".$this->getOpt('note_str')."\n",$res_notes); + } + + return $res; + } + + /* PRIVATE + --------------------------------------------------- */ + + function __initTags() + { + $tags = array( + 'em' => array("''","''"), + 'strong' => array('__','__'), + 'acronym' => array('??','??'), + 'a' => array('[',']'), + 'img' => array('((','))'), + 'q' => array('{{','}}'), + 'code' => array('@@','@@'), + 'anchor' => array('~','~'), + 'del' => array('--','--'), + 'ins' => array('++','++'), + 'inline' => array('``','``'), + 'note' => array('$$','$$'), + 'word' => array('¶¶¶','¶¶¶') + ); + + $this->tags = array_merge($tags,$this->custom_tags); + + # Suppression des tags selon les options + if (!$this->getOpt('active_urls')) { + unset($this->tags['a']); + } + if (!$this->getOpt('active_img')) { + unset($this->tags['img']); + } + if (!$this->getOpt('active_anchor')) { + unset($this->tags['anchor']); + } + if (!$this->getOpt('active_em')) { + unset($this->tags['em']); + } + if (!$this->getOpt('active_strong')) { + unset($this->tags['strong']); + } + if (!$this->getOpt('active_q')) { + unset($this->tags['q']); + } + if (!$this->getOpt('active_code')) { + unset($this->tags['code']); + } + if (!$this->getOpt('active_acronym')) { + unset($this->tags['acronym']); + } + if (!$this->getOpt('active_ins')) { + unset($this->tags['ins']); + } + if (!$this->getOpt('active_del')) { + unset($this->tags['del']); + } + if (!$this->getOpt('active_inline_html')) { + unset($this->tags['inline']); + } + if (!$this->getOpt('active_footnotes')) { + unset($this->tags['note']); + } + if (!$this->getOpt('active_wikiwords')) { + unset($this->tags['word']); + } + + $this->open_tags = $this->__getTags(); + $this->close_tags = $this->__getTags(false); + $this->all_tags = $this->__getAllTags(); + $this->tag_pattern = $this->__getTagsPattern(); + + $this->escape_table = $this->all_tags; + array_walk($this->escape_table,create_function('&$a','$a = \'\\\\\'.$a;')); + } + + function __getTags($open=true) + { + $res = array(); + foreach ($this->tags as $k => $v) { + $res[$k] = ($open) ? $v[0] : $v[1]; + } + return $res; + } + + function __getAllTags() + { + $res = array(); + foreach ($this->tags as $v) { + $res[] = $v[0]; + $res[] = $v[1]; + } + return array_values(array_unique($res)); + } + + function __getTagsPattern($escape=false) + { + $res = $this->all_tags; + array_walk($res,create_function('&$a','$a = preg_quote($a,"/");')); + + if (!$escape) { + return '/(?T); + + for ($i=0; $i<$max; $i++) + { + $pre_mode = $mode; + $pre_type = $type; + $end = ($i+1 == $max); + + $line = $this->__getLine($i,$type,$mode); + + if ($type != 'pre' || $this->getOpt('parse_pre')) { + $line = $this->__inlineWalk($line); + } + + $res .= $this->__closeLine($type,$mode,$pre_type,$pre_mode); + $res .= $this->__openLine($type,$mode,$pre_type,$pre_mode); + + # P dans les blockquotes + if ($type == 'blockquote' && trim($line) == '' && $pre_type == $type) { + $res .= "

    \n

    "; + } + + # Correction de la syntaxe FR dans tous sauf pre et hr + # Sur idée de Christophe Bonijol + # Changement de regex (Nicolas Chachereau) + if ($this->getOpt('active_fr_syntax') && $type != null && $type != 'pre' && $type != 'hr') { + $line = preg_replace('%[ ]+([:?!;\x{00BB}](\s|$))%u',' $1',$line); + $line = preg_replace('%(\x{00AB})[ ]+%u','$1 ',$line); + } + + $res .= $line; + } + + return trim($res); + } + + function __getLine($i,&$type,&$mode) + { + $pre_type = $type; + $pre_mode = $mode; + $type = $mode = null; + + if (empty($this->T[$i])) { + return false; + } + + $line = htmlspecialchars($this->T[$i],ENT_NOQUOTES); + + # Ligne vide + if (empty($line)) + { + $type = null; + } + elseif ($this->getOpt('active_empty') && preg_match('/^øøø(.*)$/',$line,$cap)) + { + $type = null; + $line = trim($cap[1]); + } + # Titre + elseif ($this->getOpt('active_title') && preg_match('/^([!]{1,4})(.*)$/',$line,$cap)) + { + $type = 'title'; + $mode = strlen($cap[1]); + $line = trim($cap[2]); + } + # Ligne HR + elseif ($this->getOpt('active_hr') && preg_match('/^[-]{4}[- ]*$/',$line)) + { + $type = 'hr'; + $line = null; + } + # Blockquote + elseif ($this->getOpt('active_quote') && preg_match('/^(>|;:)(.*)$/',$line,$cap)) + { + $type = 'blockquote'; + $line = trim($cap[2]); + } + # Liste + elseif ($this->getOpt('active_lists') && preg_match('/^([*#]+)(.*)$/',$line,$cap)) + { + $type = 'list'; + $mode = $cap[1]; + $valid = true; + + # Vérification d'intégrité + $dl = ($type != $pre_type) ? 0 : strlen($pre_mode); + $d = strlen($mode); + $delta = $d-$dl; + + if ($delta < 0 && strpos($pre_mode,$mode) !== 0) { + $valid = false; + } + if ($delta > 0 && $type == $pre_type && strpos($mode,$pre_mode) !== 0) { + $valid = false; + } + if ($delta == 0 && $mode != $pre_mode) { + $valid = false; + } + if ($delta > 1) { + $valid = false; + } + + if (!$valid) { + $type = 'p'; + $mode = null; + $line = '
    '.$line; + } else { + $line = trim($cap[2]); + } + } + # Préformaté + elseif ($this->getOpt('active_pre') && preg_match('/^[ ]{1}(.*)$/',$line,$cap)) + { + $type = 'pre'; + $line = $cap[1]; + } + # Paragraphe + else { + $type = 'p'; + $line = trim($line); + } + + return $line; + } + + function __openLine($type,$mode,$pre_type,$pre_mode) + { + $open = ($type != $pre_type); + + if ($open && $type == 'p') + { + return "\n

    "; + } + elseif ($open && $type == 'blockquote') + { + return "\n

    "; + } + elseif (($open || $mode != $pre_mode) && $type == 'title') + { + $fl = $this->getOpt('first_title_level'); + $fl = $fl+3; + $l = $fl-$mode; + return "\n'; + } + elseif ($open && $type == 'pre') + { + return "\n

    ";
    +		}
    +		elseif ($open && $type == 'hr')
    +		{
    +			return "\n
    "; + } + elseif ($type == 'list') + { + $dl = ($open) ? 0 : strlen($pre_mode); + $d = strlen($mode); + $delta = $d-$dl; + $res = ''; + + if($delta > 0) { + if(substr($mode, -1, 1) == '*') { + $res .= "
      \n"; + } else { + $res .= "
        \n"; + } + } elseif ($delta < 0) { + $res .= "\n"; + for($j = 0; $j < abs($delta); $j++) { + if (substr($pre_mode,(0 - $j - 1), 1) == '*') { + $res .= "
    \n
  • \n"; + } else { + $res .= "\n\n"; + } + } + } else { + $res .= "\n"; + } + + return $res."
  • "; + } + else + { + return null; + } + } + + function __closeLine($type,$mode,$pre_type,$pre_mode) + { + $close = ($type != $pre_type); + + if ($close && $pre_type == 'p') + { + return "

    \n"; + } + elseif ($close && $pre_type == 'blockquote') + { + return "

  • \n"; + } + elseif (($close || $mode != $pre_mode) && $pre_type == 'title') + { + $fl = $this->getOpt('first_title_level'); + $fl = $fl+3; + $l = $fl-$pre_mode; + return '\n"; + } + elseif ($close && $pre_type == 'pre') + { + return "
    \n"; + } + elseif ($close && $pre_type == 'list') + { + $res = ''; + for($j = 0; $j < strlen($pre_mode); $j++) { + if(substr($pre_mode,(0 - $j - 1), 1) == '*') { + $res .= "\n"; + } else { + $res .= "\n"; + } + } + return $res; + } + else + { + return "\n"; + } + } + + + /* Inline + --------------------------------------------------- */ + function __inlineWalk($str,$allow_only=null) + { + $tree = preg_split($this->tag_pattern,$str,-1,PREG_SPLIT_DELIM_CAPTURE); + + $res = ''; + for ($i=0; $iopen_tags)) && + ($allow_only == null || in_array(array_search($tree[$i],$this->open_tags),$allow_only))) + { + $tag = array_search($tree[$i],$this->open_tags); + $tag_type = 'open'; + + if (($tidy = $this->__makeTag($tree,$tag,$i,$i,$attr,$tag_type)) !== false) + { + if ($tag != '') { + $res .= '<'.$tag.$attr; + $res .= ($tag_type == 'open') ? '>' : ' />'; + } + $res .= $tidy; + } + else + { + $res .= $tree[$i]; + } + } + else + { + $res .= $tree[$i]; + } + } + + # Suppression des echappements + $res = str_replace($this->escape_table,$this->all_tags,$res); + + return $res; + } + + function __makeTag(&$tree,&$tag,$position,&$j,&$attr,&$type) + { + $res = ''; + $closed = false; + + $itag = $this->close_tags[$tag]; + + # Recherche fermeture + for ($i=$position+1;$i__parseLink($res,$tag,$attr,$type); + break; + case 'img': + $type = 'close'; + $res = $this->__parseImg($res,$attr); + break; + case 'acronym': + $res = $this->__parseAcronym($res,$attr); + break; + case 'q': + $res = $this->__parseQ($res,$attr); + break; + case 'anchor': + $tag = 'a'; + $res = $this->__parseAnchor($res,$attr); + break; + case 'note': + $tag = ''; + $res = $this->__parseNote($res); + break; + case 'inline': + $tag = ''; + $res = $this->__parseInlineHTML($res); + break; + case 'word': + $res = $this->parseWikiWord($res,$tag,$attr,$type); + break; + default : + $res = $this->__inlineWalk($res); + break; + } + + if ($type == 'open' && $tag != '') { + $res .= ''; + } + $j = $i; + break; + } + } + + return $res; + } + else + { + return false; + } + } + + function __splitTagsAttr($str) + { + $res = preg_split('/(? $v) { + $res[$k] = str_replace("\|",'|',$v); + } + + return $res; + } + + # Antispam (Jérôme Lipowicz) + function __antiSpam($str) + { + $encoded = bin2hex($str); + $encoded = chunk_split($encoded, 2, '%'); + $encoded = '%'.substr($encoded, 0, strlen($encoded) - 1); + return $encoded; + } + + function __parseLink($str,&$tag,&$attr,&$type) + { + $n_str = $this->__inlineWalk($str,array('acronym','img')); + $data = $this->__splitTagsAttr($n_str); + $no_image = false; + + # Only URL in data + if (count($data) == 1) + { + $url = trim($str); + $content = strlen($url) > 35 ? substr($url,0,35).'...' : $url; + $lang = ''; + $title = $url; + } + elseif (count($data) > 1) + { + $url = trim($data[1]); + $content = $data[0]; + $lang = (!empty($data[2])) ? $this->protectAttr($data[2],true) : ''; + $title = (!empty($data[3])) ? $data[3] : ''; + $no_image = (!empty($data[4])) ? (boolean) $data[4] : false; + } + + # Remplacement si URL spéciale + $this->__specialUrls($url,$content,$lang,$title); + + # On vire les   dans l'url + $url = str_replace(' ',' ',$url); + + if (preg_match('/^(.+)[.](gif|jpg|jpeg|png)$/', $url) && !$no_image && $this->getOpt('active_auto_img')) + { + # On ajoute les dimensions de l'image si locale + # Idée de Stephanie + $img_size = null; + if (!preg_match('#[a-zA-Z]+://#', $url)) { + if (preg_match('#^/#',$url)) { + $path_img = $_SERVER['DOCUMENT_ROOT'] . $url; + } else { + $path_img = $url; + } + + $img_size = @getimagesize($path_img); + } + + $attr = ' src="'.$this->protectAttr($this->protectUrls($url)).'"'. + $attr .= (count($data) > 1) ? ' alt="'.$this->protectAttr($content).'"' : ' alt=""'; + $attr .= ($lang) ? ' lang="'.$lang.'"' : ''; + $attr .= ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; + $attr .= (is_array($img_size)) ? ' '.$img_size[3] : ''; + + $tag = 'img'; + $type = 'close'; + return null; + } + else + { + if ($this->getOpt('active_antispam') && preg_match('/^mailto:/',$url)) { + $content = $content == $url ? preg_replace('%^mailto:%','',$content) : $content; + $url = 'mailto:'.$this->__antiSpam(substr($url,7)); + + } + + $attr = ' href="'.$this->protectAttr($this->protectUrls($url)).'"'; + $attr .= ($lang) ? ' hreflang="'.$lang.'"' : ''; + $attr .= ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; + + return $content; + } + } + + function __specialUrls(&$url,&$content,&$lang,&$title) + { + foreach ($this->functions as $k => $v) + { + if (strpos($k,'url:') === 0 && strpos($url,substr($k,4)) === 0) + { + $res = call_user_func($v,$url,$content); + + $url = isset($res['url']) ? $res['url'] : $url; + $content = isset($res['content']) ? $res['content'] : $content; + $lang = isset($res['lang']) ? $res['lang'] : $lang; + $title = isset($res['title']) ? $res['title'] : $title; + + break; + } + } + } + + function __parseImg($str,&$attr) + { + $data = $this->__splitTagsAttr($str); + + $alt = ''; + $url = $data[0]; + if (!empty($data[1])) { + $alt = $data[1]; + } + + $attr = ' src="'.$this->protectAttr($this->protectUrls($url)).'"'; + $attr .= ' alt="'.$this->protectAttr($alt).'"'; + + if (!empty($data[2])) { + $data[2] = strtoupper($data[2]); + if ($data[2] == 'G' || $data[2] == 'L') { + $attr .= ' style="float:left; margin: 0 1em 1em 0;"'; + } elseif ($data[2] == 'D' || $data[2] == 'R') { + $attr .= ' style="float:right; margin: 0 0 1em 1em;"'; + } elseif ($data[2] == 'C') { + $attr .= ' style="display:block; margin:0 auto;"'; + } + } + + if (!empty($data[3])) { + $attr .= ' title="'.$this->protectAttr($data[3]).'"'; + } + + return null; + } + + function __parseQ($str,&$attr) + { + $str = $this->__inlineWalk($str); + $data = $this->__splitTagsAttr($str); + + $content = $data[0]; + $lang = (!empty($data[1])) ? $this->protectAttr($data[1],true) : ''; + + $attr .= (!empty($lang)) ? ' lang="'.$lang.'"' : ''; + $attr .= (!empty($data[2])) ? ' cite="'.$this->protectAttr($this->protectUrls($data[2])).'"' : ''; + + return $content; + } + + function __parseAnchor($str,&$attr) + { + $name = $this->protectAttr($str,true); + + if ($name != '') { + $attr = ' name="'.$name.'"'; + } + return null; + } + + function __parseNote($str) + { + $i = count($this->foot_notes)+1; + $id = $this->getOpt('note_prefix').'-'.$i; + $this->foot_notes[$id] = $this->__inlineWalk($str); + return '\['.$i.'\]'; + } + + function __parseInlineHTML($str) + { + return str_replace(array('>','<'),array('>','<'),$str); + } + + # Obtenir un acronyme + function __parseAcronym($str,&$attr) + { + $data = $this->__splitTagsAttr($str); + + $acronym = $data[0]; + $title = $lang = ''; + + if (count($data) > 1) + { + $title = $data[1]; + $lang = (!empty($data[2])) ? $this->protectAttr($data[2],true) : ''; + } + + if ($title == '' && !empty($this->acro_table[$acronym])) { + $title = $this->acro_table[$acronym]; + } + + $attr = ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; + $attr .= ($lang) ? ' lang="'.$lang.'"' : ''; + + return $acronym; + } + + # Définition des acronymes, dans le fichier acronyms.txt + function __getAcronyms() + { + $file = $this->getOpt('acronyms_file'); + $res = array(); + + if (file_exists($file)) + { + if (($fc = @file($file)) !== false) + { + foreach ($fc as $v) + { + $v = trim($v); + if ($v != '') + { + $p = strpos($v,':'); + $K = (string) trim(substr($v,0,$p)); + $V = (string) trim(substr($v,($p+1))); + + if ($K) { + $res[$K] = $V; + } + } + } + } + } + + return $res; + } + + # Mots wiki (pour héritage) + function parseWikiWord($str,&$tag,&$attr,&$type) + { + $tag = $attr = ''; + + if (isset($this->functions['wikiword'])) { + return call_user_func($this->functions['wikiword'],$str); + } + + return $str; + } + + /* Protection des attributs */ + function protectAttr($str,$name=false) + { + if ($name && !preg_match('/^[A-Za-z][A-Za-z0-9_:.-]*$/',$str)) { + return ''; + } + + return str_replace(array("'",'"'),array(''','"'),$str); + } + + /* Protection des urls */ + function protectUrls($str) + { + if (preg_match('/^javascript:/',$str)) { + $str = '#'; + } + + return $str; + } + + /* Auto BR */ + function __autoBR($m) + { + return $m[1].str_replace("\n","
    \n",$m[2]).$m[3]; + } + + /* Macro + --------------------------------------------------- */ + function __getMacro($s) + { + $this->macros[] = str_replace('\"','"',$s); + return 'øøø##########MACRO#'.(count($this->macros)-1).'#'; + } + + function __putMacro($id) + { + $id = (integer) $id; + if (isset($this->macros[$id])) + { + $content = str_replace("\r",'',$this->macros[$id]); + + $c = explode("\n",$content); + + # première ligne, premier mot + $fl = trim($c[0]); + $fw = $fl; + + if ($fl) { + if (strpos($fl,' ') !== false) { + $fw = substr($fl,0,strpos($fl,' ')); + } + $content = implode("\n",array_slice($c,1)); + } + + if ($c[0] == "\n") { + $content = implode("\n",array_slice($c,1)); + } + + if ($fw) + { + if (isset($this->functions['macro:'.$fw])) + { + return call_user_func($this->functions['macro:'.$fw],$content,$fl); + } + } + + # Si on n'a rien pu faire, on retourne le tout sous + # forme de
    +			return '
    '.htmlspecialchars($this->macros[$id]).'
    '; + } + + return null; + } + + function __macroHTML($s) + { + return $s; + } + + /* Aide et debug + --------------------------------------------------- */ + function help() + { + $help['b'] = array(); + $help['i'] = array(); + + $help['b'][] = 'Laisser une ligne vide entre chaque bloc de même nature.'; + $help['b'][] = 'Paragraphe : du texte et une ligne vide'; + + if ($this->getOpt('active_title')) { + $help['b'][] = 'Titre : !!!, !!, '. + '! pour des titres plus ou moins importants'; + } + + if ($this->getOpt('active_hr')) { + $help['b'][] = 'Trait horizontal : ----'; + } + + if ($this->getOpt('active_lists')) { + $help['b'][] = 'Liste : ligne débutant par * ou '. + '#. Il est possible de mélanger les listes '. + '(*#*) pour faire des listes de plusieurs niveaux. '. + 'Respecter le style de chaque niveau'; + } + + if ($this->getOpt('active_pre')) { + $help['b'][] = 'Texte préformaté : espace devant chaque ligne de texte'; + } + + if ($this->getOpt('active_quote')) { + $help['b'][] = 'Bloc de citation : > ou '. + ';: devant chaque ligne de texte'; + } + + if ($this->getOpt('active_fr_syntax')) { + $help['i'][] = 'La correction de ponctuation est active. Un espace '. + 'insécable remplacera automatiquement tout espace '. + 'précédant les marques ";","?",":" et "!".'; + } + + if ($this->getOpt('active_em')) { + $help['i'][] = 'Emphase : deux apostrophes \'\'texte\'\''; + } + + if ($this->getOpt('active_strong')) { + $help['i'][] = 'Forte emphase : deux soulignés __texte__'; + } + + if ($this->getOpt('active_br')) { + $help['i'][] = 'Retour forcé à la ligne : %%%'; + } + + if ($this->getOpt('active_ins')) { + $help['i'][] = 'Insertion : deux plus ++texte++'; + } + + if ($this->getOpt('active_del')) { + $help['i'][] = 'Suppression : deux moins --texte--'; + } + + if ($this->getOpt('active_urls')) { + $help['i'][] = 'Lien : [url], [nom|url], '. + '[nom|url|langue] ou [nom|url|langue|titre].'; + + $help['i'][] = 'Image : comme un lien mais avec une extension d\'image.'. + '
    Pour désactiver la reconnaissance d\'image mettez 0 dans un dernier '. + 'argument. Par exemple [image|image.gif||0] fera un lien vers l\'image au '. + 'lieu de l\'afficher.'. + '
    Il est conseillé d\'utiliser la nouvelle syntaxe.'; + } + + if ($this->getOpt('active_img')) { + $help['i'][] = 'Image (nouvelle syntaxe) : '. + '((url|texte alternatif)), '. + '((url|texte alternatif|position)) ou '. + '((url|texte alternatif|position|description longue)). '. + '
    La position peut prendre les valeur L ou G (gauche), R ou D (droite) ou C (centré).'; + } + + if ($this->getOpt('active_anchor')) { + $help['i'][] = 'Ancre : ~ancre~'; + } + + if ($this->getOpt('active_acronym')) { + $help['i'][] = 'Acronyme : ??acronyme?? ou '. + '??acronyme|titre??'; + } + + if ($this->getOpt('active_q')) { + $help['i'][] = 'Citation : {{citation}}, '. + '{{citation|langue}} ou {{citation|langue|url}}'; + } + + if ($this->getOpt('active_code')) { + $help['i'][] = 'Code : @@code ici@@'; + } + + if ($this->getOpt('active_footnotes')) { + $help['i'][] = 'Note de bas de page : $$Corps de la note$$'; + } + + $res = '
    '; + + $res .= '
    Blocs
    '; + if (count($help['b']) > 0) + { + $res .= '
    • '; + $res .= implode(' ;
    • ', $help['b']); + $res .= '.
    '; + } + $res .= '
    '; + + $res .= '
    Éléments en ligne
    '; + if (count($help['i']) > 0) + { + $res .= '
    • '; + $res .= implode(' ;
    • ', $help['i']); + $res .= '.
    '; + } + $res .= '
    '; + + $res .= '
    '; + + return $res; + } + + /* + function debug() + { + $mode = $type = null; + $max = count($this->T); + + $res = + ''. + ''; + + for ($i=0; $i<$max; $i++) + { + $pre_mode = $mode; + $pre_type = $type; + + $line = $this->__getLine($i,$type,$mode); + + $res .= + ''. + ''; + + } + $res .= '
    p-modep-typemodetypechaine
    '.$pre_mode.''.$pre_type.''.$mode.''.$type.''.$line.'
    '; + + return $res; + } + //*/ +} + +?> \ No newline at end of file diff --git a/inc/clearbricks/url.handler/class.url.handler.php b/inc/clearbricks/url.handler/class.url.handler.php new file mode 100644 index 0000000..72ab627 --- /dev/null +++ b/inc/clearbricks/url.handler/class.url.handler.php @@ -0,0 +1,203 @@ +mode = $mode; + } + + public function register($type,$url,$representation,$handler) + { + $this->types[$type] = array( + 'url' => $url, + 'representation' => $representation, + 'handler' => $handler + ); + } + + public function registerDefault($handler) + { + $this->default_handler = $handler; + } + + public function unregister($type) + { + if (isset($this->types[$type])) { + unset($this->types[$type]); + } + } + + public function getTypes() + { + return $this->types; + } + + public function getBase($type) + { + if (isset($this->types[$type])) { + return $this->types[$type]['url']; + } + return null; + } + + public function getDocument() + { + $type = $args = ''; + + if ($this->mode == 'path_info') + { + $part = substr($_SERVER['PATH_INFO'],1); + } + else + { + $part = ''; + + $qs = $this->parseQueryString(); + + # Recreates some _GET and _REQUEST pairs + if (!empty($qs)) + { + foreach ($_GET as $k => $v) { + if (isset($_REQUEST[$k])) { + unset($_REQUEST[$k]); + } + } + $_GET = $qs; + $_REQUEST = array_merge($qs,$_REQUEST); + + list($k,$v) = each($qs); + if ($v === null) { + $part = $k; + unset($_GET[$k]); + unset($_REQUEST[$k]); + } + } + } + + $_SERVER['URL_REQUEST_PART'] = $part; + + $this->getArgs($part,$type,$args); + + if (!$type) + { + $this->type = 'default'; + $this->callDefaultHandler($args); + } + else + { + $this->type = $type; + $this->callHandler($type,$args); + } + } + + public function getArgs($part,&$type,&$args) + { + if ($part == '') { + $type = null; + $args = null; + return; + } + + $this->sortTypes(); + + foreach ($this->types as $k => $v) + { + $repr = $v['representation']; + if ($repr == $part) { + $type = $k; + $args = null; + return; + } + elseif (preg_match('#'.$repr.'#',$part,$m)) + { + $type = $k; + $args = isset($m[1]) ? $m[1] : null; + return; + } + } + + # No type, pass args to default + $args = $part; + } + + public function callHandler($type,$args) + { + if (!isset($this->types[$type])) { + throw new Exception('Unknown URL type'); + } + + $handler = $this->types[$type]['handler']; + if (!is_callable($handler)) { + throw new Exception('Unable to call function'); + } + + call_user_func($handler,$args); + } + + public function callDefaultHandler($args) + { + if (!is_callable($this->default_handler)) { + throw new Exception('Unable to call function'); + } + + call_user_func($this->default_handler,$args); + } + + protected function parseQueryString() + { + if (!empty($_SERVER['QUERY_STRING'])) + { + $q = explode('&',$_SERVER['QUERY_STRING']); + $T = array(); + foreach ($q as $v) + { + $t = explode('=',$v,2); + + $t[0] = rawurldecode($t[0]); + if (!isset($t[1])) { + $T[$t[0]] = null; + } else { + $T[$t[0]] = urldecode($t[1]); + } + } + + return $T; + } + return array(); + } + + protected function sortTypes() + { + foreach ($this->types as $k => $v) { + $r[$k] = $v['url']; + } + array_multisort($r,SORT_DESC,$this->types); + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/zip/class.unzip.php b/inc/clearbricks/zip/class.unzip.php new file mode 100644 index 0000000..dad397a --- /dev/null +++ b/inc/clearbricks/zip/class.unzip.php @@ -0,0 +1,522 @@ +file_name = $file_name; + } + + public function __destruct() + { + $this->close(); + } + + public function close() + { + if ($this->fp) { + fclose($this->fp); + $this->fp = null; + } + + if ($this->memory_limit) { + ini_set('memory_limit',$this->memory_limit); + } + } + + public function getList($stop_on_file=false,$exclude=false) + { + if (!empty($this->compressed_list)) { + return $this->compressed_list; + } + + if (!$this->loadFileListByEOF($stop_on_file,$exclude)) { + if(!$this->loadFileListBySignatures($stop_on_file,$exclude)) { + return false; + } + } + + return $this->compressed_list; + } + + public function unzipAll($target) + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + foreach ($this->compressed_list as $k => $v) + { + if ($v['is_dir']) { + continue; + } + + $this->unzip($k,$target.'/'.$k); + } + } + + public function unzip($file_name,$target=false) + { + if (empty($this->compressed_list)) { + $this->getList($file_name); + } + + if (!isset($this->compressed_list[$file_name])) { + throw new Exception(sprintf(__('File %s is not compressed in the zip.'),$file_name)); + } + $details =& $this->compressed_list[$file_name]; + + if ($details['is_dir']) { + throw new Exception(sprintf(__('Trying to unzip a folder name %s'),$file_name)); + } + + if (!$details['uncompressed_size']) { + return $this->putContent('',$target); + } + + if ($target) { + $this->testTargetDir(dirname($target)); + } + + fseek($this->fp(),$details['contents_start_offset']); + + $this->memoryAllocate($details['compressed_size']); + return $this->uncompress( + fread($this->fp(), $details['compressed_size']), + $details['compression_method'], + $details['uncompressed_size'], + $target + ); + } + + public function getFilesList() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + $res = array(); + foreach ($this->compressed_list as $k => $v) { + if (!$v['is_dir']) { + $res[] = $k; + } + } + return $res; + } + + public function getDirsList() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + $res = array(); + foreach ($this->compressed_list as $k => $v) { + if ($v['is_dir']) { + $res[] = substr($k,0,-1); + } + } + return $res; + } + + public function getRootDir() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + $files = $this->getFilesList(); + $dirs = $this->getDirsList(); + + $root_files = 0; + $root_dirs = 0; + foreach ($files as $v) { if (strpos($v,'/') === false) { $root_files++; }} + foreach ($dirs as $v) { if (strpos($v,'/') === false) { $root_dirs++; }} + + if ($root_files == 0 && $root_dirs == 1) { + return $dirs[0]; + } else { + return false; + } + } + + public function isEmpty() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + return count($this->compressed_list) == 0; + } + + public function hasFile($f) + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + return isset($this->compressed_list[$f]); + } + + protected function fp() + { + if ($this->fp === null) { + $this->fp = @fopen($this->file_name,'rb'); + } + + if ($this->fp === false) { + throw new Exception('Unable to open file.'); + } + + return $this->fp; + } + + protected function putContent($content,$target=false) + { + if ($target) { + $r = @file_put_contents($target,$content); + if ($r === false) { + throw new Exception(__('Unable to write destination file.')); + } + files::inheritChmod($target); + return true; + } + return $content; + } + + protected function testTargetDir($dir) + { + if (is_dir($dir) && !is_writable($dir)) { + throw new Exception(__('Unable to write in target directory, permission denied.')); + } + + if (!is_dir($dir)) { + files::makeDir($dir,true); + } + } + + protected function uncompress($content,$mode,$size,$target=false) + { + switch ($mode) + { + case 0: + # Not compressed + $this->memoryAllocate($size*2); + return $this->putContent($content,$target); + case 1: + throw new Exception('Shrunk mode is not supported.'); + case 2: + case 3: + case 4: + case 5: + throw new Exception('Compression factor '.($mode-1).' is not supported.'); + case 6: + throw new Exception('Implode is not supported.'); + case 7: + throw new Exception('Tokenizing compression algorithm is not supported.'); + case 8: + # Deflate + if (!function_exists('gzinflate')) { + throw new Exception('Gzip functions are not available.'); + } + $this->memoryAllocate($size*2); + return $this->putContent(gzinflate($content,$size),$target); + case 9: + throw new Exception('Enhanced Deflating is not supported.'); + case 10: + throw new Exception('PKWARE Date Compression Library Impoloding is not supported.'); + case 12: + # Bzip2 + if (!function_exists('bzdecompress')) { + throw new Exception('Bzip2 functions are not available.'); + } + $this->memoryAllocate($size*2); + return $this->putContent(bzdecompress($content),$target); + case 18: + throw new Exception('IBM TERSE is not supported.'); + default: + throw new Exception('Unknown uncompress method'); + } + } + + protected function loadFileListByEOF($stop_on_file=false,$exclude=false) + { + $fp = $this->fp(); + + for ($x=0; $x<1024; $x++) + { + fseek($fp,-22-$x,SEEK_END); + $signature = fread($fp,4); + + if ($signature == $this->dir_sig_e) + { + $dir_list = array(); + + $eodir = array( + 'disk_number_this' => unpack('v', fread($fp,2)), + 'disk_number' => unpack('v', fread($fp,2)), + 'total_entries_this' => unpack('v', fread($fp,2)), + 'total_entries' => unpack('v', fread($fp,2)), + 'size_of_cd' => unpack('V', fread($fp,4)), + 'offset_start_cd' => unpack('V', fread($fp,4)) + ); + + $zip_comment_len = unpack('v', fread($fp,2)); + $eodir['zipfile_comment'] = $zip_comment_len[1] ? fread($fp,$zip_comment_len) : ''; + + $this->eo_central = array( + 'disk_number_this' => $eodir['disk_number_this'][1], + 'disk_number' => $eodir['disk_number'][1], + 'total_entries_this' => $eodir['total_entries_this'][1], + 'total_entries' => $eodir['total_entries'][1], + 'size_of_cd' => $eodir['size_of_cd'][1], + 'offset_start_cd' => $eodir['offset_start_cd'][1], + 'zipfile_comment' => $eodir['zipfile_comment'] + ); + + fseek($fp, $this->eo_central['offset_start_cd']); + $signature = fread($fp,4); + + while ($signature == $this->dir_sig) + { + $dir = array(); + $dir['version_madeby'] = unpack("v",fread($fp, 2)); # version made by + $dir['version_needed'] = unpack("v",fread($fp, 2)); # version needed to extract + $dir['general_bit_flag'] = unpack("v",fread($fp, 2)); # general purpose bit flag + $dir['compression_method'] = unpack("v",fread($fp, 2)); # compression method + $dir['lastmod_time'] = unpack("v",fread($fp, 2)); # last mod file time + $dir['lastmod_date'] = unpack("v",fread($fp, 2)); # last mod file date + $dir['crc-32'] = fread($fp,4); # crc-32 + $dir['compressed_size'] = unpack("V",fread($fp, 4)); # compressed size + $dir['uncompressed_size'] = unpack("V",fread($fp, 4)); # uncompressed size + + $file_name_len = unpack("v",fread($fp, 2)); # filename length + $extra_field_len = unpack("v",fread($fp, 2)); # extra field length + $file_comment_len = unpack("v",fread($fp, 2)); # file comment length + + $dir['disk_number_start'] = unpack("v",fread($fp, 2)); # disk number start + $dir['internal_attributes'] = unpack("v",fread($fp, 2)); # internal file attributes-byte1 + $dir['external_attributes1'] = unpack("v",fread($fp, 2)); # external file attributes-byte2 + $dir['external_attributes2'] = unpack("v",fread($fp, 2)); # external file attributes + $dir['relative_offset'] = unpack("V",fread($fp, 4)); # relative offset of local header + $dir['file_name'] = $this->cleanFileName(fread($fp, $file_name_len[1])); # filename + $dir['extra_field'] = $extra_field_len[1] ? fread($fp, $extra_field_len[1]) : ''; # extra field + $dir['file_comment'] = $file_comment_len[1] ? fread($fp, $file_comment_len[1]) : ''; # file comment + + $dir_list[$dir['file_name']] = array( + 'version_madeby' => $dir['version_madeby'][1], + 'version_needed' => $dir['version_needed'][1], + 'general_bit_flag' => str_pad(decbin($dir['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), + 'compression_method' => $dir['compression_method'][1], + 'lastmod_datetime' => $this->getTimeStamp($dir['lastmod_date'][1],$dir['lastmod_time'][1]), + 'crc-32' => str_pad(dechex(ord($dir['crc-32'][3])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($dir['crc-32'][2])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($dir['crc-32'][1])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($dir['crc-32'][0])), 2, '0', STR_PAD_LEFT), + 'compressed_size' => $dir['compressed_size'][1], + 'uncompressed_size' => $dir['uncompressed_size'][1], + 'disk_number_start' => $dir['disk_number_start'][1], + 'internal_attributes' => $dir['internal_attributes'][1], + 'external_attributes1' => $dir['external_attributes1'][1], + 'external_attributes2' => $dir['external_attributes2'][1], + 'relative_offset' => $dir['relative_offset'][1], + 'file_name' => $dir['file_name'], + 'extra_field' => $dir['extra_field'], + 'file_comment' => $dir['file_comment'] + ); + $signature = fread($fp, 4); + } + + foreach ($dir_list as $k => $v) + { + if ($exclude && preg_match($exclude,$k)) { + continue; + } + + $i = $this->getFileHeaderInformation($v['relative_offset']); + + $this->compressed_list[$k]['file_name'] = $k; + $this->compressed_list[$k]['is_dir'] = $v['external_attributes1'] == 16 || substr($k,-1,1) == '/'; + $this->compressed_list[$k]['compression_method'] = $v['compression_method']; + $this->compressed_list[$k]['version_needed'] = $v['version_needed']; + $this->compressed_list[$k]['lastmod_datetime'] = $v['lastmod_datetime']; + $this->compressed_list[$k]['crc-32'] = $v['crc-32']; + $this->compressed_list[$k]['compressed_size'] = $v['compressed_size']; + $this->compressed_list[$k]['uncompressed_size'] = $v['uncompressed_size']; + $this->compressed_list[$k]['lastmod_datetime'] = $v['lastmod_datetime']; + $this->compressed_list[$k]['extra_field'] = $i['extra_field']; + $this->compressed_list[$k]['contents_start_offset'] = $i['contents_start_offset']; + + if(strtolower($stop_on_file) == strtolower($k)) { + break; + } + } + return true; + } + } + return false; + } + + protected function loadFileListBySignatures($stop_on_file=false,$exclude=false) + { + $fp = $this->fp(); + fseek($fp,0); + + $return = false; + while(true) + { + $details = $this->getFileHeaderInformation(); + if (!$details) { + fseek($fp,12-4,SEEK_CUR); # 12: Data descriptor - 4: Signature (that will be read again) + $details = $this->getFileHeaderInformation(); + } + if (!$details) { + break; + } + $filename = $details['file_name']; + + if ($exclude && preg_match($exclude,$filename)) { + continue; + } + + $this->compressed_list[$filename] = $details; + $return = true; + + if (strtolower($stop_on_file) == strtolower($filename)) { + break; + } + } + + return $return; + } + + protected function getFileHeaderInformation($start_offset=false) + { + $fp = $this->fp(); + + if ($start_offset !== false) { + fseek($fp,$start_offset); + } + + $signature = fread($fp, 4); + if ($signature == $this->zip_sig) + { + # Get information about the zipped file + $file = array(); + $file['version_needed'] = unpack("v",fread($fp, 2)); # version needed to extract + $file['general_bit_flag'] = unpack("v",fread($fp, 2)); # general purpose bit flag + $file['compression_method'] = unpack("v",fread($fp, 2)); # compression method + $file['lastmod_time'] = unpack("v",fread($fp, 2)); # last mod file time + $file['lastmod_date'] = unpack("v",fread($fp, 2)); # last mod file date + $file['crc-32'] = fread($fp,4); # crc-32 + $file['compressed_size'] = unpack("V",fread($fp, 4)); # compressed size + $file['uncompressed_size'] = unpack("V",fread($fp, 4)); # uncompressed size + + $file_name_len = unpack("v",fread($fp, 2)); # filename length + $extra_field_len = unpack("v",fread($fp, 2)); # extra field length + + $file['file_name'] = $this->cleanFileName(fread($fp,$file_name_len[1])); # filename + $file['extra_field'] = $extra_field_len[1] ? fread($fp, $extra_field_len[1]) : ''; # extra field + $file['contents_start_offset'] = ftell($fp); + + # Look for the next file + fseek($fp, $file['compressed_size'][1], SEEK_CUR); + + # Mount file table + $i = array( + 'file_name' => $file['file_name'], + 'is_dir' => substr($file['file_name'],-1,1) == '/', + 'compression_method' => $file['compression_method'][1], + 'version_needed' => $file['version_needed'][1], + 'lastmod_datetime' => $this->getTimeStamp($file['lastmod_date'][1],$file['lastmod_time'][1]), + 'crc-32' => str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT), + 'compressed_size' => $file['compressed_size'][1], + 'uncompressed_size' => $file['uncompressed_size'][1], + 'extra_field' => $file['extra_field'], + 'general_bit_flag' => str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), + 'contents_start_offset'=>$file['contents_start_offset'] + ); + return $i; + } + return false; + } + + protected function getTimeStamp($date,$time) + { + $BINlastmod_date = str_pad(decbin($date), 16, '0', STR_PAD_LEFT); + $BINlastmod_time = str_pad(decbin($time), 16, '0', STR_PAD_LEFT); + $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980; + $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4)); + $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5)); + $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5)); + $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6)); + $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)) * 2; + + return mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY); + } + + protected function cleanFileName($n) + { + $n = str_replace('../','',$n); + $n = preg_replace('#^/+#','',$n); + return $n; + } + + protected function memoryAllocate($size) + { + $mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000; + $mem_limit = @ini_get('memory_limit'); + if ($mem_used && $mem_limit) + { + $mem_limit = files::str2bytes($mem_limit); + $mem_avail = $mem_limit-$mem_used-(512*1024); + $mem_needed = $size; + + if ($mem_needed > $mem_avail) + { + if (@ini_set('memory_limit',$mem_limit+$mem_needed+$mem_used) === false) { + throw new Exception(__('Not enough memory to open file.')); + } + + if (!$this->memory_limit) { + $this->memory_limit = $mem_limit; + } + } + } + } +} +?> \ No newline at end of file diff --git a/inc/clearbricks/zip/class.zip.php b/inc/clearbricks/zip/class.zip.php new file mode 100644 index 0000000..f62c419 --- /dev/null +++ b/inc/clearbricks/zip/class.zip.php @@ -0,0 +1,370 @@ +fp = $out_fp; + } + + public function __destruct() + { + $this->close(); + } + + public function close() + { + if ($this->memory_limit) { + ini_set('memory_limit',$this->memory_limit); + } + } + + public function addExclusion($reg) + { + $this->exclusions[] = $reg; + } + + public function addFile($file,$name=null) + { + $file = preg_replace('#[\\\/]+#','/',$file); + + if (!$name) { + $name = $file; + } + $name = $this->formatName($name); + + if ($this->isExcluded($name)) { + return; + } + + if (!file_exists($file) || !is_file($file)) { + throw new Exception(__('File does not exist')); + } + if (!is_readable($file)) { + throw new Exception(__('Cannot read file')); + } + + $info = stat($file); + + $this->entries[$name] = array( + 'file' => $file, + 'is_dir' => false, + 'mtime' => $info['mtime'], + 'size' => $info['size'] + ); + } + + public function addDirectory($dir,$name=null,$recursive=false) + { + $dir = preg_replace('#[\\\/]+#','/',$dir); + if (substr($dir,-1-1) != '/') { + $dir .= '/'; + } + + if (!$name && $name !== '') { + $name = $dir; + } + + if ($this->isExcluded($name)) { + return; + } + + if ($name !== '') + { + if (substr($name,-1,1) != '/') { + $name .= '/'; + } + + $name = $this->formatName($name); + + if ($name !== '') + { + $this->entries[$name] = array( + 'file' => null, + 'is_dir' => true, + 'mtime' => time(), + 'size' => 0 + ); + } + } + + if ($recursive) + { + if (!is_dir($dir)) { + throw new Exception(__('Directory does not exist')); + } + if (!is_readable($dir)) { + throw new Exception(__('Cannot read directory')); + } + + $D = dir($dir); + while (($e = $D->read()) !== false) { + if ($e == '.' || $e == '..') { + continue; + } + + if (is_dir($dir.'/'.$e)) { + $this->addDirectory($dir.$e,$name.$e,true); + } elseif (is_file($dir.'/'.$e)) { + $this->addFile($dir.$e,$name.$e); + } + } + } + } + + public function write() + { + foreach ($this->entries as $name => $v) + { + if ($v['is_dir']) { + $this->writeDirectory($name); + } else { + $this->writeFile($name,$v['file'],$v['size'],$v['mtime']); + } + } + + $ctrldir = implode('',$this->ctrl_dir); + + fwrite($this->fp, + $ctrldir. + $this->eof_ctrl_dir. + pack('v',sizeof($this->ctrl_dir)). # total # of entries "on this disk" + pack('v',sizeof($this->ctrl_dir)). # total # of entries overall + pack('V',strlen($ctrldir)). # size of central dir + pack('V',$this->old_offset). # offset to start of central dir + "\x00\x00" # .zip file comment length + ); + } + + protected function writeDirectory($name) + { + if (!isset($this->entries[$name])) { + return; + } + + $mdate = $this->makeDate(time()); + $mtime = $this->makeTime(time()); + + # Data descriptor + $data_desc = + "\x50\x4b\x03\x04". + "\x0a\x00". # ver needed to extract + "\x00\x00". # gen purpose bit flag + "\x00\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',0). # crc32 + pack('V',0). # compressed filesize + pack('V',0). # uncompressed filesize + pack('v',strlen($name)). # length of pathname + pack('v',0). # extra field length + $name. # end of "local file header" segment + pack('V',0). # crc32 + pack('V',0). # compressed filesize + pack('V',0); # uncompressed filesize + + $new_offset = $this->old_offset + strlen($data_desc); + fwrite($this->fp,$data_desc); + + # Add to central record + $cdrec = + "\x50\x4b\x01\x02". + "\x00\x00". # version made by + "\x0a\x00". # version needed to extract + "\x00\x00". # gen purpose bit flag + "\x00\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',0). # crc32 + pack('V',0). # compressed filesize + pack('V',0). # uncompressed filesize + pack('v',strlen($name)). # length of filename + pack('v',0). # extra field length + pack('v',0). # file comment length + pack('v',0). # disk number start + pack('v',0). # internal file attributes + pack('V',16). # external file attributes - 'directory' bit set + pack('V',$this->old_offset). # relative offset of local header + $name; + + $this->old_offset = $new_offset; + $this->ctrl_dir[] = $cdrec; + } + + protected function writeFile($name,$file,$size,$mtime) + { + if (!isset($this->entries[$name])) { + return; + } + + $size = filesize($file); + $this->memoryAllocate($size*3); + + $content = file_get_contents($file); + + $unc_len = strlen($content); + $crc = crc32($content); + $zdata = gzdeflate($content); + $c_len = strlen($zdata); + + unset($content); + + $mdate = $this->makeDate($mtime); + $mtime = $this->makeTime($mtime); + + # Data descriptor + $data_desc = + "\x50\x4b\x03\x04". + "\x14\x00". # ver needed to extract + "\x00\x00". # gen purpose bit flag + "\x08\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',$crc). # crc32 + pack('V',$c_len). # compressed filesize + pack('V',$unc_len). # uncompressed filesize + pack('v',strlen($name)). # length of filename + pack('v',0). # extra field length + $name. # end of "local file header" segment + $zdata. # "file data" segment + pack('V',$crc). # crc32 + pack('V',$c_len). # compressed filesize + pack('V',$unc_len); # uncompressed filesize + + fwrite($this->fp,$data_desc); + unset($zdata); + + $new_offset = $this->old_offset + strlen($data_desc); + + # Add to central directory record + $cdrec = + "\x50\x4b\x01\x02". + "\x00\x00". # version made by + "\x14\x00". # version needed to extract + "\x00\x00". # gen purpose bit flag + "\x08\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',$crc). # crc32 + pack('V',$c_len). # compressed filesize + pack('V',$unc_len). # uncompressed filesize + pack('v',strlen($name)). # length of filename + pack('v',0). # extra field length + pack('v',0). # file comment length + pack('v',0). # disk number start + pack('v',0). # internal file attributes + pack('V',32). # external file attributes - 'archive' bit set + pack('V',$this->old_offset). # relative offset of local header + $name; + + $this->old_offset = $new_offset; + $this->ctrl_dir[] = $cdrec; + } + + protected function formatName($name) + { + if (substr($name,0,1) == '/') { + $name = substr($name,1); + } + + return $name; + } + + protected function isExcluded($name) + { + foreach ($this->exclusions as $reg) { + if (preg_match($reg,$name)) { + return true; + } + } + + return false; + } + + protected function makeDate($ts) + { + $year = date('Y',$ts)-1980; + if ($year < 0) { + $year = 0; + } + + $year = sprintf('%07b',$year); + $month = sprintf('%04b',date('n',$ts)); + $day = sprintf('%05b',date('j',$ts)); + + return bindec($year.$month.$day); + } + + protected function makeTime($ts) + { + $hour = sprintf('%05b',date('G',$ts)); + $minute = sprintf('%06b',date('i',$ts)); + $second = sprintf('%05b',ceil(date('s',$ts)/2)); + + return bindec($hour.$minute.$second); + } + + protected function memoryAllocate($size) + { + $mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000; + $mem_limit = @ini_get('memory_limit'); + if ($mem_used && $mem_limit) + { + $mem_limit = files::str2bytes($mem_limit); + $mem_avail = $mem_limit-$mem_used-(512*1024); + $mem_needed = $size; + + if ($mem_needed > $mem_avail) + { + if (@ini_set('memory_limit',$mem_limit+$mem_needed+$mem_used) === false) { + throw new Exception(__('Not enough memory to open file.')); + } + + if (!$this->memory_limit) { + $this->memory_limit = $mem_limit; + } + } + } + } +} +?> \ No newline at end of file diff --git a/inc/config.php b/inc/config.php new file mode 100644 index 0000000..16f2adb --- /dev/null +++ b/inc/config.php @@ -0,0 +1,67 @@ + \ No newline at end of file diff --git a/inc/config.php.in b/inc/config.php.in new file mode 100644 index 0000000..ce888b3 --- /dev/null +++ b/inc/config.php.in @@ -0,0 +1,67 @@ + \ No newline at end of file diff --git a/inc/core/class.dc.auth.php b/inc/core/class.dc.auth.php new file mode 100644 index 0000000..c19b7a2 --- /dev/null +++ b/inc/core/class.dc.auth.php @@ -0,0 +1,593 @@ +dcCore dcCore instance + protected $con; ///< connection Database connection object + + protected $user_table; ///< string User table name + protected $perm_table; ///< string Perm table name + + protected $user_id; ///< string Current user ID + protected $user_info = array(); ///< array Array with user information + protected $user_options = array(); ///< a Array with user options + protected $user_admin; ///< boolean User is super admin + protected $permissions = array(); ///< array Permissions for each blog + protected $allow_pass_change = true; ///< boolean User can change its password + protected $blogs = array(); ///< array List of blogs on which the user has permissions + public $blog_count = null; ///< integer Count of user blogs + + protected $perm_types; ///< array Permission types + + /** + Class constructor. Takes dcCore object as single argument. + + @param core dcCore dcCore object + */ + public function __construct(&$core) + { + $this->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 = array( + '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 user_id string User ID + @param pwd string User password + @param user_key string User key check + @return boolean + */ + public function checkUser($user_id, $pwd=null, $user_key=null) + { + # Check user and password + $strReq = 'SELECT user_id, user_super, user_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)."' "; + + $rs = $this->con->select($strReq); + + if ($rs->isEmpty()) { + return false; + } + + $rs->extend('rsExtUser'); + + if ($pwd != '') + { + if (crypt::hmac(DC_MASTER_KEY,$pwd) != $rs->user_pwd) { + sleep(rand(2,5)); + return false; + } + } + elseif ($user_key != '') + { + if (http::browserUID(DC_MASTER_KEY.$rs->user_id.$rs->user_pwd) != $user_key) { + return false; + } + } + + $this->user_id = $rs->user_id; + $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()); + + # Get permissions on blogs + if ($this->findUserBlog() === false) { + return false; + } + return true; + } + + /** + This method only check current user password. + + @param pwd string User password + @return boolean + */ + public function checkPassword($pwd) + { + if (!empty($this->user_info['user_pwd'])) { + return $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 ? $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 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 permissions string Permissions list + @param blog_id string Blog ID + @return boolean + */ + public function check($permissions,$blog_id) + { + if ($this->user_admin) { + return true; + } + + $p = 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*',crypt::hmac(DC_MASTER_KEY,$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('H40hex',substr($code,32,40)); + + 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 (crypt::hmac(DC_MASTER_KEY,$rs->user_pwd) != $pwd) { + return false; + } + + return $rs->user_id; + } + //@} + + + /// @name Sudo + //@{ + /** + Calls $f function with super admin rights. + + @param f callback Callback function + @return mixed Function result + */ + public function sudo($f) + { + if (!is_callable($f)) { + throw new Exception($f.' function doest not exist'); + } + + $args = func_get_args(); + array_shift($args); + + 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 + - ... + + @return array + */ + public function getPermissions($blog_id) + { + if (isset($this->blogs[$blog_id])) { + return $this->blogs[$blog_id]; + } + + if ($this->blog_count === null) { + $this->blog_count = $this->core->getBlogs(array(),true)->f(0); + } + + 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 : array('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 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 blog_id '. + 'FROM '.$this->perm_table.' '. + "WHERE user_id = '".$this->con->escape($this->user_id)."' ". + "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ". + 'ORDER BY 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 n string Information name + @return string Information value + */ + public function getInfo($n) + { + if (isset($this->user_info[$n])) { + return $this->user_info[$n]; + } + + return null; + } + + /** + Returns a specific user option + + @param n string Option name + @return string Option value + */ + public function getOption($n) + { + if (isset($this->user_options[$n])) { + return $this->user_options[$n]; + } + return null; + } + + /** + 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 level string Permissions string + @return array + */ + public function parsePermissions($level) + { + $level = preg_replace('/^\|/','',$level); + $level = preg_replace('/\|$/','',$level); + + $res = array(); + 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 name string Permission name + @param title string 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 user_id string User ID + @param user_email string User Email + @return string Recover key + */ + 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()); + + $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 recover_key string 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 = crypt::hmac(DC_MASTER_KEY,$new_pass); + $cur->user_recover_key = null; + + $cur->update("WHERE user_recover_key = '".$this->con->escape($recover_key)."'"); + + return array('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 cur cursor User cursor + */ + public function afterAddUser(&$cur) {} + + /** + Called after core->updUser + @see dcCore::updUser + @param id string User ID + @param cur cursor User cursor + */ + public function afterUpdUser($id,&$cur) {} + + /** + Called after core->delUser + @see dcCore::delUser + @param id string User ID + */ + public function afterDelUser($id) {} + //@} +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.blog.php b/inc/core/class.dc.blog.php new file mode 100644 index 0000000..60a55d4 --- /dev/null +++ b/inc/core/class.dc.blog.php @@ -0,0 +1,2006 @@ +dcCore dcCore instance + public $con; ///< connection Database connection object + public $prefix; ///< string Database table prefix + + public $id; ///< string Blog ID + public $uid; ///< string Blog unique ID + public $name; ///< string Blog name + public $desc; ///< string Blog description + public $url; ///< string Blog URL + public $host; ///< string Blog host + public $creadt; ///< string Blog creation date + public $upddt; ///< string Blog last update date + public $status; ///< string Blog status + + public $settings; ///< dcSettings dcSettings object + public $themes_path; ///< string Blog theme path + public $public_path; ///< string Blog public path + + private $post_status = array(); + private $comment_status = array(); + + private $categories; + + public $without_password = true; ///< boolean Disallow entries password protection + + /** + Inits dcBlog object + + @param core 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 = preg_replace('|^([a-z]{3,}://)(.*?)/.*$|','$1$2',$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->themes_path,DC_ROOT); + $this->public_path = path::fullFromRoot($this->settings->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 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) + { + $id = (integer) $id; + + $strReq = 'SELECT post_id, comment_trackback '. + 'FROM '.$this->prefix.'comment '. + 'WHERE comment_id = '.$id.' '; + + $rs = $this->con->select($strReq); + + $post_id = $rs->post_id; + $tb = (boolean) $rs->comment_trackback; + + $strReq = 'SELECT COUNT(post_id) '. + 'FROM '.$this->prefix.'comment '. + 'WHERE post_id = '.(integer) $post_id.' '. + 'AND comment_trackback = '.(integer) $tb.' '. + 'AND comment_status = 1 '; + + if ($del) { + $strReq .= 'AND comment_id <> '.$id.' '; + } + + $rs = $this->con->select($strReq); + + $cur = $this->con->openCursor($this->prefix.'post'); + + if ($rs->isEmpty()) { + return; + } + + if ($tb) { + $cur->nb_trackback = (integer) $rs->f(0); + } else { + $cur->nb_comment = (integer) $rs->f(0); + } + + $cur->update('WHERE post_id = '.(integer) $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=array()) + { + $c_params = array(); + if (isset($params['post_type'])) { + $c_params['post_type'] = $params['post_type']; + unset($params['post_type']); + } + $counter = $this->getCategoriesCounter($c_params); + + $without_empty = $this->core->auth->userID() == false; # For public 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 = array(); + $stack = array(); + $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 = array(); + 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 (!empty($params['cat_id'])) + { + $found = false; + foreach ($data as $v) { + if ($v['cat_id'] == $params['cat_id']) { + $found = true; + $data = array($v); + break; + } + } + if (!$found) { + $data = array(); + } + } + + if (!empty($params['cat_url']) && empty($params['cat_id'])) + { + $found = false; + foreach ($data as $v) { + if ($v['cat_url'] == $params['cat_url']) { + $found = true; + $data = array($v); + break; + } + } + if (!$found) { + $data = array(); + } + } + + return staticRecord::newFromArray($data); + } + + /** + Retrieves a category by its ID. + + @param id integer Category ID + @return record + */ + public function getCategory($id) + { + return $this->getCategories(array('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(array('start' => $id,'level' => $id == 0 ? 1 : 2)); + } + + private function getCategoriesCounter($params=array()) + { + $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 post_type = '".$this->con->escape($params['post_type'])."' "; + } + + $strReq .= 'GROUP BY C.cat_id '; + + $rs = $this->con->select($strReq); + $counters = array(); + 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 = array(); + if ($parent != 0) + { + $rs = $this->getCategory($parent); + if ($rs->isEmpty()) { + $url = array(); + } 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; + + $this->categories()->addNode($cur,$parent); + $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 = array(); + $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); + + $cur->update( + 'WHERE cat_id = '.(integer) $id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' "); + $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 delete categories')); + } + + $this->categories()->resetOrder(); + } + + private function checkCategory($title,$url,$id=null) + { + $strReq = 'SELECT cat_id '. + 'FROM '.$this->prefix.'category '. + "WHERE cat_url = '".$this->con->escape($url)."' ". + "AND blog_id = '".$this->con->escape($this->id)."' "; + + if ($id !== null) { + $strReq .= 'AND cat_id <> '.(integer) $id.' '; + } + + $rs = $this->con->select($strReq); + + if (!$rs->isEmpty()) { + throw new Exception(__('Category URL must be unique.')); + } + } + + 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 + $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) 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 + + 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 + */ + public function getPosts($params=array(),$count_only=false) + { + if ($count_only) + { + $strReq = 'SELECT count(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_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']) && !empty($params['post_type'])) { + $strReq .= 'AND post_type '.$this->con->in($params['post_type']); + } elseif ($params['post_type'] != '') { + $strReq .= "AND post_type = '".$this->con->escape($params['post_type'])."' "; + } + } + else + { + $strReq .= "AND post_type = 'post' "; + } + + if (!empty($params['post_id'])) { + if (is_array($params['post_id'])) { + array_walk($params['post_id'],create_function('&$v,$k','if($v!==null){$v=(integer)$v;}')); + } else { + $params['post_id'] = array((integer) $params['post_id']); + } + $strReq .= 'AND P.post_id '.$this->con->in($params['post_id']); + } + + if (!empty($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 (!empty($params['cat_id'])) + { + if (!is_array($params['cat_id'])) { + $params['cat_id'] = array($params['cat_id']); + } + if (!empty($params['cat_id_not'])) { + array_walk($params['cat_id'],create_function('&$v,$k','$v=$v." ?not";')); + } + $strReq .= 'AND '.$this->getPostsCategoryFilter($params['cat_id'],'cat_id').' '; + } + elseif (!empty($params['cat_url'])) + { + if (!is_array($params['cat_url'])) { + $params['cat_url'] = array($params['cat_url']); + } + if (!empty($params['cat_url_not'])) { + array_walk($params['cat_url'],create_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_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,array(&$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 (!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']); + } + + $rs = $this->con->select($strReq); + $rs->core = $this->core; + $rs->_nb_media = array(); + $rs->extend('rsExtPost'); + + # --BEHAVIOR-- coreBlogGetPosts + $this->core->callBehavior('coreBlogGetPosts',$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 null; + } + + 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=array()) + { + $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=array()) + { + $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 (!empty($params['cat_id'])) { + $catReq = 'AND P.cat_id = '.(integer) $params['cat_id'].' '; + $cat_field = ', C.cat_url '; + } elseif (!empty($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->escape($params['post_type'])."' "; + } + + 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; + } + + $cur->insert(); + $this->con->unlock(); + } + catch (Exception $e) + { + $this->con->unlock(); + throw $e; + } + + $this->triggerBlog(); + + 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')); + } + } + + $cur->update('WHERE post_id = '.$id.' '); + $this->triggerBlog(); + } + + /** + Updates post status. + + @param id integer Post ID + @param status integer Post status + */ + public function updPostStatus($id,$status) + { + if (!$this->core->auth->check('publish,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to change this entry status')); + } + + $id = (integer) $id; + $status = (integer) $status; + + #If user can only publish, 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 blog_id = '".$this->con->escape($this->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')); + } + } + + $cur = $this->con->openCursor($this->prefix.'post'); + + $cur->post_status = $status; + $cur->post_upddt = date('Y-m-d H:i:s'); + + $cur->update( + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' " + ); + $this->triggerBlog(); + } + + public function updPostSelected($id,$selected) + { + if (!$this->core->auth->check('usage,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to change this entry category')); + } + + $id = (integer) $id; + $selected = (boolean) $selected; + + # 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 blog_id = '".$this->con->escape($this->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 mark this entry as selected')); + } + } + + $cur = $this->con->openCursor($this->prefix.'post'); + + $cur->post_selected = (integer) $selected; + $cur->post_upddt = date('Y-m-d H:i:s'); + + $cur->update( + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' " + ); + $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) + { + if (!$this->core->auth->check('usage,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to change this entry category')); + } + + $id = (integer) $id; + $cat_id = (integer) $cat_id; + + # 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 blog_id = '".$this->con->escape($this->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 category')); + } + } + + $cur = $this->con->openCursor($this->prefix.'post'); + + $cur->cat_id = ($cat_id ? $cat_id : null); + $cur->post_upddt = date('Y-m-d H:i:s'); + + $cur->update( + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' " + ); + $this->triggerBlog(); + } + + /** + Deletes a post. + + @param id integer Post ID + */ + public function delPost($id) + { + if (!$this->core->auth->check('delete,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to delete entries')); + } + + $id = (integer) $id; + + if (empty($id)) { + throw new Exception(__('No such entry ID')); + } + + #If user can only delete, 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 blog_id = '".$this->con->escape($this->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 delete this entry')); + } + } + + + $strReq = 'DELETE FROM '.$this->prefix.'post '. + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' "; + + $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 = array(); + + 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 (!empty($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($to_change).' '; + + $this->con->execute($strReq); + $this->triggerBlog(); + } + } + + /** + 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 = array(); + $not = array(); + $queries = array(); + + 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') { + $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 = array( + 0 => array(), # wanted categories + 1 => array() # 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)); + } + } + + 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); + 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',array( + '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 = array( + '{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->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()) + { + $strReq = 'SELECT post_url FROM '.$this->prefix.'post '. + "WHERE post_url LIKE '".$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); + $a = array(); + 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) Get comment with given ID + - 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 + + @param params array Parameters + @param count_only boolean Only counts results + @return record A record with some more capabilities + */ + public function getComments($params=array(),$count_only=false) + { + if ($count_only) + { + $strReq = 'SELECT count(comment_id) '; + } + else + { + if (!empty($params['no_content'])) { + $content_req = ''; + } else { + $content_req = 'comment_content, '; + } + + $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'])) + { + if (is_array($params['post_type']) && !empty($params['post_type'])) { + $strReq .= 'AND post_type '.$this->con->in($params['post_type']); + } else { + $strReq .= "AND post_type = '".$this->con->escape($params['post_type'])."' "; + } + } + + if (!empty($params['post_id'])) { + $strReq .= 'AND P.post_id = '.(integer) $params['post_id'].' '; + } + + if (!empty($params['cat_id'])) { + $strReq .= 'AND P.cat_id = '.(integer) $params['cat_id'].' '; + } + + if (!empty($params['comment_id'])) { + $strReq .= 'AND comment_id = '.(integer) $params['comment_id'].' '; + } + + 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'])) { + $strReq .= "AND comment_ip = '".$this->con->escape($params['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,array(&$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']); + } + + $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->blog_timezone); + $cur->comment_dt = date('Y-m-d H:i:s',time() + $offset); + $cur->comment_tz = $this->settings->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(array('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) + { + if (!$this->core->auth->check('publish,contentadmin',$this->id)) { + throw new Exception(__("You are not allowed to change this comment's status")); + } + + $cur = $this->con->openCursor($this->prefix.'comment'); + $cur->comment_status = (integer) $status; + $this->updComment($id,$cur); + } + + /** + Delete a comment + + @param id integer Comment ID + */ + public function delComment($id) + { + if (!$this->core->auth->check('delete,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to delete comments')); + } + + $id = (integer) $id; + + if (empty($id)) { + throw new Exception(__('No such comment ID')); + } + + #If user can only delete, we need to check the post's owner + if (!$this->core->auth->check('contentadmin',$this->id)) + { + $strReq = 'SELECT P.post_id '. + 'FROM '.$this->prefix.'post P, '.$this->prefix.'comment C '. + 'WHERE P.post_id = C.post_id '. + "AND P.blog_id = '".$this->con->escape($this->id)."' ". + 'AND comment_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 delete this comment')); + } + } + + $strReq = 'DELETE FROM '.$this->prefix.'comment '. + 'WHERE comment_id = '.$id.' '; + + $this->triggerComment($id,true); + $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?)://|',$cur->comment_site)) { + $cur->comment_site = 'http://'.$cur->comment_site; + } + } + + if ($cur->comment_status === null) { + $cur->comment_status = (integer) $this->settings->comments_pub; + } + + # Words list + if ($cur->comment_content !== null) + { + $cur->comment_words = implode(' ',text::splitWords($cur->comment_content)); + } + } + //@} +} +?> diff --git a/inc/core/class.dc.categories.php b/inc/core/class.dc.categories.php new file mode 100644 index 0000000..9263da0 --- /dev/null +++ b/inc/core/class.dc.categories.php @@ -0,0 +1,483 @@ +core =& $core; + $this->con =& $core->con; + $this->blog_id = $core->blog->id; + $this->table = $core->prefix.'category'; + $this->add_condition = array('blog_id' => "'".$this->con->escape($this->blog_id)."'"); + } + + public function getChildren($start=0,$id=null,$sort='asc',$fields=array()) + { + $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields); + return parent::getChildren($start,$id,$sort,$fields); + } + + public function getParents($id,$fields=array()) + { + $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields); + return parent::getParents($id,$fields); + } + + public function getParent($id,$fields=array()) + { + $fields = array_merge(array('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 = array(); + + protected $parents; + + public function __construct(&$con) + { + $this->con =& $con; + } + + public function getChildren($start=0,$id=null,$sort='asc',$fields=array()) + { + $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=array()) + { + $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=array()) + { + $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 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 = array(); + foreach ($this->add_condition as $c => $n) { + $w[] = $prefix.$c.' = '.$n; + } + return ' '.$start.' '.implode(' AND ',$w).' '; + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.core.php b/inc/core/class.dc.core.php new file mode 100644 index 0000000..778b3cd --- /dev/null +++ b/inc/core/class.dc.core.php @@ -0,0 +1,1461 @@ +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 $rest; ///< dcRestServer dcRestServer object + + private $versions = null; + private $formaters = array(); + private $behaviors = array(); + private $post_types = array(); + + /** + 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) + { + $this->con = dbLayer::init($driver,$host,$db,$user,$password,$persist); + + # define weak_locks for mysql + if ($this->con instanceof mysqlConnection) { + mysqlConnection::$weak_locks = true; + } + + $this->prefix = $prefix; + + $this->error = new dcError(); + $this->auth = $this->authInstance(); + $this->session = new sessionDB($this->con,$this->prefix.'session',DC_SESSION_NAME,'',null,DC_ADMIN_SSL); + $this->url = new urlHandler(); + + $this->plugins = new dcModules($this); + + $this->rest = new dcRestServer($this); + + $this->addFormater('xhtml', create_function('$s','return $s;')); + $this->addFormater('wiki', array($this,'wikiTransform')); + } + + 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') + { + $r = new ReflectionClass($c); + $p = $r->getParentClass(); + + if (!$p || $p->name != '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 array( + 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 crypt::hmac(DC_MASTER_KEY,session_id()); + } + + public function checkNonce($secret) + { + if (!preg_match('/^([0-9a-f]{40,})$/i',$secret)) { + return false; + } + + return $secret == crypt::hmac(DC_MASTER_KEY,session_id()); + } + + public function formNonce() + { + if (!session_id()) { + return; + } + + return form::hidden(array('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 name string Formater name + @param func callback Function to use, must be a valid and callable callback + */ + public function addFormater($name,$func) + { + if (is_callable($func)) { + $this->formaters[$name] = $func; + } + } + + /** + Returns formaters list. + + @return array An array of formaters names in values. + */ + public function getFormaters() + { + return array_keys($this->formaters); + } + + /** + 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) + { + if (isset($this->formaters[$name])) { + return call_user_func($this->formaters[$name],$str); + } + + return $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 null; + + if ($behavior == '') { + return $this->behaviors; + } elseif (isset($this->behaviors[$behavior])) { + return $this->behaviors[$behavior]; + } + + return array(); + } + + /** + 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) + { + if (isset($this->behaviors[$behavior])) + { + $args = func_get_args(); + array_shift($args); + + $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) + { + $this->post_types[$type] = array( + 'admin_url' => $admin_url, + 'public_url' => $public_url + ); + } + + 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 null; + } + } + + /** + 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=array(),$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_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_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) { + $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 = array('NOW()'); + } + + $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 = array(); + + while ($rs->fetch()) + { + $res[$rs->blog_id] = array( + '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 = crypt::hmac(DC_MASTER_KEY,$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 = array('NOW()'); + } + + 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 array( + 'edit_size' => 24, + 'enable_wysiwyg' => true, + '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, 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, NULL AS permissions ". + 'FROM '.$this->prefix.'user U '. + 'WHERE user_super = 1 '; + } + + $rs = $this->con->select($strReq); + + $res = array(); + + while ($rs->fetch()) + { + $res[$rs->user_id] = array( + 'name' => $rs->user_name, + 'firstname' => $rs->user_firstname, + 'displayname' => $rs->user_displayname, + '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(array('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=array(),$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 = 1 '; + } + + if (!empty($params['blog_id'])) { + $where .= "AND B.blog_id = '".$this->con->escape($params['blog_id'])."' "; + } + + if (!empty($params['q'])) { + $params['q'] = 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)) { + throw new Exception(__('Blog ID must contain at least 2 characters using letters, numbers or symbols.')); + } + + if ($cur->blog_name !== null && $cur->blog_name == '') { + throw new Exception(__('No blog name')); + } + + if ($cur->blog_url !== null && $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->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(array( + 'active_title' => 1, + 'active_setext_title' => 0, + 'active_hr' => 1, + 'active_lists' => 1, + 'active_quote' => 1, + 'active_pre' => 1, + 'active_empty' => 1, + 'active_auto_br' => 0, + 'active_auto_urls' => 0, + '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, + 'parse_pre' => 1, + 'active_fr_syntax' => 0, + 'first_title_level' => 3, + 'note_prefix' => 'wiki-footnote', + 'note_str' => '

    Notes

    %s
    ' + )); + + $this->wiki2xhtml->registerFunction('url:post',array($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(array( + 'active_title' => 0, + 'active_setext_title' => 0, + 'active_hr' => 0, + 'active_lists' => 0, + 'active_quote' => 0, + 'active_pre' => 0, + 'active_empty' => 0, + 'active_auto_br' => 1, + 'active_auto_urls' => 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_footnotes' => 0, + 'active_wikiwords' => 0, + 'active_macros' => 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(array( + 'active_title' => 0, + 'active_setext_title' => 0, + 'active_hr' => 0, + 'active_lists' => 1, + 'active_quote' => 0, + 'active_pre' => 1, + '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_wikiwords' => 0, + 'active_macros' => 0, + '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 array(); + } + + $post_id = abs((integer) substr($url,5)); + if (!$post_id) { + return array(); + } + + $post = $this->blog->getPosts(array('post_id'=>$post_id)); + if ($post->isEmpty()) { + return array(); + } + + $res = array('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 = array( + array('allow_comments','boolean',true, + 'Allow comments on blog'), + array('allow_trackbacks','boolean',true, + 'Allow trackbacks on blog'), + array('blog_timezone','string','Europe/London', + 'Blog timezone'), + array('comments_nofollow','boolean',true, + 'Add rel="nofollow" to comments URLs'), + array('comments_pub','boolean',true, + 'Publish comments immediately'), + array('comments_ttl','integer',0, + 'Number of days to keep comments open (0 means no ttl)'), + array('copyright_notice','string','','Copyright notice (simple text)'), + array('date_format','string','%A, %B %e %Y', + 'Date format. See PHP strftime function for patterns'), + array('editor','string','', + 'Person responsible of the content'), + array('enable_html_filter','boolean',0, + 'Enable HTML filter'), + array('enable_xmlrpc','boolean',0, + 'Enable XML/RPC interface'), + array('lang','string','en', + 'Default blog language'), + array('media_exclusion','string','', + 'File name exclusion pattern in media manager. (PCRE value)'), + array('media_img_m_size','integer',448, + 'Image medium size in media manager'), + array('media_img_s_size','integer',240, + 'Image small size in media manager'), + array('media_img_t_size','integer',100, + 'Image thumbnail size in media manager'), + array('media_img_title_pattern','string','Title ;; Date(%b %Y) ;; separator(, )', + 'Pattern to set image title when you insert it in a post'), + array('nb_post_per_page','integer',20, + 'Number of entries on home page and category pages'), + array('nb_post_per_feed','integer',20, + 'Number of entries on feeds'), + array('nb_comment_per_feed','integer',20, + 'Number of comments on feeds'), + array('post_url_format','string','{y}/{m}/{d}/{t}', + 'Post URL format. {y}: year, {m}: month, {d}: day, {id}: post id, {t}: entry title'), + array('public_path','string','public', + 'Path to public directory, begins with a / for a full system path'), + array('public_url','string','/public', + 'URL to public directory'), + array('robots_policy','string','INDEX,FOLLOW', + 'Search engines robots policy'), + array('short_feed_items','boolean',false, + 'Display short feed items'), + array('theme','string','default', + 'Blog theme'), + array('themes_path','string','themes', + 'Themes root path'), + array('themes_url','string','/themes', + 'Themes root URL'), + array('time_format','string','%H:%M', + 'Time format. See PHP strftime function for patterns'), + array('tpl_allow_php','boolean',false, + 'Allow PHP code in templates'), + array('tpl_use_cache','boolean',true, + 'Use template caching'), + array('trackbacks_pub','boolean',true, + 'Publish trackbacks immediately'), + array('trackbacks_ttl','integer',0, + 'Number of days to keep trackbacks open (0 means no ttl)'), + array('url_scan','string','query_string', + 'URL handle mode (path_info or query_string)'), + array('use_smilies','boolean',false, + 'Show smilies on entries and comments'), + array('wiki_comments','boolean',false, + 'Allow commenters to use a subset of wiki syntax') + ); + } + + $settings = new dcSettings($this,null); + $settings->setNameSpace('system'); + + foreach ($defaults as $v) { + $settings->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 null; + } + 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 null; + } + return $start+$limit; + } + + /** + Reinits nb_comment and nb_trackback in post table. + */ + public function countAllComments() + { + $strReq = 'SELECT COUNT(comment_id) AS nb, post_id '. + 'FROM '.$this->prefix.'comment '. + 'WHERE comment_trackback %s 1 '. + 'AND comment_status = 1 '. + 'GROUP BY post_id '; + + $rsC = $this->con->select(sprintf($strReq,'<>')); + $rsT = $this->con->select(sprintf($strReq,'=')); + + $cur = $this->con->openCursor($this->prefix.'post'); + while ($rsC->fetch()) { + $cur->nb_comment = (integer) $rsC->nb; + $cur->update('WHERE post_id = '.(integer) $rsC->post_id); + $cur->clean(); + } + + while ($rsT->fetch()) { + $cur->nb_trackback = (integer) $rsT->nb; + $cur->update('WHERE post_id = '.(integer) $rsT->post_id); + $cur->clean(); + } + } + + /** + Empty templates cache directory + */ + public function emptyTemplatesCache() + { + if (is_dir(DC_TPL_CACHE.'/cbtpl')) { + files::deltree(DC_TPL_CACHE.'/cbtpl'); + } + } + //@} +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.error.php b/inc/core/class.dc.error.php new file mode 100644 index 0000000..2db5e02 --- /dev/null +++ b/inc/core/class.dc.error.php @@ -0,0 +1,129 @@ +array Errors stack + protected $flag = false; ///< boolean True if stack is not empty + protected $html_list = "
      \n%s
    \n"; ///< string HTML errors list pattern + protected $html_item = "
  • %s
  • \n"; ///< string HTML error item pattern + + /** + Object constructor. + */ + public function __construct() + { + $this->code = 0; + $this->msg = ''; + } + + /** + 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 msg string Error message + */ + public function add($msg) + { + $this->flag = true; + $this->errors[] = $msg; + } + + /** + Returns the value of flag property. + + @return boolean True if errors stack is not empty + */ + public function flag() + { + return $this->flag; + } + + /** + Resets errors stack. + */ + public function reset() + { + $this->flag = false; + $this->errors = array(); + } + + /** + Returns errors property. + + @return array + */ + public function getErrors() + { + return $this->errors; + } + + /** + Sets list and item properties. + + @param list string HTML errors list pattern + @param item string HTML error item pattern + */ + public function setHTMLFormat($list,$item) + { + $this->html_list = $list; + $this->html_item = $item; + } + + /** + Returns errors stack as HTML. + + @return string + */ + public function toHTML() + { + $res = ''; + + if ($this->flag) + { + foreach ($this->errors as $msg) + { + $res .= sprintf($this->html_item,$msg); + } + + $res = sprintf($this->html_list,$res); + } + + return $res; + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.media.php b/inc/core/class.dc.media.php new file mode 100644 index 0000000..1205dd5 --- /dev/null +++ b/inc/core/class.dc.media.php @@ -0,0 +1,1070 @@ +dcCore dcCore instance + protected $con; ///< connection Database connection + protected $table; ///< string Media table name + protected $table_ref; ///< string Post-media relation table name + protected $type; ///< string Media type filter + protected $file_sort = 'name-asc'; + + protected $file_handler = array(); ///< array Array of callbacks + + public $thumb_tp = '%s/.%s_%s.jpg'; ///< string Thumbnail file pattern + + /** + array Tubmnail sizes: + - m: medium image + - s: small image + - t: thumbnail image + - sq: square image + */ + public $thumb_sizes = array( + 'm' => array(448,'ratio','medium'), + 's' => array(240,'ratio','small'), + 't' => array(100,'ratio','thumbnail'), + 'sq' => array(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; + + if ($this->core->blog == null) { + throw new Exception(__('No blog defined.')); + } + + $this->table = $this->core->prefix.'media'; + $this->table_ref = $this->core->prefix.'post_media'; + $root = $this->core->blog->public_path; + + if (preg_match('#^http(s)?://#',$this->core->blog->settings->public_url)) { + $root_url = rawurldecode($this->core->blog->settings->public_url); + } else { + $root_url = rawurldecode($this->core->blog->host.path::clean($this->core->blog->settings->public_url)); + } + + if (!is_dir($root)) { + throw new Exception(sprintf(__('Directory %s does not exist.'),$root)); + } + + $this->type = $type; + + parent::__construct($root,$root_url); + $this->chdir(''); + + $this->path = $this->core->blog->settings->public_path; + + $this->addExclusion(DC_RC_PATH); + $this->addExclusion(dirname(__FILE__).'/../'); + + $this->exclude_pattern = $core->blog->settings->media_exclusion; + + # Event handlers + $this->addFileHandler('image/jpeg','create',array($this,'imageThumbCreate')); + $this->addFileHandler('image/png','create',array($this,'imageThumbCreate')); + $this->addFileHandler('image/gif','create',array($this,'imageThumbCreate')); + + $this->addFileHandler('image/png','update',array($this,'imageThumbUpdate')); + $this->addFileHandler('image/jpeg','update',array($this,'imageThumbUpdate')); + $this->addFileHandler('image/gif','update',array($this,'imageThumbUpdate')); + + $this->addFileHandler('image/png','remove',array($this,'imageThumbRemove')); + $this->addFileHandler('image/jpeg','remove',array($this,'imageThumbRemove')); + $this->addFileHandler('image/gif','remove',array($this,'imageThumbRemove')); + + $this->addFileHandler('image/jpeg','create',array($this,'imageMetaCreate')); + + # Thumbnails sizes + $this->thumb_sizes['m'][0] = abs($core->blog->settings->media_img_m_size); + $this->thumb_sizes['s'][0] = abs($core->blog->settings->media_img_s_size); + $this->thumb_sizes['t'][0] = abs($core->blog->settings->media_img_t_size); + } + + /** + 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) + { + if (!empty($this->file_handler[$type][$event])) + { + $args = func_get_args(); + array_shift($args); + array_shift($args); + + 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 + @return string HTML code + */ + public function breadCrumb($href) + { + $res = ''; + if ($this->relpwd && $this->relpwd != '.') { + $pwd = ''; + foreach (explode('/',$this->relpwd) as $v) { + $pwd .= rawurlencode($v).'/'; + $res .= ''.$v.' / '; + } + } + return $res; + + } + + protected function fileRecord(&$rs) + { + if ($rs->isEmpty()) { return null; } + + 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 null; + } + + $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-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 = array(); + $p = path::info($f->relname); + $thumb = sprintf($this->thumb_tp,$this->root.'/'.$p['dirname'],$p['base'],'%s'); + $thumb_url = sprintf($this->thumb_tp,$this->root_url.$p['dirname'],$p['base'],'%s'); + + # Cleaner URLs + $thumb_url = preg_replace('#\./#','/',$thumb_url); + $thumb_url = preg_replace('#(?thumb_sizes as $suffix => $s) { + if (file_exists(sprintf($thumb,$suffix))) { + $f->media_thumb[$suffix] = sprintf($thumb_url,$suffix); + } + } + + if (isset($f->media_thumb['sq']) && $f->media_type == 'image') { + $f->media_icon = $f->media_thumb['sq']; + } + + return $f; + } + + return null; + } + + + public function setFileSort($type='name') + { + if (in_array($type,array('name-asc','name-desc','date-asc','date-desc'))) { + $this->file_sort = $type; + } + } + + protected function sortFileHandler($a,$b) + { + 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. + + @param type string Media type filter + */ + public function getDir($type=null) + { + if ($type) { + $this->type = $type; + } + + $media_dir = $this->relpwd ? $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 = array(); + $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 = array(); + + 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) { + $f_res[] = $this->fileRecord($rs); + $f_reg[$rs->media_file] = 1; + } + } + elseif (!empty($p_dir['files']) && $this->relpwd == '') + { + # Physica 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,'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)) !== false) { + $this->dir['files'][] = $this->getFile($id); + } + } + } + } + usort($this->dir['files'],array($this,'sortFileHandler')); + } + + /** + 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); + } + + /** + 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($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 = array(); + + while ($rs->fetch()) { + $f = $this->fileRecord($rs); + if ($f !== null) { + $res[] = $f; + } + } + + return $res; + } + + /** + Attaches a media to a post. + + @param post_id integer Post ID + @param media_id integer Optionnal media ID + */ + public function addPostMedia($post_id,$media_id) + { + $post_id = (integer) $post_id; + $media_id = (integer) $media_id; + + $f = $this->getPostMedia($post_id,$media_id); + + if (!empty($f)) { + return; + } + + $cur = $this->con->openCursor($this->table_ref); + $cur->post_id = $post_id; + $cur->media_id = $media_id; + + $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 + */ + public function removePostMedia($post_id,$media_id) + { + $post_id = (integer) $post_id; + $media_id = (integer) $media_id; + + $strReq = 'DELETE FROM '.$this->table_ref.' '. + 'WHERE post_id = '.$post_id.' '. + 'AND media_id = '.$media_id.' '; + + $this->con->execute($strReq); + $this->core->blog->triggerBlog(); + } + + /** + 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 ? $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 = array(); + + 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) + { + 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 = array('NOW()'); + $cur->media_upddt = array('NOW()'); + + $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 = array('NOW()'); + + $cur->update('WHERE media_id = '.$media_id); + } + + $this->callFileHandler($media_type,'create',$cur,$name,$media_id); + + 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 (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 = array('NOW()'); + $cur->media_private = (integer) $newFile->media_priv; + + $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); + } + + /** + Extract zip file in current location + + @param f fileRecord fileRecord object + */ + public function inflateZipFile($f,$create_dir=true) + { + $zip = new fileUnzip($f->file); + $zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.DS_Store|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(); + 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|\.DS_Store|Thumbs\.db)(/|$)#'); + $zip->close(); + return $list; + } + + /* Image handlers + ------------------------------------------------------- */ + public function imageThumbCreate(&$cur,$f) + { + $file = $this->pwd.'/'.$f; + + if (!file_exists($file)) { + return false; + } + + $p = path::info($file); + $thumb = sprintf($this->thumb_tp,$p['dirname'],$p['base'],'%s'); + + try + { + $img = new imageTools(); + $img->loadImage($file); + + $w = $img->getW(); + $h = $img->getH(); + + $this->imageThumbRemove($f); + + foreach ($this->thumb_sizes as $suffix => $s) { + if ($s[0] > 0 && ($suffix == 'sq' || $w > $s[0] || $h > $s[0])) { + $img->resize($s[0],$s[0],$s[1]); + $img->output('jpeg',sprintf($thumb,$suffix),80); + } + } + $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); + $thumb_old = sprintf($this->thumb_tp,$p['dirname'],$p['base'],'%s'); + + $p = path::info($newFile->relname); + $thumb_new = sprintf($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) {} + } + } + } + + protected function imageThumbRemove($f) + { + $p = path::info($f); + $thumb = sprintf($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 MP3 player + + @param url string MP3 URL to play + @param player string Player URL + @param args array Player parameters + @return string + */ + public static function mp3player($url,$player=null,$args=null) + { + if (!$player) { + $player = 'player_mp3.swf'; + } + + if (!is_array($args)) + { + $args = array( + 'showvolume' => 1, + 'loadingcolor' => 'ff9900', + 'bgcolor1' => 'eeeeee', + 'bgcolor2' => 'cccccc', + 'buttoncolor' => '0066cc', + 'buttonovercolor' => 'ff9900', + 'slidercolor1' => 'cccccc', + 'slidercolor2' => '999999', + 'sliderovercolor' => '0066cc' + ); + } + + $args['mp3'] = $url; + + if (empty($args['width'])) { + $args['width'] = 200; + } + if (empty($args['height'])) { + $args['height'] = 20; + } + + $vars = array(); + foreach ($args as $k => $v) { + $vars[] = $k.'='.$v; + } + + return + ''. + ''. + ''. + ''. + ''; + } + + public static function flvplayer($url,$player=null,$args=null) + { + if (!$player) { + $player = 'player_flv.swf'; + } + + if (!is_array($args)) + { + $args = array( + 'margin' => 1, + 'showvolume' => 1, + 'showtime' => 1, + 'showfullscreen' => 1, + 'buttonovercolor' => 'ff9900', + 'slidercolor1' => 'cccccc', + 'slidercolor2' => '999999', + 'sliderovercolor' => '0066cc' + ); + } + + $args['flv'] = $url; + + if (empty($args['width'])) { + $args['width'] = 400; + } + if (empty($args['height'])) { + $args['height'] = 300; + } + + $vars = array(); + foreach ($args as $k => $v) { + $vars[] = $k.'='.$v; + } + + return + ''. + ''. + ''. + ''. + ''. + ''; + } +} +?> diff --git a/inc/core/class.dc.modules.php b/inc/core/class.dc.modules.php new file mode 100644 index 0000000..c916c69 --- /dev/null +++ b/inc/core/class.dc.modules.php @@ -0,0 +1,519 @@ +dcCore dcCore instance + + /** + Object constructor. + + @param core dcCore dcCore instance + */ + public function __construct(&$core) + { + $this->core =& $core; + } + + /** + 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; + + 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')) + { + $this->id = $entry; + $this->mroot = $full_entry; + require $full_entry.'/_define.php'; + $this->id = null; + $this->mroot = null; + } + else + { + $this->disabled[$entry] = array( + 'root' => $full_entry, + 'root_writable' => is_writable($full_entry) + ); + } + } + } + $d->close(); + } + + # Sort plugins + uasort($this->modules,array($this,'sortModules')); + + # Load translation, _prepend and ns_file + foreach ($this->modules as $id => $m) + { + if (file_exists($m['root'].'/_prepend.php')) + { + $r = require $m['root'].'/_prepend.php'; + + # If _prepend.php file returns null (ie. it has a void return statement) + if (is_null($r)) { + continue; + } + unset($r); + } + + $this->loadModuleL10N($id,$lang,'main'); + if ($ns == 'admin') { + $this->loadModuleL10Nresources($id,$lang); + } + $this->loadNsFile($id,$ns); + } + } + + public function requireDefine($dir,$id) + { + if (file_exists($dir.'/_define.php')) { + $this->id = $id; + require $dir.'/_define.php'; + $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 permissions string Module permissions + @param priority integer Module priority + */ + public function registerModule($name,$desc,$author,$version,$permissions=null,$priority=1000) + { + if ($this->ns == 'admin') { + if ($permissions == '' && !$this->core->auth->isSuperAdmin()) { + return; + } elseif (!$this->core->auth->check($permissions,$this->core->blog->id)) { + return; + } + } + + if ($this->id) { + $this->modules[$this->id] = array( + 'root' => $this->mroot, + 'name' => $name, + 'desc' => $desc, + 'author' => $author, + 'version' => $version, + 'permissions' => $permissions, + 'priority' => $priority === null ? 1000 : (integer) $priority, + 'root_writable' => is_writable($this->mroot) + ); + } + } + + public function resetModulesList() + { + $this->modules = array(); + } + + public static function installPackage($zip_file,dcModules &$modules) + { + $zip = new fileUnzip($zip_file); + $zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.DS_Store|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)) + { + # 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) && $new_modules[$id]['version'] != $cur_module['version']) + { + # 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". (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 = array('success'=>array(),'failure'=>array()); + 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) + { + try { + $i = $this->loadModuleFile($this->modules[$id]['root'].'/_install.php'); + if ($i === true) { + return true; + } + } catch (Exception $e) { + $msg = $e->getMessage(); + return false; + } + + return null; + } + + 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) + { + 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; + } + } + + protected function loadModuleFile($________) + { + 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}; + } + } + + return require $________; + } + + private function sortModules($a,$b) + { + if ($a['priority'] == $b['priority']) { + return strcasecmp($a['name'],$b['name']); + } + + return ($a['priority'] < $b['priority']) ? -1 : 1; + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.rest.php b/inc/core/class.dc.rest.php new file mode 100644 index 0000000..b547679 --- /dev/null +++ b/inc/core/class.dc.rest.php @@ -0,0 +1,52 @@ +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); + } + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.rs.extensions.php b/inc/core/class.dc.rs.extensions.php new file mode 100644 index 0000000..d17c81e --- /dev/null +++ b/inc/core/class.dc.rs.extensions.php @@ -0,0 +1,815 @@ +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->allow_comments + && $rs->post_open_comment + && ($rs->core->blog->settings->comments_ttl == 0 || + time()-($rs->core->blog->settings->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->allow_trackbacks + && $rs->post_open_tb + && ($rs->core->blog->settings->trackbacks_ttl == 0 || + time()-($rs->core->blog->settings->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 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->getBase('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 + @return integer + */ + public static function getTS(&$rs) + { + return strtotime($rs->post_dt); + } + + /** + Returns post date formating according ISO 8601 standard. + + @param rs Invisible parameter + @return string + */ + public static function getISO8601Date(&$rs) + { + return dt::iso8601($rs->getTS(),$rs->post_tz); + } + + /** + Returns post date formating according RFC 822. + + @param rs Invisible parameter + @return string + */ + public static function getRFC822Date(&$rs) + { + return dt::rfc822($rs->getTS(),$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 + @return string + */ + public static function getDate(&$rs,$format) + { + if ($format) { + return dt::dt2str($format,$rs->post_dt); + } else { + return dt::dt2str($rs->core->blog->settings->date_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 + @return string + */ + public static function getTime(&$rs,$format) + { + if ($format) { + return dt::dt2str($format,$rs->post_dt); + } else { + return dt::dt2str($rs->core->blog->settings->time_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,array('@'=>'%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); + + $url = parse_url($rs->core->blog->url); + $date_part = date('Y-m-d',strtotime($rs->post_creadt)); + + return 'tag:'.$url['host'].','.$date_part.':'.$rs->post_id; + } + + /** + Returns trackback RDF information block in HTML comment. + + @param rs Invisible parameter + @return string + */ + public static function getTrackbackData(&$rs) + { + return + "\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->getBase('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) + { + 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.' '; + + $res = (integer) $rs->core->con->select($strReq)->f(0); + $rs->_nb_media[$rs->index()] = $res; + return $res; + } + } +} + +/** +@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 + @return string + */ + public static function getDate(&$rs,$format) + { + if ($format) { + return dt::dt2str($format,$rs->comment_dt); + } else { + return dt::dt2str($rs->core->blog->settings->date_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 + @return string + */ + public static function getTime(&$rs,$format) + { + if ($format) { + return dt::dt2str($format,$rs->comment_dt); + } else { + return dt::dt2str($rs->core->blog->settings->time_format,$rs->comment_dt); + } + } + + /** + Returns comment timestamp. + + @param rs Invisible parameter + @return integer + */ + public static function getTS(&$rs) + { + return strtotime($rs->comment_dt); + } + + /** + Returns comment date formating according ISO 8601 standard. + + @param rs Invisible parameter + @return string + */ + public static function getISO8601Date(&$rs) + { + return dt::iso8601($rs->getTS(),$rs->comment_tz); + } + + /** + Returns comment date formating according RFC 822. + + @param rs Invisible parameter + @return string + */ + public static function getRFC822Date(&$rs) + { + 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->comments_nofollow) { + $res = preg_replace_callback('##ms',array('self','noFollowURL'),$res); + } + + if ($absolute_urls) { + $res = html::absoluteURLs($res,$rs->getPostURL()); + } + + return $res; + } + + private static function noFollowURL($m) + { + if (preg_match('/rel="nofollow"/',$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'; + } + + $nofollow = ''; + if ($rs->core->blog->settings->comments_nofollow) { + $nofollow = ' rel="nofollow"'; + } + + return sprintf($res,html::escapeHTML($rs->comment_author),html::escapeHTML($url),$nofollow); + } + + /** + 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,array('@'=>'%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); + + $url = parse_url($rs->core->blog->url); + $date_part = date('Y-m-d',strtotime($rs->comment_dt)); + + return 'tag:'.$url['host'].','.$date_part.':'.$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->getBase('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 +{ + /** + 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 null; + } + + /** + 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 array(); + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.settings.php b/inc/core/class.dc.settings.php new file mode 100644 index 0000000..19f832b --- /dev/null +++ b/inc/core/class.dc.settings.php @@ -0,0 +1,330 @@ +connection Database connection object + protected $table; ///< string Permission table name + protected $blog_id; ///< string Blog ID + + protected $settings = array(); ///< array Associative settings array + protected $global_settings = array(); ///< array Global settings array + protected $local_settings = array(); ///< array Local 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 core dcCore dcCore object + @param blog_id string Blog ID + */ + public function __construct(&$core,$blog_id) + { + $this->con =& $core->con; + $this->table = $core->prefix.'setting'; + $this->blog_id =& $blog_id; + + $this->getSettings(); + } + + private function getSettings() + { + $strReq = 'SELECT blog_id, setting_id, setting_ns, setting_value, '. + 'setting_type, setting_label '. + 'FROM '.$this->table.' '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' ". + 'OR blog_id IS NULL '. + 'ORDER BY setting_ns, 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()) + { + $id = trim($rs->f('setting_id')); + $value = $rs->f('setting_value'); + $type = $rs->f('setting_type'); + + 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] = array( + 'ns' => $rs->f('setting_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; + } + + private function settingExists($id,$global=false) + { + $array = $global ? 'global' : 'local'; + return isset($this->{$array.'_settings'}[$id]); + } + + /** + Sets a working namespace. You should do this before adding any setting. + + @param ns string Namespace name + */ + public function setNamespace($ns) + { + if (preg_match('/^[a-zA-Z][a-zA-Z0-9]+$/',$ns)) { + $this->ns = $ns; + } else { + throw new Exception(sprintf(__('Invalid setting namespace: %s'),$ns)); + } + } + + /** + 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) + { + if (!$this->ns) { + throw new Exception(__('No namespace specified')); + } + + 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 { + $type = 'string'; + } + } + elseif ($type != 'boolean' && $type != 'integer' && $type != 'float') + { + $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']; + } + } + + settype($value,$type); + + $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)."' "); + } + else + { + $cur->setting_id = $id; + $cur->blog_id = $global ? null : $this->blog_id; + $cur->setting_ns = $this->ns; + + $cur->insert(); + } + } + + /** + Removes an existing setting. Namespace + + @param id string Setting ID + */ + public function drop($id) + { + $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)."' "; + + $this->con->execute($strReq); + } + + /** + 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 null; + } + + /** + 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; + } else { + $this->settings[$n] = array( + 'ns' => $this->ns, + 'value' => $v, + 'type' => gettype($n), + 'label' => '', + 'global' => false + ); + } + } + + /** + Magic __set method. + @copydoc ::set + */ + public function __set($n,$v) + { + $this->set($n,$v); + } + + /** + Returns $settings property content. + + @return array + */ + public function dumpSettings() + { + return $this->settings; + } + + /** + Returns $global_settings property content. + + @return array + */ + public function dumpGlobalSettings() + { + return $this->global_settings; + } + + /** + @pre + toto + @endpre + */ +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.themes.php b/inc/core/class.dc.themes.php new file mode 100644 index 0000000..f4d6989 --- /dev/null +++ b/inc/core/class.dc.themes.php @@ -0,0 +1,84 @@ +$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 parent string Module parent + @param priority integer Module priority + */ + public function registerModule($name,$desc,$author,$version,$parent = null,$priority = 1000) + { + if ($this->id) { + $this->modules[$this->id] = array( + 'root' => $this->mroot, + 'name' => $name, + 'desc' => $desc, + 'author' => $author, + 'version' => $version, + 'parent' => $parent, + 'priority' => 1000, + 'root_writable' => is_writable($this->mroot) + ); + } + } + + /** + 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; + } + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.trackback.php b/inc/core/class.dc.trackback.php new file mode 100644 index 0000000..61fd878 --- /dev/null +++ b/inc/core/class.dc.trackback.php @@ -0,0 +1,404 @@ +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 trackbacks + //@{ + /** + 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)); + } + + $data = array( + '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] : ''; + + 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 = array('NOW()'); + + $cur->insert(); + } + } + //@} + + /// @name Receive trackbacks + //@{ + /** + Receives a trackback and insert it as a comment of given post. + + @param post_id integer Post ID + */ + public function receive($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(array('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.'; + } + } + + if (!$err) + { + $charset = self::getCharsetFromRequest(); + + if (!$charset) { + $charset = mb_detect_encoding($title.' '.$excerpt.' '.$blog_name, + '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'); + } + + 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); + + $url = trim(html::clean($url)); + + if (!$blog_name) { + $blog_name = 'Anonymous blog'; + } + + $comment = + "\n". + '

    '.($title ? $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->trackbacks_pub ? 1 : -1; + $cur->comment_ip = http::realIP(); + + try + { + # --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); + } + } + catch (Exception $e) + { + $err = 1; + $msg = 'Something went wrong : '.$e->getMessage(); + } + } + + + $debug_trace = + " \n". + ' '.$title."\n". + ' '.$excerpt."\n". + ' '.$url."\n". + ' '.$blog_name."\n". + ' '.$charset."\n". + ' '.$comment."\n". + " \n"; + + $resp = + ''."\n". + "\n". + ' '.(integer) $err."\n"; + + if ($msg) { + $resp .= ' '.$msg."\n"; + } + + if (!empty($_POST['__debug'])) { + $resp .= $debug_trace; + } + + echo $resp.""; + } + //@} + + private static function initHttp($url,&$path) + { + $client = netHttp::initClient($url,$path); + $client->setTimeout(5); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + + return $client; + } + + private static function getCharsetFromRequest() + { + if (isset($_SERVER['CONTENT_TYPE'])) + { + if (preg_match('|charset=([a-zA-Z0-9-]+)|',$_SERVER['CONTENT_TYPE'],$m)) { + return $m[1]; + } + } + + return null; + } + + /// @name Trackbacks auto discovery + //@{ + /** + Returns an array containing all discovered trackbacks URLs in + $text. + + @param text string Input text + @return array + */ + public function discover($text) + { + $res = array(); + + foreach ($this->getTextLinks($text) as $link) + { + if (($url = $this->getPingURL($link)) !== null) { + $res[] = $url; + } + } + + return $res; + } + //@} + + private function getTextLinks($text) + { + $res = array(); + + # href attribute on "a" tags + if (preg_match_all('/]+)>/ms', $text, $match, PREG_SET_ORDER)) + { + for ($i = 0; $i]+)>/ms', $text, $match, PREG_SET_ORDER)) + { + for ($i = 0; $iget($path); + $page_content = $http->getContent(); + } + catch (Exception $e) + { + return false; + } + + $pattern_rdf = + '/.*?'. + ''. + '.*?<\/rdf:RDF>'. + '/ms'; + + preg_match_all($pattern_rdf,$page_content,$rdf_all,PREG_SET_ORDER); + + for ($i=0; $i \ No newline at end of file diff --git a/inc/core/class.dc.update.php b/inc/core/class.dc.update.php new file mode 100644 index 0000000..b99da14 --- /dev/null +++ b/inc/core/class.dc.update.php @@ -0,0 +1,465 @@ + null, + 'href' => null, + 'checksum' => null, + 'info' => null, + 'notify' => true + ); + + protected $cache_ttl = '-6 hours'; + protected $forced_files = array(); + + /** + 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 + @return string Latest version if available + */ + public function check($version) + { + $this->getVersionInfo(); + $v = $this->getVersion(); + if ($v && version_compare($version,$v,'<')) { + return $v; + } + + return false; + } + + public function getVersionInfo() + { + # Check cached file + if (is_readable($this->cache_file) && filemtime($this->cache_file) > strtotime($this->cache_ttl)) + { + $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 = ''; + $client = netHttp::initClient($this->url,$path); + if ($client !== false) { + $client->setTimeout(4); + $client->setUserAgent($_SERVER['HTTP_USER_AGENT']); + $client->get($path); + + $this->readVersion($client->getContent()); + } + } + catch (Exception $e) {} + + # 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 getNotify() + { + return $this->version_info['notify']; + } + + public function getForcedFiles() + { + return $this->forced_files; + } + + public function setForcedFiles() + { + $this->forced_files = func_get_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 + { + $client = netHttp::initClient($url,$path); + $client->setTimeout(4); + $client->setUserAgent($_SERVER['HTTP_USER_AGENT']); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + + if ($client->getStatus() != 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 seems not 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 = array(); + + 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 seems not 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 = array(); + $not_writable = array(); + + 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, array($this,'parseLine'),1); + array_walk($cur_path,array($this,'parseLine'),2); + array_walk($new_md5, array($this,'parseLine'),1); + array_walk($new_path,array($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; + } + } + 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 = array(); + + 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) || md5_file($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); + } +} +?> \ No newline at end of file diff --git a/inc/core/class.dc.utils.php b/inc/core/class.dc.utils.php new file mode 100644 index 0000000..040292c --- /dev/null +++ b/inc/core/class.dc.utils.php @@ -0,0 +1,53 @@ +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; + } +} + +?> \ No newline at end of file diff --git a/inc/core/class.dc.xmlrpc.php b/inc/core/class.dc.xmlrpc.php new file mode 100644 index 0000000..e3d07c2 --- /dev/null +++ b/inc/core/class.dc.xmlrpc.php @@ -0,0 +1,1634 @@ +core =& $core; + $this->blog_id = $blog_id; + + # Blogger methods + $this->addCallback('blogger.newPost',array($this,'blogger_newPost'), + array('string','string','string','string','string','string','integer'), + 'New post'); + + $this->addCallback('blogger.editPost',array($this,'blogger_editPost'), + array('boolean','string','string','string','string','string','integer'), + 'Edit a post'); + + $this->addCallback('blogger.getPost',array($this,'blogger_getPost'), + array('struct','string','integer','string','string'), + 'Return a posts by ID'); + + $this->addCallback('blogger.deletePost',array($this,'blogger_deletePost'), + array('string','string','string','string','string','integer'), + 'Delete a post'); + + $this->addCallback('blogger.getRecentPosts',array($this,'blogger_getRecentPosts'), + array('array','string','string','string','string','integer'), + 'Return a list of recent posts'); + + $this->addCallback('blogger.getUsersBlogs',array($this,'blogger_getUserBlogs'), + array('struct','string','string','string'), + "Return user's blog"); + + $this->addCallback('blogger.getUserInfo',array($this,'blogger_getUserInfo'), + array('struct','string','string','string'), + 'Return User Info'); + + # Metaweblog methods + $this->addCallback('metaWeblog.newPost',array($this,'mw_newPost'), + array('string','string','string','string','struct','boolean'), + 'Creates a new post, and optionnaly publishes it.'); + + $this->addCallback('metaWeblog.editPost',array($this,'mw_editPost'), + array('boolean','string','string','string','struct','boolean'), + 'Updates information about an existing entry'); + + $this->addCallback('metaWeblog.getPost',array($this,'mw_getPost'), + array('struct','string','string','string'), + 'Returns information about a specific post'); + + $this->addCallback('metaWeblog.getRecentPosts',array($this,'mw_getRecentPosts'), + array('array','string','string','string','integer'), + 'List of most recent posts in the system'); + + $this->addCallback('metaWeblog.getCategories',array($this,'mw_getCategories'), + array('array','string','string','string'), + 'List of all categories defined in the weblog'); + + $this->addCallback('metaWeblog.newMediaObject',array($this,'mw_newMediaObject'), + array('struct','string','string','string','struct'), + 'Upload a file on the web server'); + + # MovableType methods + $this->addCallback('mt.getRecentPostTitles',array($this,'mt_getRecentPostTitles'), + array('array','string','string','string','integer'), + 'List of most recent posts in the system'); + + $this->addCallback('mt.getCategoryList',array($this,'mt_getCategoryList'), + array('array','string','string','string'), + 'List of all categories defined in the weblog'); + + $this->addCallback('mt.getPostCategories',array($this,'mt_getPostCategories'), + array('array','string','string','string'), + 'List of all categories to which the post is assigned'); + + $this->addCallback('mt.setPostCategories',array($this,'mt_setPostCategories'), + array('boolean','string','string','string','array'), + 'Sets the categories for a post'); + + $this->addCallback('mt.publishPost',array($this,'mt_publishPost'), + array('boolean','string','string','string'), + 'Retrieve pings list for a post'); + + $this->addCallback('mt.supportedMethods',array($this,'listMethods'), + array(),'Retrieve information about the XML-RPC methods supported by the server.'); + + $this->addCallback('mt.supportedTextFilters',array($this,'mt_supportedTextFilters'), + array(),'Retrieve information about supported text filters.'); + + # WordPress methods + $this->addCallback('wp.getUsersBlogs',array($this,'wp_getUsersBlogs'), + array('array','string','string'), + 'Retrieve the blogs of the user.'); + + $this->addCallback('wp.getPage',array($this,'wp_getPage'), + array('struct','integer','integer','string','string'), + 'Get the page identified by the page ID.'); + + $this->addCallback('wp.getPages',array($this,'wp_getPages'), + array('array','integer','string','string','integer'), + 'Get an array of all the pages on a blog.'); + + $this->addCallback('wp.newPage',array($this,'wp_newPage'), + array('integer','integer','string','string','struct','boolean'), + 'Create a new page.'); + + $this->addCallback('wp.deletePage',array($this,'wp_deletePage'), + array('boolean','integer','string','string','integer'), + 'Removes a page from the blog.'); + + $this->addCallback('wp.editPage',array($this,'wp_editPage'), + array('boolean','integer','integer','string','string','struct','boolean'), + 'Make changes to a blog page.'); + + $this->addCallback('wp.getPageList',array($this,'wp_getPageList'), + array('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',array($this,'wp_getAuthors'), + array('array','integer','string','string'), + 'Get an array of users for the blog.'); + + $this->addCallback('wp.getCategories',array($this,'wp_getCategories'), + array('array','integer','string','string'), + 'Get an array of available categories on a blog.'); + + $this->addCallback('wp.getTags',array($this,'wp_getTags'), + array('array','integer','string','string'), + 'Get list of all tags for the blog.'); + + $this->addCallback('wp.newCategory',array($this,'wp_newCategory'), + array('integer','integer','string','string','struct'), + 'Create a new category.'); + + $this->addCallback('wp.deleteCategory',array($this,'wp_deleteCategory'), + array('boolean','integer','string','string','integer'), + 'Delete a category with a given ID.'); + + $this->addCallback('wp.suggestCategories',array($this,'wp_suggestCategories'), + array('array','integer','string','string','string','integer'), + 'Get an array of categories that start with a given string.'); + + $this->addCallback('wp.uploadFile',array($this,'wp_uploadFile'), + array('struct','integer','string','string','struct'), + 'Upload a file'); + + $this->addCallback('wp.getPostStatusList',array($this,'wp_getPostStatusList'), + array('array','integer','string','string'), + 'Retrieve all of the post statuses.'); + + $this->addCallback('wp.getPageStatusList',array($this,'wp_getPageStatusList'), + array('array','integer','string','string'), + 'Retrieve all of the pages statuses.'); + + $this->addCallback('wp.getPageTemplates',array($this,'wp_getPageTemplates'), + array('struct','integer','string','string'), + 'Retrieve page templates.'); + + $this->addCallback('wp.getOptions',array($this,'wp_getOptions'), + array('struct','integer','string','string','array'), + 'Retrieve blog options'); + + $this->addCallback('wp.setOptions',array($this,'wp_setOptions'), + array('struct','integer','string','string','struct'), + 'Update blog options'); + + $this->addCallback('wp.getComment',array($this,'wp_getComment'), + array('struct','integer','string','string','integer'), + "Gets a comment, given it's comment ID."); + + $this->addCallback('wp.getCommentCount',array($this,'wp_getCommentCount'), + array('array','integer','string','string','integer'), + 'Retrieve comment count.'); + + $this->addCallback('wp.getComments',array($this,'wp_getComments'), + array('array','integer','string','string','struct'), + 'Gets a set of comments for a given post.'); + + $this->addCallback('wp.deleteComment',array($this,'wp_deleteComment'), + array('boolean','integer','string','string','integer'), + 'Delete a comment with given ID.'); + + $this->addCallback('wp.editComment',array($this,'wp_editComment'), + array('boolean','integer','string','string','integer','struct'), + 'Edit a comment with given ID.'); + + $this->addCallback('wp.newComment',array($this,'wp_newComment'), + array('integer','integer','string','string','integer','struct'), + 'Create a new comment for a given post ID.'); + + $this->addCallback('wp.getCommentStatusList',array($this,'wp_getCommentStatusList'), + array('array','integer','string','string'), + 'Retrieve all of the comment statuses.'); + } + + 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,array($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 ($this->core->auth->userID() == $user_id) { + return true; + } + + if ($this->core->auth->checkUser($user_id,$pwd) !== true) { + throw new Exception('Login error'); + } + + return true; + } + + private function setBlog() + { + 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 (!$this->core->blog->settings->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(array( + '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(array('cat_url' => $cat_url)); + + return $rs->isEmpty() ? null : $rs->cat_id; + } + + /* Generic methods + --------------------------------------------------- */ + private function newPost($blog_id,$user,$pwd,$content,$struct=array(),$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->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=array(),$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->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 = array(); + + $res['dateCreated'] = new xmlrpcDate($post->getTS()); + $res['userid'] = $post->user_id; + $res['postid'] = $post->post_id; + + if ($post->cat_id) { + $res['categories'] = array($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,array(&$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 = array(); + $params['limit'] = $nb_post; + + $posts = $this->core->blog->getPosts($params); + + $res = array(); + while ($posts->fetch()) + { + $tres = array(); + + $tres['dateCreated'] = new xmlrpcDate($posts->getTS()); + $tres['userid'] = $posts->user_id; + $tres['postid'] = $posts->post_id; + + if ($posts->cat_id) { + $tres['categories'] = array($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,array(&$tres)); + + $res[] = $tres; + } + + return $res; + } + + private function getUserBlogs($user,$pwd) + { + $this->setUser($user,$pwd); + $this->setBlog(); + + return array(array( + 'url' => $this->core->blog->url, + 'blogid' => '1', + 'blogName' => $this->core->blog->name + )); + } + + private function getUserInfo($user,$pwd) + { + $this->setUser($user,$pwd); + + return array( + '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 = array(); + + $l = $rs->level; + $stack = array('',$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[] = array( + 'categoryId' => $rs->cat_url, + 'parentId' => $parent, + 'description' => $rs->cat_title, + 'categoryName' => $rs->cat_url, + 'htmlUrl' => $this->core->blog->url.$this->core->url->getBase('category').'/'.$rs->cat_url, + 'rssUrl' => $this->core->blog->url.$this->core->url->getBase('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 array( + array( + '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 array( + 'file' => $file_name, + 'url' => $f->file_url, + 'type' => files::getMimeType($file_name) + ); + } + + private function translateWpStatus($s) + { + $status = array( + '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 = array( + '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=array()) + { + $timezone = 0; + if ($this->core->blog->settings->blog_timezone) { + $timezone = dt::getTimeOffset($this->core->blog->settings->blog_timezone)/3600; + } + + $res = array ( + 'software_name' => array ( + 'desc' => 'Software Name', + 'readonly' => true, + 'value' => 'Dotclear' + ), + 'software_version' => array ( + 'desc' => 'Software Version', + 'readonly' => true, + 'value' => DC_VERSION + ), + 'blog_url' => array ( + 'desc' => 'Blog URL', + 'readonly' => true, + 'value' => $this->core->blog->url + ), + 'time_zone' => array ( + 'desc' => 'Time Zone', + 'readonly' => true, + 'value' => (string) $timezone + ), + 'blog_title' => array ( + 'desc' => 'Blog Title', + 'readonly' => false, + 'value' => $this->core->blog->name + ), + 'blog_tagline' => array ( + 'desc' => 'Blog Tagline', + 'readonly' => false, + 'value' => $this->core->blog->desc + ), + 'date_format' => array ( + 'desc' => 'Date Format', + 'readonly' => false, + 'value' => $this->core->blog->settings->date_format + ), + 'time_format' => array ( + 'desc' => 'Time Format', + 'readonly' => false, + 'value' => $this->core->blog->settings->time_format + ) + ); + + if (!empty($options)) + { + $r = array(); + 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 array( + '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 array( + '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 = array( + '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 = array(); + while ($posts->fetch()) + { + $tres = array( + "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" => array(), + "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" => array(), + "wp_page_template" => 'default' + ); + + # --BEHAVIOR-- xmlrpcGetPageInfo + $this->core->callBehavior('xmlrpcGetPageInfo',$this,array(&$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 = array(); + + foreach($rs as $k => $v) + { + $res[] = array( + 'user_id' => $k, + 'user_login' => $k, + 'display_name' => dcUtils::getUserCN($k,$v['name'],$v['firstname'],$v['displayname']) + ); + } + return $res; + } + + private function getTags($user,$pwd) + { + if (!class_exists('dcMeta')) { + throw new Exception('Metadata management is not available on this blog.'); + } + + $this->setUser($user,$pwd); + $this->setBlog(); + + $meta = new dcMeta($this->core); + + $tags = $meta->getMeta('tag'); + $tags->sort('meta_id_lower','asc'); + + $res = array(); + $url = $this->core->blog->url.$this->core->url->getBase('tag').'/%s'; + $f_url = $this->core->blog->url.$this->core->url->getBase('tag_feed').'/%s'; + while ($tags->fetch()) + { + $res[] = array( + '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(array('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 = array(); + while ($rs->fetch()) + { + $res[] = array( + '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 = array( + 'approved' => 0, + 'awaiting_moderation' => 0, + 'spam' => 0, + 'total' => 0 + ); + $rs = $this->core->blog->getComments(array('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 = array(); + + 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'] = array($offset,$limit); + + $rs = $this->core->blog->getComments($params); + $res = array(); + while ($rs->fetch()) + { + $res[] = array( + '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,array(),$publish); + } + + public function blogger_editPost($appkey,$postid,$username,$password,$content,$publish) + { + return $this->editPost($postid,$username,$password,$content,array(),$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 array(); + } + + /* 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 = array(); + foreach ($A as $v) { + $res[] = array( + '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 array('Default' => 'default'); + } + + public function wp_getOptions($blogid,$username,$password,$options=array()) + { + $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 = array(); + $blog_changes = false; + $cur = $this->core->con->openCursor($this->core->prefix.'blog'); + + $this->core->blog->settings->setNameSpace('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->put('date_format',$value); + $done[] = $name; + break; + case 'time_format': + $this->core->blog->settings->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,array(),$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 array( + 'hold' => 'Unapproved', + 'approve' => 'Approved', + 'spam' => 'Spam' + ); + } +} +?> \ No newline at end of file diff --git a/inc/core_error.php b/inc/core_error.php new file mode 100644 index 0000000..fcf0b60 --- /dev/null +++ b/inc/core_error.php @@ -0,0 +1,63 @@ + + + + + + + + + + Dotclear - Error + + + + +
    +

    Dotclear

    +

    +
    + + \ No newline at end of file diff --git a/inc/dbschema/db-schema.php b/inc/dbschema/db-schema.php new file mode 100644 index 0000000..1628fb9 --- /dev/null +++ b/inc/dbschema/db-schema.php @@ -0,0 +1,253 @@ +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_id','blog_id') + ; + +$_s->user + ->user_id ('varchar', 32, false) + ->user_super ('smallint', 0, true) + ->user_status ('smallint', 0, false, 1) + ->user_pwd ('varchar', 40, false) + ->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_words ('text', 0, true, null) + ->post_status ('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) + + ->primary('pk_post_media','media_id','post_id') + ; + +$_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 ('varchar', 255, false) + + ->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') + ; + +/* 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->log->index ('idx_log_user_id', 'btree', 'user_id'); +$_s->comment->index ('idx_comment_post_id', 'btree', 'post_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'); + +/* 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'); +} +?> \ No newline at end of file diff --git a/inc/dbschema/upgrade-cli.php b/inc/dbschema/upgrade-cli.php new file mode 100644 index 0000000..13613e5 --- /dev/null +++ b/inc/dbschema/upgrade-cli.php @@ -0,0 +1,52 @@ +#!/usr/bin/env php +con->begin(); + try { + $changes = 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); +} +?> \ No newline at end of file diff --git a/inc/dbschema/upgrade.php b/inc/dbschema/upgrade.php new file mode 100644 index 0000000..5a1b812 --- /dev/null +++ b/inc/dbschema/upgrade.php @@ -0,0 +1,142 @@ +getVersion('core'); + + if ($version === null) { + return false; + } + + if (version_compare($version,DC_VERSION,'<') == 1) + { + try + { + if ($core->con->driver() == 'sqlite') { + throw new Exception(__('SQLite Database Schema cannot be upgraded.')); + } + + # 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 + ------------------------------------ */ + # 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 = array ( + '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); + } + } + + $core->setVersion('core',DC_VERSION); + $core->blogDefaults(); + + # Drop content from session table + $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; +} +?> \ No newline at end of file diff --git a/inc/digests b/inc/digests new file mode 100644 index 0000000..8ad2662 --- /dev/null +++ b/inc/digests @@ -0,0 +1,531 @@ +3a1c6cc728dddc258091a601f28a9c12 ./inc/swf/swfupload.swf +362b9537b53c8d3eb363598e5f38fb8b ./inc/swf/player_mp3.swf +9dcb71aec92e94b1b905192412a1f2a5 ./inc/swf/player_flv.swf +18ff6240e31ccadc7e1a63db0afb9ae0 ./inc/core/class.dc.media.php +d14d28e70ccb6028c038861728298329 ./inc/core/class.dc.update.php +096468190e67d8e3ff44c6f4a81fa4b1 ./inc/core/class.dc.categories.php +8fbf442f092b6da5e18ab55766bdde2a ./inc/core/class.dc.xmlrpc.php +42c635f3085466704b8ea752c02c9918 ./inc/core/class.dc.error.php +6558c346e6308e15f7c39ba051347d26 ./inc/core/class.dc.utils.php +0191ee5ffb45f0af14e27d07ea4c077d ./inc/core/class.dc.rest.php +2cdfe25f28ba62d3e2e9293931deb43e ./inc/core/class.dc.themes.php +466e62c97781015f72d27e4de1c3ab0b ./inc/core/class.dc.auth.php +494c9a4c0c2494b69c7d31ab25157455 ./inc/core/class.dc.rs.extensions.php +2376b6e6399d9bff92d39067dc35c321 ./inc/core/class.dc.trackback.php +bfbb0c6cb9a699929e2bf3c4c29fef4a ./inc/core/class.dc.settings.php +036a7b3ec5634cdad6c555ba215ae19b ./inc/core/class.dc.blog.php +1c1338e5f700ad38dba4801741251597 ./inc/core/class.dc.modules.php +1453376f19e35d3bc203a21380ba89ad ./inc/core/class.dc.core.php +d9261f6761ffc3e8d14293d196c961e7 ./inc/clearbricks/net/class.net.socket.php +280613489841de5b4ec85ee300851592 ./inc/clearbricks/zip/class.unzip.php +056182476f51e6c5e80cf0460524e0dd ./inc/clearbricks/zip/class.zip.php +500f1a3a8b33c149157741489ca51ad5 ./inc/clearbricks/mail/class.socket.mail.php +0bd7854fbca81ae4a6f78a75e9dd4974 ./inc/clearbricks/mail/class.mail.php +567eadb4af2b986d1a28a0f243255522 ./inc/clearbricks/rest/class.rest.php +c86dcac25e84dc49a0ab98fd6befeb0f ./inc/clearbricks/dblayer/class.pgsql.php +4023755972fdc74509bc49e70353245b ./inc/clearbricks/dblayer/class.mysql.php +fa10d27aa59682a59ffa13eadf8b664c ./inc/clearbricks/dblayer/class.sqlite.php +1c35e6a84a5db695dc9b9f8a0e913f3b ./inc/clearbricks/dblayer/dblayer.php +45f56a2cc770dce9c3db6ba48af22dd5 ./inc/clearbricks/dblayer/class.cursor.php +95660aed4163cea3edefbf722c971f50 ./inc/clearbricks/html.validator/class.html.validator.php +cd01379889d351003cdf481163edd159 ./inc/clearbricks/html.filter/class.html.filter.php +0f9ffdd56e5848cf7da180cd154453a0 ./inc/clearbricks/image/class.image.tools.php +03e7cf2145d28aeedf29a7c061f8e4f9 ./inc/clearbricks/image/class.image.meta.php +546fde4538dd36615535f9ca1abc9a7a ./inc/clearbricks/filemanager/class.filemanager.php +da9d2c8705847220fe0645b0ba81e0aa ./inc/clearbricks/pager/class.pager.php +93a9889af02b59605a8b7257dacaa6d5 ./inc/clearbricks/net.http.feed/class.feed.parser.php +1f01f605ca981b5aa6c3954ce00c1c3f ./inc/clearbricks/net.http.feed/class.feed.reader.php +74b2a9cb057b9c7fa81ebb8a23f6c722 ./inc/clearbricks/net.xmlrpc/class.net.xmlrpc.php +09d98ed0469c11fe6a64d5add21cf0bd ./inc/clearbricks/dbschema/class.dbschema.php +3d6b710784806c85a63c99a8f13b0b7d ./inc/clearbricks/dbschema/class.mysql.dbschema.php +85ebfeb20519bf427d4ca87dcd0d4d13 ./inc/clearbricks/dbschema/class.sqlite.dbschema.php +2d08b40008b003f7a46befd1a77f4fb3 ./inc/clearbricks/dbschema/class.dbstruct.php +9f528ba8e5ef50a372a4cabc778cfcbe ./inc/clearbricks/dbschema/class.pgsql.dbschema.php +e8c5e70379f9d5b042d0a48d42559c1b ./inc/clearbricks/template/class.template.php +f61cf71413b6ab77edb00f5a512a73fd ./inc/clearbricks/_common.php +e3cc1e949b8cc8b2c48fb44f88861801 ./inc/clearbricks/url.handler/class.url.handler.php +d630292807c771d0e6108f8cccf9204a ./inc/clearbricks/common/lib.form.php +8cf4506eabae4a66014b25126153e44b ./inc/clearbricks/common/lib.text.php +53f8e535da21c1f1ec0246a643d01993 ./inc/clearbricks/common/tz.dat +66f31da31787db17a7ebb57421f757b8 ./inc/clearbricks/common/lib.l10n.php +007d3a3c24ff185327db271a65d9ef69 ./inc/clearbricks/common/lib.crypt.php +f0fb3f420cdeb4fd482ec43482b4e967 ./inc/clearbricks/common/_main.php +710e0b928554748db4a7d1846595a1f8 ./inc/clearbricks/common/lib.date.php +a318b8492bdb59b25ac93eb738013099 ./inc/clearbricks/common/lib.html.php +c8647ddc62dfbfdf69d469bfe03a0eb4 ./inc/clearbricks/common/lib.files.php +26b1be314b40bd3ebf624a38f1e0edb8 ./inc/clearbricks/common/lib.http.php +7d92d7db792825e01bff2d837095f313 ./inc/clearbricks/net.http/class.net.http.php +4bdfaf313a531de79e76c4b8b2d066c2 ./inc/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php +2bed897d0ca4fd8aa7bb0b496fa6d1c2 ./inc/clearbricks/session.db/class.session.db.php +5034bd1336f60b708d9b1e980745988a ./inc/config.php.in +ff28eb3d5e9ae5cf909d5ec828c12de0 ./inc/admin/lib.pager.php +8ae4aa567cd1134818297268cb79e42f ./inc/admin/prepend.php +a6b91b473f4ad91fa41ef729fe90f24e ./inc/admin/class.dc.menu.php +39d16d043b0ad50ed0d73a2e49f83025 ./inc/admin/lib.dc.page.php +f9c5a0c91995a57e6baf126bcbd8b363 ./inc/dbschema/upgrade.php +d59926193023ae80dadb0c38ef347923 ./inc/dbschema/upgrade-cli.php +94fc7bf265d59925e1522d9e373bc1fe ./inc/dbschema/db-schema.php +209634bb0238704c4874c35d615ae59e ./inc/.htaccess +0176f5a9b0bac43ab1e1bbe33ed031cd ./inc/prepend.php +6038f9fb2eae878766345ffa586d401b ./inc/load_plugin_file.php +2921e08ddb06de37a3a8d5a1f3fcc888 ./inc/public/lib.tpl.context.php +c6f97bb39ca5107c55009ebf5e788a46 ./inc/public/prepend.php +d89e9d192b1c4181122a0f10fa69f298 ./inc/public/lib.urlhandlers.php +f60ba2b8f32356dd8ab4eee7ae4bb5d4 ./inc/public/rs.extension.php +554c0a70e8cd904d036100bfea1399fa ./inc/public/class.dc.template.php +28c6875823874e3583f035e138f0f705 ./inc/public/default-templates/atom.xml +689693d92c875db96a5bea4b306a4135 ./inc/public/default-templates/rss2.xml +a7006baa75d1c133d57acbe1f559abce ./inc/public/default-templates/rss2.xsl +252faf814965810e53ceaebca99110b8 ./inc/public/default-templates/rss2-comments.xml +9fc1fe3303129e8f38e5853daa71c11e ./inc/public/default-templates/atom-comments.xml +51adbab39d35dfd7a53bde657dd6de09 ./inc/public/default-templates/password-form.html +8cba75d3dfc9154015eb95b64e64e083 ./inc/core_error.php +4f5bbbb21f6fcb084682403822c85337 ./admin/js/ie7/IE7.js +288656dde1e8e77e7da43fe71931eeba ./admin/js/ie7/IE8.js +45becbb1ba3486fa9961b9c47614d2a0 ./admin/js/ie7/blank.gif +15c61b8ebd70424b725a8e39c7ca80c9 ./admin/js/ie7/ie7-recalc.js +e94e0c941ea37d090c39ef10c06a02f5 ./admin/js/ie7/ie7-squish.js +3090e7db27d11c74c19d41489d91af30 ./admin/js/jsToolBar/jsToolBar.js +92c3d043218f868cf339b120dd6d51b7 ./admin/js/jsToolBar/popup_posts.js +e3f0393f5bc344afddb314075193f8de ./admin/js/jsToolBar/popup_link.js +fdf73b7d33b9d76e223d42a155eeeacd ./admin/js/jsToolBar/popup_media.js +bd64332e1fbced1a9921d8fe30380006 ./admin/js/jsToolBar/jsToolBar.dotclear.js +15f0e8797ab0c5f256d03ee4b747686f ./admin/js/jsToolBar/jsToolBar.wysiwyg.js +050846e84f432233d2290997e6a20783 ./admin/js/color-picker.js +08451c09111e763c2354993025b07a66 ./admin/js/_comment.js +2819411a33e1dce79a21aeceaed37a5e ./admin/js/_post.js +787f768271c8bb6b582b102c68a4211f ./admin/js/common.js +e3b5113a5cfed6a1d9dfa72834e5fb0d ./admin/js/_preferences.js +fb7b1c55f3f25ef21f94cb237bb88b88 ./admin/js/_index.js +44c7923c51c9e8b5185d70078a1652cc ./admin/js/dragsort-tablerows.js +eadf36bbd468ceb62957c39b82cb1b07 ./admin/js/_users.js +cf014d803ae8195a2473b48b1f2c92b6 ./admin/js/_langs.js +a4f58f2e708d54d966341654a1b9710f ./admin/js/filter-controls.js +bfeba52298d8b24e30236eb02e6b1f60 ./admin/js/_media.js +a90da2f0f50f7aff0bc42990b04ca921 ./admin/js/_blog_theme.js +334a1345290da857abe41f3807c14967 ./admin/js/date-picker.js +81b61be64f338c2fd40da2bf47d2183e ./admin/js/_categories.js +49e972305f261af8830eac093dbed82f ./admin/js/confirm-close.js +dacda4414ed2d1db6b25ed16bdcc8ee9 ./admin/js/_media_item.js +10b1885ee8c7ad1c439bc3de868d4bb4 ./admin/js/_trackbacks.js +478c38cb403ab2cfb6b0affac12d3148 ./admin/js/jquery/jquery.pageTabs.js +447ce7210d6451137e4e93ac39cd9c8f ./admin/js/jquery/jquery.farbtastic.js +ce7a22089d470d15dc6a061bf7fe3d09 ./admin/js/jquery/jquery.bgFade.js +aee44d7ab273cd5d53611bad4ab0e11b ./admin/js/jquery/jquery.js +7f5f5bb62524a0ec4be1985031e7e141 ./admin/js/jquery/jquery.modal.js +93e11ef8585fbfa48f6dc595eb17a12c ./admin/js/jquery/jquery.biscuit.js +ad20456532afaf966bdc8ee50d00209d ./admin/js/jquery/jquery.candyUpload.js +aa314073e0ef73b815959563872f6c2e ./admin/js/_permissions.js +614f3c5a1eec10b7b83f4c0f38b2e67b ./admin/js/_posts_list.js +b46e51e327a0bb8f2711f230fc1467b0 ./admin/js/tool-man/dragsort.js +814404153ee1d350adf4bf7d1eae4cf8 ./admin/js/tool-man/core.js +6084f5f1fbcb14b085014ffa06961029 ./admin/js/tool-man/drag.js +02fba17f9a80f43ad07c55f2e092e7de ./admin/js/tool-man/coordinates.js +7d13dc9c59316624c051e6256c86bcb7 ./admin/js/tool-man/events.js +af77cd03cd87c611f1f495d6d29bab44 ./admin/js/tool-man/css.js +87aabde2f9d09bac13b8483e369cca38 ./admin/js/tool-man/cookies.js +fd92ba025b34df5e3784ae9cca6c4f8d ./admin/js/_permissions_blog.js +20f7eabec172e5c6cb511181e1b1ae57 ./admin/js/_comments.js +f0f861db8bed322c14508f592aeef119 ./admin/js/_plugins.js +29698c9e3ac842ca5c30abe9a69b338a ./admin/js/_category.js +00db46ceb034d8e909105225f4a0a5d4 ./admin/blogs.php +e487586329acb6096938fd9dfe626a70 ./admin/posts_actions.php +0fe85f696c9210e87d98f9987433e991 ./admin/blog_del.php +8e45eb714a43536dbfb5e99d6d53ca26 ./admin/update.php +f73c46ef14a0e1b75eaf3cb58c6ccecf ./admin/plugins.php +d2751a29c317ba588b12cb0f00f3f21f ./admin/popup_posts.php +b816dd28a252d2a2727365b48f41e6bb ./admin/style/jsToolBar/jsToolBar.css +3e0f67a5296128dbe1857c1935f6da47 ./admin/style/jsToolBar/bt_link.png +64cdd7af332808399e65fa0538761c2d ./admin/style/jsToolBar/resize.png +6a388ba1cdeff25c79d540333b96ed27 ./admin/style/jsToolBar/bt_clean.png +a6ae01491523b9335334eea1b98c136f ./admin/style/jsToolBar/bt_img_select.png +828adda895735a8624fd819506078af7 ./admin/style/jsToolBar/bt_br.png +e5aae58c14ca63c366fe02acec03d928 ./admin/style/jsToolBar/bt_em.png +0c7181b5c37930f422fb9e6e7488c1d9 ./admin/style/jsToolBar/bt_img.png +ee7ee162173d1b91896415988e8cd006 ./admin/style/jsToolBar/bt_bquote.png +47c71f6ee3cf31296ff1f18348748754 ./admin/style/jsToolBar/bt_ins.png +e9f233c1c309337911b5b03fb9e1dbc4 ./admin/style/jsToolBar/bt_pre.png +22ad5189d9d94f9a24af5311ad06805b ./admin/style/jsToolBar/bt_paragraph.png +2ca06c1c3e80079ea378fe061e4cfdc3 ./admin/style/jsToolBar/bt_code.png +78d7a07a669055db5c3966edc31c94e4 ./admin/style/jsToolBar/bt_ol.png +a3a4c43798d387734737ed81a65eb7f9 ./admin/style/jsToolBar/bt_quote.png +94a2fd910832b31e18b4bb4e63f9aa79 ./admin/style/jsToolBar/bt_strong.png +e7e674fbd3612010f154475c449b3b79 ./admin/style/jsToolBar/bt_del.png +e43a1380a8ae14425ca9ce400dd336b7 ./admin/style/jsToolBar/bt_post.png +39f73a440303baed6920a769370091da ./admin/style/jsToolBar/bt_ul.png +dc66f1288966eba4739b35f590b43e41 ./admin/style/candyUpload/style.css +caa11e523e5a3ed38d90480bed5d5a73 ./admin/style/candyUpload/loader.png +0f4c059f8aceadfb2dd932e7a02ce22a ./admin/style/candyUpload/cancel.png +493914ba4fd6581eb119500982c9b137 ./admin/style/msg-error.png +d8d0578b583b3879b3ee697fb4068648 ./admin/style/modal/modal.css +14a0936bd54fd18c22b5e7fb86ba26a3 ./admin/style/modal/loader.gif +4134330cedefac4a9075a7756ca496bd ./admin/style/modal/close.png +2f3a444b380a72b9073880cd7d22ae43 ./admin/style/package.png +7c598bafd53a127ee25043a7b1956e96 ./admin/style/default.css +ff0826bd48edc94b38534ce3d2d26997 ./admin/style/footer-bg.png +d905059cafb6ddd38cabba894c3dbee1 ./admin/style/tab-bg.png +2f7d618efdcae3859b36bbfa38fc75bf ./admin/style/page-bg.png +9c3e7b71f42e2d6c64b52fe7d46d5f16 ./admin/style/grid.png +003b8b1d1103cff11a707c5f80861a75 ./admin/style/default-rtl.css +5a7ae1d8c8b2343a9e5a4aebf4234392 ./admin/style/farbtastic/marker.png +2b6d304868ff398c17252b7b0a0414c4 ./admin/style/farbtastic/wheel.png +fbbd113ed6e6cf4adcab0f720b3b9b9c ./admin/style/farbtastic/farbtastic.css +c6dc921c0d6f2197793d9174b4267ca0 ./admin/style/farbtastic/mask.png +5afe28dbeb084fc5cd47212fa33b300b ./admin/style/tab-n-l.png +0b57db80571f538897e663bee26cb5a2 ./admin/style/tab-n-r.png +57688345aa080a9a391efc40608ce98b ./admin/style/dotclear-logo.png +af7d5adfaf36b1b1d83061295d5f68da ./admin/style/msg-std.png +6bc7580d52332c7267cb94f357630700 ./admin/style/drag.png +6fbf1d5cf4953ace09e5041a53d477ec ./admin/style/head-bg.png +4606f74acbdf16980fb16b8bc1e60099 ./admin/style/tab-c-l.png +b4671dcafdcd2a1ba6450d03acfce3b3 ./admin/style/tab-c-r.png +5625c7bc5997742b45925105fd9a4303 ./admin/style/magnifier.png +895cb5dd1b08be6bbf3ebd423248e3be ./admin/style/cat-bg.png +28551d64a9bbc7045c4b8eff4869afbe ./admin/style/iesucks.css +37e482ed14e13a1a8a141deb0eea81f2 ./admin/style/date-picker.css +60561d6c50898f057cc37eed843be317 ./admin/style/tab-l-l.png +8e603ba37b2b50e342a3135a6e2c2127 ./admin/style/head-logo.png +4b7349095e157bd0e83c841cb2d3747d ./admin/style/tab-l-r.png +e6e2cc17d4e986318d8b5a7750918157 ./admin/xmlrpc.php +e19529bc96ec56b62aa00e0647c15020 ./admin/auth.php +128057016b1339ef139cdb3d5fda9300 ./admin/post.php +fd589b6457f2d38e050aea8059ee4d4c ./admin/permissions.php +871d5fb3cf0490d2a7c1938fda35e438 ./admin/blog.php +d7023399307c45b30a3f34d180528841 ./admin/plugin.php +8df147fc78f19b966494c221ed3aa131 ./admin/media.php +045d75fc9de350bb0492beadf2d7fe69 ./admin/media_item.php +f0adbe078cc42ac3ec356b1fa2cd67cd ./admin/images/plus.png +2e6ca41f8d5d7c256594ffcc05b06e24 ./admin/images/menu/edit.png +097a854b6f9cbe35a0d09b01f745b487 ./admin/images/menu/blogs.png +d9edc49028ec933be21a2cde223cf55f ./admin/images/menu/dashboard.png +7a45e5d3af30b8b2f6e481b74ed18e08 ./admin/images/menu/update.png +778ef6ba7b0569d8c2a33eb036dfdcb1 ./admin/images/menu/plugins.png +af904ee4f98c3d0454f970eb90198cf7 ./admin/images/menu/blog-theme-b.png +bfed5f95ca5157d3fde0f4e4a4eaa9a3 ./admin/images/menu/media.png +4f4b1dab66fa2901815c0fec68a39371 ./admin/images/menu/entries.png +da47c798afefd12b44076cb827c6d019 ./admin/images/menu/comments.png +36c86b1fea44340979269e5d0653448d ./admin/images/menu/search.png +91e09572a6d2b47498eb7633808fccbd ./admin/images/menu/user-pref-b.png +15c7649c7f91216e9221c6114e941f48 ./admin/images/menu/blog-pref-b.png +db79ab64acee2cd9b7ac686deea28737 ./admin/images/menu/entries-b.png +00e426d4817de89ec7586c162c10ad74 ./admin/images/menu/users.png +05ddb3df17ac3336804ab562d1dea18c ./admin/images/menu/edit-b.png +6c24e8664283448459c6d2ec96863b25 ./admin/images/menu/comments-b.png +ae2e6d937abe776bf651094a736aa003 ./admin/images/menu/categories.png +f7a4f2dd4f9b5c679e2744822e56ed48 ./admin/images/menu/langs.png +86583b1fd690088cd78f91983aaf8575 ./admin/images/dotclear_pw.png +50bf28433834e6d2f6f46d1b84421d2b ./admin/images/media/video.png +8e35fccfaa42070297cef723428183ba ./admin/images/media/blank.png +7e619d7762995a3c3ef9edfb3b6b4c92 ./admin/images/media/spreadsheet.png +c2b2d27914d57241d3d0e96a66ede1ca ./admin/images/media/package.png +487708f4b8f6fa0ecff78f26ded3806a ./admin/images/media/html.png +90864902424cf16cd85d2a6e54fe0272 ./admin/images/media/document.png +be57e76497791a2540d91f9149f38a6b ./admin/images/media/audio.png +6884619665a6a6a91996236dd26ad2fc ./admin/images/media/text.png +27013d32dfc30e5512ff184c2869bb3f ./admin/images/media/image.png +34f401c85ee26edfffb2c317ae2b1b54 ./admin/images/media/presentation.png +c6db076f177ccaf062d017c5db228085 ./admin/images/media/folder.png +4d8611170bd9173aa87cfbd4878ac588 ./admin/images/media/executable.png +61334007afc261a93d677bd3460ee36d ./admin/images/trash.png +c8806b7d90e29532ec65e74933c4ea67 ./admin/images/selected.png +e7c196b87a5e022549b71e6f4e811d18 ./admin/images/menu_on.png +c3380a4422bb8a7e8756a6a167ddea22 ./admin/images/check-off.png +355fbab6dc8dc6c779ba687bcc187fd7 ./admin/images/scheduled.png +913ff19cacf4d03fc835b4255c236c79 ./admin/images/picker.png +3ee61de988743c744af9b9224d35284b ./admin/images/minus.png +b742e51edacbf99451e12c5bb01531b0 ./admin/images/junk.png +f86e9d6679d8b27334bbb8df82b1c7d9 ./admin/images/menu_off.png +45c5ccf8249c5ec4800b9e0c3d34b7ca ./admin/images/check-wrn.png +02437c955382fa358b357632557a6f4e ./admin/images/attach.png +dfab5698cd3abbb30538331690413d30 ./admin/images/locker.png +cb8f61218854ef04d73714cc52aa5dbc ./admin/images/check-on.png +07181f8c47f322410aea182338b031b3 ./admin/images/date-picker.png +fc080751a6ae822596cbdb54c97c4fd6 ./admin/images/edit-mini.png +93d023c510f85f951839c251814291ab ./admin/images/noscreenshot.png +8776864d4c4505e2cc4040a418497bc9 ./admin/images/help.png +87f5dea12797a8eb428ea1954866584c ./admin/index.php +f10ae181969bd912a9eb68da2956c23d ./admin/permissions_blog.php +64c3e93f7e541ed757142e5de18380e9 ./admin/posts.php +17036fa2d8a7656614b781a4c7b9426f ./admin/comments.php +9c7d01d916f3aa1d881ee7819667263b ./admin/popup_link.php +3f65bb3cbd860e9fb81f3b2d6277c5b7 ./admin/services.php +290ee52f1bc1a016f251f036d4ff2478 ./admin/search.php +3548d9af451b2e6af1809b555d4c88e8 ./admin/blog_theme.php +9bf0b3af8f41807602ef2323dd006762 ./admin/comments_actions.php +fbdcee6ef06a409e23260133941e4244 ./admin/comment.php +a701b5ad177608322c0008ba9f424e11 ./admin/user.php +3b1fcad912db22bc88c58011e6fb720d ./admin/dispatcher.php +3f23b0534157d6ed65e94c1b8ea9dcd1 ./admin/preferences.php +6c154f459f2f87a830edc64d949a6db0 ./admin/users.php +e5375f24957f9d5d52631519e049d266 ./admin/install/index.php +5c09c6c55a50b6ad49beb4147d4dea49 ./admin/install/check.php +ac45ae7c40f5e3412e6ba051420ca33f ./admin/install/wizard.php +487561551d32cae95ad8a7fd15485617 ./admin/category.php +153aab11365f581e9f0b50378013d4e2 ./admin/post_media.php +c4ce01f48c57bd31b755c102486ae9f9 ./admin/blog_pref.php +0d89d53b637614bde68935d36e77fee0 ./admin/categories.php +7b91f4c7e8861f0af36c7a08a7b2df5e ./admin/trackbacks.php +7411861ab76537f6c52d7b6cdc243a32 ./admin/langs.php +597e43bb54850373eeb2c2676f30f3fd ./locales/en/date.po +fc51ec4fe28cadc83913b369fc77d65a ./locales/en/help/comments.html +d5122c309d77b7a499fab7dec52e9b29 ./locales/en/help/blog_pref.html +5591fcbe6853807c82659bddda528325 ./locales/en/help/media.html +c260a45249e6e678ce089d7ef6b88e98 ./locales/en/help/post.html +efc9a224297e6902a5f0efa196bc7c6f ./locales/en/help/wiki.html +601f769c326ea8920e0ff67e64cb9c01 ./locales/en/help/categories.html +dac7cda43ac667f5689ef48e8ca36c4d ./locales/en/help/user_pref.html +09352986157f34af6730061ce06bd8cd ./locales/en/help/posts.html +bcaf88842838bc943ec14f2dec691466 ./locales/en/date.lang.php +086e64533816640d615e69afeba0b680 ./locales/en/resources.php +d7f6cc471b5bc7cd871e4d395b8c276e ./locales/fr/date.po +087aaec908f335474f273d81fcfec249 ./locales/fr/help/core_media.html +1019b0c0fd0cf1e2b1b354e0d1df4f18 ./locales/fr/help/core_user_pref.html +a9f897d204180419de34dcfdc9b18f76 ./locales/fr/help/themeEditor.html +2bc53951d2998ab67979a5b1ea4f3df9 ./locales/fr/help/core_comments.html +996bbf4b20290530884d25844b1ee32d ./locales/fr/help/blowupConfig.html +2f8538f01d3c3a46faa42595f1bc42ef ./locales/fr/help/core_categories.html +5b1623e4a9bb1b32ec9f9a4b7bae3366 ./locales/fr/help/core_post.html +e38f790349f0accca7c63a7f76671c0c ./locales/fr/help/core_posts.html +590e7cdb6a0010defabfae0c5e1554c2 ./locales/fr/help/core_wiki.html +068c9bc2bc27a2478cb5af2fd97e9645 ./locales/fr/help/core_blog_pref.html +b251c88e5e41bf4b381d4398018c5fbb ./locales/fr/public.lang.php +9bc4415f19d72a5762c782a31bcb955f ./locales/fr/date.lang.php +e44ca473dcb048250e0fc7ec025eb91a ./locales/fr/main.po +8ba1a2c161a45493e40da4ff2955b97f ./locales/fr/plugins.po +bfff3c066a52b55cfbcf1f89d282a91a ./locales/fr/plugins.lang.php +6920fcce560d9d1c6e2b3bf007f76dc5 ./locales/fr/resources.php +97ba3984d4efc605b977a27a3811f35e ./locales/fr/public.po +fc6f85748920e451eb3231a9261c2a2e ./locales/fr/main.lang.php +d4ff299dabd4b8334cc7259d0eb65b6e ./locales/README +04abe6412946b1fa2ed72963a7f8237c ./index.php +aee44d7ab273cd5d53611bad4ab0e11b ./themes/default/js/jquery.js +0399de1c63edf55deb1cffa811905d57 ./themes/default/js/jquery.cookie.js +d7dc9f56f2b68076900853c61b8a6fcf ./themes/default/js/post.js +a802d1f382c8b22d1e6c6e28ae618498 ./themes/default/img/trackback.png +74eb29efade1edeff692446b5e6f8258 ./themes/default/img/commentmy-b.png +ef130b60877aad5a4cc8d4c5ead17317 ./themes/default/img/commentmy-t.png +f3b1ebaf5a27959228b736f87db11776 ./themes/default/img/page-bg.png +e199613e6ec2cead05b92a6f2c76e2ec ./themes/default/img/comment-b.png +90835fdf81ee294b89674795ee6ed71f ./themes/default/img/tag.png +3f7ae2737f49605199554ccac96dc748 ./themes/default/img/comment-t.png +c85f9d3ea9319236e67db78be1707478 ./themes/default/img/page-b.png +42fa213bf2a5b110afeb7552767700c6 ./themes/default/img/page-t.png +68f80507e1d6123f57c5f25f006a699f ./themes/default/img/feed.png +e4d37aac2d638c4a358a7fd4c76b2789 ./themes/default/img/attach.png +19741777bd991f387b6d01d187f9cacc ./themes/default/img/comment.png +dff6f47c5dcfce7c1a909f015f43a91b ./themes/default/img/body-bg.png +a2877d50faebc326ce5984496cb3fe3b ./themes/default/tpl/404.html +16bdc8783f0b0b181bca2922afa6707f ./themes/default/tpl/_head.html +6c96c9afa5258bfe009b97197324286c ./themes/default/tpl/category.html +94df989e651b3fab9ae5f889da8957a5 ./themes/default/tpl/_top.html +b8901ab0ebed68dc2c989fb5bca4506e ./themes/default/tpl/archive_month.html +a1751e1f10a3b96b05d0401c97e72008 ./themes/default/tpl/.htaccess +4672a386be84c3b8aabe7f0f11eadd1f ./themes/default/tpl/_footer.html +4aceb31c39646ddef57802c8e6d09cf3 ./themes/default/tpl/archive.html +c0859258eafc9dfdeb5c77310738527b ./themes/default/tpl/_mp3_player.html +3f32c76d09e83f0dc647d80ae76f561c ./themes/default/tpl/tag.html +b34754e53b51132b11c3f15e9275c196 ./themes/default/tpl/post.html +51c379d5e96b3cfff2410872ab2cdeb4 ./themes/default/tpl/user_head.html +7964b89c503810f4c29a9277b8bce384 ./themes/default/tpl/tags.html +e68a44c097d3ff407fff0efa118cec64 ./themes/default/tpl/search.html +4fd60dab641f6d4a6992091e9d001ed9 ./themes/default/tpl/home.html +d29f8d7ed1bdd0a755d696885a4086a5 ./themes/default/tpl/_flv_player.html +ffdba5c2bedd3e76d57eca1cb07e5e44 ./themes/default/_define.php +646621422212fb347c48849b2c0dce90 ./themes/default/style.css +acdc336f9249b84310c6bc541d6e66ed ./themes/default/smilies/laugh.png +e8cfd3101dc2a0cdb7a343e1b873a38f ./themes/default/smilies/cry.png +062fe26eb6105ce6bbc0d133b50c4fcc ./themes/default/smilies/confused.png +c8372d57625893f1c06a409cf1fb84d5 ./themes/default/smilies/sad.png +c1fa8d897b82f938e2abada9bfa7c2c1 ./themes/default/smilies/redface.png +f97fc26a57559afec46806aa42083d9c ./themes/default/smilies/eek.png +919a6951304ce994203f86edd3f5d359 ./themes/default/smilies/dizzy.png +5933cf36a8be5147933b6499d589eb01 ./themes/default/smilies/idea.png +d2855d2d68f6467db69219534638f4bb ./themes/default/smilies/exclam.png +93d6fed1604e9e479f65940e52884ac3 ./themes/default/smilies/lol.png +0d16e0502c576f32662ad14dfde3cf53 ./themes/default/smilies/rolleyes.png +18f5ac3609425f10e910e64d5620edda ./themes/default/smilies/cool.png +1808a6c8affe37c094a038dc983b0ffc ./themes/default/smilies/mrgreen.png +d3671aa8ccb54bbc9b89a83ba202b626 ./themes/default/smilies/smilies.txt +51b2f454563975cc4b2fcc07d31ce12d ./themes/default/smilies/alien.png +5b36ab1e0f2c2dde883a6e32cc3feebf ./themes/default/smilies/smile.png +a1650e5c1ff8e9f40ef90828011f91fa ./themes/default/smilies/normal.png +efe8138e2d0b366a9dcdbe1c6ab75eee ./themes/default/smilies/evil.png +987742e347ef66bbc2b6b9546a0aa360 ./themes/default/smilies/angry.png +83362893d53db01b5595a7e8021fcdac ./themes/default/smilies/wink.png +57a457e1818d6e9419ced9366d2e33dc ./themes/default/smilies/razz.png +9ae025a44deba07f3e3fe2c3992e5d9c ./themes/default/smilies/arrow.png +25adf8de879b3e83c12ab668bc3c983f ./themes/default/smilies/question.png +32f2a33008491d7c070d70cfc64585c0 ./themes/default/smilies/surprised.png +c75e685b085a7e188b42f995626ec3be ./themes/default/screenshot.jpg +4e6c671f9777afc2948fb172631e7611 ./themes/default/print.css +28e5e068b8b973e3d1997ba7c24205f2 ./themes/blueSilence/img/background.png +8ebf5666d9d8bac9fdeedbd17daaa348 ./themes/blueSilence/img/report.png +a58dc745b52f9bb33a44fb9ccd5a776d ./themes/blueSilence/img/rss.png +a56d97cb87cfd1970760fff3345af5d8 ./themes/blueSilence/img/tag.png +f7a34b6d7c3641b7afa0132a9723ece3 ./themes/blueSilence/img/top.jpg +f9e699b16c85dd103e725d70efdacd2f ./themes/blueSilence/img/li.png +ab2a5e87e0c395d12a221485be1ff821 ./themes/blueSilence/img/commentaire.png +4c8c6a8fe121912b6cf6a8edaa920aec ./themes/blueSilence/img/tags.png +e8bb6589fe0e7bc810acc4c350f2a54d ./themes/blueSilence/img/commentaire_bulle.png +851317b61d8b6b08d6bb58c06896df21 ./themes/blueSilence/img/attach.png +c1dda264fa5d4120717ba558f01fe652 ./themes/blueSilence/img/footer.png +986f44c4d95f0b4d30defec242f0f76d ./themes/blueSilence/img/retrolien.png +e40bfe45e815e751015bf122e16fb1f2 ./themes/blueSilence/img/sidebar_li.png +4f5fd6f1c016f670fad151b0039886e1 ./themes/blueSilence/_define.php +4c99f9d0bd6843a5484740269dd72770 ./themes/blueSilence/style.css +6c0c644258713357329a5bcac1f44768 ./themes/blueSilence/screenshot.jpg +800bcd981cdcb38b2321d465db858147 ./themes/customCSS/_define.php +d41d8cd98f00b204e9800998ecf8427e ./themes/customCSS/style.css +4cd898ae3cb55d817c1046bba6c6b9ce ./themes/customCSS/_public.php +da18cf4aaed165d14dd612ed09a377b1 ./themes/customCSS/locales/fr/main.po +eb0e5f0211f741849acfccaf155b1be6 ./themes/customCSS/locales/fr/main.lang.php +602e79fa926e9b841f3f75e95bdca48a ./themes/customCSS/_config.php +9195e781a1146a794aa7678343af4a0d ./plugins/maintenance/_define.php +b60a33878195e9855dff47abe862e26e ./plugins/maintenance/icon.png +528f065f8f506656f2ac9cdae8a1732e ./plugins/maintenance/_admin.php +343e841912410913d232241164067cf5 ./plugins/maintenance/index.php +cb304b3d12e1493f07b1323ca1c9f6b1 ./plugins/metadata/_define.php +80155a9abf9d5583262fc1928027192f ./plugins/metadata/_install.php +39b72570feafa373b59b2e37166e383c ./plugins/metadata/style.css +d6f79878139ff3b303002f529999dab2 ./plugins/metadata/_widgets.php +7c51fbb5c908a74434dd16448703a324 ./plugins/metadata/_public.php +6e3a9c34cd45a3223f72436d21183d5b ./plugins/metadata/_prepend.php +6153779453db6919728b74eb0d3f5618 ./plugins/metadata/_admin.php +3987d3c7792f3a22864bdd14cc6ab62b ./plugins/metadata/class.dc.meta.php +ea2ce91dab0b252599fd2b04eda1a0c1 ./plugins/metadata/index.php +cac0a6c35ca3fcb985246bb197c48801 ./plugins/metadata/_xmlrpc.php +f2ca37bc37ff0e5cdc95370a0431d5c1 ./plugins/metadata/tags.php +828753a0de820938df6ae3a0676e97ad ./plugins/metadata/tags.png +52d2c48f3c56b68929129d335d04a4c0 ./plugins/metadata/tag_posts.php +ea424458edbdb1f6c4c01d4486019252 ./plugins/metadata/tag-add.png +0f942eb58886c0ec269507c0f8163d7f ./plugins/metadata/post.js +3dc636d068035accb61b00ee4fe4fcac ./plugins/pages/list.php +bf0f3b795af1195b0168599d82f0c549 ./plugins/pages/_define.php +30715dda782c45e93d3725ae4df17b51 ./plugins/pages/page.php +b98d94ece8f106a2292e3c81fa5ca113 ./plugins/pages/_widgets.php +e48c5cb3b44f1b16791327bb91c0e34d ./plugins/pages/_public.php +745d97d5c53c155c822166545190f003 ./plugins/pages/icon.png +383fb9e8763e4e5bea7cd6690fc88b16 ./plugins/pages/_prepend.php +d241f50078a5286feceac6f569ca896a ./plugins/pages/_admin.php +ba920f53414aa9e243c4c2ec9c0e3b35 ./plugins/pages/index.php +ea9b46834d4d768f1030ecd809a9fbe5 ./plugins/pages/icon-big.png +cf21a309465b3d8c9b7ebb2ae58de0ea ./plugins/pages/default-templates/page.html +8d06d95623305ca4e8190eaceb7d4cfa ./plugins/pings/lib.pings.php +6f49a0a44a3df7fd9a111b43ae9ee509 ./plugins/pings/_define.php +8ed6b5e4a1c64ef157af27035dd36710 ./plugins/pings/icon.png +a004559d94ac47d448a1ca906c9e8d72 ./plugins/pings/_admin.php +58e9f5d0040e26b1e193708b9552745b ./plugins/pings/index.php +5f826c13498df394b5c7a1a4b2972903 ./plugins/pings/post.js +49c2a00d5026bcb5975519ec3673f251 ./plugins/antispam/inc/class.dc.spamfilters.php +8a94ca06ec0f7fea031a34c5e33d6c05 ./plugins/antispam/inc/lib.dc.antispam.php +3b067c387d57ae696f9c754796cfe87f ./plugins/antispam/inc/class.dc.spamfilter.php +e87f7343d0758b41867c350847544e49 ./plugins/antispam/inc/lib.dc.antispam.url.php +b3c0fc9e29e60d1d371dd54d3ddc8fee ./plugins/antispam/_define.php +2ee8dfae1cf7bbe75a0a9a6728a16a4c ./plugins/antispam/_install.php +c28748b62667080cf3a0fbf5dd98f7b2 ./plugins/antispam/style.css +26a71db6f5842bfca51614af14d40449 ./plugins/antispam/_public.php +e0a3c27c4bee15b0cc9acb47d8d5fa80 ./plugins/antispam/icon.png +e3639b0227f46b75e25b8649f8d0b525 ./plugins/antispam/_prepend.php +e70ea13ca819364ead3b295eac0ec100 ./plugins/antispam/filters/class.dc.filter.linkslookup.php +c18038c662707206bb79370a32ef3999 ./plugins/antispam/filters/class.dc.filter.words.php +32a53ea01b6cfa6d9f84fb684fb745ee ./plugins/antispam/filters/class.dc.filter.ip.php +d654ea3e8c8598fc40938395124bf141 ./plugins/antispam/filters/class.dc.filter.iplookup.php +bd95e625b15104caf95fa2c85c7126b3 ./plugins/antispam/_admin.php +4328b42921acd050f9912622d3898c24 ./plugins/antispam/index.php +b82da4aa0a7b272ae5e2de8dd7359b3a ./plugins/antispam/feed.png +56dd843cb7ae5c0e71ea479c96136352 ./plugins/antispam/antispam.js +8c6505a27374d4f1cbef3362cf6f901a ./plugins/themeEditor/_define.php +df6d24d467b24cf0914f388864ff3000 ./plugins/themeEditor/style.css +5789e6f200e5634889714946356bc0b4 ./plugins/themeEditor/_admin.php +276b5f6b0381ed5d14ad9024aaa064bf ./plugins/themeEditor/index.php +e9a020825c231f7025aac4a313f9f602 ./plugins/themeEditor/class.themeEditor.php +6c62071513731346b5a251cf1ec2c1ef ./plugins/themeEditor/help.html +f75e3f88c0dea27c23c1b02486cf485d ./plugins/themeEditor/script.js +209634bb0238704c4874c35d615ae59e ./plugins/.htaccess +34750fedb71f2858ac9c451bee109aa7 ./plugins/blogroll/icon-small.png +5be6207fc2634cafcabedca5e7708e10 ./plugins/blogroll/edit.php +dff163989fc9d1941d6689e6db4fa9f4 ./plugins/blogroll/_define.php +59d33a02127d37539506f0f5f36ce3c9 ./plugins/blogroll/class.dc.importblogroll.php +da2dbd651ff5dddcf172d86b4cb63daf ./plugins/blogroll/_install.php +4e66c188ea53d182a678a3745884fb8b ./plugins/blogroll/_widgets.php +a35970aa5074ed40c5d4b4500ab556d7 ./plugins/blogroll/_public.php +921145b71dc3516a3d4ad60b68e78dad ./plugins/blogroll/icon.png +7303c091ac6b473839dbee7d70f814dc ./plugins/blogroll/locales/en/main.po +04f114d237e28b43baada59d68ae11a3 ./plugins/blogroll/locales/en/main.lang.php +b48b67849fa06c95ee73552a44e11abe ./plugins/blogroll/_prepend.php +90afd97ac0d6f8054239da6eb9e1240b ./plugins/blogroll/_admin.php +2b17bc2179de12ae4ef23e899be89bcd ./plugins/blogroll/class.dc.blogroll.php +349a097dae6a0aa822f0108c9fae1289 ./plugins/blogroll/index.php +16c051428cc53314f5bfe269aff1b8d1 ./plugins/akismet/_define.php +6ae97039acba2c1591d13ece6932a044 ./plugins/akismet/_prepend.php +930f6da919a4a7daf63a4baec1e5117a ./plugins/akismet/class.dc.filter.akismet.php +23361f3333c6d390813bd4aece69add5 ./plugins/aboutConfig/_define.php +b8baeee677712dd72df37e3a041a353b ./plugins/aboutConfig/icon.png +b9168dfac656478fb29863363486ae2d ./plugins/aboutConfig/_admin.php +c55f2aa45c22963a96ac10ff3be277a7 ./plugins/aboutConfig/index.php +038556fbe48e919ead6c101d6902932f ./plugins/externalMedia/_define.php +bc0a1f83d40449a030d2c16793bae775 ./plugins/externalMedia/popup.js +62b92949c582abcbe0cab2627f6a986e ./plugins/externalMedia/_admin.php +d2f4c40252d0e4c974df2039681435b7 ./plugins/externalMedia/bt_video.png +f221d727652b753df02590247f062d5d ./plugins/externalMedia/index.php +cf3694d85cc49a597e0602571409a7ea ./plugins/externalMedia/post.js +3e0bd4f8a787a2d0d34f246d3ea444f1 ./plugins/widgets/_define.php +fe3f620da53a4cc011d92894b894f264 ./plugins/widgets/style.css +28836e015ca92980acdba3e19a33c1e8 ./plugins/widgets/widgets.js +1d9265ec44b487c1e2be0c437aa0b0e0 ./plugins/widgets/_widgets_functions.php +505471d9316198939b981bc47ad12d07 ./plugins/widgets/_public.php +73e41e8ae95ef82fb2eb2529d2cf1ca3 ./plugins/widgets/_default_widgets.php +848c9d774eb3c3ebb3f25c3ed0a9fac9 ./plugins/widgets/icon.png +72d242e2ed2e4550ad4a24da01deafc5 ./plugins/widgets/class.widgets.php +86644c18aac829b9ddf826280f88be0f ./plugins/widgets/_admin.php +5affcbdca523969d8ea31d09f3af621f ./plugins/widgets/index.php +3b552f7e600dd6c98edc66e7e62ad1fb ./plugins/widgets/dragdrop.js +14fbced6e7458a7e4b3e10d969b5048e ./plugins/fairTrackbacks/_define.php +a967f6943de24a3e46e35182b6c4fe8d ./plugins/fairTrackbacks/_public.php +ada0cfdf0148421317943366b71d85be ./plugins/fairTrackbacks/_prepend.php +f569f055e740ef053ff9ca5d61b03efc ./plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php +d4bf4c07c1749e71f83d4c55632b4e95 ./plugins/blowupConfig/lib/class.blowup.config.php +1942d7e37e185fb289aa16757b6e876f ./plugins/blowupConfig/_define.php +656ca7404ded88fcbe79563b012e3e65 ./plugins/blowupConfig/config.js +9efefd97ec4e70402e86aa104ac62647 ./plugins/blowupConfig/_public.php +ab20193417b32b04f4036c68801b216c ./plugins/blowupConfig/_admin.php +a93df332a256a18530195dc1673f95f2 ./plugins/blowupConfig/index.php +f3b1ebaf5a27959228b736f87db11776 ./plugins/blowupConfig/alpha-img/page-bg.png +4e391773b51ea336fb0f647cb15379ef ./plugins/blowupConfig/alpha-img/comment-b.png +78e0f7cf458db47eef8b8ff7a241ac8e ./plugins/blowupConfig/alpha-img/comment-t.png +1c7448246376dc8cecee8f0479723099 ./plugins/blowupConfig/alpha-img/page-t/flamingo.png +f4c8777ac3a935e7b9f171a948a78280 ./plugins/blowupConfig/alpha-img/page-t/blank.png +68daa93bbe1ed00ff190cf851033fe93 ./plugins/blowupConfig/alpha-img/page-t/animals.png +a140e3ae9bf5dcc8c3152a315bd17cd3 ./plugins/blowupConfig/alpha-img/page-t/default.png +c1582964ea8ac500d8f1358cbc11b472 ./plugins/blowupConfig/alpha-img/page-t/typo.png +a13d367c5520f17ee7b3a2dd6cd8068f ./plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png +a33e8d4310696d64bb065cddd23b5292 ./plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png +c48b1a888ebd601e610a425c733367fa ./plugins/blowupConfig/alpha-img/page-t/rabbit.png +508813560e6465f057c07287ad40dd88 ./plugins/blowupConfig/alpha-img/page-t/flourish-1.png +7b79aa5c11177401d7b2cac032d09038 ./plugins/blowupConfig/alpha-img/page-t/flourish-2.png +722ad2b43138abc067ade1c4ed6d0297 ./plugins/blowupConfig/alpha-img/page-t/light-trails-1.png +ee440de20028785d2c0a4fd7b25ae060 ./plugins/blowupConfig/alpha-img/page-t/light-trails-2.png +3f9e394ed187b401c4446b77490944b6 ./plugins/blowupConfig/alpha-img/page-t/light-trails-3.png +ca76fc93ebbce051feb507869165ab6d ./plugins/blowupConfig/alpha-img/page-t/light-trails-4.png +4b3117cca2f73c47d5892684443c74fb ./plugins/blowupConfig/alpha-img/page-t/image-mask.png +ffacd6551c2bd0c863740e7c2fcc29e4 ./plugins/blowupConfig/alpha-img/page-t/butterflies.png +652d06f1c3f7057cd7ab6a01d22b72ba ./plugins/blowupConfig/alpha-img/page-b.png +52db59a23ae5743c614bf9457de0167a ./plugins/blowupConfig/alpha-img/gradient-d.png +bd908640438b6cc2088e81df0894c864 ./plugins/blowupConfig/alpha-img/gradient-l.png +d1e0bdff64011042f02910c6518b1d00 ./plugins/blowupConfig/alpha-img/gradient-m.png +81ef6980e71af45e03f56f66fa6fbd8e ./plugins/blowupConfig/help.html +67a59fdadac3ef3545015b4c0e15419d ./plugins/importExport/inc/flat/class.backupFile.php +d82d2e98a3eacc8d265a520eccb0116a ./plugins/importExport/inc/flat/class.db.export.php +38c5dbc68284930e00058d9bb98fe784 ./plugins/importExport/inc/flat/class.dc.import.php +2ba80acbd4984685a3028a5ee1cb0fce ./plugins/importExport/inc/class.dc.export.flat.php +8a883d5ee9caa0de210a91c0d160814a ./plugins/importExport/inc/class.dc.ieModule.php +5dacef6fdc0a3d9676bb2e1d28dcbeec ./plugins/importExport/inc/class.dc.import.dc1.php +50a742bd58ebf3b24c4f8f60a9fa28dd ./plugins/importExport/inc/class.dc.import.wp.php +daac7535525565d0cf272472a0f5fd22 ./plugins/importExport/inc/class.dc.import.flat.php +2740fd54e99ad819d774697a37eaf57e ./plugins/importExport/inc/class.dc.import.feed.php +5a1dfc62e8d2fdc1f1f73390e6d90ccc ./plugins/importExport/_define.php +caa11e523e5a3ed38d90480bed5d5a73 ./plugins/importExport/progress.png +7f63214bd02f1700fd236e470c19f579 ./plugins/importExport/icon.png +86c054457dacdcfda8000e02a8e71ac6 ./plugins/importExport/_admin.php +bff88e23be2c2f69d237fdcb099d0635 ./plugins/importExport/index.php +b39d8c8adaaac48561c435648faa0d64 ./plugins/importExport/script.js diff --git a/inc/load_plugin_file.php b/inc/load_plugin_file.php new file mode 100644 index 0000000..c97c5dc --- /dev/null +++ b/inc/load_plugin_file.php @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/inc/prepend.php b/inc/prepend.php new file mode 100644 index 0000000..d5f4f8b --- /dev/null +++ b/inc/prepend.php @@ -0,0 +1,260 @@ +config.php
    file. '. + 'I need this before we can get started. Need more help? '. + 'We got it. '. + 'You can create a config.php '. + 'file through a web interface, but this doesn\'t work for all '. + 'server setups. The safest way is to manually create the file.' + ,10); +} + +require DC_RC_PATH; + +# Constants +define('DC_ROOT',path::real(dirname(__FILE__).'/..')); +define('DC_VERSION','2.1.7'); +define('DC_DIGESTS',dirname(__FILE__).'/digests'); +define('DC_L10N_ROOT',dirname(__FILE__).'/../locales'); +define('DC_L10N_UPDATE_URL','http://services.dotclear.net/dc2.l10n/?version=%s'); + +if (!defined('DC_VENDOR_NAME')) { + define('DC_VENDOR_NAME','Dotclear'); +} + +if (!defined('DC_XMLRPC_URL')) { + define('DC_XMLRPC_URL','%1$sxmlrpc/%2$s'); +} + +if (!defined('DC_ADMIN_SSL')) { + define('DC_ADMIN_SSL',false); +} + +if (defined('DC_FORCE_SCHEME_443') && DC_FORCE_SCHEME_443) { + http::$https_scheme_on_443 = true; +} + +if (!defined('DC_DBPERSIST')) { + define('DC_DBPERSIST',false); +} + +if (!defined('DC_UPDATE_URL')) { + define('DC_UPDATE_URL','http://download.dotclear.net/versions.xml'); +} + +if (!defined('DC_UPDATE_VERSION')) { + define('DC_UPDATE_VERSION','stable'); +} + +try { + $core = new dcCore(DC_DBDRIVER,DC_DBHOST,DC_DBNAME,DC_DBUSER,DC_DBPASSWORD,DC_DBPREFIX,DC_DBPERSIST); +} catch (Exception $e) { + __error($e->getMessage() + ,$e->getCode() == 0 ? + '

    This either means that the username and password information in '. + 'your config.php file is incorrect or we can\'t contact '. + 'the database server at "'.DC_DBHOST.'". This could mean your '. + 'host\'s database server is down.

    '. + '
    • Are you sure you have the correct username and password?
    • '. + '
    • Are you sure that you have typed the correct hostname?
    • '. + '
    • Are you sure that the database server is running?
    '. + '

    If you\'re unsure what these terms mean you should probably contact '. + 'your host. If you still need help you can always visit the '. + 'Dotclear Support Forums.

    ' + : '' + ,20); +} + +# If we have some __top_behaviors, we load them +if (isset($__top_behaviors) && is_array($__top_behaviors)) +{ + foreach ($__top_behaviors as $b) { + $core->addBehavior($b[0],$b[1]); + } + unset($b); +} + +http::trimRequest(); +try { + http::unsetGlobals(); +} catch (Exception $e) { + header('Content-Type: text/plain'); + echo $e->getMessage(); + exit; +} + +l10n::init(); + +$core->url->registerDefault(array('dcUrlHandlers','home')); +$core->url->register('lang','','^([a-zA-Z]{2}(?:-[a-z]{2})?(?:/page/[0-9]+)?)$',array('dcUrlHandlers','lang')); +$core->url->register('post','post','^post/(.+)$',array('dcUrlHandlers','post')); +$core->url->register('preview','preview','^preview/(.+)$',array('dcUrlHandlers','preview')); +$core->url->register('category','category','^category/(.+)$',array('dcUrlHandlers','category')); +$core->url->register('archive','archive','^archive(/.+)?$',array('dcUrlHandlers','archive')); + +$core->url->register('feed','feed','^feed/(.+)$',array('dcUrlHandlers','feed')); +$core->url->register('trackback','trackback','^trackback/(.+)$',array('dcUrlHandlers','trackback')); +$core->url->register('rsd','rsd','^rsd$',array('dcUrlHandlers','rsd')); +$core->url->register('xmlrpc','xmlrpc','^xmlrpc/(.+)$',array('dcUrlHandlers','xmlrpc')); + +$core->setPostType('post','post.php?id=%d',$core->url->getBase('post').'/%s'); + +# Store upload_max_filesize in bytes +$u_max_size = files::str2bytes(ini_get('upload_max_filesize')); +$p_max_size = files::str2bytes(ini_get('post_max_size')); +if ($p_max_size < $u_max_size) { + $u_max_size = $p_max_size; +} +define('DC_MAX_UPLOAD_SIZE',$u_max_size); +unset($u_max_size); unset($p_max_size); + +# Shutdown +register_shutdown_function('__shutdown'); + +function __shutdown() +{ + global $__shutdown; + if (is_array($__shutdown)) { + foreach ($__shutdown as $f) { + if (is_callable($f)) { + call_user_func($f); + } + } + } + + # Explicitly close session before DB connection + if (session_id()) { + session_write_close(); + } + $GLOBALS['core']->con->close(); +} + +function __error($summary,$message,$code=0) +{ + # Error codes + # 10 : no config file + # 20 : database issue + # 30 : blog is not defined + # 40 : template files creation + # 50 : no default theme + # 60 : template processing error + + if (CLI_MODE) + { + trigger_error($summary,E_USER_ERROR); + exit(1); + } + else + { + if (defined('DC_ERRORFILE') && is_file(DC_ERRORFILE)) { + include DC_ERRORFILE; + } else { + include dirname(__FILE__).'/core_error.php'; + } + exit; + } +} +?> \ No newline at end of file diff --git a/inc/public/class.dc.template.php b/inc/public/class.dc.template.php new file mode 100644 index 0000000..3045dd3 --- /dev/null +++ b/inc/public/class.dc.template.php @@ -0,0 +1,2916 @@ +remove_php = !$core->blog->settings->tpl_allow_php; + $this->use_cache = $core->blog->settings->tpl_use_cache; + + $this->tag_block = '|>)(.*?)'; + $this->tag_value = '{{tpl:(%s)(\s(.*?))?}}'; + + $this->core =& $core; + + # Transitional tags + $this->addValue('EntryTrackbackCount',array($this,'EntryPingCount')); + $this->addValue('EntryTrackbackData',array($this,'EntryPingData')); + $this->addValue('EntryTrackbackLink',array($this,'EntryPingLink')); + + # l10n + $this->addValue('lang',array($this,'l10n')); + + # Loops test tags + $this->addBlock('LoopPosition',array($this,'LoopPosition')); + + # Archives + $this->addBlock('Archives',array($this,'Archives')); + $this->addBlock('ArchivesHeader',array($this,'ArchivesHeader')); + $this->addBlock('ArchivesFooter',array($this,'ArchivesFooter')); + $this->addBlock('ArchivesYearHeader',array($this,'ArchivesYearHeader')); + $this->addBlock('ArchivesYearFooter',array($this,'ArchivesYearFooter')); + $this->addValue('ArchiveDate',array($this,'ArchiveDate')); + $this->addBlock('ArchiveNext',array($this,'ArchiveNext')); + $this->addBlock('ArchivePrevious',array($this,'ArchivePrevious')); + $this->addValue('ArchiveEntriesCount',array($this,'ArchiveEntriesCount')); + $this->addValue('ArchiveURL',array($this,'ArchiveURL')); + + # Attachments + $this->addBlock('Attachments',array($this,'Attachments')); + $this->addBlock('AttachmentsHeader',array($this,'AttachmentsHeader')); + $this->addBlock('AttachmentsFooter',array($this,'AttachmentsFooter')); + $this->addValue('AttachmentMimeType',array($this,'AttachmentMimeType')); + $this->addValue('AttachmentType',array($this,'AttachmentType')); + $this->addValue('AttachmentFileName',array($this,'AttachmentFileName')); + $this->addValue('AttachmentSize',array($this,'AttachmentSize')); + $this->addValue('AttachmentTitle',array($this,'AttachmentTitle')); + $this->addValue('AttachmentThumbnailURL',array($this,'AttachmentThumbnailURL')); + $this->addValue('AttachmentURL',array($this,'AttachmentURL')); + $this->addBlock('AttachmentIf',array($this,'AttachmentIf')); + $this->addValue('MediaURL',array($this,'MediaURL')); + + # Blog + $this->addValue('BlogArchiveURL',array($this,'BlogArchiveURL')); + $this->addValue('BlogCopyrightNotice',array($this,'BlogCopyrightNotice')); + $this->addValue('BlogDescription',array($this,'BlogDescription')); + $this->addValue('BlogEditor',array($this,'BlogEditor')); + $this->addValue('BlogFeedID',array($this,'BlogFeedID')); + $this->addValue('BlogFeedURL',array($this,'BlogFeedURL')); + $this->addValue('BlogRSDURL',array($this,'BlogRSDURL')); + $this->addValue('BlogName',array($this,'BlogName')); + $this->addValue('BlogLanguage',array($this,'BlogLanguage')); + $this->addValue('BlogThemeURL',array($this,'BlogThemeURL')); + $this->addValue('BlogUpdateDate',array($this,'BlogUpdateDate')); + $this->addValue('BlogID',array($this,'BlogID')); + $this->addValue('BlogURL',array($this,'BlogURL')); + $this->addValue('BlogPublicURL',array($this,'BlogPublicURL')); + $this->addValue('BlogQmarkURL',array($this,'BlogQmarkURL')); + $this->addValue('BlogMetaRobots',array($this,'BlogMetaRobots')); + + # Categories + $this->addBlock('Categories',array($this,'Categories')); + $this->addBlock('CategoriesHeader',array($this,'CategoriesHeader')); + $this->addBlock('CategoriesFooter',array($this,'CategoriesFooter')); + $this->addBlock('CategoryIf',array($this,'CategoryIf')); + $this->addBlock('CategoryFirstChildren',array($this,'CategoryFirstChildren')); + $this->addBlock('CategoryParents',array($this,'CategoryParents')); + $this->addValue('CategoryFeedURL',array($this,'CategoryFeedURL')); + $this->addValue('CategoryURL',array($this,'CategoryURL')); + $this->addValue('CategoryShortURL',array($this,'CategoryShortURL')); + $this->addValue('CategoryDescription',array($this,'CategoryDescription')); + $this->addValue('CategoryTitle',array($this,'CategoryTitle')); + + # Comments + $this->addBlock('Comments',array($this,'Comments')); + $this->addValue('CommentAuthor',array($this,'CommentAuthor')); + $this->addValue('CommentAuthorDomain',array($this,'CommentAuthorDomain')); + $this->addValue('CommentAuthorLink',array($this,'CommentAuthorLink')); + $this->addValue('CommentAuthorMailMD5',array($this,'CommentAuthorMailMD5')); + $this->addValue('CommentAuthorURL',array($this,'CommentAuthorURL')); + $this->addValue('CommentContent',array($this,'CommentContent')); + $this->addValue('CommentDate',array($this,'CommentDate')); + $this->addValue('CommentTime',array($this,'CommentTime')); + $this->addValue('CommentEmail',array($this,'CommentEmail')); + $this->addValue('CommentEntryTitle',array($this,'CommentEntryTitle')); + $this->addValue('CommentFeedID',array($this,'CommentFeedID')); + $this->addValue('CommentID',array($this,'CommentID')); + $this->addBlock('CommentIf',array($this,'CommentIf')); + $this->addValue('CommentIfFirst',array($this,'CommentIfFirst')); + $this->addValue('CommentIfMe',array($this,'CommentIfMe')); + $this->addValue('CommentIfOdd',array($this,'CommentIfOdd')); + $this->addValue('CommentIP',array($this,'CommentIP')); + $this->addValue('CommentOrderNumber',array($this,'CommentOrderNumber')); + $this->addBlock('CommentsFooter',array($this,'CommentsFooter')); + $this->addBlock('CommentsHeader',array($this,'CommentsHeader')); + $this->addValue('CommentPostURL',array($this,'CommentPostURL')); + $this->addBlock('IfCommentAuthorEmail',array($this,'IfCommentAuthorEmail')); + + # Comment preview + $this->addBlock('IfCommentPreview',array($this,'IfCommentPreview')); + $this->addValue('CommentPreviewName',array($this,'CommentPreviewName')); + $this->addValue('CommentPreviewEmail',array($this,'CommentPreviewEmail')); + $this->addValue('CommentPreviewSite',array($this,'CommentPreviewSite')); + $this->addValue('CommentPreviewContent',array($this,'CommentPreviewContent')); + $this->addValue('CommentPreviewCheckRemember',array($this,'CommentPreviewCheckRemember')); + + # Entries + $this->addBlock('DateFooter',array($this,'DateFooter')); + $this->addBlock('DateHeader',array($this,'DateHeader')); + $this->addBlock('Entries',array($this,'Entries')); + $this->addBlock('EntriesFooter',array($this,'EntriesFooter')); + $this->addBlock('EntriesHeader',array($this,'EntriesHeader')); + $this->addValue('EntryExcerpt',array($this,'EntryExcerpt')); + $this->addValue('EntryAttachmentCount',array($this,'EntryAttachmentCount')); + $this->addValue('EntryAuthorCommonName',array($this,'EntryAuthorCommonName')); + $this->addValue('EntryAuthorDisplayName',array($this,'EntryAuthorDisplayName')); + $this->addValue('EntryAuthorEmail',array($this,'EntryAuthorEmail')); + $this->addValue('EntryAuthorID',array($this,'EntryAuthorID')); + $this->addValue('EntryAuthorLink',array($this,'EntryAuthorLink')); + $this->addValue('EntryAuthorURL',array($this,'EntryAuthorURL')); + $this->addValue('EntryBasename',array($this,'EntryBasename')); + $this->addValue('EntryCategory',array($this,'EntryCategory')); + $this->addBlock('EntryCategoriesBreadcrumb',array($this,'EntryCategoriesBreadcrumb')); + $this->addValue('EntryCategoryID',array($this,'EntryCategoryID')); + $this->addValue('EntryCategoryURL',array($this,'EntryCategoryURL')); + $this->addValue('EntryCategoryShortURL',array($this,'EntryCategoryShortURL')); + $this->addValue('EntryCommentCount',array($this,'EntryCommentCount')); + $this->addValue('EntryContent',array($this,'EntryContent')); + $this->addValue('EntryDate',array($this,'EntryDate')); + $this->addValue('EntryFeedID',array($this,'EntryFeedID')); + $this->addValue('EntryFirstImage',array($this,'EntryFirstImage')); + $this->addValue('EntryID',array($this,'EntryID')); + $this->addBlock('EntryIf',array($this,'EntryIf')); + $this->addValue('EntryIfFirst',array($this,'EntryIfFirst')); + $this->addValue('EntryIfOdd',array($this,'EntryIfOdd')); + $this->addValue('EntryIfSelected',array($this,'EntryIfSelected')); + $this->addValue('EntryLang',array($this,'EntryLang')); + $this->addBlock('EntryNext',array($this,'EntryNext')); + $this->addValue('EntryPingCount',array($this,'EntryPingCount')); + $this->addValue('EntryPingData',array($this,'EntryPingData')); + $this->addValue('EntryPingLink',array($this,'EntryPingLink')); + $this->addBlock('EntryPrevious',array($this,'EntryPrevious')); + $this->addValue('EntryTitle',array($this,'EntryTitle')); + $this->addValue('EntryTime',array($this,'EntryTime')); + $this->addValue('EntryURL',array($this,'EntryURL')); + + # Languages + $this->addBlock('Languages',array($this,'Languages')); + $this->addBlock('LanguagesHeader',array($this,'LanguagesHeader')); + $this->addBlock('LanguagesFooter',array($this,'LanguagesFooter')); + $this->addValue('LanguageCode',array($this,'LanguageCode')); + $this->addBlock('LanguageIfCurrent',array($this,'LanguageIfCurrent')); + $this->addValue('LanguageURL',array($this,'LanguageURL')); + + # Pagination + $this->addBlock('Pagination',array($this,'Pagination')); + $this->addValue('PaginationCounter',array($this,'PaginationCounter')); + $this->addValue('PaginationCurrent',array($this,'PaginationCurrent')); + $this->addBlock('PaginationIf',array($this,'PaginationIf')); + $this->addValue('PaginationURL',array($this,'PaginationURL')); + + # Trackbacks + $this->addValue('PingBlogName',array($this,'PingBlogName')); + $this->addValue('PingContent',array($this,'PingContent')); + $this->addValue('PingDate',array($this,'PingDate')); + $this->addValue('PingEntryTitle',array($this,'PingEntryTitle')); + $this->addValue('PingFeedID',array($this,'PingFeedID')); + $this->addValue('PingID',array($this,'PingID')); + $this->addValue('PingIfFirst',array($this,'PingIfFirst')); + $this->addValue('PingIfOdd',array($this,'PingIfOdd')); + $this->addValue('PingIP',array($this,'PingIP')); + $this->addValue('PingNoFollow',array($this,'PingNoFollow')); + $this->addValue('PingOrderNumber',array($this,'PingOrderNumber')); + $this->addValue('PingPostURL',array($this,'PingPostURL')); + $this->addBlock('Pings',array($this,'Pings')); + $this->addBlock('PingsFooter',array($this,'PingsFooter')); + $this->addBlock('PingsHeader',array($this,'PingsHeader')); + $this->addValue('PingTime',array($this,'PingTime')); + $this->addValue('PingTitle',array($this,'PingTitle')); + $this->addValue('PingAuthorURL',array($this,'PingAuthorURL')); + + # System + $this->addValue('SysBehavior',array($this,'SysBehavior')); + $this->addBlock('SysIf',array($this,'SysIf')); + $this->addBlock('SysIfCommentPublished',array($this,'SysIfCommentPublished')); + $this->addBlock('SysIfCommentPending',array($this,'SysIfCommentPending')); + $this->addBlock('SysIfFormError',array($this,'SysIfFormError')); + $this->addValue('SysFeedSubtitle',array($this,'SysFeedSubtitle')); + $this->addValue('SysFormError',array($this,'SysFormError')); + $this->addValue('SysPoweredBy',array($this,'SysPoweredBy')); + $this->addValue('SysSearchString',array($this,'SysSearchString')); + $this->addValue('SysSelfURI',array($this,'SysSelfURI')); + } + + public function getData($________) + { + # --BEHAVIOR-- tplBeforeData + if ($this->core->hasBehavior('tplBeforeData')) + { + self::$_r = $this->core->callBehavior('tplBeforeData',$this->core); + if (self::$_r) { + return self::$_r; + } + } + + parent::getData($________); + + # --BEHAVIOR-- tplAfterData + if ($this->core->hasBehavior('tplAfterData')) { + $this->core->callBehavior('tplAfterData',$this->core,self::$_r); + } + + return self::$_r; + } + + protected function compileBlock($match) + { + $this->current_tag = $match[1]; + $content = $match[3]; + $attr = new ArrayObject($this->getAttrs($match[2])); + + # --BEHAVIOR-- templateBeforeBlock + $res = $this->core->callBehavior('templateBeforeBlock',$this->core,$this->current_tag,$attr); + + $res .= call_user_func($this->blocks[$this->current_tag],$attr,$content); + + # --BEHAVIOR-- templateAfterBlock + $res .= $this->core->callBehavior('templateAfterBlock',$this->core,$this->current_tag,$attr); + + return $res; + } + + protected function compileValue($match) + { + $this->current_tag = $match[1]; + + $str_attr = ''; + $attr = new ArrayObject(); + if (isset($match[2])) { + $str_attr = $match[2]; + $attr = new ArrayObject($this->getAttrs($match[2])); + } + + # --BEHAVIOR-- templateBeforeValue + $res = $this->core->callBehavior('templateBeforeValue',$this->core,$this->current_tag,$attr); + + $res .= call_user_func($this->values[$this->current_tag],$attr,ltrim($str_attr)); + + # --BEHAVIOR-- templateAfterValue + $res .= $this->core->callBehavior('templateAfterValue',$this->core,$this->current_tag,$attr); + + return $res; + } + + public function getFilters($attr) + { + $p[0] = '0'; # encode_xml + $p[1] = '0'; # remove_html + $p[2] = '0'; # cut_string + $p[3] = '0'; # lower_case + $p[4] = '0'; # upper_case + + $p[0] = (integer) (!empty($attr['encode_xml']) || !empty($attr['encode_html'])); + $p[1] = (integer) !empty($attr['remove_html']); + + if (!empty($attr['cut_string']) && (integer) $attr['cut_string'] > 0) { + $p[2] = (integer) $attr['cut_string']; + } + + $p[3] = (integer) !empty($attr['lower_case']); + $p[4] = (integer) !empty($attr['upper_case']); + + return "context::global_filter(%s,".implode(",",$p).",'".addslashes($this->current_tag)."')"; + } + + protected function getOperator($op) + { + switch (strtolower($op)) + { + case 'or': + case '||': + return '||'; + case 'and': + case '&&': + default: + return '&&'; + } + } + + /* TEMPLATE FUNCTIONS + ------------------------------------------------------- */ + + public function l10n($attr,$str_attr) + { + # Normalize content + $str_attr = preg_replace('/\s+/x',' ',$str_attr); + + return ""; + } + + public function LoopPosition($attr,$content) + { + $start = isset($attr['start']) ? (integer) $attr['start'] : '0'; + $length = isset($attr['length']) ? (integer) $attr['length'] : 'null'; + $even = isset($attr['even']) ? (integer) (boolean) $attr['even'] : 'null'; + + if ($start > 0) { + $start--; + } + + return + 'loopPosition('.$start.','.$length.','.$even.')) : ?>'. + $content. + ""; + } + + + /* Archives ------------------------------------------- */ + /*dtd + + + */ + public function Archives($attr,$content) + { + $p = '$params = array();'; + $p .= "\$params['type'] = 'month';\n"; + if (isset($attr['type'])) { + $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n"; + } + + if (isset($attr['category'])) { + $p .= "\$params['cat_url'] = '".addslashes($attr['category'])."';\n"; + } + + $p .= "\$params['post_type'] = 'post';\n"; + if (isset($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (isset($attr['post_lang'])) { + $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n"; + } + + if (empty($attr['no_context'])) + { + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + } + + $order = 'desc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $p .= "\$params['order'] = '".$attr['order']."';\n "; + } + + $res = "archives = $core->blog->getDates($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'archives->fetch()) : ?>'.$content.'archives = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function ArchivesHeader($attr,$content) + { + return + "archives->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function ArchivesFooter($attr,$content) + { + return + "archives->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function ArchivesYearHeader($attr,$content) + { + return + "archives->yearHeader()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function ArchivesYearFooter($attr,$content) + { + return + "archives->yearFooter()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function ArchiveDate($attr) + { + $format = '%B %Y'; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $f = $this->getFilters($attr); + return 'archives->dt)").'; ?>'; + } + + /*dtd + + */ + public function ArchiveEntriesCount($attr) + { + $f = $this->getFilters($attr); + return 'archives->nb_post').'; ?>'; + } + + /*dtd + + + */ + public function ArchiveNext($attr,$content) + { + $p = '$params = array();'; + $p .= "\$params['type'] = 'month';\n"; + if (isset($attr['type'])) { + $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n"; + } + + $p .= "\$params['post_type'] = 'post';\n"; + if (isset($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (isset($attr['post_lang'])) { + $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n"; + } + + $p .= "\$params['next'] = \$_ctx->archives->dt;"; + + $res = "archives = $core->blog->getDates($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'archives->fetch()) : ?>'.$content.'archives = null; ?>'; + + return $res; + } + + /*dtd + + + */ + public function ArchivePrevious($attr,$content) + { + $p = '$params = array();'; + $p .= "\$params['type'] = 'month';\n"; + if (isset($attr['type'])) { + $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n"; + } + + $p .= "\$params['post_type'] = 'post';\n"; + if (isset($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (isset($attr['post_lang'])) { + $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n"; + } + + $p .= "\$params['previous'] = \$_ctx->archives->dt;"; + + $res = "archives = $core->blog->getDates($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'archives->fetch()) : ?>'.$content.'archives = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function ArchiveURL($attr) + { + $f = $this->getFilters($attr); + return 'archives->url($core)').'; ?>'; + } + + /* Attachments ---------------------------------------- */ + /*dtd + + */ + public function Attachments($attr,$content) + { + $res = + "posts !== null && $core->media) {'."\n". + '$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id));'."\n". + "?>\n". + + 'attachments as $attach_i => $attach_f) : '. + '$GLOBALS[\'attach_i\'] = $attach_i; $GLOBALS[\'attach_f\'] = $attach_f;'. + '$_ctx->file_url = $attach_f->file_url; ?>'. + $content. + 'attachments = null; unset($attach_i,$attach_f,$_ctx->file_url); ?>'. + + "\n"; + + return $res; + } + + /*dtd + + */ + public function AttachmentsHeader($attr,$content) + { + return + "". + $content. + ""; + } + + /*dtd + + */ + public function AttachmentsFooter($attr,$content) + { + return + "attachments)) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function AttachmentIf($attr,$content) + { + $if = array(); + + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['is_image'])) { + $sign = (boolean) $attr['is_image'] ? '' : '!'; + $if[] = $sign.'$attach_f->media_image'; + } + + if (isset($attr['has_thumb'])) { + $sign = (boolean) $attr['has_thumb'] ? '' : '!'; + $if[] = $sign.'isset($attach_f->media_thumb[\'sq\'])'; + } + + if (isset($attr['is_mp3'])) { + $sign = (boolean) $attr['is_mp3'] ? '==' : '!='; + $if[] = '$attach_f->type '.$sign.' "audio/mpeg3"'; + } + + if (isset($attr['is_flv'])) { + $sign = (boolean) $attr['is_flv'] ? '' : '!'; + $if[] = $sign. + '($attach_f->type == "video/x-flv" || '. + '$attach_f->type == "video/mp4" || '. + '$attach_f->type == "video/x-m4v")'; + } + + if (!empty($if)) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + */ + public function AttachmentMimeType($attr) + { + $f = $this->getFilters($attr); + return 'type').'; ?>'; + } + + /*dtd + + */ + public function AttachmentType($attr) + { + $f = $this->getFilters($attr); + return 'media_type').'; ?>'; + } + + /*dtd + + */ + public function AttachmentFileName($attr) + { + $f = $this->getFilters($attr); + return 'basename').'; ?>'; + } + + /*dtd + + + */ + public function AttachmentSize($attr) + { + $f = $this->getFilters($attr); + if (!empty($attr['full'])) { + return 'size').'; ?>'; + } + return 'size)').'; ?>'; + } + + /*dtd + + */ + public function AttachmentTitle($attr) + { + $f = $this->getFilters($attr); + return 'media_title').'; ?>'; + } + + /*dtd + + */ + public function AttachmentThumbnailURL($attr) + { + $f = $this->getFilters($attr); + return + 'media_thumb[\'sq\'])) {'. + 'echo '.sprintf($f,'$attach_f->media_thumb[\'sq\']').';'. + '}'. + '?>'; + } + + /*dtd + + */ + public function AttachmentURL($attr) + { + $f = $this->getFilters($attr); + return 'file_url').'; ?>'; + } + + public function MediaURL($attr) + { + $f = $this->getFilters($attr); + return 'file_url').'; ?>'; + } + + /* Blog ----------------------------------------------- */ + /*dtd + + */ + public function BlogArchiveURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getBase("archive")').'; ?>'; + } + + /*dtd + + */ + public function BlogCopyrightNotice($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->copyright_notice').'; ?>'; + } + + /*dtd + + */ + public function BlogDescription($attr) + { + $f = $this->getFilters($attr); + return 'blog->desc').'; ?>'; + } + + /*dtd + + */ + public function BlogEditor($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->editor').'; ?>'; + } + + /*dtd + + */ + public function BlogFeedID($attr) + { + $f = $this->getFilters($attr); + return 'blog->uid').'; ?>'; + } + + /*dtd + + + */ + public function BlogFeedURL($attr) + { + $type = !empty($attr['type']) ? $attr['type'] : 'atom'; + + if (!preg_match('#^(rss2|atom)$#',$type)) { + $type = 'atom'; + } + + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getBase("feed")."/'.$type.'"').'; ?>'; + } + + /*dtd + + */ + public function BlogName($attr) + { + $f = $this->getFilters($attr); + return 'blog->name').'; ?>'; + } + + /*dtd + + */ + public function BlogLanguage($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->lang').'; ?>'; + } + + /*dtd + + */ + public function BlogThemeURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->themes_url."/".$core->blog->settings->theme').'; ?>'; + } + + /*dtd + + */ + public function BlogPublicURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->public_url').'; ?>'; + } + + /*dtd + + + */ + public function BlogUpdateDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } else { + $format = '%Y-%m-%d %H:%M:%S'; + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'blog->upddt,\$core->blog->settings->blog_timezone)").'; ?>'; + } elseif ($iso8601) { + return 'blog->upddt,\$core->blog->settings->blog_timezone)").'; ?>'; + } else { + return 'blog->upddt)").'; ?>'; + } + } + + /*dtd + + */ + public function BlogID($attr) + { + $f = $this->getFilters($attr); + return 'blog->id').'; ?>'; + } + + /*dtd + + */ + public function BlogRSDURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getBase(\'rsd\')').'; ?>'; + } + + /*dtd + + */ + public function BlogURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url').'; ?>'; + } + + /*dtd + + */ + public function BlogQmarkURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->getQmarkURL()').'; ?>'; + } + + /*dtd + + + */ + public function BlogMetaRobots($attr) + { + $robots = isset($attr['robots']) ? addslashes($attr['robots']) : ''; + return "blog->settings->robots_policy,'".$robots."'); ?>"; + } + + /* Categories ----------------------------------------- */ + + /*dtd + + */ + public function Categories($attr,$content) + { + $p = "\$params = array();\n"; + + if (isset($attr['url'])) { + $p .= "\$params['cat_url'] = '".addslashes($attr['url'])."';\n"; + } + + if (!empty($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (!empty($attr['level'])) { + $p .= "\$params['level'] = ".(integer) $attr['level'].";\n"; + } + + $res = "categories = $core->blog->getCategories($params);'."\n"; + $res .= "?>\n"; + $res .= 'categories->fetch()) : ?>'.$content.'categories = null; unset($params); ?>'; + + return $res; + } + + /*dtd + + */ + public function CategoriesHeader($attr,$content) + { + return + "categories->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CategoriesFooter($attr,$content) + { + return + "categories->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function CategoryIf($attr,$content) + { + $if = array(); + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['url'])) { + $url = addslashes(trim($attr['url'])); + if (substr($url,0,1) == '!') { + $url = substr($url,1); + $if[] = '($_ctx->categories->cat_url != "'.$url.'")'; + } else { + $if[] = '($_ctx->categories->cat_url == "'.$url.'")'; + } + } + + if (isset($attr['has_entries'])) { + $sign = (boolean) $attr['has_entries'] ? '>' : '=='; + $if[] = '$_ctx->categories->nb_post '.$sign.' 0'; + } + + if (!empty($if)) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + */ + public function CategoryFirstChildren($attr,$content) + { + return + "categories = $core->blog->getCategoryFirstChildren($_ctx->categories->cat_id);'."\n". + 'while ($_ctx->categories->fetch()) : ?>'.$content.'categories = null; ?>'; + } + + /*dtd + + */ + public function CategoryParents($attr,$content) + { + return + "categories = $core->blog->getCategoryParents($_ctx->categories->cat_id);'."\n". + 'while ($_ctx->categories->fetch()) : ?>'.$content.'categories = null; ?>'; + } + + /*dtd + + + */ + public function CategoryFeedURL($attr) + { + $type = !empty($attr['type']) ? $attr['type'] : 'atom'; + + if (!preg_match('#^(rss2|atom)$#',$type)) { + $type = 'atom'; + } + + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getBase("feed")."/category/".'. + '$_ctx->categories->cat_url."/'.$type.'"').'; ?>'; + } + + /*dtd + + */ + public function CategoryURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getBase("category")."/".$_ctx->categories->cat_url').'; ?>'; + } + + /*dtd + + */ + public function CategoryShortURL($attr) + { + $f = $this->getFilters($attr); + return 'categories->cat_url').'; ?>'; + } + + /*dtd + + */ + public function CategoryDescription($attr) + { + $f = $this->getFilters($attr); + return 'categories->cat_desc').'; ?>'; + } + + /*dtd + + */ + public function CategoryTitle($attr) + { + $f = $this->getFilters($attr); + return 'categories->cat_title').'; ?>'; + } + + /* Entries -------------------------------------------- */ + /*dtd + + + */ + public function Entries($attr,$content) + { + $lastn = -1; + if (isset($attr['lastn'])) { + $lastn = abs((integer) $attr['lastn'])+0; + } + + $p = 'if (!isset($_page_number)) { $_page_number = 1; }'."\n"; + + if ($lastn != 0) { + if ($lastn > 0) { + $p .= "\$params['limit'] = ".$lastn.";\n"; + } else { + $p .= "\$params['limit'] = \$_ctx->nb_entry_per_page;\n"; + } + + if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == "0") { + $p .= "\$params['limit'] = array(((\$_page_number-1)*\$params['limit']),\$params['limit']);\n"; + } else { + $p .= "\$params['limit'] = array(0, \$params['limit']);\n"; + } + } + + if (isset($attr['author'])) { + $p .= "\$params['user_id'] = '".addslashes($attr['author'])."';\n"; + } + + if (isset($attr['category'])) { + $p .= "\$params['cat_url'] = '".addslashes($attr['category'])."';\n"; + $p .= "context::categoryPostParam(\$params);\n"; + } + + if (isset($attr['no_category'])) { + $p .= "@\$params['sql'] .= ' AND P.cat_id IS NULL ';\n"; + $p .= "unset(\$params['cat_url']);\n"; + } + + if (!empty($attr['type'])) { + $p .= "\$params['post_type'] = preg_split('/\s*,\s*/','".addslashes($attr['type'])."',-1,PREG_SPLIT_NO_EMPTY);\n"; + } + + if (!empty($attr['url'])) { + $p .= "\$params['post_url'] = '".addslashes($attr['url'])."';\n"; + } + + if (empty($attr['no_context'])) + { + $p .= + 'if ($_ctx->exists("users")) { '. + "\$params['user_id'] = \$_ctx->users->user_id; ". + "}\n"; + + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + + $p .= + 'if ($_ctx->exists("archives")) { '. + "\$params['post_year'] = \$_ctx->archives->year(); ". + "\$params['post_month'] = \$_ctx->archives->month(); ". + "unset(\$params['limit']); ". + "}\n"; + + $p .= + 'if ($_ctx->exists("langs")) { '. + "\$params['post_lang'] = \$_ctx->langs->post_lang; ". + "}\n"; + + $p .= + 'if (isset($_search)) { '. + "\$params['search'] = \$_search; ". + "}\n"; + } + + + + $sortby = 'post_dt'; + $order = 'desc'; + if (isset($attr['sortby'])) { + switch ($attr['sortby']) { + case 'title': $sortby = 'post_title'; break; + case 'selected' : $sortby = 'post_selected'; break; + case 'author' : $sortby = 'user_id'; break; + case 'date' : $sortby = 'post_dt'; break; + case 'id' : $sortby = 'post_id'; break; + } + } + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $order = $attr['order']; + } + + $p .= "\$params['order'] = '".$sortby." ".$order."';\n"; + + if (isset($attr['no_content']) && $attr['no_content']) { + $p .= "\$params['no_content'] = true;\n"; + } + + if (isset($attr['selected'])) { + $p .= "\$params['post_selected'] = ".(integer) (boolean) $attr['selected'].";"; + } + + $res = "post_params = $params;'."\n"; + $res .= '$_ctx->posts = $core->blog->getPosts($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'posts->fetch()) : ?>'.$content.'posts = null; $_ctx->post_params = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function DateHeader($attr,$content) + { + return + "posts->firstPostOfDay()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function DateFooter($attr,$content) + { + return + "posts->lastPostOfDay()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function EntryIf($attr,$content) + { + $if = array(); + $extended = null; + $hascategory = null; + + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['type'])) { + $type = trim($attr['type']); + $type = !empty($type)?$type:'post'; + $if[] = '$_ctx->posts->post_type == "'.addslashes($type).'"'; + } + + if (isset($attr['url'])) { + $url = trim($attr['url']); + if (substr($url,0,1) == '!') { + $url = substr($url,1); + $if[] = '$_ctx->posts->post_url != "'.addslashes($url).'"'; + } else { + $if[] = '$_ctx->posts->post_url == "'.addslashes($url).'"'; + } + } + + if (isset($attr['category'])) { + $category = addslashes(trim($attr['category'])); + if (substr($category,0,1) == '!') { + $category = substr($category,1); + $if[] = '($_ctx->posts->cat_url != "'.$category.'")'; + } else { + $if[] = '($_ctx->posts->cat_url == "'.$category.'")'; + } + } + + if (isset($attr['first'])) { + $sign = (boolean) $attr['first'] ? '=' : '!'; + $if[] = '$_ctx->posts->index() '.$sign.'= 0'; + } + + if (isset($attr['odd'])) { + $sign = (boolean) $attr['odd'] ? '=' : '!'; + $if[] = '($_ctx->posts->index()+1)%2 '.$sign.'= 1'; + } + + if (isset($attr['extended'])) { + $sign = (boolean) $attr['extended'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->isExtended()'; + } + + if (isset($attr['selected'])) { + $sign = (boolean) $attr['selected'] ? '' : '!'; + $if[] = $sign.'(boolean)$_ctx->posts->post_selected'; + } + + if (isset($attr['has_category'])) { + $sign = (boolean) $attr['has_category'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->cat_id'; + } + + if (isset($attr['has_attachment'])) { + $sign = (boolean) $attr['has_attachment'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->countMedia()'; + } + + if (isset($attr['comments_active'])) { + $sign = (boolean) $attr['comments_active'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->commentsActive()'; + } + + if (isset($attr['pings_active'])) { + $sign = (boolean) $attr['pings_active'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->trackbacksActive()'; + } + + if (isset($attr['has_comment'])) { + $sign = (boolean) $attr['has_comment'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->hasComments()'; + } + + if (isset($attr['has_ping'])) { + $sign = (boolean) $attr['has_ping'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->hasTrackbacks()'; + } + + if (isset($attr['show_comments'])) { + if ((boolean) $attr['show_comments']) { + $if[] = '($_ctx->posts->hasComments() || $_ctx->posts->commentsActive())'; + } else { + $if[] = '(!$_ctx->posts->hasComments() && !$_ctx->posts->commentsActive())'; + } + } + + if (isset($attr['show_pings'])) { + if ((boolean) $attr['show_pings']) { + $if[] = '($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())'; + } else { + $if[] = '(!$_ctx->posts->hasTrackbacks() && !$_ctx->posts->trackbacksActive())'; + } + } + + if (!empty($if)) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + + */ + public function EntryIfFirst($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'first'; + $ret = html::escapeHTML($ret); + + return + 'posts->index() == 0) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function EntryIfOdd($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'odd'; + $ret = html::escapeHTML($ret); + + return + 'posts->index()+1)%2 == 1) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function EntryIfSelected($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'selected'; + $ret = html::escapeHTML($ret); + + return + 'posts->post_selected) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function EntryContent($attr) + { + $urls = '0'; + if (!empty($attr['absolute_urls'])) { + $urls = '1'; + } + + $f = $this->getFilters($attr); + + if (!empty($attr['full'])) { + return 'posts->getExcerpt('.$urls.')." ".$_ctx->posts->getContent('.$urls.')').'; ?>'; + } else { + return 'posts->getContent('.$urls.')').'; ?>'; + } + } + + /*dtd + + + */ + public function EntryExcerpt($attr) + { + $urls = '0'; + if (!empty($attr['absolute_urls'])) { + $urls = '1'; + } + + $f = $this->getFilters($attr); + return 'posts->getExcerpt('.$urls.')').'; ?>'; + } + + /*dtd + + + */ + public function EntryAttachmentCount($attr) + { + $none = 'no attachment'; + $one = 'one attachment'; + $more = '%d attachments'; + + if (isset($attr['none'])) { + $none = addslashes($attr['none']); + } + if (isset($attr['one'])) { + $one = addslashes($attr['one']); + } + if (isset($attr['more'])) { + $more = addslashes($attr['more']); + } + + return + "posts->countMedia() == 0) {\n". + " printf(__('".$none."'),(integer) \$_ctx->posts->countMedia());\n". + "} elseif (\$_ctx->posts->countMedia() == 1) {\n". + " printf(__('".$one."'),(integer) \$_ctx->posts->countMedia());\n". + "} else {\n". + " printf(__('".$more."'),(integer) \$_ctx->posts->countMedia());\n". + "} ?>"; + } + + /*dtd + + */ + public function EntryAuthorCommonName($attr) + { + $f = $this->getFilters($attr); + return 'posts->getAuthorCN()').'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorDisplayName($attr) + { + $f = $this->getFilters($attr); + return 'posts->user_displayname').'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorID($attr) + { + $f = $this->getFilters($attr); + return 'posts->user_id').'; ?>'; + } + + /*dtd + + + */ + public function EntryAuthorEmail($attr) + { + $p = 'true'; + if (isset($attr['spam_protected']) && !$attr['spam_protected']) { + $p = 'false'; + } + + $f = $this->getFilters($attr); + return 'posts->getAuthorEmail(".$p.")").'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorLink($attr) + { + $f = $this->getFilters($attr); + return 'posts->getAuthorLink()').'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->user_url').'; ?>'; + } + + /*dtd + + */ + public function EntryBasename($attr) + { + $f = $this->getFilters($attr); + return 'posts->post_url').'; ?>'; + } + + /*dtd + + */ + public function EntryCategory($attr) + { + $f = $this->getFilters($attr); + return 'posts->cat_title').'; ?>'; + } + + /*dtd + + */ + public function EntryCategoriesBreadcrumb($attr,$content) + { + return + "categories = $core->blog->getCategoryParents($_ctx->posts->cat_id);'."\n". + 'while ($_ctx->categories->fetch()) : ?>'.$content.'categories = null; ?>'; + } + + /*dtd + + */ + public function EntryCategoryID($attr) + { + $f = $this->getFilters($attr); + return 'posts->cat_id').'; ?>'; + } + + /*dtd + + */ + public function EntryCategoryURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->getCategoryURL()').'; ?>'; + } + + /*dtd + + */ + public function EntryCategoryShortURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->cat_url').'; ?>'; + } + + + /*dtd + + */ + public function EntryFeedID($attr) + { + $f = $this->getFilters($attr); + return 'posts->getFeedID()').'; ?>'; + } + + /*dtd + + + */ + public function EntryFirstImage($attr) + { + $size = !empty($attr['size']) ? $attr['size'] : ''; + $class = !empty($attr['class']) ? $attr['class'] : ''; + $with_category = !empty($attr['with_category']) ? 'true' : 'false'; + + return ""; + } + + /*dtd + + */ + public function EntryID($attr) + { + $f = $this->getFilters($attr); + return 'posts->post_id').'; ?>'; + } + + /*dtd + + */ + public function EntryLang($attr) + { + $f = $this->getFilters($attr); + return + 'posts->post_lang) { '. + 'echo '.sprintf($f,'$_ctx->posts->post_lang').'; '. + '} else {'. + 'echo '.sprintf($f,'$core->blog->settings->lang').'; '. + '} ?>'; + } + + /*dtd + + + */ + public function EntryNext($attr,$content) + { + $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0'; + $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0'; + + return + 'blog->getNextPost($_ctx->posts,1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n". + ''. + + 'posts = $next_post; unset($next_post);'."\n". + 'while ($_ctx->posts->fetch()) : ?>'. + $content. + 'posts = null; ?>'. + "\n"; + } + + /*dtd + + + */ + public function EntryPrevious($attr,$content) + { + $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0'; + $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0'; + + return + 'blog->getNextPost($_ctx->posts,-1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n". + ''. + + 'posts = $prev_post; unset($prev_post);'."\n". + 'while ($_ctx->posts->fetch()) : ?>'. + $content. + 'posts = null; ?>'. + "\n"; + } + + /*dtd + + */ + public function EntryTitle($attr) + { + $f = $this->getFilters($attr); + return 'posts->post_title').'; ?>'; + } + + /*dtd + + */ + public function EntryURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->getURL()').'; ?>'; + } + + /*dtd + + + */ + public function EntryDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'posts->getRFC822Date()").'; ?>'; + } elseif ($iso8601) { + return 'posts->getISO8601Date()").'; ?>'; + } else { + return 'posts->getDate('".$format."')").'; ?>'; + } + } + + /*dtd + + + */ + public function EntryTime($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $f = $this->getFilters($attr); + return 'posts->getTime('".$format."')").'; ?>'; + } + + /*dtd + + */ + public function EntriesHeader($attr,$content) + { + return + "posts->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function EntriesFooter($attr,$content) + { + return + "posts->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function EntryCommentCount($attr) + { + $none = 'no comment'; + $one = 'one comment'; + $more = '%d comments'; + + if (isset($attr['none'])) { + $none = addslashes($attr['none']); + } + if (isset($attr['one'])) { + $one = addslashes($attr['one']); + } + if (isset($attr['more'])) { + $more = addslashes($attr['more']); + } + + if (empty($attr['count_all'])) { + $operation = '$_ctx->posts->nb_comment'; + } else { + $operation = '($_ctx->posts->nb_comment + $_ctx->posts->nb_trackback)'; + } + + return + ""; + } + + /*dtd + + + */ + public function EntryPingCount($attr) + { + $none = 'no trackback'; + $one = 'one trackback'; + $more = '%d trackbacks'; + + if (isset($attr['none'])) { + $none = addslashes($attr['none']); + } + if (isset($attr['one'])) { + $one = addslashes($attr['one']); + } + if (isset($attr['more'])) { + $more = addslashes($attr['more']); + } + + return + "posts->nb_trackback == 0) {\n". + " printf(__('".$none."'),(integer) \$_ctx->posts->nb_trackback);\n". + "} elseif (\$_ctx->posts->nb_trackback == 1) {\n". + " printf(__('".$one."'),(integer) \$_ctx->posts->nb_trackback);\n". + "} else {\n". + " printf(__('".$more."'),(integer) \$_ctx->posts->nb_trackback);\n". + "} ?>"; + } + + /*dtd + + */ + public function EntryPingData($attr) + { + return "posts->trackbacksActive()) { echo \$_ctx->posts->getTrackbackData(); } ?>\n"; + } + + /*dtd + + */ + public function EntryPingLink($attr) + { + return "posts->trackbacksActive()) { echo \$_ctx->posts->getTrackbackLink(); } ?>\n"; + } + + /* Languages -------------------------------------- */ + /*dtd + + + */ + public function Languages($attr,$content) + { + $p = '$params = array();'; + + if (isset($attr['lang'])) { + $p = "\$params['lang'] = '".addslashes($attr['lang'])."';\n"; + } + + $order = 'desc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $p .= "\$params['order'] = '".$attr['order']."';\n "; + } + + $res = "langs = $core->blog->getLangs($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'langs->count() > 1) : '. + 'while ($_ctx->langs->fetch()) : ?>'.$content. + 'langs = null; endif; ?>'; + + return $res; + } + + /*dtd + + */ + public function LanguagesHeader($attr,$content) + { + return + "langs->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function LanguagesFooter($attr,$content) + { + return + "langs->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function LanguageCode($attr) + { + $f = $this->getFilters($attr); + return 'langs->post_lang').'; ?>'; + } + + /*dtd + + */ + public function LanguageIfCurrent($attr,$content) + { + return + "cur_lang == \$_ctx->langs->post_lang) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function LanguageURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getBase("lang").$_ctx->langs->post_lang').'; ?>'; + } + + /* Pagination ------------------------------------- */ + /*dtd + + + */ + public function Pagination($attr,$content) + { + $p = "post_params;'."\n"; + $p .= '$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);'."\n"; + $p .= "?>\n"; + + if (isset($attr['no_context'])) { + return $p.$content; + } + + return + $p. + 'pagination->f(0) > $_ctx->posts->count()) : ?>'. + $content. + ''; + } + + /*dtd + + */ + public function PaginationCounter($attr) + { + $f = $this->getFilters($attr); + return ''; + } + + /*dtd + + */ + public function PaginationCurrent($attr) + { + $offset = 0; + if (isset($attr['offset'])) { + $offset = (integer) $attr['offset']; + } + + $f = $this->getFilters($attr); + return ''; + } + + /*dtd + + + */ + public function PaginationIf($attr,$content) + { + $if = array(); + + if (isset($attr['start'])) { + $sign = (boolean) $attr['start'] ? '' : '!'; + $if[] = $sign.'context::PaginationStart()'; + } + + if (isset($attr['end'])) { + $sign = (boolean) $attr['end'] ? '' : '!'; + $if[] = $sign.'context::PaginationEnd()'; + } + + if (!empty($if)) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + + */ + public function PaginationURL($attr) + { + $offset = 0; + if (isset($attr['offset'])) { + $offset = (integer) $attr['offset']; + } + + $f = $this->getFilters($attr); + return ''; + } + + /* Comments --------------------------------------- */ + /*dtd + + + */ + public function Comments($attr,$content) + { + $p = + "if (\$_ctx->posts !== null) { ". + "\$params['post_id'] = \$_ctx->posts->post_id; ". + "\$core->blog->withoutPassword(false);\n". + "}\n"; + + if (empty($attr['with_pings'])) { + $p .= "\$params['comment_trackback'] = false;\n"; + } + + $lastn = 0; + if (isset($attr['lastn'])) { + $lastn = abs((integer) $attr['lastn'])+0; + } + + if ($lastn > 0) { + $p .= "\$params['limit'] = ".$lastn.";\n"; + } else { + $p .= "if (\$_ctx->nb_comment_per_page !== null) { \$params['limit'] = \$_ctx->nb_comment_per_page; }\n"; + } + + if (empty($attr['no_context'])) + { + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + + $p .= + 'if ($_ctx->exists("langs")) { '. + "\$params['sql'] = \"AND P.post_lang = '\".\$core->blog->con->escape(\$_ctx->langs->post_lang).\"' \"; ". + "}\n"; + } + + $order = 'asc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $order = $attr['order']; + } + + $p .= "\$params['order'] = 'comment_dt ".$order."';\n"; + + if (isset($attr['no_content']) && $attr['no_content']) { + $p .= "\$params['no_content'] = true;\n"; + } + + $res = "comments = $core->blog->getComments($params); unset($params);'."\n"; + $res .= "if (\$_ctx->posts !== null) { \$core->blog->withoutPassword(true);}\n"; + + if (!empty($attr['with_pings'])) { + $res .= '$_ctx->pings = $_ctx->comments;'."\n"; + } + + $res .= "?>\n"; + + $res .= + 'comments->fetch()) : ?>'.$content.'comments = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function CommentAuthor($attr) + { + $f = $this->getFilters($attr); + return 'comments->comment_author").'; ?>'; + } + + /*dtd + + */ + public function CommentAuthorDomain($attr) + { + return 'comments->comment_site); ?>'; + } + + /*dtd + + */ + public function CommentAuthorLink($attr) + { + $f = $this->getFilters($attr); + return 'comments->getAuthorLink()').'; ?>'; + } + + /*dtd + + */ + public function CommentAuthorMailMD5($attr) + { + return 'comments->comment_email) ; ?>'; + } + + /*dtd + + */ + public function CommentAuthorURL($attr) + { + $f = $this->getFilters($attr); + return 'comments->getAuthorURL()').'; ?>'; + } + + /*dtd + + + */ + public function CommentContent($attr) + { + $urls = '0'; + if (!empty($attr['absolute_urls'])) { + $urls = '1'; + } + + $f = $this->getFilters($attr); + return 'comments->getContent('.$urls.')').'; ?>'; + } + + /*dtd + + + */ + public function CommentDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'comments->getRFC822Date()").'; ?>'; + } elseif ($iso8601) { + return 'comments->getISO8601Date()").'; ?>'; + } else { + return 'comments->getDate('".$format."')").'; ?>'; + } + } + + /*dtd + + + */ + public function CommentTime($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $f = $this->getFilters($attr); + return 'comments->getTime('".$format."')").'; ?>'; + } + + /*dtd + + + */ + public function CommentEmail($attr) + { + $p = 'true'; + if (isset($attr['spam_protected']) && !$attr['spam_protected']) { + $p = 'false'; + } + + $f = $this->getFilters($attr); + return 'comments->getEmail(".$p.")").'; ?>'; + } + + /*dtd + + */ + public function CommentEntryTitle($attr) + { + $f = $this->getFilters($attr); + return 'comments->post_title').'; ?>'; + } + + /*dtd + + */ + public function CommentFeedID($attr) + { + $f = $this->getFilters($attr); + return 'comments->getFeedID()').'; ?>'; + } + + /*dtd + + */ + public function CommentID($attr) + { + return 'comments->comment_id; ?>'; + } + + /*dtd + + + */ + public function CommentIf($attr,$content) + { + $if = array(); + $is_ping = null; + + if (isset($attr['is_ping'])) { + $sign = (boolean) $attr['is_ping'] ? '' : '!'; + $if[] = $sign.'$_ctx->comments->comment_trackback'; + } + + if (!empty($if)) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + + */ + public function CommentIfFirst($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'first'; + $ret = html::escapeHTML($ret); + + return + 'comments->index() == 0) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function CommentIfMe($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'me'; + $ret = html::escapeHTML($ret); + + return + 'comments->isMe()) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function CommentIfOdd($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'odd'; + $ret = html::escapeHTML($ret); + + return + 'comments->index()+1)%2) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + */ + public function CommentIP($attr) + { + return 'comments->comment_ip; ?>'; + } + + /*dtd + + */ + public function CommentOrderNumber($attr) + { + return 'comments->index()+1; ?>'; + } + + /*dtd + + */ + public function CommentsFooter($attr,$content) + { + return + "comments->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CommentsHeader($attr,$content) + { + return + "comments->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CommentPostURL($attr) + { + $f = $this->getFilters($attr); + return 'comments->getPostURL()').'; ?>'; + } + + /*dtd + + */ + public function IfCommentAuthorEmail($attr,$content) + { + return + "comments->comment_email) : ?>". + $content. + ""; + } + + /* Comment preview -------------------------------- */ + /*dtd + + */ + public function IfCommentPreview($attr,$content) + { + return + 'comment_preview !== null && $_ctx->comment_preview["preview"]) : ?>'. + $content. + ''; + } + + /*dtd + + */ + public function CommentPreviewName($attr) + { + $f = $this->getFilters($attr); + return 'comment_preview["name"]').'; ?>'; + } + + /*dtd + + */ + public function CommentPreviewEmail($attr) + { + $f = $this->getFilters($attr); + return 'comment_preview["mail"]').'; ?>'; + } + + /*dtd + + */ + public function CommentPreviewSite($attr) + { + $f = $this->getFilters($attr); + return 'comment_preview["site"]').'; ?>'; + } + + /*dtd + + + */ + public function CommentPreviewContent($attr) + { + $f = $this->getFilters($attr); + + if (!empty($attr['raw'])) { + $co = '$_ctx->comment_preview["rawcontent"]'; + } else { + $co = '$_ctx->comment_preview["content"]'; + } + + return ''; + } + + /*dtd + + */ + public function CommentPreviewCheckRemember($attr) + { + return + "comment_preview['remember']) { echo ' checked=\"checked\"'; } ?>"; + } + + /* Trackbacks ------------------------------------- */ + /*dtd + + */ + public function PingBlogName($attr) + { + $f = $this->getFilters($attr); + return 'pings->comment_author').'; ?>'; + } + + /*dtd + + */ + public function PingContent($attr) + { + $f = $this->getFilters($attr); + return 'pings->getTrackbackContent()').'; ?>'; + } + + /*dtd + + + */ + public function PingDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'pings->getRFC822Date()").'; ?>'; + } elseif ($iso8601) { + return 'pings->getISO8601Date()").'; ?>'; + } else { + return 'pings->getDate('".$format."')").'; ?>'; + } + } + + /*dtd + + + */ + public function PingTime($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $f = $this->getFilters($attr); + return 'pings->getTime('".$format."')").'; ?>'; + } + + /*dtd + + */ + public function PingEntryTitle($attr) + { + $f = $this->getFilters($attr); + return 'pings->post_title').'; ?>'; + } + + /*dtd + + */ + public function PingFeedID($attr) + { + $f = $this->getFilters($attr); + return 'pings->getFeedID()').'; ?>'; + } + + /*dtd + + */ + public function PingID($attr) + { + return 'pings->comment_id; ?>'; + } + + /*dtd + + + */ + public function PingIfFirst($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'first'; + $ret = html::escapeHTML($ret); + + return + 'pings->index() == 0) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function PingIfOdd($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'odd'; + $ret = html::escapeHTML($ret); + + return + 'pings->index()+1)%2) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + */ + public function PingIP($attr) + { + return 'pings->comment_ip; ?>'; + } + + /*dtd + + */ + public function PingNoFollow($attr) + { + return + 'blog->settings->comments_nofollow) { '. + 'echo \' rel="nofollow"\';'. + '} ?>'; + } + + /*dtd + + */ + public function PingOrderNumber($attr) + { + return 'pings->index()+1; ?>'; + } + + /*dtd + + */ + public function PingPostURL($attr) + { + $f = $this->getFilters($attr); + return 'pings->getPostURL()').'; ?>'; + } + + /*dtd + + + */ + public function Pings($attr,$content) + { + $p = + "if (\$_ctx->posts !== null) { ". + "\$params['post_id'] = \$_ctx->posts->post_id; ". + "\$core->blog->withoutPassword(false);\n". + "}\n"; + + $p .= "\$params['comment_trackback'] = true;\n"; + + $lastn = 0; + if (isset($attr['lastn'])) { + $lastn = abs((integer) $attr['lastn'])+0; + } + + if ($lastn > 0) { + $p .= "\$params['limit'] = ".$lastn.";\n"; + } else { + $p .= "if (\$_ctx->nb_comment_per_page !== null) { \$params['limit'] = \$_ctx->nb_comment_per_page; }\n"; + } + + if (empty($attr['no_context'])) + { + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + + $p .= + 'if ($_ctx->exists("langs")) { '. + "\$params['sql'] = \"AND P.post_lang = '\".\$core->blog->con->escape(\$_ctx->langs->post_lang).\"' \"; ". + "}\n"; + } + + $order = 'asc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $order = $attr['order']; + } + + $p .= "\$params['order'] = 'comment_dt ".$order."';\n"; + + if (isset($attr['no_content']) && $attr['no_content']) { + $p .= "\$params['no_content'] = true;\n"; + } + + $res = "pings = $core->blog->getComments($params); unset($params);'."\n"; + $res .= "if (\$_ctx->posts !== null) { \$core->blog->withoutPassword(true);}\n"; + $res .= "?>\n"; + + $res .= + 'pings->fetch()) : ?>'.$content.'pings = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function PingsFooter($attr,$content) + { + return + "pings->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function PingsHeader($attr,$content) + { + return + "pings->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function PingTitle($attr) + { + $f = $this->getFilters($attr); + return 'pings->getTrackbackTitle()').'; ?>'; + } + + /*dtd + + */ + public function PingAuthorURL($attr) + { + $f = $this->getFilters($attr); + return 'pings->getAuthorURL()').'; ?>'; + } + + # System + /*dtd + + + */ + public function SysBehavior($attr,$raw) + { + if (!isset($attr['behavior'])) { + return; + } + + $b = addslashes($attr['behavior']); + return + 'hasBehavior(\''.$b.'\')) { '. + '$core->callBehavior(\''.$b.'\',$core,$_ctx);'. + '} ?>'; + } + + /*dtd + + + */ + public function SysIf($attr,$content) + { + $if = array(); + $is_ping = null; + + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['categories'])) { + $sign = (boolean) $attr['categories'] ? '!' : '='; + $if[] = '$_ctx->categories '.$sign.'== null'; + } + + if (isset($attr['posts'])) { + $sign = (boolean) $attr['posts'] ? '!' : '='; + $if[] = '$_ctx->posts '.$sign.'== null'; + } + + if (isset($attr['blog_lang'])) { + $if[] = "\$core->blog->settings->lang == '".addslashes($attr['blog_lang'])."'"; + } + + if (isset($attr['current_tpl'])) { + $sign = '='; + if (substr($attr['current_tpl'],0,1) == '!') { + $sign = '!'; + $attr['current_tpl'] = substr($attr['current_tpl'],1); + } + $if[] = "\$_ctx->current_tpl ".$sign."= '".addslashes($attr['current_tpl'])."'"; + } + + if (isset($attr['current_mode'])) { + $sign = '='; + if (substr($attr['current_mode'],0,1) == '!') { + $sign = '!'; + $attr['current_mode'] = substr($attr['current_mode'],1); + } + $if[] = "\$core->url->type ".$sign."= '".addslashes($attr['current_mode'])."'"; + } + + if (isset($attr['has_tpl'])) { + $sign = ''; + if (substr($attr['has_tpl'],0,1) == '!') { + $sign = '!'; + $attr['has_tpl'] = substr($attr['has_tpl'],1); + } + $if[] = $sign."\$core->tpl->getFilePath('".addslashes($attr['has_tpl'])."') !== false"; + } + + if (isset($attr['has_tag'])) { + $sign = ''; + if (substr($attr['has_tag'],0,1) == '!') { + $sign = '!'; + $attr['has_tag'] = substr($attr['has_tag'],1); + } + $if[] = $sign."(\$core->tpl->tagExists('".addslashes($attr['has_tag'])."') )"; + } + + if (isset($attr['comments_active'])) { + $sign = (boolean) $attr['comments_active'] ? '' : '!'; + $if[] = $sign.'$core->blog->settings->allow_comments'; + } + + if (isset($attr['pings_active'])) { + $sign = (boolean) $attr['pings_active'] ? '' : '!'; + $if[] = $sign.'$core->blog->settings->allow_trackbacks'; + } + + if (isset($attr['wiki_comments'])) { + $sign = (boolean) $attr['wiki_comments'] ? '' : '!'; + $if[] = $sign.'$core->blog->settings->wiki_comments'; + } + + if (isset($attr['search_count']) && + preg_match('/^((=|!|>|<)=|(>|<))\s*[0-9]+$/',trim($attr['search_count']))) { + $if[] = '(isset($_search_count) && $_search_count '.html::decodeEntities($attr['search_count']).')'; + } + + if (!empty($if)) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + */ + public function SysIfCommentPublished($attr,$content) + { + return + ''. + $content. + ''; + } + + /*dtd + + */ + public function SysIfCommentPending($attr,$content) + { + return + ''. + $content. + ''; + } + + /*dtd + + */ + public function SysFeedSubtitle($attr) + { + $f = $this->getFilters($attr); + return 'feed_subtitle !== null) { echo '.sprintf($f,'$_ctx->feed_subtitle').';} ?>'; + } + + /*dtd + + */ + public function SysIfFormError($attr,$content) + { + return + 'form_error !== null) : ?>'. + $content. + ''; + } + + /*dtd + + */ + public function SysFormError($attr) + { + return + 'form_error !== null) { echo $_ctx->form_error; } ?>'; + } + + public function SysPoweredBy($attr) + { + return + 'Dotclear"); ?>'; + } + + public function SysSearchString($attr) + { + $s = isset($attr['string']) ? $attr['string'] : '%1$s'; + + $f = $this->getFilters($attr); + return ''; + } + + public function SysSelfURI($attr) + { + $f = $this->getFilters($attr); + return ''; + } +} +?> diff --git a/inc/public/default-templates/atom-comments.xml b/inc/public/default-templates/atom-comments.xml new file mode 100644 index 0000000..7cd4efb --- /dev/null +++ b/inc/public/default-templates/atom-comments.xml @@ -0,0 +1,45 @@ + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + - {{tpl:lang Comments}} + {{tpl:BlogDescription encode_xml="1"}} + + + {{tpl:BlogUpdateDate iso8601="1"}} + + {{tpl:BlogEditor encode_xml="1"}} + + {{tpl:BlogFeedID}} + Dotclear + + + + + + [ping] {{tpl:PingEntryTitle encode_xml="1"}} - {{tpl:PingBlogName encode_xml="1"}} + + {{tpl:PingFeedID}} + {{tpl:PingDate iso8601="1"}} + {{tpl:PingBlogName encode_xml="1"}} + <p><a href="{{tpl:PingAuthorURL encode_xml="1"}}">{{tpl:PingTitle encode_xml="1"}}</a></p> {{tpl:PingContent encode_xml="1"}} + + + + + + + {{tpl:CommentEntryTitle encode_xml="1"}} - {{tpl:CommentAuthor encode_xml="1"}} + + {{tpl:CommentFeedID}} + {{tpl:CommentDate iso8601="1"}} + {{tpl:CommentAuthor encode_xml="1"}} + {{tpl:CommentContent absolute_urls="1" encode_xml="1"}} + + + + + \ No newline at end of file diff --git a/inc/public/default-templates/atom.xml b/inc/public/default-templates/atom.xml new file mode 100644 index 0000000..483c003 --- /dev/null +++ b/inc/public/default-templates/atom.xml @@ -0,0 +1,49 @@ + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + {{tpl:BlogDescription encode_xml="1"}} + + + {{tpl:BlogUpdateDate iso8601="1"}} + + {{tpl:BlogEditor encode_xml="1"}} + + {{tpl:BlogFeedID}} + Dotclear + + + + + {{tpl:EntryTitle encode_xml="1"}} + + {{tpl:EntryFeedID}} + {{tpl:EntryDate iso8601="1"}} + {{tpl:EntryAuthorCommonName encode_xml="1"}} + + {{tpl:EntryCategory encode_html="1"}} + + {{tpl:MetaID}} + + {{tpl:EntryExcerpt absolute_urls="1" encode_xml="1"}} + {{tpl:EntryContent absolute_urls="1" encode_xml="1"}} + + + + + + + + {{tpl:EntryURL}}#comment-form + {{tpl:BlogFeedURL type="atom"}}/comments/{{tpl:EntryID}} + + + + + \ No newline at end of file diff --git a/inc/public/default-templates/password-form.html b/inc/public/default-templates/password-form.html new file mode 100644 index 0000000..bbbf63c --- /dev/null +++ b/inc/public/default-templates/password-form.html @@ -0,0 +1,43 @@ + + + + + + + {{tpl:lang Password needed}} - {{tpl:BlogName encode_html="1"}} + + + + + + +
    +

    {{tpl:lang Password needed}}

    + +

    {{tpl:lang You must give a password to access this area.}}

    +

    + +
    + + + \ No newline at end of file diff --git a/inc/public/default-templates/rss2-comments.xml b/inc/public/default-templates/rss2-comments.xml new file mode 100644 index 0000000..ccea31b --- /dev/null +++ b/inc/public/default-templates/rss2-comments.xml @@ -0,0 +1,48 @@ + + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + - {{tpl:lang Comments}} + {{tpl:BlogURL}} + + {{tpl:BlogDescription encode_xml="1"}} + {{tpl:BlogLanguage}} + {{tpl:BlogUpdateDate rfc822="1"}} + {{tpl:BlogCopyrightNotice encode_xml="1"}} + http://blogs.law.harvard.edu/tech/rss + Dotclear + + + + + + [ping] {{tpl:PingEntryTitle encode_xml="1"}} - {{tpl:PingBlogName encode_xml="1"}} + {{tpl:PingPostURL encode_xml="1"}}#c{{tpl:PingID}} + {{tpl:PingFeedID}} + {{tpl:PingDate rfc822="1"}} + {{tpl:PingBlogName encode_xml="1"}} + + <p><a href="{{tpl:PingAuthorURL encode_xml="1"}}">{{tpl:PingTitle encode_xml="1"}}</a></p> + {{tpl:PingContent encode_xml="1"}} + + + + + + {{tpl:CommentEntryTitle encode_xml="1"}} - {{tpl:CommentAuthor encode_xml="1"}} + {{tpl:CommentPostURL encode_xml="1"}}#c{{tpl:CommentID}} + {{tpl:CommentFeedID}} + {{tpl:CommentDate rfc822="1"}} + {{tpl:CommentAuthor encode_xml="1"}} + + {{tpl:CommentContent absolute_urls="1" encode_xml="1"}} + + + + + + \ No newline at end of file diff --git a/inc/public/default-templates/rss2.xml b/inc/public/default-templates/rss2.xml new file mode 100644 index 0000000..5e82dff --- /dev/null +++ b/inc/public/default-templates/rss2.xml @@ -0,0 +1,49 @@ + + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + {{tpl:BlogURL}} + + {{tpl:BlogDescription encode_xml="1"}} + {{tpl:BlogLanguage}} + {{tpl:BlogUpdateDate rfc822="1"}} + {{tpl:BlogCopyrightNotice encode_xml="1"}} + http://blogs.law.harvard.edu/tech/rss + Dotclear + + + + + {{tpl:EntryTitle encode_xml="1"}} + {{tpl:EntryURL}} + {{tpl:EntryFeedID}} + {{tpl:EntryDate rfc822="1"}} + {{tpl:EntryAuthorCommonName encode_xml="1"}} + + {{tpl:EntryCategory encode_html="1"}} + + {{tpl:MetaID}} + + {{tpl:EntryExcerpt absolute_urls="1" encode_xml="1"}} + {{tpl:EntryContent absolute_urls="1" encode_xml="1"}} + + + + + + + {{tpl:EntryURL}}#comment-form + {{tpl:EntryURL}}#comment-form + {{tpl:BlogFeedURL}}/comments/{{tpl:EntryID}} + + + + + + \ No newline at end of file diff --git a/inc/public/default-templates/rss2.xsl b/inc/public/default-templates/rss2.xsl new file mode 100644 index 0000000..9dcd864 --- /dev/null +++ b/inc/public/default-templates/rss2.xsl @@ -0,0 +1,120 @@ + + + + + + + + + {{tpl:lang Subscribe to}} <xsl:value-of select="/rss/channel/title"/> + + + + +
    +
    +

    +

    +
    +
    +

    {{tpl:lang What is an RSS feed?}}

    +

    {{tpl:lang RSS feed is a free blog summary. It provides content + (either posts or comments) or summaries of content, together with links + to the full versions, and other metadata. The last published items may + then be read by your favorite RSS + aggregator.}}

    +

    {{tpl:lang Subscribe}}

    +

    {{tpl:lang Simply copy the following URL into your aggregator:}}

    +

    +
    +
    + +
    + +
    + + +
    + + + +
    +

    +
    +
    +
    + +
    \ No newline at end of file diff --git a/inc/public/lib.tpl.context.php b/inc/public/lib.tpl.context.php new file mode 100644 index 0000000..3e37c67 --- /dev/null +++ b/inc/public/lib.tpl.context.php @@ -0,0 +1,435 @@ +pop($name); + } else { + $this->stack[$name][] =& $var; + if ($var instanceof record) { + $this->stack['cur_loop'][] =& $var; + } + } + } + + public function __get($name) + { + if (!isset($this->stack[$name])) { + return null; + } + + $n = count($this->stack[$name]); + if ($n > 0) { + return $this->stack[$name][($n-1)]; + } + + return null; + } + + public function exists($name) + { + return isset($this->stack[$name][0]); + } + + public function pop($name) + { + if (isset($this->stack[$name])) { + $v = array_pop($this->stack[$name]); + if ($v instanceof record) { + array_pop($this->stack['cur_loop']); + } + unset($v); + } + } + + # Loop position tests + public function loopPosition($start,$length=null,$even=null) + { + if (!$this->cur_loop) { + return false; + } + + $index = $this->cur_loop->index(); + $size = $this->cur_loop->count(); + + $test = false; + if ($start >= 0) + { + $test = $index >= $start; + if ($length !== null) { + if ($length >= 0) { + $test = $test && $index < $start + $length; + } else { + $test = $test && $index < $size + $length; + } + } + } + else + { + $test = $index >= $size + $start; + if ($length !== null) { + if ($length >= 0) { + $test = $test && $index < $size + $start + $length; + } else { + $test = $test && $index < $size + $length; + } + } + } + + if ($even !== null) { + $test = $test && $index%2 == $even; + } + + return $test; + } + + # Static methods + public static function global_filter($str, + $encode_xml, $remove_html, $cut_string, $lower_case, $upper_case ,$tag='') + { + $args = func_get_args(); + array_pop($args); + $args[0] =& $str; + + # --BEHAVIOR-- publicBeforeContentFilter + $res = $GLOBALS['core']->callBehavior('publicBeforeContentFilter',$GLOBALS['core'],$tag,$args); + + if ($remove_html) { + $str = self::remove_html($str); + $str = preg_replace('/\s+/',' ',$str); + } + + if ($encode_xml) { + $str = self::encode_xml($str); + } + + if ($cut_string) { + $str = self::cut_string($str,(integer) $cut_string); + } + + if ($lower_case) { + $str = self::lower_case($str); + } elseif ($upper_case) { + $str = self::upper_case($str); + } + + # --BEHAVIOR-- publicAfterContentFilter + $res = $GLOBALS['core']->callBehavior('publicAfterContentFilter',$GLOBALS['core'],$tag,$args); + + return $str; + } + + + public static function cut_string($str,$l) + { + return text::cutString($str,$l); + } + + public static function encode_xml($str) + { + return html::escapeHTML($str); + } + + public static function remove_html($str) + { + return html::decodeEntities(html::clean($str)); + } + + public static function lower_case($str) + { + return mb_strtolower($str); + } + + public static function upper_case($str) + { + return mb_strtoupper($str); + } + + public static function categoryPostParam(&$p) + { + $not = substr($p['cat_url'],0,1) == '!'; + if ($not) { + $p['cat_url'] = substr($p['cat_url'],1); + } + + $p['cat_url'] = preg_split('/\s*,\s*/',$p['cat_url'],-1,PREG_SPLIT_NO_EMPTY); + + foreach ($p['cat_url'] as &$v) + { + if ($not) { + $v .= ' ?not'; + } + if ($GLOBALS['_ctx']->exists('categories') && preg_match('/#self/',$v)) { + $v = preg_replace('/#self/',$GLOBALS['_ctx']->categories->cat_url,$v); + } elseif ($GLOBALS['_ctx']->exists('posts') && preg_match('/#self/',$v)) { + $v = preg_replace('/#self/',$GLOBALS['_ctx']->posts->cat_url,$v); + } + } + } + + # Static methods for pagination + public static function PaginationNbPages() + { + global $_ctx; + + if ($_ctx->pagination === null) { + return false; + } + + $nb_posts = $_ctx->pagination->f(0); + $nb_per_page = $_ctx->post_params['limit'][1]; + + $nb_pages = ceil($nb_posts/$nb_per_page); + + return $nb_pages; + } + + public static function PaginationPosition($offset=0) + { + if (isset($GLOBALS['_page_number'])) { + $p = $GLOBALS['_page_number']; + } else { + $p = 1; + } + + $p = $p+$offset; + + $n = self::PaginationNbPages(); + if (!$n) { + return $p; + } + + if ($p > $n || $p <= 0) { + return 1; + } else { + return $p; + } + } + + public static function PaginationStart() + { + if (isset($GLOBALS['_page_number'])) { + return self::PaginationPosition() == 1; + } + + return true; + } + + public static function PaginationEnd() + { + if (isset($GLOBALS['_page_number'])) { + return self::PaginationPosition() == self::PaginationNbPages(); + } + + return false; + } + + public static function PaginationURL($offset=0) + { + $args = $_SERVER['URL_REQUEST_PART']; + + $n = self::PaginationPosition($offset); + + $args = preg_replace('#(^|/)page/([0-9]+)$#','',$args); + + $url = $GLOBALS['core']->blog->url.$args; + + if ($n > 1) { + $url = preg_replace('#/$#','',$url); + $url .= '/page/'.$n; + } + + # If search param + if (!empty($_GET['q'])) { + $s = strpos($url,'?') !== false ? '&' : '?'; + $url .= $s.'q='.rawurlencode($_GET['q']); + } + return $url; + } + + # Robots policy + public static function robotsPolicy($base,$over) + { + $pol = array('INDEX' => 'INDEX','FOLLOW' => 'FOLLOW', 'ARCHIVE' => 'ARCHIVE'); + $base = array_flip(preg_split('/\s*,\s*/',$base)); + $over = array_flip(preg_split('/\s*,\s*/',$over)); + + foreach ($pol as $k => &$v) + { + if (isset($base[$k]) || isset($base['NO'.$k])) { + $v = isset($base['NO'.$k]) ? 'NO'.$k : $k; + } + if (isset($over[$k]) || isset($over['NO'.$k])) { + $v = isset($over['NO'.$k]) ? 'NO'.$k : $k; + } + } + + if ($pol['ARCHIVE'] == 'ARCHIVE') { + unset($pol['ARCHIVE']); + } + + return implode(', ',$pol); + } + + # Smilies static methods + public static function getSmilies(&$blog) + { + $path = array(); + if (isset($GLOBALS['__theme'])) { + $path[] = $GLOBALS['__theme']; + } + $path[] = 'default'; + $definition = $blog->themes_path.'/%s/smilies/smilies.txt'; + $base_url = $blog->settings->themes_url.'/%s/smilies/'; + + $res = array(); + + foreach ($path as $t) + { + if (file_exists(sprintf($definition,$t))) { + $base_url = sprintf($base_url,$t); + return self::smiliesDefinition(sprintf($definition,$t),$base_url); + } + } + return false; + } + + public static function smiliesDefinition($f,$url) + { + $def = file($f); + + $res = array(); + foreach($def as $v) + { + $v = trim($v); + if (preg_match('|^([^\t]*)[\t]+(.*)$|',$v,$matches)) + { + $r = '/(\A|[\s]+|>)('.preg_quote($matches[1],'/').')([\s]+|[<]|\Z)/ms'; + $s = '$1$3'; + $res[$r] = $s; + } + } + + return $res; + } + + public static function addSmilies($str) + { + if (!isset($GLOBALS['__smilies']) || !is_array($GLOBALS['__smilies'])) { + return $str; + } + + return preg_replace(array_keys($GLOBALS['__smilies']),array_values($GLOBALS['__smilies']),$str); + } + + # First post image helpers + public static function EntryFirstImageHelper($size,$with_category,$class="") + { + if (!preg_match('/^sq|t|s|m|o$/',$size)) { + $size = 's'; + } + + global $core, $_ctx; + + $p_url = $core->blog->settings->public_url; + $p_site = preg_replace('#^(.+?//.+?)/(.*)$#','$1',$core->blog->url); + $p_root = $core->blog->public_path; + + $pattern = '(?:'.preg_quote($p_site,'/').')?'.preg_quote($p_url,'/'); + $pattern = sprintf('/]+/msu',$pattern); + + $src = ''; + $alt = ''; + + # We first look in post content + if ($_ctx->posts) + { + $subject = $_ctx->posts->post_excerpt_xhtml.$_ctx->posts->post_content_xhtml.$_ctx->posts->cat_desc; + if (preg_match_all($pattern,$subject,$m) > 0) + { + foreach ($m[1] as $i => $img) { + if (($src = self::ContentFirstImageLookup($p_root,$img,$size)) !== false) { + $src = $p_url.'/'.dirname($img).'/'.$src; + if (preg_match('/alt="([^"]+)"/',$m[0][$i],$malt)) { + $alt = $malt[1]; + } + break; + } + } + } + } + + # No src, look in category description if available + if (!$src && $with_category && $_ctx->categories) + { + if (preg_match_all($pattern,$_ctx->categories->cat_desc,$m) > 0) + { + foreach ($m[1] as $i => $img) { + if (($src = self::ContentFirstImageLookup($p_root,$img,$size)) !== false) { + $src = $p_url.'/'.dirname($img).'/'.$src; + if (preg_match('/alt="([^"]+)"/',$m[0][$i],$malt)) { + $alt = $malt[1]; + } + break; + } + } + }; + } + + if ($src) { + return ''.$alt.''; + } + } + + private static function ContentFirstImageLookup($root,$img,$size) + { + # Get base name and extension + $info = path::info($img); + $base = $info['base']; + + if (preg_match('/^\.(.+)_(sq|t|s|m)$/',$base,$m)) { + $base = $m[1]; + } + + $res = false; + if ($size != 'o' && file_exists($root.'/'.$info['dirname'].'/.'.$base.'_'.$size.'.jpg')) + { + $res = '.'.$base.'_'.$size.'.jpg'; + } + else + { + $f = $root.'/'.$info['dirname'].'/'.$base; + if (file_exists($f.'.'.$info['extension'])) { + $res = $base.'.'.$info['extension']; + } elseif (file_exists($f.'.jpg')) { + $res = $base.'.jpg'; + } elseif (file_exists($f.'.png')) { + $res = $base.'.png'; + } elseif (file_exists($f.'.gif')) { + $res = $base.'.gif'; + } + } + + if ($res) { + return $res; + } + return false; + } +} +?> \ No newline at end of file diff --git a/inc/public/lib.urlhandlers.php b/inc/public/lib.urlhandlers.php new file mode 100644 index 0000000..0eee676 --- /dev/null +++ b/inc/public/lib.urlhandlers.php @@ -0,0 +1,560 @@ +url->type = '404'; + echo $core->tpl->getData('404.html'); + + # --BEHAVIOR-- publicAfterDocument + $core->callBehavior('publicAfterDocument',$core); + exit; + } + + protected static function getPageNumber(&$args) + { + if (preg_match('#(^|/)page/([0-9]+)$#',$args,$m)) { + $n = (integer) $m[2]; + if ($n > 0) { + $args = preg_replace('#(^|/)page/([0-9]+)$#','',$args); + return $n; + } + } + + return false; + } + + protected static function serveDocument($tpl,$content_type='text/html',$http_cache=true,$http_etag=true) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + if ($_ctx->nb_entry_per_page === null) { + $_ctx->nb_entry_per_page = $core->blog->settings->nb_post_per_page; + } + + $tpl_file = $core->tpl->getFilePath($tpl); + + if (!$tpl_file) { + throw new Exception('Unable to find template'); + } + + if ($http_cache) { + $GLOBALS['mod_files'][] = $tpl_file; + http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); + } + + $result = new ArrayObject; + + header('Content-Type: '.$content_type.'; charset=UTF-8'); + $_ctx->current_tpl = $tpl; + $result['content'] = $core->tpl->getData($tpl); + $result['content_type'] = $content_type; + $result['tpl'] = $tpl; + $result['blogupddt'] = $core->blog->upddt; + + # --BEHAVIOR-- urlHandlerServeDocument + $core->callBehavior('urlHandlerServeDocument',$result); + + if ($http_cache && $http_etag) { + http::etag($result['content'],http::getSelfURI()); + } + echo $result['content']; + } + + public static function home($args) + { + $n = self::getPageNumber($args); + + if ($args && !$n) + { + # "Then specified URL went unrecognized by all URL handlers and + # defaults to the home page, but is not a page number. + self::p404(); + } + else + { + $core =& $GLOBALS['core']; + + if ($n) { + $GLOBALS['_page_number'] = $n; + $core->url->type = $n > 1 ? 'default-page' : 'default'; + } + + if (empty($_GET['q'])) { + self::serveDocument('home.html'); + $core->blog->publishScheduledEntries(); + } else { + self::search(); + } + } + } + + public static function search() + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $GLOBALS['_search'] = !empty($_GET['q']) ? rawurldecode($_GET['q']) : ''; + if ($GLOBALS['_search']) { + $GLOBALS['_search_count'] = $core->blog->getPosts(array('search' => $GLOBALS['_search']),true)->f(0); + } + + self::serveDocument('search.html'); + } + + public static function lang($args) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $n = self::getPageNumber($args); + + $params['lang'] = $args; + $_ctx->langs = $core->blog->getLangs($params); + + if ($_ctx->langs->isEmpty()) { + # The specified language does not exist. + self::p404(); + } + else + { + if ($n) { + $GLOBALS['_page_number'] = $n; + } + $_ctx->cur_lang = $args; + self::home(null); + } + } + + public static function category($args) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $n = self::getPageNumber($args); + + if ($args == '' && !$n) { + # No category was specified. + self::p404(); + } + else + { + $params['cat_url'] = $args; + $params['post_type'] = 'post'; + + $_ctx->categories = $core->blog->getCategories($params); + + if ($_ctx->categories->isEmpty()) { + # The specified category does no exist. + self::p404(); + } + else + { + if ($n) { + $GLOBALS['_page_number'] = $n; + } + self::serveDocument('category.html'); + } + } + } + + public static function archive($args) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $year = $month = $cat_url = null; + # Nothing or year and month + if ($args == '') + { + self::serveDocument('archive.html'); + } + elseif (preg_match('|^/([0-9]{4})/([0-9]{2})$|',$args,$m)) + { + $params['year'] = $m[1]; + $params['month'] = $m[2]; + $params['type'] = 'month'; + $_ctx->archives = $core->blog->getDates($params); + + if ($_ctx->archives->isEmpty()) { + # There is no entries for the specified period. + self::p404(); + } + else + { + self::serveDocument('archive_month.html'); + } + } + else { + # The specified URL is not a date. + self::p404(); + } + } + + public static function post($args) + { + if ($args == '') { + # No entry was specified. + self::p404(); + } + else + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $core->blog->withoutPassword(false); + + $params = new ArrayObject(); + $params['post_url'] = $args; + + $_ctx->posts = $core->blog->getPosts($params); + + $_ctx->comment_preview = new ArrayObject(); + $_ctx->comment_preview['content'] = ''; + $_ctx->comment_preview['rawcontent'] = ''; + $_ctx->comment_preview['name'] = ''; + $_ctx->comment_preview['mail'] = ''; + $_ctx->comment_preview['site'] = ''; + $_ctx->comment_preview['preview'] = false; + $_ctx->comment_preview['remember'] = false; + + $core->blog->withoutPassword(true); + + + if ($_ctx->posts->isEmpty()) + { + # The specified entry does not exist. + self::p404(); + } + else + { + $post_id = $_ctx->posts->post_id; + $post_password = $_ctx->posts->post_password; + + # Password protected entry + if ($post_password != '' && !$_ctx->preview) + { + # Get passwords cookie + if (isset($_COOKIE['dc_passwd'])) { + $pwd_cookie = unserialize($_COOKIE['dc_passwd']); + } else { + $pwd_cookie = array(); + } + + # Check for match + if ((!empty($_POST['password']) && $_POST['password'] == $post_password) + || (isset($pwd_cookie[$post_id]) && $pwd_cookie[$post_id] == $post_password)) + { + $pwd_cookie[$post_id] = $post_password; + setcookie('dc_passwd',serialize($pwd_cookie),0,'/'); + } + else + { + self::serveDocument('password-form.html','text/html',false); + return; + } + } + + $post_comment = + isset($_POST['c_name']) && isset($_POST['c_mail']) && + isset($_POST['c_site']) && isset($_POST['c_content']) && + $_ctx->posts->commentsActive(); + + # Posting a comment + if ($post_comment) + { + # Spam trap + if (!empty($_POST['f_mail'])) { + http::head(412,'Precondition Failed'); + header('Content-Type: text/plain'); + echo "So Long, and Thanks For All the Fish"; + # Exits immediately the application to preserve the server. + exit; + } + + $name = $_POST['c_name']; + $mail = $_POST['c_mail']; + $site = $_POST['c_site']; + $content = $_POST['c_content']; + $preview = !empty($_POST['preview']); + + if ($content != '') + { + if ($core->blog->settings->wiki_comments) { + $core->initWikiComment(); + } else { + $core->initWikiSimpleComment(); + } + $content = $core->wikiTransform($content); + $content = $core->HTMLfilter($content); + } + + $_ctx->comment_preview['content'] = $content; + $_ctx->comment_preview['rawcontent'] = $_POST['c_content']; + $_ctx->comment_preview['name'] = $name; + $_ctx->comment_preview['mail'] = $mail; + $_ctx->comment_preview['site'] = $site; + + if ($preview) + { + # --BEHAVIOR-- publicBeforeCommentPreview + $core->callBehavior('publicBeforeCommentPreview',$_ctx->comment_preview); + + $_ctx->comment_preview['preview'] = true; + } + else + { + # Post the comment + $cur = $core->con->openCursor($core->prefix.'comment'); + $cur->comment_author = $name; + $cur->comment_site = html::clean($site); + $cur->comment_email = html::clean($mail); + $cur->comment_content = $content; + $cur->post_id = $_ctx->posts->post_id; + $cur->comment_status = $core->blog->settings->comments_pub ? 1 : -1; + $cur->comment_ip = http::realIP(); + + $redir = $_ctx->posts->getURL(); + $redir .= strpos($redir,'?') !== false ? '&' : '?'; + + try + { + if (!text::isEmail($cur->comment_email)) { + throw new Exception(__('You must provide a valid email address.')); + } + + # --BEHAVIOR-- publicBeforeCommentCreate + $core->callBehavior('publicBeforeCommentCreate',$cur); + if ($cur->post_id) { + $comment_id = $core->blog->addComment($cur); + + # --BEHAVIOR-- publicAfterCommentCreate + $core->callBehavior('publicAfterCommentCreate',$cur,$comment_id); + } + + if ($cur->comment_status == 1) { + $redir_arg = 'pub=1'; + } else { + $redir_arg = 'pub=0'; + } + + header('Location: '.$redir.$redir_arg); + } + catch (Exception $e) + { + $_ctx->form_error = $e->getMessage(); + $_ctx->form_error; + } + } + } + + # The entry + self::serveDocument('post.html'); + } + } + } + + public static function preview($args) + { + $core = $GLOBALS['core']; + $_ctx = $GLOBALS['_ctx']; + + if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#',$args,$m)) { + # The specified Preview URL is malformed. + self::p404(); + } + else + { + $user_id = $m[1]; + $user_key = $m[2]; + $post_url = $m[3]; + if (!$core->auth->checkUser($user_id,null,$user_key)) { + # The user has no access to the entry. + self::p404(); + } + else + { + $_ctx->preview = true; + self::post($post_url); + } + } + } + + public static function feed($args) + { + $type = null; + $comments = false; + $cat_url = false; + $post_id = null; + $params = array(); + $subtitle = ''; + + $mime = 'application/xml'; + + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + if (preg_match('!^([a-z]{2}(-[a-z]{2})?)/(.*)$!',$args,$m)) { + $params['lang'] = $m[1]; + $args = $m[3]; + + $_ctx->langs = $core->blog->getLangs($params); + + if ($_ctx->langs->isEmpty()) { + # The specified language does not exist. + self::p404(); + return; + } else { + $_ctx->cur_lang = $m[1]; + } + } + + if (preg_match('#^rss2/xslt$#',$args,$m)) + { + # RSS XSLT stylesheet + self::serveDocument('rss2.xsl','text/xml'); + return; + } + elseif (preg_match('#^(atom|rss2)/comments/([0-9]+)$#',$args,$m)) + { + # Post comments feed + $type = $m[1]; + $comments = true; + $post_id = (integer) $m[2]; + } + elseif (preg_match('#^(?:category/(.+)/)?(atom|rss2)(/comments)?$#',$args,$m)) + { + # All posts or comments feed + $type = $m[2]; + $comments = !empty($m[3]); + if (!empty($m[1])) { + $cat_url = $m[1]; + } + } + else + { + # The specified Feed URL is malformed. + self::p404(); + return; + } + + if ($cat_url) + { + $params['cat_url'] = $cat_url; + $params['post_type'] = 'post'; + $_ctx->categories = $core->blog->getCategories($params); + + if ($_ctx->categories->isEmpty()) { + # The specified category does no exist. + self::p404(); + return; + } + + $subtitle = ' - '.$_ctx->categories->cat_title; + } + elseif ($post_id) + { + $params['post_id'] = $post_id; + $params['post_type'] = ''; + $_ctx->posts = $core->blog->getPosts($params); + + if ($_ctx->posts->isEmpty()) { + # The specified post does not exist. + self::p404(); + return; + } + + $subtitle = ' - '.$_ctx->posts->post_title; + } + + $tpl = $type; + if ($comments) { + $tpl .= '-comments'; + $_ctx->nb_comment_per_page = $core->blog->settings->nb_comment_per_feed; + } else { + $_ctx->nb_entry_per_page = $core->blog->settings->nb_post_per_feed; + $_ctx->short_feed_items = $core->blog->settings->short_feed_items; + } + $tpl .= '.xml'; + + if ($type == 'atom') { + $mime = 'application/atom+xml'; + } + + $_ctx->feed_subtitle = $subtitle; + + header('X-Robots-Tag: '.context::robotsPolicy($core->blog->settings->robots_policy,'')); + self::serveDocument($tpl,$mime); + if (!$comments && !$cat_url) { + $core->blog->publishScheduledEntries(); + } + } + + public static function trackback($args) + { + if (!preg_match('/^[0-9]+$/',$args)) { + # The specified trackback URL is not an number + self::p404(); + } else { + $tb = new dcTrackback($GLOBALS['core']); + $tb->receive($args); + } + } + + public static function rsd($args) + { + $core =& $GLOBALS['core']; + http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); + + header('Content-Type: text/xml; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + " Dotclear\n". + " http://www.dotclear.org/\n". + ' '.html::escapeHTML($core->blog->url)."\n"; + + if ($core->blog->settings->enable_xmlrpc) + { + $u = sprintf(DC_XMLRPC_URL,$core->blog->url,$core->blog->id); + + echo + " \n". + ' '."\n". + ' '."\n". + ' '."\n". + ' '."\n". + " \n"; + } + + echo + "\n". + "\n"; + } + + public static function xmlrpc($args) + { + $core =& $GLOBALS['core']; + $server = new dcXmlRpc($core,$args); + $server->serve(); + } +} +?> \ No newline at end of file diff --git a/inc/public/prepend.php b/inc/public/prepend.php new file mode 100644 index 0000000..75fed25 --- /dev/null +++ b/inc/public/prepend.php @@ -0,0 +1,133 @@ +setBlog(DC_BLOG_ID); +} + +if ($core->blog->id == null) { + __error(__('Blog is not defined.') + ,__('Did you change your Blog ID?') + ,30); +} + +# Loading media +try { + $core->media = new dcMedia($core); +} catch (Exception $e) {} + +# Creating template context +$_ctx = new context(); +try { + $core->tpl = new dcTemplate(DC_TPL_CACHE,'$core->tpl',$core); +} catch (Exception $e) { + __error(__('Can\'t create template files.') + ,$e->getMessage() + ,40); +} + +# Loading locales +$_lang = $core->blog->settings->lang; +$_lang = preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$_lang) ? $_lang : 'en'; + +if (l10n::set(dirname(__FILE__).'/../../locales/'.$_lang.'/date') === false && $_lang != 'en') { + l10n::set(dirname(__FILE__).'/../../locales/en/date'); +} +l10n::set(dirname(__FILE__).'/../../locales/'.$_lang.'/public'); +l10n::set(dirname(__FILE__).'/../../locales/'.$_lang.'/plugins'); + +# Loading plugins +$core->plugins->loadModules(DC_PLUGINS_ROOT,'public',$_lang); + +# Loading themes +$core->themes = new dcThemes($core); +$core->themes->loadModules($core->blog->themes_path); + +# Defining theme if not defined +if (!isset($__theme)) { + $__theme = $core->blog->settings->theme; +} + +if (!$core->themes->moduleExists($__theme)) { + $__theme = $core->blog->settings->theme = 'default'; +} + +$__parent_theme = $core->themes->moduleInfo($__theme,'parent'); +if ($__parent_theme) { + if (!$core->themes->moduleExists($__parent_theme)) { + $__theme = $core->blog->settings->theme = 'default'; + $__parent_theme = null; + } +} + +# If theme doesn't exist, stop everything +if (!$core->themes->moduleExists($__theme)) { + __error(__('Default theme not found.') + ,__('This either means you removed your default theme or set a wrong theme '. + 'path in your blog configuration. Please check theme_path value in '. + 'about:config module or reinstall default theme.') + ,50); +} + +# Loading _public.php file for selected theme +$core->themes->loadNsFile($__theme,'public'); + +# --BEHAVIOR-- publicPrepend +$core->callBehavior('publicPrepend',$core); + +# Prepare the HTTP cache thing +$mod_files = get_included_files(); +$mod_ts = array(); +$mod_ts[] = $core->blog->upddt; + +$__theme_tpl_path = array( + $core->blog->themes_path.'/'.$__theme.'/tpl' +); +if ($__parent_theme) { + $__theme_tpl_path[] = $core->blog->themes_path.'/'.$__parent_theme.'/tpl'; +} + +$core->tpl->setPath( + $__theme_tpl_path, + $core->blog->themes_path.'/default/tpl', + dirname(__FILE__).'/default-templates', + $core->tpl->getPath()); + +$core->url->mode = $core->blog->settings->url_scan; + +try { + # --BEHAVIOR-- publicBeforeDocument + $core->callBehavior('publicBeforeDocument',$core); + + $core->url->getDocument(); + + # --BEHAVIOR-- publicAfterDocument + $core->callBehavior('publicAfterDocument',$core); +} catch (Exception $e) { + __error($e->getMessage() + ,__('Something went wrong while loading template file for your blog.') + ,60); +} +?> \ No newline at end of file diff --git a/inc/public/rs.extension.php b/inc/public/rs.extension.php new file mode 100644 index 0000000..3456a82 --- /dev/null +++ b/inc/public/rs.extension.php @@ -0,0 +1,93 @@ +addBehavior('coreBlogGetPosts',array('rsExtendPublic','coreBlogGetPosts')); +$core->addBehavior('coreBlogGetComments',array('rsExtendPublic','coreBlogGetComments')); + +class rsExtendPublic +{ + public static function coreBlogGetPosts(&$rs) + { + $rs->extend('rsExtPostPublic'); + } + + public static function coreBlogGetComments(&$rs) + { + $rs->extend('rsExtCommentPublic'); + } +} + +class rsExtPostPublic extends rsExtPost +{ + public static function getContent(&$rs,$absolute_urls=false) + { + # Not very nice hack but it does the job :) + if (isset($GLOBALS['_ctx']) && $GLOBALS['_ctx']->short_feed_items === true) { + $_ctx =& $GLOBALS['_ctx']; + $c = parent::getContent($rs,$absolute_urls); + $c = context::remove_html($c); + $c = context::cut_string($c,350); + + $c = + '

    '.$c.'... '. + ''.__('Read').' '. + html::escapeHTML($rs->post_title).'

    '; + + return $c; + } + + if ($rs->core->blog->settings->use_smilies) + { + return self::smilies(parent::getContent($rs,$absolute_urls),$rs->core->blog); + } + + return parent::getContent($rs,$absolute_urls); + } + + public static function getExcerpt(&$rs,$absolute_urls=false) + { + if ($rs->core->blog->settings->use_smilies) + { + return self::smilies(parent::getExcerpt($rs,$absolute_urls),$rs->core->blog); + } + + return parent::getExcerpt($rs,$absolute_urls); + } + + protected static function smilies($c,&$blog) + { + if (!isset($GLOBALS['__smilies'])) { + $GLOBALS['__smilies'] = context::getSmilies($blog); + } + return context::addSmilies($c); + } +} + +class rsExtCommentPublic extends rsExtComment +{ + public static function getContent(&$rs,$absolute_urls=false) + { + if ($rs->core->blog->settings->use_smilies) + { + $c = parent::getContent($rs,$absolute_urls); + + if (!isset($GLOBALS['__smilies'])) { + $GLOBALS['__smilies'] = context::getSmilies($rs->core->blog); + } + return context::addSmilies($c); + } + + return parent::getContent($rs,$absolute_urls); + } +} +?> \ No newline at end of file diff --git a/inc/swf/player_flv.swf b/inc/swf/player_flv.swf new file mode 100644 index 0000000..01ec373 Binary files /dev/null and b/inc/swf/player_flv.swf differ diff --git a/inc/swf/player_mp3.swf b/inc/swf/player_mp3.swf new file mode 100644 index 0000000..487b7df Binary files /dev/null and b/inc/swf/player_mp3.swf differ diff --git a/inc/swf/swfupload.swf b/inc/swf/swfupload.swf new file mode 100644 index 0000000..e3f7670 Binary files /dev/null and b/inc/swf/swfupload.swf differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..7d446ab --- /dev/null +++ b/index.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/locales/README b/locales/README new file mode 100644 index 0000000..d021aa6 --- /dev/null +++ b/locales/README @@ -0,0 +1 @@ +Please see http://www.dotclear.org/translate diff --git a/locales/en/date.lang.php b/locales/en/date.lang.php new file mode 100644 index 0000000..726c436 --- /dev/null +++ b/locales/en/date.lang.php @@ -0,0 +1,38 @@ + \ No newline at end of file diff --git a/locales/en/date.po b/locales/en/date.po new file mode 100644 index 0000000..6f56683 --- /dev/null +++ b/locales/en/date.po @@ -0,0 +1,56 @@ +msgid "_Jan" +msgstr "Jan" + +msgid "_Feb" +msgstr "Feb" + +msgid "_Mar" +msgstr "Mar" + +msgid "_Apr" +msgstr "Apr" + +msgid "_May" +msgstr "May" + +msgid "_Jun" +msgstr "Jun" + +msgid "_Jul" +msgstr "Jul" + +msgid "_Aug" +msgstr "Aug" + +msgid "_Sep" +msgstr "Sep" + +msgid "_Oct" +msgstr "Oct" + +msgid "_Nov" +msgstr "Nov" + +msgid "_Dec" +msgstr "Dec" + +msgid "_Mon" +msgstr "Mon" + +msgid "_Tue" +msgstr "Tue" + +msgid "_Wed" +msgstr "Wed" + +msgid "_Thu" +msgstr "Thu" + +msgid "_Fri" +msgstr "Fri" + +msgid "_Sat" +msgstr "Sat" + +msgid "_Sun" +msgstr "Sun" \ No newline at end of file diff --git a/locales/en/help/blog_pref.html b/locales/en/help/blog_pref.html new file mode 100644 index 0000000..40b7ac1 --- /dev/null +++ b/locales/en/help/blog_pref.html @@ -0,0 +1,148 @@ + + + Blog settings + + + + +

    Blog details

    +
    +
    Blog ID
    +
    Unique ID of the blog. Mandatory, can only be modified by a super administrator.
    + +
    Blog name
    +
    Name of the blog, mandatory.
    + +
    Blog URL
    +
    Complete blog URL. Mandatory, can only be modified by a super administrator.
    + +
    Blog status
    +
    +
      +
    • online: blog open to visitors
    • +
    • offline: blog closed to visitors + but open to writers.
    • +
    • removed: blog closed to both visitors and writers.
    • +
    +
    + +
    Blog description
    +
    Here you can put any description you want for your blog. This is a + simple text, without formatting.
    +
    + +

    Blog configuration

    +
    +
    Blog editor's name
    +
    Name of the person responsible for the blog. It may be the owner or the + publisher if he exists.
    + +
    Copyright notice
    +
    Note about the reproduction rights for the blog.
    + +
    Default language
    +
    Language of the blog interface. Either this language is among the avalaible ones, + and the interface will be translated, or it is not, and the interface will then + default to English.
    + +
    Blog timezone
    +
    Defines the timezone that will be used for comments and trackbacks.
    + +
    Date formats
    +
    The date format of the blog posts. See the date and time + format reference section below for further details.
    + +
    Time format
    +
    The time format of the blog posts. See the date and time + format reference section below for further details.
    + +
    Display .. entries per page
    +
    The number will be used as the limit for the number of entries + on the homepage, the first page of each category and the rss feeds.
    + +
    Leave comments open for .. days
    +
    The number of days during wich comments and trackbacks will be open + after the post publication. When empty, the comments will be kept open + indefinitly.
    + +
    Display smilies on entries and comments
    +
    Display sequences of characters as :-) or ;-) as images.
    + +
    Accept comments
    +
    Globally accepts comments on the blog. Takes precedence over the + per post setting.
    + +
    Accept trackbacks
    +
    Globally accepts trackbacks on the blog. Takes precedence over the + per post setting.
    + +
    Moderate comments and trackbacks
    +
    With such an option, comments and trackbacks will be published after + editor agreement.
    + +
    Add "nofollow" relation on comments and trackbacks links
    +
    Adds an attribut on the links in comments and trackbacks, instructing the + search bots not to follow those links. This antispam features has yet to + prove its value.
    + +
    Wiki syntax for comments
    +
    Allows a subset of the wiki syntax to be used in comments.
    +
    + +

    Format reference

    +

    Some fields can be configured to format the date and time to your needs. +Here is a reference:

    + +
      +
    • %a: abreviated week day (local).
    • +
    • %A: complete week day (local).
    • +
    • %b: abbreviated month name (local).
    • +
    • %B: complete month name (local).
    • +
    • %c: default local date and time display.
    • +
    • %C: century (the year, divided by 100 and + round up beetween 00 and 99)
    • +
    • %d: numeric month day (from 01 to 31)
    • +
    • %D: identical to %m/%d/%y
    • +
    • %e: numeric month day (from 1 to 31)
    • +
    • %g: identical to %G, 2 digits.
    • +
    • %G: The year on 4 digits, according to the week number + (cf. %V). Format and value identical to %Y, except that if the number of the + week belongs to the preceding or following year, the current year + will be used instead.
    • +
    • %h: identical to %b
    • +
    • %H: digital hour of the day, 24H mode + (from 00 to 23)
    • +
    • %I: digital hour of the day, 12H mode + (from 01 to 12)
    • +
    • %j: day of the year, digital (from 001 to 366)
    • +
    • %m: month, digital (from 1 à 12)
    • +
    • %M: minute, digital
    • +
    • %n: newline character
    • +
    • %p: either `am' or `pm', according to the absolute time, + or depending of the local settings.
    • +
    • %r: the hour, a.m. and p.m. format
    • +
    • %R: the hour, 24h format
    • +
    • %S: seconds, digital
    • +
    • %t: tab character
    • +
    • %T: the current time (identical to %H:%M:%S)
    • +
    • %u: day week, digit, from 1 to 7. (1 stands for monday)
    • +
    • %U: week number, the first sundqy of the year being the + first day of the first week.
    • +
    • %V: the week numpber as defined in ISO + 8601:1988, digital, from 01 to 53. The week 1 is the first week + with more than 4 days in the current year, whose monday is the first day. + (Use %G or %g for the year elements corresponding to the number of the + week for the considered timestamp.)
    • +
    • %W: number of the week in the year, the first monday of + the year being the start of the first week
    • +
    • %w: day of the week, digital, 0 stands for sunday
    • +
    • %x: prefered format for the date without the time
    • +
    • %X: prefered format for the time without the date
    • +
    • %y: the year, two digits (from 00 to 99)
    • +
    • %Y: the year, four digits
    • +
    • %Z ou %z: timezone, name or abbreviation
    • +
    • %%: a litteral `%' character
    • +
    + + + diff --git a/locales/en/help/categories.html b/locales/en/help/categories.html new file mode 100644 index 0000000..983607c --- /dev/null +++ b/locales/en/help/categories.html @@ -0,0 +1,41 @@ + + + Categories + + + +

    Categories list

    +
    +
    Create a new category
    +
    Choose the name you wish to use for your new category + then click on Save to validate its creation. To modify it, + click on its name in the list.
    + +
    Change category order
    +
    To change the category order, drag and drop the categories by using + the arrow shaped images then click on save order. + If you do not see the arrows, use the numbered fields + beside each category.
    +
    + +

    Modifying a category

    +
    +
    Title
    +
    Choose the name you wish to give to your category + the click on Save to validate.
    + +
    URL
    +
    DotClear creates a default url for the category, making it reachable + with a path similar to this one : + http://monblog.tld/category/My-category. You can click + on the small lock on the right to modify it according to your needs.
    + +
    Description
    +
    What's inside the description field will be shown when selecting + one of the blog's categories. You can fill it with any kind of content + (paragraphs, lists, etc.) The format used for this field is HTML.
    + +
    + + + diff --git a/locales/en/help/comments.html b/locales/en/help/comments.html new file mode 100644 index 0000000..83338f2 --- /dev/null +++ b/locales/en/help/comments.html @@ -0,0 +1,58 @@ + + + Comments + + + +

    Filters for comment list

    +
    +
    Type:
    +
    None, comments or trackbacks.
    + +
    Status:
    +
      +
    • junk:
    • +
    • pending:
    • +
    • unpublished: offline ;
    • +
    • published: online.
    • +
    + +
    Order by:
    +
    Choose: date, entry title, author or status, then + choose the sort order.
    + +
    Sort
    +
    Choose whether the list will be sorted in ascending or descending order.
    + +
    Comment author.
    +
    The search is not case sensitive. If you want to search only a part of + the name, you can use the * generic character.
    + +
    Comments per page :
    +
    Allows you to choose the number of comments on each page of this list.
    +
    + +

    Modify or add a comment

    +
    +
    Author:
    +
    Author name. This field is mandatory.
    + +
    Email:
    +
    Author's email address.
    + +
    Web site:
    +
    URL of the comment author's blog or website.
    + +
    Status:
    +
      +
    • junk:
    • +
    • pending:
    • +
    • unpublished: offline
    • +
    • published: online
    • +
    + +
    Comment:
    +
    Comment's content. This field is html formated.
    +
    + + diff --git a/locales/en/help/media.html b/locales/en/help/media.html new file mode 100644 index 0000000..f81f9e8 --- /dev/null +++ b/locales/en/help/media.html @@ -0,0 +1,89 @@ + + + Media manager + + + + +

    Media list

    +

    The media manager's main page displays a list of available files (medias) +for the currently selected blog.

    + +

    You can click the icons or names of the medias in the folder or any +subfolder to display them.

    + +

    Selecting a MP3 sound file displays a player to immediately play +the file. The Flash plugin must be installed on your browser for +this player to operate.

    + +

    The images are displayed with a thumbnail when possible.

    + +

    Add files

    +
    +
    Choose a file
    +
    Here you can select one of your local files to send it on your + blog. Should the file be bigger than the maximum size, it will not be sent.
    + +
    Title
    +
    Optional title for the file you want to send.
    + +
    Private
    +
    Check this if you want the file to be private, i.e. accessible only by + its owner and not accessible from the blog.
    +
    + +

    New folder

    +

    This form allows you to create an new subfolder in the currently displayed +folder. Just choose a name for it as you would do on your own computer.

    + +

    Media details

    +

    This page displays a set of informations about the currently selected +file and allows you to perform a few operations.

    + +
    +
    File name
    +
    Change the file name of the current file.
    + +
    File title
    +
    Give a title to the file, or change the current one.
    + +
    File date
    +
    Change the file date.
    + +
    Private
    +
    Specify that the file will be hidden to anyone but its owner in the media manager.
    + +
    New directory
    +
    Allows you to move the file in another folder.
    + +
    Change file
    +
    With this form, you can replace the file while keeping all the attributes + (name, title...). As always, you have to respect the maximum file size.
    +
    + +

    A specific treatment for images

    +

    When you add an image, depending of its size, up to four versions will be made available:

    +
      +
    • square: a 48x48 pixels square thumbnail,
    • +
    • thumbnail: the image reduced to 100 pixels on its longest side.
    • +
    • small: the longuest side is reduced to 240 pixels.
    • +
    • medium: the longuest side is reduced to 500 pixels.
    • +
    • originale: the original, unchanged image.
    • +
    + +

    Add a file to an entry

    +

    You can easily attach a file to one of your posts. First, create a new post +and save it. Then click on "Add files to this entry" to open the media manager.

    + +

    Just click on the [+] icon (Attach this file to entry). The file will then +be attached to the post.

    + +

    The attached files will be shown on the post page, as a list beside the post content. +The MP3 files will be made available through the Flash player, so that they can easily by read by visitors.

    + +

    Podcast and broadcasting of music files

    +

    All attached files will be included in your RSS feeds, allowing for easy +podcasting of any type of file.

    + + + diff --git a/locales/en/help/post.html b/locales/en/help/post.html new file mode 100644 index 0000000..4282109 --- /dev/null +++ b/locales/en/help/post.html @@ -0,0 +1,125 @@ + + + Editing an entry + + + + +

    Editing an entry

    + +
    +
    Entry title
    +
    Type in the entry title. This field is mandatory.
    + +
    Excerpt
    +
    The content of this field will be shown in pages showing entries as + a list, such as the homepage or categories pages, followed by a "continue reading" + link. It will also be visible at the begining of the page showing the + whole post. Should you leave it blank, the content field will be shown + on the homepage and other entry list pages.
    + +
    Content
    +
    The content of your entry. This field is mandatory.
    + +
    Notes
    +
    This text area is for personal use, for notes or memos. What you enter here + will never be displayed on the blog..
    + +
    Category
    +
    Your entry's category. To create a new category, please see the + "categories" section. You can choose to leave + you entry without a category by selecting the blank line.
    + +
    Entry status
    +
    Allows you to choose your post's status: +
      +
    • pending: the publication status has not yet been decided.
    • +
    • scheduled: the post will be set online at the time and date + set in the Published on field.
    • +
    • unpublished: the post is offline.
    • +
    • published: the post is online.
    • +
    +
    + +
    Published on
    +
    Here you can change the post's publication date and time. If the post's + status is scheduled, it will come online at the said date and time.
    + +
    Text formating
    +
    To choose the post syntax. Wiki is a simplified syntax + and will be converted to valid xhtml ; Unless you have a perfect + understanding of html, we advise you to choose the wiki syntax. + See the Wiki syntax reference for more information.
    + +
    Accept comments
    +
    Select this box to allow your visitors to comment the entry. The corresponding + global setting is to be found in the blog settings.
    + +
    Accept trackbacks
    +
    A trackback is a way to let a portion of your entry as a comment on + another blog. Select this box to allow others to trackback your entry. The + corresponding global setting is to be found in the blog settings.
    + +
    Selected entry
    +
    The selected entries will be listed on your blog menu, under the title "Best + of me".
    + +
    Entry password
    +
    You can enter a password for your entry. A password protected entry will + not be displayed on your blog, it will only be reachable to those you will + give the entry url (see the view entry link) and password.
    + +
    Basename
    +
    By clicking on its lock, you can unprotect this field and and choose another + URL for your entry. If the URL you're trying to use is already used by another + entry, a number will be added to it.
    + +
    Entry lang.
    +
    The two character language code of your entry. It defaults to your (as a + user) language but you can change it to whatever language code you want, ie. + "en" or "fr-qc". This code is used for the entry's display, no chack is made + on it.
    + +
    Attachments
    +
    The attachments are all the medias you joined to the entry. The link add + file to this entry allows you to attach other files to the entry. Remember + to save your changes before clicking on this link. For further information, + see the media manager help.
    +
    + +

    Comments

    +
    +
    Comment list
    +
    On the Comments tab, you can read and change the status of + your entry's comments. If you're allowed to by the blog administrator, + you will be able to set your comments online or offline, to delete them or + to mark them as junk.
    + +
    Add a comment
    +
    On the Add a comment tab, you can reply to one of your entry's + comments without leaving your blog's backend. The syntax to be used here is + plain, unlimited, xhtml. Use the fields as if you were modifying a comment.
    +
    + +

    Trackback

    +

    To make a trackback, click on the Ping blogs link. +You will not see this link if your entry's status is not published.

    + +
    +
    URLs to ping
    +
    Paste here the trackback URLs you found in the posts you want to ping.
    + + +
    Send excerpt
    +
    This field defaults to your entry's first few sentences. That's wht will be + sent to the blog you're pinging, along with a link to your post. You can modify + the excerpt by editing this field.
    + + +
    Auto discover ping URLs
    +
    If your entries links specific posts and if the blog carrying those posts + is set to allow it, this tool will find the URLs to ping for you.
    +
    + + + diff --git a/locales/en/help/posts.html b/locales/en/help/posts.html new file mode 100644 index 0000000..82730c6 --- /dev/null +++ b/locales/en/help/posts.html @@ -0,0 +1,65 @@ + + + Managing entries + + + + +

    Filters for the entry list

    + +
    +
    Author
    +
    Allows you to filter by author.
    + +
    Category
    +
    Filter by category.
    + +
    Status
    +
      +
    • pending: posts whose publication status has not yet + been decided.
    • +
    • scheduled: post to be set online automatically.
    • +
    • unpublished: offline posts.
    • +
    • published: online posts.
    • +
    + +
    Selected
    +
    Only the selected posts, or the non selected ones.
    + +
    Month
    +
    Filter on a specific month and year.
    + +
    Language
    +
    Filter on the different languages used on the blog.
    + +
    Order by
    +
    Defines the list order, either on the date, the title, the author, + the status or the selected status.
    + +
    Sort
    +
    Choose whether the list will be sorted in ascending or descending order.
    + +
    Entries per page
    +
    Number of entries that will be displayed on each page of the resulting + set.
    +
    + +

    Selected entry actions

    +

    If your user permission level allows it, you can perform batch operations on the +entries you select in the list.

    + +
      +
    • Publish: set the entries online.
    • +
    • Unpublish: set the entries offline.
    • +
    • Schedule: schedule the entries to be set online at their + publication date.
    • +
    • Mark as pending: erase other publication status.
    • +
    • Change category: directs you to the category list to choose a + new one for the entries.
    • +
    • Change author: allows you to change the posts' author by + entering the new author's ID.
    • +
    • Delete: suppress the posts (this operation cannot be undone).
    • +
    + + + diff --git a/locales/en/help/user_pref.html b/locales/en/help/user_pref.html new file mode 100644 index 0000000..0da30cf --- /dev/null +++ b/locales/en/help/user_pref.html @@ -0,0 +1,61 @@ + + + User preferences + + + + +

    User preferences

    +
    +
    Name, First name
    +
    If the display name is empty, the author's name will be displayed + as his Firstname and Name as set here.
    + +
    Display name
    +
    You can choose here how your name will be displayed under your posts.
    + +
    Email
    +
    Address where will be sent the new comment notifications.
    + +
    URL
    +
    The user's web site. If set, the author name will be displayed as + a link to the said site.
    + +
    Preferred format
    +
    To choose the prefered post syntax. Wiki is a simplified syntax + and will be converted to valid xhtml ; Unless you have a perfect + understanding of html, we advise you to choose the wiki syntax.
    + +
    Default entry status
    +
    The default entry status can be set to: +
      +
    • pending: the publication status has not yet been decided.
    • +
    • scheduled: the post will be set online at the time and date + set in the Published on field.
    • +
    • unpublished: the post is offline.
    • +
    • published: the post is online.
    • +
    +
    + +
    Entry edit field height
    +
    The height of the Entry edit field. Defaults to 24.
    + +
    User language
    +
    The language in wich one publishes his posts. If the translation exists, + the interface will also be displayed in that language.
    + +
    User timezone
    +
    That choice will define the time displayed on post publication.
    + +
    Enable WYSIWYG mode
    +
    Allow the use of the WYSIWYG editor for the posts and description fields.
    +
    + +

    Change your password

    +

    To change your password, write down twice your password in the fields +New password and Confirm password. The minimal password +lenght is 6 characters.

    +

    If no new password is provided, the current one is kept.

    + + + diff --git a/locales/en/help/wiki.html b/locales/en/help/wiki.html new file mode 100644 index 0000000..3bf005e --- /dev/null +++ b/locales/en/help/wiki.html @@ -0,0 +1,85 @@ + + + Wiki syntax reference + + + + +

    Wiki syntax reference

    + +

    The wiki syntax is a way to enhance your text with a minimal set +of tags, studied to cover the basic needs (titles, paragraphs, quotes, +lists...)

    + +
    +
    Block elements
    +
    +
      +
    • Leave an empty line between two similar blocks.
    • +
    • Paragraph: free text, ended by an empty line if + another paragraph is to follow.
    • +
    • Title: !!! title, !! title + or ! title, allowing you to use three different levels of + heading.
    • +
    • Horizontal line: ----
    • +
    • Lists: Start each line with + * or # for unnumbered or numbered lists respectively. + List imbrication is done by mixing list markers this way: +
      +* item 1
      +** item 1.1
      +* item 2
      +*# item 2.1
      +...
      +
      +
    • +
    • Preformatted text: each line must start with a space.
    • +
    • Block quote: each line must start with a semicolon (>).
    • +
    +
    + +
    Formatting tags
    +
    +
      +
    • Emphasis: two quotes ''text''
    • +
    • Strong emphasis: two underscore __text__
    • +
    • New line: %%%
    • +
    • Insertion: two plusses ++text++
    • +
    • Deletion: two minuses --text--
    • +
    • Link: [url], [name|url], + [name|url|language] or [name|url|languagee|title]
    • +
    • Image: + ((url|alternative text)), + ((url|alternative text|position)) or + ((url|alternative text|position|long description)). +
      The position can be either L or G (left), R or D (right) or C (centered).
    • +
    • Anchor: ~anchor~
    • +
    • Acronym: ??acronym|title??
    • +
    • Inline HTML: two backquotes ``html code``
    • +
    • Inline quote: {{quote}}, + {{quote|language}} or {{quote|language|url}}
    • +
    • Code: @@code@@
    • +
    • Footnotes: $$footnote$$
    • +
    +
    + +
    Unformatted text
    +
    If you don't want one formatting character to be interpreted as such, add a +\ just before it. This way: +\[text in brackets without being a link\] +
    + +
    HTML Code
    +
    Even if you chose the wiki syntax, you may sometimes need a more +powerful formatting, i.e. HTML syntax. Do it the following way: +
    +///html
    +<p style="color:red">my text in red</p>
    +///
    +
    +
    +
    + + + + diff --git a/locales/en/resources.php b/locales/en/resources.php new file mode 100644 index 0000000..7aa2c62 --- /dev/null +++ b/locales/en/resources.php @@ -0,0 +1,33 @@ + 'http://dotclear.org/documentation/2.0', + 'Dotclear 2 presentation' => 'http://dotclear.org/documentation/2.0/overview/tour', + "User manual" => 'http://dotclear.org/documentation/2.0/usage', + "Installation and administration guides" => 'http://dotclear.org/documentation/2.0/admin' +); + +$__resources['help'] = array( + 'core_blog_pref' => dirname(__FILE__).'/help/blog_pref.html', + 'core_categories' => dirname(__FILE__).'/help/categories.html', + 'core_comments' => dirname(__FILE__).'/help/comments.html', + 'core_media' => dirname(__FILE__).'/help/media.html', + 'core_post' => dirname(__FILE__).'/help/post.html', + 'core_posts' => dirname(__FILE__).'/help/posts.html', + 'core_user_pref' => dirname(__FILE__).'/help/user_pref.html', + 'core_wiki' => dirname(__FILE__).'/help/wiki.html' +); +?> \ No newline at end of file diff --git a/locales/fr/date.lang.php b/locales/fr/date.lang.php new file mode 100644 index 0000000..638a9b2 --- /dev/null +++ b/locales/fr/date.lang.php @@ -0,0 +1,58 @@ + \ No newline at end of file diff --git a/locales/fr/date.po b/locales/fr/date.po new file mode 100644 index 0000000..a5b67dd --- /dev/null +++ b/locales/fr/date.po @@ -0,0 +1,132 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-04-15 16:27+0200\n" +"PO-Revision-Date: 2008-04-15 16:34+0100\n" +"Last-Translator: Olivier Meunier \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" + +msgid "%Y-%m-%d %H:%M" +msgstr "%d/%m/%Y %H:%M" + +msgid "_Jan" +msgstr "janv." + +msgid "_Feb" +msgstr "fév." + +msgid "_Mar" +msgstr "mar." + +msgid "_Apr" +msgstr "avr." + +msgid "_May" +msgstr "mai" + +msgid "_Jun" +msgstr "juin" + +msgid "_Jul" +msgstr "juil." + +msgid "_Aug" +msgstr "août" + +msgid "_Sep" +msgstr "sept." + +msgid "_Oct" +msgstr "oct." + +msgid "_Nov" +msgstr "nov." + +msgid "_Dec" +msgstr "déc." + +msgid "January" +msgstr "janvier" + +msgid "February" +msgstr "février" + +msgid "March" +msgstr "mars" + +msgid "April" +msgstr "avril" + +msgid "May" +msgstr "mai" + +msgid "June" +msgstr "juin" + +msgid "July" +msgstr "juillet" + +msgid "August" +msgstr "août" + +msgid "September" +msgstr "septembre" + +msgid "October" +msgstr "octobre" + +msgid "November" +msgstr "novembre" + +msgid "December" +msgstr "décembre" + +msgid "_Mon" +msgstr "lun." + +msgid "_Tue" +msgstr "mar." + +msgid "_Wed" +msgstr "mer." + +msgid "_Thu" +msgstr "jeu." + +msgid "_Fri" +msgstr "ven." + +msgid "_Sat" +msgstr "sam." + +msgid "_Sun" +msgstr "dim." + +msgid "Monday" +msgstr "lundi" + +msgid "Tuesday" +msgstr "mardi" + +msgid "Wednesday" +msgstr "mercredi" + +msgid "Thursday" +msgstr "jeudi" + +msgid "Friday" +msgstr "vendredi" + +msgid "Saturday" +msgstr "samedi" + +msgid "Sunday" +msgstr "dimanche" diff --git a/locales/fr/help/blowupConfig.html b/locales/fr/help/blowupConfig.html new file mode 100644 index 0000000..9d41c14 --- /dev/null +++ b/locales/fr/help/blowupConfig.html @@ -0,0 +1,71 @@ + + + + Configuration du thème Blowup + + + +

    En modifiant la configuration du thème Blowup, vous pouvez personnaliser votre +thème très facilement. Pour cela, renseignez simplement les champs de configuration +ou choisissez un style prédéfini.

    + +

    Couleurs

    + +

    Quand vous devez indiquer une valeur de couleur, celle-ci doit être au format +hexadécimal. Par exemple : "#FF0000" donnera du rouge. Vous pouvez vous aider +de la pipette à côté de chaque champs de couleur.

    +

    Si vous manquez d'inspiration vous pouvez consulter :

    + + +

    Unités de mesure

    + +

    Quand vous devez indiquer une taille, celle-ci doit être suivie d'une unité +de mesure. Par exemple : "1em". Si vous n'indiquez aucune unité, la valeur +donnée prendra des pixels comme unité par défaut.

    +

    Les unités possibles sont :

    +
      +
    • px
    • +
    • em
    • +
    • ex
    • +
    • pt
    • +
    • %
    • +
    + +

    Images d'en-tête

    + +

    Vous pouvez choisir une image d'en-tête parmi une liste de nombreuses images +afin de remplacer celle utilisée par défaut.

    + +

    En choisissant "Personnalisé..." parmi la liste d'images, vous pourrez +déposer votre propre image. Celle-ci doit être au format JPG ou PNG et +avoir une largeur exacte de 800 pixels. + +

    Si vous déposez une image au format JPG, un cadre sera ajouté autours de +l'image, ce qui n'est pas le cas avec une image au format PNG (dont la +transparence sera également préservée).

    + +

    Styles prédéfinis

    + +

    Vous pouvez choisir un style prédéfini dans la liste d'option "Styles prédéfinis". +Une fois le style choisi, vous devez valider le formulaire pour appliquer les +changements.

    + +

    Vous pouvez ensuite modifier le style prédéfini comme bon vous semble.

    + +

    Import / export de configuration

    + +

    À la fin des options de Blowup, vous pouvez afficher une zone appelée "Import +/ export de configuration". Dans cette zone de texte se trouve la configuration +en cours d'utilisation. Vous pouvez la copier pour la partager avec d'autres.

    + +

    Pour appliquer (importer) une configuration, il suffit simplement de remplacer +le contenu de la zone de texte par celui que vous voulez utiliser. N'oubliez pas +de cliquer sur "appliquer le code".

    + + + \ No newline at end of file diff --git a/locales/fr/help/core_blog_pref.html b/locales/fr/help/core_blog_pref.html new file mode 100644 index 0000000..97a8aec --- /dev/null +++ b/locales/fr/help/core_blog_pref.html @@ -0,0 +1,190 @@ + + + Préférences du blog + + + + +

    Informations du blog

    +
    +
    Identifiant du blog
    +
    Identifiant unique du blog. Obligatoire, ne peut être modifié que par un + super administrateur.
    + +
    Nom du blog
    +
    Nom du blog, obligatoire.
    + +
    URL du blog
    +
    URL complète du blog. Obligatoire, ne peut être modifiée que par un super + administrateur.
    + +
    Méthode de lecture de l'URL
    +
    Définit le mode de lecture de l'URL. PATH_INFO est conseillé, QUERY_STRING + doit être utilisé si le premier ne fonctionne pas. En mode PATH_INFO, l'URL du + blog doit se terminer par un "/" et en mode QUERY_STRING, par un "?".
    + +
    État du blog
    +
    +
      +
    • en ligne : blog accessible aux visiteurs
    • +
    • hors ligne : blog inaccessible aux visiteurs + mais les rédacteurs peuvent se connecter
    • +
    • supprimé : blog inaccessible aux visiteurs + comme aux rédacteurs
    • +
    +
    + +
    Description du blog
    +
    Texte libre de description du blog. Il s'agit d'un texte simple, sans + formatage quelconque.
    +
    + +

    Configuration du blog

    +
    +
    Nom de l'éditeur du blog
    +
    Nom de la personne en charge du blog. Peut être le propriétaire ou le + directeur de publication s'il existe.
    + +
    Note de copyright
    +
    Note indiquant les droits de reproduction autorisés pour le blog.
    + +
    Langue par défaut
    +
    Langue de l'interface du blog. Si cette langue existe parmis les + traductions, l'interface sera traduite dans celle-ci, sinon elle apparaîtra + en Anglais.
    + +
    Fuseau horaire du blog
    +
    Définit le fuseau horaire qui sera utilisé pour dater les commentaires et + trackbacks entrant sur le blog.
    + +
    Format des dates
    +
    Indique le format de la date d'un billet sur le blog. Voir la section + "formatage des dates" pour plus détails.
    + +
    Format des heures
    +
    Indique le format de l'heure d'un billet sur le blog. Voir la section + "formatage des dates" pour plus détails.
    + +
    Laisser les commentaires ouvert durant .. jours
    +
    Permet d'indiquer le nombre de jours durant lesquels les commentaires + sont possibles sur un billet après sa publication. Aucune valeur laissera + les commentaires toujours ouverts.
    + +
    Laisser les rétroliens ouvert durant .. jours
    +
    Permet d'indiquer le nombre de jours durant lesquels les rétroliens + sont possibles sur un billet après sa publication. Aucune valeur laissera + les rétroliens toujours ouverts.
    + +
    Accepter les commentaires
    +
    Accepter globalement les commentaires sur le blog. Ce paramètre est + supérieur à celui autorisant ou non les commentaires sur un billet.
    + +
    Accepter les rétroliens
    +
    Accepter globalement les rétroliens sur le blog. Ce paramètre est + supérieur à celui autorisant ou non les rétroliens sur un billet.
    + +
    Modérer les commentaires
    +
    Si cette option est activée, les commentaires ne seront publiés qu'après + l'approbation d'un rédacteur.
    + +
    Modérer les rétroliens
    +
    Si cette option est activée, les rétroliens ne seront publiés qu'après + l'approbation d'un rédacteur.
    + +
    Ajouter la relation "nofollow" aux liens des commentaires et rétroliens
    +
    Ajoute un attribut sur les liens des commentaires et rétroliens indiquant + aux robots des moteurs de recherche de ne pas aller sur ce liens. Cette mesure + est censée permettre de lutter contre le spam mais n'a pas encore vraiment + prouvé son efficacité.
    + +
    Syntaxe wiki pour les commentaires
    +
    Autoriser quelques éléments de la syntaxe wiki dans les commentaires.
    + +
    Afficher .. billets par page
    +
    Le nombre donné sera utilisé comme limite d'affichage des billets sur la + page d'accueil et la première page de chaque catégorie.
    + +
    Afficher des émoticones dans les billets et commentaires
    +
    Remplacer certains symboles comme :-) ou ;-) en image.
    + +
    Afficher .. billets par flux de syndication
    +
    Le nombre donné sera utilisé comme limite d'affichage des billets dans + les flux de syndication.
    + +
    Afficher .. commentaires par flux de syndication
    +
    Le nombre donné sera utilisé comme limite d'affichage des commentaires dans + les flux de syndication.
    + +
    Tronquer le flux de syndication
    +
    Cette option permet de ne fournir qu'un extrait des billets dans les flux + de syndication.
    +
    + +

    Formatage des dates

    +

    Certains champs permettent de formater les dates avec des caractères +particuliers dont voici la définition :

    + +
      +
    • %a : nom abrégé du jour de la semaine (local).
    • +
    • %A : nom complet du jour de la semaine (local).
    • +
    • %b : nom abrégé du mois (local).
    • +
    • %B : nom complet du mois (local).
    • +
    • %c : représentation préférée pour les dates et + heures, en local.
    • +
    • %C : numéro de siècle (l'année, divisée par 100 et + arrondie entre 00 et 99)
    • +
    • %d : jour du mois en numérique (intervalle 01 à + 31)
    • +
    • %D : identique à %m/%d/%y
    • +
    • %e : numéro du jour du mois. Les chiffres sont + précédés d'un espace (de ' 1' à '31')
    • +
    • %g : identique à %G, sur 2 chiffres.
    • +
    • %G : L'année sur 4 chiffres correspondant au numéro + de semaine (voir %V). Même format et valeur que %Y, excepté que si le numéro + de la semaine appartient à l'année précédente ou suivante, l'année courante + sera utilisé à la place.
    • +
    • %h : identique à %b
    • +
    • %H : heure de la journée en numérique, et sur + 24-heures (intervalle de 00 à 23)
    • +
    • %I : heure de la journée en numérique, et sur 12- + heures (intervalle 01 à 12)
    • +
    • %j : jour de l'année, en numérique (intervalle 001 à + 366)
    • +
    • %m : mois en numérique (intervalle 1 à 12)
    • +
    • %M : minute en numérique
    • +
    • %n : newline character
    • +
    • %p : soit `am' ou `pm' en fonction de l'heure + absolue, ou en fonction des valeurs enregistrées en local.
    • +
    • %r : l'heure au format a.m. et p.m.
    • +
    • %R : l'heure au format 24h
    • +
    • %S : secondes en numérique
    • +
    • %t : tabulation
    • +
    • %T : l'heure actuelle (égal à %H:%M:%S)
    • +
    • %u : le numéro de jour dans la semaine, de 1 à 7. (1 + représente Lundi)
    • +
    • %U : numéro de semaine dans l'année, en considérant + le premier dimanche de l'année comme le premier jour de la première + semaine.
    • +
    • %V : le numéro de semaine comme défini dans l'ISO + 8601:1988, sous forme décimale, de 01 à 53. La semaine 1 est la première + semaine qui a plus de 4 jours dans l'année courante, et dont Lundi est le + premier jour. (Utilisez %G ou %g pour les éléments de l'année qui + correspondent au numéro de la semaine pour le timestamp donné.)
    • +
    • %W : numéro de semaine dans l'année, en considérant + le premier lundi de l'année comme le premier jour de la première semaine
    • +
    • %w : jour de la semaine, numérique, avec Dimanche = + 0
    • +
    • %x : format préféré de représentation de la date + sans l'heure
    • +
    • %X : format préféré de représentation de l'heure + sans la date
    • +
    • %y : l'année, numérique, sur deux chiffres (de 00 à + 99)
    • +
    • %Y : l'année, numérique, sur quatre chiffres
    • +
    • %Z ou %z : fuseau horaire, ou nom ou + abréviation
    • +
    • %% : un caractère `%' littéral
    • +
    + + + \ No newline at end of file diff --git a/locales/fr/help/core_categories.html b/locales/fr/help/core_categories.html new file mode 100644 index 0000000..22069a4 --- /dev/null +++ b/locales/fr/help/core_categories.html @@ -0,0 +1,43 @@ + + + Catégories + + + +

    Liste des categories

    +
    +
    Créer une nouvelle catégorie
    +
    Choisissez le nom que vous souhaitez donner à votre nouvelle catégorie + puis cliquer sur Enregistrer pour valider sa création. Pour la modifier, + cliquez sur son nom dans la liste.
    + +
    Changer l'ordre des catégories
    +
    Pour changer l'ordre des catégories, faites glisser les catégories en + utilisant les images en forme de flèche puis cliquez sur enregistrer + l'ordre. Si vous ne voyez pas les flèches, utilisez les champs + numérotés à côté de chaque catégorie.
    +
    + +

    Modifier une catégorie

    +
    +
    Titre
    +
    Choisissez le nom que vous souhaitez donner à votre nouvelle catégorie + puis cliquer sur Enregistrer pour valider sa création.
    + +
    URL
    +
    DotClear crée une url par défaut de la catégorie, qui sera ainsi + accessible avec un chemin de la forme + http://monblog.tld/category/Ma-categorie. En cliquant sur le + petit verrou situé à la droite, vous pouvez la modifier comme il vous + plaira.
    + +
    Description
    +
    Le contenu de ce champ de description sera affiché lors de la sélection + d'une catégorie dans le blog. Vous pouvez le remplir avec toute forme de + contenu (paragraphes, listes, etc.) Le format du texte de la description est + HTML.
    + +
    + + + \ No newline at end of file diff --git a/locales/fr/help/core_comments.html b/locales/fr/help/core_comments.html new file mode 100644 index 0000000..1fe0676 --- /dev/null +++ b/locales/fr/help/core_comments.html @@ -0,0 +1,59 @@ + + + Commentaires + + + +

    Filtres de la liste des commentaires

    +
    +
    Type :
    +
    Aucun ou commentaires ou rétroliens.
    + +
    État :
    +
      +
    • indésirable : reconnu comme spam ;
    • +
    • en attente : en attente de modération ;
    • +
    • non publié : hors ligne ;
    • +
    • publié : en ligne.
    • +
    + +
    Trier par :
    +
    Choisir : date, ou titre du billet, ou auteur ou état, puis indiquez si vous + souhaitez que les résultats s'affichent par ordre croissant ou décroissant.
    + +
    Trier
    +
    Indique l'ordre dans lequel on souhaite effectuer le tri.
    + +
    Auteur du commentaire.
    +
    Recherche indifféremment avec des minuscules ou majuscules. On peut chercher + sur une partie du nom en utilisant le caractère joker + *.
    + +
    Commentaires par page :
    +
    Détermine le nombre de commentaires affichés par page de cette liste.
    +
    + +

    Modifier ou ajouter un commentaire

    +
    +
    Auteur :
    +
    Nom de l'auteur. Ce champ est obligatoire.
    + +
    Email :
    +
    Adresse mail de l'auteur du commmentaire.
    + +
    Site web :
    +
    URL du site ou du blog de l'auteur du commentaire.
    + +
    État :
    +
      +
    • indésirable : reconnu comme spam ;
    • +
    • en attente : en attente de modération ;
    • +
    • non publié : hors ligne
    • +
    • publié : en ligne
    • +
    + +
    Commentaire :
    +
    Contenu du commentaire. Ce champ utilise la syntaxe HTML.
    +
    + + \ No newline at end of file diff --git a/locales/fr/help/core_media.html b/locales/fr/help/core_media.html new file mode 100644 index 0000000..6191dc0 --- /dev/null +++ b/locales/fr/help/core_media.html @@ -0,0 +1,96 @@ + + + Gestionnaire de media + + + + +

    Liste des media

    +

    La page principale du gestionnaire de media présente la liste des fichiers +(media) disponibles pour le blog en cours d'utilisation.

    + +

    Il est possible d'afficher les media dans dans des répertoires ou sous +répertoires en cliquant sur leurs icônes ou leurs noms.

    + +

    Les fichiers son au format MP3 sont présentés avec un lecteur permettant +d'en prendre immédiatement connaissance. Ce lecteur nécessite un lecteur (ou +plugin) Flash sur votre navigateur.

    + +

    Les images sont affichées avec une miniature quand cela est possible.

    + +

    Ajouter des fichiers

    +
    +
    Choisissez un fichier
    +
    Permet de choisir un fichier sur son disque dur pour l'envoyer sur le + blog. Si le fichier dépasse la taille maximale, il ne pourra être envoyé.
    + +
    Titre
    +
    Titre optionnel du fichier à envoyer.
    + +
    Privé
    +
    Indique que le fichier envoyé n'est visible que par son propriétaire tant + qu'il n'est pas publié sur le blog.
    +
    + +

    Nouveau répertoire

    +

    Ce formulaire permet de créer un nouveau répertoire dans le répertoire en +cours d'utilisation. Indiquez simplement un nom comme vous le feriez sur votre +ordinateur.

    + +

    Détail du media

    +

    La page individuelle d'un media présente un ensemble d'informations sur +celui-ci et permet d'effectuer quelques opérations.

    + +
    +
    Nom du fichier
    +
    Changer le nom du fichier en cours.
    + +
    Titre du fichier
    +
    Changer ou donner un titre au fichier en cours.
    + +
    Date du fichier
    +
    Changer la date du fichier.
    + +
    Privé
    +
    Changer le caractère privé ou nom du média.
    + +
    Nouveau répertoire
    +
    Permet de changer l'emplacement du fichier.
    + +
    Changer le fichier
    +
    Ce formulaire permet de changer le fichier tout en en conservant les + attributs (nom, titre...). Comme toujours, il ne peut dépasser la taille + maximalle indiquée.
    +
    + +

    Cas particulier des images

    +

    Lors de l'ajout d'une image, jusqu'à quatre versions de celle-ci peuvent +être disponibles (selon la taille de l'image d'origine) :

    +
      +
    • square : carré de 48 pixels de côté,
    • +
    • thumbnail : image réduite à 100 pixels sur son plus grand côté,
    • +
    • small : image réduite à 240 pixels sur son plus grand côté,
    • +
    • medium : image réduite à 500 pixes sur son plus grand côté,
    • +
    • originale : image originale non transformée.
    • +
    + +

    Attacher un fichier à un billet

    +

    Vous pouvez attacher un fichier à un billet très facilement. Commencez par +créer un nouveau billet et enregistrez-le. Cliquez alors sur "ajouter un fichier +au billet" pour ouvrir le gestionnaire de media.

    + +

    En cliquant sur l'icône [+] (Attacher un fichier au billet) vous pourrez +joindre le fichier à votre billet.

    + +

    Les fichiers attachés à un billet seront visibles sur la page individuelle +du billet, sous la forme d'une liste après le contenu du billet. Les fichiers +MP3 seront automatiquement accompagnés d'un lecteur en Flash permettant la +lecture directe du fichier.

    + +

    Podcast et diffusion de fichiers musicaux

    +

    Tous les fichiers attachés à un billet seront présents dans les fils RSS de +vos billets, vous permettant ainsi de réaliser des podcast de n'importe quel +type de fichier.

    + + + \ No newline at end of file diff --git a/locales/fr/help/core_post.html b/locales/fr/help/core_post.html new file mode 100644 index 0000000..54281a1 --- /dev/null +++ b/locales/fr/help/core_post.html @@ -0,0 +1,137 @@ + + + Rédaction d'un billet + + + + +

    Rédaction du billet

    + +
    +
    Titre du billet
    +
    Inscrivez le titre du billet. Ce champ est obligatoire.
    + +
    Extrait
    +
    Le contenu de ce champ s'affichera dans les pages présentant les listes de + billets telles que la page d'accueil ou le tri sur une catégorie, suivi d'un + lien « Lire la suite ». Il figurera également au début du billet dans le + contexte de l'affichage du billet seul. Si vous ne remplissez pas ce champ, le + champ Contenu sera intégralement affiché dès la page d'accueil.
    + +
    Contenu
    +
    Le contenu du billet. Ce champ est obligatoire.
    + +
    Notes
    +
    Cette zone de texte sert à la prise de notes ou pense-bête divers. Elle ne + sera jamais affichée sur le blog.
    + +
    Catégorie
    +
    La catégorie de votre billet. Pour créer une nouvelle catégorie, rendez vous + dans la section "catégories". Vous pouvez n'affecter + votre billet à aucune catégorie en choisissant la ligne vide.
    + +
    État du billet
    +
    Permet de choisir l'état du billet après enregistrement : +
      +
    • en attente : en attente de publication.
    • +
    • programmé : le billet sera mis en ligne aux date et heure + indiquées dans le champ Publié le.
    • +
    • non publié : billet hors ligne.
    • +
    • publié : billet en ligne.
    • +
    +
    + +
    Publié le
    +
    Permet de modifier la date et l'heure de publication du billet. Si vous avez + choisi le statut programmé il sera mis en ligne aux date et heure + définis dans ce champ.
    + +
    Format du texte
    +
    Permet de choisir la syntaxe de saisie du billet. Le wiki est une syntaxe + simplifiée et sera converti en xhtml valide ; à moins que vous maîtrisiez + parfaitement le HTML nous vous conseillons de la choisir de préférence. + Consultez la référence de la syntaxe Wiki pour plus + d'informations.
    + +
    Accepter les commentaires
    +
    Cochez ou décochez la case selon que vous souhaitez permettre ou interdire + les commentaires sur le billet en particulier. L'option pour permettre ou + autoriser de façon générale les commentaires se situe dans le menu Préférences + du blog.
    + +
    Accepter les rétroliens
    +
    Un rétrolien permet de signaler un billet dans les commentaires d'un autre + blog. Cochez ou décochez la case selon que vous souhaitez permettre ou interdire + les rétroliens sur le billet. L'option pour permettre ou autoriser de façon + générale les rétroliens se situe dans le menu Préférences du blog.
    + +
    Billet sélectionné
    +
    Les billets marqués comme sélectionnés apparaîtront dans le menu de votre + blog, sous l'intitulé « A retenir ».
    + +
    Mot de passe du billet
    +
    Permet de déterminer un mot de passe d'accès à un billet dans le blog. Un + billet protégé par mot de passe ne sera visible nulle part sur votre blog, vous + pourrez en donner l'adresse à vos lecteurs en vous rendant sur le lien + voir le billet.
    + +
    Basename
    +
    Ce champ permet de choisir une URL pour un billet autre que celle par défaut + après avoir cliqué sur le petit verrou placé à sa droite. Si vous essayez + d'utiliser une URL déjà existante, celle-ci se verra incrémentée d'un chiffre.
    + +
    Langue du billet
    +
    Code langue de votre billet. Par défaut, il s'agit du code de votre + langue. Vous pouvez le changer, par le code d'une autre langue, par + exemple "en", "fr-qc". Ce code est libre, il sera utilisé lors de l'affichage + des billets.
    + +
    Pièces jointes
    +
    Les pièces jointes sont tous les media attachés au billet. Le lien + d'ajout d'une pièce jointe permet de choisir un fichier à ajouter au billet. + Pensez à sauvegarder votre billet avant de choisir une pièce jointe. Consultez + l'aide du gestionnaire de media pour plus de détails.
    +
    + +

    Commentaires

    +
    +
    Liste les commentaires
    +
    Depuis l'onglets Commentaires vous pouvez lire et changer l'état + des commentaires de votre billet. Suivant vos permissions, vous pouvez modifier, + mettre en ligne ou hors ligne, supprimer ou classer comme commentaire + indésirable.
    + +
    Ajouter un commentaire
    +
    Depuis l'onglet Ajouter un commentaire vous pouvez répondre + directement à un commentaire sans passer par votre blog. Cette interface vous + permet également de saisir votre commentaire en HTML, sans limitation. Vous + devrez remplir les champs de la même manière qu'en modifiant un commentaire
    +
    + +

    Rétroliens

    +

    Pour faire un rétrolien cliquez sur le lien Faire des rétroliens. +Si vous ne voyez pas ce lien, vérifiez que votre billet est bien dans l'état +publié.

    + +
    +
    URLs à rétrolier
    +
    Indiquez ici la ou les URL que vous aurez relevé dans le billet vers lequel + vous souhaitez envoyer un rétrolien.
    + + +
    Envoyer l'extrait
    +
    Par défaut, ce champ comporte le début de votre billet. C'est ce qui sera + envoyé vers le blog que vous rétroliez avec un lien vers votre billet complet. + Vous pouvez modifier le contenu de cette "accroche" en saisissant directement le + texte de votre choix dans ce champ.
    + + +
    Découverte automatique des URLs à rétrolier
    +
    Si votre billet comporte des liens vers des billets précis et si la + plate-forme du blog destinataire est configurée pour le permettre, cette + fonction découvrira automatiquement les URLs spécifiques d'envoi de + rétrolien.
    +
    + + + diff --git a/locales/fr/help/core_posts.html b/locales/fr/help/core_posts.html new file mode 100644 index 0000000..69b8cf1 --- /dev/null +++ b/locales/fr/help/core_posts.html @@ -0,0 +1,65 @@ + + + Gestion des billets + + + + +

    Filtres de la liste des billets

    + +
    +
    Auteur
    +
    Permet de filtrer les billets par auteur.
    + +
    Catégorie
    +
    Filtrer les billets par catégorie.
    + +
    État
    +
      +
    • en attente : en attente de publication.
    • +
    • programmé : le billet sera mis en ligne aux date et heure + indiquées dans le champ Publié le.
    • +
    • non publié : billet hors ligne.
    • +
    • publié : billet en ligne.
    • +
    + +
    Sélectionné
    +
    Aucun, billet marqué comme sélectionné ou non sélectionné.
    + +
    Mois
    +
    Filtre les billets d'un mois d'une année donné.
    + +
    Langue
    +
    Filtre les billets selon la langue indiquée dans le champ langue du + billet.
    + +
    Trier par
    +
    Permet de trier les résultats de filtrage selon la date, le titre, + la catégorie, l'auteur, l'état de publication ou l'état de sélection.
    + +
    Trier
    +
    Indique l'ordre dans lequel on souhaite effectuer le tri.
    + +
    Billets par page
    +
    Nombre de billets à afficher par page de résultat.
    +
    + +

    Actions par lot sur les billets

    +

    Il est possible d'effectuer un ensemble d'actions sur plusieurs billets, d'un +seul coup. Les actions possibles dépendent des permissions de l'utilisateur.

    + +
      +
    • Publier : mettre le billet en ligne,
    • +
    • Hors ligne : mettre le billet hors ligne,
    • +
    • Programmer : programmer le billet pour mise en ligne à la date de + publication,
    • +
    • En attente : en attente de publication,
    • +
    • Changer de catégorie : envoie sur la liste des catégories pour + changer celle des billets sélectionnés,
    • +
    • Changer l'auteur : permet de changer l'auteur du billet en indiquant + l'identifiant de l'utilisateur qui deviendra le nouvel auteur,
    • +
    • Supprimer : supprime le billet (cette opération est irréversible).
    • +
    + + + \ No newline at end of file diff --git a/locales/fr/help/core_user_pref.html b/locales/fr/help/core_user_pref.html new file mode 100644 index 0000000..d3d935a --- /dev/null +++ b/locales/fr/help/core_user_pref.html @@ -0,0 +1,64 @@ + + + Préférences utilisateur + + + + +

    Informations utilisateurs

    +
    +
    Nom, Prénom
    +
    Si le pseudonyme n'est pas renseigné, le nom de l'auteur qui sera affiché + sera composé des noms et prénoms renseignés dans ces champs.
    + +
    Pseudonyme
    +
    Vous pouvez choisir ici le nom sous lequel vos billets seront signés.
    + +
    Email
    +
    Adresse à laquelle devront être envoyées les notifications de nouveaux + commentaires par email.
    + +
    URL
    +
    Indique le site web de l'utilisateur. S'il est indiqué, le nom du rédacteur + d'un billet sera présenté comme un lien vers le site donné.
    + +
    Format d'édition préféré
    +
    Choix de la syntaxe par défaut pour la saisie des billets. Le wiki est une + syntaxe simplifiée et sera converti en xhtml valide ; à moins que vous ne + maîtrisiez parfaitement le HTML nous vous conseillons le choix du wiki.
    + +
    État des billets par défaut
    +
    Le statut par défaut des billets peut être déterminé à : +
      +
    • en attente : le statut de publication n'a pas encore + été décidé.
    • +
    • programmé : le billet sera mis en ligne aux date et + heure indiquées dans le champ Publié le.
    • +
    • non publié : billet hors ligne.
    • +
    • publié : billet en ligne.
    • +
    +
    + +
    Taille de la zone d'édition
    +
    Fixe la hauteur de la zone d'édtion du billet. Par défaut ce paramètre est + réglé à la valeur 24.
    + +
    Langue de l'utilisateur
    +
    Indique la langue dans laquelle on souhaite publier ses billets. Si la + traduction existe, l'interface sera également traduite dans cette langue.
    + +
    Fuseau horaire de l'utilisateur
    +
    Ce choix déterminera l'affichage de l'heure de publication des billets.
    + +
    Activer l'éditeur visuel
    +
    Permet d'activer ou non l'éditeur visuel des billets et des catégories.
    +
    + +

    Changer le mot de passe

    +

    Pour changer votre mot de passe, indiquez deux fois le nouveau mot de passe +dans les champs Nouveau mot de passe et Confirmer le mot de +passe. Le mot de passe doit être long d'au moins 6 caractères.

    +

    Si aucun mot de passe n'est donné, celui-ci ne sera pas changé.

    + + + \ No newline at end of file diff --git a/locales/fr/help/core_wiki.html b/locales/fr/help/core_wiki.html new file mode 100644 index 0000000..1fb3b90 --- /dev/null +++ b/locales/fr/help/core_wiki.html @@ -0,0 +1,85 @@ + + + Référence de la syntaxe Wiki + + + + +

    Référence de la syntaxe Wiki

    + +

    La syntaxe Wiki est une manière d'écrire du texte avec un jeu de balises +réduit au minimum, permettant de couvrir les besoins les plus courants +(titres, paragraphes, citations, listes...).

    + +
    +
    Éléments de bloc
    +
    +
      +
    • Laissez une ligne vide entre chaque bloc de même nature.
    • +
    • Paragraphe : texte libre, terminé par une ligne + vide si suivi d'un second paragraphe.
    • +
    • Titre : !!! titre, !! titre + ou ! titre pour des titres plus ou moins importants.
    • +
    • Trait horizontal : ----
    • +
    • Listes : lignes débutant par * pour des + listes à puce ou # pour des listes numérotées. Vous pouvez faire + des listes imbriquées en mélangeant les codes de liste. Par exemple : +
      +* item 1
      +** item 1.1
      +* item 2
      +*# item 2.1
      +...
      +
      +
    • +
    • Texte préformaté : espace avant chaque ligne de texte.
    • +
    • Bloc de citation : > devant chaque + ligne de texte.
    • +
    +
    + +
    Éléments de formatage
    +
    +
      +
    • Emphase : deux apostrophes ''texte''
    • +
    • Forte emphase : deux soulignés __texte__
    • +
    • Retour forcé à la ligne : %%%
    • +
    • Insertion : deux plus ++texte++
    • +
    • Suppression : deux moins --texte--
    • +
    • Lien : [url], [nom|url], + [nom|url|langue] ou [nom|url|langue|titre]
    • +
    • Image : + ((url|texte alternatif)), + ((url|texte alternatif|position)) ou + ((url|texte alternatif|position|description longue)). +
      La position peut prendre les valeurs L ou G (gauche), R ou D (droite) ou C (centré).
    • +
    • Ancre : ~ancre~
    • +
    • Acronyme : ??acronyme|titre??
    • +
    • Code HTML en ligne: deux apostrophes inversées ``code html``
    • +
    • Citation en ligne : {{citation}}, + {{citation|langue}} ou {{citation|langue|url}}
    • +
    • Code : @@code ici@@
    • +
    • Note de bas de page : $$Corps de la note$$
    • +
    +
    + +
    Empêcher le formatage du texte
    +
    Pour insérer un caractère sans que celui-ci soit reconnu comme un caractère +de formatage, ajoutez le caractère \ avant celui-ci. Par exemple : +\[texte entre crochet qui n'est pas un lien\] +
    + +
    Insérer du code HTML
    +
    Vous pouvez ponctuellement avoir besoin d'insérer du code HTML dans votre +texte au format Wiki. Pour cela, utilisez le code suivant : +
    +///html
    +<p style="color:red">mon texte en rouge</p>
    +///
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/locales/fr/help/themeEditor.html b/locales/fr/help/themeEditor.html new file mode 100644 index 0000000..b79802b --- /dev/null +++ b/locales/fr/help/themeEditor.html @@ -0,0 +1,37 @@ + + + + Éditeur de thème + + + +

    L'éditeur de thème vous permet de modifier les fichiers template, les feuilles +de style et les fichiers JavaScript de votre thème en cours d'utilisation.

    + +

    La liste des fichiers est divisée en trois parties :

    +
      +
    • Fichiers template : les templates
    • +
    • Fichiers CSS : les feuilles de style
    • +
    • Fichiers JavaScript
    • +
    + +

    Une puce jaune contre le nom de chaque fichier indique que celui-ci fait partie +du thème. Une puce rouge indique qu'il se trouve dans le thème parent. +Une puce noire indique qu'il se trouve dans le thème "default".

    + +

    Si vous modifiez un fichier se trouvant dans le thème "default", celui-ci +sera copié dans votre thème en cours d'utilisation.

    + +

    Pour modifier un fichier, cliquez sur son nom, le contenu s'affichera alors +dans une zone d'édition. Si le fichier peut-être écrit vous pourrez sauvegarder +celui-ci en cliquant sur "enregistrer".

    + +

    Les modifications sont immédiatement appliquées lors de la sauvegarde, soyez +vigilants.

    + +

    Pour éditer vos templates, n'hésitez pas à consultez la +liste des marqueurs +de template.

    + + + \ No newline at end of file diff --git a/locales/fr/main.lang.php b/locales/fr/main.lang.php new file mode 100644 index 0000000..bee1ae2 --- /dev/null +++ b/locales/fr/main.lang.php @@ -0,0 +1,816 @@ +This is a comment.

    +

    To delete it, log in and view your blog\'s comments. Then you might remove or edit it.

    '] = '

    Ceci est un commentaire

    Pour le supprimer, connectez-vous et affichez les commentaires de votre blog. Vous pourrez alors le supprimer ou le modifier.

    '; +$GLOBALS['__l10n']['Dotclear installation'] = 'Installation de Dotclear'; +$GLOBALS['__l10n']['Cache directory %s is not writable.'] = 'Le répertoire de cache %s n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['Errors:'] = 'Erreurs :'; +$GLOBALS['__l10n']['Configuration file has been successfully created.'] = 'Le fichier de configuration a été créé avec succès.'; +$GLOBALS['__l10n']['User information'] = 'Informations utilisateur'; +$GLOBALS['__l10n']['Please provide the following information needed to create the first user.'] = 'Merci de fournir les informations suivantes pour créer le premier utilisateur.'; +$GLOBALS['__l10n']['First Name:'] = 'Prénom :'; +$GLOBALS['__l10n']['Last Name:'] = 'Nom'; +$GLOBALS['__l10n']['Username and password'] = 'Identifiant ou mot de passe incorrect'; +$GLOBALS['__l10n']['Confirm password:'] = 'Confirmez le mot de passe :'; +$GLOBALS['__l10n']['All done!'] = 'Terminé !'; +$GLOBALS['__l10n']['Dotclear has been successfully installed. Here is some useful information you should keep.'] = 'Dotclear a été installé avec succès. Conservez les informations suivantes précieusement.'; +$GLOBALS['__l10n']['Your account'] = 'Votre compte'; +$GLOBALS['__l10n']['Your blog'] = 'Votre blog'; +$GLOBALS['__l10n']['Blog address:'] = 'Adresse du blog :'; +$GLOBALS['__l10n']['Administration interface:'] = 'Interface d\'administration :'; +$GLOBALS['__l10n']['Manage your blog now'] = 'Gérez votre blog'; +$GLOBALS['__l10n']['The file %s already exists. If you need to reset any of the configuration items in this file, please delete it first or you may continue to install.'] = 'Le fichier %s existe déjà. Pour réinitialiser votre configuration, supprimez d\'abord ce fichier ou bien continuez l\'installation.'; +$GLOBALS['__l10n']['File %s does not exist.'] = 'Le fichier %s n\'existe pas.'; +$GLOBALS['__l10n']['Cannot write %s file.'] = 'Impossible d\'écrire le fichier %s.'; +$GLOBALS['__l10n']['Dotclear installation wizard'] = 'Assistant d\'installation de Dotclear'; +$GLOBALS['__l10n']['System information'] = 'Informations système'; +$GLOBALS['__l10n']['Please provide the following information needed to create your configuration file.'] = 'Merci de fournir les informations suivantes pour créer votre fichier de configuration.'; +$GLOBALS['__l10n']['Database type:'] = 'Type de base de données :'; +$GLOBALS['__l10n']['Database Host Name:'] = 'Nom d\'hôte de la base de données :'; +$GLOBALS['__l10n']['Database Name:'] = 'Nom de la base de données :'; +$GLOBALS['__l10n']['Database User Name:'] = 'Nom d\'utilisateur de la base de données :'; +$GLOBALS['__l10n']['Database Password:'] = 'Mot de passe de la base de données :'; +$GLOBALS['__l10n']['Database Tables Prefix:'] = 'Préfixe des tables de la base de données :'; +$GLOBALS['__l10n']['No such installed language'] = 'Cette langue n\'est pas installée'; +$GLOBALS['__l10n']['You can\'t remove English language.'] = 'Vous ne pouvez pas supprimer la langue anglaise.'; +$GLOBALS['__l10n']['Permissions to delete language denied.'] = 'Permission de supprimer la langue refusée.'; +$GLOBALS['__l10n']['Invalid language file URL.'] = 'URL de fichier de langue invalide.'; +$GLOBALS['__l10n']['Languages management'] = 'Gestion des langues'; +$GLOBALS['__l10n']['Language has been successfully deleted.'] = 'La langue a été supprimée avec succès.'; +$GLOBALS['__l10n']['Language has been successfully installed.'] = 'La langue a été installée avec succès.'; +$GLOBALS['__l10n']['Language has been successfully upgraded'] = 'La langue a été mise à jour avec succès.'; +$GLOBALS['__l10n']['Here you can install, upgrade or remove languages for your Dotclear installation.'] = 'Sur cette page, vous pouvez installer, mettre à jour ou supprimer des langues de votre installation de Dotclear.'; +$GLOBALS['__l10n']['You can change your user language in your preferences or change your blog\'s main language in your blog settings.'] = 'Vous pouvez changer votre langue d\'utilisateur dans vos préférences ou changer la langue principale de votre blog dans vos paramètres de blog.'; +$GLOBALS['__l10n']['Installed languages'] = 'Langues installées'; +$GLOBALS['__l10n']['No additional language is installed.'] = 'Aucune langue supplémentaire n\'est installée.'; +$GLOBALS['__l10n']['Language'] = 'Langue'; +$GLOBALS['__l10n']['Action'] = 'Action'; +$GLOBALS['__l10n']['Delete'] = 'Supprimer'; +$GLOBALS['__l10n']['Install or upgrade languages'] = 'Installer ou mettre à jour une langue'; +$GLOBALS['__l10n']['You can install or remove a language by adding or removing the relevant directory in your %s folder.'] = 'Vous pouvez installer ou supprimer une langue en ajoutant ou supprimant le répertoire correspondant dans votre répertoire %s.'; +$GLOBALS['__l10n']['Available languages'] = 'Langues disponibles'; +$GLOBALS['__l10n']['You can download and install a additional language directly from Dotclear.net. Proposed languages are based on your version: %s.'] = 'Vous pouvez télécharger et installer une langue supplémentaire directement depuis Dotclear.net. Les langues proposées sont basées sur votre version : %s.'; +$GLOBALS['__l10n']['Language:'] = 'Langue :'; +$GLOBALS['__l10n']['Install language'] = 'Installer la langue'; +$GLOBALS['__l10n']['You can install languages by uploading zip files.'] = 'Vous pouvez installer des langues en déposant des fichiers zip.'; +$GLOBALS['__l10n']['Language zip file:'] = 'Fichier zip de la langue :'; +$GLOBALS['__l10n']['Upload language'] = 'Déposer la langue'; +$GLOBALS['__l10n']['Invalid language zip file.'] = 'Fichier zip de langue invalide.'; +$GLOBALS['__l10n']['The zip file does not appear to be a valid Dotclear language pack.'] = 'Le fichier zip ne semble pas être un fichier valide de langue Dotclear.'; +$GLOBALS['__l10n']['An error occurred during language upgrade.'] = 'Une erreur est survenue durant la mise à jour de la langue.'; +$GLOBALS['__l10n']['Error:'] = 'Erreur :'; +$GLOBALS['__l10n']['By names, ascendant'] = 'Par noms, croissants'; +$GLOBALS['__l10n']['By names, descendant'] = 'Par noms, décroissants'; +$GLOBALS['__l10n']['By dates, ascendant'] = 'Par dates, croissantes'; +$GLOBALS['__l10n']['By dates, descendant'] = 'Par dates, décroissantes'; +$GLOBALS['__l10n']['Media manager'] = 'Gestionnaire de médias'; +$GLOBALS['__l10n']['confirm removal'] = 'Confirmer la suppression'; +$GLOBALS['__l10n']['Are you sure you want to remove %s?'] = 'Êtes-vous certain de vouloir supprimer %s ?'; +$GLOBALS['__l10n']['cancel'] = 'annuler'; +$GLOBALS['__l10n']['yes'] = 'oui'; +$GLOBALS['__l10n']['Directory has been successfully created.'] = 'Répertoire créé avec succès.'; +$GLOBALS['__l10n']['Files have been successfully uploaded.'] = 'Fichier chargé avec succès.'; +$GLOBALS['__l10n']['File has been successfully removed.'] = 'Fichier supprimé avec succès.'; +$GLOBALS['__l10n']['Directory has been successfully removed.'] = 'Répertoire supprimé avec succès.'; +$GLOBALS['__l10n']['Directory has been successfully rebuilt.'] = 'Répertoire reconstruit avec succès.'; +$GLOBALS['__l10n']['Zip file has been successfully extracted.'] = 'Le fichier zip a été extrait avec succès.'; +$GLOBALS['__l10n']['Choose a file to attach to entry %s by clicking on %s.'] = 'Choisissez un fichier à attacher au billet %s en cliquant sur %s.'; +$GLOBALS['__l10n']['Attach this file to entry'] = 'Attacher ce fichier au billet'; +$GLOBALS['__l10n']['Choose a file to insert into entry by clicking on %s.'] = 'Choisissez un fichier à insérer dans le billet en cliquant sur %s.'; +$GLOBALS['__l10n']['No file.'] = 'Aucun fichier.'; +$GLOBALS['__l10n']['Sort files:'] = 'Trier les fichiers :'; +$GLOBALS['__l10n']['Add files'] = 'Ajouter des fichiers'; +$GLOBALS['__l10n']['Choose a file:'] = 'Choisissez un fichier :'; +$GLOBALS['__l10n']['Maximum size %s'] = 'Taille maximale %s'; +$GLOBALS['__l10n']['Private'] = 'Privé'; +$GLOBALS['__l10n']['send'] = 'envoyer'; +$GLOBALS['__l10n']['Please take care to publish media that you own and that are not protected by copyright.'] = 'Veuillez prendre garde à ne publier que des médias que vous possédez ou qui ne sont pas protégés contre la copie.'; +$GLOBALS['__l10n']['New directory'] = 'Nouveau répertoire'; +$GLOBALS['__l10n']['Directory Name:'] = 'Nom du répertoire :'; +$GLOBALS['__l10n']['Download this directory as a zip file'] = 'Télécharger ce répertoire dans un fichier zip'; +$GLOBALS['__l10n']['open'] = 'ouvrir'; +$GLOBALS['__l10n']['Insert this file into entry'] = 'Insérer ce fichier dans le billet'; +$GLOBALS['__l10n']['Not a valid file'] = 'Fichier invalide'; +$GLOBALS['__l10n']['File has been successfully updated.'] = 'Fichier mis à jour avec succès.'; +$GLOBALS['__l10n']['Thumbnails have been successfully updated.'] = 'Les miniatures ont été mises à jour avec succès.'; +$GLOBALS['__l10n']['Insert media item'] = 'Insérer un média'; +$GLOBALS['__l10n']['Image size:'] = 'Taille de l\'image :'; +$GLOBALS['__l10n']['original'] = 'originale'; +$GLOBALS['__l10n']['Image alignment'] = 'Alignement de l\'image'; +$GLOBALS['__l10n']['None'] = 'Aucun'; +$GLOBALS['__l10n']['Left'] = 'Gauche'; +$GLOBALS['__l10n']['Right'] = 'Droite'; +$GLOBALS['__l10n']['Center'] = 'Centre'; +$GLOBALS['__l10n']['Image insertion'] = 'Insertion de l\'image'; +$GLOBALS['__l10n']['As a single image'] = 'En tant qu\'image uniquement'; +$GLOBALS['__l10n']['As a link to original image'] = 'En tant que lien vers l\'image originale'; +$GLOBALS['__l10n']['MP3 disposition'] = 'Disposition du MP3'; +$GLOBALS['__l10n']['Please note that you cannot insert mp3 files with visual editor.'] = 'Merci de noter que vous ne pouvez pas insérer de fichier mp3 avec l\'éditeur visuel.'; +$GLOBALS['__l10n']['Please note that you cannot insert video files with visual editor.'] = 'Merci de noter que vous ne pouvez pas insérer de fichier vidéo avec l\'éditeur visuel.'; +$GLOBALS['__l10n']['Video size'] = 'Taille de la vidéo'; +$GLOBALS['__l10n']['Width:'] = 'Largeur :'; +$GLOBALS['__l10n']['Height:'] = 'Hauteur :'; +$GLOBALS['__l10n']['Video disposition'] = 'Disposition de la vidéo'; +$GLOBALS['__l10n']['Media item will be inserted as a link.'] = 'Le média sera inséré en tant que lien.'; +$GLOBALS['__l10n']['Cancel'] = 'Annuler'; +$GLOBALS['__l10n']['Insert'] = 'Insérer'; +$GLOBALS['__l10n']['Media details'] = 'Détails du média'; +$GLOBALS['__l10n']['Available sizes:'] = 'Tailles disponibles :'; +$GLOBALS['__l10n']['File owner:'] = 'Propriétaire du fichier :'; +$GLOBALS['__l10n']['File type:'] = 'Type de fichier :'; +$GLOBALS['__l10n']['File size:'] = 'Taille du fichier :'; +$GLOBALS['__l10n']['File URL:'] = 'URL du fichier :'; +$GLOBALS['__l10n']['Show entries containing this media'] = 'Afficher les billets contenant ce média'; +$GLOBALS['__l10n']['Entries containing this media'] = 'Billets contenant ce média'; +$GLOBALS['__l10n']['No entry seems contain this media.'] = 'Aucun billet ne semble contenir ce média.'; +$GLOBALS['__l10n']['Image details'] = 'Détails de l\'image'; +$GLOBALS['__l10n']['No detail'] = 'Aucun détail'; +$GLOBALS['__l10n']['Update thumbnails'] = 'Mettre à jour les miniatures'; +$GLOBALS['__l10n']['This will create or update thumbnails for this image.'] = 'Ceci va créer ou mettre à jour les miniatures pour cette image.'; +$GLOBALS['__l10n']['update thumbnails'] = 'mettre à jour les miniatures'; +$GLOBALS['__l10n']['Extract in a new directory'] = 'Extraire dans un nouveau répertoire'; +$GLOBALS['__l10n']['Extract in current directory'] = 'Extraire dans le répertoire actuel'; +$GLOBALS['__l10n']['Extract archive'] = 'Extraire l\'archive'; +$GLOBALS['__l10n']['This will extract archive in a new directory that should not exists yet.'] = 'Ceci va extraire l\'archive dans un nouveau répertoire qui ne doit pas encore exister.'; +$GLOBALS['__l10n']['This will extract archive in current directory and will overwrite existing files or directory.'] = 'Ceci va extraire l\'archive dans le répertoire actuel et va écraser les fichiers ou répertoires existants.'; +$GLOBALS['__l10n']['Extract mode:'] = 'Mode d\'extraction :'; +$GLOBALS['__l10n']['extract'] = 'extraire'; +$GLOBALS['__l10n']['Change media properties'] = 'Changer les propriétés du média'; +$GLOBALS['__l10n']['File name:'] = 'Nom du fichier :'; +$GLOBALS['__l10n']['File title:'] = 'Titre du fichier :'; +$GLOBALS['__l10n']['File date:'] = 'Date du fichier :'; +$GLOBALS['__l10n']['New directory:'] = 'Nouveau répertoire :'; +$GLOBALS['__l10n']['Change file'] = 'Changer le fichier'; +$GLOBALS['__l10n']['No blog or user given.'] = 'Vous n\'avez pas indiqué de blog ou d\'utilisateur'; +$GLOBALS['__l10n']['permissions'] = 'permissions'; +$GLOBALS['__l10n']['Permissions'] = 'Permissions'; +$GLOBALS['__l10n']['The permissions have been successfully updated.'] = 'Permissions mises à jour avec succès.'; +$GLOBALS['__l10n']['You are about to change permissions on the following blogs for users %s.'] = 'Vous allez changer les permissions des utilisateurs %s pour ces blogs.'; +$GLOBALS['__l10n']['choose a blog'] = 'choisissez un blog'; +$GLOBALS['__l10n']['Choose a blog'] = 'Choisissez un blog'; +$GLOBALS['__l10n']['Entries per page'] = 'Billets par page'; +$GLOBALS['__l10n']['Choose one or more blogs to which you want to give permissions to users %s.'] = 'Choisissez un ou plusieurs blogs pour lesquels les utilisateurs suivants auront des permissions : %s.'; +$GLOBALS['__l10n']['set permissions'] = 'définir les permissions'; +$GLOBALS['__l10n']['No content found on this plugin.'] = 'Aucun contenu pour cette extension.'; +$GLOBALS['__l10n']['Plugin not found'] = 'Extension introuvable'; +$GLOBALS['__l10n']['The plugin you reached does not exist or does not have an admin page.'] = 'L\'extension que vous essayez d\'atteindre n\'existe pas ou n\'a pas de page d\'administration.'; +$GLOBALS['__l10n']['No such plugin.'] = 'Extension inexistante.'; +$GLOBALS['__l10n']['You don\'t have permissions to delete this plugin.'] = 'Vous n\'avez pas les permissions pour effacer cette extension'; +$GLOBALS['__l10n']['You don\'t have permissions to deactivate this plugin.'] = 'Vous n\'avez pas les permissions pour désactiver cette extension.'; +$GLOBALS['__l10n']['Plugins management'] = 'Gestion des extensions'; +$GLOBALS['__l10n']['Plugin has been successfully deleted.'] = 'L\'extension a été supprimée avec succès.'; +$GLOBALS['__l10n']['Plugin has been successfully installed.'] = 'L\'extension a été installée avec succès.'; +$GLOBALS['__l10n']['Plugin has been successfully upgraded'] = 'L\'extension a été mise à jour avec succès.'; +$GLOBALS['__l10n']['Plugins add new functionalities to Dotclear. Here you can activate or deactivate installed plugins.'] = 'Les extensions ajoutent de nouvelles fonctionnalités à Dotclear. Ici, vous pouvez activer ou désactiver les extensions installées.'; +$GLOBALS['__l10n']['You can find additional plugins for your blog on %s.'] = 'Vous pouvez trouver de nouvelles extensions pour votre blog sur %s.'; +$GLOBALS['__l10n']['To install or upgrade a plugin you generally just need to upload it in "Install or upgrade a plugin" section.'] = 'Tout ce que vous avez à faire pour installer ou mettre à jour une extension est généralement de la déposer dans la section "Installer ou mettre à jour une extension".'; +$GLOBALS['__l10n']['To install or upgrade a plugin you just need to extract it in your plugins directory.'] = 'Tout ce que vous avez à faire pour installer une extension est de l\'extraire dans votre répertoire d\'extensions.'; +$GLOBALS['__l10n']['Plugins'] = 'Extensions'; +$GLOBALS['__l10n']['Activated plugins'] = 'Extensions activées'; +$GLOBALS['__l10n']['Plugin'] = 'Extension'; +$GLOBALS['__l10n']['Version'] = 'Version'; +$GLOBALS['__l10n']['Details'] = 'Détails'; +$GLOBALS['__l10n']['Deactivate'] = 'Désactiver'; +$GLOBALS['__l10n']['Deactivated plugins'] = 'Extensions désactivées'; +$GLOBALS['__l10n']['Activate'] = 'Activer'; +$GLOBALS['__l10n']['Install or upgrade a plugin'] = 'Installer ou mettre à jour une extension'; +$GLOBALS['__l10n']['You can install plugins by uploading or downloading zip files.'] = 'Vous pouvez installer des extensions en déposant ou téléchargeant des fichiers zip.'; +$GLOBALS['__l10n']['Plugin zip file:'] = 'Fichier zip de l\'extension :'; +$GLOBALS['__l10n']['Upload plugin'] = 'Déposer l\'extension'; +$GLOBALS['__l10n']['Plugin zip file URL:'] = 'URL du fichier zip de l\'extension :'; +$GLOBALS['__l10n']['Download plugin'] = 'Télécharger l\'extension'; +$GLOBALS['__l10n']['To enable this function, please give write access to your plugins directory.'] = 'Pour activer cette fonction, donnez un accès en écriture à votre répertoire d\'extensions.'; +$GLOBALS['__l10n']['Add a link'] = 'Ajouter un lien'; +$GLOBALS['__l10n']['Available'] = 'Disponible'; +$GLOBALS['__l10n']['Most used'] = 'Plus utilisées'; +$GLOBALS['__l10n']['Link URL:'] = 'URL du lien :'; +$GLOBALS['__l10n']['Link language:'] = 'Langue du lien :'; +$GLOBALS['__l10n']['insert'] = 'insérer'; +$GLOBALS['__l10n']['Add a link to an entry'] = 'Ajouter un lien vers un billet'; +$GLOBALS['__l10n']['Search entry:'] = 'Rechercher un billet :'; +$GLOBALS['__l10n']['This entry does not exist.'] = 'Ce billet n\'existe pas.'; +$GLOBALS['__l10n']['Edit entry'] = 'Modifier le billet'; +$GLOBALS['__l10n']['next entry'] = 'billet suivant'; +$GLOBALS['__l10n']['previous entry'] = 'billet précédent'; +$GLOBALS['__l10n']['Entry has been successfully updated.'] = 'Billet mis à jour avec succès.'; +$GLOBALS['__l10n']['Entry has been successfully created.'] = 'Billet créé avec succès.'; +$GLOBALS['__l10n']['File has been successfully attached.'] = 'Fichier attaché avec succès.'; +$GLOBALS['__l10n']['Attachment has been successfully removed.'] = 'Pièce jointe retirée avec succès.'; +$GLOBALS['__l10n']['Comment has been successfully created.'] = 'Commentaire créé avec succès.'; +$GLOBALS['__l10n']['Don\'t forget to validate your XHTML conversion by saving your post.'] = 'Enregistrez votre billet pour valider la transformation en XHTML.'; +$GLOBALS['__l10n']['View entry'] = 'Voir le billet'; +$GLOBALS['__l10n']['Preview entry'] = 'Prévisualiser le billet'; +$GLOBALS['__l10n']['Entry status:'] = 'État du billet :'; +$GLOBALS['__l10n']['Published on:'] = 'Publié le :'; +$GLOBALS['__l10n']['Text formating:'] = 'Format du texte :'; +$GLOBALS['__l10n']['Convert to XHTML'] = 'Convertir en XHTML'; +$GLOBALS['__l10n']['Selected entry'] = 'Billet sélectionné'; +$GLOBALS['__l10n']['Entry lang:'] = 'Langue du billet :'; +$GLOBALS['__l10n']['Entry password:'] = 'Mot de passe du billet :'; +$GLOBALS['__l10n']['Basename:'] = 'URL spécifique :'; +$GLOBALS['__l10n']['Warning: If you set the URL manually, it may conflict with another entry.'] = 'Attention : si vous indiquez l\'URL manuellement, celle-ci peut entrer en conflit avec un autre billet.'; +$GLOBALS['__l10n']['Attachments'] = 'Pièces jointes'; +$GLOBALS['__l10n']['remove'] = 'supprimer'; +$GLOBALS['__l10n']['No attachment.'] = 'Aucune pièce jointe'; +$GLOBALS['__l10n']['Add files to this entry'] = 'Ajouter un fichier au billet'; +$GLOBALS['__l10n']['Excerpt:'] = 'Extrait :'; +$GLOBALS['__l10n']['Notes:'] = 'Notes :'; +$GLOBALS['__l10n']['Ping blogs'] = 'Faire des rétroliens'; +$GLOBALS['__l10n']['Trackbacks'] = 'Rétroliens'; +$GLOBALS['__l10n']['No trackback'] = 'Aucun rétrolien'; +$GLOBALS['__l10n']['Add a comment'] = 'Ajouter un commentaire'; +$GLOBALS['__l10n']['Name:'] = 'Nom :'; +$GLOBALS['__l10n']['IP address'] = 'Adresse IP'; +$GLOBALS['__l10n']['published'] = 'publié'; +$GLOBALS['__l10n']['unpublished'] = 'non publié'; +$GLOBALS['__l10n']['pending'] = 'en attente'; +$GLOBALS['__l10n']['junk'] = 'indésirable'; +$GLOBALS['__l10n']['Edit this comment'] = 'Modifier ce commentaire'; +$GLOBALS['__l10n']['This attachment does not exist'] = 'Cette pièce jointe n\'existe pas'; +$GLOBALS['__l10n']['Remove attachment'] = 'Supprimer la pièce jointe'; +$GLOBALS['__l10n']['Attachment'] = 'Pièce jointe'; +$GLOBALS['__l10n']['Are you sure you want to remove this attachment?'] = 'Êtes-vous certain de vouloir supprimer cette pièce jointe ?'; +$GLOBALS['__l10n']['selected'] = 'sélectionné'; +$GLOBALS['__l10n']['not selected'] = 'non sélectionné'; +$GLOBALS['__l10n']['Category'] = 'Catégorie'; +$GLOBALS['__l10n']['Selected'] = 'Sélectionné'; +$GLOBALS['__l10n']['schedule'] = 'programmer'; +$GLOBALS['__l10n']['mark as selected'] = 'marquer sélectionné'; +$GLOBALS['__l10n']['mark as unselected'] = 'marquer non sélectionné'; +$GLOBALS['__l10n']['change category'] = 'changer la catégorie'; +$GLOBALS['__l10n']['change author'] = 'changer l\'auteur'; +$GLOBALS['__l10n']['Selected:'] = 'Sélectionné :'; +$GLOBALS['__l10n']['Month:'] = 'Mois :'; +$GLOBALS['__l10n']['Lang:'] = 'Langue :'; +$GLOBALS['__l10n']['Selected entries action:'] = 'Action sur les billets sélectionnés :'; +$GLOBALS['__l10n']['This user does not exist'] = 'Cet utilisateur n\'existe pas'; +$GLOBALS['__l10n']['Change category for entries'] = 'Changer de catégorie pour les billets'; +$GLOBALS['__l10n']['Change author for entries'] = 'Changer l\'auteur des billets'; +$GLOBALS['__l10n']['Author ID:'] = 'Identifiant de l\'utilisateur'; +$GLOBALS['__l10n']['If you want to change your email or password you must provide your current password.'] = 'Si vous voulez changer votre adresse email ou votre mot de passe, vous devez indiquer votre mot de passe.'; +$GLOBALS['__l10n']['Personal information has been successfully updated.'] = 'Informations personnelles mises à jour avec succès.'; +$GLOBALS['__l10n']['Display name:'] = 'Pseudonyme :'; +$GLOBALS['__l10n']['Preferred format:'] = 'Format d\'édition préféré :'; +$GLOBALS['__l10n']['Default entry status:'] = 'État des billets par défaut :'; +$GLOBALS['__l10n']['Entry edit field height:'] = 'Taille de la zone d\'édition :'; +$GLOBALS['__l10n']['User language:'] = 'Langue de l\'utilisateur :'; +$GLOBALS['__l10n']['User timezone:'] = 'Fuseau horaire de l\'utilisateur :'; +$GLOBALS['__l10n']['Enable WYSIWYG mode'] = 'Activer l\'éditeur visuel'; +$GLOBALS['__l10n']['Change your password'] = 'Changer votre mot de passe'; +$GLOBALS['__l10n']['New password:'] = 'Nouveau mot de passe :'; +$GLOBALS['__l10n']['Search'] = 'Rechercher'; +$GLOBALS['__l10n']['Search options'] = 'Options de recherche'; +$GLOBALS['__l10n']['Query:'] = 'Requête :'; +$GLOBALS['__l10n']['search entries'] = 'rechercher des billets'; +$GLOBALS['__l10n']['search comments'] = 'recherche des commentaires'; +$GLOBALS['__l10n']['%d entries found'] = '%d billets trouvés'; +$GLOBALS['__l10n']['%d entry found'] = '%d billet trouvé'; +$GLOBALS['__l10n']['%d comment found'] = '%d commentaire trouvé'; +$GLOBALS['__l10n']['%d comments found'] = '%d commentaires trouvés'; +$GLOBALS['__l10n']['This entry does not exist or is not published'] = 'Ce billet n\'existe pas ou n\'est pas publié'; +$GLOBALS['__l10n']['All pings sent.'] = 'Tous les rétroliens ont été envoyés.'; +$GLOBALS['__l10n']['Auto discover ping URLs'] = 'Découverte automatique des URL à rétrolier'; +$GLOBALS['__l10n']['URLs to ping:'] = 'URLs à rétrolier :'; +$GLOBALS['__l10n']['Send excerpt:'] = 'Envoyer l\'extrait :'; +$GLOBALS['__l10n']['Previously sent pings'] = 'Rétroliens déjà envoyés'; +$GLOBALS['__l10n']['Dotclear update'] = 'Mise à jour de Dotclear'; +$GLOBALS['__l10n']['Unable to delete file %s'] = 'Impossible de supprimer le fichier %s'; +$GLOBALS['__l10n']['Downloaded Dotclear archive seems to be corrupted. Try download it again.'] = 'L\'archive téléchargée de Dotclear semble être corrompue. Essayer de la télécharger à nouveau.'; +$GLOBALS['__l10n']['The following files of your Dotclear installation have been modified so we won\'t try to update your installation. Please try to update manually.'] = 'Comme les fichiers suivants de votre installation de Dotclear ont été modifiés, votre installation ne peut être mise à jour. Merci de mettre à jour manuellement.'; +$GLOBALS['__l10n']['The following files of your Dotclear installation are not readable. Please fix this or try to make a backup file named %s manually.'] = 'Les fichiers suivants de votre installation de Dotclear ne peuvent pas être lus. Veuillez corriger ceci ou créer un fichier de backup nommé %s manuellement.'; +$GLOBALS['__l10n']['The following files of your Dotclear installation cannot be written. Please fix this or try to update manually.'] = 'Les fichiers suivants de votre installation de Dotclear ne peuvent pas être écrits. Veuillez corriger ceci ou mettre à jour manuellement.'; +$GLOBALS['__l10n']['No newer Dotclear version available.'] = 'Aucune nouvelle version de Dotclear n\'est disponible.'; +$GLOBALS['__l10n']['Dotclear %s is available.'] = 'Dotclear %s est disponible.'; +$GLOBALS['__l10n']['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.'] = 'Pour mettre à jour votre installation de Dotclear, cliquez sur le bouton suivant. Un fichier de sauvegarde de votre installation actuelle sera créé dans votre répertoire principal.'; +$GLOBALS['__l10n']['Update Dotclear'] = 'Mettre à jour Dotclear'; +$GLOBALS['__l10n']['Update backup files'] = 'Sauvegardes des mises à jour'; +$GLOBALS['__l10n']['The following files are backups of previously updates. You can revert your previous installation or delete theses files.'] = 'Les fichiers suivants sont des sauvegardes de mises à jour précédentes. Vous pouvez rétablir votre installation précédente ou supprimer ces fichiers.'; +$GLOBALS['__l10n']['Please note that reverting your Dotclear version may have some unwanted side-effects. Consider reverting only if you experience strong issues with this new version.'] = 'Merci de noter que rétablir votre version de Dotclear peut avoir des effets non désirés. N\'envisagez ceci que si vous rencontrez d\'importantes difficultés avec cette nouvelle version.'; +$GLOBALS['__l10n']['You should not revert to version prior to last one (%s).'] = 'Vous ne devez pas rétablir une version précédant la dernière (%s).'; +$GLOBALS['__l10n']['Delete selected file'] = 'Supprimer le fichier sélectionné'; +$GLOBALS['__l10n']['Revert to selected file'] = 'Rétablir le fichier sélectionné'; +$GLOBALS['__l10n']['Congratulations, you\'re one click away from the end of the update.'] = 'Félicitations, vous êtes à un clic de la fin de la mise à jour.'; +$GLOBALS['__l10n']['Finish the update.'] = 'Finir la mise à jour.'; +$GLOBALS['__l10n']['new user'] = 'nouvel utilisateur'; +$GLOBALS['__l10n']['User "%s" already exists.'] = 'L\'utilisateur "%s" existe déjà.'; +$GLOBALS['__l10n']['User has been successfully updated.'] = 'Utilisateur mis à jour avec succès.'; +$GLOBALS['__l10n']['User has been successfully created.'] = 'Utilisateur créé avec succès.'; +$GLOBALS['__l10n']['Warning:'] = 'Attention :'; +$GLOBALS['__l10n']['If you change your username, you will have to log in again.'] = 'Si vous changez votre login, vous devrez vous identifier à nouveau.'; +$GLOBALS['__l10n']['No permissions.'] = 'Aucune permission.'; +$GLOBALS['__l10n']['Add new permissions'] = 'Ajouter de nouvelles permissions'; +$GLOBALS['__l10n']['Username'] = 'Identifiant :'; +$GLOBALS['__l10n']['Last Name'] = 'Nom'; +$GLOBALS['__l10n']['First Name'] = 'Prénom'; +$GLOBALS['__l10n']['Display name'] = 'Pseudonyme'; +$GLOBALS['__l10n']['Number of entries'] = 'Nombre de billets'; +$GLOBALS['__l10n']['users'] = 'utilisateurs'; +$GLOBALS['__l10n']['User has been successfully removed.'] = 'Utilisateur supprimé avec succès.'; +$GLOBALS['__l10n']['Create a new user'] = 'Créer un nouvel utilisateur'; +$GLOBALS['__l10n']['Users per page'] = 'Utilisateurs par page'; +$GLOBALS['__l10n']['Selected users action:'] = 'Action sur les utilisateurs sélectionnés :'; +$GLOBALS['__l10n']['Set permissions'] = 'Définir les permissions'; +$GLOBALS['__l10n']['Blog:'] = 'Blog :'; +$GLOBALS['__l10n']['Change blog'] = 'Changer de blog'; +$GLOBALS['__l10n']['Blogs:'] = 'Blogs :'; +$GLOBALS['__l10n']['View site'] = 'Voir le site'; +$GLOBALS['__l10n']['User:'] = 'Utilisateur :'; +$GLOBALS['__l10n']['Logout'] = 'Déconnexion'; +$GLOBALS['__l10n']['Thank you for using %s.'] = 'Merci d\'utiliser %s.'; +$GLOBALS['__l10n']['Help'] = 'Aide'; +$GLOBALS['__l10n']['uncover'] = 'dévoiler'; +$GLOBALS['__l10n']['hide'] = 'cacher'; +$GLOBALS['__l10n']['help'] = 'aide'; +$GLOBALS['__l10n']['No selection'] = 'Aucune sélection'; +$GLOBALS['__l10n']['select all'] = 'tout sélectionner'; +$GLOBALS['__l10n']['invert selection'] = 'inverser la sélection'; +$GLOBALS['__l10n']['view entry'] = 'voir le billet'; +$GLOBALS['__l10n']['Are you sure you want to delete selected entries?'] = 'Êtes-vous certain de vouloir supprimer les billets sélectionnés ?'; +$GLOBALS['__l10n']['Are you sure you want to delete this entry?'] = 'Êtes-vous certain de vouloir supprimer ce billet ?'; +$GLOBALS['__l10n']['Are you sure you want to delete selected comments?'] = 'Êtes-vous certain de vouloir supprimer les commentaires sélectionnés ?'; +$GLOBALS['__l10n']['Are you sure you want to delete this comment?'] = 'Êtes-vous certain de vouloir supprimer ce commentaire ?'; +$GLOBALS['__l10n']['Users with posts cannot be deleted.'] = 'Les utilisateurs ayant écrit des billets ne peuvent être effacées.'; +$GLOBALS['__l10n']['Are you sure you want to delete selected users?'] = 'Êtes-vous certain de vouloir supprimer les utilisateurs sélectionnés ?'; +$GLOBALS['__l10n']['Are you sure you want to delete category "%s"?'] = 'Êtes-vous certain de vouloir supprimer la catégorie "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to reorder all categories?'] = 'Êtes-vous certain de vouloir réinitialiser l\'ordre des catégories ?'; +$GLOBALS['__l10n']['Are you sure you want to remove media "%s"?'] = 'Êtes-vous certain de vouloir supprimer le média "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to extract archive in current directory?'] = 'Êtes-vous certain de vouloir extraire l\'archive dans le répertoire actuel ?'; +$GLOBALS['__l10n']['Are you sure you want to remove attachment "%s"?'] = 'Êtes-vous certain de vouloir supprimer la pièce jointe "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to delete "%s" language?'] = 'Êtes-vous certain de vouloir supprimer la langue "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to delete "%s" plugin?'] = 'Êtes-vous certain de vouloir supprimer l\'extension "%s" ?'; +$GLOBALS['__l10n']['Use this theme'] = 'Utiliser ce thème'; +$GLOBALS['__l10n']['Remove this theme'] = 'Supprimer ce thème'; +$GLOBALS['__l10n']['Are you sure you want to delete "%s" theme?'] = 'Êtes-vous certain de vouloir supprimer le thème "%s" ?'; +$GLOBALS['__l10n']['Zip file content'] = 'Contenu du fichier zip'; +$GLOBALS['__l10n']['XHTML markup validator'] = 'Validation XHTML'; +$GLOBALS['__l10n']['XHTML content is valid.'] = 'Le contenu XHTML est valide.'; +$GLOBALS['__l10n']['There are XHTML markup errors.'] = 'Il y a des erreurs XHTML.'; +$GLOBALS['__l10n']['You have unsaved changes.'] = 'Vous n\'avez pas enregistré vos modifications.'; +$GLOBALS['__l10n']['close'] = 'fermer'; +$GLOBALS['__l10n']['now'] = 'maintenant'; +$GLOBALS['__l10n']['visual'] = 'visuel'; +$GLOBALS['__l10n']['source'] = 'source'; +$GLOBALS['__l10n']['You can use the following shortcuts to format your text.'] = 'Vous pouvez utiliser les raccourcis suivants pour formater votre texte.'; +$GLOBALS['__l10n']['-- none --'] = '-- aucun --'; +$GLOBALS['__l10n']['-- block format --'] = '-- format bloc --'; +$GLOBALS['__l10n']['Paragraph'] = 'Paragraphe'; +$GLOBALS['__l10n']['Level 1 header'] = 'Titre de niveau 1'; +$GLOBALS['__l10n']['Level 2 header'] = 'Titre de niveau 2'; +$GLOBALS['__l10n']['Level 3 header'] = 'Titre de niveau 3'; +$GLOBALS['__l10n']['Level 4 header'] = 'Titre de niveau 4'; +$GLOBALS['__l10n']['Level 5 header'] = 'Titre de niveau 5'; +$GLOBALS['__l10n']['Level 6 header'] = 'Titre de niveau 6'; +$GLOBALS['__l10n']['Strong emphasis'] = 'Forte emphase'; +$GLOBALS['__l10n']['Emphasis'] = 'Emphase'; +$GLOBALS['__l10n']['Inserted'] = 'Insertion'; +$GLOBALS['__l10n']['Deleted'] = 'Suppression'; +$GLOBALS['__l10n']['Inline quote'] = 'Citation en ligne'; +$GLOBALS['__l10n']['Code'] = 'Code'; +$GLOBALS['__l10n']['Line break'] = 'Passage à la ligne'; +$GLOBALS['__l10n']['Blockquote'] = 'Bloc de citation'; +$GLOBALS['__l10n']['Preformated text'] = 'Texte préformaté'; +$GLOBALS['__l10n']['Unordered list'] = 'Liste à puces'; +$GLOBALS['__l10n']['Ordered list'] = 'Liste numérotée'; +$GLOBALS['__l10n']['Link'] = 'Lien'; +$GLOBALS['__l10n']['URL?'] = 'URL ?'; +$GLOBALS['__l10n']['Language?'] = 'Langue ?'; +$GLOBALS['__l10n']['External image'] = 'Image externe'; +$GLOBALS['__l10n']['Media chooser'] = 'Sélecteur de média'; +$GLOBALS['__l10n']['Link to an entry'] = 'Lien vers un billet'; +$GLOBALS['__l10n']['Activate enhanced uploader'] = 'Activer l\'interface avancée'; +$GLOBALS['__l10n']['Disable enhanced uploader'] = 'Désactiver l\'interface avancée'; +$GLOBALS['__l10n']['File successfully uploaded.'] = 'Fichier envoyé avec succès.'; +$GLOBALS['__l10n']['Maximum file size allowed:'] = 'Taille maximale de fichier autorisée :'; +$GLOBALS['__l10n']['Limit exceeded.'] = 'Limite dépassée.'; +$GLOBALS['__l10n']['File size exceeds allowed limit.'] = 'La taille du fichier dépasse la limite autorisée.'; +$GLOBALS['__l10n']['Canceled.'] = 'Annulé.'; +$GLOBALS['__l10n']['HTTP Error:'] = 'Erreur HTTP :'; +$GLOBALS['__l10n']['Choose file'] = 'Choisir un fichier'; +$GLOBALS['__l10n']['Choose files'] = 'Choisir des fichiers'; +$GLOBALS['__l10n']['Clean'] = 'Nettoyer'; +$GLOBALS['__l10n']['Upload'] = 'Envoyer'; +$GLOBALS['__l10n']['No file in queue.'] = 'Aucun fichier en file d\'attente.'; +$GLOBALS['__l10n']['1 file in queue.'] = '1 fichier en attente.'; +$GLOBALS['__l10n']['%d files in queue.'] = '%d fichiers en attente.'; +$GLOBALS['__l10n']['Queue error:'] = 'Erreur de file d\'attente :'; +$GLOBALS['__l10n']['«prev.'] = '«préc.'; +$GLOBALS['__l10n']['next»'] = 'suiv.»'; +$GLOBALS['__l10n']['No entry'] = 'Pas de billet'; +$GLOBALS['__l10n']['scheduled'] = 'programmé'; +$GLOBALS['__l10n']['protected'] = 'protégé'; +$GLOBALS['__l10n']['%d attachment'] = '%d annexe'; +$GLOBALS['__l10n']['%d attachments'] = '%d annexes'; +$GLOBALS['__l10n']['Type'] = 'Type'; +$GLOBALS['__l10n']['No user'] = 'Pas d\'utilisateur'; +$GLOBALS['__l10n']['System'] = 'Système'; +$GLOBALS['__l10n']['Blog'] = 'Blog'; +$GLOBALS['__l10n']['Updates'] = 'Mises à jour'; +$GLOBALS['__l10n']['Languages'] = 'Langues'; +$GLOBALS['__l10n']['Unable to open directory.'] = 'Impossible d\'ouvrir le répertoire.'; +$GLOBALS['__l10n']['Unable to create directory.'] = 'Impossible de créer le répertoire.'; +$GLOBALS['__l10n']['File is not writable.'] = 'Le fichier n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['Unable to open file.'] = 'Impossible d\'ouvrir le fichier.'; +$GLOBALS['__l10n']['Not an uploaded file.'] = 'Pas un fichier déposé.'; +$GLOBALS['__l10n']['The uploaded file exceeds the maximum file size allowed.'] = 'Le fichier déposé est plus grand que la taille maximale autorisée.'; +$GLOBALS['__l10n']['The uploaded file was only partially uploaded.'] = 'Le fichier n\'a été chargé qu\'en partie.'; +$GLOBALS['__l10n']['No file was uploaded.'] = 'Aucun fichier chargé.'; +$GLOBALS['__l10n']['Missing a temporary folder.'] = 'Il manque un répertoire temporaire.'; +$GLOBALS['__l10n']['Failed to write file to disk.'] = 'Impossible d\'écrire le fichier.'; +$GLOBALS['__l10n']['%s is not a directory.'] = '%s n\'est pas un répertoire.'; +$GLOBALS['__l10n']['Uploading this file is not allowed.'] = 'L\'envoi de ce fichier n\'est pas autorisé.'; +$GLOBALS['__l10n']['Destination directory is not in jail.'] = 'Le répertoire cible n\'est pas en jail.'; +$GLOBALS['__l10n']['File already exists.'] = 'Le nouveau fichier existe déjà.'; +$GLOBALS['__l10n']['Cannot write in this directory.'] = 'Impossible d\'écrire dans ce répertoire.'; +$GLOBALS['__l10n']['An error occurred while writing the file.'] = 'Une erreur est survenue pendant l\'écriture du fichier.'; +$GLOBALS['__l10n']['Source file does not exist.'] = 'Le fichier source n\'existe pas'; +$GLOBALS['__l10n']['File is not in jail.'] = 'Le fichier n\'est pas en jail.'; +$GLOBALS['__l10n']['Destination directory is not writable.'] = 'Le répertoire cible n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['Unable to rename file.'] = 'Impossible de renommer le fichier.'; +$GLOBALS['__l10n']['File cannot be removed.'] = 'Ce fichier ne peut pas être supprimé.'; +$GLOBALS['__l10n']['Directory is not in jail.'] = 'Le répertoire n\'est pas en jail.'; +$GLOBALS['__l10n']['Directory cannot be removed.'] = 'Ce répertoire ne peut pas être supprimé.'; +$GLOBALS['__l10n']['Not enough memory to open image.'] = 'Mémoire insuffisante pour ouvrir l\'image.'; +$GLOBALS['__l10n']['File %s is not compressed in the zip.'] = 'Le fichier %s n\'est pas compressé dans le zip.'; +$GLOBALS['__l10n']['Trying to unzip a folder name %s'] = 'Tentative de décompresser un répertoire %s'; +$GLOBALS['__l10n']['Unable to write destination file.'] = 'Impossible d\'écrire le fichier de destination'; +$GLOBALS['__l10n']['Unable to write in target directory, permission denied.'] = 'Impossible d\'écrire dans le répertoire cible, permission refusée.'; +$GLOBALS['__l10n']['Not enough memory to open file.'] = 'Mémoire insuffisante pour ouvrir le fichier.'; +$GLOBALS['__l10n']['File does not exist'] = 'Le fichier n\'existe pas'; +$GLOBALS['__l10n']['Cannot read file'] = 'Impossible de lire le fichier'; +$GLOBALS['__l10n']['Directory does not exist'] = 'Le répertoire n\'existe pas'; +$GLOBALS['__l10n']['Cannot read directory'] = 'Impossible de lire le répertoire'; +$GLOBALS['__l10n']['administrator'] = 'administrateur'; +$GLOBALS['__l10n']['manage their own entries and comments'] = 'gérer ses propres billets et commentaires'; +$GLOBALS['__l10n']['publish entries and comments'] = 'publier des billets et des commentaires'; +$GLOBALS['__l10n']['delete entries and comments'] = 'supprimer des billets et des commentaires'; +$GLOBALS['__l10n']['manage all entries and comments'] = 'gérer tous les billets et commentaires'; +$GLOBALS['__l10n']['manage categories'] = 'gérer les catégories'; +$GLOBALS['__l10n']['manage their own media items'] = 'gérer ses propres médias'; +$GLOBALS['__l10n']['manage all media items'] = 'gérer tous les médias'; +$GLOBALS['__l10n']['That user does not exist in the database.'] = 'Cet utilisateur n\'existe pas dans la base de données.'; +$GLOBALS['__l10n']['That key does not exist in the database.'] = 'Cette clé n\'existe pas dans la base de données.'; +$GLOBALS['__l10n']['You are not allowed to add categories'] = 'Vous n\'êtes pas autorisé à créer des catégories'; +$GLOBALS['__l10n']['You are not allowed to update categories'] = 'Vous n\'êtes pas autorisé à modifier des catégories'; +$GLOBALS['__l10n']['You are not allowed to delete categories'] = 'Vous n\'êtes pas autorisé à supprimer des catégories'; +$GLOBALS['__l10n']['This category is not empty.'] = 'Cette catégorie n\'est pas vide.'; +$GLOBALS['__l10n']['Category URL must be unique.'] = 'L\'URL de chaque catégorie doit être unique.'; +$GLOBALS['__l10n']['You must provide a category title'] = 'Vous devez indiquer un titre de catégorie'; +$GLOBALS['__l10n']['You must provide a category URL'] = 'Vous devez indiquer une URL de catégorie'; +$GLOBALS['__l10n']['You are not allowed to create an entry'] = 'Vous n\'êtes pas autorisé à créer des billets'; +$GLOBALS['__l10n']['You are not allowed to update entries'] = 'Vous n\'êtes pas autorisé à modifier les billets'; +$GLOBALS['__l10n']['No such entry ID'] = 'Identifiant du billet inconnu'; +$GLOBALS['__l10n']['You are not allowed to edit this entry'] = 'Vous n\'êtes pas autorisé à modifier ce billet'; +$GLOBALS['__l10n']['You are not allowed to change this entry status'] = 'Vous n\'êtes pas autorisé à modifier l\'état de ce billet'; +$GLOBALS['__l10n']['You are not allowed to change this entry category'] = 'Vous n\'êtes pas autorisé à modifier la catégorie de ce billet'; +$GLOBALS['__l10n']['You are not allowed to mark this entry as selected'] = 'Vous n\'êtes pas autorisé à marquer ce billet comme sélectionné'; +$GLOBALS['__l10n']['You are not allowed to delete entries'] = 'Vous n\'êtes pas autorisé à supprimer des billets'; +$GLOBALS['__l10n']['You are not allowed to delete this entry'] = 'Vous n\'êtes pas autorisé à supprimer ce billet'; +$GLOBALS['__l10n']['No entry title'] = 'Pas de titre de billet'; +$GLOBALS['__l10n']['No entry content'] = 'Pas de contenu de billet'; +$GLOBALS['__l10n']['Empty entry URL'] = 'URL du billet vide'; +$GLOBALS['__l10n']['You are not allowed to update comments'] = 'Vous n\'êtes pas autorisé à modifier des commentaires'; +$GLOBALS['__l10n']['No such comment ID'] = 'Identifiant du commentaire inconnu'; +$GLOBALS['__l10n']['You are not allowed to update this comment'] = 'Vous n\'êtes pas autorisé à modifier ce commentaire'; +$GLOBALS['__l10n']['You are not allowed to change this comment\'s status'] = 'Vous n\'êtes pas autorisé à changer l\'état de ce commentaire'; +$GLOBALS['__l10n']['You are not allowed to delete comments'] = 'Vous n\'êtes pas autorisé à supprimer des commentaires'; +$GLOBALS['__l10n']['You are not allowed to delete this comment'] = 'Vous n\'êtes pas autorisé à supprimer ce commentaire'; +$GLOBALS['__l10n']['You must provide a comment'] = 'Vous devez indiquer un commentaire'; +$GLOBALS['__l10n']['You must provide an author name'] = 'Vous devez indiquer un nom d\'auteur'; +$GLOBALS['__l10n']['Email address is not valid.'] = 'Adresse email invalide.'; +$GLOBALS['__l10n']['online'] = 'en ligne'; +$GLOBALS['__l10n']['offline'] = 'hors ligne'; +$GLOBALS['__l10n']['removed'] = 'supprimé'; +$GLOBALS['__l10n']['You are not an administrator'] = 'Vous n\'êtes pas administrateur'; +$GLOBALS['__l10n']['Invalid user language code'] = 'Code langue de l\'utilisateur invalide'; +$GLOBALS['__l10n']['Blog ID must contain at least 2 characters using letters, numbers or symbols.'] = 'L\'identifiant du blog doit contenir au moins 2 caractères composés de lettres, chiffres ou symboles.'; +$GLOBALS['__l10n']['No blog name'] = 'Pas de nom de blog'; +$GLOBALS['__l10n']['No blog URL'] = 'Pas d\'URL de blog'; +$GLOBALS['__l10n']['No blog defined.'] = 'Aucun blog défini.'; +$GLOBALS['__l10n']['Directory %s does not exist.'] = 'Le répertoire %s n\'existe pas.'; +$GLOBALS['__l10n']['You are not a super administrator.'] = 'Vous n\'êtes pas super administrateur.'; +$GLOBALS['__l10n']['Permission denied.'] = 'Permission refusée.'; +$GLOBALS['__l10n']['You are not the file owner.'] = 'Vous n\'êtes pas le propriétaire de ce fichier.'; +$GLOBALS['__l10n']['New file already exists.'] = 'Le nouveau fichier existe déjà.'; +$GLOBALS['__l10n']['File does not exist in the database.'] = 'Ce fichier n\'existe pas dans la base de données.'; +$GLOBALS['__l10n']['Extract destination directory %s already exists.'] = 'Le répertoire de destination d\'extraction %s existe déjà.'; +$GLOBALS['__l10n']['Empty module zip file.'] = 'Fichier zip de module vide.'; +$GLOBALS['__l10n']['The zip file does not appear to be a valid Dotclear module.'] = 'Le fichier zip ne semble pas être un fichier valide de module Dotclear.'; +$GLOBALS['__l10n']['An error occurred during module deletion.'] = 'Une erreur est survenue durant la suppression du module.'; +$GLOBALS['__l10n']['Unable to upgrade "%s". (same version)'] = 'Impossible de mettre à jour "%s" (même version).'; +$GLOBALS['__l10n']['Unable to read new _define.php file'] = 'Impossible de lire le nouveau fichier _define.php.'; +$GLOBALS['__l10n']['No such module.'] = 'Module inexistant.'; +$GLOBALS['__l10n']['Cannot remove module files'] = 'Impossible de supprimer les fichiers du module'; +$GLOBALS['__l10n']['Cannot deactivate plugin.'] = 'L\'extension ne peut pas être désactivée.'; +$GLOBALS['__l10n']['Cannot activate plugin.'] = 'L\'extension ne peut pas être activée.'; +$GLOBALS['__l10n']['Unable to retrieve settings:'] = 'Impossible d\'obtenir les paramètres :'; +$GLOBALS['__l10n']['Invalid setting namespace: %s'] = 'Espace de nommage du paramètre invalide : %s'; +$GLOBALS['__l10n']['No namespace specified'] = 'Aucun espace de nommage spécifié'; +$GLOBALS['__l10n']['%s is not a valid setting id'] = '%s n\'est pas un identifiant de paramètre valide'; +$GLOBALS['__l10n']['%s has still been pinged'] = 'Un rétrolien vers %s a déjà été fait'; +$GLOBALS['__l10n']['Unable to ping URL'] = 'Impossible de réaliser le rétrolien'; +$GLOBALS['__l10n']['%s is not a ping URL'] = '%s n\'est pas une URL de rétrolien'; +$GLOBALS['__l10n']['%s, ping error:'] = '%s, erreur de rétrolien :'; +$GLOBALS['__l10n']['Digests file not found.'] = 'Fichier de contrôle introuvable.'; +$GLOBALS['__l10n']['No file to download'] = 'Aucun fichier à télécharger.'; +$GLOBALS['__l10n']['Root directory is not writable.'] = 'Le répertoire principal n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['An error occurred while downloading archive.'] = 'Une erreur est survenue lors du téléchargement de l\'archive.'; +$GLOBALS['__l10n']['Archive not found.'] = 'L\'archive n\'a pas été trouvée.'; +$GLOBALS['__l10n']['Unable to read current digests file.'] = 'Impossible de lire le fichier de contrôle actuel.'; +$GLOBALS['__l10n']['Downloaded file seems not to be a valid archive.'] = 'Le fichier téléchargé ne semble pas être une archive valide.'; +$GLOBALS['__l10n']['Incomplete archive.'] = 'Archive incomplète.'; +$GLOBALS['__l10n']['Unable to read digests file.'] = 'Impossible de lire le fichier de contrôle.'; +$GLOBALS['__l10n']['Invalid digests file.'] = 'Fichier de contrôle invalide.'; +$GLOBALS['__l10n']['SQLite Database Schema cannot be upgraded.'] = 'Le schema de base de données SQLite ne peut pas être mis à jour.'; +$GLOBALS['__l10n']['Something went wrong with auto upgrade:'] = 'Une erreur est survenue durant la mise à jour automatique :'; +?> \ No newline at end of file diff --git a/locales/fr/main.po b/locales/fr/main.po new file mode 100644 index 0000000..dc99596 --- /dev/null +++ b/locales/fr/main.po @@ -0,0 +1,2485 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-09-16 20:57+0200\n" +"PO-Revision-Date: 2009-09-16 21:11+0100\n" +"Last-Translator: xave \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" + +msgid "Dotclear has been upgraded." +msgstr "Dotclear a été mis à jour." + +msgid "Password reset" +msgstr "Réinitialisation du mot de passe" + +msgid "Someone has requested to reset the password for the following site and username." +msgstr "Quelqu'un a demandé la réinitialisation du mot de passe pour le site et l'utilisateur suivants." + +msgid "Username:" +msgstr "Nom d'utilisateur :" + +msgid "To reset your password visit the following address, otherwise just ignore this email and nothing will happen." +msgstr "Pour réinitialiser votre mot de passe, rendez-vous à l'adresse suivante. Sinon ignorez simplement ce message et rien ne se passera." + +#, php-format +msgid "The e-mail was sent successfully to %s." +msgstr "Le message a été envoyé avec succès à %s." + +msgid "Your new password" +msgstr "Votre nouveau mot de passe" + +msgid "Password:" +msgstr "Mot de passe :" + +msgid "Your new password is in your mailbox." +msgstr "Votre nouveau mot de passe est dans votre boîte à lettres." + +msgid "Wrong username or password" +msgstr "Nom d'utilisateur ou mot de passe incorrect" + +msgid "Back to login screen" +msgstr "Retour à l'écran de connexion" + +msgid "Request a new password" +msgstr "Demander un nouveau mot de passe" + +msgid "Email:" +msgstr "Email :" + +msgid "recover" +msgstr "récupérer" + +msgid "Remember my ID on this computer" +msgstr "Se souvenir de mon identifiant sur cet ordinateur" + +msgid "login" +msgstr "login" + +msgid "You must accept cookies in order to use the private area." +msgstr "Vous devez accepter les cookies pour utiliser l'interface privée." + +msgid "I forgot my password" +msgstr "J'ai oublié mon mot de passe" + +msgid "New blog" +msgstr "Nouveau blog" + +msgid "Blogs" +msgstr "Blogs" + +msgid "Blog details" +msgstr "Informations du blog" + +msgid "Blog ID:" +msgstr "Identifiant du blog :" + +msgid "Required field" +msgstr "Champ obligatoire" + +msgid "At least 2 characters using letters, numbers or symbols." +msgstr "Au moins 2 caractères, composés de lettres non accentuées, chiffres ou symboles." + +msgid "Please note that changing your blog ID may require changes in your public index.php file." +msgstr "Veuillez noter que changer l'identifiant de votre blog peut nécessiter des changements dans votre fichier index.php public." + +msgid "Blog name:" +msgstr "Nom du blog :" + +msgid "Blog URL:" +msgstr "URL du blog :" + +msgid "Blog description:" +msgstr "Description du blog :" + +msgid "save" +msgstr "enregistrer" + +msgid "No such blog ID" +msgstr "Identifiant de blog inconnu" + +msgid "Password verification failed" +msgstr "La vérification du mot de passe a échoué" + +msgid "Delete a blog" +msgstr "Supprimer un blog" + +msgid "Warning" +msgstr "Attention" + +#, php-format +msgid "You are about to delete the blog %s. Every entry, comment and category will be deleted." +msgstr "Vous êtes sur le point de supprimer le blog %s. Tous ses billets, commentaires et catégories seront supprimés." + +msgid "Please give your password to confirm the blog deletion." +msgstr "Veuillez indiquer votre mot de passe pour confirmer la suppression du blog." + +msgid "Your password:" +msgstr "Votre mot de passe :" + +msgid "Delete this blog" +msgstr "Supprimer ce blog" + +msgid "No given blog id." +msgstr "Pas d'identifiant de blog." + +msgid "No such blog." +msgstr "Blog inexistant." + +msgid "year/month/day/title" +msgstr "année/mois/jour/titre" + +msgid "year/month/title" +msgstr "année/mois/titre" + +msgid "year/title" +msgstr "année/titre" + +msgid "title" +msgstr "titre" + +msgid "Title" +msgstr "Titre" + +msgid "Title, Date" +msgstr "Titre, date" + +msgid "Title, Country, Date" +msgstr "Titre, pays, date" + +msgid "Title, City, Country, Date" +msgstr "Titre, ville, pays, date" + +msgid "I would like search engines and archivers to index and archive my blog's content." +msgstr "Je souhaite que mon blog soit indexé et archivé par les moteurs de recherche et archiveurs." + +msgid "I would like search engines and archivers to index but not archive my blog's content." +msgstr "Je souhaite que mon blog soit indexé mais pas archivé par les moteurs de recherche et archiveurs." + +msgid "I would like to prevent search engines and archivers from indexing or archiving my blog's content." +msgstr "Je souhaite que mon blog ne soit ni indexé ni archivé par les moteurs de recherche et archiveurs." + +msgid "Invalid language code" +msgstr "Code langue invalide" + +msgid "Blog settings" +msgstr "Paramètres du blog" + +msgid "Blog has been successfully created." +msgstr "Blog créé avec succès." + +msgid "Blog has been successfully updated." +msgstr "Blog mis à jour avec succès." + +msgid "Parameters" +msgstr "Paramètres" + +msgid "URL scan method:" +msgstr "Méthode de lecture de l'URL" + +msgid "Blog status:" +msgstr "État du blog" + +msgid "Blog configuration" +msgstr "Configuration du blog" + +msgid "Blog editor name:" +msgstr "Nom de l'éditeur du blog :" + +msgid "Default language:" +msgstr "Langue par défaut :" + +msgid "Blog timezone:" +msgstr "Fuseau horaire du blog :" + +msgid "Copyright notice:" +msgstr "Note de copyright :" + +msgid "New post URL format:" +msgstr "Format d'URL des nouveaux billets :" + +msgid "Enable XML/RPC interface" +msgstr "Activer l'interface XML/RPC" + +msgid "more information" +msgstr "plus d'informations" + +msgid "Comments and trackbacks" +msgstr "Commentaires et rétroliens" + +msgid "Accept comments" +msgstr "Accepter les commentaires" + +msgid "Moderate comments" +msgstr "Modérer les commentaires" + +#, php-format +msgid "Leave comments open for %s days" +msgstr "Laisser les commentaires ouverts durant %s jours" + +msgid "Leave blank to disable this feature." +msgstr "Laissez vide pour annuler ce comportement." + +msgid "Wiki syntax for comments" +msgstr "Syntaxe wiki pour les commentaires" + +msgid "Accept trackbacks" +msgstr "Accepter les rétroliens" + +msgid "Moderate trackbacks" +msgstr "Modérer les rétroliens" + +#, php-format +msgid "Leave trackbacks open for %s days" +msgstr "Laisser les rétroliens ouverts durant %s jours" + +msgid "Add \"nofollow\" relation on comments and trackbacks links" +msgstr "Ajouter la relation \"nofollow\" aux liens des commentaires et rétroliens" + +msgid "Blog presentation" +msgstr "Présentation du blog" + +msgid "Date format:" +msgstr "Format des dates :" + +msgid "Time format:" +msgstr "Format des heures :" + +msgid "Display smilies on entries and comments" +msgstr "Afficher des émoticones dans les billets et commentaires" + +#, php-format +msgid "Display %s entries per page" +msgstr "Afficher %s billets par page" + +#, php-format +msgid "Display %s entries per feed" +msgstr "Afficher %s billets par flux de syndication" + +#, php-format +msgid "Display %s comments per feed" +msgstr "Afficher %s commentaires par flux de syndication" + +msgid "Truncate feeds" +msgstr "Tronquer les flux de syndication" + +msgid "Media and images" +msgstr "Médias et images" + +msgid "Generated image sizes (in pixels)" +msgstr "Tailles des images générées (en pixels)" + +msgid "Thumbnails:" +msgstr "Miniatures :" + +msgid "Small:" +msgstr "Petites :" + +msgid "Medium:" +msgstr "Moyennes :" + +msgid "Inserted image title" +msgstr "Titres des images insérées" + +msgid "This defines image tag title when you insert it in a post from the media manager. It is retrieved from the picture's metadata." +msgstr "Ceci définit le titre de la balise d'une image insérée depuis le gestionnaire de media. Les informations sont obtenues depuis les métadonnées de l'image." + +msgid "Search engines robots policy" +msgstr "Paramètres d'indexation par les moteurs de recherche" + +msgid "XML/RPC interface" +msgstr "Interface XML/RPC" + +msgid "XML/RPC interface allows you to edit your blog with an external client." +msgstr "L'interface XML/RPC vous permet de publier sur votre blog avec un client externe." + +msgid "XML/RPC interface is not active. Change settings to enable it." +msgstr "L'interface XML/RPC n'est pas active. Changez vos paramètres pour l'activer." + +msgid "XML/RPC interface is active. You should set the following parameters on your XML/RPC client:" +msgstr "L'interface XML/RPC est active. Vous êtes invités à indiquer les paramètres suivants dans votre client XML/RPC :" + +msgid "Server URL:" +msgstr "URL du serveur :" + +msgid "Blogging system:" +msgstr "Système de blog :" + +msgid "User name:" +msgstr "Nom d'utilisateur :" + +msgid "your password" +msgstr "votre mot de passe" + +msgid "Users" +msgstr "Utilisateurs" + +msgid "Users on this blog" +msgstr "Utilisateurs de ce blog" + +msgid "No users" +msgstr "Aucun utilisateur" + +msgid "change permissions" +msgstr "changer les permissions" + +msgid "Super administrator" +msgstr "Super administrateur" + +msgid "You can't remove default theme." +msgstr "Vous ne pouvez pas supprimer le thème par défaut." + +msgid "Theme does not exist." +msgstr "Ce thème n'existe pas." + +msgid "Unable to move uploaded file." +msgstr "Impossible de déplacer le fichier téléchargé." + +msgid "An error occurred while downloading the file." +msgstr "Une erreur est survenue lors du téléchargement du fichier." + +#, php-format +msgid "by %s" +msgstr "par %s" + +#, php-format +msgid "version %s" +msgstr "version %s" + +#, php-format +msgid "(built on \"%s\")" +msgstr "(basé sur \"%s\")" + +#, php-format +msgid "(requires \"%s\")" +msgstr "(nécessite \"%s\")" + +msgid "Stylesheet" +msgstr "Feuille de style" + +msgid "Theme configuration" +msgstr "Configuration du thème" + +msgid "Blog themes" +msgstr "Thèmes du blog" + +msgid "Blog aspect" +msgstr "Apparence du blog" + +msgid "Theme has been successfully changed." +msgstr "Thème changé avec succès." + +msgid "Theme has been successfully installed." +msgstr "Le thème a été installé avec succès." + +msgid "Theme has been successfully upgraded" +msgstr "Le thème a été mis à jour avec succès." + +msgid "Theme has been successfully deleted." +msgstr "Le thème a été supprimé avec succès." + +#, php-format +msgid "You can find additional themes for your blog on %s." +msgstr "Vous pouvez trouver de nouveaux thèmes pour votre blog sur %s." + +msgid "To install or upgrade a theme you generally just need to upload it in \"Install or upgrade a theme\" section." +msgstr "Tout ce que vous avez à faire pour installer ou mettre à jour un thème est généralement de le déposer dans la section \"Installer ou mettre à jour un thème\"." + +msgid "Themes" +msgstr "Thèmes" + +#, php-format +msgid "You are currently using \"%s\"" +msgstr "Vous utilisez actuellement \"%s\"" + +msgid "use selected theme" +msgstr "utiliser le thème sélectionné" + +msgid "delete selected theme" +msgstr "Supprimer le thème sélectionné" + +msgid "Install or upgrade a theme" +msgstr "Installer ou mettre à jour un thème" + +msgid "You can install themes by uploading or downloading zip files." +msgstr "Vous pouvez installer des thèmes en déposant ou téléchargeant des fichiers zip." + +msgid "Upload a zip file" +msgstr "Déposer un fichier zip" + +msgid "Theme zip file:" +msgstr "Fichier zip du thème :" + +msgid "Upload theme" +msgstr "Déposer un thème" + +msgid "Download a zip file" +msgstr "Télécharger un fichier zip" + +msgid "Theme zip file URL:" +msgstr "URL du fichier zip du thème :" + +msgid "Download theme" +msgstr "Télécharger le thème" + +msgid "To enable this function, please give write access to your themes directory." +msgstr "Pour activer cette fonction, donnez un accès en écriture à votre répertoire de thèmes." + +msgid "back" +msgstr "retour" + +msgid "Last update" +msgstr "Dernière mise à jour" + +msgid "Blog name" +msgstr "Nom du blog" + +msgid "Blog ID" +msgstr "Identifiant du blog" + +msgid "Descending" +msgstr "Décroissant" + +msgid "Ascending" +msgstr "Croissant" + +msgid "List of blogs" +msgstr "Liste des blogs" + +msgid "Blog has been successfully deleted." +msgstr "Blog supprimé avec succès." + +msgid "Create a new blog" +msgstr "Créer un nouveau blog" + +msgid "Filters" +msgstr "Filtres" + +msgid "Order by:" +msgstr "Trier par :" + +msgid "Sort:" +msgstr "Trier :" + +msgid "Search:" +msgstr "Chercher :" + +msgid "Blogs per page" +msgstr "Blogs par page" + +msgid "filter" +msgstr "filtre" + +msgid "No blog" +msgstr "Pas de blog" + +msgid "Page(s)" +msgstr "Page(s)" + +msgid "Entries" +msgstr "Billets" + +msgid "Status" +msgstr "État" + +#, php-format +msgid "Edit blog %s" +msgstr "Modifier le blog %s" + +msgid "edit" +msgstr "modifier" + +#, php-format +msgid "Switch to blog %s" +msgstr "Passer au blog %s" + +msgid "This category does not exist." +msgstr "Cette catégorie n'existe pas." + +msgid "Categories" +msgstr "Catégories" + +msgid "The category has been successfully created." +msgstr "Catégorie créée avec succès." + +msgid "The category has been successfully removed." +msgstr "Catégorie supprimée avec succès." + +msgid "Categories have been successfully reordered." +msgstr "Catégories réordonnées avec succès." + +msgid "The category has been successfully moved." +msgstr "Catégorie déplacée avec succès." + +msgid "No category yet." +msgstr "Pas encore de catégorie." + +msgid "Categories list" +msgstr "Liste des catégories" + +#, php-format +msgid "%d entries" +msgstr "%d billets" + +#, php-format +msgid "%d entry" +msgstr "%d billet" + +msgid "total:" +msgstr "total :" + +msgid "URL:" +msgstr "URL :" + +msgid "Add a new category" +msgstr "Créer une nouvelle catégorie" + +msgid "Title:" +msgstr "Titre :" + +msgid "Parent:" +msgstr "Parent :" + +msgid "Top level" +msgstr "Premier niveau" + +msgid "Save" +msgstr "Enregistrer" + +msgid "Remove a category" +msgstr "Supprimer une catégorie" + +msgid "Choose a category to remove:" +msgstr "Choisissez une catégorie à supprimer :" + +msgid "ok" +msgstr "ok" + +msgid "Reorder categories" +msgstr "Réordonner les catégories" + +msgid "This will relocate all categories on the top level" +msgstr "Ceci va déplacer toutes les catégories au premier niveau" + +msgid "Reorder" +msgstr "Réordonner" + +msgid "New category" +msgstr "Nouvelle catégorie" + +msgid "Category has been successfully updated." +msgstr "Catégorie mise à jour avec succès." + +msgid "Category information" +msgstr "Détails de la catégorie" + +msgid "Warning: If you set the URL manually, it may conflict with another category." +msgstr "Attention : si vous indiquez l'URL manuellement, celle-ci peut entrer en conflit avec une autre catégorie." + +msgid "Description:" +msgstr "Description :" + +msgid "Move this category" +msgstr "Déplacer cette catégorie" + +msgid "Category parent" +msgstr "Catégorie parente" + +msgid "Category sibling" +msgstr "Catégorie voisine" + +msgid "Move current category" +msgstr "Déplacer la catégorie" + +msgid "after" +msgstr "après" + +msgid "before" +msgstr "avant" + +msgid "Entry does not exist." +msgstr "Ce billet n'existe pas." + +msgid "No comment" +msgstr "Aucun commentaire" + +msgid "You can't edit this comment." +msgstr "Vous ne pouvez pas modifier ce commentaire." + +msgid "Edit comment" +msgstr "Modifier le commentaire" + +msgid "Comment has been successfully updated." +msgstr "Commentaire mis à jour avec succès." + +#, php-format +msgid "Your comment on my blog %s" +msgstr "Votre commentaire sur mon blog %s" + +#, php-format +msgid "" +"Hi!\n" +"\n" +"You wrote a comment on:\n" +"%s\n" +"\n" +"\n" +msgstr "" +"Bonjour,\n" +"\n" +"Vous avez déposé un commentaire sur \n" +"%s\n" +"\n" +"\n" + +msgid "Send an e-mail" +msgstr "Envoyer un email" + +#, php-format +msgid "Back to \"%s\"" +msgstr "Retour à \"%s\"" + +msgid "IP address:" +msgstr "Adresse IP :" + +msgid "Date:" +msgstr "Date :" + +msgid "Author:" +msgstr "Auteur :" + +msgid "Web site:" +msgstr "Site web :" + +msgid "Status:" +msgstr "État :" + +msgid "Comment:" +msgstr "Commentaire :" + +msgid "delete" +msgstr "supprimer" + +msgid "comment" +msgstr "commentaire" + +msgid "trackback" +msgstr "rétrolien" + +msgid "Date" +msgstr "Date" + +msgid "Entry title" +msgstr "Titre du billet" + +msgid "Author" +msgstr "Auteur" + +msgid "publish" +msgstr "publier" + +msgid "unpublish" +msgstr "hors ligne" + +msgid "mark as pending" +msgstr "en attente" + +msgid "mark as junk" +msgstr "indésirable" + +msgid "Comments" +msgstr "Commentaires" + +msgid "Type:" +msgstr "Type :" + +msgid "Comments per page" +msgstr "Commentaires par page" + +msgid "Comment author:" +msgstr "Auteur du commentaire :" + +msgid "You have one spam comments." +msgstr "Vous avez un commentaire indésirable." + +msgid "Show it." +msgstr "L'afficher" + +#, php-format +msgid "You have %s spam comments." +msgstr "Vous avez %s commentaires indésirables." + +msgid "Show them." +msgstr "Les afficher." + +msgid "Selected comments action:" +msgstr "Action sur les commentaires sélectionnés :" + +#, php-format +msgid "%d comment" +msgstr "%d commentaire" + +#, php-format +msgid "%d comments" +msgstr "%d commentaires" + +msgid "New entry" +msgstr "Nouveau billet" + +msgid "User preferences" +msgstr "Préférences utilisateur" + +msgid "Documentation" +msgstr "Documentation" + +msgid "Latest news" +msgstr "Actualités" + +msgid "Dashboard" +msgstr "Tableau de bord" + +msgid "Make this blog my default blog" +msgstr "Définir comme blog par défaut" + +msgid "This blog is offline" +msgstr "Ce blog est hors ligne" + +msgid "This blog is removed" +msgstr "Ce blog est supprimé" + +msgid "DC_ADMIN_URL is not defined, you should edit your configuration file." +msgstr "DC_ADMIN_URL n'est pas défini, vous devriez corriger votre fichier de configuration." + +msgid "Following plugins have been installed:" +msgstr "Les extensions suivantes ont été installés :" + +msgid "Following plugins have not been installed:" +msgstr "Les extensions suivantes suivants n'ont pas été installés :" + +msgid "Quick entry" +msgstr "Billet rapide" + +msgid "Content:" +msgstr "Contenu :" + +msgid "Category:" +msgstr "Catégorie :" + +msgid "save and publish" +msgstr "enregister et publier" + +#, php-format +msgid "Dotclear %s is available!" +msgstr "Dotclear %s est disponible !" + +msgid "Upgrade now" +msgstr "Mettre à jour maintenant" + +msgid "Remind me later" +msgstr "Me le rappeler plus tard" + +#, php-format +msgid "PHP version is %s (5.0 or earlier needed)." +msgstr "La version de PHP est %s (5.0 ou plus récent nécessaire.)" + +msgid "Multibyte string module (mbstring) is not available." +msgstr "Le support des chaînes multi-octets (mbstring) n'est pas disponible." + +msgid "Iconv module is not available." +msgstr "Le module iconv n'est pas disponible." + +msgid "Output control functions are not available." +msgstr "Les fonctions de bufferisation de sortie ne sont pas disponibles." + +msgid "SimpleXML module is not available." +msgstr "Le module SimpleXML n'est pas disponible." + +msgid "DOM XML module is not available." +msgstr "Le module DOM XML n'est pas disponible." + +msgid "PCRE engine does not support UTF-8 strings." +msgstr "Le moteur d'expressions rationnelles PCRE ne gère pas les chaînes UTF-8." + +msgid "SPL module is not available." +msgstr "Le module SPL n'est pas disponible." + +#, php-format +msgid "MySQL version is %s (4.1 or earlier needed)." +msgstr "La version de MySQL est %s (4.1 ou plus récent nécessaire)." + +msgid "MySQL InnoDB engine is not available." +msgstr "Le gestionnaire de stockage InnoDB de MySQL n'est pas disponible." + +#, php-format +msgid "PostgreSQL version is %s (8.0 or earlier needed)." +msgstr "La version de PostgreSQL est %s (8.0 ou plus récent nécessaire)." + +msgid "Please set a master key (DC_MASTER_KEY) in configuration file." +msgstr "Veuillez indiquer une clé de référence (DC_MASTER_KEY) dans le fichier de configuration." + +msgid "Dotclear is already installed." +msgstr "Dotclear est déjà installé." + +msgid "Dotclear cannot be installed." +msgstr "Dotclear ne peut pas être installé." + +msgid "No user ID given" +msgstr "Aucun identifiant utilisateur spécifié" + +msgid "User ID must contain at least 2 characters using letters, numbers or symbols." +msgstr "L'identifiant utilisateur doit contenir au moins 2 caractères composés de lettres, chiffres ou symboles." + +msgid "Invalid email address" +msgstr "Adresse email incorrecte" + +msgid "No password given" +msgstr "Aucun mot de passe spécifié" + +msgid "Passwords don't match" +msgstr "Les mots de passe ne correspondent pas" + +msgid "Password must contain at least 6 characters." +msgstr "Le mot de passe doit contenir au moins 6 caractères." + +msgid "My first blog" +msgstr "Mon premier blog" + +msgid "Welcome to Dotclear!" +msgstr "Bienvenue sur Dotclear !" + +msgid "This is your first entry. When you're ready to blog, log in to edit or delete it." +msgstr "Ceci est votre premier billet. Quand vous serez prêt à bloguer, connectez-vous pour le modifier ou le supprimer." + +msgid "Dotclear Team" +msgstr "L'équipe Dotclear" + +msgid "" +"

    This is a comment.

    \n" +"

    To delete it, log in and view your blog's comments. Then you might remove or edit it.

    " +msgstr "

    Ceci est un commentaire

    Pour le supprimer, connectez-vous et affichez les commentaires de votre blog. Vous pourrez alors le supprimer ou le modifier.

    " + +msgid "Dotclear installation" +msgstr "Installation de Dotclear" + +#, php-format +msgid "Cache directory %s is not writable." +msgstr "Le répertoire de cache %s n'est pas accessible en écriture." + +msgid "Errors:" +msgstr "Erreurs :" + +msgid "Configuration file has been successfully created." +msgstr "Le fichier de configuration a été créé avec succès." + +msgid "User information" +msgstr "Informations utilisateur" + +msgid "Please provide the following information needed to create the first user." +msgstr "Merci de fournir les informations suivantes pour créer le premier utilisateur." + +msgid "First Name:" +msgstr "Prénom :" + +msgid "Last Name:" +msgstr "Nom" + +msgid "Username and password" +msgstr "Identifiant ou mot de passe incorrect" + +msgid "Confirm password:" +msgstr "Confirmez le mot de passe :" + +msgid "All done!" +msgstr "Terminé !" + +msgid "Dotclear has been successfully installed. Here is some useful information you should keep." +msgstr "Dotclear a été installé avec succès. Conservez les informations suivantes précieusement." + +msgid "Your account" +msgstr "Votre compte" + +msgid "Your blog" +msgstr "Votre blog" + +msgid "Blog address:" +msgstr "Adresse du blog :" + +msgid "Administration interface:" +msgstr "Interface d'administration :" + +msgid "Manage your blog now" +msgstr "Gérez votre blog" + +#, php-format +msgid "The file %s already exists. If you need to reset any of the configuration items in this file, please delete it first or you may continue to install." +msgstr "Le fichier %s existe déjà. Pour réinitialiser votre configuration, supprimez d'abord ce fichier ou bien continuez l'installation." + +#, php-format +msgid "File %s does not exist." +msgstr "Le fichier %s n'existe pas." + +#, php-format +msgid "Cannot write %s file." +msgstr "Impossible d'écrire le fichier %s." + +msgid "Dotclear installation wizard" +msgstr "Assistant d'installation de Dotclear" + +msgid "System information" +msgstr "Informations système" + +msgid "Please provide the following information needed to create your configuration file." +msgstr "Merci de fournir les informations suivantes pour créer votre fichier de configuration." + +msgid "Database type:" +msgstr "Type de base de données :" + +msgid "Database Host Name:" +msgstr "Nom d'hôte de la base de données :" + +msgid "Database Name:" +msgstr "Nom de la base de données :" + +msgid "Database User Name:" +msgstr "Nom d'utilisateur de la base de données :" + +msgid "Database Password:" +msgstr "Mot de passe de la base de données :" + +msgid "Database Tables Prefix:" +msgstr "Préfixe des tables de la base de données :" + +msgid "No such installed language" +msgstr "Cette langue n'est pas installée" + +msgid "You can't remove English language." +msgstr "Vous ne pouvez pas supprimer la langue anglaise." + +msgid "Permissions to delete language denied." +msgstr "Permission de supprimer la langue refusée." + +msgid "Invalid language file URL." +msgstr "URL de fichier de langue invalide." + +msgid "Languages management" +msgstr "Gestion des langues" + +msgid "Language has been successfully deleted." +msgstr "La langue a été supprimée avec succès." + +msgid "Language has been successfully installed." +msgstr "La langue a été installée avec succès." + +msgid "Language has been successfully upgraded" +msgstr "La langue a été mise à jour avec succès." + +msgid "Here you can install, upgrade or remove languages for your Dotclear installation." +msgstr "Sur cette page, vous pouvez installer, mettre à jour ou supprimer des langues de votre installation de Dotclear." + +#, php-format +msgid "You can change your user language in your preferences or change your blog's main language in your blog settings." +msgstr "Vous pouvez changer votre langue d'utilisateur dans vos préférences ou changer la langue principale de votre blog dans vos paramètres de blog." + +msgid "Installed languages" +msgstr "Langues installées" + +msgid "No additional language is installed." +msgstr "Aucune langue supplémentaire n'est installée." + +msgid "Language" +msgstr "Langue" + +msgid "Action" +msgstr "Action" + +msgid "Delete" +msgstr "Supprimer" + +msgid "Install or upgrade languages" +msgstr "Installer ou mettre à jour une langue" + +#, php-format +msgid "You can install or remove a language by adding or removing the relevant directory in your %s folder." +msgstr "Vous pouvez installer ou supprimer une langue en ajoutant ou supprimant le répertoire correspondant dans votre répertoire %s." + +msgid "Available languages" +msgstr "Langues disponibles" + +#, php-format +msgid "You can download and install a additional language directly from Dotclear.net. Proposed languages are based on your version: %s." +msgstr "Vous pouvez télécharger et installer une langue supplémentaire directement depuis Dotclear.net. Les langues proposées sont basées sur votre version : %s." + +msgid "Language:" +msgstr "Langue :" + +msgid "Install language" +msgstr "Installer la langue" + +msgid "You can install languages by uploading zip files." +msgstr "Vous pouvez installer des langues en déposant des fichiers zip." + +msgid "Language zip file:" +msgstr "Fichier zip de la langue :" + +msgid "Upload language" +msgstr "Déposer la langue" + +msgid "Invalid language zip file." +msgstr "Fichier zip de langue invalide." + +msgid "The zip file does not appear to be a valid Dotclear language pack." +msgstr "Le fichier zip ne semble pas être un fichier valide de langue Dotclear." + +msgid "An error occurred during language upgrade." +msgstr "Une erreur est survenue durant la mise à jour de la langue." + +msgid "Error:" +msgstr "Erreur :" + +msgid "By names, ascendant" +msgstr "Par noms, croissants" + +msgid "By names, descendant" +msgstr "Par noms, décroissants" + +msgid "By dates, ascendant" +msgstr "Par dates, croissantes" + +msgid "By dates, descendant" +msgstr "Par dates, décroissantes" + +msgid "Media manager" +msgstr "Gestionnaire de médias" + +msgid "confirm removal" +msgstr "Confirmer la suppression" + +#, php-format +msgid "Are you sure you want to remove %s?" +msgstr "Êtes-vous certain de vouloir supprimer %s ?" + +msgid "cancel" +msgstr "annuler" + +msgid "yes" +msgstr "oui" + +msgid "Directory has been successfully created." +msgstr "Répertoire créé avec succès." + +msgid "Files have been successfully uploaded." +msgstr "Fichier chargé avec succès." + +msgid "File has been successfully removed." +msgstr "Fichier supprimé avec succès." + +msgid "Directory has been successfully removed." +msgstr "Répertoire supprimé avec succès." + +msgid "Directory has been successfully rebuilt." +msgstr "Répertoire reconstruit avec succès." + +msgid "Zip file has been successfully extracted." +msgstr "Le fichier zip a été extrait avec succès." + +#, php-format +msgid "Choose a file to attach to entry %s by clicking on %s." +msgstr "Choisissez un fichier à attacher au billet %s en cliquant sur %s." + +msgid "Attach this file to entry" +msgstr "Attacher ce fichier au billet" + +#, php-format +msgid "Choose a file to insert into entry by clicking on %s." +msgstr "Choisissez un fichier à insérer dans le billet en cliquant sur %s." + +msgid "No file." +msgstr "Aucun fichier." + +msgid "Sort files:" +msgstr "Trier les fichiers :" + +msgid "Add files" +msgstr "Ajouter des fichiers" + +msgid "Choose a file:" +msgstr "Choisissez un fichier :" + +#, php-format +msgid "Maximum size %s" +msgstr "Taille maximale %s" + +msgid "Private" +msgstr "Privé" + +msgid "send" +msgstr "envoyer" + +msgid "Please take care to publish media that you own and that are not protected by copyright." +msgstr "Veuillez prendre garde à ne publier que des médias que vous possédez ou qui ne sont pas protégés contre la copie." + +msgid "New directory" +msgstr "Nouveau répertoire" + +msgid "Directory Name:" +msgstr "Nom du répertoire :" + +msgid "Download this directory as a zip file" +msgstr "Télécharger ce répertoire dans un fichier zip" + +msgid "open" +msgstr "ouvrir" + +msgid "Insert this file into entry" +msgstr "Insérer ce fichier dans le billet" + +msgid "Not a valid file" +msgstr "Fichier invalide" + +msgid "File has been successfully updated." +msgstr "Fichier mis à jour avec succès." + +msgid "Thumbnails have been successfully updated." +msgstr "Les miniatures ont été mises à jour avec succès." + +msgid "Insert media item" +msgstr "Insérer un média" + +msgid "Image size:" +msgstr "Taille de l'image :" + +msgid "original" +msgstr "originale" + +msgid "Image alignment" +msgstr "Alignement de l'image" + +msgid "None" +msgstr "Aucun" + +msgid "Left" +msgstr "Gauche" + +msgid "Right" +msgstr "Droite" + +msgid "Center" +msgstr "Centre" + +msgid "Image insertion" +msgstr "Insertion de l'image" + +msgid "As a single image" +msgstr "En tant qu'image uniquement" + +msgid "As a link to original image" +msgstr "En tant que lien vers l'image originale" + +msgid "MP3 disposition" +msgstr "Disposition du MP3" + +msgid "Please note that you cannot insert mp3 files with visual editor." +msgstr "Merci de noter que vous ne pouvez pas insérer de fichier mp3 avec l'éditeur visuel." + +msgid "Please note that you cannot insert video files with visual editor." +msgstr "Merci de noter que vous ne pouvez pas insérer de fichier vidéo avec l'éditeur visuel." + +msgid "Video size" +msgstr "Taille de la vidéo" + +msgid "Width:" +msgstr "Largeur :" + +msgid "Height:" +msgstr "Hauteur :" + +msgid "Video disposition" +msgstr "Disposition de la vidéo" + +msgid "Media item will be inserted as a link." +msgstr "Le média sera inséré en tant que lien." + +msgid "Cancel" +msgstr "Annuler" + +msgid "Insert" +msgstr "Insérer" + +msgid "Media details" +msgstr "Détails du média" + +msgid "Available sizes:" +msgstr "Tailles disponibles :" + +msgid "File owner:" +msgstr "Propriétaire du fichier :" + +msgid "File type:" +msgstr "Type de fichier :" + +msgid "File size:" +msgstr "Taille du fichier :" + +msgid "File URL:" +msgstr "URL du fichier :" + +msgid "Show entries containing this media" +msgstr "Afficher les billets contenant ce média" + +msgid "Entries containing this media" +msgstr "Billets contenant ce média" + +msgid "No entry seems contain this media." +msgstr "Aucun billet ne semble contenir ce média." + +msgid "Image details" +msgstr "Détails de l'image" + +msgid "No detail" +msgstr "Aucun détail" + +msgid "Update thumbnails" +msgstr "Mettre à jour les miniatures" + +msgid "This will create or update thumbnails for this image." +msgstr "Ceci va créer ou mettre à jour les miniatures pour cette image." + +msgid "update thumbnails" +msgstr "mettre à jour les miniatures" + +msgid "Extract in a new directory" +msgstr "Extraire dans un nouveau répertoire" + +msgid "Extract in current directory" +msgstr "Extraire dans le répertoire actuel" + +msgid "Extract archive" +msgstr "Extraire l'archive" + +msgid "This will extract archive in a new directory that should not exists yet." +msgstr "Ceci va extraire l'archive dans un nouveau répertoire qui ne doit pas encore exister." + +msgid "This will extract archive in current directory and will overwrite existing files or directory." +msgstr "Ceci va extraire l'archive dans le répertoire actuel et va écraser les fichiers ou répertoires existants." + +msgid "Extract mode:" +msgstr "Mode d'extraction :" + +msgid "extract" +msgstr "extraire" + +msgid "Change media properties" +msgstr "Changer les propriétés du média" + +msgid "File name:" +msgstr "Nom du fichier :" + +msgid "File title:" +msgstr "Titre du fichier :" + +msgid "File date:" +msgstr "Date du fichier :" + +msgid "New directory:" +msgstr "Nouveau répertoire :" + +msgid "Change file" +msgstr "Changer le fichier" + +msgid "No blog or user given." +msgstr "Vous n'avez pas indiqué de blog ou d'utilisateur" + +msgid "permissions" +msgstr "permissions" + +msgid "Permissions" +msgstr "Permissions" + +msgid "The permissions have been successfully updated." +msgstr "Permissions mises à jour avec succès." + +#, php-format +msgid "You are about to change permissions on the following blogs for users %s." +msgstr "Vous allez changer les permissions des utilisateurs %s pour ces blogs." + +msgid "choose a blog" +msgstr "choisissez un blog" + +msgid "Choose a blog" +msgstr "Choisissez un blog" + +msgid "Entries per page" +msgstr "Billets par page" + +#, php-format +msgid "Choose one or more blogs to which you want to give permissions to users %s." +msgstr "Choisissez un ou plusieurs blogs pour lesquels les utilisateurs suivants auront des permissions : %s." + +msgid "set permissions" +msgstr "définir les permissions" + +msgid "No content found on this plugin." +msgstr "Aucun contenu pour cette extension." + +msgid "Plugin not found" +msgstr "Extension introuvable" + +msgid "The plugin you reached does not exist or does not have an admin page." +msgstr "L'extension que vous essayez d'atteindre n'existe pas ou n'a pas de page d'administration." + +msgid "No such plugin." +msgstr "Extension inexistante." + +msgid "You don't have permissions to delete this plugin." +msgstr "Vous n'avez pas les permissions pour effacer cette extension" + +msgid "You don't have permissions to deactivate this plugin." +msgstr "Vous n'avez pas les permissions pour désactiver cette extension." + +msgid "Plugins management" +msgstr "Gestion des extensions" + +msgid "Plugin has been successfully deleted." +msgstr "L'extension a été supprimée avec succès." + +msgid "Plugin has been successfully installed." +msgstr "L'extension a été installée avec succès." + +msgid "Plugin has been successfully upgraded" +msgstr "L'extension a été mise à jour avec succès." + +msgid "Plugins add new functionalities to Dotclear. Here you can activate or deactivate installed plugins." +msgstr "Les extensions ajoutent de nouvelles fonctionnalités à Dotclear. Ici, vous pouvez activer ou désactiver les extensions installées." + +#, php-format +msgid "You can find additional plugins for your blog on %s." +msgstr "Vous pouvez trouver de nouvelles extensions pour votre blog sur %s." + +msgid "To install or upgrade a plugin you generally just need to upload it in \"Install or upgrade a plugin\" section." +msgstr "Tout ce que vous avez à faire pour installer ou mettre à jour une extension est généralement de la déposer dans la section \"Installer ou mettre à jour une extension\"." + +msgid "To install or upgrade a plugin you just need to extract it in your plugins directory." +msgstr "Tout ce que vous avez à faire pour installer une extension est de l'extraire dans votre répertoire d'extensions." + +msgid "Plugins" +msgstr "Extensions" + +msgid "Activated plugins" +msgstr "Extensions activées" + +msgid "Plugin" +msgstr "Extension" + +msgid "Version" +msgstr "Version" + +msgid "Details" +msgstr "Détails" + +msgid "Deactivate" +msgstr "Désactiver" + +msgid "Deactivated plugins" +msgstr "Extensions désactivées" + +msgid "Activate" +msgstr "Activer" + +msgid "Install or upgrade a plugin" +msgstr "Installer ou mettre à jour une extension" + +msgid "You can install plugins by uploading or downloading zip files." +msgstr "Vous pouvez installer des extensions en déposant ou téléchargeant des fichiers zip." + +msgid "Plugin zip file:" +msgstr "Fichier zip de l'extension :" + +msgid "Upload plugin" +msgstr "Déposer l'extension" + +msgid "Plugin zip file URL:" +msgstr "URL du fichier zip de l'extension :" + +msgid "Download plugin" +msgstr "Télécharger l'extension" + +msgid "To enable this function, please give write access to your plugins directory." +msgstr "Pour activer cette fonction, donnez un accès en écriture à votre répertoire d'extensions." + +msgid "Add a link" +msgstr "Ajouter un lien" + +msgid "Available" +msgstr "Disponible" + +msgid "Most used" +msgstr "Plus utilisées" + +msgid "Link URL:" +msgstr "URL du lien :" + +msgid "Link language:" +msgstr "Langue du lien :" + +msgid "insert" +msgstr "insérer" + +msgid "Add a link to an entry" +msgstr "Ajouter un lien vers un billet" + +msgid "Search entry:" +msgstr "Rechercher un billet :" + +msgid "This entry does not exist." +msgstr "Ce billet n'existe pas." + +msgid "Edit entry" +msgstr "Modifier le billet" + +msgid "next entry" +msgstr "billet suivant" + +msgid "previous entry" +msgstr "billet précédent" + +msgid "Entry has been successfully updated." +msgstr "Billet mis à jour avec succès." + +msgid "Entry has been successfully created." +msgstr "Billet créé avec succès." + +msgid "File has been successfully attached." +msgstr "Fichier attaché avec succès." + +msgid "Attachment has been successfully removed." +msgstr "Pièce jointe retirée avec succès." + +msgid "Comment has been successfully created." +msgstr "Commentaire créé avec succès." + +msgid "Don't forget to validate your XHTML conversion by saving your post." +msgstr "Enregistrez votre billet pour valider la transformation en XHTML." + +msgid "View entry" +msgstr "Voir le billet" + +msgid "Preview entry" +msgstr "Prévisualiser le billet" + +msgid "Entry status:" +msgstr "État du billet :" + +msgid "Published on:" +msgstr "Publié le :" + +msgid "Text formating:" +msgstr "Format du texte :" + +msgid "Convert to XHTML" +msgstr "Convertir en XHTML" + +msgid "Selected entry" +msgstr "Billet sélectionné" + +msgid "Entry lang:" +msgstr "Langue du billet :" + +msgid "Entry password:" +msgstr "Mot de passe du billet :" + +msgid "Basename:" +msgstr "URL spécifique :" + +msgid "Warning: If you set the URL manually, it may conflict with another entry." +msgstr "Attention : si vous indiquez l'URL manuellement, celle-ci peut entrer en conflit avec un autre billet." + +msgid "Attachments" +msgstr "Pièces jointes" + +msgid "remove" +msgstr "supprimer" + +msgid "No attachment." +msgstr "Aucune pièce jointe" + +msgid "Add files to this entry" +msgstr "Ajouter un fichier au billet" + +msgid "Excerpt:" +msgstr "Extrait :" + +msgid "Notes:" +msgstr "Notes :" + +msgid "Ping blogs" +msgstr "Faire des rétroliens" + +msgid "Trackbacks" +msgstr "Rétroliens" + +msgid "No trackback" +msgstr "Aucun rétrolien" + +msgid "Add a comment" +msgstr "Ajouter un commentaire" + +msgid "Name:" +msgstr "Nom :" + +msgid "IP address" +msgstr "Adresse IP" + +msgid "published" +msgstr "publié" + +msgid "unpublished" +msgstr "non publié" + +msgid "pending" +msgstr "en attente" + +msgid "junk" +msgstr "indésirable" + +msgid "Edit this comment" +msgstr "Modifier ce commentaire" + +msgid "This attachment does not exist" +msgstr "Cette pièce jointe n'existe pas" + +msgid "Remove attachment" +msgstr "Supprimer la pièce jointe" + +msgid "Attachment" +msgstr "Pièce jointe" + +msgid "Are you sure you want to remove this attachment?" +msgstr "Êtes-vous certain de vouloir supprimer cette pièce jointe ?" + +msgid "selected" +msgstr "sélectionné" + +msgid "not selected" +msgstr "non sélectionné" + +msgid "Category" +msgstr "Catégorie" + +msgid "Selected" +msgstr "Sélectionné" + +msgid "schedule" +msgstr "programmer" + +msgid "mark as selected" +msgstr "marquer sélectionné" + +msgid "mark as unselected" +msgstr "marquer non sélectionné" + +msgid "change category" +msgstr "changer la catégorie" + +msgid "change author" +msgstr "changer l'auteur" + +msgid "Selected:" +msgstr "Sélectionné :" + +msgid "Month:" +msgstr "Mois :" + +msgid "Lang:" +msgstr "Langue :" + +msgid "Selected entries action:" +msgstr "Action sur les billets sélectionnés :" + +msgid "This user does not exist" +msgstr "Cet utilisateur n'existe pas" + +msgid "Change category for entries" +msgstr "Changer de catégorie pour les billets" + +msgid "Change author for entries" +msgstr "Changer l'auteur des billets" + +msgid "Author ID:" +msgstr "Identifiant de l'utilisateur" + +msgid "If you want to change your email or password you must provide your current password." +msgstr "Si vous voulez changer votre adresse email ou votre mot de passe, vous devez indiquer votre mot de passe." + +msgid "Personal information has been successfully updated." +msgstr "Informations personnelles mises à jour avec succès." + +msgid "Display name:" +msgstr "Pseudonyme :" + +msgid "Preferred format:" +msgstr "Format d'édition préféré :" + +msgid "Default entry status:" +msgstr "État des billets par défaut :" + +msgid "Entry edit field height:" +msgstr "Taille de la zone d'édition :" + +msgid "User language:" +msgstr "Langue de l'utilisateur :" + +msgid "User timezone:" +msgstr "Fuseau horaire de l'utilisateur :" + +msgid "Enable WYSIWYG mode" +msgstr "Activer l'éditeur visuel" + +msgid "Change your password" +msgstr "Changer votre mot de passe" + +msgid "New password:" +msgstr "Nouveau mot de passe :" + +msgid "Search" +msgstr "Rechercher" + +msgid "Search options" +msgstr "Options de recherche" + +msgid "Query:" +msgstr "Requête :" + +msgid "search entries" +msgstr "rechercher des billets" + +msgid "search comments" +msgstr "recherche des commentaires" + +#, php-format +msgid "%d entries found" +msgstr "%d billets trouvés" + +#, php-format +msgid "%d entry found" +msgstr "%d billet trouvé" + +#, php-format +msgid "%d comment found" +msgstr "%d commentaire trouvé" + +#, php-format +msgid "%d comments found" +msgstr "%d commentaires trouvés" + +msgid "This entry does not exist or is not published" +msgstr "Ce billet n'existe pas ou n'est pas publié" + +msgid "All pings sent." +msgstr "Tous les rétroliens ont été envoyés." + +msgid "Auto discover ping URLs" +msgstr "Découverte automatique des URL à rétrolier" + +msgid "URLs to ping:" +msgstr "URLs à rétrolier :" + +msgid "Send excerpt:" +msgstr "Envoyer l'extrait :" + +msgid "Previously sent pings" +msgstr "Rétroliens déjà envoyés" + +msgid "Dotclear update" +msgstr "Mise à jour de Dotclear" + +#, php-format +msgid "Unable to delete file %s" +msgstr "Impossible de supprimer le fichier %s" + +#, php-format +msgid "Downloaded Dotclear archive seems to be corrupted. Try download it again." +msgstr "L'archive téléchargée de Dotclear semble être corrompue. Essayer de la télécharger à nouveau." + +msgid "The following files of your Dotclear installation have been modified so we won't try to update your installation. Please try to update manually." +msgstr "Comme les fichiers suivants de votre installation de Dotclear ont été modifiés, votre installation ne peut être mise à jour. Merci de mettre à jour manuellement." + +#, php-format +msgid "The following files of your Dotclear installation are not readable. Please fix this or try to make a backup file named %s manually." +msgstr "Les fichiers suivants de votre installation de Dotclear ne peuvent pas être lus. Veuillez corriger ceci ou créer un fichier de backup nommé %s manuellement." + +msgid "The following files of your Dotclear installation cannot be written. Please fix this or try to update manually." +msgstr "Les fichiers suivants de votre installation de Dotclear ne peuvent pas être écrits. Veuillez corriger ceci ou mettre à jour manuellement." + +msgid "No newer Dotclear version available." +msgstr "Aucune nouvelle version de Dotclear n'est disponible." + +#, php-format +msgid "Dotclear %s is available." +msgstr "Dotclear %s est disponible." + +msgid "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." +msgstr "Pour mettre à jour votre installation de Dotclear, cliquez sur le bouton suivant. Un fichier de sauvegarde de votre installation actuelle sera créé dans votre répertoire principal." + +msgid "Update Dotclear" +msgstr "Mettre à jour Dotclear" + +msgid "Update backup files" +msgstr "Sauvegardes des mises à jour" + +msgid "The following files are backups of previously updates. You can revert your previous installation or delete theses files." +msgstr "Les fichiers suivants sont des sauvegardes de mises à jour précédentes. Vous pouvez rétablir votre installation précédente ou supprimer ces fichiers." + +msgid "Please note that reverting your Dotclear version may have some unwanted side-effects. Consider reverting only if you experience strong issues with this new version." +msgstr "Merci de noter que rétablir votre version de Dotclear peut avoir des effets non désirés. N'envisagez ceci que si vous rencontrez d'importantes difficultés avec cette nouvelle version." + +#, php-format +msgid "You should not revert to version prior to last one (%s)." +msgstr "Vous ne devez pas rétablir une version précédant la dernière (%s)." + +msgid "Delete selected file" +msgstr "Supprimer le fichier sélectionné" + +msgid "Revert to selected file" +msgstr "Rétablir le fichier sélectionné" + +msgid "Congratulations, you're one click away from the end of the update." +msgstr "Félicitations, vous êtes à un clic de la fin de la mise à jour." + +msgid "Finish the update." +msgstr "Finir la mise à jour." + +msgid "new user" +msgstr "nouvel utilisateur" + +#, php-format +msgid "User \"%s\" already exists." +msgstr "L'utilisateur \"%s\" existe déjà." + +msgid "User has been successfully updated." +msgstr "Utilisateur mis à jour avec succès." + +msgid "User has been successfully created." +msgstr "Utilisateur créé avec succès." + +msgid "Warning:" +msgstr "Attention :" + +msgid "If you change your username, you will have to log in again." +msgstr "Si vous changez votre login, vous devrez vous identifier à nouveau." + +msgid "No permissions." +msgstr "Aucune permission." + +msgid "Add new permissions" +msgstr "Ajouter de nouvelles permissions" + +msgid "Username" +msgstr "Identifiant :" + +msgid "Last Name" +msgstr "Nom" + +msgid "First Name" +msgstr "Prénom" + +msgid "Display name" +msgstr "Pseudonyme" + +msgid "Number of entries" +msgstr "Nombre de billets" + +msgid "users" +msgstr "utilisateurs" + +msgid "User has been successfully removed." +msgstr "Utilisateur supprimé avec succès." + +msgid "Create a new user" +msgstr "Créer un nouvel utilisateur" + +msgid "Users per page" +msgstr "Utilisateurs par page" + +msgid "Selected users action:" +msgstr "Action sur les utilisateurs sélectionnés :" + +msgid "Set permissions" +msgstr "Définir les permissions" + +msgid "Blog:" +msgstr "Blog :" + +msgid "Change blog" +msgstr "Changer de blog" + +msgid "Blogs:" +msgstr "Blogs :" + +msgid "View site" +msgstr "Voir le site" + +msgid "User:" +msgstr "Utilisateur :" + +msgid "Logout" +msgstr "Déconnexion" + +#, php-format +msgid "Thank you for using %s." +msgstr "Merci d'utiliser %s." + +msgid "Help" +msgstr "Aide" + +msgid "uncover" +msgstr "dévoiler" + +msgid "hide" +msgstr "cacher" + +msgid "help" +msgstr "aide" + +msgid "No selection" +msgstr "Aucune sélection" + +msgid "select all" +msgstr "tout sélectionner" + +msgid "invert selection" +msgstr "inverser la sélection" + +msgid "view entry" +msgstr "voir le billet" + +msgid "Are you sure you want to delete selected entries?" +msgstr "Êtes-vous certain de vouloir supprimer les billets sélectionnés ?" + +msgid "Are you sure you want to delete this entry?" +msgstr "Êtes-vous certain de vouloir supprimer ce billet ?" + +msgid "Are you sure you want to delete selected comments?" +msgstr "Êtes-vous certain de vouloir supprimer les commentaires sélectionnés ?" + +msgid "Are you sure you want to delete this comment?" +msgstr "Êtes-vous certain de vouloir supprimer ce commentaire ?" + +msgid "Users with posts cannot be deleted." +msgstr "Les utilisateurs ayant écrit des billets ne peuvent être effacées." + +msgid "Are you sure you want to delete selected users?" +msgstr "Êtes-vous certain de vouloir supprimer les utilisateurs sélectionnés ?" + +#, php-format +msgid "Are you sure you want to delete category \"%s\"?" +msgstr "Êtes-vous certain de vouloir supprimer la catégorie \"%s\" ?" + +msgid "Are you sure you want to reorder all categories?" +msgstr "Êtes-vous certain de vouloir réinitialiser l'ordre des catégories ?" + +#, php-format +msgid "Are you sure you want to remove media \"%s\"?" +msgstr "Êtes-vous certain de vouloir supprimer le média \"%s\" ?" + +msgid "Are you sure you want to extract archive in current directory?" +msgstr "Êtes-vous certain de vouloir extraire l'archive dans le répertoire actuel ?" + +#, php-format +msgid "Are you sure you want to remove attachment \"%s\"?" +msgstr "Êtes-vous certain de vouloir supprimer la pièce jointe \"%s\" ?" + +#, php-format +msgid "Are you sure you want to delete \"%s\" language?" +msgstr "Êtes-vous certain de vouloir supprimer la langue \"%s\" ?" + +#, php-format +msgid "Are you sure you want to delete \"%s\" plugin?" +msgstr "Êtes-vous certain de vouloir supprimer l'extension \"%s\" ?" + +msgid "Use this theme" +msgstr "Utiliser ce thème" + +msgid "Remove this theme" +msgstr "Supprimer ce thème" + +#, php-format +msgid "Are you sure you want to delete \"%s\" theme?" +msgstr "Êtes-vous certain de vouloir supprimer le thème \"%s\" ?" + +msgid "Zip file content" +msgstr "Contenu du fichier zip" + +msgid "XHTML markup validator" +msgstr "Validation XHTML" + +msgid "XHTML content is valid." +msgstr "Le contenu XHTML est valide." + +msgid "There are XHTML markup errors." +msgstr "Il y a des erreurs XHTML." + +msgid "You have unsaved changes." +msgstr "Vous n'avez pas enregistré vos modifications." + +msgid "close" +msgstr "fermer" + +msgid "now" +msgstr "maintenant" + +msgid "visual" +msgstr "visuel" + +msgid "source" +msgstr "source" + +msgid "You can use the following shortcuts to format your text." +msgstr "Vous pouvez utiliser les raccourcis suivants pour formater votre texte." + +msgid "-- none --" +msgstr "-- aucun --" + +msgid "-- block format --" +msgstr "-- format bloc --" + +msgid "Paragraph" +msgstr "Paragraphe" + +msgid "Level 1 header" +msgstr "Titre de niveau 1" + +msgid "Level 2 header" +msgstr "Titre de niveau 2" + +msgid "Level 3 header" +msgstr "Titre de niveau 3" + +msgid "Level 4 header" +msgstr "Titre de niveau 4" + +msgid "Level 5 header" +msgstr "Titre de niveau 5" + +msgid "Level 6 header" +msgstr "Titre de niveau 6" + +msgid "Strong emphasis" +msgstr "Forte emphase" + +msgid "Emphasis" +msgstr "Emphase" + +msgid "Inserted" +msgstr "Insertion" + +msgid "Deleted" +msgstr "Suppression" + +msgid "Inline quote" +msgstr "Citation en ligne" + +msgid "Code" +msgstr "Code" + +msgid "Line break" +msgstr "Passage à la ligne" + +msgid "Blockquote" +msgstr "Bloc de citation" + +msgid "Preformated text" +msgstr "Texte préformaté" + +msgid "Unordered list" +msgstr "Liste à puces" + +msgid "Ordered list" +msgstr "Liste numérotée" + +msgid "Link" +msgstr "Lien" + +msgid "URL?" +msgstr "URL ?" + +msgid "Language?" +msgstr "Langue ?" + +msgid "External image" +msgstr "Image externe" + +msgid "Media chooser" +msgstr "Sélecteur de média" + +msgid "Link to an entry" +msgstr "Lien vers un billet" + +msgid "Activate enhanced uploader" +msgstr "Activer l'interface avancée" + +msgid "Disable enhanced uploader" +msgstr "Désactiver l'interface avancée" + +msgid "File successfully uploaded." +msgstr "Fichier envoyé avec succès." + +msgid "Maximum file size allowed:" +msgstr "Taille maximale de fichier autorisée :" + +msgid "Limit exceeded." +msgstr "Limite dépassée." + +msgid "File size exceeds allowed limit." +msgstr "La taille du fichier dépasse la limite autorisée." + +msgid "Canceled." +msgstr "Annulé." + +msgid "HTTP Error:" +msgstr "Erreur HTTP :" + +msgid "Choose file" +msgstr "Choisir un fichier" + +msgid "Choose files" +msgstr "Choisir des fichiers" + +msgid "Clean" +msgstr "Nettoyer" + +msgid "Upload" +msgstr "Envoyer" + +msgid "No file in queue." +msgstr "Aucun fichier en file d'attente." + +msgid "1 file in queue." +msgstr "1 fichier en attente." + +#, php-format +msgid "%d files in queue." +msgstr "%d fichiers en attente." + +msgid "Queue error:" +msgstr "Erreur de file d'attente :" + +msgid "«prev." +msgstr "«préc." + +msgid "next»" +msgstr "suiv.»" + +msgid "No entry" +msgstr "Pas de billet" + +msgid "scheduled" +msgstr "programmé" + +msgid "protected" +msgstr "protégé" + +#, php-format +msgid "%d attachment" +msgstr "%d annexe" + +#, php-format +msgid "%d attachments" +msgstr "%d annexes" + +msgid "Type" +msgstr "Type" + +msgid "No user" +msgstr "Pas d'utilisateur" + +msgid "System" +msgstr "Système" + +msgid "Blog" +msgstr "Blog" + +msgid "Updates" +msgstr "Mises à jour" + +msgid "Languages" +msgstr "Langues" + +msgid "Unable to open directory." +msgstr "Impossible d'ouvrir le répertoire." + +msgid "Unable to create directory." +msgstr "Impossible de créer le répertoire." + +msgid "File is not writable." +msgstr "Le fichier n'est pas accessible en écriture." + +msgid "Unable to open file." +msgstr "Impossible d'ouvrir le fichier." + +# pas compris +msgid "Not an uploaded file." +msgstr "Pas un fichier déposé." + +msgid "The uploaded file exceeds the maximum file size allowed." +msgstr "Le fichier déposé est plus grand que la taille maximale autorisée." + +msgid "The uploaded file was only partially uploaded." +msgstr "Le fichier n'a été chargé qu'en partie." + +msgid "No file was uploaded." +msgstr "Aucun fichier chargé." + +msgid "Missing a temporary folder." +msgstr "Il manque un répertoire temporaire." + +msgid "Failed to write file to disk." +msgstr "Impossible d'écrire le fichier." + +#, php-format +msgid "%s is not a directory." +msgstr "%s n'est pas un répertoire." + +msgid "Uploading this file is not allowed." +msgstr "L'envoi de ce fichier n'est pas autorisé." + +# "en jail" ? c'est du geek ? +msgid "Destination directory is not in jail." +msgstr "Le répertoire cible n'est pas en jail." + +msgid "File already exists." +msgstr "Le nouveau fichier existe déjà." + +msgid "Cannot write in this directory." +msgstr "Impossible d'écrire dans ce répertoire." + +msgid "An error occurred while writing the file." +msgstr "Une erreur est survenue pendant l'écriture du fichier." + +msgid "Source file does not exist." +msgstr "Le fichier source n'existe pas" + +# geek ? +msgid "File is not in jail." +msgstr "Le fichier n'est pas en jail." + +msgid "Destination directory is not writable." +msgstr "Le répertoire cible n'est pas accessible en écriture." + +msgid "Unable to rename file." +msgstr "Impossible de renommer le fichier." + +msgid "File cannot be removed." +msgstr "Ce fichier ne peut pas être supprimé." + +# décidément ! mais que veut dire "jail" ? +msgid "Directory is not in jail." +msgstr "Le répertoire n'est pas en jail." + +msgid "Directory cannot be removed." +msgstr "Ce répertoire ne peut pas être supprimé." + +msgid "Not enough memory to open image." +msgstr "Mémoire insuffisante pour ouvrir l'image." + +#, php-format +msgid "File %s is not compressed in the zip." +msgstr "Le fichier %s n'est pas compressé dans le zip." + +#, php-format +msgid "Trying to unzip a folder name %s" +msgstr "Tentative de décompresser un répertoire %s" + +msgid "Unable to write destination file." +msgstr "Impossible d'écrire le fichier de destination" + +msgid "Unable to write in target directory, permission denied." +msgstr "Impossible d'écrire dans le répertoire cible, permission refusée." + +msgid "Not enough memory to open file." +msgstr "Mémoire insuffisante pour ouvrir le fichier." + +msgid "File does not exist" +msgstr "Le fichier n'existe pas" + +msgid "Cannot read file" +msgstr "Impossible de lire le fichier" + +msgid "Directory does not exist" +msgstr "Le répertoire n'existe pas" + +msgid "Cannot read directory" +msgstr "Impossible de lire le répertoire" + +msgid "administrator" +msgstr "administrateur" + +msgid "manage their own entries and comments" +msgstr "gérer ses propres billets et commentaires" + +msgid "publish entries and comments" +msgstr "publier des billets et des commentaires" + +msgid "delete entries and comments" +msgstr "supprimer des billets et des commentaires" + +msgid "manage all entries and comments" +msgstr "gérer tous les billets et commentaires" + +msgid "manage categories" +msgstr "gérer les catégories" + +msgid "manage their own media items" +msgstr "gérer ses propres médias" + +msgid "manage all media items" +msgstr "gérer tous les médias" + +msgid "That user does not exist in the database." +msgstr "Cet utilisateur n'existe pas dans la base de données." + +msgid "That key does not exist in the database." +msgstr "Cette clé n'existe pas dans la base de données." + +msgid "You are not allowed to add categories" +msgstr "Vous n'êtes pas autorisé à créer des catégories" + +msgid "You are not allowed to update categories" +msgstr "Vous n'êtes pas autorisé à modifier des catégories" + +msgid "You are not allowed to delete categories" +msgstr "Vous n'êtes pas autorisé à supprimer des catégories" + +msgid "This category is not empty." +msgstr "Cette catégorie n'est pas vide." + +msgid "Category URL must be unique." +msgstr "L'URL de chaque catégorie doit être unique." + +msgid "You must provide a category title" +msgstr "Vous devez indiquer un titre de catégorie" + +msgid "You must provide a category URL" +msgstr "Vous devez indiquer une URL de catégorie" + +msgid "You are not allowed to create an entry" +msgstr "Vous n'êtes pas autorisé à créer des billets" + +msgid "You are not allowed to update entries" +msgstr "Vous n'êtes pas autorisé à modifier les billets" + +msgid "No such entry ID" +msgstr "Identifiant du billet inconnu" + +msgid "You are not allowed to edit this entry" +msgstr "Vous n'êtes pas autorisé à modifier ce billet" + +msgid "You are not allowed to change this entry status" +msgstr "Vous n'êtes pas autorisé à modifier l'état de ce billet" + +msgid "You are not allowed to change this entry category" +msgstr "Vous n'êtes pas autorisé à modifier la catégorie de ce billet" + +msgid "You are not allowed to mark this entry as selected" +msgstr "Vous n'êtes pas autorisé à marquer ce billet comme sélectionné" + +msgid "You are not allowed to delete entries" +msgstr "Vous n'êtes pas autorisé à supprimer des billets" + +msgid "You are not allowed to delete this entry" +msgstr "Vous n'êtes pas autorisé à supprimer ce billet" + +msgid "No entry title" +msgstr "Pas de titre de billet" + +msgid "No entry content" +msgstr "Pas de contenu de billet" + +msgid "Empty entry URL" +msgstr "URL du billet vide" + +msgid "You are not allowed to update comments" +msgstr "Vous n'êtes pas autorisé à modifier des commentaires" + +msgid "No such comment ID" +msgstr "Identifiant du commentaire inconnu" + +msgid "You are not allowed to update this comment" +msgstr "Vous n'êtes pas autorisé à modifier ce commentaire" + +msgid "You are not allowed to change this comment's status" +msgstr "Vous n'êtes pas autorisé à changer l'état de ce commentaire" + +msgid "You are not allowed to delete comments" +msgstr "Vous n'êtes pas autorisé à supprimer des commentaires" + +msgid "You are not allowed to delete this comment" +msgstr "Vous n'êtes pas autorisé à supprimer ce commentaire" + +msgid "You must provide a comment" +msgstr "Vous devez indiquer un commentaire" + +msgid "You must provide an author name" +msgstr "Vous devez indiquer un nom d'auteur" + +msgid "Email address is not valid." +msgstr "Adresse email invalide." + +msgid "online" +msgstr "en ligne" + +msgid "offline" +msgstr "hors ligne" + +msgid "removed" +msgstr "supprimé" + +msgid "You are not an administrator" +msgstr "Vous n'êtes pas administrateur" + +msgid "Invalid user language code" +msgstr "Code langue de l'utilisateur invalide" + +msgid "Blog ID must contain at least 2 characters using letters, numbers or symbols." +msgstr "L'identifiant du blog doit contenir au moins 2 caractères composés de lettres, chiffres ou symboles." + +msgid "No blog name" +msgstr "Pas de nom de blog" + +msgid "No blog URL" +msgstr "Pas d'URL de blog" + +msgid "No blog defined." +msgstr "Aucun blog défini." + +#, php-format +msgid "Directory %s does not exist." +msgstr "Le répertoire %s n'existe pas." + +msgid "You are not a super administrator." +msgstr "Vous n'êtes pas super administrateur." + +msgid "Permission denied." +msgstr "Permission refusée." + +msgid "You are not the file owner." +msgstr "Vous n'êtes pas le propriétaire de ce fichier." + +msgid "New file already exists." +msgstr "Le nouveau fichier existe déjà." + +msgid "File does not exist in the database." +msgstr "Ce fichier n'existe pas dans la base de données." + +#, php-format +msgid "Extract destination directory %s already exists." +msgstr "Le répertoire de destination d'extraction %s existe déjà." + +msgid "Empty module zip file." +msgstr "Fichier zip de module vide." + +msgid "The zip file does not appear to be a valid Dotclear module." +msgstr "Le fichier zip ne semble pas être un fichier valide de module Dotclear." + +msgid "An error occurred during module deletion." +msgstr "Une erreur est survenue durant la suppression du module." + +#, php-format +msgid "Unable to upgrade \"%s\". (same version)" +msgstr "Impossible de mettre à jour \"%s\" (même version)." + +msgid "Unable to read new _define.php file" +msgstr "Impossible de lire le nouveau fichier _define.php." + +msgid "No such module." +msgstr "Module inexistant." + +msgid "Cannot remove module files" +msgstr "Impossible de supprimer les fichiers du module" + +msgid "Cannot deactivate plugin." +msgstr "L'extension ne peut pas être désactivée." + +msgid "Cannot activate plugin." +msgstr "L'extension ne peut pas être activée." + +msgid "Unable to retrieve settings:" +msgstr "Impossible d'obtenir les paramètres :" + +#, php-format +msgid "Invalid setting namespace: %s" +msgstr "Espace de nommage du paramètre invalide : %s" + +msgid "No namespace specified" +msgstr "Aucun espace de nommage spécifié" + +#, php-format +msgid "%s is not a valid setting id" +msgstr "%s n'est pas un identifiant de paramètre valide" + +#, php-format +msgid "%s has still been pinged" +msgstr "Un rétrolien vers %s a déjà été fait" + +msgid "Unable to ping URL" +msgstr "Impossible de réaliser le rétrolien" + +#, php-format +msgid "%s is not a ping URL" +msgstr "%s n'est pas une URL de rétrolien" + +#, php-format +msgid "%s, ping error:" +msgstr "%s, erreur de rétrolien :" + +msgid "Digests file not found." +msgstr "Fichier de contrôle introuvable." + +msgid "No file to download" +msgstr "Aucun fichier à télécharger." + +msgid "Root directory is not writable." +msgstr "Le répertoire principal n'est pas accessible en écriture." + +msgid "An error occurred while downloading archive." +msgstr "Une erreur est survenue lors du téléchargement de l'archive." + +msgid "Archive not found." +msgstr "L'archive n'a pas été trouvée." + +msgid "Unable to read current digests file." +msgstr "Impossible de lire le fichier de contrôle actuel." + +msgid "Downloaded file seems not to be a valid archive." +msgstr "Le fichier téléchargé ne semble pas être une archive valide." + +msgid "Incomplete archive." +msgstr "Archive incomplète." + +msgid "Unable to read digests file." +msgstr "Impossible de lire le fichier de contrôle." + +msgid "Invalid digests file." +msgstr "Fichier de contrôle invalide." + +msgid "SQLite Database Schema cannot be upgraded." +msgstr "Le schema de base de données SQLite ne peut pas être mis à jour." + +msgid "Something went wrong with auto upgrade:" +msgstr "Une erreur est survenue durant la mise à jour automatique :" + +#~ msgid "Login:" +#~ msgstr "Login :" +#~ msgid "Login and password" +#~ msgstr "Identifiant et mot de passe" +#~ msgid "User ID" +#~ msgstr "Identifiant utilisateur" +#~ msgid "Name" +#~ msgstr "Nom" + diff --git a/locales/fr/plugins.lang.php b/locales/fr/plugins.lang.php new file mode 100644 index 0000000..e84f570 --- /dev/null +++ b/locales/fr/plugins.lang.php @@ -0,0 +1,457 @@ +DC1 redirect plugin and activate it in your blog configuration.'] = 'Veuillez noter que Dotclear 2 a un nouveau format d\'URL. Vous pouvez éviter les liens morts en installant le plugin DC1 redirect et en l\'activant dans la configuration de votre blog.'; +$GLOBALS['__l10n']['next step'] = 'étape suivante'; +$GLOBALS['__l10n']['Dotclear tables not found'] = 'Tables Dotclear non trouvées'; +$GLOBALS['__l10n']['Feed import'] = 'Importer depuis un flux'; +$GLOBALS['__l10n']['Imports a feed as new entries.'] = 'Importe un flux comme nouveaux billets.'; +$GLOBALS['__l10n']['Cannot retrieve feed URL.'] = 'Impossible d\'atteindre l\'URL du fil.'; +$GLOBALS['__l10n']['No items in feed.'] = 'Aucun élément dans le feed.'; +$GLOBALS['__l10n']['Content successfully imported.'] = 'Contenu importé avec succès.'; +$GLOBALS['__l10n']['Import from a feed'] = 'Importer depuis un fil de nouvelles'; +$GLOBALS['__l10n']['This will import a feed (RSS or Atom) a as new content in the current blog: %s.'] = 'Ceci va importer un fil (RSS ou Atom) comme un nouveau contenu dans le blog en cours : %s.'; +$GLOBALS['__l10n']['Feed URL:'] = 'URL du fil'; +$GLOBALS['__l10n']['Send'] = 'Envoyer'; +$GLOBALS['__l10n']['Flat file import'] = 'Importer depuis un fichier texte'; +$GLOBALS['__l10n']['Imports a blog or a full Dotclear installation from flat file.'] = 'Importe un blog ou toutes les données depuis un fichier texte.'; +$GLOBALS['__l10n']['Single blog successfully imported.'] = 'Blog importé avec succès.'; +$GLOBALS['__l10n']['Are you sure you want to import a full backup file?'] = 'Êtes-vous certain de vouloir charger un fichier de sauvegarde complet ?'; +$GLOBALS['__l10n']['Import a single blog'] = 'Importer un blog'; +$GLOBALS['__l10n']['This will import a single blog backup as new content in the current blog: %s.'] = 'Ceci va charger une sauvegarde de blog comme un nouveau contenu dans le blog en cours : %s.'; +$GLOBALS['__l10n']['Upload a backup file'] = 'Charger un fichier de sauvegarde'; +$GLOBALS['__l10n']['or pick up a local file in your public directory'] = 'ou choisissez un fichier local dans votre répertoire public'; +$GLOBALS['__l10n']['Import a full backup file'] = 'Import d\'un fichier de sauvegarde complet'; +$GLOBALS['__l10n']['Warning: This will reset all the content of your database, except users.'] = 'Attention : Ceci va remettre à zéro tout le contenu, sauf les utilisateurs.'; +$GLOBALS['__l10n']['WordPress import'] = 'Importer depuis WordPress'; +$GLOBALS['__l10n']['Import a WordPress installation into your current blog.'] = 'Importe un blog WordPress dans votre blog en cours.'; +$GLOBALS['__l10n']['This will import your WordPress content as new content in the current blog: %s.'] = 'Ceci va importer votre blog WordPress comme un nouveau contenu dans le blog en cours : %s.'; +$GLOBALS['__l10n']['We first need some information about your old WordPress installation.'] = 'Nous avons d\'abord besoin de renseignements à propos de votre ancienne installation WordPress.'; +$GLOBALS['__l10n']['WordPress and Dotclear\'s handling of categories are quite different. You can assign several categories to a single post in WordPress. In the Dotclear world, we see it more like "One category, several tags." Therefore Dotclear can only import one category per post and will chose the lowest numbered one. If you want to keep a trace of every category, you can import them as tags, with an optional prefix.'] = 'WordPress et Dotclear gèrent les catégories différemment. Dans WordPress, un article peut être classé dans plusieurs catégories, alors que dans Dotclear, on voit ça plutôt comme "Une catégorie, plusieurs mots-clefs." Dotclear ne peut donc importer qu\'une seule catégorie par article et choisira dans ce cas la première créée. Si vous désirez garder une trace de chaque catégorie, vous pouvez les importer en tant que mots-clefs, avec un préfixe optionnel.'; +$GLOBALS['__l10n']['On the other hand, in WordPress, a post can not be uncategorized, and a default installation has a first category labelised "Uncategorized". If you did not change that category, you can just ignore it while importing your blog, as Dotclear allows you to actually keep your posts uncategorized.'] = 'Par contre, il est impossible dans WordPress de ne pas assigner une catégorie à un article, c\'est pourquoi lors de l\'installation est créée une première catégorie appelée "Non classée". Si vous n\'avez pas modifié cette catégorie, l\'importation peut simplement l\'ignorer. En effet, Dotclear vous permet d\'avoir des articles sans catégorie.'; +$GLOBALS['__l10n']['Ignore the first category:'] = 'Ignorer la première catégorie :'; +$GLOBALS['__l10n']['Import lowest numbered category on posts:'] = 'Importer la catégorie la plus ancienne sur les articles :'; +$GLOBALS['__l10n']['Import all categories as tags:'] = 'Importer les catégories comme des tags :'; +$GLOBALS['__l10n']['Prefix such tags with:'] = 'Préfixer ces tags avec :'; +$GLOBALS['__l10n']['Content filters'] = 'Filtres de contenu'; +$GLOBALS['__l10n']['You may want to process your post and/or comment content with the following filters.'] = 'Vous pouvez utiliser les filtres suivants sur vos articles et/ou commentaires.'; +$GLOBALS['__l10n']['Post content formatter:'] = 'Formatage des articles :'; +$GLOBALS['__l10n']['Comment content formatter:'] = 'Formatage des commentaires :'; +$GLOBALS['__l10n']['WordPress tables not found'] = 'Tables WordPress non trouvées'; +$GLOBALS['__l10n']['No file to read.'] = 'Aucun fichier lisible.'; +$GLOBALS['__l10n']['File is not a DotClear backup.'] = 'Le fichier n\'est pas une sauvegarde DotClear.'; +$GLOBALS['__l10n']['File is not a single blog export.'] = 'Le fichier n\'est pas un fichier d\'export simple.'; +$GLOBALS['__l10n']['File is not a full export.'] = 'Le fichier n\'est pas un fichier d\'export complet.'; +$GLOBALS['__l10n']['Please wait...'] = 'Veuillez patienter...'; +$GLOBALS['__l10n']['Import'] = 'Importer'; +$GLOBALS['__l10n']['Maintenance'] = 'Maintenance'; +$GLOBALS['__l10n']['Optimization successful.'] = 'Optimisation réalisée avec succès.'; +$GLOBALS['__l10n']['Comments and trackback counted.'] = 'Commentaires et rétroliens comptés.'; +$GLOBALS['__l10n']['Templates cache directory emptied.'] = 'Répertoire du cache des templates vidé.'; +$GLOBALS['__l10n']['Indexing entry %d to %d.'] = 'Index des billets %d à %d'; +$GLOBALS['__l10n']['next'] = 'suivant'; +$GLOBALS['__l10n']['Entries index done.'] = 'Index des billets complet.'; +$GLOBALS['__l10n']['Back'] = 'Retour'; +$GLOBALS['__l10n']['Indexing comment %d to %d.'] = 'Index des commentaires %d à %d'; +$GLOBALS['__l10n']['Comments index done.'] = 'Index des commentaires complet.'; +$GLOBALS['__l10n']['Optimize database room'] = 'Optimiser l\'espace de la base de donnée'; +$GLOBALS['__l10n']['Vacuum tables'] = 'Nettoyer les tables'; +$GLOBALS['__l10n']['Counters'] = 'Compteurs'; +$GLOBALS['__l10n']['Reset comments and ping counters'] = 'Réinitialiser les compteurs des commentaires et rétroliens'; +$GLOBALS['__l10n']['Search engine index'] = 'Index du moteur de recherche'; +$GLOBALS['__l10n']['This may take a very long time'] = 'Ceci peut prendre beaucoup de temps'; +$GLOBALS['__l10n']['Index all posts'] = 'Indexer tous les billets'; +$GLOBALS['__l10n']['Index all comments'] = 'Indexer tous les commentaires'; +$GLOBALS['__l10n']['Empty templates cache directory'] = 'Vider le répertoire du cache des templates'; +$GLOBALS['__l10n']['Empty directory'] = 'Vider le répertoire'; +$GLOBALS['__l10n']['Tags'] = 'Tags'; +$GLOBALS['__l10n']['Tags:'] = 'Tags :'; +$GLOBALS['__l10n']['Are you sure you want to remove this %s?'] = 'Êtes vous certain de vouloir supprimer ce %s ?'; +$GLOBALS['__l10n']['Add a %s to this entry'] = 'Ajouter un %s à ce billet'; +$GLOBALS['__l10n']['Choose from list'] = 'Choisir depuis la liste'; +$GLOBALS['__l10n']['all'] = 'tous'; +$GLOBALS['__l10n']['Tag'] = 'Tag'; +$GLOBALS['__l10n']['add tags'] = 'ajouter des tags'; +$GLOBALS['__l10n']['remove tags'] = 'supprimer des tags'; +$GLOBALS['__l10n']['Add tags to entries'] = 'Ajouter des tags aux billets'; +$GLOBALS['__l10n']['Tags to add:'] = 'Tags à ajouter :'; +$GLOBALS['__l10n']['Remove selected tags from entries'] = 'Supprimer les tags sélectionnés des billets'; +$GLOBALS['__l10n']['No tags for selected entries'] = 'Aucun tag pour les billets sélectionnés'; +$GLOBALS['__l10n']['Following tags have been found in selected entries:'] = 'Les tags suivants ont été trouvés dans les billets sélectionnés :'; +$GLOBALS['__l10n']['This tag\'s comments Atom feed'] = 'Fil Atom des commentaires de ce tag'; +$GLOBALS['__l10n']['This tag\'s entries Atom feed'] = 'Fil Atom des billets de ce tag'; +$GLOBALS['__l10n']['All tags'] = 'Tous les tags'; +$GLOBALS['__l10n']['Limit (empty means no limit):'] = 'Limite (laisser vide pour aucune limite)'; +$GLOBALS['__l10n']['Entries count'] = 'Nombre de billets'; +$GLOBALS['__l10n']['Tag name'] = 'Nom du tag'; +$GLOBALS['__l10n']['Edit tag'] = 'Modifier un tag'; +$GLOBALS['__l10n']['Tag has been successfully renamed'] = 'Tag renommé avec succès'; +$GLOBALS['__l10n']['Back to tags list'] = 'Retour à la liste des tags'; +$GLOBALS['__l10n']['Rename this tag:'] = 'Renommer ce tag :'; +$GLOBALS['__l10n']['Delete this tag'] = 'Supprimer ce tag'; +$GLOBALS['__l10n']['Tag has been successfully removed'] = 'Tag supprimé avec succès'; +$GLOBALS['__l10n']['entries'] = 'billets'; +$GLOBALS['__l10n']['No tags on this blog.'] = 'Aucun tag sur ce blog'; +$GLOBALS['__l10n']['Pages'] = 'Pages'; +$GLOBALS['__l10n']['manage pages'] = 'gérer les pages'; +$GLOBALS['__l10n']['Published on'] = 'Publié le'; +$GLOBALS['__l10n']['This page\'s comments feed'] = 'Fil des commentaires de cette page'; +$GLOBALS['__l10n']['You must provide a valid email address.'] = 'Vous devez indiquer une adresse email valide.'; +$GLOBALS['__l10n']['Page title'] = 'Titre de la page'; +$GLOBALS['__l10n']['Page position'] = 'Position de la page'; +$GLOBALS['__l10n']['Publication date'] = 'Date de publication'; +$GLOBALS['__l10n']['No page'] = 'Aucune page'; +$GLOBALS['__l10n']['Are you sure you want to delete selected pages?'] = 'Êtes-vous certain de vouloir supprimer les pages sélectionnées ?'; +$GLOBALS['__l10n']['New page'] = 'Nouvelle page'; +$GLOBALS['__l10n']['Selected pages action:'] = 'Action sur les pages sélectionnées :'; +$GLOBALS['__l10n']['This page does not exist.'] = 'Cette page n\'existe pas.'; +$GLOBALS['__l10n']['Edit page'] = 'Modifier la page'; +$GLOBALS['__l10n']['next page'] = 'page suivante'; +$GLOBALS['__l10n']['previous page'] = 'page précédente'; +$GLOBALS['__l10n']['Are you sure you want to delete this page?'] = 'Êtes-vous certain de vouloir supprimer cette pages ?'; +$GLOBALS['__l10n']['Page has been successfully updated.'] = 'La page a été mise à jour avec succès.'; +$GLOBALS['__l10n']['Page has been successfully created.'] = 'La page a été créée avec succès.'; +$GLOBALS['__l10n']['View page'] = 'Voir la page'; +$GLOBALS['__l10n']['Preview page'] = 'Prévisualiser la page'; +$GLOBALS['__l10n']['Page status:'] = 'État de la page :'; +$GLOBALS['__l10n']['Page position:'] = 'Position de la page :'; +$GLOBALS['__l10n']['Page lang:'] = 'Langue de la page :'; +$GLOBALS['__l10n']['Page password:'] = 'Mot de passe de la page :'; +$GLOBALS['__l10n']['Warning: If you set the URL manually, it may conflict with another page.'] = 'Attention : si vous indiquez l\'URL manuellement, celle-ci peut entrer en conflit avec une autre page.'; +$GLOBALS['__l10n']['Add files to this page'] = 'Ajouter un fichier à la page'; +$GLOBALS['__l10n']['Pings'] = 'Pings'; +$GLOBALS['__l10n']['Pings configuration'] = 'Configuration des pings'; +$GLOBALS['__l10n']['Settings have been successfully updated.'] = 'Paramètres enregistrés avec succès.'; +$GLOBALS['__l10n']['Activate pings extension'] = 'Activer le module de ping'; +$GLOBALS['__l10n']['Service name:'] = 'Nom du service :'; +$GLOBALS['__l10n']['Service URI:'] = 'URI du service :'; +$GLOBALS['__l10n']['error'] = 'erreur'; +$GLOBALS['__l10n']['Test ping services'] = 'Tester les services de ping'; +$GLOBALS['__l10n']['Pings:'] = 'Pings :'; +$GLOBALS['__l10n']['Theme Editor'] = 'Éditeur de thème'; +$GLOBALS['__l10n']['No file'] = 'Aucun fichier'; +$GLOBALS['__l10n']['File does not exist.'] = 'Le fichier n\'existe pas.'; +$GLOBALS['__l10n']['File %s is not readable'] = 'Le fichier %s n\'est pas lisible'; +$GLOBALS['__l10n']['Unable to write file %s. Please check your theme files and folders permissions.'] = 'Impossible d\'écrire le fichier %s. Veuillez vérifier les permissions des fichiers et répertoires de votre thème.'; +$GLOBALS['__l10n']['Saving document...'] = 'Sauvegarde du document...'; +$GLOBALS['__l10n']['Document saved'] = 'Document sauvegardé'; +$GLOBALS['__l10n']['An error occurred:'] = 'Une erreur s\'est produite :'; +$GLOBALS['__l10n']['Your current theme on this blog is "%s".'] = 'Le thème utilisé actuellement sur votre blog est "%s".'; +$GLOBALS['__l10n']['You can\'t edit default theme.'] = 'Vous ne pouvez pas modifier le thème par défaut.'; +$GLOBALS['__l10n']['Please select a file to edit.'] = 'Veuillez sélectionner un fichier à modifier.'; +$GLOBALS['__l10n']['File editor'] = 'Éditeur de fichier'; +$GLOBALS['__l10n']['Editing file %s'] = 'Modification du fichier %s'; +$GLOBALS['__l10n']['This file is not writable. Please check your theme files permissions.'] = 'Ce fichier ne peut pas être modifié. Veuillez vérifier les permissions des fichiers de votre thème.'; +$GLOBALS['__l10n']['Templates files'] = 'Fichiers template'; +$GLOBALS['__l10n']['CSS files'] = 'Fichiers CSS'; +$GLOBALS['__l10n']['JavaScript files'] = 'Fichiers JavaScript'; +$GLOBALS['__l10n']['Presentation widgets'] = 'Widgets de présentation'; +$GLOBALS['__l10n']['Search engine'] = 'Moteur de recherche'; +$GLOBALS['__l10n']['Navigation links'] = 'Liens de navigation'; +$GLOBALS['__l10n']['Selected entries'] = 'Billets sélectionnés'; +$GLOBALS['__l10n']['Best of me'] = 'À retenir'; +$GLOBALS['__l10n']['Blog languages'] = 'Langues du blog'; +$GLOBALS['__l10n']['With entries counts'] = 'Afficher le nombre de billets'; +$GLOBALS['__l10n']['Subscribe links'] = 'Liens d\'abonnement'; +$GLOBALS['__l10n']['Subscribe'] = 'S\'abonner'; +$GLOBALS['__l10n']['Feeds type:'] = 'Types de fil :'; +$GLOBALS['__l10n']['Feed reader'] = 'Lecteur de fils de nouvelles'; +$GLOBALS['__l10n']['Somewhere else'] = 'Ailleurs'; +$GLOBALS['__l10n']['Entries limit:'] = 'Nombre de billets maximum :'; +$GLOBALS['__l10n']['Text'] = 'Texte'; +$GLOBALS['__l10n']['Text:'] = 'Texte :'; +$GLOBALS['__l10n']['Last entries'] = 'Derniers billets'; +$GLOBALS['__l10n']['Uncategorized'] = 'Non catégorisé'; +$GLOBALS['__l10n']['Tag:'] = 'Tag :'; +$GLOBALS['__l10n']['Last comments'] = 'Derniers commentaires'; +$GLOBALS['__l10n']['Comments limit:'] = 'Nombre de commentaires maximum :'; +$GLOBALS['__l10n']['Home'] = 'Accueil'; +$GLOBALS['__l10n']['Archives'] = 'Archives'; +$GLOBALS['__l10n']['This blog\'s entries %s feed'] = 'Fil %s des billets de ce blog'; +$GLOBALS['__l10n']['This blog\'s comments %s feed'] = 'Fil %s des commentaires de ce blog'; +$GLOBALS['__l10n']['Entries feed'] = 'Fil des billets'; +$GLOBALS['__l10n']['Comments feed'] = 'Fil des commentaires'; +$GLOBALS['__l10n']['navigation'] = 'navigation'; +$GLOBALS['__l10n']['extra'] = 'extra'; +$GLOBALS['__l10n']['Widgets'] = 'Widgets'; +$GLOBALS['__l10n']['Are you sure you want to reset sidebars?'] = 'Êtes-vous certain de vouloir réinitialiser les bandeaux ?'; +$GLOBALS['__l10n']['Available widgets'] = 'Widgets disponibles'; +$GLOBALS['__l10n']['Append to:'] = 'Ajouter à :'; +$GLOBALS['__l10n']['add widgets to sidebars'] = 'ajouter les widgets aux bandeaux'; +$GLOBALS['__l10n']['Navigation sidebar'] = 'Bandeau de navigation'; +$GLOBALS['__l10n']['Extra sidebar'] = 'Bandeau d\'extra'; +$GLOBALS['__l10n']['update sidebars'] = 'Mettre à jour les bandeaux'; +$GLOBALS['__l10n']['reset sidebars'] = 'Réinitialiser les bandeaux'; +$GLOBALS['__l10n']['Widget templates tags'] = 'Marqueurs de template des widgets'; +$GLOBALS['__l10n']['If you are allowed to edit your theme templates, you can directly add widgets as templates tags, with their own configuration.'] = 'Si vous avez le droit de modifier les templates de votre thème, vous pouvez directement ajouter des widgets à l\'aide de marqueurs de template, avec leur propre configuration.'; +$GLOBALS['__l10n']['To add a widget in your template, you need to write code like this:'] = 'Pour ajouter un widget dans votre template, vous devez écrire un code comme ceci :'; +$GLOBALS['__l10n']['Widget ID'] = 'Identifiant du widget'; +$GLOBALS['__l10n']['Setting name'] = 'Nom du paramètre'; +$GLOBALS['__l10n']['Setting value'] = 'Valeur du paramètre'; +$GLOBALS['__l10n']['Here are the following available widgets for your blog:'] = 'Voici les widgets disponibles pour votre blog :'; +$GLOBALS['__l10n']['Widget ID:'] = 'Identifiant du widget :'; +$GLOBALS['__l10n']['No setting for this widget'] = 'Aucun paramètre pour ce widget'; +$GLOBALS['__l10n']['Setting name:'] = 'Nom du paramètre :'; +$GLOBALS['__l10n']['No widget.'] = 'Aucun widget.'; +$GLOBALS['__l10n']['order'] = 'ordre'; +$GLOBALS['__l10n']['Remove widget'] = 'Supprimer le widget'; +?> \ No newline at end of file diff --git a/locales/fr/plugins.po b/locales/fr/plugins.po new file mode 100644 index 0000000..3a65ac9 --- /dev/null +++ b/locales/fr/plugins.po @@ -0,0 +1,1358 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-09-16 20:57+0200\n" +"PO-Revision-Date: \n" +"Last-Translator: xave \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" + +msgid "no" +msgstr "non" + +msgid "Configuration successfully updated" +msgstr "Configuration mise à jour avec succès" + +msgid "Settings definition successfully updated" +msgstr "Définition des paramètres mise à jour avec succès" + +msgid "blog settings" +msgstr "paramètres du blog" + +msgid "Value" +msgstr "Valeur" + +msgid "Description" +msgstr "Description" + +msgid "global settings" +msgstr "paramètres globaux" + +msgid "Akismet spam filter" +msgstr "Filtre de spam Akismet" + +#, php-format +msgid "Filtered by %s." +msgstr "Filtré par %s" + +msgid "Akismet API key:" +msgstr "Clé de l'interface Akismet :" + +msgid "API key verified" +msgstr "Clé vérifiée" + +msgid "API key not verified" +msgstr "Clé non vérifiée" + +msgid "Get your own API key" +msgstr "Obtenez votre clé Akismet" + +msgid "Antispam" +msgstr "Antispam" + +msgid "Delete junk comments older than" +msgstr "Supprimer les commentaires indésirables après" + +msgid "days" +msgstr "jours" + +msgid "IP Blacklist / Whitelist Filter" +msgstr "Liste noire / Liste blanche IP" + +#, php-format +msgid "Filtered by %1$s with rule %2$s." +msgstr "Filtré par %1$s avec la règle %2$s." + +msgid "IP address has been successfully added." +msgstr "L'adresse IP a été ajoutée avec succès." + +msgid "IP addresses have been successfully removed." +msgstr "L'adresse IP a été supprimée avec succès." + +msgid "Blacklist" +msgstr "Liste noire" + +msgid "Whitelist" +msgstr "Liste blanche" + +msgid "Add an IP address" +msgstr "Ajouter une adresse IP" + +msgid "Global IP" +msgstr "IP globale" + +msgid "Add" +msgstr "Ajouter" + +msgid "No IP address in list." +msgstr "Aucune adresse IP dans la liste." + +msgid "IP list" +msgstr "Liste d'IP" + +msgid "Checks sender IP address against DNSBL servers" +msgstr "Vérifie l'adresse IP de l'auteur sur des serveurs DNSBL" + +#, php-format +msgid "Filtered by %1$s with server %2$s." +msgstr "Filtré par %1$s avec le serveur %2$s." + +msgid "IP Lookup servers" +msgstr "Serveurs de résolution d'IP" + +msgid "Add here a coma separated list of servers." +msgstr "Ajoutez ici une liste de serveurs, séparés par des virgules." + +msgid "Checks links in comments against surbl.org" +msgstr "Vérifie les liens dans les commentaires sur surbl.org" + +msgid "Words Blacklist" +msgstr "Liste de termes interdits" + +#, php-format +msgid "Filtered by %1$s with word %2$s." +msgstr "Filtré par %1$s avec le terme %2$s." + +msgid "Words have been successfully added." +msgstr "Les mots ont été ajouté avec succès." + +msgid "Word has been successfully added." +msgstr "Le mot a été ajouté avec succès." + +msgid "Words have been successfully removed." +msgstr "Les mots ont été supprimés avec succès." + +msgid "Add a word" +msgstr "Ajouter un mot" + +msgid "Global word" +msgstr "Mot global" + +msgid "No word in list." +msgstr "Aucun mot dans la liste." + +msgid "List" +msgstr "Liste" + +msgid "Delete selected words" +msgstr "Supprimer les mots sélectionnés" + +msgid "Create default wordlist" +msgstr "Créer une liste par défaut" + +msgid "This word exists" +msgstr "Ce mot existe" + +msgid "No description" +msgstr "Aucune description" + +#, php-format +msgid "Filtered by %1$s (%2$s)" +msgstr "Filtré par %1$s (%2$s)" + +msgid "Unknown filter." +msgstr "Filtre inconnu" + +msgid "This comment is a spam:" +msgstr "Ce commentaire est un indésirable :" + +#, php-format +msgid "(including %d spam comment)" +msgstr "(dont %d commentaire indésirable)" + +#, php-format +msgid "(including %d spam comments)" +msgstr "(dont %d commentaires indésirables)" + +msgid "Spam moderation" +msgstr "Modération des indésirables" + +msgid "Spam" +msgstr "Spam" + +msgid "Ham" +msgstr "Non spam" + +msgid "Filter does not exist." +msgstr "Le filtre n'existe pas." + +msgid "Filter has no user interface." +msgstr "Le filtre n'a pas d'interface utilisateur." + +msgid "Return to filters" +msgstr "Retour aux filtres" + +#, php-format +msgid "%s configuration" +msgstr "%s configuration" + +msgid "Information" +msgstr "Informations" + +msgid "Spam comments have been successfully deleted." +msgstr "Les commentaires indésirables ont été supprimés avec succès." + +msgid "Junk comments:" +msgstr "Commentaires indésirables :" + +msgid "Published comments:" +msgstr "Commentaires publiés :" + +msgid "Delete all spams" +msgstr "Supprimer tous les indésirables" + +#, php-format +msgid "All spam comments older than %s day(s) will be automatically deleted." +msgstr "Tous les indésirables de plus de %s jour(s) seront automatiquement supprimés." + +msgid "Available spam filters" +msgstr "Filtres disponibles" + +msgid "Filters configuration has been successfully saved." +msgstr "La configuration des filtres a été enregistrée avec succès." + +msgid "Order" +msgstr "Ordre" + +msgid "Active" +msgstr "Actif" + +msgid "Auto Del." +msgstr "Suppr. Auto" + +msgid "Filter name" +msgstr "Nom du filtre" + +msgid "Filter configuration" +msgstr "Configuration du filtre" + +msgid "Syndication" +msgstr "Syndication" + +msgid "Junk comments RSS feed" +msgstr "Fil RSS des commentaires indésirables" + +msgid "Published comments RSS feed" +msgstr "Fil RSS des commentaires publiés" + +msgid "Blogroll" +msgstr "Liens" + +msgid "manage blogroll" +msgstr "gestion de la liste de liens" + +msgid "Links" +msgstr "Liens" + +msgid "All categories" +msgstr "Toutes les catégories" + +msgid "Home page only" +msgstr "Page d'accueil uniquement" + +msgid "You must provide a link title" +msgstr "Vous devez indiquer un titre de lien" + +msgid "You must provide a link URL" +msgstr "Vous devez indiquer une URL" + +msgid "You need to provide a XBEL or OPML file." +msgstr "Vous devez fournir un fichier XBEL ou OPML." + +msgid "File is not in XML format." +msgstr "Le fichier n'est pas au format XML." + +msgid "No such link or title" +msgstr "Lien ou catégorie inexistant" + +msgid "Return to blogroll" +msgstr "Retour à la liste des liens" + +msgid "Category has been successfully updated" +msgstr "Catégorie mise à jour avec succès" + +msgid "Edit category" +msgstr "Modifier une catégorie" + +msgid "Link has been successfully updated" +msgstr "Lien mis à jour avec succès" + +msgid "Edit link" +msgstr "Modifier le lien" + +msgid "XFN" +msgstr "XFN" + +msgid "_xfn_Me" +msgstr "Moi" + +msgid "_xfn_Another link for myself" +msgstr "Autre lien m'appartenant" + +msgid "_xfn_Friendship" +msgstr "Amitié" + +msgid "_xfn_Contact" +msgstr "Contact" + +msgid "_xfn_Acquaintance" +msgstr "Connaissance" + +msgid "_xfn_Friend" +msgstr "Ami(e)" + +msgid "_xfn_Physical" +msgstr "Physique" + +msgid "_xfn_Met" +msgstr "Rencontré(e)" + +msgid "_xfn_Professional" +msgstr "Professionnel" + +msgid "_xfn_Co-worker" +msgstr "Camarade de travail" + +msgid "_xfn_Colleague" +msgstr "Collègue" + +msgid "_xfn_Geographical" +msgstr "Géographique" + +msgid "_xfn_Co-resident" +msgstr "Colocataire" + +msgid "_xfn_Neighbor" +msgstr "Voisin(e)" + +msgid "_xfn_Family" +msgstr "Famille" + +msgid "_xfn_Child" +msgstr "Enfant" + +msgid "_xfn_Parent" +msgstr "Parent" + +msgid "_xfn_Sibling" +msgstr "Frère/Soeur" + +msgid "_xfn_Spouse" +msgstr "Époux/Épouse" + +msgid "_xfn_Kin" +msgstr "Famille" + +msgid "_xfn_Romantic" +msgstr "Romantique" + +msgid "_xfn_Muse" +msgstr "Muse" + +msgid "_xfn_Crush" +msgstr "Bluette" + +msgid "_xfn_Date" +msgstr "Petit(e) ami(e)" + +msgid "_xfn_Sweetheart" +msgstr "Ami(e)" + +msgid "Nothing to import" +msgstr "Rien à importer" + +msgid "Import operation cancelled." +msgstr "Importation annulée." + +msgid "Items order has been successfully updated" +msgstr "L'ordre des éléments a été mis à jour avec succès" + +msgid "Items have been successfully removed." +msgstr "Éléments supprimés avec succès." + +msgid "Link has been successfully created." +msgstr "Lien créé avec succès." + +msgid "category has been successfully created." +msgstr "Catégorie mise à jour avec succès." + +msgid "links have been successfully imported." +msgstr "Liens importés avec succès." + +msgid "URL" +msgstr "URL" + +msgid "Lang" +msgstr "Langue" + +msgid "Save order" +msgstr "Enregistrer l'ordre" + +msgid "Delete selected links" +msgstr "Supprimer les liens sélectionnés" + +msgid "Are you sure you you want to delete selected links?" +msgstr "Êtes-vous certain de vouloir supprimer les liens sélectionnés ?" + +msgid "Add a new link" +msgstr "Ajouter un nouveau lien" + +msgid "Add a category" +msgstr "Ajouter une catégorie" + +msgid "Import links" +msgstr "Importer des liens" + +msgid "OPML or XBEL File:" +msgstr "Fichier OPML ou XBEL :" + +msgid "import" +msgstr "importer" + +msgid "Light linear gradient" +msgstr "Dégradé linéaire clair" + +msgid "Medium linear gradient" +msgstr "Dégradé linéaire moyen" + +msgid "Dark linear gradient" +msgstr "Dégradé linéaire foncé" + +msgid "Solid color" +msgstr "Couleur unie" + +msgid "Custom..." +msgstr "Personnalisé..." + +msgid "Blowup configuration" +msgstr "Configuration Blowup" + +msgid "Predefined styles" +msgstr "Styles prédéfinis" + +msgid "Apply code" +msgstr "Appliquer le code" + +msgid "As images cannot be created, you won't be able to change some background properties." +msgstr "Les images ne pouvant être créées, vous ne pourrez pas changer certaines couleurs de fond." + +msgid "Theme configuration has been successfully updated." +msgstr "La configuration du thème a été mise à jour avec succès." + +msgid "General" +msgstr "Général" + +msgid "Background color:" +msgstr "Couleur de fond :" + +msgid "Background color fill:" +msgstr "Remplissage de la couleur de fond :" + +msgid "Main text font:" +msgstr "Police du texte principal :" + +msgid "Main text font size:" +msgstr "Taille du texte principal :" + +msgid "Main text color:" +msgstr "Couleur du texte principal :" + +msgid "Text line height:" +msgstr "Hauteur des lignes :" + +msgid "Links color:" +msgstr "Couleur des liens :" + +msgid "Visited links color:" +msgstr "Couleurs des liens visités :" + +msgid "Focus links color:" +msgstr "Couleur des liens survolés :" + +msgid "Page top" +msgstr "Haut de page" + +msgid "Prelude color:" +msgstr "Couleur du prélude :" + +msgid "Hide main title" +msgstr "Cacher le titre principal" + +msgid "Main title font:" +msgstr "Police du titre principal :" + +msgid "Main title font size:" +msgstr "Taille du titre principal :" + +msgid "Main title color:" +msgstr "Couleur du titre principal :" + +msgid "Main title alignment:" +msgstr "Alignement du titre principal :" + +msgid "center" +msgstr "centre" + +msgid "left" +msgstr "gauche" + +msgid "right" +msgstr "droite" + +msgid "Main title position (x:y)" +msgstr "Position du titre principal (x:y)" + +msgid "Top image" +msgstr "Image d'en-tête" + +msgid "Choose \"Custom...\" to upload your own image." +msgstr "Choisissez \"Personnalisé...\" pour déposer votre propre image." + +msgid "Add your image:" +msgstr "Ajouter votre image :" + +#, php-format +msgid "JPEG or PNG file, 800 pixels wide, maximum size %s" +msgstr "Fichier JPEG ou PNG, 800 pixels de large et de taille maximale %s" + +msgid "Preview" +msgstr "Prévisualisation" + +msgid "Sidebar" +msgstr "Bandeau" + +msgid "Sidebar position:" +msgstr "Position du bandeau" + +msgid "Sidebar text font:" +msgstr "Police du texte du bandeau :" + +msgid "Sidebar text font size:" +msgstr "Taille du texte du bandeau :" + +msgid "Sidebar text color:" +msgstr "Couleur du texte du bandeau :" + +msgid "Sidebar titles font:" +msgstr "Police des titres du bandeau :" + +msgid "Sidebar titles font size:" +msgstr "Taille des titres du bandeau :" + +msgid "Sidebar titles color:" +msgstr "Couleur des titres du bandeau :" + +msgid "Sidebar 2nd level titles font:" +msgstr "Police des titres de niveau 2 du bandeau :" + +msgid "Sidebar 2nd level titles font size:" +msgstr "Taille des titres de niveau 2 du bandeau :" + +msgid "Sidebar 2nd level titles color:" +msgstr "Couleur des titres de niveau 2 du bandeau :" + +msgid "Sidebar lines color:" +msgstr "Couleur des lignes du bandeau :" + +msgid "Sidebar links color:" +msgstr "Couleur des liens du bandeau :" + +msgid "Sidebar visited links color:" +msgstr "Couleurs des liens vistés du bandeau :" + +msgid "Sidebar focus links color:" +msgstr "Couleurs des liens survolés du bandeau :" + +msgid "Date title font:" +msgstr "Police des titres de dates :" + +msgid "Date title font size:" +msgstr "Taille des titres de dates :" + +msgid "Date title color:" +msgstr "Couleur des titres de date :" + +msgid "Entry title font:" +msgstr "Police des titres de billet :" + +msgid "Entry title font size:" +msgstr "Taille des titres des billets :" + +msgid "Entry title color:" +msgstr "Couleur des titres des billets :" + +msgid "Comment background color:" +msgstr "Couleur du fond des commentaires :" + +msgid "Comment text color:" +msgstr "Couleur du texte des commentaires :" + +msgid "My comment background color:" +msgstr "Couleur du fond de mes commentaires :" + +msgid "My comment text color:" +msgstr "Couleur du texte de mes commentaires :" + +msgid "Footer" +msgstr "Pied de page" + +msgid "Footer font:" +msgstr "Police du pied de page :" + +msgid "Footer font size:" +msgstr "Taille du texte du pied de page :" + +msgid "Footer color:" +msgstr "Couleur du texte du pied de page :" + +msgid "Footer links color:" +msgstr "Couleur des liens du pied de page :" + +msgid "Footer background color:" +msgstr "Couleur de fond du pied de page :" + +msgid "Configuration import / export" +msgstr "Import / export de configuration" + +msgid "You can share your configuration using the following code. To apply a configuration, paste the code, click on \"Apply code\" and save." +msgstr "Vous pouvez partager votre configuration en utilisant le code suivant. Pour appliquer une configuration, copiez le code, cliquez sur \"Appliquer le code\" et enregistrez." + +msgid "default" +msgstr "par défaut" + +msgid "Unable to create images." +msgstr "Impossible de créer les images." + +msgid "Invalid file type." +msgstr "Type de fichier non valide." + +msgid "Unable to open image." +msgstr "Impossible d'ouvrir l'image." + +msgid "External media" +msgstr "Média externe" + +msgid "Unsupported service" +msgstr "Service non pris en charge" + +msgid "Invalid page URL" +msgstr "URL de la page non valide" + +msgid "External media selector" +msgstr "Sélecteur de médias externes" + +msgid "Supported media services" +msgstr "Services de média supportés" + +msgid "Please enter the URL of the page containing the video you want to include in your post." +msgstr "Entrez l'URL de la page contenant la video que vous voulez intégrer à votre billet." + +msgid "Page URL:" +msgstr "URL de la page :" + +msgid "Media alignment" +msgstr "Alignement du média" + +msgid "Media title" +msgstr "Titre du média" + +msgid "Checks trackback source for a link to the post" +msgstr "Vérifie si la source du rétrolien possède un lien vers le billet" + +msgid "Import/Export" +msgstr "Import/Export" + +msgid "Flat file export" +msgstr "Exporter un fichier texte." + +msgid "Exports a blog or a full Dotclear installation to flat file." +msgstr "Exporte un blog ou toutes les données de Dotclear dans un fichier texte." + +msgid "Export file not found." +msgstr "Fichier d'export non trouvé." + +msgid "Export a blog" +msgstr "Exporter un blog" + +#, php-format +msgid "This will create an export of your current blog: %s" +msgstr "Ceci va exporter le contenu du blog en cours : %s." + +msgid "Export" +msgstr "Exporter" + +msgid "You may also want to download your media directory as a zip file" +msgstr "Vous pouvez également télécharger votre répertoire de médias au format zip." + +msgid "Export all content" +msgstr "Exporter tout le contenu" + +msgid "Congratulation!" +msgstr "Félicitations !" + +msgid "Your blog has been successfully imported. Welcome on Dotclear 2!" +msgstr "Votre blog a été importé avec succès. Bienvenue sur Dotclear 2 !" + +msgid "Why don't you blog this now?" +msgstr "Pourquoi ne pas le bloguer maintenant ?" + +msgid "or" +msgstr "ou" + +msgid "visit your dashboard" +msgstr "vous rendre sur votre tableau de bord" + +msgid "Dotclear 1.2 import" +msgstr "Importer depuis Dotclear 1.2" + +msgid "Import a Dotclear 1.2 installation into your current blog." +msgstr "Importe un blog Dotclear 1.2 dans votre blog en cours." + +#, php-format +msgid "This will import your Dotclear 1.2 content as new content in the current blog: %s." +msgstr "Ceci va importer votre blog Dotclear 1.2 comme un nouveau contenu dans le blog en cours : %s." + +msgid "Please note that this process will empty your categories, blogroll, entries and comments on the current blog." +msgstr "Veuillez noter que cette opération va vider vos catégories, liens, billets et commentaires sur le blog en cours." + +msgid "Depending on the size of your blog, it could take a few minutes." +msgstr "Selon la taille de votre blog, ceci peut prendre quelques minutes." + +msgid "General information" +msgstr "Informations générales" + +msgid "Import my blog now" +msgstr "Importer mon blog" + +msgid "We first need some information about your old Dotclear 1.2 installation." +msgstr "Nous avons d'abord besoin de renseignements à propos de votre ancienne installation Dotclear 1.2." + +msgid "Entries import options" +msgstr "Options d'importation des billets" + +msgid "Number of entries to import at once:" +msgstr "Nombre de billets à importer à chaque étape :" + +msgid "Importing users" +msgstr "Importe les utilisateurs" + +msgid "Importing categories" +msgstr "Importe les catégories" + +msgid "Importing blogroll" +msgstr "Importe les liens" + +#, php-format +msgid "Importing entries from %d to %d / %d" +msgstr "Importation des billets %d à %d" + +msgid "Please read carefully" +msgstr "Merci de lire attentivement" + +msgid "Every newly imported user has received a random password and will need to ask for a new one by following the \"I forgot my password\" link on the login page (Their registered email address has to be valid.)" +msgstr "Chaque utilisateur nouvellement importé a reçu un mot de passe aléatoire et devra en demander un nouveau en suivant le lien \"J'ai oublié mon mot de passe\" sur la page de connexion à l'interface d'aministration." + +#, php-format +msgid "Please note that Dotclear 2 has a new URL layout. You can avoid broken links by installing DC1 redirect plugin and activate it in your blog configuration." +msgstr "Veuillez noter que Dotclear 2 a un nouveau format d'URL. Vous pouvez éviter les liens morts en installant le plugin DC1 redirect et en l'activant dans la configuration de votre blog." + +msgid "next step" +msgstr "étape suivante" + +msgid "Dotclear tables not found" +msgstr "Tables Dotclear non trouvées" + +msgid "Feed import" +msgstr "Importer depuis un flux" + +msgid "Imports a feed as new entries." +msgstr "Importe un flux comme nouveaux billets." + +msgid "Cannot retrieve feed URL." +msgstr "Impossible d'atteindre l'URL du fil." + +msgid "No items in feed." +msgstr "Aucun élément dans le feed." + +msgid "Content successfully imported." +msgstr "Contenu importé avec succès." + +msgid "Import from a feed" +msgstr "Importer depuis un fil de nouvelles" + +#, php-format +msgid "This will import a feed (RSS or Atom) a as new content in the current blog: %s." +msgstr "Ceci va importer un fil (RSS ou Atom) comme un nouveau contenu dans le blog en cours : %s." + +msgid "Feed URL:" +msgstr "URL du fil" + +msgid "Send" +msgstr "Envoyer" + +msgid "Flat file import" +msgstr "Importer depuis un fichier texte" + +msgid "Imports a blog or a full Dotclear installation from flat file." +msgstr "Importe un blog ou toutes les données depuis un fichier texte." + +msgid "Single blog successfully imported." +msgstr "Blog importé avec succès." + +msgid "Are you sure you want to import a full backup file?" +msgstr "Êtes-vous certain de vouloir charger un fichier de sauvegarde complet ?" + +msgid "Import a single blog" +msgstr "Importer un blog" + +#, php-format +msgid "This will import a single blog backup as new content in the current blog: %s." +msgstr "Ceci va charger une sauvegarde de blog comme un nouveau contenu dans le blog en cours : %s." + +msgid "Upload a backup file" +msgstr "Charger un fichier de sauvegarde" + +msgid "or pick up a local file in your public directory" +msgstr "ou choisissez un fichier local dans votre répertoire public" + +msgid "Import a full backup file" +msgstr "Import d'un fichier de sauvegarde complet" + +msgid "Warning: This will reset all the content of your database, except users." +msgstr "Attention : Ceci va remettre à zéro tout le contenu, sauf les utilisateurs." + +msgid "WordPress import" +msgstr "Importer depuis WordPress" + +msgid "Import a WordPress installation into your current blog." +msgstr "Importe un blog WordPress dans votre blog en cours." + +#, php-format +msgid "This will import your WordPress content as new content in the current blog: %s." +msgstr "Ceci va importer votre blog WordPress comme un nouveau contenu dans le blog en cours : %s." + +msgid "We first need some information about your old WordPress installation." +msgstr "Nous avons d'abord besoin de renseignements à propos de votre ancienne installation WordPress." + +msgid "WordPress and Dotclear's handling of categories are quite different. You can assign several categories to a single post in WordPress. In the Dotclear world, we see it more like \"One category, several tags.\" Therefore Dotclear can only import one category per post and will chose the lowest numbered one. If you want to keep a trace of every category, you can import them as tags, with an optional prefix." +msgstr "WordPress et Dotclear gèrent les catégories différemment. Dans WordPress, un article peut être classé dans plusieurs catégories, alors que dans Dotclear, on voit ça plutôt comme \"Une catégorie, plusieurs mots-clefs.\" Dotclear ne peut donc importer qu'une seule catégorie par article et choisira dans ce cas la première créée. Si vous désirez garder une trace de chaque catégorie, vous pouvez les importer en tant que mots-clefs, avec un préfixe optionnel." + +msgid "On the other hand, in WordPress, a post can not be uncategorized, and a default installation has a first category labelised \"Uncategorized\". If you did not change that category, you can just ignore it while importing your blog, as Dotclear allows you to actually keep your posts uncategorized." +msgstr "Par contre, il est impossible dans WordPress de ne pas assigner une catégorie à un article, c'est pourquoi lors de l'installation est créée une première catégorie appelée \"Non classée\". Si vous n'avez pas modifié cette catégorie, l'importation peut simplement l'ignorer. En effet, Dotclear vous permet d'avoir des articles sans catégorie." + +msgid "Ignore the first category:" +msgstr "Ignorer la première catégorie :" + +msgid "Import lowest numbered category on posts:" +msgstr "Importer la catégorie la plus ancienne sur les articles :" + +msgid "Import all categories as tags:" +msgstr "Importer les catégories comme des tags :" + +msgid "Prefix such tags with:" +msgstr "Préfixer ces tags avec :" + +msgid "Content filters" +msgstr "Filtres de contenu" + +msgid "You may want to process your post and/or comment content with the following filters." +msgstr "Vous pouvez utiliser les filtres suivants sur vos articles et/ou commentaires." + +msgid "Post content formatter:" +msgstr "Formatage des articles :" + +msgid "Comment content formatter:" +msgstr "Formatage des commentaires :" + +msgid "WordPress tables not found" +msgstr "Tables WordPress non trouvées" + +msgid "No file to read." +msgstr "Aucun fichier lisible." + +msgid "File is not a DotClear backup." +msgstr "Le fichier n'est pas une sauvegarde DotClear." + +msgid "File is not a single blog export." +msgstr "Le fichier n'est pas un fichier d'export simple." + +msgid "File is not a full export." +msgstr "Le fichier n'est pas un fichier d'export complet." + +msgid "Please wait..." +msgstr "Veuillez patienter..." + +msgid "Import" +msgstr "Importer" + +# French translation of DotClea" +msgid "Maintenance" +msgstr "Maintenance" + +msgid "Optimization successful." +msgstr "Optimisation réalisée avec succès." + +msgid "Comments and trackback counted." +msgstr "Commentaires et rétroliens comptés." + +msgid "Templates cache directory emptied." +msgstr "Répertoire du cache des templates vidé." + +#, php-format +msgid "Indexing entry %d to %d." +msgstr "Index des billets %d à %d" + +msgid "next" +msgstr "suivant" + +msgid "Entries index done." +msgstr "Index des billets complet." + +msgid "Back" +msgstr "Retour" + +#, php-format +msgid "Indexing comment %d to %d." +msgstr "Index des commentaires %d à %d" + +msgid "Comments index done." +msgstr "Index des commentaires complet." + +msgid "Optimize database room" +msgstr "Optimiser l'espace de la base de donnée" + +msgid "Vacuum tables" +msgstr "Nettoyer les tables" + +msgid "Counters" +msgstr "Compteurs" + +msgid "Reset comments and ping counters" +msgstr "Réinitialiser les compteurs des commentaires et rétroliens" + +msgid "Search engine index" +msgstr "Index du moteur de recherche" + +msgid "This may take a very long time" +msgstr "Ceci peut prendre beaucoup de temps" + +msgid "Index all posts" +msgstr "Indexer tous les billets" + +msgid "Index all comments" +msgstr "Indexer tous les commentaires" + +msgid "Empty templates cache directory" +msgstr "Vider le répertoire du cache des templates" + +msgid "Empty directory" +msgstr "Vider le répertoire" + +msgid "Tags" +msgstr "Tags" + +msgid "Tags:" +msgstr "Tags :" + +#, php-format +msgid "Are you sure you want to remove this %s?" +msgstr "Êtes vous certain de vouloir supprimer ce %s ?" + +#, php-format +msgid "Add a %s to this entry" +msgstr "Ajouter un %s à ce billet" + +msgid "Choose from list" +msgstr "Choisir depuis la liste" + +msgid "all" +msgstr "tous" + +msgid "Tag" +msgstr "Tag" + +msgid "add tags" +msgstr "ajouter des tags" + +msgid "remove tags" +msgstr "supprimer des tags" + +msgid "Add tags to entries" +msgstr "Ajouter des tags aux billets" + +msgid "Tags to add:" +msgstr "Tags à ajouter :" + +msgid "Remove selected tags from entries" +msgstr "Supprimer les tags sélectionnés des billets" + +msgid "No tags for selected entries" +msgstr "Aucun tag pour les billets sélectionnés" + +msgid "Following tags have been found in selected entries:" +msgstr "Les tags suivants ont été trouvés dans les billets sélectionnés :" + +msgid "This tag's comments Atom feed" +msgstr "Fil Atom des commentaires de ce tag" + +msgid "This tag's entries Atom feed" +msgstr "Fil Atom des billets de ce tag" + +msgid "All tags" +msgstr "Tous les tags" + +msgid "Limit (empty means no limit):" +msgstr "Limite (laisser vide pour aucune limite)" + +msgid "Entries count" +msgstr "Nombre de billets" + +msgid "Tag name" +msgstr "Nom du tag" + +msgid "Edit tag" +msgstr "Modifier un tag" + +msgid "Tag has been successfully renamed" +msgstr "Tag renommé avec succès" + +msgid "Back to tags list" +msgstr "Retour à la liste des tags" + +msgid "Rename this tag:" +msgstr "Renommer ce tag :" + +msgid "Delete this tag" +msgstr "Supprimer ce tag" + +msgid "Tag has been successfully removed" +msgstr "Tag supprimé avec succès" + +msgid "entries" +msgstr "billets" + +msgid "No tags on this blog." +msgstr "Aucun tag sur ce blog" + +msgid "Pages" +msgstr "Pages" + +msgid "manage pages" +msgstr "gérer les pages" + +msgid "Published on" +msgstr "Publié le" + +msgid "This page's comments feed" +msgstr "Fil des commentaires de cette page" + +msgid "You must provide a valid email address." +msgstr "Vous devez indiquer une adresse email valide." + +msgid "Page title" +msgstr "Titre de la page" + +msgid "Page position" +msgstr "Position de la page" + +msgid "Publication date" +msgstr "Date de publication" + +msgid "No page" +msgstr "Aucune page" + +msgid "Are you sure you want to delete selected pages?" +msgstr "Êtes-vous certain de vouloir supprimer les pages sélectionnées ?" + +msgid "New page" +msgstr "Nouvelle page" + +msgid "Selected pages action:" +msgstr "Action sur les pages sélectionnées :" + +msgid "This page does not exist." +msgstr "Cette page n'existe pas." + +msgid "Edit page" +msgstr "Modifier la page" + +msgid "next page" +msgstr "page suivante" + +msgid "previous page" +msgstr "page précédente" + +msgid "Are you sure you want to delete this page?" +msgstr "Êtes-vous certain de vouloir supprimer cette pages ?" + +msgid "Page has been successfully updated." +msgstr "La page a été mise à jour avec succès." + +msgid "Page has been successfully created." +msgstr "La page a été créée avec succès." + +msgid "View page" +msgstr "Voir la page" + +msgid "Preview page" +msgstr "Prévisualiser la page" + +msgid "Page status:" +msgstr "État de la page :" + +msgid "Page position:" +msgstr "Position de la page :" + +msgid "Page lang:" +msgstr "Langue de la page :" + +msgid "Page password:" +msgstr "Mot de passe de la page :" + +msgid "Warning: If you set the URL manually, it may conflict with another page." +msgstr "Attention : si vous indiquez l'URL manuellement, celle-ci peut entrer en conflit avec une autre page." + +msgid "Add files to this page" +msgstr "Ajouter un fichier à la page" + +msgid "Pings" +msgstr "Pings" + +msgid "Pings configuration" +msgstr "Configuration des pings" + +msgid "Settings have been successfully updated." +msgstr "Paramètres enregistrés avec succès." + +msgid "Activate pings extension" +msgstr "Activer le module de ping" + +msgid "Service name:" +msgstr "Nom du service :" + +msgid "Service URI:" +msgstr "URI du service :" + +msgid "error" +msgstr "erreur" + +msgid "Test ping services" +msgstr "Tester les services de ping" + +msgid "Pings:" +msgstr "Pings :" + +msgid "Theme Editor" +msgstr "Éditeur de thème" + +msgid "No file" +msgstr "Aucun fichier" + +msgid "File does not exist." +msgstr "Le fichier n'existe pas." + +#, php-format +msgid "File %s is not readable" +msgstr "Le fichier %s n'est pas lisible" + +#, php-format +msgid "Unable to write file %s. Please check your theme files and folders permissions." +msgstr "Impossible d'écrire le fichier %s. Veuillez vérifier les permissions des fichiers et répertoires de votre thème." + +msgid "Saving document..." +msgstr "Sauvegarde du document..." + +msgid "Document saved" +msgstr "Document sauvegardé" + +msgid "An error occurred:" +msgstr "Une erreur s'est produite :" + +#, php-format +msgid "Your current theme on this blog is \"%s\"." +msgstr "Le thème utilisé actuellement sur votre blog est \"%s\"." + +msgid "You can't edit default theme." +msgstr "Vous ne pouvez pas modifier le thème par défaut." + +msgid "Please select a file to edit." +msgstr "Veuillez sélectionner un fichier à modifier." + +msgid "File editor" +msgstr "Éditeur de fichier" + +#, php-format +msgid "Editing file %s" +msgstr "Modification du fichier %s" + +msgid "This file is not writable. Please check your theme files permissions." +msgstr "Ce fichier ne peut pas être modifié. Veuillez vérifier les permissions des fichiers de votre thème." + +msgid "Templates files" +msgstr "Fichiers template" + +msgid "CSS files" +msgstr "Fichiers CSS" + +msgid "JavaScript files" +msgstr "Fichiers JavaScript" + +msgid "Presentation widgets" +msgstr "Widgets de présentation" + +msgid "Search engine" +msgstr "Moteur de recherche" + +msgid "Navigation links" +msgstr "Liens de navigation" + +msgid "Selected entries" +msgstr "Billets sélectionnés" + +msgid "Best of me" +msgstr "À retenir" + +msgid "Blog languages" +msgstr "Langues du blog" + +msgid "With entries counts" +msgstr "Afficher le nombre de billets" + +msgid "Subscribe links" +msgstr "Liens d'abonnement" + +msgid "Subscribe" +msgstr "S'abonner" + +msgid "Feeds type:" +msgstr "Types de fil :" + +msgid "Feed reader" +msgstr "Lecteur de fils de nouvelles" + +msgid "Somewhere else" +msgstr "Ailleurs" + +msgid "Entries limit:" +msgstr "Nombre de billets maximum :" + +msgid "Text" +msgstr "Texte" + +msgid "Text:" +msgstr "Texte :" + +msgid "Last entries" +msgstr "Derniers billets" + +msgid "Uncategorized" +msgstr "Non catégorisé" + +msgid "Tag:" +msgstr "Tag :" + +msgid "Last comments" +msgstr "Derniers commentaires" + +msgid "Comments limit:" +msgstr "Nombre de commentaires maximum :" + +msgid "Home" +msgstr "Accueil" + +msgid "Archives" +msgstr "Archives" + +#, php-format +msgid "This blog's entries %s feed" +msgstr "Fil %s des billets de ce blog" + +#, php-format +msgid "This blog's comments %s feed" +msgstr "Fil %s des commentaires de ce blog" + +msgid "Entries feed" +msgstr "Fil des billets" + +msgid "Comments feed" +msgstr "Fil des commentaires" + +msgid "navigation" +msgstr "navigation" + +msgid "extra" +msgstr "extra" + +msgid "Widgets" +msgstr "Widgets" + +msgid "Are you sure you want to reset sidebars?" +msgstr "Êtes-vous certain de vouloir réinitialiser les bandeaux ?" + +msgid "Available widgets" +msgstr "Widgets disponibles" + +msgid "Append to:" +msgstr "Ajouter à :" + +msgid "add widgets to sidebars" +msgstr "ajouter les widgets aux bandeaux" + +msgid "Navigation sidebar" +msgstr "Bandeau de navigation" + +msgid "Extra sidebar" +msgstr "Bandeau d'extra" + +msgid "update sidebars" +msgstr "Mettre à jour les bandeaux" + +msgid "reset sidebars" +msgstr "Réinitialiser les bandeaux" + +msgid "Widget templates tags" +msgstr "Marqueurs de template des widgets" + +msgid "If you are allowed to edit your theme templates, you can directly add widgets as templates tags, with their own configuration." +msgstr "Si vous avez le droit de modifier les templates de votre thème, vous pouvez directement ajouter des widgets à l'aide de marqueurs de template, avec leur propre configuration." + +msgid "To add a widget in your template, you need to write code like this:" +msgstr "Pour ajouter un widget dans votre template, vous devez écrire un code comme ceci :" + +msgid "Widget ID" +msgstr "Identifiant du widget" + +msgid "Setting name" +msgstr "Nom du paramètre" + +msgid "Setting value" +msgstr "Valeur du paramètre" + +msgid "Here are the following available widgets for your blog:" +msgstr "Voici les widgets disponibles pour votre blog :" + +msgid "Widget ID:" +msgstr "Identifiant du widget :" + +msgid "No setting for this widget" +msgstr "Aucun paramètre pour ce widget" + +msgid "Setting name:" +msgstr "Nom du paramètre :" + +msgid "No widget." +msgstr "Aucun widget." + +msgid "order" +msgstr "ordre" + +msgid "Remove widget" +msgstr "Supprimer le widget" + diff --git a/locales/fr/public.lang.php b/locales/fr/public.lang.php new file mode 100644 index 0000000..e26b166 --- /dev/null +++ b/locales/fr/public.lang.php @@ -0,0 +1,93 @@ +%1$s
    returned no result.'] = 'Votre recherche de %1$s n\'a donné aucun résultat.'; +$GLOBALS['__l10n']['Your search for %1$s returned %2$s result.'] = 'Votre recherche de %1$s a donné %2$s résultat.'; +$GLOBALS['__l10n']['Your search for %1$s returned %2$s results.'] = 'Votre recherche de %1$s a donné %2$s résultats.'; +$GLOBALS['__l10n']['Home'] = 'Accueil'; +$GLOBALS['__l10n']['All keywords'] = 'Tous les mots clés'; +$GLOBALS['__l10n']['Best of me'] = 'À retenir'; +$GLOBALS['__l10n']['Languages'] = 'Langues'; +$GLOBALS['__l10n']['Categories'] = 'Catégories'; +$GLOBALS['__l10n']['Subcategories'] = 'Sous-catégories'; +$GLOBALS['__l10n']['Archives'] = 'Archives'; +$GLOBALS['__l10n']['Links'] = 'Liens'; +$GLOBALS['__l10n']['Subscribe'] = 'S\'abonner'; +$GLOBALS['__l10n']['Entries feed'] = 'Fil des billets'; +$GLOBALS['__l10n']['Comments feed'] = 'Fil des commentaires'; +$GLOBALS['__l10n']['This blog\'s comments Atom feed'] = 'Fil Atom des commentaires de ce blog'; +$GLOBALS['__l10n']['This category\'s entries Atom feed'] = 'Fil Atom des billets de cette catégorie'; +$GLOBALS['__l10n']['This category\'s comments Atom feed'] = 'Fil Atom des commentaires de cette catégorie'; +$GLOBALS['__l10n']['This post\'s comments feed'] = 'Fil des commentaires de ce billet'; +$GLOBALS['__l10n']['This post\'s comments Atom feed'] = 'Fil Atom des commentaires de ce billet'; +$GLOBALS['__l10n']['Attachments'] = 'Annexes'; +$GLOBALS['__l10n']['Permalink'] = 'Lien permanent'; +$GLOBALS['__l10n']['Comments'] = 'Commentaires'; +$GLOBALS['__l10n']['Your comment'] = 'Votre commentaire'; +$GLOBALS['__l10n']['Your comment has been published.'] = 'Votre commentaire a été publié.'; +$GLOBALS['__l10n']['Your comment has been submitted and will be reviewed for publication.'] = 'Votre commentaire a été enregistré et sera publié après validation.'; +$GLOBALS['__l10n']['Add a comment'] = 'Ajouter un commentaire'; +$GLOBALS['__l10n']['Name or nickname'] = 'Nom ou pseudo'; +$GLOBALS['__l10n']['Email address'] = 'Adresse email'; +$GLOBALS['__l10n']['Website'] = 'Site web'; +$GLOBALS['__l10n']['optional'] = 'facultatif'; +$GLOBALS['__l10n']['Comment'] = 'Commentaire'; +$GLOBALS['__l10n']['HTML code is displayed as text and web addresses are automatically converted.'] = 'Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.'; +$GLOBALS['__l10n']['Remember me on this blog'] = 'Se souvenir de moi sur ce blog'; +$GLOBALS['__l10n']['preview'] = 'prévisualiser'; +$GLOBALS['__l10n']['send'] = 'envoyer'; +$GLOBALS['__l10n']['They posted on the same topic'] = 'La discussion continue ailleurs'; +$GLOBALS['__l10n']['Trackback URL'] = 'URL de rétrolien'; +$GLOBALS['__l10n']['You must provide an author name'] = 'Vous devez indiquer un nom'; +$GLOBALS['__l10n']['You must provide a comment'] = 'Vous devez écrire un commentaire'; +$GLOBALS['__l10n']['Email address is not valid'] = 'Adresse email incorrecte'; +$GLOBALS['__l10n']['Document not found'] = 'Document non trouvé'; +$GLOBALS['__l10n']['The document you are looking for does not exist.'] = 'Le document que vous cherchez n\'existe pas.'; +$GLOBALS['__l10n']['Powered by %s'] = 'Propulsé par %s'; +$GLOBALS['__l10n']['Subscribe to'] = 'S\'abonner à'; +$GLOBALS['__l10n']['What is an RSS feed?'] = 'Qu\'est ce qu\'un fil RSS ?'; +$GLOBALS['__l10n']['RSS feed is a free blog summary. It provides content (either posts or comments) or summaries of content, together with links to the full versions, and other metadata. The last published items may then be read by your favorite RSS aggregator.'] = 'Un fil RSS recueille les informations de mise à jour d\'un site. Il fournit le contenu des billets ou des commentaires ou un extrait de ceux-ci, ainsi qu\'un lien vers les versions complètes et quelques autres informations. Ce fil a pour vocation d\'être lu par un agrégateur RSS.'; +$GLOBALS['__l10n']['Simply copy the following URL into your aggregator:'] = 'Copier simplement l\'adresse suivante dans votre agrégateur :'; +$GLOBALS['__l10n']['Password needed'] = 'Mot de passe nécessaire'; +$GLOBALS['__l10n']['You must give a password to access this area.'] = 'Vous devez indiquer un mot de passe pour accéder à cette partie.'; +$GLOBALS['__l10n']['Password:'] = 'Mot de passe :'; +$GLOBALS['__l10n']['You must provide a valid email address.'] = 'Vous devez indiquer une adresse e-mail valide.'; +$GLOBALS['__l10n']['Read'] = 'Lire'; +?> \ No newline at end of file diff --git a/locales/fr/public.po b/locales/fr/public.po new file mode 100644 index 0000000..464320f --- /dev/null +++ b/locales/fr/public.po @@ -0,0 +1,237 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-10-13 05:22+0200\n" +"PO-Revision-Date: 2008-07-30 11:52+0100\n" +"Last-Translator: Olivier Meunier \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" + +msgid "To content" +msgstr "Aller au contenu" + +msgid "To menu" +msgstr "Aller au menu" + +msgid "To search" +msgstr "Aller à la recherche" + +msgid "By" +msgstr "Par" + +msgid "by" +msgstr "par" + +msgid "on" +msgstr "le" + +msgid "On" +msgstr "Le" + +msgid "Continue reading" +msgstr "Lire la suite" + +msgid "no comment" +msgstr "aucun commentaire" + +msgid "one comment" +msgstr "un commentaire" + +msgid "%d comments" +msgstr "%d commentaires" + +msgid "no trackback" +msgstr "aucun rétrolien" + +msgid "one trackback" +msgstr "un rétrolien" + +msgid "%d trackbacks" +msgstr "%d rétroliens" + +msgid "no attachment" +msgstr "aucune annexe" + +msgid "one attachment" +msgstr "une annexe" + +msgid "%d attachments" +msgstr "%d annexes" + +msgid "previous entries" +msgstr "billets précédents" + +msgid "page" +msgstr "page" + +msgid "of" +msgstr "de" + +msgid "next entries" +msgstr "billets suivants" + +msgid "Search" +msgstr "Recherche" + +msgid "Your search for %1$s returned no result." +msgstr "Votre recherche de %1$s n'a donné aucun résultat." + +msgid "Your search for %1$s returned %2$s result." +msgstr "Votre recherche de %1$s a donné %2$s résultat." + +msgid "Your search for %1$s returned %2$s results." +msgstr "Votre recherche de %1$s a donné %2$s résultats." + +msgid "Home" +msgstr "Accueil" + +msgid "All keywords" +msgstr "Tous les mots clés" + +msgid "Best of me" +msgstr "À retenir" + +msgid "Languages" +msgstr "Langues" + +msgid "Categories" +msgstr "Catégories" + +msgid "Subcategories" +msgstr "Sous-catégories" + +msgid "Archives" +msgstr "Archives" + +msgid "Links" +msgstr "Liens" + +msgid "Subscribe" +msgstr "S'abonner" + +msgid "Entries feed" +msgstr "Fil des billets" + +msgid "Comments feed" +msgstr "Fil des commentaires" + +msgid "This blog's comments Atom feed" +msgstr "Fil Atom des commentaires de ce blog" + +msgid "This category's entries Atom feed" +msgstr "Fil Atom des billets de cette catégorie" + +msgid "This category's comments Atom feed" +msgstr "Fil Atom des commentaires de cette catégorie" + +msgid "This post's comments feed" +msgstr "Fil des commentaires de ce billet" + +msgid "This post's comments Atom feed" +msgstr "Fil Atom des commentaires de ce billet" + +msgid "Attachments" +msgstr "Annexes" + +msgid "Permalink" +msgstr "Lien permanent" + +msgid "Comments" +msgstr "Commentaires" + +msgid "Your comment" +msgstr "Votre commentaire" + +msgid "Your comment has been published." +msgstr "Votre commentaire a été publié." + +msgid "Your comment has been submitted and will be reviewed for publication." +msgstr "Votre commentaire a été enregistré et sera publié après validation." + +msgid "Add a comment" +msgstr "Ajouter un commentaire" + +msgid "Name or nickname" +msgstr "Nom ou pseudo" + +msgid "Email address" +msgstr "Adresse email" + +msgid "Website" +msgstr "Site web" + +msgid "optional" +msgstr "facultatif" + +msgid "Comment" +msgstr "Commentaire" + +msgid "HTML code is displayed as text and web addresses are automatically converted." +msgstr "Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées." + +msgid "Remember me on this blog" +msgstr "Se souvenir de moi sur ce blog" + +msgid "preview" +msgstr "prévisualiser" + +msgid "send" +msgstr "envoyer" + +msgid "They posted on the same topic" +msgstr "La discussion continue ailleurs" + +msgid "Trackback URL" +msgstr "URL de rétrolien" + +msgid "You must provide an author name" +msgstr "Vous devez indiquer un nom" + +msgid "You must provide a comment" +msgstr "Vous devez écrire un commentaire" + +msgid "Email address is not valid" +msgstr "Adresse email incorrecte" + +msgid "Document not found" +msgstr "Document non trouvé" + +msgid "The document you are looking for does not exist." +msgstr "Le document que vous cherchez n'existe pas." + +msgid "Powered by %s" +msgstr "Propulsé par %s" + +msgid "Subscribe to" +msgstr "S'abonner à" + +msgid "What is an RSS feed?" +msgstr "Qu'est ce qu'un fil RSS ?" + +msgid "RSS feed is a free blog summary. It provides content (either posts or comments) or summaries of content, together with links to the full versions, and other metadata. The last published items may then be read by your favorite RSS aggregator." +msgstr "Un fil RSS recueille les informations de mise à jour d'un site. Il fournit le contenu des billets ou des commentaires ou un extrait de ceux-ci, ainsi qu'un lien vers les versions complètes et quelques autres informations. Ce fil a pour vocation d'être lu par un agrégateur RSS." + +msgid "Simply copy the following URL into your aggregator:" +msgstr "Copier simplement l'adresse suivante dans votre agrégateur :" + +msgid "Password needed" +msgstr "Mot de passe nécessaire" + +msgid "You must give a password to access this area." +msgstr "Vous devez indiquer un mot de passe pour accéder à cette partie." + +msgid "Password:" +msgstr "Mot de passe :" + +msgid "You must provide a valid email address." +msgstr "Vous devez indiquer une adresse e-mail valide." + +msgid "Read" +msgstr "Lire" diff --git a/locales/fr/resources.php b/locales/fr/resources.php new file mode 100644 index 0000000..ee941ea --- /dev/null +++ b/locales/fr/resources.php @@ -0,0 +1,22 @@ + 'http://doc.dotclear.net/2.0', + 'Présentation de Dotclear 2' => 'http://doc.dotclear.net/2.0/overview/tour', + "Manuel de l'utilisateur" => 'http://doc.dotclear.net/2.0/usage', + "Guide d'installation et d'administration" => 'http://doc.dotclear.net/2.0/admin' +); +?> \ No newline at end of file diff --git a/plugins/.htaccess b/plugins/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/plugins/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/plugins/aboutConfig/_admin.php b/plugins/aboutConfig/_admin.php new file mode 100644 index 0000000..39e987b --- /dev/null +++ b/plugins/aboutConfig/_admin.php @@ -0,0 +1,17 @@ +addItem('about:config','plugin.php?p=aboutConfig','index.php?pf=aboutConfig/icon.png', + preg_match('/plugin.php\?p=aboutConfig(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); +?> \ No newline at end of file diff --git a/plugins/aboutConfig/_define.php b/plugins/aboutConfig/_define.php new file mode 100644 index 0000000..c5c4019 --- /dev/null +++ b/plugins/aboutConfig/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "about:config", + /* Description*/ "Manage every blog configuration directive", + /* Author */ "Olivier Meunier", + /* Version */ '0.2', + /* Permissions */ null +); +?> \ No newline at end of file diff --git a/plugins/aboutConfig/icon.png b/plugins/aboutConfig/icon.png new file mode 100644 index 0000000..426cd27 Binary files /dev/null and b/plugins/aboutConfig/icon.png differ diff --git a/plugins/aboutConfig/index.php b/plugins/aboutConfig/index.php new file mode 100644 index 0000000..dfdc3cf --- /dev/null +++ b/plugins/aboutConfig/index.php @@ -0,0 +1,179 @@ + $s) + { + $core->blog->settings->setNameSpace($ns); + + foreach ($s as $k => $v) { + $core->blog->settings->put($k,$v); + } + + $core->blog->triggerBlog(); + } + + http::redirect($p_url.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Global settings update +if (!empty($_POST['gs']) && is_array($_POST['gs'])) +{ + try + { + foreach ($_POST['gs'] as $ns => $s) + { + $core->blog->settings->setNameSpace($ns); + + foreach ($s as $k => $v) { + $core->blog->settings->put($k,$v,null,null,true,true); + } + + $core->blog->triggerBlog(); + } + + http::redirect($p_url.'&upd=1&part=global'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +$part = !empty($_GET['part']) && $_GET['part'] == 'global' ? 'global' : 'local'; + +function settingLine($id,$s,$ns,$field_name,$strong_label) +{ + if ($s['type'] == 'boolean') { + $field = form::combo(array($field_name.'['.$ns.']['.$id.']',$field_name.'_'.$id), + array(__('yes') => 1, __('no') => 0),$s['value']); + } else { + $field = form::field(array($field_name.'['.$ns.']['.$id.']',$field_name.'_'.$id),40,null, + html::escapeHTML($s['value'])); + } + + $slabel = $strong_label ? '%s' : '%s'; + + return + ''. + ''. + ''.$field.''. + ''.$s['type'].''. + ''.html::escapeHTML($s['label']).''. + ''; +} +?> + + + about:config + + + + + +'.__('Configuration successfully updated').'

    '; +} + +if (!empty($_GET['upda'])) { + echo '

    '.__('Settings definition successfully updated').'

    '; +} +?> +

    blog->name); ?> › about:config

    + +
    +
    + + + + + + + +blog->settings->dumpSettings() as $k => $v) { + $settings[$v['ns']][$k] = $v; +} + +ksort($settings); + +foreach ($settings as $ns => $s) +{ + ksort($s); + echo ''; + + foreach ($s as $k => $v) + { + echo settingLine($k,$v,$ns,'s',!$v['global']); + } +} +?> +
    Setting ID
    namespace: '.$ns.'
    +

    + +formNonce(); ?>

    +
    +
    + +
    +
    + + + + + + + +blog->settings->dumpGlobalSettings() as $k => $v) { + $settings[$v['ns']][$k] = $v; +} + +ksort($settings); + +foreach ($settings as $ns => $s) +{ + ksort($s); + echo ''; + + foreach ($s as $k => $v) + { + echo settingLine($k,$v,$ns,'gs',false); + } +} +?> +
    Setting ID
    namespace: '.$ns.'
    +

    + +formNonce(); ?>

    +
    +
    + + + \ No newline at end of file diff --git a/plugins/accessibleCaptcha/LICENSE b/plugins/accessibleCaptcha/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/plugins/accessibleCaptcha/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/plugins/accessibleCaptcha/_define.php b/plugins/accessibleCaptcha/_define.php new file mode 100644 index 0000000..9bafb48 --- /dev/null +++ b/plugins/accessibleCaptcha/_define.php @@ -0,0 +1,12 @@ +registerModule( + /* Name */ "Accessible Captcha", + /* Description*/ "This is an accessible captcha", + /* Author */ "Julien Wajsberg", + /* Version */ '1.2', + /* Permissions */ 'usage,contentadmin', + /* Priority */ 200 +); +?> diff --git a/plugins/accessibleCaptcha/_install.php b/plugins/accessibleCaptcha/_install.php new file mode 100644 index 0000000..f57f898 --- /dev/null +++ b/plugins/accessibleCaptcha/_install.php @@ -0,0 +1,67 @@ + + + This file is part of dotclear-accessible-captcha. + + dotclear-accessible-captcha is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3 of the License. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ + +if (!defined('DC_CONTEXT_ADMIN')) { return; } + +// c'est le nom du répertoire +$moduleName = "accessibleCaptcha"; + +# On lit la version du plugin +$m_version = $core->plugins->moduleInfo($moduleName, 'version'); + +# On lit la version du plugin dans la table des versions +$i_version = $core->getVersion($moduleName); + +# La version dans la table est supérieure ou égale à +# celle du module, on ne fait rien puisque celui-ci +# est installé +//echo $i_version . '
    ' . $m_version . '
    '; + +if (version_compare($i_version, $m_version,'>=')) { + return; +} + +$s = new dbStruct($core->con,$core->prefix); +$s->captcha + ->id('bigint', 0, false) + ->question('varchar', 150, false) + ->answer('varchar', 150, false) + ->blog_id('varchar', 32, false) + + ->primary('pk_captcha', 'id') + ->index('idx_captcha_blog_btree', 'btree', 'blog_id') + ->reference('fk_captcha_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'cascade'); + +$s->captcha_hash + ->id('bigint', 0, false) + ->hash('varchar', 150, false) + ->captcha_id('bigint', 0, false) + ->timestamp('timestamp', 0, false) + + ->primary('pk_captcha_hash', 'id') + ->index('idx_captcha_hash_btree', 'btree', 'hash') + ->reference('fk_captcha_hash_captcha', 'captcha_id', 'captcha', 'id', 'cascade', 'cascade'); + +$si = new dbStruct($core->con,$core->prefix); +$changes = $si->synchronize($s); + +# La procédure d'installation commence vraiment là +$core->setVersion($moduleName, $m_version); +return true; + diff --git a/plugins/accessibleCaptcha/_prepend.php b/plugins/accessibleCaptcha/_prepend.php new file mode 100644 index 0000000..4c06b16 --- /dev/null +++ b/plugins/accessibleCaptcha/_prepend.php @@ -0,0 +1,34 @@ + + + This file is part of dotclear-accessible-captcha. + + dotclear-accessible-captcha is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3 of the License. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ + +if (!defined('DC_RC_PATH')) { return; } + +global $__autoload, $core; +$__autoload['dcFilterAccessibleCaptcha'] = dirname(__FILE__).'/class.dc.filter.accessible.captcha.php'; +$__autoload['AccessibleCaptcha'] = dirname(__FILE__).'/class.accessible.captcha.php'; + + +$core->spamfilters[] = 'dcFilterAccessibleCaptcha'; + +$core->addBehavior('publicCommentFormAfterContent', + array('dcFilterAccessibleCaptcha','publicCommentFormAfterContent')); + +$core->addBehavior('exportFull',array('dcFilterAccessibleCaptcha','exportFull')); +$core->addBehavior('exportSingle',array('dcFilterAccessibleCaptcha','exportSingle')); +?> diff --git a/plugins/accessibleCaptcha/class.accessible.captcha.php b/plugins/accessibleCaptcha/class.accessible.captcha.php new file mode 100644 index 0000000..cff843c --- /dev/null +++ b/plugins/accessibleCaptcha/class.accessible.captcha.php @@ -0,0 +1,303 @@ + + + This file is part of dotclear-accessible-captcha. + + dotclear-accessible-captcha is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3 of the License. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ + +class AccessibleCaptcha { + // nom des tables + public static $table = "captcha"; + private static $table_hash = "captcha_hash"; + + // ttl des hash en minutes + // devrait être en settings ? + private static $hash_ttl_min = 60; // 1h + + + // ----- méthodes publiques ----- + + /** + * retourne une question au pif et crée un hash dans la base de données. + * Ce hash fait l'association entre cette question et le formulaire, et sera supprimé + * lorsque la question sera répondue. + * @param $blog_id l'id du blog + * @return le hash créé et inséré dans la base + */ + public function getRandomQuestionAndHash($blog_id) { + $question = $this->getRandomQuestion($blog_id); + $question['hash'] = $this->setAndReturnHashForQuestion($question['id']); + return $question; + } + + /** + * vérifie une réponse par rapport à un hash + * si la réponse est correcte, on supprime le hash + * cette méthode supprime aussi les hash obsolètes + * @param $hash le hash correspondant à la question à vérifier + * @param $answer la réponse à vérifier + * @return true si la réponse est juste + */ + public function isAnswerCorrectForHash($hash, $answer) { + if ($this->checkAnswer($hash, $answer)) { + $this->removeHash($hash); + return true; + } + + // si non + return false; + } + + /** + * Retourne la question associée à un hash en particulier + * @param $hash le hash correspondant à la question à récupérer + * @return la question associée au hash + */ + public function getQuestionForHash($hash) { + global $core; + $con =& $core->con; + + $query = 'select c.id as id, c.question as question ' + . 'from ' . $core->prefix . self::$table . ' as c, ' . $core->prefix . self::$table_hash . ' as ch ' + . " where ch.hash = '" . $con->escape($hash) . "' and ch.captcha_id = c.id "; + + $question = $con->select($query); + + return array( + 'id' => $question->id, + 'question' => $question->question, + 'hash' => $hash + ); + } + + // ----- fin des méthodes publiques ----- + + + // ----- méthodes privées ----- + + /** + * retourne une question au pif + * cette méthode initialise une question pour ce blog s'il n'en existe pas encore + * @return une question au pif + */ + private function getRandomQuestion($blog_id) { + + $this->checkAndInitQuestions($blog_id); + // on récupère le nombre de questions + $count = $this->getCountQuestions($blog_id); + // on demande la nieme + $rand = rand(0, $count - 1); + // on va récupérer la nieme + $question = $this->getQuestionInOrder($blog_id, $rand); + return $question; + } + + + private function getQuestionInOrder($blog_id, $nb) { + global $core; + $con =& $core->con; + + $query = 'select id, question from ' . $core->prefix . self::$table . + " where blog_id = '" . $con->escape($blog_id) . "' order by id asc " + . $con->limit($nb, 1); + + $question = $con->select($query); + return array( + 'id' => $question->id, + 'question' => $question->question, + ); + } + + private function checkAnswer($hash, $answer) { + global $core; + $con =& $core->con; + + // vérifions que la réponse est correcte + $query = 'select count(c.id) from ' . $core->prefix . self::$table . ' as c, ' . $core->prefix . self::$table_hash . ' as ch ' + . " where ch.hash = '" . $con->escape($hash) . "' and ch.captcha_id = c.id " + . " and c.answer = '" . $con->escape($answer) . "'"; + $count = $con->select($query)->f(0); + + return ($count > 0); + } + + private function removeHash($hash) { + global $core; + $con =& $core->con; + + $query = 'delete from ' . $core->prefix . self::$table_hash . " where hash = '" . $con->escape($hash) . "'"; + + // et on en profite pour enlever les anciens + $expired_timestamp = gmmktime(gmdate('H'), gmdate('i') - self::$hash_ttl_min); + $expired_datetime = gmdate('Y-m-d H:i:s', $expired_timestamp); + $query .= " or timestamp < '" . $con->escape($expired_datetime) . "'"; + $con->execute($query); + } + + /** + * retourne une question en particulier + * @param $id id de la question à retourner + * @ return Question qui à l'$id passé en paramètre + */ + private function getQuestion($id) { + global $core; + $con =& $core->con; + + $query = 'select question, answer from ' . $core->prefix . self::$table . " where id = '" . $con->escape($id) ."'"; + $question = $con->select($query); + + return array( + 'id' => $id, + 'question' => $question->question, + 'answer' => $question->answer + ); + } + + private function setAndReturnHashForQuestion($id) { + global $core; + $con =& $core->con; + + $con->writeLock($core->prefix . self::$table_hash); + try { + $new_id = $con->select( + 'SELECT MAX(id) '. + 'FROM '. $core->prefix . self::$table_hash + )->f(0) + 1; + + $hash = $this->getHash(); + $cur = $con->openCursor($core->prefix . self::$table_hash); + $cur->captcha_id = $id; + $cur->id = $new_id; + $cur->timestamp = gmdate('Y-m-d H:i:s'); + $cur->hash = $hash; + + $cur->insert(); + + $con->unlock(); + } catch (Exception $e) { + $con->unlock(); + throw $e; + } + + return $hash; + } + + // on va supposer que c'est suffisamment random pour un captcha + private function getHash() { + $key = http::browserUID(crypt::hmac(DC_MASTER_KEY,crypt::createPassword())); + return $key; + } + + private function getCountQuestions($blog_id) { + global $core; + $con =& $core->con; + + $query = 'select count(id) from '. $core->prefix . self::$table . " where blog_id = '" . $con->escape($blog_id) . "'"; + $count = $con->select($query)->f(0); + return $count; + } + + private function checkAndInitQuestions($blog_id) { + global $core; + $con =& $core->con; + + $con->writeLock($core->prefix . self::$table); + + try { + $count = $this->getCountQuestions($blog_id); + if ($count == 0) { + $this->initQuestions($blog_id); + } + $con->unlock(); + } catch (Exception $e) { + $con->unlock(); + throw $e; + } + } + + public function initQuestions($blog_id) { + global $core; + $con =& $core->con; + + // on supprime tout + $delete_query = 'delete from ' . $core->prefix . self::$table . " where blog_id = '" . $con->escape($blog_id) . "'"; + $con->execute($delete_query); + + // et on ajoute la question par défaut + $this->addQuestion( + $blog_id, + __("What makes two plus two?"), + __("4") + ); + } + + public function addQuestion($blog_id, $question, $answer, $id = -1) { + global $core; + $con =& $core->con; + + // calculate new id + $new_id = $con->select( + 'SELECT MAX(id) '. + 'FROM '. $core->prefix . self::$table + )->f(0); + + if (is_numeric($new_id)) { + $new_id++; + } else { + // no id yet + $new_id = 0; + } + + $cur = $con->openCursor($core->prefix . self::$table); + $cur->id = $new_id; + $cur->question = $question; + $cur->answer = $answer; + $cur->blog_id = $blog_id; + $cur->insert(); + } + + public function getAllQuestions($blog_id) { + global $core; + $con =& $core->con; + + $query = "select id, question, answer from " . $core->prefix . self::$table . " where blog_id = '" . $con->escape($blog_id) . "'"; + $rs = $con->select($query); + + $result = array(); + while($rs->fetch()) { + $result[] = array( + "id" => $rs->id, + "question" => $rs->question, + "answer" => $rs->answer + ); + } + return $result; + } + + public function removeQuestions($blog_id, $arr_ids) { + global $core; + $con =& $core->con; + + $delete_query = 'delete from ' . $core->prefix . self::$table . " where blog_id = '" . $con->escape($blog_id) . "'" . + ' and (false'; + foreach($arr_ids as $id) { + $delete_query .= " or id = '" . $con->escape($id) . "'"; + } + $delete_query .= ')'; + + $con->execute($delete_query); + + } +} diff --git a/plugins/accessibleCaptcha/class.dc.filter.accessible.captcha.php b/plugins/accessibleCaptcha/class.dc.filter.accessible.captcha.php new file mode 100644 index 0000000..eb95c83 --- /dev/null +++ b/plugins/accessibleCaptcha/class.dc.filter.accessible.captcha.php @@ -0,0 +1,182 @@ + + + This file is part of dotclear-accessible-captcha. + + dotclear-accessible-captcha is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3 of the License. + + Foobar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see . +*/ + +class dcFilterAccessibleCaptcha extends dcSpamFilter +{ + public $name = 'Accessible Captcha'; + public $has_gui = true; + private $style_p = 'margin: .2em 0; padding: 0 0.5em; '; + private $style_answer = 'margin: 0 0 0 .5em; '; + + protected function setInfo() + { + $this->description = __('This is an accessible captcha.'); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) { + $accessibleCaptcha = new AccessibleCaptcha(); + + $question_hash = $_POST['c_question_hash']; + $answer = $_POST['c_answer']; + if (! $answer) { + $status = 'Filtered'; + return true; + } + + if (! $accessibleCaptcha->isAnswerCorrectForHash($question_hash, $answer)) { + $status = 'Filtered'; + return true; + } + } + + public function getStatusMessage($status, $comment_id) { + return sprintf(__('Filtered by %s.'),$this->guiLink()); + } + + public static function publicCommentFormAfterContent($core, $_ctx) { + $accessibleCaptcha = new AccessibleCaptcha(); + + if (($hash = $_POST['c_question_hash'])) { + $question = $accessibleCaptcha->getQuestionForHash($hash); + } else { + $question = $accessibleCaptcha->getRandomQuestionAndHash($core->blog->id); + } + + $escaped_value = htmlspecialchars($_POST['c_answer'], ENT_QUOTES); + $escaped_question = htmlspecialchars($question['question'], ENT_QUOTES); + $escaped_hash = htmlspecialchars($question['hash'], ENT_QUOTES); + + echo "

    + + +

    "; + } + + // plugin d'import export + public static function exportFull($core, $exp) { + $exp->exportTable(AccessibleCaptcha::$table); + } + + public static function exportSingle($core, $exp, $blog_id) { + $exp->export(AccessibleCaptcha::$table, + 'SELECT * '. + 'FROM '. $core->prefix . AccessibleCaptcha::$table . ' '. + "WHERE blog_id = '{$blog_id}'" + ); + } + // fin des méthodes pour le plugin d'import export + + // pour la gui + public function gui($url) { + global $core; + + $accessibleCaptcha = new AccessibleCaptcha(); + + // ajout de questions + if (! (empty($_POST['c_question']) || empty($_POST['c_answer']))) { + $accessibleCaptcha->addQuestion( + $core->blog->id, + $_POST['c_question'], + $_POST['c_answer'] + ); + + // redirection pour que l'user puisse faire "reload" + http::redirect($url.'&added=1'); + } + + // suppression de questions + if (! empty($_POST['c_d_questions']) && is_array($_POST['c_d_questions'])) { + $accessibleCaptcha->removeQuestions($core->blog->id, $_POST['c_d_questions']); + http::redirect($url.'&deleted=1'); + } + + // réinit + if (! empty($_POST['c_createlist'])) { + $accessibleCaptcha->initQuestions($core->blog->id); + http::redirect($url.'&reset=1'); + } + + // assez joué, maintenant on affiche + $res = ''; + + if (!empty($_GET['added'])) { + $res .= '

    '.__('Question has been successfully added.').'

    '; + } + if (!empty($_GET['deleted'])) { + $res .= '

    '.__('Questions have been successfully removed.').'

    '; + } + if (!empty($_GET['reset'])) { + $res .= '

    '.__('Questions list has been successfully reinitialized.').'

    '; + } + + $res .= + '
    '. + '
    '.__('Add a question').''. + '

    '. + '

    '; + $res .= + $core->formNonce(). + '

    '. + '
    '. + '
    '; + + $allquestions = $accessibleCaptcha->getAllQuestions($core->blog->id); + + + $res .= '
    '. + '
    ' . __('Question list') . ''; + + foreach($allquestions as $question) { + $res .= '

    '; + } + + $res .= '

    '. $core->formNonce() . + '

    '; + + $res .= "
    "; + + $res .= + '
    ' . + '

    ' . + form::hidden(array('c_createlist'), 1) . + $core->formNonce().'

    ' . + '
    '; + + $disableText = __('To disable this plugin, you need to disable it %sfrom the plugins page%s.'); + $disableText = sprintf($disableText, '', ''); + + $res .= + '

    '.$disableText.'

    '; + + return $res; + + } + +} + diff --git a/plugins/accessibleCaptcha/locales/fr/main.po b/plugins/accessibleCaptcha/locales/fr/main.po new file mode 100644 index 0000000..7eb9afc --- /dev/null +++ b/plugins/accessibleCaptcha/locales/fr/main.po @@ -0,0 +1,87 @@ +# French translations for dotclear-accessible-captcha package. +# Copyright (C) 2009 Julien Wajsberg +# This file is distributed under the same license as the dotclear-accessible-captcha package. +# Julien Wajsberg , 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: dotclear-acessible-captcha 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-03-29 19:13+0100\n" +"PO-Revision-Date: 2009-11-22 15:22+0100\n" +"Last-Translator: Julien Wajsberg \n" +"Language-Team: French\n" +"Language: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: class.accessible.captcha.php:241 +msgid "What makes two plus two?" +msgstr "Combien font deux plus deux ?" + +#: class.accessible.captcha.php:242 +msgid "4" +msgstr "4" + +#: class.dc.filter.accessible.captcha.php:29 +msgid "This is an accessible captcha." +msgstr "Ce plugin implémente un captcha accessible." + +#: class.dc.filter.accessible.captcha.php:49 +#, php-format +msgid "Filtered by %s." +msgstr "Filtré par %s" + +#: class.dc.filter.accessible.captcha.php:119 +msgid "Question has been successfully added." +msgstr "La question a été ajoutée." + +#: class.dc.filter.accessible.captcha.php:122 +msgid "Questions have been successfully removed." +msgstr "Les questions ont été supprimées." + +#: class.dc.filter.accessible.captcha.php:125 +msgid "Questions list has been successfully reinitialized." +msgstr "La liste des questions a été réinitialisée." + +#: class.dc.filter.accessible.captcha.php:130 +msgid "Add a question" +msgstr "Ajouter une question" + +#: class.dc.filter.accessible.captcha.php:131 +msgid "Question to add:" +msgstr "Question à ajouter :" + +#: class.dc.filter.accessible.captcha.php:134 +#: class.dc.filter.accessible.captcha.php:154 +msgid "Answer:" +msgstr "Réponse :" + +#: class.dc.filter.accessible.captcha.php:139 +msgid "Add" +msgstr "Ajouter" + +#: class.dc.filter.accessible.captcha.php:147 +msgid "Question list" +msgstr "Liste des questions" + +#: class.dc.filter.accessible.captcha.php:152 +msgid "Question:" +msgstr "Question :" + +#: class.dc.filter.accessible.captcha.php:160 +msgid "Delete selected questions" +msgstr "Supprimer les questions sélectionnées" + +#: class.dc.filter.accessible.captcha.php:166 +msgid "Reset the list" +msgstr "Réinitialiser la liste des questions" + +#: class.dc.filter.accessible.captcha.php:171 +#, php-format +msgid "" +"To disable this plugin, you need to disable it %sfrom the plugins page%s." +msgstr "" +"Pour désactiver ce plugin, vous devez le faire %sdepuis la page générale des plugins%s." diff --git a/plugins/akismet/_define.php b/plugins/akismet/_define.php new file mode 100644 index 0000000..fec101a --- /dev/null +++ b/plugins/akismet/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "Akismet", + /* Description*/ "Akismet interface for Dotclear", + /* Author */ "Olivier Meunier", + /* Version */ '1.0', + /* Permissions */ 'usage,contentadmin', + /* Priority */ 200 +); +?> \ No newline at end of file diff --git a/plugins/akismet/_prepend.php b/plugins/akismet/_prepend.php new file mode 100644 index 0000000..3d1fd40 --- /dev/null +++ b/plugins/akismet/_prepend.php @@ -0,0 +1,17 @@ +spamfilters[] = 'dcFilterAkismet'; +?> \ No newline at end of file diff --git a/plugins/akismet/class.dc.filter.akismet.php b/plugins/akismet/class.dc.filter.akismet.php new file mode 100644 index 0000000..88cb167 --- /dev/null +++ b/plugins/akismet/class.dc.filter.akismet.php @@ -0,0 +1,260 @@ +auth->isSuperAdmin()) { + $this->has_gui = false; + } + } + + protected function setInfo() + { + $this->description = __('Akismet spam filter'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %s.'),$this->guiLink()); + } + + private function akInit() + { + $blog =& $this->core->blog; + + if (!$blog->settings->ak_key) { + return false; + } + + return new akismet($blog->url,$blog->settings->ak_key); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (($ak = $this->akInit()) === false) { + return; + } + + $blog =& $this->core->blog; + + try + { + if ($ak->verify()) + { + $post = $blog->getPosts(array('post_id' => $post_id)); + + $c = $ak->comment_check( + $post->getURL(), + $type, + $author, + $email, + $site, + $content + ); + + if ($c) { + $status = 'Filtered by Akismet'; + return true; + } + } + } catch (Exception $e) {} # If http or akismet is dead, we don't need to know it + } + + public function trainFilter($status,$filter,$type,$author,$email,$site,$ip,$content,$rs) + { + # We handle only false positive from akismet + if ($status == 'spam' && $filter != 'dcFilterAkismet') + { + return; + } + + $f = $status == 'spam' ? 'submit_spam' : 'submit_ham'; + + if (($ak = $this->akInit()) === false) { + return; + } + + try + { + if ($ak->verify()) { + $ak->{$f}($rs->getPostURL(),$type,$author,$email,$site,$content); + } + } catch (Exception $e) {} # If http or akismet is dead, we don't need to know it + } + + public function gui($url) + { + $blog =& $this->core->blog; + + $ak_key = $blog->settings->ak_key; + $ak_verified = null; + + if (isset($_POST['ak_key'])) + { + try + { + $ak_key = $_POST['ak_key']; + + $blog->settings->setNameSpace('akismet'); + $blog->settings->put('ak_key',$ak_key,'string'); + + http::redirect($url.'&up=1'); + } + catch (Exception $e) + { + $this->core->error->add($e->getMessage()); + } + } + + if ($blog->settings->ak_key) + { + try { + $ak = new akismet($blog->url,$blog->settings->ak_key); + $ak_verified = $ak->verify(); + } catch (Exception $e) { + $this->core->error->add($e->getMessage()); + } + } + + $res = + '
    '. + '

    '; + + if ($ak_verified !== null) { + if ($ak_verified) { + $res .= ' '.__('API key verified'); + } else { + $res .= ' '.__('API key not verified'); + } + } + + $res .= '

    '; + + $res .= + '

    '.__('Get your own API key').'

    '. + '

    '. + $this->core->formNonce().'

    '. + '
    '; + + return $res; + } +} + +class akismet extends netHttp +{ + protected $base_host = 'rest.akismet.com'; + protected $ak_host = ''; + protected $ak_version = '1.1'; + protected $ak_path = '/%s/%s'; + + protected $ak_key = null; + protected $blog_url; + + protected $timeout = 3; + + public function __construct($blog_url,$api_key) + { + $this->blog_url = $blog_url; + $this->ak_key = $api_key; + + $this->ak_path = sprintf($this->ak_path,$this->ak_version,'%s'); + $this->ak_host = $this->ak_key.'.'.$this->base_host; + + parent::__construct($this->ak_host,80); + } + + public function verify() + { + $this->host = $this->base_host; + $path = sprintf($this->ak_path,'verify-key'); + + $data = array( + 'key' => $this->ak_key, + 'blog' => $this->blog_url + ); + + if ($this->post($path,$data,'UTF-8')) + { + return $this->getContent() == 'valid'; + } + + return false; + } + + public function comment_check($permalink,$type,$author,$email,$url,$content) + { + $info_ignore = array('HTTP_COOKIE'); + $info = array(); + + foreach ($_SERVER as $k => $v) { + if (strpos($k,'HTTP_') === 0 && !in_array($k,$info_ignore)) { + $info[$k] = $v; + } + } + + return $this->callFunc('comment-check',$permalink,$type,$author,$email,$url,$content,$info); + } + + public function submit_spam($permalink,$type,$author,$email,$url,$content) + { + $this->callFunc('submit-spam',$permalink,$type,$author,$email,$url,$content); + return true; + } + + public function submit_ham($permalink,$type,$author,$email,$url,$content) + { + $this->callFunc('submit-ham',$permalink,$type,$author,$email,$url,$content); + return true; + } + + protected function callFunc($function,$permalink,$type,$author,$email,$url,$content,$info=array()) + { + $ua = isset($info['HTTP_USER_AGENT']) ? $info['HTTP_USER_AGENT'] : ''; + $referer = isset($info['HTTP_REFERER']) ? $info['HTTP_REFERER'] : ''; + + # Prepare comment data + $data = array( + 'blog' => $this->blog_url, + 'user_ip' => http::realIP(), + 'user_agent' => $ua, + 'referrer' => $referer, + 'permalink' => $permalink, + 'comment_type' => $type, + 'comment_author' => $author, + 'comment_author_email' => $email, + 'comment_author_url' => $url, + 'comment_content' => $content + ); + + $data = array_merge($data,$info); + + $this->host = $this->ak_host; + $path = sprintf($this->ak_path,$function); + + if (!$this->post($path,$data,'UTF-8')) { + throw new Exception('HTTP error: '.$this->getError()); + } + + return $this->getContent() == 'true'; + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/_admin.php b/plugins/antispam/_admin.php new file mode 100644 index 0000000..758b0d9 --- /dev/null +++ b/plugins/antispam/_admin.php @@ -0,0 +1,51 @@ +addItem(__('Antispam'),'plugin.php?p=antispam','index.php?pf=antispam/icon.png', + preg_match('/plugin.php\?p=antispam(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id)); + +$core->addBehavior('coreAfterCommentUpdate',array('dcAntispam','trainFilters')); +$core->addBehavior('adminAfterCommentDesc',array('dcAntispam','statusMessage')); +$core->addBehavior('adminDashboardIcons',array('dcAntispam','dashboardIcon')); + +if (!DC_ANTISPAM_CONF_SUPER || $core->auth->isSuperAdmin()) { + $core->addBehavior('adminBlogPreferencesForm',array('antispamBehaviors','adminBlogPreferencesForm')); + $core->addBehavior('adminBeforeBlogSettingsUpdate',array('antispamBehaviors','adminBeforeBlogSettingsUpdate')); +} + +class antispamBehaviors +{ + public static function adminBlogPreferencesForm(&$core,&$settings) + { + echo + '
    Antispam'. + '

    '. + '
    '; + } + + public static function adminBeforeBlogSettingsUpdate(&$settings) + { + $settings->setNameSpace('antispam'); + $settings->put('antispam_moderation_ttl',(integer)$_POST['antispam_moderation_ttl']); + $settings->setNameSpace('system'); + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/_define.php b/plugins/antispam/_define.php new file mode 100644 index 0000000..39d91f8 --- /dev/null +++ b/plugins/antispam/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "Antispam", + /* Description*/ "Generic antispam plugin for Dotclear", + /* Author */ "Alain Vagner", + /* Version */ '1.2', + /* Permissions */ 'usage,contentadmin', + /* Priority */ 10 +); +?> \ No newline at end of file diff --git a/plugins/antispam/_install.php b/plugins/antispam/_install.php new file mode 100644 index 0000000..6ffb734 --- /dev/null +++ b/plugins/antispam/_install.php @@ -0,0 +1,52 @@ +plugins->moduleInfo('antispam','version'); +if (version_compare($core->getVersion('antispam'),$version,'>=')) { + return; +} + +/* Database schema +-------------------------------------------------------- */ +$s = new dbStruct($core->con,$core->prefix); + +$s->spamrule + ->rule_id ('bigint', 0, false) + ->blog_id ('varchar', 32, true) + ->rule_type ('varchar', 16, false, "'word'") + ->rule_content ('varchar', 128, false) + + ->primary('pk_spamrule','rule_id') + ; + +$s->spamrule->index('idx_spamrule_blog_id','btree','blog_id'); +$s->spamrule->reference('fk_spamrule_blog','blog_id','blog','blog_id','cascade','cascade'); + +if ($s->driver() == 'pgsql') { + $s->spamrule->index('idx_spamrule_blog_id_null','btree','(blog_id IS NULL)'); +} + +# Schema installation +$si = new dbStruct($core->con,$core->prefix); +$changes = $si->synchronize($s); + +# Creating default wordslist +if ($core->getVersion('antispam') === null) { + $_o = new dcFilterWords($core); + $_o->defaultWordsList(); + unset($_o); +} + +$core->setVersion('antispam',$version); +return true; +?> \ No newline at end of file diff --git a/plugins/antispam/_prepend.php b/plugins/antispam/_prepend.php new file mode 100644 index 0000000..1f16bbd --- /dev/null +++ b/plugins/antispam/_prepend.php @@ -0,0 +1,30 @@ +spamfilters = array('dcFilterIP','dcFilterWords','dcFilterIpLookup','dcFilterLinksLookup'); + +$core->url->register('spamfeed','spamfeed','^spamfeed/(.+)$',array('dcAntispamURL','spamFeed')); +$core->url->register('hamfeed','hamfeed','^hamfeed/(.+)$',array('dcAntispamURL','hamFeed')); +?> \ No newline at end of file diff --git a/plugins/antispam/_public.php b/plugins/antispam/_public.php new file mode 100644 index 0000000..2d7cf87 --- /dev/null +++ b/plugins/antispam/_public.php @@ -0,0 +1,17 @@ +addBehavior('publicBeforeCommentCreate',array('dcAntispam','isSpam')); +$core->addBehavior('publicBeforeTrackbackCreate',array('dcAntispam','isSpam')); +$core->addBehavior('publicBeforeDocument',array('dcAntispam','purgeOldSpam')); +?> \ No newline at end of file diff --git a/plugins/antispam/antispam.js b/plugins/antispam/antispam.js new file mode 100644 index 0000000..8f243e9 --- /dev/null +++ b/plugins/antispam/antispam.js @@ -0,0 +1,3 @@ + +var dragsort=ToolMan.dragsort();$(function(){$("#filters-list").each(function(){dragsort.makeTableSortable(this,dotclear.sortable.setHandle,dotclear.sortable.saveOrder);});});dotclear.sortable={setHandle:function(item){var handle=$(item).find('td.handle').get(0);while(handle.firstChild){handle.removeChild(handle.firstChild);} +item.toolManDragGroup.setHandle(handle);$(handle).addClass('handler');},saveOrder:function(item){var group=item.toolManDragGroup;var order=$('#filters_order').get(0);group.register('dragend',function(){order.value='';items=item.parentNode.getElementsByTagName('tr');for(var i=0;icon =& $core->con; + $this->table = $core->prefix.'spamrule'; + } + + protected function setInfo() + { + $this->description = __('IP Blacklist / Whitelist Filter'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with rule %2$s.'),$this->guiLink(),$status); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (!$ip) { + return; + } + + # White list check + if ($this->checkIP($ip,'white') !== false) { + return false; + } + + # Black list check + if (($s = $this->checkIP($ip,'black')) !== false) { + $status = $s; + return true; + } + } + + public function gui($url) + { + global $default_tab; + $core =& $this->core; + + # Set current type and tab + $ip_type = 'black'; + if (!empty($_REQUEST['ip_type']) && $_REQUEST['ip_type'] == 'white') { + $ip_type = 'white'; + } + $default_tab = 'tab_'.$ip_type; + + # Add IP to list + if (!empty($_POST['addip'])) + { + try + { + $global = !empty($_POST['globalip']) && $core->auth->isSuperAdmin(); + + $this->addIP($ip_type,$_POST['addip'],$global); + http::redirect($url.'&added=1&ip_type='.$ip_type); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + + # Remove IP from list + if (!empty($_POST['delip']) && is_array($_POST['delip'])) + { + try { + $this->removeRule($_POST['delip']); + http::redirect($url.'&removed=1&ip_type='.$ip_type); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + /* DISPLAY + ---------------------------------------------- */ + $res = ''; + + if (!empty($_GET['added'])) { + $res .= '

    '.__('IP address has been successfully added.').'

    '; + } + if (!empty($_GET['removed'])) { + $res .= '

    '.__('IP addresses have been successfully removed.').'

    '; + } + + $res .= + $this->displayForms($url,'black',__('Blacklist')). + $this->displayForms($url,'white',__('Whitelist')); + + return $res; + } + + private function displayForms($url,$type,$title) + { + $core =& $this->core; + + $res = + '
    '. + + '
    '. + '
    '.__('Add an IP address').'

    '. + form::hidden(array('ip_type'),$type). + form::field(array('addip'),18,255).' '; + + if ($core->auth->isSuperAdmin()) { + $res .= ' '; + } + + $res .= + $core->formNonce(). + '

    '. + '
    '; + + $rs = $this->getRules($type); + + if ($rs->isEmpty()) + { + $res .= '

    '.__('No IP address in list.').'

    '; + } + else + { + $res .= + '
    '. + '
    ' . __('IP list') . ''. + '
    '; + + while ($rs->fetch()) + { + $bits = explode(':',$rs->rule_content); + $pattern = $bits[0]; + $ip = $bits[1]; + $bitmask = $bits[2]; + + $disabled_ip = false; + $p_style = $this->style_p; + if (!$rs->blog_id) { + $disabled_ip = !$core->auth->isSuperAdmin(); + $p_style .= $this->style_global; + } + + $res .= + '

    '; + } + $res .= + '
    '. + '

    '. + $core->formNonce(). + form::hidden(array('ip_type'),$type). + '

    '. + '
    '; + } + + $res .= '
    '; + + return $res; + } + + private function ipmask($pattern,&$ip,&$mask) + { + $bits = explode('/',$pattern); + + # Set IP + $bits[0] .= str_repeat(".0", 3 - substr_count($bits[0], ".")); + $ip = ip2long($bits[0]); + + if (!$ip || $ip == -1) { + throw new Exception('Invalid IP address'); + } + + # Set mask + if (!isset($bits[1])) { + $mask = -1; + } elseif (strpos($bits[1],'.')) { + $mask = ip2long($bits[1]); + if (!$mask) { + $mask = -1; + } + } else { + $mask = ~((1 << (32 - $bits[1])) - 1); + } + } + + private function addIP($type,$pattern,$global) + { + $this->ipmask($pattern,$ip,$mask); + $pattern = long2ip($ip).($mask != -1 ? '/'.long2ip($mask) : ''); + $content = $pattern.':'.$ip.':'.$mask; + + $old = $this->getRuleCIDR($type,$global,$ip,$mask); + $cur = $this->con->openCursor($this->table); + + if ($old->isEmpty()) + { + $id = $this->con->select('SELECT MAX(rule_id) FROM '.$this->table)->f(0) + 1; + + $cur->rule_id = $id; + $cur->rule_type = (string) $type; + $cur->rule_content = (string) $content; + + if ($global && $this->core->auth->isSuperAdmin()) { + $cur->blog_id = null; + } else { + $cur->blog_id = $this->core->blog->id; + } + + $cur->insert(); + } + else + { + $cur->rule_type = (string) $type; + $cur->rule_content = (string) $content; + $cur->update('WHERE rule_id = '.(integer) $old->rule_id); + } + } + + private function getRules($type='all') + { + $strReq = + 'SELECT rule_id, rule_type, blog_id, rule_content '. + 'FROM '.$this->table.' '. + "WHERE rule_type = '".$this->con->escape($type)."' ". + "AND (blog_id = '".$this->core->blog->id."' OR blog_id IS NULL) ". + 'ORDER BY blog_id ASC, rule_content ASC '; + + return $this->con->select($strReq); + } + + private function getRuleCIDR($type,$global,$ip,$mask) + { + $strReq = + 'SELECT * FROM '.$this->table.' '. + "WHERE rule_type = '".$this->con->escape($type)."' ". + "AND rule_content LIKE '%:".(integer) $ip.":".(integer) $mask."' ". + 'AND blog_id '.($global ? 'IS NULL ' : "= '".$this->core->blog->id."' "); + + return $this->con->select($strReq); + } + + private function checkIP($cip,$type) + { + $core =& $this->core; + + $strReq = + 'SELECT DISTINCT(rule_content) '. + 'FROM '.$this->table.' '. + "WHERE rule_type = '".$this->con->escape($type)."' ". + "AND (blog_id = '".$this->core->blog->id."' OR blog_id IS NULL) ". + 'ORDER BY rule_content ASC '; + + $rs = $this->con->select($strReq); + while ($rs->fetch()) + { + list($pattern,$ip,$mask) = explode(':',$rs->rule_content); + if ((ip2long($cip) & (integer) $mask) == ((integer) $ip & (integer) $mask)) { + return $pattern; + } + } + return false; + } + + private function removeRule($ids) + { + $strReq = 'DELETE FROM '.$this->table.' '; + + if (is_array($ids)) { + foreach ($ids as $i => $v) { + $ids[$i] = (integer) $v; + } + $strReq .= 'WHERE rule_id IN ('.implode(',',$ids).') '; + } else { + $ids = (integer) $ids; + $strReq .= 'WHERE rule_id = '.$ids.' '; + } + + if (!$this->core->auth->isSuperAdmin()) { + $strReq .= "AND blog_id = '".$this->core->blog->id."' "; + } + + $this->con->execute($strReq); + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/filters/class.dc.filter.iplookup.php b/plugins/antispam/filters/class.dc.filter.iplookup.php new file mode 100644 index 0000000..ed81be4 --- /dev/null +++ b/plugins/antispam/filters/class.dc.filter.iplookup.php @@ -0,0 +1,120 @@ +auth->isSuperAdmin()) { + $this->has_gui = false; + } + } + + protected function setInfo() + { + $this->description = __('Checks sender IP address against DNSBL servers'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with server %2$s.'),$this->guiLink(),$status); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (!$ip || long2ip(ip2long($ip)) != $ip) { + return; + } + + $match = array(); + + $bls = $this->getServers(); + $bls = preg_split('/\s*,\s*/',$bls); + + foreach ($bls as $bl) + { + if ($this->dnsblLookup($ip,$bl)) { + $match[] = $bl; + } + } + + if (!empty($match)) { + $status = substr(implode(', ',$match),0,128); + return true; + } + } + + public function gui($url) + { + $bls = $this->getServers(); + + if (isset($_POST['bls'])) + { + try { + $this->core->blog->settings->setNameSpace('antispam'); + $this->core->blog->settings->put('antispam_dnsbls',$_POST['bls'],'string','Antispam DNSBL servers',true,false); + http::redirect($url.'&upd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + /* DISPLAY + ---------------------------------------------- */ + $res = ''; + + $res .= + '
    '. + '
    ' . __('IP Lookup servers') . ''. + '

    '.__('Add here a coma separated list of servers.').'

    '. + '

    '.form::textarea('bls',40,3,html::escapeHTML($bls),'maximal').'

    '. + '

    '. + $this->core->formNonce().'

    '. + '
    '. + '
    '; + + return $res; + } + + private function getServers() + { + $bls = $this->core->blog->settings->antispam_dnsbls; + if ($bls === null) { + $this->core->blog->settings->setNameSpace('antispam'); + $this->core->blog->settings->put('antispam_dnsbls',$this->default_bls,'string','Antispam DNSBL servers',true,false); + return $this->default_bls; + } + + return $bls; + } + + private function dnsblLookup($ip,$bl) + { + $revIp = implode('.',array_reverse(explode('.',$ip))); + + $host = $revIp.'.'.$bl.'.'; + if (gethostbyname($host) != $host) { + return true; + } + + return false; + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/filters/class.dc.filter.linkslookup.php b/plugins/antispam/filters/class.dc.filter.linkslookup.php new file mode 100644 index 0000000..d7e5b7e --- /dev/null +++ b/plugins/antispam/filters/class.dc.filter.linkslookup.php @@ -0,0 +1,74 @@ +description = __('Checks links in comments against surbl.org'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with server %2$s.'),$this->guiLink(),$status); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (!$ip || long2ip(ip2long($ip)) != $ip) { + return; + } + + $urls = $this->getLinks($content); + array_unshift($urls,$site); + + foreach ($urls as $u) + { + $b = parse_url($u); + if (!isset($b['host']) || !$b['host']) { + continue; + } + + $domain = preg_replace('/^(.*\.)([^.]+\.[^.]+)$/','$2',$b['host']); + $host = $domain.'.'.$this->server; + + if (gethostbyname($host) != $host) { + $status = substr($domain,0,128); + return true; + } + } + } + + private function getLinks($text) + { + $res = array(); + + # href attribute on "a" tags + if (preg_match_all('/]+)>/ms', $text, $match, PREG_SET_ORDER)) + { + for ($i = 0; $i \ No newline at end of file diff --git a/plugins/antispam/filters/class.dc.filter.words.php b/plugins/antispam/filters/class.dc.filter.words.php new file mode 100644 index 0000000..ee53f84 --- /dev/null +++ b/plugins/antispam/filters/class.dc.filter.words.php @@ -0,0 +1,357 @@ +con =& $core->con; + $this->table = $core->prefix.'spamrule'; + } + + protected function setInfo() + { + $this->description = __('Words Blacklist'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with word %2$s.'),$this->guiLink(),''.$status.''); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + $str = $author.' '.$email.' '.$site.' '.$content; + + $rs = $this->getRules(); + + while ($rs->fetch()) + { + $word = $rs->rule_content; + + if (substr($word,0,1) == '/' && substr($word,-1,1) == '/') { + $reg = substr(substr($word,1),0,-1); + } else { + $reg = preg_quote($word, '/'); + $reg = '(^|\s+|>|<)'.$reg.'(>|<|\s+|\.|$)'; + } + + if (preg_match('/'.$reg.'/msiu',$str)) { + $status = $word; + return true; + } + } + } + + public function gui($url) + { + $core =& $this->core; + + # Create list + if (!empty($_POST['createlist'])) + { + try { + $this->defaultWordsList(); + http::redirect($url.'&list=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Adding a word + if (!empty($_POST['swa'])) + { + $globalsw = !empty($_POST['globalsw']) && $core->auth->isSuperAdmin(); + + try { + $this->addRule($_POST['swa'],$globalsw); + http::redirect($url.'&added=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Removing spamwords + if (!empty($_POST['swd']) && is_array($_POST['swd'])) + { + try { + $this->removeRule($_POST['swd']); + http::redirect($url.'&removed=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + /* DISPLAY + ---------------------------------------------- */ + $res = ''; + + if (!empty($_GET['list'])) { + $res .= '

    '.__('Words have been successfully added.').'

    '; + } + if (!empty($_GET['added'])) { + $res .= '

    '.__('Word has been successfully added.').'

    '; + } + if (!empty($_GET['removed'])) { + $res .= '

    '.__('Words have been successfully removed.').'

    '; + } + + $res .= + '
    '. + '
    '.__('Add a word').''. + '

    '.form::field('swa',20,128).' '; + + if ($core->auth->isSuperAdmin()) { + $res .= ' '; + } + + $res .= + $core->formNonce(). + '

    '. + '
    '. + '
    '; + + $rs = $this->getRules(); + if ($rs->isEmpty()) + { + $res .= '

    '.__('No word in list.').'

    '; + } + else + { + $res .= + '
    '. + '
    ' . __('List') . ''. + '
    '; + + while ($rs->fetch()) + { + $disabled_word = false; + $p_style = $this->style_p; + if (!$rs->blog_id) { + $disabled_word = !$core->auth->isSuperAdmin(); + $p_style .= $this->style_global; + } + + $res .= + '

    '; + } + + $res .= + '
    '. + '

    '.form::hidden(array('spamwords'),1). + $core->formNonce(). + '

    '. + '
    '; + } + + if ($core->auth->isSuperAdmin()) + { + $res .= + '
    '. + '

    '. + form::hidden(array('spamwords'),1). + form::hidden(array('createlist'),1). + $core->formNonce().'

    '. + '
    '; + } + + return $res; + } + + private function getRules() + { + $strReq = 'SELECT rule_id, blog_id, rule_content '. + 'FROM '.$this->table.' '. + "WHERE rule_type = 'word' ". + "AND ( blog_id = '".$this->con->escape($this->core->blog->id)."' ". + "OR blog_id IS NULL ) ". + 'ORDER BY blog_id ASC, rule_content ASC '; + + return $this->con->select($strReq); + } + + private function addRule($content,$general=false) + { + $strReq = 'SELECT rule_id FROM '.$this->table.' '. + "WHERE rule_type = 'word' ". + "AND rule_content = '".$this->con->escape($content)."' "; + $rs = $this->con->select($strReq); + + if (!$rs->isEmpty()) { + throw new Exception(__('This word exists')); + } + + $rs = $this->con->select('SELECT MAX(rule_id) FROM '.$this->table); + $id = (integer) $rs->f(0) + 1; + + $cur = $this->con->openCursor($this->table); + $cur->rule_id = $id; + $cur->rule_type = 'word'; + $cur->rule_content = (string) $content; + + if ($general && $this->core->auth->isSuperAdmin()) { + $cur->blog_id = null; + } else { + $cur->blog_id = $this->core->blog->id; + } + + $cur->insert(); + } + + private function removeRule($ids) + { + $strReq = 'DELETE FROM '.$this->table.' '; + + if (is_array($ids)) { + foreach ($ids as &$v) { + $v = (integer) $v; + } + $strReq .= 'WHERE rule_id IN ('.implode(',',$ids).') '; + } else { + $ids = (integer) $ids; + $strReq .= 'WHERE rule_id = '.$ids.' '; + } + + if (!$this->core->auth->isSuperAdmin()) { + $strReq .= "AND blog_id = '".$this->con->escape($this->core->blog->id)."' "; + } + + $this->con->execute($strReq); + } + + public function defaultWordsList() + { + $words = array( + '/-credit(\s+|$)/', + '/-digest(\s+|$)/', + '/-loan(\s+|$)/', + '/-online(\s+|$)/', + '4u', + 'adipex', + 'advicer', + 'ambien', + 'baccarat', + 'baccarrat', + 'blackjack', + 'bllogspot', + 'bolobomb', + 'booker', + 'byob', + 'car-rental-e-site', + 'car-rentals-e-site', + 'carisoprodol', + 'cash', + 'casino', + 'casinos', + 'chatroom', + 'cialis', + 'craps', + 'credit-card', + 'credit-report-4u', + 'cwas', + 'cyclen', + 'cyclobenzaprine', + 'dating-e-site', + 'day-trading', + 'debt', + 'digest-', + 'discount', + 'discreetordering', + 'duty-free', + 'dutyfree', + 'estate', + 'favourits', + 'fioricet', + 'flowers-leading-site', + 'freenet', + 'freenet-shopping', + 'gambling', + 'gamias', + 'health-insurancedeals-4u', + 'holdem', + 'holdempoker', + 'holdemsoftware', + 'holdemtexasturbowilson', + 'hotel-dealse-site', + 'hotele-site', + 'hotelse-site', + 'incest', + 'insurance-quotesdeals-4u', + 'insurancedeals-4u', + 'jrcreations', + 'levitra', + 'macinstruct', + 'mortgage', + 'online-gambling', + 'onlinegambling-4u', + 'ottawavalleyag', + 'ownsthis', + 'palm-texas-holdem-game', + 'paxil', + 'pharmacy', + 'phentermine', + 'pills', + 'poker', + 'poker-chip', + 'poze', + 'prescription', + 'rarehomes', + 'refund', + 'rental-car-e-site', + 'roulette', + 'shemale', + 'slot', + 'slot-machine', + 'soma', + 'taboo', + 'tamiflu', + 'texas-holdem', + 'thorcarlson', + 'top-e-site', + 'top-site', + 'tramadol', + 'trim-spa', + 'ultram', + 'v1h', + 'vacuum', + 'valeofglamorganconservatives', + 'viagra', + 'vicodin', + 'vioxx', + 'xanax', + 'zolus' + ); + + foreach ($words as $w) { + try { + $this->addRule($w,true); + } catch (Exception $e) {} + } + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/icon.png b/plugins/antispam/icon.png new file mode 100644 index 0000000..2011311 Binary files /dev/null and b/plugins/antispam/icon.png differ diff --git a/plugins/antispam/inc/class.dc.spamfilter.php b/plugins/antispam/inc/class.dc.spamfilter.php new file mode 100644 index 0000000..6e657d5 --- /dev/null +++ b/plugins/antispam/inc/class.dc.spamfilter.php @@ -0,0 +1,161 @@ +dcCore Dotclear core object + */ + public function __construct(&$core) + { + $this->core =& $core; + $this->setInfo(); + + if (!$this->name) { + $this->name = get_class($this); + } + + $this->gui_url = 'plugin.php?p=antispam&f='.get_class($this); + } + + /** + This method is called by the constructor and allows you to change some + object properties without overloading object constructor. + */ + protected function setInfo() + { + $this->description = __('No description'); + } + + /** + This method should return if a comment is a spam or not. If it returns true + or false, execution of next filters will be stoped. If should return nothing + to let next filters apply. + + Your filter should also fill $status variable with its own information if + comment is a spam. + + @param type string Comment type (comment or trackback) + @param author string Comment author + @param email string Comment author email + @param site string Comment author website + @param ip string Comment author IP address + @param content string Comment content + @param post_id integer Comment post_id + @param[out] status integer Comment status + @return boolean + */ + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + } + + /** + This method is called when a non-spam (ham) comment becomes spam or when a + spam becomes a ham. + + @param type string Comment type (comment or trackback) + @param filter string Filter name + @param author string Comment author + @param email string Comment author email + @param site string Comment author website + @param ip string Comment author IP address + @param content string Comment content + @param post_url string Post URL + @param rs record Comment record + @return boolean + */ + public function trainFilter($status,$filter,$type,$author,$email,$site,$ip,$content,$rs) + { + } + + /** + This method returns filter status message. You can overload this method to + return a custom message. Message is shown in comment details and in + comments list. + + @param status string Filter status. + @param comment_id record Comment record + @return string + */ + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s (%2$s)'),$this->guiLink(),$status); + } + + /** + This method is called when you enter filter configuration. Your class should + have $has_gui property set to "true" to enable GUI. + + In this method you should put everything related to filter configuration. + $url variable is the URL of GUI unescaped. + + @param url string GUI URL. + */ + public function gui($url) + { + } + + public function hasGUI() + { + if (!$this->core->auth->check('admin',$this->core->blog->id)) { + return false; + } + + if (!$this->has_gui) { + return false; + } + + return true; + } + + public function guiURL() + { + if (!$this->hasGui()) { + return false; + } + + return $this->gui_url; + } + + /** + Returns a link to filter GUI if exists or only filter name if has_gui + property is false. + + @return string + */ + public function guiLink() + { + if (($url = $this->guiURL()) !== false) { + $url = html::escapeHTML($url); + $link = '
    %1$s'; + } else { + $link = '%1$s'; + } + + return sprintf($link,$this->name,$url); + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/inc/class.dc.spamfilters.php b/plugins/antispam/inc/class.dc.spamfilters.php new file mode 100644 index 0000000..e921d7f --- /dev/null +++ b/plugins/antispam/inc/class.dc.spamfilters.php @@ -0,0 +1,164 @@ +core =& $core; + } + + public function init($filters) + { + foreach ($filters as $f) + { + if (!class_exists($f)) { + continue; + } + + $r = new ReflectionClass($f); + $p = $r->getParentClass(); + + if (!$p || $p->name != 'dcSpamFilter') { + continue; + } + + $this->filters[$f] = new $f($this->core); + } + + $this->setFilterOpts(); + if (!empty($this->filters_opt)) { + uasort($this->filters,array($this,'orderCallBack')); + } + } + + public function getFilters() + { + return $this->filters; + } + + public function isSpam(&$cur) + { + foreach ($this->filters as $fid => $f) + { + if (!$f->active) { + continue; + } + + $type = $cur->comment_trackback ? 'trackback' : 'comment'; + $author = $cur->comment_author; + $email = $cur->comment_email; + $site = $cur->comment_site; + $ip = $cur->comment_ip; + $content = $cur->comment_content; + $post_id = $cur->post_id; + + $is_spam = $f->isSpam($type,$author,$email,$site,$ip,$content,$post_id,$status); + + if ($is_spam === true) { + if ($f->auto_delete) { + $cur->clean(); + } else { + $cur->comment_status = -2; + $cur->comment_spam_status = $status; + $cur->comment_spam_filter = $fid; + } + return true; + } elseif ($is_spam === false) { + return false; + } + } + + return false; + } + + public function trainFilters(&$rs,$status,$filter_name) + { + foreach ($this->filters as $fid => $f) + { + if (!$f->active) { + continue; + } + + $type = $rs->comment_trackback ? 'trackback' : 'comment'; + $author = $rs->comment_author; + $email = $rs->comment_email; + $site = $rs->comment_site; + $ip = $rs->comment_ip; + $content = $rs->comment_content; + + $f->trainFilter($status,$filter_name,$type,$author,$email,$site,$ip,$content,$rs); + } + } + + public function statusMessage(&$rs,$filter_name) + { + $f = isset($this->filters[$filter_name]) ? $this->filters[$filter_name] : null; + + if ($f === null) + { + return __('Unknown filter.'); + } + else + { + $status = $rs->exists('comment_spam_status') ? $rs->comment_spam_status : null; + + return $f->getStatusMessage($status,$rs->comment_id); + } + } + + public function saveFilterOpts($opts,$global=false) + { + $this->core->blog->settings->setNameSpace('antispam'); + if ($global) { + $this->core->blog->settings->drop('antispam_filters'); + } + $this->core->blog->settings->put('antispam_filters',serialize($opts),'string','Antispam Filters',true,$global); + } + + private function setFilterOpts() + { + if ($this->core->blog->settings->antispam_filters !== null) { + $this->filters_opt = @unserialize($this->core->blog->settings->antispam_filters); + } + + # Create default options if needed + if (!is_array($this->filters_opt)) { + $this->saveFilterOpts(array(),true); + $this->filters_opt = array(); + } + + foreach ($this->filters_opt as $k => $o) + { + if (isset($this->filters[$k]) && is_array($o)) { + $this->filters[$k]->active = isset($o[0])?$o[0]:false; + $this->filters[$k]->order = isset($o[1])?$o[1]:0; + $this->filters[$k]->auto_delete = isset($o[2])?$o[2]:false; + } + } + } + + private function orderCallBack($a,$b) + { + if ($a->order == $b->order) { + return 0; + } + + return $a->order > $b->order ? 1 : -1; + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/inc/lib.dc.antispam.php b/plugins/antispam/inc/lib.dc.antispam.php new file mode 100644 index 0000000..04adfb4 --- /dev/null +++ b/plugins/antispam/inc/lib.dc.antispam.php @@ -0,0 +1,195 @@ +spamfilters)) { + return; + } + + self::$filters = new dcSpamFilters($core); + self::$filters->init($core->spamfilters); + } + + public static function isSpam(&$cur) + { + self::initFilters(); + self::$filters->isSpam($cur); + } + + public static function trainFilters(&$blog,&$cur,&$rs) + { + $status = null; + # From ham to spam + if ($rs->comment_status != -2 && $cur->comment_status == -2) { + $status = 'spam'; + } + + # From spam to ham + if ($rs->comment_status == -2 && $cur->comment_status == 1) { + $status = 'ham'; + } + + # the status of this comment has changed + if ($status) + { + $filter_name = $rs->exists('comment_spam_filter') ? $rs->comment_spam_filter : null; + + self::initFilters(); + self::$filters->trainFilters($rs,$status,$filter_name); + } + } + + public static function statusMessage(&$rs) + { + if ($rs->exists('comment_status') && $rs->comment_status == -2) + { + $filter_name = $rs->exists('comment_spam_filter') ? $rs->comment_spam_filter : null; + + self::initFilters(); + + return + '

    '.__('This comment is a spam:').' '. + self::$filters->statusMessage($rs,$filter_name).'

    '; + } + } + + public static function dashboardIcon(&$core, &$icons) + { + if (($count = self::countSpam($core)) > 0) { + $str = ($count > 1) ? __('(including %d spam comments)') : __('(including %d spam comment)'); + $icons['comments'][0] .= '
    '.sprintf($str,$count); + } + } + + public static function countSpam(&$core) + { + return $core->blog->getComments(array('comment_status'=>-2),true)->f(0); + } + + public static function countPublishedComments(&$core) + { + return $core->blog->getComments(array('comment_status'=>1),true)->f(0); + } + + public static function delAllSpam(&$core, $beforeDate = null) + { + $strReq = + 'SELECT comment_id '. + 'FROM '.$core->prefix.'comment C '. + 'JOIN '.$core->prefix.'post P ON P.post_id = C.post_id '. + "WHERE blog_id = '".$core->con->escape($core->blog->id)."' ". + 'AND comment_status = -2 '; + if ($beforeDate) { + $strReq .= 'AND comment_dt < \''.$beforeDate.'\' '; + } + + $rs = $core->con->select($strReq); + $r = array(); + while ($rs->fetch()) { + $r[] = (integer) $rs->comment_id; + } + + if (empty($r)) { + return; + } + + $strReq = + 'DELETE FROM '.$core->prefix.'comment '. + 'WHERE comment_id '.$core->con->in($r).' '; + + $core->con->execute($strReq); + } + + public static function getUserCode($core) + { + $code = + pack('a32',$core->auth->userID()). + pack('H*',crypt::hmac(DC_MASTER_KEY,$core->auth->getInfo('user_pwd'))); + return bin2hex($code); + } + + public static function checkUserCode($core,$code) + { + $code = pack('H*',$code); + + $user_id = trim(@pack('a32',substr($code,0,32))); + $pwd = @unpack('H40hex',substr($code,32,40)); + + if ($user_id === false || $pwd === false) { + return false; + } + + $pwd = $pwd['hex']; + + $strReq = 'SELECT user_id, user_pwd '. + 'FROM '.$core->prefix.'user '. + "WHERE user_id = '".$core->con->escape($user_id)."' "; + + $rs = $core->con->select($strReq); + + if ($rs->isEmpty()) { + return false; + } + + if (crypt::hmac(DC_MASTER_KEY,$rs->user_pwd) != $pwd) { + return false; + } + + return $rs->user_id; + } + + public static function purgeOldSpam(&$core) + { + $defaultDateLastPurge = time(); + $defaultModerationTTL = '7'; + $init = false; + + // settings + $core->blog->settings->setNameSpace('antispam'); + + $dateLastPurge = $core->blog->settings->antispam_date_last_purge; + if ($dateLastPurge === null) { + $init = true; + $core->blog->settings->put('antispam_date_last_purge',$defaultDateLastPurge,'integer','Antispam Date Last Purge (unix timestamp)',true,false); + $dateLastPurge = $defaultDateLastPurge; + } + $moderationTTL = $core->blog->settings->antispam_moderation_ttl; + if ($moderationTTL === null) { + $core->blog->settings->put('antispam_moderation_ttl',$defaultModerationTTL,'integer','Antispam Moderation TTL (days)',true,false); + $moderationTTL = $defaultModerationTTL; + } + + if ($moderationTTL < 0) { + // disabled + return; + } + + // we call the purge every day + if ((time()-$dateLastPurge) > (86400)) { + // update dateLastPurge + if (!$init) { + $core->blog->settings->put('antispam_date_last_purge',time(),null,null,true,false); + } + $date = date('Y-m-d H:i:s', time() - $moderationTTL*86400); + dcAntispam::delAllSpam($core, $date); + } + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/inc/lib.dc.antispam.url.php b/plugins/antispam/inc/lib.dc.antispam.url.php new file mode 100644 index 0000000..1c44e21 --- /dev/null +++ b/plugins/antispam/inc/lib.dc.antispam.url.php @@ -0,0 +1,99 @@ +auth->checkUser($user_id,null,null); + + header('Content-Type: application/xml; charset=UTF-8'); + + $title = $core->blog->name.' - '.__('Spam moderation'). ' - '; + $params = array(); + $end_url = ''; + if ($type == 'spam') { + $title .= __('Spam'); + $params['comment_status'] = -2; + $end_url = '?status=-2'; + } else { + $title .= __('Ham'); + $params['sql'] = ' AND comment_status IN (1,-1) '; + } + + echo + ''."\n". + ''."\n". + ''."\n". + ''.html::escapeHTML($title).''."\n". + ''.(DC_ADMIN_URL ? DC_ADMIN_URL.'comments.php'.$end_url : 'about:blank').''."\n". + ''."\n"; + + $rs = $core->blog->getComments($params); + $maxitems = 20; + $nbitems = 0; + + while ($rs->fetch() && ($nbitems < $maxitems)) + { + $nbitems++; + $uri = DC_ADMIN_URL ? DC_ADMIN_URL.'comment.php?id='.$rs->comment_id : 'about:blank'; + $author = $rs->comment_author; + $title = $rs->post_title.' - '.$author; + if ($type == 'spam') { + $title .= '('.$rs->comment_spam_filter.')'; + } + $id = $rs->getFeedID(); + + $content = '

    IP: '.$rs->comment_ip; + + if (trim($rs->comment_site)) { + $content .= '
    URL:
    '.$rs->comment_site.''; + } + $content .= "


    \n"; + $content .= $rs->comment_content; + + echo + ''."\n". + ' '.html::escapeHTML($title).''."\n". + ' '.$uri.''."\n". + ' '.$id.''."\n". + ' '.$rs->getRFC822Date().''."\n". + ' '.html::escapeHTML($author).''."\n". + ' '.html::escapeHTML($content).''."\n". + ''; + } + + echo "\n"; + } +} +?> \ No newline at end of file diff --git a/plugins/antispam/index.php b/plugins/antispam/index.php new file mode 100644 index 0000000..204ba5f --- /dev/null +++ b/plugins/antispam/index.php @@ -0,0 +1,222 @@ +getFilters(); + +$page_name = __('Antispam'); +$filter_gui = false; +$default_tab = null; + +try +{ + # Show filter configuration GUI + if (!empty($_GET['f'])) + { + if (!isset($filters[$_GET['f']])) { + throw new Exception(__('Filter does not exist.')); + } + + if (!$filters[$_GET['f']]->hasGUI()) { + throw new Exception(__('Filter has no user interface.')); + } + + $filter = $filters[$_GET['f']]; + $filter_gui = $filter->gui($filter->guiURL()); + } + + # Remove all spam + if (!empty($_POST['delete_all'])) + { + dcAntispam::delAllSpam($core); + http::redirect($p_url.'&del=1'); + } + + # Update filters + if (isset($_POST['filters_upd'])) + { + $filters_opt = array(); + $i = 0; + foreach ($filters as $fid => $f) { + $filters_opt[$fid] = array(false,$i); + $i++; + } + + # Enable active filters + if (isset($_POST['filters_active']) && is_array($_POST['filters_active'])) { + foreach ($_POST['filters_active'] as $v) { + $filters_opt[$v][0] = true; + } + } + + # Order filters + if (!empty($_POST['f_order']) && empty($_POST['filters_order'])) + { + $order = $_POST['f_order']; + asort($order); + $order = array_keys($order); + } + elseif (!empty($_POST['filters_order'])) + { + $order = explode(',',trim($_POST['filters_order'],',')); + } + + if (isset($order)) { + foreach ($order as $i => $f) { + $filters_opt[$f][1] = $i; + } + } + + # Set auto delete flag + if (isset($_POST['filters_auto_del']) && is_array($_POST['filters_auto_del'])) { + foreach ($_POST['filters_auto_del'] as $v) { + $filters_opt[$v][2] = true; + } + } + + dcAntispam::$filters->saveFilterOpts($filters_opt); + http::redirect($p_url.'&upd=1'); + } +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} +?> + + + <?php echo $page_name; ?> + + + + +'.html::escapeHTML($core->blog->name).' › '.$page_name.''; + +if ($filter_gui !== false) +{ + echo '

    '.__('Return to filters').'

    '; + printf('

    '.__('%s configuration').'

    ',$filter->name); + + echo $filter_gui; +} +else +{ + # Information + $spam_count = dcAntispam::countSpam($core); + $published_count = dcAntispam::countPublishedComments($core); + $moderationTTL = $core->blog->settings->antispam_moderation_ttl; + + echo + '
    '. + '
    '.__('Information').''; + + if (!empty($_GET['del'])) { + echo '

    '.__('Spam comments have been successfully deleted.').'

    '; + } + + echo + ''; + + if ($spam_count > 0) + { + echo + '

    '.$core->formNonce(). + '

    '; + } + if ($moderationTTL != null && $moderationTTL >=0) { + echo '

    '.sprintf(__('All spam comments older than %s day(s) will be automatically deleted.'), $moderationTTL).'

    '; + } + echo '
    '; + + + # Filters + echo + '
    '. + '
    '.__('Available spam filters').''; + + if (!empty($_GET['upd'])) { + echo '

    '.__('Filters configuration has been successfully saved.').'

    '; + } + + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + $i = 0; + foreach ($filters as $fid => $f) + { + $gui_link = ' '; + if ($f->hasGUI()) { + $gui_link = + ''. + ''.__('Filter configuration').''; + } + + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + $i++; + } + echo + '
    '.__('Order').''.__('Active').''.__('Auto Del.').''.__('Filter name').''.__('Description').'
    '.form::field(array('f_order['.$fid.']'),2,5,(string) $i).''.form::checkbox(array('filters_active[]'),$fid,$f->active).''.form::checkbox(array('filters_auto_del[]'),$fid,$f->auto_delete).''.$f->name.''.$f->description.''.$gui_link.'
    '. + '

    '.form::hidden('filters_order',''). + $core->formNonce(). + '

    '. + '
    '; + + + # Syndication + if (DC_ADMIN_URL) + { + $ham_feed = $core->blog->url.$core->url->getBase('hamfeed').'/'.$code = dcAntispam::getUserCode($core); + $spam_feed = $core->blog->url.$core->url->getBase('spamfeed').'/'.$code = dcAntispam::getUserCode($core); + + echo + '
    '.__('Syndication').''. + ''. + '
    '; + } +} +?> + + + \ No newline at end of file diff --git a/plugins/antispam/style.css b/plugins/antispam/style.css new file mode 100644 index 0000000..c93cf1a --- /dev/null +++ b/plugins/antispam/style.css @@ -0,0 +1,23 @@ +ul.spaminfo { + display: block; + list-style: none; + margin: 1em 0; + padding: 0; +} +ul.spaminfo li { + margin: 0.5em 0; + padding-left: 16px; + /*background: url(index.php?pf=antispam/feed.png) no-repeat center left;*/ + background-color: transparent; + background-position: center left; + background-repeat: no-repeat; +} +ul.spaminfo .spamcount { + background-image: url(images/junk.png); +} +ul.spaminfo .hamcount { + background-image: url(images/check-on.png); +} +ul.spaminfo .feed { + background-image: url(index.php?pf=antispam/feed.png); +} \ No newline at end of file diff --git a/plugins/blogroll/_admin.php b/plugins/blogroll/_admin.php new file mode 100644 index 0000000..4919910 --- /dev/null +++ b/plugins/blogroll/_admin.php @@ -0,0 +1,27 @@ +addBehavior('adminDashboardIcons','blogroll_dashboard'); +function blogroll_dashboard(&$core,&$icons) +{ + $icons['blogroll'] = new ArrayObject(array(__('Blogroll'),'plugin.php?p=blogroll','index.php?pf=blogroll/icon.png')); +} + +$_menu['Plugins']->addItem(__('Blogroll'),'plugin.php?p=blogroll','index.php?pf=blogroll/icon-small.png', + preg_match('/plugin.php\?p=blogroll(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + +$core->auth->setPermissionType('blogroll',__('manage blogroll')); + +require dirname(__FILE__).'/_widgets.php'; +?> \ No newline at end of file diff --git a/plugins/blogroll/_define.php b/plugins/blogroll/_define.php new file mode 100644 index 0000000..977ea67 --- /dev/null +++ b/plugins/blogroll/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "Blogroll", + /* Description*/ "Manage your blogroll", + /* Author */ "Olivier Meunier", + /* Version */ '1.1', + /* Permissions */ 'blogroll' +); +?> \ No newline at end of file diff --git a/plugins/blogroll/_install.php b/plugins/blogroll/_install.php new file mode 100644 index 0000000..c716698 --- /dev/null +++ b/plugins/blogroll/_install.php @@ -0,0 +1,46 @@ +plugins->moduleInfo('blogroll','version'); + +if (version_compare($core->getVersion('blogroll'),$version,'>=')) { + return; +} + +/* Database schema +-------------------------------------------------------- */ +$s = new dbStruct($core->con,$core->prefix); + +$s->link + ->link_id ('bigint', 0, false) + ->blog_id ('varchar', 32, false) + ->link_href ('varchar', 255, false) + ->link_title ('varchar', 255, false) + ->link_desc ('varchar', 255, true) + ->link_lang ('varchar', 5, true) + ->link_xfn ('varchar', 255, true) + ->link_position ('integer', 0, false, 0) + + ->primary('pk_link','link_id') + ; + +$s->link->index('idx_link_blog_id','btree','blog_id'); +$s->link->reference('fk_link_blog','blog_id','blog','blog_id','cascade','cascade'); + +# Schema installation +$si = new dbStruct($core->con,$core->prefix); +$changes = $si->synchronize($s); + +$core->setVersion('blogroll',$version); +return true; +?> \ No newline at end of file diff --git a/plugins/blogroll/_prepend.php b/plugins/blogroll/_prepend.php new file mode 100644 index 0000000..53e3785 --- /dev/null +++ b/plugins/blogroll/_prepend.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/plugins/blogroll/_public.php b/plugins/blogroll/_public.php new file mode 100644 index 0000000..52c26ad --- /dev/null +++ b/plugins/blogroll/_public.php @@ -0,0 +1,229 @@ +tpl->addValue('Blogroll',array('tplBlogroll','blogroll')); +$core->tpl->addValue('BlogrollXbelLink',array('tplBlogroll','blogrollXbelLink')); + +$core->url->register('xbel','xbel','^xbel(?:/?)$',array('urlBlogroll','xbel')); + +class tplBlogroll +{ + public static function blogroll($attr) + { + $category='

    %s

    '; + $block='
      %s
    '; + $item='%1$s'; + + if (isset($attr['category'])) { + $category = addslashes($attr['category']); + } + + if (isset($attr['block'])) { + $block = addslashes($attr['block']); + } + + if (isset($attr['item'])) { + $item = addslashes($attr['item']); + } + + $only_cat = 'null'; + if (!empty($attr['only_category'])) { + $only_cat = "'".addslashes($attr['only_category'])."'"; + } + + return + ''; + } + + public static function blogrollXbelLink($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'blog->url.$core->url->getBase("xbel")').'; ?>'; + } + + public static function getList($cat_title='

    %s

    ',$block='
      %s
    ',$item='
  • %s
  • ',$category=null) + { + $blogroll = new dcBlogroll($GLOBALS['core']->blog); + + try { + $links = $blogroll->getLinks(); + } catch (Exception $e) { + return false; + } + + $res = ''; + + $hierarchy = $blogroll->getLinksHierarchy($links); + + if ($category) { + if (!isset($hierarchy[$category])) { + return ''; + } + $hierarchy = array($hierarchy[$category]); + } + + foreach ($hierarchy as $k => $v) + { + if ($k != '') { + $res .= sprintf($cat_title,html::escapeHTML($k))."\n"; + } + + $res .= self::getLinksList($v,$block,$item); + } + + return $res; + } + + private static function getLinksList($links,$block='
      %s
    ',$item='%1$s') + { + $list = ''; + + # Find current link item if any + $current = -1; + $current_size = 0; + $self_uri = http::getSelfURI(); + + foreach ($links as $k => $v) + { + if (!preg_match('$^([a-z][a-z0-9.+-]+://)$',$v['link_href'])) { + $url = http::concatURL($self_uri,$v['link_href']); + if (strlen($url) > $current_size && preg_match('/^'.preg_quote($url,'/').'/',$self_uri)) { + $current = $k; + $current_size = strlen($url); + } + } + } + + foreach ($links as $k => $v) + { + $title = $v['link_title']; + $href = $v['link_href']; + $desc = $v['link_desc']; + $lang = $v['link_lang']; + $xfn = $v['link_xfn']; + + $link = + ''. + html::escapeHTML($title). + ''; + + $current_class = $current == $k ? ' class="active"' : ''; + + $list .= sprintf($item,$link,$current_class)."\n"; + } + + return sprintf($block,$list)."\n"; + } + + # Widget function + public static function linksWidget(&$w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + return + ''; + } +} + +class urlBlogroll extends dcUrlHandlers +{ + public static function xbel($args) + { + $blogroll = new dcBlogroll($GLOBALS['core']->blog); + + try { + $links = $blogroll->getLinks(); + } catch (Exception $e) { + self::p404(); + return; + } + + if ($args) { + self::p404(); + return; + } + + http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); + + header('Content-Type: text/xml; charset=UTF-8'); + + echo + ''."\n". + ''."\n". + ''."\n". + ''.html::escapeHTML($GLOBALS['core']->blog->name)." blogroll\n"; + + $i = 1; + foreach ($blogroll->getLinksHierarchy($links) as $cat_title => $links) + { + if ($cat_title != '') { + echo + ''."\n". + "".html::escapeHTML($cat_title)."\n"; + } + + foreach ($links as $k => $v) + { + $lang = $v['link_lang'] ? ' xml:lang="'.$v['link_lang'].'"' : ''; + + echo + ''."\n". + ''.html::escapeHTML($v['link_title'])."\n"; + + if ($v['link_desc']) { + echo ''.html::escapeHTML($v['link_desc'])."\n"; + } + + if ($v['link_xfn']) { + echo + "\n". + ''.$v['link_xfn']."\n". + "\n"; + } + + echo + "\n"; + } + + if ($cat_title != '') { + echo "\n"; + } + + $i++; + } + + echo + ''; + } +} +?> \ No newline at end of file diff --git a/plugins/blogroll/_widgets.php b/plugins/blogroll/_widgets.php new file mode 100644 index 0000000..f8b8686 --- /dev/null +++ b/plugins/blogroll/_widgets.php @@ -0,0 +1,44 @@ +addBehavior('initWidgets',array('blogrollWidgets','initWidgets')); +$core->addBehavior('initDefaultWidgets',array('blogrollWidgets','initDefaultWidgets')); + +class blogrollWidgets +{ + public static function initWidgets(&$w) + { + $w->create('links',__('Blogroll'),array('tplBlogroll','linksWidget')); + $w->links->setting('title',__('Title:'),__('Links')); + + $br = new dcBlogroll($GLOBALS['core']->blog); + $h = $br->getLinksHierarchy($br->getLinks()); + $h = array_keys($h); + $categories = array(__('All categories') => ''); + foreach ($h as $v) { + if ($v) { + $categories[$v] = $v; + } + } + unset($br,$h); + $w->links->setting('category',__('Category'),'','combo',$categories); + + $w->links->setting('homeonly',__('Home page only'),1,'check'); + } + + public static function initDefaultWidgets(&$w,&$d) + { + $d['extra']->append($w->links); + } +} +?> \ No newline at end of file diff --git a/plugins/blogroll/class.dc.blogroll.php b/plugins/blogroll/class.dc.blogroll.php new file mode 100644 index 0000000..c46ae9c --- /dev/null +++ b/plugins/blogroll/class.dc.blogroll.php @@ -0,0 +1,198 @@ +blog =& $blog; + $this->con =& $blog->con; + $this->table = $this->blog->prefix.'link'; + } + + public function getLinks($params=array()) + { + $strReq = 'SELECT link_id, link_title, link_desc, link_href, '. + 'link_lang, link_xfn, link_position '. + 'FROM '.$this->table.' '. + "WHERE blog_id = '".$this->con->escape($this->blog->id)."' "; + + if (isset($params['link_id'])) { + $strReq .= 'AND link_id = '.(integer) $params['link_id'].' '; + } + + $strReq .= 'ORDER BY link_position '; + + $rs = $this->con->select($strReq); + $rs = $rs->toStatic(); + + $this->setLinksData($rs); + + return $rs; + } + + public function getLink($id) + { + $params['link_id'] = $id; + + $rs = $this->getLinks($params); + + return $rs; + } + + public function addLink($title,$href,$desc='',$lang='', $xfn='') + { + $cur = $this->con->openCursor($this->table); + + $cur->blog_id = (string) $this->blog->id; + $cur->link_title = (string) $title; + $cur->link_href = (string) $href; + $cur->link_desc = (string) $desc; + $cur->link_lang = (string) $lang; + $cur->link_xfn = (string) $xfn; + + if ($cur->link_title == '') { + throw new Exception(__('You must provide a link title')); + } + + if ($cur->link_href == '') { + throw new Exception(__('You must provide a link URL')); + } + + $strReq = 'SELECT MAX(link_id) FROM '.$this->table; + $rs = $this->con->select($strReq); + $cur->link_id = (integer) $rs->f(0) + 1; + + $cur->insert(); + $this->blog->triggerBlog(); + } + + public function updateLink($id,$title,$href,$desc='',$lang='', $xfn='') + { + $cur = $this->con->openCursor($this->table); + + $cur->link_title = (string) $title; + $cur->link_href = (string) $href; + $cur->link_desc = (string) $desc; + $cur->link_lang = (string) $lang; + $cur->link_xfn = (string) $xfn; + + if ($cur->link_title == '') { + throw new Exception(__('You must provide a link title')); + } + + if ($cur->link_href == '') { + throw new Exception(__('You must provide a link URL')); + } + + $cur->update('WHERE link_id = '.(integer) $id. + " AND blog_id = '".$this->con->escape($this->blog->id)."'"); + $this->blog->triggerBlog(); + } + + public function updateCategory($id,$desc) + { + $cur = $this->con->openCursor($this->table); + + $cur->link_desc = (string) $desc; + + if ($cur->link_desc == '') { + throw new Exception(__('You must provide a category title')); + } + + $cur->update('WHERE link_id = '.(integer) $id. + " AND blog_id = '".$this->con->escape($this->blog->id)."'"); + $this->blog->triggerBlog(); + } + + public function addCategory($title) + { + $cur = $this->con->openCursor($this->table); + + $cur->blog_id = (string) $this->blog->id; + $cur->link_desc = (string) $title; + $cur->link_href = ''; + $cur->link_title = ''; + + if ($cur->link_desc == '') { + throw new Exception(__('You must provide a category title')); + } + + $strReq = 'SELECT MAX(link_id) FROM '.$this->table; + $rs = $this->con->select($strReq); + $cur->link_id = (integer) $rs->f(0) + 1; + + $cur->insert(); + $this->blog->triggerBlog(); + + return $cur->link_id; + } + + public function delItem($id) + { + $id = (integer) $id; + + $strReq = 'DELETE FROM '.$this->table.' '. + "WHERE blog_id = '".$this->con->escape($this->blog->id)."' ". + 'AND link_id = '.$id.' '; + + $this->con->execute($strReq); + $this->blog->triggerBlog(); + } + + public function updateOrder($id,$position) + { + $cur = $this->con->openCursor($this->table); + $cur->link_position = (integer) $position; + + $cur->update('WHERE link_id = '.(integer) $id. + " AND blog_id = '".$this->con->escape($this->blog->id)."'"); + $this->blog->triggerBlog(); + } + + + private function setLinksData(&$rs) + { + $cat_title = null; + while ($rs->fetch()) { + $rs->set('is_cat',!$rs->link_title && !$rs->link_href); + + if ($rs->is_cat) { + $cat_title = $rs->link_desc; + $rs->set('cat_title',null); + } else { + $rs->set('cat_title',$cat_title); + } + } + $rs->moveStart(); + } + + public function getLinksHierarchy($rs) + { + $res = array(); + + foreach ($rs->rows() as $k => $v) + { + if (!$v['is_cat']) { + $res[$v['cat_title']][] = $v; + } + } + + return $res; + } +} +?> \ No newline at end of file diff --git a/plugins/blogroll/class.dc.importblogroll.php b/plugins/blogroll/class.dc.importblogroll.php new file mode 100644 index 0000000..3b79a93 --- /dev/null +++ b/plugins/blogroll/class.dc.importblogroll.php @@ -0,0 +1,102 @@ +_parseXBEL($data); + } + elseif (preg_match('!_parseOPML($data); + } + else { + throw new Exception(__('You need to provide a XBEL or OPML file.')); + } + } + + + protected function _parseOPML($data) + { + $xml = @simplexml_load_string($data); + if (!$xml) throw new Exception(__('File is not in XML format.')); + + $outlines = $xml->xpath("//outline"); + + $this->entries = array(); + foreach ($outlines as $outline) { + if (isset($outline['htmlUrl'])) { + $link = $outline['htmlUrl']; + } + elseif (isset($outline['url'])) { + $link = $outline['url']; + } + else continue; + $entry = new StdClass(); + $entry->link = $link; + $entry->title = (!empty($outline['title']))?$outline['title']:''; + if (empty($entry->title)) { + $entry->title = (!empty($outline['text']))?$outline['text']:$entry->link; + } + $entry->desc = (!empty($outline['description']))?$outline['description']:''; + $this->entries[] = $entry; + } + } + + + protected function _parseXBEL($data) + { + $xml = @simplexml_load_string($data); + if (!$xml) throw new Exception(__('File is not in XML format.')); + + $outlines = $xml->xpath("//bookmark"); + + $this->entries = array(); + foreach ($outlines as $outline) { + if (!isset($outline['href'])) continue; + $entry = new StdClass(); + $entry->link = $outline['href']; + $entry->title = (!empty($outline->title))?$outline->title:''; + if (empty($entry->title)) { + $entry->title = $entry->link; + } + $entry->desc = (!empty($outline->desc))?$outline->desc:''; + $this->entries[] = $entry; + } + } + + + public function getAll() + { + if (!$this->entries) return null; + return $this->entries; + } + +} + +class dcImportBlogroll { + + public static function loadFile($file) + { + if (file_exists($file) && is_readable($file)) { + $importer = new linksImporter(); + $importer->parse(file_get_contents($file)); + return $importer->getAll(); + } + return false; + } +} +?> \ No newline at end of file diff --git a/plugins/blogroll/edit.php b/plugins/blogroll/edit.php new file mode 100644 index 0000000..d6b3976 --- /dev/null +++ b/plugins/blogroll/edit.php @@ -0,0 +1,241 @@ +getLink($id); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +if (!$core->error->flag() && $rs->isEmpty()) { + $core->error->add(__('No such link or title')); +} else { + $link_title = $rs->link_title; + $link_href = $rs->link_href; + $link_desc = $rs->link_desc; + $link_lang = $rs->link_lang; + $link_xfn = $rs->link_xfn; +} + +# Update a link +if (isset($rs) && !$rs->is_cat && !empty($_POST['edit_link'])) +{ + $link_title = $_POST['link_title']; + $link_href = $_POST['link_href']; + $link_desc = $_POST['link_desc']; + $link_lang = $_POST['link_lang']; + + $link_xfn = ''; + + if (!empty($_POST['identity'])) + { + $link_xfn .= $_POST['identity']; + } + else + { + if(!empty($_POST['friendship'])) { + $link_xfn .= ' '.$_POST['friendship']; + } + if(!empty($_POST['physical'])) { + $link_xfn .= ' met'; + } + if(!empty($_POST['professional'])) { + $link_xfn .= ' '.implode(' ',$_POST['professional']); + } + if(!empty($_POST['geographical'])) { + $link_xfn .= ' '.$_POST['geographical']; + } + if(!empty($_POST['family'])) { + $link_xfn .= ' '.$_POST['family']; + } + if(!empty($_POST['romantic'])) { + $link_xfn .= ' '.implode(' ',$_POST['romantic']); + } + } + + try { + $blogroll->updateLink($id,$link_title,$link_href,$link_desc,$link_lang,trim($link_xfn)); + http::redirect($p_url.'&edit=1&id='.$id.'&upd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + + +# Update a category +if (isset($rs) && $rs->is_cat && !empty($_POST['edit_cat'])) +{ + $link_desc = $_POST['link_desc']; + + try { + $blogroll->updateCategory($id,$link_desc); + http::redirect($p_url.'&edit=1&id='.$id.'&upd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +?> + + + Blogroll + + + +'.__('Return to blogroll').'

    '; ?> + +is_cat) +{ + if (!empty($_GET['upd'])) { + echo '

    '.__('Category has been successfully updated').'

    '; + } + + echo + '
    '. + '
    '.__('Edit category').''. + + '

    '. + + form::hidden('edit',1). + form::hidden('id',$id). + $core->formNonce(). + '

    '. + '
    '. + '
    '; +} +if (isset($rs) && !$rs->is_cat) +{ + if (!empty($_GET['upd'])) { + echo '

    '.__('Link has been successfully updated').'

    '; + } + + echo + '
    '. + '
    '.__('Edit link').''. + + '

    '. + + '

    '. + + '

    '. + + '

    '. + + '

    '.form::hidden('p','blogroll'). + form::hidden('edit',1). + form::hidden('id',$id). + $core->formNonce(). + '

    '. + '
    '. + + + # XFN nightmare + '
    '.__('XFN').''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + '
    '.__('_xfn_Me').'

    '.'

    '.__('_xfn_Friendship').'

    '. + ' '. + ' '. + ' '. + ''. + '

    '.__('_xfn_Physical').'

    '. + ''. + '

    '.__('_xfn_Professional').'

    '. + ' '. + ''. + '

    '.__('_xfn_Geographical').'

    '. + ' '. + ' '. + ''. + '

    '.__('_xfn_Family').'

    '. + ' '. + ' '. + ' '. + ' '. + ' '. + ''. + '

    '.__('_xfn_Romantic').'

    '. + ' '. + ' '. + ' '. + ' '. + '

    '. + + '
    '. + + '
    '; +} +?> + + \ No newline at end of file diff --git a/plugins/blogroll/icon-small.png b/plugins/blogroll/icon-small.png new file mode 100644 index 0000000..8908339 Binary files /dev/null and b/plugins/blogroll/icon-small.png differ diff --git a/plugins/blogroll/icon.png b/plugins/blogroll/icon.png new file mode 100644 index 0000000..6258a88 Binary files /dev/null and b/plugins/blogroll/icon.png differ diff --git a/plugins/blogroll/index.php b/plugins/blogroll/index.php new file mode 100644 index 0000000..fa33c4b --- /dev/null +++ b/plugins/blogroll/index.php @@ -0,0 +1,400 @@ +blog); + +if (!empty($_REQUEST['edit']) && !empty($_REQUEST['id'])) { + include dirname(__FILE__).'/edit.php'; + return; +} + +$default_tab = ''; +$link_title = $link_href = $link_desc = $link_lang = ''; +$cat_title = ''; + +# Import links +if (!empty($_POST['import_links']) && !empty($_FILES['links_file'])) +{ + $default_tab = 'import-links'; + + try + { + files::uploadStatus($_FILES['links_file']); + $ifile = DC_TPL_CACHE.'/'.md5(uniqid()); + if (!move_uploaded_file($_FILES['links_file']['tmp_name'],$ifile)) { + throw new Exception(__('Unable to move uploaded file.')); + } + + require_once dirname(__FILE__).'/class.dc.importblogroll.php'; + try { + $imported = dcImportBlogroll::loadFile($ifile); + @unlink($ifile); + } catch (Exception $e) { + @unlink($ifile); + throw $e; + } + + + if (empty($imported)) { + unset($imported); + throw new Exception(__('Nothing to import')); + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +if (!empty($_POST['import_links_do'])) { + foreach ($_POST['entries'] as $idx) { + $link_title = $_POST['title'][$idx]; + $link_href = $_POST['url'][$idx]; + $link_desc = $_POST['desc'][$idx]; + try { + $blogroll->addLink($link_title,$link_href,$link_desc,''); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'import-links'; + } + } + http::redirect($p_url.'&importlinks=1'); +} + +if (!empty($_POST['cancel_import'])) { + $core->error->add(__('Import operation cancelled.')); + $default_tab = 'import-links'; +} + +# Add link +if (!empty($_POST['add_link'])) +{ + $link_title = $_POST['link_title']; + $link_href = $_POST['link_href']; + $link_desc = $_POST['link_desc']; + $link_lang = $_POST['link_lang']; + + try { + $blogroll->addLink($link_title,$link_href,$link_desc,$link_lang); + http::redirect($p_url.'&addlink=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'add-link'; + } +} + +# Add category +if (!empty($_POST['add_cat'])) +{ + $cat_title = $_POST['cat_title']; + + try { + $blogroll->addCategory($cat_title); + http::redirect($p_url.'&addcat=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'add-cat'; + } +} + +# Delete link +if (!empty($_POST['removeaction']) && !empty($_POST['remove'])) { + foreach ($_POST['remove'] as $k => $v) + { + try { + $blogroll->delItem($v); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + break; + } + } + + if (!$core->error->flag()) { + http::redirect($p_url.'&removed=1'); + } +} + +# Order links +$order = array(); +if (empty($_POST['links_order']) && !empty($_POST['order'])) { + $order = $_POST['order']; + asort($order); + $order = array_keys($order); +} elseif (!empty($_POST['links_order'])) { + $order = explode(',',$_POST['links_order']); +} + +if (!empty($_POST['saveorder']) && !empty($order)) +{ + foreach ($order as $pos => $l) { + $pos = ((integer) $pos)+1; + + try { + $blogroll->updateOrder($l,$pos); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect($p_url.'&neworder=1'); + } +} + + +# Get links +try { + $rs = $blogroll->getLinks(); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +?> + + + Blogroll + + + + + + + +

    blog->name); ?> › Blogroll

    + +'.__('Items order has been successfully updated').'

    '; +} + +if (!empty($_GET['removed'])) { + echo '

    '.__('Items have been successfully removed.').'

    '; +} + +if (!empty($_GET['addlink'])) { + echo '

    '.__('Link has been successfully created.').'

    '; +} + +if (!empty($_GET['addcat'])) { + echo '

    '.__('category has been successfully created.').'

    '; +} + +if (!empty($_GET['importlinks'])) { + echo '

    '.__('links have been successfully imported.').'

    '; +} +?> + +
    + +
    + +'. +''. +'
    '; + +echo +'
    '. +'
    '. +'
    '.__('Add a new category').''. +'

    '. +form::hidden(array('p'),'blogroll'). +$core->formNonce(). +'

    '. +'
    '. +'
    '. +'
    '; + +echo +''; +?> + + + \ No newline at end of file diff --git a/plugins/blogroll/locales/en/main.lang.php b/plugins/blogroll/locales/en/main.lang.php new file mode 100644 index 0000000..4f2b366 --- /dev/null +++ b/plugins/blogroll/locales/en/main.lang.php @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/plugins/blogroll/locales/en/main.po b/plugins/blogroll/locales/en/main.po new file mode 100644 index 0000000..bfb0e25 --- /dev/null +++ b/plugins/blogroll/locales/en/main.po @@ -0,0 +1,74 @@ +msgid "_xfn_Me" +msgstr "Me" + +msgid "_xfn_Another link for myself" +msgstr "Another link for myself" + +msgid "_xfn_Friendship" +msgstr "Friendship" + +msgid "_xfn_Contact" +msgstr "Contact" + +msgid "_xfn_Acquaintance" +msgstr "Acquaintance" + +msgid "_xfn_Friend" +msgstr "Friend" + +msgid "_xfn_Physical" +msgstr "Physical" + +msgid "_xfn_Met" +msgstr "Met" + +msgid "_xfn_Professional" +msgstr "Professional" + +msgid "_xfn_Co-worker" +msgstr "Co-worker" + +msgid "_xfn_Colleague" +msgstr "Colleague" + +msgid "_xfn_Geographical" +msgstr "Geographical" + +msgid "_xfn_Co-resident" +msgstr "Co-resident" + +msgid "_xfn_Neighbor" +msgstr "Neighbor" + +msgid "_xfn_Family" +msgstr "Family" + +msgid "_xfn_Child" +msgstr "Child" + +msgid "_xfn_Parent" +msgstr "Parent" + +msgid "_xfn_Sibling" +msgstr "Sibling" + +msgid "_xfn_Spouse" +msgstr "Spouse" + +msgid "_xfn_Kin" +msgstr "Kin" + +msgid "_xfn_Romantic" +msgstr "Romantic" + +msgid "_xfn_Muse" +msgstr "Muse" + +msgid "_xfn_Crush" +msgstr "Crush" + +msgid "_xfn_Date" +msgstr "Date" + +msgid "_xfn_Sweetheart" +msgstr "Sweetheart" \ No newline at end of file diff --git a/plugins/blowupConfig/_admin.php b/plugins/blowupConfig/_admin.php new file mode 100644 index 0000000..f6546e2 --- /dev/null +++ b/plugins/blowupConfig/_admin.php @@ -0,0 +1,26 @@ +addBehavior('adminCurrentThemeDetails','blowup_config_details'); + +if (!isset($__resources['help']['blowupConfig'])) { + $__resources['help']['blowupConfig'] = dirname(__FILE__).'/help.html'; +} + +function blowup_config_details($core,$id) +{ + if ($id == 'default' && $core->auth->check('admin',$core->blog->id)) { + return '

    '.__('Theme configuration').'

    '; + } +} +?> \ No newline at end of file diff --git a/plugins/blowupConfig/_define.php b/plugins/blowupConfig/_define.php new file mode 100644 index 0000000..bf280bc --- /dev/null +++ b/plugins/blowupConfig/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "Blowup Config", + /* Description*/ "Configure your Blowup Theme", + /* Author */ "Olivier Meunier", + /* Version */ '1.0', + /* Permissions */ 'admin' +); +?> \ No newline at end of file diff --git a/plugins/blowupConfig/_public.php b/plugins/blowupConfig/_public.php new file mode 100644 index 0000000..4e3fed2 --- /dev/null +++ b/plugins/blowupConfig/_public.php @@ -0,0 +1,193 @@ +blog->settings->theme != 'default') { + return; +} + +require dirname(__FILE__).'/lib/class.blowup.config.php'; +$core->addBehavior('publicHeadContent',array('tplBlowupTheme','publicHeadContent')); + +class tplBlowUpTheme +{ + public static function publicHeadContent(&$core) + { + echo '\n"; + } + + public static function blowUpStyleHelper() + { + $s = $GLOBALS['core']->blog->settings->blowup_style; + + if ($s === null) { + return; + } + + $s = @unserialize($s); + if (!is_array($s)) { + return; + } + + $css = array(); + + /* Sidebar position + ---------------------------------------------- */ + if ($s['sidebar_position'] == 'left') { + $css['#wrapper']['background-position'] = '-300px 0'; + $css['#main']['float'] = 'right'; + $css['#sidebar']['float'] = 'left'; + } + + /* Properties + ---------------------------------------------- */ + self::prop($css,'body','background-color',$s['body_bg_c']); + + self::prop($css,'body','color',$s['body_txt_c']); + self::prop($css,'.post-tags li a:link, .post-tags li a:visited, .post-info-co a:link, .post-info-co a:visited','color',$s['body_txt_c']); + self::prop($css,'#page','font-size',$s['body_txt_s']); + self::prop($css,'body','font-family',blowupConfig::fontDef($s['body_txt_f'])); + + self::prop($css,'.post-content, .post-excerpt, #comments dd, #pings dd, dd.comment-preview','line-height',$s['body_line_height']); + + if (!$s['blog_title_hide']) + { + self::prop($css,'#top h1 a','color',$s['blog_title_c']); + self::prop($css,'#top h1','font-size',$s['blog_title_s']); + self::prop($css,'#top h1','font-family',blowupConfig::fontDef($s['blog_title_f'])); + + if ($s['blog_title_a'] == 'right' || $s['blog_title_a'] == 'left') { + $css['#top h1'][$s['blog_title_a']] = '0px'; + $css['#top h1']['width'] = 'auto'; + } + + if ($s['blog_title_p']) + { + $_p = explode(':',$s['blog_title_p']); + $css['#top h1']['top'] = $_p[1].'px'; + if ($s['blog_title_a'] != 'center') { + $_a = $s['blog_title_a'] == 'right' ? 'right' : 'left'; + $css['#top h1'][$_a] = $_p[0].'px'; + } + } + } + else + { + self::prop($css,'#top h1 span','text-indent','-5000px'); + self::prop($css,'#top h1','top','0px'); + $css['#top h1 a'] = array( + 'display' => 'block', + 'height' => $s['top_height'] ? ($s['top_height']-10).'px' : '120px', + 'width' => '800px' + ); + } + self::prop($css,'#top','height',$s['top_height']); + + self::prop($css,'.day-date','color',$s['date_title_c']); + self::prop($css,'.day-date','font-family',blowupConfig::fontDef($s['date_title_f'])); + self::prop($css,'.day-date','font-size',$s['date_title_s']); + + self::prop($css,'a','color',$s['body_link_c']); + self::prop($css,'a:visited','color',$s['body_link_v_c']); + self::prop($css,'a:hover, a:focus, a:active','color',$s['body_link_f_c']); + + self::prop($css,'#comment-form input, #comment-form textarea','color',$s['body_link_c']); + self::prop($css,'#comment-form input.preview','color',$s['body_link_c']); + self::prop($css,'#comment-form input.preview:hover','background',$s['body_link_f_c']); + self::prop($css,'#comment-form input.preview:hover','border-color',$s['body_link_f_c']); + self::prop($css,'#comment-form input.submit','color',$s['body_link_c']); + self::prop($css,'#comment-form input.submit:hover','background',$s['body_link_f_c']); + self::prop($css,'#comment-form input.submit:hover','border-color',$s['body_link_f_c']); + + self::prop($css,'#sidebar','font-family',blowupConfig::fontDef($s['sidebar_text_f'])); + self::prop($css,'#sidebar','font-size',$s['sidebar_text_s']); + self::prop($css,'#sidebar','color',$s['sidebar_text_c']); + + self::prop($css,'#sidebar h2','font-family',blowupConfig::fontDef($s['sidebar_title_f'])); + self::prop($css,'#sidebar h2','font-size',$s['sidebar_title_s']); + self::prop($css,'#sidebar h2','color',$s['sidebar_title_c']); + + self::prop($css,'#sidebar h3','font-family',blowupConfig::fontDef($s['sidebar_title2_f'])); + self::prop($css,'#sidebar h3','font-size',$s['sidebar_title2_s']); + self::prop($css,'#sidebar h3','color',$s['sidebar_title2_c']); + + self::prop($css,'#sidebar ul','border-top-color',$s['sidebar_line_c']); + self::prop($css,'#sidebar li','border-bottom-color',$s['sidebar_line_c']); + self::prop($css,'#topnav ul','border-bottom-color',$s['sidebar_line_c']); + + self::prop($css,'#sidebar li a','color',$s['sidebar_link_c']); + self::prop($css,'#sidebar li a:visited','color',$s['sidebar_link_v_c']); + self::prop($css,'#sidebar li a:hover, #sidebar li a:focus, #sidebar li a:active','color',$s['sidebar_link_f_c']); + self::prop($css,'#search input','color',$s['sidebar_link_c']); + self::prop($css,'#search .submit','color',$s['sidebar_link_c']); + self::prop($css,'#search .submit:hover','background',$s['sidebar_link_f_c']); + self::prop($css,'#search .submit:hover','border-color',$s['sidebar_link_f_c']); + + self::prop($css,'.post-title','color',$s['post_title_c']); + self::prop($css,'.post-title a, .post-title a:visited','color',$s['post_title_c']); + self::prop($css,'.post-title','font-family',blowupConfig::fontDef($s['post_title_f'])); + self::prop($css,'.post-title','font-size',$s['post_title_s']); + + self::prop($css,'#comments dd','background-color',$s['post_comment_bg_c']); + self::prop($css,'#comments dd','color',$s['post_comment_c']); + self::prop($css,'#comments dd.me','background-color',$s['post_commentmy_bg_c']); + self::prop($css,'#comments dd.me','color',$s['post_commentmy_c']); + + self::prop($css,'#prelude, #prelude a','color',$s['prelude_c']); + + self::prop($css,'#footer p','background-color',$s['footer_bg_c']); + self::prop($css,'#footer p','color',$s['footer_c']); + self::prop($css,'#footer p','font-size',$s['footer_s']); + self::prop($css,'#footer p','font-family',blowupConfig::fontDef($s['footer_f'])); + self::prop($css,'#footer p a','color',$s['footer_l_c']); + + /* Images + ------------------------------------------------------ */ + self::backgroundImg($css,'body',$s['body_bg_c'],'body-bg.png'); + self::backgroundImg($css,'body',$s['body_bg_g'] != 'light','body-bg.png'); + self::backgroundImg($css,'body',$s['prelude_c'],'body-bg.png'); + self::backgroundImg($css,'#top',$s['body_bg_c'],'page-t.png'); + self::backgroundImg($css,'#top',$s['body_bg_g'] != 'light','page-t.png'); + self::backgroundImg($css,'#top',$s['uploaded'] || $s['top_image'],'page-t.png'); + self::backgroundImg($css,'#footer',$s['body_bg_c'],'page-b.png'); + self::backgroundImg($css,'#comments dt',$s['post_comment_bg_c'],'comment-t.png'); + self::backgroundImg($css,'#comments dd',$s['post_comment_bg_c'],'comment-b.png'); + self::backgroundImg($css,'#comments dt.me',$s['post_commentmy_bg_c'],'commentmy-t.png'); + self::backgroundImg($css,'#comments dd.me',$s['post_commentmy_bg_c'],'commentmy-b.png'); + + $res = ''; + foreach ($css as $selector => $values) { + $res .= $selector." {\n"; + foreach ($values as $k => $v) { + $res .= $k.':'.$v.";\n"; + } + $res .= "}\n"; + } + return $res; + } + + protected static function prop(&$css,$selector,$prop,$value) + { + if ($value) { + $css[$selector][$prop] = $value; + } + } + + protected static function backgroundImg(&$css,$selector,$value,$image) + { + $file = blowupConfig::imagesPath().'/'.$image; + if ($value && file_exists($file)){ + $css[$selector]['background-image'] = 'url('.blowupConfig::imagesURL().'/'.$image.')'; + } + } +} +?> \ No newline at end of file diff --git a/plugins/blowupConfig/alpha-img/comment-b.png b/plugins/blowupConfig/alpha-img/comment-b.png new file mode 100644 index 0000000..b1d37c6 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/comment-b.png differ diff --git a/plugins/blowupConfig/alpha-img/comment-t.png b/plugins/blowupConfig/alpha-img/comment-t.png new file mode 100644 index 0000000..b320da3 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/comment-t.png differ diff --git a/plugins/blowupConfig/alpha-img/gradient-d.png b/plugins/blowupConfig/alpha-img/gradient-d.png new file mode 100644 index 0000000..74bf9cf Binary files /dev/null and b/plugins/blowupConfig/alpha-img/gradient-d.png differ diff --git a/plugins/blowupConfig/alpha-img/gradient-l.png b/plugins/blowupConfig/alpha-img/gradient-l.png new file mode 100644 index 0000000..c395679 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/gradient-l.png differ diff --git a/plugins/blowupConfig/alpha-img/gradient-m.png b/plugins/blowupConfig/alpha-img/gradient-m.png new file mode 100644 index 0000000..b25c6ba Binary files /dev/null and b/plugins/blowupConfig/alpha-img/gradient-m.png differ diff --git a/plugins/blowupConfig/alpha-img/page-b.png b/plugins/blowupConfig/alpha-img/page-b.png new file mode 100644 index 0000000..1c05ea4 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-b.png differ diff --git a/plugins/blowupConfig/alpha-img/page-bg.png b/plugins/blowupConfig/alpha-img/page-bg.png new file mode 100644 index 0000000..6033125 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-bg.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/animals.png b/plugins/blowupConfig/alpha-img/page-t/animals.png new file mode 100644 index 0000000..4db1e88 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/animals.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/blank.png b/plugins/blowupConfig/alpha-img/page-t/blank.png new file mode 100644 index 0000000..18ae6e8 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/blank.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/butterflies.png b/plugins/blowupConfig/alpha-img/page-t/butterflies.png new file mode 100644 index 0000000..2a790c5 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/butterflies.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/default.png b/plugins/blowupConfig/alpha-img/page-t/default.png new file mode 100644 index 0000000..1ea0620 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/default.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/flamingo.png b/plugins/blowupConfig/alpha-img/page-t/flamingo.png new file mode 100644 index 0000000..71388ee Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/flamingo.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/flourish-1.png b/plugins/blowupConfig/alpha-img/page-t/flourish-1.png new file mode 100644 index 0000000..48c01e3 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/flourish-1.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/flourish-2.png b/plugins/blowupConfig/alpha-img/page-t/flourish-2.png new file mode 100644 index 0000000..2707e5a Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/flourish-2.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/image-mask.png b/plugins/blowupConfig/alpha-img/page-t/image-mask.png new file mode 100644 index 0000000..998613b Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/image-mask.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/light-trails-1.png b/plugins/blowupConfig/alpha-img/page-t/light-trails-1.png new file mode 100644 index 0000000..c1e58c6 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/light-trails-1.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/light-trails-2.png b/plugins/blowupConfig/alpha-img/page-t/light-trails-2.png new file mode 100644 index 0000000..81acee6 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/light-trails-2.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/light-trails-3.png b/plugins/blowupConfig/alpha-img/page-t/light-trails-3.png new file mode 100644 index 0000000..d1dacc2 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/light-trails-3.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/light-trails-4.png b/plugins/blowupConfig/alpha-img/page-t/light-trails-4.png new file mode 100644 index 0000000..901b6e7 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/light-trails-4.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/rabbit.png b/plugins/blowupConfig/alpha-img/page-t/rabbit.png new file mode 100644 index 0000000..272e406 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/rabbit.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png b/plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png new file mode 100644 index 0000000..64f15f6 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png b/plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png new file mode 100644 index 0000000..4c363d2 Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png differ diff --git a/plugins/blowupConfig/alpha-img/page-t/typo.png b/plugins/blowupConfig/alpha-img/page-t/typo.png new file mode 100644 index 0000000..ef7689d Binary files /dev/null and b/plugins/blowupConfig/alpha-img/page-t/typo.png differ diff --git a/plugins/blowupConfig/config.js b/plugins/blowupConfig/config.js new file mode 100644 index 0000000..109ac4b --- /dev/null +++ b/plugins/blowupConfig/config.js @@ -0,0 +1,11 @@ + +$(function(){if($('#blog_title_hide').attr('checked')){toggleDisable($('#blog_title_f'));toggleDisable($('#blog_title_s'));toggleDisable($('#blog_title_c'));toggleDisable($('#blog_title_a'));toggleDisable($('#blog_title_p'));} +$('#blog_title_hide').click(function(){toggleDisable($('#blog_title_f'));toggleDisable($('#blog_title_s'));toggleDisable($('#blog_title_c'));toggleDisable($('#blog_title_a'));toggleDisable($('#blog_title_p'));});if($('#top_image').val()=='custom'){$('#uploader').show();}else{$('#uploader').hide();} +$('#top_image').change(function(){if(this.value=='custom'){$('#uploader').show();$('#image-preview').attr('src',dotclear.blowup_public_url+'/page-t.png');}else{$('#uploader').hide();$('#uploader input').val('');$('#image-preview').attr('src','index.php?pf=blowupConfig/alpha-img/page-t/'+this.value+'.png');}});var styles_combo=document.createElement('select');$(styles_combo).append('');$(styles_combo).append('');for(var style in dotclear.blowup_styles){styles_option=document.createElement('option');styles_option.value=dotclear.blowup_styles[style];$(styles_option).append(style);$(styles_combo).append(styles_option);} +$('#theme_config').prepend(styles_combo);$(styles_combo).wrap('
    ').before(''+dotclear.msg.predefined_styles+'').wrap('

    ');$(styles_combo).change(function(){if(this.value=='none'){$(this.form).find('input[type=text]').val('').css({backgroundColor:'#FFF',color:'#000'});$(this.form).find('select').not($(this)).val('');$('#top_image').val('default');}else{applyBlowupValues(this.value);}});var e=$('#bu_export ~ fieldset:first');e.toggle();var img=document.createElement('img');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;img.className='expand';$(img).css('cursor','pointer');$(document.createTextNode(' ')).prependTo('#bu_export');$(img).prependTo('#bu_export');$(img).click(function(){if(e.css('display')=='none'){this.src=dotclear.img_minus_src;this.alt=dotclear.img_minus_alt;}else{this.src=dotclear.img_plus_src;this.alt=dotclear.img_plus_alt;} +e.toggle();});var a=document.createElement('a');a.href='#';$(a).text(dotclear.msg.apply_code);e.append(a);$(a).click(function(){var code=e.find('#export_code');if(code.size()==0){return false;} +applyBlowupValues(code.val());return false;});function toggleDisable(e){if(e.attr('disabled')){e.removeAttr('disabled');}else{e.attr('disabled','disabled');}} +function applyBlowupValues(code){code=code.replace("\n","");var re=/(^| )([a-zA-Z0-9_]+):"(.*?)"(;|$)/g;var s=code.match(re);if(typeof(s)=='object'&&s.length>0){var member,target,value,t_e;var reg=new RegExp('^(.+):"(.*)"(;?)\s*$');for(var i=0;i0.5?'#000':'#fff'});}} +function getColorLum(color) +{var rgb=[parseInt('0x'+color.substring(1,3))/255,parseInt('0x'+color.substring(3,5))/255,parseInt('0x'+color.substring(5,7))/255];return(Math.min(rgb[0],Math.min(rgb[1],rgb[2]))+Math.max(rgb[0],Math.max(rgb[1],rgb[2])))/2;}});dotclear.blowup_styles={'Spring Time':'body_bg_c:"#E0E0E0"; body_bg_g:"light"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#6B6B6B"; body_line_height:"1.4em"; top_image:"light-trails-1"; blog_title_hide:"0"; blog_title_f:""; blog_title_s:"3.5em"; blog_title_c:"#9AC528"; blog_title_a:"center"; blog_title_p:""; body_link_c:"#279AC4"; body_link_f_c:"#6D8824"; body_link_v_c:"#279AC4"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#6B6B6B"; sidebar_title_f:""; sidebar_title_s:""; sidebar_title_c:"#8FB22F"; sidebar_title2_f:""; sidebar_title2_s:""; sidebar_title2_c:"#279AC4"; sidebar_line_c:"#FFD02C"; sidebar_link_c:"#6B6B6B"; sidebar_link_f_c:"#9AC528"; sidebar_link_v_c:"#6B6B6B"; date_title_f:""; date_title_s:"1em"; date_title_c:"#279AC4"; post_title_f:""; post_title_s:"1.7em"; post_title_c:"#9AC528"; post_comment_bg_c:"#FFFAD1"; post_comment_c:"#6B6B6B"; post_commentmy_bg_c:"#F5F9D9"; post_commentmy_c:"#6B6B6B"; prelude_c:"#EDEDED"; footer_f:""; footer_s:"1.2em"; footer_c:"#9AC528"; footer_l_c:"#6D8824"; footer_bg_c:"#E0E0E0"','Forest':'body_bg_c:"#80661A"; body_bg_g:"light"; body_txt_f:""; body_txt_s:""; body_txt_c:"#0A0A00"; body_line_height:"1.4em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s3"; blog_title_s:"4em"; blog_title_c:"#D9D9BF"; blog_title_a:""; blog_title_p:""; body_link_c:"#666600"; body_link_f_c:"#CC9933"; body_link_v_c:"#8D8D40"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#0A0A00"; sidebar_title_f:"s2"; sidebar_title_s:"1.6em"; sidebar_title_c:"#4D4D00"; sidebar_title2_f:"s2"; sidebar_title2_s:""; sidebar_title2_c:"#575700"; sidebar_line_c:"#D9D9BF"; sidebar_link_c:"#40330D"; sidebar_link_f_c:"#666600"; sidebar_link_v_c:"#40330D"; date_title_f:""; date_title_s:""; date_title_c:"#B3B380"; post_title_f:"s2"; post_title_s:"2em"; post_title_c:"#4D4D00"; post_comment_bg_c:"#F0F0E6"; post_comment_c:"#0A0A00"; prelude_c:"#140F05"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:"#D9AD2B"; footer_bg_c:"#33260D"','Flamingo':'body_bg_c:"#CC9999"; body_bg_g:"light"; body_txt_f:"ss3"; body_txt_s:"1.2em"; body_txt_c:"#1A1A00"; body_line_height:"1.5em"; top_image:"flamingo"; blog_title_hide:"0"; blog_title_f:"ss1"; blog_title_s:"3.5em"; blog_title_c:"#FFFFFF"; blog_title_a:""; blog_title_p:""; body_link_c:"#AD8282"; body_link_f_c:"#8282D9"; body_link_v_c:"#997373"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss4"; sidebar_title_s:"1.4em"; sidebar_title_c:"#8282D9"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#AD8282"; sidebar_line_c:"#CDCDFF"; sidebar_link_c:"#262640"; sidebar_link_f_c:"#AD8282"; sidebar_link_v_c:"#262640"; date_title_f:"ss4"; date_title_s:""; date_title_c:"#D9B3B3"; post_title_f:"ss4"; post_title_s:"1.8em"; post_title_c:"#8282D9"; post_comment_bg_c:"#F2E5E5"; post_comment_c:""; prelude_c:"#140F0F"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#140F0F"','Iceberg':'body_bg_c:"#5280A3"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#757575"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3em"; blog_title_c:"#FFFFFF"; blog_title_a:""; blog_title_p:""; body_link_c:"#BDB000"; body_link_f_c:"#F3E66D"; body_link_v_c:"#BDB000"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.4em"; sidebar_title_c:"#689B9C"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#66664D"; sidebar_link_f_c:"#5280A3"; sidebar_link_v_c:"#66664D"; date_title_f:""; date_title_s:""; date_title_c:"#000000"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#6F6800"; post_comment_bg_c:"#E4E4E2"; post_comment_c:""; prelude_c:"#0E2734"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#0E2734"','Night':'body_bg_c:"#0D1A26"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#050A0F"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3.5em"; blog_title_c:"#F2F2E5"; blog_title_a:""; body_link_c:"#336699"; body_link_f_c:"#66664D"; body_link_v_c:"#2B5782"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.4em"; sidebar_title_c:"#336699"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#66664D"; sidebar_link_f_c:"#336699"; sidebar_link_v_c:"#66664D"; date_title_f:""; date_title_s:""; date_title_c:"#ADAD82"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#737300"; post_comment_bg_c:"#E6E6CD"; post_comment_c:""; prelude_c:"#070E14"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#14140F"; blog_title_p:""','Peas & Carrots':'body_bg_c:"#9DCA25"; body_bg_g:"light"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#383839"; body_line_height:"1.5em"; top_image:"butterflies"; blog_title_hide:"0"; blog_title_f:"ss4"; blog_title_s:"3em"; blog_title_c:"#DBDB9D"; blog_title_a:"left"; blog_title_p:""; body_link_c:"#646B10"; body_link_f_c:"#DF6C01"; body_link_v_c:"#919924"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss4"; sidebar_title_s:""; sidebar_title_c:"#FE9017"; sidebar_title2_f:"s2"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#826228"; sidebar_line_c:"#D3EB8B"; sidebar_link_c:"#858547"; sidebar_link_f_c:"#FE9017"; sidebar_link_v_c:"#8F9645"; date_title_f:""; date_title_s:""; date_title_c:"#826228"; post_title_f:"ss4"; post_title_s:"1.8em"; post_title_c:"#806432"; post_comment_bg_c:"#EFFDCC"; post_comment_c:"#826228"; prelude_c:"#C8E186"; footer_f:""; footer_s:"1em"; footer_c:"#FFFFFF"; footer_l_c:"#FFFFFF"; footer_bg_c:"#484432"','Rabbit':'body_bg_c:"#8F9645"; body_bg_g:"solid"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#625D47"; body_line_height:"1.4em"; top_image:"rabbit"; blog_title_hide:"0"; blog_title_f:"ss1"; blog_title_s:"3.5em"; blog_title_c:"#DBDB9D"; blog_title_a:""; blog_title_p:"130:70"; body_link_c:"#646B10"; body_link_f_c:"#484C12"; body_link_v_c:"#919924"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#858547"; sidebar_title_f:""; sidebar_title_s:""; sidebar_title_c:"#8F9645"; sidebar_title2_f:"s2"; sidebar_title2_s:""; sidebar_title2_c:"#826228"; sidebar_line_c:"#95956B"; sidebar_link_c:"#858547"; sidebar_link_f_c:"#826228"; sidebar_link_v_c:"#8F9645"; date_title_f:"s2"; date_title_s:"1em"; date_title_c:"#826228"; post_title_f:"s2"; post_title_s:"1.6em"; post_title_c:"#806432"; post_comment_bg_c:"#D6DE91"; post_comment_c:"#826228"; prelude_c:"#484432"; footer_f:""; footer_s:"1em"; footer_c:"#A6AF50"; footer_l_c:"#DBDB9D"; footer_bg_c:"#484432"','Rec Room':'body_bg_c:"#9B5E1C"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#757575"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3em"; blog_title_c:"#F9FAD6"; blog_title_a:""; blog_title_p:""; body_link_c:"#D1BF1D"; body_link_f_c:"#EEE168"; body_link_v_c:"#D1BF1D"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.2em"; sidebar_title_c:"#689B9C"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#66664D"; sidebar_link_f_c:"#689B9C"; sidebar_link_v_c:"#66664D"; date_title_f:""; date_title_s:""; date_title_c:"#000000"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#689B9C"; post_comment_bg_c:"#E4E4E2"; post_comment_c:""; prelude_c:"#3B2C16"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#3B2C16"','Seville':'body_bg_c:"#B51A0D"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#383839"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3em"; blog_title_c:"#FFFFFF"; blog_title_a:""; blog_title_p:""; body_link_c:"#F18A32"; body_link_f_c:"#F1B232"; body_link_v_c:"#F18A32"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.4em"; sidebar_title_c:"#97471C"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#6E6E72"; sidebar_link_f_c:"#F18A32"; sidebar_link_v_c:"#6E6E72"; date_title_f:""; date_title_s:""; date_title_c:"#97471C"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#F18A32"; post_comment_bg_c:"#E4E4E2"; post_comment_c:""; prelude_c:"#381A1A"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#381A1A"','Typo':'body_bg_c:"#FFFFFF"; body_bg_g:"solid"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#000000"; body_line_height:"1.4em"; top_image:"typo"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3.5em"; blog_title_c:"#B11508"; blog_title_a:"left"; blog_title_p:"140:50"; body_link_c:"#B11508"; body_link_f_c:"#000000"; body_link_v_c:"#4D4D4D"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#000000"; sidebar_title_f:"s2"; sidebar_title_s:""; sidebar_title_c:"#B11508"; sidebar_title2_f:"s2"; sidebar_title2_s:""; sidebar_title2_c:"#000000"; sidebar_line_c:"#000000"; sidebar_link_c:"#000000"; sidebar_link_f_c:"#B11508"; sidebar_link_v_c:"#000000"; date_title_f:"s2"; date_title_s:"1em"; date_title_c:"#000000"; post_title_f:"s2"; post_title_s:"1.6em"; post_title_c:"#B11508"; post_comment_bg_c:"#FFFFFF"; post_comment_c:"#000000"; prelude_c:"#FFFFFF"; footer_f:""; footer_s:"1em"; footer_c:"#000000"; footer_l_c:"#B11508"; footer_bg_c:"#FFFFFF"'}; \ No newline at end of file diff --git a/plugins/blowupConfig/help.html b/plugins/blowupConfig/help.html new file mode 100644 index 0000000..8442326 --- /dev/null +++ b/plugins/blowupConfig/help.html @@ -0,0 +1,55 @@ + + + + Blowup theme configuration + + + +

    Modifying Blowup theme configuration allows you to customize your theme very easily. To do so, simply fill in the configuration fields or choose a predefined style.

    + +

    Colors

    + +

    When you have to enter a color value, you should use the hexadecimal format. For example: "#FF0000" will give you red. You may also use the color picker located next to every color field.

    +

    If you lack inspiration, try and visit the following pages:

    + + +

    Measurement units

    + +

    When you have to indicate a size, it must be followed by a measurement unit. For example: "1em". If you do not include the unit, pixels will be used as default measurement unit.

    +

    The following measurement units are available:

    +
      +
    • px
    • +
    • em
    • +
    • ex
    • +
    • pt
    • +
    • %
    • +
    + +

    Header images

    + +

    You can choose a header image among the list to use in place of the default one.

    + +

    When you choose "Custom..." in the image list, you will be able to upload your own image. The image file must be in JPG or PNG format and must be precisely 800px wide. + +

    If you upload a JPG image, a border will be added to the image, which is not the case with PNG images (the transparency of which will also be kept).

    + +

    Predefined styles

    + +

    You can choose a predefined style in the "Predefinite styles" drop-down list. +Once you have chosen the style, you have to submit the form to apply the modifications.

    + +

    You may then modify the predefined style according to your taste and needs.

    + +

    Configuration import / export

    + +

    At the end of Blowup options, you can display an area names "Configuration import/export". This text area contains the configuration you are currently using. You can copy it to share it with others.

    + +

    To apply (import) a configuration, you simply need to replace the content of the text area with the configuration you want to use. Don't forget to click on "apply code".

    + + + diff --git a/plugins/blowupConfig/index.php b/plugins/blowupConfig/index.php new file mode 100644 index 0000000..0db37f6 --- /dev/null +++ b/plugins/blowupConfig/index.php @@ -0,0 +1,449 @@ + null, + 'body_bg_g' => 'light', + + 'body_txt_f' => null, + 'body_txt_s' => null, + 'body_txt_c' => null, + 'body_line_height' => null, + + 'top_image' => 'default', + 'top_height' => null, + 'uploaded' => null, + + 'blog_title_hide' => null, + 'blog_title_f' => null, + 'blog_title_s' => null, + 'blog_title_c' => null, + 'blog_title_a' => null, + 'blog_title_p' => null, + + 'body_link_c' => null, + 'body_link_f_c' => null, + 'body_link_v_c' => null, + + 'sidebar_position' => null, + 'sidebar_text_f' => null, + 'sidebar_text_s' => null, + 'sidebar_text_c' => null, + 'sidebar_title_f' => null, + 'sidebar_title_s' => null, + 'sidebar_title_c' => null, + 'sidebar_title2_f' => null, + 'sidebar_title2_s' => null, + 'sidebar_title2_c' => null, + 'sidebar_line_c' => null, + 'sidebar_link_c' => null, + 'sidebar_link_f_c' => null, + 'sidebar_link_v_c' => null, + + 'date_title_f' => null, + 'date_title_s' => null, + 'date_title_c' => null, + + 'post_title_f' => null, + 'post_title_s' => null, + 'post_title_c' => null, + 'post_comment_bg_c' => null, + 'post_comment_c' => null, + 'post_commentmy_bg_c' => null, + 'post_commentmy_c' => null, + + 'prelude_c' => null, + 'footer_f' => null, + 'footer_s' => null, + 'footer_c' => null, + 'footer_l_c' => null, + 'footer_bg_c' => null, +); + +$blowup_user = $core->blog->settings->blowup_style; + +if ($blowup_user === null) { + $core->blog->settings->setNamespace('themes'); + $core->blog->settings->put('blowup_style','','string','Blow Up custom style',true); +} + +$blowup_user = @unserialize($blowup_user); +if (!is_array($blowup_user)) { + $blowup_user = array(); +} + +$blowup_user = array_merge($blowup_base,$blowup_user); + +$gradient_types = array( + __('Light linear gradient') => 'light', + __('Medium linear gradient') => 'medium', + __('Dark linear gradient') => 'dark', + __('Solid color') => 'solid' +); + +$top_images = array(__('Custom...') => 'custom'); +$top_images = array_merge($top_images,array_flip(blowupConfig::$top_images)); + + +if (!empty($_POST)) +{ + try + { + $blowup_user['body_txt_f'] = $_POST['body_txt_f']; + $blowup_user['body_txt_s'] = blowupConfig::adjustFontSize($_POST['body_txt_s']); + $blowup_user['body_txt_c'] = blowupConfig::adjustColor($_POST['body_txt_c']); + $blowup_user['body_line_height'] = blowupConfig::adjustFontSize($_POST['body_line_height']); + + $blowup_user['blog_title_hide'] = (integer) !empty($_POST['blog_title_hide']); + $update_blog_title = !$blowup_user['blog_title_hide'] && ( + !empty($_POST['blog_title_f']) || !empty($_POST['blog_title_s']) || + !empty($_POST['blog_title_c']) || !empty($_POST['blog_title_a']) || + !empty($_POST['blog_title_p']) + ); + + if ($update_blog_title) + { + $blowup_user['blog_title_f'] = $_POST['blog_title_f']; + $blowup_user['blog_title_s'] = blowupConfig::adjustFontSize($_POST['blog_title_s']); + $blowup_user['blog_title_c'] = blowupConfig::adjustColor($_POST['blog_title_c']); + $blowup_user['blog_title_a'] = preg_match('/^(left|center|right)$/',$_POST['blog_title_a']) ? $_POST['blog_title_a'] : null; + $blowup_user['blog_title_p'] = blowupConfig::adjustPosition($_POST['blog_title_p']); + } + + $blowup_user['body_link_c'] = blowupConfig::adjustColor($_POST['body_link_c']); + $blowup_user['body_link_f_c'] = blowupConfig::adjustColor($_POST['body_link_f_c']); + $blowup_user['body_link_v_c'] = blowupConfig::adjustColor($_POST['body_link_v_c']); + + $blowup_user['sidebar_text_f'] = $_POST['sidebar_text_f']; + $blowup_user['sidebar_text_s'] = blowupConfig::adjustFontSize($_POST['sidebar_text_s']); + $blowup_user['sidebar_text_c'] = blowupConfig::adjustColor($_POST['sidebar_text_c']); + $blowup_user['sidebar_title_f'] = $_POST['sidebar_title_f']; + $blowup_user['sidebar_title_s'] = blowupConfig::adjustFontSize($_POST['sidebar_title_s']); + $blowup_user['sidebar_title_c'] = blowupConfig::adjustColor($_POST['sidebar_title_c']); + $blowup_user['sidebar_title2_f'] = $_POST['sidebar_title2_f']; + $blowup_user['sidebar_title2_s'] = blowupConfig::adjustFontSize($_POST['sidebar_title2_s']); + $blowup_user['sidebar_title2_c'] = blowupConfig::adjustColor($_POST['sidebar_title2_c']); + $blowup_user['sidebar_line_c'] = blowupConfig::adjustColor($_POST['sidebar_line_c']); + $blowup_user['sidebar_link_c'] = blowupConfig::adjustColor($_POST['sidebar_link_c']); + $blowup_user['sidebar_link_f_c'] = blowupConfig::adjustColor($_POST['sidebar_link_f_c']); + $blowup_user['sidebar_link_v_c'] = blowupConfig::adjustColor($_POST['sidebar_link_v_c']); + + $blowup_user['sidebar_position'] = ($_POST['sidebar_position'] == 'left') ? 'left' : null; + + $blowup_user['date_title_f'] = $_POST['date_title_f']; + $blowup_user['date_title_s'] = blowupConfig::adjustFontSize($_POST['date_title_s']); + $blowup_user['date_title_c'] = blowupConfig::adjustColor($_POST['date_title_c']); + + $blowup_user['post_title_f'] = $_POST['post_title_f']; + $blowup_user['post_title_s'] = blowupConfig::adjustFontSize($_POST['post_title_s']); + $blowup_user['post_title_c'] = blowupConfig::adjustColor($_POST['post_title_c']); + $blowup_user['post_comment_c'] = blowupConfig::adjustColor($_POST['post_comment_c']); + $blowup_user['post_commentmy_c'] = blowupConfig::adjustColor($_POST['post_commentmy_c']); + + + $blowup_user['footer_f'] = $_POST['footer_f']; + $blowup_user['footer_s'] = blowupConfig::adjustFontSize($_POST['footer_s']); + $blowup_user['footer_c'] = blowupConfig::adjustColor($_POST['footer_c']); + $blowup_user['footer_l_c'] = blowupConfig::adjustColor($_POST['footer_l_c']); + $blowup_user['footer_bg_c'] = blowupConfig::adjustColor($_POST['footer_bg_c']); + + if ($can_write_images) + { + $uploaded = null; + if ($blowup_user['uploaded'] && is_file(blowupConfig::imagesPath().'/'.$blowup_user['uploaded'])) { + $uploaded = blowupConfig::imagesPath().'/'.$blowup_user['uploaded']; + } + + if (!empty($_FILES['upfile']) && !empty($_FILES['upfile']['name'])) { + files::uploadStatus($_FILES['upfile']); + $uploaded = blowupConfig::uploadImage($_FILES['upfile']); + $blowup_user['uploaded'] = basename($uploaded); + } + + $blowup_user['top_image'] = in_array($_POST['top_image'],$top_images) ? $_POST['top_image'] : 'default'; + + $blowup_user['body_bg_c'] = blowupConfig::adjustColor($_POST['body_bg_c']); + $blowup_user['body_bg_g'] = in_array($_POST['body_bg_g'],$gradient_types) ? $_POST['body_bg_g'] : ''; + $blowup_user['post_comment_bg_c'] = blowupConfig::adjustColor($_POST['post_comment_bg_c']); + $blowup_user['post_commentmy_bg_c'] = blowupConfig::adjustColor($_POST['post_commentmy_bg_c']); + $blowup_user['prelude_c'] = blowupConfig::adjustColor($_POST['prelude_c']); + blowupConfig::createImages($blowup_user,$uploaded); + } + + $core->blog->settings->setNamespace('themes'); + $core->blog->settings->put('blowup_style',serialize($blowup_user)); + $core->blog->triggerBlog(); + + http::redirect($p_url.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +?> + + + <?php echo __('Blowup configuration'); ?> + + + + + + +'.html::escapeHTML($core->blog->name). +' › '.__('Blog aspect').' › '.__('Blowup configuration').''. +'

    '.__('back').'

    '; + + +if (!$can_write_images) { + echo '

    '.__('As images cannot be created, you won\'t be able to change some background properties.').'

    '; +} + +if (!empty($_GET['upd'])) { + echo '

    '.__('Theme configuration has been successfully updated.').'

    '; +} + +echo '
    '; + +echo '
    '.__('General').''; + +if ($can_write_images) { + echo + '

    '. + + '

    '; +} + +echo +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '. + +'
    '.__('Links').''. +'

    '. + +'

    '. + +'

    '. +'
    '. + +'
    '.__('Page top').''; + +if ($can_write_images) { + echo + '

    '; +} + +echo +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '; + +if ($can_write_images) { + if ($blowup_user['top_image'] == 'custom' && $blowup_user['uploaded']) { + $preview_image = http::concatURL($core->blog->url,blowupConfig::imagesURL().'/page-t.png'); + } else { + $preview_image = 'index.php?pf=blowupConfig/alpha-img/page-t/'.$blowup_user['top_image'].'.png'; + } + + echo + '
    '.__('Top image').''. + '

    '. + '

    '.__('Choose "Custom..." to upload your own image.').'

    '. + + '

    '. + + '

    '.__('Preview').'

    '. + '
    '. + ''. + '
    '. + '
    '; +} + +echo +'
    '.__('Sidebar').''. +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '. + +'
    '.__('Entries').''. +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '; + +if ($can_write_images) { + echo + '

    '; +} + +echo +'

    '; + +if ($can_write_images) { + echo + '

    '; +} + +echo +'

    '. +'
    '. + +'
    '.__('Footer').''. +'

    '. + +'

    '. + +'

    '. + +'

    '. + +'

    '. +'
    '; + +// Import / Export configuration +$tmp_array = array(); +$tmp_exclude = array('uploaded','top_height'); +if ($blowup_user['top_image'] == 'custom') { + $tmp_exclude[] = 'top_image'; +} +foreach ($blowup_user as $k => $v) { + if (!in_array($k,$tmp_exclude)) { + $tmp_array[] = $k.':'.'"'.$v.'"'; + } +} +echo +'

    '.__('Configuration import / export').'

    '. +'

    '.__('You can share your configuration using the following code. To apply a configuration, paste the code, click on "Apply code" and save.').'

    '. +'

    '.form::textarea('export_code',72,5,implode('; ',$tmp_array),'maximal').'

    '. +'
    '; + +echo +'

    '. +$core->formNonce().'

    '. +'
    '; + +dcPage::helpBlock('blowupConfig'); +?> + + \ No newline at end of file diff --git a/plugins/blowupConfig/lib/class.blowup.config.php b/plugins/blowupConfig/lib/class.blowup.config.php new file mode 100644 index 0000000..6aa78d2 --- /dev/null +++ b/plugins/blowupConfig/lib/class.blowup.config.php @@ -0,0 +1,418 @@ + array( + 'ss1' => 'Arial, Helvetica, sans-serif', + 'ss2' => 'Verdana,Geneva, Arial, Helvetica, sans-serif', + 'ss3' => '"Lucida Grande", "Lucida Sans Unicode", sans-serif', + 'ss4' => '"Trebuchet MS", Helvetica, sans-serif', + 'ss5' => 'Impact, Charcoal, sans-serif' + ), + + 'serif' => array( + 's1' => 'Times, "Times New Roman", serif', + 's2' => 'Georgia, serif', + 's3' => 'Baskerville, "Palatino Linotype", serif' + ), + + 'monospace' => array( + 'm1' => '"Andale Mono", "Courier New", monospace', + 'm2' => '"Courier New", Courier, mono, monospace' + ) + ); + + protected static $fonts_combo = array(); + protected static $fonts_list = array(); + + public static $top_images = array( + 'default' => 'Default', + 'blank' => 'Blank', + 'light-trails-1' => 'Light Trails 1', + 'light-trails-2' => 'Light Trails 2', + 'light-trails-3' => 'Light Trails 3', + 'light-trails-4' => 'Light Trails 4', + 'butterflies' => 'Butterflies', + 'flourish-1' => 'Flourished 1', + 'flourish-2' => 'Flourished 2', + 'animals' => 'Animals', + 'flamingo' => 'Flamingo', + 'rabbit' => 'Rabbit', + 'roadrunner-1' => 'Road Runner 1', + 'roadrunner-2' => 'Road Runner 2', + 'typo' => 'Typo', + ); + + public static function fontsList() + { + if (empty(self::$fonts_combo)) + { + self::$fonts_combo[__('default')] = ''; + foreach (self::$fonts as $family => $g) + { + $fonts = array(); + foreach ($g as $code => $font) { + $fonts[str_replace('"','',$font)] = $code; + } + self::$fonts_combo[$family] = $fonts; + } + } + + return self::$fonts_combo; + } + + public static function fontDef($c) + { + if (empty(self::$fonts_list)) + { + foreach (self::$fonts as $family => $g) + { + foreach ($g as $code => $font) { + self::$fonts_list[$code] = $font; + } + } + } + + return isset(self::$fonts_list[$c]) ? self::$fonts_list[$c] : null; + } + + public static function adjustFontSize($s) + { + if (preg_match('/^([0-9.]+)\s*(%|pt|px|em|ex)?$/',$s,$m)) { + if (empty($m[2])) { + $m[2] = 'px'; + } + return $m[1].$m[2]; + } + + return null; + } + + public static function adjustPosition($p) + { + if (!preg_match('/^[0-9]+(:[0-9]+)?$/',$p)) { + return null; + } + + $p = explode(':',$p); + + return $p[0].(count($p) == 1 ? ':0' : ':'.$p[1]); + } + + 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 ''; + } + + public static function imagesPath() + { + global $core; + return path::real($core->blog->public_path).'/blowup-images'; + } + + public static function imagesURL() + { + global $core; + return $core->blog->settings->public_url.'/blowup-images'; + } + + public static function canWriteImages($create=false) + { + global $core; + + $public = path::real($core->blog->public_path); + $imgs = self::imagesPath(); + + if (!function_exists('imagecreatetruecolor') || !function_exists('imagepng') || !function_exists('imagecreatefrompng')) { + return false; + } + + if (!is_dir($public)) { + return false; + } + + if (!is_dir($imgs)) { + if (!is_writable($public)) { + return false; + } + if ($create) { + files::makeDir($imgs); + } + return true; + } + + if (!is_writable($imgs)) { + return false; + } + + return true; + } + + public static function uploadImage($f) + { + if (!self::canWriteImages(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().'/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.')); + } + + $s = getimagesize($dest); + if ($s[0] != 800) { + throw new Exception('Uploaded image is not 800 pixels wide.'); + } + + return $dest; + } + + public static function createImages(&$config,$uploaded) + { + $body_color = $config['body_bg_c']; + $prelude_color = $config['prelude_c']; + $gradient = $config['body_bg_g']; + $comment_color = $config['post_comment_bg_c']; + $comment_color_my = $config['post_commentmy_bg_c']; + $top_image = $config['top_image']; + + $config['top_height'] = null; + + if ($top_image != 'custom' && !isset(self::$top_images[$top_image])) { + $top_image = 'default'; + } + if ($uploaded && !is_file($uploaded)) { + $uploaded = null; + } + + if (!self::canWriteImages(true)) { + throw new Exception(__('Unable to create images.')); + } + + $body_fill = array( + 'light' => dirname(__FILE__).'/../alpha-img/gradient-l.png', + 'medium' => dirname(__FILE__).'/../alpha-img/gradient-m.png', + 'dark' => dirname(__FILE__).'/../alpha-img/gradient-d.png' + ); + + $body_g = isset($body_fill[$gradient]) ? $body_fill[$gradient] : false; + + if ($top_image == 'custom' && $uploaded) { + $page_t = $uploaded; + } else { + $page_t = dirname(__FILE__).'/../alpha-img/page-t/'.$top_image.'.png'; + } + + $body_bg = dirname(__FILE__).'/../alpha-img/body-bg.png'; + $page_t_mask = dirname(__FILE__).'/../alpha-img/page-t/image-mask.png'; + $page_b = dirname(__FILE__).'/../alpha-img/page-b.png'; + $comment_t = dirname(__FILE__).'/../alpha-img/comment-t.png'; + $comment_b = dirname(__FILE__).'/../alpha-img/comment-b.png'; + $default_bg = '#e0e0e0'; + $default_prelude = '#ededed'; + + self::dropImage(basename($body_bg)); + self::dropImage('page-t.png'); + self::dropImage(basename($page_b)); + self::dropImage(basename($comment_t)); + self::dropImage(basename($comment_b)); + + $body_color = self::adjustColor($body_color); + $prelude_color = self::adjustColor($prelude_color); + $comment_color = self::adjustColor($comment_color); + + if ($top_image || $body_color || $gradient != 'light' || $prelude_color || $uploaded) + { + if (!$body_color) { + $body_color = $default_bg; + } + $body_color = sscanf($body_color,'#%2X%2X%2X'); + + # Create body gradient with color + $d_body_bg = imagecreatetruecolor(50,180); + $fill = imagecolorallocate($d_body_bg,$body_color[0],$body_color[1],$body_color[2]); + imagefill($d_body_bg,0,0,$fill); + + # User choosed a gradient + if ($body_g) { + $s_body_bg = imagecreatefrompng($body_g); + imagealphablending($s_body_bg,true); + imagecopy($d_body_bg,$s_body_bg,0,0,0,0,50,180); + imagedestroy($s_body_bg); + } + + if (!$prelude_color) { + $prelude_color = $default_prelude; + } + $prelude_color = sscanf($prelude_color,'#%2X%2X%2X'); + + $s_prelude = imagecreatetruecolor(50,30); + $fill = imagecolorallocate($s_prelude,$prelude_color[0],$prelude_color[1],$prelude_color[2]); + imagefill($s_prelude,0,0,$fill); + imagecopy($d_body_bg,$s_prelude,0,0,0,0,50,30); + + imagepng($d_body_bg,self::imagesPath().'/'.basename($body_bg)); + } + + if ($top_image || $body_color || $gradient != 'light') + { + # Create top image from uploaded image + $size = getimagesize($page_t); + $size = $size[1]; + $type = files::getMimeType($page_t); + + $d_page_t = imagecreatetruecolor(800,$size); + + if ($type == 'image/png') { + $s_page_t = @imagecreatefrompng($page_t); + } else { + $s_page_t = @imagecreatefromjpeg($page_t); + } + + if (!$s_page_t) { + throw new exception(__('Unable to open image.')); + } + + $fill = imagecolorallocate($d_page_t,$body_color[0],$body_color[1],$body_color[2]); + imagefill($d_page_t,0,0,$fill); + + if ($type == 'image/png') + { + # PNG, we only add body gradient and image + imagealphablending($s_page_t,true); + imagecopyresized($d_page_t,$d_body_bg,0,0,0,50,800,130,50,130); + imagecopy($d_page_t,$s_page_t,0,0,0,0,800,$size); + } + else + { + # JPEG, we add image and a frame with rounded corners + imagecopy($d_page_t,$s_page_t,0,0,0,0,800,$size); + + imagecopy($d_page_t,$d_body_bg,0,0,0,50,8,4); + imagecopy($d_page_t,$d_body_bg,0,4,0,54,4,4); + imagecopy($d_page_t,$d_body_bg,792,0,0,50,8,4); + imagecopy($d_page_t,$d_body_bg,796,4,0,54,4,4); + + $mask = imagecreatefrompng($page_t_mask); + imagealphablending($mask,true); + imagecopy($d_page_t,$mask,0,0,0,0,800,11); + imagedestroy($mask); + + $fill = imagecolorallocate($d_page_t,255,255,255); + imagefilledrectangle($d_page_t,0,11,3,$size-1,$fill); + imagefilledrectangle($d_page_t,796,11,799,$size-1,$fill); + imagefilledrectangle($d_page_t,0,$size-9,799,$size-1,$fill); + } + + $config['top_height'] = ($size).'px'; + + imagepng($d_page_t,self::imagesPath().'/page-t.png'); + + imagedestroy($d_body_bg); + imagedestroy($d_page_t); + imagedestroy($s_page_t); + + # Create bottom image with color + $d_page_b = imagecreatetruecolor(800,8); + $fill = imagecolorallocate($d_page_b,$body_color[0],$body_color[1],$body_color[2]); + imagefill($d_page_b,0,0,$fill); + + $s_page_b = imagecreatefrompng($page_b); + imagealphablending($s_page_b,true); + imagecopy($d_page_b,$s_page_b,0,0,0,0,800,160); + + imagepng($d_page_b,self::imagesPath().'/'.basename($page_b)); + + imagedestroy($d_page_b); + imagedestroy($s_page_b); + } + + if ($comment_color) { + self::commentImages($comment_color,$comment_t,$comment_b,basename($comment_t),basename($comment_b)); + } + if ($comment_color_my) { + self::commentImages($comment_color_my,$comment_t,$comment_b,'commentmy-t.png','commentmy-b.png'); + } + } + + protected static function commentImages($comment_color,$comment_t,$comment_b,$dest_t,$dest_b) + { + $comment_color = sscanf($comment_color,'#%2X%2X%2X'); + + $d_comment_t = imagecreatetruecolor(500,25); + $fill = imagecolorallocate($d_comment_t,$comment_color[0],$comment_color[1],$comment_color[2]); + imagefill($d_comment_t,0,0,$fill); + + $s_comment_t = imagecreatefrompng($comment_t); + imagealphablending($s_comment_t,true); + imagecopy($d_comment_t,$s_comment_t,0,0,0,0,500,25); + + imagepng($d_comment_t,self::imagesPath().'/'.$dest_t); + imagedestroy($d_comment_t); + imagedestroy($s_comment_t); + + $d_comment_b = imagecreatetruecolor(500,7); + $fill = imagecolorallocate($d_comment_b,$comment_color[0],$comment_color[1],$comment_color[2]); + imagefill($d_comment_b,0,0,$fill); + + $s_comment_b = imagecreatefrompng($comment_b); + imagealphablending($s_comment_b,true); + imagecopy($d_comment_b,$s_comment_b,0,0,0,0,500,7); + + imagepng($d_comment_b,self::imagesPath().'/'.$dest_b); + imagedestroy($d_comment_b); + imagedestroy($s_comment_b); + } + + public static function dropImage($img) + { + $img = path::real(self::imagesPath().'/'.$img); + if (is_writable(dirname($img))) { + @unlink($img); + @unlink(dirname($img).'/.'.basename($img,'.png').'_sq.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_m.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_s.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_sq.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_t.jpg'); + } + } +} +?> \ No newline at end of file diff --git a/plugins/daInstaller/_admin.php b/plugins/daInstaller/_admin.php new file mode 100644 index 0000000..b63b72e --- /dev/null +++ b/plugins/daInstaller/_admin.php @@ -0,0 +1,58 @@ +auth->isSuperAdmin()) + { + $id = 'dainstaller'; + $title = __('DotAddict.org Installer'); + $link = 'plugin.php?p=daInstaller'; + $icon = 'index.php?pf=daInstaller/icon.png'; + $icon_big = 'index.php?pf=daInstaller/icon-big.png'; + $favs[$id] = new ArrayObject(array($id,$title,$link,$icon,$icon_big,null,null,null)); + } +} +function adminDashboardFavsIcon($core,$name,$icon) +{ + if ($name === 'dainstaller') { + $daInstaller = new daInstaller($core); + if ($daInstaller->check()) { + $upd_plugins = $daInstaller->getModules('plugins',true); + $upd_themes = $daInstaller->getModules('themes',true); + if ( + (is_array($upd_plugins) && count($upd_plugins)) || + (is_array($upd_themes) && count($upd_themes)) + ) { + $icon[0] .= '
    '.__('Updates are available'); + $icon[1] .= '&tab=update'; + $icon[2] = 'index.php?pf=daInstaller/icon-big-update.png'; + } + } + } +} + +$core->addBehavior('adminDashboardFavs','adminDashboardFavs'); +$core->addBehavior('adminDashboardFavsIcon','adminDashboardFavsIcon'); + +$_menu['System']->addItem( + __('DotAddict.org Installer'), + 'plugin.php?p=daInstaller', + 'index.php?pf=daInstaller/icon.png', + preg_match('/plugin.php\?p=daInstaller(&.*)?$/', + $_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin() +); + +?> \ No newline at end of file diff --git a/plugins/daInstaller/_define.php b/plugins/daInstaller/_define.php new file mode 100644 index 0000000..70ada7b --- /dev/null +++ b/plugins/daInstaller/_define.php @@ -0,0 +1,19 @@ +registerModule( + 'daInstaller', + 'Install and update your extensions live from DotAddict.org', + 'Tomtom / DotAddict.org', + '1.0.3', + null, + 10000 +); + +?> \ No newline at end of file diff --git a/plugins/daInstaller/_install.php b/plugins/daInstaller/_install.php new file mode 100644 index 0000000..ae6ea73 --- /dev/null +++ b/plugins/daInstaller/_install.php @@ -0,0 +1,50 @@ +plugins->moduleInfo('daInstaller','version'); +$i_version = $core->getVersion('daInstaller'); +if (version_compare($i_version,$m_version,'>=')) { + return; +} + +# Settings compatibility test +if (!version_compare(DC_VERSION,'2.2-x','<')) { + $core->blog->settings->addNamespace('dainstaller'); + $s = $core->blog->settings->dainstaller; +} +else { + $core->blog->settings->setNamespace('dainstaller'); + $s = $core->blog->settings; +} + +# Création du setting +$s->put( + 'dainstaller_plugins_xml', + 'http://update.dotaddict.org/dc2/plugins.xml', + 'string','Plugins XML feed location',true,true +); +$s->put( + 'dainstaller_themes_xml', + 'http://update.dotaddict.org/dc2/themes.xml', + 'string','Themes XML feed location',true,true +); +$s->put( + 'dainstaller_allow_multi_install', + false, + 'boolean','Allow the multi-installation',true,true +); + +$daInstaller = new daInstaller($core); +$daInstaller->check(true); +unset($daInstaller); + +$core->setVersion('daInstaller',$m_version); +return true; + +?> \ No newline at end of file diff --git a/plugins/daInstaller/_prepend.php b/plugins/daInstaller/_prepend.php new file mode 100644 index 0000000..31048e2 --- /dev/null +++ b/plugins/daInstaller/_prepend.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/plugins/daInstaller/icon-big-update.png b/plugins/daInstaller/icon-big-update.png new file mode 100644 index 0000000..463909c Binary files /dev/null and b/plugins/daInstaller/icon-big-update.png differ diff --git a/plugins/daInstaller/icon-big.png b/plugins/daInstaller/icon-big.png new file mode 100644 index 0000000..77a2d9b Binary files /dev/null and b/plugins/daInstaller/icon-big.png differ diff --git a/plugins/daInstaller/icon.png b/plugins/daInstaller/icon.png new file mode 100644 index 0000000..8eb1b7c Binary files /dev/null and b/plugins/daInstaller/icon.png differ diff --git a/plugins/daInstaller/img/help.png b/plugins/daInstaller/img/help.png new file mode 100644 index 0000000..9077265 Binary files /dev/null and b/plugins/daInstaller/img/help.png differ diff --git a/plugins/daInstaller/img/support.png b/plugins/daInstaller/img/support.png new file mode 100644 index 0000000..2fb98bf Binary files /dev/null and b/plugins/daInstaller/img/support.png differ diff --git a/plugins/daInstaller/inc/class.da.installer.php b/plugins/daInstaller/inc/class.da.installer.php new file mode 100644 index 0000000..84a819b --- /dev/null +++ b/plugins/daInstaller/inc/class.da.installer.php @@ -0,0 +1,317 @@ +core = $core; + + # Settings compatibility test + $s = !version_compare(DC_VERSION,'2.2-x','<') ? $core->blog->settings->dainstaller : $core->blog->settings; + $this->themes_xml = $s->dainstaller_themes_xml; + $this->plugins_xml = $s->dainstaller_plugins_xml; + $this->modules = array( + 'plugins' => array( + 'new' => array(), + 'update' => array() + ), + 'themes' => array( + 'new' => array(), + 'update' => array() + ) + ); + } + + /** + * Check new/updated plugins availability + * Plugins already installed but marked as disabled will be ignored. + * Results of this method are stored as two subarrays 'new' and 'update' in $modules['plugins']. + * + * @param boolean $force Forces datas refresh if true (default to false) + * + * @return boolean Reports operatio status : + * - true if operation was successful, + * - false otherwise, you may check internal error trace. + * + */ + protected function checkPlugins($force = false) + { + if (!$this->plugins_xml) { + return false; + } + if (($parser = daModulesReader::quickParse($this->plugins_xml,DC_TPL_CACHE,$force)) === false) { + return false; + } + + $raw_datas = $parser->getModules(); + + uasort($raw_datas,array('self','sort')); + + # On se débarasse des plugins désactivés. + $skipped = array_keys($this->core->plugins->getDisabledModules()); + foreach ($skipped as $p_id) { + if (isset($raw_datas[$p_id])) { + unset($raw_datas[$p_id]); + } + } + + # On vérifie les mises à jour + $updates = array(); + $current = $this->core->plugins->getModules(); + foreach ($current as $p_id => $p_infos) { + if (isset($raw_datas[$p_id])) { + if (self::da_version_compare($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->modules['plugins'] = array( + 'new' => $raw_datas, + 'update' => $updates + ); + + return true; + } + + /** + * Check new/updated themes availability + * Results of this method are stored as two subarrays 'new' and 'update' in $modules['themes']. + * + * @param boolean $force Forces datas refresh if true (default to false) + * + * @return boolean Reports operatio status : + * - true if operation was successful, + * - false otherwise, you may check internal error trace. + * + */ + protected function checkThemes($force = false) + { + if (!$this->themes_xml) { + return false; + } + if (($parser = daModulesReader::quickParse($this->themes_xml,DC_TPL_CACHE,$force)) === false) { + return false; + } + + $raw_datas = $parser->getModules(); + + uasort($raw_datas,array('self','sort')); + + # On vérifie les mises à jour + $updates = array(); + $core_themes = new dcModules($this->core); + $core_themes->loadModules($this->core->blog->themes_path,null); + $current = $core_themes->getModules(); + foreach ($current as $p_id => $p_infos) { + if (isset($raw_datas[$p_id])) { + if (self::da_version_compare($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->modules['themes'] = array( + 'new' => $raw_datas, + 'update' => $updates + ); + + return true; + } + + /** + * Get informations about available new//updated themes/plugins. + * Probably the first method to invoke after instanciation. + * + * @param boolean $force Forces datas refresh if true (default to false) + * @param boolean $verbose Display exceptions when exists (default to false) + * + * @return boolean Returns if the check is completed or not + * + */ + public function check($force = false, $verbose = false) + { + $ret = true; + + try { + if (!$this->checkPlugins($force)) { + $ret = false; + } + if (!$this->checkThemes($force)) { + $ret = false; + } + } + catch (daModuleParserException $e) { + if ($verbose) { + $this->core->error->add(sprintf( + __('Oops, something went wrong with %s, we are working on it. %s'), + sprintf('%2$s','http://dotaddict.org',__('dotaddict.org website')), + sprintf('%2$s','http://dotclear.org/donate',__('Wanna help too?')) + )); + } + $ret = false; + } + catch (Exception $e) { + # Probablement à compléter. + $ret = false; + } + + return $ret; + } + + /** + * Retrieve a specific module list + * + * @param string $type The type of modules wanted ('plugins' or 'themes') + * @param boolean $update Flag to choose between new or updated modules (default to false - new modules list) + * + * @return mixed The matching modules entries as an array, if any. Or a boolean set to false. + * + */ + public function getModules($type, $update = false) + { + $type = ($type == 'themes') ? 'themes' : 'plugins'; + $what = ($update) ? 'update' : 'new'; + if (isset($this->modules[$type][$what])) { + return $this->modules[$type][$what]; + } + return false; + } + + /** + * Search a string in module id, label and description. + * Search is case-insensitive and can be apply to available themes or plugins + * + * @param string $search The search string + * @param string $type The type of targeted modules ('plugins' or 'themes') + * + * @return array An array of matching modules entries + * + */ + public function search($search,$type = 'plugins') + { + $type = ($type == 'themes') ? 'themes' : 'plugins'; + $result = array(); + + foreach ($this->modules[$type]['new'] as $module) + { + if ( preg_match('/'.$search.'/i',$module['id']) || + preg_match('/'.$search.'/i',$module['label']) || + preg_match('/'.$search.'/i',$module['desc'])) + { + $result[] = $module; + } + } + return $result; + } + + /** + * Helper method to fetch and install a ZIP package. + * + * @param string $url Source file URL + * @param string $dest Target file destination + * @param dcModules $coreModules Target modules stack + * + * @return integer Basic operation status code : + * - 1 : everything's all right, + * - 2 : tempfile couldn't be deleted. + * + */ + public function processPackage($url,$dest,dcModules $coreModules) + { + try { + $client = netHttp::initClient($url,$path); + $client->setUserAgent(self::getUserAgent()); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + } + catch (Exception $e) { + throw new Exception(__('An error occurred while downloading the file.')); + } + + unset($client); + $ret_code = dcModules::installPackage($dest,$coreModules); + + return $ret_code; + } + + /** + * Helper method to get user agent according to DC and daInstaller version + * + * @return string daInstaller user agent + * + */ + public static function getUserAgent() + { + $m_version = $GLOBALS['core']->plugins->moduleInfo('daInstaller','version'); + return sprintf('daInstaller/%s (Dotclear/%s)',$m_version,DC_VERSION); + } + + /** + * Helper method to compare correctly version. + * + * @param string $v1 Version of first module + * @param string $v2 Version of second module + * @param string $op Operator + * + * @return boolean True if test of version is correct according to operator + * + */ + private static function da_version_compare($v1,$v2,$op) + { + $v1 = preg_replace('!-r(\d+)$!','-p$1',$v1); + $v2 = preg_replace('!-r(\d+)$!','-p$1',$v2); + return version_compare($v1,$v2,$op); + } + + /** + * Helper method to sort module list. + * + * @param array $a First module + * @param array $b Second 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; + } +} + +?> \ No newline at end of file diff --git a/plugins/daInstaller/inc/class.da.modules.parser.php b/plugins/daInstaller/inc/class.da.modules.parser.php new file mode 100644 index 0000000..29c9e57 --- /dev/null +++ b/plugins/daInstaller/inc/class.da.modules.parser.php @@ -0,0 +1,81 @@ +xml = simplexml_load_string($data); + $this->items = array(); + + if ($this->xml === false) { + throw new daModuleParserException(__('Wrong data feed')); + } + + $this->_parse(); + + unset($data); + unset($this->xml); + } + + protected function _parse() + { + if (empty($this->xml->module)) { + return; + } + + foreach ($this->xml->module as $i) + { + $attrs = $i->attributes(); + + $item = array(); + + # DC/DA shared markers + $item['id'] = (string) $attrs['id']; + $item['file'] = (string) $i->file; + $item['label'] = (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('http://dotaddict.org/da/')->dcmin; + $item['details'] = (string) $i->children('http://dotaddict.org/da/')->details; + /*$item['section'] = (string) $i->children('http://dotaddict.org/da/')->section;*/ + $item['support'] = (string) $i->children('http://dotaddict.org/da/')->support; + $item['sshot'] = (string) $i->children('http://dotaddict.org/da/')->sshot; + + # First filter right now + if (version_compare(DC_VERSION,$item['dc_min'],'>=')) { + $this->items[$item['id']] = $item; + } + } + } + + public function getModules() + { + return $this->items; + } +} + +?> \ No newline at end of file diff --git a/plugins/daInstaller/inc/class.da.modules.reader.php b/plugins/daInstaller/inc/class.da.modules.reader.php new file mode 100644 index 0000000..3a24dd1 --- /dev/null +++ b/plugins/daInstaller/inc/class.da.modules.reader.php @@ -0,0 +1,196 @@ +array HTTP Cache validators + protected $cache_dir = null; ///< string Cache temporary directory + protected $cache_file_prefix = 'daorg'; ///< string Cache file prefix + protected $cache_ttl = '-30 minutes'; ///< string Cache TTL + protected $force = false; + + public function __construct() + { + parent::__construct(''); + $this->setUserAgent(daInstaller::getUserAgent()); + } + + public function parse($url) + { + $this->validators = array(); + if ($this->cache_dir) + { + return $this->withCache($url); + } + else + { + if (!$this->getModulesXML($url)) { + return false; + } + + if ($this->getStatus() != '200') { + return false; + } + + return new daModulesParser($this->getContent()); + } + } + + 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); + } + + 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; + } + + public function setCacheTTL($str) + { + $str = trim($str); + if (!empty($str)) + { + if (substr($str,0,1) != '-') { + $str = '-'.$str; + } + $this->cache_ttl = $str; + } + } + + public function setForce($force) + { + $this->force = $force; + } + + 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); + + return $this->get($path); + } + + 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; + + 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); + } + + if (!$this->getModulesXML($url)) + { + if ($may_use_cached) + { + # connection failed - fetched from cache + return unserialize(file_get_contents($cached_file)); + } + return false; + } + + switch ($this->getStatus()) + { + case '304': + @files::touch($cached_file); + return unserialize(file_get_contents($cached_file)); + case '200': + if ($modules = new daModulesParser($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; + } + + 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; + } + + private function setValidator($key,$value) + { + if ($key == 'IfModifiedSince') { + $value = gmdate('D, d M Y H:i:s',$value).' GMT'; + } + + $this->validators[$key] = $value; + } +} + +?> \ No newline at end of file diff --git a/plugins/daInstaller/inc/lib.da.installer.ui.php b/plugins/daInstaller/inc/lib.da.installer.ui.php new file mode 100644 index 0000000..aced565 --- /dev/null +++ b/plugins/daInstaller/inc/lib.da.installer.ui.php @@ -0,0 +1,310 @@ +rs->isEmpty()) { + echo '

    '.$msg_no_entry.'

    '; + } + else { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + if (!$search) { + $pager->base_url = $url.'&tab='.$type.'&page=%s'; + } + else { + $pager->var_pager = 'page'; + } + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s'. + '
    '.$msg_th_label.''.__('Lastest Version').''.__('Quick description').''.__('Actions').'
    '; + + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + $blocks = explode('%s',$html_block); + echo $blocks[0]; + + $this->rs->index(((integer)$page - 1) * $nb_per_page); + $iter = 0; + while ($iter < $nb_per_page) { + echo $this->{$lineMethod}($url); + if ($this->rs->isEnd()) { + break; + } + else { + $this->rs->moveNext(); + $iter++; + } + } + echo $blocks[1]; + echo '

    '.__('Page(s)').' : '.$pager->getLinks().'

    '; + } + } + + /** + * Return a generic plugin row + * + * @param string url + * + * @return string + */ + private function pluginLine($url) + { + return + ''."\n". + # Extension + ''. + ''.html::escapeHTML($this->rs->id).''. + "\n". + # Version + ''. + html::escapeHTML($this->rs->version). + "\n". + # Quick description + ''. + '

    '.html::escapeHTML($this->rs->label).'
    '. + ''.html::escapeHTML($this->rs->desc).'

    '. + __('by').' '.html::escapeHTML($this->rs->author).'
    '. + '( '.__('More details').' )'. + "\n". + # Action + ''. + '
    '. + '

    '. + $this->core->formNonce(). + '

    '. + '
    '. + "\n". + ''."\n"; + } + + /** + * Return a generic theme row + * + * @param string url + * + * @return string + */ + private function themeLine($url) + { + return + ''."\n". + # Extension + ''. + ''.html::escapeHTML($this->rs->id).''. + '

    '. + "\n". + # Version + ''. + html::escapeHTML($this->rs->version). + "\n". + # Quick description + ''. + '

    '.html::escapeHTML($this->rs->label).'
    '. + ''.html::escapeHTML($this->rs->desc).'

    '. + __('by').' '.html::escapeHTML($this->rs->author).'
    '. + '( '.__('More details').' )'. + "\n". + # Action + ''. + '
    '. + '

    '. + $this->core->formNonce(). + '

    '. + '
    '. + "\n". + ''."\n"; + } +} + +/** + * Class daModulesUpdateList + */ +class daModulesUpdateList +{ + protected $rs; + protected $nb; + + /** + * Class constructor + */ + public function __construct($core,$rs,$nb) + { + $this->core = $core; + $this->rs = $rs; + $this->nb = $nb; + $this->p_link = '%s'; + } + + /** + * Display data table for plugins and themes update lists + * + * @param string type + * @param string url + * + * @return string + */ + public function display($type,$url) + { + $type = ($type == 'themes') ? 'themes' : 'plugins'; + if ($type == 'themes') { + $msg_th_label = __('Theme'); + $lineMethod = 'themeLine'; + } + else { + $msg_th_label = __('Plugins'); + $lineMethod = 'pluginLine'; + } + + $iter = 0; + $items = ''; + $html_block = + '
    '. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%2$s'. + '
    '.$msg_th_label.''.__('Lastest Version').''.__('Quick description').'
    '. + '
    '. + '

    '. + '

    '. + $this->core->formNonce(). + '

    '. + '
    '. + '
    '; + + while ($iter < $this->rs->count()) { + $items .= $this->{$lineMethod}($url); + $this->rs->moveNext(); + $iter++; + } + + echo $this->nb > 0 ? sprintf($html_block,$type,$items) : ''; + + } + + /** + * Return a update plugin row + * + * @param string url + * + * @return string + */ + private function pluginLine($url) + { + $support = + strlen($this->rs->support) > 0 ? + sprintf($this->p_link,$this->rs->support,'support modal',__('Support')) : + ''.__('No support available').''; + + return + ''."\n". + # Extension + ''. + form::checkbox(array('plugins_id[]'),$this->rs->id). + ''.html::escapeHTML($this->rs->id).''. + "\n". + # Version + ''. + html::escapeHTML($this->rs->version). + "\n". + # Quick description + ''. + '

    '.html::escapeHTML($this->rs->label).'
    '. + ''.html::escapeHTML($this->rs->desc).'

    '. + __('by').' '.html::escapeHTML($this->rs->author).'
    '. + '( '. + __('More details').' - '.$support.' )'. + "\n". + ''."\n"; + } + + /** + * Return a update theme row + * + * @param string url + * + * @return string + */ + private function themeLine($url) + { + $support = + strlen($this->rs->support) > 0 ? + sprintf($this->p_link,$this->rs->support,'support modal',__('Support')) : + ''.__('No support available').''; + + return + ''."\n". + # Themes + ''. + form::checkbox(array('themes_id[]'),$this->rs->id). + ''.html::escapeHTML($this->rs->id).''. + '

    '. + "\n". + # Version + ''. + html::escapeHTML($this->rs->version). + "\n". + # Quick description + ''. + '

    '.html::escapeHTML($this->rs->label).'
    '. + ''.html::escapeHTML($this->rs->desc).'

    '. + __('by').' '.html::escapeHTML($this->rs->author).'
    '. + '( '. + __('More details').' - '.$support.' )'. + "\n". + ''."\n"; + } +} + +?> \ No newline at end of file diff --git a/plugins/daInstaller/index.php b/plugins/daInstaller/index.php new file mode 100644 index 0000000..e10e391 --- /dev/null +++ b/plugins/daInstaller/index.php @@ -0,0 +1,321 @@ +blog->settings->addNamespace('dainstaller'); + $s = $core->blog->settings->dainstaller; +} +else { + $core->blog->settings->setNamespace('dainstaller'); + $s = $core->blog->settings; +} + +# Initialisation des variables +$p_url = 'plugin.php?p=daInstaller'; +$default_tab = !empty($_GET['tab']) ? html::escapeHTML($_GET['tab']) : 'plugins'; +$page = !empty($_GET['page']) ? (integer)$_GET['page'] : 1; +$nb_per_page = 10; +$q = !empty($_GET['q']) ? trim(html::escapeHTML($_GET['q'])) : ''; +$default_tab = !empty($q) ? 'search' : $default_tab; +$mode = isset($_GET['mode']) ? html::escapeHTML($_GET['mode']) : 'plugins'; +$ppaths = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT); +$plugins_path = array_pop($ppaths); +$daInstaller = new daInstaller($core); +unset($ppaths); + +$pages['plugins'] = $pages['themes'] = $pages['search'] = 1; +$pages[$default_tab] = $page; + +# Ajout d'un plugin +if (!empty($_POST['add_plugin']) && !empty($_POST['package_url'])) { + try { + $url = html::escapeHTML($_POST['package_url']); + $dest = $plugins_path.'/'.basename($url); + + $ret_code = $daInstaller->processPackage($url,$dest,$core->plugins); + http::redirect($p_url.'&p_added='.$ret_code); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'plugins'; + } +} +# Mise à jour de plugins +elseif (!empty($_POST['upd_plugins']) && !empty($_POST['plugins_id'])) { + try { + $daInstaller->check(); + $ids = $_POST['plugins_id']; + $modules = $daInstaller->getModules('plugins',true); + + foreach ($modules as $module) { + if (in_array($module['id'],$ids)) { + if (!$s->dainstaller_allow_multi_install) { + $dest = $module['root'].'/../'.basename($module['file']); + } + else { + $dest = $plugins_path.'/'.basename($module['file']); + if ($module['root'] != $dest) @file_put_contents($module['root'].'/_disabled',''); + } + $ret_code[] = $daInstaller->processPackage($module['file'],$dest,$core->plugins); + } + } + + $arg = 'p_updated='.implode('|',$ids).'&p_status='.implode('|',$ret_code); + + http::redirect($p_url.'&tab=update&'.$arg); + + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'update'; + } +} +# Ajout d'un thème +elseif (!empty($_POST['add_theme']) && !empty($_POST['package_url'])) { + try { + $url = html::escapeHTML($_POST['package_url']); + $dest = $core->blog->themes_path.'/'.basename($url); + + $core_themes = new dcModules($core); + $core_themes->loadModules($core->blog->themes_path,null); + $ret_code = $daInstaller->processPackage($url,$dest,$core_themes); + http::redirect($p_url.'&tab=themes&t_added='.$ret_code); + } + catch (Exception $e) { + unset($core_themes); + $core->error->add($e->getMessage()); + $default_tab = 'themes'; + } +} +# Mise à jour de thèmes +elseif (!empty($_POST['upd_themes']) && !empty($_POST['themes_id'])) { + try { + $daInstaller->check(); + $core_themes = new dcModules($core); + $core_themes->loadModules($core->blog->themes_path,null); + $ids = $_POST['themes_id']; + $modules = $daInstaller->getModules('themes',true); + + foreach ($modules as $module) { + if (in_array($module['id'],$ids)) { + $dest = $core->blog->themes_path.'/'.basename($module['file']); + $ret_code[] = $daInstaller->processPackage($module['file'],$dest,$core_themes); + } + } + + $arg = 't_updated='.implode('|',$ids).'&t_status='.implode('|',$ret_code); + + http::redirect($p_url.'&tab=update&'.$arg); + + } + catch (Exception $e) { + unset($core_themes); + $core->error->add($e->getMessage()); + $default_tab = 'update'; + } +} + +# Et c'est parti ! +$plugins_install = $core->plugins->installModules(); +$daInstaller->check(false,true); + +/** + * This function returns all error / success messages + * + * @return string + */ +function infoMessages() +{ + $res = ''; + $p_msg = '

    %s

    '; + $p_err = '

    %s

    '; + + # Plugins install message + if (!empty($_GET['p_added']) && $_GET['p_added'] != 2) { + $res .= sprintf($p_msg,__('The plugin has been successfully installed.')); + } + # Themes install message + if (!empty($_GET['t_added']) && $_GET['t_added'] != 2) { + $res .= sprintf($p_msg,__('The theme has been successfully installed.')); + } + # Plugins update message + if (!empty($_GET['p_updated'])) { + $err = $upd = ''; + $ids = explode('|',html::escapeHTML($_GET['p_updated'])); + $status = explode('|',html::escapeHTML($_GET['p_status'])); + foreach ($ids as $k => $v) { + if ($status[$k] != 2) { + $err .= '
  • '.$v.'
  • '; + } + else { + $upd .= '
  • '.$v.'
  • '; + } + } + if (!empty($err)) { + $res .= '
    '.__('Following plugins have not been updated:').'
      '.$err.'
    '; + } + else { + $res .= '
    '.__('Following plugins have been updated:').'
      '.$upd.'
    '; + } + } + # Themes update message + if (!empty($_GET['t_updated'])) { + $err = $upd = ''; + $ids = explode('|',html::escapeHTML($_GET['t_updated'])); + $status = explode('|',html::escapeHTML($_GET['t_status'])); + foreach ($ids as $k => $v) { + if ($status[$k] != 2) { + $err .= '
  • '.$v.'
  • '; + } + else { + $upd .= '
  • '.$v.'
  • '; + } + } + if (!empty($err)) { + $res .= '
    '.__('Following themes have not been updated:').'
      '.$err.'
    '; + } + else { + $res .= '
    '.__('Following themes have been updated:').'
      '.$upd.'
    '; + } + } + # Plugins install settings messages + if (!empty($plugins_install['success'])) { + $res .= '
    '.__('Following plugins have been installed:').'
      '; + foreach ($plugins_install['success'] as $k => $v) { + $res .= '
    • '.$k.'
    • '; + } + $res .= '
    '; + } + if (!empty($plugins_install['failure'])) { + $res .= '
    '.__('Following plugins have not been installed:').'
      '; + foreach ($plugins_install['failure'] as $k => $v) { + $res .= '
    • '.$k.' ('.$v.')
    • '; + } + $res .= '
    '; + } + + return $res; +} + +# Récupération de la liste des mises à jour des plugins +# et préparation de l'objet d'affichage +$u_p_rs = $daInstaller->getModules('plugins',true); +$u_p_nb = count($u_p_rs); +$u_p_rs = staticRecord::newFromArray($u_p_rs); +$u_p_list = new daModulesUpdateList($core,$u_p_rs,$u_p_nb); + +# Récupération de la liste des mises à jour des thèmes +# et préparation de l'objet d'affichage +$u_t_rs = $daInstaller->getModules('themes',true); +$u_t_nb = count($u_t_rs); +$u_t_rs = staticRecord::newFromArray($u_t_rs); +$u_t_list = new daModulesUpdateList($core,$u_t_rs,$u_t_nb); + +# Récupération de la liste des plugins disponibles +# et préparation de l'objet d'affichage +$avail_plugins = $daInstaller->getModules('plugins'); +$a_p_nb = count($avail_plugins); +$a_p_rs = staticRecord::newFromArray($avail_plugins); +$a_p_list = new daModulesList($core,$a_p_rs,$a_p_nb); + +# Récupération de la liste des thèmes disponibles +# et préparation de l'objet d'affichage +$avail_themes = $daInstaller->getModules('themes'); +$a_t_nb = count($avail_themes); +$a_t_rs = staticRecord::newFromArray($avail_themes); +$a_t_list = new daModulesList($core,$a_t_rs,$a_t_nb); + +# Récupération de la liste des plugins recherchés +if (!empty($q)) +{ + $default_tab = 'search'; + $search_modules = $daInstaller->search($q,$mode); + $s_m_nb = count($search_modules); + $s_m_rs = staticRecord::newFromArray($search_modules); + $s_m_list = new daModulesList($core,$s_m_rs,$s_m_nb); +} + +# DISPLAY +# ------- +echo +''. +''. + ''.__('DotAddict.org Installer').''. + dcPage::jsModal(). + dcPage::jsPageTabs($default_tab). + dcPage::jsLoad('index.php?pf=daInstaller/js/_dainstaller.js'). + ''. +''. +''. +infoMessages(). +'

    '.__('DotAddict.org Installer').'

    '. +'

    '.__('Install and update your extensions live from DotAddict.org').'

    '; + +echo +''. +'
    '; +if ($u_p_nb > 0 || $u_t_nb > 0) { + echo + '

    '.__('Detected updates for your system').'

    '. + '

    '. + __('Changes can be required after installation of updates. Click on a support link before to be aware about'). + '

    '; +} +else { + echo '

    '.__('No update available').'

    '; +} + $u_p_list->display('plugins',$p_url); + $u_t_list->display('themes',$p_url); +echo +'
    '. +''. +'
    '. + '

    '.($a_p_nb > 0 ? sprintf(__('Available plugins (%s)'),$a_p_nb) : '').'

    '; + $a_p_list->display($pages['plugins'],$nb_per_page,'plugins',$p_url); +echo +'
    '. +''. +'
    '. + '

    '.($a_t_nb > 0 ? sprintf(__('Available themes (%s)'),$a_t_nb) : '').'

    '; + $a_t_list->display($pages['themes'],5,'themes',$p_url); +echo +'
    '. +''. +''; + +dcPage::helpBlock('da_installer'); + +echo +''. +''; \ No newline at end of file diff --git a/plugins/daInstaller/js/_dainstaller.js b/plugins/daInstaller/js/_dainstaller.js new file mode 100644 index 0000000..1c9c878 --- /dev/null +++ b/plugins/daInstaller/js/_dainstaller.js @@ -0,0 +1,17 @@ +/* +# ***** BEGIN LICENSE BLOCK ***** +# This file is part of daInstaller, a plugin for DotClear2. +# Copyright (c) 2008-2011 Tomtom, Pep and contributors, for DotAddict.org. +# All rights reserved. +# +# ***** END LICENSE BLOCK ***** +*/ +$(function() { + $('a.modal').modalWeb($(window).width()-60,$(window).height()-60); +}); +$(function() { + $('.checkboxes-helpers').each(function() { + dotclear.checkboxesHelpers(this); + }); + dotclear.commentsActionsHelper(); +}); \ No newline at end of file diff --git a/plugins/daInstaller/locales/_pot/main.pot b/plugins/daInstaller/locales/_pot/main.pot new file mode 100644 index 0000000..9949e24 --- /dev/null +++ b/plugins/daInstaller/locales/_pot/main.pot @@ -0,0 +1,98 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-08-20 19:31+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: _admin.php:20 _admin.php:36 index.php:132 index.php:138 +msgid "DotAddict.org Installer" +msgstr "" + +#: _admin.php:26 +msgid "Updates are available" +msgstr "" + +#: index.php:57 +msgid "Following plugins have been installed:" +msgstr "" + +#: index.php:64 +msgid "Following plugins have not been installed:" +msgstr "" + +#: index.php:72 +msgid " has been successfully upgraded" +msgstr "" + +#: index.php:72 +msgid " has been successfully installed." +msgstr "" + +#: index.php:88 +msgid "Search options" +msgstr "" + +#: index.php:92 +msgid "Query:" +msgstr "" + +#: index.php:97 +msgid "Plugins" +msgstr "" + +#: index.php:100 +msgid "Themes" +msgstr "" + +#: index.php:101 +msgid "ok" +msgstr "" + +#: index.php:139 +msgid "Install and update your extensions live from DotAddict.org" +msgstr "" + +#: index.php:143 +msgid "Available updates" +msgstr "" + +#: index.php:144 +msgid "Detected updates for your system" +msgstr "" + +#: index.php:149 +msgid "Get more plugins" +msgstr "" + +#: index.php:150 +msgid "Available plugins" +msgstr "" + +#: index.php:155 +msgid "Get more themes" +msgstr "" + +#: index.php:156 +msgid "Available themes" +msgstr "" + +#: index.php:160 index.php:161 +msgid "Search" +msgstr "" + +#: index.php:164 +#, php-format +msgid "%u plugin(s) found" +msgstr "" diff --git a/plugins/daInstaller/locales/fr/help/da_installer.html b/plugins/daInstaller/locales/fr/help/da_installer.html new file mode 100644 index 0000000..62f958c --- /dev/null +++ b/plugins/daInstaller/locales/fr/help/da_installer.html @@ -0,0 +1,30 @@ + + + Mise à jour d'extension(s) + + + + +

    Mise à jour d'extension(s)

    + +
    +
    Il est possible dans Dotclear 2 de spécifier dans sa configuration (PATH) + plusieurs chemins de dossiers contenant des extensions. Or, les mises à jour des + extensions se font dans le dernier répertoire du PATH. + Il se peut, pour des raisons variées, que dans un des premiers, il existe une + extension avec une version inférieure à celle présente sur DotAddict. Dans + ce cas préçis, vous avez deux choix possibles :
      +
    • Autoriser la multi-installation : la nouvelle version de + l’extension sera téléchargée puis installée dans le dernier répertoire du PATH + et l’ancienne extension recevera un fichier _disabled afin de la désactiver. L’ancienne + version ne sera pas supprimée
    • +
    • Ne pas autoriser la multi-installation : la nouvelle version + ira écraser l’ancienne dans le bon répertoire.
    + + Par défaut, la multi-installation est désactivée mais vous pouvez + changer ce comportement en modifiant la variable dainstaller_allow_multi_install + dans about:config
    +
    + + + diff --git a/plugins/daInstaller/locales/fr/main.lang.php b/plugins/daInstaller/locales/fr/main.lang.php new file mode 100644 index 0000000..3924861 --- /dev/null +++ b/plugins/daInstaller/locales/fr/main.lang.php @@ -0,0 +1,120 @@ + \ No newline at end of file diff --git a/plugins/daInstaller/locales/fr/main.po b/plugins/daInstaller/locales/fr/main.po new file mode 100644 index 0000000..eafb5e4 --- /dev/null +++ b/plugins/daInstaller/locales/fr/main.po @@ -0,0 +1,163 @@ +# Language: Français +# Module: daInstaller - 1.0.3 +# Date: 2011-05-16 11:47:48 +# Translated with translater 1.5 + +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: daInstaller 1.0.3\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2011-05-16T11:47:48+00:00\n" +"Last-Translator: Thomas Bouron\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" + +#: _admin.php:20 +#: _admin.php:50 +#: index.php:250 +#: index.php:258 +msgid "DotAddict.org Installer" +msgstr "Installateur DotAddict.org" + +#: _admin.php:38 +msgid "Updates are available" +msgstr "Des mises à jour sont disponibles" + +#: inc/class.da.installer.php:175 +msgid "Oops, something went wrong with %s, we are working on it. %s" +msgstr "Oops, il semble y avoir un problème avec %s, nous travaillons actuellement à sa résolution. %s" + +#: inc/class.da.installer.php:176 +msgid "dotaddict.org website" +msgstr "le site dotaddict.org" + +#: inc/class.da.installer.php:177 +msgid "Wanna help too?" +msgstr "Vous voulez nous aider aussi?" + +#: inc/class.da.modules.parser.php:25 +msgid "Impossible to read data feed" +msgstr "Impossible de lire le flux de données" + +#: inc/class.da.modules.parser.php:32 +msgid "Wrong data feed" +msgstr "Flux de données incorrect" + +#: inc/lib.da.installer.ui.php:31 +msgid "No theme" +msgstr "Aucun thème disponible" + +#: inc/lib.da.installer.ui.php:36 +msgid "No extension" +msgstr "Aucune extension disponible" + +#: inc/lib.da.installer.ui.php:58 +#: inc/lib.da.installer.ui.php:212 +msgid "Lastest Version" +msgstr "Dernière version" + +#: inc/lib.da.installer.ui.php:59 +#: inc/lib.da.installer.ui.php:213 +msgid "Quick description" +msgstr "Description rapide" + +#: inc/lib.da.installer.ui.php:60 +msgid "Actions" +msgstr "Actions" + +#: inc/lib.da.installer.ui.php:111 +#: inc/lib.da.installer.ui.php:150 +#: inc/lib.da.installer.ui.php:267 +#: inc/lib.da.installer.ui.php:304 +msgid "More details" +msgstr "En savoir plus" + +#: inc/lib.da.installer.ui.php:119 +#: inc/lib.da.installer.ui.php:158 +msgid "Install" +msgstr "Installer" + +#: inc/lib.da.installer.ui.php:196 +msgid "Theme" +msgstr "Thèmes" + +#: inc/lib.da.installer.ui.php:222 +msgid "Update selected modules" +msgstr "Mettre à jour les modules sélectionnés" + +#: inc/lib.da.installer.ui.php:247 +#: inc/lib.da.installer.ui.php:283 +msgid "Support" +msgstr "Support" + +#: inc/lib.da.installer.ui.php:248 +#: inc/lib.da.installer.ui.php:284 +msgid "No support available" +msgstr "Aucun support disponible" + +#: index.php:142 +msgid "The plugin has been successfully installed." +msgstr "Le plugin a été installé avec succès" + +#: index.php:146 +msgid "The theme has been successfully installed." +msgstr "Le thème a été installé avec succès" + +#: index.php:162 +msgid "Following plugins have not been updated:" +msgstr "Les extensions suivantes n'ont pas été mises à jour :" + +#: index.php:165 +msgid "Following plugins have been updated:" +msgstr "Les extensions suivantes ont été mises à jour :" + +#: index.php:182 +msgid "Following themes have not been updated:" +msgstr "Les thèmes suivants n'ont pas été mis à jour :" + +#: index.php:185 +msgid "Following themes have been updated:" +msgstr "Les thèmes suivants ont été mis à jour :" + +#: index.php:259 +msgid "Install and update your extensions live from DotAddict.org" +msgstr "Installez et mettez à jour vos extensions en direct depuis DotAddict.org" + +#: index.php:263 +msgid "Available updates" +msgstr "Mises à jour disponibles" + +#: index.php:266 +msgid "Detected updates for your system" +msgstr "Mises à jour détectées pour votre système" + +#: index.php:268 +msgid "Changes can be required after installation of updates. Click on a support link before to be aware about" +msgstr "Des changements sont peut être nécessaires après la mise à jour. Pensez à vérifier en cliquant sur le lien de support" + +#: index.php:272 +msgid "No update available" +msgstr "Aucune mise à jour détectée pour votre système" + +#: index.php:279 +msgid "Get more plugins" +msgstr "Obtenir plus d'extensions" + +#: index.php:280 +msgid "Available plugins (%s)" +msgstr "Plugins disponibles (%s)" + +#: index.php:285 +msgid "Get more themes" +msgstr "Obtenir plus de thèmes" + +#: index.php:286 +msgid "Available themes (%s)" +msgstr "Thèmes disponibles (%s)" + +#: index.php:309 +msgid "%u %s found" +msgstr "%u %s trouvé(s)" + diff --git a/plugins/daInstaller/locales/fr/resources.php b/plugins/daInstaller/locales/fr/resources.php new file mode 100644 index 0000000..4ea1b39 --- /dev/null +++ b/plugins/daInstaller/locales/fr/resources.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/plugins/daInstaller/locales/l10n-daInstaller-fr-1304423937.bck.zip b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304423937.bck.zip new file mode 100644 index 0000000..52d83c7 Binary files /dev/null and b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304423937.bck.zip differ diff --git a/plugins/daInstaller/locales/l10n-daInstaller-fr-1304423968.bck.zip b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304423968.bck.zip new file mode 100644 index 0000000..da5d0e4 Binary files /dev/null and b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304423968.bck.zip differ diff --git a/plugins/daInstaller/locales/l10n-daInstaller-fr-1304424006.bck.zip b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304424006.bck.zip new file mode 100644 index 0000000..f5774f9 Binary files /dev/null and b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304424006.bck.zip differ diff --git a/plugins/daInstaller/locales/l10n-daInstaller-fr-1304424011.bck.zip b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304424011.bck.zip new file mode 100644 index 0000000..1a447bd Binary files /dev/null and b/plugins/daInstaller/locales/l10n-daInstaller-fr-1304424011.bck.zip differ diff --git a/plugins/daInstaller/locales/l10n-daInstaller-fr-1305546468.bck.zip b/plugins/daInstaller/locales/l10n-daInstaller-fr-1305546468.bck.zip new file mode 100644 index 0000000..1e07210 Binary files /dev/null and b/plugins/daInstaller/locales/l10n-daInstaller-fr-1305546468.bck.zip differ diff --git a/plugins/daInstaller/style.css b/plugins/daInstaller/style.css new file mode 100644 index 0000000..ed6af47 --- /dev/null +++ b/plugins/daInstaller/style.css @@ -0,0 +1,29 @@ +/* +# ***** BEGIN LICENSE BLOCK ***** +# This file is part of daInstaller, a plugin for DotClear2. +# Copyright (c) 2008-2011 Tomtom, Pep and contributors, for DotAddict.org. +# All rights reserved. +# +# ***** END LICENSE BLOCK ***** +*/ +#themes { + border: none; + margin: 0; +} +.learnmore { + background: transparent url(index.php?pf=daInstaller/img/help.png) no-repeat top right; + padding-right: 20px; +} +.support { + background: transparent url(index.php?pf=daInstaller/img/support.png) no-repeat top right; + padding-right: 20px; +} +.sshot img { + margin:4px 0; + width:160px; + height:140px; + border:1px solid #999; +} +form { + clear:both; +} \ No newline at end of file diff --git a/plugins/externalMedia/_admin.php b/plugins/externalMedia/_admin.php new file mode 100644 index 0000000..8b09054 --- /dev/null +++ b/plugins/externalMedia/_admin.php @@ -0,0 +1,32 @@ +addBehavior('adminPostHeaders',array('externalMediaBehaviors','jsLoad')); +$core->addBehavior('adminPageHeaders',array('externalMediaBehaviors','jsLoad')); +$core->addBehavior('adminRelatedHeaders',array('externalMediaBehaviors','jsLoad')); +$core->addBehavior('adminDashboardHeaders',array('externalMediaBehaviors','jsLoad')); + +class externalMediaBehaviors +{ + public static function jsLoad() + { + return + ''. + '\n"; + } +} +?> \ No newline at end of file diff --git a/plugins/externalMedia/_define.php b/plugins/externalMedia/_define.php new file mode 100644 index 0000000..4268ea2 --- /dev/null +++ b/plugins/externalMedia/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "External Media", + /* Description*/ "Insert external media from Internet", + /* Author */ "Olivier Meunier", + /* Version */ '0.6.1', + /* Permissions */ 'usage,contentadmin', + /* Priority */ 50 +); +?> \ No newline at end of file diff --git a/plugins/externalMedia/bt_video.png b/plugins/externalMedia/bt_video.png new file mode 100644 index 0000000..3309386 Binary files /dev/null and b/plugins/externalMedia/bt_video.png differ diff --git a/plugins/externalMedia/index.php b/plugins/externalMedia/index.php new file mode 100644 index 0000000..e85ae13 --- /dev/null +++ b/plugins/externalMedia/index.php @@ -0,0 +1,282 @@ + '#^(http(s?)://.+\.mp3)$#', + 'flv' => '#^(http(s?)://.+\.flv)$#', + 'dailymotion' => '#^http://(www.)?dailymotion.com/(.+)#', + 'googlevideo' => '#^http://video.google.([a-z]{2,})/videoplay\?docid=(.+?)(&|$)#', + 'vimeo' => '#^http://www.vimeo.com/([0-9]+?)#', + 'youtube' => '#^http://([a-z]{2,}.)?youtube.com/(.+)#', + 'jamendo' => '#^http://(www.)?jamendo.com/[a-z]{2}/(playlist|album|track)/([0-9]+)#', + 'deezer' => '#^http://(www.)?deezer.com/track/(.+)#' +); + +if ($media_page) +{ + try + { + $media_service = false; + foreach ($services_regs as $k => $v) { + if (preg_match($v,$media_page)) { + $media_service = $k; + break; + } + } + + if (!$media_service) { + throw new Exception(__('Unsupported service')); + } + + if ($media_service != 'mp3' && $media_service != 'flv' && $media_service != 'jamendo') + { + $http = netHttp::initClient($media_page,$media_path); + $http->setTimeout(5); + $http->setUserAgent($_SERVER['HTTP_USER_AGENT']); + $http->get($media_path); + + if ($http->getStatus() != 200) { + throw new Exception(__('Invalid page URL')); + } + + $content = $http->getContent(); + } + + switch ($media_service) + { + case 'mp3': + $m_url = $media_page; + $m_title = basename($media_page); + $m_object = dcMedia::mp3player($m_url,$core->blog->getQmarkURL().'pf=player_mp3.swf'); + break; + case 'flv': + $m_url = $media_page; + $m_title = basename($media_page); + $m_object = dcMedia::flvPlayer($m_url,$core->blog->getQmarkURL().'pf=player_flv.swf'); + break; + case 'dailymotion': + if (preg_match('##ms',$content,$m)) + { + $cap = html::decodeEntities($m[1]); + $movie; + + if (preg_match('#param\s+name="movie"\s+value="(.+?)"#s',$cap,$M)) { + $movie = html::escapeHTML($M[1]); + } + + if (preg_match('#
    (.+?)#s',$cap,$M)) { + $m_title = html::decodeEntities($M[2]); + $m_url = $M[1]; + } + + if ($movie) + { + $m_object = + ''."\n". + ' '."\n". + ' '."\n". + ' '."\n". + ''; + } + } + break; + case 'googlevideo': + if (preg_match('#docid=(.+?)(&|$)#',$media_path,$m)) + { + $movie = 'http://video.google.com/googleplayer.swf?docid='.$m[1]; + + if (preg_match('#(.+?)#si',$content,$M)) { + $m_title = $M[1]; + } + + $m_object = + ''."\n". + ' '."\n". + ' '."\n". + ''; + } + break; + case 'vimeo': + if (preg_match('#'."\n". + ' '."\n". + ' '."\n". + ' '."\n". + ''; + } + break; + case 'youtube': + if (preg_match('#Youtube\s+-\s+(.+?)#si',$content,$M)) { + $m_title = $M[1]; + } + + if ($movie) + { + $m_object = + ''."\n". + ' '."\n". + ' '."\n". + ''; + } + } + break; + case 'jamendo': + if (preg_match('#^http://(www.)?jamendo.com/[a-z]{2}/(playlist|album|track)/([0-9]+)#',$media_page,$m)) + { + $type = $m[2]; + $id = $m[3]; + + $req = 'name'; + if ($type == 'track') { + $req .= '+stream'; + } + + $http = netHttp::initClient('http://api.jamendo.com/get2/'.$req.'/'.$type.'/plain/?streamencoding=mp31&id='.$id,$media_path); + $http->setTimeout(5); + $http->setUserAgent($_SERVER['HTTP_USER_AGENT']); + $http->get($media_path); + + if ($http->getStatus() != 200) { + throw new Exception(__('Invalid page URL')); + } + + if ($type != 'track') { + $m_title = $http->getContent(); + $m_object = + ''."\n". + ''; + $m_url = $media_page; + } else { + $t = explode("\t", $http->getContent()); + $m_title = $t[0]; + $url = $t[1]; + $m_object = dcMedia::mp3player($url,$core->blog->getQmarkURL().'pf=player_mp3.swf'); + $m_url = $media_page; + } + } + break; + case 'deezer': + if (preg_match('#/track/(.+?)(&|$)#',$media_path,$m)) + { + $idSong = $m[1]; + + if (preg_match('#(.+?) \| Deezer#si',$content,$M)) { + $m_title = $M[1]; + } + + $m_object = + ''."\n". + ''; + } + break; + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +?> + + + <?php echo __('External media selector') ?> + + + + +'.__('External media selector').''; + +if (!$m_object) +{ + echo + '
    '. + '

    '.__('Supported media services').'

    '. + '
    • '.implode('
    • ',array_keys($services_regs)).'
    '. + '

    '.__('Please enter the URL of the page containing the video you want to include in your post.').'

    '. + '

    '. + + '

    '. + $core->formNonce().'

    '. + '
    '; +} +else +{ + echo + '
    '.$m_object.'
    '. + '
    '; + + $i_align = array( + 'none' => array(__('None'),0), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),1) + ); + + echo '

    '.__('Media alignment').'

    '; + echo '

    '; + foreach ($i_align as $k => $v) { + echo '
    '; + } + echo '

    '; + + echo + '

    '.__('Media title').'

    '. + '

    '; + + echo + '

    '.__('Cancel').' - '. + ''.__('Insert').''. + form::hidden(array('m_object'),html::escapeHTML($m_object)). + form::hidden(array('m_url'),html::escapeHTML($m_url)). + '

    '; +} + +?> + + \ No newline at end of file diff --git a/plugins/externalMedia/popup.js b/plugins/externalMedia/popup.js new file mode 100644 index 0000000..cc8efca --- /dev/null +++ b/plugins/externalMedia/popup.js @@ -0,0 +1,3 @@ + +$(function(){$('#media-insert-cancel').click(function(){window.close();});$('#media-insert-ok').click(function(){sendClose();window.close();});$('#supported_media').prev().toggleWithLegend($('#supported_media'));function sendClose(){var insert_form=$('#media-insert-form').get(0);if(insert_form==undefined){return;} +var tb=window.opener.the_toolbar;var data=tb.elements.extmedia.data;data.alignment=$('input[name="alignment"]:checked',insert_form).val();data.title=insert_form.m_title.value;data.url=insert_form.m_url.value;data.m_object=insert_form.m_object.value;tb.elements.extmedia.fncall[tb.mode].call(tb);};}); \ No newline at end of file diff --git a/plugins/externalMedia/post.js b/plugins/externalMedia/post.js new file mode 100644 index 0000000..3b87ce0 --- /dev/null +++ b/plugins/externalMedia/post.js @@ -0,0 +1,6 @@ + +jsToolBar.prototype.elements.extmedia={type:'button',title:'External Media',icon:'index.php?pf=externalMedia/bt_video.png',fn:{},fncall:{},open_url:'plugin.php?p=externalMedia&popup=1',data:{},popup:function(){window.the_toolbar=this;this.elements.extmedia.data={};var p_win=window.open(this.elements.extmedia.open_url,'dc_popup','alwaysRaised=yes,dependent=yes,toolbar=yes,height=500,width=760,'+'menubar=no,resizable=yes,scrollbars=yes,status=no');},gethtml:function(){var d=this.data;if(d.m_object==''){return false;} +var res='
    '+d.title+'';} +res+='\n
    '+d.title;} +res+='\n
    ';return res;}};jsToolBar.prototype.elements.extmedia.fn.wiki=function(){this.elements.extmedia.popup.call(this);};jsToolBar.prototype.elements.extmedia.fn.xhtml=function(){this.elements.extmedia.popup.call(this);};jsToolBar.prototype.elements.extmedia.fncall.wiki=function(){var html=this.elements.extmedia.gethtml();this.encloseSelection('','',function(){return'\n///html\n'+html+'\n///\n';});};jsToolBar.prototype.elements.extmedia.fncall.xhtml=function(){var html=this.elements.extmedia.gethtml();this.encloseSelection('','',function(){return html;});}; \ No newline at end of file diff --git a/plugins/fairTrackbacks/_define.php b/plugins/fairTrackbacks/_define.php new file mode 100644 index 0000000..eedcd84 --- /dev/null +++ b/plugins/fairTrackbacks/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "Fair Trackbacks", + /* Description*/ "Trackback validity check", + /* Author */ "Olivier Meunier", + /* Version */ '1.0', + /* Permissions */ 'usage,contentadmin', + /* Priority */ 200 +); +?> \ No newline at end of file diff --git a/plugins/fairTrackbacks/_prepend.php b/plugins/fairTrackbacks/_prepend.php new file mode 100644 index 0000000..2ca33cd --- /dev/null +++ b/plugins/fairTrackbacks/_prepend.php @@ -0,0 +1,22 @@ +spamfilters[] = 'dcFilterFairTrackbacks'; +} +?> \ No newline at end of file diff --git a/plugins/fairTrackbacks/_public.php b/plugins/fairTrackbacks/_public.php new file mode 100644 index 0000000..82d8849 --- /dev/null +++ b/plugins/fairTrackbacks/_public.php @@ -0,0 +1,18 @@ +spamfilters[] = 'dcFilterFairTrackbacks'; +} +?> \ No newline at end of file diff --git a/plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php b/plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php new file mode 100644 index 0000000..fbc9793 --- /dev/null +++ b/plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php @@ -0,0 +1,85 @@ +description = __('Checks trackback source for a link to the post'); + } + + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if ($type != 'trackback') { + return; + } + + try + { + $default_parse = array('scheme'=>'','host'=>'','path'=>'','query'=>''); + $S = array_merge($default_parse,parse_url($site)); + + if ($S['scheme'] != 'http' || !$S['host'] || !$S['path']) { + throw new Exception('Invalid URL'); + } + + # Check incomink link page + $post = $this->core->blog->getPosts(array('post_id' => $post_id)); + $post_url = $post->getURL(); + $P = array_merge($default_parse,parse_url($post_url)); + + if ($post_url == $site) { + throw new Exception('Same source and destination'); + } + + $o = netHttp::initClient($site,$path); + $o->setTimeout(3); + $o->get($path); + + # Trackback source does not return 200 status code + if ($o->getStatus() != 200) { + throw new Exception('Invalid Status Code'); + } + + $tb_page = $o->getContent(); + + # Do we find a link to post in trackback source? + if ($S['host'] == $P['host']) { + $pattern = $P['path'].($P['query'] ? '?'.$P['query'] : ''); + } else { + $pattern = $post_url; + } + $pattern = preg_quote($pattern,'/'); + + if (!preg_match('/'.$pattern.'/',$tb_page)) { + throw new Exception('Unfair'); + } + } + catch (Exception $e) + { + throw new Exception('Trackback not allowed for this URL.'); + } + } +} +?> \ No newline at end of file diff --git a/plugins/googleTools/_admin.php b/plugins/googleTools/_admin.php new file mode 100644 index 0000000..2581a43 --- /dev/null +++ b/plugins/googleTools/_admin.php @@ -0,0 +1,48 @@ +addBehavior('adminBlogPreferencesForm',array('googlestuffAdminBehaviours','adminBlogPreferencesForm')); +$core->addBehavior('adminBeforeBlogSettingsUpdate',array('googlestuffAdminBehaviours','adminBeforeBlogSettingsUpdate')); + +class googlestuffAdminBehaviours +{ + public static function adminBlogPreferencesForm(&$core,&$settings) + { + echo + '
    Google Stuff'. + '
    '. + '

    '. + '
    '. + '

    '. + '
    '. + '
    '; + } + public static function adminBeforeBlogSettingsUpdate(&$settings) + { + $settings->setNameSpace('googlestuff'); + $settings->put('googlestuff_uacct',empty($_POST['googlestuff_uacct'])?"":$_POST['googlestuff_uacct'],'string'); + $settings->put('googlestuff_verify',empty($_POST['googlestuff_verify'])?"":$_POST['googlestuff_verify'],'string'); + $settings->setNameSpace('system'); + } + +} +?> \ No newline at end of file diff --git a/plugins/googleTools/_define.php b/plugins/googleTools/_define.php new file mode 100644 index 0000000..ddd883a --- /dev/null +++ b/plugins/googleTools/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "Google Tools", + /* Description*/ "Handles Google tools (Analytics & Webmaster Tools)", + /* Author */ "xave", + /* Version */ '0.2.3', + /* Permissions */ 'contentadmin' +); +?> \ No newline at end of file diff --git a/plugins/googleTools/_install.php b/plugins/googleTools/_install.php new file mode 100644 index 0000000..04eae23 --- /dev/null +++ b/plugins/googleTools/_install.php @@ -0,0 +1,35 @@ +plugins->moduleInfo('googlestuff','version'); +$installed_version = $core->getVersion('googlestuff'); + +if (version_compare($installed_version,$this_version,'>=')) { + return; +} + +$core->blog->settings->setNamespace('googlestuff'); +$core->blog->settings->put('googlestuff_uacct','','string','Google Analytics PageTracker ID',true,true); +$core->blog->settings->put('googlestuff_verify','','string','Google Webmaster Tools Verify code',true,true); +$core->blog->settings->setNamespace('system'); + +$core->setVersion('googlestuff',$this_version); + +return true; +?> \ No newline at end of file diff --git a/plugins/googleTools/_public.php b/plugins/googleTools/_public.php new file mode 100644 index 0000000..caf4702 --- /dev/null +++ b/plugins/googleTools/_public.php @@ -0,0 +1,52 @@ +addBehavior('publicHeadContent',array('googlestuffPublicBehaviours','publicHeadContent')); + $core->addBehavior('publicFooterContent',array('googlestuffPublicBehaviours','publicFooterContent')); + + +class googlestuffPublicBehaviours +{ + public static function publicHeadContent(&$core) + { + if ($core->blog->settings->googlestuff_verify != "") { + $res = ''."\n"; + echo $res; + } + } + + public static function publicFooterContent(&$core) + { + if ($core->blog->settings->googlestuff_uacct != "") { + $res = ''."\n". + ''."\n"; + echo $res; + } + } + +} +?> \ No newline at end of file diff --git a/plugins/googleTools/locales/fr/main.lang.php b/plugins/googleTools/locales/fr/main.lang.php new file mode 100644 index 0000000..2986eca --- /dev/null +++ b/plugins/googleTools/locales/fr/main.lang.php @@ -0,0 +1,19 @@ + diff --git a/plugins/importExport/_admin.php b/plugins/importExport/_admin.php new file mode 100644 index 0000000..025193e --- /dev/null +++ b/plugins/importExport/_admin.php @@ -0,0 +1,22 @@ +addItem(__('Import/Export'),'plugin.php?p=importExport','index.php?pf=importExport/icon.png', + preg_match('/plugin.php\?p=importExport(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id)); + +# Files needed by flat files import / export +$__autoload['backupFile'] = dirname(__FILE__).'/inc/flat/class.backupFile.php'; +$__autoload['dcImport'] = dirname(__FILE__).'/inc/flat/class.dc.import.php'; +$__autoload['dbExport'] = dirname(__FILE__).'/inc/flat/class.db.export.php'; +?> \ No newline at end of file diff --git a/plugins/importExport/_define.php b/plugins/importExport/_define.php new file mode 100644 index 0000000..9c49a3e --- /dev/null +++ b/plugins/importExport/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "Import / Export", + /* Description*/ "Import and Export your blog", + /* Author */ "Olivier Meunier", + /* Version */ '2.1', + /* Permissions */ 'admin', + /* Priority */ 10 +); +?> \ No newline at end of file diff --git a/plugins/importExport/icon.png b/plugins/importExport/icon.png new file mode 100644 index 0000000..b0bc5e8 Binary files /dev/null and b/plugins/importExport/icon.png differ diff --git a/plugins/importExport/inc/class.dc.export.flat.php b/plugins/importExport/inc/class.dc.export.flat.php new file mode 100644 index 0000000..d042882 --- /dev/null +++ b/plugins/importExport/inc/class.dc.export.flat.php @@ -0,0 +1,176 @@ +type = 'e'; + $this->name = __('Flat file export'); + $this->description = __('Exports a blog or a full Dotclear installation to flat file.'); + } + + public function process($do) + { + # Export a blog + if ($do == 'export_blog' && $this->core->auth->check('admin',$this->core->blog->id)) + { + $fullname = $this->core->blog->public_path.'/.backup_'.sha1(uniqid()); + $blog_id = $this->core->con->escape($this->core->blog->id); + + try + { + $exp = new dbExport($this->core->con,$fullname,$this->core->prefix); + fwrite($exp->fp,'///DOTCLEAR|'.DC_VERSION."|single\n"); + + $exp->export('category', + 'SELECT * FROM '.$this->core->prefix.'category '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('link', + 'SELECT * FROM '.$this->core->prefix.'link '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('setting', + 'SELECT * FROM '.$this->core->prefix.'setting '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('post', + 'SELECT * FROM '.$this->core->prefix.'post '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('media', + 'SELECT * FROM '.$this->core->prefix."media WHERE media_path = '". + $this->core->con->escape($this->core->blog->settings->public_path)."'" + ); + $exp->export('post_media', + 'SELECT media_id, M.post_id '. + 'FROM '.$this->core->prefix.'post_media M, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = M.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + $exp->export('ping', + 'SELECT ping.post_id, ping_url, ping_dt '. + 'FROM '.$this->core->prefix.'ping ping, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = ping.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + $exp->export('comment', + 'SELECT C.* '. + 'FROM '.$this->core->prefix.'comment C, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = C.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + + # --BEHAVIOR-- exportSingle + $this->core->callBehavior('exportSingle',$this->core,$exp,$blog_id); + + $_SESSION['export_file'] = $fullname; + $_SESSION['export_filename'] = $_POST['file_name']; + http::redirect($this->getURL().'&do=ok'); + } + catch (Exception $e) + { + @unlink($fullname); + throw $e; + } + } + + # Export all content + if ($do == 'export_all' && $this->core->auth->isSuperAdmin()) + { + $fullname = $this->core->blog->public_path.'/.backup_'.sha1(uniqid()); + try + { + $exp = new dbExport($this->core->con,$fullname,$this->core->prefix); + fwrite($exp->fp,'///DOTCLEAR|'.DC_VERSION."|full\n"); + $exp->exportTable('blog'); + $exp->exportTable('category'); + $exp->exportTable('link'); + $exp->exportTable('setting'); + $exp->exportTable('user'); + $exp->exportTable('permissions'); + $exp->exportTable('post'); + $exp->exportTable('media'); + $exp->exportTable('post_media'); + $exp->exportTable('log'); + $exp->exportTable('ping'); + $exp->exportTable('comment'); + $exp->exportTable('spamrule'); + $exp->exportTable('version'); + + # --BEHAVIOR-- exportFull + $this->core->callBehavior('exportFull',$this->core,$exp); + + $_SESSION['export_file'] = $fullname; + $_SESSION['export_filename'] = $_POST['file_name']; + http::redirect($this->getURL().'&do=ok'); + } + catch (Exception $e) + { + @unlink($fullname); + throw $e; + } + } + + # Send file content + if ($do == 'ok') + { + if (!file_exists($_SESSION['export_file'])) { + throw new Exception(__('Export file not found.')); + } + + ob_end_clean(); + header('Content-Disposition: attachment;filename='.$_SESSION['export_filename']); + header('Content-Type: text/plain; charset=UTF-8'); + readfile($_SESSION['export_file']); + unlink($_SESSION['export_file']); + unset($_SESSION['export_file']); + unset($_SESSION['export_filename']); + exit; + } + } + + public function gui() + { + echo + '
    '. + '
    '.__('Export a blog').''. + '

    '.sprintf(__('This will create an export of your current blog: %s'), + ''.html::escapeHTML($this->core->blog->name).'').'

    '. + '

    '. + '

    '. + form::hidden(array('do'),'export_blog'). + $this->core->formNonce().'

    '. + '

    '. + __('You may also want to download your media directory as a zip file').'

    '. + '
    '; + + if ($this->core->auth->isSuperAdmin()) + { + echo + '
    '. + '
    '.__('Export all content').''. + '

    '. + ''. + form::hidden(array('do'),'export_all'). + $this->core->formNonce().'

    '. + '
    '; + } + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/class.dc.ieModule.php b/plugins/importExport/inc/class.dc.ieModule.php new file mode 100644 index 0000000..2e23c38 --- /dev/null +++ b/plugins/importExport/inc/class.dc.ieModule.php @@ -0,0 +1,89 @@ +core =& $core; + $this->setInfo(); + + $this->id = get_class($this); + if (!$this->type) { + throw new Exception('No type for module'.$this->id); + } + + $this->url = 'plugin.php?p=importExport&t='.$this->type.'&f='.$this->id; + + if (!$this->name) { + $this->name = get_class($this); + } + } + + public function init() + { + } + + protected function setInfo() + { + } + + final public function getURL($escape=false) + { + if ($escape) { + return html::escapeHTML($this->url); + } + return $this->url; + } + + public function process($do) + { + } + + public function gui() + { + } + + protected function progressBar($percent) + { + $percent = ceil($percent); + if ($percent > 100) { + $percent = 100; + } + return '
    '.$percent.' %
    '; + } + + protected function autoSubmit() + { + return form::hidden(array('autosubmit'),1); + } + + protected function congratMessage() + { + return + '

    '.__('Congratulation!').'

    '. + '

    '.__('Your blog has been successfully imported. Welcome on Dotclear 2!').'

    '. + ''; + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/class.dc.import.dc1.php b/plugins/importExport/inc/class.dc.import.dc1.php new file mode 100644 index 0000000..9944a6a --- /dev/null +++ b/plugins/importExport/inc/class.dc.import.dc1.php @@ -0,0 +1,627 @@ + '', + 'db_name' => '', + 'db_user' => '', + 'db_pwd' => '', + 'db_prefix' => 'dc_', + 'post_limit' => 20, + 'cat_ids' => array() + ); + + protected function setInfo() + { + $this->type = 'i'; + $this->name = __('Dotclear 1.2 import'); + $this->description = __('Import a Dotclear 1.2 installation into your current blog.'); + } + + public function init() + { + $this->con =& $this->core->con; + $this->prefix = $this->core->prefix; + $this->blog_id = $this->core->blog->id; + + if (!isset($_SESSION['dc1_import_vars'])) { + $_SESSION['dc1_import_vars'] = $this->base_vars; + } + $this->vars =& $_SESSION['dc1_import_vars']; + + if ($this->vars['post_limit'] > 0) { + $this->post_limit = $this->vars['post_limit']; + } + } + + public function resetVars() + { + $this->vars = $this->base_vars;; + unset($_SESSION['dc1_import_vars']); + } + + public function process($do) + { + $this->action = $do; + } + + # We handle process in another way to always display something to + # user + protected function guiprocess($do) + { + switch ($do) + { + case 'step1': + $this->vars['db_host'] = $_POST['db_host']; + $this->vars['db_name'] = $_POST['db_name']; + $this->vars['db_user'] = $_POST['db_user']; + $this->vars['db_pwd'] = $_POST['db_pwd']; + $this->vars['post_limit'] = abs((integer) $_POST['post_limit']) > 0 ? $_POST['post_limit'] : 0; + $this->vars['db_prefix'] = $_POST['db_prefix']; + $db = $this->db(); + $db->close(); + $this->step = 2; + echo $this->progressBar(1); + break; + case 'step2': + $this->step = 2; + $this->importUsers(); + $this->step = 3; + echo $this->progressBar(3); + break; + case 'step3': + $this->step = 3; + $this->importCategories(); + if ($this->core->plugins->moduleExists('blogroll')) { + $this->step = 4; + echo $this->progressBar(5); + } else { + $this->step = 5; + echo $this->progressBar(7); + } + break; + case 'step4': + $this->step = 4; + $this->importLinks(); + $this->step = 5; + echo $this->progressBar(7); + break; + case 'step5': + $this->step = 5; + $this->post_offset = !empty($_REQUEST['offset']) ? abs((integer) $_REQUEST['offset']) : 0; + if ($this->importPosts($percent) === -1) { + http::redirect($this->getURL().'&do=ok'); + } else { + echo $this->progressBar(ceil($percent*0.93)+7); + } + break; + case 'ok': + $this->resetVars(); + $this->core->blog->triggerBlog(); + $this->step = 6; + echo $this->progressBar(100); + break; + } + } + + public function gui() + { + try { + $this->guiprocess($this->action); + } catch (Exception $e) { + $this->error($e); + } + + switch ($this->step) + { + case 1: + echo + '

    '.sprintf(__('This will import your Dotclear 1.2 content as new content in the current blog: %s.'), + ''.html::escapeHTML($this->core->blog->name).'').'

    '. + '

    '.__('Please note that this process '. + 'will empty your categories, blogroll, entries and comments on the current blog.').'

    '. + '

    '.__('Depending on the size of your blog, it could take a few minutes.').'

    '; + + printf($this->imForm(1,__('General information'),__('Import my blog now')), + '

    '.__('We first need some information about your old Dotclear 1.2 installation.').'

    '. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '.__('Entries import options').'

    '. + '

    ' + ); + break; + case 2: + printf($this->imForm(2,__('Importing users')), + $this->autoSubmit() + ); + break; + case 3: + printf($this->imForm(3,__('Importing categories')), + $this->autoSubmit() + ); + break; + case 4: + printf($this->imForm(4,__('Importing blogroll')), + $this->autoSubmit() + ); + break; + case 5: + $t = sprintf(__('Importing entries from %d to %d / %d'),$this->post_offset, + min(array($this->post_offset+$this->post_limit,$this->post_count)),$this->post_count); + printf($this->imForm(5,$t), + form::hidden(array('offset'),$this->post_offset). + $this->autoSubmit() + ); + break; + case 6: + echo + '

    '.__('Please read carefully').'

    '. + '
      '. + '
    • '.__('Every newly imported user has received a random password '. + 'and will need to ask for a new one by following the "I forgot my password" link on the login page '. + '(Their registered email address has to be valid.)').'
    • '. + + '
    • '.sprintf(__('Please note that Dotclear 2 has a new URL layout. You can avoid broken '. + 'links by installing DC1 redirect plugin and activate it in your blog configuration.'), + 'http://www.dotclear.org/extensions').'
    • '. + '
    '. + + $this->congratMessage(); + + break; + } + } + + # Simple form for step by step process + protected function imForm($step,$legend,$submit_value=null) + { + if (!$submit_value) { + $submit_value = __('next step').' >'; + } + + return + '
    '. + '
    '.$legend.''. + $this->core->formNonce(). + form::hidden(array('do'),'step'.$step). + '%s'. + '

    '. + '
    '. + '
    '; + } + + # Error display + protected function error($e) + { + echo '
    '.__('Errors:').''. + '

    '.$e->getMessage().'

    '; + } + + # Database init + protected function db() + { + $db = dbLayer::init('mysql',$this->vars['db_host'],$this->vars['db_name'],$this->vars['db_user'],$this->vars['db_pwd']); + + $rs = $db->select("SHOW TABLES LIKE '".$this->vars['db_prefix']."%'"); + if ($rs->isEmpty()) { + throw new Exception(__('Dotclear tables not found')); + } + + while ($rs->fetch()) { + $this->has_table[$rs->f(0)] = true; + } + + # Set this to read data as they were written in Dotclear 1 + try { + $db->execute('SET NAMES DEFAULT'); + } catch (Exception $e) {} + + $db->execute('SET CHARACTER SET DEFAULT'); + $db->execute("SET COLLATION_CONNECTION = DEFAULT"); + $db->execute("SET COLLATION_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_DATABASE = DEFAULT"); + + $this->post_count = $db->select( + 'SELECT COUNT(post_id) FROM '.$this->vars['db_prefix'].'post ' + )->f(0); + + return $db; + } + + protected function cleanStr($str) + { + return text::cleanUTF8(@text::toUTF8($str)); + } + + # Users import + protected function importUsers() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'user'); + + try + { + $this->con->begin(); + + while ($rs->fetch()) + { + if (!$this->core->userExists($rs->user_id)) + { + $cur = $this->con->openCursor($this->prefix.'user'); + $cur->user_id = $rs->user_id; + $cur->user_name = $rs->user_nom; + $cur->user_firstname = $rs->user_prenom; + $cur->user_displayname = $rs->user_pseudo; + $cur->user_pwd = crypt::createPassword(); + $cur->user_email = $rs->user_email; + $cur->user_lang = $rs->user_lang; + $cur->user_tz = $this->core->blog->settings->blog_timezone; + $cur->user_post_status = $rs->user_post_pub ? 1 : -2; + $cur->user_options = new ArrayObject(array( + 'edit_size' => (integer) $rs->user_edit_size, + 'post_format' => $rs->user_post_format + )); + + $permissions = array(); + switch ($rs->user_level) + { + case '0': + $cur->user_status = 0; + break; + case '1': # editor + $permissions['usage'] = true; + break; + case '5': # advanced editor + $permissions['contentadmin'] = true; + $permissions['categories'] = true; + $permissions['media_admin'] = true; + break; + case '9': # admin + $permissions['admin'] = true; + break; + } + + $this->core->addUser($cur); + $this->core->setUserBlogPermissions( + $rs->user_id, + $this->blog_id, + $permissions + ); + } + } + + $this->con->commit(); + $db->close(); + } + catch (Exception $e) + { + $this->con->rollback(); + $db->close(); + throw $e; + } + } + + # Categories import + protected function importCategories() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'categorie ORDER BY cat_ord ASC'); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + $ord = 2; + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'category'); + $cur->blog_id = $this->blog_id; + $cur->cat_title = $this->cleanStr($rs->cat_libelle); + $cur->cat_desc = $this->cleanStr($rs->cat_desc); + $cur->cat_url = $this->cleanStr($rs->cat_libelle_url); + $cur->cat_lft = $ord++; + $cur->cat_rgt = $ord++; + + $cur->cat_id = $this->con->select( + 'SELECT MAX(cat_id) FROM '.$this->prefix.'category' + )->f(0) + 1; + $this->vars['cat_ids'][$rs->cat_id] = $cur->cat_id; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Blogroll import + protected function importLinks() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'link ORDER BY link_id ASC'); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'link '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'link'); + $cur->blog_id = $this->blog_id; + $cur->link_href = $this->cleanStr($rs->href); + $cur->link_title = $this->cleanStr($rs->label); + $cur->link_desc = $this->cleanStr($rs->title); + $cur->link_lang = $this->cleanStr($rs->lang); + $cur->link_xfn = $this->cleanStr($rs->rel); + $cur->link_position = (integer) $rs->position; + + $cur->link_id = $this->con->select( + 'SELECT MAX(link_id) FROM '.$this->prefix.'link' + )->f(0) + 1; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Entries import + protected function importPosts(&$percent) + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + + $count = $db->select('SELECT COUNT(post_id) FROM '.$prefix.'post')->f(0); + + $rs = $db->select( + 'SELECT * FROM '.$prefix.'post ORDER BY post_id ASC '. + $db->limit($this->post_offset,$this->post_limit) + ); + + try + { + if ($this->post_offset == 0) + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'post '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + } + + while ($rs->fetch()) { + $this->importPost($rs,$db); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + + if ($rs->count() < $this->post_limit) { + return -1; + } else { + $this->post_offset += $this->post_limit; + } + + if ($this->post_offset > $this->post_count) { + $percent = 100; + } else { + $percent = $this->post_offset * 100 / $this->post_count; + } + } + + protected function importPost(&$rs,&$db) + { + $cur = $this->con->openCursor($this->prefix.'post'); + $cur->blog_id = $this->blog_id; + $cur->user_id = $rs->user_id; + $cur->cat_id = (integer) $this->vars['cat_ids'][$rs->cat_id]; + $cur->post_dt = $rs->post_dt; + $cur->post_creadt = $rs->post_creadt; + $cur->post_upddt = $rs->post_upddt; + $cur->post_title = html::decodeEntities($this->cleanStr($rs->post_titre)); + + $cur->post_url = date('Y/m/d/',strtotime($cur->post_dt)).$rs->post_id.'-'.$rs->post_titre_url; + $cur->post_url = substr($cur->post_url,0,255); + + $cur->post_format = $rs->post_content_wiki == '' ? 'xhtml' : 'wiki'; + $cur->post_content_xhtml = $this->cleanStr($rs->post_content); + $cur->post_excerpt_xhtml = $this->cleanStr($rs->post_chapo); + + if ($cur->post_format == 'wiki') { + $cur->post_content = $this->cleanStr($rs->post_content_wiki); + $cur->post_excerpt = $this->cleanStr($rs->post_chapo_wiki); + } else { + $cur->post_content = $this->cleanStr($rs->post_content); + $cur->post_excerpt = $this->cleanStr($rs->post_chapo); + } + + $cur->post_notes = $this->cleanStr($rs->post_notes); + $cur->post_status = (integer) $rs->post_pub; + $cur->post_selected = (integer) $rs->post_selected; + $cur->post_open_comment = (integer) $rs->post_open_comment; + $cur->post_open_tb = (integer) $rs->post_open_tb; + $cur->post_lang = $rs->post_lang; + + $cur->post_words = implode(' ',text::splitWords( + $cur->post_title.' '. + $cur->post_excerpt_xhtml.' '. + $cur->post_content_xhtml + )); + + $cur->post_id = $this->con->select( + 'SELECT MAX(post_id) FROM '.$this->prefix.'post' + )->f(0) + 1; + + $cur->insert(); + $this->importComments($rs->post_id,$cur->post_id,$db); + $this->importPings($rs->post_id,$cur->post_id,$db); + + # Load meta if we have some in DC1 and metadata plugin in DC2 + if (isset($this->has_table[$this->prefix.'post_meta']) && class_exists('dcMeta')) { + $this->importMeta($rs->post_id,$cur->post_id,$db); + } + } + + # Comments import + protected function importComments($post_id,$new_post_id,&$db) + { + $count_c = $count_t = 0; + + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'comment '. + 'WHERE post_id = '.(integer) $post_id.' ' + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'comment'); + $cur->post_id = (integer) $new_post_id; + $cur->comment_author = $this->cleanStr($rs->comment_auteur); + $cur->comment_status = (integer) $rs->comment_pub; + $cur->comment_dt = $rs->comment_dt; + $cur->comment_upddt = $rs->comment_upddt; + $cur->comment_email = $this->cleanStr($rs->comment_email); + $cur->comment_content = $this->cleanStr($rs->comment_content); + $cur->comment_ip = $rs->comment_ip; + $cur->comment_trackback = (integer) $rs->comment_trackback; + + $cur->comment_site = $this->cleanStr($rs->comment_site); + if ($cur->comment_site != '' && !preg_match('!^http://.*$!',$cur->comment_site)) { + $cur->comment_site = substr('http://'.$cur->comment_site,0,255); + } + + if ($rs->exists('spam') && $rs->spam && $rs->comment_status = 0) { + $cur->comment_status = -2; + } + + $cur->comment_words = implode(' ',text::splitWords($cur->comment_content)); + + $cur->comment_id = $this->con->select( + 'SELECT MAX(comment_id) FROM '.$this->prefix.'comment' + )->f(0) + 1; + + $cur->insert(); + + if ($cur->comment_trackback && $cur->comment_status == 1) { + $count_t++; + } elseif ($cur->comment_status == 1) { + $count_c++; + } + } + + if ($count_t > 0 || $count_c > 0) + { + $this->con->execute( + 'UPDATE '.$this->prefix.'post SET '. + 'nb_comment = '.$count_c.', '. + 'nb_trackback = '.$count_t.' '. + 'WHERE post_id = '.(integer) $new_post_id.' ' + ); + } + } + + # Pings import + protected function importPings($post_id,$new_post_id,&$db) + { + $urls = array(); + + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'ping '. + 'WHERE post_id = '.(integer) $post_id + ); + + while ($rs->fetch()) + { + $url = $this->cleanStr($rs->ping_url); + if (isset($urls[$url])) { + continue; + } + + $cur = $this->con->openCursor($this->prefix.'ping'); + $cur->post_id = (integer) $new_post_id; + $cur->ping_url = $url; + $cur->ping_dt = $rs->ping_dt; + $cur->insert(); + + $urls[$url] = true; + } + } + + # Meta import + protected function importMeta($post_id,$new_post_id,&$db) + { + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'post_meta '. + 'WHERE post_id = '.(integer) $post_id.' '. + "AND meta_key = 'tag' " + ); + + if ($rs->isEmpty()) { + return; + } + + $meta = new dcMeta($this->core); + while ($rs->fetch()) { + $meta->setPostMeta($new_post_id,'tag',$this->cleanStr($rs->meta_value)); + } + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/class.dc.import.feed.php b/plugins/importExport/inc/class.dc.import.feed.php new file mode 100644 index 0000000..7042e38 --- /dev/null +++ b/plugins/importExport/inc/class.dc.import.feed.php @@ -0,0 +1,105 @@ +type = 'i'; + $this->name = __('Feed import'); + $this->description = __('Imports a feed as new entries.'); + } + + public function process($do) + { + if ($do == 'ok') { + $this->status = true; + return; + } + + if (empty($_POST['feed_url'])) { + return; + } + + $this->feed_url = $_POST['feed_url']; + + $feed = feedReader::quickParse($this->feed_url); + if ($feed === false) { + throw new Exception(__('Cannot retrieve feed URL.')); + } + if (count($feed->items) == 0) { + throw new Exception(__('No items in feed.')); + } + + if ($this->core->plugins->moduleExists('metadata')) { + $meta = new dcMeta($this->core); + } + + $cur = $this->core->con->openCursor($this->core->prefix.'post'); + $this->core->con->begin(); + foreach ($feed->items as $item) + { + $cur->clean(); + $cur->user_id = $this->core->auth->userID(); + $cur->post_content = $item->content ? $item->content : $item->description; + $cur->post_title = $item->title ? $item->title : text::cutString(html::clean($cur->post_content),60); + $cur->post_format = 'xhtml'; + $cur->post_status = -2; + $cur->post_dt = strftime('%Y-%m-%d %H:%M:%S',$item->TS); + + try { + $post_id = $this->core->blog->addPost($cur); + } catch (Exception $e) { + $this->core->con->rollback(); + throw $e; + } + + if (isset($meta)) + { + foreach ($item->subject as $subject) { + $meta->setPostMeta($post_id,'tag',dcMeta::sanitizeMetaID($subject)); + } + } + } + + $this->core->con->commit(); + http::redirect($this->getURL().'&do=ok'); + + } + + public function gui() + { + if ($this->status) { + echo '

    '.__('Content successfully imported.').'

    '; + } + + echo + '

    '.__('Import from a feed').'

    '. + '

    '.sprintf(__('This will import a feed (RSS or Atom) a as new content in the current blog: %s.'), + ''.html::escapeHTML($this->core->blog->name).'').'

    '. + '
    '. + + '
    '. + $this->core->formNonce(). + form::hidden(array('do'),1). + '

    '. + '

    '. + '
    '. + '
    '; + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/class.dc.import.flat.php b/plugins/importExport/inc/class.dc.import.flat.php new file mode 100644 index 0000000..d512345 --- /dev/null +++ b/plugins/importExport/inc/class.dc.import.flat.php @@ -0,0 +1,238 @@ +type = 'i'; + $this->name = __('Flat file import'); + $this->description = __('Imports a blog or a full Dotclear installation from flat file.'); + } + + public function process($do) + { + if ($do == 'single' || $do == 'full') { + $this->status = $do; + return; + } + + $to_unlink = false; + + # Single blog import + $files = $this->getPublicFiles(); + $single_upl = null; + if (!empty($_POST['public_single_file']) && in_array($_POST['public_single_file'],$files)) { + $single_upl = false; + } elseif (!empty($_FILES['up_single_file'])) { + $single_upl = true; + } + + if ($single_upl !== null) + { + if ($single_upl) { + files::uploadStatus($_FILES['up_single_file']); + $file = DC_TPL_CACHE.'/'.md5(uniqid()); + if (!move_uploaded_file($_FILES['up_single_file']['tmp_name'],$file)) { + throw new Exception(__('Unable to move uploaded file.')); + } + $to_unlink = true; + } else { + $file = $_POST['public_single_file']; + } + + try { + $bk = new dcImport($this->core,$file); + $bk->importSingle(); + } catch (Exception $e) { + if ($to_unlink) { + @unlink($file); + } + throw $e; + } + if ($to_unlink) { + @unlink($file); + } + http::redirect($this->getURL().'&do=single'); + } + + # Full import + $full_upl = null; + if (!empty($_POST['public_full_file']) && in_array($_POST['public_full_file'],$files)) { + $full_upl = false; + } elseif (!empty($_FILES['up_full_file'])) { + $full_upl = true; + } + + if ($full_upl !== null && $this->core->auth->isSuperAdmin()) + { + if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + if ($full_upl) { + files::uploadStatus($_FILES['up_full_file']); + $file = DC_TPL_CACHE.'/'.md5(uniqid()); + if (!move_uploaded_file($_FILES['up_full_file']['tmp_name'],$file)) { + throw new Exception(__('Unable to move uploaded file.')); + } + $to_unlink = true; + } else { + $file = $_POST['public_full_file']; + } + + try { + $bk = new dcImport($this->core,$file); + $bk->importFull(); + } catch (Exception $e) { + if ($to_unlink) { + @unlink($file); + } + throw $e; + } + if ($to_unlink) { + @unlink($file); + } + http::redirect($this->getURL().'&do=full'); + } + + header('content-type:text/plain'); + var_dump($_POST); + exit; + + $this->status = true; + } + + public function gui() + { + if ($this->status == 'single') + { + echo '

    '.__('Single blog successfully imported.').'

    '; + return; + } + if ($this->status == 'full') + { + echo '

    '.__('Content successfully imported.').'

    '; + return; + } + + echo + '\n"; + + echo + '

    '.__('Import a single blog').'

    '. + '

    '.sprintf(__('This will import a single blog backup as new content in the current blog: %s.'), + ''.html::escapeHTML($this->core->blog->name).'').'

    '. + '
    '. + + '
    '. + $this->core->formNonce(). + form::hidden(array('do'),1). + form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE). + '

    '; + + $public_files = $this->getPublicFiles(); + + $empty = empty($public_files); + $public_files = array_merge(array('-' => ''),$public_files); + echo + '

    '; + + echo + '

    '. + '
    '. + '
    '; + + if ($this->core->auth->isSuperAdmin()) + { + echo + '

    '.__('Import a full backup file').'

    '. + '
    '. + '
    '.form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE).'
    '. + + '
    '. + $this->core->formNonce(). + form::hidden(array('do'),1). + form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE). + '

    '; + + echo + '

    '; + + echo + '

    '.__('Warning: This will reset all the content of your database, except users.').'

    '. + + '

    '. + + '

    '. + '
    '. + '
    '; + } + } + + protected function getPublicFiles() + { + $public_files = array(); + $dir = @dir($this->core->blog->public_path); + if ($dir) + { + while (($entry = $dir->read()) !== false) { + $entry_path = $dir->path.'/'.$entry; + + if (is_file($entry_path) && is_readable($entry_path)) + { + $fp = fopen($entry_path,'rb'); + if (strpos(fgets($fp),'///DOTCLEAR|') === 0) { + $public_files[$entry] = $entry_path; + } + fclose($fp); + } + } + } + return $public_files; + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/class.dc.import.wp.php b/plugins/importExport/inc/class.dc.import.wp.php new file mode 100644 index 0000000..cca5408 --- /dev/null +++ b/plugins/importExport/inc/class.dc.import.wp.php @@ -0,0 +1,786 @@ + '', + 'db_name' => '', + 'db_user' => '', + 'db_pwd' => '', + 'db_prefix' => 'wp_', + 'ignore_first_cat' => 1, + 'cat_import' => 1, + 'cat_as_tags' => '', + 'cat_tags_prefix' => 'cat: ', + 'post_limit' => 20, + 'post_formater' => 'xhtml', + 'comment_formater' => 'xhtml', + 'user_ids' => array(), + 'cat_ids' => array(), + 'permalink_template' => 'p=%post_id%', + 'permalink_tags' => array( + '%year%', + '%monthnum%', + '%day%', + '%hour%', + '%minute%', + '%second%', + '%postname%', + '%post_id%', + '%category%', + '%author%' + ) + ); + protected $formaters; + + protected function setInfo() + { + $this->type = 'i'; + $this->name = __('WordPress import'); + $this->description = __('Import a WordPress installation into your current blog.'); + } + + public function init() + { + $this->con =& $this->core->con; + $this->prefix = $this->core->prefix; + $this->blog_id = $this->core->blog->id; + + if (!isset($_SESSION['wp_import_vars'])) { + $_SESSION['wp_import_vars'] = $this->base_vars; + } + $this->vars =& $_SESSION['wp_import_vars']; + + if ($this->vars['post_limit'] > 0) { + $this->post_limit = $this->vars['post_limit']; + } + + foreach ($this->core->getFormaters() as $v) { + $this->formaters[$v] = $v; + } + } + + public function resetVars() + { + $this->vars = $this->base_vars;; + unset($_SESSION['wp_import_vars']); + } + + public function process($do) + { + $this->action = $do; + } + + # We handle process in another way to always display something to + # user + protected function guiprocess($do) + { + switch ($do) + { + case 'step1': + $this->vars['db_host'] = $_POST['db_host']; + $this->vars['db_name'] = $_POST['db_name']; + $this->vars['db_user'] = $_POST['db_user']; + $this->vars['db_pwd'] = $_POST['db_pwd']; + $this->vars['db_prefix'] = $_POST['db_prefix']; + $this->vars['ignore_first_cat'] = isset($_POST['ignore_first_cat']); + $this->vars['cat_import'] = isset($_POST['cat_import']); + $this->vars['cat_as_tags'] = isset($_POST['cat_as_tags']); + $this->vars['cat_tags_prefix'] = $_POST['cat_tags_prefix']; + $this->vars['post_limit'] = abs((integer) $_POST['post_limit']) > 0 ? $_POST['post_limit'] : 0; + $this->vars['post_formater'] = isset($this->formaters[$_POST['post_formater']]) ? $_POST['post_formater'] : 'xhtml'; + $this->vars['comment_formater'] = isset($this->formaters[$_POST['comment_formater']]) ? $_POST['comment_formater'] : 'xhtml'; + $db = $this->db(); + $db->close(); + $this->step = 2; + echo $this->progressBar(1); + break; + case 'step2': + $this->step = 2; + $this->importUsers(); + $this->step = 3; + echo $this->progressBar(3); + break; + case 'step3': + $this->step = 3; + $this->importCategories(); + if ($this->core->plugins->moduleExists('blogroll')) { + $this->step = 4; + echo $this->progressBar(5); + } else { + $this->step = 5; + echo $this->progressBar(7); + } + break; + case 'step4': + $this->step = 4; + $this->importLinks(); + $this->step = 5; + echo $this->progressBar(7); + break; + case 'step5': + $this->step = 5; + $this->post_offset = !empty($_REQUEST['offset']) ? abs((integer) $_REQUEST['offset']) : 0; + if ($this->importPosts($percent) === -1) { + http::redirect($this->getURL().'&do=ok'); + } else { + echo $this->progressBar(ceil($percent*0.93)+7); + } + break; + case 'ok': + $this->resetVars(); + $this->core->blog->triggerBlog(); + $this->step = 6; + echo $this->progressBar(100); + break; + } + } + + public function gui() + { + try { + $this->guiprocess($this->action); + } catch (Exception $e) { + $this->error($e); + } + + switch ($this->step) + { + case 1: + echo + '

    '.sprintf(__('This will import your WordPress content as new content in the current blog: %s.'), + ''.html::escapeHTML($this->core->blog->name).'').'

    '. + '

    '.__('Please note that this process '. + 'will empty your categories, blogroll, entries and comments on the current blog.').'

    '. + '

    '.__('Depending on the size of your blog, it could take a few minutes.').'

    '; + + printf($this->imForm(1,__('General information'),__('Import my blog now')), + '

    '.__('We first need some information about your old WordPress installation.').'

    '. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '.__('Entries import options').'

    '. + '

    '.__('WordPress and Dotclear\'s handling of categories are quite different. '. + 'You can assign several categories to a single post in WordPress. In the Dotclear world, '. + 'we see it more like "One category, several tags." Therefore Dotclear can only import one '. + 'category per post and will chose the lowest numbered one. If you want to keep a trace of '. + 'every category, you can import them as tags, with an optional prefix.').'

    '. + '

    '.__('On the other hand, in WordPress, a post can not be uncategorized, and a default '. + 'installation has a first category labelised "Uncategorized". If you did not change that '. + 'category, you can just ignore it while importing your blog, as Dotclear allows you to '. + 'actually keep your posts uncategorized.').'

    '. + '

    '. + '

    '. + '

    '. + '

    '. + '

    '. + + '

    '.__('Content filters').'

    '. + '

    '.__('You may want to process your post and/or comment content with the following filters.').'

    '. + '

    '. + '

    ' + ); + break; + case 2: + printf($this->imForm(2,__('Importing users')), + $this->autoSubmit() + ); + break; + case 3: + printf($this->imForm(3,__('Importing categories')), + $this->autoSubmit() + ); + break; + case 4: + printf($this->imForm(4,__('Importing blogroll')), + $this->autoSubmit() + ); + break; + case 5: + $t = sprintf(__('Importing entries from %d to %d / %d'),$this->post_offset, + min(array($this->post_offset+$this->post_limit,$this->post_count)),$this->post_count); + printf($this->imForm(5,$t), + form::hidden(array('offset'),$this->post_offset). + $this->autoSubmit() + ); + break; + case 6: + echo + '

    '.__('Every newly imported user has received a random password '. + 'and will need to ask for a new one by following the "I forgot my password" link on the login page '. + '(Their registered email address has to be valid.)').'

    '. + $this->congratMessage(); + break; + } + } + + # Simple form for step by step process + protected function imForm($step,$legend,$submit_value=null) + { + if (!$submit_value) { + $submit_value = __('next step').' >'; + } + + return + '
    '. + '
    '.$legend.''. + $this->core->formNonce(). + form::hidden(array('do'),'step'.$step). + '%s'. + '

    '. + '
    '. + '
    '; + } + + # Error display + protected function error($e) + { + echo '
    '.__('Errors:').''. + '

    '.$e->getMessage().'

    '; + } + + # Database init + protected function db() + { + $db = dbLayer::init('mysql',$this->vars['db_host'],$this->vars['db_name'],$this->vars['db_user'],$this->vars['db_pwd']); + + $rs = $db->select("SHOW TABLES LIKE '".$this->vars['db_prefix']."%'"); + if ($rs->isEmpty()) { + throw new Exception(__('WordPress tables not found')); + } + + while ($rs->fetch()) { + $this->has_table[$rs->f(0)] = true; + } + + # Set this to read data as they were written + try { + $db->execute('SET NAMES DEFAULT'); + } catch (Exception $e) {} + + $db->execute('SET CHARACTER SET DEFAULT'); + $db->execute("SET COLLATION_CONNECTION = DEFAULT"); + $db->execute("SET COLLATION_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_DATABASE = DEFAULT"); + + $this->post_count = $db->select( + 'SELECT COUNT(ID) FROM '.$this->vars['db_prefix'].'posts '. + 'WHERE post_type = \'post\' OR post_type = \'page\'' + )->f(0); + + return $db; + } + + protected function cleanStr($str) + { + return text::cleanUTF8(@text::toUTF8($str)); + } + + # Users import + protected function importUsers() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'users'); + + try + { + $this->con->begin(); + + while ($rs->fetch()) + { + $user_login = preg_replace('/[^A-Za-z0-9@._-]/','-',$rs->user_login); + $this->vars['user_ids'][$rs->ID] = $user_login; + if (!$this->core->userExists($user_login)) + { + $cur = $this->con->openCursor($this->prefix.'user'); + $cur->user_id = $user_login; + $cur->user_pwd = crypt::createPassword(); + $cur->user_displayname = $rs->user_nicename; + $cur->user_email = $rs->user_email; + $cur->user_url = $rs->user_url; + $cur->user_creadt = $rs->user_registered; + $cur->user_lang = $this->core->blog->settings->lang; + $cur->user_tz = $this->core->blog->settings->blog_timezone; + $permissions = array(); + + $rs_meta = $db->select('SELECT * FROM '.$prefix.'usermeta WHERE user_id = '.$rs->ID); + while ($rs_meta->fetch()) + { + switch ($rs_meta->meta_key) + { + case 'first_name': + $cur->user_firstname = $this->cleanStr($rs_meta->meta_value); + break; + case 'last_name': + $cur->user_name = $this->cleanStr($rs_meta->meta_value); + break; + case 'description': + $cur->user_desc = $this->cleanStr($rs_meta->meta_value); + break; + case 'rich_editing': + $cur->user_options = new ArrayObject(array( + 'enable_wysiwyg' => $rs_meta->meta_value == 'true' ? true : false + )); + break; + case 'wp_user_level': + switch ($rs_meta->meta_value) + { + case '0': # Subscriber + $cur->user_status = 0; + break; + case '1': # Contributor + $permissions['usage'] = true; + $permissions['publish'] = true; + $permissions['delete'] = true; + break; + case '2': # Author + case '3': + case '4': + $permissions['contentadmin'] = true; + $permissions['media'] = true; + break; + case '5': # Editor + case '6': + case '7': + $permissions['contentadmin'] = true; + $permissions['categories'] = true; + $permissions['media_admin'] = true; + $permissions['pages'] = true; + $permissions['blogroll'] = true; + break; + case '8': # Administrator + case '9': + case '10': + $permissions['admin'] = true; + break; + } + break; + } + } + $this->core->addUser($cur); + $this->core->setUserBlogPermissions( + $cur->user_id, + $this->blog_id, + $permissions + ); + } + } + $this->con->commit(); + $db->close(); + } + catch (Exception $e) + { + $this->con->rollback(); + $db->close(); + throw $e; + } + } + + # Categories import + protected function importCategories() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select( + 'SELECT * FROM '.$prefix.'terms AS t, '.$prefix.'term_taxonomy AS x '. + 'WHERE x.taxonomy = \'category\' '. + 'AND t.term_id = x.term_id '. + ($this->vars['ignore_first_cat'] ? 'AND t.term_id <> 1 ' : ''). + 'ORDER BY t.term_id ASC' + ); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + $ord = 2; + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'category'); + $cur->blog_id = $this->blog_id; + $cur->cat_title = $this->cleanStr($rs->name); + $cur->cat_desc = $this->cleanStr($rs->description); + $cur->cat_url = $this->cleanStr($rs->slug); + $cur->cat_lft = $ord++; + $cur->cat_rgt = $ord++; + + $cur->cat_id = $this->con->select( + 'SELECT MAX(cat_id) FROM '.$this->prefix.'category' + )->f(0) + 1; + $this->vars['cat_ids'][$rs->term_id] = $cur->cat_id; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Blogroll import + protected function importLinks() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'links ORDER BY link_id ASC'); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'link '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'link'); + $cur->blog_id = $this->blog_id; + $cur->link_href = $this->cleanStr($rs->link_url); + $cur->link_title = $this->cleanStr($rs->link_name); + $cur->link_desc = $this->cleanStr($rs->link_description); + $cur->link_xfn = $this->cleanStr($rs->link_rel); + + $cur->link_id = $this->con->select( + 'SELECT MAX(link_id) FROM '.$this->prefix.'link' + )->f(0) + 1; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Entries import + protected function importPosts(&$percent) + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + + $plink = $db->select( + 'SELECT option_value FROM '.$prefix.'options '. + "WHERE option_name = 'permalink_structure'" + )->option_value; + if ($plink) { + $this->vars['permalink_template'] = substr($plink,1); + } + + $rs = $db->select( + 'SELECT * FROM '.$prefix.'posts '. + 'WHERE post_type = \'post\' OR post_type = \'page\' '. + 'ORDER BY ID ASC '. + $db->limit($this->post_offset,$this->post_limit) + ); + + try + { + if ($this->post_offset == 0) + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'post '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + } + + while ($rs->fetch()) { + $this->importPost($rs,$db); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + + if ($rs->count() < $this->post_limit) { + return -1; + } else { + $this->post_offset += $this->post_limit; + } + + if ($this->post_offset > $this->post_count) { + $percent = 100; + } else { + $percent = $this->post_offset * 100 / $this->post_count; + } + } + + protected function importPost(&$rs,&$db) + { + $post_date = !@strtotime($rs->post_date) ? '1970-01-01 00:00' : $rs->post_date; + if (!isset($this->vars['user_ids'][$rs->post_author])) { + $user_id = $this->core->auth->userID(); + } else { + $user_id = $this->vars['user_ids'][$rs->post_author]; + } + + $cur = $this->con->openCursor($this->prefix.'post'); + $cur->blog_id = $this->blog_id; + $cur->user_id = $user_id; + $cur->post_dt = $post_date; + $cur->post_creadt = $post_date; + $cur->post_upddt = $rs->post_modified; + $cur->post_title = $this->cleanStr($rs->post_title); + + if (!$cur->post_title) { + $cur->post_title = 'No title'; + } + + if ($this->vars['cat_import'] || $this->vars['cat_as_tags']) + { + $old_cat_ids = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'terms AS t, '. + $this->vars['db_prefix'].'term_taxonomy AS x, '. + $this->vars['db_prefix'].'term_relationships AS r '. + 'WHERE t.term_id = x.term_id '. + ($this->vars['ignore_first_cat'] ? 'AND t.term_id <> 1 ' : ''). + 'AND x.taxonomy = \'category\' '. + 'AND t.term_id = r.term_taxonomy_id '. + 'AND r.object_id ='.$rs->ID. + ' ORDER BY t.term_id ASC ' + ); + if (!$old_cat_ids->isEmpty() && $this->vars['cat_import']) + { + $cur->cat_id = $this->vars['cat_ids'][(integer) $old_cat_ids->term_id]; + } + } + + $permalink_infos = array( + date('Y',strtotime($cur->post_dt)), + date('m',strtotime($cur->post_dt)), + date('d',strtotime($cur->post_dt)), + date('H',strtotime($cur->post_dt)), + date('i',strtotime($cur->post_dt)), + date('s',strtotime($cur->post_dt)), + $rs->post_name, + $rs->ID, + $cur->cat_id, + $cur->user_id + ); + $cur->post_url = str_replace( + $this->vars['permalink_tags'], + $permalink_infos, + $rs->post_type== 'post' ? $this->vars['permalink_template'] : '%postname%' + ); + $cur->post_url = substr($cur->post_url,0,255); + + if (!$cur->post_url) { + $cur->post_url = $rs->ID; + } + + $cur->post_format = $this->vars['post_formater']; + $cur->post_content = $this->cleanStr($rs->post_content); + $cur->post_excerpt = $this->cleanStr($rs->post_excerpt); + + $cur->post_content_xhtml = $this->core->callFormater($this->vars['post_formater'],$cur->post_content); + $cur->post_excerpt_xhtml = $this->core->callFormater($this->vars['post_formater'],$cur->post_excerpt); + + switch ($rs->post_status) + { + case 'publish': + $cur->post_status = 1; + break; + case 'draft': + $cur->post_status = 0; + break; + case 'pending': + $cur->post_status = -2; + break; + default: + $cur->post_status = -2; + } + $cur->post_type = $rs->post_type; + $cur->post_password = $rs->post_password ? $rs->post_password : NULL; + $cur->post_open_comment = $rs->comment_status == 'open' ? 1 : 0; + $cur->post_open_tb = $rs->ping_status == 'open' ? 1 : 0; + + $cur->post_words = implode(' ',text::splitWords( + $cur->post_title.' '. + $cur->post_excerpt_xhtml.' '. + $cur->post_content_xhtml + )); + + $cur->post_id = $this->con->select( + 'SELECT MAX(post_id) FROM '.$this->prefix.'post' + )->f(0) + 1; + + $cur->post_url = $this->core->blog->getPostURL($cur->post_url,$cur->post_dt,$cur->post_title,$cur->post_id); + + $cur->insert(); + $this->importComments($rs->ID,$cur->post_id,$db); + $this->importPings($rs->ID,$cur->post_id,$db); + + # Create tags if we have metadata plugin in DC2 + if (class_exists('dcMeta')) { + $this->importTags($rs->ID,$cur->post_id,$db); + + if (!$old_cat_ids->isEmpty() && $this->vars['cat_as_tags']) + { + $old_cat_ids->moveStart(); + $meta = new dcMeta($this->core); + while ($old_cat_ids->fetch()) { + $meta->setPostMeta($cur->post_id,'tag',$this->cleanStr($this->vars['cat_tags_prefix'].$old_cat_ids->name)); + } + } + } + } + + # Comments import + protected function importComments($post_id,$new_post_id,&$db) + { + $count_c = $count_t = 0; + + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'comments '. + 'WHERE comment_post_ID = '.(integer) $post_id.' ' + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'comment'); + $cur->post_id = (integer) $new_post_id; + $cur->comment_author = $this->cleanStr($rs->comment_author); + $cur->comment_status = (integer) $rs->comment_approved ; + $cur->comment_dt = $rs->comment_date; + $cur->comment_email = $this->cleanStr($rs->comment_author_email); + $cur->comment_content = $this->core->callFormater($this->vars['comment_formater'],$this->cleanStr($rs->comment_content)); + $cur->comment_ip = $rs->comment_author_IP; + $cur->comment_trackback = $rs->comment_type == 'trackback' ? 1 : 0; + $cur->comment_site = substr($this->cleanStr($rs->comment_author_url),0,255); + if (!isset($cur->comment_site)) $cur->comment_site = NULL; + + if ($rs->comment_approved == 'spam') { + $cur->comment_status = -2; + } + + $cur->comment_words = implode(' ',text::splitWords($cur->comment_content)); + + $cur->comment_id = $this->con->select( + 'SELECT MAX(comment_id) FROM '.$this->prefix.'comment' + )->f(0) + 1; + + $cur->insert(); + + if ($cur->comment_trackback && $cur->comment_status == 1) { + $count_t++; + } elseif ($cur->comment_status == 1) { + $count_c++; + } + } + + if ($count_t > 0 || $count_c > 0) + { + $this->con->execute( + 'UPDATE '.$this->prefix.'post SET '. + 'nb_comment = '.$count_c.', '. + 'nb_trackback = '.$count_t.' '. + 'WHERE post_id = '.(integer) $new_post_id.' ' + ); + } + } + + # Pings import + protected function importPings($post_id,$new_post_id,&$db) + { + $urls = array(); + $pings = array(); + + $rs = $db->select( + 'SELECT pinged FROM '.$this->vars['db_prefix'].'posts '. + 'WHERE ID = '.(integer) $post_id + ); + $pings = explode ("\n",$rs->pinged); + unset ($pings[0]); + + foreach($pings as $ping_url) + { + $url = $this->cleanStr($ping_url); + if (isset($urls[$url])) { + continue; + } + + $cur = $this->con->openCursor($this->prefix.'ping'); + $cur->post_id = (integer) $new_post_id; + $cur->ping_url = $url; + $cur->insert(); + + $urls[$url] = true; + } + } + + # Meta import + protected function importTags($post_id,$new_post_id,&$db) + { + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'terms AS t, '. + $this->vars['db_prefix'].'term_taxonomy AS x, '. + $this->vars['db_prefix'].'term_relationships AS r '. + 'WHERE t.term_id = x.term_id '. + 'AND x.taxonomy = \'post_tag\' '. + 'AND t.term_id = r.term_taxonomy_id '. + 'AND r.object_id ='.$post_id. + ' ORDER BY t.term_id ASC' + ); + + if ($rs->isEmpty()) { + return; + } + + $meta = new dcMeta($this->core); + while ($rs->fetch()) { + $meta->setPostMeta($new_post_id,'tag',$this->cleanStr($rs->name)); + } + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/flat/class.backupFile.php b/plugins/importExport/inc/flat/class.backupFile.php new file mode 100644 index 0000000..fdc97d4 --- /dev/null +++ b/plugins/importExport/inc/flat/class.backupFile.php @@ -0,0 +1,132 @@ + "\n", + '\r' => "\r", + '\"' => '"' + ); + + public function __construct($file) + { + if (file_exists($file) && is_readable($file)) { + $this->fp = fopen($file,'rb'); + } else { + throw new Exception(__('No file to read.')); + } + } + + public function __destruct() + { + if ($this->fp) { + fclose($this->fp); + } + } + + public function getLine() + { + if (feof($this->fp)) { + return false; + } + + $line = trim(fgets($this->fp)); + + if (substr($line,0,1) == '[') + { + $this->line_name = substr($line,1,strpos($line,' ')-1); + + $line = substr($line,strpos($line,' ')+1,-1); + $this->line_cols = explode(',',$line); + + return $this->getLine(); + } + elseif (substr($line,0,1) == '"') + { + $line = preg_replace('/^"|"$/','',$line); + $line = preg_split('/(^"|","|"$)/m',$line); + + if (count($this->line_cols) != count($line)) { + throw new Exception('Invalid row count'); + } + + $res = array(); + + for ($i=0; $iline_cols[$i]] = + str_replace(array_keys($this->replacement),array_values($this->replacement),$line[$i]); + } + + return new backupFileItem($this->line_name,$res); + } + else + { + return $this->getLine(); + } + } +} + +class backupFileItem +{ + public $__name; + private $__data = array(); + + public function __construct($name,$data) + { + $this->__name = $name; + $this->__data = $data; + } + + public function f($name) + { + return iconv('UTF-8','UTF-8//IGNORE',$this->__data[$name]); + } + + public function __get($name) + { + return $this->f($name); + } + + public function __set($n,$v) + { + $this->__data[$n] = $v; + } + + public function exists($n) + { + return isset($this->__data[$n]); + } + + public function drop() + { + foreach (func_get_args() as $n) { + if (isset($this->__data[$n])) { + unset($this->__data[$n]); + } + } + } + + public function substitute($old,$new) + { + if (isset($this->__data[$old])) { + $this->__data[$new] = $this->__data[$old]; + unset($this->__data[$old]); + } + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/flat/class.db.export.php b/plugins/importExport/inc/flat/class.db.export.php new file mode 100644 index 0000000..198eccd --- /dev/null +++ b/plugins/importExport/inc/flat/class.db.export.php @@ -0,0 +1,106 @@ +con =& $con; + $this->prefix = $prefix; + + if (($this->fp = fopen($out,'w')) === false) { + return false; + } + @set_time_limit(300); + } + + function __destruct() + { + if (is_resource($this->fp)) { + fclose($this->fp); + } + } + + function export($name,$sql) + { + $rs = $this->con->select($sql); + + if (!$rs->isEmpty()) + { + fwrite($this->fp,"\n[".$name.' '.implode(',',$rs->columns())."]\n"); + while ($rs->fetch()) { + fwrite($this->fp,$this->getLine($rs)); + } + fflush($this->fp); + } + } + + function exportAll() + { + $tables = $this->getTables(); + + foreach ($tables as $table) + { + $this->exportTable($table); + } + } + + function exportTable($table) + { + $req = 'SELECT * FROM '.$this->con->escapeSystem($this->prefix.$table); + + $this->export($table,$req); + } + + function getTables() + { + $schema = dbSchema::init($this->con); + $db_tables = $schema->getTables(); + + $tables = array(); + foreach ($db_tables as $t) + { + if ($this->prefix) { + if (strpos($t,$this->prefix) === 0) { + $tables[] = $t; + } + } else { + $tables[] = $t; + } + } + + return $tables; + } + + function getLine(&$rs) + { + $l = array(); + $cols = $rs->columns(); + foreach ($cols as $i => &$c) { + $s = $rs->f($c); + $s = preg_replace($this->line_reg,$this->line_rep,$s); + $s = '"'.$s.'"'; + $l[$i] = $s; + } + return implode(',',$l)."\n"; + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/inc/flat/class.dc.import.php b/plugins/importExport/inc/flat/class.dc.import.php new file mode 100644 index 0000000..c3c925d --- /dev/null +++ b/plugins/importExport/inc/flat/class.dc.import.php @@ -0,0 +1,730 @@ + array(), + 'post' => array(), + 'media' => array() + ); + + public $stack = array( + 'categories'=>null, + 'cat_id'=>1, + 'cat_lft'=>array(), + 'post_id'=>1, + 'media_id'=>1, + 'comment_id'=>1, + 'link_id'=>1 + ); + + public $has_categories = false; + + public function __construct(&$core,$file) + { + parent::__construct($file); + + $first_line = fgets($this->fp); + if (strpos($first_line,'///DOTCLEAR|') !== 0) { + throw new Exception(__('File is not a DotClear backup.')); + } + + @set_time_limit(300); + + $l = explode('|',$first_line); + + if (isset($l[1])) { + $this->dc_version = $l[1]; + } + + $this->mode = isset($l[2]) ? strtolower(trim($l[2])) : 'single'; + if ($this->mode != 'full' && $this->mode != 'single') { + $this->mode = 'single'; + } + + if (version_compare('1.2',$this->dc_version,'<=') && + version_compare('1.3',$this->dc_version,'>')) { + $this->dc_major_version = '1.2'; + } else { + $this->dc_major_version = '2.0'; + } + + $this->core =& $core; + $this->con =& $core->con; + $this->prefix = $core->prefix; + + $this->cur_blog = $this->con->openCursor($this->prefix.'blog'); + $this->cur_category = $this->con->openCursor($this->prefix.'category'); + $this->cur_link = $this->con->openCursor($this->prefix.'link'); + $this->cur_setting = $this->con->openCursor($this->prefix.'setting'); + $this->cur_user = $this->con->openCursor($this->prefix.'user'); + $this->cur_permissions = $this->con->openCursor($this->prefix.'permissions'); + $this->cur_post = $this->con->openCursor($this->prefix.'post'); + $this->cur_media = $this->con->openCursor($this->prefix.'media'); + $this->cur_post_media = $this->con->openCursor($this->prefix.'post_media'); + $this->cur_log = $this->con->openCursor($this->prefix.'log'); + $this->cur_ping = $this->con->openCursor($this->prefix.'ping'); + $this->cur_comment = $this->con->openCursor($this->prefix.'comment'); + $this->cur_spamrule = $this->con->openCursor($this->prefix.'spamrule'); + $this->cur_version = $this->con->openCursor($this->prefix.'version'); + + # --BEHAVIOR-- importInit + $this->core->callBehavior('importInit',$this,$this->core); + } + + public function importSingle() + { + if ($this->mode != 'single') { + throw new Exception(__('File is not a single blog export.')); + } + + if (!$this->core->auth->check('admin',$this->core->blog->id)) { + throw new Exception(__('Permission denied.')); + } + + $this->blog_id = $this->core->blog->id; + + $this->stack['categories'] = $this->con->select( + 'SELECT cat_id, cat_title, cat_url '. + 'FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + $rs = $this->con->select('SELECT MAX(cat_id) FROM '.$this->prefix.'category'); + $this->stack['cat_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(link_id) FROM '.$this->prefix.'link'); + $this->stack['link_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(post_id) FROM '.$this->prefix.'post'); + $this->stack['post_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(media_id) FROM '.$this->prefix.'media'); + $this->stack['media_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(comment_id) FROM '.$this->prefix.'comment'); + $this->stack['comment_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select( + 'SELECT MAX(cat_rgt) AS cat_rgt FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->core->blog->id)."'" + ); + + if ((integer) $rs->cat_rgt > 0) { + $this->has_categories = true; + $this->stack['cat_lft'][$this->core->blog->id] = (integer) $rs->cat_rgt + 1; + } + + $this->con->begin(); + + try + { + while (($line = $this->getLine()) !== false) + { + # import DC 1.2.x, we fix lines before insert + if ($this->dc_major_version == '1.2') { + $this->prepareDC12line($line); + } + + switch ($line->__name) + { + case 'category': + $this->insertCategorySingle($line); + break; + case 'link': + $this->insertLinkSingle($line); + break; + case 'post': + $this->insertPostSingle($line); + break; + case 'media': + $this->insertMediaSingle($line); + break; + case 'post_media': + $this->insertPostMediaSingle($line); + break; + case 'ping': + $this->insertPingSingle($line); + break; + case 'comment': + $this->insertCommentSingle($line); + break; + } + + # --BEHAVIOR-- importSingle + $this->core->callBehavior('importSingle',$line,$this,$this->core); + } + } + catch (Exception $e) + { + $this->con->rollback(); + throw $e; + } + + $this->con->commit(); + } + + public function importFull() + { + if ($this->mode != 'full') { + throw new Exception(__('File is not a full export.')); + } + + if (!$this->core->auth->isSuperAdmin()) { + throw new Exception(__('Permission denied.')); + } + + $this->con->begin(); + $this->con->execute('DELETE FROM '.$this->prefix.'blog'); + $this->con->execute('DELETE FROM '.$this->prefix.'media'); + $this->con->execute('DELETE FROM '.$this->prefix.'spamrule'); + $this->con->execute('DELETE FROM '.$this->prefix.'setting'); + + try + { + while (($line = $this->getLine()) !== false) + { + switch ($line->__name) + { + case 'blog': + $this->insertBlog($line); + break; + case 'category': + $this->insertCategory($line); + break; + case 'link': + $this->insertLink($line); + break; + case 'setting': + $this->insertSetting($line); + break; + case 'user': + $this->insertUser($line); + break; + case 'permissions': + $this->insertPermissions($line); + break; + case 'post': + $this->insertPost($line); + break; + case 'media': + $this->insertMedia($line); + break; + case 'post_media': + $this->insertPostMedia($line); + break; + case 'log'; + $this->insertLog($line); + break; + case 'ping': + $this->insertPing($line); + break; + case 'comment': + $this->insertComment($line); + break; + case 'spamrule': + $this->insertSpamRule($line); + break; + } + # --BEHAVIOR-- importFull + $this->core->callBehavior('importFull',$line,$this,$this->core); + } + } + catch (Exception $e) + { + $this->con->rollback(); + throw $e; + } + + $this->con->commit(); + } + + private function insertBlog($blog) + { + $this->cur_blog->clean(); + + $this->cur_blog->blog_id = (string) $blog->blog_id; + $this->cur_blog->blog_uid = (string) $blog->blog_uid; + $this->cur_blog->blog_creadt = (string) $blog->blog_creadt; + $this->cur_blog->blog_upddt = (string) $blog->blog_upddt; + $this->cur_blog->blog_url = (string) $blog->blog_url; + $this->cur_blog->blog_name = (string) $blog->blog_name; + $this->cur_blog->blog_desc = (string) $blog->blog_desc; + + $this->cur_blog->blog_status = $blog->exists('blog_status') ? (integer) $blog->blog_status : 1; + + $this->cur_blog->insert(); + } + + private function insertCategory($category) + { + $this->cur_category->clean(); + + $this->cur_category->cat_id = (string) $category->cat_id; + $this->cur_category->blog_id = (string) $category->blog_id; + $this->cur_category->cat_title = (string) $category->cat_title; + $this->cur_category->cat_url = (string) $category->cat_url; + $this->cur_category->cat_desc = (string) $category->cat_desc; + + if (!$this->has_categories && $category->exists('cat_lft') && $category->exists('cat_rgt')) { + $this->cur_category->cat_lft = (integer) $category->cat_lft; + $this->cur_category->cat_rgt = (integer) $category->cat_rgt; + } else { + if (!isset($this->stack['cat_lft'][$category->blog_id])) { + $this->stack['cat_lft'][$category->blog_id] = 2; + } + $this->cur_category->cat_lft = $this->stack['cat_lft'][$category->blog_id]++; + $this->cur_category->cat_rgt = $this->stack['cat_lft'][$category->blog_id]++; + } + + $this->cur_category->insert(); + } + + private function insertLink($link) + { + $this->cur_link->clean(); + + $this->cur_link->link_id = (integer) $link->link_id; + $this->cur_link->blog_id = (string) $link->blog_id; + $this->cur_link->link_href = (string) $link->link_href; + $this->cur_link->link_title = (string) $link->link_title; + $this->cur_link->link_desc = (string) $link->link_desc; + $this->cur_link->link_lang = (string) $link->link_lang; + $this->cur_link->link_xfn = (string) $link->link_xfn; + $this->cur_link->link_position = (integer) $link->link_position; + + $this->cur_link->insert(); + } + + private function insertSetting($setting) + { + $this->cur_setting->clean(); + + $this->cur_setting->setting_id = (string) $setting->setting_id; + $this->cur_setting->blog_id = !$setting->blog_id ? null : (string) $setting->blog_id; + $this->cur_setting->setting_ns = (string) $setting->setting_ns; + $this->cur_setting->setting_value = (string) $setting->setting_value; + $this->cur_setting->setting_type = (string) $setting->setting_type; + $this->cur_setting->setting_label = (string) $setting->setting_label; + + $this->cur_setting->insert(); + } + + private function insertUser($user) + { + if ($this->userExists($user->user_id)) { + return; + } + + $this->cur_user->clean(); + + $this->cur_user->user_id = (string) $user->user_id; + $this->cur_user->user_super = (integer) $user->user_super; + $this->cur_user->user_pwd = (string) $user->user_pwd; + $this->cur_user->user_recover_key = (string) $user->user_recover_key; + $this->cur_user->user_name = (string) $user->user_name; + $this->cur_user->user_firstname = (string) $user->user_firstname; + $this->cur_user->user_displayname = (string) $user->user_displayname; + $this->cur_user->user_email = (string) $user->user_email; + $this->cur_user->user_url = (string) $user->user_url; + $this->cur_user->user_default_blog = !$user->user_default_blog ? null : (string) $user->user_default_blog; + $this->cur_user->user_lang = (string) $user->user_lang; + $this->cur_user->user_tz = (string) $user->user_tz; + $this->cur_user->user_post_status = (integer) $user->user_post_status; + $this->cur_user->user_creadt = (string) $user->user_creadt; + $this->cur_user->user_upddt = (string) $user->user_upddt; + + $this->cur_user->user_desc = $user->exists('user_desc') ? (string) $user->user_desc : null; + $this->cur_user->user_options = $user->exists('user_options') ? (string) $user->user_options : null; + $this->cur_user->user_status = $user->exists('user_status') ? (integer) $user->user_status : 1; + + $this->cur_user->insert(); + + $this->stack['users'][$user->user_id] = true; + } + + private function insertPermissions($permissions) + { + $this->cur_permissions->clean(); + + $this->cur_permissions->user_id = (string) $permissions->user_id; + $this->cur_permissions->blog_id = (string) $permissions->blog_id; + $this->cur_permissions->permissions = (string) $permissions->permissions; + + $this->cur_permissions->insert(); + } + + private function insertPost($post) + { + $this->cur_post->clean(); + + $cat_id = (integer) $post->cat_id; + if (!$cat_id) { + $cat_id = null; + } + + $post_password = $post->post_password ? (string) $post->post_password : null; + + $this->cur_post->post_id = (integer) $post->post_id; + $this->cur_post->blog_id = (string) $post->blog_id; + $this->cur_post->user_id = (string) $this->getUserId($post->user_id); + $this->cur_post->cat_id = $cat_id; + $this->cur_post->post_dt = (string) $post->post_dt; + $this->cur_post->post_creadt = (string) $post->post_creadt; + $this->cur_post->post_upddt = (string) $post->post_upddt; + $this->cur_post->post_password = $post_password; + $this->cur_post->post_type = (string) $post->post_type; + $this->cur_post->post_format = (string) $post->post_format; + $this->cur_post->post_url = (string) $post->post_url; + $this->cur_post->post_lang = (string) $post->post_lang; + $this->cur_post->post_title = (string) $post->post_title; + $this->cur_post->post_excerpt = (string) $post->post_excerpt; + $this->cur_post->post_excerpt_xhtml = (string) $post->post_excerpt_xhtml; + $this->cur_post->post_content = (string) $post->post_content; + $this->cur_post->post_content_xhtml = (string) $post->post_content_xhtml; + $this->cur_post->post_notes = (string) $post->post_notes; + $this->cur_post->post_words = (string) $post->post_words; + $this->cur_post->post_meta = (string) $post->post_meta; + $this->cur_post->post_status = (integer) $post->post_status; + $this->cur_post->post_selected = (integer) $post->post_selected; + $this->cur_post->post_open_comment = (integer) $post->post_open_comment; + $this->cur_post->post_open_tb = (integer) $post->post_open_tb; + $this->cur_post->nb_comment = (integer) $post->nb_comment; + $this->cur_post->nb_trackback = (integer) $post->nb_trackback; + + $this->cur_post->post_tz = $post->exists('post_tz') ? (string) $post->post_tz : 'UTC'; + + $this->cur_post->insert(); + } + + private function insertMedia($media) + { + $this->cur_media->clean(); + + $this->cur_media->media_id = (integer) $media->media_id; + $this->cur_media->user_id = (string) $media->user_id; + $this->cur_media->media_path = (string) $media->media_path; + $this->cur_media->media_title = (string) $media->media_title; + $this->cur_media->media_file = (string) $media->media_file; + $this->cur_media->media_meta = (string) $media->media_meta; + $this->cur_media->media_dt = (string) $media->media_dt; + $this->cur_media->media_creadt = (string) $media->media_creadt; + $this->cur_media->media_upddt = (string) $media->media_upddt; + $this->cur_media->media_private = (integer) $media->media_private; + + $this->cur_media->media_dir = $media->exists('media_dir') ? (string) $media->media_dir : dirname($media->media_file); + + $this->cur_media->insert(); + } + + private function insertPostMedia($post_media) + { + $this->cur_post_media->clean(); + + $this->cur_post_media->media_id = (integer) $post_media->media_id; + $this->cur_post_media->post_id = (integer) $post_media->post_id; + + $this->cur_post_media->insert(); + } + + private function insertLog($log) + { + $this->cur_log->clean(); + + $this->cur_log->log_id = (integer) $log->log_id; + $this->cur_log->user_id = (string) $log->user_id; + $this->cur_log->log_table = (string) $log->log_table; + $this->cur_log->log_dt = (string) $log->log_dt; + $this->cur_log->log_ip = (string) $log->log_ip; + $this->cur_log->log_msg = (string) $log->log_msg; + + $this->cur_log->insert(); + } + + private function insertPing($ping) + { + $this->cur_ping->clean(); + + $this->cur_ping->post_id = (integer) $ping->post_id; + $this->cur_ping->ping_url = (string) $ping->ping_url; + $this->cur_ping->ping_dt = (string) $ping->ping_dt; + + $this->cur_ping->insert(); + } + + private function insertComment($comment) + { + $this->cur_comment->clean(); + + $this->cur_comment->comment_id = (integer) $comment->comment_id; + $this->cur_comment->post_id = (integer) $comment->post_id; + $this->cur_comment->comment_dt = (string) $comment->comment_dt; + $this->cur_comment->comment_upddt = (string) $comment->comment_upddt; + $this->cur_comment->comment_author = (string) $comment->comment_author; + $this->cur_comment->comment_email = (string) $comment->comment_email; + $this->cur_comment->comment_site = (string) $comment->comment_site; + $this->cur_comment->comment_content = (string) $comment->comment_content; + $this->cur_comment->comment_words = (string) $comment->comment_words; + $this->cur_comment->comment_ip = (string) $comment->comment_ip; + $this->cur_comment->comment_status = (integer) $comment->comment_status; + $this->cur_comment->comment_spam_status = (integer) $comment->comment_spam_status; + $this->cur_comment->comment_trackback = (integer) $comment->comment_trackback; + + $this->cur_comment->comment_tz = $comment->exists('comment_tz') ? (string) $comment->comment_tz : 'UTC'; + $this->cur_comment->comment_spam_filter = $comment->exists('comment_spam_filter') ? (integer) $comment->comment_spam_filter : null; + + $this->cur_comment->insert(); + } + + private function insertSpamRule($spamrule) + { + $this->cur_spamrule->clean(); + + $this->cur_spamrule->rule_id = (integer) $spamrule->rule_id; + $this->cur_spamrule->blog_id = !$spamrule->blog_id ? null : (string) $spamrule->blog_id; + $this->cur_spamrule->rule_type = (string) $spamrule->rule_type; + $this->cur_spamrule->rule_content = (string) $spamrule->rule_content; + + $this->cur_spamrule->insert(); + } + + private function insertCategorySingle($category) + { + $this->cur_category->clean(); + + $m = $this->searchCategory($this->stack['categories'],$category->cat_url); + + $old_id = $category->cat_id; + if ($m !== false) + { + $cat_id = $m; + } + else + { + $cat_id = $this->stack['cat_id']; + $category->cat_id = $cat_id; + $category->blog_id = $this->blog_id; + + $this->insertCategory($category); + $this->stack['cat_id']++; + } + + $this->old_ids['category'][(integer) $old_id] = $cat_id; + } + + private function insertLinkSingle($link) + { + $link->blog_id = $this->blog_id; + $link->link_id = $this->stack['link_id']; + + $this->insertLink($link); + $this->stack['link_id']++; + } + + private function insertPostSingle($post) + { + $post_id = $this->stack['post_id']; + $this->old_ids['post'][(integer) $post->post_id] = $post_id; + + $cat_id = $post->cat_id ? $this->old_ids['category'][(integer) $post->cat_id] : null; + + $post->post_id = $post_id; + $post->cat_id = $cat_id; + $post->blog_id = $this->blog_id; + + $post->post_url = $this->core->blog->getPostURL( + $post->post_url,$post->post_dt,$post->post_title,$post->post_id + ); + + $this->insertPost($post); + $this->stack['post_id']++; + } + + private function insertMediaSingle($media) + { + $media_id = $this->stack['media_id']; + $old_id = $media->media_id; + + $media->media_id = $media_id; + $media->media_path = $this->core->blog->settings->public_path; + $media->user_id = $this->getUserId($media->user_id); + + $this->insertMedia($media); + $this->stack['media_id']++; + $this->old_ids['media'][(integer) $old_id] = $media_id; + } + + private function insertPostMediaSingle($post_media) + { + $post_media->media_id = $this->old_ids['media'][(integer) $post_media->media_id]; + $post_media->post_id = $this->old_ids['post'][(integer) $post_media->post_id]; + + $this->insertPostMedia($post_media); + } + + private function insertPingSingle($ping) + { + $ping->post_id = $this->old_ids['post'][(integer) $ping->post_id]; + + $this->insertPing($ping); + } + + private function insertCommentSingle($comment) + { + $comment_id = $this->stack['comment_id']; + + $comment->comment_id = $comment_id; + $comment->post_id = $this->old_ids['post'][(integer) $comment->post_id]; + + $this->insertComment($comment); + $this->stack['comment_id']++; + } + + public function searchCategory(&$rs,$url) + { + while ($rs->fetch()) + { + if ($rs->cat_url == $url) { + return $rs->cat_id; + } + } + + return false; + } + + public function getUserId($user_id) + { + if (!$this->userExists($user_id)) + { + if ($this->core->auth->isSuperAdmin()) + { + # Sanitizes user_id and create a lambda user + $user_id = preg_replace('/[^A-Za-z0-9]$/','',$user_id); + $user_id .= strlen($user_id) < 2 ? '-a' : ''; + + # We change user_id, we need to check again + if (!$this->userExists($user_id)) + { + $this->cur_user->clean(); + $this->cur_user->user_id = (string) $user_id; + $this->cur_user->user_pwd = md5(uniqid()); + + $this->core->addUser($this->cur_user); + + $this->stack['users'][$user_id] = true; + } + } + else + { + # Returns current user id + $user_id = $this->core->auth->userID(); + } + } + + return $user_id; + } + + private function userExists($user_id) + { + if (isset($this->stack['users'][$user_id])) { + return $this->stack['users'][$user_id]; + } + + $strReq = 'SELECT user_id '. + 'FROM '.$this->prefix.'user '. + "WHERE user_id = '".$this->con->escape($user_id)."' "; + + $rs = $this->con->select($strReq); + + $this->stack['users'][$user_id] = !$rs->isEmpty(); + return $this->stack['users'][$user_id]; + } + + private function prepareDC12line(&$line) + { + $settings = array('dc_theme','dc_nb_post_per_page','dc_allow_comments', + 'dc_allow_trackbacks','dc_comment_pub','dc_comments_ttl', + 'dc_wiki_comments','dc_use_smilies','dc_date_format','dc_time_format', + 'dc_url_scan'); + + switch ($line->__name) + { + case 'categorie': + $line->substitute('cat_libelle','cat_title'); + $line->substitute('cat_libelle_url','cat_url'); + $line->__name = 'category'; + $line->blog_id = 'default'; + break; + case 'link': + $line->substitute('href','link_href'); + $line->substitute('label','link_title'); + $line->substitute('title','link_desc'); + $line->substitute('lang','link_lang'); + $line->substitute('rel','link_xfn'); + $line->substitute('position','link_position'); + $line->blog_id = 'default'; + break; + case 'post': + $line->substitute('post_titre','post_title'); + $line->post_title = html::decodeEntities($line->post_title); + $line->post_url = date('Y/m/d/',strtotime($line->post_dt)).$line->post_id.'-'.$line->post_titre_url; + $line->post_url = substr($line->post_url,0,255); + $line->post_format = $line->post_content_wiki == '' ? 'xhtml' : 'wiki'; + $line->post_content_xhtml = $line->post_content; + $line->post_excerpt_xhtml = $line->post_chapo; + + if ($line->post_format == 'wiki') { + $line->post_content = $line->post_content_wiki; + $line->post_excerpt = $line->post_chapo_wiki; + } else { + $line->post_content = $line->post_content; + $line->post_excerpt = $line->post_chapo; + } + + $line->post_status = (integer) $line->post_pub; + $line->post_type = 'post'; + $line->blog_id = 'default'; + + $line->drop('post_titre_url','post_content_wiki','post_chapo','post_chapo_wiki','post_pub'); + + break; + case 'comment': + $line->substitute('comment_auteur','comment_author'); + if ($line->comment_site != '' && !preg_match('!^http://.*$!', $line->comment_site,$m)) { + $line->comment_site = 'http://'.$line->comment_site; + } + $line->comment_status = (integer) $line->comment_pub; + $line->drop('comment_pub'); + break; + } + + # --BEHAVIOR-- importPrepareDC12 + $this->core->callBehavior('importPrepareDC12',$line,$this,$this->core); + } +} +?> \ No newline at end of file diff --git a/plugins/importExport/index.php b/plugins/importExport/index.php new file mode 100644 index 0000000..e0010b0 --- /dev/null +++ b/plugins/importExport/index.php @@ -0,0 +1,143 @@ + dirname(__FILE__).'/inc/class.dc.import.flat.php', + 'dcImportFeed' => dirname(__FILE__).'/inc/class.dc.import.feed.php', +)); +$modules['e'] = new ArrayObject(array( + 'dcExportFlat' => dirname(__FILE__).'/inc/class.dc.export.flat.php' +)); + +if ($core->auth->isSuperAdmin()) { + $modules['i']['dcImportDC1'] = dirname(__FILE__).'/inc/class.dc.import.dc1.php'; + $modules['i']['dcImportWP'] = dirname(__FILE__).'/inc/class.dc.import.wp.php'; +} + +# --BEHAVIOR-- importExportModules +$core->callBehavior('importExportModules',$modules); + +$type = null; +if (!empty($_REQUEST['t']) && ($_REQUEST['t'] == 'e' || $_REQUEST['t'] == 'i')) { + $type = $_REQUEST['t']; +} + +$current_module = null; +if ($type && !empty($_REQUEST['f'])) { + if (isset($modules[$type][$_REQUEST['f']])) { + require_once $modules[$type][$_REQUEST['f']]; + $current_module = new $_REQUEST['f']($core); + $current_module->init(); + } +} + +if ($type && $current_module !== null && !empty($_REQUEST['do'])) +{ + try { + $current_module->process($_REQUEST['do']); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} +?> + + + <?php echo __('Import/Export'); ?> + + + + + + +'.__('Import/Export').''. + ' › '.html::escapeHTML($current_module->name).''; + + echo '
    '; + $current_module->gui(); + echo '
    '; +} +else +{ + echo '

    '.__('Import/Export').'

    '; + echo '

    '.__('Import').'

    '; + + echo '
    '; + foreach ($modules['i'] as $k => $v) + { + require_once $v; + $o = new $k($core); + + echo + '
    '.html::escapeHTML($o->name).'
    '. + '
    '.html::escapeHTML($o->description).'
    '; + + unset($o); + } + echo '
    '; + + echo '

    '.__('Export').'

    '; + + echo '
    '; + foreach ($modules['e'] as $k => $v) + { + require_once $v; + $o = new $k($core); + + echo + '
    '.html::escapeHTML($o->name).'
    '. + '
    '.html::escapeHTML($o->description).'
    '; + + unset($o); + } + echo '
    '; +} +?> + + + \ No newline at end of file diff --git a/plugins/importExport/progress.png b/plugins/importExport/progress.png new file mode 100644 index 0000000..267f541 Binary files /dev/null and b/plugins/importExport/progress.png differ diff --git a/plugins/importExport/script.js b/plugins/importExport/script.js new file mode 100644 index 0000000..82892fd --- /dev/null +++ b/plugins/importExport/script.js @@ -0,0 +1,3 @@ + +$(function(){if($('*.error').length>0){return;} +$('#ie-gui form[method=post]:has(input[type=hidden][name=autosubmit])').each(function(){$('input[type=submit]',this).remove();$(this).after('

    '+dotclear.msg.please_wait+'

    ');$(this).submit();});}); \ No newline at end of file diff --git a/plugins/maintenance/_admin.php b/plugins/maintenance/_admin.php new file mode 100644 index 0000000..2c1e1f4 --- /dev/null +++ b/plugins/maintenance/_admin.php @@ -0,0 +1,17 @@ +addItem(__('Maintenance'),'plugin.php?p=maintenance','index.php?pf=maintenance/icon.png', + preg_match('/plugin.php\?p=maintenance(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); +?> \ No newline at end of file diff --git a/plugins/maintenance/_define.php b/plugins/maintenance/_define.php new file mode 100644 index 0000000..e64330c --- /dev/null +++ b/plugins/maintenance/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "Maintenance", + /* Description*/ "Maintain your database", + /* Author */ "Olivier Meunier", + /* Version */ '1.0', + /* Permissions */ null +); +?> \ No newline at end of file diff --git a/plugins/maintenance/icon.png b/plugins/maintenance/icon.png new file mode 100644 index 0000000..57bdcdd Binary files /dev/null and b/plugins/maintenance/icon.png differ diff --git a/plugins/maintenance/index.php b/plugins/maintenance/index.php new file mode 100644 index 0000000..ceba23f --- /dev/null +++ b/plugins/maintenance/index.php @@ -0,0 +1,164 @@ +con); + $db_tables = $schema->getTables(); + + foreach ($db_tables as $t) { + if (strpos($t,$core->prefix) === 0) { + $core->con->vacuum($t); + } + } + http::redirect($p_url.'&vacuum=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +elseif ($action == 'commentscount') +{ + try { + $core->countAllComments(); + http::redirect($p_url.'&commentscount=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} +elseif ($action == 'empty_cache') +{ + try { + $core->emptyTemplatesCache(); + http::redirect($p_url.'&empty_cache=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +?> + + + <?php echo __('Maintenance'); ?> + + + +

    + +'.__('Optimization successful.').'

    '; +} +if (!empty($_GET['commentscount'])) { + echo '

    '.__('Comments and trackback counted.').'

    '; +} +if (!empty($_GET['empty_cache'])) { + echo '

    '.__('Templates cache directory emptied.').'

    '; +} + +if ($action == 'index' && !empty($_GET['indexposts'])) +{ + $limit = 1000; + echo '

    '.sprintf(__('Indexing entry %d to %d.'),$start,$start+$limit).'

    '; + + $new_start = $core->indexAllPosts($start,$limit); + + if ($new_start) + { + $new_url = $p_url.'&action=index&indexposts=1&start='.$new_start; + echo + ''. + ''; + } + else + { + echo '

    '.__('Entries index done.').'

    '; + echo '

    '.__('Back').'

    '; + } +} +elseif ($action == 'index' && !empty($_GET['indexcomments'])) +{ + $limit = 1000; + echo '

    '.sprintf(__('Indexing comment %d to %d.'),$start,$start+$limit).'

    '; + + $new_start = $core->indexAllComments($start,$limit); + + if ($new_start) + { + $new_url = $p_url.'&action=index&indexcomments=1&start='.$new_start; + echo + ''. + ''; + } + else + { + echo '

    '.__('Comments index done.').'

    '; + echo '

    '.__('Back').'

    '; + } +} +else +{ + echo + '

    '.__('Optimize database room').'

    '. + '
    '. + '

    '. + $core->formNonce(). + form::hidden(array('action'),'vacuum'). + form::hidden(array('p'),'maintenance').'

    '. + '
    '; + + echo + '

    '.__('Counters').'

    '. + '
    '. + '

    '. + $core->formNonce(). + form::hidden(array('action'),'commentscount'). + form::hidden(array('p'),'maintenance').'

    '. + '
    '; + + echo + '

    '.__('Search engine index').' ('.__('This may take a very long time').')

    '. + '
    '. + '

    '. + ' '. + form::hidden(array('action'),'index'). + form::hidden(array('p'),'maintenance').'

    '. + '
    '; + + echo + '

    '.__('Empty templates cache directory').'

    '. + '
    '. + '

    '. + $core->formNonce(). + form::hidden(array('action'),'empty_cache'). + form::hidden(array('p'),'maintenance').'

    '. + '
    '; +} +?> + + + \ No newline at end of file diff --git a/plugins/metadata/_admin.php b/plugins/metadata/_admin.php new file mode 100644 index 0000000..c2ca314 --- /dev/null +++ b/plugins/metadata/_admin.php @@ -0,0 +1,413 @@ +addItem(__('Tags'),'plugin.php?p=metadata&m=tags','index.php?pf=metadata/tags.png', + preg_match('/plugin.php\?p=metadata&m=tag(s|_posts)?(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + +require dirname(__FILE__).'/_widgets.php'; + +$core->addBehavior('adminPostFormSidebar',array('metaBehaviors','tagsField')); + +$core->addBehavior('adminAfterPostCreate',array('metaBehaviors','setTags')); +$core->addBehavior('adminAfterPostUpdate',array('metaBehaviors','setTags')); + +$core->addBehavior('adminPostHeaders',array('metaBehaviors','postHeaders')); + +$core->addBehavior('adminPostsActionsCombo',array('metaBehaviors','adminPostsActionsCombo')); +$core->addBehavior('adminPostsActions',array('metaBehaviors','adminPostsActions')); +$core->addBehavior('adminPostsActionsContent',array('metaBehaviors','adminPostsActionsContent')); + +$core->addBehavior('coreInitWikiPost',array('metaBehaviors','coreInitWikiPost')); + +$core->addBehavior('exportFull',array('metaBehaviors','exportFull')); +$core->addBehavior('exportSingle',array('metaBehaviors','exportSingle')); +$core->addBehavior('importInit',array('metaBehaviors','importInit')); +$core->addBehavior('importSingle',array('metaBehaviors','importSingle')); +$core->addBehavior('importFull',array('metaBehaviors','importFull')); +$core->addBehavior('importPrepareDC12',array('metaBehaviors','importPrepareDC12')); + +$core->rest->addFunction('getMeta',array('metaRest','getMeta')); +$core->rest->addFunction('delMeta',array('metaRest','delMeta')); +$core->rest->addFunction('setPostMeta',array('metaRest','setPostMeta')); + +# BEHAVIORS +class metaBehaviors +{ + public static function coreInitWikiPost(&$wiki2xhtml) + { + $wiki2xhtml->registerFunction('url:tag',array('metaBehaviors','wiki2xhtmlTag')); + } + + public static function wiki2xhtmlTag($url,$content) + { + $url = substr($url,4); + if (strpos($content,'tag:') === 0) { + $content = substr($content,4); + } + + + $tag_url = html::stripHostURL($GLOBALS['core']->blog->url.$GLOBALS['core']->url->getBase('tag')); + $res['url'] = $tag_url.'/'.rawurlencode(dcMeta::sanitizeMetaID($url)); + $res['content'] = $content; + + return $res; + } + + public static function tagsField(&$post) + { + $meta = new dcMeta($GLOBALS['core']); + + if (!empty($_POST['post_tags'])) { + $value = $_POST['post_tags']; + } else { + $value = ($post) ? $meta->getMetaStr($post->post_meta,'tag') : ''; + } + + echo + '

    '. + '
    '.form::textarea('post_tags',20,3,$value,'maximal',3).'
    '; + } + + public static function setTags(&$cur,&$post_id) + { + $post_id = (integer) $post_id; + + if (isset($_POST['post_tags'])) { + $tags = $_POST['post_tags']; + + $meta = new dcMeta($GLOBALS['core']); + + $meta->delPostMeta($post_id,'tag'); + + foreach ($meta->splitMetaValues($tags) as $tag) { + $meta->setPostMeta($post_id,'tag',$tag); + } + } + } + + public static function postHeaders() + { + $tag_url = $GLOBALS['core']->blog->url.$GLOBALS['core']->url->getBase('tag'); + + return + ''. + '\n". + ''; + } + + public static function adminPostsActionsCombo(&$args) + { + $args[0][__('add tags')] = 'tags'; + + if ($GLOBALS['core']->auth->check('delete,contentadmin',$GLOBALS['core']->blog->id)) { + $args[0][__('remove tags')] = 'tags_remove'; + } + } + + public static function adminPostsActions(&$core,$posts,$action,$redir) + { + if ($action == 'tags' && !empty($_POST['new_tags'])) + { + try + { + $meta = new dcMeta($core); + $tags = $meta->splitMetaValues($_POST['new_tags']); + + while ($posts->fetch()) + { + # Get tags for post + $post_meta = $meta->getMeta('tag',null,null,$posts->post_id); + $pm = array(); + while ($post_meta->fetch()) { + $pm[] = $post_meta->meta_id; + } + + foreach ($tags as $t) { + if (!in_array($t,$pm)) { + $meta->setPostMeta($posts->post_id,'tag',$t); + } + } + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'tags_remove' && !empty($_POST['meta_id']) && $core->auth->check('delete,contentadmin',$core->blog->id)) + { + try + { + $meta = new dcMeta($core); + while ($posts->fetch()) + { + foreach ($_POST['meta_id'] as $v) + { + $meta->delPostMeta($posts->post_id,'tag',$v); + } + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + } + + public static function adminPostsActionsContent($core,$action,$hidden_fields) + { + if ($action == 'tags') + { + echo + '

    '.__('Add tags to entries').'

    '. + '
    '. + '

    '. + + $hidden_fields. + $core->formNonce(). + form::hidden(array('action'),'tags'). + '

    '. + '
    '; + } + elseif ($action == 'tags_remove') + { + $meta = new dcMeta($core); + $tags = array(); + + foreach ($_POST['entries'] as $id) { + $post_tags = $meta->getMeta('tag',null,null,(integer) $id)->rows(); + foreach ($post_tags as $v) { + if (isset($tags[$v['meta_id']])) { + $tags[$v['meta_id']]++; + } else { + $tags[$v['meta_id']] = 1; + } + } + } + + echo '

    '.__('Remove selected tags from entries').'

    '; + + if (empty($tags)) { + echo '

    '.__('No tags for selected entries').'

    '; + return; + } + + $posts_count = count($_POST['entries']); + + echo + '
    '. + '
    '.__('Following tags have been found in selected entries:').''; + + foreach ($tags as $k => $n) { + $label = ''; + if ($posts_count == $n) { + $label = sprintf($label,'%s','%s'); + } + echo '

    '.sprintf($label, + form::checkbox(array('meta_id[]'),html::escapeHTML($k)), + html::escapeHTML($k)). + '

    '; + } + + echo + '

    '. + $hidden_fields. + $core->formNonce(). + form::hidden(array('action'),'tags_remove'). + '
    '; + } + } + + public static function exportFull(&$core,&$exp) + { + $exp->exportTable('meta'); + } + + public static function exportSingle(&$core,&$exp,$blog_id) + { + $exp->export('meta', + 'SELECT meta_id, meta_type, M.post_id '. + 'FROM '.$core->prefix.'meta M, '.$core->prefix.'post P '. + 'WHERE P.post_id = M.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + } + + public static function importInit(&$bk,&$core) + { + $bk->cur_meta = $core->con->openCursor($core->prefix.'meta'); + $bk->meta = new dcMeta($core); + } + + public static function importFull(&$line,&$bk,&$core) + { + if ($line->__name == 'meta') + { + $bk->cur_meta->clean(); + + $bk->cur_meta->meta_id = (string) $line->meta_id; + $bk->cur_meta->meta_type = (string) $line->meta_type; + $bk->cur_meta->post_id = (integer) $line->post_id; + + $bk->cur_meta->insert(); + } + } + + public static function importSingle(&$line,&$bk,&$core) + { + if ($line->__name == 'meta' && isset($bk->old_ids['post'][(integer) $line->post_id])) + { + $line->post_id = $bk->old_ids['post'][(integer) $line->post_id]; + $bk->meta->setPostMeta($line->post_id,$line->meta_type,$line->meta_id); + } + } + + public static function importPrepareDC12(&$line,&$bk,&$core) + { + if ($line->__name == 'post_meta') + { + $line->drop('meta_id'); + $line->substitute('meta_key','meta_type'); + $line->substitute('meta_value','meta_id'); + $line->__name = 'meta'; + $line->blog_id = 'default'; + } + } +} + +# REST +class metaRest +{ + public static function getMeta(&$core,$get) + { + $meta = new dcMeta($core); + + $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 = $meta->getMeta($metaType,$limit,$metaId,$postid); + + $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'])) { + throw new Exception('No meta'); + } + + if (empty($post['metaType'])) { + throw new Exception('No meta type'); + } + + $meta = new dcMeta($core); + + # Get previous meta for post + $post_meta = $meta->getMeta($post['metaType'],null,null,$post['postId']); + $pm = array(); + while ($post_meta->fetch()) { + $pm[] = $post_meta->meta_id; + } + + foreach ($meta->splitMetaValues($post['meta']) as $m) + { + if (!in_array($m,$pm)) { + $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'])) { + throw new Exception('No meta ID'); + } + + if (empty($post['metaType'])) { + throw new Exception('No meta type'); + } + + $meta = new dcMeta($core); + + $meta->delPostMeta($post['postId'],$post['metaType'],$post['metaId']); + + return true; + } +} +?> \ No newline at end of file diff --git a/plugins/metadata/_define.php b/plugins/metadata/_define.php new file mode 100644 index 0000000..2ae7b58 --- /dev/null +++ b/plugins/metadata/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "Metadata", + /* Description*/ "Metadata for posts", + /* Author */ "Olivier Meunier", + /* Version */ '1.0.2', + /* Permissions */ 'usage,contentadmin' +); +?> \ No newline at end of file diff --git a/plugins/metadata/_install.php b/plugins/metadata/_install.php new file mode 100644 index 0000000..bb30a84 --- /dev/null +++ b/plugins/metadata/_install.php @@ -0,0 +1,47 @@ +plugins->moduleInfo('metadata','version'); + +if (version_compare($core->getVersion('metadata'),$version,'>=')) { + return; +} + +/* Database schema +-------------------------------------------------------- */ +$s = new dbStruct($core->con,$core->prefix); + +$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->post + ->post_meta ('text', 0, true, null) + ; + +$s->meta->index('idx_meta_post_id','btree','post_id'); +$s->meta->index('idx_meta_meta_type','btree','meta_type'); +$s->meta->reference('fk_meta_post','post_id','post','post_id','cascade','cascade'); + + +# Schema installation +$si = new dbStruct($core->con,$core->prefix); +$changes = $si->synchronize($s); + +$core->setVersion('metadata',$version); +return true; +?> \ No newline at end of file diff --git a/plugins/metadata/_prepend.php b/plugins/metadata/_prepend.php new file mode 100644 index 0000000..3269c4a --- /dev/null +++ b/plugins/metadata/_prepend.php @@ -0,0 +1,19 @@ +url->register('tag','tag','^tag/(.+)$',array('urlMetadata','tag')); +$GLOBALS['core']->url->register('tags','tags','^tags$',array('urlMetadata','tags')); +$GLOBALS['core']->url->register('tag_feed','feed/tag','^feed/tag/(.+)$',array('urlMetadata','tagFeed')); + +$GLOBALS['__autoload']['dcMeta'] = dirname(__FILE__).'/class.dc.meta.php'; +?> \ No newline at end of file diff --git a/plugins/metadata/_public.php b/plugins/metadata/_public.php new file mode 100644 index 0000000..635c1ef --- /dev/null +++ b/plugins/metadata/_public.php @@ -0,0 +1,345 @@ +tpl->addBlock('Tags',array('tplMetadata','Tags')); +$core->tpl->addBlock('TagsHeader',array('tplMetadata','TagsHeader')); +$core->tpl->addBlock('TagsFooter',array('tplMetadata','TagsFooter')); +$core->tpl->addBlock('EntryTags',array('tplMetadata','EntryTags')); +$core->tpl->addValue('TagID',array('tplMetadata','TagID')); +$core->tpl->addValue('TagPercent',array('tplMetadata','TagPercent')); +$core->tpl->addValue('TagRoundPercent',array('tplMetadata','TagRoundPercent')); +$core->tpl->addValue('TagURL',array('tplMetadata','TagURL')); +$core->tpl->addValue('TagCloudURL',array('tplMetadata','TagCloudURL')); +$core->tpl->addValue('TagFeedURL',array('tplMetadata','TagFeedURL')); + +# Kept for backward compatibility (for now) +$core->tpl->addBlock('MetaData',array('tplMetadata','Tags')); +$core->tpl->addBlock('MetaDataHeader',array('tplMetadata','TagsHeader')); +$core->tpl->addBlock('MetaDataFooter',array('tplMetadata','TagsFooter')); +$core->tpl->addValue('MetaID',array('tplMetadata','TagID')); +$core->tpl->addValue('MetaPercent',array('tplMetadata','TagPercent')); +$core->tpl->addValue('MetaRoundPercent',array('tplMetadata','TagRoundPercent')); +$core->tpl->addValue('MetaURL',array('tplMetadata','TagURL')); +$core->tpl->addValue('MetaAllURL',array('tplMetadata','TagCloudURL')); +$core->tpl->addBlock('EntryMetaData',array('tplMetadata','EntryTags')); + + + +$core->addBehavior('templateBeforeBlock',array('behaviorsMetadata','templateBeforeBlock')); + +class behaviorsMetadata +{ + public static function templateBeforeBlock(&$core,$b,$attr) + { + if (($b == 'Entries' || $b == 'Comments') && isset($attr['tag'])) + { + return + "prefix.'meta META ';\n". + "@\$params['sql'] .= 'AND META.post_id = P.post_id ';\n". + "\$params['sql'] .= \"AND META.meta_type = 'tag' \";\n". + "\$params['sql'] .= \"AND META.meta_id = '".$core->con->escape($attr['tag'])."' \";\n". + "?>\n"; + } + elseif (empty($attr['no_context']) && ($b == 'Entries' || $b == 'Comments')) + { + return + 'exists("meta")) { '. + "@\$params['from'] .= ', '.\$core->prefix.'meta META ';\n". + "@\$params['sql'] .= 'AND META.post_id = P.post_id ';\n". + "\$params['sql'] .= \"AND META.meta_type = 'tag' \";\n". + "\$params['sql'] .= \"AND META.meta_id = '\".\$core->con->escape(\$_ctx->meta->meta_id).\"' \";\n". + "} ?>\n"; + } + } +} + +class tplMetadata +{ + public static function Tags($attr,$content) + { + $type = isset($attr['type']) ? addslashes($attr['type']) : 'tag'; + + $limit = isset($attr['limit']) ? (integer) $attr['limit'] : 'null'; + + $sortby = 'meta_id_lower'; + if (isset($attr['sortby']) && $attr['sortby'] == 'count') { + $sortby = 'count'; + } + + $order = 'asc'; + if (isset($attr['order']) && $attr['order'] == 'desc') { + $order = 'desc'; + } + + $res = + "meta = \$objMeta->getMeta('".$type."',".$limit."); ". + "\$_ctx->meta->sort('".$sortby."','".$order."'); ". + '?>'; + + $res .= + 'meta->fetch()) : ?>'.$content.'meta = null; unset($objMeta); ?>'; + + return $res; + } + + public static function TagsHeader($attr,$content) + { + return + "meta->isStart()) : ?>". + $content. + ""; + } + + public static function TagsFooter($attr,$content) + { + return + "meta->isEnd()) : ?>". + $content. + ""; + } + + public static function EntryTags($attr,$content) + { + $type = isset($attr['type']) ? addslashes($attr['type']) : 'tag'; + + $sortby = 'meta_id_lower'; + if (isset($attr['sortby']) && $attr['sortby'] == 'count') { + $sortby = 'count'; + } + + $order = 'asc'; + if (isset($attr['order']) && $attr['order'] == 'desc') { + $order = 'desc'; + } + + $res = + "meta = \$objMeta->getMetaRecordset(\$_ctx->posts->post_meta,'".$type."'); ". + "\$_ctx->meta->sort('".$sortby."','".$order."'); ". + '?>'; + + $res .= + 'meta->fetch()) : ?>'.$content.'meta = null; unset($objMeta); ?>'; + + return $res; + } + + public static function TagID($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'meta->meta_id').'; ?>'; + } + + public static function TagPercent($attr) + { + return 'meta->percent; ?>'; + } + + public static function TagRoundPercent($attr) + { + return 'meta->roundpercent; ?>'; + } + + public static function TagURL($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'blog->url.$core->url->getBase("tag").'. + '"/".rawurlencode($_ctx->meta->meta_id)').'; ?>'; + } + + public static function TagCloudURL($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'blog->url.$core->url->getBase("tags")').'; ?>'; + } + + public static function TagFeedURL($attr) + { + $type = !empty($attr['type']) ? $attr['type'] : 'rss2'; + + if (!preg_match('#^(rss2|atom)$#',$type)) { + $type = 'rss2'; + } + + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'blog->url.$core->url->getBase("tag_feed")."/".'. + 'rawurlencode($_ctx->meta->meta_id)."/'.$type.'"').'; ?>'; + } + + # Widget function + public static function tagsWidget(&$w) + { + global $core; + + $limit = abs((integer) $w->limit); + + $objMeta = new dcMeta($core); + $rs = $objMeta->getMeta('tag',$limit); + + if ($rs->isEmpty()) { + return; + } + + $sort = $w->sortby; + if (!in_array($sort,array('meta_id_lower','count'))) { + $sort = 'meta_id_lower'; + } + + $order = $w->orderby; + if ($order != 'asc') { + $order = 'desc'; + } + + $rs->sort($sort,$order); + + $res = + '
    '. + ($w->title ? '

    '.html::escapeHTML($w->title).'

    ' : ''). + ''; + + if ($core->url->getBase('tags')) + { + $res .= + '

    '. + __('All tags').'

    '; + } + + $res .= '
    '; + + return $res; + } +} + +class urlMetadata extends dcUrlHandlers +{ + public static function tag($args) + { + $n = self::getPageNumber($args); + + if ($args == '' && !$n) + { + self::p404(); + } + elseif (preg_match('%(.*?)/feed/(rss2|atom)?$%u',$args,$m)) + { + $type = $m[2] == 'atom' ? 'atom' : 'rss2'; + $mime = 'application/xml'; + $comments = !empty($m[3]); + + $objMeta = new dcMeta($GLOBALS['core']); + $GLOBALS['_ctx']->meta = $objMeta->getMeta('tag',null,$m[1]); + + if ($GLOBALS['_ctx']->meta->isEmpty()) { + self::p404(); + } + else + { + $tpl = $type; + + if ($type == 'atom') { + $mime = 'application/atom+xml'; + } + + self::serveDocument($tpl.'.xml',$mime); + } + } + else + { + if ($n) { + $GLOBALS['_page_number'] = $n; + } + + $objMeta = new dcMeta($GLOBALS['core']); + $GLOBALS['_ctx']->meta = $objMeta->getMeta('tag',null,$args); + + if ($GLOBALS['_ctx']->meta->isEmpty()) { + self::p404(); + } else { + self::serveDocument('tag.html'); + } + } + } + + public static function tags($args) + { + self::serveDocument('tags.html'); + } + + public static function tagFeed($args) + { + if (!preg_match('#^(.+)/(atom|rss2)(/comments)?$#',$args,$m)) + { + self::p404(); + } + else + { + $tag = $m[1]; + $type = $m[2]; + $comments = !empty($m[3]); + + $objMeta = new dcMeta($GLOBALS['core']); + $GLOBALS['_ctx']->meta = $objMeta->getMeta('tag',null,$tag); + + if ($GLOBALS['_ctx']->meta->isEmpty()) { + # The specified tag does not exist. + self::p404(); + } + else + { + $GLOBALS['_ctx']->feed_subtitle = ' - '.__('Tag').' - '.$GLOBALS['_ctx']->meta->meta_id; + + if ($type == 'atom') { + $mime = 'application/atom+xml'; + } else { + $mime = 'application/xml'; + } + + $tpl = $type; + if ($comments) { + $tpl .= '-comments'; + $GLOBALS['_ctx']->nb_comment_per_page = $GLOBALS['core']->blog->settings->nb_comment_per_feed; + } else { + $GLOBALS['_ctx']->nb_entry_per_page = $GLOBALS['core']->blog->settings->nb_post_per_feed; + $GLOBALS['_ctx']->short_feed_items = $GLOBALS['core']->blog->settings->short_feed_items; + } + $tpl .= '.xml'; + + self::serveDocument($tpl,$mime); + } + } + } +} +?> \ No newline at end of file diff --git a/plugins/metadata/_widgets.php b/plugins/metadata/_widgets.php new file mode 100644 index 0000000..5aeb87b --- /dev/null +++ b/plugins/metadata/_widgets.php @@ -0,0 +1,37 @@ +addBehavior('initWidgets',array('metaWidgets','initWidgets')); +$core->addBehavior('initDefaultWidgets',array('metaWidgets','initDefaultWidgets')); + +class metaWidgets +{ + public static function initWidgets(&$w) + { + $w->create('tags',__('Tags'),array('tplMetadata','tagsWidget')); + $w->tags->setting('title',__('Title:'),__('Tags')); + $w->tags->setting('limit',__('Limit (empty means no limit):'),'20'); + $w->tags->setting('sortby',__('Order by:'),'meta_id_lower','combo', + array(__('Tag name') => 'meta_id_lower', __('Entries count') => 'count') + ); + $w->tags->setting('orderby',__('Sort:'),'asc','combo', + array(__('Ascending') => 'asc', __('Descending') => 'desc') + ); + } + + public static function initDefaultWidgets(&$w,&$d) + { + $d['nav']->append($w->tags); + } +} +?> \ No newline at end of file diff --git a/plugins/metadata/_xmlrpc.php b/plugins/metadata/_xmlrpc.php new file mode 100644 index 0000000..61d0d63 --- /dev/null +++ b/plugins/metadata/_xmlrpc.php @@ -0,0 +1,51 @@ +addBehavior('xmlrpcGetPostInfo',array('metaXMLRPCbehaviors','getPostInfo')); +$core->addBehavior('xmlrpcAfterNewPost',array('metaXMLRPCbehaviors','editPost')); +$core->addBehavior('xmlrpcAfterEditPost',array('metaXMLRPCbehaviors','editPost')); + +class metaXMLRPCbehaviors +{ + public static function getPostInfo(&$x,$type,&$res) + { + $res =& $res[0]; + + $meta = new dcMeta($x->core); + $rs = $meta->getMeta('tag',null,null,$res['postid']); + + $m = array(); + while($rs->fetch()) { + $m[] = $rs->meta_id; + } + + $res['mt_keywords'] = implode(', ',$m); + } + + # Same function for newPost and editPost + public static function editPost($x,$post_id,&$cur,$content,$struct,$publish) + { + # Check if we have mt_keywords in struct + if (isset($struct['mt_keywords'])) + { + $meta = new dcMeta($x->core); + + $meta->delPostMeta($post_id,'tag'); + + foreach ($meta->splitMetaValues($struct['mt_keywords']) as $m) { + $meta->setPostMeta($post_id,'tag',$m); + } + } + } +} +?> \ No newline at end of file diff --git a/plugins/metadata/class.dc.meta.php b/plugins/metadata/class.dc.meta.php new file mode 100644 index 0000000..aff5df1 --- /dev/null +++ b/plugins/metadata/class.dc.meta.php @@ -0,0 +1,422 @@ +core =& $core; + $this->con =& $this->core->con; + $this->table = $this->core->prefix.'meta'; + } + + public function splitMetaValues($str) + { + $res = array(); + foreach (explode(',',$str) as $i => $tag) + { + $tag = trim($tag); + $tag = self::sanitizeMetaID($tag); + + if ($tag) { + $res[$i] = $tag; + } + } + + return array_unique($res); + } + + public static function sanitizeMetaID($str) + { + return text::tidyURL($str,false,true); + } + + public function getMetaArray($str) + { + $meta = @unserialize($str); + + if (!is_array($meta)) { + return array(); + } + + return $meta; + } + + public function getMetaStr($str,$type) + { + $meta = $this->getMetaArray($str); + + if (!isset($meta[$type])) { + return ''; + } + + return implode(', ',$meta[$type]); + } + + public function getMetaRecordset($str,$type) + { + $meta = $this->getMetaArray($str); + $data = array(); + + if (isset($meta[$type])) + { + foreach ($meta[$type] as $v) + { + $data[] = array( + 'meta_id' => $v, + 'meta_type' => $type, + 'meta_id_lower' => mb_strtolower($v), + 'count' => 0, + 'percent' => 0, + 'roundpercent' => 0 + ); + } + } + + return staticRecord::newFromArray($data); + } + + public static function getMetaRecord(&$core,$str,$type) + { + $meta = new self($core); + return $meta->getMetaRecordset($str,$type); + } + + 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')); + } + } + } + + 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 = array(); + 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(); + } + + public function getPostsByMeta($params=array(),$count_only=false) + { + if (!isset($params['meta_id'])) { + return null; + } + + $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); + } + + public function getCommentsByMeta($params=array(),$count_only=false) + { + if (!isset($params['meta_id'])) { + return null; + } + + $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); + } + + public function getMeta($type=null,$limit=null,$meta_id=null,$post_id=null) + { + $strReq = 'SELECT meta_id, meta_type, COUNT(M.post_id) as count '. + '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 ($type !== null) { + $strReq .= " AND meta_type = '".$this->con->escape($type)."' "; + } + + if ($meta_id !== null) { + $strReq .= " AND meta_id = '".$this->con->escape($meta_id)."' "; + } + + if ($post_id !== null) { + $strReq .= ' AND P.post_id = '.(integer) $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 .= ') '; + } + } + + $strReq .= + 'GROUP BY meta_id,meta_type,P.blog_id '. + 'ORDER BY count DESC'; + + if ($limit) { + $strReq .= $this->con->limit($limit); + } + + $rs = $this->con->select($strReq); + $rs = $rs->toStatic(); + + $max = array(); + while ($rs->fetch()) + { + $type = $rs->meta_type; + if (!isset($max[$type])) { + $max[$type] = $rs->count; + } else { + if ($rs->count > $max[$type]) { + $max[$type] = $rs->count; + } + } + } + + while ($rs->fetch()) + { + $rs->set('meta_id_lower',mb_strtolower($rs->meta_id)); + + $count = $rs->count; + $percent = ((integer) $rs->count) * 100 / $max[$rs->meta_type]; + + $rs->set('percent',(integer) round($percent)); + $rs->set('roundpercent',round($percent/10)*10); + } + + return $rs; + } + + public function setPostMeta($post_id,$type,$value) + { + $this->checkPermissionsOnPost($post_id); + + $value = trim($value); + if (!$value) { 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); + } + + 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); + } + + 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 = array(); + + $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; + } + + 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); + + $ids = array(); + 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/plugins/metadata/index.php b/plugins/metadata/index.php new file mode 100644 index 0000000..501206d --- /dev/null +++ b/plugins/metadata/index.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/plugins/metadata/post.js b/plugins/metadata/post.js new file mode 100644 index 0000000..6512830 --- /dev/null +++ b/plugins/metadata/post.js @@ -0,0 +1,17 @@ + +$(function(){$('#edit-entry').onetabload(function(){var meta_edit_tags=$('#meta-edit-tags');var post_id=$('#id');var meta_field=null;if(meta_edit_tags.length>0){post_id=(post_id.length>0)?post_id.get(0).value:false;if(post_id==false){meta_field=$('');meta_field.val($('#post_tags').val());} +var mEdit=new metaEditor(meta_edit_tags,meta_field);mEdit.displayMeta('tag',post_id);window.dc_tag_editor=mEdit;}});});function metaEditor(target,meta_field){this.target=target;this.meta_field=meta_field;};metaEditor.prototype={meta_ur:'',text_confirm_remove:'Are you sure you want to remove this %s?',text_add_meta:'Add a %s to this entry',text_choose:'Choose from list',text_all:'all',target:null,meta_type:null,meta_dialog:null,meta_field:null,post_id:false,service_uri:'services.php',displayMeta:function(type,post_id){this.meta_type=type;this.post_id=post_id;this.target.empty();this.meta_dialog=$('');this.meta_dialog.attr('title',this.text_add_meta.replace(/%s/,this.meta_type));var This=this;this.addMetaDialog();if(this.post_id==false){this.target.append(this.meta_field);} +this.displayMetaList();},displayMetaList:function(){var li;if(this.meta_list==undefined){this.meta_list=$('
      ');this.target.prepend(this.meta_list);} +if(this.post_id==false){var meta=this.splitMetaValues(this.meta_field.val());this.meta_list.empty();for(var i=0;i'+meta[i]+'');a_remove=$('[x]');a_remove.get(0).caller=this;a_remove.get(0).meta_id=meta[i];a_remove.click(function(){this.caller.removeMeta(this.meta_id);return false;});li.append(' ').append(a_remove);this.meta_list.append(li);}}else{var This=this;var params={f:'getMeta',metaType:this.meta_type,sortby:'metaId,asc',postId:this.post_id};$.get(this.service_uri,params,function(data){data=$(data);if(data.find('rsp').attr('status')!='ok'){return;} +This.meta_list.empty();data.find('meta').each(function(){var meta_id=$(this).text();li=$('
    • '+meta_id+'
    • ');a_remove=$('[x]');a_remove.get(0).caller=This;a_remove.get(0).meta_id=meta_id;a_remove.click(function(){this.caller.removeMeta(this.meta_id);return false;});li.append(' ').append(a_remove);This.meta_list.append(li);});});}},addMetaDialog:function(){var This=this;this.meta_dialog.keypress(function(evt){if(evt.keyCode==13){This.addMeta(this.value);return false;} +return true;});var S=$('');S.click(function(){var v=This.meta_dialog.val();This.addMeta(v);return false;});this.target.append($('

      ').append(this.meta_dialog).append(' ').append(S));var a=$(''+this.text_choose+'');a.click(function(){This.showMetaList('more',$(this).parent());return false;});this.target.append($('

      ').append(a));},showMetaList:function(type,target){target.empty();target.append('...');target.addClass('addMeta');var params={f:'getMeta',metaType:this.meta_type,sortby:'metaId,asc'};if(type=='more'){params.limit='30';} +var This=this;$.get(this.service_uri,params,function(data){if($(data).find('meta').length>0){target.empty();var meta_link;$(data).find('meta').each(function(i){meta_link=$(''+$(this).text()+'');meta_link.get(0).meta_id=$(this).text();meta_link.click(function(){var v=This.splitMetaValues(This.meta_dialog.val()+','+this.meta_id);This.meta_dialog.val(v.join(','));return false;});if(i>0){target.append(', ');} +target.append(meta_link);});if(type=='more'){var a_more=$('');a_more.append(This.text_all+String.fromCharCode(160)+String.fromCharCode(187));a_more.click(function(){This.showMetaList('all',target);return false;});target.append(', ').append(a_more);}}else{target.empty();}});},addMeta:function(str){str=this.splitMetaValues(str).join(',');if(this.post_id==false){str=this.splitMetaValues(this.meta_field.val()+','+str);this.meta_field.val(str);this.meta_dialog.val('');this.displayMetaList();}else{var params={xd_check:dotclear.nonce,f:'setPostMeta',postId:this.post_id,metaType:this.meta_type,meta:str};var This=this;$.post(this.service_uri,params,function(data){if($(data).find('rsp').attr('status')=='ok'){This.meta_dialog.val('');This.displayMetaList();}else{alert($(data).find('message').text());}});}},removeMeta:function(meta_id){if(this.post_id==false){var meta=this.splitMetaValues(this.meta_field.val());for(var i=0;i'+str+'';}});};jsToolBar.prototype.elements.tag.fn.wysiwyg=function(){var t=this.getSelectedText();if(t==''){window.alert(dotclear.msg.no_selection);return;} +if(t.indexOf(',')!=-1){return;} +var n=this.getSelectedNode();var a=document.createElement('a');a.href=this.stripBaseURL(this.elements.tag.url+'/'+t);a.appendChild(n);this.insertNode(a);window.dc_tag_editor.addMeta(t);}; \ No newline at end of file diff --git a/plugins/metadata/style.css b/plugins/metadata/style.css new file mode 100644 index 0000000..4e275bb --- /dev/null +++ b/plugins/metadata/style.css @@ -0,0 +1,56 @@ +.meta0 { } +.meta10 { font-size: 140%; } +.meta20 { font-size: 150%; } +.meta30 { font-size: 160%; } +.meta40 { font-size: 170%; } +.meta50 { font-size: 180%; } +.meta60 { font-size: 190%; } +.meta70 { font-size: 200%; } +.meta80 { font-size: 210%; } +.meta90 { font-size: 220%; } +.meta100 { font-size: 230%; } + +table.tags { + margin-left: 3em; +} + + +tr.tagLetter span { + font-size : 250%; + border: none; + display : block; + width: 1ex; + position: relative; + top: 0.80em; + left: -2ex; +} + +ul.metaList { + margin: 0 0 1em 0; + padding: 0; +} +ul.metaList li { + margin: 0; + padding: 0; + list-style: square; + list-style-position: inside; +} + +a.metaRemove { + color : #999 !important; + border: none; +} +a.metaRemove:hover, a.metaRemove:focus { + color : #06c !important; +} + +.addMeta a { + border: none; +} +.addMeta a:hover, .addMeta a:focus { + background: #fc0; + color: #000; +} +.addMeta a.metaGetMore { + font-weight: bold; +} \ No newline at end of file diff --git a/plugins/metadata/tag-add.png b/plugins/metadata/tag-add.png new file mode 100644 index 0000000..d0facbe Binary files /dev/null and b/plugins/metadata/tag-add.png differ diff --git a/plugins/metadata/tag_posts.php b/plugins/metadata/tag_posts.php new file mode 100644 index 0000000..84d1215 --- /dev/null +++ b/plugins/metadata/tag_posts.php @@ -0,0 +1,156 @@ +updateMeta($tag,$new_id,'tag')) { + http::redirect($p_url.'&m=tag_posts&tag='.$new_id.'&renamed=1'); + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Delete a tag +if (!empty($_POST['delete']) && $core->auth->check('publish,contentadmin',$core->blog->id)) +{ + try { + $meta->delMeta($tag,'tag'); + http::redirect($p_url.'&m=tags&del=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +$params = array(); +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; + +$params['meta_id'] = $tag; +$params['meta_type'] = 'tag'; +$params['post_type'] = ''; + +# Get posts +try { + $posts = $meta->getPostsByMeta($params); + $counter = $meta->getPostsByMeta($params,true); + $post_list = new adminPostList($core,$posts,$counter->f(0)); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +# Actions combo box +$combo_action = array(); +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('schedule')] = 'schedule'; + $combo_action[__('mark as pending')] = 'pending'; +} +$combo_action[__('mark as selected')] = 'selected'; +$combo_action[__('mark as unselected')] = 'unselected'; +$combo_action[__('change category')] = 'category'; +if ($core->auth->check('delete,contentadmin',$core->blog->id)) { + $combo_action[__('delete')] = 'delete'; +} +$combo_action[__('add tags')] = 'tags'; +if ($core->auth->check('delete,contentadmin',$core->blog->id)) { + $combo_action[__('remove tags')] = 'tags_remove'; +} + +?> + + + Tags + + + + + + +

      blog->name); ?> › +

      + +'.__('Tag has been successfully renamed').'

      '; +} + +echo '

      '.__('Back to tags list').'

      '; + +if (!$core->error->flag()) +{ + if (!$posts->isEmpty()) + { + echo + '
      '. + '

      '. + $core->formNonce().'

      '. + '
      '; + } + + # Show posts + $post_list->display($page,$nb_per_page, + '
      '. + + '%s'. + + '
      '. + '

      '. + + '

      '.__('Selected entries action:').' '. + form::combo('action',$combo_action). + '

      '. + form::hidden('post_type',''). + form::hidden('redir',$p_url.'&m=tag_posts&tag='. + str_replace('%','%%',rawurlencode($tag)).'&page='.$page). + $core->formNonce(). + '
      '. + '
      '); + + # Remove tag + if (!$posts->isEmpty() && $core->auth->check('contentadmin',$core->blog->id)) { + echo + '
      '. + '

      '. + $core->formNonce().'

      '. + '
      '; + } +} +?> + + diff --git a/plugins/metadata/tags.php b/plugins/metadata/tags.php new file mode 100644 index 0000000..677e6b0 --- /dev/null +++ b/plugins/metadata/tags.php @@ -0,0 +1,77 @@ + + + + Tags + + + + +

      blog->name); ?> › +

      + +'.__('Tag has been successfully removed').'

      '; +} + +$meta = new dcMeta($core); + +$tags = $meta->getMeta('tag'); +$tags->sort('meta_id_lower','asc'); + +$last_letter = null; +$cols = array('',''); +$col = 0; +while ($tags->fetch()) +{ + $letter = mb_strtoupper(mb_substr($tags->meta_id,0,1)); + + if ($last_letter != $letter) { + if ($tags->index() >= round($tags->count()/2)) { + $col = 1; + } + $cols[$col] .= ''.$letter.''; + } + + $cols[$col] .= + ''. + ''.$tags->meta_id.''. + ''.$tags->count.' '.__('entries').''. + ''; + + $last_letter = $letter; +} + +$table = '
      %s
      '; + +if ($cols[0]) +{ + echo '
      '; + printf($table,$cols[0]); + if ($cols[1]) { + printf($table,$cols[1]); + } + echo '
      '; +} +else +{ + echo '

      '.__('No tags on this blog.').'

      '; +} +?> + + + \ No newline at end of file diff --git a/plugins/metadata/tags.png b/plugins/metadata/tags.png new file mode 100644 index 0000000..9db1d4c Binary files /dev/null and b/plugins/metadata/tags.png differ diff --git a/plugins/pages/_admin.php b/plugins/pages/_admin.php new file mode 100644 index 0000000..e78a240 --- /dev/null +++ b/plugins/pages/_admin.php @@ -0,0 +1,27 @@ +addBehavior('adminDashboardIcons','pages_dashboard'); +function pages_dashboard(&$core,&$icons) +{ + $icons['pages'] = new ArrayObject(array(__('Pages'),'plugin.php?p=pages','index.php?pf=pages/icon-big.png')); +} + +$_menu['Blog']->addItem(__('Pages'),'plugin.php?p=pages','index.php?pf=pages/icon.png', + preg_match('/plugin.php\?p=pages(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('contentadmin,pages',$core->blog->id)); + +$core->auth->setPermissionType('pages',__('manage pages')); + +require dirname(__FILE__).'/_widgets.php'; +?> \ No newline at end of file diff --git a/plugins/pages/_define.php b/plugins/pages/_define.php new file mode 100644 index 0000000..94e919b --- /dev/null +++ b/plugins/pages/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "Pages", + /* Description*/ "Serve entries as simple web pages", + /* Author */ "Olivier Meunier", + /* Version */ '1.0', + /* Permissions */ 'contentadmin,pages', + 999 +); +?> \ No newline at end of file diff --git a/plugins/pages/_prepend.php b/plugins/pages/_prepend.php new file mode 100644 index 0000000..b4ae6f8 --- /dev/null +++ b/plugins/pages/_prepend.php @@ -0,0 +1,21 @@ +url->register('pages','pages','^pages/(.+)$',array('urlPages','pages')); +$core->url->register('pagespreview','pagespreview','^pagespreview/(.+)$',array('urlPages','pagespreview')); + +$core->setPostType('page','plugin.php?p=pages&act=page&id=%d',$core->url->getBase('pages').'/%s'); + +# We should put this as settings later +$GLOBALS['page_url_format'] = '{t}'; +?> \ No newline at end of file diff --git a/plugins/pages/_public.php b/plugins/pages/_public.php new file mode 100644 index 0000000..4fdf779 --- /dev/null +++ b/plugins/pages/_public.php @@ -0,0 +1,265 @@ +blog->withoutPassword(false); + + $params = new ArrayObject(); + $params['post_type'] = 'page'; + $params['post_url'] = $args; + + $_ctx->posts = $core->blog->getPosts($params); + + $_ctx->comment_preview = new ArrayObject(); + $_ctx->comment_preview['content'] = ''; + $_ctx->comment_preview['rawcontent'] = ''; + $_ctx->comment_preview['name'] = ''; + $_ctx->comment_preview['mail'] = ''; + $_ctx->comment_preview['site'] = ''; + $_ctx->comment_preview['preview'] = false; + $_ctx->comment_preview['remember'] = false; + + $core->blog->withoutPassword(true); + + + if ($_ctx->posts->isEmpty()) + { + # The specified page does not exist. + self::p404(); + } + else + { + $post_id = $_ctx->posts->post_id; + $post_password = $_ctx->posts->post_password; + + # Password protected entry + if ($post_password != '' && !$_ctx->preview) + { + # Get passwords cookie + if (isset($_COOKIE['dc_passwd'])) { + $pwd_cookie = unserialize($_COOKIE['dc_passwd']); + } else { + $pwd_cookie = array(); + } + + # Check for match + if ((!empty($_POST['password']) && $_POST['password'] == $post_password) + || (isset($pwd_cookie[$post_id]) && $pwd_cookie[$post_id] == $post_password)) + { + $pwd_cookie[$post_id] = $post_password; + setcookie('dc_passwd',serialize($pwd_cookie),0,'/'); + } + else + { + self::serveDocument('password-form.html','text/html',false); + return; + } + } + + $post_comment = + isset($_POST['c_name']) && isset($_POST['c_mail']) && + isset($_POST['c_site']) && isset($_POST['c_content']) && + $_ctx->posts->commentsActive(); + + # Posting a comment + if ($post_comment) + { + # Spam trap + if (!empty($_POST['f_mail'])) { + http::head(412,'Precondition Failed'); + header('Content-Type: text/plain'); + echo "So Long, and Thanks For All the Fish"; + # Exits immediately the application to preserve the server. + exit; + } + + $name = $_POST['c_name']; + $mail = $_POST['c_mail']; + $site = $_POST['c_site']; + $content = $_POST['c_content']; + $preview = !empty($_POST['preview']); + + if ($content != '') + { + if ($core->blog->settings->wiki_comments) { + $core->initWikiComment(); + } else { + $core->initWikiSimpleComment(); + } + $content = $core->wikiTransform($content); + $content = $core->HTMLfilter($content); + } + + $_ctx->comment_preview['content'] = $content; + $_ctx->comment_preview['rawcontent'] = $_POST['c_content']; + $_ctx->comment_preview['name'] = $name; + $_ctx->comment_preview['mail'] = $mail; + $_ctx->comment_preview['site'] = $site; + + if ($preview) + { + # --BEHAVIOR-- publicBeforeCommentPreview + $core->callBehavior('publicBeforeCommentPreview',$_ctx->comment_preview); + + $_ctx->comment_preview['preview'] = true; + } + else + { + # Post the comment + $cur = $core->con->openCursor($core->prefix.'comment'); + $cur->comment_author = $name; + $cur->comment_site = html::clean($site); + $cur->comment_email = html::clean($mail); + $cur->comment_content = $content; + $cur->post_id = $_ctx->posts->post_id; + $cur->comment_status = $core->blog->settings->comments_pub ? 1 : -1; + $cur->comment_ip = http::realIP(); + + $redir = $_ctx->posts->getURL(); + $redir .= strpos($redir,'?') !== false ? '&' : '?'; + + try + { + if (!text::isEmail($cur->comment_email)) { + throw new Exception(__('You must provide a valid email address.')); + } + + # --BEHAVIOR-- publicBeforeCommentCreate + $core->callBehavior('publicBeforeCommentCreate',$cur); + if ($cur->post_id) { + $comment_id = $core->blog->addComment($cur); + + # --BEHAVIOR-- publicAfterCommentCreate + $core->callBehavior('publicAfterCommentCreate',$cur,$comment_id); + } + + if ($cur->comment_status == 1) { + $redir_arg = 'pub=1'; + } else { + $redir_arg = 'pub=0'; + } + + header('Location: '.$redir.$redir_arg); + } + catch (Exception $e) + { + $_ctx->form_error = $e->getMessage(); + $_ctx->form_error; + } + } + } + + # The entry + $core->tpl->setPath($core->tpl->getPath(), dirname(__FILE__).'/default-templates'); + self::serveDocument('page.html'); + } + } + } + + public static function pagespreview($args) + { + $core = $GLOBALS['core']; + $_ctx = $GLOBALS['_ctx']; + + if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#',$args,$m)) { + # The specified Preview URL is malformed. + self::p404(); + } + else + { + $user_id = $m[1]; + $user_key = $m[2]; + $post_url = $m[3]; + if (!$core->auth->checkUser($user_id,null,$user_key)) { + # The user has no access to the entry. + self::p404(); + } + else + { + $_ctx->preview = true; + self::pages($post_url); + } + } + } +} + +class tplPages +{ + # Widget function + public static function pagesWidget(&$w) + { + global $core, $_ctx; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $params['post_type'] = 'page'; + $params['limit'] = abs((integer) $w->limit); + $params['no_content'] = true; + + $sort = $w->sortby; + if (!in_array($sort,array('post_title','post_position','post_dt'))) { + $sort = 'post_title'; + } + + $order = $w->orderby; + if ($order != 'asc') { + $order = 'desc'; + } + $params['order'] = $sort.' '.$order; + + $rs = $core->blog->getPosts($params); + + if ($rs->isEmpty()) { + return; + } + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
        '; + + while ($rs->fetch()) { + $class = ''; + if (($core->url->type == 'pages' && $_ctx->posts instanceof record && $_ctx->posts->post_id == $rs->post_id)) { + $class = ' class="page-current"'; + } + $res .= ''. + html::escapeHTML($rs->post_title).''; + } + + $res .= '
      '; + + return $res; + } +} +?> diff --git a/plugins/pages/_widgets.php b/plugins/pages/_widgets.php new file mode 100644 index 0000000..d89ef39 --- /dev/null +++ b/plugins/pages/_widgets.php @@ -0,0 +1,41 @@ +addBehavior('initWidgets',array('pagesWidgets','initWidgets')); +$core->addBehavior('initDefaultWidgets',array('pagesWidgets','initDefaultWidgets')); + +class pagesWidgets +{ + public static function initWidgets(&$w) + { + $w->create('pages',__('Pages'),array('tplPages','pagesWidget')); + $w->pages->setting('title',__('Title:'),__('Pages')); + $w->pages->setting('homeonly',__('Home page only'),1,'check'); + $w->pages->setting('sortby',__('Order by:'),'meta_id_lower','combo', + array( + __('Page title') => 'post_title', + __('Page position') => 'post_position', + __('Publication date') => 'post_dt' + ) + ); + $w->pages->setting('orderby',__('Sort:'),'asc','combo', + array(__('Ascending') => 'asc', __('Descending') => 'desc') + ); + } + + public static function initDefaultWidgets(&$w,&$d) + { + $d['extra']->append($w->pages); + } +} +?> \ No newline at end of file diff --git a/plugins/pages/default-templates/page.html b/plugins/pages/default-templates/page.html new file mode 100644 index 0000000..2aa53e0 --- /dev/null +++ b/plugins/pages/default-templates/page.html @@ -0,0 +1,250 @@ + + + + + + + + {{tpl:EntryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + + + + +
      +{{tpl:EntryPingData}} + +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:EntryTitle encode_html="1"}}

      + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + +
      {{tpl:EntryExcerpt}}
      +
      +
      {{tpl:EntryContent}}
      + +

      {{tpl:lang Published on}} {{tpl:EntryDate}} + {{tpl:lang by}} {{tpl:EntryAuthorLink}}

      + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} +
      + + + + +
      +

      {{tpl:lang Attachments}}

      +
        + +
      • + + {{tpl:include src="_mp3_player.html"/}} - + + + {{tpl:include src="_flv_player.html"/}} + + + {{tpl:AttachmentTitle}} + +
      • + +
      +
      + +
      + + + + + +
      +

      {{tpl:lang Comments}}

      +
      + +
      {{tpl:CommentOrderNumber}}. + {{tpl:lang On}} {{tpl:CommentDate}}, {{tpl:CommentTime}} + {{tpl:lang by}} {{tpl:CommentAuthorLink}}
      + +
      + + {{tpl:SysBehavior behavior="publicCommentBeforeContent"}} + + {{tpl:CommentContent}} + + + {{tpl:SysBehavior behavior="publicCommentAfterContent"}} +
      + +
      +
      + +
      +
      + + + +

      {{tpl:SysFormError}}

      +
      + + +

      {{tpl:lang Your comment has been published.}}

      +
      + + +

      {{tpl:lang Your comment has been submitted and + will be reviewed for publication.}}

      +
      + + +
      + +
      +

      {{tpl:lang Your comment}}

      +
      +
      {{tpl:CommentPreviewContent}}
      +
      +

      +
      +
      + +

      {{tpl:lang Add a comment}}

      +
      + + {{tpl:SysBehavior behavior="publicCommentFormBeforeContent"}} + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      {{tpl:lang HTML code is displayed as text and web addresses are + automatically converted.}}

      + + + {{tpl:SysBehavior behavior="publicCommentFormAfterContent"}} +
      + +
      +

      +

      +
      +
      +
      + + + +
      +

      {{tpl:lang They posted on the same topic}}

      + + +
      + +
      {{tpl:PingOrderNumber}}. + {{tpl:lang On}} {{tpl:PingDate}}, {{tpl:PingTime}} + {{tpl:lang by}} {{tpl:PingBlogName encode_html="1"}}
      + +
      + + {{tpl:SysBehavior behavior="publicPingBeforeContent"}} + +

      {{tpl:PingTitle encode_html="1"}}

      + {{tpl:PingContent}} + + + {{tpl:SysBehavior behavior="publicPingAfterContent"}} +
      + +
      + +
      +
      +
      + + +

      {{tpl:lang Trackback URL}} : {{tpl:EntryPingLink}}

      +
      + + +

      {{tpl:lang This page's comments feed}}

      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/plugins/pages/icon-big.png b/plugins/pages/icon-big.png new file mode 100644 index 0000000..4b466de Binary files /dev/null and b/plugins/pages/icon-big.png differ diff --git a/plugins/pages/icon.png b/plugins/pages/icon.png new file mode 100644 index 0000000..02b3bd3 Binary files /dev/null and b/plugins/pages/icon.png differ diff --git a/plugins/pages/index.php b/plugins/pages/index.php new file mode 100644 index 0000000..3563cec --- /dev/null +++ b/plugins/pages/index.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/plugins/pages/list.php b/plugins/pages/list.php new file mode 100644 index 0000000..a997d25 --- /dev/null +++ b/plugins/pages/list.php @@ -0,0 +1,203 @@ +rs->isEmpty()) + { + echo '

      '.__('No page').'

      '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
      '.__('Title').''.__('Date').''.__('Author').''.__('Comments').''.__('Trackbacks').''.__('Status').'
      '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

      '.__('Page(s)').' : '.$pager->getLinks().'

      '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->postLine(); + } + + echo $blocks[1]; + + echo '

      '.__('Page(s)').' : '.$pager->getLinks().'

      '; + } + } + + private function postLine() + { + $img = '%1$s'; + switch ($this->rs->post_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('scheduled'),'scheduled.png'); + break; + case -2: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + 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 = ''; + + $res .= + ''. + form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable()).''. + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).''. + + ''.$this->rs->user_id.''. + ''.$this->rs->nb_comment.''. + ''.$this->rs->nb_trackback.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''; + + return $res; + } +} + +/* Getting pages +-------------------------------------------------------- */ +$params = array( + 'post_type' => 'page' +); + +$page = !empty($_GET['page']) ? (integer) $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = (integer) $_GET['nb']; +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; +$params['order'] = 'post_position ASC, post_title ASC'; + +try { + $pages = $core->blog->getPosts($params); + $counter = $core->blog->getPosts($params,true); + $post_list = new adminPageList($core,$pages,$counter->f(0)); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +# Actions combo box +$combo_action = array(); +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; +} +if ($core->auth->check('admin',$core->blog->id)) { + $combo_action[__('change author')] = 'author'; +} +if ($core->auth->check('delete,contentadmin',$core->blog->id)) +{ + $combo_action[__('delete')] = 'delete'; +} + +# --BEHAVIOR-- adminPagesActionsCombo +$core->callBehavior('adminPagesActionsCombo',array(&$combo_action)); + +/* Display +-------------------------------------------------------- */ +?> + + + <?php echo __('Pages'); ?> + + + + + +'.html::escapeHTML($core->blog->name).' › '.__('Pages'). +' - '.__('New page').''; + +if (!$core->error->flag()) +{ + # Show pages + $post_list->display($page,$nb_per_page, + '
      '. + + '%s'. + + '
      '. + '

      '. + + '

      '.__('Selected pages action:').' '. + form::combo('action',$combo_action). + '

      '. + form::hidden(array('post_type'),'page'). + form::hidden(array('redir'),html::escapeHTML($_SERVER['REQUEST_URI'])). + $core->formNonce(). + '
      '. + '
      '); +} +?> + + \ No newline at end of file diff --git a/plugins/pages/page.php b/plugins/pages/page.php new file mode 100644 index 0000000..1bc0eda --- /dev/null +++ b/plugins/pages/page.php @@ -0,0 +1,638 @@ +auth->getOption('post_format'); +$post_password = ''; +$post_url = ''; +$post_lang = $core->auth->getInfo('user_lang'); +$post_title = ''; +$post_excerpt = ''; +$post_excerpt_xhtml = ''; +$post_content = ''; +$post_content_xhtml = ''; +$post_status = $core->auth->getInfo('user_post_status'); +$post_position = 0; +$post_open_comment = false; +$post_open_tb = false; + +$post_media = array(); + +$page_title = __('New page'); + +$can_view_page = true; +$can_edit_page = $core->auth->check('page,usage',$core->blog->id); +$can_publish = $core->auth->check('page,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; +} + +# Status combo +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# Formaters combo +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +# Languages combo +$rs = $core->blog->getLangs(array('order'=>'asc')); +$all_langs = l10n::getISOcodes(0,1); +$lang_combo = array('' => '', __('Most used') => array(), __('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); + + +# Get page informations +if (!empty($_REQUEST['id'])) +{ + $params['post_type'] = 'page'; + $params['post_id'] = $_REQUEST['id']; + + $post = $core->blog->getPosts($params); + + if ($post->isEmpty()) + { + $core->error->add(__('This page does not exist.')); + $can_view_page = false; + } + else + { + $post_id = $post->post_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_status = $post->post_status; + $post_position = (integer) $post->post_position; + $post_open_comment = (boolean) $post->post_open_comment; + $post_open_tb = (boolean) $post->post_open_tb; + + $page_title = __('Edit page'); + + $can_edit_page = $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 page').' »'); + $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 page')); + $prev_headlink = sprintf($post_headlink,'previous', + html::escapeHTML($prev_rs->post_title),$prev_rs->post_id); + } + + try { + $core->media = new dcMedia($core); + $post_media = $core->media->getPostMedia($post_id); + } catch (Exception $e) {} + } +} + +# Format content +if (!empty($_POST) && $can_edit_page) +{ + $post_format = $_POST['post_format']; + $post_excerpt = $_POST['post_excerpt']; + $post_content = $_POST['post_content']; + + $post_title = $_POST['post_title']; + + if (isset($_POST['post_status'])) { + $post_status = (integer) $_POST['post_status']; + } + + if (empty($_POST['post_dt'])) { + $post_dt = ''; + } else { + $post_dt = strtotime($_POST['post_dt']); + $post_dt = date('Y-m-d H:i',$post_dt); + } + + $post_open_comment = !empty($_POST['post_open_comment']); + $post_open_tb = !empty($_POST['post_open_tb']); + $post_lang = $_POST['post_lang']; + $post_password = !empty($_POST['post_password']) ? $_POST['post_password'] : null; + $post_position = (integer) $_POST['post_position']; + + 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 + ); +} + +# Create or update post +if (!empty($_POST) && !empty($_POST['save']) && $can_edit_page) +{ + $cur = $core->con->openCursor($core->prefix.'post'); + + # Magic tweak :) + $core->blog->settings->post_url_format = $page_url_format; + + $cur->post_type = 'page'; + $cur->post_title = $post_title; + $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_status = $post_status; + $cur->post_position = $post_position; + $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-- adminBeforePageUpdate + $core->callBehavior('adminBeforePageUpdate',$cur,$post_id); + + $core->blog->updPost($post_id,$cur); + + # --BEHAVIOR-- adminAfterPageUpdate + $core->callBehavior('adminAfterPageUpdate',$cur,$post_id); + + http::redirect($redir_url.'&id='.$post_id.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + else + { + $cur->user_id = $core->auth->userID(); + + try + { + # --BEHAVIOR-- adminBeforePageCreate + $core->callBehavior('adminBeforePageCreate',$cur); + + $return_id = $core->blog->addPost($cur); + + # --BEHAVIOR-- adminAfterPageCreate + $core->callBehavior('adminAfterPageCreate',$cur,$return_id); + + http::redirect($redir_url.'&id='.$return_id.'&crea=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } +} + +if (!empty($_POST['delete']) && $can_delete) +{ + try { + # --BEHAVIOR-- adminBeforePageDelete + $core->callBehavior('adminBeforePageDelete',$post_id); + $core->blog->delPost($post_id); + http::redirect($p_url); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +$default_tab = 'edit-entry'; +if (!$can_edit_page) { + $default_tab = ''; +} +if (!empty($_GET['co'])) { + $default_tab = 'comments'; +} + +?> + + + <?php echo $page_title.' - '.__('Pages'); ?> + + callBehavior('adminPageHeaders'). + dcPage::jsPageTabs($default_tab). + $next_headlink."\n".$prev_headlink; + ?> + + + + +'.__('Page has been successfully updated.').'

      '; +} +elseif (!empty($_GET['crea'])) { + echo '

      '.__('Page has been successfully created.').'

      '; +} +elseif (!empty($_GET['attached'])) { + echo '

      '.__('File has been successfully attached.').'

      '; +} +elseif (!empty($_GET['rmattach'])) { + echo '

      '.__('Attachment has been successfully removed.').'

      '; +} + +# XHTML conversion +if (!empty($_GET['xconv'])) +{ + $post_excerpt = $post_excerpt_xhtml; + $post_content = $post_content_xhtml; + $post_format = 'xhtml'; + + echo '

      '.__('Don\'t forget to validate your XHTML conversion by saving your post.').'

      '; +} + +echo '

      '.html::escapeHTML($core->blog->name). +' › '.__('Pages').' › '.$page_title; + +if ($post_id && $post->post_status == 1) { + echo ' - '.__('View page').''; +} elseif ($post_id) { + $preview_url = + $core->blog->url.$core->url->getBase('pagespreview').'/'. + $core->auth->userID().'/'. + http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')). + '/'.$post->post_url; + echo ' - '.__('Preview page').''; +} + +echo '

      '; + +if ($post_id) +{ + echo '

      '; + if ($prev_link) { echo $prev_link; } + if ($next_link && $prev_link) { echo ' - '; } + if ($next_link) { echo $next_link; } + + # --BEHAVIOR-- adminPageNavLinks + $core->callBehavior('adminPageNavLinks',isset($post) ? $post : null); + + echo '

      '; +} + +# Exit if we cannot view page +if (!$can_view_page) { + echo ''; + return; +} + + +/* Post form if we can edit post +-------------------------------------------------------- */ +if ($can_edit_page) +{ + echo '
      '; + echo '
      '; + echo '
      '; + + echo + '

      '. + + '

      '. + + '

      '. + + '

      '. + '

      '. + + '

      '. + + '

      '. + + '

      '. + + '
      '. + '

      '. + '

      '. + __('Warning: If you set the URL manually, it may conflict with another page.'). + '

      '. + '
      '; + + if ($post_id) + { + echo + '

      '.__('Attachments').'

      '; + foreach ($post_media as $f) + { + $ftitle = $f->media_title; + if (strlen($ftitle) > 18) { + $ftitle = substr($ftitle,0,16).'...'; + } + echo + '
      '. + ''. + ''. + ''. + '
      '; + } + unset($f); + + if (empty($post_media)) { + echo '

      '.__('No attachment.').'

      '; + } + echo '

      '.__('Add files to this page').'

      '; + } + + # --BEHAVIOR-- adminPageFormSidebar + $core->callBehavior('adminPageFormSidebar',isset($post) ? $post : null); + + echo '
      '; // End #entry-sidebar + + echo '
      '; + + echo + '

      '. + + '

      '. + form::textarea('post_excerpt',50,5,html::escapeHTML($post_excerpt),'',2). + '

      '. + + '

      '. + form::textarea('post_content',50,$core->auth->getOption('edit_size'),html::escapeHTML($post_content),'',2). + '

      '. + + # --BEHAVIOR-- adminPageForm + $core->callBehavior('adminPageForm',isset($post) ? $post : null); + + echo + '

      '. + ($post_id ? form::hidden('id',$post_id) : ''). + ' '. + ($can_delete ? '' : ''). + $core->formNonce(). + '

      '; + + echo '
      '; // End #entry-content + echo '
      '; + echo '
      '; + + if ($post_id && !empty($post_media)) + { + echo + '
      '. + '
      '.form::hidden(array('post_id'),$post_id). + form::hidden(array('media_id'),''). + form::hidden(array('remove'),1). + $core->formNonce().'
      '; + } +} + + +/* Comments and trackbacks +-------------------------------------------------------- */ +if ($post_id) +{ + $params = array('post_id' => $post_id, 'order' => 'comment_dt ASC'); + + $comments = $core->blog->getComments(array_merge($params,array('comment_trackback'=>0))); + $trackbacks = $core->blog->getComments(array_merge($params,array('comment_trackback'=>1))); + + # Actions combo box + $combo_action = array(); + if ($can_edit_page && $core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; + } + + if ($can_edit_page && $core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('delete')] = 'delete'; + } + + $has_action = !empty($combo_action) && (!$trackbacks->isEmpty() || !$comments->isEmpty()); + + echo + '
      '; + + if ($has_action) { + echo '
      '; + } + + echo '

      '.__('Trackbacks').'

      '; + + if (!$trackbacks->isEmpty()) { + showComments($trackbacks,$has_action); + } else { + echo '

      '.__('No trackback').'

      '; + } + + echo '

      '.__('Comments').'

      '; + if (!$comments->isEmpty()) { + showComments($comments,$has_action); + } else { + echo '

      '.__('No comment').'

      '; + } + + if ($has_action) { + echo + '
      '. + '

      '. + + '

      '.__('Selected comments action:').' '. + form::combo('action',$combo_action). + form::hidden('redir',html::escapeURL($redir_url).'&id='.$post_id.'&co=1'). + $core->formNonce(). + '

      '. + '
      '. + '
      '; + } + + echo '
      '; +} + +/* Add a comment +-------------------------------------------------------- */ +if ($post_id) +{ + echo + '
      '. + '

      '.__('Add a comment').'

      '. + + '
      '. + '
      '. + '

      '. + + '

      '. + + '

      '. + + '

      '. + form::textarea('comment_content',50,8,html::escapeHTML('')). + '

      '. + + '

      '.form::hidden('post_id',$post_id). + $core->formNonce(). + '

      '. + '
      '. + '
      '. + '
      '; +} + + +# Show comments or trackbacks +function showComments(&$rs,$has_action) +{ + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + while($rs->fetch()) + { + $comment_url = 'comment.php?id='.$rs->comment_id; + + $img = '%1$s'; + switch ($rs->comment_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + break; + case -2: + $img_status = sprintf($img,__('junk'),'junk.png'); + break; + } + + echo + ''. + + ''. + ''. + ''. + ''. + ''. + ''. + + ''; + } + + echo '
      '.__('Author').''.__('Date').''.__('IP address').''.__('Status').' 
      '. + ($has_action ? form::checkbox(array('comments[]'),$rs->comment_id,'','','',0) : '').''.$rs->comment_author.''.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->comment_dt).''.$rs->comment_ip.''.$img_status.''. + '
      '; +} +dcPage::helpBlock('core_wiki'); +?> + + \ No newline at end of file diff --git a/plugins/pings/_admin.php b/plugins/pings/_admin.php new file mode 100644 index 0000000..be8e909 --- /dev/null +++ b/plugins/pings/_admin.php @@ -0,0 +1,25 @@ +addItem(__('Pings'),'plugin.php?p=pings','index.php?pf=pings/icon.png', + preg_match('/plugin.php\?p=pings/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + +$__autoload['pingsAPI'] = dirname(__FILE__).'/lib.pings.php'; +$__autoload['pingsBehaviors'] = dirname(__FILE__).'/lib.pings.php'; + +$core->addBehavior('adminPostHeaders',array('pingsBehaviors','pingJS')); +$core->addBehavior('adminPostFormSidebar',array('pingsBehaviors','pingsForm')); +$core->addBehavior('adminAfterPostCreate',array('pingsBehaviors','doPings')); +$core->addBehavior('adminAfterPostUpdate',array('pingsBehaviors','doPings')); +?> \ No newline at end of file diff --git a/plugins/pings/_define.php b/plugins/pings/_define.php new file mode 100644 index 0000000..27f0600 --- /dev/null +++ b/plugins/pings/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "Pings", + /* Description*/ "Ping services", + /* Author */ "Olivier Meunier", + /* Version */ '1.0', + /* Permissions */ 'usage,contentadmin' +); +?> \ No newline at end of file diff --git a/plugins/pings/icon.png b/plugins/pings/icon.png new file mode 100644 index 0000000..05bd28e Binary files /dev/null and b/plugins/pings/icon.png differ diff --git a/plugins/pings/index.php b/plugins/pings/index.php new file mode 100644 index 0000000..895e5b8 --- /dev/null +++ b/plugins/pings/index.php @@ -0,0 +1,113 @@ +blog->settings->pings_active === null) + { + $default_pings_uris = array( + 'Ping-o-Matic!' => 'http://rpc.pingomatic.com/', + 'Google Blog Search' => 'http://blogsearch.google.com/ping/RPC2' + ); + + $core->blog->settings->setNameSpace('pings'); + $core->blog->settings->put('pings_active',1,'boolean','Activate pings plugin',true,true); + $core->blog->settings->put('pings_uris',serialize($default_pings_uris),'string','Pings services URIs',true,true); + http::redirect($p_url); + } + + $pings_uris = @unserialize($core->blog->settings->pings_uris); + if (!$pings_uris) { + $pings_uris = array(); + } + + if (isset($_POST['pings_srv_name'])) + { + $pings_srv_name = is_array($_POST['pings_srv_name']) ? $_POST['pings_srv_name'] : array(); + $pings_srv_uri = is_array($_POST['pings_srv_uri']) ? $_POST['pings_srv_uri'] : array(); + $pings_uris = array(); + + foreach ($pings_srv_name as $k => $v) { + if (trim($v) && trim($pings_srv_uri[$k])) { + $pings_uris[trim($v)] = trim($pings_srv_uri[$k]); + } + } + + $core->blog->settings->setNameSpace('pings'); + $core->blog->settings->put('pings_active',!empty($_POST['pings_active']),null,null,true,true); + $core->blog->settings->put('pings_uris',serialize($pings_uris),null,null,true,true); + http::redirect($p_url.'&up=1'); + } +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} +?> + + + <?php echo __('Pings'); ?> + + + +'.__('Pings configuration').''; + +if (!empty($_GET['up'])) { + echo '

      '.__('Settings have been successfully updated.').'

      '; +} + +echo +'
      '. +'

      '; + +foreach ($pings_uris as $n => $u) +{ + echo + '

      '. + ''; + + if (!empty($_GET['test'])) + { + try { + pingsAPI::doPings($u,'Example site','http://example.com'); + echo ' ok'; + } catch (Exception $e) { + echo ' '.__('error').' '.$e->getMessage(); + } + } + + echo '

      '; +} + +echo +'

      '. +''. +'

      '. + +'

      '. +$core->formNonce().'

      '. +'
      '; + +echo '

      '.__('Test ping services').'

      '; +?> + + \ No newline at end of file diff --git a/plugins/pings/lib.pings.php b/plugins/pings/lib.pings.php new file mode 100644 index 0000000..04a217d --- /dev/null +++ b/plugins/pings/lib.pings.php @@ -0,0 +1,92 @@ +timeout = 3; + + $rsp = $o->query('weblogUpdates.ping',$site_name,$site_url); + + if (isset($rsp['flerror']) && $rsp['flerror']) { + throw new Exception($rsp['message']); + } + + return true; + } +} + +class pingsBehaviors +{ + public static function pingJS() + { + return dcPage::jsLoad('index.php?pf=pings/post.js'); + } + + public static function pingsForm(&$post) + { + $core =& $GLOBALS['core']; + if (!$core->blog->settings->pings_active) { + return; + } + + $pings_uris = @unserialize($core->blog->settings->pings_uris); + if (empty($pings_uris) || !is_array($pings_uris)) { + return; + } + + if (!empty($_POST['pings_do']) && is_array($_POST['pings_do'])) { + $pings_do = $_POST['pings_do']; + } else { + $pings_do = array(); + } + + echo '

      '.__('Pings:').'

      '; + foreach ($pings_uris as $k => $v) + { + echo + '

      '; + } + } + + public static function doPings(&$cur,&$post_id) + { + if (empty($_POST['pings_do']) || !is_array($_POST['pings_do'])) { + return; + } + + $core =& $GLOBALS['core']; + if (!$core->blog->settings->pings_active) { + return; + } + + $pings_uris = @unserialize($core->blog->settings->pings_uris); + if (empty($pings_uris) || !is_array($pings_uris)) { + return; + } + + foreach ($_POST['pings_do'] as $uri) + { + if (in_array($uri,$pings_uris)) { + try { + pingsAPI::doPings($uri,$core->blog->name,$core->blog->url); + } catch (Exception $e) {} + } + } + } +} +?> \ No newline at end of file diff --git a/plugins/pings/post.js b/plugins/pings/post.js new file mode 100644 index 0000000..578f173 --- /dev/null +++ b/plugins/pings/post.js @@ -0,0 +1,2 @@ + +$(function(){$('#edit-entry').onetabload(function(){$('h3.ping-services').toggleWithLegend($('p.ping-services'),{cookie:'dcx_ping_services'});});}); \ No newline at end of file diff --git a/plugins/themeEditor/_admin.php b/plugins/themeEditor/_admin.php new file mode 100644 index 0000000..9ed790e --- /dev/null +++ b/plugins/themeEditor/_admin.php @@ -0,0 +1,26 @@ +addBehavior('adminCurrentThemeDetails','theme_editor_details'); + +function theme_editor_details($core,$id) +{ + if ($id != 'default' && $core->auth->isSuperAdmin()) { + return '

      '.__('Theme Editor').'

      '; + } +} +?> \ No newline at end of file diff --git a/plugins/themeEditor/_define.php b/plugins/themeEditor/_define.php new file mode 100644 index 0000000..1dead06 --- /dev/null +++ b/plugins/themeEditor/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "themeEditor", + /* Description*/ "Theme Editor", + /* Author */ "Olivier Meunier", + /* Version */ '0.6', + /* Permissions */ null +); +?> \ No newline at end of file diff --git a/plugins/themeEditor/class.themeEditor.php b/plugins/themeEditor/class.themeEditor.php new file mode 100644 index 0000000..7abfe70 --- /dev/null +++ b/plugins/themeEditor/class.themeEditor.php @@ -0,0 +1,252 @@ +core =& $core; + $this->default_theme = path::real($this->core->blog->themes_path.'/default'); + $this->user_theme = path::real($this->core->blog->themes_path.'/'.$this->core->blog->settings->theme); + if (null !== $this->core->themes) { + $parent_theme = $this->core->themes->moduleInfo($this->core->blog->settings->theme,'parent'); + if ($parent_theme) { + $this->parent_theme = path::real($this->core->blog->themes_path.'/'.$parent_theme); + } + } + $this->findTemplates(); + $this->findStyles(); + $this->findScripts(); + } + + public function filesList($type,$item='%1$s') + { + $files = $this->getFilesFromType($type); + + if (empty($files)) { + return '

      '.__('No file').'

      '; + } + + $list = ''; + foreach ($files as $k => $v) + { + if (strpos($v,$this->user_theme) === 0) { + $li = sprintf('
    • %s
    • ',$item); + } elseif ($this->parent_theme && strpos($v,$this->parent_theme) === 0) { + $li = sprintf('
    • %s
    • ',$item); + } else { + $li = sprintf('
    • %s
    • ',$item); + } + $list .= sprintf($li,$k,html::escapeHTML($k)); + } + + return sprintf('
        %s
      ',$list); + } + + public function getFileContent($type,$f) + { + $files = $this->getFilesFromType($type); + + if (!isset($files[$f])) { + throw new Exception(__('File does not exist.')); + } + + $F = $files[$f]; + if (!is_readable($F)) { + throw new Exception(sprintf(__('File %s is not readable'),$f)); + } + + return array( + 'c' => file_get_contents($F), + 'w' => $this->getDestinationFile($type,$f) !== false, + 'type' => $type, + 'f' => $f + ); + } + + public function writeFile($type,$f,$content) + { + $files = $this->getFilesFromType($type); + + if (!isset($files[$f])) { + throw new Exception(__('File does not exist.')); + } + + try + { + $dest = $this->getDestinationFile($type,$f); + + if ($dest == false) { + throw new Exception(); + } + + if ($type == 'tpl' && !is_dir(dirname($dest))) { + files::makeDir(dirname($dest)); + } + + $fp = @fopen($dest,'wb'); + if (!$fp) { + throw new Exception('tocatch'); + } + + $content = preg_replace('/(\r?\n)/m',"\n",$content); + $content = preg_replace('/\r/m',"\n",$content); + + fwrite($fp,$content); + fclose($fp); + + # Updating inner files list + $this->updateFileInList($type,$f,$dest); + } + catch (Exception $e) + { + throw new Exception(sprintf(__('Unable to write file %s. Please check your theme files and folders permissions.'),$f)); + } + } + + protected function getDestinationFile($type,$f) + { + if ($type == 'tpl') { + $dest = $this->user_theme.'/tpl/'.$f; + } else { + $dest = $this->user_theme.'/'.$f; + } + + if (file_exists($dest) && is_writable($dest)) { + return $dest; + } + + if ($type == 'tpl' && !is_dir(dirname($dest))) { + if (is_writable($this->user_theme)) { + return $dest; + } + } + + if (is_writable(dirname($dest))) { + return $dest; + } + + return false; + } + + protected function getFilesFromType($type) + { + switch ($type) + { + case 'tpl': + return $this->tpl; + case 'css': + return $this->css; + case 'js': + return $this->js; + default: + return array(); + } + } + + protected function updateFileInList($type,$f,$file) + { + switch ($type) + { + case 'tpl': + $list =& $this->tpl; + break; + case 'css': + $list =& $this->css; + break; + case 'js': + $list =& $this->js; + break; + default: + return; + } + + $list[$f] = $file; + } + + protected function findTemplates() + { + # First, we look in template paths + $this->default_tpl = $this->getFilesInDir($this->default_theme.'/tpl'); + + $this->tpl = array_merge( + $this->default_tpl, + $this->getFilesInDir($this->parent_theme.'/tpl'), + $this->getFilesInDir($this->user_theme.'/tpl') + ); + $this->tpl = array_merge($this->getFilesInDir(DC_ROOT.'/inc/public/default-templates'),$this->tpl); + + # Then we look in 'default-templates' plugins directory + $plugins = $this->core->plugins->getModules(); + foreach ($plugins as $p) { + $this->tpl = array_merge($this->getFilesInDir($p['root'].'/default-templates'),$this->tpl); + } + + uksort($this->tpl,array($this,'sortFilesHelper')); + } + + protected function findStyles() + { + $this->css = $this->getFilesInDir($this->user_theme,'css'); + $this->css= array_merge($this->css,$this->getFilesInDir($this->user_theme.'/style','css','style/')); + } + + protected function findScripts() + { + $this->js = $this->getFilesInDir($this->user_theme,'js'); + $this->js = array_merge($this->js,$this->getFilesInDir($this->user_theme.'/js','js','js/')); + } + + protected function getFilesInDir($dir,$ext=null,$prefix='') + { + $dir = path::real($dir); + if (!$dir || !is_dir($dir) || !is_readable($dir)) { + return array(); + } + + $d = dir($dir); + $res = array(); + while (($f = $d->read()) !== false) + { + if (is_file($dir.'/'.$f) && !preg_match('/^\./',$f) && (!$ext || preg_match('/\.'.preg_quote($ext).'$/i',$f))) { + $res[$prefix.$f] = $dir.'/'.$f; + } + } + + return $res; + } + + protected function sortFilesHelper($a,$b) + { + if ($a == $b) { + return 0; + } + + $ext_a = files::getExtension($a); + $ext_b = files::getExtension($b); + + return strcmp($ext_a.'.'.$a,$ext_b.'.'.$b); + } +} +?> \ No newline at end of file diff --git a/plugins/themeEditor/help.html b/plugins/themeEditor/help.html new file mode 100644 index 0000000..e190cb5 --- /dev/null +++ b/plugins/themeEditor/help.html @@ -0,0 +1,28 @@ + + + + Theme Editor + + + +

      The Theme Editor lets you modify the template files, the stylesheets and the JavaScript files of the theme you are currently using..

      + +

      The file list is divided in three sections :

      +
        +
      • Template files : the templates
      • +
      • CSS files : the stylesheets
      • +
      • JavaScript files
      • +
      + +

      The yellow bullet in front of the name of a file indicates that this file is part of the theme. The red bullet means it is a part of the parent theme. The black bullet means it is a part of the "default" theme.

      + +

      If you modify a file belonging to the "default" theme, it will be copied into the theme you are currently using.

      + +

      To edit a file, click on its name to display its content in the edition area. If the file can be written, you will be able to save your modifications by ckicking on "save".

      + +

      The modifications are applied immediately after saving, so be careful.

      + +

      To help modify your templates, you may consult the template tag list.

      + + + diff --git a/plugins/themeEditor/index.php b/plugins/themeEditor/index.php new file mode 100644 index 0000000..0999dc5 --- /dev/null +++ b/plugins/themeEditor/index.php @@ -0,0 +1,128 @@ +null, 'w'=>false, 'type'=>null, 'f'=>null, 'default_file'=>false); + +# Loading themes +$core->themes = new dcThemes($core); +$core->themes->loadModules($core->blog->themes_path,null); +$T = $core->themes->getModules($core->blog->settings->theme); +$o = new dcThemeEditor($core); + +try +{ + try + { + if (!empty($_REQUEST['tpl'])) { + $file = $o->getFileContent('tpl',$_REQUEST['tpl']); + } elseif (!empty($_REQUEST['css'])) { + $file = $o->getFileContent('css',$_REQUEST['css']); + } elseif (!empty($_REQUEST['js'])) { + $file = $o->getFileContent('js',$_REQUEST['js']); + } + } + catch (Exception $e) + { + $file = $file_default; + throw $e; + } + + # Write file + if (!empty($_POST['write'])) + { + $file['c'] = $_POST['file_content']; + $o->writeFile($file['type'],$file['f'],$file['c']); + } +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} +?> + + + + <?php echo __('Theme Editor'); ?> + + + + + + +'.html::escapeHTML($core->blog->name). +' › '.__('Blog aspect').' › '.__('Theme Editor').''; ?> + +

      + +blog->settings->theme == 'default') { ?> +

      + + + +
      +
      +'.__('Please select a file to edit.').'

      '; +} +else +{ + echo + '
      '. + '
      '.__('File editor').''. + '

      '.sprintf(__('Editing file %s'),''.$file['f']).'

      '. + '

      '.form::textarea('file_content',72,25,html::escapeHTML($file['c']),'maximal','',!$file['w']).'

      '; + + if ($file['w']) + { + echo + '

      '. + $core->formNonce(). + ($file['type'] ? form::hidden(array($file['type']),$file['f']) : ''). + '

      '; + } + else + { + echo '

      '.__('This file is not writable. Please check your theme files permissions.').'

      '; + } + + echo + '
      '; +} +?> +
      +
      + +
      +

      +filesList('tpl','%1$s'); ?> + +

      +filesList('css','%1$s'); ?> + +

      +filesList('js','%1$s'); ?> +
      + + + + \ No newline at end of file diff --git a/plugins/themeEditor/script.js b/plugins/themeEditor/script.js new file mode 100644 index 0000000..7b60c5f --- /dev/null +++ b/plugins/themeEditor/script.js @@ -0,0 +1,3 @@ + +$(function(){var msg=false;$('#file-form input[name="write"]').click(function(){var f=this.form;var data={file_content:$(f).find('#file_content').get(0).value,xd_check:$(f).find('input[name="xd_check"]').get(0).value,write:1};if(msg==false){msg=$('

      ');$('#file_content').parent().after(msg);} +msg.text(dotclear.msg.saving_document);$.post(document.location.href,data,function(res,status){var err=$(res).find('div.error li:first');if(err.length>0){msg.text(dotclear.msg.error_occurred+' '+err.text());return;}else{msg.text(dotclear.msg.document_saved);$('#file-chooser').empty();$(res).find('#file-chooser').children().appendTo('#file-chooser');}});return false;});}); \ No newline at end of file diff --git a/plugins/themeEditor/style.css b/plugins/themeEditor/style.css new file mode 100644 index 0000000..9fce472 --- /dev/null +++ b/plugins/themeEditor/style.css @@ -0,0 +1,29 @@ +#file-box { + width: 100%; + float: left; + margin-right: -180px; +} + +#file-editor { + margin-right: 180px; +} + +#file-chooser { + float: right; + width: 170px; +} +#file-chooser ul { + padding-left: 0; +} +#file-chooser li { + list-style: square inside; +} +#file-chooser li.default-file { + color: #f90; +} +#file-chooser li.parent-file { + color:#c00; +} +textarea { + font: 1em Monaco,"Courier New",Courier,monospace; +} \ No newline at end of file diff --git a/plugins/widgets/_admin.php b/plugins/widgets/_admin.php new file mode 100644 index 0000000..a162b3b --- /dev/null +++ b/plugins/widgets/_admin.php @@ -0,0 +1,17 @@ +addItem(__('Presentation widgets'),'plugin.php?p=widgets','index.php?pf=widgets/icon.png', + preg_match('/plugin.php\?p=widgets(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id)); +?> \ No newline at end of file diff --git a/plugins/widgets/_default_widgets.php b/plugins/widgets/_default_widgets.php new file mode 100644 index 0000000..0554e61 --- /dev/null +++ b/plugins/widgets/_default_widgets.php @@ -0,0 +1,90 @@ +create('search',__('Search engine'),array('defaultWidgets','search')); +$__widgets->search->setting('title',__('Title:'),__('Search')); + +$__widgets->create('navigation',__('Navigation links'),array('defaultWidgets','navigation')); +$__widgets->navigation->setting('title',__('Title:'),''); + +$__widgets->create('bestof',__('Selected entries'),array('defaultWidgets','bestof')); +$__widgets->bestof->setting('title',__('Title:'),__('Best of me')); +$__widgets->bestof->setting('homeonly',__('Home page only'),1,'check'); + +$__widgets->create('langs',__('Blog languages'),array('defaultWidgets','langs')); +$__widgets->langs->setting('title',__('Title:'),__('Languages')); +$__widgets->langs->setting('homeonly',__('Home page only'),1,'check'); + +$__widgets->create('categories',__('Categories list'),array('defaultWidgets','categories')); +$__widgets->categories->setting('title',__('Title:'),__('Categories')); +$__widgets->categories->setting('postcount',__('With entries counts'),0,'check'); + +$__widgets->create('subscribe',__('Subscribe links'),array('defaultWidgets','subscribe')); +$__widgets->subscribe->setting('title',__('Title:'),__('Subscribe')); +$__widgets->subscribe->setting('type',__('Feeds type:'),'atom','combo',array('Atom' => 'atom', 'RSS' => 'rss2')); +$__widgets->subscribe->setting('homeonly',__('Home page only'),0,'check'); + +$__widgets->create('feed',__('Feed reader'),array('defaultWidgets','feed')); +$__widgets->feed->setting('title',__('Title:'),__('Somewhere else')); +$__widgets->feed->setting('url',__('Feed URL:'),''); +$__widgets->feed->setting('limit',__('Entries limit:'),10); +$__widgets->feed->setting('homeonly',__('Home page only'),1,'check'); + +$__widgets->create('text',__('Text'),array('defaultWidgets','text')); +$__widgets->text->setting('title',__('Title:'),''); +$__widgets->text->setting('text',__('Text:'),'','textarea'); +$__widgets->text->setting('homeonly',__('Home page only'),0,'check'); + +$__widgets->create('lastposts',__('Last entries'),array('defaultWidgets','lastposts')); +$__widgets->lastposts->setting('title',__('Title:'),__('Last entries')); +$rs = $core->blog->getCategories(array('post_type'=>'post')); +$categories = array('' => '', __('Uncategorized') => 'null'); +while ($rs->fetch()) { + $categories[str_repeat('  ',$rs->level-1).'• '.html::escapeHTML($rs->cat_title)] = $rs->cat_id; +} +$__widgets->lastposts->setting('category',__('Category:'),'','combo',$categories); +unset($rs,$categories); +if ($core->plugins->moduleExists('metadata')) { + $__widgets->lastposts->setting('tag',__('Tag:'),''); +} +$__widgets->lastposts->setting('limit',__('Entries limit:'),10); +$__widgets->lastposts->setting('homeonly',__('Home page only'),1,'check'); + + +$__widgets->create('lastcomments',__('Last comments'),array('defaultWidgets','lastcomments')); +$__widgets->lastcomments->setting('title',__('Title:'),__('Last comments')); +$__widgets->lastcomments->setting('limit',__('Comments limit:'),10); +$__widgets->lastcomments->setting('homeonly',__('Home page only'),1,'check'); + +# --BEHAVIOR-- initWidgets +$core->callBehavior('initWidgets',$__widgets); + +# Default widgets +global $__default_widgets; +$__default_widgets = array('nav'=> new dcWidgets(), 'extra'=> new dcWidgets()); + +$__default_widgets['nav']->append($__widgets->search); +$__default_widgets['nav']->append($__widgets->navigation); +$__default_widgets['nav']->append($__widgets->bestof); +$__default_widgets['nav']->append($__widgets->categories); +$__default_widgets['extra']->append($__widgets->subscribe); + +# --BEHAVIOR-- initDefaultWidgets +$core->callBehavior('initDefaultWidgets',$__widgets,$__default_widgets); +?> \ No newline at end of file diff --git a/plugins/widgets/_define.php b/plugins/widgets/_define.php new file mode 100644 index 0000000..51a8a7b --- /dev/null +++ b/plugins/widgets/_define.php @@ -0,0 +1,22 @@ +registerModule( + /* Name */ "Widgets", + /* Description*/ "Widgets for your blog sidebars", + /* Author */ "Olivier Meunier", + /* Version */ '1.6', + /* Permissions */ 'admin', + /* Priority */ 1000000000 +); +?> \ No newline at end of file diff --git a/plugins/widgets/_public.php b/plugins/widgets/_public.php new file mode 100644 index 0000000..f38f843 --- /dev/null +++ b/plugins/widgets/_public.php @@ -0,0 +1,115 @@ +tpl->addValue('Widgets',array('publicWidgets','tplWidgets')); +$core->tpl->addBlock('Widget',array('publicWidgets','tplWidget')); + +class publicWidgets +{ + public static function tplWidgets($attr) + { + $type = isset($attr['type']) ? $attr['type'] : 'nav'; + + return + ''; + } + + public static function widgetsHandler($type) + { + $wtype = 'widgets_'.$type; + $widgets = $GLOBALS['core']->blog->settings->{$wtype}; + + if (!$widgets) { // If widgets value is empty, get defaults + $widgets = self::defaultWidgets($type); + } else { // Otherwise, load widgets + $widgets = dcWidgets::load($widgets); + } + + if ($widgets->isEmpty()) { // Widgets are empty, don't show anything + return; + } + + foreach ($widgets->elements() as $k => $w) { + echo $w->call($k); + } + } + + private static function defaultWidgets($type) + { + $widgets = new dcWidgets(); + $w = new dcWidgets(); + + if (isset($GLOBALS['__default_widgets'][$type])) { + $w = $GLOBALS['__default_widgets'][$type]; + } + + return $w; + } + + public static function tplWidget($attr,$content) + { + if (!isset($attr['id']) || !($GLOBALS['__widgets']->{$attr['id']} instanceof dcWidget)) { + return; + } + + # We change tpl:lang syntax, we need it + $content = preg_replace('/\{\{tpl:lang\s+(.*?)\}\}/msu','{tpl:lang $1}',$content); + + # We remove every {{tpl: + $content = preg_replace('/\{\{tpl:.*?\}\}/msu','',$content); + + return + ""; + } + + public static function widgetHandler($id,$xml) + { + $widgets =& $GLOBALS['__widgets']; + + if (!($widgets->{$id} instanceof dcWidget)) { + return; + } + + $xml = ''.$xml.''; + $xml = @simplexml_load_string($xml); + if (!($xml instanceof SimpleXMLElement)) { + echo "Invalid widget XML fragment"; + return; + } + + $w = clone $widgets->{$id}; + + foreach ($xml->setting as $e) + { + if (empty($e['name'])) { + continue; + } + + $setting = (string) $e['name']; + $w->{$setting} = preg_replace_callback('/\{tpl:lang (.*?)\}/msu',array('self','widgetL10nHandler'),(string) $e); + } + + echo $w->call(0); + } + + private static function widgetL10nHandler($m) + { + return __($m[1]); + } +} +?> \ No newline at end of file diff --git a/plugins/widgets/_widgets_functions.php b/plugins/widgets/_widgets_functions.php new file mode 100644 index 0000000..c855a7f --- /dev/null +++ b/plugins/widgets/_widgets_functions.php @@ -0,0 +1,368 @@ +'. + ($w->title ? '

      ' : ''). + '
      '. + '
      '. + '

      '. + '

      '. + '
      '. + '
      '. + '
      '; + } + + public static function navigation(&$w) + { + global $core; + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + ''. + '
      '; + + return $res; + } + + public static function categories(&$w) + { + global $core, $_ctx; + + $rs = $core->blog->getCategories(array('post_type'=>'post')); + if ($rs->isEmpty()) { + return; + } + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''); + + $ref_level = $level = $rs->level-1; + while ($rs->fetch()) + { + $class = ''; + if (($core->url->type == 'category' && $_ctx->categories instanceof record && $_ctx->categories->cat_id == $rs->cat_id) + || ($core->url->type == 'post' && $_ctx->posts instanceof record && $_ctx->posts->cat_id == $rs->cat_id)) { + $class = ' class="category-current"'; + } + + if ($rs->level > $level) { + $res .= str_repeat('
        ',$rs->level - $level); + } elseif ($rs->level < $level) { + $res .= str_repeat('
      ',-($rs->level - $level)); + } + + if ($rs->level <= $level) { + $res .= ''; + } + + $res .= + ''. + html::escapeHTML($rs->cat_title).''. + ($w->postcount ? ' ('.$rs->nb_post.')' : ''); + + + $level = $rs->level; + } + + if ($ref_level - $level < 0) { + $res .= str_repeat('',-($ref_level - $level)); + } + $res .= '
      '; + + return $res; + } + + public static function bestof(&$w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $params = array( + 'post_selected'=>true, + 'no_content'=>true, + 'order'=>'post_dt desc'); + + $rs = $core->blog->getPosts($params); + + if ($rs->isEmpty()) { + return; + } + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
      '; + + return $res; + } + + public static function langs(&$w) + { + global $core, $_ctx; + + if ($w->homeonly && $core->url->type != 'default' && $core->url->type != 'lang') { + return; + } + + $rs = $core->blog->getLangs(); + + if ($rs->count() <= 1) { + return; + } + + $langs = l10n::getISOcodes(); + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
        '; + + while ($rs->fetch()) + { + $l = ($_ctx->cur_lang == $rs->post_lang) ? '%s' : '%s'; + + $lang_name = isset($langs[$rs->post_lang]) ? $langs[$rs->post_lang] : $rs->post_lang; + + $res .= + '
      • '. + sprintf($l, + ''. + $lang_name.''). + '
      • '; + } + + $res .= '
      '; + + return $res; + } + + public static function subscribe(&$w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $type = ($w->type == 'atom' || $w->type == 'rss2') ? $w->type : 'rss2'; + $mime = $type == 'rss2' ? 'application/rss+xml' : 'application/atom+xml'; + + $p_title = __('This blog\'s entries %s feed'); + $c_title = __('This blog\'s comments %s feed'); + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
      '; + + return $res; + } + + public static function feed(&$w) + { + if (!$w->url) { + return; + } + + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $limit = abs((integer) $w->limit); + + try { + $feed = feedReader::quickParse($w->url,DC_TPL_CACHE); + if ($feed == false || count($feed->items) == 0) { + return; + } + } catch (Exception $e) { + return; + } + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
        '; + + $i = 0; + foreach ($feed->items as $item) { + $li = isset($item->link) ? ''.$item->title.'' : $item->title; + $res .= '
      • '.$li.'
      • '; + $i++; + if ($i >= $limit) { + break; + } + } + + $res .= '
      '; + + return $res; + } + + public static function text(&$w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + $w->text. + '
      '; + + return $res; + } + + public static function lastposts(&$w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $params['limit'] = abs((integer) $w->limit); + $params['order'] = 'post_dt desc'; + $params['no_content'] = true; + + if ($w->category) + { + if ($w->category == 'null') { + $params['sql'] = ' AND p.cat_id IS NULL '; + } elseif (is_numeric($w->category)) { + $params['cat_id'] = (integer) $w->category; + } else { + $params['cat_url'] = $w->category; + } + } + + if ($core->plugins->moduleExists('metadata') && $w->tag) + { + $m = new dcMeta($core); + $params['meta_id'] = $w->tag; + $rs = $m->getPostsByMeta($params); + } + else + { + $rs = $core->blog->getPosts($params); + } + + if ($rs->isEmpty()) { + return; + } + + $res = + '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
      '; + + return $res; + } + + public static function lastcomments(&$w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $params['limit'] = abs((integer) $w->limit); + $params['order'] = 'comment_dt desc'; + $rs = $core->blog->getComments($params); + + if ($rs->isEmpty()) { + return; + } + + $res = '
      '. + ($w->title ? '

      '.html::escapeHTML($w->title).'

      ' : ''). + '
      '; + + return $res; + } +} +?> \ No newline at end of file diff --git a/plugins/widgets/class.widgets.php b/plugins/widgets/class.widgets.php new file mode 100644 index 0000000..ae7cf96 --- /dev/null +++ b/plugins/widgets/class.widgets.php @@ -0,0 +1,234 @@ +__widgets[$id] = new dcWidget($id,$name,$callback); + $this->__widgets[$id]->append_callback = $append_callback; + } + + public function append($widget) + { + if ($widget instanceof dcWidget) { + if (is_callable($widget->append_callback)) { + call_user_func($widget->append_callback,$widget); + } + $this->__widgets[] = $widget; + } + } + + public function isEmpty() + { + return count($this->__widgets) == 0; + } + + public function elements() + { + return $this->__widgets; + } + + public function __get($id) + { + if (!isset($this->__widgets[$id])) { + return null; + } + return $this->__widgets[$id]; + } + + public function __wakeup() + { + foreach ($this->__widgets as $i => $w) + { + if (!($w instanceof dcWidget)) { + unset($this->__widgets[$i]); + } + } + } + + public static function loadArray($A,&$widgets) + { + if (!($widgets instanceof self)) { + return false; + } + + uasort($A,array('self','arraySort')); + + $result = new self; + foreach ($A as $v) + { + if ($widgets->{$v['id']} != null) + { + $w = clone $widgets->{$v['id']}; + + # Settings + unset($v['id']); + unset($v['order']); + foreach ($v as $sid => $s) { + $w->{$sid} = $s; + } + + $result->append($w); + } + } + + return $result; + } + + private static function arraySort($a, $b) + { + if ($a['order'] == $b['order']) { + return 0; + } + return $a['order'] > $b['order'] ? 1 : -1; + } +} + +class dcWidget +{ + private $id; + private $name; + private $public_callback = null; + public $append_callback = null; + private $settings = array(); + + public function __construct($id,$name,$callback) + { + $this->public_callback = $callback; + $this->id = $id; + $this->name = $name; + } + + public function id() + { + return $this->id; + } + + public function name() + { + return $this->name; + } + + public function getCallback() + { + return $this->public_callback; + } + + public function call($i=0) + { + if (is_callable($this->public_callback)) { + return call_user_func($this->public_callback,$this,$i); + } + return '

      Callback not found for widget '.$this->id.'

      '; + } + + /* Widget settings + --------------------------------------------------- */ + public function __get($n) + { + if (isset($this->settings[$n])) { + return $this->settings[$n]['value']; + } + return null; + } + + public function __set($n,$v) + { + if (isset($this->settings[$n])) { + $this->settings[$n]['value'] = $v; + } + } + + public function setting($name,$title,$value,$type='text') + { + if ($type == 'combo') { + $options = @func_get_arg(4); + if (!is_array($options)) { + return false; + } + } + + $this->settings[$name] = array( + 'title' => $title, + 'type' => $type, + 'value' => $value + ); + + if (isset($options)) { + $this->settings[$name]['options'] = $options; + } + } + + public function settings() + { + return $this->settings; + } + + public function formSettings($pr='') + { + $res = ''; + foreach ($this->settings as $id => $s) + { + $iname = $pr ? $pr.'['.$id.']' : $id; + switch ($s['type']) + { + case 'text': + $res .= + '

      '; + break; + case 'textarea': + $res .= + '

      '; + break; + case 'check': + $res .= + '

      '.form::hidden(array($iname),'0'). + '

      '; + break; + case 'combo': + $res .= + '

      '; + break; + } + } + + return $res; + } +} +?> \ No newline at end of file diff --git a/plugins/widgets/dragdrop.js b/plugins/widgets/dragdrop.js new file mode 100644 index 0000000..ec01538 --- /dev/null +++ b/plugins/widgets/dragdrop.js @@ -0,0 +1,29 @@ + +ToolMan._coordinatesFactory.inside=function(item,container){var iTL=this.topLeftOffset(item);var iBR=this.bottomRightOffset(item);var cTL=this.topLeftOffset(container);var cBR=this.bottomRightOffset(container);return(iTL.x>=cTL.x&&iTL.x<=cBR.x&&iTL.y>=cTL.y&&iTL.y<=cBR.y)||(iBR.x>=cTL.x&&iBR.x<=cBR.x&&iBR.y>=cTL.y&&iBR.y<=cBR.y);} +ToolMan._dragdropFactory={firstContainer:null,lastContainer:null,makeDragable:function(item){var group=ToolMan.drag().createSimpleGroup(item);group.register('dragstart',this._onDragStart);group.register('dragmove',this._onDragMove);group.register('dragend',this._onDragEnd);item.isOutside=false;item.started=false;return group;},makeListContainer:function(list,name){if(this.firstContainer==null){this.firstContainer=this.lastContainer=list;list.previousContainer=null;list.nextContainer=null;}else{list.previousContainer=this.lastContainer;list.nextContainer=null;this.lastContainer.nextContainer=list;this.lastContainer=list;} +var helpers=ToolMan.helpers();var coordinates=ToolMan.coordinates();var items=new Array();for(var i=0;iblog->settings->widgets_nav) { + $widgets_nav = dcWidgets::load($core->blog->settings->widgets_nav); +} +$widgets_extra = null; +if ($core->blog->settings->widgets_extra) { + $widgets_extra = dcWidgets::load($core->blog->settings->widgets_extra); +} + +$append_combo = array( + '-' => 0, + __('navigation') => 'nav', + __('extra') => 'extra' +); + +# Adding widgets to sidebars +if (!empty($_POST['append']) && is_array($_POST['addw'])) +{ + # Filter selection + $addw = array(); + foreach ($_POST['addw'] as $k => $v) { + if (($v == 'extra' || $v == 'nav') && $__widgets->{$k} !== null ) { + $addw[$k] = $v; + } + } + + # Append widgets + if (!empty($addw)) + { + if (!($widgets_nav instanceof dcWidgets)) { + $widgets_nav = new dcWidgets; + } + if (!($widgets_extra instanceof dcWidgets)) { + $widgets_extra = new dcWidgets(); + } + + foreach ($addw as $k => $v) + { + switch ($v) { + case 'nav': + $widgets_nav->append($__widgets->{$k}); + break; + case 'extra': + $widgets_extra->append($__widgets->{$k}); + break; + + } + } + + try { + $core->blog->settings->setNameSpace('widgets'); + $core->blog->settings->put('widgets_nav',$widgets_nav->store()); + $core->blog->settings->put('widgets_extra',$widgets_extra->store()); + $core->blog->triggerBlog(); + http::redirect($p_url); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } +} + +# Update sidebars +if (!empty($_POST['wup'])) +{ + if (!isset($_POST['w']) || !is_array($_POST['w'])) { + $_POST['w'] = array(); + } + + try + { + # Removing mark as _rem widgets + foreach ($_POST['w'] as $nsid => $nsw) { + foreach ($nsw as $i => $v) { + if (!empty($v['_rem'])) { + unset($_POST['w'][$nsid][$i]); + continue; + } + } + } + + if (!isset($_POST['w']['nav'])) { + $_POST['w']['nav'] = array(); + } + if (!isset($_POST['w']['extra'])) { + $_POST['w']['extra'] = array(); + } + + $widgets_nav = dcWidgets::loadArray($_POST['w']['nav'],$__widgets); + $widgets_extra = dcWidgets::loadArray($_POST['w']['extra'],$__widgets); + + $core->blog->settings->setNameSpace('widgets'); + $core->blog->settings->put('widgets_nav',$widgets_nav->store()); + $core->blog->settings->put('widgets_extra',$widgets_extra->store()); + $core->blog->triggerBlog(); + + http::redirect($p_url); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +elseif (!empty($_POST['wreset'])) +{ + try + { + $core->blog->settings->setNameSpace('widgets'); + $core->blog->settings->put('widgets_nav',''); + $core->blog->settings->put('widgets_extra',''); + $core->blog->triggerBlog(); + + http::redirect($p_url); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +?> + + + <?php echo __('Widgets'); ?> + + + + + + + + + + + +'.html::escapeHTML($core->blog->name).' › '.__('Widgets').''; + +# All widgets +echo +'
      '. +'
      '.__('Available widgets').''. +'
      '; + +foreach ($__widgets->elements() as $w) { + echo + '
      '.form::hidden(array('w[void][0][id]'),html::escapeHTML($w->id())). + '

      '.form::field(array('w[void][0][order]'),2,3,0,'hideControl').' '. + $w->name().'

      '. + '

      '. + '
      '.$w->formSettings('w[void][0]').'
      '. + '
      '; +} + +echo +'
      '. +'
      '. +'

      '. +$core->formNonce().'

      '. +'
      '; + +echo '
      '; +# Nav sidebar +echo +'
      '. +sidebarWidgets('dndnav',__('Navigation sidebar'),$widgets_nav,'nav',$__default_widgets['nav']). +'
      '; + +# Extra sidebar +echo +'
      '. +sidebarWidgets('dndextra',__('Extra sidebar'),$widgets_extra,'extra',$__default_widgets['extra']). +'
      '; + +echo +'

      '. +$core->formNonce(). +' '. +'

      '. +'
      '; + +$widget_elements = new stdClass; +$widget_elements->content = +'

      '.__('Widget templates tags').'

      '. +'
      '. +'

      '.__('If you are allowed to edit your theme templates, you can directly add widgets as '. +'templates tags, with their own configuration.').'

      '. +'

      '.__('To add a widget in your template, you need to write code like this:').'

      '. +'
      <tpl:Widget id="'.__('Widget ID').'">
      +  <setting name="'.__('Setting name').'">'.__('Setting value').'</setting>
      +  ...
      +</tpl:Widget>
      '. +'

      '.__('Here are the following available widgets for your blog:').'

      '; + +$widget_elements->content .= '
      '; +foreach ($__widgets->elements() as $w) +{ + $widget_elements->content .= + '
      '.html::escapeHTML($w->name()).' ('. + __('Widget ID:').' '.html::escapeHTML($w->id()).')
      '. + '
      '; + + $w_settings = $w->settings(); + if (empty($w_settings)) + { + $widget_elements->content .= '

      '.__('No setting for this widget').'

      '; + } + else + { + $widget_elements->content .= '
        '; + foreach ($w->settings() as $n => $s) + { + switch ($s['type']) { + case 'check': + $s_type = '0|1'; + break; + case 'combo': + $s_type = implode('|',$s['options']); + break; + case 'text': + case 'textarea': + default: + $s_type = 'text'; + break; + } + + $widget_elements->content .= + '
      • '. + __('Setting name:').' '.html::escapeHTML($n).''. + ' ('.$s_type.')'. + '
      • '; + } + $widget_elements->content .= '
      '; + } + $widget_elements->content .= '
      '; +} +$widget_elements->content .= '
      '; + +dcPage::helpBlock($widget_elements); + +function sidebarWidgets($id,$title,&$widgets,$pr,$default_widgets) +{ + $res = '
      '.$title.'
      '; + + if (!($widgets instanceof dcWidgets)) + { + $widgets = $default_widgets; + } + + if ($widgets->isEmpty()) { + $res .= '

      '.__('No widget.').'

      '; + } + + $i = 0; + foreach ($widgets->elements() as $w) + { + $iname = 'w['.$pr.']['.$i.']'; + + $res .= + '
      '.form::hidden(array($iname.'[id]'),html::escapeHTML($w->id())). + '

      '.form::field(array($iname.'[order]'),2,3,(string) $i,'js-hide','',0,'title="'.__('order').'"').' '. + $w->name().'

      '. + '

      '. + '
      '.$w->formSettings($iname).'
      '. + '
      '; + + $i++; + } + + $res .= '
      '; + + return $res; +} +?> + + \ No newline at end of file diff --git a/plugins/widgets/style.css b/plugins/widgets/style.css new file mode 100644 index 0000000..9873027 --- /dev/null +++ b/plugins/widgets/style.css @@ -0,0 +1,76 @@ +#listWidgets { + float: left; + width: 33%; +} + +#sidebarsWidgets { + float: left; + width: 65%; + margin-left: 1%; +} +#sidebarNav { + float: left; + width: 50%; + margin-left: 0%; +} +#sidebarExtra { + float: left; + width: 49%; + margin-left: 1%; +} + +#sidebarsControl { + clear: left; +} + +#widgets, #dndnav, #dndextra { + padding: 1em 0; +} + +.widgets fieldset { + border: 1px solid #999; +} +.widgets legend { + border: 1px solid #999; +} + +.widgets p.widget-name { + background: #d4d0bc; + color: #000; + padding: 4px; + margin: 0.5em 0 0 0; + border-width: 1px 1px 1px 1px; + border-style: solid; + border-color: #666; + font-weight: bold; +} +.widgets p.widget-name img { + cursor: pointer; + margin-bottom: -1px; +} + +.widgetSettings { + border-width: 0 1px 1px 1px; + border-style: solid; + border-color: #666; + padding: 4px; + background: #eceade; +} + +#widgets .widgetSettings { + display: none; +} + +.hideControls .widgetSettings { + display: none; +} +.hideControl { + display: none; +} + +#widgets-tpl dt { + margin: 1em 0 0 0; +} +#widgets-tpl dd { + margin: 0.2em 0 1em 0; +} \ No newline at end of file diff --git a/plugins/widgets/widgets.js b/plugins/widgets/widgets.js new file mode 100644 index 0000000..b61f6d6 --- /dev/null +++ b/plugins/widgets/widgets.js @@ -0,0 +1,21 @@ + +var dragdrop=ToolMan.dragdrop();$(function(){$('input[name="wreset"]').click(function(){return window.confirm(dotclear.msg.confirm_widgets_reset);});});$(function(){var widgets=document.getElementById('widgets');var w_nav=document.getElementById('dndnav');var w_ext=document.getElementById('dndextra');w_nav.className='hideControls';w_ext.className='hideControls';removeElements(document.getElementById('listWidgets'),'input');removeElements(widgets,'p');removeElements(w_nav,'p');removeElements(w_ext,'p');hideElements(w_nav,'input');hideElements(w_ext,'input');configControls(w_nav);configControls(w_ext);dragdrop.makeListContainer(widgets,'div',setHandle);if(!document.all){widgets.factory=true;} +dragdrop.makeListContainer(w_nav,'div',setHandle);w_nav.onDragEnd=navDragEnd;dragdrop.makeListContainer(w_ext,'div',setHandle);w_ext.onDragEnd=extraDragEnd;function removeElements(p,name){name=name||'div';$(p).find(name+'.js-remove').each(function(){this.parentNode.removeChild(this);});} +function hideElements(p,name){name=name||'div';$(p).find(name+'.js-hide').each(function(){$(this).toggle();});} +function removeEmptyMsg(p){$(p).find('p.empty-widgets').each(function(){this.parentNode.removeChild(this);});} +function navDragEnd(){formControls(this.parentNode,'nav');configControls(this.parentNode);removeEmptyMsg(this.parentNode);} +function extraDragEnd(){formControls(this.parentNode,'extra');configControls(this.parentNode);removeEmptyMsg(this.parentNode);} +function formControls(e,pr){var items=new Array();for(var i=0;i');f.submit();return false;});});} \ No newline at end of file diff --git a/public/.IMG4_m.jpg b/public/.IMG4_m.jpg new file mode 100644 index 0000000..47d6d8e Binary files /dev/null and b/public/.IMG4_m.jpg differ diff --git a/public/.IMG4_s.jpg b/public/.IMG4_s.jpg new file mode 100644 index 0000000..7c161c0 Binary files /dev/null and b/public/.IMG4_s.jpg differ diff --git a/public/.IMG4_sq.jpg b/public/.IMG4_sq.jpg new file mode 100644 index 0000000..7b7c6b5 Binary files /dev/null and b/public/.IMG4_sq.jpg differ diff --git a/public/.IMG4_t.jpg b/public/.IMG4_t.jpg new file mode 100644 index 0000000..b1404d0 Binary files /dev/null and b/public/.IMG4_t.jpg differ diff --git a/public/.La_fracture_52_m.jpg b/public/.La_fracture_52_m.jpg new file mode 100644 index 0000000..7110d4b Binary files /dev/null and b/public/.La_fracture_52_m.jpg differ diff --git a/public/.La_fracture_52_s.jpg b/public/.La_fracture_52_s.jpg new file mode 100644 index 0000000..bbe534b Binary files /dev/null and b/public/.La_fracture_52_s.jpg differ diff --git a/public/.La_fracture_52_sq.jpg b/public/.La_fracture_52_sq.jpg new file mode 100644 index 0000000..011ae0f Binary files /dev/null and b/public/.La_fracture_52_sq.jpg differ diff --git a/public/.La_fracture_52_t.jpg b/public/.La_fracture_52_t.jpg new file mode 100644 index 0000000..0593446 Binary files /dev/null and b/public/.La_fracture_52_t.jpg differ diff --git a/public/.La_fracture_53_m.jpg b/public/.La_fracture_53_m.jpg new file mode 100644 index 0000000..e2faccd Binary files /dev/null and b/public/.La_fracture_53_m.jpg differ diff --git a/public/.La_fracture_53_s.jpg b/public/.La_fracture_53_s.jpg new file mode 100644 index 0000000..12a8d20 Binary files /dev/null and b/public/.La_fracture_53_s.jpg differ diff --git a/public/.La_fracture_53_sq.jpg b/public/.La_fracture_53_sq.jpg new file mode 100644 index 0000000..cf32ce8 Binary files /dev/null and b/public/.La_fracture_53_sq.jpg differ diff --git a/public/.La_fracture_53_t.jpg b/public/.La_fracture_53_t.jpg new file mode 100644 index 0000000..06505f5 Binary files /dev/null and b/public/.La_fracture_53_t.jpg differ diff --git a/public/.La_fracture_54_m.jpg b/public/.La_fracture_54_m.jpg new file mode 100644 index 0000000..db23b4e Binary files /dev/null and b/public/.La_fracture_54_m.jpg differ diff --git a/public/.La_fracture_54_s.jpg b/public/.La_fracture_54_s.jpg new file mode 100644 index 0000000..b6149d7 Binary files /dev/null and b/public/.La_fracture_54_s.jpg differ diff --git a/public/.La_fracture_54_sq.jpg b/public/.La_fracture_54_sq.jpg new file mode 100644 index 0000000..6bf1cfc Binary files /dev/null and b/public/.La_fracture_54_sq.jpg differ diff --git a/public/.La_fracture_54_t.jpg b/public/.La_fracture_54_t.jpg new file mode 100644 index 0000000..f754f05 Binary files /dev/null and b/public/.La_fracture_54_t.jpg differ diff --git a/public/.La_fracture_55_m.jpg b/public/.La_fracture_55_m.jpg new file mode 100644 index 0000000..367159c Binary files /dev/null and b/public/.La_fracture_55_m.jpg differ diff --git a/public/.La_fracture_55_s.jpg b/public/.La_fracture_55_s.jpg new file mode 100644 index 0000000..75690e6 Binary files /dev/null and b/public/.La_fracture_55_s.jpg differ diff --git a/public/.La_fracture_55_sq.jpg b/public/.La_fracture_55_sq.jpg new file mode 100644 index 0000000..0180cbb Binary files /dev/null and b/public/.La_fracture_55_sq.jpg differ diff --git a/public/.La_fracture_55_t.jpg b/public/.La_fracture_55_t.jpg new file mode 100644 index 0000000..a8a229b Binary files /dev/null and b/public/.La_fracture_55_t.jpg differ diff --git a/public/.La_fracture_56_m.jpg b/public/.La_fracture_56_m.jpg new file mode 100644 index 0000000..52107e9 Binary files /dev/null and b/public/.La_fracture_56_m.jpg differ diff --git a/public/.La_fracture_56_s.jpg b/public/.La_fracture_56_s.jpg new file mode 100644 index 0000000..3162c5a Binary files /dev/null and b/public/.La_fracture_56_s.jpg differ diff --git a/public/.La_fracture_56_sq.jpg b/public/.La_fracture_56_sq.jpg new file mode 100644 index 0000000..6bd7aca Binary files /dev/null and b/public/.La_fracture_56_sq.jpg differ diff --git a/public/.La_fracture_56_t.jpg b/public/.La_fracture_56_t.jpg new file mode 100644 index 0000000..730caf2 Binary files /dev/null and b/public/.La_fracture_56_t.jpg differ diff --git a/public/.La_fracture_57_m.jpg b/public/.La_fracture_57_m.jpg new file mode 100644 index 0000000..c3b43bf Binary files /dev/null and b/public/.La_fracture_57_m.jpg differ diff --git a/public/.La_fracture_57_s.jpg b/public/.La_fracture_57_s.jpg new file mode 100644 index 0000000..cabd646 Binary files /dev/null and b/public/.La_fracture_57_s.jpg differ diff --git a/public/.La_fracture_57_sq.jpg b/public/.La_fracture_57_sq.jpg new file mode 100644 index 0000000..91ada6e Binary files /dev/null and b/public/.La_fracture_57_sq.jpg differ diff --git a/public/.La_fracture_57_t.jpg b/public/.La_fracture_57_t.jpg new file mode 100644 index 0000000..c772c41 Binary files /dev/null and b/public/.La_fracture_57_t.jpg differ diff --git a/public/020323585666.pdf b/public/020323585666.pdf new file mode 100644 index 0000000..2c1a294 Binary files /dev/null and b/public/020323585666.pdf differ diff --git a/public/IMG4.jpg b/public/IMG4.jpg new file mode 100644 index 0000000..c772921 Binary files /dev/null and b/public/IMG4.jpg differ diff --git a/public/La_fracture_52.jpg b/public/La_fracture_52.jpg new file mode 100644 index 0000000..d32b04e Binary files /dev/null and b/public/La_fracture_52.jpg differ diff --git a/public/La_fracture_53.jpg b/public/La_fracture_53.jpg new file mode 100644 index 0000000..e7b06ff Binary files /dev/null and b/public/La_fracture_53.jpg differ diff --git a/public/La_fracture_54.jpg b/public/La_fracture_54.jpg new file mode 100644 index 0000000..fafc34e Binary files /dev/null and b/public/La_fracture_54.jpg differ diff --git a/public/La_fracture_55.jpg b/public/La_fracture_55.jpg new file mode 100644 index 0000000..f763870 Binary files /dev/null and b/public/La_fracture_55.jpg differ diff --git a/public/La_fracture_56.jpg b/public/La_fracture_56.jpg new file mode 100644 index 0000000..ba598bd Binary files /dev/null and b/public/La_fracture_56.jpg differ diff --git a/public/La_fracture_57.jpg b/public/La_fracture_57.jpg new file mode 100644 index 0000000..3f49964 Binary files /dev/null and b/public/La_fracture_57.jpg differ diff --git a/themes/altowithjquery/Change.txt b/themes/altowithjquery/Change.txt new file mode 100644 index 0000000..ce95435 --- /dev/null +++ b/themes/altowithjquery/Change.txt @@ -0,0 +1,18 @@ +v1.5.2 : Dotclear 2.1.1. Mise ˆ jour des styles. + +v1.5.1 : DŽplacement de la division #pr vers #comment-form comme le template post.html par dŽfaut. Correction de style.css pour l'adaptation. + +v1.5 : Dotclear 2.0. + +v1.3 : Retour en arrire. _head.html rempile. Plus d'utilisation de jQuery no conflict. Changement de librairie. Prise en compte de Pages. + +v1.2 : DC2 RC1. + +v 1.1.2 : DC2 beta 7. Modification en-tte html suite aux changements du validateur W3C. Adaptation du fichier style.css avec l'ajout du bouton Envoyer lors de la prŽvisualisation d'un commentaire. + +v1.1.1 : DC2 beta 7. Passage des scripts jQuery en jQuery no conflict. Merci ˆ GŽrard sur http://geraldetsonblog.free.fr/. + +v1.1 : Remplacement de la librairie jquery.corner par curvycorner. Mise ˆ niveau des styles des commentaires. Support des plug-ins Gallery et Related. +jQuery 1.1.2 est inclus dans le thme car elle corrige des bugs pour certains navigateurs. + +v1.0 : DC2 beta 5 \ No newline at end of file diff --git a/themes/altowithjquery/_define.php b/themes/altowithjquery/_define.php new file mode 100644 index 0000000..2a7245d --- /dev/null +++ b/themes/altowithjquery/_define.php @@ -0,0 +1,29 @@ +registerModule( + /* Name */ "altowithjQuery", + /* Description*/ "a multi theme", + /* Author */ "pyeb", + /* Version */ '1.5.2' +); +?> \ No newline at end of file diff --git a/themes/altowithjquery/custom.css b/themes/altowithjquery/custom.css new file mode 100644 index 0000000..2772ee8 --- /dev/null +++ b/themes/altowithjquery/custom.css @@ -0,0 +1,15 @@ +/********************** +* Plug-in specifiques * +**********************/ + +#tribunelibreformulaire input { + width: 170px; + padding: 0px; + margin: 5px 0; + } + +#tribunelibreformulaire input.submit { + width: auto; + margin: 0 0 2px 0; + padding: 2px; + } \ No newline at end of file diff --git a/themes/altowithjquery/img/attach.png b/themes/altowithjquery/img/attach.png new file mode 100644 index 0000000..233671d Binary files /dev/null and b/themes/altowithjquery/img/attach.png differ diff --git a/themes/altowithjquery/img/background.gif b/themes/altowithjquery/img/background.gif new file mode 100644 index 0000000..63ed6bb Binary files /dev/null and b/themes/altowithjquery/img/background.gif differ diff --git a/themes/altowithjquery/img/commentaire.png b/themes/altowithjquery/img/commentaire.png new file mode 100644 index 0000000..26719db Binary files /dev/null and b/themes/altowithjquery/img/commentaire.png differ diff --git a/themes/altowithjquery/img/footer.png b/themes/altowithjquery/img/footer.png new file mode 100644 index 0000000..23320bd Binary files /dev/null and b/themes/altowithjquery/img/footer.png differ diff --git a/themes/altowithjquery/img/footer.psd b/themes/altowithjquery/img/footer.psd new file mode 100644 index 0000000..0f3f782 Binary files /dev/null and b/themes/altowithjquery/img/footer.psd differ diff --git a/themes/altowithjquery/img/page.png b/themes/altowithjquery/img/page.png new file mode 100644 index 0000000..058f729 Binary files /dev/null and b/themes/altowithjquery/img/page.png differ diff --git a/themes/altowithjquery/img/q.png b/themes/altowithjquery/img/q.png new file mode 100644 index 0000000..4fb7ed6 Binary files /dev/null and b/themes/altowithjquery/img/q.png differ diff --git a/themes/altowithjquery/img/retrolien.png b/themes/altowithjquery/img/retrolien.png new file mode 100644 index 0000000..8828bd2 Binary files /dev/null and b/themes/altowithjquery/img/retrolien.png differ diff --git a/themes/altowithjquery/img/rss.png b/themes/altowithjquery/img/rss.png new file mode 100644 index 0000000..65506b8 Binary files /dev/null and b/themes/altowithjquery/img/rss.png differ diff --git a/themes/altowithjquery/img/style.css b/themes/altowithjquery/img/style.css new file mode 100644 index 0000000..d06f406 --- /dev/null +++ b/themes/altowithjquery/img/style.css @@ -0,0 +1,671 @@ +/* +alto with jQuery +-------------------------------------------------------- */ + +@import "custom.css"; + + +* { margin: 0; padding: 0; border: 0; } + +body { + background: #E5E5E5; + background: transparent url(img/background.gif) repeat-x top left;; + font-family: Georgia, serif; + font-size: 0.8em; + color: #333333; + line-height: 1.8em; + } + +/* Structure +-------------------------------------------------------- */ +#page { + width: 880px; + margin: 0 auto; + padding: 0; + position: relative; + background: transparent url(img/page.png) repeat-y top left; + } + +#top { + height: 153px; + background: transparent url(img/top.png) no-repeat top left; + margin: 0; + padding: 0; + z-index: 0; + } + +#banner { + display : block; + position : absolute; + background-color: transparent; + width: 780px; + height: 10px; + top : 41px ; + left : 50px ; + z-index: 1; + padding: 0; + } + + + +#top h1 { + position : absolute; + top: 50px; + left: 50px; + z-index: 2; + } + + +#prelude { + display: none; + position: absolute; + top: 45px; + right: 120px; + padding: 0 10px; + font-size: 0.8em; + } + +#prelude a { color: #ddd;} + +#wrapper { + width: 780px; + margin: 0 50px; + } + +#main { + text-align: justify; + float: left; + width: 530px; + overflow: visible; + } + +#content { + margin-left: 30px; + overflow: visible; + } + +#sidebar { + float: right; + width: 230px; + margin-bottom: 30px; + } + +#blognav, #blogextra { + margin-right: 0px; + margin-left:40px; + } + +#footer { + height: 120px; + background: #fff url(img/footer.png) repeat-y top left; + } + +#footer p { + padding: 20px 100px 10px 100px; + font-size: 0.8em; + text-align: center; + } + +/* Styles communs +-------------------------------------------------------- */ +h1, h2, h3, h4, h5, .post-title { + font-family: Georgia, serif; + line-height: 24px; + letter-spacing: -1px; + font-style: normal; + font-weight: 500; +} + +h1{font-size: 46px;} +h2{font-size: 34px;} +h3{font-size: 20px;} +h4{font-size: 11px;} +h5{font-size: 1.1em;} +h6{font-size: 1em;} + +li { list-style: none; } + +blockquote { + text-align:left; + margin: 6px 20px 6px 20px; +} + +html>body blockquote { + padding-left:10px; + } + +acronym { cursor: help; text-decoration: underline; } + +pre { + text-align:left; + overflow: scroll; + width: 475px; + border-left : 3px solid #008ACF; + font-family : Georgia, serif; + color : #0273b9; + font-size : 12px; + margin : 10px; + line-height :14px; +} + +html>body pre { + width: auto; + } + +code { + font-family : Georgia, serif; + color : #0273b9; + font-size : 12px; + margin : 0px; + text-align : left; + padding : 1px 5px 1px 5px; + line-height :14px; +} + +input { text-transform: lowercase; } + +.left { float: left; } + +.right { float: right; } + +img { + border : none; + } + +.error { + border: 1px dashed #fff; + padding: 5px 0; + margin: 20px 0 0 0; + color: #fff; + background: #E2001A; + } + +.message { + padding: 10px 0; + margin: 0; + } + +.post, .post-content, .footnotes, .postend, .attachments, .post-info-co, #comments, #footer, .pagination, #navlinks { + clear: both; + } + +/* Couleurs +--------------------------*/ + +body { color: #000; } + +h1, h2, h3, h4, h5, .post-title, .day-date { color: #333333; } + +a { + color: #0273B9; + text-decoration : none; + } +#top a:link { + color: #fff; +} +#top a:visited { + color: #fff; +} +#top a:hover { + color: #fff; +} +a:visited { + color: #0273B9; + text-decoration : none; + } + +a:hover { + color: #0273B9; + text-decoration : none; + } + + +input, textarea, select { border: 1px solid #999; background: #fff; color: #333333; } + +#comment-form textarea {border: 1px solid #999;} + +blockquote, pre { border-left : 3px solid #008ACF; } + +code { color : #0273b9; } + +.post, #comment-form .hidecomment-form, .content-inner ul, .dc-archive-month .content-inner { background: #ffffff; } + +.pagination, #navlinks { color: #333; } + +/* Choix des couleurs pour les éléments arrondis */ +#sidebar .tags ul, #sidebar .selected ul, #sidebar .feed ul, .dc-tags ul.tags, #sidebar .pages ul { background: #e5e5e5; } + +#sidebar .links ul, #sidebar .langs ul, #sidebar #authors ul { background: #e5e5e5; } + +#sidebar .syndicate ul, #sidebar .syndicate a/* le fond des liens RSS est blanc sinon */, #sidebar .lastposts ul, #attachments ul, #sidebar #related ul { background: #E5E5E5; } + +#sidebar .categories ul, #sidebar .lastcomments ul, #sidebar #galleries ul { background: #e5e5e5; } + +/* Pour Safari, ajouter les divisions contenant les élements arrondis */ +#content, #topnav, #sidebar .tags, #sidebar .selected, #sidebar .feed, div.content-inner, #sidebar .links, #sidebar .langs, #sidebar #authors, #sidebar .syndicate, #sidebar .lastposts, #attachments, dl, #sidebar .categories, #sidebar .lastcomments, #sidebar #galleries, #comment-form, #sidebar .pages {background: #ececec;} + +input:hover { + color: #0273B9; + } + +dd, dt { + background: #F7DFE6; + } + +#comment-form p.form-help { color: #999999; } + +#calendar table{ + font-size: 9px; + text-align: center; + margin: 0 auto; + border-collapse: collapse; + } + +#calendar table caption{ + margin: 0 auto; + } + +#calendar table th{ + color: #333333; + background: transparent; + } + +#calendar table td{ + width: 14%; + line-height: 2em; + border: 0.1em solid #EEE; + } + +#calendar table td a{ + display: block; + background: #F7DFE6; + color: #0273b9; + font-weight: normal; + text-decoration: none; + } + +#calendar table td a:hover{ + background: #0273b9; + color: #F7DFE6; + } + +/* Post +-------------------------------------------------------- */ +.post { + padding: 10px; + margin-bottom: 50px; + position: relative; + font-size: 1em; + overflow: visible; + } + +.post p { + padding: 0 0 5px 0; + text-align: justify; + } + +.read-it { + padding: 5px 0; + } + +.footnotes { + font-size:0.9em; + } + +.footnotes, .footnotes p { + margin-top : 1em; text-indent : 0; + } + +.post-content ul, .post-excerpt ul, .post-content ol, .post-excerpt ol { + padding: 0; + margin: 0 0 0 20px; + } + +.post-content ul li, .post-excerpt ul li, #attachments li { + padding: 0 0 5px 5px; + list-style-type: disc; + } + +.post-content ol li, .post-excerpt ol li { + padding: 0 0 5px 5px; + list-style-type: decimal; + } + +.day-date { + margin-top: -3px; + padding-bottom: 10px; + color: #B1B1B1; + } + +.post-title { + font-family: Georgia, serif; + font-size: 28px; + line-height: 28px; + margin: 0 0 20px 0; + text-align: left; + } + +.post-info { display: none; font-family: gerogia, serif; padding: 10px 0; } + +.post-tags { font-family: Georgia, Helvetica, "Trebuchet MS", serif; padding-bottom: 10px; } + +.post-tags a { padding: 2px 0 2px 18px; background: transparent url(img/tag.png) no-repeat 0 50%; } + +ul.post-tags li:after { content: ', '; } + +ul.post-tags li:last-child:after { content: ''; } + +.post-tags li { + display: inline; + /*white-space: nowrap;*/ + text-align: left; + } + +.post-info-co { + text-align: right; + padding: 20px 0; + } + +.post-info-co a:after { content: ' -'; } + +.post-info-co a:last-child:after { content: ''; } + +.post-info-co a { padding: 2px 0 2px 18px; } + +.comment_count { background: transparent url(img/commentaire.png) no-repeat 0 50%; } + +.ping_count { background: transparent url(img/retrolien.png) no-repeat 0 50%; } + +.attach_count { background: transparent url(img/attach.png) no-repeat 0 50%; } + +.read-it { + font-weight: bold; + padding-bottom: 10px; + clear: left; + } + +.pagination, #navlinks { + text-align: center; + font-weight: bold; + } +.pagination { padding-bottom: 10px; } + +#navlinks { padding-bottom: 20px; } + +#attachments { + clear: left; + padding: 0; + margin-bottom: 20px; + } + +#attachments ul { margin: 20px 0; padding: 10px; } + +#attachments li { list-style: none; } + +/* Comment +-------------------------------------------------------- */ +#comments, #pings { + margin: 20px 0; + font-size: 0.9em; + } + +#comments dt, #pings dt { + margin: 20px 0 0; + padding: 10px; + font-family:"century gothic", "gill sans", serif; + } + +#comments dd, #pings dd { + padding: 10px; + margin-bottom: 30px; + text-align: justify; + } + +#comments dd.me, #comments dt.me a { font-weight: bold; } + + +#comments dt .comment-number, #pings dt .comment-number { + font-size : 1.8em; + } + +#comments-feed { + font-weight: bold; + margin-top : 10px;; + } + +#comments-feed a:link, #comments-feed a:visited { + background: transparent url(img/rss.png) no-repeat 0px 50%; + padding: 3px 5px 2px 22px; + } + +#ping-url { + font-size: 0.85em; + text-align: left; + padding: 0; + } + +/* Comments forms +-------------------------------------------------------- */ +#pr p.buttons { margin: 0 0 20px 10px;} + +#pr dd { margin: 20px 0; padding: 10px; } + +#comment-form .hidecomment-form { + padding: 10px; + margin-bottom: 20px; + font-size: 0.9em; + } + +#comment-form h3 { margin-bottom: 20px;} + +#comment-form fieldset p { padding: 5px 0; } + +#comment-form fieldset p.field { padding: 7px 0; width: auto; margin: 0; } + +#comment-form p.form-help { + margin: 0; + text-align: justify; + } + +#comment-form p label { + width: auto; + } +#comment-form p.field label { + font-weight: bold; + display: block; + padding: 0 4px 4px 0; + } + +#comment-form input, #comment-form textarea { + padding: 1px 2px; + font: 1em Georgia, Helvetica, serif; + } + +#comment-form input { + width: 50%; + } + +#comment-form textarea { + width: 490px; + } + +#comment-form input#c_remember { + width: auto; + border: 0; + margin: 0 5px 0 0; + } + +#comment-form input.preview, #comment-form input.submit { + width: auto; + font-size: 1em; + font-weight: bold; + } + +/* Sidebar +-------------------------------------------------------- */ +#sidebar { + font-size: 0.85em; + overflow: visible; + } + +#sidebar h2 { + font-family: Georgia, Helvetica, "Trebuchet MS", serif; + font-size: 21px; + padding: 0 0 5px 0; + } + +#sidebar h3 { + font-size: 1em; + } + +#sidebar p { + margin: 5px 0; + } + +#sidebar .text { + margin: 5px 0; + overflow: visible; + } + +#sidebar ul { + margin: 10px 0; + padding: 10px; + } + +#sidebar ul li { + margin: 0; + font-size: 1em; + } + +#topnav { + text-align: center; + margin: 0; + padding: 5px 0; + } + +#sidebar #topnav ul { margin: 0; padding: 10px; } + +#sidebar #topnav li { + display: inline; + } + +#sidebar .tags ul { + font-size: 110%; + } + +#sidebar .tags ul li { + display: inline; + } + +.tag0 { font-size: 75%; } +.tag10 { font-size: 80%; } +.tag20 { font-size: 85%; } +.tag30 { font-size: 90%; } +.tag40 { font-size: 100%; } +.tag50 { font-size: 110%; } +.tag60 { font-size: 120%; } +.tag70 { font-size: 130%; } +.tag80 { font-size: 140%; } +.tag90 { font-size: 150%; } +.tag100 { font-size: 160%; } + +#sidebar .syndicate li { + margin: 0; + background: transparent url(img/rss.png) no-repeat 0px 50%; + padding: 5px 4px 5px 22px; + } + +/* Sidebar forms +-------------------------------------------------------- */ +#search input#q { + padding: 1px 1px 1px 18px; + margin: 0 5px 0 0; + width: 125px; + font-size: 0.9em; + background: #fff url(img/q.png) no-repeat 4px center; + } + +#search input[type=submit] { + padding: 0; + margin: 0; +} + +#search input.submit { + padding: 0; + margin: 0; +} + +#sidebar fieldset { + display : block; + border : none; + } + +#sidebar fieldset p { + margin-bottom : 1em; + } + +#sidebar input, #sidebar textarea, #sidebar select { + font-size: 0.9em; + margin: 5px 0; + padding: 1px; + line-height: 1.5em; + } + +#sidebar select, #sidebar input.text { + width: 175px; + } + +#sidebar input[type=text]#sidebar { + width: 175px; + } + +/* Page +-------------------------------------------------------- */ +#content-info { + font-size: 1.2em; + margin:0; + padding: 10px 0; + } + +#content-info h2, .content-inner h3 { + padding-bottom: 10px; + } + +.dc-archive-month .content-inner { + padding: 10px; + } +#subcategories { padding: 10px 0;} + +.dc-archive .content-inner ul { + padding: 10px; + margin-bottom: 30px; + } + +.dc-archive .content-inner ul li { + line-height: 2em; + } + +.dc-tags ul.tags { + margin: 0; + padding: 20px; + } + +.dc-tags ul.tags li { + display: inline; + font-size: 1.5em; + line-height: 1.8em; + } + +#content-info p { + padding: 10px 0; + font-size: 0.9em; + } + +#content-info p a.feed:link, #content-info p a.feed:visited { + padding: 3px 0 3px 22px; + background: transparent url(img/rss.png) no-repeat 1px 3px; + } + +.dc-category #content-info { + text-align: justify; + } \ No newline at end of file diff --git a/themes/altowithjquery/img/tag.png b/themes/altowithjquery/img/tag.png new file mode 100644 index 0000000..fd62196 Binary files /dev/null and b/themes/altowithjquery/img/tag.png differ diff --git a/themes/altowithjquery/img/top.png b/themes/altowithjquery/img/top.png new file mode 100644 index 0000000..75d946e Binary files /dev/null and b/themes/altowithjquery/img/top.png differ diff --git a/themes/altowithjquery/img/top.psd b/themes/altowithjquery/img/top.psd new file mode 100644 index 0000000..a4c24e1 Binary files /dev/null and b/themes/altowithjquery/img/top.psd differ diff --git a/themes/altowithjquery/screenshot.jpg b/themes/altowithjquery/screenshot.jpg new file mode 100644 index 0000000..dec670c Binary files /dev/null and b/themes/altowithjquery/screenshot.jpg differ diff --git a/themes/altowithjquery/style.css b/themes/altowithjquery/style.css new file mode 100644 index 0000000..8d872a5 --- /dev/null +++ b/themes/altowithjquery/style.css @@ -0,0 +1,679 @@ +/* +alto with jQuery +-------------------------------------------------------- */ + +@import "custom.css"; + + +* { margin: 0; padding: 0; border: 0; } + +body { + background: #fff; + background: transparent url(img/background.gif) repeat-x top left;; + font-family: Georgia, serif; + font-size: 0.8em; + color: #333333; + line-height: 1.8em; + } + +/* Structure +-------------------------------------------------------- */ +#page { + width: 880px; + margin: 0 auto; + padding: 0; + position: relative; + background: transparent url(img/page.png) repeat-y top left; + } + +#top { + height: 153px; + background: transparent url(img/top.png) no-repeat top left; + margin: 0; + padding: 0; + z-index: 0; + } + +#banner { + display : block; + position : absolute; + background-color: transparent; + width: 780px; + height: 10px; + top : 41px ; + left : 50px ; + z-index: 1; + padding: 0; + } + + + +#top h1 { + position : absolute; + top: 50px; + left: 50px; + z-index: 2; + } + +#top h2 { + position : absolute; + top: 0px; + left: 50px; + z-index: 2; + } + + +#prelude { + display: none; + position: absolute; + top: 45px; + right: 120px; + padding: 0 10px; + font-size: 0.8em; + } + +#prelude a { color: #ddd;} + +#wrapper { + width: 780px; + margin: 0 50px; + } + +#main { + text-align: justify; + float: left; + width: 530px; + overflow: visible; + } + +#content { + margin-left: 30px; + overflow: visible; + } + +#sidebar { + float: right; + width: 230px; + margin-bottom: 30px; + } + +#blognav, #blogextra { + margin-right: 0px; + margin-left:40px; + } + +#footer { + height: 120px; + background: #fff url(img/footer.png) repeat-y top left; + } + +#footer p { + padding: 20px 100px 10px 100px; + font-size: 0.8em; + text-align: center; + } + +/* Styles communs +-------------------------------------------------------- */ +h1, h2, h3, h4, h5, .post-title { + font-family: Georgia, serif; + line-height: 24px; + letter-spacing: -1px; + font-style: normal; + font-weight: 500; +} + +h1{font-size: 46px;} +h2{font-size: 34px;} +h3{font-size: 20px;} +h4{font-size: 11px;} +h5{font-size: 1.1em;} +h6{font-size: 1em;} + +li { list-style: none; } + +blockquote { + text-align:left; + margin: 6px 20px 6px 20px; +} + +html>body blockquote { + padding-left:10px; + } + +acronym { cursor: help; text-decoration: underline; } + +pre { + text-align:left; + overflow: scroll; + width: 475px; + border-left : 3px solid #008ACF; + font-family : Georgia, serif; + color : #0273b9; + font-size : 12px; + margin : 10px; + line-height :14px; +} + +html>body pre { + width: auto; + } + +code { + font-family : Georgia, serif; + color : #0273b9; + font-size : 12px; + margin : 0px; + text-align : left; + padding : 1px 5px 1px 5px; + line-height :14px; +} + +input { text-transform: lowercase; } + +.left { float: left; } + +.right { float: right; } + +img { + border : none; + } + +.error { + border: 1px dashed #fff; + padding: 5px 0; + margin: 20px 0 0 0; + color: #fff; + background: #E2001A; + } + +.message { + padding: 10px 0; + margin: 0; + } + +.post, .post-content, .footnotes, .postend, .attachments, .post-info-co, #comments, #footer, .pagination, #navlinks { + clear: both; + } + +/* Couleurs +--------------------------*/ + +body { color: #000; } + +h1, h2, h3, h4, h5, .post-title, .day-date { color: #333333; } + +a { + color: #0273B9; + text-decoration : none; + } +#top a:link { + color: #fff; +} +#top a:visited { + color: #fff; +} +#top a:hover { + color: #fff; +} +a:visited { + color: #0273B9; + text-decoration : none; + } + +a:hover { + color: #0273B9; + text-decoration : none; + } + + +input, textarea, select { border: 1px solid #999; background: #fff; color: #333333; } + +#comment-form textarea {border: 1px solid #999;} + +blockquote, pre { border-left : 3px solid #008ACF; } + +code { color : #0273b9; } + +.post, #comment-form .hidecomment-form, .content-inner ul, .dc-archive-month .content-inner { background: #ffffff; } + +.pagination, #navlinks { color: #333; } + +/* Choix des couleurs pour les éléments arrondis */ +#sidebar .tags ul, #sidebar .selected ul, #sidebar .feed ul, .dc-tags ul.tags, #sidebar .pages ul { background: #ececec; } + +#sidebar .links ul, #sidebar .langs ul, #sidebar #authors ul { background: #ececec; } + +#sidebar .syndicate ul, #sidebar .syndicate a/* le fond des liens RSS est blanc sinon */, #sidebar .lastposts ul, #attachments ul, #sidebar #related ul { background: #ececec; } + +#sidebar .categories ul, #sidebar .lastcomments ul, #sidebar #galleries ul { background: #ececec; } + +/* Pour Safari, ajouter les divisions contenant les élements arrondis */ +#topnav, #sidebar .tags, #sidebar .selected, #sidebar .feed, div.content-inner, #sidebar .links, #sidebar .langs, #sidebar #authors, #sidebar .syndicate, #sidebar .lastposts, #attachments, dl, #sidebar .categories, #sidebar .lastcomments, #sidebar #galleries, #comment-form, #sidebar .pages {background: #ececec;} + +input:hover { + color: #0273B9; + } + +dd, dt { + background: #F7DFE6; + } + +#comment-form p.form-help { color: #999999; } + +#calendar table{ + font-size: 9px; + text-align: center; + margin: 0 auto; + border-collapse: collapse; + } + +#calendar table caption{ + margin: 0 auto; + } + +#calendar table th{ + color: #333333; + background: transparent; + } + +#calendar table td{ + width: 14%; + line-height: 2em; + border: 0.1em solid #EEE; + } + +#calendar table td a{ + display: block; + background: #F7DFE6; + color: #0273b9; + font-weight: normal; + text-decoration: none; + } + +#calendar table td a:hover{ + background: #0273b9; + color: #F7DFE6; + } + +/* Post +-------------------------------------------------------- */ +.post { + padding: 10px; + margin-bottom: 50px; + position: relative; + font-size: 1em; + overflow: visible; + } + +.post p { + padding: 0 0 5px 0; + text-align: justify; + } + +.read-it { + padding: 5px 0; + } + +.footnotes { + font-size:0.9em; + } + +.footnotes, .footnotes p { + margin-top : 1em; text-indent : 0; + } + +.post-content ul, .post-excerpt ul, .post-content ol, .post-excerpt ol { + padding: 0; + margin: 0 0 0 20px; + } + +.post-content ul li, .post-excerpt ul li, #attachments li { + padding: 0 0 5px 5px; + list-style-type: disc; + } + +.post-content ol li, .post-excerpt ol li { + padding: 0 0 5px 5px; + list-style-type: decimal; + } + +.day-date { + margin-top: -3px; + padding-bottom: 10px; + color: #B1B1B1; + } + +.post-title { + font-family: Georgia, serif; + font-size: 28px; + line-height: 28px; + margin: 0 0 20px 0; + text-align: left; + } + +.post-info { display: none; font-family: gerogia, serif; padding: 10px 0; } + +.post-tags { font-family: Georgia, Helvetica, "Trebuchet MS", serif; padding-bottom: 10px; } + +.post-tags a { padding: 2px 0 2px 18px; background: transparent url(img/tag.png) no-repeat 0 50%; } + +ul.post-tags li:after { content: ', '; } + +ul.post-tags li:last-child:after { content: ''; } + +.post-tags li { + display: inline; + /*white-space: nowrap;*/ + text-align: left; + } + +.post-info-co { + text-align: right; + padding: 20px 0; + } + +.post-info-co a:after { content: ' -'; } + +.post-info-co a:last-child:after { content: ''; } + +.post-info-co a { padding: 2px 0 2px 18px; } + +.comment_count { background: transparent url(img/commentaire.png) no-repeat 0 50%; } + +.ping_count { background: transparent url(img/retrolien.png) no-repeat 0 50%; } + +.attach_count { background: transparent url(img/attach.png) no-repeat 0 50%; } + +.read-it { + font-weight: bold; + padding-bottom: 10px; + clear: left; + } + +.pagination, #navlinks { + text-align: center; + font-weight: bold; + } +.pagination { padding-bottom: 10px; } + +#navlinks { padding-bottom: 20px; } + +#attachments { + clear: left; + padding: 0; + margin-bottom: 20px; + } + +#attachments ul { margin: 20px 0; padding: 10px; } + +#attachments li { list-style: none; } + +/* Comment +-------------------------------------------------------- */ +#comments, #pings { + margin: 20px 0; + font-size: 0.9em; + } + +#comments dt, #pings dt { + margin: 20px 0 0; + padding: 10px; + font-family:"century gothic", "gill sans", serif; + } + +#comments dd, #pings dd { + padding: 10px; + margin-bottom: 30px; + text-align: justify; + } + +#comments dd.me, #comments dt.me a { font-weight: bold; } + + +#comments dt .comment-number, #pings dt .comment-number { + font-size : 1.8em; + } + +#comments-feed { + font-weight: bold; + margin-top : 10px;; + } + +#comments-feed a:link, #comments-feed a:visited { + background: transparent url(img/rss.png) no-repeat 0px 50%; + padding: 3px 5px 2px 22px; + } + +#ping-url { + font-size: 0.85em; + text-align: left; + padding: 0; + } + +/* Comments forms +-------------------------------------------------------- */ +#pr p.buttons { margin: 0 0 20px 10px;} + +#pr dd { margin: 20px 0; padding: 10px; } + +#comment-form .hidecomment-form { + padding: 10px; + margin-bottom: 20px; + font-size: 0.9em; + } + +#comment-form h3 { margin-bottom: 20px;} + +#comment-form fieldset p { padding: 5px 0; } + +#comment-form fieldset p.field { padding: 7px 0; width: auto; margin: 0; } + +#comment-form p.form-help { + margin: 0; + text-align: justify; + } + +#comment-form p label { + width: auto; + } +#comment-form p.field label { + font-weight: bold; + display: block; + padding: 0 4px 4px 0; + } + +#comment-form input, #comment-form textarea { + padding: 1px 2px; + font: 1em Georgia, Helvetica, serif; + } + +#comment-form input { + width: 50%; + } + +#comment-form textarea { + width: 490px; + } + +#comment-form input#c_remember { + width: auto; + border: 0; + margin: 0 5px 0 0; + } + +#comment-form input.preview, #comment-form input.submit { + width: auto; + font-size: 1em; + font-weight: bold; + } + +/* Sidebar +-------------------------------------------------------- */ +#sidebar { + font-size: 0.85em; + overflow: visible; + } + +#sidebar h2 { + font-family: Georgia, Helvetica, "Trebuchet MS", serif; + font-size: 21px; + text-decoration:none; + padding: 10px 0 5px 0; + } + +#sidebar h3 { + font-size: 1em; + } + +#sidebar p { + margin: 5px 0; + } + +#sidebar .text { + margin: 5px 0; + overflow: visible; + } + +#sidebar ul { + margin: 10px 0; + padding: 10px; + } + +#sidebar ul li { + margin: 0; + font-size: 1em; + } + +#topnav { + text-align: center; + margin: 0; + padding: 5px 0; + } + +#sidebar #topnav ul { margin: 0; padding: 10px; } + +#sidebar #topnav li { + display: inline; + } + +#sidebar .tags ul { + font-size: 110%; + } + +#sidebar .tags ul li { + display: inline; + } + +.tag0 { font-size: 75%; } +.tag10 { font-size: 80%; } +.tag20 { font-size: 85%; } +.tag30 { font-size: 90%; } +.tag40 { font-size: 100%; } +.tag50 { font-size: 110%; } +.tag60 { font-size: 120%; } +.tag70 { font-size: 130%; } +.tag80 { font-size: 140%; } +.tag90 { font-size: 150%; } +.tag100 { font-size: 160%; } + +#sidebar .syndicate li { + margin: 0; + background: transparent url(img/rss.png) no-repeat 0px 50%; + padding: 5px 4px 5px 22px; + } + +/* Sidebar forms +-------------------------------------------------------- */ +#search input#q { + padding: 1px 1px 1px 18px; + margin: 0 5px 0 0; + width: 125px; + font-size: 0.9em; + background: #fff url(img/q.png) no-repeat 4px center; + } + +#search input[type=submit] { + padding: 0; + margin: 0; +} + +#search input.submit { + padding: 0; + margin: 0; +} + +#sidebar fieldset { + display : block; + border : none; + } + +#sidebar fieldset p { + margin-bottom : 1em; + } + +#sidebar input, #sidebar textarea, #sidebar select { + font-size: 0.9em; + margin: 5px 0; + padding: 1px; + line-height: 1.5em; + } + +#sidebar select, #sidebar input.text { + width: 175px; + } + +#sidebar input[type=text]#sidebar { + width: 175px; + } + +/* Page +-------------------------------------------------------- */ +#content-info { + font-size: 1.2em; + margin:0; + padding: 10px 0; + } + +#content-info h2, .content-inner h3 { + padding-bottom: 10px; + } + +.dc-archive-month .content-inner { + padding: 10px; + } +#subcategories { padding: 10px 0;} + +.dc-archive .content-inner ul { + padding: 10px; + margin-bottom: 30px; + } + +.dc-archive .content-inner ul li { + line-height: 2em; + } + +.dc-tags ul.tags { + margin: 0; + padding: 20px; + } + +.dc-tags ul.tags li { + display: inline; + font-size: 1.5em; + line-height: 1.8em; + } + +#content-info p { + padding: 10px 0; + font-size: 0.9em; + } + +#content-info p a.feed:link, #content-info p a.feed:visited { + padding: 3px 0 3px 22px; + background: transparent url(img/rss.png) no-repeat 1px 3px; + } + +.dc-category #content-info { + text-align: justify; + } \ No newline at end of file diff --git a/themes/altowithjquery/tpl/_footer.html b/themes/altowithjquery/tpl/_footer.html new file mode 100644 index 0000000..6edc9e8 --- /dev/null +++ b/themes/altowithjquery/tpl/_footer.html @@ -0,0 +1,5 @@ + + +{{tpl:SysBehavior behavior="publicFooterContent"}} \ No newline at end of file diff --git a/themes/altowithjquery/tpl/_head.html b/themes/altowithjquery/tpl/_head.html new file mode 100644 index 0000000..dbfbf2e --- /dev/null +++ b/themes/altowithjquery/tpl/_head.html @@ -0,0 +1,16 @@ + + + + + + + + + +{{tpl:include src="user_head.html"}} +{{tpl:SysBehavior behavior="publicHeadContent"}} + diff --git a/themes/altowithjquery/tpl/_top.html b/themes/altowithjquery/tpl/_top.html new file mode 100644 index 0000000..795a4af --- /dev/null +++ b/themes/altowithjquery/tpl/_top.html @@ -0,0 +1,7 @@ + + +

      {{tpl:lang To content}} | +{{tpl:lang To menu}} | +{{tpl:lang To search}}

      \ No newline at end of file diff --git a/themes/altowithjquery/tpl/archive_day.html b/themes/altowithjquery/tpl/archive_day.html new file mode 100644 index 0000000..d39b170 --- /dev/null +++ b/themes/altowithjquery/tpl/archive_day.html @@ -0,0 +1,123 @@ + + + + + + + + {{tpl:lang Archives}} - {{tpl:ArchiveDate}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + + + +
      +

      {{tpl:ArchiveDate}}

      +
      + + +
      +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +

      {{tpl:lang Continue + reading}}...

      +
      + + + +
      {{tpl:EntryContent}}
      +
      + + + + + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + +
      + +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/altowithjquery/tpl/post.html b/themes/altowithjquery/tpl/post.html new file mode 100644 index 0000000..e5ef333 --- /dev/null +++ b/themes/altowithjquery/tpl/post.html @@ -0,0 +1,297 @@ + + + + + + + + {{tpl:EntryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + + + +
      +{{tpl:EntryPingData}} + +{{tpl:include src="_top.html"}} + +
      + +
      +
      + + + +
      +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +
      + +
      {{tpl:EntryContent}}
      + + + + + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + +
       
      +
      + + + + +
      +

      {{tpl:lang Attachments}}

      +
        + +
      • + + {{tpl:include src="_mp3_player.html"/}} - + + + {{tpl:include src="_flv_player.html"/}} + + + {{tpl:AttachmentTitle}} + +
      • + +
      +
      + +
      + + + + + +
      +

      {{tpl:lang Comments}}

      +
      + +
      {{tpl:CommentOrderNumber}}. + {{tpl:lang On}} {{tpl:CommentDate}}, {{tpl:CommentTime}} + {{tpl:lang by}} {{tpl:CommentAuthorLink}}
      + +
      + + {{tpl:SysBehavior behavior="publicCommentBeforeContent"}} + + {{tpl:CommentContent}} + + + {{tpl:SysBehavior behavior="publicCommentAfterContent"}} +
      + +
      +
      + +
      +
      + + + +

      {{tpl:SysFormError}}

      +
      + + +

      {{tpl:lang Your comment has been published.}}

      +
      + + +

      {{tpl:lang Your comment has been submitted and + will be reviewed for publication.}}

      +
      + + + + +
      + +
      +

      {{tpl:lang Your comment}}

      +
      +
      {{tpl:CommentPreviewContent}}
      +
      +

      +
      +
      + +

      {{tpl:lang Add a comment}}

      +
      +
      + + {{tpl:SysBehavior behavior="publicCommentFormBeforeContent"}} + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      +

      {{tpl:lang HTML code is displayed as text and web addresses are + automatically converted.}}

      + + + {{tpl:SysBehavior behavior="publicCommentFormAfterContent"}} +
      + +
      +

      +

      +
      +
      +
      +
      + + + +
      +

      {{tpl:lang They posted on the same topic}}

      + + +
      + +
      {{tpl:PingOrderNumber}}. + {{tpl:lang On}} {{tpl:PingDate}}, {{tpl:PingTime}} + {{tpl:lang by}} {{tpl:PingBlogName encode_html="1"}}
      + +
      + + {{tpl:SysBehavior behavior="publicPingBeforeContent"}} + +

      {{tpl:PingTitle encode_html="1"}}

      + {{tpl:PingContent}} + + + {{tpl:SysBehavior behavior="publicPingAfterContent"}} +
      + +
      + +
      +
      +
      + + +

      {{tpl:lang Trackback URL}} : {{tpl:EntryPingLink}}

      +
      + + +

      {{tpl:lang This post's comments feed}}

      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/customCSS/_config.php b/themes/customCSS/_config.php new file mode 100644 index 0000000..792b6b4 --- /dev/null +++ b/themes/customCSS/_config.php @@ -0,0 +1,41 @@ +blog->public_path).'/custom_style.css'; + +if (!is_file($css_file) && !is_writable(dirname($css_file))) { + throw new Exception( + sprintf(__('File %s does not exist and directory %s is not writable.'), + $css_file,dirname($css_file)) + ); +} + +if (isset($_POST['css'])) +{ + @$fp = fopen($css_file,'wb'); + fwrite($fp,$_POST['css']); + fclose($fp); + + echo + '

      '. + __('Style sheet upgraded.'). + '

      '; +} + +$css_content = is_file($css_file) ? file_get_contents($css_file) : ''; + +echo +'

      '; +?> \ No newline at end of file diff --git a/themes/customCSS/_define.php b/themes/customCSS/_define.php new file mode 100644 index 0000000..41dec13 --- /dev/null +++ b/themes/customCSS/_define.php @@ -0,0 +1,20 @@ +registerModule( + /* Name */ "Custom theme", + /* Description*/ "A CSS customizable theme", + /* Author */ "Olivier", + /* Version */ '1.0' +); +?> \ No newline at end of file diff --git a/themes/customCSS/_public.php b/themes/customCSS/_public.php new file mode 100644 index 0000000..8515a07 --- /dev/null +++ b/themes/customCSS/_public.php @@ -0,0 +1,26 @@ +addBehavior('publicHeadContent',array('tplCustomTheme','publicHeadContent')); + +class tplCustomTheme +{ + public static function publicHeadContent(&$core) + { + echo + '\n"; + } +} +?> \ No newline at end of file diff --git a/themes/customCSS/locales/fr/main.lang.php b/themes/customCSS/locales/fr/main.lang.php new file mode 100644 index 0000000..f532e5c --- /dev/null +++ b/themes/customCSS/locales/fr/main.lang.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/themes/customCSS/locales/fr/main.po b/themes/customCSS/locales/fr/main.po new file mode 100644 index 0000000..b84aa35 --- /dev/null +++ b/themes/customCSS/locales/fr/main.po @@ -0,0 +1,20 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +# + +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8\n" + +#: themes/custom/_config.php:27 +#, php-format +msgid "File %s does not exist and directory %s is not writable." +msgstr "Le fichier %s n'existe pas et le répertoire %s n'est pas accessible en écriture." + +#: themes/custom/_config.php:40 +msgid "Style sheet upgraded." +msgstr "Feuille de style mise à jour." + +#: themes/custom/_config.php:47 +msgid "Style sheet:" +msgstr "Feuille de style :" diff --git a/themes/customCSS/style.css b/themes/customCSS/style.css new file mode 100644 index 0000000..e69de29 diff --git a/themes/default/_define.php b/themes/default/_define.php new file mode 100644 index 0000000..ab48c68 --- /dev/null +++ b/themes/default/_define.php @@ -0,0 +1,20 @@ +registerModule( + /* Name */ "Blowup", + /* Description*/ "Fully customizable theme", + /* Author */ "Marco & Olivier", + /* Version */ '1.0' +); +?> \ No newline at end of file diff --git a/themes/default/img/attach.png b/themes/default/img/attach.png new file mode 100644 index 0000000..fd7842a Binary files /dev/null and b/themes/default/img/attach.png differ diff --git a/themes/default/img/body-bg.png b/themes/default/img/body-bg.png new file mode 100644 index 0000000..469e658 Binary files /dev/null and b/themes/default/img/body-bg.png differ diff --git a/themes/default/img/comment-b.png b/themes/default/img/comment-b.png new file mode 100644 index 0000000..c65a0f0 Binary files /dev/null and b/themes/default/img/comment-b.png differ diff --git a/themes/default/img/comment-t.png b/themes/default/img/comment-t.png new file mode 100644 index 0000000..572d85b Binary files /dev/null and b/themes/default/img/comment-t.png differ diff --git a/themes/default/img/comment.png b/themes/default/img/comment.png new file mode 100644 index 0000000..b7f65ef Binary files /dev/null and b/themes/default/img/comment.png differ diff --git a/themes/default/img/commentmy-b.png b/themes/default/img/commentmy-b.png new file mode 100644 index 0000000..531a650 Binary files /dev/null and b/themes/default/img/commentmy-b.png differ diff --git a/themes/default/img/commentmy-t.png b/themes/default/img/commentmy-t.png new file mode 100644 index 0000000..4eb8ec7 Binary files /dev/null and b/themes/default/img/commentmy-t.png differ diff --git a/themes/default/img/feed.png b/themes/default/img/feed.png new file mode 100644 index 0000000..43f1956 Binary files /dev/null and b/themes/default/img/feed.png differ diff --git a/themes/default/img/page-b.png b/themes/default/img/page-b.png new file mode 100644 index 0000000..92b4889 Binary files /dev/null and b/themes/default/img/page-b.png differ diff --git a/themes/default/img/page-bg.png b/themes/default/img/page-bg.png new file mode 100644 index 0000000..6033125 Binary files /dev/null and b/themes/default/img/page-bg.png differ diff --git a/themes/default/img/page-t.png b/themes/default/img/page-t.png new file mode 100644 index 0000000..bb44f4e Binary files /dev/null and b/themes/default/img/page-t.png differ diff --git a/themes/default/img/tag.png b/themes/default/img/tag.png new file mode 100644 index 0000000..a1562bb Binary files /dev/null and b/themes/default/img/tag.png differ diff --git a/themes/default/img/trackback.png b/themes/default/img/trackback.png new file mode 100644 index 0000000..e5f6191 Binary files /dev/null and b/themes/default/img/trackback.png differ diff --git a/themes/default/js/jquery.cookie.js b/themes/default/js/jquery.cookie.js new file mode 100644 index 0000000..9b48982 --- /dev/null +++ b/themes/default/js/jquery.cookie.js @@ -0,0 +1,6 @@ + +jQuery.cookie=function(name,value,options){if(typeof value!='undefined'){options=options||{};if(value===null){value='';options.expires=-1;} +var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toUTCString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;} +expires='; expires='+date.toUTCString();} +var path=options.path?'; path='+(options.path):'';var domain=options.domain?'; domain='+(options.domain):'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i)[^>]*$|^#([\w-]+)$/,isSimple=/^.[^:#\[\.,]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;this.context=selector;return this;} +if(typeof selector==="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1]) +selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem&&elem.id!=match[3]) +return jQuery().find(selector);var ret=jQuery(elem||[]);ret.context=document;ret.selector=selector;return ret;}}else +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector)) +return jQuery(document).ready(selector);if(selector.selector&&selector.context){this.selector=selector.selector;this.context=selector.context;} +return this.setArray(jQuery.isArray(selector)?selector:jQuery.makeArray(selector));},selector:"",jquery:"1.3.2",size:function(){return this.length;},get:function(num){return num===undefined?Array.prototype.slice.call(this):this[num];},pushStack:function(elems,name,selector){var ret=jQuery(elems);ret.prevObject=this;ret.context=this.context;if(name==="find") +ret.selector=this.selector+(this.selector?" ":"")+selector;else if(name) +ret.selector=this.selector+"."+name+"("+selector+")";return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(typeof name==="string") +if(value===undefined) +return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;} +return this.each(function(i){for(name in options) +jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0) +value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!=="object"&&text!=null) +return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8) +ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).clone();if(this[0].parentNode) +wrap.insertBefore(this[0]);wrap.map(function(){var elem=this;while(elem.firstChild) +elem=elem.firstChild;return elem;}).append(this);} +return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1) +this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1) +this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},push:[].push,sort:[].sort,splice:[].splice,find:function(selector){if(this.length===1){var ret=this.pushStack([],"find",selector);ret.length=0;jQuery.find(selector,this[0],ret);return ret;}else{return this.pushStack(jQuery.unique(jQuery.map(this,function(elem){return jQuery.find(selector,elem);})),"find",selector);}},clone:function(events){var ret=this.map(function(){if(!jQuery.support.noCloneEvent&&!jQuery.isXMLDoc(this)){var html=this.outerHTML;if(!html){var div=this.ownerDocument.createElement("div");div.appendChild(this.cloneNode(true));html=div.innerHTML;} +return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0];}else +return this.cloneNode(true);});if(events===true){var orig=this.find("*").andSelf(),i=0;ret.find("*").andSelf().each(function(){if(this.nodeName!==orig[i].nodeName) +return;var events=jQuery.data(orig[i],"events");for(var type in events){for(var handler in events[type]){jQuery.event.add(this,type,events[type][handler],events[type][handler].data);}} +i++;});} +return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,jQuery.grep(this,function(elem){return elem.nodeType===1;})),"filter",selector);},closest:function(selector){var pos=jQuery.expr.match.POS.test(selector)?jQuery(selector):null,closer=0;return this.map(function(){var cur=this;while(cur&&cur.ownerDocument){if(pos?pos.index(cur)>-1:jQuery(cur).is(selector)){jQuery.data(cur,"closest",closer);return cur;} +cur=cur.parentNode;closer++;}});},not:function(selector){if(typeof selector==="string") +if(isSimple.test(selector)) +return this.pushStack(jQuery.multiFilter(selector,this,true),"not",selector);else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector==="string"?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return!!selector&&this.is("."+selector);},val:function(value){if(value===undefined){var elem=this[0];if(elem){if(jQuery.nodeName(elem,'option')) +return(elem.attributes.value||{}).specified?elem.value:elem.text;if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0) +return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length) +this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value===undefined?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,+i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},domManip:function(args,table,callback){if(this[0]){var fragment=(this[0].ownerDocument||this[0]).createDocumentFragment(),scripts=jQuery.clean(args,(this[0].ownerDocument||this[0]),fragment),first=fragment.firstChild;if(first) +for(var i=0,l=this.length;i1||i>0?fragment.cloneNode(true):fragment);if(scripts) +jQuery.each(scripts,evalScript);} +return this;function root(elem,cur){return table&&jQuery.nodeName(elem,"table")&&jQuery.nodeName(cur,"tr")?(elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody"))):elem;}}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src) +jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode) +elem.parentNode.removeChild(elem);} +function now(){return+new Date;} +jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(typeof target==="boolean"){deep=target;target=arguments[1]||{};i=2;} +if(typeof target!=="object"&&!jQuery.isFunction(target)) +target={};if(length==i){target=this;--i;} +for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];} +callback.call(elem);for(var name in options) +elem.style[name]=old[name];},css:function(elem,name,force,extra){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;if(extra==="border") +return;jQuery.each(which,function(){if(!extra) +val-=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;if(extra==="margin") +val+=parseFloat(jQuery.curCSS(elem,"margin"+this,true))||0;else +val-=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});} +if(elem.offsetWidth!==0) +getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,Math.round(val));} +return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;if(name=="opacity"&&!jQuery.support.opacity){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;} +if(name.match(/float/i)) +name=styleFloat;if(!force&&style&&style[name]) +ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i)) +name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle) +ret=computedStyle.getPropertyValue(name);if(name=="opacity"&&ret=="") +ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}} +return ret;},clean:function(elems,context,fragment){context=context||document;if(typeof context.createElement==="undefined") +context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;if(!fragment&&elems.length===1&&typeof elems[0]==="string"){var match=/^<(\w+)\s*\/?>$/.exec(elems[0]);if(match) +return[context.createElement(match[1])];} +var ret=[],scripts=[],div=context.createElement("div");jQuery.each(elems,function(i,elem){if(typeof elem==="number") +elem+='';if(!elem) +return;if(typeof elem==="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=elem.replace(/^\s+/,"").substring(0,10).toLowerCase();var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
      "]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||!jQuery.support.htmlSerialize&&[1,"div
      ","
      "]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--) +div=div.lastChild;if(!jQuery.support.tbody){var hasBody=/"&&!hasBody?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j) +if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length) +tbody[j].parentNode.removeChild(tbody[j]);} +if(!jQuery.support.leadingWhitespace&&/^\s/.test(elem)) +div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);elem=jQuery.makeArray(div.childNodes);} +if(elem.nodeType) +ret.push(elem);else +ret=jQuery.merge(ret,elem);});if(fragment){for(var i=0;ret[i];i++){if(jQuery.nodeName(ret[i],"script")&&(!ret[i].type||ret[i].type.toLowerCase()==="text/javascript")){scripts.push(ret[i].parentNode?ret[i].parentNode.removeChild(ret[i]):ret[i]);}else{if(ret[i].nodeType===1) +ret.splice.apply(ret,[i+1,0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))));fragment.appendChild(ret[i]);}} +return scripts;} +return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8) +return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&elem.parentNode) +elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode) +throw"type property can't be changed";elem[name]=value;} +if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name)) +return elem.getAttributeNode(name).nodeValue;if(name=="tabIndex"){var attributeNode=elem.getAttributeNode("tabIndex");return attributeNode&&attributeNode.specified?attributeNode.value:elem.nodeName.match(/(button|input|object|select|textarea)/i)?0:elem.nodeName.match(/^(a|area)$/i)&&elem.href?0:undefined;} +return elem[name];} +if(!jQuery.support.style&¬xml&&name=="style") +return jQuery.attr(elem.style,"cssText",value);if(set) +elem.setAttribute(name,""+value);var attr=!jQuery.support.hrefNormalized&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;} +if(!jQuery.support.opacity&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+ +(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");} +return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";} +name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set) +elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||typeof array==="string"||jQuery.isFunction(array)||array.setInterval) +ret[0]=array;else +while(i) +ret[--i]=array[i];} +return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i0?this.clone(true):this).get();jQuery.fn[original].apply(jQuery(insert[i]),elems);ret=ret.concat(elems);} +return this.pushStack(ret,name,selector);};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1) +this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames,state){if(typeof state!=="boolean") +state=!jQuery.className.has(this,classNames);jQuery.className[state?"add":"remove"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).length){jQuery("*",this).add([this]).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode) +this.parentNode.removeChild(this);}},empty:function(){jQuery(this).children().remove();while(this.firstChild) +this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;} +var expando="jQuery"+now(),uuid=0,windowData={};jQuery.extend({cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id) +id=elem[expando]=++uuid;if(name&&!jQuery.cache[id]) +jQuery.cache[id]={};if(data!==undefined) +jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id]) +break;if(!name) +jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute) +elem.removeAttribute(expando);} +delete jQuery.cache[id];}},queue:function(elem,type,data){if(elem){type=(type||"fx")+"queue";var q=jQuery.data(elem,type);if(!q||jQuery.isArray(data)) +q=jQuery.data(elem,type,jQuery.makeArray(data));else if(data) +q.push(data);} +return q;},dequeue:function(elem,type){var queue=jQuery.queue(elem,type),fn=queue.shift();if(!type||type==="fx") +fn=queue[0];if(fn!==undefined) +fn.call(elem);}});jQuery.fn.extend({data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length) +data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},queue:function(type,data){if(typeof type!=="string"){data=type;type="fx";} +if(data===undefined) +return jQuery.queue(this[0],type);return this.each(function(){var queue=jQuery.queue(this,type,data);if(type=="fx"&&queue.length==1) +queue[0].call(this);});},dequeue:function(type){return this.each(function(){jQuery.dequeue(this,type);});}});(function(){var chunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,done=0,toString=Object.prototype.toString;var Sizzle=function(selector,context,results,seed){results=results||[];context=context||document;if(context.nodeType!==1&&context.nodeType!==9) +return[];if(!selector||typeof selector!=="string"){return results;} +var parts=[],m,set,checkSet,check,mode,extra,prune=true;chunker.lastIndex=0;while((m=chunker.exec(selector))!==null){parts.push(m[1]);if(m[2]){extra=RegExp.rightContext;break;}} +if(parts.length>1&&origPOS.exec(selector)){if(parts.length===2&&Expr.relative[parts[0]]){set=posProcess(parts[0]+parts[1],context);}else{set=Expr.relative[parts[0]]?[context]:Sizzle(parts.shift(),context);while(parts.length){selector=parts.shift();if(Expr.relative[selector]) +selector+=parts.shift();set=posProcess(selector,set);}}}else{var ret=seed?{expr:parts.pop(),set:makeArray(seed)}:Sizzle.find(parts.pop(),parts.length===1&&context.parentNode?context.parentNode:context,isXML(context));set=Sizzle.filter(ret.expr,ret.set);if(parts.length>0){checkSet=makeArray(set);}else{prune=false;} +while(parts.length){var cur=parts.pop(),pop=cur;if(!Expr.relative[cur]){cur="";}else{pop=parts.pop();} +if(pop==null){pop=context;} +Expr.relative[cur](checkSet,pop,isXML(context));}} +if(!checkSet){checkSet=set;} +if(!checkSet){throw"Syntax error, unrecognized expression: "+(cur||selector);} +if(toString.call(checkSet)==="[object Array]"){if(!prune){results.push.apply(results,checkSet);}else if(context.nodeType===1){for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&(checkSet[i]===true||checkSet[i].nodeType===1&&contains(context,checkSet[i]))){results.push(set[i]);}}}else{for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&checkSet[i].nodeType===1){results.push(set[i]);}}}}else{makeArray(checkSet,results);} +if(extra){Sizzle(extra,context,results,seed);if(sortOrder){hasDuplicate=false;results.sort(sortOrder);if(hasDuplicate){for(var i=1;i":function(checkSet,part,isXML){var isPartStr=typeof part==="string";if(isPartStr&&!/\W/.test(part)){part=isXML?part:part.toUpperCase();for(var i=0,l=checkSet.length;i=0)){if(!inplace) +result.push(elem);}else if(inplace){curLoop[i]=false;}}} +return false;},ID:function(match){return match[1].replace(/\\/g,"");},TAG:function(match,curLoop){for(var i=0;curLoop[i]===false;i++){} +return curLoop[i]&&isXML(curLoop[i])?match[1]:match[1].toUpperCase();},CHILD:function(match){if(match[1]=="nth"){var test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2]=="even"&&"2n"||match[2]=="odd"&&"2n+1"||!/\D/.test(match[2])&&"0n+"+match[2]||match[2]);match[2]=(test[1]+(test[2]||1))-0;match[3]=test[3]-0;} +match[0]=done++;return match;},ATTR:function(match,curLoop,inplace,result,not,isXML){var name=match[1].replace(/\\/g,"");if(!isXML&&Expr.attrMap[name]){match[1]=Expr.attrMap[name];} +if(match[2]==="~="){match[4]=" "+match[4]+" ";} +return match;},PSEUDO:function(match,curLoop,inplace,result,not){if(match[1]==="not"){if(match[3].match(chunker).length>1||/^\w/.test(match[3])){match[3]=Sizzle(match[3],null,null,curLoop);}else{var ret=Sizzle.filter(match[3],curLoop,inplace,true^not);if(!inplace){result.push.apply(result,ret);} +return false;}}else if(Expr.match.POS.test(match[0])||Expr.match.CHILD.test(match[0])){return true;} +return match;},POS:function(match){match.unshift(true);return match;}},filters:{enabled:function(elem){return elem.disabled===false&&elem.type!=="hidden";},disabled:function(elem){return elem.disabled===true;},checked:function(elem){return elem.checked===true;},selected:function(elem){elem.parentNode.selectedIndex;return elem.selected===true;},parent:function(elem){return!!elem.firstChild;},empty:function(elem){return!elem.firstChild;},has:function(elem,i,match){return!!Sizzle(match[3],elem).length;},header:function(elem){return/h\d/i.test(elem.nodeName);},text:function(elem){return"text"===elem.type;},radio:function(elem){return"radio"===elem.type;},checkbox:function(elem){return"checkbox"===elem.type;},file:function(elem){return"file"===elem.type;},password:function(elem){return"password"===elem.type;},submit:function(elem){return"submit"===elem.type;},image:function(elem){return"image"===elem.type;},reset:function(elem){return"reset"===elem.type;},button:function(elem){return"button"===elem.type||elem.nodeName.toUpperCase()==="BUTTON";},input:function(elem){return/input|select|textarea|button/i.test(elem.nodeName);}},setFilters:{first:function(elem,i){return i===0;},last:function(elem,i,match,array){return i===array.length-1;},even:function(elem,i){return i%2===0;},odd:function(elem,i){return i%2===1;},lt:function(elem,i,match){return imatch[3]-0;},nth:function(elem,i,match){return match[3]-0==i;},eq:function(elem,i,match){return match[3]-0==i;}},filter:{PSEUDO:function(elem,match,i,array){var name=match[1],filter=Expr.filters[name];if(filter){return filter(elem,i,match,array);}else if(name==="contains"){return(elem.textContent||elem.innerText||"").indexOf(match[3])>=0;}else if(name==="not"){var not=match[3];for(var i=0,l=not.length;i=0);}}},ID:function(elem,match){return elem.nodeType===1&&elem.getAttribute("id")===match;},TAG:function(elem,match){return(match==="*"&&elem.nodeType===1)||elem.nodeName===match;},CLASS:function(elem,match){return(" "+(elem.className||elem.getAttribute("class"))+" ").indexOf(match)>-1;},ATTR:function(elem,match){var name=match[1],result=Expr.attrHandle[name]?Expr.attrHandle[name](elem):elem[name]!=null?elem[name]:elem.getAttribute(name),value=result+"",type=match[2],check=match[4];return result==null?type==="!=":type==="="?value===check:type==="*="?value.indexOf(check)>=0:type==="~="?(" "+value+" ").indexOf(check)>=0:!check?value&&result!==false:type==="!="?value!=check:type==="^="?value.indexOf(check)===0:type==="$="?value.substr(value.length-check.length)===check:type==="|="?value===check||value.substr(0,check.length+1)===check+"-":false;},POS:function(elem,match,i,array){var name=match[2],filter=Expr.setFilters[name];if(filter){return filter(elem,i,match,array);}}}};var origPOS=Expr.match.POS;for(var type in Expr.match){Expr.match[type]=RegExp(Expr.match[type].source+/(?![^\[]*\])(?![^\(]*\))/.source);} +var makeArray=function(array,results){array=Array.prototype.slice.call(array);if(results){results.push.apply(results,array);return results;} +return array;};try{Array.prototype.slice.call(document.documentElement.childNodes);}catch(e){makeArray=function(array,results){var ret=results||[];if(toString.call(array)==="[object Array]"){Array.prototype.push.apply(ret,array);}else{if(typeof array.length==="number"){for(var i=0,l=array.length;i";var root=document.documentElement;root.insertBefore(form,root.firstChild);if(!!document.getElementById(id)){Expr.find.ID=function(match,context,isXML){if(typeof context.getElementById!=="undefined"&&!isXML){var m=context.getElementById(match[1]);return m?m.id===match[1]||typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id").nodeValue===match[1]?[m]:undefined:[];}};Expr.filter.ID=function(elem,match){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return elem.nodeType===1&&node&&node.nodeValue===match;};} +root.removeChild(form);})();(function(){var div=document.createElement("div");div.appendChild(document.createComment(""));if(div.getElementsByTagName("*").length>0){Expr.find.TAG=function(match,context){var results=context.getElementsByTagName(match[1]);if(match[1]==="*"){var tmp=[];for(var i=0;results[i];i++){if(results[i].nodeType===1){tmp.push(results[i]);}} +results=tmp;} +return results;};} +div.innerHTML="";if(div.firstChild&&typeof div.firstChild.getAttribute!=="undefined"&&div.firstChild.getAttribute("href")!=="#"){Expr.attrHandle.href=function(elem){return elem.getAttribute("href",2);};}})();if(document.querySelectorAll)(function(){var oldSizzle=Sizzle,div=document.createElement("div");div.innerHTML="

      ";if(div.querySelectorAll&&div.querySelectorAll(".TEST").length===0){return;} +Sizzle=function(query,context,extra,seed){context=context||document;if(!seed&&context.nodeType===9&&!isXML(context)){try{return makeArray(context.querySelectorAll(query),extra);}catch(e){}} +return oldSizzle(query,context,extra,seed);};Sizzle.find=oldSizzle.find;Sizzle.filter=oldSizzle.filter;Sizzle.selectors=oldSizzle.selectors;Sizzle.matches=oldSizzle.matches;})();if(document.getElementsByClassName&&document.documentElement.getElementsByClassName)(function(){var div=document.createElement("div");div.innerHTML="
      ";if(div.getElementsByClassName("e").length===0) +return;div.lastChild.className="e";if(div.getElementsByClassName("e").length===1) +return;Expr.order.splice(1,0,"CLASS");Expr.find.CLASS=function(match,context,isXML){if(typeof context.getElementsByClassName!=="undefined"&&!isXML){return context.getElementsByClassName(match[1]);}};})();function dirNodeCheck(dir,cur,doneName,checkSet,nodeCheck,isXML){var sibDir=dir=="previousSibling"&&!isXML;for(var i=0,l=checkSet.length;i0){match=elem;break;}} +elem=elem[dir];} +checkSet[i]=match;}}} +var contains=document.compareDocumentPosition?function(a,b){return a.compareDocumentPosition(b)&16;}:function(a,b){return a!==b&&(a.contains?a.contains(b):true);};var isXML=function(elem){return elem.nodeType===9&&elem.documentElement.nodeName!=="HTML"||!!elem.ownerDocument&&isXML(elem.ownerDocument);};var posProcess=function(selector,context){var tmpSet=[],later="",match,root=context.nodeType?[context]:context;while((match=Expr.match.PSEUDO.exec(selector))){later+=match[0];selector=selector.replace(Expr.match.PSEUDO,"");} +selector=Expr.relative[selector]?selector+"*":selector;for(var i=0,l=root.length;i0||elem.offsetHeight>0;};Sizzle.selectors.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem;}).length;};jQuery.multiFilter=function(expr,elems,not){if(not){expr=":not("+expr+")";} +return Sizzle.matches(expr,elems);};jQuery.dir=function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1) +matched.push(cur);cur=cur[dir];} +return matched;};jQuery.nth=function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir]) +if(cur.nodeType==1&&++num==result) +break;return cur;};jQuery.sibling=function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem) +r.push(n);} +return r;};return;window.Sizzle=Sizzle;})();jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8) +return;if(elem.setInterval&&elem!=window) +elem=window;if(!handler.guid) +handler.guid=this.guid++;if(data!==undefined){var fn=handler;handler=this.proxy(fn);handler.data=data;} +var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){return typeof jQuery!=="undefined"&&!jQuery.event.triggered?jQuery.event.handle.apply(arguments.callee.elem,arguments):undefined;});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();handler.type=namespaces.slice().sort().join(".");var handlers=events[type];if(jQuery.event.specialAll[type]) +jQuery.event.specialAll[type].setup.call(elem,data,namespaces);if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem,data,namespaces)===false){if(elem.addEventListener) +elem.addEventListener(type,handle,false);else if(elem.attachEvent) +elem.attachEvent("on"+type,handle);}} +handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8) +return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types===undefined||(typeof types==="string"&&types.charAt(0)==".")) +for(var type in events) +this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;} +jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");if(events[type]){if(handler) +delete events[type][handler.guid];else +for(var handle in events[type]) +if(namespace.test(events[type][handle].type)) +delete events[type][handle];if(jQuery.event.specialAll[type]) +jQuery.event.specialAll[type].teardown.call(elem,namespaces);for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem,namespaces)===false){if(elem.removeEventListener) +elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent) +elem.detachEvent("on"+type,jQuery.data(elem,"handle"));} +ret=null;delete events[type];}}});} +for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(event,data,elem,bubbling){var type=event.type||event;if(!bubbling){event=typeof event==="object"?event[expando]?event:jQuery.extend(jQuery.Event(type),event):jQuery.Event(type);if(type.indexOf("!")>=0){event.type=type=type.slice(0,-1);event.exclusive=true;} +if(!elem){event.stopPropagation();if(this.global[type]) +jQuery.each(jQuery.cache,function(){if(this.events&&this.events[type]) +jQuery.event.trigger(event,data,this.handle.elem);});} +if(!elem||elem.nodeType==3||elem.nodeType==8) +return undefined;event.result=undefined;event.target=elem;data=jQuery.makeArray(data);data.unshift(event);} +event.currentTarget=elem;var handle=jQuery.data(elem,"handle");if(handle) +handle.apply(elem,data);if((!elem[type]||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false) +event.result=false;if(!bubbling&&elem[type]&&!event.isDefaultPrevented()&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}} +this.triggered=false;if(!event.isPropagationStopped()){var parent=elem.parentNode||elem.ownerDocument;if(parent) +jQuery.event.trigger(event,data,parent,true);}},handle:function(event){var all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);event.currentTarget=this;var namespaces=event.type.split(".");event.type=namespaces.shift();all=!namespaces.length&&!event.exclusive;var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||namespace.test(handler.type)){event.handler=handler;event.data=handler.data;var ret=handler.apply(this,arguments);if(ret!==undefined){event.result=ret;if(ret===false){event.preventDefault();event.stopPropagation();}} +if(event.isImmediatePropagationStopped()) +break;}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(event){if(event[expando]) +return event;var originalEvent=event;event=jQuery.Event(originalEvent);for(var i=this.props.length,prop;i;){prop=this.props[--i];event[prop]=originalEvent[prop];} +if(!event.target) +event.target=event.srcElement||document;if(event.target.nodeType==3) +event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement) +event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);} +if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode)) +event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey) +event.metaKey=event.ctrlKey;if(!event.which&&event.button) +event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy=proxy||function(){return fn.apply(this,arguments);};proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:bindReady,teardown:function(){}}},specialAll:{live:{setup:function(selector,namespaces){jQuery.event.add(this,namespaces[0],liveHandler);},teardown:function(namespaces){if(namespaces.length){var remove=0,name=RegExp("(^|\\.)"+namespaces[0]+"(\\.|$)");jQuery.each((jQuery.data(this,"events").live||{}),function(){if(name.test(this.type)) +remove++;});if(remove<1) +jQuery.event.remove(this,namespaces[0],liveHandler);}}}}};jQuery.Event=function(src){if(!this.preventDefault) +return new jQuery.Event(src);if(src&&src.type){this.originalEvent=src;this.type=src.type;}else +this.type=src;this.timeStamp=now();this[expando]=true;};function returnFalse(){return false;} +function returnTrue(){return true;} +jQuery.Event.prototype={preventDefault:function(){this.isDefaultPrevented=returnTrue;var e=this.originalEvent;if(!e) +return;if(e.preventDefault) +e.preventDefault();e.returnValue=false;},stopPropagation:function(){this.isPropagationStopped=returnTrue;var e=this.originalEvent;if(!e) +return;if(e.stopPropagation) +e.stopPropagation();e.cancelBubble=true;},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=returnTrue;this.stopPropagation();},isDefaultPrevented:returnFalse,isPropagationStopped:returnFalse,isImmediatePropagationStopped:returnFalse};var withinElement=function(event){var parent=event.relatedTarget;while(parent&&parent!=this) +try{parent=parent.parentNode;} +catch(e){parent=this;} +if(parent!=this){event.type=event.data;jQuery.event.handle.apply(this,arguments);}};jQuery.each({mouseover:'mouseenter',mouseout:'mouseleave'},function(orig,fix){jQuery.event.special[fix]={setup:function(){jQuery.event.add(this,orig,withinElement,fix);},teardown:function(){jQuery.event.remove(this,orig,withinElement);}};});jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data){return this.each(function(){jQuery.event.trigger(type,data,this);});},triggerHandler:function(type,data){if(this[0]){var event=jQuery.Event(type);event.preventDefault();event.stopPropagation();jQuery.event.trigger(event,data,this[0]);return event.result;}},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);} +var type="GET";if(params) +if(jQuery.isFunction(params)){callback=params;params=null;}else if(typeof params==="object"){params=jQuery.param(params);type="POST";} +var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified") +self.html(selector?jQuery("
      ").append(res.responseText.replace(//g,"")).find(selector):res.responseText);if(callback) +self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return this.elements?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;} +return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};} +return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!=="string") +s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre)) +s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre)) +s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";} +if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data) +s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){} +if(head) +head.removeChild(script);};} +if(s.dataType=="script"&&s.cache==null) +s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");} +if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;} +if(s.global&&!jQuery.active++) +jQuery.event.trigger("ajaxStart");var parts=/^(\w+:)?\/\/([^\/?#]+)/.exec(s.url);if(s.dataType=="script"&&type=="GET"&&parts&&(parts[1]&&parts[1]!=location.protocol||parts[2]!=location.host)){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset) +script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();script.onload=script.onreadystatechange=null;head.removeChild(script);}};} +head.appendChild(script);return undefined;} +var requestDone=false;var xhr=s.xhr();if(s.username) +xhr.open(type,s.url,s.async,s.username,s.password);else +xhr.open(type,s.url,s.async);try{if(s.data) +xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified) +xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){} +if(s.beforeSend&&s.beforeSend(xhr,s)===false){if(s.global&&!--jQuery.active) +jQuery.event.trigger("ajaxStop");xhr.abort();return false;} +if(s.global) +jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(xhr.readyState==0){if(ival){clearInterval(ival);ival=null;if(s.global&&!--jQuery.active) +jQuery.event.trigger("ajaxStop");}}else if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;} +status=isTimeout=="timeout"?"timeout":!jQuery.httpSuccess(xhr)?"error":s.ifModified&&jQuery.httpNotModified(xhr,s.url)?"notmodified":"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s);}catch(e){status="parsererror";}} +if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){} +if(s.ifModified&&modRes) +jQuery.lastModified[s.url]=modRes;if(!jsonp) +success();}else +jQuery.handleError(s,xhr,status);complete();if(isTimeout) +xhr.abort();if(s.async) +xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0) +setTimeout(function(){if(xhr&&!requestDone) +onreadystatechange("timeout");},s.timeout);} +try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);} +if(!s.async) +onreadystatechange();function success(){if(s.success) +s.success(data,status);if(s.global) +jQuery.event.trigger("ajaxSuccess",[xhr,s]);} +function complete(){if(s.complete) +s.complete(xhr,status);if(s.global) +jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active) +jQuery.event.trigger("ajaxStop");} +return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global) +jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223;}catch(e){} +return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url];}catch(e){} +return false;},httpData:function(xhr,type,s){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror") +throw"parsererror";if(s&&s.dataFilter) +data=s.dataFilter(data,type);if(typeof data==="string"){if(type=="script") +jQuery.globalEval(data);if(type=="json") +data=window["eval"]("("+data+")");} +return data;},param:function(a){var s=[];function add(key,value){s[s.length]=encodeURIComponent(key)+'='+encodeURIComponent(value);};if(jQuery.isArray(a)||a.jquery) +jQuery.each(a,function(){add(this.name,this.value);});else +for(var j in a) +if(jQuery.isArray(a[j])) +jQuery.each(a[j],function(){add(j,this);});else +add(j,jQuery.isFunction(a[j])?a[j]():a[j]);return s.join("&").replace(/%20/g,"+");}});var elemdisplay={},timerId,fxAttrs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function genFx(type,num){var obj={};jQuery.each(fxAttrs.concat.apply([],fxAttrs.slice(0,num)),function(){obj[this]=type;});return obj;} +jQuery.fn.extend({show:function(speed,callback){if(speed){return this.animate(genFx("show",3),speed,callback);}else{for(var i=0,l=this.length;i").appendTo("body");display=elem.css("display");if(display==="none") +display="block";elem.remove();elemdisplay[tagName]=display;} +jQuery.data(this[i],"olddisplay",display);}} +for(var i=0,l=this.length;i=0;i--) +if(timers[i].elem==this){if(gotoEnd) +timers[i](true);timers.splice(i,1);}});if(!gotoEnd) +this.dequeue();return this;}});jQuery.each({slideDown:genFx("show",1),slideUp:genFx("hide",1),slideToggle:genFx("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(name,props){jQuery.fn[name]=function(speed,callback){return this.animate(props,speed,callback);};});jQuery.extend({speed:function(speed,easing,fn){var opt=typeof speed==="object"?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&!jQuery.isFunction(easing)&&easing};opt.duration=jQuery.fx.off?0:typeof opt.duration==="number"?opt.duration:jQuery.fx.speeds[opt.duration]||jQuery.fx.speeds._default;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false) +jQuery(this).dequeue();if(jQuery.isFunction(opt.old)) +opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig) +options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step) +this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style) +this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)) +return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;var self=this;function t(gotoEnd){return self.step(gotoEnd);} +t.elem=this.elem;if(t()&&jQuery.timers.push(t)&&!timerId){timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim) +if(this.options.curAnim[i]!==true) +done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none") +this.elem.style.display="block";} +if(this.options.hide) +jQuery(this.elem).hide();if(this.options.hide||this.options.show) +for(var p in this.options.curAnim) +jQuery.attr(this.elem.style,p,this.options.orig[p]);this.options.complete.call(this.elem);} +return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();} +return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){if(fx.elem.style&&fx.elem.style[fx.prop]!=null) +fx.elem.style[fx.prop]=fx.now+fx.unit;else +fx.elem[fx.prop]=fx.now;}}});if(document.documentElement["getBoundingClientRect"]) +jQuery.fn.offset=function(){if(!this[0])return{top:0,left:0};if(this[0]===this[0].ownerDocument.body)return jQuery.offset.bodyOffset(this[0]);var box=this[0].getBoundingClientRect(),doc=this[0].ownerDocument,body=doc.body,docElem=doc.documentElement,clientTop=docElem.clientTop||body.clientTop||0,clientLeft=docElem.clientLeft||body.clientLeft||0,top=box.top+(self.pageYOffset||jQuery.boxModel&&docElem.scrollTop||body.scrollTop)-clientTop,left=box.left+(self.pageXOffset||jQuery.boxModel&&docElem.scrollLeft||body.scrollLeft)-clientLeft;return{top:top,left:left};};else +jQuery.fn.offset=function(){if(!this[0])return{top:0,left:0};if(this[0]===this[0].ownerDocument.body)return jQuery.offset.bodyOffset(this[0]);jQuery.offset.initialized||jQuery.offset.initialize();var elem=this[0],offsetParent=elem.offsetParent,prevOffsetParent=elem,doc=elem.ownerDocument,computedStyle,docElem=doc.documentElement,body=doc.body,defaultView=doc.defaultView,prevComputedStyle=defaultView.getComputedStyle(elem,null),top=elem.offsetTop,left=elem.offsetLeft;while((elem=elem.parentNode)&&elem!==body&&elem!==docElem){computedStyle=defaultView.getComputedStyle(elem,null);top-=elem.scrollTop,left-=elem.scrollLeft;if(elem===offsetParent){top+=elem.offsetTop,left+=elem.offsetLeft;if(jQuery.offset.doesNotAddBorder&&!(jQuery.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(elem.tagName))) +top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0;prevOffsetParent=offsetParent,offsetParent=elem.offsetParent;} +if(jQuery.offset.subtractsBorderForOverflowNotVisible&&computedStyle.overflow!=="visible") +top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0;prevComputedStyle=computedStyle;} +if(prevComputedStyle.position==="relative"||prevComputedStyle.position==="static") +top+=body.offsetTop,left+=body.offsetLeft;if(prevComputedStyle.position==="fixed") +top+=Math.max(docElem.scrollTop,body.scrollTop),left+=Math.max(docElem.scrollLeft,body.scrollLeft);return{top:top,left:left};};jQuery.offset={initialize:function(){if(this.initialized)return;var body=document.body,container=document.createElement('div'),innerDiv,checkDiv,table,td,rules,prop,bodyMarginTop=body.style.marginTop,html='
      ';rules={position:'absolute',top:0,left:0,margin:0,border:0,width:'1px',height:'1px',visibility:'hidden'};for(prop in rules)container.style[prop]=rules[prop];container.innerHTML=html;body.insertBefore(container,body.firstChild);innerDiv=container.firstChild,checkDiv=innerDiv.firstChild,td=innerDiv.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(checkDiv.offsetTop!==5);this.doesAddBorderForTableAndCells=(td.offsetTop===5);innerDiv.style.overflow='hidden',innerDiv.style.position='relative';this.subtractsBorderForOverflowNotVisible=(checkDiv.offsetTop===-5);body.style.marginTop='1px';this.doesNotIncludeMarginInBodyOffset=(body.offsetTop===0);body.style.marginTop=bodyMarginTop;body.removeChild(container);this.initialized=true;},bodyOffset:function(body){jQuery.offset.initialized||jQuery.offset.initialize();var top=body.offsetTop,left=body.offsetLeft;if(jQuery.offset.doesNotIncludeMarginInBodyOffset) +top+=parseInt(jQuery.curCSS(body,'marginTop',true),10)||0,left+=parseInt(jQuery.curCSS(body,'marginLeft',true),10)||0;return{top:top,left:left};}};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};} +return results;},offsetParent:function(){var offsetParent=this[0].offsetParent||document.body;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static')) +offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return null;return val!==undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom",lower=name.toLowerCase();jQuery.fn["inner"+name]=function(){return this[0]?jQuery.css(this[0],lower,false,"padding"):null;};jQuery.fn["outer"+name]=function(margin){return this[0]?jQuery.css(this[0],lower,false,margin?"margin":"border"):null;};var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(document.documentElement["client"+name],document.body["scroll"+name],document.documentElement["scroll"+name],document.body["offset"+name],document.documentElement["offset"+name]):size===undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,typeof size==="string"?size:size+"px");};});})(); \ No newline at end of file diff --git a/themes/default/js/post.js b/themes/default/js/post.js new file mode 100644 index 0000000..dd1ddde --- /dev/null +++ b/themes/default/js/post.js @@ -0,0 +1,7 @@ + +$(function(){$('#comment-form p:has(input[type=submit][name=preview])').before('

      '+''+'

      ');var cookie=readCookie($.cookie('comment_info'));if(cookie!=false){$('#c_name').val(cookie[0]);$('#c_mail').val(cookie[1]);$('#c_site').val(cookie[2]);$('#c_remember').attr('checked','checked');} +$('#c_remember').click(function(){if(this.checked){setCookie();}else{dropCookie();}});$('#c_name').change(function(){if($('#c_remember').get(0).checked){setCookie();}});$('#c_mail').change(function(){if($('#c_remember').get(0).checked){setCookie();}});$('#c_site').change(function(){if($('#c_remember').get(0).checked){setCookie();}});function setCookie(){var name=$('#c_name').val();var mail=$('#c_mail').val();var site=$('#c_site').val();$.cookie('comment_info',name+'\n'+mail+'\n'+site,{expires:60,path:'/'});} +function dropCookie(){$.cookie('comment_info','',{expires:-30,path:'/'});} +function readCookie(c){if(!c){return false;} +var s=c.split('\n');if(s.length!=3){dropCookie();return false;} +return s;}}); \ No newline at end of file diff --git a/themes/default/print.css b/themes/default/print.css new file mode 100644 index 0000000..d6a8624 --- /dev/null +++ b/themes/default/print.css @@ -0,0 +1,50 @@ +/* +# -- BEGIN LICENSE BLOCK ---------------------------------- +# +# This file is part of Dotclear 2. +# +# Copyright (c) 2003-2010 Olivier Meunier and contributors +# Licensed under the GPL version 2.0 license. +# See LICENSE file or +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# +# -- END LICENSE BLOCK ------------------------------------ +*/ +body { + font : 10pt serif; + margin: 0; + color: #000; + background: #fff; +} + +#prelude, #sidebar, .pagination, #comment-form, object { + 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; +} \ No newline at end of file diff --git a/themes/default/screenshot.jpg b/themes/default/screenshot.jpg new file mode 100644 index 0000000..6724ac6 Binary files /dev/null and b/themes/default/screenshot.jpg differ diff --git a/themes/default/smilies/alien.png b/themes/default/smilies/alien.png new file mode 100644 index 0000000..34df41b Binary files /dev/null and b/themes/default/smilies/alien.png differ diff --git a/themes/default/smilies/angry.png b/themes/default/smilies/angry.png new file mode 100644 index 0000000..5adba59 Binary files /dev/null and b/themes/default/smilies/angry.png differ diff --git a/themes/default/smilies/arrow.png b/themes/default/smilies/arrow.png new file mode 100644 index 0000000..73570ca Binary files /dev/null and b/themes/default/smilies/arrow.png differ diff --git a/themes/default/smilies/confused.png b/themes/default/smilies/confused.png new file mode 100644 index 0000000..5c05d74 Binary files /dev/null and b/themes/default/smilies/confused.png differ diff --git a/themes/default/smilies/cool.png b/themes/default/smilies/cool.png new file mode 100644 index 0000000..dc573f1 Binary files /dev/null and b/themes/default/smilies/cool.png differ diff --git a/themes/default/smilies/cry.png b/themes/default/smilies/cry.png new file mode 100644 index 0000000..61f6453 Binary files /dev/null and b/themes/default/smilies/cry.png differ diff --git a/themes/default/smilies/dizzy.png b/themes/default/smilies/dizzy.png new file mode 100644 index 0000000..373ad9d Binary files /dev/null and b/themes/default/smilies/dizzy.png differ diff --git a/themes/default/smilies/eek.png b/themes/default/smilies/eek.png new file mode 100644 index 0000000..d4c2926 Binary files /dev/null and b/themes/default/smilies/eek.png differ diff --git a/themes/default/smilies/evil.png b/themes/default/smilies/evil.png new file mode 100644 index 0000000..54fbd06 Binary files /dev/null and b/themes/default/smilies/evil.png differ diff --git a/themes/default/smilies/exclam.png b/themes/default/smilies/exclam.png new file mode 100644 index 0000000..0d85b30 Binary files /dev/null and b/themes/default/smilies/exclam.png differ diff --git a/themes/default/smilies/idea.png b/themes/default/smilies/idea.png new file mode 100644 index 0000000..dea2402 Binary files /dev/null and b/themes/default/smilies/idea.png differ diff --git a/themes/default/smilies/laugh.png b/themes/default/smilies/laugh.png new file mode 100644 index 0000000..174b82a Binary files /dev/null and b/themes/default/smilies/laugh.png differ diff --git a/themes/default/smilies/lol.png b/themes/default/smilies/lol.png new file mode 100644 index 0000000..b28c7ac Binary files /dev/null and b/themes/default/smilies/lol.png differ diff --git a/themes/default/smilies/mrgreen.png b/themes/default/smilies/mrgreen.png new file mode 100644 index 0000000..d8fe18a Binary files /dev/null and b/themes/default/smilies/mrgreen.png differ diff --git a/themes/default/smilies/normal.png b/themes/default/smilies/normal.png new file mode 100644 index 0000000..d8d7832 Binary files /dev/null and b/themes/default/smilies/normal.png differ diff --git a/themes/default/smilies/question.png b/themes/default/smilies/question.png new file mode 100644 index 0000000..700d399 Binary files /dev/null and b/themes/default/smilies/question.png differ diff --git a/themes/default/smilies/razz.png b/themes/default/smilies/razz.png new file mode 100644 index 0000000..bb74936 Binary files /dev/null and b/themes/default/smilies/razz.png differ diff --git a/themes/default/smilies/redface.png b/themes/default/smilies/redface.png new file mode 100644 index 0000000..c7040b8 Binary files /dev/null and b/themes/default/smilies/redface.png differ diff --git a/themes/default/smilies/rolleyes.png b/themes/default/smilies/rolleyes.png new file mode 100644 index 0000000..c71f0f9 Binary files /dev/null and b/themes/default/smilies/rolleyes.png differ diff --git a/themes/default/smilies/sad.png b/themes/default/smilies/sad.png new file mode 100644 index 0000000..de83faf Binary files /dev/null and b/themes/default/smilies/sad.png differ diff --git a/themes/default/smilies/smile.png b/themes/default/smilies/smile.png new file mode 100644 index 0000000..c67937d Binary files /dev/null and b/themes/default/smilies/smile.png differ diff --git a/themes/default/smilies/smilies.txt b/themes/default/smilies/smilies.txt new file mode 100644 index 0000000..a838a96 --- /dev/null +++ b/themes/default/smilies/smilies.txt @@ -0,0 +1,17 @@ +:-) smile.png +:) smile.png +;-) wink.png +;) wink.png +:-/ confused.png +:-| normal.png +LOL lol.png +:-D laugh.png +:( sad.png +:-( sad.png +:-C angry.png +8-) cool.png +:-o surprised.png +:-O surprised.png +;-( cry.png +;(' cry.png +8-O eek.png \ No newline at end of file diff --git a/themes/default/smilies/surprised.png b/themes/default/smilies/surprised.png new file mode 100644 index 0000000..b2e04a5 Binary files /dev/null and b/themes/default/smilies/surprised.png differ diff --git a/themes/default/smilies/wink.png b/themes/default/smilies/wink.png new file mode 100644 index 0000000..684c84b Binary files /dev/null and b/themes/default/smilies/wink.png differ diff --git a/themes/default/style.css b/themes/default/style.css new file mode 100644 index 0000000..1f27a36 --- /dev/null +++ b/themes/default/style.css @@ -0,0 +1,576 @@ +/* +# -- BEGIN LICENSE BLOCK ---------------------------------- +# +# This file is part of Dotclear 2. +# +# Copyright (c) 2003-2010 Olivier Meunier and contributors +# Licensed under the GPL version 2.0 license. +# See LICENSE file or +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# +# -- END LICENSE BLOCK ------------------------------------ +*/ +body { + margin: 0; + padding: 0; + background: #e0e0e0 url(img/body-bg.png) repeat-x top; + color: #6b6b6b; + font: 62.5% "Lucida Grande",Arial,sans-serif; +} + +a { + color: #279ac4; + text-decoration: none; +} +a:hover, a:active, a:focus { + color:#6d8824; + text-decoration: underline; +} + +a img { + border: none; +} + +pre { + white-space: pre; + white-space: -moz-pre-wrap; + white-space: -hp-pre-wrap; + white-space: -o-pre-wrap; + white-space: -pre-wrap; + white-space: pre-wrap; + white-space: pre-line; + word-wrap: break-word; +} + +sup { + font-size:80%; + line-height:50%; +} + +.left { + float: left; +} + +.right { + float: right; +} + +/* Layout +-------------------------------------------------------- */ +#page { + font-size: 1.2em; +} + +#top { + position: relative; + height: 180px; + width: 800px; + padding: 0; + margin: 50px auto 0 auto; + background: transparent url(img/page-t.png) no-repeat bottom left; +} +#top h1 { + position: absolute; + top: 25px; + width: 100%; + margin: 0; + text-align: center; + font-size: 3.5em; +} +#top h1 a { + color: #9ac528; + text-decoration: none; +} + +#prelude { + position: absolute; + top: 7px; + right: 7px; + margin: 0; + padding: 0; + color: #ededed; +} +#prelude a { + color: #ededed; +} +#prelude a:focus, #prelude a:hover { + background: yellow; + color: red; + text-decoration: none; +} + +#wrapper { + width: 800px; + overflow: hidden; + margin: 0 auto 0 auto; + padding: 20px 0; + background: #fff url(img/page-bg.png) repeat-y top left; +} + +#main { + width: 550px; + float: left; +} + +#content { + padding: 0 25px; +} + +#sidebar { + width: 250px; + float: right; +} +#blognav, #blogextra { + padding: 1px 25px; +} + +#footer { + clear: both; + padding: 30px 0 0 0; + background: transparent url(img/page-b.png) no-repeat top center; +} +#footer p { + margin: 0 auto; + padding: 10px 0 20px; + background: #e0e0e0; + color: #9ac528; + text-align: center; + font-size: 1.2em; +} +#footer p a { + color: #6d8824; +} + + +/* Sidebar styles +-------------------------------------------------------- */ +#sidebar h2 { + margin: 0; + color: #8fb22f; +} +#sidebar h3 { + margin: 1em 0 0.5em; + color: #279ac4; +} + +#sidebar div div { + margin-bottom: 2em; +} +#sidebar div div div { + margin-bottom: 0; +} + +#sidebar ul { + list-style: none; + margin: 1em 0; + padding: 0; + border-top: 1px solid #ffd02c; +} +#sidebar li { + display: block; + margin: 0; + padding: 4px 0; + border-bottom: 1px solid #FFD02C; +} +#sidebar li a { + color: #6B6B6B; + text-decoration: none; +} +#sidebar li a:hover, #sidebar li a:focus, #sidebar li a:active { + color: #9ac528; +} +#sidebar ul ul { + list-style: disc; + margin: 0; + padding: 0 0 0 15px; + border: none; +} +#sidebar li li { + display: list-item; + margin: 0.5em 0 0 0; + padding: 0 0; + border: none; +} + + +#search fieldset { + border: none; + margin: 0; + padding: 0; +} +#q { + border: 1px solid #bfbfbf; + width: 150px; + color: #6b6b6b; +} +#search .submit { + padding: 0; + border: 1px solid #fff; + background: none; + font-weight: bold; + color: #6b6b6b; + text-transform: uppercase; +} +#search .submit:hover { + background: #9ac528; + color: #fff; + border: 1px solid #9ac528; +} + +#topnav ul { + border-bottom: 1px solid #ffd02c; + padding: 4px 0; +} +#topnav li { + display: inline; + border-bottom: none; +} + +#sidebar .syndicate ul { + border-top: none; +} +#sidebar .syndicate li { + border-bottom: none; +} + +#sidebar .categories ul { + list-style: disc; + margin: 0; + padding: 0 0 0 15px; + border: none; +} +#sidebar .categories li { + display: list-item; + margin: 0.5em 0 0 0; + padding: 0 0; + border: none; +} +#sidebar .categories li.category-current { + font-weight: bold; +} +#sidebar .categories li.category-current li { + font-weight: normal; +} + +#sidebar .tags ul { + border-top: none; + font-size: 1.1em; + text-align: justify; +} +#sidebar .tags ul li { + display: inline; + background: none; + margin: 0; + padding: 0; + border: none; + line-height: 1.8em; +} + +.tag0 { font-size: 75%; } +.tag10 { font-size: 80%; } +.tag20 { font-size: 90%; } +.tag30 { font-size: 100%; } +.tag40 { font-size: 110%; } +.tag50 { font-size: 120%; } +.tag60 { font-size: 140%; } +.tag70 { font-size: 150%; } +.tag80 { font-size: 160%; } +.tag90 { font-size: 170%; } +.tag100 { font-size: 180%; } + +ul.tags { + list-style: none; + margin: 1em 0; + padding: 0; + font-size: 1.3em; + text-align: justify; +} +ul.tags li { + display: inline; + margin: 0; + padding: 0; + line-height: 1.8em; +} + + +/* Main content styles +-------------------------------------------------------- */ +h2, h3, h4 { + margin: 1em 0 0 0; + padding: 0; +} + +h2 { + font-size: 1.4em; +} +h3 { + font-size: 1.2em; +} +h4 { + font-size: 1em; +} + +#subcategories { + background: #fbfbfb; + border-color: #BFBFBF; + border-style: solid; + border-width: 1px 0; + margin: 1em 0; + padding: 0.5em; +} +#subcategories h3 { + margin: 0; +} +#subcategories ul { + margin: 0.5em 0 0 0; + padding: 0 0 0 20px; + line-height: 1.4; +} + +.day-date { + font-size: 1em; + margin: 0 0 0.5em; + text-align: right; + color: #279ac4; +} + +.post { + margin: 0 0 2em 0; +} +.post-title { + color:#9ac528; + font-size:1.7em; +} +.post-title a { + color:#9ac528; +} +.post-info { + margin: 0 0 0 0; +} + +.post-tags { + list-style: none; + margin: 1em 0 1em; + padding: 3px 0; +} +.post-tags li { + display: inline; + margin-right: 8px; + padding: 5px 0 5px 22px; + background: transparent url(img/tag.png) no-repeat 0 70%; +} +.post-tags li a { + color: #6b6b6b; +} + +.post-info-co { + clear: left; + padding: 3px 0 3px 0.5em; + border-width: 1px 0; + border-style: solid; + border-color: #bfbfbf; + background: #fbfbfb; +} +.post-info-co a { + color: #2b2b2b; + padding: 2px 10px 2px 18px; +} + +.comment_count { + background: transparent url(img/comment.png) no-repeat 0 50%; +} +.ping_count { + background: transparent url(img/trackback.png) no-repeat 0 50%; +} +.attach_count { + background: transparent url(img/attach.png) no-repeat 0 50%; +} + +.read-it { + font-weight: bold; + clear: left; +} + +a.feed { + background: transparent url(img/feed.png) no-repeat 0 0.25em; + padding: 5px 0 5px 22px; +} + +/* Post content +-------------------------------------------------------- */ +.post-content, .post-excerpt, #comments dd, #pings dd, dd.comment-preview { + line-height:1.4em; +} +.post-content acronym, .post-excerpt acronym { + cursor: help; + border-bottom: 1px dotted #666; +} + +.post-content pre, .post-excerpt pre { + padding: 10px ; + font: 1.1em 'courier new', courier, monospace; +} + +.post-content ul, .post-excerpt ul, .post-content ol, .post-excerpt ol { + margin: 0 0 0.5em 0; + padding: 0 0 0 15px; +} + +.post-content li, .post-excerpt li { + margin: 0; + padding: 0; +} + +.post-content blockquote, .post-excerpt blockquote { + margin: 4px 0 4px 0; + padding: 0 5px; + border-left: 4px solid #bfbfbf; +} + + +#attachments h3 { + font-size: 1.2em; +} + +#attachments ul { + list-style: none; + margin: 0; + padding: 4px 0; +} + +#attachments li { + margin: 0 0 0.6em; + padding: 2px 10px 2px 18px; + background: transparent url(img/attach.png) no-repeat 0 50%; +} + +#attachments li object { + display: inline; + margin: 0; + padding: 0; + vertical-align: bottom; +} + +/* Comments and trackbacks +-------------------------------------------------------- */ +#comments { + clear: both; +} +#comments h3, #comment-form h3, #pings h3 { + border-width: 1px 0; + border-style: solid; + border-color: #bfbfbf; + background: #fbfbfb; + padding: 5px; + margin: 20px 0 8px 0; + font-size: 1.2em; +} + +#comments dt { + margin: 1.5em 0 0 0; + padding: 5px 0 16px 0; + background: transparent url(img/comment-t.png) no-repeat bottom left; +} +#comments dt.me { + background-image: url(img/commentmy-t.png); +} +#comments a.comment-number { + display: block; + float: left; + width: 30px; + margin-right: 40px; + font-size: 1.2em; +} +#comments dd { + margin: 0; + padding: 1px 1em 0.5em 1em; + background: #fffad1 url(img/comment-b.png) no-repeat bottom left; + color:#6b6b6b; +} +#comments dd.me { + background-color:#f5f9d9; + color:#6b6b6b; + background-image:url(img/commentmy-b.png); +} +#comments dd p { + margin: 0.5em 0; + line-height: 1.5em; +} + +.error { + margin: 20px 0 0; + padding: 10px 5px; + background: #ffcccc; + border: 2px solid red; + font-weight: bold; +} + +/* Forms +-------------------------------------------------------- */ +#comment-form { + padding-top: 10px; +} + +#comment-form fieldset { + border: none; +} +#comment-form fieldset p { + padding: 5px 0; +} +#comment-form fieldset p.field { + padding: 5px 0; + width: auto; + margin: 0; + clear: left; +} +#comment-form p.form-help { + width: 60%; + margin: 0 0 0 30%; + font-style: italic; +} + +#comment-form p label { + width: auto; +} +#comment-form p.field label { + font-weight: bold; + display: block; + padding: 0 1% 0 0; + width: 29%; + float: left; + text-align: right; +} +#comment-form input, #comment-form textarea { + font: 1em "Lucida Grande",Arial,sans-serif; + color: #279ac4; + border: 1px solid #cdcdcd; + padding: 1px 2px; + width: 68%; +} + +#comment-form p.remember { + margin: 0; +} +#comment-form input#c_remember { + width: auto; + border: 0; + margin: 0 5px 0 30%; +} +#comment-form input.preview { + margin-left: 30%; +} + +#comment-form input.preview, #comment-form input.submit { + width: auto; + color: #279ac4; + background: #fff; + font-size: 1em; + font-weight: bold; + text-transform: uppercase; + border: 1px solid #fff; +} +#comment-form input.preview:hover, #comment-form input.submit:hover { + background: #6d8824; + color: #fff; + border: 1px solid #6d8824; +} diff --git a/themes/default/tpl/.htaccess b/themes/default/tpl/.htaccess new file mode 100644 index 0000000..3a42882 --- /dev/null +++ b/themes/default/tpl/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/themes/default/tpl/404.html b/themes/default/tpl/404.html new file mode 100644 index 0000000..7dbf306 --- /dev/null +++ b/themes/default/tpl/404.html @@ -0,0 +1,63 @@ + + + + + + + + {{tpl:lang Document not found}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:lang Document not found}}

      +
      + +
      +

      {{tpl:lang The document you are looking for does not exist.}}

      +
      + +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/_flv_player.html b/themes/default/tpl/_flv_player.html new file mode 100644 index 0000000..bb34328 --- /dev/null +++ b/themes/default/tpl/_flv_player.html @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/themes/default/tpl/_footer.html b/themes/default/tpl/_footer.html new file mode 100644 index 0000000..fac8db1 --- /dev/null +++ b/themes/default/tpl/_footer.html @@ -0,0 +1,5 @@ + + +{{tpl:SysBehavior behavior="publicFooterContent"}} \ No newline at end of file diff --git a/themes/default/tpl/_head.html b/themes/default/tpl/_head.html new file mode 100644 index 0000000..a79816f --- /dev/null +++ b/themes/default/tpl/_head.html @@ -0,0 +1,12 @@ + + + + + + +{{tpl:include src="user_head.html"}} +{{tpl:SysBehavior behavior="publicHeadContent"}} \ No newline at end of file diff --git a/themes/default/tpl/_mp3_player.html b/themes/default/tpl/_mp3_player.html new file mode 100644 index 0000000..df9fda9 --- /dev/null +++ b/themes/default/tpl/_mp3_player.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/themes/default/tpl/_top.html b/themes/default/tpl/_top.html new file mode 100644 index 0000000..829edae --- /dev/null +++ b/themes/default/tpl/_top.html @@ -0,0 +1,10 @@ +
      +

      {{tpl:BlogName encode_html="1"}}

      + + + {{tpl:SysBehavior behavior="publicTopAfterContent"}} +
      + +

      {{tpl:lang To content}} | +{{tpl:lang To menu}} | +{{tpl:lang To search}}

      \ No newline at end of file diff --git a/themes/default/tpl/archive.html b/themes/default/tpl/archive.html new file mode 100644 index 0000000..0df0406 --- /dev/null +++ b/themes/default/tpl/archive.html @@ -0,0 +1,80 @@ + + + + + + + + {{tpl:lang Archives}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:lang Archives}}

      +
      + +
      + + +

      {{tpl:ArchiveDate format="%Y"}}

      + + +
      +
      + +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/archive_month.html b/themes/default/tpl/archive_month.html new file mode 100644 index 0000000..b7a3102 --- /dev/null +++ b/themes/default/tpl/archive_month.html @@ -0,0 +1,97 @@ + + + + + + + + {{tpl:lang Archives}} - {{tpl:ArchiveDate}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + + + +
      +

      {{tpl:ArchiveDate}}

      +
      + +
      + + +

      {{tpl:EntryDate}}

      + +

      {{tpl:EntryTitle encode_html="1"}}

      + + +
      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/category.html b/themes/default/tpl/category.html new file mode 100644 index 0000000..e0fa4b8 --- /dev/null +++ b/themes/default/tpl/category.html @@ -0,0 +1,168 @@ + + + + + + + + {{tpl:CategoryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:CategoryTitle encode_html="1"}} + {{tpl:CategoryTitle encode_html="1"}}

      + {{tpl:CategoryDescription}} + + +

      {{tpl:lang Entries feed}} + + + - {{tpl:lang Comments feed}} + +

      +
      +
      + + + +
      +

      {{tpl:lang Subcategories}}

      + +
      + +
      + + +
      + +

      {{tpl:EntryDate}}

      + +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +

      {{tpl:lang Continue + reading}}...

      +
      + + + +
      {{tpl:EntryContent}}
      +
      + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
      + + + +

      - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

      +
      +
      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/home.html b/themes/default/tpl/home.html new file mode 100644 index 0000000..5258d07 --- /dev/null +++ b/themes/default/tpl/home.html @@ -0,0 +1,145 @@ + + + + + + + + {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      + +

      {{tpl:EntryDate}}

      + +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +

      {{tpl:lang Continue + reading}}...

      +
      + + + +
      {{tpl:EntryContent}}
      +
      + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
      + + + +

      - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

      +
      +
      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/post.html b/themes/default/tpl/post.html new file mode 100644 index 0000000..e4d5219 --- /dev/null +++ b/themes/default/tpl/post.html @@ -0,0 +1,277 @@ + + + + + + + + {{tpl:EntryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + + + + +
      +{{tpl:EntryPingData}} + +{{tpl:include src="_top.html"}} + +
      + +
      +
      + + + +
      +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +
      + +
      {{tpl:EntryContent}}
      + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} +
      + + + + +
      +

      {{tpl:lang Attachments}}

      +
        + +
      • + + {{tpl:include src="_mp3_player.html"/}} - + + + {{tpl:include src="_flv_player.html"/}} + + + {{tpl:AttachmentTitle}} + +
      • + +
      +
      + +
      + + + + + +
      +

      {{tpl:lang Comments}}

      +
      + +
      {{tpl:CommentOrderNumber}}. + {{tpl:lang On}} {{tpl:CommentDate}}, {{tpl:CommentTime}} + {{tpl:lang by}} {{tpl:CommentAuthorLink}}
      + +
      + + {{tpl:SysBehavior behavior="publicCommentBeforeContent"}} + + {{tpl:CommentContent}} + + + {{tpl:SysBehavior behavior="publicCommentAfterContent"}} +
      + +
      +
      + +
      +
      + + + +

      {{tpl:SysFormError}}

      +
      + + +

      {{tpl:lang Your comment has been published.}}

      +
      + + +

      {{tpl:lang Your comment has been submitted and + will be reviewed for publication.}}

      +
      + + +
      + +
      +

      {{tpl:lang Your comment}}

      +
      +
      {{tpl:CommentPreviewContent}}
      +
      +

      +
      +
      + +

      {{tpl:lang Add a comment}}

      +
      + + {{tpl:SysBehavior behavior="publicCommentFormBeforeContent"}} + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      + +

      +

      {{tpl:lang HTML code is displayed as text and web addresses are + automatically converted.}}

      + + + {{tpl:SysBehavior behavior="publicCommentFormAfterContent"}} +
      + +
      +

      +

      +
      +
      +
      + + + +
      +

      {{tpl:lang They posted on the same topic}}

      + + +
      + +
      {{tpl:PingOrderNumber}}. + {{tpl:lang On}} {{tpl:PingDate}}, {{tpl:PingTime}} + {{tpl:lang by}} {{tpl:PingBlogName encode_html="1"}}
      + +
      + + {{tpl:SysBehavior behavior="publicPingBeforeContent"}} + +

      {{tpl:PingTitle encode_html="1"}}

      + {{tpl:PingContent}} + + + {{tpl:SysBehavior behavior="publicPingAfterContent"}} +
      + +
      + +
      +
      +
      + + +

      {{tpl:lang Trackback URL}} : {{tpl:EntryPingLink}}

      +
      + + +

      {{tpl:lang This post's comments feed}}

      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + diff --git a/themes/default/tpl/search.html b/themes/default/tpl/search.html new file mode 100644 index 0000000..f18058c --- /dev/null +++ b/themes/default/tpl/search.html @@ -0,0 +1,153 @@ + + + + + + + + {{tpl:lang Search}} - {{tpl:SysSearchString encode_html="1"}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:lang Search}}

      + +

      {{tpl:SysSearchString encode_html="1" string="Your search for %1$s returned no result."}}

      +
      + +

      {{tpl:SysSearchString encode_html="1" string="Your search for %1$s returned %2$s result."}}

      +
      + +

      {{tpl:SysSearchString encode_html="1" string="Your search for %1$s returned %2$s results."}}

      +
      +
      + + +
      + +

      {{tpl:EntryDate}}

      + +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +

      {{tpl:lang Continue + reading}}...

      +
      + + + +
      {{tpl:EntryContent}}
      +
      + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
      + + + +

      - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

      +
      +
      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/tag.html b/themes/default/tpl/tag.html new file mode 100644 index 0000000..40d8955 --- /dev/null +++ b/themes/default/tpl/tag.html @@ -0,0 +1,154 @@ + + + + + + + + {{tpl:lang Tag}} - {{tpl:TagID}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:lang Tag}} - {{tpl:TagID}}

      + +

      {{tpl:lang Entries feed}} + + + - {{tpl:lang Comments feed}} + +

      +
      + + +
      + +

      {{tpl:EntryDate}}

      + +

      {{tpl:EntryTitle encode_html="1"}}

      + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
      {{tpl:EntryExcerpt}}
      +

      {{tpl:lang Continue + reading}}...

      +
      + + + +
      {{tpl:EntryContent}}
      +
      + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
      + + + +

      - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

      +
      +
      +
      +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/tags.html b/themes/default/tpl/tags.html new file mode 100644 index 0000000..a6323c3 --- /dev/null +++ b/themes/default/tpl/tags.html @@ -0,0 +1,70 @@ + + + + + + + + {{tpl:lang Tags}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
      +{{tpl:include src="_top.html"}} + +
      + +
      +
      + +
      +

      {{tpl:lang Tags}}

      +
      + +
      + +
      + +
      +
      + + + +
      + +{{tpl:include src="_footer.html"}} +
      + + \ No newline at end of file diff --git a/themes/default/tpl/user_head.html b/themes/default/tpl/user_head.html new file mode 100644 index 0000000..4b380d4 --- /dev/null +++ b/themes/default/tpl/user_head.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v2/dotclear/CHANGELOG b/v2/dotclear/CHANGELOG new file mode 100644 index 0000000..4c0e362 --- /dev/null +++ b/v2/dotclear/CHANGELOG @@ -0,0 +1,385 @@ +Dotclear 2.4.4 - 2012-08-13 +=========================================================== +* Bugfix: Programmed entries works again. +* Compatibility fix: widgets are now fully php >=5.3 compliant +* Security fix: potential CSRF in user management +* has_tag now part of dotclear core, moved from tag plugin. +* empty title fixed on rss reader widget + + +Dotclear 2.4.3 - 2012-05-18 +=========================================================== +* Admin: My favorites menu can be hidden +* Admin: Fix wordpress importer +* Admin: about:config and user:pref tables are now more readable +* Ductile theme: Blog logo can be changed +* New lithuanian language (thanks to Paulius Černakauskas) +* Various bug fixes + +Dotclear 2.4.2 - 2012-02-11 +=========================================================== +* Security fix release +* 4 XSS vulnerabilities fixed, discovered by High-Tech Bridge + +Dotclear 2.4.1.2 - 2011-12-24 +=========================================================== +* Happy Christmas! +* Security: fixed one SQL injection vulnerability in Clearbricks, thanks to Adjaya +* New behaviour: publicGetURLFor +* New behaviour: publicRegisterURL +* New behaviour: templatePrepareParams +* Changed the way to get artefacts URLs, through $core->url->getURLFor calls, instead of $core->url->getBase() +* new/updated parameter sql_only in $core->blog->getPosts and $core->blog->getComments + +Dotclear 2.4.0 - 2011-11-13 +=========================================================== +* Admin: new iconset from Thomas Daveluy +* Admin: Accessibility enhancements +* Added a custom widget sidebar +* Added a new theme (Ductile) +* Added a new plugin (simpleMenu) +* handling of postgres non default schemas (db_prefix = 'schema.prefix') +* New iconset mechanism +* New behaviour: coreBlogBeforeGetPosts +* Security fix: Spam comments feed now checks for blog permission. Thanks to Romuald Brunet. +* Various bug fixes + +Dotclear 2.3.1 - 2011-06-14 +=========================================================== +* Updated makefile for cleaner distrib. +* Better localization handling for prefs and shortcuts. +* Misc JS & CSS cleaning. +* Import/Export preferences-related bugfix. +* Administrative mail address is now configurable. +* Security: one minor fix and changes for two potential problems. Thanks to Jeremie Boutoille + +Dotclear 2.3.0 - 2011-05-16 +=========================================================== +* Admin: Major backend redesign +* Admin: Customizable Dashboard +* Admin: New Favourites admin submenu +* Admin: New user preferences backend +* Admin: Accessibility enhancements +* Admin: Inline help extended +* Templates: Default theme templates moved to inc/public/default-templates +* Clearbricks: External libraries relocated to inc/libs +* Clearbricks: fixed utf-8 and mysql strict mode problems +* Added a safe mode connection, disabling all plugins +* Mysqli support (config.php may need to be updated manually) +* Fixed dcLog bug with pgsql +* Fixed comment/trackbacks counters reset. +* Several other bug fixes + + +Dotclear 2.2.3 - 2011-04-01 +=========================================================== +* Security fix in media manager. Thx to Raphaël +* Bugfix : 2.2.2 was preventing manual thumbnail regeneration. +* Database handling bugfixes + +Dotclear 2.2.2 - 2011-01-17 +=========================================================== +* Bugfix: 2.2.1 was blocking new installations +* Autoupdate procedure should now be "bad ftp client configuration"-proof. +* Several other small bugfixes + +Dotclear 2.2.1 - 2011-01-15 +=========================================================== +* ExternalMedia is not part of the core distribution anymore +* New attribute to tpl:SysIf: blog_id +* New behaviour: adminMediaItemForm +* Several bugfixes +* Several code optimizations +* Several typos corrected +* Security fix in Clearbricks. Thx to François Pierre-Doray for pointing it out. + +Dotclear 2.2 - 2010-07-01 +=========================================================== +* New installation wizard. +* Several new behaviours: + - adminCommentHeaders + - adminCommentsActionsCombo + - adminCommentsActions + - adminCommentsActionsContent + - adminBeforeCommentDelete + - adminPostsActionsHeaders + - adminUsersActionsCombo + - coreBeforeCategoryCreate & coreAfterCategoryCreate + - coreBeforeCategoryUpdate & coreAfterCategoryUpdate + - coreBeforeLogCreate & coreAfterLogCreate + - coreBeforePostCreate & coreAfterPostCreate + - coreBeforePostUpdate & coreAfterPostUpdate + - coreMediaConstruct + - templateCustomSortByAlias + - urlHandlerGetArgsDocument +* New methods for several core classes. +* Metadata integration to the core. +* Error handlers can now be extended. +* Templates: blocks can now be recursive. +* Templates: Entries & Comments tags can now be sorted. +* Templates: The template subsystem is quicker, linier, and ready to be extended. +* Complete reworking of the settings system +* Correct handling of postgresql non default schemas. +* Admin: Autocompletion and further enhancing to tags handling. +* Admin: Accessibility & ergonomic tweaks. +* Admin: Administrator tag in users list. +* Comment cookies are now specific to the blog rather than to the domain. +* Password changes can now be mandatory. +* jQuery updated to 1.4.2. +* And way too many bugfixes and typos squashes to be listed. + + +Dotclear 2.1.7 - 2010-05-25 +=========================================================== +* Auto-update procedure fix + +Dotclear 2.1.6 - 2009-10-01 +=========================================================== +* Install procedure fixes +* Admin: Page managers can now create pages +* Admin: several typos corrected. +* Admin: Widgets now work in IE8. +* Admin: Password protected posts can now be previewed. +* Templates: tpl:Meta* are now tpl:Tags*. +* Templates: now display all posts. +* new behavior: adminPageHTMLHead +* DB schema: new blog_id field in log table +* Media manager: Pubic folder can now be set on a different host. +* WordPress import fixes +* Dailymotion insertion fix +* Upgrade procedure: CRLF removed in files that were bugging the upgrade. +* JQuery updated to 1.3 +* IE7-js update +* security: Full Path Disclosure protection. Thx to Karim Ayad for pointing it out. +* and way too many bugfixes to be listed. + +Dotclear 2.1.5 - 2009-02-05 +=========================================================== +* Security release +* Youtube insertion update + +Dotclear 2.1.4 - 2008-12-21 +=========================================================== +* Security flaw fix +* WordPress import refining +* XML-RPC improvements + +Dotclear 2.1.3 - 2008-11-19 +=========================================================== +* Admin: New upgrade procedure +* Admin: Fixed video insertion bug +* Template: New attributes + * url on EntryIf + * only_category on Blogroll + * no_context on Pagination +* Template: New tag + * BlogID +* Admin: escaped blog_id on authentication page + +Dotclear 2.1.1 - 2008-11-07 +=========================================================== +* Admin: Automatic Update bug fixes +* Admin: Disable Automatic Update if no digests file +* Admin: Javascript fixes in authentication page +* Admin: Fixed errors with categories select boxes +* Template: Added level attribute in tpl:Categories +* Media: Added H.264/MPEG-4 AVC for mp4 files + +Dotclear 2.1 - 2008-11-01 +=========================================================== +* Subcategories +* Admin: Automatic Update +* Admin: Flash 10 support for uploader +* Admin: mailto link in comment details +* Admin: Embedded video size selection +* Admin: Restrict session cookie path to admin +* Media: H.264/MPEG-4 AVC (HD) support with m4v files +* Inherited themes +* WordPress XML-RPC methods support +* True unicode URLs +* Plugin: Widgets as template tags +* Plugin: Filters in entries widgets and Blogroll +* Plugin: Added vimeo.com in external media +* Template: New tags + * LoopPosition + * CommentAuthorDomain + * CommentAuthorMD5 + * EntryFirstImage + * EntryCategoryShortURL + * CategoryIf + * CategoryFirstChildren + * CategoryParents + * EntryCategoriesBreadcrum + * MediaURL + +Dotclear 2.0.2 - 2008-09-05 +=========================================================== +* New installation procedure +* Plugin: WordPress import fixes +* Plugin: Plain text export as downloadable files +* Plugin: Message about URLs in Dotclear 1.2 import +* Public: Display a message if search returns no result +* Admin: Fixed some CSS bugs +* Admin: Batch select/unselect entries +* Admin: In a media item, find entries containing it + +Dotclear 2.0.1 - 2008-08-16 +=========================================================== +* Plugin: Fixed a bug with Dotclear 1.2 URLs import. +* Plugin: Fixed a l10n bug in Pages +* Admin: Enhanced plugins resources loading and cache + +Dotclear 2.0 - 2008-08-01 +=========================================================== +* Public: Atom becomes the default feed format. RSS 2 is always available. +* Admin: design enhancements and new Dotclear logo +* Admin: entries preview in blog context +* L10N: New language manager with zip files support +* Plugin: Import/Export plugin version 2.0 with import from Dotclear 1.2 and WordPress +* Plugin: Pages enhancements (preview, sorting) +* Plugin: support for jamendo and deezer in External Media +* JSMin on JavaScript files instead of JS packing +* SQLite 3 only support (PDO based) +* Many bug fixes and major performances improvements + +Dotclear 2.0 RC2 - 2008-06-21 +=========================================================== +* FairTrackback spam filter +* Language pack infrastructure +* Bug fix on comment search with author "0" +* Javascript fixes +* dcAuth::sessionExists and dcAuth::checkSession new methods +* Right management in dcAuth::sudo +* Media File sorting options in media manager +* CandyUpload, new uploader tool based on SWFUpload +* New search engine robots options +* New image options +* L10N: Japanese and Portugues (Brazil) language packs +* Many bug fixes and enhancements + +Dotclear 2.0 RC1 - 2008-05-01 +=========================================================== +* New: Pages plugin +* New: Theme editor plugin +* Entries: Text and WYSIWYG enhancements +* Entries: Markup validator +* Entries: Insertion of links to other entries from toolbar +* Entries: External media insertion (dailymotion, youtube, google video) +* Tags: Same list for new and existing entries +* Tags: Tags can be removed on all associated entries +* Tags: Tags can be removed on a post selection +* Admin: Ask password for user management tasks, theme upload and plugin upload +* Admin: New contextual help viewer +* Media manager: Recreate thumbnails option +* Media manager: Custom medium thumbnail size (per blog) +* Media manager: Zip files extract support +* Media manager: Zip file download of directory +* Media manager: File exclusion pattern option +* Themes and plugins: Zip as new package format +* Themes and plugins: Upload +* Themes and plugins: Upgrade within administration interface +* Themes and plugins: Deletion +* Public: New default theme: Blowup (fully customizable) +* Public: Changed the way commenter cookie is handled +* Themes: Template files moved to tpl/ directory +* L10N: Polish, Catalan and Spanish translations +* Misc: jQuery upgraded to 1.2.3 +* Misc: Crushed png files +* Fixed many bugs + +Dotclear 2.0 beta 7 - 2007-07-12 +=========================================================== +* New way to display comments and trackbacks on entries in backend +* Dashboard visual improvements +* Default cache dir created by installation process +* Option to limit posts and comments in feeds +* Introduced UDBS for installation and upgrade +* Changed handling of XML-RPC URLs +* New option to force HTTPS redirect if wanted +* Enforced cookies security (directory and ssl support) +* Added Plugin auto-install and auto-upgrade support +* Added trackbacks ttl and moderation preferences +* Added an Internal search engine +* FLV support in backend with Neolao player +* Added nice messages if database is broken or Dotclear not installed +* upgrade jQuery to 1.1.3 +* Fixed many bugs +* Fixed security issues in backend + +Dotclear 2.0 beta 6 - 2007-02-19 +=========================================================== +* New antispam plugin, with a set of filters (rbl, ipblacklist, spamwords, akismet) +* New admin dashboard page +* Fixed unwanted logout bug +* Added settings to disable template caching and allow PHP code +* Blog preferences panel bug fix +* New XML-RPC Client and Server +* Comment posting permissions bug fix + +Dotclear 2.0 beta 5.4 - 2007-01-19 +=========================================================== +* Minor change on spam display in comments.php +* Command line upgrade script and fix in load_plugin_file.php +* Make akismet configurable only by superadmin with DC_AKISMET_SUPER +* SQL optimisations +* New comments view in post + +Dotclear 2.0 beta 5.2 - 2007-01-11 +=========================================================== +* Fixed a bug with imageMeta::getMeta +* Enhanced dynamic file uploader +* Move clearbricks files to their own repository +* Fixed a bug with auto_br in wikiSimpleComment +* Support for language restriction in feeds +* Default theme structure changes +* Fixed a PHP 5.0 compatibility issue +* Installation Wizard + +Dotclear 2.0 beta 4 - 2006-12-26 +=========================================================== +* Performances enhancements. +* Administration UI enhancements. +* More user-friendly Widgets (version 1.5). +* Switch to jQuery . +* Added jQuery in default theme. +* Major changes in HTTP client and Feed Parser based on a + generic socket handler. +* PHP 5.2 compatibility. +* Code documentation (all core and most of clearbricks). +* Many bug fixes. + +Dotclear 2.0 beta 3 - 2006-11-05 +=========================================================== +* Disallow special wrappers for fopen like functions. +* XML/RPC improvements. +* Read IPTC and EXIF metadata in uploaded pictures. +* MySQL 4.1 support only. +* Metadata import from Dotclear 1.2.x. +* Akismet plugin. +* Pings plugin. +* Added a priority setting for plugins. +* Many bug fixes. + +Dotclear 2.0 beta 2 - 2006-08-09 +=========================================================== +* DC_PLUGIN_ROOT can handle more than one path. +* OPML/XBEL import in blogroll plugin. +* Fixed a security issue in html::absoluteURLs(). +* Fixed issues with timezone on scheduled entries. +* Multiple categories selection in tpl:Entries. +* Improved dbLayer. +* Changed category feed URL. +* Feeds for tags (entries and comments). +* Added attachments count on backend and frontend. +* New settings code design. Can now handle wide system settings. +* Memory usage improvements with autoloader. +* Some code cleanup. +* Feed parser improvements. +* Themes can be configured if needed. +* XMP support on JPEG files. +* Media manager improvements. +* Spamplemousse now uses DNSBL (and the guy who left the bug was fired). +* Javascript editor and toolbar improvements. +* RDS support (XML/RPC API discovery). +* Added a theme with user stylesheet. +* Plugins manager diff --git a/v2/dotclear/CREDITS b/v2/dotclear/CREDITS new file mode 100644 index 0000000..76b9172 --- /dev/null +++ b/v2/dotclear/CREDITS @@ -0,0 +1,46 @@ +This is a credits file of people that have contributed to the Dotclear project. + +Dotclear Team +------------- + +Thomas Bouron +Anne Cavalier +Noé Cendrier +Benoit Clerc +Grégory Corvisier +Florent Cotton +Philippe Hénaff +Bruno Hondelatte +Olivier Meunier +Franck Paul +Michel Pelletier +Xavier Plantefève +Jean-Michel Royer +Anne Sophie Tranchet +Alain Vagner + +Dotclear Translators +-------------------- + +Benjamin Bank +Alain Béarez +Claire Cambier +Luis Correia +Andreas Diller +Alain Fagot +Sabrina Favier +Charles Hebert +Guillaume Jonquiere +Dennis S. L. Jørgensen +Miguel A. Muñoz +Sebestyén Muráncsik +Nnidŷu +Polo +Aina Chabert Ramon +Adnan Shameem +Enrique Matias Sanchez +Jan Skrasek +Takafumi +Regina Timbó + +... and all contributors. \ No newline at end of file diff --git a/v2/dotclear/LICENSE b/v2/dotclear/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/v2/dotclear/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/v2/dotclear/README b/v2/dotclear/README new file mode 100644 index 0000000..ed0c4c4 --- /dev/null +++ b/v2/dotclear/README @@ -0,0 +1,22 @@ +Welcome to Dotclear + +WHAT YOU NEED TO RUN DOTCLEAR +----------------------------- +In order to run Dotclear you need: + + * A web server (Apache, Cheerokee, lightHttpd, etc.) + * PHP 5.2 with the following modules: + * mbstring + * iconv + * simplexml + * mysql, postgresql or sqlite + * A database server (MySQL or PostgreSQL) or SQLite. + + +INSTALLATION +------------ +Copy inc/config.php.in to inc/config.php and fill in the fields with appropriate +values. + +If you intend to use MySQL or PostgreSQL, you'll have to create a database +first. With SQLite, db/ folder will have to be writable by your web server. \ No newline at end of file diff --git a/v2/dotclear/admin/auth.php b/v2/dotclear/admin/auth.php new file mode 100644 index 0000000..414914c --- /dev/null +++ b/v2/dotclear/admin/auth.php @@ -0,0 +1,431 @@ +auth->allowPassChange() && isset($_POST['new_pwd']) && isset($_POST['new_pwd_c']) && isset($_POST['login_data']); +$login_data = !empty($_POST['login_data']) ? html::escapeHTML($_POST['login_data']) : null; +$recover = $core->auth->allowPassChange() && !empty($_REQUEST['recover']); +$safe_mode = !empty($_REQUEST['safe_mode']); +$akey = $core->auth->allowPassChange() && !empty($_GET['akey']) ? $_GET['akey'] : null; +$user_id = $user_pwd = $user_key = $user_email = null; +$err = $msg = null; + +# Auto upgrade +if (empty($_GET) && empty($_POST)) { + require dirname(__FILE__).'/../inc/dbschema/upgrade.php'; + try { + if (($changes = dotclearUpgrade($core)) !== false) { + $msg = __('Dotclear has been upgraded.').''; + } + } catch (Exception $e) { + $err = $e->getMessage(); + } +} + +# If we have POST login informations, go throug auth process +if (!empty($_POST['user_id']) && !empty($_POST['user_pwd'])) +{ + $user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null; + $user_pwd = !empty($_POST['user_pwd']) ? $_POST['user_pwd'] : null; +} +# If we have COOKIE login informations, go throug auth process +elseif (isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104) +{ + # If we have a remember cookie, go through auth process with user_key + $user_id = substr($_COOKIE['dc_admin'],40); + $user_id = @unpack('a32',@pack('H*',$user_id)); + if (is_array($user_id)) + { + $user_id = $user_id[1]; + $user_key = substr($_COOKIE['dc_admin'],0,40); + $user_pwd = null; + } + else + { + $user_id = null; + } +} + +# Recover password +if ($recover && !empty($_POST['user_id']) && !empty($_POST['user_email'])) +{ + $user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null; + $user_email = !empty($_POST['user_email']) ? $_POST['user_email'] : ''; + try + { + $recover_key = $core->auth->setRecoverKey($user_id,$user_email); + + $subject = mail::B64Header('DotClear '.__('Password reset')); + $message = + __('Someone has requested to reset the password for the following site and username.')."\n\n". + $page_url."\n".__('Username:').' '.$user_id."\n\n". + __('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.')."\n". + $page_url.'?akey='.$recover_key; + + $headers[] = 'From: '.(defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local'); + $headers[] = 'Content-Type: text/plain; charset=UTF-8;'; + + mail::sendMail($user_email,$subject,$message,$headers); + $msg = sprintf(__('The e-mail was sent successfully to %s.'),$user_email); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} +# Send new password +elseif ($akey) +{ + try + { + $recover_res = $core->auth->recoverUserPassword($akey); + + $subject = mb_encode_mimeheader('DotClear '.__('Your new password'),'UTF-8','B'); + $message = + __('Username:').' '.$recover_res['user_id']."\n". + __('Password:').' '.$recover_res['new_pass']."\n\n". + preg_replace('/\?(.*)$/','',$page_url); + + $headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST']; + $headers[] = 'Content-Type: text/plain; charset=UTF-8;'; + + mail::sendMail($recover_res['user_email'],$subject,$message,$headers); + $msg = __('Your new password is in your mailbox.'); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} +# Change password and retry to log +elseif ($change_pwd) +{ + try + { + $tmp_data = explode('/',$_POST['login_data']); + if (count($tmp_data) != 3) { + throw new Exception(); + } + $data = array( + 'user_id'=>base64_decode($tmp_data[0]), + 'cookie_admin'=>$tmp_data[1], + 'user_remember'=>$tmp_data[2]=='1' + ); + if ($data['user_id'] === false) { + throw new Exception(); + } + + # Check login informations + $check_user = false; + if (isset($data['cookie_admin']) && strlen($data['cookie_admin']) == 104) + { + $user_id = substr($data['cookie_admin'],40); + $user_id = @unpack('a32',@pack('H*',$user_id)); + if (is_array($user_id)) + { + $user_id = $user_id[1]; + $user_key = substr($data['cookie_admin'],0,40); + $check_user = $core->auth->checkUser($user_id,null,$user_key) === true; + } + } + + if (!$core->auth->allowPassChange() || !$check_user) { + $change_pwd = false; + throw new Exception(); + } + + if ($_POST['new_pwd'] != $_POST['new_pwd_c']) { + throw new Exception(__("Passwords don't match")); + } + + if ($core->auth->checkUser($user_id,$_POST['new_pwd']) === true) { + throw new Exception(__("You didn't change your password.")); + } + + $cur = $core->con->openCursor($core->prefix.'user'); + $cur->user_change_pwd = 0; + $cur->user_pwd = $_POST['new_pwd']; + $core->updUser($core->auth->userID(),$cur); + + $core->session->start(); + $_SESSION['sess_user_id'] = $user_id; + $_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY); + + if ($data['user_remember']) + { + setcookie('dc_admin',$data['cookie_admin'],strtotime('+15 days'),'','',DC_ADMIN_SSL); + } + + http::redirect('index.php'); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} +# Try to log +elseif ($user_id !== null && ($user_pwd !== null || $user_key !== null)) +{ + # We check the user + $check_user = $core->auth->checkUser($user_id,$user_pwd,$user_key) === true; + + $cookie_admin = http::browserUID(DC_MASTER_KEY.$user_id. + crypt::hmac(DC_MASTER_KEY,$user_pwd)).bin2hex(pack('a32',$user_id)); + + if ($check_user && $core->auth->mustChangePassword()) + { + $login_data = join('/',array( + base64_encode($user_id), + $cookie_admin, + empty($_POST['user_remember'])?'0':'1' + )); + + if (!$core->auth->allowPassChange()) { + $err = __('You have to change your password before you can login.'); + } else { + $err = __('In order to login, you have to change your password now.'); + $change_pwd = true; + } + } + elseif ($check_user && !empty($_POST['safe_mode']) && !$core->auth->isSuperAdmin()) + { + $err = __('Safe Mode can only be used for super administrators.'); + } + elseif ($check_user) + { + $core->session->start(); + $_SESSION['sess_user_id'] = $user_id; + $_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY); + + if (!empty($_POST['blog'])) { + $_SESSION['sess_blog_id'] = $_POST['blog']; + } + + if (!empty($_POST['safe_mode']) && $core->auth->isSuperAdmin()) { + $_SESSION['sess_safe_mode'] = true; + } + + if (!empty($_POST['user_remember'])) { + setcookie('dc_admin',$cookie_admin,strtotime('+15 days'),'','',DC_ADMIN_SSL); + } + + http::redirect('index.php'); + } + else + { + if (isset($_COOKIE['dc_admin'])) { + unset($_COOKIE['dc_admin']); + setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL); + } + $err = __('Wrong username or password'); + } +} + +if (isset($_GET['user'])) { + $user_id = $_GET['user']; +} + +header('Content-Type: text/html; charset=UTF-8'); +?> + + + + + + + + + + <?php echo html::escapeHTML(DC_VENDOR_NAME); ?> + + + + + + callBehavior('loginPageHTMLHead'); + ?> + + + + + + +
      +

      + +'.$err.'
      '; +} +if ($msg) { + echo '

      '.$msg.'

      '; +} + +if ($akey) +{ + echo '

      '.__('Back to login screen').'

      '; +} +elseif ($recover) +{ + echo + '
      '.__('Request a new password').''. + '

      '. + form::field(array('user_id','user_id'),20,32,html::escapeHTML($user_id)).'

      '. + + '

      '. + form::field(array('user_email','user_email'),20,255,html::escapeHTML($user_email)).'

      '. + + '

      '. + form::hidden(array('recover'),1).'

      '. + '
      '. + + ''; +} +elseif ($change_pwd) +{ + echo + '
      '.__('Change your password').''. + '

      '. + form::password(array('new_pwd','new_pwd'),20,255).'

      '. + + '

      '. + form::password(array('new_pwd_c','new_pwd_c'),20,255).'

      '. + ''. + + '

      '. + form::hidden('login_data',$login_data).'

      '; +} +else +{ + if (is_callable(array($core->auth,'authForm'))) + { + echo $core->auth->authForm($user_id); + } + else + { + if ($safe_mode) { + echo '
      '; + echo ''.__('Safe mode login').''; + echo + '

      '. + __('This mode allows you to login without activating any of your plugins. This may be useful to solve compatibility problems').' 
      '. + __('Disable or delete any plugin suspected to cause trouble, then log out and log back in normally.'). + '

      '; + } + else { + echo '
      '; + } + + echo + '

      '. + form::field(array('user_id','user_id'),20,32,html::escapeHTML($user_id)).'

      '. + + '

      '. + form::password(array('user_pwd','user_pwd'),20,255).'

      '. + + '

      '. + form::checkbox(array('user_remember','user_remember'),1). + '

      '. + + '

      '; + + if (!empty($_REQUEST['blog'])) { + echo form::hidden('blog',html::escapeHTML($_REQUEST['blog'])); + } + if($safe_mode) { + echo + form::hidden('safe_mode',1). + '
      '; + } + else { + echo '
      '; + } + echo + ''; + + echo '
      '; + + if ($safe_mode) { + echo + '

      '.__('Get back to normal authentication').'

      '; + } else { + echo '

      '.__('Connection issue?').'

      '; + if ($core->auth->allowPassChange()) { + echo '

      '.__('I forgot my password').'

      '; + } + echo '

      '.__('I want to log in in safe mode').'

      '; + } + + echo '
      '; + } +} +?> + + + diff --git a/v2/dotclear/admin/blog.php b/v2/dotclear/admin/blog.php new file mode 100644 index 0000000..fc01915 --- /dev/null +++ b/v2/dotclear/admin/blog.php @@ -0,0 +1,96 @@ +con->openCursor($core->prefix.'blog'); + $blog_id = $cur->blog_id = $_POST['blog_id']; + $blog_url = $cur->blog_url = $_POST['blog_url']; + $blog_name = $cur->blog_name = $_POST['blog_name']; + $blog_desc = $cur->blog_desc = $_POST['blog_desc']; + + try + { + # --BEHAVIOR-- adminBeforeBlogCreate + $core->callBehavior('adminBeforeBlogCreate',$cur,$blog_id); + + $core->addBlog($cur); + + # Default settings and override some + $core->blogDefaults($cur->blog_id); + $blog_settings = new dcSettings($core,$cur->blog_id); + $blog_settings->addNamespace('system'); + $blog_settings->system->put('lang',$core->auth->getInfo('user_lang')); + $blog_settings->system->put('blog_timezone',$core->auth->getInfo('user_tz')); + + if (substr($blog_url,-1) == '?') { + $blog_settings->system->put('url_scan','query_string'); + } else { + $blog_settings->system->put('url_scan','path_info'); + } + + # --BEHAVIOR-- adminAfterBlogCreate + $core->callBehavior('adminAfterBlogCreate',$cur,$blog_id,$blog_settings); + + http::redirect('blog.php?id='.$cur->blog_id.'&add=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +if (!empty($_REQUEST['id'])) +{ + $edit_blog_mode = true; + include dirname(__FILE__).'/blog_pref.php'; +} +else +{ + dcPage::open(__('New blog'),dcPage::jsConfirmClose('blog-form')); + + echo + '

      '.__('Blogs').''.__('New blog').'

      '. + + '
      '. + + $core->formNonce(). + '

      '. + '

      '.__('At least 2 characters using letters, numbers or symbols.').'

      '. + '

      '.__('Please note that changing your blog ID may require changes in your public index.php file.').'

      '. + + '

      '. + + '

      '. + + '

      '. + form::textarea('blog_desc',60,5,html::escapeHTML($blog_desc)).'

      '. + + '

      '. + '
      '; + + dcPage::close(); +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/blog_del.php b/v2/dotclear/admin/blog_del.php new file mode 100644 index 0000000..e0d31fe --- /dev/null +++ b/v2/dotclear/admin/blog_del.php @@ -0,0 +1,73 @@ +getBlog($_POST['blog_id']); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if ($rs->isEmpty()) { + $core->error->add(__('No such blog ID')); + } else { + $blog_id = $rs->blog_id; + $blog_name = $rs->blog_name; + } +} + +# Delete the blog +if (!$core->error->flag() && $blog_id && !empty($_POST['del'])) +{ + if (!$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['pwd']))) { + $core->error->add(__('Password verification failed')); + } else { + try { + $core->delBlog($blog_id); + http::redirect('blogs.php?del=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } +} + +dcPage::open('Delete a blog'); + +if (!$core->error->flag()) +{ + echo + '

      '.__('Delete a blog').'

      '. + '

      '.__('Warning').'

      '. + '

      '.sprintf(__('You are about to delete the blog %s. Every entry, comment and category will be deleted.'), + ''.$blog_id.' ('.$blog_name.')').'

      '. + '

      '.__('Please give your password to confirm the blog deletion.').'

      '; + + echo + '
      '. + '
      '.$core->formNonce().'
      '. + '

      '. + '

      '. + form::hidden('blog_id',$blog_id).'

      '. + '
      '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/blog_pref.php b/v2/dotclear/admin/blog_pref.php new file mode 100644 index 0000000..e8e900a --- /dev/null +++ b/v2/dotclear/admin/blog_pref.php @@ -0,0 +1,553 @@ +blog->id; + $blog_status = $core->blog->status; + $blog_name = $core->blog->name; + $blog_desc = $core->blog->desc; + $blog_settings = $core->blog->settings; + $blog_url = $core->blog->url; + + $action = 'blog_pref.php'; + $redir = 'blog_pref.php?upd=1'; +} +else +{ + dcPage::checkSuper(); + try + { + if (empty($_REQUEST['id'])) { + throw new Exception(__('No given blog id.')); + } + $rs = $core->getBlog($_REQUEST['id']); + + if (!$rs) { + throw new Exception(__('No such blog.')); + } + + $blog_id = $rs->blog_id; + $blog_status = $rs->blog_status; + $blog_name = $rs->blog_name; + $blog_desc = $rs->blog_desc; + $blog_settings = new dcSettings($core,$blog_id); + $blog_url = $rs->blog_url ; + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + + $action = 'blog.php'; + $redir = 'blog.php?id=%s&upd=1'; +} + +# Language codes +$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' : ''); +} + +# Status combo +foreach ($core->getAllBlogStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# URL scan modes +$url_scan_combo = array( + 'PATH_INFO' => 'path_info', + 'QUERY_STRING' => 'query_string' +); + +# Post URL combo +$post_url_combo = array( + __('year/month/day/title') => '{y}/{m}/{d}/{t}', + __('year/month/title') => '{y}/{m}/{t}', + __('year/title') => '{y}/{t}', + __('title') => '{t}' +); +if (!in_array($blog_settings->system->post_url_format,$post_url_combo)) { + $post_url_combo[html::escapeHTML($blog_settings->system->post_url_format)] = html::escapeHTML($blog_settings->system->post_url_format); +} + +# Image title combo +$img_title_combo = array( + __('Title') => 'Title ;; separator(, )', + __('Title, Date') => 'Title ;; Date(%b %Y) ;; separator(, )', + __('Title, Country, Date') => 'Title ;; Country ;; Date(%b %Y) ;; separator(, )', + __('Title, City, Country, Date') => 'Title ;; City ;; Country ;; Date(%b %Y) ;; separator(, )', +); +if (!in_array($blog_settings->system->media_img_title_pattern,$img_title_combo)) { + $img_title_combo[html::escapeHTML($blog_settings->system->media_img_title_pattern)] = html::escapeHTML($blog_settings->system->media_img_title_pattern); +} + +# Robots policy options +$robots_policy_options = array( + 'INDEX,FOLLOW' => __("I would like search engines and archivers to index and archive my blog's content."), + 'INDEX,FOLLOW,NOARCHIVE' => __("I would like search engines and archivers to index but not archive my blog's content."), + 'NOINDEX,NOFOLLOW,NOARCHIVE' => __("I would like to prevent search engines and archivers from indexing or archiving my blog's content."), +); + +# Update a blog +if ($blog_id && !empty($_POST) && $core->auth->check('admin',$blog_id)) +{ + $cur = $core->con->openCursor($core->prefix.'blog'); + if ($core->auth->isSuperAdmin()) { + $cur->blog_id = $_POST['blog_id']; + $cur->blog_url = preg_replace('/\?+$/','?',$_POST['blog_url']); + if (in_array($_POST['blog_status'],$status_combo)) { + $cur->blog_status = (integer) $_POST['blog_status']; + } + } + $cur->blog_name = $_POST['blog_name']; + $cur->blog_desc = $_POST['blog_desc']; + + $media_img_t_size = abs((integer) $_POST['media_img_t_size']); + if ($media_img_t_size < 0) { $media_img_t_size = 100; } + + $media_img_s_size = abs((integer) $_POST['media_img_s_size']); + if ($media_img_s_size < 0) { $media_img_s_size = 240; } + + $media_img_m_size = abs((integer) $_POST['media_img_m_size']); + if ($media_img_m_size < 0) { $media_img_m_size = 448; } + + $nb_post_per_page = abs((integer) $_POST['nb_post_per_page']); + if ($nb_post_per_page <= 1) { $nb_post_per_page = 1; } + + $nb_post_per_feed = abs((integer) $_POST['nb_post_per_feed']); + if ($nb_post_per_feed <= 1) { $nb_post_per_feed = 1; } + + $nb_comment_per_feed = abs((integer) $_POST['nb_comment_per_feed']); + if ($nb_comment_per_feed <= 1) { $nb_comment_per_feed = 1; } + + try + { + if ($cur->blog_id != null && $cur->blog_id != $blog_id) { + $rs = $core->getBlog($cur->blog_id); + + if ($rs) { + throw new Exception(__('That blog Id is already in use.')); + } + } + + # --BEHAVIOR-- adminBeforeBlogUpdate + $core->callBehavior('adminBeforeBlogUpdate',$cur,$blog_id); + + if (!preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$_POST['lang'])) { + throw new Exception(__('Invalid language code')); + } + + $core->updBlog($blog_id,$cur); + + # --BEHAVIOR-- adminAfterBlogUpdate + $core->callBehavior('adminAfterBlogUpdate',$cur,$blog_id); + + if ($cur->blog_id != null && $cur->blog_id != $blog_id) { + if ($blog_id == $core->blog->id) { + $core->setBlog($cur->blog_id); + $_SESSION['sess_blog_id'] = $cur->blog_id; + $blog_settings = $core->blog->settings; + } else { + $blog_settings = new dcSettings($core,$cur->blog_id); + } + + $blog_id = $cur->blog_id; + } + + + $blog_settings->addNameSpace('system'); + + $blog_settings->system->put('editor',$_POST['editor']); + $blog_settings->system->put('copyright_notice',$_POST['copyright_notice']); + $blog_settings->system->put('post_url_format',$_POST['post_url_format']); + $blog_settings->system->put('lang',$_POST['lang']); + $blog_settings->system->put('blog_timezone',$_POST['blog_timezone']); + $blog_settings->system->put('date_format',$_POST['date_format']); + $blog_settings->system->put('time_format',$_POST['time_format']); + $blog_settings->system->put('comments_ttl',abs((integer) $_POST['comments_ttl'])); + $blog_settings->system->put('trackbacks_ttl',abs((integer) $_POST['trackbacks_ttl'])); + $blog_settings->system->put('allow_comments',!empty($_POST['allow_comments'])); + $blog_settings->system->put('allow_trackbacks',!empty($_POST['allow_trackbacks'])); + $blog_settings->system->put('comments_pub',empty($_POST['comments_pub'])); + $blog_settings->system->put('trackbacks_pub',empty($_POST['trackbacks_pub'])); + $blog_settings->system->put('comments_nofollow',!empty($_POST['comments_nofollow'])); + $blog_settings->system->put('wiki_comments',!empty($_POST['wiki_comments'])); + $blog_settings->system->put('enable_xmlrpc',!empty($_POST['enable_xmlrpc'])); + + $blog_settings->system->put('nb_post_per_page',$nb_post_per_page); + $blog_settings->system->put('use_smilies',!empty($_POST['use_smilies'])); + $blog_settings->system->put('media_img_t_size',$media_img_t_size); + $blog_settings->system->put('media_img_s_size',$media_img_s_size); + $blog_settings->system->put('media_img_m_size',$media_img_m_size); + $blog_settings->system->put('media_img_title_pattern',$_POST['media_img_title_pattern']); + $blog_settings->system->put('nb_post_per_feed',$nb_post_per_feed); + $blog_settings->system->put('nb_comment_per_feed',$nb_comment_per_feed); + $blog_settings->system->put('short_feed_items',!empty($_POST['short_feed_items'])); + + if (isset($_POST['robots_policy'])) { + $blog_settings->system->put('robots_policy',$_POST['robots_policy']); + } + + # --BEHAVIOR-- adminBeforeBlogSettingsUpdate + $core->callBehavior('adminBeforeBlogSettingsUpdate',$blog_settings); + + if ($core->auth->isSuperAdmin() && in_array($_POST['url_scan'],$url_scan_combo)) { + $blog_settings->system->put('url_scan',$_POST['url_scan']); + } + + http::redirect(sprintf($redir,$blog_id)); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +dcPage::open(__('Blog settings'), + '". + dcPage::jsConfirmClose('blog-form'). + dcPage::jsLoad('js/_blog_pref.js'). + + + # --BEHAVIOR-- adminBlogPreferencesHeaders + $core->callBehavior('adminBlogPreferencesHeaders'). + + dcPage::jsPageTabs() +); + +if ($blog_id) +{ + echo '

      '.(!$standalone ? ''.__('Blogs').' › ' : ''). + html::escapeHTML($blog_name).' › '. + __('Blog settings').'

      '; + + if (!empty($_GET['add'])) { + echo '

      '.__('Blog has been successfully created.').'

      '; + } + + if (!empty($_GET['upd'])) { + echo '

      '.__('Blog has been successfully updated.').'

      '; + } + + echo + '
      '. + '

      '.__('Parameters').'

      '. + '
      '; + + echo + '
      '.__('Blog details').''. + $core->formNonce(); + + if ($core->auth->isSuperAdmin()) + { + echo + '

      '. + '

      '.__('At least 2 characters using letters, numbers or symbols.').'

      '. + '

      '.__('Please note that changing your blog ID may require changes in your public index.php file.').'

      '; + } + + echo + '

      '; + + if ($core->auth->isSuperAdmin()) + { + echo + '

      '. + + '

      '. + + '

      '; + } + + echo + '

      '. + form::textarea('blog_desc',60,5,html::escapeHTML($blog_desc)).'

      '. + '
      '; + + + echo + '
      '.__('Blog configuration').''. + '
      '. + '
      '. + '

      '. + + '

      '. + + '

      '. + '
      '. + + '
      '. + '

      '. + + '

      '. + + '

      '. + ' - '.__('more information').'

      '. + '
      '. + '
      '. + '
      '. //Opera sucks + '
      '; + + echo + '
      '.__('Comments and trackbacks').''. + '
      '. + '
      '. + '

      '. + + '

      '. + + '

      '. + '

      '.__('Leave blank to disable this feature.').'

      '. + + '

      '. + '
      '. + + '
      '. + '

      '. + + '

      '. + + '

      '. + '

      '.__('Leave blank to disable this feature.').'

      '. + + '

      '. + '
      '. + '
      '. + '
      '. //Opera sucks + '
      '; + + echo + '
      '.__('Blog presentation').''. + '
      '. + '
      '. + '

      '. + + '

      '. + + '

      '. + '
      '. + + '
      '. + '

      '. + + '

      '. + + '

      '. + + '

      '. + '
      '. + '
      '. + '
      '. //Opera sucks + '
      '; + + echo + '
      '.__('Media and images').''. + '
      '. + '
      '. + '

      '.__('Generated image sizes (in pixels)').'

      '. + '

      '. + + '

      '. + + '

      '. + '
      '. + + '
      '. + '

      '. + '

      '.__('This defines image tag title when you insert it in a post from the media manager. It is retrieved from the picture\'s metadata.').'

      '. + '

      '.form::combo('media_img_title_pattern',$img_title_combo,html::escapeHTML($blog_settings->system->media_img_title_pattern)).'

      '. + '
      '. + '
      '. + '
      '; + + echo + '
      '.__('Search engines robots policy').''; + + $i = 0; + foreach ($robots_policy_options as $k => $v) + { + echo '

      '; + $i++; + } + + echo '
      '; + + + # --BEHAVIOR-- adminBlogPreferencesForm + $core->callBehavior('adminBlogPreferencesForm',$core,$blog_settings); + + echo + '

      '. + (!$standalone ? form::hidden('id',$blog_id) : ''). + '

      '. + '
      '; + + if ($core->auth->isSuperAdmin() && $blog_id != $core->blog->id) + { + echo + '
      '. + '

      '. + form::hidden(array('blog_id'),$blog_id). + $core->formNonce().'

      '. + '
      '; + } + + # XML/RPC information + echo '

      '.__('XML/RPC interface').'

      '; + + echo '

      '.__('XML/RPC interface allows you to edit your blog with an external client.').'

      '; + + if (!$blog_settings->system->enable_xmlrpc) + { + echo '

      '.__('XML/RPC interface is not active. Change settings to enable it.').'

      '; + } + else + { + echo + '

      '.__('XML/RPC interface is active. You should set the following parameters on your XML/RPC client:').'

      '. + '
        '. + '
      • '.__('Server URL:').' '. + sprintf(DC_XMLRPC_URL,$core->blog->url,$core->blog->id). + '
      • '. + '
      • '.__('Blogging system:').' Movable Type
      • '. + '
      • '.__('User name:').' '.$core->auth->userID().'
      • '. + '
      • '.__('Password:').' '.__('your password').'
      • '. + '
      • '.__('Blog ID:').' 1
      • '. + '
      '; + } + + echo '
      '; + + # + # Users on the blog (with permissions) + + $blog_users = $core->getBlogPermissions($blog_id,$core->auth->isSuperAdmin()); + $perm_types = $core->auth->getPermissionsTypes(); + + echo + '
      '. + '

      '.__('Users on this blog').'

      '; + + if (empty($blog_users)) + { + echo '

      '.__('No users').'

      '; + } + else + { + if ($core->auth->isSuperAdmin()) { + $user_url_p = '%1$s'; + } else { + $user_url_p = '%1$s'; + } + + foreach ($blog_users as $k => $v) + { + if (count($v['p']) > 0) + { + echo + '

      '.sprintf($user_url_p,html::escapeHTML($k)). + ' ('.html::escapeHTML(dcUtils::getUserCN( + $k, $v['name'], $v['firstname'], $v['displayname'] + )).')'; + + if (!$v['super'] && $core->auth->isSuperAdmin()) { + echo + ' - ' + .__('Change permissions').''; + } + + echo '

      '; + + echo '
        '; + if ($v['super']) { + echo '
      • '.__('Super administrator').'
      • '; + } else { + foreach ($v['p'] as $p => $V) { + echo '
      • '.__($perm_types[$p]).'
      • '; + } + } + echo '
      '; + } + } + } + + echo '
      '; +} + +dcPage::helpBlock('core_blog_pref'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/blog_theme.php b/v2/dotclear/admin/blog_theme.php new file mode 100644 index 0000000..d463379 --- /dev/null +++ b/v2/dotclear/admin/blog_theme.php @@ -0,0 +1,363 @@ +themes = new dcThemes($core); +$core->themes->loadModules($core->blog->themes_path,null); + +# Theme screenshot +if (!empty($_GET['shot']) && $core->themes->moduleExists($_GET['shot'])) +{ + if (empty($_GET['src'])) { + $f = $core->blog->themes_path.'/'.$_GET['shot'].'/screenshot.jpg'; + } else { + $f = $core->blog->themes_path.'/'.$_GET['shot'].'/'.path::clean($_GET['src']); + } + + $f = path::real($f); + + if (!file_exists($f)) { + $f = dirname(__FILE__).'/images/noscreenshot.png'; + } + + http::cache(array_merge(array($f),get_included_files())); + + header('Content-Type: '.files::getMimeType($f)); + header('Content-Length: '.filesize($f)); + readfile($f); + + exit; +} + +$can_install = $core->auth->isSuperAdmin(); +$is_writable = is_dir($core->blog->themes_path) && is_writable($core->blog->themes_path); +$default_tab = 'themes-list'; + +# Selecting theme +if (!empty($_POST['theme']) && !empty($_POST['select']) && empty($_REQUEST['conf'])) +{ + $core->blog->settings->addNamespace('system'); + $core->blog->settings->system->put('theme',$_POST['theme']); + $core->blog->triggerBlog(); + http::redirect('blog_theme.php?upd=1'); +} + +if ($can_install && !empty($_POST['theme']) && !empty($_POST['remove']) && empty($_REQUEST['conf'])) +{ + try + { + if ($_POST['theme'] == 'default') { + throw new Exception(__('You can\'t remove default theme.')); + } + + if (!$core->themes->moduleExists($_POST['theme'])) { + throw new Exception(__('Theme does not exist.')); + } + + $theme = $core->themes->getModules($_POST['theme']); + + # --BEHAVIOR-- themeBeforeDelete + $core->callBehavior('themeBeforeDelete',$theme); + + $core->themes->deleteModule($_POST['theme']); + + # --BEHAVIOR-- themeAfterDelete + $core->callBehavior('themeAfterDelete',$theme); + + http::redirect('blog_theme.php?del=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Theme upload +if ($can_install && $is_writable && ((!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])) || + (!empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url'])))) +{ + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + if (!empty($_POST['upload_pkg'])) + { + files::uploadStatus($_FILES['pkg_file']); + + $dest = $core->blog->themes_path.'/'.$_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 = $core->blog->themes_path.'/'.basename($url); + + try + { + $client = netHttp::initClient($url,$path); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + } + catch( Exception $e) + { + throw new Exception(__('An error occurred while downloading the file.')); + } + + unset($client); + } + + $ret_code = dcModules::installPackage($dest,$core->themes); + http::redirect('blog_theme.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + $default_tab = 'add-theme'; + } +} + +$theme_conf_mode = false; +if (!empty($_REQUEST['conf'])) +{ + $theme_conf_file = path::real($core->blog->themes_path.'/'.$core->blog->settings->system->theme).'/_config.php'; + if (file_exists($theme_conf_file)) { + $theme_conf_mode = true; + } +} + +function display_theme_details($id,$details,$current) +{ + global $core; + + $screenshot = 'images/noscreenshot.png'; + if (file_exists($core->blog->themes_path.'/'.$id.'/screenshot.jpg')) { + $screenshot = 'blog_theme.php?shot='.rawurlencode($id); + } + + $radio_id = 'theme_'.html::escapeHTML($id); + if (preg_match('#^http(s)?://#',$core->blog->settings->system->themes_url)) { + $theme_url = http::concatURL($core->blog->settings->system->themes_url,'/'.$id); + } else { + $theme_url = http::concatURL($core->blog->url,$core->blog->settings->system->themes_url.'/'.$id); + } + $has_conf = file_exists(path::real($core->blog->themes_path.'/'.$id).'/_config.php'); + $has_css = file_exists(path::real($core->blog->themes_path.'/'.$id).'/style.css'); + $parent = $core->themes->moduleInfo($id,'parent'); + $has_parent = (boolean)$parent; + if ($has_parent) { + $is_parent_present = $core->themes->moduleExists($parent); + } + + $res = + '
      '. + '
      '. + '
      '. + '

      '.form::radio(array('theme',$radio_id),html::escapeHTML($id),$current,'','',($has_parent && !$is_parent_present)).' '. + '

      '. + '

      '.html::escapeHTML($details['desc']).' '. + ''.sprintf(__('by %s'),html::escapeHTML($details['author'])).' '. + ''.sprintf(__('version %s'),html::escapeHTML($details['version'])).' '; + if ($has_parent) { + if ($is_parent_present) { + $res .= ''.sprintf(__('(built on "%s")'),html::escapeHTML($parent)).' '; + } else { + $res .= ''.sprintf(__('(requires "%s")'),html::escapeHTML($parent)).' '; + } + } + if ($has_css) { + $res .= ''.__('Stylesheet').''; + } + $res .= '

      '; + $res .= + '
      '. + '
      '; + if ($current && $has_conf) { + $res .= '

      '.__('Configure theme').'

      '; + } + if ($current) { + # --BEHAVIOR-- adminCurrentThemeDetails + $res .= $core->callBehavior('adminCurrentThemeDetails',$core,$id,$details); + } + $res .= + '
      '. + '
      '; + + return $res; +} + +dcPage::open(__('Blog appearance'), + (!$theme_conf_mode ? dcPage::jsLoad('js/_blog_theme.js') : ''). + dcPage::jsPageTabs($default_tab). + dcPage::jsColorPicker() +); + +if (!$theme_conf_mode) +{ + echo + '

      '.html::escapeHTML($core->blog->name).' › '.__('Blog appearance').'

      '; + + if (!empty($_GET['upd'])) { + echo '

      '.__('Theme has been successfully changed.').'

      '; + } + + if (!empty($_GET['added'])) { + echo '

      '. + ($_GET['added'] == 2 ? __('Theme has been successfully upgraded') : __('Theme has been successfully installed.')). + '

      '; + } + + if (!empty($_GET['del'])) { + echo '

      '.__('Theme has been successfully deleted.').'

      '; + } + + if ($can_install) { + echo + '

      '.sprintf(__('You can find additional themes for your blog on %s.'), + 'Dotaddict').' '. + __('To install or upgrade a theme you generally just need to upload it '. + 'in "Install or upgrade a theme" section.').'

      '; + } + + # Themes list + echo '
      '; + + $themes = $core->themes->getModules(); + if (isset($themes[$core->blog->settings->system->theme])) { + echo '

      '.sprintf(__('You are currently using "%s"'),$themes[$core->blog->settings->system->theme]['name']).'

      '; + } + + echo + '
      '. + '
      '; + + if (isset($themes[$core->blog->settings->system->theme])) { + echo display_theme_details($core->blog->settings->system->theme,$themes[$core->blog->settings->system->theme],true); + } + + foreach ($themes as $k => $v) + { + if ($core->blog->settings->system->theme == $k) { // Current theme + continue; + } + echo display_theme_details($k,$v,false); + } + + echo '
      '; + + echo + '
      '. + $core->formNonce(). + '

      '; + + if ($can_install) { + echo '

      '; + } + + echo + '
      '. + '
      '. + '
      '; + + # Add a new theme + if ($can_install) + { + echo + '
      '; + + if ($is_writable) + { + echo '

      '.__('You can install themes by uploading or downloading zip files.').'

      '; + + # 'Upload theme' form + echo + '
      '. + '
      '. + ''.__('Upload a zip file').''. + '

      '. + '

      '. + ''. + $core->formNonce(). + '
      '. + '
      '; + + # 'Fetch theme' form + echo + '
      '. + '
      '. + ''.__('Download a zip file').''. + '

      '. + '

      '. + ''. + $core->formNonce(). + '
      '. + '
      '; + } + else + { + echo + '

      '. + __('To enable this function, please give write access to your themes directory.'). + '

      '; + } + echo '
      '; + } +} +else +{ + $theme_name = $core->themes->moduleInfo($core->blog->settings->system->theme,'name'); + $core->themes->loadModuleL10Nresources($core->blog->settings->system->theme,$_lang); + echo + '

      '.html::escapeHTML($core->blog->name). + ' › '.__('Blog appearance').''.__('Theme configuration').'

      '. + '

      '.__('back').'

      '; + + try + { + # Let theme configuration set their own form(s) if required + $standalone_config = (boolean) $core->themes->moduleInfo($core->blog->settings->system->theme,'standalone_config'); + + if (!$standalone_config) + echo '
      '; + + include $theme_conf_file; + + if (!$standalone_config) + echo + '

      '. + $core->formNonce().'

      '. + '
      '; + + } + catch (Exception $e) + { + echo '

      '.$e->getMessage().'

      '; + } +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/blogs.php b/v2/dotclear/admin/blogs.php new file mode 100644 index 0000000..ce572d6 --- /dev/null +++ b/v2/dotclear/admin/blogs.php @@ -0,0 +1,185 @@ + 'blog_upddt', +__('Blog name') => 'UPPER(blog_name)', +__('Blog ID') => 'B.blog_id' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + +$q = !empty($_GET['q']) ? $_GET['q'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'blog_upddt'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'desc'; + +$page = !empty($_GET['page']) ? $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = (integer) $_GET['nb']; +} + +$show_filters = false; + +# - Search filter +if ($q) { + $params['q'] = $q; + $show_filters = true; +} + +# - Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + } + + if ($sortby != 'blog_upddt' || $order != 'desc') { + $show_filters = true; + } +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); + +try { + $counter = $core->getBlogs($params,1); + $rs = $core->getBlogs($params); + $nb_blog = $counter->f(0); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = ''; +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} +dcPage::open(__('List of blogs'),$starting_script); + +if (!empty($_GET['del'])) { + echo '

      '.__('Blog has been successfully deleted.').'

      '; +} + +echo '

      '.__('List of blogs').'

      '; + +if (!$core->error->flag()) +{ + if ($core->auth->isSuperAdmin()) { + echo '

      '.__('Create a new blog').'

      '; + } + + if (!$show_filters) { + echo '

      '.__('Filters').'

      '; + } + + echo + '
      '. + '
      '.__('Filters').''. + + '
      '. + '

      '. + '

      '. + '
      '. + + '
      '. + '

      '. + '

      '. + '

      '. + '
      '. + + '
      '. //Opera sucks + '
      '. + '
      '; + + # Show blogs + if ($nb_blog == 0) + { + echo '

      '.__('No blog').'

      '; + } + else + { + $pager = new pager($page,$nb_blog,$nb_per_page,10); + $pager->var_page = 'page'; + + echo '

      '.__('Page(s)').' : '.$pager->getLinks().'

      '; + + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + while ($rs->fetch()) { + echo blogLine($rs); + } + + echo '
      '.__('Blog name').''.__('Last update').''.__('Entries').''.__('Blog ID').' '.__('Status').'
      '; + + echo '

      '.__('Page(s)').' : '.$pager->getLinks().'

      '; + } +} + +dcPage::close(); + +function blogLine($rs) +{ + global $core; + + $blog_id = html::escapeHTML($rs->blog_id); + $edit_link = ''; + + if ($GLOBALS['core']->auth->isSuperAdmin()) { + $edit_link = + ''. + __('edit').''; + } + + $img_status = $rs->blog_status == 1 ? 'check-on' : 'check-off'; + $txt_status = $GLOBALS['core']->getBlogStatus($rs->blog_status); + $img_status = sprintf('%2$s',$img_status,$txt_status); + $offset = dt::getTimeOffset($core->auth->getInfo('user_tz')); + $blog_upddt = dt::str(__('%Y-%m-%d %H:%M'),strtotime($rs->blog_upddt) + $offset); + + return + ''. + ''. + html::escapeHTML($rs->blog_name).''. + ''.$blog_upddt.''. + ''.$core->countBlogPosts($rs->blog_id).''. + ''.$blog_id.''. + ''.$edit_link.''. + ''.$img_status.''. + ''; +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/categories.php b/v2/dotclear/admin/categories.php new file mode 100644 index 0000000..15f0301 --- /dev/null +++ b/v2/dotclear/admin/categories.php @@ -0,0 +1,190 @@ +blog->getCategory((integer) $_POST['del_cat']); + if ($c->isEmpty()) { + throw new Exception(__('This category does not exist.')); + } + unset($c); + $core->blog->delCategory($_POST['del_cat']); + http::redirect('categories.php?del=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Reset order +if (!empty($_POST['reset'])) +{ + try + { + $core->blog->resetCategoriesOrder(); + http::redirect('categories.php?reord=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +/* Display +-------------------------------------------------------- */ +dcPage::open(__('Categories'), + dcPage::jsToolMan()."\n". + dcPage::jsLoad('js/_categories.js') +); + +if (!empty($_GET['add'])) { + echo '

      '.__('The category has been successfully created.').'

      '; +} +if (!empty($_GET['del'])) { + echo '

      '.__('The category has been successfully removed.').'

      '; +} +if (!empty($_GET['reord'])) { + echo '

      '.__('Categories have been successfully reordered.').'

      '; +} +if (!empty($_GET['moved'])) { + echo '

      '.__('The category has been successfully moved.').'

      '; +} + +echo '

      '.html::escapeHTML($core->blog->name).' › '.__('Categories').'

      '; + +$rs = $core->blog->getCategories(array('post_type'=>'post')); + +echo +'
      '. +'
      '; +if ($rs->isEmpty()) +{ + echo '

      '.__('No category yet.').'

      '; +} +else +{ + echo + '

      '.__('Categories list').'

      '. + '
      '; + + $ref_level = $level = $rs->level-1; + while ($rs->fetch()) + { + $attr = 'id="cat'.$rs->cat_id.'"'; + if ($rs->nb_total == 0) { + $attr .= ' class="deletable"'; + } + + if ($rs->level > $level) { + echo str_repeat('
      • ',$rs->level - $level); + } elseif ($rs->level < $level) { + echo str_repeat('
      ',-($rs->level - $level)); + } + + if ($rs->level <= $level) { + echo '
    • '; + } + + echo + '

      '.html::escapeHTML($rs->cat_title).''. + ' ('. + sprintf(($rs->nb_post > 1 ? __('%d entries') : __('%d entry') ),$rs->nb_post).''. + ', '.__('total:').' '.$rs->nb_total.')

      '. + '

      '.__('URL:').' '.html::escapeHTML($rs->cat_url).'

      '; + + $level = $rs->level; + } + + if ($ref_level - $level < 0) { + echo str_repeat('
    • ',-($ref_level - $level)); + } + echo '
      '; +} +echo '
      '; + +echo '
      '. + +'
      '. +'
      '.__('Add a new category').''. +'

      '. +'

      '. +'

      '. +$core->formNonce(). +'
      '. +'
      '; + +if (!$rs->isEmpty()) +{ + $deletable = array(); + $l = $rs->level; + $full_name = array($rs->cat_title); + while ($rs->fetch()) + { + if ($rs->level < $l) { + $full_name = array(); + } elseif ($rs->level == $l) { + array_pop($full_name); + } + $full_name[] = html::escapeHTML($rs->cat_title); + if ($rs->nb_post == 0) { + $deletable[implode(' / ',$full_name)] = $rs->cat_id; + } + $l = $rs->level; + } + + if (count($deletable) > 0) + { + echo + '
      '. + '
      '.__('Remove a category').''. + '

      '. + '

      '. + $core->formNonce(). + '
      '. + '
      '; + } + + echo + '
      '. + '
      '.__('Reorder categories').''. + '

      '.__('This will relocate all categories on the top level').'

      '. + '

      '. + form::hidden(array('reset'),1). + $core->formNonce(). + '
      '. + '
      '; +} +echo '
      '; +echo '
      '; + +dcPage::helpBlock('core_categories'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/category.php b/v2/dotclear/admin/category.php new file mode 100644 index 0000000..d91895a --- /dev/null +++ b/v2/dotclear/admin/category.php @@ -0,0 +1,260 @@ +blog->getCategory($_REQUEST['id']); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if (!$core->error->flag() && !$rs->isEmpty()) + { + $cat_id = (integer) $rs->cat_id; + $cat_title = $rs->cat_title; + $cat_url = $rs->cat_url; + $cat_desc = $rs->cat_desc; + } + unset($rs); + + # Getting hierarchy information + $parents = $core->blog->getCategoryParents($cat_id); + $rs = $core->blog->getCategoryParent($cat_id); + $cat_parent = $rs->isEmpty() ? 0 : (integer) $rs->cat_id; + unset($rs); + + # Allowed parents list + $children = $core->blog->getCategories(array('post_type'=>'post','start'=>$cat_id)); + $allowed_parents = array(__('Top level')=>0); + + $p = array(); + while ($children->fetch()) { + $p[$children->cat_id] = 1; + } + + $rs = $core->blog->getCategories(array('post_type'=>'post')); + while ($rs->fetch()) { + if (!isset($p[$rs->cat_id])) { + $allowed_parents[] = new formSelectOption( + str_repeat('  ',$rs->level-1).($rs->level-1 == 0 ? '' : '• ').html::escapeHTML($rs->cat_title), + $rs->cat_id + ); + } + } + unset($rs); + + # Allowed siblings list + $siblings = array(); + $rs = $core->blog->getCategoryFirstChildren($cat_parent); + while ($rs->fetch()) { + if ($rs->cat_id != $cat_id) { + $siblings[html::escapeHTML($rs->cat_title)] = $rs->cat_id; + } + } + unset($rs); +} + +# Changing parent +if ($cat_id && isset($_POST['cat_parent'])) +{ + $new_parent = (integer) $_POST['cat_parent']; + if ($cat_parent != $new_parent) + { + try { + $core->blog->setCategoryParent($cat_id,$new_parent); + http::redirect('categories.php?moved=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } +} + +# Changing sibling +if ($cat_id && isset($_POST['cat_sibling'])) +{ + try { + $core->blog->setCategoryPosition($cat_id,(integer) $_POST['cat_sibling'],$_POST['cat_move']); + http::redirect('categories.php?moved=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Create or update a category +if (isset($_POST['cat_title'])) +{ + $cur = $core->con->openCursor($core->prefix.'category'); + + $cur->cat_title = $cat_title = $_POST['cat_title']; + + if (isset($_POST['cat_desc'])) { + $cur->cat_desc = $cat_desc = $_POST['cat_desc']; + } + + if (isset($_POST['cat_url'])) { + $cur->cat_url = $cat_url = $_POST['cat_url']; + } else { + $cur->cat_url = $cat_url; + } + + try + { + # Update category + if ($cat_id) + { + # --BEHAVIOR-- adminBeforeCategoryUpdate + $core->callBehavior('adminBeforeCategoryUpdate',$cur,$cat_id); + + $core->blog->updCategory($_POST['id'],$cur); + + # --BEHAVIOR-- adminAfterCategoryUpdate + $core->callBehavior('adminAfterCategoryUpdate',$cur,$cat_id); + + http::redirect('category.php?id='.$_POST['id'].'&upd=1'); + } + # Create category + else + { + # --BEHAVIOR-- adminBeforeCategoryCreate + $core->callBehavior('adminBeforeCategoryCreate',$cur); + + $id = $core->blog->addCategory($cur,(integer) $_POST['new_cat_parent']); + + # --BEHAVIOR-- adminAfterCategoryCreate + $core->callBehavior('adminAfterCategoryCreate',$cur,$id); + + http::redirect('categories.php?add=1'); + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +$title = $cat_id ? html::escapeHTML($cat_title) : __('New category'); + +dcPage::open($title, + dcPage::jsConfirmClose('category-form'). + dcPage::jsToolBar(). + dcPage::jsLoad('js/_category.js') +); + +if (!empty($_GET['upd'])) { + echo '

      '.__('Category has been successfully updated.').'

      '; +} + +echo +'

      '.html::escapeHTML($core->blog->name).' › '. +__('Categories').' › '; + +if ($cat_id) +{ + while($parents->fetch()) { + echo ''.html::escapeHTML($parents->cat_title).''; + echo " › "; + } +} + +echo ''.$title.'

      '; + +echo +'
      '. +'
      '.__('Category information').''. +'

      '; +if (!$cat_id) +{ + $rs = $core->blog->getCategories(array('post_type'=>'post')); + echo + '

      '; + unset($rs); +} +echo +'
      '. +'

      '. +'

      '. +__('Warning: If you set the URL manually, it may conflict with another category.').'

      '. +'
      '. + +'

      '. +form::textarea('cat_desc',50,8,html::escapeHTML($cat_desc)). +'

      '. + +'

      '. +($cat_id ? form::hidden('id',$cat_id) : ''). +$core->formNonce(). +'

      '. +'
      '. +'
      '; + +if ($cat_id) +{ + echo + '

      '.__('Move this category').'

      '. + '
      '. + '
      '. + + '
      '. + '
      '.__('Category parent').''. + '

      '. + '

      '. + form::hidden(array('id'),$cat_id).$core->formNonce().'

      '. + '
      '. + '
      '. + '
      '; + + if (count($siblings) > 0) { + echo + '
      '. + '
      '. + '
      '.__('Category sibling').''. + '

      '. + form::combo('cat_move',array(__('before')=>'before',__('after')=>'after'),'','','',false,'title="'.__('position: ').'"').' '. + form::combo('cat_sibling',$siblings).'

      '. + '

      '. + form::hidden(array('id'),$cat_id).$core->formNonce().'

      '. + '
      '. + '
      '. + '
      '; + } + + echo '
      '; +} + +dcPage::helpBlock('core_categories'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/comment.php b/v2/dotclear/admin/comment.php new file mode 100644 index 0000000..0ef8cec --- /dev/null +++ b/v2/dotclear/admin/comment.php @@ -0,0 +1,236 @@ +blog->getAllCommentStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# Adding comment +if (!empty($_POST['add']) && !empty($_POST['post_id'])) +{ + try + { + $rs = $core->blog->getPosts(array('post_id' => $_POST['post_id'], 'post_type' => '')); + + if ($rs->isEmpty()) { + throw new Exception(__('Entry does not exist.')); + } + + $cur = $core->con->openCursor($core->prefix.'comment'); + + $cur->comment_author = $_POST['comment_author']; + $cur->comment_email = html::clean($_POST['comment_email']); + $cur->comment_site = html::clean($_POST['comment_site']); + $cur->comment_content = $core->HTMLfilter($_POST['comment_content']); + $cur->post_id = (integer) $_POST['post_id']; + + # --BEHAVIOR-- adminBeforeCommentCreate + $core->callBehavior('adminBeforeCommentCreate',$cur); + + $comment_id = $core->blog->addComment($cur); + + # --BEHAVIOR-- adminAfterCommentCreate + $core->callBehavior('adminAfterCommentCreate',$cur,$comment_id); + + http::redirect($core->getPostAdminURL($rs->post_type,$rs->post_id,false).'&co=1&creaco=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +if (!empty($_REQUEST['id'])) +{ + $params['comment_id'] = $_REQUEST['id']; + + try { + $rs = $core->blog->getComments($params); + if (!$rs->isEmpty()) { + $comment_id = $rs->comment_id; + $post_id = $rs->post_id; + $post_type = $rs->post_type; + $post_title = $rs->post_title; + $comment_dt = $rs->comment_dt; + $comment_author = $rs->comment_author; + $comment_email = $rs->comment_email; + $comment_site = $rs->comment_site; + $comment_content = $rs->comment_content; + $comment_ip = $rs->comment_ip; + $comment_status = $rs->comment_status; + $comment_trackback = (boolean) $rs->comment_trackback; + $comment_spam_status = $rs->comment_spam_status; + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +if (!$comment_id && !$core->error->flag()) { + $core->error->add(__('No comment')); +} + +if (!$core->error->flag() && isset($rs)) +{ + $can_edit = $can_delete = $can_publish = $core->auth->check('contentadmin',$core->blog->id); + + if (!$core->auth->check('contentadmin',$core->blog->id) && $core->auth->userID() == $rs->user_id) { + $can_edit = true; + if ($core->auth->check('delete',$core->blog->id)) { + $can_delete = true; + } + if ($core->auth->check('publish',$core->blog->id)) { + $can_publish = true; + } + } + + # update comment + if (!empty($_POST['update']) && $can_edit) + { + $cur = $core->con->openCursor($core->prefix.'comment'); + + $cur->comment_author = $_POST['comment_author']; + $cur->comment_email = html::clean($_POST['comment_email']); + $cur->comment_site = html::clean($_POST['comment_site']); + $cur->comment_content = $core->HTMLfilter($_POST['comment_content']); + + if (isset($_POST['comment_status'])) { + $cur->comment_status = (integer) $_POST['comment_status']; + } + + try + { + # --BEHAVIOR-- adminBeforeCommentUpdate + $core->callBehavior('adminBeforeCommentUpdate',$cur,$comment_id); + + $core->blog->updComment($comment_id,$cur); + + # --BEHAVIOR-- adminAfterCommentUpdate + $core->callBehavior('adminAfterCommentUpdate',$cur,$comment_id); + + http::redirect('comment.php?id='.$comment_id.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + + if (!empty($_POST['delete']) && $can_delete) + { + try { + # --BEHAVIOR-- adminBeforeCommentDelete + $core->callBehavior('adminBeforeCommentDelete',$comment_id); + + $core->blog->delComment($comment_id); + http::redirect($core->getPostAdminURL($rs->post_type,$rs->post_id).'&co=1#c'.$comment_id,false); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$can_edit) { + $core->error->add(__("You can't edit this comment.")); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Edit comment'), + dcPage::jsConfirmClose('comment-form'). + dcPage::jsToolBar(). + dcPage::jsLoad('js/_comment.js'). + # --BEHAVIOR-- adminCommentHeaders + $core->callBehavior('adminCommentHeaders') +); + +if ($comment_id) +{ + if (!empty($_GET['upd'])) { + echo '

      '.__('Comment has been successfully updated.').'

      '; + } + + $comment_mailto = ''; + if ($comment_email) + { + $comment_mailto = 'getPostURL())) + .'">'.__('Send an e-mail').''; + } + + echo '

      '.html::escapeHTML($core->blog->name).' › '. + $post_title.''.__('Edit comment').'

      '; + + echo + '
      '. + '

      '.__('IP address:').'
      '. + ''.$comment_ip.'

      '. + + '

      '.__('Date:').'
      '. + dt::dt2str(__('%Y-%m-%d %H:%M'),$comment_dt).'

      '. + + '

      '. + + '

      '. + + '

      '. + + '

      '. + + # --BEHAVIOR-- adminAfterCommentDesc + $core->callBehavior('adminAfterCommentDesc', $rs). + + '

      '. + form::textarea('comment_content',50,10,html::escapeHTML($comment_content)). + '

      '. + + '

      '.form::hidden('id',$comment_id). + $core->formNonce(). + ' '; + + if ($can_delete) { + echo ''; + } + echo + '

      '. + '
      '; +} + +dcPage::helpBlock('core_comments'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/comments.php b/v2/dotclear/admin/comments.php new file mode 100644 index 0000000..1d1d303 --- /dev/null +++ b/v2/dotclear/admin/comments.php @@ -0,0 +1,251 @@ + '' +); +foreach ($core->blog->getAllCommentStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +$type_combo = array( +'-' => '', +__('comment') => 'co', +__('trackback') => 'tb' +); + +$sortby_combo = array( +__('Date') => 'comment_dt', +__('Entry title') => 'post_title', +__('Author') => 'comment_author', +__('Status') => 'comment_status' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + + +/* Get comments +-------------------------------------------------------- */ +$author = isset($_GET['author']) ? $_GET['author'] : ''; +$status = isset($_GET['status']) ? $_GET['status'] : ''; +$type = !empty($_GET['type']) ? $_GET['type'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'comment_dt'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'desc'; +$ip = !empty($_GET['ip']) ? $_GET['ip'] : ''; + +$with_spam = $author || $status || $type || $sortby != 'comment_dt' || $order != 'desc' || $ip; + +$show_filters = false; + +$page = !empty($_GET['page']) ? (integer) $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + if ($nb_per_page != $_GET['nb']) { + $show_filters = true; + } + $nb_per_page = (integer) $_GET['nb']; +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; + +# Author filter +if ($author !== '') { + $params['q_author'] = $author; + $show_filters = true; +} else { + $author=''; +} + +# - Type filter +if ($type == 'tb' || $type == 'co') { + $params['comment_trackback'] = ($type == 'tb'); + $show_filters = true; +} else { + $type=''; +} + +# - Status filter +if ($status !== '' && in_array($status,$status_combo)) { + $params['comment_status'] = $status; + $show_filters = true; +} elseif (!$with_spam) { + $params['comment_status_not'] = -2; + $status=''; +} else { + $status=''; +} + +# - IP filter +if ($ip) { + $params['comment_ip'] = $ip; + $show_filters = true; +} + +# Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + } else { + $order = 'desc'; + } + + if ($sortby != 'comment_dt' || $order != 'desc') { + $show_filters = true; + } +} else { + $sortby = 'comment_dt'; + $order = 'desc'; +} + +# Actions combo box +$combo_action = array(); +$default = ''; +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; +} +if ($core->auth->check('delete,contentadmin',$core->blog->id)) +{ + $combo_action[__('Delete')] = 'delete'; + if ($status == -2) { + $default = 'delete'; + } +} + +# --BEHAVIOR-- adminCommentsActionsCombo +$core->callBehavior('adminCommentsActionsCombo',array(&$combo_action)); + +/* Get comments +-------------------------------------------------------- */ +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()); +} + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_comments.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} +# --BEHAVIOR-- adminCommentsHeaders +$starting_script .= $core->callBehavior('adminCommentsHeaders'); + +dcPage::open(__('Comments and trackbacks'),$starting_script); + +echo '

      '.html::escapeHTML($core->blog->name).' › '.__('Comments and trackbacks').'

      '; + +if (!$core->error->flag()) +{ + # Filters + if (!$show_filters) { + echo '

      '. + __('Filters').'

      '; + } + + echo + '
      '. + '
      '.__('Filters').''. + '
      '. + '
      '. + ' '. + ''. + '
      '. + + '
      '. + '

      '. + '

      '. + '

      '. + '
      '. + + '
      '. + '

      '. + '

      '. + '

      '. + '
      '. + + '
      '. + '
      '. //Opera sucks + '
      '. + '
      '; + + if (!$with_spam) { + $spam_count = $core->blog->getComments(array('comment_status'=>-2),true)->f(0); + if ($spam_count == 1) { + echo '

      '.sprintf(__('You have one spam comments.'),''.$spam_count.'').' '. + ''.__('Show it.').'

      '; + } elseif ($spam_count > 1) { + echo '

      '.sprintf(__('You have %s spam comments.'),''.$spam_count.'').' '. + ''.__('Show them.').'

      '; + } + } + + # Show comments + $comment_list->display($page,$nb_per_page, + '
      '. + + '%s'. + + '
      '. + '

      '. + + '

      '. + form::combo('action',$combo_action,$default,'','','','title="'.__('action: ').'"'). + $core->formNonce(). + '

      '. + form::hidden(array('type'),$type). + form::hidden(array('sortby'),$sortby). + form::hidden(array('order'),$order). + form::hidden(array('author'),preg_replace('/%/','%%',$author)). + form::hidden(array('status'),$status). + form::hidden(array('ip'),preg_replace('/%/','%%',$ip)). + form::hidden(array('page'),$page). + form::hidden(array('nb'),$nb_per_page). + '
      '. + + '
      ' + ); +} + +dcPage::helpBlock('core_comments'); +dcPage::close(); +?> diff --git a/v2/dotclear/admin/comments_actions.php b/v2/dotclear/admin/comments_actions.php new file mode 100644 index 0000000..c90edb3 --- /dev/null +++ b/v2/dotclear/admin/comments_actions.php @@ -0,0 +1,136 @@ + $v) { + $comments[$k] = (integer) $v; + } + + $params['sql'] = 'AND C.comment_id IN('.implode(',',$comments).') '; + + if (!isset($_POST['full_content']) || empty($_POST['full_content'])) { + $params['no_content'] = true; + } + + $co = $core->blog->getComments($params); + + # --BEHAVIOR-- adminCommentsActions + $core->callBehavior('adminCommentsActions',$core,$co,$action,$redir); + + if (preg_match('/^(publish|unpublish|pending|junk)$/',$action)) + { + switch ($action) { + case 'unpublish' : $status = 0; break; + case 'pending' : $status = -1; break; + case 'junk' : $status = -2; break; + default : $status = 1; break; + } + + while ($co->fetch()) + { + try { + $core->blog->updCommentStatus($co->comment_id,$status); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect($redir); + } + } + elseif ($action == 'delete') + { + while ($co->fetch()) + { + try { + # --BEHAVIOR-- adminBeforeCommentDelete + $core->callBehavior('adminBeforeCommentDelete',$co->comment_id); + + $core->blog->delComment($co->comment_id); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect($redir); + } + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Comments')); + +if (!isset($action)) { + dcPage::close(); + exit; +} + +$hidden_fields = ''; +while ($co->fetch()) { + $hidden_fields .= form::hidden(array('comments[]'),$co->comment_id); +} + +if (isset($_POST['redir']) && strpos($_POST['redir'],'://') === false) +{ + $hidden_fields .= form::hidden(array('redir'),html::escapeURL($_POST['redir'])); +} +else +{ + $hidden_fields .= + form::hidden(array('type'),$_POST['type']). + form::hidden(array('author'),$_POST['author']). + form::hidden(array('status'),$_POST['status']). + form::hidden(array('sortby'),$_POST['sortby']). + form::hidden(array('ip'),$_POST['ip']). + form::hidden(array('order'),$_POST['order']). + form::hidden(array('page'),$_POST['page']). + form::hidden(array('nb'),$_POST['nb']); +} + +# --BEHAVIOR-- adminCommentsActionsContent +$core->callBehavior('adminCommentsActionsContent',$core,$action,$hidden_fields); + +echo '

      '.__('back').'

      '; + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/dispatcher.php b/v2/dotclear/admin/dispatcher.php new file mode 100644 index 0000000..9c923ab --- /dev/null +++ b/v2/dotclear/admin/dispatcher.php @@ -0,0 +1,34 @@ +What the hell are you doing here?

      '; +exit; +?> \ No newline at end of file diff --git a/v2/dotclear/admin/images/add.png b/v2/dotclear/admin/images/add.png new file mode 100644 index 0000000..e892690 Binary files /dev/null and b/v2/dotclear/admin/images/add.png differ diff --git a/v2/dotclear/admin/images/admin.png b/v2/dotclear/admin/images/admin.png new file mode 100644 index 0000000..074b406 Binary files /dev/null and b/v2/dotclear/admin/images/admin.png differ diff --git a/v2/dotclear/admin/images/attach.png b/v2/dotclear/admin/images/attach.png new file mode 100644 index 0000000..f87bc77 Binary files /dev/null and b/v2/dotclear/admin/images/attach.png differ diff --git a/v2/dotclear/admin/images/check-off.png b/v2/dotclear/admin/images/check-off.png new file mode 100644 index 0000000..fc5f661 Binary files /dev/null and b/v2/dotclear/admin/images/check-off.png differ diff --git a/v2/dotclear/admin/images/check-on.png b/v2/dotclear/admin/images/check-on.png new file mode 100644 index 0000000..00a9a7a Binary files /dev/null and b/v2/dotclear/admin/images/check-on.png differ diff --git a/v2/dotclear/admin/images/check-wrn.png b/v2/dotclear/admin/images/check-wrn.png new file mode 100644 index 0000000..fa327f0 Binary files /dev/null and b/v2/dotclear/admin/images/check-wrn.png differ diff --git a/v2/dotclear/admin/images/date-picker.png b/v2/dotclear/admin/images/date-picker.png new file mode 100644 index 0000000..e35e932 Binary files /dev/null and b/v2/dotclear/admin/images/date-picker.png differ diff --git a/v2/dotclear/admin/images/dotclear_pw.png b/v2/dotclear/admin/images/dotclear_pw.png new file mode 100644 index 0000000..593a913 Binary files /dev/null and b/v2/dotclear/admin/images/dotclear_pw.png differ diff --git a/v2/dotclear/admin/images/edit-mini.png b/v2/dotclear/admin/images/edit-mini.png new file mode 100644 index 0000000..890bae6 Binary files /dev/null and b/v2/dotclear/admin/images/edit-mini.png differ diff --git a/v2/dotclear/admin/images/favicon.png b/v2/dotclear/admin/images/favicon.png new file mode 100644 index 0000000..01fa2e2 Binary files /dev/null and b/v2/dotclear/admin/images/favicon.png differ diff --git a/v2/dotclear/admin/images/help.png b/v2/dotclear/admin/images/help.png new file mode 100644 index 0000000..57c87c2 Binary files /dev/null and b/v2/dotclear/admin/images/help.png differ diff --git a/v2/dotclear/admin/images/junk.png b/v2/dotclear/admin/images/junk.png new file mode 100644 index 0000000..7f4b667 Binary files /dev/null and b/v2/dotclear/admin/images/junk.png differ diff --git a/v2/dotclear/admin/images/locker.png b/v2/dotclear/admin/images/locker.png new file mode 100644 index 0000000..cec3260 Binary files /dev/null and b/v2/dotclear/admin/images/locker.png differ diff --git a/v2/dotclear/admin/images/logout.png b/v2/dotclear/admin/images/logout.png new file mode 100644 index 0000000..ce75f91 Binary files /dev/null and b/v2/dotclear/admin/images/logout.png differ diff --git a/v2/dotclear/admin/images/media/audio.png b/v2/dotclear/admin/images/media/audio.png new file mode 100644 index 0000000..fec7e22 Binary files /dev/null and b/v2/dotclear/admin/images/media/audio.png differ diff --git a/v2/dotclear/admin/images/media/blank.png b/v2/dotclear/admin/images/media/blank.png new file mode 100644 index 0000000..0f0c2b9 Binary files /dev/null and b/v2/dotclear/admin/images/media/blank.png differ diff --git a/v2/dotclear/admin/images/media/document.png b/v2/dotclear/admin/images/media/document.png new file mode 100644 index 0000000..5af28a8 Binary files /dev/null and b/v2/dotclear/admin/images/media/document.png differ diff --git a/v2/dotclear/admin/images/media/executable.png b/v2/dotclear/admin/images/media/executable.png new file mode 100644 index 0000000..bb830b8 Binary files /dev/null and b/v2/dotclear/admin/images/media/executable.png differ diff --git a/v2/dotclear/admin/images/media/folder.png b/v2/dotclear/admin/images/media/folder.png new file mode 100644 index 0000000..055a9bb Binary files /dev/null and b/v2/dotclear/admin/images/media/folder.png differ diff --git a/v2/dotclear/admin/images/media/html.png b/v2/dotclear/admin/images/media/html.png new file mode 100644 index 0000000..eed6e04 Binary files /dev/null and b/v2/dotclear/admin/images/media/html.png differ diff --git a/v2/dotclear/admin/images/media/image.png b/v2/dotclear/admin/images/media/image.png new file mode 100644 index 0000000..811e0ed Binary files /dev/null and b/v2/dotclear/admin/images/media/image.png differ diff --git a/v2/dotclear/admin/images/media/package.png b/v2/dotclear/admin/images/media/package.png new file mode 100644 index 0000000..82f5cdf Binary files /dev/null and b/v2/dotclear/admin/images/media/package.png differ diff --git a/v2/dotclear/admin/images/media/presentation.png b/v2/dotclear/admin/images/media/presentation.png new file mode 100644 index 0000000..282d350 Binary files /dev/null and b/v2/dotclear/admin/images/media/presentation.png differ diff --git a/v2/dotclear/admin/images/media/spreadsheet.png b/v2/dotclear/admin/images/media/spreadsheet.png new file mode 100644 index 0000000..8427509 Binary files /dev/null and b/v2/dotclear/admin/images/media/spreadsheet.png differ diff --git a/v2/dotclear/admin/images/media/text.png b/v2/dotclear/admin/images/media/text.png new file mode 100644 index 0000000..1e91806 Binary files /dev/null and b/v2/dotclear/admin/images/media/text.png differ diff --git a/v2/dotclear/admin/images/media/video.png b/v2/dotclear/admin/images/media/video.png new file mode 100644 index 0000000..a0ed12c Binary files /dev/null and b/v2/dotclear/admin/images/media/video.png differ diff --git a/v2/dotclear/admin/images/menu/add_to_favorites.png b/v2/dotclear/admin/images/menu/add_to_favorites.png new file mode 100644 index 0000000..43c807e Binary files /dev/null and b/v2/dotclear/admin/images/menu/add_to_favorites.png differ diff --git a/v2/dotclear/admin/images/menu/blog-pref-b.png b/v2/dotclear/admin/images/menu/blog-pref-b.png new file mode 100644 index 0000000..47ffc03 Binary files /dev/null and b/v2/dotclear/admin/images/menu/blog-pref-b.png differ diff --git a/v2/dotclear/admin/images/menu/blog-pref.png b/v2/dotclear/admin/images/menu/blog-pref.png new file mode 100644 index 0000000..6fa527f Binary files /dev/null and b/v2/dotclear/admin/images/menu/blog-pref.png differ diff --git a/v2/dotclear/admin/images/menu/blog-theme-b.png b/v2/dotclear/admin/images/menu/blog-theme-b.png new file mode 100644 index 0000000..ecbc7c4 Binary files /dev/null and b/v2/dotclear/admin/images/menu/blog-theme-b.png differ diff --git a/v2/dotclear/admin/images/menu/blogs-b.png b/v2/dotclear/admin/images/menu/blogs-b.png new file mode 100644 index 0000000..33904fc Binary files /dev/null and b/v2/dotclear/admin/images/menu/blogs-b.png differ diff --git a/v2/dotclear/admin/images/menu/blogs.png b/v2/dotclear/admin/images/menu/blogs.png new file mode 100644 index 0000000..ab89d06 Binary files /dev/null and b/v2/dotclear/admin/images/menu/blogs.png differ diff --git a/v2/dotclear/admin/images/menu/categories-b.png b/v2/dotclear/admin/images/menu/categories-b.png new file mode 100644 index 0000000..d91c5dd Binary files /dev/null and b/v2/dotclear/admin/images/menu/categories-b.png differ diff --git a/v2/dotclear/admin/images/menu/categories.png b/v2/dotclear/admin/images/menu/categories.png new file mode 100644 index 0000000..0587a86 Binary files /dev/null and b/v2/dotclear/admin/images/menu/categories.png differ diff --git a/v2/dotclear/admin/images/menu/comments-b.png b/v2/dotclear/admin/images/menu/comments-b.png new file mode 100644 index 0000000..2defa40 Binary files /dev/null and b/v2/dotclear/admin/images/menu/comments-b.png differ diff --git a/v2/dotclear/admin/images/menu/comments.png b/v2/dotclear/admin/images/menu/comments.png new file mode 100644 index 0000000..75b1835 Binary files /dev/null and b/v2/dotclear/admin/images/menu/comments.png differ diff --git a/v2/dotclear/admin/images/menu/dashboard.png b/v2/dotclear/admin/images/menu/dashboard.png new file mode 100644 index 0000000..95a9ac4 Binary files /dev/null and b/v2/dotclear/admin/images/menu/dashboard.png differ diff --git a/v2/dotclear/admin/images/menu/edit-b.png b/v2/dotclear/admin/images/menu/edit-b.png new file mode 100644 index 0000000..c386f2a Binary files /dev/null and b/v2/dotclear/admin/images/menu/edit-b.png differ diff --git a/v2/dotclear/admin/images/menu/edit.png b/v2/dotclear/admin/images/menu/edit.png new file mode 100644 index 0000000..516e8ba Binary files /dev/null and b/v2/dotclear/admin/images/menu/edit.png differ diff --git a/v2/dotclear/admin/images/menu/entries-b.png b/v2/dotclear/admin/images/menu/entries-b.png new file mode 100644 index 0000000..b87638b Binary files /dev/null and b/v2/dotclear/admin/images/menu/entries-b.png differ diff --git a/v2/dotclear/admin/images/menu/entries.png b/v2/dotclear/admin/images/menu/entries.png new file mode 100644 index 0000000..2f00706 Binary files /dev/null and b/v2/dotclear/admin/images/menu/entries.png differ diff --git a/v2/dotclear/admin/images/menu/favorite-b.png b/v2/dotclear/admin/images/menu/favorite-b.png new file mode 100644 index 0000000..a2dca4c Binary files /dev/null and b/v2/dotclear/admin/images/menu/favorite-b.png differ diff --git a/v2/dotclear/admin/images/menu/favorite.png b/v2/dotclear/admin/images/menu/favorite.png new file mode 100644 index 0000000..0c98780 Binary files /dev/null and b/v2/dotclear/admin/images/menu/favorite.png differ diff --git a/v2/dotclear/admin/images/menu/langs-b.png b/v2/dotclear/admin/images/menu/langs-b.png new file mode 100644 index 0000000..6f476c6 Binary files /dev/null and b/v2/dotclear/admin/images/menu/langs-b.png differ diff --git a/v2/dotclear/admin/images/menu/langs.png b/v2/dotclear/admin/images/menu/langs.png new file mode 100644 index 0000000..67a4e99 Binary files /dev/null and b/v2/dotclear/admin/images/menu/langs.png differ diff --git a/v2/dotclear/admin/images/menu/media-b.png b/v2/dotclear/admin/images/menu/media-b.png new file mode 100644 index 0000000..b96dbb5 Binary files /dev/null and b/v2/dotclear/admin/images/menu/media-b.png differ diff --git a/v2/dotclear/admin/images/menu/media.png b/v2/dotclear/admin/images/menu/media.png new file mode 100644 index 0000000..f4de5e3 Binary files /dev/null and b/v2/dotclear/admin/images/menu/media.png differ diff --git a/v2/dotclear/admin/images/menu/plugins-b.png b/v2/dotclear/admin/images/menu/plugins-b.png new file mode 100644 index 0000000..c3b5926 Binary files /dev/null and b/v2/dotclear/admin/images/menu/plugins-b.png differ diff --git a/v2/dotclear/admin/images/menu/plugins.png b/v2/dotclear/admin/images/menu/plugins.png new file mode 100644 index 0000000..39995bb Binary files /dev/null and b/v2/dotclear/admin/images/menu/plugins.png differ diff --git a/v2/dotclear/admin/images/menu/search-b.png b/v2/dotclear/admin/images/menu/search-b.png new file mode 100644 index 0000000..6cd92c5 Binary files /dev/null and b/v2/dotclear/admin/images/menu/search-b.png differ diff --git a/v2/dotclear/admin/images/menu/search.png b/v2/dotclear/admin/images/menu/search.png new file mode 100644 index 0000000..bd89177 Binary files /dev/null and b/v2/dotclear/admin/images/menu/search.png differ diff --git a/v2/dotclear/admin/images/menu/themes.png b/v2/dotclear/admin/images/menu/themes.png new file mode 100644 index 0000000..9af61c3 Binary files /dev/null and b/v2/dotclear/admin/images/menu/themes.png differ diff --git a/v2/dotclear/admin/images/menu/update.png b/v2/dotclear/admin/images/menu/update.png new file mode 100644 index 0000000..5ea2c56 Binary files /dev/null and b/v2/dotclear/admin/images/menu/update.png differ diff --git a/v2/dotclear/admin/images/menu/user-pref-b.png b/v2/dotclear/admin/images/menu/user-pref-b.png new file mode 100644 index 0000000..b2facf9 Binary files /dev/null and b/v2/dotclear/admin/images/menu/user-pref-b.png differ diff --git a/v2/dotclear/admin/images/menu/user-pref.png b/v2/dotclear/admin/images/menu/user-pref.png new file mode 100644 index 0000000..b62fb69 Binary files /dev/null and b/v2/dotclear/admin/images/menu/user-pref.png differ diff --git a/v2/dotclear/admin/images/menu/users-b.png b/v2/dotclear/admin/images/menu/users-b.png new file mode 100644 index 0000000..e39152e Binary files /dev/null and b/v2/dotclear/admin/images/menu/users-b.png differ diff --git a/v2/dotclear/admin/images/menu/users.png b/v2/dotclear/admin/images/menu/users.png new file mode 100644 index 0000000..81531fd Binary files /dev/null and b/v2/dotclear/admin/images/menu/users.png differ diff --git a/v2/dotclear/admin/images/menu_off.png b/v2/dotclear/admin/images/menu_off.png new file mode 100644 index 0000000..7bb6523 Binary files /dev/null and b/v2/dotclear/admin/images/menu_off.png differ diff --git a/v2/dotclear/admin/images/menu_on.png b/v2/dotclear/admin/images/menu_on.png new file mode 100644 index 0000000..9b3ccdf Binary files /dev/null and b/v2/dotclear/admin/images/menu_on.png differ diff --git a/v2/dotclear/admin/images/minus.png b/v2/dotclear/admin/images/minus.png new file mode 100644 index 0000000..5baa0f3 Binary files /dev/null and b/v2/dotclear/admin/images/minus.png differ diff --git a/v2/dotclear/admin/images/noscreenshot.png b/v2/dotclear/admin/images/noscreenshot.png new file mode 100644 index 0000000..e9ba66d Binary files /dev/null and b/v2/dotclear/admin/images/noscreenshot.png differ diff --git a/v2/dotclear/admin/images/outgoing-blue.png b/v2/dotclear/admin/images/outgoing-blue.png new file mode 100644 index 0000000..d3a3c93 Binary files /dev/null and b/v2/dotclear/admin/images/outgoing-blue.png differ diff --git a/v2/dotclear/admin/images/outgoing.png b/v2/dotclear/admin/images/outgoing.png new file mode 100644 index 0000000..8a36d2f Binary files /dev/null and b/v2/dotclear/admin/images/outgoing.png differ diff --git a/v2/dotclear/admin/images/picker.png b/v2/dotclear/admin/images/picker.png new file mode 100644 index 0000000..d70e6b2 Binary files /dev/null and b/v2/dotclear/admin/images/picker.png differ diff --git a/v2/dotclear/admin/images/plus.png b/v2/dotclear/admin/images/plus.png new file mode 100644 index 0000000..77229ad Binary files /dev/null and b/v2/dotclear/admin/images/plus.png differ diff --git a/v2/dotclear/admin/images/scheduled.png b/v2/dotclear/admin/images/scheduled.png new file mode 100644 index 0000000..f6e2e9c Binary files /dev/null and b/v2/dotclear/admin/images/scheduled.png differ diff --git a/v2/dotclear/admin/images/selected.png b/v2/dotclear/admin/images/selected.png new file mode 100644 index 0000000..373a85c Binary files /dev/null and b/v2/dotclear/admin/images/selected.png differ diff --git a/v2/dotclear/admin/images/superadmin.png b/v2/dotclear/admin/images/superadmin.png new file mode 100644 index 0000000..a0d9bbf Binary files /dev/null and b/v2/dotclear/admin/images/superadmin.png differ diff --git a/v2/dotclear/admin/images/trash.png b/v2/dotclear/admin/images/trash.png new file mode 100644 index 0000000..3991899 Binary files /dev/null and b/v2/dotclear/admin/images/trash.png differ diff --git a/v2/dotclear/admin/index.php b/v2/dotclear/admin/index.php new file mode 100644 index 0000000..3693e9c --- /dev/null +++ b/v2/dotclear/admin/index.php @@ -0,0 +1,360 @@ +setUserDefaultBlog($core->auth->userID(),$core->blog->id); + http::redirect('index.php'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +dcPage::check('usage,contentadmin'); + +# Logout +if (!empty($_GET['logout'])) { + $core->session->destroy(); + if (isset($_COOKIE['dc_admin'])) { + unset($_COOKIE['dc_admin']); + setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL); + } + http::redirect('auth.php'); + exit; +} + +# Plugin install +$plugins_install = $core->plugins->installModules(); + +# Check dashboard module prefs +$ws = $core->auth->user_prefs->addWorkspace('dashboard'); +if (!$core->auth->user_prefs->dashboard->prefExists('doclinks')) { + if (!$core->auth->user_prefs->dashboard->prefExists('doclinks',true)) { + $core->auth->user_prefs->dashboard->put('doclinks',true,'boolean','',null,true); + } + $core->auth->user_prefs->dashboard->put('doclinks',true,'boolean'); +} +if (!$core->auth->user_prefs->dashboard->prefExists('dcnews')) { + if (!$core->auth->user_prefs->dashboard->prefExists('dcnews',true)) { + $core->auth->user_prefs->dashboard->put('dcnews',true,'boolean','',null,true); + } + $core->auth->user_prefs->dashboard->put('dcnews',true,'boolean'); +} +if (!$core->auth->user_prefs->dashboard->prefExists('quickentry')) { + if (!$core->auth->user_prefs->dashboard->prefExists('quickentry',true)) { + $core->auth->user_prefs->dashboard->put('quickentry',true,'boolean','',null,true); + } + $core->auth->user_prefs->dashboard->put('quickentry',true,'boolean'); +} + +# Dashboard icons +$__dashboard_icons = new ArrayObject(); + +# Dashboard favorites +$post_count = $core->blog->getPosts(array(),true)->f(0); +$str_entries = ($post_count > 1) ? __('%d entries') : __('%d entry'); + +$comment_count = $core->blog->getComments(array(),true)->f(0); +$str_comments = ($comment_count > 1) ? __('%d comments') : __('%d comment'); + +$ws = $core->auth->user_prefs->addWorkspace('favorites'); +$count = 0; +foreach ($ws->dumpPrefs() as $k => $v) { + // User favorites only + if (!$v['global']) { + $fav = unserialize($v['value']); + if (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)) { + $count++; + $title = ($fav['name'] == 'posts' ? sprintf($str_entries,$post_count) : + ($fav['name'] == 'comments' ? sprintf($str_comments,$comment_count) : $fav['title'])); + $__dashboard_icons[$fav['name']] = new ArrayObject(array(__($title),$fav['url'],$fav['large-icon'])); + + # Let plugins set their own title for favorite on dashboard + $core->callBehavior('adminDashboardFavsIcon',$core,$fav['name'],$__dashboard_icons[$fav['name']]); + } + } +} +if (!$count) { + // Global favorites if any + foreach ($ws->dumpPrefs() as $k => $v) { + $fav = unserialize($v['value']); + if (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)) { + $count++; + $title = ($fav['name'] == 'posts' ? sprintf($str_entries,$post_count) : + ($fav['name'] == 'comments' ? sprintf($str_comments,$comment_count) : $fav['title'])); + $__dashboard_icons[$fav['name']] = new ArrayObject(array(__($title),$fav['url'],$fav['large-icon'])); + + # Let plugins set their own title for favorite on dashboard + $core->callBehavior('adminDashboardFavsIcon',$core,$fav['name'],$__dashboard_icons[$fav['name']]); + } + } +} +if (!$count) { + // No user or global favorites, add "user pref" and "new entry" fav + if ($core->auth->check('usage,contentadmin',$core->blog->id)) { + $__dashboard_icons['new_post'] = new ArrayObject(array(__('New entry'),'post.php','images/menu/edit-b.png')); + } + $__dashboard_icons['prefs'] = new ArrayObject(array(__('My preferences'),'preferences.php','images/menu/user-pref-b.png')); +} + +# Latest news for dashboard +$__dashboard_items = new ArrayObject(array(new ArrayObject,new ArrayObject)); + +# Documentation links +$dashboardItem = 0; +if ($core->auth->user_prefs->dashboard->doclinks) { + if (!empty($__resources['doc'])) + { + $doc_links = '

      '.__('Documentation and support').'

        '; + + foreach ($__resources['doc'] as $k => $v) { + $doc_links .= '
      • '.$k.'
      • '; + } + + $doc_links .= '
      '; + $__dashboard_items[$dashboardItem][] = $doc_links; + $dashboardItem++; + } +} + +if ($core->auth->user_prefs->dashboard->dcnews) { + try + { + if (empty($__resources['rss_news'])) { + throw new Exception(); + } + + $feed_reader = new feedReader; + $feed_reader->setCacheDir(DC_TPL_CACHE); + $feed_reader->setTimeout(2); + $feed_reader->setUserAgent('Dotclear - http://www.dotclear.org/'); + $feed = $feed_reader->parse($__resources['rss_news']); + if ($feed) + { + $latest_news = '

      '.__('Latest news').'

      '; + $i = 1; + foreach ($feed->items as $item) + { + $dt = isset($item->link) ? ''.$item->title.'' : $item->title; + + if ($i < 3) { + $latest_news .= + '
      '.$dt.'
      '. + '

      '.dt::dt2str('%d %B %Y',$item->pubdate,'Europe/Paris').': '. + ''.text::cutString(html::clean($item->content),120).'...

      '; + } else { + $latest_news .= + '
      '.$dt.'
      '. + '
      '.dt::dt2str('%d %B %Y',$item->pubdate,'Europe/Paris').'
      '; + } + $i++; + if ($i > 3) { break; } + } + $latest_news .= '
      '; + $__dashboard_items[$dashboardItem][] = $latest_news; + $dashboardItem++; + } + } + catch (Exception $e) {} +} + +$core->callBehavior('adminDashboardItems', $core, $__dashboard_items); + +# Dashboard content +$dashboardContents = ''; +$__dashboard_contents = new ArrayObject(array(new ArrayObject,new ArrayObject)); +$core->callBehavior('adminDashboardContents', $core, $__dashboard_contents); + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('Dashboard'), + dcPage::jsToolBar(). + dcPage::jsLoad('js/_index.js'). + # --BEHAVIOR-- adminDashboardHeaders + $core->callBehavior('adminDashboardHeaders') +); + +echo '

      '.html::escapeHTML($core->blog->name).' › '.__('Dashboard').'

      '; + +if ($core->auth->getInfo('user_default_blog') != $core->blog->id && $core->auth->blog_count > 1) { + echo + '

      '.__('Make this blog my default blog').'

      '; +} + +if ($core->blog->status == 0) { + echo '

      '.__('This blog is offline').'

      '; +} elseif ($core->blog->status == -1) { + echo '

      '.__('This blog is removed').'

      '; +} + +if (!defined('DC_ADMIN_URL') || !DC_ADMIN_URL) { + echo + '

      '. + 'DC_ADMIN_URL '.__('is not defined, you should edit your configuration file.'). + '

      '; +} + +if (!defined('DC_ADMIN_MAILFROM') || !DC_ADMIN_MAILFROM) { + echo + '

      '. + 'DC_ADMIN_MAILFROM '.__('is not defined, you should edit your configuration file.'). + '

      '; +} + +# 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 '
      '; +} + +# Dashboard columns (processed first, as we need to know the result before displaying the icons.) +$dashboardItems = ''; + +# Dotclear updates notifications +if ($core->auth->isSuperAdmin() && is_readable(DC_DIGESTS)) +{ + $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) { + $dashboardItems .= + '

      '.sprintf(__('Dotclear %s is available!'),$new_v).'

      '. + '
      '; + } +} + +# Errors modules notifications +if ($core->auth->isSuperAdmin()) +{ + $list = array(); + foreach ($core->plugins->getErrors() as $k => $error) { + $list[] = '
    • '.$error.'
    • '; + } + + if (count($list) > 0) { + $dashboardItems .= + '

      '.__('Some plugins are installed twice:').'

      '. + '
        '.implode("\n",$list).'
      '; + } + +} + +foreach ($__dashboard_items as $i) +{ + if ($i->count() > 0) + { + $dashboardItems .= '
      '; + foreach ($i as $v) { + $dashboardItems .= $v; + } + $dashboardItems .= '
      '; + } +} + +# Dashboard icons +echo '
      '; +foreach ($__dashboard_icons as $i) +{ + echo + '

      '. + '
      '.$i[0].'

      '; +} +echo '
      '; + +if ($core->auth->user_prefs->dashboard->quickentry) { + if ($core->auth->check('usage,contentadmin',$core->blog->id)) + { + $categories_combo = array(' ' => ''); + try { + $categories = $core->blog->getCategories(array('post_type'=>'post')); + while ($categories->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$categories->level-1). + ($categories->level-1 == 0 ? '' : '• ').html::escapeHTML($categories->cat_title), + $categories->cat_id + ); + } + } catch (Exception $e) { } + + echo + '
      '. + '

      '.__('Quick entry').'

      '. + '
      '. + '
      '.__('New entry').''. + '

      '. + '

      '. + form::textarea('post_content',50,7). + '

      '. + '

      '. + '

      '. + ($core->auth->check('publish',$core->blog->id) + ? '' + : ''). + $core->formNonce(). + form::hidden('post_status',-2). + form::hidden('post_format',$core->auth->getOption('post_format')). + form::hidden('post_excerpt',''). + form::hidden('post_lang',$core->auth->getInfo('user_lang')). + form::hidden('post_notes',''). + '

      '. + '
      '. + '
      '. + '
      '; + } +} + +foreach ($__dashboard_contents as $i) +{ + if ($i->count() > 0) + { + $dashboardContents .= '
      '; + foreach ($i as $v) { + $dashboardContents .= $v; + } + $dashboardContents .= '
      '; + } +} +echo ($dashboardContents ? '
      '.$dashboardContents.'
      ' : ''); + +echo '
      '; + +echo ($dashboardItems ? '
      '.$dashboardItems.'
      ' : ''); + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/install/check.php b/v2/dotclear/admin/install/check.php new file mode 100644 index 0000000..3d4ebc1 --- /dev/null +++ b/v2/dotclear/admin/install/check.php @@ -0,0 +1,83 @@ +driver() == 'mysql') + { + if (version_compare($con->version(),'4.1','<')) + { + $err[] = sprintf(__('MySQL version is %s (4.1 or earlier needed).'),$con->version()); + } + else + { + $rs = $con->select('SHOW ENGINES'); + $innodb = false; + while ($rs->fetch()) { + if (strtolower($rs->f(0)) == 'innodb' && strtolower($rs->f(1)) != 'disabled' && strtolower($rs->f(1)) != 'no') { + $innodb = true; + break; + } + } + + if (!$innodb) { + $err[] = __('MySQL InnoDB engine is not available.'); + } + } + } + elseif ($con->driver() == 'pgsql') + { + if (version_compare($con->version(),'8.0','<')) + { + $err[] = sprintf(__('PostgreSQL version is %s (8.0 or earlier needed).'),$con->version()); + } + } + + return count($err) == 0; +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/install/index.php b/v2/dotclear/admin/install/index.php new file mode 100644 index 0000000..9705b59 --- /dev/null +++ b/v2/dotclear/admin/install/index.php @@ -0,0 +1,401 @@ +'.__('Please set a master key (DC_MASTER_KEY) in configuration file.').'

      '; +} + +# Check if dotclear is already installed +$schema = dbSchema::init($core->con); +if (in_array($core->prefix.'post',$schema->getTables())) { + $can_install = false; + $err = '

      '.__('Dotclear is already installed.').'

      '; +} + +# Check system capabilites +if (!dcSystemCheck($core->con,$_e)) { + $can_install = false; + $err = '

      '.__('Dotclear cannot be installed.').'

      • '.implode('
      • ',$_e).'
      '; +} + +# Get information and perform install +$u_email = $u_firstname = $u_name = $u_login = $u_pwd = ''; +$mail_sent = false; +if ($can_install && !empty($_POST)) +{ + $u_email = !empty($_POST['u_email']) ? $_POST['u_email'] : null; + $u_firstname = !empty($_POST['u_firstname']) ? $_POST['u_firstname'] : null; + $u_name = !empty($_POST['u_name']) ? $_POST['u_name'] : null; + $u_login = !empty($_POST['u_login']) ? $_POST['u_login'] : null; + $u_pwd = !empty($_POST['u_pwd']) ? $_POST['u_pwd'] : null; + $u_pwd2 = !empty($_POST['u_pwd2']) ? $_POST['u_pwd2'] : null; + + try + { + # Check user information + if (empty($u_login)) { + throw new Exception(__('No user ID given')); + } + if (!preg_match('/^[A-Za-z0-9@._-]{2,}$/',$u_login)) { + throw new Exception(__('User ID must contain at least 2 characters using letters, numbers or symbols.')); + } + if ($u_email && !text::isEmail($u_email)) { + throw new Exception(__('Invalid email address')); + } + + if (empty($u_pwd)) { + throw new Exception(__('No password given')); + } + if ($u_pwd != $u_pwd2) { + throw new Exception(__("Passwords don't match")); + } + if (strlen($u_pwd) < 6) { + throw new Exception(__('Password must contain at least 6 characters.')); + } + + # Try to guess timezone + $default_tz = 'Europe/London'; + if (!empty($_POST['u_date']) && function_exists('timezone_open')) + { + if (preg_match('/\((.+)\)$/',$_POST['u_date'],$_tz)) { + $_tz = $_tz[1]; + $_tz = @timezone_open($_tz); + if ($_tz instanceof DateTimeZone) { + $_tz = @timezone_name_get($_tz); + if ($_tz) { + $default_tz = $_tz; + } + } + unset($_tz); + } + } + + # Create schema + $_s = new dbStruct($core->con,$core->prefix); + require dirname(__FILE__).'/../../inc/dbschema/db-schema.php'; + + $si = new dbStruct($core->con,$core->prefix); + $changes = $si->synchronize($_s); + + # Create user + $cur = $core->con->openCursor($core->prefix.'user'); + $cur->user_id = $u_login; + $cur->user_super = 1; + $cur->user_pwd = crypt::hmac(DC_MASTER_KEY,$u_pwd); + $cur->user_name = (string) $u_name; + $cur->user_firstname = (string) $u_firstname; + $cur->user_email = (string) $u_email; + $cur->user_lang = $dlang; + $cur->user_tz = $default_tz; + $cur->user_creadt = date('Y-m-d H:i:s'); + $cur->user_upddt = date('Y-m-d H:i:s'); + $cur->user_options = serialize($core->userDefaults()); + $cur->insert(); + + $core->auth->checkUser($u_login); + + $admin_url = preg_replace('%install/index.php$%','',$_SERVER['REQUEST_URI']); + $root_url = preg_replace('%/admin/install/index.php$%','',$_SERVER['REQUEST_URI']); + + # Create blog + $cur = $core->con->openCursor($core->prefix.'blog'); + $cur->blog_id = 'default'; + $cur->blog_url = http::getHost().$root_url.'/index.php?'; + $cur->blog_name = __('My first blog'); + $core->addBlog($cur); + $core->blogDefaults($cur->blog_id); + + $blog_settings = new dcSettings($core,'default'); + $blog_settings->addNamespace('system'); + $blog_settings->system->put('blog_timezone',$default_tz); + $blog_settings->system->put('lang',$dlang); + $blog_settings->system->put('public_url',$root_url.'/public'); + $blog_settings->system->put('themes_url',$root_url.'/themes'); + $blog_settings->system->put('date_format',__('%A, %B %e %Y')); + + # Add Dotclear version + $cur = $core->con->openCursor($core->prefix.'version'); + $cur->module = 'core'; + $cur->version = (string) DC_VERSION; + $cur->insert(); + + # Create first post + $core->setBlog('default'); + + $cur = $core->con->openCursor($core->prefix.'post'); + $cur->user_id = $u_login; + $cur->post_format = 'xhtml'; + $cur->post_lang = $dlang; + $cur->post_title = __('Welcome to Dotclear!'); + $cur->post_content = '

      '.__('This is your first entry. When you\'re ready '. + 'to blog, log in to edit or delete it.').'

      '; + $cur->post_content_xhtml = $cur->post_content; + $cur->post_status = 1; + $cur->post_open_comment = 1; + $cur->post_open_tb = 0; + $post_id = $core->blog->addPost($cur); + + # Add a comment to it + $cur = $core->con->openCursor($core->prefix.'comment'); + $cur->post_id = $post_id; + $cur->comment_tz = $default_tz; + $cur->comment_author = __('Dotclear Team'); + $cur->comment_email = 'contact@dotclear.net'; + $cur->comment_site = 'http://www.dotclear.org/'; + $cur->comment_content = __("

      This is a comment.

      \n

      To delete it, log in and ". + "view your blog's comments. Then you might remove or edit it.

      "); + $core->blog->addComment($cur); + + # Plugins initialization + define('DC_CONTEXT_ADMIN',true); + $core->plugins->loadModules(DC_PLUGINS_ROOT); + $plugins_install = $core->plugins->installModules(); + + # Add dashboard module options + $core->auth->user_prefs->addWorkspace('dashboard'); + $core->auth->user_prefs->dashboard->put('doclinks',true,'boolean','',null,true); + $core->auth->user_prefs->dashboard->put('dcnews',true,'boolean','',null,true); + $core->auth->user_prefs->dashboard->put('quickentry',true,'boolean','',null,true); + + # Add accessibility options + $core->auth->user_prefs->addWorkspace('accessibility'); + $core->auth->user_prefs->accessibility->put('nodragdrop',false,'boolean','',null,true); + + # Add user interface options + $core->auth->user_prefs->addWorkspace('interface'); + $core->auth->user_prefs->interface->put('enhanceduploader',false,'boolean','',null,true); + + # Add default favorites + $core->auth->user_prefs->addWorkspace('favorites'); + + $init_fav = array(); + + $init_fav['new_post'] = array('new_post','New entry','post.php', + 'images/menu/edit.png','images/menu/edit-b.png', + 'usage,contentadmin',null,'menu-new-post'); + $init_fav['posts'] = array('posts','Entries','posts.php', + 'images/menu/entries.png','images/menu/entries-b.png', + 'usage,contentadmin',null,null); + $init_fav['comments'] = array('comments','Comments','comments.php', + 'images/menu/comments.png','images/menu/comments-b.png', + 'usage,contentadmin',null,null); + $init_fav['prefs'] = array('prefs','My preferences','preferences.php', + 'images/menu/user-pref.png','images/menu/user-pref-b.png', + '*',null,null); + $init_fav['blog_pref'] = array('blog_pref','Blog settings','blog_pref.php', + 'images/menu/blog-pref.png','images/menu/blog-pref-b.png', + 'admin',null,null); + $init_fav['blog_theme'] = array('blog_theme','Blog appearance','blog_theme.php', + 'images/menu/themes.png','images/menu/blog-theme-b.png', + 'admin',null,null); + + $init_fav['pages'] = array('pages','Pages','plugin.php?p=pages', + 'index.php?pf=pages/icon.png','index.php?pf=pages/icon-big.png', + 'contentadmin,pages',null,null); + $init_fav['blogroll'] = array('blogroll','Blogroll','plugin.php?p=blogroll', + 'index.php?pf=blogroll/icon-small.png','index.php?pf=blogroll/icon.png', + 'usage,contentadmin',null,null); + + $count = 0; + foreach ($init_fav as $k => $f) { + $t = array('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]); + $core->auth->user_prefs->favorites->put(sprintf("g%03s",$count),serialize($t),'string',null,true,true); + $count++; + } + + $step = 1; + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} + +if (!isset($step)) { + $step = 0; +} +header('Content-Type: text/html; charset=UTF-8'); +?> + + + + + + + + + + <?php echo __('Dotclear Install'); ?> + + + + + + + + +
      +'.__('Dotclear installation').''. +'
      '; + +if (!is_writable(DC_TPL_CACHE)) { + echo '

      '.sprintf(__('Cache directory %s is not writable.'),DC_TPL_CACHE).'

      '; +} + +if ($can_install && !empty($err)) { + echo '

      '.__('Errors:').'

      '.$err.'
      '; +} + +if (!empty($_GET['wiz'])) { + echo '

      '.__('Configuration file has been successfully created.').'

      '; +} + +if ($can_install && $step == 0) +{ + echo + '

      '.__('User information').'

      '. + + '

      '.__('Please provide the following information needed to create the first user.').'

      '. + + '
      '. + '
      '.__('User information').''. + '

      '. + '

      '. + '

      '. + '
      '. + + '
      '.__('Username and password').''. + '

      '. + '

      '. + '

      '. + '
      '. + + '

      '. + '
      '; +} +elseif ($can_install && $step == 1) +{ + # Plugins install messages + $plugins_install_result = ''; + if (!empty($plugins_install['success'])) + { + $plugins_install_result .= '
      '.__('Following plugins have been installed:').'
        '; + foreach ($plugins_install['success'] as $k => $v) { + $plugins_install_result .= '
      • '.$k.'
      • '; + } + $plugins_install_result .= '
      '; + } + if (!empty($plugins_install['failure'])) + { + $plugins_install_result .= '
      '.__('Following plugins have not been installed:').'
        '; + foreach ($plugins_install['failure'] as $k => $v) { + $plugins_install_result .= '
      • '.$k.' ('.$v.')
      • '; + } + $plugins_install_result .= '
      '; + } + + echo + '

      '.__('All done!').'

      '. + + $plugins_install_result. + + '

      '.__('Dotclear has been successfully installed. Here is some useful information you should keep.').'

      '. + + '

      '.__('Your account').'

      '. + '
        '. + '
      • '.__('Username:').' '.html::escapeHTML($u_login).'
      • '. + '
      • '.__('Password:').' '.html::escapeHTML($u_pwd).'
      • '. + '
      '. + + '

      '.__('Your blog').'

      '. + '
        '. + '
      • '.__('Blog address:').' '.html::escapeHTML(http::getHost().$root_url).'/index.php?
      • '. + '
      • '.__('Administration interface:').' '.html::escapeHTML(http::getHost().$admin_url).'
      • '. + '
      '. + + '
      '. + '

      '. + form::hidden(array('user_id'),html::escapeHTML($u_login)). + form::hidden(array('user_pwd'),html::escapeHTML($u_pwd)). + '

      '. + '
      '; +} +elseif (!$can_install) +{ + echo '

      '.__('Installation can not be completed').'

      '. + '

      '.__('Errors:').'

      '.$err.'
      '. + '

      '.__('For the said reasons, Dotclear can not be installed. '. + 'Please refer to '. + 'the documentation to learn how to correct the problem.').'

      '; +} +?> +
      +
      + + \ No newline at end of file diff --git a/v2/dotclear/admin/install/wizard.php b/v2/dotclear/admin/install/wizard.php new file mode 100644 index 0000000..2f9120e --- /dev/null +++ b/v2/dotclear/admin/install/wizard.php @@ -0,0 +1,190 @@ +' . __($e->getMessage()) . '

      '); + } + + # Checks system capabilites + require dirname(__FILE__).'/check.php'; + if (!dcSystemCheck($con,$_e)) { + $can_install = false; + throw new Exception('

      '.__('Dotclear cannot be installed.').'

      • '.implode('
      • ',$_e).'
      '); + } + + # Check if dotclear is already installed + $schema = dbSchema::init($con); + if (in_array($DBPREFIX.'version',$schema->getTables())) { + throw new Exception(__('Dotclear is already installed.')); + } + + # Does config.php.in exist? + $config_in = dirname(__FILE__).'/../../inc/config.php.in'; + if (!is_file($config_in)) { + throw new Exception(sprintf(__('File %s does not exist.'),$config_in)); + } + + # Can we write config.php + if (!is_writable(dirname(DC_RC_PATH))) { + throw new Exception(sprintf(__('Cannot write %s file.'),DC_RC_PATH)); + } + + # Creates config.php file + $full_conf = file_get_contents($config_in); + + writeConfigValue('DC_DBDRIVER',$DBDRIVER,$full_conf); + writeConfigValue('DC_DBHOST',$DBHOST,$full_conf); + writeConfigValue('DC_DBUSER',$DBUSER,$full_conf); + writeConfigValue('DC_DBPASSWORD',$DBPASSWORD,$full_conf); + writeConfigValue('DC_DBNAME',$DBNAME,$full_conf); + writeConfigValue('DC_DBPREFIX',$DBPREFIX,$full_conf); + + $admin_url = preg_replace('%install/wizard.php$%','',$_SERVER['REQUEST_URI']); + writeConfigValue('DC_ADMIN_URL',http::getHost().$admin_url,$full_conf); + writeConfigValue('DC_ADMIN_MAILFROM','dotclear@'.$_SERVER['HTTP_HOST'],$full_conf); + writeConfigValue('DC_MASTER_KEY',md5(uniqid()),$full_conf); + + $fp = @fopen(DC_RC_PATH,'wb'); + if ($fp === false) { + throw new Exception(sprintf(__('Cannot write %s file.'),DC_RC_PATH)); + } + fwrite($fp,$full_conf); + fclose($fp); + chmod(DC_RC_PATH, 0666); + + $con->close(); + http::redirect('index.php?wiz=1'); + } + catch (Exception $e) + { + $err = $e->getMessage(); + } +} + +function writeConfigValue($name,$val,&$str) +{ + $val = str_replace("'","\'",$val); + $str = preg_replace('/(\''.$name.'\')(.*?)$/ms','$1,\''.$val.'\');',$str); +} + +header('Content-Type: text/html; charset=UTF-8'); +?> + + + + + + + + + + <?php echo __('Dotclear installation wizard'); ?> + + + + +
      +'.__('Dotclear installation wizard').''. +'
      '; + +if (!empty($err)) { + echo '

      '.__('Errors:').'

      '.$err.'
      '; +} else { + echo '

      '.__('Welcome').'

      '. + '

      '.__('To complete your Dotclear installation and start writing on your blog, '. + 'we just need to know how to access your database and who you are. '. + 'Just fill this two steps wizard with this information and we will be done.').'

      '. + '

      '.__('Attention:').' '. + __('this wizard may not function on every host. If it does not work for you, '. + 'please refer to '. + 'the documentation to learn how to create the config.php '. + 'file manually.').'

      '; +} + +echo +'

      '.__('System information').'

      '. + +'

      '.__('Please provide the following information needed to create your configuration file.').'

      '. + +'
      '. +'

      '. +'

      '. +'

      '. +'

      '. +'

      '. +'

      '. + +'

      '. +'
      '; +?> +
      +
      + + \ No newline at end of file diff --git a/v2/dotclear/admin/js/_blog_pref.js b/v2/dotclear/admin/js/_blog_pref.js new file mode 100644 index 0000000..8762ac5 --- /dev/null +++ b/v2/dotclear/admin/js/_blog_pref.js @@ -0,0 +1,4 @@ + +function checkQueryString(){var blogUrl=$('#blog_url')[0].value;var urlScan=$('#url_scan')[0].value;errorMsg='';if(/.*[^\/]$/.exec(blogUrl)&&urlScan=='path_info'){errorMsg=dotclear.msg.warning_path_info;}else if(/.*[^\?]$/.exec(blogUrl)&&urlScan=='query_string'){errorMsg=dotclear.msg.warning_query_string;} +$("p#urlwarning").remove();if(errorMsg!=''){$("#blog_url").parents('p').before('

      '+errorMsg+'

      ');}} +$(function(){checkQueryString();$('#blog_url').focusout(checkQueryString);$('#url_scan').live("change",checkQueryString);}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_blog_theme.js b/v2/dotclear/admin/js/_blog_theme.js new file mode 100644 index 0000000..77e42ec --- /dev/null +++ b/v2/dotclear/admin/js/_blog_theme.js @@ -0,0 +1,6 @@ + +$(function(){$('#themes-actions').hide();var submit_s=$('#themes-actions input[name=select]');var submit_r=$('#themes-actions input[name=remove]');var details=$('#themes div.theme-details');$('div.theme-actions',details).hide();$('input:radio',details).hide();$('div.theme-info span, div.theme-info a',details).hide();details.removeClass('theme-details').addClass('theme-details-js');var themes_wrapper=$('
      ');var theme_box=$('
      ');$('#themes').wrap(themes_wrapper).before(theme_box);details.each(function(){var box=this;var a=$(document.createElement('a'));a.attr('href','#');a.attr('title',$('>div h3>label',this).text());$(box).wrap(a);$(box).parent().click(function(event){update_box(box);event.preventDefault();return false;});});function update_box(e){theme_box.empty();var img=$('div.theme-shot',e).clone();var info=$('div.theme-info',e).clone();if($(e).hasClass('current-theme')){var actions=$('div.theme-actions',e).clone();actions.show();}else{var actions=$('
      ');if(submit_s.length>0&&!$('input:radio',info).attr('disabled')){var select=$(''+dotclear.msg.use_this_theme+'');select.css('font-weight','bold').click(function(){submit_s.click();return false;});actions.append(select).append('  ');} +if(submit_r.length>0&&$('input:radio',info).attr('id')!='theme_default'){var remove=$(''+dotclear.msg.remove_this_theme+'');remove.click(function(){var t_name=$(this).parents('#theme-box').find('div.theme-info h3:first').text();t_name=$.trim(t_name);if(window.confirm(dotclear.msg.confirm_delete_theme.replace('%s',t_name))){submit_r.click();} +return false;});actions.append(remove);}} +$('input:radio',info).remove();$('span, a',info).show();theme_box.append(img).append(info).append(actions);details.removeClass('theme-selected');$(e).addClass('theme-selected');$('input:radio',e).attr('checked','checked');} +update_box(details[0]);}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_categories.js b/v2/dotclear/admin/js/_categories.js new file mode 100644 index 0000000..8e6929e --- /dev/null +++ b/v2/dotclear/admin/js/_categories.js @@ -0,0 +1,2 @@ + +$(function(){$('form#delete-category').submit(function(){var c_id=$('#del_cat').val();var c_name=$('#del_cat option[value='+c_id+']').text();return window.confirm(dotclear.msg.confirm_delete_category.replace('%s',c_name));});});$(function(){$('form#reset-order').submit(function(){return window.confirm(dotclear.msg.confirm_reorder_categories);});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_category.js b/v2/dotclear/admin/js/_category.js new file mode 100644 index 0000000..496a4c4 --- /dev/null +++ b/v2/dotclear/admin/js/_category.js @@ -0,0 +1,2 @@ + +$(function(){dotclear.hideLockable();var tbCategory=new jsToolBar(document.getElementById('cat_desc'));tbCategory.draw('xhtml');}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_comment.js b/v2/dotclear/admin/js/_comment.js new file mode 100644 index 0000000..a46fa52 --- /dev/null +++ b/v2/dotclear/admin/js/_comment.js @@ -0,0 +1,3 @@ + +$(function(){if(!document.getElementById){return;} +var tbComment=new jsToolBar(document.getElementById('comment_content'));tbComment.draw('xhtml');$('#comment-form input[name="delete"]').click(function(){return window.confirm(dotclear.msg.confirm_delete_comment);});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_comments.js b/v2/dotclear/admin/js/_comments.js new file mode 100644 index 0000000..55e0bf5 --- /dev/null +++ b/v2/dotclear/admin/js/_comments.js @@ -0,0 +1,6 @@ + +dotclear.commentExpander=function(line){var td=line.firstChild;var img=document.createElement('img');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;img.className='expand';$(img).css('cursor','pointer');img.line=line;img.onclick=function(){dotclear.viewCommentContent(this,this.line);};td.insertBefore(img,td.firstChild);};dotclear.viewCommentContent=function(img,line){var commentId=line.id.substr(1);var tr=document.getElementById('ce'+commentId);if(!tr){tr=document.createElement('tr');tr.id='ce'+commentId;var td=document.createElement('td');td.colSpan=6;td.className='expand';tr.appendChild(td);img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;$.get('services.php',{f:'getCommentById',id:commentId},function(data){var rsp=$(data).children('rsp')[0];if(rsp.attributes[0].value=='ok'){var comment=$(rsp).find('comment_display_content').text();if(comment){$(td).append(comment);var comment_email=$(rsp).find('comment_email').text();var comment_site=$(rsp).find('comment_site').text();var comment_ip=$(rsp).find('comment_ip').text();var comment_spam_disp=$(rsp).find('comment_spam_disp').text();$(td).append('

      '+dotclear.msg.website+' '+comment_site+'
      '+''+dotclear.msg.email+' '+comment_email+'
      '+''+dotclear.msg.ip_address+' '+comment_ip+''+'
      '+comment_spam_disp+'

      ');}}else{alert($(rsp).find('message').text());}});$(line).toggleClass('expand');line.parentNode.insertBefore(tr,line.nextSibling);} +else if(tr.style.display=='none') +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;} +else +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;}};$(function(){$('#form-comments tr.line').each(function(){dotclear.commentExpander(this);});$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});$('#form-comments td input[type=checkbox]').enableShiftClick();dotclear.commentsActionsHelper();}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_index.js b/v2/dotclear/admin/js/_index.js new file mode 100644 index 0000000..ac78b57 --- /dev/null +++ b/v2/dotclear/admin/js/_index.js @@ -0,0 +1,10 @@ + +$(function(){var f=$('#quick-entry');if(f.length>0){var contentTb=new jsToolBar($('#post_content',f)[0]);contentTb.switchMode($('#post_format',f).val());$('input[name=save]',f).click(function(){quickPost(f,-2);return false;});if($('input[name=save-publish]',f).length>0){var btn=$('');$('input[name=save-publish]',f).remove();$('input[name=save]',f).after(btn).after(' ');btn.click(function(){quickPost(f,1);return false;});} +function quickPost(f,status){if(contentTb.getMode()=='wysiwyg'){contentTb.syncContents('iframe');} +var params={f:'quickPost',xd_check:dotclear.nonce,post_title:$('#post_title',f).val(),post_content:$('#post_content',f).val(),cat_id:$('#cat_id',f).val(),post_status:status,post_format:$('#post_format',f).val(),post_lang:$('#post_lang',f).val()} +$('p.qinfo',f).remove();$.post('services.php',params,function(data){if($('rsp[status=failed]',data).length>0){var msg='

      '+dotclear.msg.error+' '+$('rsp',data).text()+'

      ';}else{var msg='

      '+dotclear.msg.entry_created+' - '+ +dotclear.msg.edit_entry+'';if($('rsp>post',data).attr('post_status')==1){msg+=' - '+ +dotclear.msg.view_entry+'';} +msg+='

      ';$('#post_title',f).val('');$('#post_content',f).val('');if(contentTb.getMode()=='wysiwyg'){contentTb.syncContents('textarea');}} +$('fieldset',f).prepend(msg);});}} +$('#quick h3').toggleWithLegend($('#quick').children().not('h3'),{cookie:'dcx_quick_entry'},positionFooter);}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_langs.js b/v2/dotclear/admin/js/_langs.js new file mode 100644 index 0000000..8824cff --- /dev/null +++ b/v2/dotclear/admin/js/_langs.js @@ -0,0 +1,2 @@ + +$(function(){$('table.plugins form input[type=submit][name=delete]').click(function(){var l_name=$(this).parents('tr.line').find('td:first').text();return window.confirm(dotclear.msg.confirm_delete_lang.replace('%s',l_name));});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_media.js b/v2/dotclear/admin/js/_media.js new file mode 100644 index 0000000..19893c0 --- /dev/null +++ b/v2/dotclear/admin/js/_media.js @@ -0,0 +1,7 @@ + +$(function(){fileRemoveAct();function fileRemoveAct(){$('a.media-remove').click(function(){var m_name=$(this).parents('ul').find('li:first>a').text();if(window.confirm(dotclear.msg.confirm_delete_media.replace('%s',m_name))){var f=$('#media-remove-hide').get(0);f.elements['remove'].value=this.href.replace(/^(.*)&remove=(.*?)(&|$)/,'$2');this.href='';f.submit();} +return false;});} +if(!$.browser.opera){if(dotclear.candyUpload_force_init=='1'){candyUploadInit();}} +function candyUploadInit() +{var candy_upload_success=false;var candy_upload_form_url=$('#media-upload').attr('action')+'&file_sort=date-desc&d='+$('#media-upload input[name=d]').val();var candy_upload_limit=$('#media-upload input[name=MAX_FILE_SIZE]').val();$('#media-upload').candyUpload({upload_url:dotclear.candyUpload.base_url+'/media.php',flash_movie:dotclear.candyUpload.movie_url,file_size_limit:candy_upload_limit+'b',params:'swfupload=1&'+dotclear.candyUpload.params,callbacks:{createControls:function(){var _this=this;this.ctrl.btn_browse.hide();this.ctrl.msg.html(dotclear.msg.load_enhanced_uploader);},flashReady:function(){var _this=this;this.ctrl.msg.fadeOut('fast',function(){$(this).text(_this.locales.no_file_in_queue).fadeIn('fast');_this.ctrl.btn_browse.fadeIn('fast',function(){_this.upldr.container.children().css({width:$('.cu-btn-browse').width(),height:$('.cu-btn-browse').height()});});});},uploadSuccess:function(o,data){if(data=='ok'){candy_upload_success=true;this.fileMsg(o.id,this.locales.file_uploaded);}else{this.fileErrorMsg(o.id,data);} +if(candy_upload_success&&$('div.cu-file:has(span.cu-filecancel a)',this.ctrl.files).length==0){$.cookie('dc_candy_upl','1',{expires:30});$.get(candy_upload_form_url,function(data){var media=$('div.media-list');media.after($('div.media-list',data)).remove();fileRemoveAct();});}},fileQueued:function(){positionFooter();}}});}}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_media_item.js b/v2/dotclear/admin/js/_media_item.js new file mode 100644 index 0000000..c9a2eeb --- /dev/null +++ b/v2/dotclear/admin/js/_media_item.js @@ -0,0 +1,4 @@ + +$(function(){$('#media-details-tab').onetabload(function(){var media_dt=document.getElementById('media_dt');if(media_dt==undefined){return;} +var post_dtPick=new datePicker(media_dt);post_dtPick.img_top='1.5em';post_dtPick.draw();});$('#file-unzip').each(function(){var a=document.createElement('a');var mediaId=$(this).find('input[name=id]').val();var self=$(this);a.href='#';$(a).text(dotclear.msg.zip_file_content);self.before(a);$(a).wrap('

      ');$(a).click(function(){$.get('services.php',{f:'getZipMediaContent',id:mediaId},function(data){var rsp=$(data).children('rsp')[0];if(rsp.attributes[0].value=='ok'){var div=document.createElement('div');var list=document.createElement('ul');var expanded=false;$(div).css({overflow:'auto',border:'1px solid #ccc',margin:'1em 0',padding:'1px 0.5em'});$(div).append(list);self.before(div);$(a).hide();$(div).before('

      '+dotclear.msg.zip_file_content+'

      ');$(rsp).find('file').each(function(){$(list).append('
    • '+$(this).text()+'
    • ');if($(div).height()>200&&!expanded){$(div).css({height:'200px'});expanded=true;}});}else{alert($(rsp).find('message').text());}});return false;});});$('#file-unzip').submit(function(){if($(this).find('#inflate_mode').val()=='current'){return window.confirm(dotclear.msg.confirm_extract_current);} +return true;});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_permissions.js b/v2/dotclear/admin/js/_permissions.js new file mode 100644 index 0000000..1547911 --- /dev/null +++ b/v2/dotclear/admin/js/_permissions.js @@ -0,0 +1,6 @@ + +jQuery.fn.updatePermissionsForm=function(){return this.each(function(){var perms={};var re=/^perm\[(.+?)\]\[(.+?)\]$/;var e,prop;for(var i=0;i'+dotclear.msg.website+' '+comment_site+'
      '+''+dotclear.msg.email+' '+ +comment_email+'
      '+comment_spam_disp+'

      ');}}else{alert($(rsp).find('message').text());}});$(line).toggleClass('expand');line.parentNode.insertBefore(tr,line.nextSibling);} +else if(tr.style.display=='none') +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;} +else +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;}};$(function(){if(!document.getElementById){return;} +if(document.getElementById('edit-entry')) +{var formatField=$('#post_format').get(0);$(formatField).change(function(){excerptTb.switchMode(this.value);contentTb.switchMode(this.value);});var excerptTb=new jsToolBar(document.getElementById('post_excerpt'));var contentTb=new jsToolBar(document.getElementById('post_content'));excerptTb.context=contentTb.context='post';} +if(document.getElementById('comment_content')){var commentTb=new jsToolBar(document.getElementById('comment_content'));} +$('#post-preview').modalWeb($(window).width()-40,$(window).height()-40);$('#edit-entry').onetabload(function(){dotclear.hideLockable();var post_dtPick=new datePicker($('#post_dt').get(0));post_dtPick.img_top='1.5em';post_dtPick.draw();$('input[name="delete"]').click(function(){return window.confirm(dotclear.msg.confirm_delete_post);});$('#notes-area label').toggleWithLegend($('#notes-area').children().not('label'),{cookie:'dcx_post_notes',hide:$('#post_notes').val()==''});$('#post_lang').parent().toggleWithLegend($('#post_lang'),{cookie:'dcx_post_lang'});$('#post_password').parent().toggleWithLegend($('#post_password'),{cookie:'dcx_post_password',hide:$('#post_password').val()==''});$('#excerpt-area label').toggleWithLegend($('#excerpt-area').children().not('label'),{fn:function(){excerptTb.switchMode(formatField.value);},cookie:'dcx_post_excerpt',hide:$('#post_excerpt').val()==''});contentTb.switchMode(formatField.value);$('a.attachment-remove').click(function(){this.href='';var m_name=$(this).parents('ul').find('li:first>a').attr('title');if(window.confirm(dotclear.msg.confirm_remove_attachment.replace('%s',m_name))){var f=$('#attachment-remove-hide').get(0);f.elements['media_id'].value=this.id.substring(11);f.submit();} +return false;});var h=document.createElement('h4');var a=document.createElement('a');a.href='#';$(a).click(function(){var params={xd_check:dotclear.nonce,f:'validatePostMarkup',excerpt:$('#post_excerpt').text(),content:$('#post_content').text(),format:$('#post_format').get(0).value,lang:$('#post_lang').get(0).value};$.post('services.php',params,function(data){if($(data).find('rsp').attr('status')!='ok'){alert($(data).find('rsp message').text());return false;} +if($(data).find('valid').text()==1){var p=document.createElement('p');p.id='markup-validator';if($('#markup-validator').length>0){$('#markup-validator').remove();} +$(p).addClass('message');$(p).text(dotclear.msg.xhtml_valid);$(p).insertAfter(h);$(p).backgroundFade({sColor:'#666666',eColor:'#ffcc00',steps:50},function(){$(this).backgroundFade({sColor:'#ffcc00',eColor:'#666666'});});}else{var div=document.createElement('div');div.id='markup-validator';if($('#markup-validator').length>0){$('#markup-validator').remove();} +$(div).addClass('error');$(div).html('

      '+dotclear.msg.xhtml_not_valid+'

      '+$(data).find('errors').text());$(div).insertAfter(h);$(div).backgroundFade({sColor:'#ffffff',eColor:'#ff9999',steps:50},function(){$(this).backgroundFade({sColor:'#ff9999',eColor:'#ffffff'});});} +return false;});return false;});a.appendChild(document.createTextNode(dotclear.msg.xhtml_validator));h.appendChild(a);$(h).appendTo('#entry-content');var excerpt=$('#post_excerpt').val();var content=$('#post_content').val();$('#convert-xhtml').click(function(){if(excerpt!=$('#post_excerpt').val()||content!=$('#post_content').val()){return window.confirm(dotclear.msg.confirm_change_post_format);}});});$('#comments').onetabload(function(){$('.comments-list tr.line').each(function(){dotclear.commentExpander(this);});$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});dotclear.commentsActionsHelper();});$('#add-comment').onetabload(function(){commentTb.draw('xhtml');});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_posts_list.js b/v2/dotclear/admin/js/_posts_list.js new file mode 100644 index 0000000..2719cca --- /dev/null +++ b/v2/dotclear/admin/js/_posts_list.js @@ -0,0 +1,7 @@ + +dotclear.postExpander=function(line){var td=line.firstChild;var img=document.createElement('img');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;img.className='expand';$(img).css('cursor','pointer');img.line=line;img.onclick=function(){dotclear.viewPostContent(this,this.line);positionFooter();};td.insertBefore(img,td.firstChild);};dotclear.viewPostContent=function(img,line){var postId=line.id.substr(1);var tr=document.getElementById('pe'+postId);if(!tr){tr=document.createElement('tr');tr.id='pe'+postId;var td=document.createElement('td');td.colSpan=8;td.className='expand';tr.appendChild(td);img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;$.get('services.php',{f:'getPostById',id:postId,post_type:''},function(data){var rsp=$(data).children('rsp')[0];if(rsp.attributes[0].value=='ok'){var post=$(rsp).find('post_display_content').text();var post_excerpt=$(rsp).find('post_display_excerpt').text();var res='';if(post){if(post_excerpt){res+=post_excerpt+'
      ';} +res+=post;$(td).append(res);}}else{alert($(rsp).find('message').text());}});$(line).toggleClass('expand');line.parentNode.insertBefore(tr,line.nextSibling);} +else if(tr.style.display=='none') +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_minus_src;img.alt=dotclear.img_minus_alt;} +else +{$(tr).toggle();$(line).toggleClass('expand');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;}};$(function(){$('#form-entries tr.line').each(function(){dotclear.postExpander(this);});$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});$('#form-entries td input[type=checkbox]').enableShiftClick();dotclear.postsActionsHelper();}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_preferences-dragdrop.js b/v2/dotclear/admin/js/_preferences-dragdrop.js new file mode 100644 index 0000000..3599f30 --- /dev/null +++ b/v2/dotclear/admin/js/_preferences-dragdrop.js @@ -0,0 +1,2 @@ + +$(function(){$("#my-favs ul").sortable({'cursor':'move'});$("#my-favs ul").hover(function(){$(this).css({'cursor':'move'});},function(){$(this).css({'cursor':'auto'});});$('#favs-form').submit(function(){var order=[];$("#my-favs ul li input.position").each(function(){order.push(this.name.replace(/^order\[([^\]]+)\]$/,'$1'));});$("input[name=favs_order]")[0].value=order.join(',');return true;});$("#my-favs ul li input.position").hide();}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_preferences.js b/v2/dotclear/admin/js/_preferences.js new file mode 100644 index 0000000..d47d010 --- /dev/null +++ b/v2/dotclear/admin/js/_preferences.js @@ -0,0 +1,5 @@ + +$(function(){if($('#new_pwd').length==0){return;} +var user_email=$('#user_email').val();$('#user-form').submit(function(){var e=this.elements['cur_pwd'];if(e.value!=''){return true;} +if($('#user_email').val()!=user_email||$('#new_pwd').val()!=''){e.focus();$(e).backgroundFade({sColor:'#ffffff',eColor:'#ff9999',steps:50},function(){$(this).backgroundFade({sColor:'#ff9999',eColor:'#ffffff'});});return false;} +return true;});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_trackbacks.js b/v2/dotclear/admin/js/_trackbacks.js new file mode 100644 index 0000000..b3842d7 --- /dev/null +++ b/v2/dotclear/admin/js/_trackbacks.js @@ -0,0 +1,2 @@ + +$(function(){$('#tb_excerpt').keypress(function(){if(this.value.length>255){this.value=this.value.substring(0,255);}});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/_users.js b/v2/dotclear/admin/js/_users.js new file mode 100644 index 0000000..ae2756f --- /dev/null +++ b/v2/dotclear/admin/js/_users.js @@ -0,0 +1,7 @@ + +$(function(){$('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);});$('#form-users').submit(function(){var action=$(this).find('select[name="dispatch_action"]').val();var user_ids=new Array();var nb_posts=new Array();var i;var msg_cannot_delete=false;$(this).find('input[name="user_id[]"]').each(function(){user_ids.push(this);});$(this).find('input[name="nb_post[]"]').each(function(){nb_posts.push(this.value);});if(action=='deleteuser'){for(i=0;i0){if(user_ids[i].checked==true){msg_cannot_delete=true;user_ids[i].checked=false;}}} +if(msg_cannot_delete==true){alert(dotclear.msg.cannot_delete_users);}} +var selectfields=0;for(i=0;iThis.height()){This.css('height',$('body').height()+'px');}};var textToggler=function(o){var i=$(''+p.img_on_alt+'');o.css('cursor','pointer');var hide=true;o.prepend(' ').prepend(i);o.click(function(){$(this).nextAll().each(function(){if($(this).is('h3')){return false;} +$(this).toggle();sizeBox();return true;});hide=!hide;var img=$(this).find('img');if(!hide){img.attr('src',p.img_off_src);}else{img.attr('src',p.img_on_src);}});};this.addClass('help-box');this.find('>hr').remove();this.find('h3').each(function(){textToggler($(this));});this.find('h3:first').nextAll('*:not(h3)').hide();sizeBox();var img=$(''+dotclear.msg.help+'');var select=$();img.click(function(){return toggle();});$('#content').append(img);return this;};var dotclear={msg:{},hideLockable:function(){$('div.lockable').each(function(){var current_lockable_div=this;$(this).find('p.form-note').hide();$(this).find('input').each(function(){this.disabled=true;$(this).width(($(this).width()-14)+'px');var imgE=document.createElement('img');imgE.src='images/locker.png';imgE.style.position='absolute';imgE.style.top='1.7em';imgE.style.left=($(this).width()+4)+'px';$(imgE).css('cursor','pointer');$(imgE).click(function(){$(this).hide();$(this).prev('input').each(function(){this.disabled=false;$(this).width(($(this).width()+14)+'px');});$(current_lockable_div).find('p.form-note').show();});$(this).parent().css('position','relative');$(this).after(imgE);});});},checkboxesHelpers:function(e){var a=document.createElement('a');a.href='#';$(a).append(document.createTextNode(dotclear.msg.select_all));a.onclick=function(){$(this).parents('form').find('input[type="checkbox"]').check();return false;};$(e).append(a);$(e).append(document.createTextNode(' - '));a=document.createElement('a');a.href='#';$(a).append(document.createTextNode(dotclear.msg.no_selection));a.onclick=function(){$(this).parents('form').find('input[type="checkbox"]').unCheck();return false;};$(e).append(a);$(e).append(document.createTextNode(' - '));a=document.createElement('a');a.href='#';$(a).append(document.createTextNode(dotclear.msg.invert_sel));a.onclick=function(){$(this).parents('form').find('input[type="checkbox"]').toggleCheck();return false;};$(e).append(a);},postsActionsHelper:function(){$('#form-entries').submit(function(){var action=$(this).find('select[name="action"]').val();var checked=false;$(this).find('input[name="entries[]"]').each(function(){if(this.checked){checked=true;}});if(!checked){return false;} +if(action=='delete'){return window.confirm(dotclear.msg.confirm_delete_posts.replace('%s',$('input[name="entries[]"]:checked').size()));} +return true;});},commentsActionsHelper:function(){$('#form-comments').submit(function(){var action=$(this).find('select[name="action"]').val();var checked=false;$(this).find('input[name="comments[]"]').each(function(){if(this.checked){checked=true;}});if(!checked){return false;} +if(action=='delete'){return window.confirm(dotclear.msg.confirm_delete_comments.replace('%s',$('input[name="comments[]"]:checked').size()));} +return true;});}};function positionFooter(){$("#wrapper").css({overflow:"auto"});var page_height=$("#top").height()+$("#wrapper").height()-$("#footer").height();if(page_height<$(window).height()){var page_width=$(document).width()-30;$("#footer").css({position:"absolute",bottom:"10px",width:page_width+"px",padding:".75em 0",marginbottom:"0"});}else{$("#footer").css({position:"static",padding:".75em 0",width:"auto"});}} +$(function(){$('#switchblog').change(function(){this.form.submit();});var menu_settings={img_on_src:dotclear.img_menu_off,img_off_src:dotclear.img_menu_on,legend_click:true,speed:100} +$('#blog-menu h3:first').toggleWithLegend($('#blog-menu ul:first'),$.extend({cookie:'dc_blog_menu'},menu_settings));$('#system-menu h3:first').toggleWithLegend($('#system-menu ul:first'),$.extend({cookie:'dc_system_menu'},menu_settings));$('#plugins-menu h3:first').toggleWithLegend($('#plugins-menu ul:first'),$.extend({cookie:'dc_plugins_menu'},menu_settings));$('#favorites-menu h3:first').toggleWithLegend($('#favorites-menu ul:first'),$.extend({cookie:'dc_favorites_menu',hide:false,reverse_cookie:true},menu_settings));$('#help').helpViewer();$('.message').backgroundFade({sColor:'#cccccc',eColor:'#666666',steps:20});$('.error').backgroundFade({sColor:'#f5e5e5',eColor:'#e5bfbf',steps:20});$('form:has(input[type=password][name=your_pwd])').submit(function(){var e=this.elements['your_pwd'];if(e.value==''){e.focus();$(e).backgroundFade({sColor:'#ffffff',eColor:'#ff9999',steps:50},function(){$(this).backgroundFade({sColor:'#ff9999',eColor:'#ffffff'});});return false;} +return true;});positionFooter();$(window).resize(positionFooter);}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/confirm-close.js b/v2/dotclear/admin/js/confirm-close.js new file mode 100644 index 0000000..41489c9 --- /dev/null +++ b/v2/dotclear/admin/js/confirm-close.js @@ -0,0 +1,12 @@ + +function confirmClose(){if(arguments.length>0){for(var i=0;i0){var res=new Array();var f;for(var i=0;i23){h=0;} +if(h<10){h='0'+h;} +this.hour=h*1;this.oHour.value=h;},setMinute:function(m){if(m<0){m=59;} +if(m>59){m=0;} +if(m<10){m='0'+m;} +this.minute=m*1;this.oMinute.value=m;},changeMonth:function(dir){var y=this.year;var m=this.month;m=m+dir;if(m>12){this.month=1;this.year++;} +else if(m<1){this.month=12;this.year--;} +else{this.month=m;} +this.setDate();},changeYear:function(dir){this.year=this.year+dir;this.setDate();},changeHour:function(dir){this.setHour(this.hour*1+dir);},changeMinute:function(dir){this.setMinute(this.minute*1+dir);},sendDate:function(d){var m=this.month;var hour=this.oHour.value*1;var minute=this.oMinute.value*1;if(hour<0||hour>23||isNaN(hour)){hour=0;} +if(minute<0||minute>59||isNaN(minute)){minute=0;} +if(m<10){m='0'+m;} +if(d<10){d='0'+d;} +if(hour<10){hour='0'+hour;} +if(minute<10){minute='0'+minute;} +this.target.value=this.year+'-'+m+'-'+d+' '+hour+':'+minute;this.close();},sendNow:function(){var dt=new Date();var y=dt.getFullYear();var m=dt.getMonth()+1;var d=dt.getDate();var h=dt.getHours();var i=dt.getMinutes();if(m<10){m='0'+m;} +if(d<10){d='0'+d;} +if(h<10){h='0'+h;} +if(i<10){i='0'+i;} +this.target.value=y+'-'+m+'-'+d+' '+h+':'+i;this.close();},close:function(){document.body.removeChild(this.oTable);},numberOfDays:function(){var res=31;if(this.month==4||this.month==6||this.month==9||this.month==11){res=30;}else if(this.month==2){res=28;if(this.year%4==0&&(this.year%100!=0||this.year%400==0)){res=29;}} +return res;},firstDay:function(){var dt=new Date(this.year,this.month-1,1);var res=dt.getDay();if(res==0){res=7;} +return res;},show:function(){var re=/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/;var match=re.exec(this.target.value);if(match){this.year=match[1]*1;this.month=match[2]*1;this.day=match[3]*1;this.hour=match[4]*1;this.minute=match[5]*1;}else{var dt=new Date();this.year=dt.getFullYear();this.month=dt.getMonth()+1;this.day=dt.getDate();this.hour=dt.getHours();this.minute=dt.getMinutes();} +this.oTable.appendChild(this.oBody);this.setDate();this.setPosition();document.body.appendChild(this.oTable);this.oHour.focus();},setPosition:function(){var t_x=this.findPosX(this.target);var t_y=this.findPosY(this.target);var o_h=this.oTable.offsetHeight;var o_w=this.oTable.offsetWidth;this.oTable.style.position='absolute';this.oTable.style.zIndex='100';this.oTable.style.top=t_y+'px';this.oTable.style.left=t_x+'px';},findPosX:function(obj){var curleft=0;if(obj.offsetParent){while(1){curleft+=obj.offsetLeft;if(!obj.offsetParent){break;} +obj=obj.offsetParent;}}else if(obj.x){curleft+=obj.x;} +return curleft;},findPosY:function(obj){var curtop=0;if(obj.offsetParent){while(1){curtop+=obj.offsetTop;if(!obj.offsetParent){break;} +obj=obj.offsetParent;}}else if(obj.y){curtop+=obj.y;} +return curtop;},draw:function(){var imgE=document.createElement('img');imgE.src=this.img_src;imgE.style.position='absolute';imgE.style.top=this.img_top;imgE.style.left=(this.target.clientWidth+4)+'px';imgE.obj=this;imgE.fn=this.show;imgE.onclick=function(){this.fn.apply(this.obj);};this.target.parentNode.style.position='relative';this.target.parentNode.insertBefore(imgE,this.target.nextSibling);}}; \ No newline at end of file diff --git a/v2/dotclear/admin/js/dragsort-tablerows.js b/v2/dotclear/admin/js/dragsort-tablerows.js new file mode 100644 index 0000000..164b5ec --- /dev/null +++ b/v2/dotclear/admin/js/dragsort-tablerows.js @@ -0,0 +1,2 @@ + +ToolMan._dragsortFactory.makeTableSortable=function(table){if(table==null)return;var helpers=ToolMan.helpers();var coordinates=ToolMan.coordinates();var items=table.getElementsByTagName("tr");helpers.map(items,function(item){var dragGroup=dragsort.makeSortable(item);dragGroup.setThreshold(4);var min,max;dragGroup.addTransform(function(coordinate,dragEvent){return coordinate.constrainTo(min,max);});dragGroup.register('dragstart',function(){var items=table.getElementsByTagName("tr");min=max=coordinates.topLeftOffset(items[0]);for(var i=1,n=items.length;i":"","\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/":"","@(namespace|import)[^;\\n]+[;\\n]":"","'(\\\\.|[^'\\\\])*'":encodeString,'"(\\\\.|[^"\\\\])*"':encodeString,"\\s+":" "});function encode(cssText){return encoder.exec(cssText);};function decode(cssText){return cssText.replace(ENCODED,function(match,index){return _strings[index-1];});};function encodeString(string){return"\x01"+_strings.push(string.replace(UNICODE,function(match,chr){return eval("'\\u"+"0000".slice(chr.length)+chr+"'");}).slice(1,-1).replace(QUOTES,"\\'"));};function getString(value){return STRING.test(value)?_strings[value.slice(1)-1]:value;};var rotater=new RegGrp({Width:"Height",width:"height",Left:"Top",left:"top",Right:"Bottom",right:"bottom",onX:"onY"});function rotate(fn){return rotater.exec(fn);};var eventHandlers=[];function addResize(handler){addRecalc(handler);addEventHandler(window,"onresize",handler);};function addEventHandler(element,type,handler){element.attachEvent(type,handler);eventHandlers.push(arguments);};function removeEventHandler(element,type,handler){try{element.detachEvent(type,handler);}catch(ignore){}};addEventHandler(window,"onunload",function(){var handler;while(handler=eventHandlers.pop()){removeEventHandler(handler[0],handler[1],handler[2]);}});function register(handler,element,condition){if(!handler.elements)handler.elements={};if(condition)handler.elements[element.uniqueID]=element;else delete handler.elements[element.uniqueID];return condition;};addEventHandler(window,"onbeforeprint",function(){if(!IE7.CSS.print)new StyleSheet("print");IE7.CSS.print.recalc();});var PIXEL=/^\d+(px)?$/i;var PERCENT=/^\d+%$/;var getPixelValue=function(element,value){if(PIXEL.test(value))return parseInt(value);var style=element.style.left;var runtimeStyle=element.runtimeStyle.left;element.runtimeStyle.left=element.currentStyle.left;element.style.left=value||0;value=element.style.pixelLeft;element.style.left=style;element.runtimeStyle.left=runtimeStyle;return value;};var $IE7="ie7-";var Fix=Base.extend({constructor:function(){this.fixes=[];this.recalcs=[];},init:Undefined});var recalcs=[];function addRecalc(recalc){recalcs.push(recalc);};IE7.recalc=function(){IE7.HTML.recalc();IE7.CSS.recalc();for(var i=0;i1?2:0;var block=cssParser.exec(selectors[i])||"if(0){";if(_wild){block+=format("if(e%1.nodeName!='!'){",_index);} +var store=_duplicate>1?_TEST:"";block+=format(store+_STORE,_index);block+=Array(match(block,/\{/g).length+1).join("}");fn+=block;} +eval(format(_FN,reg)+cssParser.unescape(fn)+"return s?null:r}");_cache[selector]=_selectorFunction;} +return _cache[selector](context||document,single);};var _MSIE5=appVersion<6;var _EVALUATED=/^(href|src)$/;var _ATTRIBUTES={"class":"className","for":"htmlFor"};IE7._indexed=1;IE7._byId=function(document,id){var result=document.all[id]||null;if(!result||result.id==id)return result;for(var i=0;i+~,]|[^(]\+|^)([#.:\[])/g,IMPLIED_SPACE=/(^|,)([^\s>+~])/g,WHITESPACE=/\s*([\s>+~(),]|^|$)\s*/g,WILD_CARD=/\s\*\s/g;;var CSSParser=RegGrp.extend({constructor:function(items){this.base(items);this.sorter=new RegGrp;this.sorter.add(/:not\([^)]*\)/,RegGrp.IGNORE);this.sorter.add(/([ >](\*|[\w-]+))([^: >+~]*)(:\w+-child(\([^)]+\))?)([^: >+~]*)/,"$1$3$6$4");},ignoreCase:true,escape:function(selector){return this.optimise(this.format(selector));},format:function(selector){return selector.replace(WHITESPACE,"$1").replace(IMPLIED_SPACE,"$1 $2").replace(IMPLIED_ASTERISK,"$1*$2");},optimise:function(selector){return this.sorter.exec(selector.replace(WILD_CARD,">* "));},unescape:function(selector){return decode(selector);}});var _OPERATORS={"":"%1!=null","=":"%1=='%2'","~=":/(^| )%1( |$)/,"|=":/^%1(-|$)/,"^=":/^%1/,"$=":/%1$/,"*=":/%1/};var _PSEUDO_CLASSES={"first-child":"!IE7._getPreviousElementSibling(e%1)","link":"e%1.currentStyle['ie7-link']=='link'","visited":"e%1.currentStyle['ie7-link']=='visited'"};var _VAR="var p%2=0,i%2,e%2,n%2=e%1.";var _ID="e%1.sourceIndex";var _TEST="var g="+_ID+";if(!p[g]){p[g]=1;";var _STORE="r[r.length]=e%1;if(s)return e%1;";var _FN="var _selectorFunction=function(e0,s){IE7._indexed++;var r=[],p={},reg=[%1],d=document;";var reg;var _index;var _wild;var _list;var _duplicate;var _cache={};var cssParser=new CSSParser({" (\\*|[\\w-]+)#([\\w-]+)":function(match,tagName,id){_wild=false;var replacement="var e%2=IE7._byId(d,'%4');if(e%2&&";if(tagName!="*")replacement+="e%2.nodeName=='%3'&&";replacement+="(e%1==d||e%1.contains(e%2))){";if(_list)replacement+=format("i%1=n%1.length;",_list);return format(replacement,_index++,_index,tagName.toUpperCase(),id);}," (\\*|[\\w-]+)":function(match,tagName){_duplicate++;_wild=tagName=="*";var replacement=_VAR;replacement+=(_wild&&_MSIE5)?"all":"getElementsByTagName('%3')";replacement+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";return format(replacement,_index++,_list=_index,tagName.toUpperCase());},">(\\*|[\\w-]+)":function(match,tagName){var children=_list;_wild=tagName=="*";var replacement=_VAR;replacement+=children?"children":"childNodes";if(!_wild&&children)replacement+=".tags('%3')";replacement+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";if(_wild){replacement+="if(e%2.nodeType==1){";_wild=_MSIE5;}else{if(!children)replacement+="if(e%2.nodeName=='%3'){";} +return format(replacement,_index++,_list=_index,tagName.toUpperCase());},"\\+(\\*|[\\w-]+)":function(match,tagName){var replacement="";if(_wild)replacement+="if(e%1.nodeName!='!'){";_wild=false;replacement+="e%1=IE7._getNextElementSibling(e%1);if(e%1";if(tagName!="*")replacement+="&&e%1.nodeName=='%2'";replacement+="){";return format(replacement,_index,tagName.toUpperCase());},"~(\\*|[\\w-]+)":function(match,tagName){var replacement="";if(_wild)replacement+="if(e%1.nodeName!='!'){";_wild=false;_duplicate=2;replacement+="while(e%1=e%1.nextSibling){if(e%1.ie7_adjacent==IE7._indexed)break;if(";if(tagName=="*"){replacement+="e%1.nodeType==1";if(_MSIE5)replacement+="&&e%1.nodeName!='!'";}else replacement+="e%1.nodeName=='%2'";replacement+="){e%1.ie7_adjacent=IE7._indexed;";return format(replacement,_index,tagName.toUpperCase());},"#([\\w-]+)":function(match,id){_wild=false;var replacement="if(e%1.id=='%2'){";if(_list)replacement+=format("i%1=n%1.length;",_list);return format(replacement,_index,id);},"\\.([\\w-]+)":function(match,className){_wild=false;reg.push(new RegExp("(^|\\s)"+rescape(className)+"(\\s|$)"));return format("if(e%1.className&®[%2].test(e%1.className)){",_index,reg.length-1);},"\\[([\\w-]+)\\s*([^=]?=)?\\s*([^\\]]*)\\]":function(match,attr,operator,value){var alias=_ATTRIBUTES[attr]||attr;if(operator){var getAttribute="e%1.getAttribute('%2',2)";if(!_EVALUATED.test(attr)){getAttribute="e%1.%3||"+getAttribute;} +attr=format("("+getAttribute+")",_index,attr,alias);}else{attr=format("IE7._getAttribute(e%1,'%2')",_index,attr);} +var replacement=_OPERATORS[operator||""]||"0";if(replacement&&replacement.source){reg.push(new RegExp(format(replacement.source,rescape(cssParser.unescape(value)))));replacement="reg[%2].test(%1)";value=reg.length-1;} +return"if("+format(replacement,attr,value)+"){";},":+([\\w-]+)(\\(([^)]+)\\))?":function(match,pseudoClass,$2,args){pseudoClass=_PSEUDO_CLASSES[pseudoClass];return"if("+(pseudoClass?format(pseudoClass,_index,args||""):"0")+"){";}});var HYPERLINK=/a(#[\w-]+)?(\.[\w-]+)?:(hover|active)/i;var BRACE1=/\s*\{\s*/,BRACE2=/\s*\}\s*/,COMMA=/\s*\,\s*/;var FIRST_LINE_LETTER=/(.*)(:first-(line|letter))/;var styleSheets=document.styleSheets;IE7.CSS=new(Fix.extend({parser:new Parser,screen:"",print:"",styles:[],rules:[],pseudoClasses:appVersion<7?"first\\-child":"",dynamicPseudoClasses:{toString:function(){var strings=[];for(var pseudoClass in this)strings.push(pseudoClass);return strings.join("|");}},init:function(){var NONE="^\x01$";var CLASS="\\[class=?[^\\]]*\\]";var pseudoClasses=[];if(this.pseudoClasses)pseudoClasses.push(this.pseudoClasses);var dynamicPseudoClasses=this.dynamicPseudoClasses.toString();if(dynamicPseudoClasses)pseudoClasses.push(dynamicPseudoClasses);pseudoClasses=pseudoClasses.join("|");var unknown=appVersion<7?["[>+~[(]|([:.])\\w+\\1"]:[CLASS];if(pseudoClasses)unknown.push(":("+pseudoClasses+")");this.UNKNOWN=new RegExp(unknown.join("|")||NONE,"i");var complex=appVersion<7?["\\[[^\\]]+\\]|[^\\s(\\[]+\\s*[+~]"]:[CLASS];var complexRule=complex.concat();if(pseudoClasses)complexRule.push(":("+pseudoClasses+")");Rule.COMPLEX=new RegExp(complexRule.join("|")||NONE,"ig");if(this.pseudoClasses)complex.push(":("+this.pseudoClasses+")");DynamicRule.COMPLEX=new RegExp(complex.join("|")||NONE,"i");DynamicRule.MATCH=new RegExp(dynamicPseudoClasses?"(.*):("+dynamicPseudoClasses+")(.*)":NONE,"i");this.createStyleSheet();this.refresh();},addEventHandler:function(){addEventHandler.apply(null,arguments);},addFix:function(expression,replacement){this.parser.add(expression,replacement);},addRecalc:function(propertyName,test,handler,replacement){test=new RegExp("([{;\\s])"+propertyName+"\\s*:\\s*"+test+"[^;}]*");var id=this.recalcs.length;if(replacement)replacement=propertyName+":"+replacement;this.addFix(test,function(match,$1){return(replacement?$1+replacement:match)+";ie7-"+match.slice(1)+";ie7_recalc"+id+":1";});this.recalcs.push(arguments);return id;},apply:function(){this.getInlineStyles();new StyleSheet("screen");this.trash();},createStyleSheet:function(){this.styleSheet=document.createStyleSheet();this.styleSheet.ie7=true;this.styleSheet.owningElement.ie7=true;this.styleSheet.cssText=HEADER;},getInlineStyles:function(){var styleSheets=document.getElementsByTagName("style"),styleSheet;for(var i=styleSheets.length-1;(styleSheet=styleSheets[i]);i--){if(!styleSheet.disabled&&!styleSheet.ie7){this.styles.push(styleSheet.innerHTML);}}},getText:function(styleSheet,path){try{var cssText=styleSheet.cssText;}catch(e){cssText="";} +if(httpRequest)cssText=loadFile(styleSheet.href,path)||cssText;return cssText;},recalc:function(){this.screen.recalc();var RECALCS=/ie7_recalc\d+/g;var start=HEADER.match(/[{,]/g).length;var stop=start+(this.screen.cssText.match(/\{/g)||"").length;var rules=this.styleSheet.rules,rule;var calcs,calc,elements,element,i,j,k,id;for(i=start;i0&&Rule.CLASS.test(simple)){simple=simple.replace(Rule.CLASS,"");classes--;} +while(tags>0&&Rule.TAG.test(simple)){simple=simple.replace(Rule.TAG,"$1*");tags--;} +simple+="."+this.className;classes=Math.min(classes,2);tags=Math.min(tags,2);var score=-10*classes-tags;if(score>0){simple=simple+","+Rule.MAP[score]+" "+simple;} +return simple;},remove:function(element){element.className=element.className.replace(this.MATCH,"$1");},toString:function(){return format("%1 {%2}",this.selectorText,this.cssText);}},{CHILD:/>/g,CLASS:/\.[\w-]+/,CLASSES:/[.:\[]/g,MULTI:/(\.[\w-]+)+/g,PREFIX:"ie7_class",TAG:/^\w+|([\s>+~])\w+/,TAGS:/^\w|[\s>+~]\w/g,MAP:{1:"html",2:"html body",10:".ie7_html",11:"html.ie7_html",12:"html.ie7_html body",20:".ie7_html .ie7_body",21:"html.ie7_html .ie7_body",22:"html.ie7_html body.ie7_body"}});var DynamicRule=Rule.extend({constructor:function(selector,attach,dynamicPseudoClass,target,cssText){this.attach=attach||"*";this.dynamicPseudoClass=IE7.CSS.dynamicPseudoClasses[dynamicPseudoClass];this.target=target;this.base(selector,cssText);},recalc:function(){var attaches=cssQuery(this.attach),attach;for(var i=0;attach=attaches[i];i++){var target=this.target?cssQuery(this.target,attach):[attach];if(target.length)this.dynamicPseudoClass.apply(attach,target,this);}}});var DynamicPseudoClass=Base.extend({constructor:function(name,apply){this.name=name;this.apply=apply;this.instances={};IE7.CSS.dynamicPseudoClasses[name]=this;},register:function(instance){var _class=instance[2];instance.id=_class.id+instance[0].uniqueID;if(!this.instances[instance.id]){var target=instance[1],j;for(j=0;j*:"+(type=="marginTop"?"first":"last")+"-child",element,true);if(child&&child.currentStyle.styleFloat=="none"&&IE7.hasLayout(child)){collapseMargin(child,type);margin=_getMargin(element,element.currentStyle[type]);childMargin=_getMargin(child,child.currentStyle[type]);if(margin<0||childMargin<0){element.runtimeStyle[type]=margin+childMargin;}else{element.runtimeStyle[type]=Math.max(childMargin,margin);} +child.runtimeStyle[type]="0px";}}};function _getMargin(element,value){return value=="auto"?0:getPixelValue(element,value);};var UNIT=/^[.\d][\w%]*$/,AUTO=/^(auto|0cm)$/;var applyWidth,applyHeight;IE7.Layout.borderBox=function(element){applyWidth(element);applyHeight(element);};var fixWidth=function(HEIGHT){applyWidth=function(element){if(!PERCENT.test(element.currentStyle.width))fixWidth(element);collapseMargins(element);};function fixWidth(element,value){if(!element.runtimeStyle.fixedWidth){if(!value)value=element.currentStyle.width;element.runtimeStyle.fixedWidth=(UNIT.test(value))?Math.max(0,getFixedWidth(element,value)):value;setOverrideStyle(element,"width",element.runtimeStyle.fixedWidth);}};function layoutWidth(element){if(!isFixed(element)){var layoutParent=element.offsetParent;while(layoutParent&&!IE7.hasLayout(layoutParent))layoutParent=layoutParent.offsetParent;} +return(layoutParent||viewport).clientWidth;};function getPixelWidth(element,value){if(PERCENT.test(value))return parseInt(parseFloat(value)/100*layoutWidth(element));return getPixelValue(element,value);};var getFixedWidth=function(element,value){var borderBox=element.currentStyle["box-sizing"]=="border-box";var adjustment=0;if(quirksMode&&!borderBox) +adjustment+=getBorderWidth(element)+getWidth(element,"padding");else if(!quirksMode&&borderBox) +adjustment-=getBorderWidth(element)+getWidth(element,"padding");return getPixelWidth(element,value)+adjustment;};function getBorderWidth(element){return element.offsetWidth-element.clientWidth;};function getWidth(element,type){return getPixelWidth(element,element.currentStyle[type+"Left"])+getPixelWidth(element,element.currentStyle[type+"Right"]);};HEADER+="*{minWidth:none;maxWidth:none;min-width:none;max-width:none}";layout.minWidth=function(element){if(element.currentStyle["min-width"]!=null){element.style.minWidth=element.currentStyle["min-width"];} +if(register(arguments.callee,element,element.currentStyle.minWidth!="none")){layout.boxSizing(element);fixWidth(element);resizeWidth(element);}};eval("IE7.Layout.maxWidth="+String(layout.minWidth).replace(/min/g,"max"));function resizeWidth(element){var rect=element.getBoundingClientRect();var width=rect.right-rect.left;if(element.currentStyle.minWidth!="none"&&width<=getFixedWidth(element,element.currentStyle.minWidth)){element.runtimeStyle.width=element.currentStyle.minWidth;}else if(element.currentStyle.maxWidth!="none"&&width>=getFixedWidth(element,element.currentStyle.maxWidth)){element.runtimeStyle.width=element.currentStyle.maxWidth;}else{element.runtimeStyle.width=element.runtimeStyle.fixedWidth;}};function fixRight(element){if(register(fixRight,element,/^(fixed|absolute)$/.test(element.currentStyle.position)&&getDefinedStyle(element,"left")!="auto"&&getDefinedStyle(element,"right")!="auto"&&AUTO.test(getDefinedStyle(element,"width")))){resizeRight(element);IE7.Layout.boxSizing(element);}};IE7.Layout.fixRight=fixRight;function resizeRight(element){var left=getPixelWidth(element,element.runtimeStyle._left||element.currentStyle.left);var width=layoutWidth(element)-getPixelWidth(element,element.currentStyle.right)-left-getWidth(element,"margin");if(parseInt(element.runtimeStyle.width)==width)return;element.runtimeStyle.width="";if(isFixed(element)||HEIGHT||element.offsetWidth=5.5&&appVersion<7){IE7.CSS.addFix(/background(-image)?\s*:\s*([^};]*)?url\(([^\)]+)\)([^;}]*)?/,function(match,$1,$2,url,$4){url=getString(url);return PNG.test(url)?"filter:"+format(PNG_FILTER,url,"crop")+";zoom:1;background"+($1||"")+":"+($2||"")+"none"+($4||""):match;});IE7.HTML.addRecalc("img,input",function(element){if(element.tagName=="INPUT"&&element.type!="image")return;fixImage(element);addEventHandler(element,"onpropertychange",function(){if(!printing&&event.propertyName=="src"&&element.src.indexOf(BLANK_GIF)==-1)fixImage(element);});});var printing=false;addEventHandler(window,"onbeforeprint",function(){printing=true;for(var i=0;i=7)return;IE7.CSS.addRecalc("position","fixed",_positionFixed,"absolute");IE7.CSS.addRecalc("background(-attachment)?","[^};]*fixed",_backgroundFixed);var $viewport=quirksMode?"body":"documentElement";function _fixBackground(){if(body.currentStyle.backgroundAttachment!="fixed"){if(body.currentStyle.backgroundImage=="none"){body.runtimeStyle.backgroundRepeat="no-repeat";body.runtimeStyle.backgroundImage="url("+BLANK_GIF+")";} +body.runtimeStyle.backgroundAttachment="fixed";} +_fixBackground=Undefined;};var _tmp=createTempElement("img");function _isFixed(element){return element?isFixed(element)||_isFixed(element.parentElement):false;};function _setExpression(element,propertyName,expression){setTimeout("document.all."+element.uniqueID+".runtimeStyle.setExpression('"+propertyName+"','"+expression+"')",0);};function _backgroundFixed(element){if(register(_backgroundFixed,element,element.currentStyle.backgroundAttachment=="fixed"&&!element.contains(body))){_fixBackground();bgLeft(element);bgTop(element);_backgroundPosition(element);}};function _backgroundPosition(element){_tmp.src=element.currentStyle.backgroundImage.slice(5,-2);var parentElement=element.canHaveChildren?element:element.parentElement;parentElement.appendChild(_tmp);setOffsetLeft(element);setOffsetTop(element);parentElement.removeChild(_tmp);};function bgLeft(element){element.style.backgroundPositionX=element.currentStyle.backgroundPositionX;if(!_isFixed(element)){_setExpression(element,"backgroundPositionX","(parseInt(runtimeStyle.offsetLeft)+document."+$viewport+".scrollLeft)||0");}};eval(rotate(bgLeft));function setOffsetLeft(element){var propertyName=_isFixed(element)?"backgroundPositionX":"offsetLeft";element.runtimeStyle[propertyName]=getOffsetLeft(element,element.style.backgroundPositionX)- +element.getBoundingClientRect().left-element.clientLeft+2;};eval(rotate(setOffsetLeft));function getOffsetLeft(element,position){switch(position){case"left":case"top":return 0;case"right":case"bottom":return viewport.clientWidth-_tmp.offsetWidth;case"center":return(viewport.clientWidth-_tmp.offsetWidth)/2;default:if(PERCENT.test(position)){return parseInt((viewport.clientWidth-_tmp.offsetWidth)*parseFloat(position)/100);} +_tmp.style.left=position;return _tmp.offsetLeft;}};eval(rotate(getOffsetLeft));function _positionFixed(element){if(register(_positionFixed,element,isFixed(element))){setOverrideStyle(element,"position","absolute");setOverrideStyle(element,"left",element.currentStyle.left);setOverrideStyle(element,"top",element.currentStyle.top);_fixBackground();IE7.Layout.fixRight(element);_foregroundPosition(element);}};function _foregroundPosition(element,recalc){positionTop(element,recalc);positionLeft(element,recalc,true);if(!element.runtimeStyle.autoLeft&&element.currentStyle.marginLeft=="auto"&&element.currentStyle.right!="auto"){var left=viewport.clientWidth-getPixelWidth(element,element.currentStyle.right)- +getPixelWidth(element,element.runtimeStyle._left)-element.clientWidth;if(element.currentStyle.marginRight=="auto")left=parseInt(left/2);if(_isFixed(element.offsetParent))element.runtimeStyle.pixelLeft+=left;else element.runtimeStyle.shiftLeft=left;} +clipWidth(element);clipHeight(element);};function clipWidth(element){var fixWidth=element.runtimeStyle.fixWidth;element.runtimeStyle.borderRightWidth="";element.runtimeStyle.width=fixWidth?getPixelWidth(element,fixWidth):"";if(element.currentStyle.width!="auto"){var rect=element.getBoundingClientRect();var width=element.offsetWidth-viewport.clientWidth+rect.left-2;if(width>=0){element.runtimeStyle.borderRightWidth="0px";width=Math.max(getPixelValue(element,element.currentStyle.width)-width,0);setOverrideStyle(element,"width",width);return width;}}};eval(rotate(clipWidth));function positionLeft(element,recalc){if(!recalc&&PERCENT.test(element.currentStyle.width)){element.runtimeStyle.fixWidth=element.currentStyle.width;} +if(element.runtimeStyle.fixWidth){element.runtimeStyle.width=getPixelWidth(element,element.runtimeStyle.fixWidth);} +element.runtimeStyle.shiftLeft=0;element.runtimeStyle._left=element.currentStyle.left;element.runtimeStyle.autoLeft=element.currentStyle.right!="auto"&&element.currentStyle.left=="auto";element.runtimeStyle.left="";element.runtimeStyle.screenLeft=getScreenLeft(element);element.runtimeStyle.pixelLeft=element.runtimeStyle.screenLeft;if(!recalc&&!_isFixed(element.offsetParent)){_setExpression(element,"pixelLeft","runtimeStyle.screenLeft+runtimeStyle.shiftLeft+document."+$viewport+".scrollLeft");}};eval(rotate(positionLeft));function getScreenLeft(element){var screenLeft=element.offsetLeft,nested=1;if(element.runtimeStyle.autoLeft){screenLeft=viewport.clientWidth-element.offsetWidth-getPixelWidth(element,element.currentStyle.right);} +if(element.currentStyle.marginLeft!="auto"){screenLeft-=getPixelWidth(element,element.currentStyle.marginLeft);} +while(element=element.offsetParent){if(element.currentStyle.position!="static")nested=-1;screenLeft+=element.offsetLeft*nested;} +return screenLeft;};eval(rotate(getScreenLeft));function getPixelWidth(element,value){return PERCENT.test(value)?parseInt(parseFloat(value)/100*viewport.clientWidth):getPixelValue(element,value);};eval(rotate(getPixelWidth));function _resize(){var elements=_backgroundFixed.elements;for(var i in elements)_backgroundPosition(elements[i]);elements=_positionFixed.elements;for(i in elements){_foregroundPosition(elements[i],true);_foregroundPosition(elements[i],true);} +_timer=0;};var _timer;addResize(function(){if(!_timer)_timer=setTimeout(_resize,0);});};var WRAPPER_STYLE={backgroundColor:"transparent",backgroundImage:"none",backgroundPositionX:null,backgroundPositionY:null,backgroundRepeat:null,borderTopWidth:0,borderRightWidth:0,borderBottomWidth:0,borderLeftStyle:"none",borderTopStyle:"none",borderRightStyle:"none",borderBottomStyle:"none",borderLeftWidth:0,height:null,marginTop:0,marginBottom:0,marginRight:0,marginLeft:0,width:"100%"};IE7.CSS.addRecalc("overflow","visible",function(element){if(element.parentNode.ie7_wrapped)return;if(IE7.Layout&&element.currentStyle["max-height"]!="auto"){IE7.Layout.maxHeight(element);} +if(element.currentStyle.marginLeft=="auto")element.style.marginLeft=0;if(element.currentStyle.marginRight=="auto")element.style.marginRight=0;var wrapper=document.createElement(ANON);wrapper.ie7_wrapped=element;for(var propertyName in WRAPPER_STYLE){wrapper.style[propertyName]=element.currentStyle[propertyName];if(WRAPPER_STYLE[propertyName]!=null){element.runtimeStyle[propertyName]=WRAPPER_STYLE[propertyName];}} +wrapper.style.display="block";wrapper.style.position="relative";element.runtimeStyle.position="absolute";element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(element);});function ie7Quirks(){var FONT_SIZES="xx-small,x-small,small,medium,large,x-large,xx-large".split(",");for(var i=0;i":"","\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/":"","@(namespace|import)[^;\\n]+[;\\n]":"","'(\\\\.|[^'\\\\])*'":encodeString,'"(\\\\.|[^"\\\\])*"':encodeString,"\\s+":" "});function encode(cssText){return encoder.exec(cssText);};function decode(cssText){return cssText.replace(ENCODED,function(match,index){return _strings[index-1];});};function encodeString(string){return"\x01"+_strings.push(string.replace(UNICODE,function(match,chr){return eval("'\\u"+"0000".slice(chr.length)+chr+"'");}).slice(1,-1).replace(QUOTES,"\\'"));};function getString(value){return STRING.test(value)?_strings[value.slice(1)-1]:value;};var rotater=new RegGrp({Width:"Height",width:"height",Left:"Top",left:"top",Right:"Bottom",right:"bottom",onX:"onY"});function rotate(fn){return rotater.exec(fn);};var eventHandlers=[];function addResize(handler){addRecalc(handler);addEventHandler(window,"onresize",handler);};function addEventHandler(element,type,handler){element.attachEvent(type,handler);eventHandlers.push(arguments);};function removeEventHandler(element,type,handler){try{element.detachEvent(type,handler);}catch(ignore){}};addEventHandler(window,"onunload",function(){var handler;while(handler=eventHandlers.pop()){removeEventHandler(handler[0],handler[1],handler[2]);}});function register(handler,element,condition){if(!handler.elements)handler.elements={};if(condition)handler.elements[element.uniqueID]=element;else delete handler.elements[element.uniqueID];return condition;};addEventHandler(window,"onbeforeprint",function(){if(!IE7.CSS.print)new StyleSheet("print");IE7.CSS.print.recalc();});var PIXEL=/^\d+(px)?$/i;var PERCENT=/^\d+%$/;var getPixelValue=function(element,value){if(PIXEL.test(value))return parseInt(value);var style=element.style.left;var runtimeStyle=element.runtimeStyle.left;element.runtimeStyle.left=element.currentStyle.left;element.style.left=value||0;value=element.style.pixelLeft;element.style.left=style;element.runtimeStyle.left=runtimeStyle;return value;};var $IE7="ie7-";var Fix=Base.extend({constructor:function(){this.fixes=[];this.recalcs=[];},init:Undefined});var recalcs=[];function addRecalc(recalc){recalcs.push(recalc);};IE7.recalc=function(){IE7.HTML.recalc();IE7.CSS.recalc();for(var i=0;i1?2:0;var block=cssParser.exec(selectors[i])||"if(0){";if(_wild){block+=format("if(e%1.nodeName!='!'){",_index);} +var store=_duplicate>1?_TEST:"";block+=format(store+_STORE,_index);block+=Array(match(block,/\{/g).length+1).join("}");fn+=block;} +eval(format(_FN,reg)+cssParser.unescape(fn)+"return s?null:r}");_cache[selector]=_selectorFunction;} +return _cache[selector](context||document,single);};var _MSIE5=appVersion<6;var _EVALUATED=/^(href|src)$/;var _ATTRIBUTES={"class":"className","for":"htmlFor"};IE7._indexed=1;IE7._byId=function(document,id){var result=document.all[id]||null;if(!result||result.id==id)return result;for(var i=0;i+~,]|[^(]\+|^)([#.:\[])/g,IMPLIED_SPACE=/(^|,)([^\s>+~])/g,WHITESPACE=/\s*([\s>+~(),]|^|$)\s*/g,WILD_CARD=/\s\*\s/g;;var CSSParser=RegGrp.extend({constructor:function(items){this.base(items);this.sorter=new RegGrp;this.sorter.add(/:not\([^)]*\)/,RegGrp.IGNORE);this.sorter.add(/([ >](\*|[\w-]+))([^: >+~]*)(:\w+-child(\([^)]+\))?)([^: >+~]*)/,"$1$3$6$4");},ignoreCase:true,escape:function(selector){return this.optimise(this.format(selector));},format:function(selector){return selector.replace(WHITESPACE,"$1").replace(IMPLIED_SPACE,"$1 $2").replace(IMPLIED_ASTERISK,"$1*$2");},optimise:function(selector){return this.sorter.exec(selector.replace(WILD_CARD,">* "));},unescape:function(selector){return decode(selector);}});var _OPERATORS={"":"%1!=null","=":"%1=='%2'","~=":/(^| )%1( |$)/,"|=":/^%1(-|$)/,"^=":/^%1/,"$=":/%1$/,"*=":/%1/};var _PSEUDO_CLASSES={"first-child":"!IE7._getPreviousElementSibling(e%1)","link":"e%1.currentStyle['ie7-link']=='link'","visited":"e%1.currentStyle['ie7-link']=='visited'"};var _VAR="var p%2=0,i%2,e%2,n%2=e%1.";var _ID="e%1.sourceIndex";var _TEST="var g="+_ID+";if(!p[g]){p[g]=1;";var _STORE="r[r.length]=e%1;if(s)return e%1;";var _FN="var _selectorFunction=function(e0,s){IE7._indexed++;var r=[],p={},reg=[%1],d=document;";var reg;var _index;var _wild;var _list;var _duplicate;var _cache={};var cssParser=new CSSParser({" (\\*|[\\w-]+)#([\\w-]+)":function(match,tagName,id){_wild=false;var replacement="var e%2=IE7._byId(d,'%4');if(e%2&&";if(tagName!="*")replacement+="e%2.nodeName=='%3'&&";replacement+="(e%1==d||e%1.contains(e%2))){";if(_list)replacement+=format("i%1=n%1.length;",_list);return format(replacement,_index++,_index,tagName.toUpperCase(),id);}," (\\*|[\\w-]+)":function(match,tagName){_duplicate++;_wild=tagName=="*";var replacement=_VAR;replacement+=(_wild&&_MSIE5)?"all":"getElementsByTagName('%3')";replacement+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";return format(replacement,_index++,_list=_index,tagName.toUpperCase());},">(\\*|[\\w-]+)":function(match,tagName){var children=_list;_wild=tagName=="*";var replacement=_VAR;replacement+=children?"children":"childNodes";if(!_wild&&children)replacement+=".tags('%3')";replacement+=";for(i%2=0;(e%2=n%2[i%2]);i%2++){";if(_wild){replacement+="if(e%2.nodeType==1){";_wild=_MSIE5;}else{if(!children)replacement+="if(e%2.nodeName=='%3'){";} +return format(replacement,_index++,_list=_index,tagName.toUpperCase());},"\\+(\\*|[\\w-]+)":function(match,tagName){var replacement="";if(_wild)replacement+="if(e%1.nodeName!='!'){";_wild=false;replacement+="e%1=IE7._getNextElementSibling(e%1);if(e%1";if(tagName!="*")replacement+="&&e%1.nodeName=='%2'";replacement+="){";return format(replacement,_index,tagName.toUpperCase());},"~(\\*|[\\w-]+)":function(match,tagName){var replacement="";if(_wild)replacement+="if(e%1.nodeName!='!'){";_wild=false;_duplicate=2;replacement+="while(e%1=e%1.nextSibling){if(e%1.ie7_adjacent==IE7._indexed)break;if(";if(tagName=="*"){replacement+="e%1.nodeType==1";if(_MSIE5)replacement+="&&e%1.nodeName!='!'";}else replacement+="e%1.nodeName=='%2'";replacement+="){e%1.ie7_adjacent=IE7._indexed;";return format(replacement,_index,tagName.toUpperCase());},"#([\\w-]+)":function(match,id){_wild=false;var replacement="if(e%1.id=='%2'){";if(_list)replacement+=format("i%1=n%1.length;",_list);return format(replacement,_index,id);},"\\.([\\w-]+)":function(match,className){_wild=false;reg.push(new RegExp("(^|\\s)"+rescape(className)+"(\\s|$)"));return format("if(e%1.className&®[%2].test(e%1.className)){",_index,reg.length-1);},"\\[([\\w-]+)\\s*([^=]?=)?\\s*([^\\]]*)\\]":function(match,attr,operator,value){var alias=_ATTRIBUTES[attr]||attr;if(operator){var getAttribute="e%1.getAttribute('%2',2)";if(!_EVALUATED.test(attr)){getAttribute="e%1.%3||"+getAttribute;} +attr=format("("+getAttribute+")",_index,attr,alias);}else{attr=format("IE7._getAttribute(e%1,'%2')",_index,attr);} +var replacement=_OPERATORS[operator||""]||"0";if(replacement&&replacement.source){reg.push(new RegExp(format(replacement.source,rescape(cssParser.unescape(value)))));replacement="reg[%2].test(%1)";value=reg.length-1;} +return"if("+format(replacement,attr,value)+"){";},":+([\\w-]+)(\\(([^)]+)\\))?":function(match,pseudoClass,$2,args){pseudoClass=_PSEUDO_CLASSES[pseudoClass];return"if("+(pseudoClass?format(pseudoClass,_index,args||""):"0")+"){";}});var HYPERLINK=/a(#[\w-]+)?(\.[\w-]+)?:(hover|active)/i;var BRACE1=/\s*\{\s*/,BRACE2=/\s*\}\s*/,COMMA=/\s*\,\s*/;var FIRST_LINE_LETTER=/(.*)(:first-(line|letter))/;var styleSheets=document.styleSheets;IE7.CSS=new(Fix.extend({parser:new Parser,screen:"",print:"",styles:[],rules:[],pseudoClasses:appVersion<7?"first\\-child":"",dynamicPseudoClasses:{toString:function(){var strings=[];for(var pseudoClass in this)strings.push(pseudoClass);return strings.join("|");}},init:function(){var NONE="^\x01$";var CLASS="\\[class=?[^\\]]*\\]";var pseudoClasses=[];if(this.pseudoClasses)pseudoClasses.push(this.pseudoClasses);var dynamicPseudoClasses=this.dynamicPseudoClasses.toString();if(dynamicPseudoClasses)pseudoClasses.push(dynamicPseudoClasses);pseudoClasses=pseudoClasses.join("|");var unknown=appVersion<7?["[>+~[(]|([:.])\\w+\\1"]:[CLASS];if(pseudoClasses)unknown.push(":("+pseudoClasses+")");this.UNKNOWN=new RegExp(unknown.join("|")||NONE,"i");var complex=appVersion<7?["\\[[^\\]]+\\]|[^\\s(\\[]+\\s*[+~]"]:[CLASS];var complexRule=complex.concat();if(pseudoClasses)complexRule.push(":("+pseudoClasses+")");Rule.COMPLEX=new RegExp(complexRule.join("|")||NONE,"ig");if(this.pseudoClasses)complex.push(":("+this.pseudoClasses+")");DynamicRule.COMPLEX=new RegExp(complex.join("|")||NONE,"i");DynamicRule.MATCH=new RegExp(dynamicPseudoClasses?"(.*):("+dynamicPseudoClasses+")(.*)":NONE,"i");this.createStyleSheet();this.refresh();},addEventHandler:function(){addEventHandler.apply(null,arguments);},addFix:function(expression,replacement){this.parser.add(expression,replacement);},addRecalc:function(propertyName,test,handler,replacement){test=new RegExp("([{;\\s])"+propertyName+"\\s*:\\s*"+test+"[^;}]*");var id=this.recalcs.length;if(replacement)replacement=propertyName+":"+replacement;this.addFix(test,function(match,$1){return(replacement?$1+replacement:match)+";ie7-"+match.slice(1)+";ie7_recalc"+id+":1";});this.recalcs.push(arguments);return id;},apply:function(){this.getInlineStyles();new StyleSheet("screen");this.trash();},createStyleSheet:function(){this.styleSheet=document.createStyleSheet();this.styleSheet.ie7=true;this.styleSheet.owningElement.ie7=true;this.styleSheet.cssText=HEADER;},getInlineStyles:function(){var styleSheets=document.getElementsByTagName("style"),styleSheet;for(var i=styleSheets.length-1;(styleSheet=styleSheets[i]);i--){if(!styleSheet.disabled&&!styleSheet.ie7){this.styles.push(styleSheet.innerHTML);}}},getText:function(styleSheet,path){try{var cssText=styleSheet.cssText;}catch(e){cssText="";} +if(httpRequest)cssText=loadFile(styleSheet.href,path)||cssText;return cssText;},recalc:function(){this.screen.recalc();var RECALCS=/ie7_recalc\d+/g;var start=HEADER.match(/[{,]/g).length;var stop=start+(this.screen.cssText.match(/\{/g)||"").length;var rules=this.styleSheet.rules,rule;var calcs,calc,elements,element,i,j,k,id;for(i=start;i0&&Rule.CLASS.test(simple)){simple=simple.replace(Rule.CLASS,"");classes--;} +while(tags>0&&Rule.TAG.test(simple)){simple=simple.replace(Rule.TAG,"$1*");tags--;} +simple+="."+this.className;classes=Math.min(classes,2);tags=Math.min(tags,2);var score=-10*classes-tags;if(score>0){simple=simple+","+Rule.MAP[score]+" "+simple;} +return simple;},remove:function(element){element.className=element.className.replace(this.MATCH,"$1");},toString:function(){return format("%1 {%2}",this.selectorText,this.cssText);}},{CHILD:/>/g,CLASS:/\.[\w-]+/,CLASSES:/[.:\[]/g,MULTI:/(\.[\w-]+)+/g,PREFIX:"ie7_class",TAG:/^\w+|([\s>+~])\w+/,TAGS:/^\w|[\s>+~]\w/g,MAP:{1:"html",2:"html body",10:".ie7_html",11:"html.ie7_html",12:"html.ie7_html body",20:".ie7_html .ie7_body",21:"html.ie7_html .ie7_body",22:"html.ie7_html body.ie7_body"}});var DynamicRule=Rule.extend({constructor:function(selector,attach,dynamicPseudoClass,target,cssText){this.attach=attach||"*";this.dynamicPseudoClass=IE7.CSS.dynamicPseudoClasses[dynamicPseudoClass];this.target=target;this.base(selector,cssText);},recalc:function(){var attaches=cssQuery(this.attach),attach;for(var i=0;attach=attaches[i];i++){var target=this.target?cssQuery(this.target,attach):[attach];if(target.length)this.dynamicPseudoClass.apply(attach,target,this);}}});var DynamicPseudoClass=Base.extend({constructor:function(name,apply){this.name=name;this.apply=apply;this.instances={};IE7.CSS.dynamicPseudoClasses[name]=this;},register:function(instance){var _class=instance[2];instance.id=_class.id+instance[0].uniqueID;if(!this.instances[instance.id]){var target=instance[1],j;for(j=0;j*:"+(type=="marginTop"?"first":"last")+"-child",element,true);if(child&&child.currentStyle.styleFloat=="none"&&IE7.hasLayout(child)){collapseMargin(child,type);margin=_getMargin(element,element.currentStyle[type]);childMargin=_getMargin(child,child.currentStyle[type]);if(margin<0||childMargin<0){element.runtimeStyle[type]=margin+childMargin;}else{element.runtimeStyle[type]=Math.max(childMargin,margin);} +child.runtimeStyle[type]="0px";}}};function _getMargin(element,value){return value=="auto"?0:getPixelValue(element,value);};var UNIT=/^[.\d][\w%]*$/,AUTO=/^(auto|0cm)$/;var applyWidth,applyHeight;IE7.Layout.borderBox=function(element){applyWidth(element);applyHeight(element);};var fixWidth=function(HEIGHT){applyWidth=function(element){if(!PERCENT.test(element.currentStyle.width))fixWidth(element);collapseMargins(element);};function fixWidth(element,value){if(!element.runtimeStyle.fixedWidth){if(!value)value=element.currentStyle.width;element.runtimeStyle.fixedWidth=(UNIT.test(value))?Math.max(0,getFixedWidth(element,value)):value;setOverrideStyle(element,"width",element.runtimeStyle.fixedWidth);}};function layoutWidth(element){if(!isFixed(element)){var layoutParent=element.offsetParent;while(layoutParent&&!IE7.hasLayout(layoutParent))layoutParent=layoutParent.offsetParent;} +return(layoutParent||viewport).clientWidth;};function getPixelWidth(element,value){if(PERCENT.test(value))return parseInt(parseFloat(value)/100*layoutWidth(element));return getPixelValue(element,value);};var getFixedWidth=function(element,value){var borderBox=element.currentStyle["box-sizing"]=="border-box";var adjustment=0;if(quirksMode&&!borderBox) +adjustment+=getBorderWidth(element)+getWidth(element,"padding");else if(!quirksMode&&borderBox) +adjustment-=getBorderWidth(element)+getWidth(element,"padding");return getPixelWidth(element,value)+adjustment;};function getBorderWidth(element){return element.offsetWidth-element.clientWidth;};function getWidth(element,type){return getPixelWidth(element,element.currentStyle[type+"Left"])+getPixelWidth(element,element.currentStyle[type+"Right"]);};HEADER+="*{minWidth:none;maxWidth:none;min-width:none;max-width:none}";layout.minWidth=function(element){if(element.currentStyle["min-width"]!=null){element.style.minWidth=element.currentStyle["min-width"];} +if(register(arguments.callee,element,element.currentStyle.minWidth!="none")){layout.boxSizing(element);fixWidth(element);resizeWidth(element);}};eval("IE7.Layout.maxWidth="+String(layout.minWidth).replace(/min/g,"max"));function resizeWidth(element){var rect=element.getBoundingClientRect();var width=rect.right-rect.left;if(element.currentStyle.minWidth!="none"&&width<=getFixedWidth(element,element.currentStyle.minWidth)){element.runtimeStyle.width=element.currentStyle.minWidth;}else if(element.currentStyle.maxWidth!="none"&&width>=getFixedWidth(element,element.currentStyle.maxWidth)){element.runtimeStyle.width=element.currentStyle.maxWidth;}else{element.runtimeStyle.width=element.runtimeStyle.fixedWidth;}};function fixRight(element){if(register(fixRight,element,/^(fixed|absolute)$/.test(element.currentStyle.position)&&getDefinedStyle(element,"left")!="auto"&&getDefinedStyle(element,"right")!="auto"&&AUTO.test(getDefinedStyle(element,"width")))){resizeRight(element);IE7.Layout.boxSizing(element);}};IE7.Layout.fixRight=fixRight;function resizeRight(element){var left=getPixelWidth(element,element.runtimeStyle._left||element.currentStyle.left);var width=layoutWidth(element)-getPixelWidth(element,element.currentStyle.right)-left-getWidth(element,"margin");if(parseInt(element.runtimeStyle.width)==width)return;element.runtimeStyle.width="";if(isFixed(element)||HEIGHT||element.offsetWidth=5.5&&appVersion<7){IE7.CSS.addFix(/background(-image)?\s*:\s*([^};]*)?url\(([^\)]+)\)([^;}]*)?/,function(match,$1,$2,url,$4){url=getString(url);return PNG.test(url)?"filter:"+format(PNG_FILTER,url,"crop")+";zoom:1;background"+($1||"")+":"+($2||"")+"none"+($4||""):match;});IE7.HTML.addRecalc("img,input",function(element){if(element.tagName=="INPUT"&&element.type!="image")return;fixImage(element);addEventHandler(element,"onpropertychange",function(){if(!printing&&event.propertyName=="src"&&element.src.indexOf(BLANK_GIF)==-1)fixImage(element);});});var printing=false;addEventHandler(window,"onbeforeprint",function(){printing=true;for(var i=0;i=7)return;IE7.CSS.addRecalc("position","fixed",_positionFixed,"absolute");IE7.CSS.addRecalc("background(-attachment)?","[^};]*fixed",_backgroundFixed);var $viewport=quirksMode?"body":"documentElement";function _fixBackground(){if(body.currentStyle.backgroundAttachment!="fixed"){if(body.currentStyle.backgroundImage=="none"){body.runtimeStyle.backgroundRepeat="no-repeat";body.runtimeStyle.backgroundImage="url("+BLANK_GIF+")";} +body.runtimeStyle.backgroundAttachment="fixed";} +_fixBackground=Undefined;};var _tmp=createTempElement("img");function _isFixed(element){return element?isFixed(element)||_isFixed(element.parentElement):false;};function _setExpression(element,propertyName,expression){setTimeout("document.all."+element.uniqueID+".runtimeStyle.setExpression('"+propertyName+"','"+expression+"')",0);};function _backgroundFixed(element){if(register(_backgroundFixed,element,element.currentStyle.backgroundAttachment=="fixed"&&!element.contains(body))){_fixBackground();bgLeft(element);bgTop(element);_backgroundPosition(element);}};function _backgroundPosition(element){_tmp.src=element.currentStyle.backgroundImage.slice(5,-2);var parentElement=element.canHaveChildren?element:element.parentElement;parentElement.appendChild(_tmp);setOffsetLeft(element);setOffsetTop(element);parentElement.removeChild(_tmp);};function bgLeft(element){element.style.backgroundPositionX=element.currentStyle.backgroundPositionX;if(!_isFixed(element)){_setExpression(element,"backgroundPositionX","(parseInt(runtimeStyle.offsetLeft)+document."+$viewport+".scrollLeft)||0");}};eval(rotate(bgLeft));function setOffsetLeft(element){var propertyName=_isFixed(element)?"backgroundPositionX":"offsetLeft";element.runtimeStyle[propertyName]=getOffsetLeft(element,element.style.backgroundPositionX)- +element.getBoundingClientRect().left-element.clientLeft+2;};eval(rotate(setOffsetLeft));function getOffsetLeft(element,position){switch(position){case"left":case"top":return 0;case"right":case"bottom":return viewport.clientWidth-_tmp.offsetWidth;case"center":return(viewport.clientWidth-_tmp.offsetWidth)/2;default:if(PERCENT.test(position)){return parseInt((viewport.clientWidth-_tmp.offsetWidth)*parseFloat(position)/100);} +_tmp.style.left=position;return _tmp.offsetLeft;}};eval(rotate(getOffsetLeft));function _positionFixed(element){if(register(_positionFixed,element,isFixed(element))){setOverrideStyle(element,"position","absolute");setOverrideStyle(element,"left",element.currentStyle.left);setOverrideStyle(element,"top",element.currentStyle.top);_fixBackground();IE7.Layout.fixRight(element);_foregroundPosition(element);}};function _foregroundPosition(element,recalc){positionTop(element,recalc);positionLeft(element,recalc,true);if(!element.runtimeStyle.autoLeft&&element.currentStyle.marginLeft=="auto"&&element.currentStyle.right!="auto"){var left=viewport.clientWidth-getPixelWidth(element,element.currentStyle.right)- +getPixelWidth(element,element.runtimeStyle._left)-element.clientWidth;if(element.currentStyle.marginRight=="auto")left=parseInt(left/2);if(_isFixed(element.offsetParent))element.runtimeStyle.pixelLeft+=left;else element.runtimeStyle.shiftLeft=left;} +clipWidth(element);clipHeight(element);};function clipWidth(element){var fixWidth=element.runtimeStyle.fixWidth;element.runtimeStyle.borderRightWidth="";element.runtimeStyle.width=fixWidth?getPixelWidth(element,fixWidth):"";if(element.currentStyle.width!="auto"){var rect=element.getBoundingClientRect();var width=element.offsetWidth-viewport.clientWidth+rect.left-2;if(width>=0){element.runtimeStyle.borderRightWidth="0px";width=Math.max(getPixelValue(element,element.currentStyle.width)-width,0);setOverrideStyle(element,"width",width);return width;}}};eval(rotate(clipWidth));function positionLeft(element,recalc){if(!recalc&&PERCENT.test(element.currentStyle.width)){element.runtimeStyle.fixWidth=element.currentStyle.width;} +if(element.runtimeStyle.fixWidth){element.runtimeStyle.width=getPixelWidth(element,element.runtimeStyle.fixWidth);} +element.runtimeStyle.shiftLeft=0;element.runtimeStyle._left=element.currentStyle.left;element.runtimeStyle.autoLeft=element.currentStyle.right!="auto"&&element.currentStyle.left=="auto";element.runtimeStyle.left="";element.runtimeStyle.screenLeft=getScreenLeft(element);element.runtimeStyle.pixelLeft=element.runtimeStyle.screenLeft;if(!recalc&&!_isFixed(element.offsetParent)){_setExpression(element,"pixelLeft","runtimeStyle.screenLeft+runtimeStyle.shiftLeft+document."+$viewport+".scrollLeft");}};eval(rotate(positionLeft));function getScreenLeft(element){var screenLeft=element.offsetLeft,nested=1;if(element.runtimeStyle.autoLeft){screenLeft=viewport.clientWidth-element.offsetWidth-getPixelWidth(element,element.currentStyle.right);} +if(element.currentStyle.marginLeft!="auto"){screenLeft-=getPixelWidth(element,element.currentStyle.marginLeft);} +while(element=element.offsetParent){if(element.currentStyle.position!="static")nested=-1;screenLeft+=element.offsetLeft*nested;} +return screenLeft;};eval(rotate(getScreenLeft));function getPixelWidth(element,value){return PERCENT.test(value)?parseInt(parseFloat(value)/100*viewport.clientWidth):getPixelValue(element,value);};eval(rotate(getPixelWidth));function _resize(){var elements=_backgroundFixed.elements;for(var i in elements)_backgroundPosition(elements[i]);elements=_positionFixed.elements;for(i in elements){_foregroundPosition(elements[i],true);_foregroundPosition(elements[i],true);} +_timer=0;};var _timer;addResize(function(){if(!_timer)_timer=setTimeout(_resize,0);});};var WRAPPER_STYLE={backgroundColor:"transparent",backgroundImage:"none",backgroundPositionX:null,backgroundPositionY:null,backgroundRepeat:null,borderTopWidth:0,borderRightWidth:0,borderBottomWidth:0,borderLeftStyle:"none",borderTopStyle:"none",borderRightStyle:"none",borderBottomStyle:"none",borderLeftWidth:0,height:null,marginTop:0,marginBottom:0,marginRight:0,marginLeft:0,width:"100%"};IE7.CSS.addRecalc("overflow","visible",function(element){if(element.parentNode.ie7_wrapped)return;if(IE7.Layout&&element.currentStyle["max-height"]!="auto"){IE7.Layout.maxHeight(element);} +if(element.currentStyle.marginLeft=="auto")element.style.marginLeft=0;if(element.currentStyle.marginRight=="auto")element.style.marginRight=0;var wrapper=document.createElement(ANON);wrapper.ie7_wrapped=element;for(var propertyName in WRAPPER_STYLE){wrapper.style[propertyName]=element.currentStyle[propertyName];if(WRAPPER_STYLE[propertyName]!=null){element.runtimeStyle[propertyName]=WRAPPER_STYLE[propertyName];}} +wrapper.style.display="block";wrapper.style.position="relative";element.runtimeStyle.position="absolute";element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(element);});function ie7Quirks(){var FONT_SIZES="xx-small,x-small,small,medium,large,x-large,xx-large".split(",");for(var i=0;i=%2",a,b,position,last,"&&","%","==");if(negate)query="!("+query+")";return query;};_PSEUDO_CLASSES={"link":"e%1.currentStyle['ie7-link']=='link'","visited":"e%1.currentStyle['ie7-link']=='visited'","checked":"e%1.checked","contains":"e%1.innerText.indexOf('%2')!=-1","disabled":"e%1.isDisabled","empty":"IE7._isEmpty(e%1)","enabled":"e%1.disabled===false","first-child":"!IE7._getPreviousElementSibling(e%1)","lang":"IE7._isLang(e%1,'%2')","last-child":"!IE7._getNextElementSibling(e%1)","only-child":"!IE7._getPreviousElementSibling(e%1)&&!IE7._getNextElementSibling(e%1)","target":"e%1.id==location.hash.slice(1)","indeterminate":"e%1.indeterminate"};IE7._register=function(element){if(element.rows){element.ie7_length=element.rows.length;element.ie7_lookup="rowIndex";}else if(element.cells){element.ie7_length=element.cells.length;element.ie7_lookup="cellIndex";}else if(element.ie7_indexed!=IE7._indexed){var index=0;var child=element.firstChild;while(child){if(child.nodeType==1&&child.nodeName!="!"){child.ie7_index=++index;} +child=child.nextSibling;} +element.ie7_length=index;element.ie7_lookup="ie7_index";} +element.ie7_indexed=IE7._indexed;return element;};var keys=cssParser[_KEYS];var pseudoClass=keys[keys.length-1];keys.length--;cssParser.merge({":not\\((\\*|[\\w-]+)?([^)]*)\\)":function(match,tagName,filters){var replacement=(tagName&&tagName!="*")?format("if(e%1.nodeName=='%2'){",_index,tagName.toUpperCase()):"";replacement+=cssParser.exec(filters);return"if(!"+replacement.slice(2,-1).replace(/\)\{if\(/g,"&&")+"){";},":nth(-last)?-child\\(([^)]+)\\)":function(match,last,args){_wild=false;last=format("e%1.parentNode.ie7_length",_index);var replacement="if(p%1!==e%1.parentNode)p%1=IE7._register(e%1.parentNode);";replacement+="var i=e%1[p%1.ie7_lookup];if(p%1.ie7_lookup!='ie7_index')i++;if(";return format(replacement,_index)+_nthChild(match,args,"i",last)+"){";}});keys.push(pseudoClass);var BRACKETS="\\([^)]*\\)";if(IE7.CSS.pseudoClasses)IE7.CSS.pseudoClasses+="|";IE7.CSS.pseudoClasses+="before|after|last\\-child|only\\-child|empty|root|"+"not|nth\\-child|nth\\-last\\-child|contains|lang".split("|").join(BRACKETS+"|")+BRACKETS;encoder.add(/::/,":");var Focus=new DynamicPseudoClass("focus",function(element){var instance=arguments;IE7.CSS.addEventHandler(element,"onfocus",function(){Focus.unregister(instance);Focus.register(instance);});IE7.CSS.addEventHandler(element,"onblur",function(){Focus.unregister(instance);});if(element==document.activeElement){Focus.register(instance)}});var Active=new DynamicPseudoClass("active",function(element){var instance=arguments;IE7.CSS.addEventHandler(element,"onmousedown",function(){Active.register(instance);});});addEventHandler(document,"onmouseup",function(){var instances=Active.instances;for(var i in instances)Active.unregister(instances[i]);});var Checked=new DynamicPseudoClass("checked",function(element){if(typeof element.checked!="boolean")return;var instance=arguments;IE7.CSS.addEventHandler(element,"onpropertychange",function(){if(event.propertyName=="checked"){if(element.checked)Checked.register(instance);else Checked.unregister(instance);}});if(element.checked)Checked.register(instance);});var Enabled=new DynamicPseudoClass("enabled",function(element){if(typeof element.disabled!="boolean")return;var instance=arguments;IE7.CSS.addEventHandler(element,"onpropertychange",function(){if(event.propertyName=="disabled"){if(!element.isDisabled)Enabled.register(instance);else Enabled.unregister(instance);}});if(!element.isDisabled)Enabled.register(instance);});var Disabled=new DynamicPseudoClass("disabled",function(element){if(typeof element.disabled!="boolean")return;var instance=arguments;IE7.CSS.addEventHandler(element,"onpropertychange",function(){if(event.propertyName=="disabled"){if(element.isDisabled)Disabled.register(instance);else Disabled.unregister(instance);}});if(element.isDisabled)Disabled.register(instance);});var Indeterminate=new DynamicPseudoClass("indeterminate",function(element){if(typeof element.indeterminate!="boolean")return;var instance=arguments;IE7.CSS.addEventHandler(element,"onpropertychange",function(){if(event.propertyName=="indeterminate"){if(element.indeterminate)Indeterminate.register(instance);else Indeterminate.unregister(instance);}});IE7.CSS.addEventHandler(element,"onclick",function(){Indeterminate.unregister(instance);});});var Target=new DynamicPseudoClass("target",function(element){var instance=arguments;if(!element.tabIndex)element.tabIndex=0;IE7.CSS.addEventHandler(document,"onpropertychange",function(){if(event.propertyName=="activeElement"){if(element.id&&element.id==location.hash.slice(1))Target.register(instance);else Target.unregister(instance);}});if(element.id&&element.id==location.hash.slice(1))Target.register(instance);});var ATTR=/^attr/;var URL=/^url\s*\(\s*([^)]*)\)$/;var POSITION_MAP={before0:"beforeBegin",before1:"afterBegin",after0:"afterEnd",after1:"beforeEnd"};var PseudoElement=IE7.PseudoElement=Rule.extend({constructor:function(selector,position,cssText){this.position=position;var content=cssText.match(PseudoElement.CONTENT),match,entity;if(content){content=content[1];match=content.split(/\s+/);for(var i=0;(entity=match[i]);i++){match[i]=ATTR.test(entity)?{attr:entity.slice(5,-1)}:(entity.charAt(0)=="'")?getString(entity):decode(entity);} +content=match;} +this.content=content;this.base(selector,decode(cssText));},init:function(){this.match=cssQuery(this.selector);for(var i=0;i%4",MATCH:/(.*):(before|after).*/,count:0});var UNSUCCESSFUL=/^(submit|reset|button)$/;IE7.HTML.addRecalc("button,input",function(button){if(button.tagName=="BUTTON"){var match=button.outerHTML.match(/ value="([^"]*)"/i);button.runtimeStyle.value=(match)?match[1]:"";} +if(button.type=="submit"){addEventHandler(button,"onclick",function(){button.runtimeStyle.clicked=true;setTimeout("document.all."+button.uniqueID+".runtimeStyle.clicked=false",1);});}});IE7.HTML.addRecalc("form",function(form){addEventHandler(form,"onsubmit",function(){for(var element,i=0;element=form[i];i++){if(UNSUCCESSFUL.test(element.type)&&!element.disabled&&!element.runtimeStyle.clicked){element.disabled=true;setTimeout("document.all."+element.uniqueID+".disabled=false",1);}else if(element.tagName=="BUTTON"&&element.type=="submit"){setTimeout("document.all."+element.uniqueID+".value='"+ +element.value+"'",1);element.value=element.runtimeStyle.value;}}});});IE7.HTML.addRecalc("img",function(img){if(img.alt&&!img.title)img.title="";});IE7.CSS.addRecalc("border-spacing",NUMERIC,function(element){if(element.currentStyle.borderCollapse!="collapse"){element.cellSpacing=getPixelValue(element,element.currentStyle["border-spacing"]);}});IE7.CSS.addRecalc("box-sizing","content-box",IE7.Layout.boxSizing);IE7.CSS.addRecalc("box-sizing","border-box",IE7.Layout.borderBox);IE7.CSS.addFix(/opacity\s*:\s*([\d.]+)/,function(match,value){return"zoom:1;filter:Alpha(opacity="+((value*100)||1)+")";});var IMAGE=/^image/i;IE7.HTML.addRecalc("object",function(element){if(IMAGE.test(element.type)){element.body.style.cssText="margin:0;padding:0;border:none;overflow:hidden";return element;}});IE7.loaded=true;(function(){try{documentElement.doScroll("left");}catch(e){setTimeout(arguments.callee,1);return;} +try{eval(script.innerHTML);}catch(e){} +PNG=new RegExp(rescape(typeof IE7_PNG_SUFFIX=="string"?IE7_PNG_SUFFIX:"-trans.png")+"$","i");body=document.body;viewport=quirksMode?body:documentElement;body.className+=" ie7_body";documentElement.className+=" ie7_html";if(quirksMode)ie7Quirks();IE7.CSS.init();IE7.HTML.init();IE7.HTML.apply();IE7.CSS.apply();IE7.recalc();})();})(); \ No newline at end of file diff --git a/v2/dotclear/admin/js/ie7/blank.gif b/v2/dotclear/admin/js/ie7/blank.gif new file mode 100644 index 0000000..a4fe2e6 Binary files /dev/null and b/v2/dotclear/admin/js/ie7/blank.gif differ diff --git a/v2/dotclear/admin/js/ie7/ie7-recalc.js b/v2/dotclear/admin/js/ie7/ie7-recalc.js new file mode 100644 index 0000000..3acd0cb --- /dev/null +++ b/v2/dotclear/admin/js/ie7/ie7-recalc.js @@ -0,0 +1,5 @@ + +(function(){if(!IE7.loaded)return;CLASSES=/\sie7_class\d+/g;IE7.CSS.extend({elements:{},handlers:[],reset:function(){this.removeEventHandlers();var elements=this.elements;for(var i in elements)elements[i].runtimeStyle.cssText="";this.elements={};var elements=IE7.Rule.elements;for(var i in elements){with(elements[i])className=className.replace(CLASSES,"");} +IE7.Rule.elements={};},reload:function(){this.rules=[];this.getInlineStyles();this.screen.load();if(this.print)this.print.load();this.refresh();this.trash();},addRecalc:function(propertyName,test,handler,replacement){this.base(propertyName,test,function(element){handler(element);IE7.CSS.elements[element.uniqueID]=element;},replacement);},recalc:function(){this.reset();this.base();},addEventHandler:function(element,type,handler){element.attachEvent(type,handler);this.handlers.push(arguments);},removeEventHandlers:function(){var handler;while(handler=this.handlers.pop()){handler[0].detachEvent(handler[1],handler[2]);}},getInlineStyles:function(){var styleSheets=document.getElementsByTagName("style"),styleSheet;for(var i=styleSheets.length-1;(styleSheet=styleSheets[i]);i--){if(!styleSheet.disabled&&!styleSheet.ie7){var cssText=styleSheet.cssText||styleSheet.innerHTML;this.styles.push(cssText);styleSheet.cssText=cssText;}}},trash:function(){var styleSheets=document.styleSheets,styleSheet,i;for(i=0;i=6)IE7.CSS.addRecalc("float","(left|right)",function(element){IE7.Layout.boxSizing(element.parentElement);element.style.display="inline";});IE7.CSS.addRecalc("position","absolute|fixed",function(element){if(element.offsetParent&&element.offsetParent.currentStyle.position=="relative") +IE7.Layout.boxSizing(element.offsetParent);});} \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery-ui-1.8.12.custom.min.js b/v2/dotclear/admin/js/jquery/jquery-ui-1.8.12.custom.min.js new file mode 100644 index 0000000..b99518d --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery-ui-1.8.12.custom.min.js @@ -0,0 +1,8 @@ + +(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.12",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);;(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable");this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left- +this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h- +f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g- +this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex");this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ +c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.12",animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);; \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery.bgFade.js b/v2/dotclear/admin/js/jquery/jquery.bgFade.js new file mode 100644 index 0000000..cdf201a --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery.bgFade.js @@ -0,0 +1,7 @@ + +jQuery.fn.backgroundFade=function(s,callback){var defaults={sColor:[255,0,0],eColor:[255,255,255],fColor:null,steps:200,intervals:5,powr:4},params=jQuery.extend(defaults,s);return this.each(function(){this.bgFade_sColor=jQuery.backgroundFade.parseHexColor(params.sColor);this.bgFade_eColor=jQuery.backgroundFade.parseHexColor(params.eColor);this.bgFade_fColor=params.fColor?params.fColor:jQuery(this).css('backgroundColor');this.bgFade_steps=params.steps;this.bgFade_intervals=params.intervals;this.bgFade_powr=params.powr;this.bgFade_fn=callback;jQuery.backgroundFade.doFade(this);});};jQuery.backgroundFade={parseHexColor:function(c){if(c.constructor==String&&c.substr(0,1)=='#'){return[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16)];} +return c;},easeInOut:function(minValue,maxValue,totalSteps,actualStep,powr){var delta=maxValue-minValue;var stepp=minValue+(Math.pow(((1/totalSteps)*actualStep),powr)*delta);return Math.ceil(stepp);},doFade:function(e){if(e.bgFadeInt)window.clearInterval(e.bgFadeInt);var act_step=0;e.bgFadeInt=window.setInterval(function(){e.style.backgroundColor="rgb("+ +jQuery.backgroundFade.easeInOut(e.bgFade_sColor[0],e.bgFade_eColor[0],e.bgFade_steps,act_step,e.bgFade_powr)+","+ +jQuery.backgroundFade.easeInOut(e.bgFade_sColor[1],e.bgFade_eColor[1],e.bgFade_steps,act_step,e.bgFade_powr)+","+ +jQuery.backgroundFade.easeInOut(e.bgFade_sColor[2],e.bgFade_eColor[2],e.bgFade_steps,act_step,e.bgFade_powr)+")";act_step++;if(act_step>e.bgFade_steps){window.clearInterval(e.bgFadeInt);e.bgFade_sColor=undefined;e.bgFade_eColor=undefined;e.bgFade_fColor=undefined;e.bgFade_steps=undefined;e.bgFade_intervals=undefined;e.bgFade_powr=undefined;if(typeof(e.bgFade_fn)=='function'){e.bgFade_fn.call(e);} +e.style.backgroundColor=e.bgFade_fColor;}},e.bgFade_intervals);}}; \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery.biscuit.js b/v2/dotclear/admin/js/jquery/jquery.biscuit.js new file mode 100644 index 0000000..8b74194 --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery.biscuit.js @@ -0,0 +1,5 @@ + +jQuery.cookie=function(name,value,options){if(typeof value!='undefined'){options=options||{};var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toGMTString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;} +expires='; expires='+date.toGMTString();} +var path=options.path?'; path='+options.path:'';var domain=options.domain?'; domain='+options.domain:'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');return null;}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i'+''+''+''+''+''+'';this.container=$('');if(this.params.target_element==null){this.container.css({display:'block',position:'absolute',left:$(document).scrollLeft()+'px',top:$(document).scrollTop()+'px',width:'30px',height:'30px',background:'#f00'});$('body').append(this.container);}else{$(this.params.target_element).append(this.container);this.container.css({display:'block',position:'absolute',top:0,left:0,zIndex:1});} +this.container[0].innerHTML=flash;this.movie=document.getElementById(this.params.movie_name);return this;},addFlashVar:function(fv,n,v){if(v!=null){fv.push(n+'='+encodeURIComponent(v));}},flashBind:function(){_this=this;var events={flashReady:function(){if(window[_this.params.movie_name]==undefined){window[_this.params.movie_name]=_this.movie;} +_this.flashBindEvent('flashReady',arguments);},fileDialogComplete:function(){_this.flashBindEvent('fileDialogComplete',arguments);},fileDialogStart:function(){_this.flashBindEvent('fileDialogStart',arguments);},fileQueued:function(file_object){_this.flashBindEvent('fileQueued',arguments);},fileQueueError:function(file_object,error_code,error_msg){_this.flashBindEvent('fileQueueError',arguments);},uploadStart:function(){_this.flashBindEvent('uploadStart',arguments);},uploadProgress:function(){_this.flashBindEvent('uploadProgress',arguments);},uploadError:function(){_this.flashBindEvent('uploadError',arguments);},uploadSuccess:function(){_this.flashBindEvent('uploadSuccess',arguments);},uploadComplete:function(){_this.flashBindEvent('uploadComplete',arguments);},debug:function(){_this.flashBindEvent('debug',arguments);}};window.SWFUpload.instances[this.params.movie_name]=events;window.SWFUpload.movieCount++;},flashEventQueue:[],flashBindEvent:function(evt,a){a=a||new Array();var _this=this;if($.isFunction(this.callbacks[evt])){this.flashEventQueue.push(function(){this.callbacks[evt].apply(this,a);});setTimeout(function(){_this.flashExecuteNextEvent();},0);}else if(this.callbacks[evt]!==null){throw'Event handler '+evt+' is unknown or is not a function';}},flashExecuteNextEvent:function(){var f=this.flashEventQueue?this.flashEventQueue.shift():null;if($.isFunction(f)){f.apply(this);}},destroy:function(){try{this.StopUpload();$(this.movie).remove();SWFUpload.instances[this.params.movie_name]=null;SWFUpload.movieCount--;delete SWFUpload.instances[this.movieName];delete window[this.movieName];return true;}catch(e){return false;}},StartUpload:function(file_id){return this.movie.StartUpload(file_id);},ReturnUploadStart:function(value){return this.movie.ReturnUploadStart(value);},StopUpload:function(){return this.movie.StopUpload();},CancelUpload:function(file_id){return this.movie.CancelUpload(file_id);},GetStats:function(){return this.movie.GetStats();},SetStats:function(stats){return this.movie.SetStats(stats);},GetFile:function(file_id){return this.movie.GetFile(file_id);},GetFileByIndex:function(file_index){return this.movie.GetFileByIndex(file_index);},getFileSizeLimit:function(size){var value=0;var unit='kb';size=$.trim(size.toLowerCase());var values=size.match(/^\d+/);if(values!=null&&values.length>0){value=parseInt(values[0]);} +var units=size.match(/(b|kb|mb|gb)/);if(units!=null&&units.length>0){unit=units[0];} +var multiplier=1024;if(unit==="b"){multiplier=1;}else if(unit==="mb"){multiplier=1048576;}else if(unit==="gb"){multiplier=1073741824;} +return value*multiplier;}};})(jQuery);(function($){$.fn.candyUpload=function(settings,callbacks){new $._candyUpload(this,settings,callbacks);return this;};$._candyUpload=function(target,settings,callbacks){var defaults={debug:false,upload_url:'',params:null,flash_movie:'',file_types:'*.*',file_types_description:'All files',file_size_limit:0,file_upload_limit:0,file_queue_limit:-1,callbacks:{}};this.params=$.extend(defaults,settings);this.params.movie_name='SWFU-'+(window.SWFUpload.instances.length+1);this.target=target;this.createControls();this.target.hide().after(this.ctrl.block).hide();this.params.target_element=this.ctrl.btn_browse.parent().get(0);var _this=this;this.upldr=$.uploader(this.params,{debug:function(msg){$('body').append('
      '+msg+'
      ');_this.bindEvent('debug',arguments);},flashReady:function(){_this.initControls(this);_this.bindEvent('flashReady',arguments);this.movie.style.width=_this.ctrl.btn_browse.width()+'px';this.movie.style.height=_this.ctrl.btn_browse.height()+'px';},fileDialogComplete:function(num_ref_files,num_queue_files){_this.bindEvent('fileDialogComplete',arguments);},fileDialogStart:function(){_this.bindEvent('fileQueued',arguments);},fileQueued:function(o){_this.appendFile(this,o);_this.refreshControls(this);_this.bindEvent('fileQueued',arguments);},fileQueueError:function(o,code,msg){var codes=window.SWFUpload.QUEUE_ERROR;switch(code){case codes.QUEUE_LIMIT_EXCEEDED:_this.queueErrorMsg(_this.locales.limit_exceeded);break;case codes.FILE_EXCEEDS_SIZE_LIMIT:_this.queueErrorMsg(_this.locales.size_limit_exceeded);break;case codes.ZERO_BYTE_FILE:case codes.INVALID_FILETYPE:_this.queueErrorMsg(msg);break;} +_this.bindEvent('fileQueueError',arguments);},uploadStart:function(){this.ReturnUploadStart(true);_this.bindEvent('uploadStart',arguments);},uploadProgress:function(o,bytes,total){_this.fileProgressBar(o.id,bytes,total);_this.bindEvent('uploadProgress',arguments);},uploadError:function(o,code,msg){var codes=window.SWFUpload.UPLOAD_ERROR;switch(code){case codes.FILE_CANCELLED:_this.fileErrorMsg(o.id,_this.locales.canceled);break;case codes.HTTP_ERROR:_this.fileErrorMsg(o.id,_this.locales.http_error+' '+msg);break;case codes.MISSING_UPLOAD_URL:case codes.IO_ERROR:case codes.SECURITY_ERROR:case codes.UPLOAD_LIMIT_EXCEEDED:case codes.UPLOAD_FAILED:case codes.SPECIFIED_FILE_ID_NOT_FOUND:case codes.FILE_VALIDATION_FAILED:case codes.FILE_CANCELLED:case codes.UPLOAD_STOPPED:_this.fileErrorMsg(o.id,_this.locales.error+' '+msg);break;} +_this.refreshControls(this);_this.removeFileCancel(o);_this.bindEvent('uploadError',arguments);},uploadSuccess:function(o,data){_this.fileProgressBar(o.id,1,1);_this.refreshControls(this);_this.removeFileCancel(o);_this.bindEvent('uploadSuccess',arguments);},uploadComplete:function(o){this.StartUpload();_this.refreshControls(this);_this.bindEvent('uploadComplete',arguments);}});};$._candyUpload.prototype={locales:{max_file_size:'Maximum file size allowed:',limit_exceeded:'Limit exceeded.',size_limit_exceeded:'File size exceeds allowed limit.',canceled:'Canceled.',http_error:'HTTP Error:',error:'Error:',choose_file:'Choose file',choose_files:'Choose files',cancel:'Cancel',clean:'Clean',upload:'Upload',no_file_in_queue:'No file in queue.',file_in_queue:'1 file in queue.',files_in_queue:'%d files in queue.',queue_error:'Queue error:'},ctrl:{block:$('
      '),files:null},createControls:function(){this.ctrl.btn_browse=$(' ').click(function(){return false;});this.ctrl.btn_cancel=$(''+this.locales.cancel+'').click(function(){return false;});this.ctrl.btn_clean=$(''+this.locales.clean+'').click(function(){return false;});this.ctrl.btn_upload=$(''+this.locales.upload+'').click(function(){return false;});this.ctrl.msg=$('
      '+this.locales.no_file_in_queue+'
      ').appendTo(this.ctrl.block);var btn=$('
      ').appendTo(this.ctrl.block);var brw=$('').append(this.ctrl.btn_browse).appendTo(btn);$('').append(this.ctrl.btn_upload).appendTo(btn).hide();$('').append(this.ctrl.btn_cancel).appendTo(btn).hide();$('').append(this.ctrl.btn_clean).appendTo(btn).hide();this.bindEvent('createControls');},initControls:function(upldr){if(this.params.file_queue_limit==1){this.ctrl.btn_browse.text(this.locales.choose_file);}else{this.ctrl.btn_browse.text(this.locales.choose_files);} +var _this=this;this.ctrl.btn_cancel.click(function(){_this.cancelQueue(upldr);return false;});this.ctrl.btn_clean.click(function(){_this.cleanQueue(upldr);return false;});this.ctrl.btn_upload.click(function(){_this.uploadQueue(upldr);return false;});var size=this.formatSize(upldr.params.file_size_limit);$('

      '+this.locales.max_file_size+' '+size+'

      ').appendTo(this.ctrl.block);},refreshControls:function(upldr){if(!this.ctrl.files||this.ctrl.files.length==0){return;} +var stats=upldr.GetStats();if(stats.files_queued>0){this.ctrl.btn_cancel.parent().show();this.ctrl.btn_upload.parent().show();if(this.params.file_queue_limit>0&&this.params.file_queue_limit==stats.files_queued){this.ctrl.btn_browse.hide();}else{this.ctrl.btn_browse.show();} +if(stats.files_queued>1){var msg=this.locales.files_in_queue.replace(/%d/,stats.files_queued);}else{var msg=this.locales.file_in_queue;}}else{this.ctrl.btn_browse.show();this.ctrl.btn_cancel.parent().hide();this.ctrl.btn_upload.parent().hide();var msg=this.locales.no_file_in_queue;} +this.ctrl.msg.removeClass('cu-error').text(msg);if(stats.successful_uploads>0||stats.upload_errors>0||stats.upload_cancelled>0){this.ctrl.btn_clean.parent().show();}else{this.ctrl.btn_clean.parent().hide();}},removeFileCancel:function(o){$('#'+o.id+' span.cu-filecancel',this.ctrl.files).remove();},appendFile:function(upldr,o){if(!this.ctrl.files){this.ctrl.files=$('
      ');this.ctrl.msg.after(this.ctrl.files);} +var fileblock=$('
      '+'
      '+o.name+' '+'('+this.formatSize(o.size)+') '+'cancel '+''+'
      ');$('span.cu-filecancel a',fileblock).click(function(){upldr.CancelUpload(o.id);return false;});this.ctrl.files.append(fileblock);},fileProgressBar:function(file_id,bytes,total){var bar=$('#'+file_id+' div.cu-progress>div',this.ctrl.files);if(bar.length==0){$('#'+file_id,this.ctrl.files).append('
       
      ');bar=$('#'+file_id+' div.cu-progress>div',this.ctrl.files);} +var percent=Math.round((bytes*100)/total);bar.css('width',percent+'%').text(percent+'%');},fileMsg:function(file_id,msg,error){error=error||false;var span=$('#'+file_id+' span.cu-filemsg',this.ctrl.files).attr('class','cu-filemsg');if(error){span.addClass('cu-error');} +span.text(msg);},fileErrorMsg:function(file_id,msg){this.fileMsg(file_id,msg,true);},cancelQueue:function(upldr){if(!this.ctrl.files||this.ctrl.files.length==0){return;} +this.ctrl.files.children('div').each(function(){upldr.CancelUpload(this.id);});},uploadQueue:function(upldr){if(!this.ctrl.files||this.ctrl.files.length==0){return;} +upldr.StartUpload();},cleanQueue:function(upldr){var _this=this;var e=$('div.cu-file',this.ctrl.files).not(':has(span.cu-filecancel a)');e.filter(':last').slideUp(200,function(){$(this).remove();if(e.length==1){upldr.SetStats({successful_uploads:0,upload_errors:0,upload_cancelled:0});_this.refreshControls(upldr);}else if(e.length>1){_this.cleanQueue(upldr);}});},queueErrorMsg:function(msg){this.ctrl.msg.addClass('cu-error').text(this.locales.queue_error+' '+msg);},formatSize:function(s){var a_size=Array('B','KB','MB','GB','TB');var i_index=0;while(s>1024){i_index++;s/=1024;} +return(Math.round(s*100)/100)+' '+a_size[i_index];},bindEvent:function(evt,a){if(this.params.callbacks[evt]!=undefined&&$.isFunction(this.params.callbacks[evt])){a=a||new Array();this.params.callbacks[evt].apply(this,a);}}};})(jQuery); \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery.farbtastic.js b/v2/dotclear/admin/js/jquery/jquery.farbtastic.js new file mode 100644 index 0000000..26103df --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery.farbtastic.js @@ -0,0 +1,22 @@ + +jQuery.fn.farbtastic=function(callback){$.farbtastic(this,callback);return this;};jQuery.farbtastic=function(container,callback){var container=$(container).get(0);return container.farbtastic||(container.farbtastic=new jQuery._farbtastic(container,callback));};jQuery._farbtastic=function(container,callback){var fb=this;$(container).html('
      ');var e=$('.farbtastic',container);fb.wheel=$('.wheel',container).get(0);fb.radius=84;fb.square=100;fb.width=194;if(navigator.appVersion.match(/MSIE [0-6]\./)){$('*',e).each(function(){if(this.currentStyle.backgroundImage!='none'){var image=this.currentStyle.backgroundImage;image=this.currentStyle.backgroundImage.substring(5,image.length-2);$(this).css({'backgroundImage':'none','filter':"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='"+image+"')"});}});} +fb.linkTo=function(callback){if(typeof fb.callback=='object'){$(fb.callback).unbind('keyup',fb.updateValue);} +fb.color=null;if(typeof callback=='function'){fb.callback=callback;} +else if(typeof callback=='object'||typeof callback=='string'){fb.callback=$(callback);fb.callback.bind('keyup',fb.updateValue);if(fb.callback.get(0).value){fb.setColor(fb.callback.get(0).value);}} +return this;};fb.updateValue=function(event){if(this.value&&this.value!=fb.color){fb.setColor(this.value);}};fb.setColor=function(color){var unpack=fb.unpack(color);if(fb.color!=color&&unpack){fb.color=color;fb.rgb=unpack;fb.hsl=fb.RGBToHSL(fb.rgb);fb.updateDisplay();} +return this;};fb.setHSL=function(hsl){fb.hsl=hsl;fb.rgb=fb.HSLToRGB(hsl);fb.color=fb.pack(fb.rgb);fb.updateDisplay();return this;};fb.widgetCoords=function(event){var x,y;var el=event.target||event.srcElement;var reference=fb.wheel;if(typeof event.offsetX!='undefined'){var pos={x:event.offsetX,y:event.offsetY};var e=el;while(e){e.mouseX=pos.x;e.mouseY=pos.y;pos.x+=e.offsetLeft;pos.y+=e.offsetTop;e=e.offsetParent;} +var e=reference;var offset={x:0,y:0};while(e){if(typeof e.mouseX!='undefined'){x=e.mouseX-offset.x;y=e.mouseY-offset.y;break;} +offset.x+=e.offsetLeft;offset.y+=e.offsetTop;e=e.offsetParent;} +e=el;while(e){e.mouseX=undefined;e.mouseY=undefined;e=e.offsetParent;}} +else{var pos=fb.absolutePosition(reference);x=(event.pageX||0*(event.clientX+$('html').get(0).scrollLeft))-pos.x;y=(event.pageY||0*(event.clientY+$('html').get(0).scrollTop))-pos.y;} +return{x:x-fb.width/2,y:y-fb.width/2};};fb.mousedown=function(event){if(!document.dragging){$(document).bind('mousemove',fb.mousemove).bind('mouseup',fb.mouseup);document.dragging=true;} +var pos=fb.widgetCoords(event);fb.circleDrag=Math.max(Math.abs(pos.x),Math.abs(pos.y))*2>fb.square;fb.mousemove(event);return false;};fb.mousemove=function(event){var pos=fb.widgetCoords(event);if(fb.circleDrag){var hue=Math.atan2(pos.x,-pos.y)/6.28;if(hue<0)hue+=1;fb.setHSL([hue,fb.hsl[1],fb.hsl[2]]);} +else{var sat=Math.max(0,Math.min(1,-(pos.x/fb.square)+.5));var lum=Math.max(0,Math.min(1,-(pos.y/fb.square)+.5));fb.setHSL([fb.hsl[0],sat,lum]);} +return false;};fb.mouseup=function(){$(document).unbind('mousemove',fb.mousemove);$(document).unbind('mouseup',fb.mouseup);document.dragging=false;};fb.updateDisplay=function(){var angle=fb.hsl[0]*6.28;$('.h-marker',e).css({left:Math.round(Math.sin(angle)*fb.radius+fb.width/2)+'px',top:Math.round(-Math.cos(angle)*fb.radius+fb.width/2)+'px'});$('.sl-marker',e).css({left:Math.round(fb.square*(.5-fb.hsl[1])+fb.width/2)+'px',top:Math.round(fb.square*(.5-fb.hsl[2])+fb.width/2)+'px'});$('.color',e).css('backgroundColor',fb.pack(fb.HSLToRGB([fb.hsl[0],1,0.5])));if(typeof fb.callback=='object'){$(fb.callback).css({backgroundColor:fb.color,color:fb.hsl[2]>0.5?'#000':'#fff'});$(fb.callback).each(function(){if(this.value!=fb.color){this.value=fb.color;}});$(fb.callback).change();} +else if(typeof fb.callback=='function'){fb.callback.call(fb,fb.color);}};fb.absolutePosition=function(el){var r={x:el.offsetLeft,y:el.offsetTop};if(el.offsetParent){var tmp=fb.absolutePosition(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;} +return r;};fb.pack=function(rgb){var r=Math.round(rgb[0]*255);var g=Math.round(rgb[1]*255);var b=Math.round(rgb[2]*255);return'#'+(r<16?'0':'')+r.toString(16)+ +(g<16?'0':'')+g.toString(16)+ +(b<16?'0':'')+b.toString(16);};fb.unpack=function(color){if(color.length==7){return[parseInt('0x'+color.substring(1,3))/255,parseInt('0x'+color.substring(3,5))/255,parseInt('0x'+color.substring(5,7))/255];} +else if(color.length==4){return[parseInt('0x'+color.substring(1,2))/15,parseInt('0x'+color.substring(2,3))/15,parseInt('0x'+color.substring(3,4))/15];}};fb.HSLToRGB=function(hsl){var m1,m2,r,g,b;var h=hsl[0],s=hsl[1],l=hsl[2];m2=(l<=0.5)?l*(s+1):l+s-l*s;m1=l*2-m2;return[this.hueToRGB(m1,m2,h+0.33333),this.hueToRGB(m1,m2,h),this.hueToRGB(m1,m2,h-0.33333)];};fb.hueToRGB=function(m1,m2,h){h=(h<0)?h+1:((h>1)?h-1:h);if(h*6<1)return m1+(m2-m1)*h*6;if(h*2<1)return m2;if(h*3<2)return m1+(m2-m1)*(0.66666-h)*6;return m1;};fb.RGBToHSL=function(rgb){var min,max,delta,h,s,l;var r=rgb[0],g=rgb[1],b=rgb[2];min=Math.min(r,Math.min(g,b));max=Math.max(r,Math.max(g,b));delta=max-min;l=(min+max)/2;s=0;if(l>0&&l<1){s=delta/(l<0.5?(2*l):(2-2*l));} +h=0;if(delta>0){if(max==r&&max!=g)h+=(g-b)/delta;if(max==g&&max!=b)h+=(2+(b-r)/delta);if(max==b&&max!=r)h+=(4+(r-g)/delta);h/=6;} +return[h,s,l];};$('*',e).mousedown(fb.mousedown);fb.setColor('#000000');if(callback){fb.linkTo(callback);}}; \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery.js b/v2/dotclear/admin/js/jquery/jquery.js new file mode 100644 index 0000000..772d440 --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery.js @@ -0,0 +1,471 @@ + +(function(window,undefined){var jQuery=function(selector,context){return new jQuery.fn.init(selector,context);},_jQuery=window.jQuery,_$=window.$,document=window.document,rootjQuery,quickExpr=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,isSimple=/^.[^:#\[\.,]*$/,rnotwhite=/\S/,rtrim=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,rsingleTag=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,userAgent=navigator.userAgent,browserMatch,readyBound=false,readyList=[],DOMContentLoaded,toString=Object.prototype.toString,hasOwnProperty=Object.prototype.hasOwnProperty,push=Array.prototype.push,slice=Array.prototype.slice,indexOf=Array.prototype.indexOf;jQuery.fn=jQuery.prototype={init:function(selector,context){var match,elem,ret,doc;if(!selector){return this;} +if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this;} +if(selector==="body"&&!context){this.context=document;this[0]=document.body;this.selector="body";this.length=1;return this;} +if(typeof selector==="string"){match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1]){doc=(context?context.ownerDocument||context:document);ret=rsingleTag.exec(selector);if(ret){if(jQuery.isPlainObject(context)){selector=[document.createElement(ret[1])];jQuery.fn.attr.call(selector,context,true);}else{selector=[doc.createElement(ret[1])];}}else{ret=buildFragment([match[1]],[doc]);selector=(ret.cacheable?ret.fragment.cloneNode(true):ret.fragment).childNodes;} +return jQuery.merge(this,selector);}else{elem=document.getElementById(match[2]);if(elem){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&&/^\w+$/.test(selector)){this.selector=selector;this.context=document;selector=document.getElementsByTagName(selector);return jQuery.merge(this,selector);}else if(!context||context.jquery){return(context||rootjQuery).find(selector);}else{return jQuery(context).find(selector);}}else if(jQuery.isFunction(selector)){return rootjQuery.ready(selector);} +if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context;} +return jQuery.makeArray(selector,this);},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length;},toArray:function(){return slice.call(this,0);},get:function(num){return num==null?this.toArray():(num<0?this.slice(num)[0]:this[num]);},pushStack:function(elems,name,selector){var ret=jQuery();if(jQuery.isArray(elems)){push.apply(ret,elems);}else{jQuery.merge(ret,elems);} +ret.prevObject=this;ret.context=this.context;if(name==="find"){ret.selector=this.selector+(this.selector?" ":"")+selector;}else if(name){ret.selector=this.selector+"."+name+"("+selector+")";} +return ret;},each:function(callback,args){return jQuery.each(this,callback,args);},ready:function(fn){jQuery.bindReady();if(jQuery.isReady){fn.call(document,jQuery);}else if(readyList){readyList.push(fn);} +return this;},eq:function(i){return i===-1?this.slice(i):this.slice(i,+i+1);},first:function(){return this.eq(0);},last:function(){return this.eq(-1);},slice:function(){return this.pushStack(slice.apply(this,arguments),"slice",slice.call(arguments).join(","));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},end:function(){return this.prevObject||jQuery(null);},push:push,sort:[].sort,splice:[].splice};jQuery.fn.init.prototype=jQuery.fn;jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options,name,src,copy;if(typeof target==="boolean"){deep=target;target=arguments[1]||{};i=2;} +if(typeof target!=="object"&&!jQuery.isFunction(target)){target={};} +if(length===i){target=this;--i;} +for(;i
      a";var all=div.getElementsByTagName("*"),a=div.getElementsByTagName("a")[0];if(!all||!all.length||!a){return;} +jQuery.support={leadingWhitespace:div.firstChild.nodeType===3,tbody:!div.getElementsByTagName("tbody").length,htmlSerialize:!!div.getElementsByTagName("link").length,style:/red/.test(a.getAttribute("style")),hrefNormalized:a.getAttribute("href")==="/a",opacity:/^0.55$/.test(a.style.opacity),cssFloat:!!a.style.cssFloat,checkOn:div.getElementsByTagName("input")[0].value==="on",optSelected:document.createElement("select").appendChild(document.createElement("option")).selected,parentNode:div.removeChild(div.appendChild(document.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};script.type="text/javascript";try{script.appendChild(document.createTextNode("window."+id+"=1;"));}catch(e){} +root.insertBefore(script,root.firstChild);if(window[id]){jQuery.support.scriptEval=true;delete window[id];} +try{delete script.test;}catch(e){jQuery.support.deleteExpando=false;} +root.removeChild(script);if(div.attachEvent&&div.fireEvent){div.attachEvent("onclick",function click(){jQuery.support.noCloneEvent=false;div.detachEvent("onclick",click);});div.cloneNode(true).fireEvent("onclick");} +div=document.createElement("div");div.innerHTML="";var fragment=document.createDocumentFragment();fragment.appendChild(div.firstChild);jQuery.support.checkClone=fragment.cloneNode(true).cloneNode(true).lastChild.checked;jQuery(function(){var div=document.createElement("div");div.style.width=div.style.paddingLeft="1px";document.body.appendChild(div);jQuery.boxModel=jQuery.support.boxModel=div.offsetWidth===2;document.body.removeChild(div).style.display='none';div=null;});var eventSupported=function(eventName){var el=document.createElement("div");eventName="on"+eventName;var isSupported=(eventName in el);if(!isSupported){el.setAttribute(eventName,"return;");isSupported=typeof el[eventName]==="function";} +el=null;return isSupported;};jQuery.support.submitBubbles=eventSupported("submit");jQuery.support.changeBubbles=eventSupported("change");root=script=div=all=a=null;})();jQuery.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var expando="jQuery"+now(),uuid=0,windowData={};jQuery.extend({cache:{},expando:expando,noData:{"embed":true,"object":true,"applet":true},data:function(elem,name,data){if(elem.nodeName&&jQuery.noData[elem.nodeName.toLowerCase()]){return;} +elem=elem==window?windowData:elem;var id=elem[expando],cache=jQuery.cache,thisCache;if(!id&&typeof name==="string"&&data===undefined){return null;} +if(!id){id=++uuid;} +if(typeof name==="object"){elem[expando]=id;thisCache=cache[id]=jQuery.extend(true,{},name);}else if(!cache[id]){elem[expando]=id;cache[id]={};} +thisCache=cache[id];if(data!==undefined){thisCache[name]=data;} +return typeof name==="string"?thisCache[name]:thisCache;},removeData:function(elem,name){if(elem.nodeName&&jQuery.noData[elem.nodeName.toLowerCase()]){return;} +elem=elem==window?windowData:elem;var id=elem[expando],cache=jQuery.cache,thisCache=cache[id];if(name){if(thisCache){delete thisCache[name];if(jQuery.isEmptyObject(thisCache)){jQuery.removeData(elem);}}}else{if(jQuery.support.deleteExpando){delete elem[jQuery.expando];}else if(elem.removeAttribute){elem.removeAttribute(jQuery.expando);} +delete cache[id];}}});jQuery.fn.extend({data:function(key,value){if(typeof key==="undefined"&&this.length){return jQuery.data(this[0]);}else if(typeof key==="object"){return this.each(function(){jQuery.data(this,key);});} +var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length){data=jQuery.data(this[0],key);} +return data===undefined&&parts[1]?this.data(parts[0]):data;}else{return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});}},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});}});jQuery.extend({queue:function(elem,type,data){if(!elem){return;} +type=(type||"fx")+"queue";var q=jQuery.data(elem,type);if(!data){return q||[];} +if(!q||jQuery.isArray(data)){q=jQuery.data(elem,type,jQuery.makeArray(data));}else{q.push(data);} +return q;},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),fn=queue.shift();if(fn==="inprogress"){fn=queue.shift();} +if(fn){if(type==="fx"){queue.unshift("inprogress");} +fn.call(elem,function(){jQuery.dequeue(elem,type);});}}});jQuery.fn.extend({queue:function(type,data){if(typeof type!=="string"){data=type;type="fx";} +if(data===undefined){return jQuery.queue(this[0],type);} +return this.each(function(i,elem){var queue=jQuery.queue(this,type,data);if(type==="fx"&&queue[0]!=="inprogress"){jQuery.dequeue(this,type);}});},dequeue:function(type){return this.each(function(){jQuery.dequeue(this,type);});},delay:function(time,type){time=jQuery.fx?jQuery.fx.speeds[time]||time:time;type=type||"fx";return this.queue(type,function(){var elem=this;setTimeout(function(){jQuery.dequeue(elem,type);},time);});},clearQueue:function(type){return this.queue(type||"fx",[]);}});var rclass=/[\n\t]/g,rspace=/\s+/,rreturn=/\r/g,rspecialurl=/href|src|style/,rtype=/(button|input)/i,rfocusable=/(button|input|object|select|textarea)/i,rclickable=/^(a|area)$/i,rradiocheck=/radio|checkbox/;jQuery.fn.extend({attr:function(name,value){return access(this,name,value,true,jQuery.attr);},removeAttr:function(name,fn){return this.each(function(){jQuery.attr(this,name,"");if(this.nodeType===1){this.removeAttribute(name);}});},addClass:function(value){if(jQuery.isFunction(value)){return this.each(function(i){var self=jQuery(this);self.addClass(value.call(this,i,self.attr("class")));});} +if(value&&typeof value==="string"){var classNames=(value||"").split(rspace);for(var i=0,l=this.length;i-1){return true;}} +return false;},val:function(value){if(value===undefined){var elem=this[0];if(elem){if(jQuery.nodeName(elem,"option")){return(elem.attributes.value||{}).specified?elem.value:elem.text;} +if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type==="select-one";if(index<0){return null;} +for(var i=one?index:0,max=one?index+1:options.length;i=0;}else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(val);jQuery("option",this).each(function(){this.selected=jQuery.inArray(jQuery(this).val(),values)>=0;});if(!values.length){this.selectedIndex=-1;}}else{this.value=val;}});}});jQuery.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(elem,name,value,pass){if(!elem||elem.nodeType===3||elem.nodeType===8){return undefined;} +if(pass&&name in jQuery.attrFn){return jQuery(elem)[name](value);} +var notxml=elem.nodeType!==1||!jQuery.isXMLDoc(elem),set=value!==undefined;name=notxml&&jQuery.props[name]||name;if(elem.nodeType===1){var special=rspecialurl.test(name);if(name==="selected"&&!jQuery.support.optSelected){var parent=elem.parentNode;if(parent){parent.selectedIndex;if(parent.parentNode){parent.parentNode.selectedIndex;}}} +if(name in elem&¬xml&&!special){if(set){if(name==="type"&&rtype.test(elem.nodeName)&&elem.parentNode){jQuery.error("type property can't be changed");} +elem[name]=value;} +if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name)){return elem.getAttributeNode(name).nodeValue;} +if(name==="tabIndex"){var attributeNode=elem.getAttributeNode("tabIndex");return attributeNode&&attributeNode.specified?attributeNode.value:rfocusable.test(elem.nodeName)||rclickable.test(elem.nodeName)&&elem.href?0:undefined;} +return elem[name];} +if(!jQuery.support.style&¬xml&&name==="style"){if(set){elem.style.cssText=""+value;} +return elem.style.cssText;} +if(set){elem.setAttribute(name,""+value);} +var attr=!jQuery.support.hrefNormalized&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;} +return jQuery.style(elem,name,value);}});var rnamespaces=/\.(.*)$/,fcleanup=function(nm){return nm.replace(/[^\w\s\.\|`]/g,function(ch){return"\\"+ch;});};jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType===3||elem.nodeType===8){return;} +if(elem.setInterval&&(elem!==window&&!elem.frameElement)){elem=window;} +var handleObjIn,handleObj;if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;} +if(!handler.guid){handler.guid=jQuery.guid++;} +var elemData=jQuery.data(elem);if(!elemData){return;} +var events=elemData.events=elemData.events||{},eventHandle=elemData.handle,eventHandle;if(!eventHandle){elemData.handle=eventHandle=function(){return typeof jQuery!=="undefined"&&!jQuery.event.triggered?jQuery.event.handle.apply(eventHandle.elem,arguments):undefined;};} +eventHandle.elem=elem;types=types.split(" ");var type,i=0,namespaces;while((type=types[i++])){handleObj=handleObjIn?jQuery.extend({},handleObjIn):{handler:handler,data:data};if(type.indexOf(".")>-1){namespaces=type.split(".");type=namespaces.shift();handleObj.namespace=namespaces.slice(0).sort().join(".");}else{namespaces=[];handleObj.namespace="";} +handleObj.type=type;handleObj.guid=handler.guid;var handlers=events[type],special=jQuery.event.special[type]||{};if(!handlers){handlers=events[type]=[];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;}} +handlers.push(handleObj);jQuery.event.global[type]=true;} +elem=null;},global:{},remove:function(elem,types,handler,pos){if(elem.nodeType===3||elem.nodeType===8){return;} +var ret,type,fn,i=0,all,namespaces,namespace,special,eventType,handleObj,origType,elemData=jQuery.data(elem),events=elemData&&elemData.events;if(!elemData||!events){return;} +if(types&&types.type){handler=types.handler;types=types.type;} +if(!types||typeof types==="string"&&types.charAt(0)==="."){types=types||"";for(type in events){jQuery.event.remove(elem,type+types);} +return;} +types=types.split(" ");while((type=types[i++])){origType=type;handleObj=null;all=type.indexOf(".")<0;namespaces=[];if(!all){namespaces=type.split(".");type=namespaces.shift();namespace=new RegExp("(^|\\.)"+ +jQuery.map(namespaces.slice(0).sort(),fcleanup).join("\\.(?:.*\\.)?")+"(\\.|$)")} +eventType=events[type];if(!eventType){continue;} +if(!handler){for(var j=0;j=0){event.type=type=type.slice(0,-1);event.exclusive=true;} +if(!elem){event.stopPropagation();if(jQuery.event.global[type]){jQuery.each(jQuery.cache,function(){if(this.events&&this.events[type]){jQuery.event.trigger(event,data,this.handle.elem);}});}} +if(!elem||elem.nodeType===3||elem.nodeType===8){return undefined;} +event.result=undefined;event.target=elem;data=jQuery.makeArray(data);data.unshift(event);} +event.currentTarget=elem;var handle=jQuery.data(elem,"handle");if(handle){handle.apply(elem,data);} +var parent=elem.parentNode||elem.ownerDocument;try{if(!(elem&&elem.nodeName&&jQuery.noData[elem.nodeName.toLowerCase()])){if(elem["on"+type]&&elem["on"+type].apply(elem,data)===false){event.result=false;}}}catch(e){} +if(!event.isPropagationStopped()&&parent){jQuery.event.trigger(event,data,parent,true);}else if(!event.isDefaultPrevented()){var target=event.target,old,isClick=jQuery.nodeName(target,"a")&&type==="click",special=jQuery.event.special[type]||{};if((!special._default||special._default.call(elem,event)===false)&&!isClick&&!(target&&target.nodeName&&jQuery.noData[target.nodeName.toLowerCase()])){try{if(target[type]){old=target["on"+type];if(old){target["on"+type]=null;} +jQuery.event.triggered=true;target[type]();}}catch(e){} +if(old){target["on"+type]=old;} +jQuery.event.triggered=false;}}},handle:function(event){var all,handlers,namespaces,namespace,events;event=arguments[0]=jQuery.event.fix(event||window.event);event.currentTarget=this;all=event.type.indexOf(".")<0&&!event.exclusive;if(!all){namespaces=event.type.split(".");event.type=namespaces.shift();namespace=new RegExp("(^|\\.)"+namespaces.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");} +var events=jQuery.data(this,"events"),handlers=events[event.type];if(events&&handlers){handlers=handlers.slice(0);for(var j=0,l=handlers.length;j-1?jQuery.map(elem.options,function(elem){return elem.selected;}).join("-"):"";}else if(elem.nodeName.toLowerCase()==="select"){val=elem.selectedIndex;} +return val;},testChange=function testChange(e){var elem=e.target,data,val;if(!formElems.test(elem.nodeName)||elem.readOnly){return;} +data=jQuery.data(elem,"_change_data");val=getVal(elem);if(e.type!=="focusout"||elem.type!=="radio"){jQuery.data(elem,"_change_data",val);} +if(data===undefined||val===data){return;} +if(data!=null||val){e.type="change";return jQuery.event.trigger(e,arguments[1],elem);}};jQuery.event.special.change={filters:{focusout:testChange,click:function(e){var elem=e.target,type=elem.type;if(type==="radio"||type==="checkbox"||elem.nodeName.toLowerCase()==="select"){return testChange.call(this,e);}},keydown:function(e){var elem=e.target,type=elem.type;if((e.keyCode===13&&elem.nodeName.toLowerCase()!=="textarea")||(e.keyCode===32&&(type==="checkbox"||type==="radio"))||type==="select-multiple"){return testChange.call(this,e);}},beforeactivate:function(e){var elem=e.target;jQuery.data(elem,"_change_data",getVal(elem));}},setup:function(data,namespaces){if(this.type==="file"){return false;} +for(var type in changeFilters){jQuery.event.add(this,type+".specialChange",changeFilters[type]);} +return formElems.test(this.nodeName);},teardown:function(namespaces){jQuery.event.remove(this,".specialChange");return formElems.test(this.nodeName);}};changeFilters=jQuery.event.special.change.filters;} +function trigger(type,elem,args){args[0].type=type;return jQuery.event.handle.apply(elem,args);} +if(document.addEventListener){jQuery.each({focus:"focusin",blur:"focusout"},function(orig,fix){jQuery.event.special[fix]={setup:function(){this.addEventListener(orig,handler,true);},teardown:function(){this.removeEventListener(orig,handler,true);}};function handler(e){e=jQuery.event.fix(e);e.type=fix;return jQuery.event.handle.call(this,e);}});} +jQuery.each(["bind","one"],function(i,name){jQuery.fn[name]=function(type,data,fn){if(typeof type==="object"){for(var key in type){this[name](key,data,type[key],fn);} +return this;} +if(jQuery.isFunction(data)){fn=data;data=undefined;} +var handler=name==="one"?jQuery.proxy(fn,function(event){jQuery(this).unbind(event,handler);return fn.apply(this,arguments);}):fn;if(type==="unload"&&name!=="one"){this.one(type,data,fn);}else{for(var i=0,l=this.length;i+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,done=0,toString=Object.prototype.toString,hasDuplicate=false,baseHasDuplicate=true;[0,0].sort(function(){baseHasDuplicate=false;return 0;});var Sizzle=function(selector,context,results,seed){results=results||[];var origContext=context=context||document;if(context.nodeType!==1&&context.nodeType!==9){return[];} +if(!selector||typeof selector!=="string"){return results;} +var parts=[],m,set,checkSet,extra,prune=true,contextXML=isXML(context),soFar=selector;while((chunker.exec(""),m=chunker.exec(soFar))!==null){soFar=m[3];parts.push(m[1]);if(m[2]){extra=m[3];break;}} +if(parts.length>1&&origPOS.exec(selector)){if(parts.length===2&&Expr.relative[parts[0]]){set=posProcess(parts[0]+parts[1],context);}else{set=Expr.relative[parts[0]]?[context]:Sizzle(parts.shift(),context);while(parts.length){selector=parts.shift();if(Expr.relative[selector]){selector+=parts.shift();} +set=posProcess(selector,set);}}}else{if(!seed&&parts.length>1&&context.nodeType===9&&!contextXML&&Expr.match.ID.test(parts[0])&&!Expr.match.ID.test(parts[parts.length-1])){var ret=Sizzle.find(parts.shift(),context,contextXML);context=ret.expr?Sizzle.filter(ret.expr,ret.set)[0]:ret.set[0];} +if(context){var ret=seed?{expr:parts.pop(),set:makeArray(seed)}:Sizzle.find(parts.pop(),parts.length===1&&(parts[0]==="~"||parts[0]==="+")&&context.parentNode?context.parentNode:context,contextXML);set=ret.expr?Sizzle.filter(ret.expr,ret.set):ret.set;if(parts.length>0){checkSet=makeArray(set);}else{prune=false;} +while(parts.length){var cur=parts.pop(),pop=cur;if(!Expr.relative[cur]){cur="";}else{pop=parts.pop();} +if(pop==null){pop=context;} +Expr.relative[cur](checkSet,pop,contextXML);}}else{checkSet=parts=[];}} +if(!checkSet){checkSet=set;} +if(!checkSet){Sizzle.error(cur||selector);} +if(toString.call(checkSet)==="[object Array]"){if(!prune){results.push.apply(results,checkSet);}else if(context&&context.nodeType===1){for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&(checkSet[i]===true||checkSet[i].nodeType===1&&contains(context,checkSet[i]))){results.push(set[i]);}}}else{for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&checkSet[i].nodeType===1){results.push(set[i]);}}}}else{makeArray(checkSet,results);} +if(extra){Sizzle(extra,origContext,results,seed);Sizzle.uniqueSort(results);} +return results;};Sizzle.uniqueSort=function(results){if(sortOrder){hasDuplicate=baseHasDuplicate;results.sort(sortOrder);if(hasDuplicate){for(var i=1;i":function(checkSet,part){var isPartStr=typeof part==="string";if(isPartStr&&!/\W/.test(part)){part=part.toLowerCase();for(var i=0,l=checkSet.length;i=0)){if(!inplace){result.push(elem);}}else if(inplace){curLoop[i]=false;}}} +return false;},ID:function(match){return match[1].replace(/\\/g,"");},TAG:function(match,curLoop){return match[1].toLowerCase();},CHILD:function(match){if(match[1]==="nth"){var test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2]==="even"&&"2n"||match[2]==="odd"&&"2n+1"||!/\D/.test(match[2])&&"0n+"+match[2]||match[2]);match[2]=(test[1]+(test[2]||1))-0;match[3]=test[3]-0;} +match[0]=done++;return match;},ATTR:function(match,curLoop,inplace,result,not,isXML){var name=match[1].replace(/\\/g,"");if(!isXML&&Expr.attrMap[name]){match[1]=Expr.attrMap[name];} +if(match[2]==="~="){match[4]=" "+match[4]+" ";} +return match;},PSEUDO:function(match,curLoop,inplace,result,not){if(match[1]==="not"){if((chunker.exec(match[3])||"").length>1||/^\w/.test(match[3])){match[3]=Sizzle(match[3],null,null,curLoop);}else{var ret=Sizzle.filter(match[3],curLoop,inplace,true^not);if(!inplace){result.push.apply(result,ret);} +return false;}}else if(Expr.match.POS.test(match[0])||Expr.match.CHILD.test(match[0])){return true;} +return match;},POS:function(match){match.unshift(true);return match;}},filters:{enabled:function(elem){return elem.disabled===false&&elem.type!=="hidden";},disabled:function(elem){return elem.disabled===true;},checked:function(elem){return elem.checked===true;},selected:function(elem){elem.parentNode.selectedIndex;return elem.selected===true;},parent:function(elem){return!!elem.firstChild;},empty:function(elem){return!elem.firstChild;},has:function(elem,i,match){return!!Sizzle(match[3],elem).length;},header:function(elem){return/h\d/i.test(elem.nodeName);},text:function(elem){return"text"===elem.type;},radio:function(elem){return"radio"===elem.type;},checkbox:function(elem){return"checkbox"===elem.type;},file:function(elem){return"file"===elem.type;},password:function(elem){return"password"===elem.type;},submit:function(elem){return"submit"===elem.type;},image:function(elem){return"image"===elem.type;},reset:function(elem){return"reset"===elem.type;},button:function(elem){return"button"===elem.type||elem.nodeName.toLowerCase()==="button";},input:function(elem){return/input|select|textarea|button/i.test(elem.nodeName);}},setFilters:{first:function(elem,i){return i===0;},last:function(elem,i,match,array){return i===array.length-1;},even:function(elem,i){return i%2===0;},odd:function(elem,i){return i%2===1;},lt:function(elem,i,match){return imatch[3]-0;},nth:function(elem,i,match){return match[3]-0===i;},eq:function(elem,i,match){return match[3]-0===i;}},filter:{PSEUDO:function(elem,match,i,array){var name=match[1],filter=Expr.filters[name];if(filter){return filter(elem,i,match,array);}else if(name==="contains"){return(elem.textContent||elem.innerText||getText([elem])||"").indexOf(match[3])>=0;}else if(name==="not"){var not=match[3];for(var i=0,l=not.length;i=0);}}},ID:function(elem,match){return elem.nodeType===1&&elem.getAttribute("id")===match;},TAG:function(elem,match){return(match==="*"&&elem.nodeType===1)||elem.nodeName.toLowerCase()===match;},CLASS:function(elem,match){return(" "+(elem.className||elem.getAttribute("class"))+" ").indexOf(match)>-1;},ATTR:function(elem,match){var name=match[1],result=Expr.attrHandle[name]?Expr.attrHandle[name](elem):elem[name]!=null?elem[name]:elem.getAttribute(name),value=result+"",type=match[2],check=match[4];return result==null?type==="!=":type==="="?value===check:type==="*="?value.indexOf(check)>=0:type==="~="?(" "+value+" ").indexOf(check)>=0:!check?value&&result!==false:type==="!="?value!==check:type==="^="?value.indexOf(check)===0:type==="$="?value.substr(value.length-check.length)===check:type==="|="?value===check||value.substr(0,check.length+1)===check+"-":false;},POS:function(elem,match,i,array){var name=match[2],filter=Expr.setFilters[name];if(filter){return filter(elem,i,match,array);}}}};var origPOS=Expr.match.POS;for(var type in Expr.match){Expr.match[type]=new RegExp(Expr.match[type].source+/(?![^\[]*\])(?![^\(]*\))/.source);Expr.leftMatch[type]=new RegExp(/(^(?:.|\r|\n)*?)/.source+Expr.match[type].source.replace(/\\(\d+)/g,function(all,num){return"\\"+(num-0+1);}));} +var makeArray=function(array,results){array=Array.prototype.slice.call(array,0);if(results){results.push.apply(results,array);return results;} +return array;};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType;}catch(e){makeArray=function(array,results){var ret=results||[];if(toString.call(array)==="[object Array]"){Array.prototype.push.apply(ret,array);}else{if(typeof array.length==="number"){for(var i=0,l=array.length;i";var root=document.documentElement;root.insertBefore(form,root.firstChild);if(document.getElementById(id)){Expr.find.ID=function(match,context,isXML){if(typeof context.getElementById!=="undefined"&&!isXML){var m=context.getElementById(match[1]);return m?m.id===match[1]||typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id").nodeValue===match[1]?[m]:undefined:[];}};Expr.filter.ID=function(elem,match){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return elem.nodeType===1&&node&&node.nodeValue===match;};} +root.removeChild(form);root=form=null;})();(function(){var div=document.createElement("div");div.appendChild(document.createComment(""));if(div.getElementsByTagName("*").length>0){Expr.find.TAG=function(match,context){var results=context.getElementsByTagName(match[1]);if(match[1]==="*"){var tmp=[];for(var i=0;results[i];i++){if(results[i].nodeType===1){tmp.push(results[i]);}} +results=tmp;} +return results;};} +div.innerHTML="";if(div.firstChild&&typeof div.firstChild.getAttribute!=="undefined"&&div.firstChild.getAttribute("href")!=="#"){Expr.attrHandle.href=function(elem){return elem.getAttribute("href",2);};} +div=null;})();if(document.querySelectorAll){(function(){var oldSizzle=Sizzle,div=document.createElement("div");div.innerHTML="

      ";if(div.querySelectorAll&&div.querySelectorAll(".TEST").length===0){return;} +Sizzle=function(query,context,extra,seed){context=context||document;if(!seed&&context.nodeType===9&&!isXML(context)){try{return makeArray(context.querySelectorAll(query),extra);}catch(e){}} +return oldSizzle(query,context,extra,seed);};for(var prop in oldSizzle){Sizzle[prop]=oldSizzle[prop];} +div=null;})();} +(function(){var div=document.createElement("div");div.innerHTML="
      ";if(!div.getElementsByClassName||div.getElementsByClassName("e").length===0){return;} +div.lastChild.className="e";if(div.getElementsByClassName("e").length===1){return;} +Expr.order.splice(1,0,"CLASS");Expr.find.CLASS=function(match,context,isXML){if(typeof context.getElementsByClassName!=="undefined"&&!isXML){return context.getElementsByClassName(match[1]);}};div=null;})();function dirNodeCheck(dir,cur,doneName,checkSet,nodeCheck,isXML){for(var i=0,l=checkSet.length;i0){match=elem;break;}} +elem=elem[dir];} +checkSet[i]=match;}}} +var contains=document.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16);}:function(a,b){return a!==b&&(a.contains?a.contains(b):true);};var isXML=function(elem){var documentElement=(elem?elem.ownerDocument||elem:0).documentElement;return documentElement?documentElement.nodeName!=="HTML":false;};var posProcess=function(selector,context){var tmpSet=[],later="",match,root=context.nodeType?[context]:context;while((match=Expr.match.PSEUDO.exec(selector))){later+=match[0];selector=selector.replace(Expr.match.PSEUDO,"");} +selector=Expr.relative[selector]?selector+"*":selector;for(var i=0,l=root.length;i=0)===keep;});};jQuery.fn.extend({find:function(selector){var ret=this.pushStack("","find",selector),length=0;for(var i=0,l=this.length;i0){for(var n=length;n0;},closest:function(selectors,context){if(jQuery.isArray(selectors)){var ret=[],cur=this[0],match,matches={},selector;if(cur&&selectors.length){for(var i=0,l=selectors.length;i-1:jQuery(cur).is(match)){ret.push({selector:selector,elem:cur});delete matches[selector];}} +cur=cur.parentNode;}} +return ret;} +var pos=jQuery.expr.match.POS.test(selectors)?jQuery(selectors,context||this.context):null;return this.map(function(i,cur){while(cur&&cur.ownerDocument&&cur!==context){if(pos?pos.index(cur)>-1:jQuery(cur).is(selectors)){return cur;} +cur=cur.parentNode;} +return null;});},index:function(elem){if(!elem||typeof elem==="string"){return jQuery.inArray(this[0],elem?jQuery(elem):this.parent().children());} +return jQuery.inArray(elem.jquery?elem[0]:elem,this);},add:function(selector,context){var set=typeof selector==="string"?jQuery(selector,context||this.context):jQuery.makeArray(selector),all=jQuery.merge(this.get(),set);return this.pushStack(isDisconnected(set[0])||isDisconnected(all[0])?all:jQuery.unique(all));},andSelf:function(){return this.add(this.prevObject);}});function isDisconnected(node){return!node||!node.parentNode||node.parentNode.nodeType===11;} +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 jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"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.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(until,selector){var ret=jQuery.map(this,fn,until);if(!runtil.test(name)){selector=until;} +if(selector&&typeof selector==="string"){ret=jQuery.filter(selector,ret);} +ret=this.length>1?jQuery.unique(ret):ret;if((this.length>1||rmultiselector.test(selector))&&rparentsprev.test(name)){ret=ret.reverse();} +return this.pushStack(ret,name,slice.call(arguments).join(","));};});jQuery.extend({filter:function(expr,elems,not){if(not){expr=":not("+expr+")";} +return jQuery.find.matches(expr,elems);},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;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir]){if(cur.nodeType===1&&++num===result){break;}} +return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){r.push(n);}} +return r;}});var rinlinejQuery=/ jQuery\d+="(?:\d+|null)"/g,rleadingWhitespace=/^\s+/,rxhtmlTag=/(<([\w:]+)[^>]*?)\/>/g,rselfClosing=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,rtagName=/<([\w:]+)/,rtbody=/";},wrapMap={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]};wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;if(!jQuery.support.htmlSerialize){wrapMap._default=[1,"div
      ","
      "];} +jQuery.fn.extend({text:function(text){if(jQuery.isFunction(text)){return this.each(function(i){var self=jQuery(this);self.text(text.call(this,i,self.text()));});} +if(typeof text!=="object"&&text!==undefined){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));} +return jQuery.text(this);},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){return this.each(function(){jQuery(this).wrapAll(html);});},unwrap:function(){return this.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes);}}).end();},append:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType===1){this.appendChild(elem);}});},prepend:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType===1){this.insertBefore(elem,this.firstChild);}});},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this);});}else if(arguments.length){var set=jQuery(arguments[0]);set.push.apply(set,this.toArray());return this.pushStack(set,"before",arguments);}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});}else if(arguments.length){var set=this.pushStack(this,"after",arguments);set.push.apply(set,jQuery(arguments[0]).toArray());return set;}},remove:function(selector,keepData){for(var i=0,elem;(elem=this[i])!=null;i++){if(!selector||jQuery.filter(selector,[elem]).length){if(!keepData&&elem.nodeType===1){jQuery.cleanData(elem.getElementsByTagName("*"));jQuery.cleanData([elem]);} +if(elem.parentNode){elem.parentNode.removeChild(elem);}}} +return this;},empty:function(){for(var i=0,elem;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(elem.getElementsByTagName("*"));} +while(elem.firstChild){elem.removeChild(elem.firstChild);}} +return this;},clone:function(events){var ret=this.map(function(){if(!jQuery.support.noCloneEvent&&!jQuery.isXMLDoc(this)){var html=this.outerHTML,ownerDocument=this.ownerDocument;if(!html){var div=ownerDocument.createElement("div");div.appendChild(this.cloneNode(true));html=div.innerHTML;} +return jQuery.clean([html.replace(rinlinejQuery,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(rleadingWhitespace,"")],ownerDocument)[0];}else{return this.cloneNode(true);}});if(events===true){cloneCopyEvent(this,ret);cloneCopyEvent(this.find("*"),ret.find("*"));} +return ret;},html:function(value){if(value===undefined){return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(rinlinejQuery,""):null;}else if(typeof value==="string"&&!rnocache.test(value)&&(jQuery.support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,fcloseTag);try{for(var i=0,l=this.length;i0||results.cacheable||this.length>1?fragment.cloneNode(true):fragment);}} +if(scripts.length){jQuery.each(scripts,evalScript);}} +return this;function root(elem,cur){return jQuery.nodeName(elem,"table")?(elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody"))):elem;}}});function cloneCopyEvent(orig,ret){var i=0;ret.each(function(){if(this.nodeName!==(orig[i]&&orig[i].nodeName)){return;} +var oldData=jQuery.data(orig[i++]),curData=jQuery.data(this,oldData),events=oldData&&oldData.events;if(events){delete curData.handle;curData.events={};for(var type in events){for(var handler in events[type]){jQuery.event.add(this,type,events[type][handler],events[type][handler].data);}}}});} +function buildFragment(args,nodes,scripts){var fragment,cacheable,cacheresults,doc=(nodes&&nodes[0]?nodes[0].ownerDocument||nodes[0]:document);if(args.length===1&&typeof args[0]==="string"&&args[0].length<512&&doc===document&&!rnocache.test(args[0])&&(jQuery.support.checkClone||!rchecked.test(args[0]))){cacheable=true;cacheresults=jQuery.fragments[args[0]];if(cacheresults){if(cacheresults!==1){fragment=cacheresults;}}} +if(!fragment){fragment=doc.createDocumentFragment();jQuery.clean(args,doc,fragment,scripts);} +if(cacheable){jQuery.fragments[args[0]]=cacheresults?fragment:1;} +return{fragment:fragment,cacheable:cacheable};} +jQuery.fragments={};jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(selector){var ret=[],insert=jQuery(selector),parent=this.length===1&&this[0].parentNode;if(parent&&parent.nodeType===11&&parent.childNodes.length===1&&insert.length===1){insert[original](this[0]);return this;}else{for(var i=0,l=insert.length;i0?this.clone(true):this).get();jQuery.fn[original].apply(jQuery(insert[i]),elems);ret=ret.concat(elems);} +return this.pushStack(ret,name,insert.selector);}};});jQuery.extend({clean:function(elems,context,fragment,scripts){context=context||document;if(typeof context.createElement==="undefined"){context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;} +var ret=[];for(var i=0,elem;(elem=elems[i])!=null;i++){if(typeof elem==="number"){elem+="";} +if(!elem){continue;} +if(typeof elem==="string"&&!rhtml.test(elem)){elem=context.createTextNode(elem);}else if(typeof elem==="string"){elem=elem.replace(rxhtmlTag,fcloseTag);var tag=(rtagName.exec(elem)||["",""])[1].toLowerCase(),wrap=wrapMap[tag]||wrapMap._default,depth=wrap[0],div=context.createElement("div");div.innerHTML=wrap[1]+elem+wrap[2];while(depth--){div=div.lastChild;} +if(!jQuery.support.tbody){var hasBody=rtbody.test(elem),tbody=tag==="table"&&!hasBody?div.firstChild&&div.firstChild.childNodes:wrap[1]===""&&!hasBody?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j){if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length){tbody[j].parentNode.removeChild(tbody[j]);}}} +if(!jQuery.support.leadingWhitespace&&rleadingWhitespace.test(elem)){div.insertBefore(context.createTextNode(rleadingWhitespace.exec(elem)[0]),div.firstChild);} +elem=div.childNodes;} +if(elem.nodeType){ret.push(elem);}else{ret=jQuery.merge(ret,elem);}} +if(fragment){for(var i=0;ret[i];i++){if(scripts&&jQuery.nodeName(ret[i],"script")&&(!ret[i].type||ret[i].type.toLowerCase()==="text/javascript")){scripts.push(ret[i].parentNode?ret[i].parentNode.removeChild(ret[i]):ret[i]);}else{if(ret[i].nodeType===1){ret.splice.apply(ret,[i+1,0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))));} +fragment.appendChild(ret[i]);}}} +return ret;},cleanData:function(elems){var data,id,cache=jQuery.cache,special=jQuery.event.special,deleteExpando=jQuery.support.deleteExpando;for(var i=0,elem;(elem=elems[i])!=null;i++){id=elem[jQuery.expando];if(id){data=cache[id];if(data.events){for(var type in data.events){if(special[type]){jQuery.event.remove(elem,type);}else{removeEvent(elem,type,data.handle);}}} +if(deleteExpando){delete elem[jQuery.expando];}else if(elem.removeAttribute){elem.removeAttribute(jQuery.expando);} +delete cache[id];}}}});var rexclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,ralpha=/alpha\([^)]*\)/,ropacity=/opacity=([^)]*)/,rfloat=/float/i,rdashAlpha=/-([a-z])/ig,rupper=/([A-Z])/g,rnumpx=/^-?\d+(?:px)?$/i,rnum=/^-?\d/,cssShow={position:"absolute",visibility:"hidden",display:"block"},cssWidth=["Left","Right"],cssHeight=["Top","Bottom"],getComputedStyle=document.defaultView&&document.defaultView.getComputedStyle,styleFloat=jQuery.support.cssFloat?"cssFloat":"styleFloat",fcamelCase=function(all,letter){return letter.toUpperCase();};jQuery.fn.css=function(name,value){return access(this,name,value,true,function(elem,name,value){if(value===undefined){return jQuery.curCSS(elem,name);} +if(typeof value==="number"&&!rexclude.test(name)){value+="px";} +jQuery.style(elem,name,value);});};jQuery.extend({style:function(elem,name,value){if(!elem||elem.nodeType===3||elem.nodeType===8){return undefined;} +if((name==="width"||name==="height")&&parseFloat(value)<0){value=undefined;} +var style=elem.style||elem,set=value!==undefined;if(!jQuery.support.opacity&&name==="opacity"){if(set){style.zoom=1;var opacity=parseInt(value,10)+""==="NaN"?"":"alpha(opacity="+value*100+")";var filter=style.filter||jQuery.curCSS(elem,"filter")||"";style.filter=ralpha.test(filter)?filter.replace(ralpha,opacity):opacity;} +return style.filter&&style.filter.indexOf("opacity=")>=0?(parseFloat(ropacity.exec(style.filter)[1])/100)+"":"";} +if(rfloat.test(name)){name=styleFloat;} +name=name.replace(rdashAlpha,fcamelCase);if(set){style[name]=value;} +return style[name];},css:function(elem,name,force,extra){if(name==="width"||name==="height"){var val,props=cssShow,which=name==="width"?cssWidth:cssHeight;function getWH(){val=name==="width"?elem.offsetWidth:elem.offsetHeight;if(extra==="border"){return;} +jQuery.each(which,function(){if(!extra){val-=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;} +if(extra==="margin"){val+=parseFloat(jQuery.curCSS(elem,"margin"+this,true))||0;}else{val-=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;}});} +if(elem.offsetWidth!==0){getWH();}else{jQuery.swap(elem,props,getWH);} +return Math.max(0,Math.round(val));} +return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style,filter;if(!jQuery.support.opacity&&name==="opacity"&&elem.currentStyle){ret=ropacity.test(elem.currentStyle.filter||"")?(parseFloat(RegExp.$1)/100)+"":"";return ret===""?"1":ret;} +if(rfloat.test(name)){name=styleFloat;} +if(!force&&style&&style[name]){ret=style[name];}else if(getComputedStyle){if(rfloat.test(name)){name="float";} +name=name.replace(rupper,"-$1").toLowerCase();var defaultView=elem.ownerDocument.defaultView;if(!defaultView){return null;} +var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle){ret=computedStyle.getPropertyValue(name);} +if(name==="opacity"&&ret===""){ret="1";}}else if(elem.currentStyle){var camelCase=name.replace(rdashAlpha,fcamelCase);ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!rnumpx.test(ret)&&rnum.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=camelCase==="fontSize"?"1em":(ret||0);ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}} +return ret;},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];} +callback.call(elem);for(var name in options){elem.style[name]=old[name];}}});if(jQuery.expr&&jQuery.expr.filters){jQuery.expr.filters.hidden=function(elem){var width=elem.offsetWidth,height=elem.offsetHeight,skip=elem.nodeName.toLowerCase()==="tr";return width===0&&height===0&&!skip?true:width>0&&height>0&&!skip?false:jQuery.curCSS(elem,"display")==="none";};jQuery.expr.filters.visible=function(elem){return!jQuery.expr.filters.hidden(elem);};} +var jsc=now(),rscript=//gi,rselectTextarea=/select|textarea/i,rinput=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,jsre=/=\?(&|$)/,rquery=/\?/,rts=/(\?|&)_=.*?(&|$)/,rurl=/^(\w+:)?\/\/([^\/?#]+)/,r20=/%20/g,_load=jQuery.fn.load;jQuery.fn.extend({load:function(url,params,callback){if(typeof url!=="string"){return _load.call(this,url);}else if(!this.length){return this;} +var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);} +var type="GET";if(params){if(jQuery.isFunction(params)){callback=params;params=null;}else if(typeof params==="object"){params=jQuery.param(params,jQuery.ajaxSettings.traditional);type="POST";}} +var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status==="success"||status==="notmodified"){self.html(selector?jQuery("
      ").append(res.responseText.replace(rscript,"")).find(selector):res.responseText);} +if(callback){self.each(callback,[res.responseText,status,res]);}}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return this.elements?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||rselectTextarea.test(this.nodeName)||rinput.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data=null;} +return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data={};} +return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:window.XMLHttpRequest&&(window.location.protocol!=="file:"||!window.ActiveXObject)?function(){return new window.XMLHttpRequest();}:function(){try{return new window.ActiveXObject("Microsoft.XMLHTTP");}catch(e){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(origSettings){var s=jQuery.extend(true,{},jQuery.ajaxSettings,origSettings);var jsonp,status,data,callbackContext=origSettings&&origSettings.context||s,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data,s.traditional);} +if(s.dataType==="jsonp"){if(type==="GET"){if(!jsre.test(s.url)){s.url+=(rquery.test(s.url)?"&":"?")+(s.jsonp||"callback")+"=?";}}else if(!s.data||!jsre.test(s.data)){s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";} +s.dataType="json";} +if(s.dataType==="json"&&(s.data&&jsre.test(s.data)||jsre.test(s.url))){jsonp=s.jsonpCallback||("jsonp"+jsc++);if(s.data){s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");} +s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=window[jsonp]||function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){} +if(head){head.removeChild(script);}};} +if(s.dataType==="script"&&s.cache===null){s.cache=false;} +if(s.cache===false&&type==="GET"){var ts=now();var ret=s.url.replace(rts,"$1_="+ts+"$2");s.url=ret+((ret===s.url)?(rquery.test(s.url)?"&":"?")+"_="+ts:"");} +if(s.data&&type==="GET"){s.url+=(rquery.test(s.url)?"&":"?")+s.data;} +if(s.global&&!jQuery.active++){jQuery.event.trigger("ajaxStart");} +var parts=rurl.exec(s.url),remote=parts&&(parts[1]&&parts[1]!==location.protocol||parts[2]!==location.host);if(s.dataType==="script"&&type==="GET"&&remote){var head=document.getElementsByTagName("head")[0]||document.documentElement;var script=document.createElement("script");script.src=s.url;if(s.scriptCharset){script.charset=s.scriptCharset;} +if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){done=true;success();complete();script.onload=script.onreadystatechange=null;if(head&&script.parentNode){head.removeChild(script);}}};} +head.insertBefore(script,head.firstChild);return undefined;} +var requestDone=false;var xhr=s.xhr();if(!xhr){return;} +if(s.username){xhr.open(type,s.url,s.async,s.username,s.password);}else{xhr.open(type,s.url,s.async);} +try{if(s.data||origSettings&&origSettings.contentType){xhr.setRequestHeader("Content-Type",s.contentType);} +if(s.ifModified){if(jQuery.lastModified[s.url]){xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]);} +if(jQuery.etag[s.url]){xhr.setRequestHeader("If-None-Match",jQuery.etag[s.url]);}} +if(!remote){xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");} +xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){} +if(s.beforeSend&&s.beforeSend.call(callbackContext,xhr,s)===false){if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop");} +xhr.abort();return false;} +if(s.global){trigger("ajaxSend",[xhr,s]);} +var onreadystatechange=xhr.onreadystatechange=function(isTimeout){if(!xhr||xhr.readyState===0||isTimeout==="abort"){if(!requestDone){complete();} +requestDone=true;if(xhr){xhr.onreadystatechange=jQuery.noop;}}else if(!requestDone&&xhr&&(xhr.readyState===4||isTimeout==="timeout")){requestDone=true;xhr.onreadystatechange=jQuery.noop;status=isTimeout==="timeout"?"timeout":!jQuery.httpSuccess(xhr)?"error":s.ifModified&&jQuery.httpNotModified(xhr,s.url)?"notmodified":"success";var errMsg;if(status==="success"){try{data=jQuery.httpData(xhr,s.dataType,s);}catch(err){status="parsererror";errMsg=err;}} +if(status==="success"||status==="notmodified"){if(!jsonp){success();}}else{jQuery.handleError(s,xhr,status,errMsg);} +complete();if(isTimeout==="timeout"){xhr.abort();} +if(s.async){xhr=null;}}};try{var oldAbort=xhr.abort;xhr.abort=function(){if(xhr){oldAbort.call(xhr);} +onreadystatechange("abort");};}catch(e){} +if(s.async&&s.timeout>0){setTimeout(function(){if(xhr&&!requestDone){onreadystatechange("timeout");}},s.timeout);} +try{xhr.send(type==="POST"||type==="PUT"||type==="DELETE"?s.data:null);}catch(e){jQuery.handleError(s,xhr,null,e);complete();} +if(!s.async){onreadystatechange();} +function success(){if(s.success){s.success.call(callbackContext,data,status,xhr);} +if(s.global){trigger("ajaxSuccess",[xhr,s]);}} +function complete(){if(s.complete){s.complete.call(callbackContext,xhr,status);} +if(s.global){trigger("ajaxComplete",[xhr,s]);} +if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop");}} +function trigger(type,args){(s.context?jQuery(s.context):jQuery.event).trigger(type,args);} +return xhr;},handleError:function(s,xhr,status,e){if(s.error){s.error.call(s.context||s,xhr,status,e);} +if(s.global){(s.context?jQuery(s.context):jQuery.event).trigger("ajaxError",[xhr,s,e]);}},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol==="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status===304||xhr.status===1223||xhr.status===0;}catch(e){} +return false;},httpNotModified:function(xhr,url){var lastModified=xhr.getResponseHeader("Last-Modified"),etag=xhr.getResponseHeader("Etag");if(lastModified){jQuery.lastModified[url]=lastModified;} +if(etag){jQuery.etag[url]=etag;} +return xhr.status===304||xhr.status===0;},httpData:function(xhr,type,s){var ct=xhr.getResponseHeader("content-type")||"",xml=type==="xml"||!type&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.nodeName==="parsererror"){jQuery.error("parsererror");} +if(s&&s.dataFilter){data=s.dataFilter(data,type);} +if(typeof data==="string"){if(type==="json"||!type&&ct.indexOf("json")>=0){data=jQuery.parseJSON(data);}else if(type==="script"||!type&&ct.indexOf("javascript")>=0){jQuery.globalEval(data);}} +return data;},param:function(a,traditional){var s=[];if(traditional===undefined){traditional=jQuery.ajaxSettings.traditional;} +if(jQuery.isArray(a)||a.jquery){jQuery.each(a,function(){add(this.name,this.value);});}else{for(var prefix in a){buildParams(prefix,a[prefix]);}} +return s.join("&").replace(r20,"+");function buildParams(prefix,obj){if(jQuery.isArray(obj)){jQuery.each(obj,function(i,v){if(traditional||/\[\]$/.test(prefix)){add(prefix,v);}else{buildParams(prefix+"["+(typeof v==="object"||jQuery.isArray(v)?i:"")+"]",v);}});}else if(!traditional&&obj!=null&&typeof obj==="object"){jQuery.each(obj,function(k,v){buildParams(prefix+"["+k+"]",v);});}else{add(prefix,obj);}} +function add(key,value){value=jQuery.isFunction(value)?value():value;s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value);}}});var elemdisplay={},rfxtypes=/toggle|show|hide/,rfxnum=/^([+-]=)?([\d+-.]+)(.*)$/,timerId,fxAttrs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];jQuery.fn.extend({show:function(speed,callback){if(speed||speed===0){return this.animate(genFx("show",3),speed,callback);}else{for(var i=0,l=this.length;i").appendTo("body");display=elem.css("display");if(display==="none"){display="block";} +elem.remove();elemdisplay[nodeName]=display;} +jQuery.data(this[i],"olddisplay",display);}} +for(var j=0,k=this.length;j=0;i--){if(timers[i].elem===this){if(gotoEnd){timers[i](true);} +timers.splice(i,1);}}});if(!gotoEnd){this.dequeue();} +return this;}});jQuery.each({slideDown:genFx("show",1),slideUp:genFx("hide",1),slideToggle:genFx("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(name,props){jQuery.fn[name]=function(speed,callback){return this.animate(props,speed,callback);};});jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&typeof speed==="object"?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&!jQuery.isFunction(easing)&&easing};opt.duration=jQuery.fx.off?0:typeof opt.duration==="number"?opt.duration:jQuery.fx.speeds[opt.duration]||jQuery.fx.speeds._default;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false){jQuery(this).dequeue();} +if(jQuery.isFunction(opt.old)){opt.old.call(this);}};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig){options.orig={};}}});jQuery.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this);} +(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style){this.elem.style.display="block";}},cur:function(force){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop];} +var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;var self=this;function t(gotoEnd){return self.step(gotoEnd);} +t.elem=this.elem;if(t()&&jQuery.timers.push(t)&&!timerId){timerId=setInterval(jQuery.fx.tick,13);}},show:function(){this.options.orig[this.prop]=jQuery.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now(),done=true;if(gotoEnd||t>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var i in this.options.curAnim){if(this.options.curAnim[i]!==true){done=false;}} +if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;var old=jQuery.data(this.elem,"olddisplay");this.elem.style.display=old?old:this.options.display;if(jQuery.css(this.elem,"display")==="none"){this.elem.style.display="block";}} +if(this.options.hide){jQuery(this.elem).hide();} +if(this.options.hide||this.options.show){for(var p in this.options.curAnim){jQuery.style(this.elem,p,this.options.orig[p]);}} +this.options.complete.call(this.elem);} +return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;var specialEasing=this.options.specialEasing&&this.options.specialEasing[this.prop];var defaultEasing=this.options.easing||(jQuery.easing.swing?"swing":"linear");this.pos=jQuery.easing[specialEasing||defaultEasing](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();} +return true;}};jQuery.extend(jQuery.fx,{tick:function(){var timers=jQuery.timers;for(var i=0;i
      ";jQuery.extend(container.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});container.innerHTML=html;body.insertBefore(container,body.firstChild);innerDiv=container.firstChild;checkDiv=innerDiv.firstChild;td=innerDiv.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(checkDiv.offsetTop!==5);this.doesAddBorderForTableAndCells=(td.offsetTop===5);checkDiv.style.position="fixed",checkDiv.style.top="20px";this.supportsFixedPosition=(checkDiv.offsetTop===20||checkDiv.offsetTop===15);checkDiv.style.position=checkDiv.style.top="";innerDiv.style.overflow="hidden",innerDiv.style.position="relative";this.subtractsBorderForOverflowNotVisible=(checkDiv.offsetTop===-5);this.doesNotIncludeMarginInBodyOffset=(body.offsetTop!==bodyMarginTop);body.removeChild(container);body=container=innerDiv=checkDiv=table=td=null;jQuery.offset.initialize=jQuery.noop;},bodyOffset:function(body){var top=body.offsetTop,left=body.offsetLeft;jQuery.offset.initialize();if(jQuery.offset.doesNotIncludeMarginInBodyOffset){top+=parseFloat(jQuery.curCSS(body,"marginTop",true))||0;left+=parseFloat(jQuery.curCSS(body,"marginLeft",true))||0;} +return{top:top,left:left};},setOffset:function(elem,options,i){if(/static/.test(jQuery.curCSS(elem,"position"))){elem.style.position="relative";} +var curElem=jQuery(elem),curOffset=curElem.offset(),curTop=parseInt(jQuery.curCSS(elem,"top",true),10)||0,curLeft=parseInt(jQuery.curCSS(elem,"left",true),10)||0;if(jQuery.isFunction(options)){options=options.call(elem,i,curOffset);} +var props={top:(options.top-curOffset.top)+curTop,left:(options.left-curOffset.left)+curLeft};if("using"in options){options.using.call(elem,props);}else{curElem.css(props);}}};jQuery.fn.extend({position:function(){if(!this[0]){return null;} +var elem=this[0],offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].nodeName)?{top:0,left:0}:offsetParent.offset();offset.top-=parseFloat(jQuery.curCSS(elem,"marginTop",true))||0;offset.left-=parseFloat(jQuery.curCSS(elem,"marginLeft",true))||0;parentOffset.top+=parseFloat(jQuery.curCSS(offsetParent[0],"borderTopWidth",true))||0;parentOffset.left+=parseFloat(jQuery.curCSS(offsetParent[0],"borderLeftWidth",true))||0;return{top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};},offsetParent:function(){return this.map(function(){var offsetParent=this.offsetParent||document.body;while(offsetParent&&(!/^body|html$/i.test(offsetParent.nodeName)&&jQuery.css(offsetParent,"position")==="static")){offsetParent=offsetParent.offsetParent;} +return offsetParent;});}});jQuery.each(["Left","Top"],function(i,name){var method="scroll"+name;jQuery.fn[method]=function(val){var elem=this[0],win;if(!elem){return null;} +if(val!==undefined){return this.each(function(){win=getWindow(this);if(win){win.scrollTo(!i?val:jQuery(win).scrollLeft(),i?val:jQuery(win).scrollTop());}else{this[method]=val;}});}else{win=getWindow(elem);return win?("pageXOffset"in win)?win[i?"pageYOffset":"pageXOffset"]:jQuery.support.boxModel&&win.document.documentElement[method]||win.document.body[method]:elem[method];}};});function getWindow(elem){return("scrollTo"in elem&&elem.document)?elem:elem.nodeType===9?elem.defaultView||elem.parentWindow:false;} +jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn["inner"+name]=function(){return this[0]?jQuery.css(this[0],type,false,"padding"):null;};jQuery.fn["outer"+name]=function(margin){return this[0]?jQuery.css(this[0],type,false,margin?"margin":"border"):null;};jQuery.fn[type]=function(size){var elem=this[0];if(!elem){return size==null?null:this;} +if(jQuery.isFunction(size)){return this.each(function(i){var self=jQuery(this);self[type](size.call(this,i,self[type]()));});} +return("scrollTo"in elem&&elem.document)?elem.document.compatMode==="CSS1Compat"&&elem.document.documentElement["client"+name]||elem.document.body["client"+name]:(elem.nodeType===9)?Math.max(elem.documentElement["client"+name],elem.body["scroll"+name],elem.documentElement["scroll"+name],elem.body["offset"+name],elem.documentElement["offset"+name]):size===undefined?jQuery.css(elem,type):this.css(type,typeof size==="string"?size:size+"px");};});window.jQuery=window.$=jQuery;})(window); \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery.modal.js b/v2/dotclear/admin/js/jquery/jquery.modal.js new file mode 100644 index 0000000..03703e9 --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery.modal.js @@ -0,0 +1,17 @@ + +(function($){if(/^1\.(0|1)\./.test($.fn.jquery)||/^1\.2\.(0|1|2|3|4|5)/.test($.fn.jquery)){throw('Modal requieres jQuery v1.2.6 or later. You are using v'+$.fn.jquery);return;} +$.modal=function(data,params){this.params=$.extend(this.params,params);return this.build(data);};$.modal.version='1.0';$.modal.prototype={params:{width:null,height:null,speed:300,opacity:0.9,loader_img:'loader.gif',loader_txt:'loading...',close_img:'close.png',close_txt:'close',on_close:function(){}},ctrl:{box:$(),loader:$(),overlay:$('
      '),hidden:$()},build:function(data){this.ctrl.loader=$('
      '+this.params.loader_txt+'
      ');this.addOverlay();var size=this.getBoxSize(this.ctrl.loading);this.ctrl.box=this.getBox(this.ctrl.loading,{top:Math.round($(window).height()/2+$(window).scrollTop()-size.h/2),left:Math.round($(window).width()/2+$(window).scrollLeft()-size.w/2),visibility:'hidden'});this.ctrl.overlay.after(this.ctrl.box);if(data!=undefined){this.updateBox(data);this.data=data;} +return this;},updateBox:function(data,fn){var This=this;this.hideCloser();fn=$.isFunction(fn)?fn:function(){};var content=$('div.jq-modal-content',this.ctrl.box);content.empty().append(this.ctrl.loader);var size=this.getBoxSize(data,this.params.width,this.params.height);var top=Math.round($(window).height()/2+$(window).scrollTop()-size.h/2);var left=Math.round($(window).width()/2+$(window).scrollLeft()-size.w/2);this.ctrl.box.css('visibility','visible').animate({top:top<0?0:top,left:left<0?0:left,width:size.w,height:size.h},this.params.speed,function(){This.setContentSize(content,This.params.width,This.params.height);content.empty().append(data).css('opacity',0).fadeTo(This.params.speed,1,function(){fn.call(This,content);});This.showCloser();});},getBox:function(data,css,content_w,content_h){var box=$('
      '+'
      '+'
      ').css($.extend({position:'absolute',top:0,left:0,zIndex:100},css));if(data!=undefined){$('div.jq-modal-content',box).append(data);} +this.setContentSize($('div.jq-modal-content',box),content_w,content_h);return box;},getBoxSize:function(data,content_w,content_h){var box=this.getBox(data,{visibility:'hidden'},content_w,content_h);this.ctrl.overlay.after(box);var size={w:box.width(),h:box.height()};box.remove();box=null;return size;},setContentSize:function(content,w,h){content.css({width:w>0?w:'auto',height:h>0?h:'auto'});},showCloser:function(){if($('div.jq-modal-closer',this.ctrl.box).length>0){$('div.jq-modal-closer',this.ctrl.box).show();return;} +$('div.jq-modal-container',this.ctrl.box).append('');var This=this;var close=$('div.jq-modal-closer a',this.ctrl.box) +close.css({background:'transparent url('+this.params.close_img+') no-repeat'}).click(function(){This.removeOverlay();return false;});if(document.all){close[0].runtimeStyle.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+this.params.close_img+'", sizingMethod="crop")';close[0].runtimeStyle.backgroundImage="none"}},hideCloser:function(){$('div.jq-modal-closer',this.ctrl.box).hide();},addOverlay:function(){var This=this;if(document.all){this.ctrl.hidden=$('select:visible, object:visible, embed:visible').css('visibility','hidden');} +this.ctrl.overlay.css({backgroundColor:'#000',position:'absolute',top:0,left:0,zIndex:90,opacity:this.params.opacity}).appendTo('body').dblclick(function(){This.removeOverlay();});this.resizeOverlay({data:this.ctrl});$(window).bind('resize.modal',this.ctrl,this.resizeOverlay);$(document).bind('keypress.modal',this,this.keyRemove);},resizeOverlay:function(e){e.data.overlay.css({width:$(window).width(),height:$(document).height()});if(e.data.box.parents('body').length>0){var top=Math.round($(window).height()/2+$(window).scrollTop()-e.data.box.height()/2);var left=Math.round($(window).width()/2+$(window).scrollLeft()-e.data.box.width()/2);e.data.box.css({top:top<0?0:top,left:left<0?0:left});}},keyRemove:function(e){if(e.keyCode==27){e.data.removeOverlay();} +return true;},removeOverlay:function(){$(window).unbind('resize.modal');$(document).unbind('keypress');this.params.on_close.apply(this);this.ctrl.overlay.remove();this.ctrl.hidden.css('visibility','visible');this.ctrl.box.remove();this.ctrl.box=$();}};})(jQuery);(function($){$.fn.modalImages=function(params){params=$.extend(this.params,params);var links=new Array();this.each(function(){if($(this).attr('href')==''||$(this).attr('href')==undefined||$(this).attr('href')=='#'){return false;} +var index=links.length;links.push($(this));$(this).click(function(){new $.modalImages(index,links,params);return false;});return true;});return this;};$.modalImages=function(index,links,params){this.links=links;this.modal=new $.modal(null,params);this.showImage(index);};$.modalImages.prototype={params:{prev_txt:'previous',next_txt:'next',prev_img:'prev.png',next_img:'next.png',blank_img:'blank.gif'},showImage:function(index){var This=this;$(document).unbind('keypress.modalImage');if(this.links[index]==undefined){this.modal.removeOverlay();} +var link=this.links[index];var modal=this.modal;var res=$('
      ');res.append('');var thumb=$('img:first',link);if(thumb.length>0&&thumb.attr('title')){res.append(''+thumb.attr('title')+'');}else if(link.attr('title')){res.append(''+link.attr('title')+'');} +if(index!=0){$('prev').appendTo(res);} +if(index+1next').appendTo(res);} +var img=new Image();if(this.modal.ctrl.box.css('visibility')=='visible'){$('div.jq-modal-content',this.modal.ctrl.box).empty().append(this.modal.ctrl.loader);}else{this.modal.updateBox(this.modal.ctrl.loader);} +img.onload=function(){modal.updateBox(res,function(){var Img=$('div.jq-modal-content img',this.ctrl.box);This.navBtnStyle($('a.jq-modal-next',this.ctrl.box),true).css('height',Img.height()).bind('click',index+1,navClick);This.navBtnStyle($('a.jq-modal-prev',this.ctrl.box),false).css('height',Img.height()).bind('click',index-1,navClick);Img.click(function(){This.modal.removeOverlay();});$(document).bind('keypress.modalImage',navKey);});this.onload=function(){};};img.src=link.attr('href');var navClick=function(e){This.showImage(e.data);return false;};var navKey=function(e){var key=String.fromCharCode(e.which).toLowerCase();if((key=='n'||e.keyCode==39)&&index+1').css({border:'none',width:w,height:h});return new $.modal(iframe);};$.fn.modalWeb=function(w,h){this.click(function(){if(this.href!=undefined){$.modalWeb(this.href,w,h);} +return false;});};})(jQuery); \ No newline at end of file diff --git a/v2/dotclear/admin/js/jquery/jquery.pageTabs.js b/v2/dotclear/admin/js/jquery/jquery.pageTabs.js new file mode 100644 index 0000000..5cca2c3 --- /dev/null +++ b/v2/dotclear/admin/js/jquery/jquery.pageTabs.js @@ -0,0 +1,4 @@ + +jQuery.pageTabs=function(start_tab,settings){return new jQuery._pageTabs(start_tab,settings);};jQuery._pageTabs=function(start_tab,settings){var defaults={className:'multi-part',listClassName:'part-tabs',breakerClassName:'clear'};var index=start_tab?start_tab:0;this.params=jQuery.extend(defaults,settings);this.divs=jQuery('div.'+this.params.className);this.createList();this.showDiv(index);};jQuery._pageTabs.prototype={items:new Array(),createList:function(){if(this.divs.length<=0){return;} +this.block=document.createElement('div');this.block.className=this.params.listClassName;this.list=document.createElement('ul');this.block.appendChild(this.list);var li,a;var This=this;var i=0;jQuery('.'+this.params.className).each(function(){if(this.tagName=="DIV"){li=document.createElement('li');a=document.createElement('a');a.appendChild(document.createTextNode(this.title));this.title='';a.href='#';a.fn=This.showDiv;a.index=this.id||i;a.obj=This;jQuery(a).click(function(){this.fn.call(this.obj,this.index);return false;});li.appendChild(a);This.list.appendChild(li);This.items[i]=li;i++;}else{li=document.createElement('li');li.className=This.params.listClassName+'-link';li.appendChild(this);This.list.appendChild(li);}});this.breaker=document.createElement('br');this.breaker.className=this.params.breakerClassName;jQuery(this.divs.get(0)).before(this.block);jQuery(this.block).after(this.breaker);},showDiv:function(index){var This=this;var i=0;var to_trigger=null;this.divs.each(function(){if((this.id!=''&&this.id==index)||i==index){jQuery(this).show(0,positionFooter);This.items[i].className=This.params.listClassName+'-active';to_trigger=i;}else{jQuery(this).hide(0,positionFooter);This.items[i].className='';} +i++;});if(to_trigger!=null){jQuery(this.divs[to_trigger]).onetabload();jQuery(this.divs[to_trigger]).tabload();}}};jQuery.fn.tabload=function(f){this.each(function(){if(f){chainHandler(this,'tabload',f)}else{var h=this.tabload;if(h){h.apply(this);}}});return this;};jQuery.fn.onetabload=function(f){this.each(function(){if(f){chainHandler(this,'onetabload',f);}else{var h=this.onetabload;if(h!=null){h.apply(this);this.onetabload=null;}}});return this;}; \ No newline at end of file diff --git a/v2/dotclear/admin/js/jsToolBar/jsToolBar.dotclear.js b/v2/dotclear/admin/js/jsToolBar/jsToolBar.dotclear.js new file mode 100644 index 0000000..83dd320 --- /dev/null +++ b/v2/dotclear/admin/js/jsToolBar/jsToolBar.dotclear.js @@ -0,0 +1,35 @@ + +jsToolBar.prototype.elements.link.data={};jsToolBar.prototype.elements.link.fncall={};jsToolBar.prototype.elements.link.open_url='popup_link.php';jsToolBar.prototype.elements.link.popup=function(args){window.the_toolbar=this;args=args||'';this.elements.link.data={};var url=this.elements.link.open_url+args;var p_win=window.open(url,'dc_popup','alwaysRaised=yes,dependent=yes,toolbar=yes,height=420,width=520,'+'menubar=no,resizable=yes,scrollbars=yes,status=no');};jsToolBar.prototype.elements.link.fn.wiki=function(){this.elements.link.popup.call(this,'?hreflang='+this.elements.link.default_hreflang);};jsToolBar.prototype.elements.link.fncall.wiki=function(){var data=this.elements.link.data;if(data.href==''){return;} +var etag='|'+data.href;if(data.hreflang){etag+='|'+data.hreflang;} +if(data.title){if(!data.hreflang){etag+='|';} +etag+='|'+data.title;} +if(data.content){this.encloseSelection('['+data.content,etag+']');}else{this.encloseSelection('[',etag+']');}};jsToolBar.prototype.elements.link.fn.xhtml=function(){this.elements.link.popup.call(this,'?hreflang='+this.elements.link.default_hreflang);};jsToolBar.prototype.elements.link.fncall.xhtml=function(){var data=this.elements.link.data;if(data.href==''){return;} +var stag='','>').replace('<','<').replace('"','"')+'"';} +res+=' />';if(d.link){var ltitle=(alt)?' title="'+alt.replace('&','&').replace('>','>').replace('<','<').replace('"','"')+'"':'';res=''+res+'';} +return res;});};jsToolBar.prototype.elements.img.fn.wysiwyg=function(){var src=this.elements.img.prompt.call(this);if(!src){return;} +var img=this.iwin.document.createElement('img');img.src=src;img.setAttribute('alt',this.getSelectedText());this.insertNode(img);};jsToolBar.prototype.elements.img_select.fn.wysiwyg=function(){this.elements.img_select.popup.call(this);};jsToolBar.prototype.elements.img_select.fncall.wysiwyg=function(){var d=this.elements.img_select.data;var alt=(this.getSelectedText())?this.getSelectedText():d.title;if(d.src==undefined){return;} +var img=this.iwin.document.createElement('img');img.src=d.src;img.setAttribute('alt',alt);if(d.alignment=='left'){if(img.style.styleFloat!=undefined){img.style.styleFloat='left';}else{img.style.cssFloat='left';} +img.style.marginTop=0;img.style.marginRight='1em';img.style.marginBottom='1em';img.style.marginLeft=0;}else if(d.alignment=='right'){if(img.style.styleFloat!=undefined){img.style.styleFloat='right';}else{img.style.cssFloat='right';} +img.style.marginTop=0;img.style.marginRight=0;img.style.marginBottom='1em';img.style.marginLeft='1em';}else if(d.alignment=='center'){img.style.marginTop=0;img.style.marginRight='auto';img.style.marginBottom=0;img.style.marginLeft='auto';img.style.display='block';} +if(d.description){img.setAttribute('title',d.description);} +if(d.link){var a=this.iwin.document.createElement('a');a.href=d.url;if(alt){a.setAttribute('title',alt);} +a.appendChild(img);this.insertNode(a);}else{this.insertNode(img);}};jsToolBar.prototype.elements.mp3_insert={fncall:{},data:{}};jsToolBar.prototype.elements.mp3_insert.fncall.wiki=function(){var d=this.elements.mp3_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n///html\n'+d.player+'///\n';});};jsToolBar.prototype.elements.mp3_insert.fncall.xhtml=function(){var d=this.elements.mp3_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n'+d.player+'\n';});};jsToolBar.prototype.elements.mp3_insert.fncall.wysiwyg=function(){return;};jsToolBar.prototype.elements.flv_insert={fncall:{},data:{}};jsToolBar.prototype.elements.flv_insert.fncall.wiki=function(){var d=this.elements.flv_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n///html\n'+d.player+'///\n';});};jsToolBar.prototype.elements.flv_insert.fncall.xhtml=function(){var d=this.elements.flv_insert.data;if(d.player==undefined){return;} +this.encloseSelection('','',function(str){return'\n'+d.player+'\n';});};jsToolBar.prototype.elements.flv_insert.fncall.wysiwyg=function(){return;};jsToolBar.prototype.elements.post_link={type:'button',title:'Link to an entry',fn:{},open_url:'popup_posts.php',data:{},popup:function(){window.the_toolbar=this;this.elements.img_select.data={};var p_win=window.open(this.elements.post_link.open_url,'dc_popup','alwaysRaised=yes,dependent=yes,toolbar=yes,height=500,width=760,'+'menubar=no,resizable=yes,scrollbars=yes,status=no');}};jsToolBar.prototype.elements.post_link.fn.wiki=function(){this.elements.post_link.popup.call(this);};jsToolBar.prototype.elements.post_link.fn.xhtml=function(){this.elements.post_link.popup.call(this);};jsToolBar.prototype.elements.post_link.fn.wysiwyg=function(){this.elements.post_link.popup.call(this);};jsToolBar.prototype.elements.space3={type:'space'}; \ No newline at end of file diff --git a/v2/dotclear/admin/js/jsToolBar/jsToolBar.js b/v2/dotclear/admin/js/jsToolBar/jsToolBar.js new file mode 100644 index 0000000..091df72 --- /dev/null +++ b/v2/dotclear/admin/js/jsToolBar/jsToolBar.js @@ -0,0 +1,29 @@ + +function jsToolBar(textarea){if(!document.createElement){return;} +if(!textarea){return;} +if((typeof(document["selection"])=="undefined")&&(typeof(textarea["setSelectionRange"])=="undefined")){return;} +this.textarea=textarea;this.editor=document.createElement('div');this.editor.className='jstEditor';this.textarea.parentNode.insertBefore(this.editor,this.textarea);this.editor.appendChild(this.textarea);this.toolbar=document.createElement("div");this.toolbar.className='jstElements';this.editor.parentNode.insertBefore(this.toolbar,this.editor);if(navigator.appName=='Microsoft Internet Explorer') +{if(this.editor.addEventListener) +{this.handle=document.createElement('div');this.handle.className='jstHandle';var dragStart=this.resizeDragStart;var This=this;this.handle.addEventListener('mousedown',function(event){dragStart.call(This,event);},false);this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);}} +this.context=null;this.toolNodes={};};function jsButton(title,fn,scope,className){this.title=title||null;this.fn=fn||function(){};this.scope=scope||null;this.className=className||null;};jsButton.prototype.draw=function(){if(!this.scope)return null;var button=document.createElement('button');button.setAttribute('type','button');if(this.className)button.className=this.className;button.title=this.title;var span=document.createElement('span');span.appendChild(document.createTextNode(this.title));button.appendChild(span);if(this.icon!=undefined){button.style.backgroundImage='url('+this.icon+')';} +if(typeof(this.fn)=='function'){var This=this;button.onclick=function(){try{This.fn.apply(This.scope,arguments)}catch(e){}return false;};} +return button;};function jsSpace(id){this.id=id||null;this.width=null;};jsSpace.prototype.draw=function(){var span=document.createElement('span');if(this.id)span.id=this.id;span.appendChild(document.createTextNode(String.fromCharCode(160)));span.className='jstSpacer';if(this.width)span.style.marginRight=this.width+'px';return span;};function jsCombo(title,options,scope,fn,className){this.title=title||null;this.options=options||null;this.scope=scope||null;this.fn=fn||function(){};this.className=className||null;};jsCombo.prototype.draw=function(){if(!this.scope||!this.options)return null;var select=document.createElement('select');if(this.className)select.className=className;select.title=this.title;for(var o in this.options){var option=document.createElement('option');option.value=o;option.appendChild(document.createTextNode(this.options[o]));select.appendChild(option);} +var This=this;select.onchange=function(){try{This.fn.call(This.scope,this.value);}catch(e){alert(e);} +return false;};return select;};jsToolBar.prototype={base_url:'',mode:'xhtml',elements:{},getMode:function(){return this.mode;},setMode:function(mode){this.mode=mode||'xhtml';},switchMode:function(mode){mode=mode||'xhtml';this.draw(mode);},button:function(toolName){var tool=this.elements[toolName];if(typeof tool.fn[this.mode]!='function')return null;var b=new jsButton(tool.title,tool.fn[this.mode],this,'jstb_'+toolName);if(tool.icon!=undefined){b.icon=tool.icon;} +return b;},space:function(toolName){var tool=new jsSpace(toolName);if(this.elements[toolName].width!==undefined){tool.width=this.elements[toolName].width;} +return tool;},combo:function(toolName){var tool=this.elements[toolName];var length=tool[this.mode].list.length;if(typeof tool[this.mode].fn!='function'||length==0){return null;}else{var options={};for(var i=0;i','');}catch(e){};this.toolNodes.blocks.value='nonebis';}},wiki:{list:['nonebis','h2','h3','h4','h5'],fn:function(opt){switch(opt){case'nonebis':this.textarea.focus();break;case'h2':this.encloseSelection('!!!!');break;case'h3':this.encloseSelection('!!!');break;case'h4':this.encloseSelection('!!');break;case'h5':this.encloseSelection('!');break;} +this.toolNodes.blocks.value='nonebis';}}};jsToolBar.prototype.elements.space0={type:'space'};jsToolBar.prototype.elements.strong={type:'button',title:'Strong emphasis',fn:{wiki:function(){this.singleTag('__')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.em={type:'button',title:'Emphasis',fn:{wiki:function(){this.singleTag("''")},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.ins={type:'button',title:'Inserted',fn:{wiki:function(){this.singleTag('++')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.del={type:'button',title:'Deleted',fn:{wiki:function(){this.singleTag('--')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.quote={type:'button',title:'Inline quote',fn:{wiki:function(){this.singleTag('{{','}}')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.code={type:'button',title:'Code',fn:{wiki:function(){this.singleTag('@@')},xhtml:function(){this.singleTag('','')}}};jsToolBar.prototype.elements.space1={type:'space'};jsToolBar.prototype.elements.br={type:'button',title:'Line break',fn:{wiki:function(){this.encloseSelection("%%%\n",'')},xhtml:function(){this.encloseSelection("
      \n",'')}}};jsToolBar.prototype.elements.space2={type:'space'};jsToolBar.prototype.elements.blockquote={type:'button',title:'Blockquote',fn:{xhtml:function(){this.singleTag('
      ','
      ')},wiki:function(){this.encloseSelection("\n",'',function(str){str=str.replace(/\r/g,'');return'> '+str.replace(/\n/g,"\n> ");});}}};jsToolBar.prototype.elements.pre={type:'button',title:'Preformated text',fn:{wiki:function(){this.singleTag("///\n","\n///")},xhtml:function(){this.singleTag('
      ','
      ')}}};jsToolBar.prototype.elements.ul={type:'button',title:'Unordered list',fn:{wiki:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');return'* '+str.replace(/\n/g,"\n* ");});},xhtml:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');str=str.replace(/\n/g,"\n
    • ");return"
        \n
      • "+str+"
      • \n
      ";});}}};jsToolBar.prototype.elements.ol={type:'button',title:'Ordered list',fn:{wiki:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');return'# '+str.replace(/\n/g,"\n# ");});},xhtml:function(){this.encloseSelection('','',function(str){str=str.replace(/\r/g,'');str=str.replace(/\n/g,"
    • \n
    • ");return"
        \n
      1. "+str+"
      2. \n
      ";});}}};jsToolBar.prototype.elements.space3={type:'space'};jsToolBar.prototype.elements.link={type:'button',title:'Link',fn:{},href_prompt:'Please give page URL:',hreflang_prompt:'Language of this page:',default_hreflang:'',prompt:function(href,hreflang){href=href||'';hreflang=hreflang||this.elements.link.default_hreflang;href=window.prompt(this.elements.link.href_prompt,href);if(!href){return false;} +hreflang=window.prompt(this.elements.link.hreflang_prompt,hreflang);return{href:this.stripBaseURL(href),hreflang:hreflang};}};jsToolBar.prototype.elements.link.fn.xhtml=function(){var link=this.elements.link.prompt.call(this);if(link){var stag='';var etag='';this.encloseSelection(stag,etag);}};jsToolBar.prototype.elements.link.fn.wiki=function(){var link=this.elements.link.prompt.call(this);if(link){var stag='[';var etag='|'+link.href;if(link.hreflang){etag=etag+'|'+link.hreflang;} +etag=etag+']';this.encloseSelection(stag,etag);}};jsToolBar.prototype.elements.img={type:'button',title:'External image',src_prompt:'Please give image URL:',fn:{},prompt:function(src){src=src||'';return this.stripBaseURL(window.prompt(this.elements.img.src_prompt,src));}};jsToolBar.prototype.elements.img.fn.xhtml=function(){var src=this.elements.img.prompt.call(this);if(src){this.encloseSelection('','',function(str){if(str){return''+str+'';}else{return'';}});}};jsToolBar.prototype.elements.img.fn.wiki=function(){var src=this.elements.img.prompt.call(this);if(src){this.encloseSelection('','',function(str){if(str){return'(('+src+'|'+str+'))';}else{return'(('+src+'))';}});}}; \ No newline at end of file diff --git a/v2/dotclear/admin/js/jsToolBar/jsToolBar.wysiwyg.js b/v2/dotclear/admin/js/jsToolBar/jsToolBar.wysiwyg.js new file mode 100644 index 0000000..c1e0b0a --- /dev/null +++ b/v2/dotclear/admin/js/jsToolBar/jsToolBar.wysiwyg.js @@ -0,0 +1,47 @@ + +jsToolBar.prototype.can_wwg=(document.designMode!=undefined);jsToolBar.prototype.iframe=null;jsToolBar.prototype.iwin=null;jsToolBar.prototype.ibody=null;jsToolBar.prototype.iframe_css=null;jsToolBar.prototype.drawToolBar=jsToolBar.prototype.draw;jsToolBar.prototype.draw=function(mode){mode=mode||'xhtml';if(this.can_wwg){this.mode='wysiwyg';this.drawToolBar('wysiwyg');this.initWindow();}else{this.drawToolBar(mode);}};jsToolBar.prototype.switchMode=function(mode){mode=mode||'xhtml';if(mode=='xhtml'){this.draw(mode);}else{if(this.wwg_mode){this.syncContents('iframe');} +this.removeEditor();this.textarea.style.display='';this.drawToolBar(mode);}};jsToolBar.prototype.syncContents=function(from){from=from||'textarea';var This=this;if(from=='textarea'){initContent();}else{this.validBlockquote();var html=this.applyHtmlFilters(this.ibody.innerHTML);if(html=='
      '){html='

      ';} +this.textarea.value=html;} +function initContent(){if(!This.iframe.contentWindow.document||!This.iframe.contentWindow.document.body){setTimeout(initContent,1);return;} +This.ibody=This.iframe.contentWindow.document.body;if(This.textarea.value!=''&&This.textarea.value!='

      '){This.ibody.innerHTML=This.applyWysiwygFilters(This.textarea.value);if(This.ibody.createTextRange){var IErange=This.ibody.createTextRange();IErange.execCommand("SelectAll");IErange.collapse();IErange.select();}}else if(window.navigator.product!=undefined&&window.navigator.product=='Gecko'){This.ibody.innerHTML='


      ';}else{var idoc=This.iwin.document;var para=idoc.createElement('p');para.appendChild(idoc.createTextNode(''));while(idoc.body.hasChildNodes()){idoc.body.removeChild(idoc.body.lastChild);} +idoc.body.appendChild(para);}}};jsToolBar.prototype.htmlFilters={tagsoup:function(str){return this.tagsoup2xhtml(str);}};jsToolBar.prototype.applyHtmlFilters=function(str){for(var fn in this.htmlFilters){str=this.htmlFilters[fn].call(this,str);} +return str;};jsToolBar.prototype.wysiwygFilters={};jsToolBar.prototype.applyWysiwygFilters=function(str){for(var fn in this.wysiwygFilters){str=this.wysiwygFilters[fn].call(this,str);} +return str;};jsToolBar.prototype.switchEdit=function(){if(this.wwg_mode){this.textarea.style.display='';this.iframe.style.display='none';this.syncContents('iframe');this.drawToolBar('xhtml');this.wwg_mode=false;this.focusEditor();}else{this.iframe.style.display='';this.textarea.style.display='none';this.syncContents('textarea');this.drawToolBar('wysiwyg');this.wwg_mode=true;this.focusEditor();} +this.setSwitcher();};jsToolBar.prototype.initWindow=function(){var This=this;this.iframe=document.createElement('iframe');this.textarea.parentNode.insertBefore(this.iframe,this.textarea.nextSibling);this.switcher=document.createElement('ul');this.switcher.className='jstSwitcher';this.editor.appendChild(this.switcher);this.iframe.height=this.textarea.offsetHeight+0;this.iframe.width=this.textarea.offsetWidth+0;if(this.textarea.tabIndex!=undefined){this.iframe.tabIndex=this.textarea.tabIndex;} +function initIframe(){var doc=This.iframe.contentWindow.document;if(!doc){setTimeout(initIframe,1);return false;} +doc.open();var html='\n'+'\n'+'\n'+ +(This.base_url!=''?'':'')+'\n'+'\n'+'\n'+'';doc.write(html);doc.close();if(document.all){doc.designMode='on';} +This.iwin=This.iframe.contentWindow;This.syncContents('textarea');if(This.wwg_mode==undefined){This.wwg_mode=true;} +if(This.wwg_mode){This.textarea.style.display='none';}else{This.iframe.style.display='none';} +if(This.textarea.form){chainHandler(This.textarea.form,'onsubmit',function(){if(This.wwg_mode){This.syncContents('iframe');}});} +for(var evt in This.iwinEvents){var event=This.iwinEvents[evt];This.addIwinEvent(This.iframe.contentWindow.document,event.type,event.fn,This);} +This.setSwitcher();setTimeout(function(){This.focusEditor();},1);return true;} +initIframe();};jsToolBar.prototype.addIwinEvent=function(target,type,fn,scope){var myFn=function(e){fn.call(scope,e)};addEvent(target,type,myFn,true);addEvent(scope.iwin,'unload',function(){removeEvent(target,type,myFn,true);},true);};jsToolBar.prototype.iwinEvents={block1:{type:'mouseup',fn:function(){this.adjustBlockLevelCombo()}},block2:{type:'keyup',fn:function(){this.adjustBlockLevelCombo()}}};jsToolBar.prototype.switcher_visual_title='visual';jsToolBar.prototype.switcher_source_title='source';jsToolBar.prototype.setSwitcher=function(){while(this.switcher.hasChildNodes()){this.switcher.removeChild(this.switcher.firstChild);} +var This=this;function setLink(title,link){var li=document.createElement('li');if(link){var a=document.createElement('a');a.href='#';a.editor=This;a.onclick=function(){this.editor.switchEdit();return false;};a.appendChild(document.createTextNode(title));}else{li.className='jstSwitcherCurrent';a=document.createTextNode(title);} +li.appendChild(a);This.switcher.appendChild(li);} +setLink(this.switcher_visual_title,!this.wwg_mode);setLink(this.switcher_source_title,this.wwg_mode);};jsToolBar.prototype.removeEditor=function(){if(this.iframe!=null){this.iframe.parentNode.removeChild(this.iframe);this.iframe=null;} +if(this.switcher!=undefined&&this.switcher.parentNode!=undefined){this.switcher.parentNode.removeChild(this.switcher);}};jsToolBar.prototype.focusEditor=function(){if(this.wwg_mode){try{this.iwin.document.designMode='on';}catch(e){};var This=this;setTimeout(function(){This.iframe.contentWindow.focus()},1);}else{this.textarea.focus();}};jsToolBar.prototype.resizeSetStartH=function(){if(this.wwg_mode&&this.iframe!=undefined){this.dragStartH=this.iframe.offsetHeight;return;} +this.dragStartH=this.textarea.offsetHeight+0;};jsToolBar.prototype.resizeDragMove=function(event){var new_height=(this.dragStartH+event.clientY-this.dragStartY)+'px';if(this.iframe!=undefined){this.iframe.style.height=new_height;} +this.textarea.style.height=new_height;};jsToolBar.prototype.insertNode=function(node){var range;if(this.iwin.getSelection){var sel=this.iwin.getSelection();range=sel.getRangeAt(0);sel.removeAllRanges();range.deleteContents();range.insertNode(node);range.selectNodeContents(node);range.setEndAfter(node);if(range.endContainer.childNodes.length>range.endOffset&&range.endContainer.nodeType!=Node.TEXT_NODE){range.setEnd(range.endContainer.childNodes[range.endOffset],0);}else{range.setEnd(range.endContainer.childNodes[0]);} +sel.addRange(range);sel.collapseToEnd();}else{var p=this.iwin.document.createElement('div');p.appendChild(node);range=this.iwin.document.selection.createRange();range.execCommand('delete');range.pasteHTML(p.innerHTML);range.collapse(false);range.select();} +this.iwin.focus();};jsToolBar.prototype.getSelectedNode=function(){if(this.iwin.getSelection){var sel=this.iwin.getSelection();var range=sel.getRangeAt(0);var content=range.cloneContents();}else{var sel=this.iwin.document.selection;var d=this.iwin.document.createElement('div');d.innerHTML=sel.createRange().htmlText;var content=this.iwin.document.createDocumentFragment();for(var i=0;i/gim,''],[/[\w\W]*?<\/style>/gim,''],[/<\/?font[\w\W]*?>/gim,''],[/<(\/?)(B|b|STRONG)([\s>\/])/g,"<$1strong$3"],[/<(\/?)(I|i|EM)([\s>\/])/g,"<$1em$3"],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/]*?[^\/])>/gi,""],[/
      ]*?[^\/])>/gi,"
      "],[/
      ]*?[^\/])>/gi,"
      "],[/<(\/?)U([\s>\/])/gi,"<$1ins$2"],[/<(\/?)STRIKE([\s>\/])/gi,"<$1del$2"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$1"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/span>/gm,"$2"],[/([\w\W]*?)<\/strong>/gm,"$1"],[/<([a-z]+) style="font-weight: normal;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="font-weight: bold;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="font-style: italic;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="text-decoration: underline;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="text-decoration: line-through;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="text-decoration: underline line-through;">([\w\W]*?)<\/\1>/gm,"<$1>$2"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: italic; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: italic; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline; ?){3}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: line-through; ?){3}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline line-through; ?){3}">([\w\W]*?)<\/\1>/gm,"<$1>$3"],[/

      (.*)(\n)+<\/blockquote><\/p>/i,"
      $1
      \n"],[/<\/(strong|em|ins|del|q|code)>(\s*?)<\1>/gim,"$2"],[/<(br|BR)>/g,"
      "],[/([^\s])\/>/g,"$1 />"],[/
      \s*<\/(h1|h2|h3|h4|h5|h6|ul|ol|li|p|blockquote|div)/gi,"([^\n\u000B\r\f])/gi,"\n$2"],[/
      /g,"
      "]);jsToolBar.prototype.tagsoup2xhtml=function(html){for(var reg in this.simpleCleanRegex){html=html.replace(this.simpleCleanRegex[reg][0],this.simpleCleanRegex[reg][1]);} +while(/(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/.test(html)){html=html.replace(/(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/g,"");} +html=html.replace(/<(\/?)([A-Z0-9]+)/g,function(match0,match1,match2){return"<"+match1+match2.toLowerCase();});var myRegexp=/<[^>]+((\s+\w+\s*=\s*)([^"'][\w~@+$,%\/:.#?=&;!*()-]*))[^>]*?>/;while(myRegexp.test(html)){html=html.replace(myRegexp,function(str,val1,val2,val3){var tamponRegex=new RegExp(regexpEscape(val1));return str.replace(tamponRegex,val2+'"'+val3+'"');})} +while(/(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/.test(html)){html=html.replace(/(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/gi,"$1"+"0$4");} +html=html.replace(/\r\n/g,"\n");html=html.replace(/^\s+/gm,'');html=html.replace(/\s+$/gm,'');return html;};jsToolBar.prototype.validBlockquote=function(){var blockElts=['address','blockquote','dl','div','fieldset','form','h1','h2','h3','h4','h5','h6','hr','ol','p','pre','table','ul'];var BQs=this.iwin.document.getElementsByTagName('blockquote');var bqChilds;for(var bq=0;bq=0;i--){if(bqChilds[i].nodeType==1&&arrayIndexOf(blockElts,bqChilds[i].tagName.toLowerCase())>=0) +{if(frag.childNodes.length>0){var p=this.iwin.document.createElement('p');p.appendChild(frag);BQs[bq].replaceChild(p,bqChilds[i+1]);frag=this.iwin.document.createDocumentFragment();}}else{if(frag.childNodes.length>0)BQs[bq].removeChild(bqChilds[i+1]);frag.insertBefore(bqChilds[i].cloneNode(true),frag.firstChild);}} +if(frag.childNodes.length>0){var p=this.iwin.document.createElement('p');p.appendChild(frag);BQs[bq].replaceChild(p,bqChilds[0]);}}};jsToolBar.prototype.removeFormatRegexp=new Array([/(<[a-z][^>]*)margin\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-bottom\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-left\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-right\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)margin-top\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-bottom\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-left\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-right\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)padding-top\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-family\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-size\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-style\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-variant\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)font-weight\s*:[^;]*;/mg,"$1"],[/(<[a-z][^>]*)color\s*:[^;]*;/mg,"$1"]);jsToolBar.prototype.removeTextFormating=function(html){for(var reg in this.removeFormatRegexp){html=html.replace(this.removeFormatRegexp[reg][0],this.removeFormatRegexp[reg][1]);} +html=this.tagsoup2xhtml(html);html=html.replace(/style="\s*?"/mgi,'');return html;};jsToolBar.prototype.elements.blocks.wysiwyg={list:['none','p','h1','h2','h3','h4','h5','h6'],fn:function(opt){if(opt=='none'){var blockLevel=this.getBlockLevel();if(blockLevel!==null){this.replaceNodeByContent(blockLevel);} +this.iwin.focus();}else{try{this.iwin.document.execCommand('formatblock',false,'<'+opt+'>');}catch(e){};this.iwin.focus();}}};jsToolBar.prototype.elements.strong.fn.wysiwyg=function(){this.iwin.document.execCommand('bold',false,null);this.iwin.focus();};jsToolBar.prototype.elements.em.fn.wysiwyg=function(){this.iwin.document.execCommand('italic',false,null);this.iwin.focus();};jsToolBar.prototype.elements.ins.fn.wysiwyg=function(){this.iwin.document.execCommand('underline',false,null);this.iwin.focus();};jsToolBar.prototype.elements.del.fn.wysiwyg=function(){this.iwin.document.execCommand('strikethrough',false,null);this.iwin.focus();};jsToolBar.prototype.elements.quote.fn.wysiwyg=function(){var n=this.getSelectedNode();var q=this.iwin.document.createElement('q');q.appendChild(n);this.insertNode(q);};jsToolBar.prototype.elements.code.fn.wysiwyg=function(){var n=this.getSelectedNode();var code=this.iwin.document.createElement('code');code.appendChild(n);this.insertNode(code);};jsToolBar.prototype.elements.br.fn.wysiwyg=function(){var n=this.iwin.document.createElement('br');this.insertNode(n);};jsToolBar.prototype.elements.blockquote.fn.wysiwyg=function(){var n=this.getSelectedNode();var q=this.iwin.document.createElement('blockquote');q.appendChild(n);this.insertNode(q);};jsToolBar.prototype.elements.pre.fn.wysiwyg=function(){this.iwin.document.execCommand('formatblock',false,'
      ');this.iwin.focus();};jsToolBar.prototype.elements.ul.fn.wysiwyg=function(){this.iwin.document.execCommand('insertunorderedlist',false,null);this.iwin.focus();};jsToolBar.prototype.elements.ol.fn.wysiwyg=function(){this.iwin.document.execCommand('insertorderedlist',false,null);this.iwin.focus();};jsToolBar.prototype.elements.link.fn.wysiwyg=function(){var href,hreflang;var range,commonAncestorContainer;if(this.iwin.getSelection){var selection=this.iwin.getSelection();range=selection.getRangeAt(0);commonAncestorContainer=range.commonAncestorContainer;while(commonAncestorContainer.nodeType!=1){commonAncestorContainer=commonAncestorContainer.parentNode;}}else{range=this.iwin.document.selection.createRange();commonAncestorContainer=range.parentElement();}
      +var ancestorTagName=commonAncestorContainer.tagName.toLowerCase();while(ancestorTagName!='a'&&ancestorTagName!='body'){commonAncestorContainer=commonAncestorContainer.parentNode;ancestorTagName=commonAncestorContainer.tagName.toLowerCase();}
      +if(ancestorTagName=='a'){href=commonAncestorContainer.href||'';hreflang=commonAncestorContainer.hreflang||'';}
      +href=window.prompt(this.elements.link.href_prompt,href);if(ancestorTagName=='a'&&href==''){this.replaceNodeByContent(commonAncestorContainer);}
      +if(!href)return;hreflang=window.prompt(this.elements.link.hreflang_prompt,hreflang);if(ancestorTagName=='a'&&href){commonAncestorContainer.setAttribute('href',href);if(hreflang){commonAncestorContainer.setAttribute('hreflang',hreflang);}else{commonAncestorContainer.removeAttribute('hreflang');}
      +return;}
      +var n=this.getSelectedNode();var a=this.iwin.document.createElement('a');a.href=href;if(hreflang)a.setAttribute('hreflang',hreflang);a.appendChild(n);this.insertNode(a);};jsToolBar.prototype.elements.removeFormat={type:'button',title:'Remove text formating',fn:{}};jsToolBar.prototype.elements.removeFormat.disabled=!jsToolBar.prototype.can_wwg;jsToolBar.prototype.elements.removeFormat.fn.xhtml=function(){var html=this.textarea.value;html=this.removeTextFormating(html);this.textarea.value=html;};jsToolBar.prototype.elements.removeFormat.fn.wysiwyg=function(){var html=this.iwin.document.body.innerHTML;html=this.removeTextFormating(html);this.iwin.document.body.innerHTML=html;};function arrayIndexOf(aArray,aValue){if(typeof Array.indexOf=='function'){return aArray.indexOf(aValue);}else{var index=-1;var l=aArray.length;for(var i=0;i'+player+'
    • ';} +tb.elements.mp3_insert.data.player=player.replace(/>/g,'>\n');tb.elements.mp3_insert.fncall[tb.mode].call(tb);} +else if(type=='flv') +{var oplayer=$('
      '+$('#public_player').val()+'
      ');var flashvars=$("[name=FlashVars]",oplayer).val();var align=$('input[name="alignment"]:checked',insert_form).val();var title=insert_form.elements.title.value;if(title){flashvars='title='+encodeURI(title)+'&'+flashvars;} +$('object',oplayer).attr('width',$('#video_w').val());$('object',oplayer).attr('height',$('#video_h').val());flashvars=flashvars.replace(/(width=\d*)/,'width='+$('#video_w').val());flashvars=flashvars.replace(/(height=\d*)/,'height='+$('#video_h').val());$("[name=FlashVars]",oplayer).val(flashvars);var player=oplayer.html();if(align!=undefined&&align!='none'){player='
      '+player+'
      ';} +tb.elements.flv_insert.data.player=player.replace(/>/g,'>\n');tb.elements.flv_insert.fncall[tb.mode].call(tb);} +else +{tb.elements.link.data.href=tb.stripBaseURL(insert_form.elements.url.value);tb.elements.link.data.content=insert_form.elements.title.value;tb.elements.link.fncall[tb.mode].call(tb);}};function playerFormat(s){s=s.replace(/</g,'<');s=s.replace(/>/g,'>\n');s=s.replace(/&/g,'&');return s;};}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/jsToolBar/popup_posts.js b/v2/dotclear/admin/js/jsToolBar/popup_posts.js new file mode 100644 index 0000000..0fbb77d --- /dev/null +++ b/v2/dotclear/admin/js/jsToolBar/popup_posts.js @@ -0,0 +1,2 @@ + +$(function(){$('#link-insert-cancel').click(function(){window.close();});$('#form-entries tr>td.maximal>a').click(function(){var tb=window.opener.the_toolbar;var data=tb.elements.link.data;data.href=tb.stripBaseURL($(this).attr('title'));tb.elements.link.fncall[tb.mode].call(tb);window.close();});}); \ No newline at end of file diff --git a/v2/dotclear/admin/js/meta-editor.js b/v2/dotclear/admin/js/meta-editor.js new file mode 100644 index 0000000..e9a3be3 --- /dev/null +++ b/v2/dotclear/admin/js/meta-editor.js @@ -0,0 +1,14 @@ + +function metaEditor(target,meta_field,meta_type){this.target=target;this.meta_field=meta_field;this.meta_type=meta_type;};metaEditor.prototype={meta_url:'',text_confirm_remove:'Are you sure you want to remove this %s?',text_add_meta:'Add a %s to this entry',text_choose:'Choose from list',text_all:'all',text_separation:'Separate each %s by comas',target:null,meta_type:null,meta_dialog:null,meta_field:null,submit_button:null,post_id:false,service_uri:'services.php',displayMeta:function(type,post_id){this.meta_type=type;this.post_id=post_id;this.target.empty();this.meta_dialog=$('');this.meta_dialog.attr('title',this.text_add_meta.replace(/%s/,this.meta_type));this.meta_dialog.attr('id','post_meta_input');this.meta_dialog.keypress(function(evt){if(evt.keyCode==13){This.addMeta(this.value);return false;} +return true;});var This=this;this.submit_button=$('');this.submit_button.click(function(){var v=This.meta_dialog.val();This.addMeta(v);return false;});this.addMetaDialog();if(this.post_id==false){this.target.append(this.meta_field);} +this.displayMetaList();},displayMetaList:function(){var li;if(this.meta_list==undefined){this.meta_list=$('
        ');this.target.prepend(this.meta_list);} +if(this.post_id==false){var meta=this.splitMetaValues(this.meta_field.val());this.meta_list.empty();for(var i=0;i'+meta[i]+'');a_remove=$('[x]');a_remove.get(0).caller=this;a_remove.get(0).meta_id=meta[i];a_remove.click(function(){this.caller.removeMeta(this.meta_id);return false;});li.append(' ').append(a_remove);this.meta_list.append(li);}}else{var This=this;var params={f:'getMeta',metaType:this.meta_type,sortby:'metaId,asc',postId:this.post_id};$.get(this.service_uri,params,function(data){data=$(data);if(data.find('rsp').attr('status')!='ok'){return;} +This.meta_list.empty();data.find('meta').each(function(){var meta_id=$(this).text();li=$('
      • '+meta_id+'
      • ');a_remove=$('[x]');a_remove.get(0).caller=This;a_remove.get(0).meta_id=meta_id;a_remove.click(function(){this.caller.removeMeta(this.meta_id);return false;});li.append(' ').append(a_remove);This.meta_list.append(li);});});}},addMetaDialog:function(){var This=this;if(this.submit_button==null){this.target.append($('

        ').append(this.meta_dialog));}else{this.target.append($('

        ').append(this.meta_dialog).append(' ').append(this.submit_button));} +var a=$(''+this.text_choose+'');a.click(function(){This.showMetaList(metaEditor.prototype.meta_type,$(this).parent());return false;});if(this.text_separation!=''){this.target.append($('

        ').addClass('form-note').append(this.text_separation.replace(/%s/,this.meta_type)));} +this.target.append($('

        ').append(a));},showMetaList:function(type,target){target.empty();target.append('...');target.addClass('addMeta');var params={f:'getMeta',metaType:this.meta_type,sortby:'metaId,asc'};if(type=='more'){params.limit='30';} +var This=this;$.get(this.service_uri,params,function(data){if($(data).find('meta').length>0){target.empty();var meta_link;$(data).find('meta').each(function(i){meta_link=$(''+$(this).text()+'');meta_link.get(0).meta_id=$(this).text();meta_link.click(function(){var v=This.splitMetaValues(This.meta_dialog.val()+','+this.meta_id);This.meta_dialog.val(v.join(','));return false;});if(i>0){target.append(', ');} +target.append(meta_link);});if(type=='more'){var a_more=$('');a_more.append(This.text_all+String.fromCharCode(160)+String.fromCharCode(187));a_more.click(function(){This.showMetaList('all',target);return false;});target.append(', ').append(a_more);}}else{target.empty();}});},addMeta:function(str){str=this.splitMetaValues(str).join(',');if(this.post_id==false){str=this.splitMetaValues(this.meta_field.val()+','+str);this.meta_field.val(str);this.meta_dialog.val('');this.displayMetaList();}else{var params={xd_check:dotclear.nonce,f:'setPostMeta',postId:this.post_id,metaType:this.meta_type,meta:str};var This=this;$.post(this.service_uri,params,function(data){if($(data).find('rsp').attr('status')=='ok'){This.meta_dialog.val('');This.displayMetaList();}else{alert($(data).find('message').text());}});}},removeMeta:function(meta_id){if(this.post_id==false){var meta=this.splitMetaValues(this.meta_field.val());for(var i=0;i=0){return this.create(document.body.scrollLeft,document.body.scrollTop)}else{return this.create(0,0)}},clientSize:function(){if(window.innerHeight>=0){return this.create(window.innerWidth,window.innerHeight)}else if(document.documentElement){return this.create(document.documentElement.clientWidth,document.documentElement.clientHeight)}else if(document.body.clientHeight>=0){return this.create(document.body.clientWidth,document.body.clientHeight)}else{return this.create(0,0)}},mousePosition:function(event){event=ToolMan.events().fix(event) +return this.create(event.clientX,event.clientY)},mouseOffset:function(event){event=ToolMan.events().fix(event) +if(event.pageX>=0||event.pageX<0){return this.create(event.pageX,event.pageY)}else if(event.clientX>=0||event.clientX<0){return this.mousePosition(event).plus(this.scrollOffset())}},_size:function(element){return this.create(element.offsetWidth,element.offsetHeight)},_offset:function(element){return this.create(element.offsetLeft,element.offsetTop)}} +function _ToolManCoordinate(factory,x,y){this.factory=factory +this.x=isNaN(x)?0:x +this.y=isNaN(y)?0:y} +_ToolManCoordinate.prototype={toString:function(){return"("+this.x+","+this.y+")"},plus:function(that){return this.factory.create(this.x+that.x,this.y+that.y)},minus:function(that){return this.factory.create(this.x-that.x,this.y-that.y)},min:function(that){return this.factory.create(Math.min(this.x,that.x),Math.min(this.y,that.y))},max:function(that){return this.factory.create(Math.max(this.x,that.x),Math.max(this.y,that.y))},constrainTo:function(one,two){var min=one.min(two) +var max=one.max(two) +return this.max(min).min(max)},distance:function(that){return Math.sqrt(Math.pow(this.x-that.x,2)+Math.pow(this.y-that.y,2))},reposition:function(element){element.style["top"]=this.y+"px" +element.style["left"]=this.x+"px"}} \ No newline at end of file diff --git a/v2/dotclear/admin/js/tool-man/core.js b/v2/dotclear/admin/js/tool-man/core.js new file mode 100644 index 0000000..7a46341 --- /dev/null +++ b/v2/dotclear/admin/js/tool-man/core.js @@ -0,0 +1,35 @@ + +var ToolMan={events:function(){if(!ToolMan._eventsFactory)throw"ToolMan Events module isn't loaded";return ToolMan._eventsFactory},css:function(){if(!ToolMan._cssFactory)throw"ToolMan CSS module isn't loaded";return ToolMan._cssFactory},coordinates:function(){if(!ToolMan._coordinatesFactory)throw"ToolMan Coordinates module isn't loaded";return ToolMan._coordinatesFactory},drag:function(){if(!ToolMan._dragFactory)throw"ToolMan Drag module isn't loaded";return ToolMan._dragFactory},dragsort:function(){if(!ToolMan._dragsortFactory)throw"ToolMan DragSort module isn't loaded";return ToolMan._dragsortFactory},helpers:function(){return ToolMan._helpers},cookies:function(){if(!ToolMan._cookieOven)throw"ToolMan Cookie module isn't loaded";return ToolMan._cookieOven},junkdrawer:function(){return ToolMan._junkdrawer}} +ToolMan._helpers={map:function(array,func){for(var i=0,n=array.length;i0)return identifier;identifier=trim(item.getAttribute("itemID")) +if(identifier!=null&&identifier.length>0)return identifier;return trim(item.innerHTML)},_itemsByID:function(list){var array=new Array() +var items=list.getElementsByTagName('li') +for(var i=0,n=items.length;isetCacheDir(DC_TPL_CACHE); +$feed_reader->setTimeout(5); +$feed_reader->setUserAgent('Dotclear - http://www.dotclear.org/'); +try { + $dc_langs = $feed_reader->parse(sprintf(DC_L10N_UPDATE_URL,DC_VERSION)); + if ($dc_langs !== false) { + $dc_langs = $dc_langs->items; + } +} catch (Exception $e) {} + +# Delete a language pack +if ($is_writable && !empty($_POST['delete']) && !empty($_POST['locale_id'])) +{ + try + { + $locale_id = $_POST['locale_id']; + if (!isset($iso_codes[$locale_id]) || !is_dir(DC_L10N_ROOT.'/'.$locale_id)) { + throw new Exception(__('No such installed language')); + } + + if ($locale_id == 'en') { + throw new Exception(__("You can't remove English language.")); + } + + if (!files::deltree(DC_L10N_ROOT.'/'.$locale_id)) { + throw new Exception(__('Permissions to delete language denied.')); + } + + http::redirect('langs.php?removed=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Download a language pack +if ($is_writable && !empty($_POST['pkg_url'])) +{ + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + $url = html::escapeHTML($_POST['pkg_url']); + $dest = DC_L10N_ROOT.'/'.basename($url); + if (!preg_match('#^http://[^.]+\.dotclear\.(net|org)/.*\.zip$#',$url)) { + throw new Exception(__('Invalid language file URL.')); + } + + $client = netHttp::initClient($url,$path); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + + try { + $ret_code = dc_lang_install($dest); + } catch (Exception $e) { + @unlink($dest); + throw $e; + } + + @unlink($dest); + http::redirect('langs.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Upload a language pack +if ($is_writable && !empty($_POST['upload_pkg'])) +{ + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + files::uploadStatus($_FILES['pkg_file']); + $dest = DC_L10N_ROOT.'/'.$_FILES['pkg_file']['name']; + if (!move_uploaded_file($_FILES['pkg_file']['tmp_name'],$dest)) { + throw new Exception(__('Unable to move uploaded file.')); + } + + try { + $ret_code = dc_lang_install($dest); + } catch (Exception $e) { + @unlink($dest); + throw $e; + } + + @unlink($dest); + http::redirect('langs.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +dcPage::open(__('Languages management'), + dcPage::jsLoad('js/_langs.js') +); + +echo +'

        '.__('Languages management').'

        '; + +if (!empty($_GET['removed'])) { + echo '

        '.__('Language has been successfully deleted.').'

        '; +} + +if (!empty($_GET['added'])) { + echo '

        '. + ($_GET['added'] == 2 ? __('Language has been successfully upgraded') : __('Language has been successfully installed.')). + '

        '; +} + +echo +'

        '.__('Here you can install, upgrade or remove languages for your Dotclear '. +'installation.').'

        '. +'

        '.sprintf(__('You can change your user language in your preferences or '. +'change your blog\'s main language in your blog settings.'), +'preferences.php','blog_pref.php').'

        '; + +echo +'

        '.__('Installed languages').'

        '; + +$locales_content = scandir(DC_L10N_ROOT); +$tmp = array(); +foreach ($locales_content as $v) { + $c = ($v == '.' || $v == '..' || $v == 'en' || !is_dir(DC_L10N_ROOT.'/'.$v) || !isset($iso_codes[$v])); + + if (!$c) { + $tmp[$v] = DC_L10N_ROOT.'/'.$v; + } +} +$locales_content = $tmp; + +if (empty($locales_content)) +{ + echo '

        '.__('No additional language is installed.').'

        '; +} +else +{ + echo + ''. + ''. + ''. + ''; + + foreach ($locales_content as $k => $v) + { + $is_deletable = $is_writable && is_writable($v); + + echo + ''. + ''. + ''; + } + echo '
        '.__('Language').''.__('Action').'
        ('.$k.') '. + ''.html::escapeHTML($iso_codes[$k]).''; + + if ($is_deletable) + { + echo + '
        '. + '
        '. + $core->formNonce(). + form::hidden(array('locale_id'),html::escapeHTML($k)). + ' '. + '
        '. + '
        '; + } + + echo '
        '; +} + +echo '

        '.__('Install or upgrade languages').'

        '; + +if (!$is_writable) { + echo '

        '.sprintf(__('You can install or remove a language by adding or '. + 'removing the relevant directory in your %s folder.'),'locales').'

        '; +} + +if (!empty($dc_langs) && $is_writable) +{ + $dc_langs_combo = array(); + foreach ($dc_langs as $k => $v) { + if ($v->link && isset($iso_codes[$v->title])) { + $dc_langs_combo[html::escapeHTML('('.$v->title.') '.$iso_codes[$v->title])] = html::escapeHTML($v->link); + } + } + + echo + '
        '. + '
        '. + ''.__('Available languages').''. + '

        '.sprintf(__('You can download and install a additional language directly from Dotclear.net. '. + 'Proposed languages are based on your version: %s.'),''.DC_VERSION.'').'

        '. + '

        '. + '

        '. + ''. + $core->formNonce(). + '
        '. + '
        '; +} + +if ($is_writable) +{ + # 'Upload language pack' form + echo + '
        '. + '
        '. + ''.__('Upload a zip file').''. + '

        '.__('You can install languages by uploading zip files.').'

        '. + '

        '. + '

        '. + ''. + $core->formNonce(). + '
        '. + '
        '; +} + +dcPage::close(); + + +# Language installation function +function dc_lang_install($file) +{ + $zip = new fileUnzip($file); + $zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.DS_Store|\.directory|Thumbs\.db)(/|$)#'); + + if (!preg_match('/^[a-z]{2,3}(-[a-z]{2})?$/',$zip->getRootDir())) { + throw new Exception(__('Invalid language zip file.')); + } + + if ($zip->isEmpty() || !$zip->hasFile($zip->getRootDir().'/main.po')) { + throw new Exception(__('The zip file does not appear to be a valid Dotclear language pack.')); + } + + + $target = dirname($file); + $destination = $target.'/'.$zip->getRootDir(); + $res = 1; + + if (is_dir($destination)) { + if (!files::deltree($destination)) { + throw new Exception(__('An error occurred during language upgrade.')); + } + $res = 2; + } + + $zip->unzipAll($target); + return $res; +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/media.php b/v2/dotclear/admin/media.php new file mode 100644 index 0000000..8270131 --- /dev/null +++ b/v2/dotclear/admin/media.php @@ -0,0 +1,472 @@ +auth->check('media,media_admin',$core->blog->id)) { + throw new Exception('Permission denied.'); + } + + $d = isset($_POST['d']) ? $_POST['d'] : null; + $core->media = new dcMedia($core); + $core->media->chdir($d); + $core->media->getDir(); + $dir =& $core->media->dir; + + if (empty($_FILES['Filedata'])) { + throw new Exception('No file to upload.'); + } + + files::uploadStatus($_FILES['Filedata']); + $core->media->uploadFile($_FILES['Filedata']['tmp_name'],$_FILES['Filedata']['name']); + + echo 'ok'; + } + catch (Exception $e) { + echo __('Error:').' '.__($e->getMessage()); + } + exit; +} + + +/* HTML page +-------------------------------------------------------- */ +require dirname(__FILE__).'/../inc/admin/prepend.php'; + +dcPage::check('media,media_admin'); + +$post_id = !empty($_GET['post_id']) ? (integer) $_GET['post_id'] : null; +if ($post_id) { + $post = $core->blog->getPosts(array('post_id'=>$post_id,'post_type'=>'')); + if ($post->isEmpty()) { + $post_id = null; + } + $post_title = $post->post_title; + $post_type = $post->post_type; + unset($post); +} +$d = isset($_REQUEST['d']) ? $_REQUEST['d'] : null; +$dir = null; + +$page = !empty($_GET['page']) ? $_GET['page'] : 1; +$nb_per_page = 30; + +# We are on home not comming from media manager +if ($d === null && isset($_SESSION['media_manager_dir'])) { + # We get session information + $d = $_SESSION['media_manager_dir']; +} + +if (!isset($_GET['page']) && isset($_SESSION['media_manager_page'])) { + $page = $_SESSION['media_manager_page']; +} + +# We set session information about directory and page +if ($d) { + $_SESSION['media_manager_dir'] = $d; +} else { + unset($_SESSION['media_manager_dir']); +} +if ($page != 1) { + $_SESSION['media_manager_page'] = $page; +} else { + unset($_SESSION['media_manager_page']); +} + +# Sort combo +$sort_combo = array( + __('By names, in ascending order') => 'name-asc', + __('By names, in descending order') => 'name-desc', + __('By dates, in ascending order') => 'date-asc', + __('By dates, in descending order') => 'date-desc' +); + +if (!empty($_GET['file_sort']) && in_array($_GET['file_sort'],$sort_combo)) { + $_SESSION['media_file_sort'] = $_GET['file_sort']; +} +$file_sort = !empty($_SESSION['media_file_sort']) ? $_SESSION['media_file_sort'] : null; + +$popup = (integer) !empty($_GET['popup']); + +$page_url = 'media.php?popup='.$popup.'&post_id='.$post_id; + +if ($popup) { + $open_f = array('dcPage','openPopup'); + $close_f = array('dcPage','closePopup'); +} else { + $open_f = array('dcPage','open'); + $close_f = create_function('',"dcPage::helpBlock('core_media'); dcPage::close();"); +} + +$core_media_writable = false; +try { + $core->media = new dcMedia($core); + if ($file_sort) { + $core->media->setFileSort($file_sort); + } + $core->media->chdir($d); + $core->media->getDir(); + $core_media_writable = $core->media->writable(); + $dir =& $core->media->dir; + if (!$core_media_writable) { + throw new Exception('you do not have sufficient permissions to write to this folder: '); + } +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +# Zip download +if (!empty($_GET['zipdl']) && $core->auth->check('media_admin',$core->blog->id)) +{ + try + { + @set_time_limit(300); + $fp = fopen('php://output','wb'); + $zip = new fileZip($fp); + $zip->addExclusion('#(^|/).(.*?)_(m|s|sq|t).jpg$#'); + $zip->addDirectory($core->media->root.'/'.$d,'',true); + + header('Content-Disposition: attachment;filename='.($d ? $d : 'media').'.zip'); + header('Content-Type: application/x-zip'); + $zip->write(); + unset($zip); + exit; + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# New directory +if ($dir && !empty($_POST['newdir'])) +{ + try { + $core->media->makeDir($_POST['newdir']); + http::redirect($page_url.'&d='.rawurlencode($d).'&mkdok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Adding a file +if ($dir && !empty($_FILES['upfile'])) +{ + try + { + files::uploadStatus($_FILES['upfile']); + + $f_title = (isset($_POST['upfiletitle']) ? $_POST['upfiletitle'] : ''); + $f_private = (isset($_POST['upfilepriv']) ? $_POST['upfilepriv'] : false); + + $core->media->uploadFile($_FILES['upfile']['tmp_name'],$_FILES['upfile']['name'],$f_title,$f_private); + http::redirect($page_url.'&d='.rawurlencode($d).'&upok=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +# Removing item +if ($dir && !empty($_POST['rmyes']) && !empty($_POST['remove'])) +{ + $_POST['remove'] = rawurldecode($_POST['remove']); + + try { + $core->media->removeItem($_POST['remove']); + http::redirect($page_url.'&d='.rawurlencode($d).'&rmfok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Rebuild directory +if ($dir && $core->auth->isSuperAdmin() && !empty($_POST['rebuild'])) +{ + try { + $core->media->rebuild($d); + http::redirect($page_url.'&d='.rawurlencode($d).'&rebuildok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + + +# DISPLAY confirm page for rmdir & rmfile +if ($dir && !empty($_GET['remove'])) +{ + call_user_func($open_f,__('Media manager')); + + echo '

        '.html::escapeHTML($core->blog->name).' › '.__('Media manager').' › '.__('confirm removal').'

        '; + + echo + '
        '. + '

        '.sprintf(__('Are you sure you want to remove %s?'), + html::escapeHTML($_GET['remove'])).'

        '. + '

        '. + '   '. + form::hidden('d',$d). + $core->formNonce(). + form::hidden('remove',html::escapeHTML($_GET['remove'])).'

        '. + '
        '; + + call_user_func($close_f); + exit; +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +$core->auth->user_prefs->addWorkspace('interface'); +$user_ui_enhanceduploader = $core->auth->user_prefs->interface->enhanceduploader; + +call_user_func($open_f,__('Media manager'), + '". + dcPage::jsLoad('js/_media.js'). + (($user_ui_enhanceduploader && $core_media_writable) ? dcPage::jsCandyUpload(array('d='.$d)) : '') + ); + +if (!empty($_GET['mkdok'])) { + echo '

        '.__('Directory has been successfully created.').'

        '; +} + +if (!empty($_GET['upok'])) { + echo '

        '.__('Files have been successfully uploaded.').'

        '; +} + +if (!empty($_GET['rmfok'])) { + echo '

        '.__('File has been successfully removed.').'

        '; +} + +if (!empty($_GET['rmdok'])) { + echo '

        '.__('Directory has been successfully removed.').'

        '; +} + +if (!empty($_GET['rebuildok'])) { + echo '

        '.__('Directory has been successfully rebuilt.').'

        '; +} + +if (!empty($_GET['unzipok'])) { + echo '

        '.__('Zip file has been successfully extracted.').'

        '; +} + +echo '

        '.html::escapeHTML($core->blog->name).' › '; +if (!isset($core->media)) { + echo ''.__('Media manager').'

        '; +} else { + $breadcrumb = $core->media->breadCrumb(html::escapeURL($page_url).'&d=%s','%s'); + if ($breadcrumb == '') { + echo ''.__('Media manager').''; + } else { + echo ''.__('Media manager').''.' / '.$breadcrumb.''; + } +} + +if (!$dir) { + call_user_func($close_f); + exit; +} + +if ($post_id) { + echo '

        '.sprintf(__('Choose a file to attach to entry %s by clicking on %s.'), + ''.html::escapeHTML($post_title).'', + ''.__('Attach this file to entry').'').'

        '; +} +if ($popup) { + echo '

        '.sprintf(__('Choose a file to insert into entry by clicking on %s.'), + ''.__('Attach this file to entry').'').'

        '; +} + + +$items = array_values(array_merge($dir['dirs'],$dir['files'])); +if (count($items) == 0) +{ + echo '

        '.__('No file.').'

        '; +} +else +{ + $pager = new pager($page,count($items),$nb_per_page,10); + $pager->html_prev = __($pager->html_prev); + $pager->html_next = __($pager->html_next); + + echo + '
        '. + '

        '. + form::hidden(array('popup'),$popup). + form::hidden(array('post_id'),$post_id). + '

        '. + '
        '. + + '
        '. + '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + for ($i=$pager->index_start, $j=0; $i<=$pager->index_end; $i++, $j++) + { + echo mediaItemLine($items[$i],$j); + } + + echo + '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '. + '
        '; +} + +if ($core_media_writable) +{ + echo '
        '; + + echo + '
        '. + '
        '.__('Add files').''. + '

        '.__('Please take care to publish media that you own and that are not protected by copyright.').'

        '. + '
        '. + '
        '.form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE). + $core->formNonce().'
        '. + '

        '. + '

        '. + '

        '. + '

        '.__('To send several files at the same time, you can activate the enhanced uploader in'). + ' '.__('My preferences').'

        '. + '

        '. + form::hidden(array('d'),$d).'

        '. + '
        '. + ''. + '
        '; + + echo + '
        '. + '
        '. + '
        '. + ''.__('New directory').''. + $core->formNonce(). + '

        '. + '

        '. + form::hidden(array('d'),html::escapeHTML($d)).'

        '. + '
        '. + '
        '; + + echo '
        '; +} + +# Empty remove form (for javascript actions) +echo +'
        '. +form::hidden('rmyes',1).form::hidden('d',html::escapeHTML($d)). +form::hidden('remove',''). +$core->formNonce(). +'
        '; + +# Get zip directory +if ($core->auth->check('media_admin',$core->blog->id) && + !(count($items) == 0 || (count($items) == 1 && $items[0]->parent))) +{ + echo + '

        '. + __('Download this directory as a zip file').'

        '; +} + +call_user_func($close_f); + +/* ----------------------------------------------------- */ +function mediaItemLine($f,$i) +{ + global $core, $page_url, $popup, $post_id; + + $fname = $f->basename; + + if ($f->d) { + $link = html::escapeURL($page_url).'&d='.html::sanitizeURL($f->relname); + if ($f->parent) { + $fname = '..'; + } + } else { + $link = + 'media_item.php?id='.$f->media_id.'&popup='.$popup.'&post_id='.$post_id; + } + + $class = 'media-item media-col-'.($i%2); + + $res = + '
        '. + ''. + '
          '. + '
        • '.$fname.'
        • '; + + if (!$f->d) { + $res .= + '
        • '.$f->media_title.'
        • '. + '
        • '. + $f->media_dtstr.' - '. + files::size($f->size).' - '. + ''.__('open').''. + '
        • '; + } + + $res .= '
        •  '; + + if ($post_id && !$f->d) { + $res .= '
          '. + ' '. + form::hidden('media_id',$f->media_id). + form::hidden('post_id',$post_id). + form::hidden('attach',1). + $core->formNonce(). + '
          '; + } + + if ($popup && !$f->d) { + $res .= ''.__('Insert this file into entry').' '; + } + + if ($f->del) { + $res .= ''. + ''.__('Delete').''; + } + + $res .= '
        • '; + + if ($f->type == 'audio/mpeg3') { + $res .= '
        • '.dcMedia::mp3player($f->file_url,'index.php?pf=player_mp3.swf').'
        • '; + } + + $res .= '
        '; + + return $res; +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/media_item.php b/v2/dotclear/admin/media_item.php new file mode 100644 index 0000000..3b4ffd0 --- /dev/null +++ b/v2/dotclear/admin/media_item.php @@ -0,0 +1,528 @@ +blog->getPosts(array('post_id'=>$post_id)); + if ($post->isEmpty()) { + $post_id = null; + } + $post_title = $post->post_title; + unset($post); +} + +$file = null; +$popup = (integer) !empty($_GET['popup']); +$page_url = 'media_item.php?popup='.$popup.'&post_id='.$post_id; +$media_page_url = 'media.php?popup='.$popup.'&post_id='.$post_id; + +$id = !empty($_REQUEST['id']) ? (integer) $_REQUEST['id'] : ''; + +if ($popup) { + $open_f = array('dcPage','openPopup'); + $close_f = array('dcPage','closePopup'); +} else { + $open_f = array('dcPage','open'); + $close_f = create_function('',"dcPage::helpBlock('core_media'); dcPage::close();"); +} + +$core_media_writable = false; +try +{ + $core->media = new dcMedia($core); + + if ($id) { + $file = $core->media->getFile($id); + } + + if ($file === null) { + throw new Exception(__('Not a valid file')); + } + + $core->media->chdir(dirname($file->relname)); + $core_media_writable = $core->media->writable(); + + # Prepare directories combo box + $dirs_combo = array(); + foreach ($core->media->getRootDirs() as $v) { + if ($v->w) { + $dirs_combo['/'.$v->relname] = $v->relname; + } + } + ksort($dirs_combo); +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} + +# Upload a new file +if ($file && !empty($_FILES['upfile']) && $file->editable && $core_media_writable) +{ + try { + files::uploadStatus($_FILES['upfile']); + $core->media->uploadFile($_FILES['upfile']['tmp_name'],$file->basename,null,false,true); + http::redirect($page_url.'&id='.$id.'&fupl=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Update file +if ($file && !empty($_POST['media_file']) && $file->editable && $core_media_writable) +{ + $newFile = clone $file; + + $newFile->basename = $_POST['media_file']; + + if ($_POST['media_path']) { + $newFile->dir = $_POST['media_path']; + $newFile->relname = $_POST['media_path'].'/'.$newFile->basename; + } else { + $newFile->dir = ''; + $newFile->relname = $newFile->basename; + } + $newFile->media_title = $_POST['media_title']; + $newFile->media_dt = strtotime($_POST['media_dt']); + $newFile->media_dtstr = $_POST['media_dt']; + $newFile->media_priv = !empty($_POST['media_private']); + + try { + $core->media->updateFile($file,$newFile); + http::redirect($page_url.'&id='.$id.'&fupd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Update thumbnails +if (!empty($_POST['thumbs']) && $file->media_type == 'image' && $file->editable && $core_media_writable) +{ + try { + $foo = null; + $core->media->mediaFireRecreateEvent($file); + http::redirect($page_url.'&id='.$id.'&thumbupd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Unzip file +if (!empty($_POST['unzip']) && $file->type == 'application/zip' && $file->editable && $core_media_writable) +{ + try { + $unzip_dir = $core->media->inflateZipFile($file,$_POST['inflate_mode'] == 'new'); + http::redirect($media_page_url.'&d='.$unzip_dir.'&unzipok=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Function to get image title based on meta +function dcGetImageTitle($file,$pattern) +{ + $res = array(); + $pattern = preg_split('/\s*;;\s*/',$pattern); + $sep = ', '; + + foreach ($pattern as $v) { + if ($v == 'Title') { + $res[] = $file->media_title; + } elseif ($file->media_meta->{$v}) { + $res[] = (string) $file->media_meta->{$v}; + } elseif (preg_match('/^Date\((.+?)\)$/u',$v,$m)) { + $res[] = dt::str($m[1],$file->media_dt); + } elseif (preg_match('/^DateTimeOriginal\((.+?)\)$/u',$v,$m) && $file->media_meta->DateTimeOriginal) { + $res[] = dt::dt2str($m[1],(string) $file->media_meta->DateTimeOriginal); + } elseif (preg_match('/^separator\((.*?)\)$/u',$v,$m)) { + $sep = $m[1]; + } + } + return implode($sep,$res); +} + +/* DISPLAY Main page +-------------------------------------------------------- */ +$starting_scripts = dcPage::jsLoad('js/_media_item.js'); +if ($popup) { + $starting_scripts .= + dcPage::jsLoad('js/jsToolBar/popup_media.js'); +} +call_user_func($open_f,__('Media manager'), + $starting_scripts. + dcPage::jsDatePicker(). + dcPage::jsPageTabs() +); + +if ($file === null) { + call_user_func($close_f); + exit; +} + +if (!empty($_GET['fupd']) || !empty($_GET['fupl'])) { + echo '

        '.__('File has been successfully updated.').'

        '; +} +if (!empty($_GET['thumbupd'])) { + echo '

        '.__('Thumbnails have been successfully updated.').'

        '; +} + +echo '

        '.__('Media manager').''. +' / '.$core->media->breadCrumb(html::escapeURL($media_page_url).'&d=%s'). +''.$file->basename.'

        '; + +# Insertion popup +if ($popup) +{ + $media_desc = $file->media_title; + + echo + '
        '. + '
        '; + + if ($file->media_type == 'image') + { + $media_type = 'image'; + $media_desc = dcGetImageTitle($file,$core->blog->settings->system->media_img_title_pattern); + if ($media_desc == $file->basename) { + $media_desc = ''; + } + + echo + '

        '.__('Image size:').'

        '; + + $s_checked = false; + echo '

        '; + foreach (array_reverse($file->media_thumb) as $s => $v) { + $s_checked = ($s == 'm'); + echo '
        '; + } + $s_checked = (!isset($file->media_thumb['m'])); + echo '
        '; + echo '

        '; + + + echo '

        '.__('Image alignment').'

        '; + $i_align = array( + 'none' => array(__('None'),1), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),0) + ); + + echo '

        '; + foreach ($i_align as $k => $v) { + echo '
        '; + } + echo '

        '; + + echo + '

        '.__('Image insertion').'

        '. + '

        '. + '
        '. + ''. + '

        '; + } + elseif ($file->type == 'audio/mpeg3') + { + $media_type = 'mp3'; + + echo '

        '.__('MP3 disposition').'

        '. + '

        '.__("Please note that you cannot insert mp3 files with visual editor.").'

        '; + + $i_align = array( + 'none' => array(__('None'),0), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),1) + ); + + echo '

        '; + foreach ($i_align as $k => $v) { + echo '
        '; + } + + $public_player_style = unserialize($core->blog->settings->themes->mp3player_style); + $public_player = dcMedia::mp3player($file->file_url,$core->blog->getQmarkURL().'pf=player_mp3.swf',$public_player_style); + echo form::hidden('public_player',html::escapeHTML($public_player)); + echo '

        '; + } + elseif ($file->type == 'video/x-flv' || $file->type == 'video/mp4' || $file->type == 'video/x-m4v') + { + $media_type = 'flv'; + + echo + '

        '.__("Please note that you cannot insert video files with visual editor.").'

        '; + + echo + '

        '.__('Video size').'

        '. + '

        '; + + echo '

        '.__('Video disposition').'

        '; + + $i_align = array( + 'none' => array(__('None'),0), + 'left' => array(__('Left'),0), + 'right' => array(__('Right'),0), + 'center' => array(__('Center'),1) + ); + + echo '

        '; + foreach ($i_align as $k => $v) { + echo '
        '; + } + + $public_player_style = unserialize($core->blog->settings->themes->flvplayer_style); + $public_player = dcMedia::flvplayer($file->file_url,$core->blog->getQmarkURL().'pf=player_flv.swf',$public_player_style); + echo form::hidden('public_player',html::escapeHTML($public_player)); + echo '

        '; + } + else + { + $media_type = 'default'; + echo '

        '.__('Media item will be inserted as a link.').'

        '; + } + + echo + '

        '.__('Cancel').' - '. + ''.__('Insert').''. + form::hidden(array('type'),html::escapeHTML($media_type)). + form::hidden(array('title'),html::escapeHTML($file->media_title)). + form::hidden(array('description'),html::escapeHTML($media_desc)). + form::hidden(array('url'),$file->file_url). + '

        '; + + echo '
        '; +} + +echo +'
        '. +'

        '; + +echo +'
        '; + +if ($file->media_image) +{ + $thumb_size = !empty($_GET['size']) ? $_GET['size'] : 's'; + + if (!isset($core->media->thumb_sizes[$thumb_size]) && $thumb_size != 'o') { + $thumb_size = 's'; + } + + echo '

        '.__('Available sizes:').' '; + foreach (array_reverse($file->media_thumb) as $s => $v) + { + $strong_link = ($s == $thumb_size) ? '%s' : '%s'; + printf($strong_link,''.$core->media->thumb_sizes[$s][2].' | '); + } + echo ''.__('original').''; + echo '

        '; + + if (isset($file->media_thumb[$thumb_size])) { + echo '

        '; + } elseif ($thumb_size == 'o') { + $S = getimagesize($file->file); + $class = ($S[1] > 500) ? ' class="overheight"' : ''; + unset($S); + echo '

        '; + } +} + +if ($file->type == 'audio/mpeg3') +{ + echo dcMedia::mp3player($file->file_url,'index.php?pf=player_mp3.swf'); +} + +if ($file->type == 'video/x-flv' || $file->type == 'video/mp4' || $file->type == 'video/x-m4v') +{ + echo dcMedia::flvplayer($file->file_url,'index.php?pf=player_flv.swf'); +} + +echo +'

        '.__('Media details').'

        '. +'
          '. + '
        • '.__('File owner:').' '.$file->media_user.'
        • '. + '
        • '.__('File type:').' '.$file->type.'
        • '. + '
        • '.__('File size:').' '.files::size($file->size).'
        • '. + '
        • '.__('File URL:').' '.$file->file_url.'
        • '. +'
        '; + +if (empty($_GET['find_posts'])) +{ + echo + '

        '. + __('Show entries containing this media').'

        '; +} +else +{ + echo '

        '.__('Entries containing this media').'

        '; + $params = array( + 'post_type' => '', + 'from' => 'LEFT OUTER JOIN '.$core->prefix.'post_media PM ON P.post_id = PM.post_id ', + 'sql' => 'AND ('. + 'PM.media_id = '.(integer) $id.' '. + "OR post_content_xhtml LIKE '%".$core->con->escape($file->relname)."%' ". + "OR post_excerpt_xhtml LIKE '%".$core->con->escape($file->relname)."%' " + ); + + if ($file->media_image) + { # We look for thumbnails too + if (preg_match('#^http(s)?://#',$core->blog->settings->system->public_url)) { + $media_root = $core->blog->settings->system->public_url; + } else { + $media_root = $core->blog->host.path::clean($core->blog->settings->system->public_url).'/'; + } + foreach ($file->media_thumb as $v) { + $v = preg_replace('/^'.preg_quote($media_root,'/').'/','',$v); + $params['sql'] .= "OR post_content_xhtml LIKE '%".$core->con->escape($v)."%' "; + $params['sql'] .= "OR post_excerpt_xhtml LIKE '%".$core->con->escape($v)."%' "; + } + } + + $params['sql'] .= ') '; + + $rs = $core->blog->getPosts($params); + + if ($rs->isEmpty()) + { + echo '

        '.__('No entry seems contain this media.').'

        '; + } + else + { + echo '
          '; + while ($rs->fetch()) { + echo '
        • '. + $rs->post_title.''. + ($rs->post_type != 'post' ? ' ('.html::escapeHTML($rs->post_type).')' : ''). + ' - '.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->post_dt).'
        • '; + } + echo '
        '; + } +} + +if ($file->type == 'image/jpeg') +{ + echo '

        '.__('Image details').'

        '; + + if (count($file->media_meta) == 0) + { + echo '

        '.__('No detail').'

        '; + } + else + { + echo '
          '; + foreach ($file->media_meta as $k => $v) + { + if ((string) $v) { + echo '
        • '.$k.': '.html::escapeHTML($v).'
        • '; + } + } + echo '
        '; + } +} + +if ($file->editable && $core_media_writable) +{ + if ($file->media_type == 'image') + { + echo + '
        '. + '
        '.__('Update thumbnails').''. + '

        '.__('This will create or update thumbnails for this image.').'

        '. + '

        '. + form::hidden(array('id'),$id). + $core->formNonce().'

        '. + '
        '; + } + + if ($file->type == 'application/zip') + { + $inflate_combo = array( + __('Extract in a new directory') => 'new', + __('Extract in current directory') => 'current' + ); + + echo + '
        '. + '
        '.__('Extract archive').''. + '
          '. + '
        • '.__('Extract in a new directory').' : '. + __('This will extract archive in a new directory that should not exist yet.').'
        • '. + '
        • '.__('Extract in current directory').' : '. + __('This will extract archive in current directory and will overwrite existing files or directory.').'
        • '. + '
        '. + '

        '. + ''. + form::hidden(array('id'),$id). + $core->formNonce().'

        '. + '
        '; + } + + echo + '
        '. + '
        '.__('Change media properties').''. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '. + form::hidden(array('id'),$id). + $core->formNonce().'

        '. + '
        '; + + echo + '
        '. + '
        '.__('Change file').''. + '
        '.form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE).'
        '. + '

        '. + '

        '. + form::hidden(array('id'),$id). + $core->formNonce().'

        '. + '
        '; + + # --BEHAVIOR-- adminMediaItemForm + $core->callBehavior('adminMediaItemForm',$file); +} + +echo +'
        '. +'
        '; + +call_user_func($close_f); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/permissions.php b/v2/dotclear/admin/permissions.php new file mode 100644 index 0000000..a0dd0a6 --- /dev/null +++ b/v2/dotclear/admin/permissions.php @@ -0,0 +1,160 @@ +userExists($u)) { + $users[] = $u; + } + } +} + +# Check blogs +if (!empty($_REQUEST['blog_id']) && is_array($_REQUEST['blog_id'])) +{ + foreach ($_REQUEST['blog_id'] as $b) + { + if ($core->blogExists($b)) { + $blogs[] = $b; + } + } +} + +# Update permissions +if (!empty($_POST['upd_perm']) && !empty($users) && !empty($blogs)) +{ + $redir = 'permissions.php?upd=1'; + + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + foreach ($users as $u) + { + foreach ($blogs as $b) + { + $set_perms = array(); + + 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); + } + + $redir .= '&user_id[]='.$u; + } + + foreach ($blogs as $b) { + $redir .= '&blog_id[]='.$b; + } + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + + +if (empty($blogs) || empty($users)) { + $core->error->add(__('No blog or user given.')); +} + + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open(__('permissions'), + dcPage::jsLoad('js/_permissions.js') +); + +echo '

        '.__('Users').''.__('Permissions').'

        '; + +if (!empty($_GET['upd'])) { + echo '

        '.__('The permissions have been successfully updated.').'

        '; +} + +if (!empty($blogs) && !empty($users)) +{ + $perm_form = ''; + + if (count($users) == 1) { + $user_perm = $core->getUserPermissions($users[0]); + } + + foreach ($users as $u) { + $user_list[] = ''.$u.''; + } + + echo '

        '.sprintf(__('You are about to change permissions on the following blogs for users %s.'), + implode(', ',$user_list)); + + echo '

        '; + + foreach ($blogs as $b) + { + echo '

        '.html::escapeHTML($b).''. + form::hidden(array('blog_id[]'),$b).'

        '; + + foreach ($core->auth->getPermissionsTypes() as $perm_id => $perm) + { + $checked = false; + + if (count($users) == 1) { + $checked = isset($user_perm[$b]['p'][$perm_id]) && $user_perm[$b]['p'][$perm_id]; + } + + echo + '

        '; + } + } + + echo + '
        '.__('Validate permissions').''. + '

        '. + '
        '. + '

        '. + $core->formNonce(); + + foreach ($users as $u) { + echo form::hidden(array('user_id[]'),$u); + } + + echo form::hidden('upd_perm',1).'

        '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/permissions_blog.php b/v2/dotclear/admin/permissions_blog.php new file mode 100644 index 0000000..3757788 --- /dev/null +++ b/v2/dotclear/admin/permissions_blog.php @@ -0,0 +1,196 @@ + 'B.blog_id', +__('Blog name') => 'blog_name' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + +$q = !empty($_GET['q']) ? $_GET['q'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'blog_id'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'asc'; + + +# Check users +if (!empty($_REQUEST['user_id']) && is_array($_REQUEST['user_id'])) +{ + foreach ($_REQUEST['user_id'] as $u) + { + if ($core->userExists($u)) { + $users[] = $u; + } + } +} + +if (empty($users)) +{ + $core->error->add(__('No blog or user given.')); +} +else +{ + $page = !empty($_GET['page']) ? $_GET['page'] : 1; + $nb_per_page = 30; + + if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = $_GET['nb']; + } + + $show_filters = false; + + # - Search filter + if ($q) { + $params['q'] = $q; + $show_filters = true; + } + + # - Sortby and order filter + if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + $show_filters = true; + } + } + + $params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); + + try { + $rs = $core->getBlogs($params); + $counter = $core->getBlogs($params,1); + $nb_blog = $counter->f(0); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_permissions_blog.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} +dcPage::open(__('choose a blog'),$starting_script); + +echo '

        '.__('Users').''.__('Choose a blog').'

        '; + +if (!$core->error->flag()) +{ + $hidden_fields = ''; + foreach ($users as $u) { + $hidden_fields .= form::hidden(array('user_id[]'),$u); + } + + if (!$show_filters) { + echo '

        '.__('Filters').'

        '; + } + + echo + '
        '. + '
        '.__('Filters').''. + + '
        '. + '

        '. + '

        '. + '
        '. + + '
        '. + '

        '. + '

        '. + ''. + $hidden_fields.'

        '. + '
        '. + + '
        '. //Opera sucks + '
        '. + '
        '; + + echo + '

        '. + sprintf(__('Choose one or more blogs to which you want to give permissions to users %s.'), + ''.implode(', ',$users).'').'

        '; + + # Show blogs + if ($nb_blog == 0) + { + echo '

        '.__('No blog').'

        '; + } + else + { + $pager = new pager($page,$nb_blog,$nb_per_page,10); + $pager->var_page = 'page'; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + echo + '
        '. + ''. + ''. + ''. + ''. + ''. + ''; + + while ($rs->fetch()) { + echo blogLine($rs); + } + + echo + '
        '.__('Blog ID').''.__('Blog name').''.__('Entries').''.__('Status').'
        '. + + '

        '. + + '

        '. + $hidden_fields. + $core->formNonce().'

        '. + '
        '; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + } +} + +dcPage::close(); + +function blogLine($rs) +{ + global $core; + + $img_status = $rs->blog_status == 1 ? 'check-on' : 'check-off'; + $txt_status = $GLOBALS['core']->getBlogStatus($rs->blog_status); + $img_status = sprintf('%2$s',$img_status,$txt_status); + + return + ''. + ''. + form::checkbox(array('blog_id[]'),$rs->blog_id,'','','',false,'title="'.__('select').' '.$rs->blog_id.'"').''. + ''.$rs->blog_id.''. + ''.html::escapeHTML($rs->blog_name).''. + ''.$core->countBlogPosts($rs->blog_id).''. + ''.$img_status.''. + ''; +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/plugin.php b/v2/dotclear/admin/plugin.php new file mode 100644 index 0000000..cea46d5 --- /dev/null +++ b/v2/dotclear/admin/plugin.php @@ -0,0 +1,91 @@ +plugins->moduleExists($p)) { + $p_file = $core->plugins->moduleRoot($p).'/index.php'; +} + +if (file_exists($p_file)) +{ + # Loading plugin + $p_info = $core->plugins->getModules($p); + + $p_url = 'plugin.php?p='.$p; + + $p_title = 'no content - plugin'; + $p_head = ''; + $p_content = '

        '.__('No content found on this plugin.').'

        '; + + ob_start(); + include $p_file; + $res = ob_get_contents(); + ob_end_clean(); + + if (preg_match('|(.*?)(.*?)|ms',$m[1],$mt)) { + $p_title = $mt[1]; + } + + if (preg_match_all('|(.*?)|ms',$m[1],$ms)) { + foreach ($ms[1] as $v) { + $p_head .= $v."\n"; + } + } + + if (preg_match_all('|(.*?)|ms',$m[1],$ms)) { + foreach ($ms[1] as $v) { + $p_head .= $v."\n"; + } + } + + if (preg_match_all('|()|ms',$m[1],$ms)) { + foreach ($ms[1] as $v) { + $p_head .= $v."\n"; + } + } + } + + if (preg_match('|(.+)|ms',$res,$m)) { + $p_content = $m[1]; + } + + call_user_func($open_f,$p_title,$p_head); + echo $p_content; + call_user_func($close_f); +} +else +{ + call_user_func($open_f,__('Plugin not found')); + + echo '

        '.__('Plugin not found').'

        '; + + echo '

        '.__('The plugin you reached does not exist or does not have an admin page.').'

        '; + + call_user_func($close_f); +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/plugins.php b/v2/dotclear/admin/plugins.php new file mode 100644 index 0000000..cdc7a61 --- /dev/null +++ b/v2/dotclear/admin/plugins.php @@ -0,0 +1,364 @@ +plugins->moduleExists($plugin_id)) { + throw new Exception(__('No such plugin.')); + } + + $plugin = $core->plugins->getModules($plugin_id); + $plugin['id'] = $plugin_id; + + if (!preg_match('!^'.$p_path_pat.'!', $plugin['root'])) { + throw new Exception(__('You don\'t have permissions to delete this plugin.')); + } + + # --BEHAVIOR-- pluginBeforeDelete + $core->callBehavior('pluginsBeforeDelete', $plugin); + + $core->plugins->deleteModule($plugin_id); + + # --BEHAVIOR-- pluginAfterDelete + $core->callBehavior('pluginsAfterDelete', $plugin); + } + else + { + $core->plugins->deleteModule($plugin_id,true); + } + + http::redirect('plugins.php?removed=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + # Deactivate plugin + elseif ($plugin_id && !empty($_POST['deactivate'])) + { + try + { + if (!$core->plugins->moduleExists($plugin_id)) { + throw new Exception(__('No such plugin.')); + } + + $plugin = $core->plugins->getModules($plugin_id); + $plugin['id'] = $plugin_id; + + if (!$plugin['root_writable']) { + throw new Exception(__('You don\'t have permissions to deactivate this plugin.')); + } + + $core->plugins->deactivateModule($plugin_id); + http::redirect('plugins.php'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + # Activate plugin + elseif ($plugin_id && !empty($_POST['activate'])) + { + try + { + $p = $core->plugins->getDisabledModules(); + if (!isset($p[$plugin_id])) { + throw new Exception(__('No such plugin.')); + } + $core->plugins->activateModule($plugin_id); + http::redirect('plugins.php'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + # Plugin upload + elseif ((!empty($_POST['upload_pkg']) && !empty($_FILES['pkg_file'])) || + (!empty($_POST['fetch_pkg']) && !empty($_POST['pkg_url']))) + { + try + { + if (empty($_POST['your_pwd']) || !$core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + if (!empty($_POST['upload_pkg'])) + { + files::uploadStatus($_FILES['pkg_file']); + + $dest = $p_path.'/'.$_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 = $p_path.'/'.basename($url); + + try + { + $client = netHttp::initClient($url,$path); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + } + catch( Exception $e) + { + throw new Exception(__('An error occurred while downloading the file.')); + } + + unset($client); + } + + $ret_code = $core->plugins->installPackage($dest,$core->plugins); + http::redirect('plugins.php?added='.$ret_code); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + $default_tab = 'addplugin'; + } + } +} + +# Plugin install +$plugins_install = $core->plugins->installModules(); + +/* DISPLAY Main page +-------------------------------------------------------- */ +dcPage::open(__('Plugins management'), + dcPage::jsLoad('js/_plugins.js'). + dcPage::jsPageTabs($default_tab) +); + +echo +'

        '.__('Plugins management').'

        '; + +if (!empty($_GET['removed'])) { + echo + '

        '.__('Plugin has been successfully deleted.').'

        '; +} +if (!empty($_GET['added'])) { + echo '

        '. + ($_GET['added'] == 2 ? __('Plugin has been successfully upgraded') : __('Plugin has been successfully installed.')). + '

        '; +} + +# 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 '
        '; +} + +# List all active plugins +echo '

        '.__('Plugins add new functionalities to Dotclear. '. +'Here you can activate or deactivate installed plugins.').'

        '; + +echo '

        '.sprintf(__('You can find additional plugins for your blog on %s.'), +'Dotaddict').' '; + +if ($is_writable) { + echo __('To install or upgrade a plugin you generally just need to upload it '. + 'in "Install or upgrade a plugin" section.'); +} else { + echo __('To install or upgrade a plugin you just need to extract it in your plugins directory.'); +} +echo '

        '; + +echo +'
        '; + +$p_available = $core->plugins->getModules(); +uasort($p_available,create_function('$a,$b','return strcasecmp($a["name"],$b["name"]);')); +if (!empty($p_available)) +{ + echo + '

        '.__('Activated plugins').'

        '. + ''. + ''. + ''. + ''. + ''. + ''; + + foreach ($p_available as $k => $v) + { + $is_deletable = $is_writable && preg_match('!^'.$p_path_pat.'!',$v['root']); + $is_deactivable = $v['root_writable']; + + echo + ''. + ''. + ''. + ''. + ''. + ''; + } + echo + '
        '.__('Plugin').''.__('Version').''.__('Details').''.__('Action').'
        '.html::escapeHTML($k).''.html::escapeHTML($v['version']).''.html::escapeHTML($v['name']).' '. + '
        '.html::escapeHTML($v['desc']).'
        '; + + if ($is_deletable || $is_deactivable) + { + echo + '
        '. + '
        '. + $core->formNonce(). + form::hidden(array('plugin_id'),html::escapeHTML($k)). + ($is_deactivable ? ' ' : ''). + ($is_deletable ? ' ' : ''). + '
        '. + '
        '; + } + + echo + '
        '; +} + +$p_disabled = $core->plugins->getDisabledModules(); +uksort($p_disabled,create_function('$a,$b','return strcasecmp($a,$b);')); +if (!empty($p_disabled)) +{ + echo + '

        '.__('Deactivated plugins').'

        '. + ''. + ''. + ''. + ''; + + foreach ($p_disabled as $k => $v) + { + $is_deletable = $is_writable && preg_match('!^'.$p_path_pat.'!',$v['root']); + $is_activable = $v['root_writable']; + + echo + ''. + ''. + ''. + ''; + } + echo + '
        '.__('Plugin').''.__('Action').'
        '.html::escapeHTML($k).''; + + if ($is_deletable || $is_activable) + { + echo + '
        '. + '
        '. + $core->formNonce(). + form::hidden(array('plugin_id'),html::escapeHTML($k)). + form::hidden(array('deactivated'),1). + ($is_activable ? ' ' : ''). + ($is_deletable ? ' ' : ''). + '
        '. + '
        '; + } + + echo + '
        '; +} + +echo '
        '; + +# Add a new plugin +echo +'
        '; + +if ($is_writable) +{ + echo '

        '.__('You can install plugins by uploading or downloading zip files.').'

        '; + + # 'Upload plugin' form + echo + '
        '. + '
        '. + ''.__('Upload a zip file').''. + '

        '. + '

        '. + ''. + $core->formNonce(). + '
        '. + '
        '; + + # 'Fetch plugin' form + echo + '
        '. + '
        '. + ''.__('Download a zip file').''. + '

        '. + '

        '. + ''. + $core->formNonce(). + '
        '. + '
        '; +} +else +{ + echo + '

        '. + __('To enable this function, please give write access to your plugins directory.'). + '

        '; +} +echo '
        '; + +# --BEHAVIOR-- pluginsToolsTabs +$core->callBehavior('pluginsToolsTabs',$core); + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/popup_link.php b/v2/dotclear/admin/popup_link.php new file mode 100644 index 0000000..5f3bddd --- /dev/null +++ b/v2/dotclear/admin/popup_link.php @@ -0,0 +1,62 @@ +'.__('Add a link').''; + +# Languages combo +$rs = $core->blog->getLangs(array('order'=>'asc')); +$all_langs = l10n::getISOcodes(0,1); +$lang_combo = array('' => '', __('Most used') => array(), __('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 +''. + +'

        '.__('Cancel').' - '. +''.__('Insert').'

        '."\n". + +''."\n"; + +dcPage::closePopup(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/popup_posts.php b/v2/dotclear/admin/popup_posts.php new file mode 100644 index 0000000..46591f4 --- /dev/null +++ b/v2/dotclear/admin/popup_posts.php @@ -0,0 +1,57 @@ +'.__('Add a link to an entry').''; + +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(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/post.php b/v2/dotclear/admin/post.php new file mode 100644 index 0000000..02493ec --- /dev/null +++ b/v2/dotclear/admin/post.php @@ -0,0 +1,624 @@ +auth->getOption('post_format'); +$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 entry'); + +$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 = array(' ' => ''); +try { + $categories = $core->blog->getCategories(array('post_type'=>'post')); + while ($categories->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$categories->level-1).($categories->level-1 == 0 ? '' : '• ').html::escapeHTML($categories->cat_title), + $categories->cat_id + ); + } +} catch (Exception $e) { } + +# Status combo +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# Formaters combo +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +# Languages combo +$rs = $core->blog->getLangs(array('order'=>'asc')); +$all_langs = l10n::getISOcodes(0,1); +$lang_combo = array('' => '', __('Most used') => array(), __('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); + + +# Get entry informations +if (!empty($_REQUEST['id'])) +{ + $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; + + $page_title = __('Edit entry'); + + $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) {} + } +} + +# Format excerpt and content +if (!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 { + $post_dt = strtotime($_POST['post_dt']); + $post_dt = date('Y-m-d H:i',$post_dt); + } + + $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 + ); +} + +# Create or update post +if (!empty($_POST) && !empty($_POST['save']) && $can_edit_post) +{ + $cur = $core->con->openCursor($core->prefix.'post'); + + $cur->post_title = $post_title; + $cur->cat_id = ($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); + + http::redirect('post.php?id='.$post_id.'&upd=1'); + } + 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); + + http::redirect('post.php?id='.$return_id.'&crea=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } +} + +if (!empty($_POST['delete']) && $can_delete) +{ + try { + # --BEHAVIOR-- adminBeforePostDelete + $core->callBehavior('adminBeforePostDelete',$post_id); + $core->blog->delPost($post_id); + http::redirect('posts.php'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +$default_tab = 'edit-entry'; +if (!$can_edit_post) { + $default_tab = ''; +} +if (!empty($_GET['co'])) { + $default_tab = 'comments'; +} + +dcPage::open($page_title.' - '.__('Entries'), + dcPage::jsDatePicker(). + dcPage::jsToolBar(). + dcPage::jsModal(). + dcPage::jsMetaEditor(). + 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 +); + +if (!empty($_GET['upd'])) { + echo '

        '.__('Entry has been successfully updated.').'

        '; +} +elseif (!empty($_GET['crea'])) { + echo '

        '.__('Entry has been successfully created.').'

        '; +} +elseif (!empty($_GET['attached'])) { + echo '

        '.__('File has been successfully attached.').'

        '; +} +elseif (!empty($_GET['rmattach'])) { + echo '

        '.__('Attachment has been successfully removed.').'

        '; +} + +if (!empty($_GET['creaco'])) { + echo '

        '.__('Comment has been successfully created.').'

        '; + } + +# XHTML conversion +if (!empty($_GET['xconv'])) +{ + $post_excerpt = $post_excerpt_xhtml; + $post_content = $post_content_xhtml; + $post_title = $post_title_xhtml; + $post_format = 'xhtml'; + + echo '

        '.__('Don\'t forget to validate your XHTML conversion by saving your post.').'

        '; +} + +echo '

        '.html::escapeHTML($core->blog->name).' › '.''.__('Entries').''.$page_title; + + if ($post_id) { + echo ' “'.$post_title.'”'; + } +echo '

        '; + +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); + + echo '

        '; +} + +# Exit if we cannot view page +if (!$can_view_page) { + dcPage::helpBlock('core_post'); + dcPage::close(); + exit; +} + +/* Post form if we can edit post +-------------------------------------------------------- */ +if ($can_edit_post) +{ + echo '
        '; + echo '
        '; + echo '
        '; + echo '
        '; + + echo + '

        '. + + '

        '. + form::textarea('post_excerpt',50,5,html::escapeHTML($post_excerpt)). + '

        '. + + '

        '. + form::textarea('post_content',50,$core->auth->getOption('edit_size'),html::escapeHTML($post_content)). + '

        '. + + '

        '. + form::textarea('post_notes',50,5,html::escapeHTML($post_notes)). + '

        '; + + # --BEHAVIOR-- adminPostForm + $core->callBehavior('adminPostForm',isset($post) ? $post : null); + + echo + '

        '. + ($post_id ? form::hidden('id',$post_id) : ''). + ' '; + if ($post_id) { + $preview_url = + $core->blog->url.$core->url->getURLFor('preview',$core->auth->userID().'/'. + http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')). + '/'.$post->post_url); + echo ''.__('Preview').' '; + } + echo + ($can_delete ? '' : ''). + $core->formNonce(). + '

        '; + + echo '
        '; // End #entry-content + echo '
        '; // End #entry-wrapper + + echo '
        '; + + echo + '

        '. + + '

        '. + + '

        '. + + '

        '. + '

        '. + '

        '.($post_id && $post_format != 'xhtml' ? ''.__('Convert to XHTML').'' : '').'

        '. + + '

        '. + '

        '. + '

        '. + + '

        '. + + '

        '. + + '
        '. + '

        '. + '

        '. + __('Warning: If you set the URL manually, it may conflict with another entry.'). + '

        '. + '
        '; + + # --BEHAVIOR-- adminPostFormSidebar + $core->callBehavior('adminPostFormSidebar',isset($post) ? $post : null); + + echo '
        '; // End #entry-sidebar + + echo '
        '; + + # --BEHAVIOR-- adminPostForm + $core->callBehavior('adminPostAfterForm',isset($post) ? $post : null); + + echo '
        '; + + if ($post_id && $post->post_status == 1) { + echo '

        '. + __('Ping blogs').'

        '; + } + +} + + +/* Comments and trackbacks +-------------------------------------------------------- */ +if ($post_id) +{ + $params = array('post_id' => $post_id, 'order' => 'comment_dt ASC'); + + $comments = $core->blog->getComments(array_merge($params,array('comment_trackback'=>0))); + $trackbacks = $core->blog->getComments(array_merge($params,array('comment_trackback'=>1))); + + # Actions combo box + $combo_action = array(); + if ($can_edit_post && $core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; + } + + if ($can_edit_post && $core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('Delete')] = 'delete'; + } + + # --BEHAVIOR-- adminCommentsActionsCombo + $core->callBehavior('adminCommentsActionsCombo',array(&$combo_action)); + + $has_action = !empty($combo_action) && (!$trackbacks->isEmpty() || !$comments->isEmpty()); + + echo + '
        '; + + if ($has_action) { + echo '
        '; + } + + echo '

        '.__('Trackbacks').'

        '; + + if (!$trackbacks->isEmpty()) { + showComments($trackbacks,$has_action,true); + } else { + echo '

        '.__('No trackback').'

        '; + } + + echo '

        '.__('Comments').'

        '; + if (!$comments->isEmpty()) { + showComments($comments,$has_action); + } else { + echo '

        '.__('No comment').'

        '; + } + + if ($has_action) { + echo + '
        '. + '

        '. + + '

        '. + form::combo('action',$combo_action). + form::hidden('redir','post.php?id='.$post_id.'&co=1'). + $core->formNonce(). + '

        '. + '
        '. + '
        '; + } + + echo '
        '; +} + +/* Add a comment +-------------------------------------------------------- */ +if ($post_id) +{ + echo + '
        '. + '

        '.__('Add a comment').'

        '. + + '
        '. + '
        '. + '

        '. + + '

        '. + + '

        '. + + '

        '. + form::textarea('comment_content',50,8,html::escapeHTML('')). + '

        '. + + '

        '.form::hidden('post_id',$post_id). + $core->formNonce(). + '

        '. + '
        '. + '
        '. + '
        '; +} + + +# Show comments or trackbacks +function showComments($rs,$has_action,$tb=false) +{ + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + while($rs->fetch()) + { + $comment_url = 'comment.php?id='.$rs->comment_id; + + $img = '%1$s'; + switch ($rs->comment_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + break; + case -2: + $img_status = sprintf($img,__('junk'),'junk.png'); + break; + } + + echo + ''. + + ''. + ''. + ''. + ''. + ''. + ''. + + ''; + } + + echo '
        '.__('Author').''.__('Date').''.__('IP address').''.__('Status').' 
        '. + ($has_action ? form::checkbox(array('comments[]'),$rs->comment_id,'','','',0,'title="'.($tb ? __('select this trackback') : __('select this comment')).'"') : '').''.html::escapeHTML($rs->comment_author).''.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->comment_dt).''.$rs->comment_ip.''.$img_status.''. + '
        '; +} + +dcPage::helpBlock('core_post','core_wiki'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/post_media.php b/v2/dotclear/admin/post_media.php new file mode 100644 index 0000000..1ed7d22 --- /dev/null +++ b/v2/dotclear/admin/post_media.php @@ -0,0 +1,79 @@ +blog->getPosts(array('post_id' => $post_id,'post_type'=>'')); +if ($rs->isEmpty()) { + exit; +} + +if ($post_id && $media_id && !empty($_POST['attach'])) +{ + $core->media = new dcMedia($core); + $core->media->addPostMedia($post_id,$media_id); + http::redirect($core->getPostAdminURL($rs->post_type,$post_id,false)); +} + +try { + $core->media = new dcMedia($core); + $f = $core->media->getPostMedia($post_id,$media_id); + 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); + http::redirect($core->getPostAdminURL($rs->post_type,$post_id,false).'&rmattach=1'); + } + 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 + '
        '. + '

        '.__('Are you sure you want to remove this attachment?').'

        '. + '

        '. + '   '. + form::hidden('post_id',$post_id). + form::hidden('media_id',$media_id). + $core->formNonce().'

        '. + '
        '; + + dcPage::close(); + exit; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/posts.php b/v2/dotclear/admin/posts.php new file mode 100644 index 0000000..f861074 --- /dev/null +++ b/v2/dotclear/admin/posts.php @@ -0,0 +1,324 @@ +blog->getCategories(array('post_type'=>'post')); +} 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(array('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 +if (!$core->error->flag()) +{ + # Filter form we'll put in html_block + $users_combo = $categories_combo = array(); + $users_combo['-'] = $categories_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; + } + + $categories_combo[__('None')] = 'NULL'; + while ($categories->fetch()) { + $categories_combo[str_repeat('  ',$categories->level-1).($categories->level-1 == 0 ? '' : '• '). + html::escapeHTML($categories->cat_title). + ' ('.$categories->nb_post.')'] = $categories->cat_id; + } + + $status_combo = array( + '-' => '' + ); + foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = (string) $k; + } + + $selected_combo = array( + '-' => '', + __('selected') => '1', + __('not selected') => '0' + ); + + # Months array + $dt_m_combo['-'] = ''; + while ($dates->fetch()) { + $dt_m_combo[dt::str('%B %Y',$dates->ts())] = $dates->year().$dates->month(); + } + + $lang_combo['-'] = ''; + while ($langs->fetch()) { + $lang_combo[$langs->post_lang] = $langs->post_lang; + } + + $sortby_combo = array( + __('Date') => 'post_dt', + __('Title') => 'post_title', + __('Category') => 'cat_title', + __('Author') => 'user_id', + __('Status') => 'post_status', + __('Selected') => 'post_selected' + ); + + $order_combo = array( + __('Descending') => 'desc', + __('Ascending') => 'asc' + ); +} + +# Actions combo box +$combo_action = array(); +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('Status')] = array( + __('Publish') => 'publish', + __('Unpublish') => 'unpublish', + __('Schedule') => 'schedule', + __('Mark as pending') => 'pending' + ); +} +$combo_action[__('Mark')] = array( + __('Mark as selected') => 'selected', + __('Mark as unselected') => 'unselected' +); +$combo_action[__('Change')] = array(__('Change category') => 'category'); +if ($core->auth->check('admin',$core->blog->id)) +{ + $combo_action[__('Change')] = array_merge($combo_action[__('Change')], + array(__('Change author') => 'author')); +} +if ($core->auth->check('delete,contentadmin',$core->blog->id)) +{ + $combo_action[__('Delete')] = array(__('Delete') => 'delete'); +} + +# --BEHAVIOR-- adminPostsActionsCombo +$core->callBehavior('adminPostsActionsCombo',array(&$combo_action)); + +/* 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'] : ''; +$selected = isset($_GET['selected']) ? $_GET['selected'] : ''; +$month = !empty($_GET['month']) ? $_GET['month'] : ''; +$lang = !empty($_GET['lang']) ? $_GET['lang'] : ''; +$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'post_dt'; +$order = !empty($_GET['order']) ? $_GET['order'] : 'desc'; + +$show_filters = false; + +$page = !empty($_GET['page']) ? (integer) $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + if ($nb_per_page != $_GET['nb']) { + $show_filters = true; + } + $nb_per_page = (integer) $_GET['nb']; +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; + +# - 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 !== '' && in_array($cat_id,$categories_combo)) { + $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=''; +} + +# - Selected filter +if ($selected !== '' && in_array($selected,$selected_combo)) { + $params['post_selected'] = $selected; + $show_filters = true; +} else { + $selected=''; +} + +# - 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=''; +} + +# - Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$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 +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_posts_list.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} + +dcPage::open(__('Entries'),$starting_script); + +if (!$core->error->flag()) +{ + echo + '

        '.html::escapeHTML($core->blog->name).' › '.__('Entries').'

        '. + '

        '.__('New entry').'

        '; + + if (!$show_filters) { + echo '

        '. + __('Filters').'

        '; + } + + echo + '
        '. + '
        '.__('Filters').''. + '
        '. + '
        '. + ' '. + ' '. + ' '. + '
        '. + + '
        '. + ' '. + ' '. + ' '. + '
        '. + + '
        '. + '

        '. + '

        '. + '

        '. + '

        '. + '
        '. + '
        '. + '
        '. //Opera sucks + '
        '. + '
        '; + + # Show posts + $post_list->display($page,$nb_per_page, + '
        '. + + '%s'. + + '
        '. + '

        '. + + '

        '. + form::combo('action',$combo_action). + '

        '. + form::hidden(array('user_id'),$user_id). + form::hidden(array('cat_id'),$cat_id). + form::hidden(array('status'),$status). + form::hidden(array('selected'),$selected). + form::hidden(array('month'),$month). + form::hidden(array('lang'),$lang). + form::hidden(array('sortby'),$sortby). + form::hidden(array('order'),$order). + form::hidden(array('page'),$page). + form::hidden(array('nb'),$nb_per_page). + $core->formNonce(). + '
        '. + '
        ' + ); +} + +dcPage::helpBlock('core_posts'); +dcPage::close(); +?> diff --git a/v2/dotclear/admin/posts_actions.php b/v2/dotclear/admin/posts_actions.php new file mode 100644 index 0000000..e2e03d8 --- /dev/null +++ b/v2/dotclear/admin/posts_actions.php @@ -0,0 +1,259 @@ + $v) { + $entries[$k] = (integer) $v; + } + + $params['sql'] = 'AND P.post_id IN('.implode(',',$entries).') '; + + if (!isset($_POST['full_content']) || empty($_POST['full_content'])) { + $params['no_content'] = true; + } + + if (isset($_POST['post_type'])) { + $params['post_type'] = $_POST['post_type']; + } + + $posts = $core->blog->getPosts($params); + + # --BEHAVIOR-- adminPostsActions + $core->callBehavior('adminPostsActions',$core,$posts,$action,$redir); + + if (preg_match('/^(publish|unpublish|schedule|pending)$/',$action)) + { + switch ($action) { + case 'unpublish' : $status = 0; break; + case 'schedule' : $status = -1; break; + case 'pending' : $status = -2; break; + default : $status = 1; break; + } + + try + { + while ($posts->fetch()) { + $core->blog->updPostStatus($posts->post_id,$status); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'selected' || $action == 'unselected') + { + try + { + while ($posts->fetch()) { + $core->blog->updPostSelected($posts->post_id,$action == 'selected'); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'delete') + { + try + { + while ($posts->fetch()) { + # --BEHAVIOR-- adminBeforePostDelete + $core->callBehavior('adminBeforePostDelete',$posts->post_id); + $core->blog->delPost($posts->post_id); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + + } + elseif ($action == 'category' && isset($_POST['new_cat_id'])) + { + try + { + while ($posts->fetch()) + { + $new_cat_id = (integer) $_POST['new_cat_id']; + $core->blog->updPostCategory($posts->post_id,$new_cat_id); + } + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + elseif ($action == 'author' && isset($_POST['new_auth_id']) + && $core->auth->check('admin',$core->blog->id)) + { + $new_user_id = $_POST['new_auth_id']; + + try + { + if ($core->getUser($new_user_id)->isEmpty()) { + throw new Exception(__('This user does not exist')); + } + + while ($posts->fetch()) + { + $cur = $core->con->openCursor($core->prefix.'post'); + $cur->user_id = $new_user_id; + $cur->update('WHERE post_id = '.(integer) $posts->post_id); + } + + http::redirect($redir); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open( + __('Entries'), + dcPage::jsMetaEditor(). + # --BEHAVIOR-- adminBeforePostDelete + $core->callBehavior('adminPostsActionsHeaders') +); + +if (!isset($action)) { + dcPage::close(); + exit; +} + +$hidden_fields = ''; +while ($posts->fetch()) { + $hidden_fields .= form::hidden(array('entries[]'),$posts->post_id); +} + +if (isset($_POST['redir']) && strpos($_POST['redir'],'://') === false) +{ + $hidden_fields .= form::hidden(array('redir'),html::escapeURL($_POST['redir'])); +} +else +{ + $hidden_fields .= + form::hidden(array('user_id'),$_POST['user_id']). + form::hidden(array('cat_id'),$_POST['cat_id']). + form::hidden(array('status'),$_POST['status']). + form::hidden(array('selected'),$_POST['selected']). + form::hidden(array('month'),$_POST['month']). + form::hidden(array('lang'),$_POST['lang']). + form::hidden(array('sortby'),$_POST['sortby']). + form::hidden(array('order'),$_POST['order']). + form::hidden(array('page'),$_POST['page']). + form::hidden(array('nb'),$_POST['nb']); +} + +if (isset($_POST['post_type'])) { + $hidden_fields .= form::hidden(array('post_type'),$_POST['post_type']); +} + +# --BEHAVIOR-- adminPostsActionsContent +$core->callBehavior('adminPostsActionsContent',$core,$action,$hidden_fields); + +if ($action == 'category') +{ + echo '

        '.__('Change category for entries').'

        '; + + # categories list + # Getting categories + $categories_combo = array(' ' => ''); + try { + $categories = $core->blog->getCategories(array('post_type'=>'post')); + while ($categories->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$categories->level-1). + ($categories->level-1 == 0 ? '' : '• ').html::escapeHTML($categories->cat_title), + $categories->cat_id + ); + } + } catch (Exception $e) { } + + echo + '
        '. + '

        '; + + echo + $hidden_fields. + $core->formNonce(). + form::hidden(array('action'),'category'). + '

        '. + '
        '; +} +elseif ($action == 'author' && $core->auth->check('admin',$core->blog->id)) +{ + echo '

        '.__('Change author for entries').'

        '; + + echo + '
        '. + '

        '; + + echo + $hidden_fields. + $core->formNonce(). + form::hidden(array('action'),'author'). + '

        '. + '
        '; +} + +echo '

        '.__('back').'

        '; + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/preferences.php b/v2/dotclear/admin/preferences.php new file mode 100644 index 0000000..30420ab --- /dev/null +++ b/v2/dotclear/admin/preferences.php @@ -0,0 +1,631 @@ +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(); + +$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; + +$core->auth->user_prefs->addWorkspace('accessibility'); +$user_acc_nodragdrop = $core->auth->user_prefs->accessibility->nodragdrop; + +$core->auth->user_prefs->addWorkspace('interface'); +$user_ui_enhanceduploader = $core->auth->user_prefs->interface->enhanceduploader; +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; + +$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'])) { + $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'; +} + +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = $k; +} + +$iconsets_combo = array(__('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 != '..' && is_dir($iconsets_root.'/'.$entry)) { + $iconsets_combo[$entry] = $entry; + } + } + } +} + +# Language codes +$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' : ''); +} + +# Add or update user +if (isset($_POST['user_name'])) +{ + try + { + $pwd_check = !empty($_POST['cur_pwd']) && $core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_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()); + + http::redirect('preferences.php?upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Update user options +if (isset($_POST['user_post_format'])) +{ + 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['enable_wysiwyg'] = !empty($_POST['user_wysiwyg']); + + $cur->user_options = new ArrayObject($user_options); + + # --BEHAVIOR-- adminBeforeUserUpdate + $core->callBehavior('adminBeforeUserUpdate',$cur,$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->accessibility->put('nodragdrop',!empty($_POST['user_acc_nodragdrop']),'boolean'); + $core->auth->user_prefs->interface->put('enhanceduploader',!empty($_POST['user_ui_enhanceduploader']),'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('iconset',(!empty($_POST['user_ui_iconset']) ? $_POST['user_ui_iconset'] : '')); + $core->auth->user_prefs->interface->put('nofavmenu',!empty($_POST['user_ui_nofavmenu']),'boolean'); + + # Udate user + $core->updUser($core->auth->userID(),$cur); + + # --BEHAVIOR-- adminAfterUserUpdate + $core->callBehavior('adminAfterUserUpdate',$cur,$core->auth->userID()); + + http::redirect('preferences.php?updated=1'); + } + 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')); + } + + $ws = $core->auth->user_prefs->addWorkspace('favorites'); + $user_favs = $ws->DumpLocalPrefs(); + $count = count($user_favs); + foreach ($_POST['append'] as $k => $v) + { + try { + $found = false; + foreach ($user_favs as $f) { + $f = unserialize($f['value']); + if ($f['name'] == $v) { + $found = true; + break; + } + } + if (!$found) { + $uid = sprintf("u%03s",$count); + $fav = array('name' => $_fav[$v][0],'title' => $_fav[$v][1],'url' => $_fav[$v][2],'small-icon' => $_fav[$v][3], + 'large-icon' => $_fav[$v][4],'permissions' => $_fav[$v][5],'id' => $_fav[$v][6],'class' => $_fav[$v][7]); + $core->auth->user_prefs->favorites->put($uid,serialize($fav),'string'); + $count++; + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + break; + } + } + + if (!$core->error->flag()) { + http::redirect('preferences.php?append=1'); + } + } 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')); + } + + $ws = $core->auth->user_prefs->addWorkspace('favorites'); + foreach ($_POST['remove'] as $k => $v) + { + try { + $core->auth->user_prefs->favorites->drop($v); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + break; + } + } + // Update pref_id values + try { + $user_favs = $ws->DumpLocalPrefs(); + $core->auth->user_prefs->favorites->dropAll(); + $count = 0; + foreach ($user_favs as $k => $v) + { + $uid = sprintf("u%03s",$count); + $f = unserialize($v['value']); + $fav = array('name' => $f['name'],'title' => $f['title'],'url' => $f['url'],'small-icon' => $f['small-icon'], + 'large-icon' => $f['large-icon'],'permissions' => $f['permissions'],'id' => $f['id'],'class' => $f['class']); + $core->auth->user_prefs->favorites->put($uid,serialize($fav),'string'); + $count++; + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if (!$core->error->flag()) { + http::redirect('preferences.php?removed=1'); + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# Order favs +$order = array(); +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)) +{ + try { + $ws = $core->auth->user_prefs->addWorkspace('favorites'); + $user_favs = $ws->DumpLocalPrefs(); + $core->auth->user_prefs->favorites->dropAll(); + $count = 0; + foreach ($order as $i => $k) { + $uid = sprintf("u%03s",$count); + $f = unserialize($user_favs[$k]['value']); + $fav = array('name' => $f['name'],'title' => $f['title'],'url' => $f['url'],'small-icon' => $f['small-icon'], + 'large-icon' => $f['large-icon'],'permissions' => $f['permissions'],'id' => $f['id'],'class' => $f['class']); + $core->auth->user_prefs->favorites->put($uid,serialize($fav),'string'); + $count++; + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if (!$core->error->flag()) { + http::redirect('preferences.php?&neworder=1'); + } +} + +# Replace default favorites by current set (super admin only) +if (!empty($_POST['replace']) && $core->auth->isSuperAdmin()) { + try { + $ws = $core->auth->user_prefs->addWorkspace('favorites'); + $user_favs = $ws->DumpLocalPrefs(); + $core->auth->user_prefs->favorites->dropAll(true); + $count = 0; + foreach ($user_favs as $k => $v) + { + $uid = sprintf("g%03s",$count); + $f = unserialize($v['value']); + $fav = array('name' => $f['name'],'title' => $f['title'],'url' => $f['url'],'small-icon' => $f['small-icon'], + 'large-icon' => $f['large-icon'],'permissions' => $f['permissions'],'id' => $f['id'],'class' => $f['class']); + $core->auth->user_prefs->favorites->put($uid,serialize($fav),'string',null,null,true); + $count++; + } + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + if (!$core->error->flag()) { + http::redirect('preferences.php?&replaced=1'); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open($page_title, + dcPage::jsLoad('js/_preferences.js'). + ($user_acc_nodragdrop ? '' : dcPage::jsLoad('js/_preferences-dragdrop.js')). + dcPage::jsLoad('js/jquery/jquery-ui-1.8.12.custom.min.js'). + dcPage::jsPageTabs($default_tab). + dcPage::jsConfirmClose('user-form'). + + # --BEHAVIOR-- adminPreferencesHeaders + $core->callBehavior('adminPreferencesHeaders') +); + +if (!empty($_GET['upd'])) { + echo '

        '.__('Personal information has been successfully updated.').'

        '; +} +if (!empty($_GET['updated'])) { + echo '

        '.__('Personal options has been successfully updated.').'

        '; +} +if (!empty($_GET['append'])) { + echo '

        '.__('Favorites have been successfully added.').'

        '; +} +if (!empty($_GET['neworder'])) { + echo '

        '.__('Favorites have been successfully updated.').'

        '; +} +if (!empty($_GET['removed'])) { + echo '

        '.__('Favorites have been successfully removed.').'

        '; +} +if (!empty($_GET['replaced'])) { + echo '

        '.__('Default favorites have been successfully updated.').'

        '; +} + +echo '

        '.html::escapeHTML($core->blog->name).' › '.$page_title.'

        '; + +# User profile +echo '
        '; + +echo +'
        '. +'
        '.__('My profile').''. +'
        '. +'
        '. +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'
        '. + +'
        '. + +'

        '. + +'

        '. + +'
        '. +'
        '. +'
        '. //Opera sucks +'
        '; + +if ($core->auth->allowPassChange()) +{ + echo + '
        '. + ''.__('Change your password').''. + + '

        '. + + '

        '. + '
        '. + + '

        '.__('If you have changed this user email or password you must provide your current password to save these modifications.').'

        '. + '

        '; +} + +echo +'

        '. +$core->formNonce(). +'

        '. +'
        '; + +echo '
        '; + +# User options : some from actual user profile, dashboard modules, ... +echo '
        '; + +echo +'
        '. +'
        '.__('My options').''. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '; + +if (count($iconsets_combo) > 1) { + echo + '

        '; +} else { + form::hidden('user_ui_iconset',''); +} + +if ($core->auth->isSuperAdmin()) { + echo + '

        '. + '

        '.__('This will be applied for all users').'

        '; +} + +echo +'
        '. //Opera sucks +'
        '; + +echo +'
        '.__('Accessibility options').''. + +'

        '. + +'

        '.__('Numeric fields will allow to type the elements\' ordering number.').'

        '. +'
        '; + +echo +'
        '.__('Dashboard modules').''. + +'

        '. + +'

        '. + +'

        '. + +'
        '. //Opera sucks +'
        '; + +# --BEHAVIOR-- adminPreferencesForm +$core->callBehavior('adminPreferencesForm',$core); + +echo +'

        '. +$core->formNonce(). +'

        '. +'
        '; + +echo '
        '; + +# User favorites +echo '
        '; +$ws = $core->auth->user_prefs->addWorkspace('favorites'); +echo '
        '; +echo '
        '; +echo '
        '; +echo '
        '.__('My favorites').''; + +$count = 0; +foreach ($ws->dumpPrefs() as $k => $v) { + // User favorites only + if (!$v['global']) { + $fav = unserialize($v['value']); + if (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)) { + if ($count == 0) echo '
          '; + $count++; + echo '
        • '. + ' '. + form::field(array('order['.$k.']'),2,3,$count,'position','',false,'title="'.sprintf(__('position of %s'),$fav['title']).'"'). + form::hidden(array('dynorder[]','dynorder-'.$k.''),$k). + ''. + '
        • '; + } + } +} +if ($count > 0) echo '
        '; +if ($count > 0) { + echo + '
        '. + '

        '.form::hidden('favs_order',''). + $core->formNonce(). + ' '. + + '

        '. + + ($core->auth->isSuperAdmin() ? + '
        '. + '

        '.__('If you are a super administrator, you may define this set of favorites to be used by default on all blogs of this installation:').'

        '. + '

        ' : + ''). + '

        '. + '
        '; +} else { + echo + '

        '.__('Currently no personal favorites.').'

        '; +} + +echo '
        '; + +echo '

        '.__('Default favorites').'

        '; +echo '

        '.__('Those favorites are displayed when My Favorites list is empty.').'

        '; +$count = 0; +foreach ($ws->dumpPrefs() as $k => $v) { + // Global favorites only + if ($v['global']) { + $fav = unserialize($v['value']); + if (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)) { + if ($count == 0) echo '
          '; + $count++; + echo '
        • '. + ' '.__($fav['title']).'
        • '; + } + } +} +if ($count > 0) echo '
        '; +echo '
        '; +echo '
        '; +echo '
        '; +# Available favorites +echo '
        '.__('Available favorites').''; +$count = 0; +$array = $_fav; +function cmp($a,$b) { + if (__($a[1]) == __($b[1])) { + return 0; + } + return (__($a[1]) < __($b[1])) ? -1 : 1; +} +$array=$array->getArrayCopy(); +uasort($array,'cmp'); +foreach ($array as $k => $fav) { + if (($fav[5] == '*') || $core->auth->check($fav[5],$core->blog->id)) { + if ($count == 0) echo '
          '; + $count++; + echo '
        • '.''.'
        • '; + } +} +if ($count > 0) echo '
        '; +echo +'

        '. +$core->formNonce(). +'

        '; +echo '
        '; +echo '
        '; +echo '
        '; # Two-cols +echo '
        '; +echo '
        '; # user-favorites + +dcPage::helpBlock('core_user_pref'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/search.php b/v2/dotclear/admin/search.php new file mode 100644 index 0000000..a6fc234 --- /dev/null +++ b/v2/dotclear/admin/search.php @@ -0,0 +1,180 @@ +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'] = array((($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()); + } + } +} + + +dcPage::open(__('Search'),$starting_scripts); + +echo +'

        '.html::escapeHTML($core->blog->name).' › '.__('Search').'

        '. +'
        '. +'

        '.__('Search options').'

        '. +'

        '.form::field('q',30,255,html::escapeHTML($q)).'

        '. +'

        '. +'

        '. +'

        '. +'
        '. +'
        '; + +if ($q && !$core->error->flag()) +{ + $redir = html::escapeHTML($_SERVER['REQUEST_URI']); + + # Show posts + if ($qtype == 'p') + { + # Actions combo box + $combo_action = array(); + if ($core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('schedule')] = 'schedule'; + $combo_action[__('mark as pending')] = 'pending'; + } + $combo_action[__('change category')] = 'category'; + if ($core->auth->check('admin',$core->blog->id)) { + $combo_action[__('change author')] = 'author'; + } + if ($core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('Delete')] = 'delete'; + } + + # --BEHAVIOR-- adminPostsActionsCombo + $core->callBehavior('adminPostsActionsCombo',array(&$combo_action)); + + 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, + '
        '. + + '%s'. + + '
        '. + '

        '. + + '

        '. + form::combo(array('action','action1'),$combo_action). + '

        '. + form::hidden('redir',preg_replace('/%/','%%',$redir)). + $core->formNonce(). + '
        '. + '
        ' + ); + } + # Show posts + elseif ($qtype == 'c') + { + # Actions combo box + $combo_action = array(); + if ($core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; + } + if ($core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('Delete')] = 'delete'; + } + + 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, + '
        '. + + '%s'. + + '
        '. + '

        '. + + '

        '. + form::combo(array('action','action2'),$combo_action). + '

        '. + form::hidden('redir',preg_replace('/%/','%%',$redir)). + $core->formNonce(). + '
        '. + '
        ' + ); + } +} + + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/services.php b/v2/dotclear/admin/services.php new file mode 100644 index 0000000..1d1a301 --- /dev/null +++ b/v2/dotclear/admin/services.php @@ -0,0 +1,395 @@ +rest->addFunction('getPostById',array('dcRestMethods','getPostById')); +$core->rest->addFunction('getCommentById',array('dcRestMethods','getCommentById')); +$core->rest->addFunction('quickPost',array('dcRestMethods','quickPost')); +$core->rest->addFunction('validatePostMarkup',array('dcRestMethods','validatePostMarkup')); +$core->rest->addFunction('getZipMediaContent',array('dcRestMethods','getZipMediaContent')); +$core->rest->addFunction('getMeta',array('dcRestMethods','getMeta')); +$core->rest->addFunction('delMeta',array('dcRestMethods','delMeta')); +$core->rest->addFunction('setPostMeta',array('dcRestMethods','setPostMeta')); +$core->rest->addFunction('searchMeta',array('dcRestMethods','searchMeta')); + +$core->rest->serve(); + +/* Common REST methods */ +class dcRestMethods +{ + public static function getPostById($core,$get) + { + if (empty($get['id'])) { + throw new Exception('No post ID'); + } + + $params = array('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(array('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) + { + $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(array('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'); + } + + $core->media = new dcMedia($core); + $file = $core->media->getFile($id); + + 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(array( + '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(array( + 'meta_type' => $post['metaType'], + 'post_id' => $post['postId'])); + $pm = array(); + 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(array('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 (preg_match('/'.$q.'/i',$rs->meta_id)) { + $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; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/admin/style/add.png b/v2/dotclear/admin/style/add.png new file mode 100644 index 0000000..dab4ead Binary files /dev/null and b/v2/dotclear/admin/style/add.png differ diff --git a/v2/dotclear/admin/style/candyUpload/cancel.png b/v2/dotclear/admin/style/candyUpload/cancel.png new file mode 100644 index 0000000..b1a5947 Binary files /dev/null and b/v2/dotclear/admin/style/candyUpload/cancel.png differ diff --git a/v2/dotclear/admin/style/candyUpload/loader.png b/v2/dotclear/admin/style/candyUpload/loader.png new file mode 100644 index 0000000..267f541 Binary files /dev/null and b/v2/dotclear/admin/style/candyUpload/loader.png differ diff --git a/v2/dotclear/admin/style/candyUpload/style.css b/v2/dotclear/admin/style/candyUpload/style.css new file mode 100644 index 0000000..9b0f71c --- /dev/null +++ b/v2/dotclear/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; + -moz-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/v2/dotclear/admin/style/date-picker.css b/v2/dotclear/admin/style/date-picker.css new file mode 100644 index 0000000..8cb66c7 --- /dev/null +++ b/v2/dotclear/admin/style/date-picker.css @@ -0,0 +1,35 @@ +.date-picker { + border-collapse : collapse; + background : #fff; + color : #fff; + border: 1px solid #666; + border-width : 1px 2px 2px 1px; +} +.date-picker th { + border : none; + color : #000; + text-align : center; +} +.date-picker td { + border : 1px solid #666; + 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 : #06c; + cursor : pointer; +} +.date-picker-day, .date-picker-today { + color : #000; + background : #eee; + cursor : pointer; +} +.date-picker-today { + background : #ccc; +} \ No newline at end of file diff --git a/v2/dotclear/admin/style/dc_bg.png b/v2/dotclear/admin/style/dc_bg.png new file mode 100644 index 0000000..15fb5f6 Binary files /dev/null and b/v2/dotclear/admin/style/dc_bg.png differ diff --git a/v2/dotclear/admin/style/dc_logo.png b/v2/dotclear/admin/style/dc_logo.png new file mode 100644 index 0000000..d7204e2 Binary files /dev/null and b/v2/dotclear/admin/style/dc_logo.png differ diff --git a/v2/dotclear/admin/style/dc_logo_small.png b/v2/dotclear/admin/style/dc_logo_small.png new file mode 100644 index 0000000..3ec22c9 Binary files /dev/null and b/v2/dotclear/admin/style/dc_logo_small.png differ diff --git a/v2/dotclear/admin/style/default-rtl.css b/v2/dotclear/admin/style/default-rtl.css new file mode 100644 index 0000000..f1217c4 --- /dev/null +++ b/v2/dotclear/admin/style/default-rtl.css @@ -0,0 +1,11 @@ +body { + direction: rtl; +} + +.right { + text-align: left; +} + +th { + text-align: left; +} \ No newline at end of file diff --git a/v2/dotclear/admin/style/default.css b/v2/dotclear/admin/style/default.css new file mode 100644 index 0000000..d6b7cde --- /dev/null +++ b/v2/dotclear/admin/style/default.css @@ -0,0 +1,1361 @@ +/* +# -- BEGIN LICENSE BLOCK --------------------------------------- +# +# This file is part of Dotclear 2. +# +# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear +# Licensed under the GPL version 2.0 license. +# See LICENSE file or +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# +# -- END LICENSE BLOCK ----------------------------------------- +*/ + +/* ------------------------------------------------------------------ html */ +body { + font: 75%/1.5em Helvetica,Arial,sans-serif; + color: #333; + background: #f5f5f5; + margin: 0; + padding: 0; +} +body.auth { + background: #fff; +} + +a, a:link, a:visited { + color: #2373A8; + text-decoration: none; + border-bottom: 1px dotted #f90; +} +a:hover, a:active, a:focus { + text-decoration: underline; +} +a img, a:link img, a:visited img { + border:none; +} + +h1, h2, h3, h4, h5, h6, p { + margin-top: 0; + margin-bottom: 0.6em; +} +h2 { + color: #666; + font-size: 1.4em; + padding: 4px 0; +} +.page-title { + color: #d30e60; +} +h3 { + color: #333; + font-size: 1.2em; +} +p, div.p { + margin: 0 0 1em 0; +} +hr { + height: 1px; + border-width: 1px 0 0 0; + border-color: #999; + border-style: solid; +} +pre, code { + font: 100% "Andale Mono","Courier New",monospace; +} +pre { + white-space: pre; + white-space: -moz-pre-wrap; + white-space: -hp-pre-wrap; + white-space: -o-pre-wrap; + white-space: -pre-wrap; + white-space: pre-wrap; + white-space: pre-line; + word-wrap: break-word; +} +abbr { + cursor: help; +} + +/* LAYOUT +-------------------------------------------------------- */ +/* header */ +#header { + background: #575859; + height: 3em; + position: relative; +} +#prelude { + background: #575859; + line-height: 1.5em; + margin: 0; + padding: 0 1.7em 0 1em; + overflow: hidden; + position: absolute; + top: 1.2em; + left: 0; + } +#prelude li { + list-style-type: none; + margin: 0 1em 0 0; + background:transparent; + } +#prelude li a { + color:#fff; + } +#top { + margin: 0; + padding: 0; + width: 13em; + float: left; +} +#top h1 { + padding: 0; + margin: 0; + height: 3em; + text-indent: -1000px; + background: transparent url(dc_logo.png) no-repeat 0 50%; +} +#top h1 a { + position: absolute; + top: 3px; + left: 0; + width: 130px; + height: 60px; + border: none; + outline: none; + color: #fff; +} +#info-boxes { + background: #575859; + font-size: .95em; + height: 3em; +} +#info-box1 { + margin: 0; + padding: .5em 3px 4px 0; + color: #fff; + float: left; + background: #575859; +} +#info-box2 { + margin: .1em 0 0 0; + padding: .5em 1.3em 4px 0; + color: #fff; + float: right; + text-align: right; + background: #575859; + height: 2em; +} +#info-box1 select { + width: 15em; +} +#info-box1 a img, #info-box2 a img { + vertical-align: middle; + padding-left: .3em; + } +#info-box1 a, #info-box2 a { + background: #575859; + font-weight: bold; + color: #fff; + border-bottom-color: #ccc; + margin-left: .3em; + margin-right: .3em; + white-space: nowrap; + font-weight: normal; +} +#info-box1 a { + margin-left: 1.33em; +} +#info-box2 a.active { + border-bottom-color: #f5f5f5; + margin: 0; + padding: 1.2em .5em; + background-color: #f5f5f5; + color: #333; + font-weight: bold; +} +#info-box2 span { + color: #575859; +} +/* prelude */ +#wrapper { + width: 100%; +} +#main { + width: 100%; + float: right; + margin-left: -13em; + margin-top: 0; +} +#content { + margin: 1.5em 1.5em 4em 13em; + padding: 1em; + background: #fff; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + border-radius: .5em; + border: 1px solid #ddd; +} + /* Micro clearfix thx to Nicolas Gallagher */ + #content:before, #content:after {content:"";display:table;} + #content:after {clear:both;} +/* -------------------------------------------------- layout - multipart */ +.three-cols { +} +.three-cols .col { + width: 32.3%; + float: left; + margin-left: 1%; +} +.three-cols .col:first-child { + width: 33.3%; + margin-left: 0; +} +.two-cols { + position: static; +} +.two-cols .col { + width: 49%; + margin-left: 2%; + float: left; +} +.two-cols .col:first-child { + width: 49%; + margin-left: 0; +} +.two-cols .col70{ + width: 69%; + margin-left: 0; + float: left; +} +.two-cols .col30 { + width: 28%; + margin-left: 2%; + float: left; +} +/* -------------------------------------------------------------- layout - onglets */ +.part-tabs ul { + padding: .3em 0 1px 1em; + border-bottom: 1px solid #999; +} +.part-tabs li { + list-style: none; + margin: 0; + display: inline; +} +.part-tabs li a { + padding: .3em 0.5em; + margin-right: .5em; + border: 1px solid #999; + border-bottom: none; + background: #dfdfdf; + text-decoration: none; + -webkit-border-top-left-radius: .3em; + -webkit-border-top-right-radius: .3em; + -moz-border-radius-topleft: .3em; + -moz-border-radius-topright: .3em; + border-top-left-radius: .3em; + border-top-right-radius: .3em; + color: #000; +} +.part-tabs li.part-tabs-link a { + background: #ffe; +} +.part-tabs li a:hover, .part-tabs li a:focus { + color: #000; + background: #fff; + border-bottom-color: #fff; +} +.part-tabs li.part-tabs-active a { + background: #fff; + border-bottom: 1px solid #fff; + color: #000; + font-weight: bold; +} +/* ------------------------------------------------------------------ main-menu */ +#main-menu { + width: 13em; + float: left; + margin-top: 1.2em; + margin-bottom: 1em; +} +#main-menu h3 { + margin: 0 0 0.5em; + padding: .5em 0 0 .5em; + text-transform: uppercase; + color: #666; + font-size: 1.1em; +} +#main-menu ul { + font-size: .95em; + margin: 0 0 1em 0; + padding: 0; + list-style: none; +} +#main-menu li { + display: block; + margin: 0.5em 0 0; + padding: .2em 0 0 32px; + background-repeat: no-repeat; + background-position: 12px .2em; +} +#main-menu a { + font-weight: bold; +} +#main-menu .active a { + border-bottom: none; + color: #333; +} +#main-menu .active { + background-color: #fff; + padding: .4em 0 .1em 32px; + background-position: 12px .4em; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin-right: -1px; +} +#favorites-menu { + margin: 0 0 2em; +} +#favorites-menu h3 { + color: #333; + text-transform: none; +} +#favorites-menu a { + color: #333; +} +#favorites-menu .active { + background-color: transparent; + border: none; +} +#favorites-menu .active a { + font-weight: bold; + color: #666; +} +/* ------------------------------------------------------------------ footer */ +#footer { + clear: both; + padding: .75em 2em; + margin: 6em 15px .5em 15px; + -webkit-border-radius: .3em; + -moz-border-radius: .3em; + border-radius: .3em; + background: #575859 url(dc_logo_small.png) no-repeat right bottom; + color: #fff; +} +#footer p { + margin: 0; + padding: 0 1em; + text-align: center; + font-size: 1.1em; +} +#footer a { + color: #f5f5f5; +} +#footer p span.credit { + font-size: .85em; + font-weight: normal; +} +/* ---------------------------------------------------------------------------- auth.php */ +#login-screen { + display: block; + width: 18em; + margin: 1.5em auto 0; + font-size: 1.1em; +} +#login-screen h1 { + text-indent: -2000px; + background: transparent url(dotclear-logo2.png) no-repeat top left; + height: 50px; + margin-bottom: .5em; + margin-left: .5em; +} +#login-screen fieldset, #login-screen .fieldset { + border: 1px solid #999; + padding: 1em 1em 0 1em; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +#login-screen input[type=text], #login-screen input[type=password], #login-screen input[type=submit] { + width: 100%; +} +#login-screen #issue { + margin-left: 1em; + font-size: 1em; +} +#login-screen #issue strong {font-weight: normal;} + + +/* ------------------------------------------------------------------ dashboard */ +#dashboard-main { + float: left; + overflow: hidden; + padding: 1em 2% 1em 0; + width: 70%; +} +#dashboard-main.fullwidth { + width: 100%; + padding: 1em 0; + float: none; +} +#icons { + overflow: hidden; + padding-bottom: 1em; + text-align: center; +} +#icons p { + width: 210px; + text-align: center; + margin: 2em 0 0 0; + display:inline-block; +} +#icons a, +#icons a:link, +#icons a:visited, +#icons a:hover, +#icons a:focus { + border-bottom-width: 0px; + text-decoration: none; +} +#icons a span { + border-bottom: 1px dotted #f90; +} +#icons a:focus span, #icons a:hover span { + text-decoration: underline; +} +#quick { + clear: left; + margin-top: 2em; +} +#quick h3 { + margin-bottom: 0.2em; + font-size: 1.2em; +} +#quick p.qinfo { + margin: -.7em -1em 1em; + background: #eef url(info.png) no-repeat .2em .2em; + border: 1px solid #99f; + padding: .2em 1em .1em 24px; + color: #666; +} +#dashboard-items { + float: left; + width: 27%; + overflow: hidden; + padding-bottom: 1em; + padding-top: 3em; +} +#dashboard-items img { + vertical-align: middle; +} +#dashboard-items ul { + display: block; + padding-left: 1.5em; + list-style: square; +} +#dashboard-items li { + margin: 0.25em 0 0 0; + color: #666; +} +#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; +} + +#upg-notify { +} +#upg-notify ul { + padding-left: 15px; +} +#upg-notify li { + color: #fff; +} +/* ------------------------------------------------------------------ post */ +#entry-wrapper { + float: left; + width: 100%; + margin-right: -18em; +} +#entry-content { + margin-right: 18em; +} +#entry-sidebar { + width: 17em; + float: right; +} +#comments { + clear: both; +} +/* ------------------------------------------------------------------ categories */ +#categories { + margin: 1em 0 2em; +} +#categories ul { + list-style: none; + margin: 0; + padding: 0; +} +#categories li { + margin: .5em 0; + padding: .3em 1.5em; + border: 1px solid #ccc; + border-left: 1em solid #E5E3DA; + -moz-border-radius: .3em; + -webkit-border-radius: .3em; + border-radius: .3em; +} +#categories h4 { + margin: 0; +} +#categories h4 span { + font-weight: normal; +} +#categories li p { + margin: 0; +} +select#del_cat { + width: 100%; +} +/* ------------------------------------------------------------------ media */ +#media-icon { + float: left; +} +#media-details { + margin-left: 70px; +} +#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; +} +#add-file-f { + position: relative; +} +#add-file-f .more-file { + position:absolute; + right: 0.5em; + background: #999; + color: #fff; + border: none; +} +.media-list { + position: static; +} +.media-col-0 { + clear: left; +} +.media-item { + position: relative; + border-top: 1px solid #ccc; + margin-bottom: 1em; + padding: 5px 0; +} +div.media-list .media-item { + width: 49%; + float: left; + margin-right: 1%; +} +a.media-icon { + display: block; + border-bottom: none; + float: left; +} +.media-icon img { + display: block; +} +.media-item ul { + display: block; + list-style: none; + margin: 0 0 0 60px; + padding: 0; +} +li.media-action { + display: block; + position: absolute; + top: 5px; + right: 5px; + height: 16px; +} +li.media-action a { + border: none; +} +li.media-action form { + display: inline; +} +li.media-action input { + border: none; +} +/* ------------------------------------------------------------------ preferences */ +#my-favs ul { + list-style-type: none; + margin-left: 0; + padding-left: 0; + line-height: 1.2; +} +#my-favs li { + display: block; + float: left; + width: 164px; + margin-top: 1em; + margin-bottom: 1.5em; +} +#my-favs label {height: 2.5em;width:140px;margin-top:.3em;} +#my-favs label input {display:inline;} +#my-favs img { + display: block; +} +#my-favs input.position { + margin: 0 0 .4em .2em; +} +#available-favs input, #available-favs label, #available-favs label span { + white-space: normal; + display: inline; +} +#default-favs h3 { + margin-top: 2em; + margin-bottom: 1em; +} +.fav-list { + list-style-type: none; + margin-left: 0; + padding-left: 0; +} +.fav-list li { + line-height: 2; + margin-left: 0; + padding-left: 0; + position: relative; +} +.fav-list img { + vertical-align: middle; + margin-right: .2em; +} +#available-favs label span.zoom { + display: none; +} +#available-favs li:hover label span.zoom { + display: block; + position: absolute; + bottom: 0; + left: 10em; + background-color: #f5f5f5; + border: 1px solid #ddd; + padding: .2em; + -moz-border-radius: .5em; + -webkit-border-radius: .5em; + border-radius: .5em; +} +/* -------------------------------------------------------------------- Themes */ +#themes { + border-bottom: 1px solid #ccc; + margin: 1em 0; +} +#themes div.theme-details { + clear: left; + border-top: 1px solid #ccc; + padding: 1em 0; +} +#themes div.theme-details:hover { + background: #eee; +} +#themes div.theme-details div.theme-shot { + float: left; +} +#themes div.theme-details div.theme-shot img { + display: block; + width: 57px; + height: 50px; + border: 1px solid #ccc; +} +#themes div.theme-details div.theme-info { + margin-left: 67px; +} +#themes div.theme-details div.theme-info span.theme-desc { + display: block; +} +#themes div.theme-details div.theme-info span.theme-version { + color: #666; +} +#themes div.theme-details div.theme-actions { + margin-left: 67px; +} +/* Themes list, JS version */ +#themes-wrapper { +} +#theme-box { + border: 1px solid #999; + border-left: none; + padding: 5px; + float: right; + height: 420px; + width: 320px; + overflow: auto; +} +#theme-box div.theme-shot img { + display: block; + margin: 0 0 0 10px; + width: 280px; + height: 245px; + border: 1px solid #ccc; +} +#theme-box div.theme-info { + margin: 1em 0 0 10px; +} +#theme-box h3 { + margin: 0; +} +#theme-box div.theme-info span { + display: block; +} +#theme-box span.theme-version { + color: #666; +} +#theme-box span.theme-parent-ok { + color: #666; +} +#theme-box span.theme-parent-missing { + color: #c00; + font-weight:bold; +} +#theme-box div.theme-actions { + margin-left: 10px; +} +#themes-wrapper #themes { + border: 1px solid #999; + overflow: auto; + height: 420px; + padding: 5px; + margin: 0; +} +#themes div.theme-details-js { + float: left; + width: 120px; + height: 150px; + margin: 0 10px 20px; + padding: 10px 10px 0; + text-align: center; + background: #f3f3f3; + border: 1px solid #f3f3f3; + cursor: pointer; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +#themes div.theme-details-js label { + cursor: pointer; +} +#themes div.theme-details-js.theme-selected { + background: #E5E3DA; + border: 1px solid #999; +} +#themes div.theme-details-js div.theme-shot img { + width: 120px; + height: 105px; + border: 1px solid #fff; +} +#themes div.theme-details-js h3 { + font-family: inherit; + font-weight: normal; + margin: 0; + padding: 0; +} +/* ---------------------------------------------------------- Plugins list */ +#plugins td.action { + vertical-align: middle; +} + +select.l10n option { + padding-left: 16px; +} +option.avail10n { + background: transparent url(../images/check-on.png) no-repeat 0 50%; +} +/* ------------------------------------------------------------------ contextual help */ +#help { + margin-top: 2em; + background: #f5f5f5; + z-index: 100; +} +#help-button { + position: absolute; + top: 6.2em; + right: 0px; + cursor: pointer; + background: #fc3; + border: 1px solid #dde; + border-right: none; + font-size: 1.1em; + font-weight: bold; + text-transform: capitalize; + padding: .33em .75em .33em 1em; + border-radius: 1em 0 0 1em; + -moz-border-radius: 1em 0 0 1em; + -webkit-border-top-left-radius: 1em; + -webkit-border-bottom-left-radius: 1em; + color: #444; +} +.help-box { + display: none; +} +.help-box ul { + padding-left: 20px; + margin-left: 0; +} +#content.with-help #help-button { + right: 282px; +} +#content.with-help #help { + display: block; + position: absolute; + top: 40px; + right: 0; + width: 280px; + border-left: 2px solid #fc3; + margin-top: 0; + padding: 10px 0 0 0; + overflow: auto; +} +#content.with-help .help-content { + padding: 0 5px 1em 5px; +} +.help-content dt { + font-weight: bold; + color: #626262; + margin: 0; +} +.help-content dd { + margin: 0.3em 0 1.5em 0; +} +/* ------------------------------------------------------------------ popups */ +body.popup #wrapper, body.popup #top { + margin-top: -1.5em; + float: none; +} +body.popup #top h1 { + background: transparent; +} +body.popup #main { + margin-left: -35px; + margin-bottom: 1em; +} +body.popup #content { + margin-left: 35px; + margin-left: 2em; /* 3.2 */ +} +body.popup #footer { + display: none; /* 3.2 */ +} +body.popup #footer p { + margin-left: 35px; + border: none; +} +/* ------------------------------------------------------------------ messages */ +div.error, p.error, +div.message, p.message, +div.static-msg, p.static-msg { + padding: 0.5em 0.5em 0.5em 40px; + margin-bottom: 1em; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-radius: 8px; +} +p.error, p.message, p.static-msg { + padding-top: 1em; + padding-bottom: 1em; +} +div.error, p.error { + background: #e5bfbf url(msg-error.png) no-repeat 5px 5px; + color: #600; +} +div.message, p.message, +div.static-msg, p.static-msg { + background: #666 url(msg-std.png) no-repeat 5px 5px; + color: #fff; +} +div.message a, p.message a, +div.static-msg a, p.static-msg a { + color: #fff; +} +/* ------------------------------------------------------------------ debug */ +#debug { + position: absolute; + top: 0; + width: 100%; + height: 4px; + background: #d99; +} +#debug div { + display: none; + padding: 3px 0.5em 2px; +} +#debug p { + margin: 0.5em 0; +} +#debug:hover { + height: auto; +} +#debug:hover div { + display: block; +} +/* -------------------------------------------------------------------- CLASSES COMMUNES */ + +.no-margin { + margin: 0; +} + +/* paragraphe pour bouton Nouveau bidule */ +p.top-add { + text-align: right; + margin: 0; + } +p.top-add a { + padding: .3em 1em .3em .5em; + font-weight: bold; +} +p.top-add a img { + padding-right: .3em; + vertical-align: middle; +} +/* Si quelque chose a besoin d'être caché sauf pour les revues d'écran */ +.hidden { + 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; + } +.clear { + clear: both; +} +.lclear { + clear: left; +} +div.clearer { + height: 1px; + font-size: 1px; +} +.hide { + display: none; +} +.right { + text-align: right; +} +.frame-shrink { + border: 1px solid #666; + padding: 0.5em; + margin-bottom: 1em; + height: 120px; + overflow: auto; +} +.grid { + background: transparent repeat url('grid.png') 0 0; +} +.line p { + margin: 0; +} +.offline { + color: #666; +} +ul.nice { + margin: 1em 0; + padding: 0 0 0 2em; + list-style: square; +} +ul.nice li { + margin:0; + padding: 0; +} +.zip-dl { + background: transparent url(package.png) no-repeat 0 50%; + padding: 5px 0 5px 20px; +} +/* pas trouvé dans le code */ +.comment { + border-top: 2px solid #ccc; + margin-bottom: 1em; + padding: 2em 0 1em 0; + position: relative; +} +.comment form p { + margin: 0; + position: absolute; + top: 2px; + right: 0; +} +/* TABLES +-------------------------------------------------------- */ +table { + font-size: 1em; + border-collapse: collapse; + margin: 0 0 1em 0; +} +tr.line:hover { + background: #ddd; +} +caption { + color: #333; + font-size: 1.2em; + font-weight: bold; + text-align: left; + margin-bottom: .5em; +} + +th, td { + border-width: 0 0 1px 0; + border-style: solid; + border-color: #ccc; + padding: 3px 5px; + vertical-align: top; +} +th { + text-align: left; + border-bottom-color: #666; +} +.noborder td, td.noborder, .noborder th, th.noborder { + border-width: 0; +} +table .maximal, table.maximal { + width: 100%; +} +table .minimal { + width: 1px; +} +table .nowrap { + white-space: nowrap; + vertical-align: top; +} +table.settings, table.prefs { + width: 80%; +} +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%; +} + +td.status { + vertical-align: middle; +} +td.status img { + margin-bottom: -2px; +} +td.status a { + border: none; +} + +tr.line img.expand { + margin-right: 10px; + margin-bottom: -2px; +} +tr.line input { + vertical-align: middle; +} +tr.expand td { + border-bottom: none; +} +td.expand { + padding: 1em; +} + +.dragable { + border-collapse: separate; +} +.dragable tbody td { + +} +.handle { + padding: 0; +} +.handler { + cursor: move; + background: transparent url(drag.png) no-repeat 0 50%; + padding-left: 15px; +} + +/* ----------------------------------------------------------------- FORMS */ +form { + display: block; + margin: 0; + padding: 0; +} +fieldset, .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; +} +optgroup { + font-weight: bold; + font-style: normal; +} +option { + font-weight: normal; +} +input, textarea, select { + background: #f9f9f9; + color: #000; + border-width: 1px; + border-style: solid; + border-color: #000 #ccc #ccc #000; +} +input, textarea, select, option { + font: 1em "DejaVu Sans","Lucida Grande","Lucida Sans Unicode",Arial,sans-serif; +} +input[type=text], input[type=password], textarea { + padding: 2px 0; + margin-right: .3em; +} +input[type=checkbox], input[type=radio] { + border: none; +} +textarea { + padding: 2px 0; +} + +input[type=checkbox], input[type=radio] { + margin: 0; + padding: 0; + background: transparent; +} +label { + display: block; +} +label input, label select, label span { + display: block; +} +p.form-note { + font-style:italic; + margin-top: -.7em; + color: #666; +} +p.form-note.warn, p.form-note.info, p.warning { + font-style: normal; + padding: .2em 1em .1em 24px; +} +p.form-note.warn, p.warning { + background: #ffd url(warning.png) no-repeat .2em .2em; + border: 1px solid #f0c020; +} +p.form-note.info { + background: #eef url(info.png) no-repeat .2em .2em; + border: 1px solid #99f; +} +.form-note a {border-bottom: 1px solid #99f;} + +label.classic { + display: inline; +} +label.classic input, label span input, label.classic select, label span select { + display: inline; +} + +label.area, p.area { + width: inherit !important; +} +.area textarea { + display: block; + width: 100%; + resize: vertical; +} +label.required { + font-weight: bold; +} +label.required abbr { + color: #dd0000; + font-size: 1.3em; +} +label.inline { + display: inline; +} +p.field { + position: relative; + +} +p.field label { + display: block; + width: 14em; +} +p.field input, p.field select { + display: inline; + position: absolute; + left: 15em; + top: 0; +} +label .maximal, textarea.maximal, input.maximal { + width: 100%; +} +textarea.maximal { + resize: vertical; +} + +a.form-control { + display: none; + font-weight: bold; + background: url(magnifier.png) no-repeat 0 0; + color: green; + padding-left: 20px; +} +.constrained { + margin: 0; + padding: 0; + border: none; + background: transparent; +} + +/* --------------------------------------------------------------- buttons */ +h2 a.button { + color: #333; + font-weight: normal; + font-size: .75em; + vertical-align: middle; +} +/* commun */ +input[type=submit], +input[type=reset], +input[type=button], +a.button, +a.back, +a.submit { + display: inline-block; + outline: none; + cursor: pointer; + text-align: center; + text-decoration: none; + padding: .1em .5em 0 .5em; + text-shadow: 0 1px 1px rgba(0,0,0,.3); + -webkit-border-radius: .2em; + -moz-border-radius: .2em; + border-radius: .2em; + margin-bottom: .1em; +} +/* validation */ +input[type=submit], +input[type=button], +a.submit { + 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); +} +input[type=submit]:hover, +input[type=button]:hover, +a.submit:hover, +input[type=submit]:focus, +input[type=button]:focus, +a.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; +} +/* suppression et reset */ +a.button, +a.back, +input[type=submit].reset, +input[type=submit].delete { + border: 1px solid #ccc; + background: #f5f5f5; + color: #000; + background: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#dfdfdf)); + background: -moz-linear-gradient(top, #f5f5f5, #dfdfdf); + text-shadow: none; + } +a.button:hover, +a.back:hover, +input[type=reset]:hover, +input[type=submit].reset:hover, +input[type=submit].delete:hover, +a.button:focus, +a.back:focus, +input[type=reset]:focus, +input[type=submit].reset:focus, +input[type=submit].delete:focus { + background: #dfdfdf; + background: -webkit-gradient(linear, left top, left bottom, from(#dfdfdf), to(#f5f5f5)); + background: -moz-linear-gradient(top, #dfdfdf, #f5f5f5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e5e5e5', endColorstr='#f5f5f5'); + } +a.delete, +input.delete, +input[type=submit].delete { + color: #c00; + padding-bottom: .1em; +} +#entry-content .delete { + margin-left: 1em; +} +a.delete:hover, +input.delete:hover, +input[type=submit].delete:hover, +a.delete:focus, +input.delete:focus, +input[type=submit].delete:focus { + border-color: #c00; +} +/* finasseries */ +input[type=button] { + padding: .1em; +} +#info-box a.button { + padding: 0 .5em; + margin-left: 2em; +} +a.back:before { + content: "\ab\a0"; +} +a.button.add { + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + border-radius: .5em; + margin-bottom: .1em; + background: #2C8FD1 url(../images/add.png) no-repeat 6px center; + color: #fff; + padding: .2em 16px .2em 30px; + border: 1px solid #2373A8; +} +a.button.add:hover, a.button.add:focus { + background-color: #2373A8; +} diff --git a/v2/dotclear/admin/style/dotclear-logo.png b/v2/dotclear/admin/style/dotclear-logo.png new file mode 100644 index 0000000..e39a3ea Binary files /dev/null and b/v2/dotclear/admin/style/dotclear-logo.png differ diff --git a/v2/dotclear/admin/style/dotclear-logo2.png b/v2/dotclear/admin/style/dotclear-logo2.png new file mode 100644 index 0000000..7295297 Binary files /dev/null and b/v2/dotclear/admin/style/dotclear-logo2.png differ diff --git a/v2/dotclear/admin/style/drag.png b/v2/dotclear/admin/style/drag.png new file mode 100644 index 0000000..28410fc Binary files /dev/null and b/v2/dotclear/admin/style/drag.png differ diff --git a/v2/dotclear/admin/style/farbtastic/farbtastic.css b/v2/dotclear/admin/style/farbtastic/farbtastic.css new file mode 100644 index 0000000..07d953f --- /dev/null +++ b/v2/dotclear/admin/style/farbtastic/farbtastic.css @@ -0,0 +1,33 @@ +.farbtastic { + position: relative; +} +.farbtastic * { + position: absolute; + cursor: crosshair; +} +.farbtastic, .farbtastic .wheel { + width: 195px; + height: 195px; +} +.farbtastic .color, .farbtastic .overlay { + top: 47px; + left: 47px; + width: 101px; + height: 101px; +} +.farbtastic .wheel { + background: url(wheel.png) no-repeat; + width: 195px; + height: 195px; +} +.farbtastic .overlay { + background: url(mask.png) no-repeat; +} +.farbtastic .marker { + width: 17px; + height: 17px; + margin: -8px 0 0 -8px; + overflow: hidden; + background: url(marker.png) no-repeat; +} + diff --git a/v2/dotclear/admin/style/farbtastic/marker.png b/v2/dotclear/admin/style/farbtastic/marker.png new file mode 100644 index 0000000..db7da55 Binary files /dev/null and b/v2/dotclear/admin/style/farbtastic/marker.png differ diff --git a/v2/dotclear/admin/style/farbtastic/mask.png b/v2/dotclear/admin/style/farbtastic/mask.png new file mode 100644 index 0000000..b0a4d40 Binary files /dev/null and b/v2/dotclear/admin/style/farbtastic/mask.png differ diff --git a/v2/dotclear/admin/style/farbtastic/wheel.png b/v2/dotclear/admin/style/farbtastic/wheel.png new file mode 100644 index 0000000..97b343d Binary files /dev/null and b/v2/dotclear/admin/style/farbtastic/wheel.png differ diff --git a/v2/dotclear/admin/style/grid.png b/v2/dotclear/admin/style/grid.png new file mode 100644 index 0000000..cf9402d Binary files /dev/null and b/v2/dotclear/admin/style/grid.png differ diff --git a/v2/dotclear/admin/style/head-bg.png b/v2/dotclear/admin/style/head-bg.png new file mode 100644 index 0000000..d3f91a3 Binary files /dev/null and b/v2/dotclear/admin/style/head-bg.png differ diff --git a/v2/dotclear/admin/style/iesucks.css b/v2/dotclear/admin/style/iesucks.css new file mode 100644 index 0000000..654a3f3 --- /dev/null +++ b/v2/dotclear/admin/style/iesucks.css @@ -0,0 +1,46 @@ +#content {zoom:1;} +label .maximal, textarea.maximal, input.maximal { + width : 98% !important; +} +.area textarea { + width : 98% !important; +} +textarea { + padding : 0 !important; +} +legend { + margin-bottom: 1em !important; +} + +p.area { + width: 98% !important; +} +p.area textarea { + width: 100% !important; +} + +tr.line input { + vertical-align: baseline !important; +} + +#dashboard .col { + width: 48% !important; +} + +td.status img, tr.line img.expand { + margin-bottom: 0 !important; +} + +.wysiwygIframe { + width: 98% !important; +} + +div.media-list .media-item { + width: 48% !important; +} +a.media-icon { + border-bottom:none !important; +} +#dashboard-main #icons p { + display: inline; +} diff --git a/v2/dotclear/admin/style/info.png b/v2/dotclear/admin/style/info.png new file mode 100644 index 0000000..200c448 Binary files /dev/null and b/v2/dotclear/admin/style/info.png differ diff --git a/v2/dotclear/admin/style/install.css b/v2/dotclear/admin/style/install.css new file mode 100644 index 0000000..31e2fa9 --- /dev/null +++ b/v2/dotclear/admin/style/install.css @@ -0,0 +1,165 @@ +/* + loader.css + Simple CSS rules for NetInstall + + (c) 2008 - Olivier Meunier & contributors - All rights reserved. + Please see http://dotclear.net/ for more details. +*/ +body.install { + font: 0.875em Helvetica,Arial,sans-serif; + color: #000; + background-color: #fff; + line-height: 1.5em; +} +body.install #content { + width:32em; + margin:1em auto; +} +body.install #main { + padding:1em 2em; + border:1px #ccc solid; + -moz-border-radius:8px; + -webkit-border-radius:8px; + border-radius: 8px; +} +body.install h1,h2 { + font-family: Helvetica,arial,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: #ff6000; +} +body.install h3 { + margin-top:0; +} +body.install label { + color: #555; +} + +body.install .msg { + padding:10px 10px 10px 60px; + border-radius:8px; + -moz-border-radius:8px; + -webkit-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; + -moz-border-radius: 8px; + -webkit-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: #dd0000; + 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; + -moz-border-radius: 2px; + -webkit-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); + -webkit-border-radius: .2em; + -moz-border-radius: .2em; + 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; +} diff --git a/v2/dotclear/admin/style/install/important.png b/v2/dotclear/admin/style/install/important.png new file mode 100644 index 0000000..01ad7d0 Binary files /dev/null and b/v2/dotclear/admin/style/install/important.png differ diff --git a/v2/dotclear/admin/style/install/note.png b/v2/dotclear/admin/style/install/note.png new file mode 100644 index 0000000..3e841c9 Binary files /dev/null and b/v2/dotclear/admin/style/install/note.png differ diff --git a/v2/dotclear/admin/style/install/process_warning.png b/v2/dotclear/admin/style/install/process_warning.png new file mode 100644 index 0000000..b1dbe7d Binary files /dev/null and b/v2/dotclear/admin/style/install/process_warning.png differ diff --git a/v2/dotclear/admin/style/install/w-logo.png b/v2/dotclear/admin/style/install/w-logo.png new file mode 100644 index 0000000..7295297 Binary files /dev/null and b/v2/dotclear/admin/style/install/w-logo.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_bquote.png b/v2/dotclear/admin/style/jsToolBar/bt_bquote.png new file mode 100644 index 0000000..bb6be88 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_bquote.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_br.png b/v2/dotclear/admin/style/jsToolBar/bt_br.png new file mode 100644 index 0000000..717ff6c Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_br.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_clean.png b/v2/dotclear/admin/style/jsToolBar/bt_clean.png new file mode 100644 index 0000000..3973a2c Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_clean.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_code.png b/v2/dotclear/admin/style/jsToolBar/bt_code.png new file mode 100644 index 0000000..40a149f Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_code.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_del.png b/v2/dotclear/admin/style/jsToolBar/bt_del.png new file mode 100644 index 0000000..87c3c66 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_del.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_em.png b/v2/dotclear/admin/style/jsToolBar/bt_em.png new file mode 100644 index 0000000..cddfe87 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_em.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_img.png b/v2/dotclear/admin/style/jsToolBar/bt_img.png new file mode 100644 index 0000000..b991a52 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_img.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_img_select.png b/v2/dotclear/admin/style/jsToolBar/bt_img_select.png new file mode 100644 index 0000000..b5bc5bf Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_img_select.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_ins.png b/v2/dotclear/admin/style/jsToolBar/bt_ins.png new file mode 100644 index 0000000..f690b90 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_ins.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_link.png b/v2/dotclear/admin/style/jsToolBar/bt_link.png new file mode 100644 index 0000000..07a2b0e Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_link.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_ol.png b/v2/dotclear/admin/style/jsToolBar/bt_ol.png new file mode 100644 index 0000000..65d26e4 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_ol.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_paragraph.png b/v2/dotclear/admin/style/jsToolBar/bt_paragraph.png new file mode 100644 index 0000000..5de086e Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_paragraph.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_post.png b/v2/dotclear/admin/style/jsToolBar/bt_post.png new file mode 100644 index 0000000..69f0c6f Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_post.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_pre.png b/v2/dotclear/admin/style/jsToolBar/bt_pre.png new file mode 100644 index 0000000..b765759 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_pre.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_quote.png b/v2/dotclear/admin/style/jsToolBar/bt_quote.png new file mode 100644 index 0000000..6cd98e1 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_quote.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_strong.png b/v2/dotclear/admin/style/jsToolBar/bt_strong.png new file mode 100644 index 0000000..d2ac922 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_strong.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/bt_ul.png b/v2/dotclear/admin/style/jsToolBar/bt_ul.png new file mode 100644 index 0000000..158790a Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/bt_ul.png differ diff --git a/v2/dotclear/admin/style/jsToolBar/jsToolBar.css b/v2/dotclear/admin/style/jsToolBar/jsToolBar.css new file mode 100644 index 0000000..ce8f8c8 --- /dev/null +++ b/v2/dotclear/admin/style/jsToolBar/jsToolBar.css @@ -0,0 +1,173 @@ +.jstEditor { + border-width : 0 1px 1px 1px; + border-style : solid; + border-color : #000 #ccc #ccc #000; + padding-left: 5px; + background : #f9f9f9; +} +.jstEditor textarea, .jstEditor iframe { + margin: 0; + border: 0; +} + +.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; +} + +.jstElements { + padding: 3px 3px; + border-width: 1px 1px 0 1px; + border-style: solid; + border-color: #000 #ccc #ccc #000; + background: #dfdfdf; +} + +.jstElements select, .jstElements button { + vertical-align: middle; +} +.jstElements select { + width: 12em; +} +.jstElements button { + margin-right : 2px; + width : 20px; + height: 20px; + padding: 0; + border-style: solid; + border-width: 1px; + border-color: #dfdfdf; + background-color : transparent; + background-position : 50% 50%; + background-repeat: no-repeat; +} +.jstElements button:hover { + border-color : #ccc; + background-color: #fff; +} +.jstElements button span { + display : none; +} +.jstElements span { + display : inline; +} + +.jstSpacer { + width : 0px; + font-size: 1px; + margin-right: 4px; +} + +.jstSwitcher { + display: block; + list-style: none; + margin: 0 0 0 -5px; + padding: 0 0 5px 0; + background: #dfdfdf; + border-top: 1px solid #999; + font-size: 90%; + font-weight: bold; +} +.jstSwitcher li { + display: inline; + margin: 0 0 0 5px; + padding: 2px 4px; +} +.jstSwitcher li.jstSwitcherCurrent { + color: #fff; + background: #999; + -moz-border-radius: 0 0 2px 2px; +} +.jstSwitcher a { + font-weight: normal; + border-bottom: none !important; +} + +/* Buttons +-------------------------------------------------------- */ +.jstb_strong { + background-image: url(bt_strong.png); +} +.jstb_em { + background-image: url(bt_em.png); +} +.jstb_ins { + background-image: url(bt_ins.png); +} +.jstb_del { + background-image: url(bt_del.png); +} +.jstb_quote { + background-image: url(bt_quote.png); +} +.jstb_code { + background-image: url(bt_code.png); +} +.jstb_paragraph { + background-image: url(bt_paragraph.png); +} +.jstb_br { + background-image: url(bt_br.png); +} +.jstb_blockquote { + background-image: url(bt_bquote.png); +} +.jstb_pre { + background-image: url(bt_pre.png); +} +.jstb_ul { + background-image: url(bt_ul.png); +} +.jstb_ol { + background-image: url(bt_ol.png); +} +.jstb_link { + background-image: url(bt_link.png); +} +.jstb_img { + background-image: url(bt_img.png); +} +.jstb_img_select { + background-image: url(bt_img_select.png); +} +.jstb_post_link { + background-image: url(bt_post.png); +} +.jstb_removeFormat { + background-image: url(bt_clean.png); +} + + +/* 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; +} \ No newline at end of file diff --git a/v2/dotclear/admin/style/jsToolBar/resize.png b/v2/dotclear/admin/style/jsToolBar/resize.png new file mode 100644 index 0000000..1b21270 Binary files /dev/null and b/v2/dotclear/admin/style/jsToolBar/resize.png differ diff --git a/v2/dotclear/admin/style/magnifier.png b/v2/dotclear/admin/style/magnifier.png new file mode 100644 index 0000000..1578a8d Binary files /dev/null and b/v2/dotclear/admin/style/magnifier.png differ diff --git a/v2/dotclear/admin/style/modal/close.png b/v2/dotclear/admin/style/modal/close.png new file mode 100644 index 0000000..5386506 Binary files /dev/null and b/v2/dotclear/admin/style/modal/close.png differ diff --git a/v2/dotclear/admin/style/modal/loader.gif b/v2/dotclear/admin/style/modal/loader.gif new file mode 100644 index 0000000..e2a116c Binary files /dev/null and b/v2/dotclear/admin/style/modal/loader.gif differ diff --git a/v2/dotclear/admin/style/modal/modal.css b/v2/dotclear/admin/style/modal/modal.css new file mode 100644 index 0000000..8524e7c --- /dev/null +++ b/v2/dotclear/admin/style/modal/modal.css @@ -0,0 +1,52 @@ +div.jq-modal { + line-height: 1; + background: #fff; +} +div.jq-modal-container { + padding: 1px; + position: relative; +} +div.jq-modal-content { + position: relative; + overflow: auto; +} +div.jq-modal-closer { + position: absolute; + top: -12px; + right: -12px; + width: 25px; + height: 25px; +} +div.jq-modal-closer a { + display: block; + text-indent: -5000px; + width: 25px; + height: 25px; + outline: none; + border: none; +} + +/* modalImage */ +div.jq-modal-content img { + display: block; +} +span.jq-modal-legend { + display: block; + padding: 3px 0; +} +a.jq-modal-next, a.jq-modal-prev { + display: block; + position: absolute; + top: 0; + width: 50%; + bottom: 0; + outline: none; + border: none; + text-indent: -5000px; +} +a.jq-modal-next { + right: 0; +} +a.jq-modal-prev { + left: 0; +} \ No newline at end of file diff --git a/v2/dotclear/admin/style/msg-error.png b/v2/dotclear/admin/style/msg-error.png new file mode 100644 index 0000000..43894f1 Binary files /dev/null and b/v2/dotclear/admin/style/msg-error.png differ diff --git a/v2/dotclear/admin/style/msg-std.png b/v2/dotclear/admin/style/msg-std.png new file mode 100644 index 0000000..3b6e107 Binary files /dev/null and b/v2/dotclear/admin/style/msg-std.png differ diff --git a/v2/dotclear/admin/style/package.png b/v2/dotclear/admin/style/package.png new file mode 100644 index 0000000..f138df4 Binary files /dev/null and b/v2/dotclear/admin/style/package.png differ diff --git a/v2/dotclear/admin/style/page-bg.png b/v2/dotclear/admin/style/page-bg.png new file mode 100644 index 0000000..a048e81 Binary files /dev/null and b/v2/dotclear/admin/style/page-bg.png differ diff --git a/v2/dotclear/admin/style/warning.png b/v2/dotclear/admin/style/warning.png new file mode 100644 index 0000000..5765d0c Binary files /dev/null and b/v2/dotclear/admin/style/warning.png differ diff --git a/v2/dotclear/admin/trackbacks.php b/v2/dotclear/admin/trackbacks.php new file mode 100644 index 0000000..a922d40 --- /dev/null +++ b/v2/dotclear/admin/trackbacks.php @@ -0,0 +1,150 @@ +blog->getPosts($params); + + if ($post->isEmpty()) { + $core->error->add(__('This entry does not exist or is not published')); + $can_view_page = false; + } else { + $TB = new dcTrackback($core); + $tb_excerpt = $post->post_excerpt_xhtml.' '.$post->post_content_xhtml; + $post_title = $post->post_title; + $post_url = $post->getURL(); + } +} +else +{ + $core->error->add(__('This entry does not exist.')); + $can_view_page = false; +} + +# Change excerpt +if (!empty($_POST['tb_excerpt'])) { + $tb_excerpt = $_POST['tb_excerpt']; +} + +# Sanitize excerpt +$tb_excerpt = html::clean($tb_excerpt); +$tb_excerpt = html::decodeEntities($tb_excerpt); +$tb_excerpt = text::cutString(html::escapeHTML($tb_excerpt),255); +$tb_excerpt = preg_replace('/\s+/ms',' ',$tb_excerpt); + +# Send pings +if ($post && !$post->isEmpty() && !empty($_POST['tb_urls'])) +{ + $tb_urls = $_POST['tb_urls']; + $tb_urls = str_replace("\r",'',$tb_urls); + + $post_title = html::escapeHTML(trim(html::clean($post_title))); + + foreach (explode("\n",$tb_urls) as $tb_url) + { + try { + $TB->ping($tb_url,$id,$post_title,$tb_excerpt,$post_url); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect('trackbacks.php?id='.$id.'&sent=1'); + } +} + +$page_title = __('Ping blogs'); + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open($page_title,dcPage::jsLoad('js/_trackbacks.js')); + +# Exit if we cannot view page +if (!$can_view_page) { + dcPage::close(); + exit; +} + +if (!empty($_GET['sent'])) { + echo '

        '.__('All pings sent.').'

        '; +} + +echo '

        '.html::escapeHTML($core->blog->name).' › '.$page_title.'

        '; + +echo '

        '. + sprintf(__('Back to "%s"'),html::escapeHTML($post->post_title)).'

        '; + +echo +'

        '. +html::escapeHTML($post->post_title).'

        '. +'
        '. +($post->post_excerpt_xhtml ? $post->post_excerpt_xhtml.'
        ' : ''). +$post->post_content_xhtml. +'
        '; + +if (!empty($_GET['auto'])) { + flush(); + $tb_urls = implode("\n",$TB->discover($post->post_excerpt_xhtml.' '.$post->post_content_xhtml)); +} else { + $auto_link = ''. + __('Auto discover ping URLs').''; +} + +echo +'

        '.__('Ping blogs').'

        '. +'
        '. +'

        '. + +'

        '. + +'

        '.form::hidden('id',$id). +$core->formNonce(). +'  '. +$auto_link.'

        '. +'
        '; + +$pings = $TB->getPostPings($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 '
        '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/update.php b/v2/dotclear/admin/update.php new file mode 100644 index 0000000..4759c85 --- /dev/null +++ b/v2/dotclear/admin/update.php @@ -0,0 +1,222 @@ +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); +$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,array('check','download','backup','unzip')) ? $step : ''; + +$archives = array(); +foreach (files::scanDir(DC_BACKUP_PATH) as $v) { + if (preg_match('/backup-([0-9A-Za-z\.-]+).zip/',$v)) { + $archives[] = $v; + } +} + +# 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); + } + + 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); + } + } + 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"') + ); + } + 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')); + +if (!$core->error->flag()) { + echo '

        '.__('Dotclear update').'

        '; +} + +if (!$step) +{ + 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').')' : ''). + '

        '. + + '

        '.__('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.').'

        '. + '
        '. + '

        '. + '

        '. + '
        '; + } + + if (!empty($archives)) + { + echo + '

        '.__('Update backup files').'

        '. + '

        '.__('The following files are backups of previously updates. '. + 'You can revert your previous installation or delete theses files.').'

        '; + + echo '
        '; + + foreach ($archives as $v) { + echo + '

        '; + } + + echo + '

        '.__('Please note that reverting your Dotclear version may have some '. + 'unwanted side-effects. Consider reverting only if you experience strong issues with this new version.').' '. + sprintf(__('You should not revert to version prior to last one (%s).'),end($archives)). + '

        '. + '

        '. + ''. + $core->formNonce().'

        '. + '
        '; + } +} +elseif ($step == 'unzip' && !$core->error->flag()) +{ + echo + '

        '. + __("Congratulations, you're one click away from the end of the update."). + ' '.__('Finish the update.').''. + '

        '; +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/user.php b/v2/dotclear/admin/user.php new file mode 100644 index 0000000..3af0c90 --- /dev/null +++ b/v2/dotclear/admin/user.php @@ -0,0 +1,331 @@ +auth->getInfo('user_lang'); +$user_tz = $core->auth->getInfo('user_tz'); +$user_post_status = ''; + +$user_options = $core->userDefaults(); + +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = $k; +} + +# Language codes +$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' : ''); +} + +# 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(crypt::hmac(DC_MASTER_KEY,$_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 = $_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_post_status = $user_post_status = $_POST['user_post_status']; + + if ($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'] = $_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(); + } + + http::redirect('user.php?id='.$new_id.'&upd=1'); + } + # Add user + else + { + if ($core->getUsers(array('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); + + if (!empty($_POST['saveplus'])) { + http::redirect('user.php?add=1'); + } else { + http::redirect('user.php?id='.$new_id.'&add=1'); + } + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + + +/* DISPLAY +-------------------------------------------------------- */ +dcPage::open($page_title, + dcPage::jsConfirmClose('user-form'). + + # --BEHAVIOR-- adminUserHeaders + $core->callBehavior('adminUserHeaders') +); + +if (!empty($_GET['upd'])) { + echo '

        '.__('User has been successfully updated.').'

        '; +} + +if (!empty($_GET['add'])) { + echo '

        '.__('User has been successfully created.').'

        '; +} + +echo '

        '.__('Users').''.$page_title.'

        '; + +if ($user_id == $core->auth->userID()) { + echo + '

        '.__('Warning:').' '. + __('If you change your username, you will have to log in again.').'

        '; +} + +echo +'
        '. +'
        '.__('User information').''. +'
        '. +'
        '. +'

        '. +'

        '.__('At least 2 characters using letters, numbers or symbols.').'

        '. + +'

        '. +'

        '.__('Password must contain at least 6 characters.').'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. +'

        '.__('Mandatory for password recovering procedure.').'

        '. +'
        '. + +'
        '. +'

        '. +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '; + +if ($core->auth->allowPassChange()) { + echo + '

        '; +} + +$super_disabled = $user_super && $user_id == $core->auth->userID(); + +echo +'

        '. +'
        '. +'
        '. +'
        '; + +# --BEHAVIOR-- adminUserForm +$core->callBehavior('adminUserForm',isset($rs) ? $rs : null); + +echo +'

        '. +'

        '. +($user_id != '' ? '' : ' '). +($user_id != '' ? form::hidden('id',$user_id) : ''). +$core->formNonce(). +'

        '. + +'
        '; + +if ($user_id) +{ + echo '

        '.__('Permissions').'

        '; + + $permissions = $core->getUserPermissions($user_id); + $perm_types = $core->auth->getPermissionsTypes(); + + if (count($permissions) == 0) + { + echo '

        '.__('No permissions.').'

        '; + } + else + { + foreach ($permissions as $k => $v) + { + if (count($v['p']) > 0) + { + echo '

        '. + html::escapeHTML($v['name']).' ('.html::escapeHTML($k).') - '. + '' + .__('Change permissions').'

        '; + + echo '
          '; + foreach ($v['p'] as $p => $V) { + if (isset($perm_types[$p])) { + echo '
        • '.__($perm_types[$p]).'
        • '; + } + } + echo '
        '; + } + } + } + + echo + '

        '. + __('Add new permissions').'

        '. + '
        '; +} + +dcPage::helpBlock('core_user'); +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/users.php b/v2/dotclear/admin/users.php new file mode 100644 index 0000000..2f61820 --- /dev/null +++ b/v2/dotclear/admin/users.php @@ -0,0 +1,174 @@ +callBehavior('adminBeforeUserDelete',$u); + if ($u != $core->auth->userID()) { + $core->delUser($u); + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + if (!$core->error->flag()) { + http::redirect('users.php?del=1'); + } +} + + +# Creating filter combo boxes +$sortby_combo = array( +__('Username') => 'U.user_id', +__('Last Name') => 'user_name', +__('First Name') => 'user_firstname', +__('Display name') => 'user_displayname', +__('Number of entries') => 'nb_post' +); + +$order_combo = array( +__('Descending') => 'desc', +__('Ascending') => 'asc' +); + +# Actions combo box +$combo_action = array( + __('Set permissions') => 'setpermissions', + __('Delete') => 'deleteuser' +); + +# --BEHAVIOR-- adminUsersActionsCombo +$core->callBehavior('adminUsersActionsCombo',array(&$combo_action)); + + +# Get users +$page = !empty($_GET['page']) ? $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = $_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'] = array((($page-1)*$nb_per_page),$nb_per_page); + +$show_filters = false; + +# - Search filter +if ($q) { + $params['q'] = $q; + $show_filters = true; +} + +# - Sortby and order filter +if ($sortby !== '' && in_array($sortby,$sortby_combo)) { + if ($order !== '' && in_array($order,$order_combo)) { + $params['order'] = $sortby.' '.$order; + $show_filters = true; + } +} + +try { + $rs = $core->getUsers($params); + $counter = $core->getUsers($params,1); + $user_list = new adminUserList($core,$rs,$counter->f(0)); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + + +/* DISPLAY +-------------------------------------------------------- */ +$starting_script = dcPage::jsLoad('js/_users.js'); +if (!$show_filters) { + $starting_script .= dcPage::jsLoad('js/filter-controls.js'); +} + +dcPage::open(__('users'),$starting_script); + +if (!$core->error->flag()) +{ + if (!empty($_GET['del'])) { + echo '

        '.__('User has been successfully removed.').'

        '; + } + + echo + '

        '.__('Users').'

        '. + '

        '.__('Create a new user').'

        '; + + if (!$show_filters) { + echo '

        '.__('Filters').'

        '; + } + + echo + '
        '. + '
        '.__('Filters').''. + + '
        '. + '

        '. + '

        '. + '
        '. + + '
        '. + '

        '. + '

        '. + '

        '. + '
        '. + + '
        '. //Opera sucks + '
        '. + '
        '; + + # Show users + $user_list->display($page,$nb_per_page, + '
        '. + + '%s'. + + '
        '. + '

        '. + + '

        '. + ''. + '

        '. + '
        '. + '
        ' + ); +} + +dcPage::close(); +?> \ No newline at end of file diff --git a/v2/dotclear/admin/xmlrpc.php b/v2/dotclear/admin/xmlrpc.php new file mode 100644 index 0000000..75195f3 --- /dev/null +++ b/v2/dotclear/admin/xmlrpc.php @@ -0,0 +1,35 @@ +plugins->loadModules(DC_PLUGINS_ROOT); + +# Start XML-RPC server +$server = new dcXmlRpc($core,$blog_id); +$server->serve(); +?> \ No newline at end of file diff --git a/v2/dotclear/cache/.htaccess b/v2/dotclear/cache/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/v2/dotclear/cache/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/v2/dotclear/db/.htaccess b/v2/dotclear/db/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/v2/dotclear/db/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/v2/dotclear/inc/.htaccess b/v2/dotclear/inc/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/v2/dotclear/inc/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/v2/dotclear/inc/admin/class.dc.menu.php b/v2/dotclear/inc/admin/class.dc.menu.php new file mode 100644 index 0000000..9a016dd --- /dev/null +++ b/v2/dotclear/inc/admin/class.dc.menu.php @@ -0,0 +1,88 @@ +id = $id; + $this->title = $title; + $this->itemSpace = $itemSpace; + $this->items = array(); + } + + public function addItem($title,$url,$img,$active,$show=true,$id=null,$class=null) + { + if($show) { + $this->items[] = $this->itemDef($title,$url,$img,$active,$id,$class); + } + } + + public function prependItem($title,$url,$img,$active,$show=true,$id=null,$class=null) + { + if ($show) { + array_unshift($this->items,$this->itemDef($title,$url,$img,$active,$id,$class)); + } + } + + public function draw() + { + if (count($this->items) == 0) { + return ''; + } + + $res = + '
        '. + ($this->title ? '

        '.$this->title.'

        ' : ''). + '
          '."\n"; + + for ($i=0; $iitems); $i++) + { + if ($i+1 < count($this->items) && $this->itemSpace != '') { + $res .= preg_replace('|$|',$this->itemSpace.'',$this->items[$i]); + $res .= "\n"; + } else { + $res .= $this->items[$i]."\n"; + } + } + + $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"; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/admin/lib.dc.page.php b/v2/dotclear/inc/admin/lib.dc.page.php new file mode 100644 index 0000000..a3b013a --- /dev/null +++ b/v2/dotclear/inc/admin/lib.dc.page.php @@ -0,0 +1,709 @@ +blog && $core->auth->check($permissions,$core->blog->id)) + { + return; + } + + if (session_id()) { + $core->session->destroy(); + } + http::redirect(DC_AUTH_PAGE); + } + + # Check super admin + public static function checkSuper() + { + global $core; + + if (!$core->auth->isSuperAdmin()) + { + if (session_id()) { + $core->session->destroy(); + } + http::redirect(DC_AUTH_PAGE); + } + } + + # Top of admin page + public static function open($title='', $head='') + { + global $core; + + # List of user's blogs + if ($core->auth->blog_count == 1 || $core->auth->blog_count > 20) + { + $blog_box = + __('Blog:').' '. + html::escapeHTML($core->blog->name).''; + + if ($core->auth->blog_count > 20) { + $blog_box .= ' - '.__('Change blog').''; + } + } + else + { + $rs_blogs = $core->getBlogs(array('order'=>'LOWER(blog_name)','limit'=>20)); + $blogs = array(); + while ($rs_blogs->fetch()) { + $blogs[html::escapeHTML($rs_blogs->blog_name.' - '.$rs_blogs->blog_url)] = $rs_blogs->blog_id; + } + $blog_box = + ''. + ''; + } + + $safe_mode = isset($_SESSION['sess_safe_mode']) && $_SESSION['sess_safe_mode']; + + # Display + header('Content-Type: text/html; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + ' '."\n". + ' '.$title.' - '.html::escapeHTML($core->blog->name).' - '.html::escapeHTML(DC_VENDOR_NAME).' - '.DC_VERSION.''."\n". + + ' '."\n". + ' '."\n". + + self::jsLoadIE7(). + ' '."\n"; + if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') { + echo + ' '."\n"; + } + + $core->auth->user_prefs->addWorkspace('interface'); + $user_ui_hide_std_favicon = $core->auth->user_prefs->interface->hide_std_favicon; + if (!$user_ui_hide_std_favicon) { + echo ''; + } + + echo + self::jsCommon(). + $head; + + # --BEHAVIOR-- adminPageHTMLHead + $core->callBehavior('adminPageHTMLHead'); + + echo + "\n". + ''."\n". + + ''; + + echo + '
        '."\n". + '
        '."\n". + '
        '."\n"; + + # Safe mode + if ($safe_mode) + { + echo + '

        '.__('Safe mode').'

        '. + '

        '.__('You are in safe mode. All plugins have been temporarily disabled. Remind to log out then log in again normally to get back all functionalities').'

        '. + '
        '; + } + + if ($core->error->flag()) { + echo + '
        '.__('Errors:').''. + $core->error->toHTML(). + '
        '; + } + } + + public static function close() + { + $menu =& $GLOBALS['_menu']; + + echo + "
        \n". // End of #content + "
        \n". // End of #main + + ''."\n". // End of #main-menu + ''."\n". + "
        \n"; // End of #wrapper + + if (defined('DC_DEV') && DC_DEV === true) { + echo self::debugInfo(); + } + + echo + ''; + } + + public static function openPopup($title='', $head='') + { + global $core; + + # Display + header('Content-Type: text/html; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + ' '."\n". + ' '.$title.' - '.html::escapeHTML($core->blog->name).' - '.html::escapeHTML(DC_VENDOR_NAME).' - '.DC_VERSION.''."\n". + + ' '."\n". + ' '."\n". + + self::jsLoadIE7(). + ' '."\n"; + if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') { + echo + ' '."\n"; + } + + echo + self::jsCommon(). + $head; + + # --BEHAVIOR-- adminPageHTMLHead + $core->callBehavior('adminPageHTMLHead'); + + echo + "\n". + ''."\n". + + '

        '.DC_VENDOR_NAME.'

        '."\n"; + + echo + '
        '."\n". + '
        '."\n". + '
        '."\n"; + + if ($core->error->flag()) { + echo + '
        '.__('Errors:').''. + $core->error->toHTML(). + '
        '; + } + } + + public static function closePopup() + { + echo + "
        \n". // End of #content + "
        \n". // End of #main + ''."\n". + "
        \n". // End of #wrapper + ''; + } + + private static function debugInfo() + { + $global_vars = implode(', ',array_keys($GLOBALS)); + + $res = + '
        '. + '

        memory usage: '.memory_get_usage().' ('.files::size(memory_get_usage()).')

        '; + + if (function_exists('xdebug_get_profiler_filename')) + { + $res .= '

        Elapsed time: '.xdebug_time_index().' seconds

        '; + + $prof_file = xdebug_get_profiler_filename(); + if ($prof_file) { + $res .= '

        Profiler file : '.xdebug_get_profiler_filename().'

        '; + } else { + $prof_url = http::getSelfURI(); + $prof_url .= (strpos($prof_url,'?') === false) ? '?' : '&'; + $prof_url .= 'XDEBUG_PROFILE'; + $res .= '

        Trigger profiler

        '; + } + + /* xdebug configuration: + zend_extension = /.../xdebug.so + xdebug.auto_trace = On + xdebug.trace_format = 0 + xdebug.trace_options = 1 + xdebug.show_mem_delta = On + xdebug.profiler_enable = 0 + xdebug.profiler_enable_trigger = 1 + xdebug.profiler_output_dir = /tmp + xdebug.profiler_append = 0 + xdebug.profiler_output_name = timestamp + */ + } + + $res .= + '

        Global vars: '.$global_vars.'

        '. + '
        '; + + return $res; + } + + public static function help($page,$index='') + { + # Deprecated but we keep this for plugins. + } + + public static function helpBlock() + { + $args = func_get_args(); + if (empty($args)) { + return; + }; + + global $__resources; + if (empty($__resources['help'])) { + return; + } + + $content = ''; + foreach ($args as $v) + { + if (is_object($v) && isset($v->content)) { + $content .= $v->content; + continue; + } + + if (!isset($__resources['help'][$v])) { + continue; + } + $f = $__resources['help'][$v]; + if (!file_exists($f) || !is_readable($f)) { + continue; + } + + $fc = file_get_contents($f); + if (preg_match('|]*?>(.*?)|ms',$fc,$matches)) { + $content .= $matches[1]; + } else { + $content .= $fc; + } + } + + if (trim($content) == '') { + return; + } + + echo + '

        '.__('Help').'

        '. + $content. + '
        '; + } + + public static function jsLoad($src) + { + $escaped_src = html::escapeHTML($src); + if (!isset(self::$loaded_js[$escaped_src])) { + self::$loaded_js[$escaped_src]=true; + return ''."\n"; + } + } + + public static function jsVar($n,$v) + { + return $n." = '".html::escapeJS($v)."';\n"; + } + + public static function jsCommon() + { + return + self::jsLoad('js/jquery/jquery.js'). + self::jsLoad('js/jquery/jquery.biscuit.js'). + self::jsLoad('js/jquery/jquery.bgFade.js'). + self::jsLoad('js/common.js'). + self::jsLoad('js/prelude.js'). + + '\n"; + } + + public static function jsLoadIE7() + { + return + ''."\n"; + } + + public static function jsConfirmClose() + { + $args = func_get_args(); + if (count($args) > 0) { + foreach ($args as $k => $v) { + $args[$k] = "'".html::escapeJS($v)."'"; + } + $args = implode(',',$args); + } else { + $args = ''; + } + + return + self::jsLoad('js/confirm-close.js'). + '\n"; + } + + public static function jsPageTabs($default=null) + { + if ($default) { + $default = "'".html::escapeJS($default)."'"; + } + + return + self::jsLoad('js/jquery/jquery.pageTabs.js'). + '\n"; + } + + public static function jsModal() + { + return + ''."\n". + self::jsLoad('js/jquery/jquery.modal.js'). + '\n"; + } + + public static function jsColorPicker() + { + return + ''."\n". + self::jsLoad('js/jquery/jquery.farbtastic.js'). + self::jsLoad('js/color-picker.js'); + } + + public static function jsDatePicker() + { + return + ''."\n". + self::jsLoad('js/date-picker.js'). + '\n"; + } + + public static function jsToolBar() + { + $res = + ''. + ''; + + if (isset($GLOBALS['core']->auth) && $GLOBALS['core']->auth->getOption('enable_wysiwyg')) { + $res .= ''; + } + + $res .= + ''. + '\n"; + + return $res; + } + + public static function jsCandyUpload($params=array(),$base_url=null) + { + if (!$base_url) { + $base_url = path::clean(dirname(preg_replace('/(\?.*$)?/','',$_SERVER['REQUEST_URI']))).'/'; + } + + $params = array_merge($params,array( + 'sess_id='.session_id(), + 'sess_uid='.$_SESSION['sess_browser_uid'], + 'xd_check='.$GLOBALS['core']->getNonce() + )); + + return + ''."\n". + self::jsLoad('js/jquery/jquery.candyUpload.js'). + + '\n"; + } + + public static function jsToolMan() + { + return + ''. + ''. + ''. + ''. + ''. + ''. + ''; + } + + public static function jsMetaEditor() + { + return + ''; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/admin/lib.pager.php b/v2/dotclear/inc/admin/lib.pager.php new file mode 100644 index 0000000..488e091 --- /dev/null +++ b/v2/dotclear/inc/admin/lib.pager.php @@ -0,0 +1,420 @@ +core =& $core; + $this->rs =& $rs; + $this->rs_count = $rs_count; + $this->html_prev = __('«prev.'); + $this->html_next = __('next»'); + } +} + +class adminPostList extends adminGenericList +{ + public function display($page,$nb_per_page,$enclose_block='') + { + if ($this->rs->isEmpty()) + { + echo '

        '.__('No entry').'

        '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
        '.__('Title').''.__('Date').''.__('Category').''.__('Author').''.__('Comments').''.__('Trackbacks').''.__('Status').'
        '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->postLine(); + } + + echo $blocks[1]; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + } + } + + private function postLine() + { + 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 = __('None'); + } + + $img = '%1$s'; + switch ($this->rs->post_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('scheduled'),'scheduled.png'); + break; + case -2: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + 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 = ''; + + $res .= + ''. + form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable()).''. + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).''. + ''.$cat_title.''. + ''.$this->rs->user_id.''. + ''.$this->rs->nb_comment.''. + ''.$this->rs->nb_trackback.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''; + + 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 pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + '%s
        '.__('Title').''.__('Date').''.__('Author').''.__('Status').'
        '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->postLine(); + } + + echo $blocks[1]; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + } + } + + private function postLine() + { + $img = '%1$s'; + switch ($this->rs->post_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('scheduled'),'scheduled.png'); + break; + case -2: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + 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 = ''; + + $res .= + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).''. + ''.$this->rs->user_id.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''; + + return $res; + } +} + +class adminCommentList extends adminGenericList +{ + public function display($page,$nb_per_page,$enclose_block='') + { + if ($this->rs->isEmpty()) + { + echo '

        '.__('No comment').'

        '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
        '.__('Title').''.__('Date').''.__('Author').''.__('Type').''.__('Status').' 
        '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->commentLine(); + } + + echo $blocks[1]; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + } + } + + private function commentLine() + { + global $author, $status, $sortby, $order, $nb_per_page; + + $author_url = + 'comments.php?n='.$nb_per_page. + '&status='.$status. + '&sortby='.$sortby. + '&order='.$order. + '&author='.rawurlencode($this->rs->comment_author); + + $post_url = $this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id); + + $comment_url = 'comment.php?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 = '%1$s'; + switch ($this->rs->comment_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + break; + case -2: + $img_status = sprintf($img,__('junk'),'junk.png'); + break; + } + + $comment_author = html::escapeHTML($this->rs->comment_author); + if (mb_strlen($comment_author) > 20) { + $comment_author = mb_strcut($comment_author,0,17).'...'; + } + + $res = ''; + + $res .= + ''. + form::checkbox(array('comments[]'),$this->rs->comment_id,'','','',0).''. + ''. + html::escapeHTML($this->rs->post_title).''. + ($this->rs->post_type != 'post' ? ' ('.html::escapeHTML($this->rs->post_type).')' : '').''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->comment_dt).''. + ''.$comment_author.''. + ''.($this->rs->comment_trackback ? __('trackback') : __('comment')).''. + ''.$img_status.''. + ''. + ''; + + $res .= ''; + + return $res; + } +} + +class adminUserList extends adminGenericList +{ + public function display($page,$nb_per_page,$enclose_block='') + { + if ($this->rs->isEmpty()) + { + echo '

        '.__('No user').'

        '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + '%s
        '.__('Username').''.__('First Name').''.__('Last Name').''.__('Display name').''.__('Entries').'
        '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->userLine(); + } + + echo $blocks[1]; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + } + } + + private function userLine() + { + $img = '%1$s'; + $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'); + } + return + ''. + ''.form::hidden(array('nb_post[]'),(integer) $this->rs->nb_post). + form::checkbox(array('user_id[]'),$this->rs->user_id).''. + ''. + $this->rs->user_id.' '.$img_status.''. + ''.$this->rs->user_firstname.''. + ''.$this->rs->user_name.''. + ''.$this->rs->user_displayname.''. + ''. + $this->rs->nb_post.''. + ''; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/admin/prepend.php b/v2/dotclear/inc/admin/prepend.php new file mode 100644 index 0000000..b1413c5 --- /dev/null +++ b/v2/dotclear/inc/admin/prepend.php @@ -0,0 +1,376 @@ +plugins->moduleExists($matches[1])) { + return false; + } + } + } + return true; +} + +function dc_prepare_url($url) { + + $u = str_replace(array('?','&'),array('\?','&'),$url); + return (!strpos($u,'\?') ? + '/'.$u.'$/' : + (!strpos($u,'&') ? + '/'.$u.'(\?.*)?$/' : + '/'.$u.'(&.*)?$/')); +} + +function dc_load_locales() { + global $_lang, $core; + + $_lang = $core->auth->getInfo('user_lang'); + $_lang = preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$_lang) ? $_lang : 'en'; + + 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.'/plugins'); +} + +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 = array('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; +} + +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']); + } + + # 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'); + } + +/* + # Check add to my fav fired + if (!empty($_REQUEST['add-favorite'])) { + $redir = $_SERVER['REQUEST_URI']; + # Extract admin page from URI + # TO BE COMPLETED + } +*/ +} + +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); + + $core->auth->user_prefs->addWorkspace('interface'); + $user_ui_nofavmenu = $core->auth->user_prefs->interface->nofavmenu; + + # Standard favorites + $_fav = new ArrayObject(); + + # [] : Title, URL, small icon, large icon, permissions, id, class + # NB : '*' in permissions means any, null means super admin only + + $_fav['prefs'] = new ArrayObject(array('prefs','My preferences','preferences.php', + 'images/menu/user-pref.png','images/menu/user-pref-b.png', + '*',null,null)); + + $_fav['new_post'] = new ArrayObject(array('new_post','New entry','post.php', + 'images/menu/edit.png','images/menu/edit-b.png', + 'usage,contentadmin',null,'menu-new-post')); + $_fav['posts'] = new ArrayObject(array('posts','Entries','posts.php', + 'images/menu/entries.png','images/menu/entries-b.png', + 'usage,contentadmin',null,null)); + $_fav['comments'] = new ArrayObject(array('comments','Comments','comments.php', + 'images/menu/comments.png','images/menu/comments-b.png', + 'usage,contentadmin',null,null)); + $_fav['search'] = new ArrayObject(array('search','Search','search.php', + 'images/menu/search.png','images/menu/search-b.png', + 'usage,contentadmin',null,null)); + $_fav['categories'] = new ArrayObject(array('categories','Categories','categories.php', + 'images/menu/categories.png','images/menu/categories-b.png', + 'categories',null,null)); + $_fav['media'] = new ArrayObject(array('media','Media manager','media.php', + 'images/menu/media.png','images/menu/media-b.png', + 'media,media_admin',null,null)); + $_fav['blog_pref'] = new ArrayObject(array('blog_pref','Blog settings','blog_pref.php', + 'images/menu/blog-pref.png','images/menu/blog-pref-b.png', + 'admin',null,null)); + $_fav['blog_theme'] = new ArrayObject(array('blog_theme','Blog appearance','blog_theme.php', + 'images/menu/themes.png','images/menu/blog-theme-b.png', + 'admin',null,null)); + + $_fav['blogs'] = new ArrayObject(array('blogs','Blogs','blogs.php', + 'images/menu/blogs.png','images/menu/blogs-b.png', + 'usage,contentadmin',null,null)); + $_fav['users'] = new ArrayObject(array('users','Users','users.php', + 'images/menu/users.png','images/menu/users-b.png', + null,null,null)); + $_fav['plugins'] = new ArrayObject(array('plugins','Plugins','plugins.php', + 'images/menu/plugins.png','images/menu/plugins-b.png', + null,null,null)); + $_fav['langs'] = new ArrayObject(array('langs','Languages','langs.php', + 'images/menu/langs.png','images/menu/langs-b.png', + null,null,null)); + + # Menus creation + $_menu['Dashboard'] = new dcMenu('dashboard-menu',null); + if (!$user_ui_nofavmenu) + $_menu['Favorites'] = new dcMenu('favorites-menu','My favorites'); + $_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); + + # Loading favorites info from plugins + $core->callBehavior('adminDashboardFavs', $core, $_fav); + + # Set menu titles + + $_menu['System']->title = __('System'); + $_menu['Blog']->title = __('Blog'); + $_menu['Plugins']->title = __('Plugins'); + if (!$user_ui_nofavmenu) + $_menu['Favorites']->title = __('My favorites'); + +/* + if (!preg_match('/index.php$/',$_SERVER['REQUEST_URI'])) { + # Admin index can't be add in fav's + $_menu['Dashboard']->prependItem(__('Add this page to my favorites'),'#','images/menu/add_to_favorites.png', + false,$core->auth->check('usage,contentadmin',$core->blog->id),'fav-add'); + } +*/ + $_menu['Blog']->prependItem(__('Blog appearance'),'blog_theme.php','images/menu/themes.png', + preg_match('/blog_theme.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Blog settings'),'blog_pref.php','images/menu/blog-pref.png', + preg_match('/blog_pref.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Media manager'),'media.php','images/menu/media.png', + preg_match('/media(_item)?.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('media,media_admin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Categories'),'categories.php','images/menu/categories.png', + preg_match('/categories.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('categories',$core->blog->id)); + $_menu['Blog']->prependItem(__('Search'),'search.php','images/menu/search.png', + preg_match('/search.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Comments'),'comments.php','images/menu/comments.png', + preg_match('/comments.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + $_menu['Blog']->prependItem(__('Entries'),'posts.php','images/menu/entries.png', + preg_match('/posts.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + $_menu['Blog']->prependItem(__('New entry'),'post.php','images/menu/edit.png', + preg_match('/post.php$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id),'menu-new-post'); + + $_menu['System']->prependItem(__('Updates'),'update.php','images/menu/update.png', + preg_match('/update.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin() && is_readable(DC_DIGESTS)); + $_menu['System']->prependItem(__('Languages'),'langs.php','images/menu/langs.png', + preg_match('/langs.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + $_menu['System']->prependItem(__('Plugins'),'plugins.php','images/menu/plugins.png', + preg_match('/plugins.php(\?.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + $_menu['System']->prependItem(__('Users'),'users.php','images/menu/users.png', + preg_match('/users.php$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + $_menu['System']->prependItem(__('Blogs'),'blogs.php','images/menu/blogs.png', + preg_match('/blogs.php$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin() || + $core->auth->check('usage,contentadmin',$core->blog->id) && $core->auth->blog_count > 1); + + if (!$user_ui_nofavmenu) { + // Set favorites menu + $ws = $core->auth->user_prefs->addWorkspace('favorites'); + $count = 0; + foreach ($ws->dumpPrefs() as $k => $v) { + // User favorites only + if (!$v['global']) { + $fav = unserialize($v['value']); + if (dc_valid_fav($fav['url'])) { + $count++; + $_menu['Favorites']->addItem(__($fav['title']),$fav['url'],$fav['small-icon'], + preg_match(dc_prepare_url($fav['url']),$_SERVER['REQUEST_URI']), + (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)),$fav['id'],$fav['class']); + } + } + } + if (!$count) { + // Global favorites if any + foreach ($ws->dumpPrefs() as $k => $v) { + $fav = unserialize($v['value']); + if (dc_valid_fav($fav['url'])) { + $count++; + $_menu['Favorites']->addItem(__($fav['title']),$fav['url'],$fav['small-icon'], + preg_match(dc_prepare_url($fav['url']),$_SERVER['REQUEST_URI']), + (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)),$fav['id'],$fav['class']); + } + } + } + if (!$count) { + // No user or global favorites, add "new entry" fav + $_menu['Favorites']->addItem(__('New entry'),'post.php','images/menu/edit.png', + preg_match('/post.php$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id),'menu-new-post',null); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/config.php.in b/v2/dotclear/inc/config.php.in new file mode 100644 index 0000000..65bea68 --- /dev/null +++ b/v2/dotclear/inc/config.php.in @@ -0,0 +1,70 @@ + \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.auth.php b/v2/dotclear/inc/core/class.dc.auth.php new file mode 100644 index 0000000..60eedc0 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.auth.php @@ -0,0 +1,632 @@ +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 = array( + '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()) { + return false; + } + + $rs->extend('rsExtUser'); + + if ($pwd != '') + { + if (crypt::hmac(DC_MASTER_KEY,$pwd) != $rs->user_pwd) { + sleep(rand(2,5)); + return false; + } + } + elseif ($user_key != '') + { + if (http::browserUID(DC_MASTER_KEY.$rs->user_id.$rs->user_pwd) != $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 only check current user password. + * + * @param string $pwd User password + * @return boolean + */ + public function checkPassword($pwd) + { + if (!empty($this->user_info['user_pwd'])) { + return $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 ? $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 = 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*',crypt::hmac(DC_MASTER_KEY,$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('H40hex',substr($code,32,40)); + + 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 (crypt::hmac(DC_MASTER_KEY,$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) + { + if (!is_callable($f)) { + throw new Exception($f.' function doest not exist'); + } + + $args = func_get_args(); + array_shift($args); + + 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->blog_count === null) { + $this->blog_count = $this->core->getBlogs(array(),true)->f(0); + } + + 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 : array('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 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 blog_id '. + 'FROM '.$this->perm_table.' '. + "WHERE user_id = '".$this->con->escape($this->user_id)."' ". + "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ". + 'ORDER BY 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 null; + } + + /** + * 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 null; + } + + /** + * 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 = array(); + 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()); + + $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 = crypt::hmac(DC_MASTER_KEY,$new_pass); + $cur->user_recover_key = null; + + $cur->update("WHERE user_recover_key = '".$this->con->escape($recover_key)."'"); + + return array('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) {} + //@} +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.blog.php b/v2/dotclear/inc/core/class.dc.blog.php new file mode 100644 index 0000000..3509c3f --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.blog.php @@ -0,0 +1,2089 @@ +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 = preg_replace('|^([a-z]{3,}://)(.*?)/.*$|','$1$2',$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 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) + { + $id = (integer) $id; + + $strReq = 'SELECT post_id, comment_trackback '. + 'FROM '.$this->prefix.'comment '. + 'WHERE comment_id = '.$id.' '; + + $rs = $this->con->select($strReq); + + $post_id = $rs->post_id; + $tb = (boolean) $rs->comment_trackback; + + $strReq = 'SELECT COUNT(post_id) '. + 'FROM '.$this->prefix.'comment '. + 'WHERE post_id = '.(integer) $post_id.' '. + 'AND comment_trackback = '.(integer) $tb.' '. + 'AND comment_status = 1 '; + + if ($del) { + $strReq .= 'AND comment_id <> '.$id.' '; + } + + $rs = $this->con->select($strReq); + + $cur = $this->con->openCursor($this->prefix.'post'); + + if ($rs->isEmpty()) { + return; + } + + if ($tb) { + $cur->nb_trackback = (integer) $rs->f(0); + } else { + $cur->nb_comment = (integer) $rs->f(0); + } + + $cur->update('WHERE post_id = '.(integer) $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=array()) + { + $c_params = array(); + if (isset($params['post_type'])) { + $c_params['post_type'] = $params['post_type']; + unset($params['post_type']); + } + $counter = $this->getCategoriesCounter($c_params); + + $without_empty = $this->core->auth->userID() == false; # For public 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 = array(); + $stack = array(); + $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 = array(); + 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 = array($v); + break; + } + } + if (!$found) { + $data = array(); + } + } + + 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 = array($v); + break; + } + } + if (!$found) { + $data = array(); + } + } + + return staticRecord::newFromArray($data); + } + + /** + Retrieves a category by its ID. + + @param id integer Category ID + @return record + */ + public function getCategory($id) + { + return $this->getCategories(array('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(array('start' => $id,'level' => $id == 0 ? 1 : 2)); + } + + private function getCategoriesCounter($params=array()) + { + $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 = array(); + 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 = array(); + if ($parent != 0) + { + $rs = $this->getCategory($parent); + if ($rs->isEmpty()) { + $url = array(); + } 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); + + $this->categories()->addNode($cur,$parent); + + # --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 = array(); + $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(); + } + + /** + 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) + { + $strReq = 'SELECT cat_id '. + 'FROM '.$this->prefix.'category '. + "WHERE cat_url = '".$this->con->escape($url)."' ". + "AND blog_id = '".$this->con->escape($this->id)."' "; + + if ($id !== null) { + $strReq .= 'AND cat_id <> '.(integer) $id.' '; + } + + $rs = $this->con->select($strReq); + + if (!$rs->isEmpty()) { + throw new Exception(__('Category URL must be unique.')); + } + } + + 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 + $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) 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 + + 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=array(),$count_only=false) + { + # --BEHAVIOR-- coreBlogBeforeGetPosts + $params = new ArrayObject($params); + $this->core->callBehavior('coreBlogBeforeGetPosts',$params); + + if ($count_only) + { + $strReq = 'SELECT count(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_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'],create_function('&$v,$k','if($v!==null){$v=(integer)$v;}')); + } else { + $params['post_id'] = array((integer) $params['post_id']); + } + $strReq .= 'AND P.post_id '.$this->con->in($params['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'] = array($params['cat_id']); + } + if (!empty($params['cat_id_not'])) { + array_walk($params['cat_id'],create_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'] = array($params['cat_url']); + } + if (!empty($params['cat_url_not'])) { + array_walk($params['cat_url'],create_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_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,array(&$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 (!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 = array(); + $rs->extend('rsExtPost'); + + # --BEHAVIOR-- coreBlogGetPosts + $this->core->callBehavior('coreBlogGetPosts',$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 null; + } + + 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=array()) + { + $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=array()) + { + $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(); + + 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(); + } + + /** + Updates post status. + + @param id integer Post ID + @param status integer Post status + */ + public function updPostStatus($id,$status) + { + if (!$this->core->auth->check('publish,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to change this entry status')); + } + + $id = (integer) $id; + $status = (integer) $status; + + #If user can only publish, 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 blog_id = '".$this->con->escape($this->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')); + } + } + + $cur = $this->con->openCursor($this->prefix.'post'); + + $cur->post_status = $status; + $cur->post_upddt = date('Y-m-d H:i:s'); + + $cur->update( + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' " + ); + $this->triggerBlog(); + } + + public function updPostSelected($id,$selected) + { + if (!$this->core->auth->check('usage,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to change this entry category')); + } + + $id = (integer) $id; + $selected = (boolean) $selected; + + # 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 blog_id = '".$this->con->escape($this->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 mark this entry as selected')); + } + } + + $cur = $this->con->openCursor($this->prefix.'post'); + + $cur->post_selected = (integer) $selected; + $cur->post_upddt = date('Y-m-d H:i:s'); + + $cur->update( + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' " + ); + $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) + { + if (!$this->core->auth->check('usage,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to change this entry category')); + } + + $id = (integer) $id; + $cat_id = (integer) $cat_id; + + # 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 blog_id = '".$this->con->escape($this->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 category')); + } + } + + $cur = $this->con->openCursor($this->prefix.'post'); + + $cur->cat_id = ($cat_id ? $cat_id : null); + $cur->post_upddt = date('Y-m-d H:i:s'); + + $cur->update( + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' " + ); + $this->triggerBlog(); + } + + /** + Deletes a post. + + @param id integer Post ID + */ + public function delPost($id) + { + if (!$this->core->auth->check('delete,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to delete entries')); + } + + $id = (integer) $id; + + if (empty($id)) { + throw new Exception(__('No such entry ID')); + } + + #If user can only delete, 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 blog_id = '".$this->con->escape($this->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 delete this entry')); + } + } + + + $strReq = 'DELETE FROM '.$this->prefix.'post '. + 'WHERE post_id = '.$id.' '. + "AND blog_id = '".$this->con->escape($this->id)."' "; + + $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); + } + + } + + /** + 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 = array(); + $not = array(); + $queries = array(); + + 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 = array( + 0 => array(), # wanted categories + 1 => array() # 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)); + } + } + + 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); + 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',array( + '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 = array( + '{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') { + $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 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 = array(); + 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) Get comment with given ID + - 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=array(),$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'] !== '') { + $strReq .= 'AND comment_id = '.(integer) $params['comment_id'].' '; + } + + 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'])) { + $strReq .= "AND comment_ip = '".$this->con->escape($params['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,array(&$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(array('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) + { + if (!$this->core->auth->check('publish,contentadmin',$this->id)) { + throw new Exception(__("You are not allowed to change this comment's status")); + } + + $cur = $this->con->openCursor($this->prefix.'comment'); + $cur->comment_status = (integer) $status; + $this->updComment($id,$cur); + } + + /** + Delete a comment + + @param id integer Comment ID + */ + public function delComment($id) + { + if (!$this->core->auth->check('delete,contentadmin',$this->id)) { + throw new Exception(__('You are not allowed to delete comments')); + } + + $id = (integer) $id; + + if (empty($id)) { + throw new Exception(__('No such comment ID')); + } + + #If user can only delete, we need to check the post's owner + if (!$this->core->auth->check('contentadmin',$this->id)) + { + $strReq = 'SELECT P.post_id '. + 'FROM '.$this->prefix.'post P, '.$this->prefix.'comment C '. + 'WHERE P.post_id = C.post_id '. + "AND P.blog_id = '".$this->con->escape($this->id)."' ". + 'AND comment_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 delete this comment')); + } + } + + $strReq = 'DELETE FROM '.$this->prefix.'comment '. + 'WHERE comment_id = '.$id.' '; + + $this->triggerComment($id,true); + $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?)://|',$cur->comment_site)) { + $cur->comment_site = 'http://'.$cur->comment_site; + } + } + + 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)); + } + } + //@} +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.categories.php b/v2/dotclear/inc/core/class.dc.categories.php new file mode 100644 index 0000000..1dacf94 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.categories.php @@ -0,0 +1,483 @@ +core =& $core; + $this->con =& $core->con; + $this->blog_id = $core->blog->id; + $this->table = $core->prefix.'category'; + $this->add_condition = array('blog_id' => "'".$this->con->escape($this->blog_id)."'"); + } + + public function getChildren($start=0,$id=null,$sort='asc',$fields=array()) + { + $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields); + return parent::getChildren($start,$id,$sort,$fields); + } + + public function getParents($id,$fields=array()) + { + $fields = array_merge(array('cat_title','cat_url','cat_desc'),$fields); + return parent::getParents($id,$fields); + } + + public function getParent($id,$fields=array()) + { + $fields = array_merge(array('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 = array(); + + protected $parents; + + public function __construct($con) + { + $this->con =& $con; + } + + public function getChildren($start=0,$id=null,$sort='asc',$fields=array()) + { + $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=array()) + { + $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=array()) + { + $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 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 = array(); + foreach ($this->add_condition as $c => $n) { + $w[] = $prefix.$c.' = '.$n; + } + return ' '.$start.' '.implode(' AND ',$w).' '; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.core.php b/v2/dotclear/inc/core/class.dc.core.php new file mode 100644 index 0000000..d7d5acf --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.core.php @@ -0,0 +1,1467 @@ +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 + + private $versions = null; + private $formaters = array(); + private $behaviors = array(); + private $post_types = array(); + + /** + 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) + { + $this->con = dbLayer::init($driver,$host,$db,$user,$password,$persist); + + # define weak_locks for mysql + if ($this->con instanceof mysqlConnection) { + mysqlConnection::$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; + + $this->error = new dcError(); + $this->auth = $this->authInstance(); + $this->session = new sessionDB($this->con,$this->prefix.'session',DC_SESSION_NAME,'',null,DC_ADMIN_SSL); + $this->url = new dcUrlHandlers(); + + $this->plugins = new dcModules($this); + + $this->rest = new dcRestServer($this); + + $this->meta = new dcMeta($this); + + $this->log = new dcLog($this); + + $this->addFormater('xhtml', create_function('$s','return $s;')); + $this->addFormater('wiki', array($this,'wikiTransform')); + } + + 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 array( + 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 crypt::hmac(DC_MASTER_KEY,session_id()); + } + + public function checkNonce($secret) + { + if (!preg_match('/^([0-9a-f]{40,})$/i',$secret)) { + return false; + } + + return $secret == crypt::hmac(DC_MASTER_KEY,session_id()); + } + + public function formNonce() + { + if (!session_id()) { + return; + } + + return form::hidden(array('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 name string Formater name + @param func callback Function to use, must be a valid and callable callback + */ + public function addFormater($name,$func) + { + if (is_callable($func)) { + $this->formaters[$name] = $func; + } + } + + /** + Returns formaters list. + + @return array An array of formaters names in values. + */ + public function getFormaters() + { + return array_keys($this->formaters); + } + + /** + 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) + { + if (isset($this->formaters[$name])) { + return call_user_func($this->formaters[$name],$str); + } + + return $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 null; + + if ($behavior == '') { + return $this->behaviors; + } elseif (isset($this->behaviors[$behavior])) { + return $this->behaviors[$behavior]; + } + + return array(); + } + + /** + 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) + { + if (isset($this->behaviors[$behavior])) + { + $args = func_get_args(); + array_shift($args); + + $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) + { + $this->post_types[$type] = array( + 'admin_url' => $admin_url, + 'public_url' => $public_url + ); + } + + 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 null; + } + } + + /** + 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=array(),$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) { + $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 = array(); + + while ($rs->fetch()) + { + $res[$rs->blog_id] = array( + '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 = crypt::hmac(DC_MASTER_KEY,$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 array( + 'edit_size' => 24, + 'enable_wysiwyg' => true, + '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, 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, NULL AS permissions ". + 'FROM '.$this->prefix.'user U '. + 'WHERE user_super = 1 '; + } + + $rs = $this->con->select($strReq); + + $res = array(); + + while ($rs->fetch()) + { + $res[$rs->user_id] = array( + 'name' => $rs->user_name, + 'firstname' => $rs->user_firstname, + 'displayname' => $rs->user_displayname, + '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(array('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=array(),$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 (!empty($params['blog_id'])) { + $where .= "AND B.blog_id = '".$this->con->escape($params['blog_id'])."' "; + } + + if (!empty($params['q'])) { + $params['q'] = 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)) { + throw new Exception(__('Blog ID must contain at least 2 characters using letters, numbers or symbols.')); + } + + if ($cur->blog_name !== null && $cur->blog_name == '') { + throw new Exception(__('No blog name')); + } + + if ($cur->blog_url !== null && $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(array( + 'active_title' => 1, + 'active_setext_title' => 0, + 'active_hr' => 1, + 'active_lists' => 1, + 'active_quote' => 1, + 'active_pre' => 1, + 'active_empty' => 1, + 'active_auto_br' => 0, + 'active_auto_urls' => 0, + '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, + 'parse_pre' => 1, + 'active_fr_syntax' => 0, + 'first_title_level' => 3, + 'note_prefix' => 'wiki-footnote', + 'note_str' => '

        Notes

        %s
        ' + )); + + $this->wiki2xhtml->registerFunction('url:post',array($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(array( + 'active_title' => 0, + 'active_setext_title' => 0, + 'active_hr' => 0, + 'active_lists' => 0, + 'active_quote' => 0, + 'active_pre' => 0, + 'active_empty' => 0, + 'active_auto_br' => 1, + 'active_auto_urls' => 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_footnotes' => 0, + 'active_wikiwords' => 0, + 'active_macros' => 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(array( + 'active_title' => 0, + 'active_setext_title' => 0, + 'active_hr' => 0, + 'active_lists' => 1, + 'active_quote' => 0, + 'active_pre' => 1, + '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_wikiwords' => 0, + 'active_macros' => 0, + '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 array(); + } + + $post_id = abs((integer) substr($url,5)); + if (!$post_id) { + return array(); + } + + $post = $this->blog->getPosts(array('post_id'=>$post_id)); + if ($post->isEmpty()) { + return array(); + } + + $res = array('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 = array( + array('allow_comments','boolean',true, + 'Allow comments on blog'), + array('allow_trackbacks','boolean',true, + 'Allow trackbacks on blog'), + array('blog_timezone','string','Europe/London', + 'Blog timezone'), + array('comments_nofollow','boolean',true, + 'Add rel="nofollow" to comments URLs'), + array('comments_pub','boolean',true, + 'Publish comments immediately'), + array('comments_ttl','integer',0, + 'Number of days to keep comments open (0 means no ttl)'), + array('copyright_notice','string','','Copyright notice (simple text)'), + array('date_format','string','%A, %B %e %Y', + 'Date format. See PHP strftime function for patterns'), + array('editor','string','', + 'Person responsible of the content'), + array('enable_html_filter','boolean',0, + 'Enable HTML filter'), + array('enable_xmlrpc','boolean',0, + 'Enable XML/RPC interface'), + array('lang','string','en', + 'Default blog language'), + array('media_exclusion','string','/\.php$/i', + 'File name exclusion pattern in media manager. (PCRE value)'), + array('media_img_m_size','integer',448, + 'Image medium size in media manager'), + array('media_img_s_size','integer',240, + 'Image small size in media manager'), + array('media_img_t_size','integer',100, + 'Image thumbnail size in media manager'), + array('media_img_title_pattern','string','Title ;; Date(%b %Y) ;; separator(, )', + 'Pattern to set image title when you insert it in a post'), + array('nb_post_per_page','integer',20, + 'Number of entries on home page and category pages'), + array('nb_post_per_feed','integer',20, + 'Number of entries on feeds'), + array('nb_comment_per_feed','integer',20, + 'Number of comments on feeds'), + array('post_url_format','string','{y}/{m}/{d}/{t}', + 'Post URL format. {y}: year, {m}: month, {d}: day, {id}: post id, {t}: entry title'), + array('public_path','string','public', + 'Path to public directory, begins with a / for a full system path'), + array('public_url','string','/public', + 'URL to public directory'), + array('robots_policy','string','INDEX,FOLLOW', + 'Search engines robots policy'), + array('short_feed_items','boolean',false, + 'Display short feed items'), + array('theme','string','default', + 'Blog theme'), + array('themes_path','string','themes', + 'Themes root path'), + array('themes_url','string','/themes', + 'Themes root URL'), + array('time_format','string','%H:%M', + 'Time format. See PHP strftime function for patterns'), + array('tpl_allow_php','boolean',false, + 'Allow PHP code in templates'), + array('tpl_use_cache','boolean',true, + 'Use template caching'), + array('trackbacks_pub','boolean',true, + 'Publish trackbacks immediately'), + array('trackbacks_ttl','integer',0, + 'Number of days to keep trackbacks open (0 means no ttl)'), + array('url_scan','string','query_string', + 'URL handle mode (path_info or query_string)'), + array('use_smilies','boolean',false, + 'Show smilies on entries and comments'), + array('wiki_comments','boolean',false, + 'Allow commenters to use a subset of wiki syntax') + ); + } + + $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 null; + } + 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 null; + } + 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'); + } + } + //@} +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.error.php b/v2/dotclear/inc/core/class.dc.error.php new file mode 100644 index 0000000..f72c251 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.error.php @@ -0,0 +1,133 @@ +\n%s\n"; + /** @var string HTML error item pattern */ + protected $html_item = "
      • %s
      • \n"; + + /** + * Object constructor. + */ + public function __construct() + { + $this->code = 0; + $this->msg = ''; + } + + /** + * 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 = array(); + } + + /** + * 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) + { + $this->html_list = $list; + $this->html_item = $item; + } + + /** + * Returns errors stack as HTML. + * + * @return string + */ + public function toHTML() + { + $res = ''; + + if ($this->flag) + { + foreach ($this->errors as $msg) + { + $res .= sprintf($this->html_item,$msg); + } + + $res = sprintf($this->html_list,$res); + } + + return $res; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.log.php b/v2/dotclear/inc/core/class.dc.log.php new file mode 100644 index 0000000..9c93d09 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.log.php @@ -0,0 +1,204 @@ +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 = array(),$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; + } +} + +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.media.php b/v2/dotclear/inc/core/class.dc.media.php new file mode 100644 index 0000000..ded3834 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.media.php @@ -0,0 +1,1093 @@ +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 Array of callbacks + + public $thumb_tp = '%s/.%s_%s.jpg'; ///< string Thumbnail file pattern + + /** + array Tubmnail sizes: + - m: medium image + - s: small image + - t: thumbnail image + - sq: square image + */ + public $thumb_sizes = array( + 'm' => array(448,'ratio','medium'), + 's' => array(240,'ratio','small'), + 't' => array(100,'ratio','thumbnail'), + 'sq' => array(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)) { + throw new Exception(sprintf(__('Directory %s does not exist.'),$root)); + } + + $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',array($this,'imageThumbCreate')); + $this->addFileHandler('image/png','create',array($this,'imageThumbCreate')); + $this->addFileHandler('image/gif','create',array($this,'imageThumbCreate')); + + $this->addFileHandler('image/png','update',array($this,'imageThumbUpdate')); + $this->addFileHandler('image/jpeg','update',array($this,'imageThumbUpdate')); + $this->addFileHandler('image/gif','update',array($this,'imageThumbUpdate')); + + $this->addFileHandler('image/png','remove',array($this,'imageThumbRemove')); + $this->addFileHandler('image/jpeg','remove',array($this,'imageThumbRemove')); + $this->addFileHandler('image/gif','remove',array($this,'imageThumbRemove')); + + $this->addFileHandler('image/jpeg','create',array($this,'imageMetaCreate')); + + $this->addFileHandler('image/jpeg','recreate',array($this,'imageThumbCreate')); + $this->addFileHandler('image/png','recreate',array($this,'imageThumbCreate')); + $this->addFileHandler('image/gif','recreate',array($this,'imageThumbCreate')); + + $this->addFileHandler('image/jpeg','recreate',array($this,'imageThumbCreate')); + $this->addFileHandler('image/png','recreate',array($this,'imageThumbCreate')); + $this->addFileHandler('image/gif','recreate',array($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) + { + if (!empty($this->file_handler[$type][$event])) + { + $args = func_get_args(); + array_shift($args); + array_shift($args); + + 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 null; } + + 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 null; + } + + $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 = array(); + $p = path::info($f->relname); + $thumb = sprintf($this->thumb_tp,$this->root.'/'.$p['dirname'],$p['base'],'%s'); + $thumb_url = sprintf($this->thumb_tp,$this->root_url.$p['dirname'],$p['base'],'%s'); + + # Cleaner URLs + $thumb_url = preg_replace('#\./#','/',$thumb_url); + $thumb_url = preg_replace('#(?thumb_sizes as $suffix => $s) { + if (file_exists(sprintf($thumb,$suffix))) { + $f->media_thumb[$suffix] = sprintf($thumb_url,$suffix); + } + } + + if (isset($f->media_thumb['sq']) && $f->media_type == 'image') { + $f->media_icon = $f->media_thumb['sq']; + } + + return $f; + } + + return null; + } + + + public function setFileSort($type='name') + { + if (in_array($type,array('name-asc','name-desc','date-asc','date-desc'))) { + $this->file_sort = $type; + } + } + + protected function sortFileHandler($a,$b) + { + 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. + + @param type string Media type filter + */ + public function getDir($type=null) + { + if ($type) { + $this->type = $type; + } + + $media_dir = $this->relpwd ? $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 = array(); + $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 = array(); + + 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,'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); + } + } + } + } + usort($this->dir['files'],array($this,'sortFileHandler')); + } + + /** + 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); + } + + /** + 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($post_id,$media_id=null) + { + $params = array( + 'post_id' => $post_id, + 'media_path' => $this->path + ); + if ($media_id) { + $params['media_id'] = (integer) $media_id; + } + $rs = $this->postmedia->getPostMedia($params); + + $res = array(); + + 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) + { + $this->postmedia->addPostMedia($post_id,$media_id); + } + + /** + @deprecated since version 2.4 + @see dcPostMedia::removePostMedia + */ + public function removePostMedia($post_id,$media_id) + { + $this->postmedia->removePostMedia($post_id,$media_id,"attachment"); + } + + /** + 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 ? $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 = array(); + + 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; + + $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); + } + + /** + 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); + $zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.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(); + 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|\.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); + $thumb = sprintf($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])) + { + $img->resize($s[0],$s[0],$s[1]); + $img->output('jpeg',$thumb_file,80); + } + } + $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); + $thumb_old = sprintf($this->thumb_tp,$p['dirname'],$p['base'],'%s'); + + $p = path::info($newFile->relname); + $thumb_new = sprintf($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) {} + } + } + } + + protected function imageThumbRemove($f) + { + $p = path::info($f); + $thumb = sprintf($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 MP3 player + + @param url string MP3 URL to play + @param player string Player URL + @param args array Player parameters + @return string + */ + public static function mp3player($url,$player=null,$args=null) + { + if (!$player) { + $player = 'player_mp3.swf'; + } + + if (!is_array($args)) + { + $args = array( + 'showvolume' => 1, + 'loadingcolor' => 'ff9900', + 'bgcolor1' => 'eeeeee', + 'bgcolor2' => 'cccccc', + 'buttoncolor' => '0066cc', + 'buttonovercolor' => 'ff9900', + 'slidercolor1' => 'cccccc', + 'slidercolor2' => '999999', + 'sliderovercolor' => '0066cc' + ); + } + + $args['mp3'] = $url; + + if (empty($args['width'])) { + $args['width'] = 200; + } + if (empty($args['height'])) { + $args['height'] = 20; + } + + $vars = array(); + foreach ($args as $k => $v) { + $vars[] = $k.'='.$v; + } + + return + ''. + ''. + ''. + ''. + __('Embedded Audio Player'). + ''; + } + + public static function flvplayer($url,$player=null,$args=null) + { + if (!$player) { + $player = 'player_flv.swf'; + } + + if (!is_array($args)) + { + $args = array( + 'margin' => 1, + 'showvolume' => 1, + 'showtime' => 1, + 'showfullscreen' => 1, + 'buttonovercolor' => 'ff9900', + 'slidercolor1' => 'cccccc', + 'slidercolor2' => '999999', + 'sliderovercolor' => '0066cc' + ); + } + + $args['flv'] = $url; + + if (empty($args['width'])) { + $args['width'] = 400; + } + if (empty($args['height'])) { + $args['height'] = 300; + } + + $vars = array(); + foreach ($args as $k => $v) { + $vars[] = $k.'='.$v; + } + + return + ''. + ''. + ''. + ''. + ''. + __('Embedded Video Player'). + ''; + } +} +?> diff --git a/v2/dotclear/inc/core/class.dc.meta.php b/v2/dotclear/inc/core/class.dc.meta.php new file mode 100644 index 0000000..9108d44 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.meta.php @@ -0,0 +1,615 @@ +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 = array(); + 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 array(); + } + + 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 = array(); + + if (isset($meta[$type])) + { + foreach ($meta[$type] as $v) + { + $data[] = array( + '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 = array(); + 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=array(),$count_only=false) + { + if (!isset($params['meta_id'])) { + return null; + } + + $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=array(),$count_only=false) + { + if (!isset($params['meta_id'])) { + return null; + } + + $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 = array(); + + 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=array(), $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 '; + } + + $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 = array(); + 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',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 = array(); + + $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 array(); + + $ids = array(); + 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/v2/dotclear/inc/core/class.dc.modules.php b/v2/dotclear/inc/core/class.dc.modules.php new file mode 100644 index 0000000..efe87e2 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.modules.php @@ -0,0 +1,563 @@ +dcCore dcCore instance + + /** + Object constructor. + + @param core dcCore dcCore instance + */ + public function __construct($core) + { + $this->core =& $core; + } + + /** + 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; + + 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; + require $full_entry.'/_define.php'; + $this->id = null; + $this->mroot = null; + } + else + { + $this->disabled[$entry] = array( + 'root' => $full_entry, + 'root_writable' => is_writable($full_entry) + ); + } + } + } + $d->close(); + } + + # Sort plugins + uasort($this->modules,array($this,'sortModules')); + + # Load translation, _prepend and ns_file + foreach ($this->modules as $id => $m) + { + if (file_exists($m['root'].'/_prepend.php')) + { + $r = require $m['root'].'/_prepend.php'; + + # If _prepend.php file returns null (ie. it has a void return statement) + if (is_null($r)) { + continue; + } + unset($r); + } + + $this->loadModuleL10N($id,$lang,'main'); + if ($ns == 'admin') { + $this->loadModuleL10Nresources($id,$lang); + } + $this->loadNsFile($id,$ns); + } + } + + public function requireDefine($dir,$id) + { + if (file_exists($dir.'/_define.php')) { + $this->id = $id; + require $dir.'/_define.php'; + $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) + */ + public function registerModule($name,$desc,$author,$version, $properties = array()) + { + if (!is_array($properties)) { + //Fallback to legacy registerModule parameters + $args = func_get_args(); + $properties = array(); + if (isset($args[4])) { + $properties['permissions']=$args[4]; + } + if (isset($args[5])) { + $properties['priority']= (integer)$args[5]; + } + } + $properties = array_merge( + array( + 'permissions' => null, + 'priority' => 1000 + ), $properties + ); + $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; + } + } + + 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, + array( + '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( + __('%s: in [%s] and [%s]'), + ''.$name.'', + ''.$path1.'', + ''.$path2.'' + ); + } + } + } + + public function resetModulesList() + { + $this->modules = array(); + $this->modules_names = array(); + } + + public static function installPackage($zip_file,dcModules &$modules) + { + $zip = new fileUnzip($zip_file); + $zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.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)) + { + # 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) && $new_modules[$id]['version'] != $cur_module['version']) + { + # 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". (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 = array('success'=>array(),'failure'=>array()); + 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) + { + try { + $i = $this->loadModuleFile($this->modules[$id]['root'].'/_install.php'); + if ($i === true) { + return true; + } + } catch (Exception $e) { + $msg = $e->getMessage(); + return false; + } + + return null; + } + + 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) + { + 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($________) + { + 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}; + } + } + + return require $________; + } + + private function sortModules($a,$b) + { + if ($a['priority'] == $b['priority']) { + return strcasecmp($a['name'],$b['name']); + } + + return ($a['priority'] < $b['priority']) ? -1 : 1; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.namespace.php b/v2/dotclear/inc/core/class.dc.namespace.php new file mode 100644 index 0000000..cb81f6b --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.namespace.php @@ -0,0 +1,308 @@ +connection Database connection object + protected $table; ///< string Settings table name + protected $blog_id; ///< string Blog ID + + protected $global_settings = array(); ///< array Global settings array + protected $local_settings = array(); ///< array Local settings array + protected $settings = array(); ///< 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 == '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] = array( + '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; + } + + private 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 null; + } + + /** + 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 { + $type = 'string'; + } + } + elseif ($type != 'boolean' && $type != 'integer' && $type != 'float') + { + $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']; + } + } + + settype($value,$type); + + $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(); + } + } + + /** + Removes an existing setting. 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); + } + + /** + Returns $settings property content. + + @return array + */ + public function dumpSettings() + { + return $this->settings; + } + + /** + Returns $global_settings property content. + + @return array + */ + public function dumpGlobalSettings() + { + return $this->global_settings; + } + +} +?> diff --git a/v2/dotclear/inc/core/class.dc.postmedia.php b/v2/dotclear/inc/core/class.dc.postmedia.php new file mode 100644 index 0000000..2d55a2e --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.postmedia.php @@ -0,0 +1,172 @@ +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=array()) + { + $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']; + } + //echo $strReq; exit; + $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 + */ + public function addPostMedia($post_id,$media_id,$link_type='attachment') + { + $post_id = (integer) $post_id; + $media_id = (integer) $media_id; + + $f = $this->getPostMedia(array('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 + */ + 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 = array(); + + while ($rs->fetch()) { + $f = $this->fileRecord($rs); + if ($f !== null) { + $res[] = $f; + } + } + + return $res; + } + +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.prefs.php b/v2/dotclear/inc/core/class.dc.prefs.php new file mode 100644 index 0000000..f38af35 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.prefs.php @@ -0,0 +1,134 @@ +connection Database connection object + protected $table; ///< string Prefs table name + protected $user_id; ///< string User ID + + protected $workspaces = array(); ///< 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]; + } + + /** + 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 null; + } + + /** + 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; + } + +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.rest.php b/v2/dotclear/inc/core/class.dc.rest.php new file mode 100644 index 0000000..e9334f9 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.rest.php @@ -0,0 +1,52 @@ +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); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.rs.extensions.php b/v2/dotclear/inc/core/class.dc.rs.extensions.php new file mode 100644 index 0000000..ad87acf --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.rs.extensions.php @@ -0,0 +1,870 @@ +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 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,array('@'=>'%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); + + $url = parse_url($rs->core->blog->url); + $date_part = date('Y-m-d',strtotime($rs->post_creadt)); + + return 'tag:'.$url['host'].','.$date_part.':'.$rs->post_id; + } + + /** + Returns trackback RDF information block in HTML comment. + + @param rs Invisible parameter + @return string + */ + public static function getTrackbackData($rs) + { + return + "\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) + { + 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.' '; + + $res = (integer) $rs->core->con->select($strReq)->f(0); + $rs->_nb_media[$rs->index()] = $res; + return $res; + } + } +} + +/** +@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',array('self','noFollowURL'),$res); + } + + if ($absolute_urls) { + $res = html::absoluteURLs($res,$rs->getPostURL()); + } + + return $res; + } + + private static function noFollowURL($m) + { + if (preg_match('/rel="nofollow"/',$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'; + } + + $nofollow = ''; + if ($rs->core->blog->settings->system->comments_nofollow) { + $nofollow = ' rel="nofollow"'; + } + + return sprintf($res,html::escapeHTML($rs->comment_author),html::escapeHTML($url),$nofollow); + } + + /** + 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,array('@'=>'%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); + + $url = parse_url($rs->core->blog->url); + $date_part = date('Y-m-d',strtotime($rs->comment_dt)); + + return 'tag:'.$url['host'].','.$date_part.':'.$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 +{ + /** + 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 null; + } + + /** + 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 array(); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.settings.php b/v2/dotclear/inc/core/class.dc.settings.php new file mode 100644 index 0000000..1924085 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.settings.php @@ -0,0 +1,384 @@ +connection Database connection object + protected $table; ///< string Settings table name + protected $blog_id; ///< string Blog ID + + protected $namespaces = array(); ///< 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->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($GLOBALS['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($GLOBALS['core'], $this->blog_id, $ns); + } + return $this->namespaces[$ns]; + } + + /** + 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 = array( + '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($GLOBALS['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 null; + } + + /** + @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 = array(); + // 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 = array(); + // 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=array()) + { + $strReq = "SELECT * from ".$this->table." "; + $where = array(); + 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); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.themes.php b/v2/dotclear/inc/core/class.dc.themes.php new file mode 100644 index 0000000..fda0a05 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.themes.php @@ -0,0 +1,102 @@ +$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) + */ + public function registerModule($name,$desc,$author,$version,$properties = array()) + { + if (!is_array($properties)) { + //Fallback to legacy registerModule parameters + $args = func_get_args(); + $properties = array(); + if (isset($args[4])) { + $properties['parent']=$args[4]; + } + if (isset($args[5])) { + $properties['priority']= (integer)$args[5]; + } + } + $properties = array_merge( + array( + 'parent' => null, + 'priority' => 1000, + 'standalone_config' => false + ), $properties + ); + if ($this->id) { + $this->modules[$this->id] = array_merge( + $properties, + array( + 'root' => $this->mroot, + 'name' => $name, + 'desc' => $desc, + 'author' => $author, + 'version' => $version, + 'root_writable' => is_writable($this->mroot) + ) + ); + } + } + + /** + 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; + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.trackback.php b/v2/dotclear/inc/core/class.dc.trackback.php new file mode 100644 index 0000000..aaf3f64 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.trackback.php @@ -0,0 +1,404 @@ +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 trackbacks + //@{ + /** + 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)); + } + + $data = array( + '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] : ''; + + 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 trackbacks + //@{ + /** + Receives a trackback and insert it as a comment of given post. + + @param post_id integer Post ID + */ + public function receive($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(array('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.'; + } + } + + if (!$err) + { + $charset = self::getCharsetFromRequest(); + + if (!$charset) { + $charset = mb_detect_encoding($title.' '.$excerpt.' '.$blog_name, + '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'); + } + + 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); + + $url = trim(html::clean($url)); + + if (!$blog_name) { + $blog_name = 'Anonymous blog'; + } + + $comment = + "\n". + '

        '.($title ? $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(); + + try + { + # --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); + } + } + catch (Exception $e) + { + $err = 1; + $msg = 'Something went wrong : '.$e->getMessage(); + } + } + + + $debug_trace = + " \n". + ' '.$title."\n". + ' '.$excerpt."\n". + ' '.$url."\n". + ' '.$blog_name."\n". + ' '.$charset."\n". + ' '.$comment."\n". + " \n"; + + $resp = + ''."\n". + "\n". + ' '.(integer) $err."\n"; + + if ($msg) { + $resp .= ' '.$msg."\n"; + } + + if (!empty($_POST['__debug'])) { + $resp .= $debug_trace; + } + + echo $resp.""; + } + //@} + + private static function initHttp($url,&$path) + { + $client = netHttp::initClient($url,$path); + $client->setTimeout(5); + $client->setUserAgent('Dotclear - http://www.dotclear.org/'); + $client->useGzip(false); + $client->setPersistReferers(false); + + return $client; + } + + private static function getCharsetFromRequest() + { + if (isset($_SERVER['CONTENT_TYPE'])) + { + if (preg_match('|charset=([a-zA-Z0-9-]+)|',$_SERVER['CONTENT_TYPE'],$m)) { + return $m[1]; + } + } + + return null; + } + + /// @name Trackbacks auto discovery + //@{ + /** + Returns an array containing all discovered trackbacks URLs in + $text. + + @param text string Input text + @return array + */ + public function discover($text) + { + $res = array(); + + foreach ($this->getTextLinks($text) as $link) + { + if (($url = $this->getPingURL($link)) !== null) { + $res[] = $url; + } + } + + return $res; + } + //@} + + private function getTextLinks($text) + { + $res = array(); + + # href attribute on "a" tags + if (preg_match_all('/]+)>/ms', $text, $match, PREG_SET_ORDER)) + { + for ($i = 0; $i]+)>/ms', $text, $match, PREG_SET_ORDER)) + { + for ($i = 0; $iget($path); + $page_content = $http->getContent(); + } + catch (Exception $e) + { + return false; + } + + $pattern_rdf = + '/.*?'. + ''. + '.*?<\/rdf:RDF>'. + '/msi'; + + preg_match_all($pattern_rdf,$page_content,$rdf_all,PREG_SET_ORDER); + + for ($i=0; $i \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.update.php b/v2/dotclear/inc/core/class.dc.update.php new file mode 100644 index 0000000..e2b5975 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.update.php @@ -0,0 +1,478 @@ + null, + 'href' => null, + 'checksum' => null, + 'info' => null, + 'notify' => true + ); + + protected $cache_ttl = '-6 hours'; + protected $forced_files = array(); + + /** + * 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 + * @return string Latest version if available + */ + public function check($version) + { + $this->getVersionInfo(); + $v = $this->getVersion(); + if ($v && version_compare($version,$v,'<')) { + return $v; + } + + return false; + } + + public function getVersionInfo() + { + # Check cached file + if (is_readable($this->cache_file) && filemtime($this->cache_file) > strtotime($this->cache_ttl)) + { + $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 = ''; + $client = netHttp::initClient($this->url,$path); + if ($client !== false) { + $client->setTimeout(4); + $client->setUserAgent($_SERVER['HTTP_USER_AGENT']); + $client->get($path); + + $this->readVersion($client->getContent()); + } + } + catch (Exception $e) {} + + # 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 getNotify() + { + return $this->version_info['notify']; + } + + public function getForcedFiles() + { + return $this->forced_files; + } + + public function setForcedFiles() + { + $this->forced_files = func_get_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 + { + $client = netHttp::initClient($url,$path); + $client->setTimeout(4); + $client->setUserAgent($_SERVER['HTTP_USER_AGENT']); + $client->useGzip(false); + $client->setPersistReferers(false); + $client->setOutput($dest); + $client->get($path); + + if ($client->getStatus() != 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 = array(); + + 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 = array(); + $not_writable = array(); + + 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, array($this,'parseLine'),1); + array_walk($cur_path,array($this,'parseLine'),2); + array_walk($new_md5, array($this,'parseLine'),1); + array_walk($new_path,array($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; + } + } + 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 = array(); + + 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; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.utils.php b/v2/dotclear/inc/core/class.dc.utils.php new file mode 100644 index 0000000..cfb75c4 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.utils.php @@ -0,0 +1,53 @@ +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; + } +} + +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.workspace.php b/v2/dotclear/inc/core/class.dc.workspace.php new file mode 100644 index 0000000..d5f0106 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.workspace.php @@ -0,0 +1,368 @@ +connection Database connection object + protected $table; ///< string Preferences table name + protected $user_id; ///< string User ID + + protected $global_prefs = array(); ///< array Global prefs array + protected $local_prefs = array(); ///< array Local prefs array + protected $prefs = array(); ///< 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 == '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] = array( + '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; + } + + 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 null; + } + + /** + 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 { + $type = 'string'; + } + } + elseif ($type != 'boolean' && $type != 'integer' && $type != 'float') + { + $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']; + } + } + + settype($value,$type); + + $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(); + } + } + + /** + 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 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(); + + $array = $global ? 'local' : 'global'; + $this->prefs = $this->{$array.'_prefs'}; + } + + /** + 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; + } + +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core/class.dc.xmlrpc.php b/v2/dotclear/inc/core/class.dc.xmlrpc.php new file mode 100644 index 0000000..70a7310 --- /dev/null +++ b/v2/dotclear/inc/core/class.dc.xmlrpc.php @@ -0,0 +1,1632 @@ +core =& $core; + $this->blog_id = $blog_id; + + # Blogger methods + $this->addCallback('blogger.newPost',array($this,'blogger_newPost'), + array('string','string','string','string','string','string','integer'), + 'New post'); + + $this->addCallback('blogger.editPost',array($this,'blogger_editPost'), + array('boolean','string','string','string','string','string','integer'), + 'Edit a post'); + + $this->addCallback('blogger.getPost',array($this,'blogger_getPost'), + array('struct','string','integer','string','string'), + 'Return a posts by ID'); + + $this->addCallback('blogger.deletePost',array($this,'blogger_deletePost'), + array('string','string','string','string','string','integer'), + 'Delete a post'); + + $this->addCallback('blogger.getRecentPosts',array($this,'blogger_getRecentPosts'), + array('array','string','string','string','string','integer'), + 'Return a list of recent posts'); + + $this->addCallback('blogger.getUsersBlogs',array($this,'blogger_getUserBlogs'), + array('struct','string','string','string'), + "Return user's blog"); + + $this->addCallback('blogger.getUserInfo',array($this,'blogger_getUserInfo'), + array('struct','string','string','string'), + 'Return User Info'); + + # Metaweblog methods + $this->addCallback('metaWeblog.newPost',array($this,'mw_newPost'), + array('string','string','string','string','struct','boolean'), + 'Creates a new post, and optionnaly publishes it.'); + + $this->addCallback('metaWeblog.editPost',array($this,'mw_editPost'), + array('boolean','string','string','string','struct','boolean'), + 'Updates information about an existing entry'); + + $this->addCallback('metaWeblog.getPost',array($this,'mw_getPost'), + array('struct','string','string','string'), + 'Returns information about a specific post'); + + $this->addCallback('metaWeblog.getRecentPosts',array($this,'mw_getRecentPosts'), + array('array','string','string','string','integer'), + 'List of most recent posts in the system'); + + $this->addCallback('metaWeblog.getCategories',array($this,'mw_getCategories'), + array('array','string','string','string'), + 'List of all categories defined in the weblog'); + + $this->addCallback('metaWeblog.newMediaObject',array($this,'mw_newMediaObject'), + array('struct','string','string','string','struct'), + 'Upload a file on the web server'); + + # MovableType methods + $this->addCallback('mt.getRecentPostTitles',array($this,'mt_getRecentPostTitles'), + array('array','string','string','string','integer'), + 'List of most recent posts in the system'); + + $this->addCallback('mt.getCategoryList',array($this,'mt_getCategoryList'), + array('array','string','string','string'), + 'List of all categories defined in the weblog'); + + $this->addCallback('mt.getPostCategories',array($this,'mt_getPostCategories'), + array('array','string','string','string'), + 'List of all categories to which the post is assigned'); + + $this->addCallback('mt.setPostCategories',array($this,'mt_setPostCategories'), + array('boolean','string','string','string','array'), + 'Sets the categories for a post'); + + $this->addCallback('mt.publishPost',array($this,'mt_publishPost'), + array('boolean','string','string','string'), + 'Retrieve pings list for a post'); + + $this->addCallback('mt.supportedMethods',array($this,'listMethods'), + array(),'Retrieve information about the XML-RPC methods supported by the server.'); + + $this->addCallback('mt.supportedTextFilters',array($this,'mt_supportedTextFilters'), + array(),'Retrieve information about supported text filters.'); + + # WordPress methods + $this->addCallback('wp.getUsersBlogs',array($this,'wp_getUsersBlogs'), + array('array','string','string'), + 'Retrieve the blogs of the user.'); + + $this->addCallback('wp.getPage',array($this,'wp_getPage'), + array('struct','integer','integer','string','string'), + 'Get the page identified by the page ID.'); + + $this->addCallback('wp.getPages',array($this,'wp_getPages'), + array('array','integer','string','string','integer'), + 'Get an array of all the pages on a blog.'); + + $this->addCallback('wp.newPage',array($this,'wp_newPage'), + array('integer','integer','string','string','struct','boolean'), + 'Create a new page.'); + + $this->addCallback('wp.deletePage',array($this,'wp_deletePage'), + array('boolean','integer','string','string','integer'), + 'Removes a page from the blog.'); + + $this->addCallback('wp.editPage',array($this,'wp_editPage'), + array('boolean','integer','integer','string','string','struct','boolean'), + 'Make changes to a blog page.'); + + $this->addCallback('wp.getPageList',array($this,'wp_getPageList'), + array('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',array($this,'wp_getAuthors'), + array('array','integer','string','string'), + 'Get an array of users for the blog.'); + + $this->addCallback('wp.getCategories',array($this,'wp_getCategories'), + array('array','integer','string','string'), + 'Get an array of available categories on a blog.'); + + $this->addCallback('wp.getTags',array($this,'wp_getTags'), + array('array','integer','string','string'), + 'Get list of all tags for the blog.'); + + $this->addCallback('wp.newCategory',array($this,'wp_newCategory'), + array('integer','integer','string','string','struct'), + 'Create a new category.'); + + $this->addCallback('wp.deleteCategory',array($this,'wp_deleteCategory'), + array('boolean','integer','string','string','integer'), + 'Delete a category with a given ID.'); + + $this->addCallback('wp.suggestCategories',array($this,'wp_suggestCategories'), + array('array','integer','string','string','string','integer'), + 'Get an array of categories that start with a given string.'); + + $this->addCallback('wp.uploadFile',array($this,'wp_uploadFile'), + array('struct','integer','string','string','struct'), + 'Upload a file'); + + $this->addCallback('wp.getPostStatusList',array($this,'wp_getPostStatusList'), + array('array','integer','string','string'), + 'Retrieve all of the post statuses.'); + + $this->addCallback('wp.getPageStatusList',array($this,'wp_getPageStatusList'), + array('array','integer','string','string'), + 'Retrieve all of the pages statuses.'); + + $this->addCallback('wp.getPageTemplates',array($this,'wp_getPageTemplates'), + array('struct','integer','string','string'), + 'Retrieve page templates.'); + + $this->addCallback('wp.getOptions',array($this,'wp_getOptions'), + array('struct','integer','string','string','array'), + 'Retrieve blog options'); + + $this->addCallback('wp.setOptions',array($this,'wp_setOptions'), + array('struct','integer','string','string','struct'), + 'Update blog options'); + + $this->addCallback('wp.getComment',array($this,'wp_getComment'), + array('struct','integer','string','string','integer'), + "Gets a comment, given it's comment ID."); + + $this->addCallback('wp.getCommentCount',array($this,'wp_getCommentCount'), + array('array','integer','string','string','integer'), + 'Retrieve comment count.'); + + $this->addCallback('wp.getComments',array($this,'wp_getComments'), + array('array','integer','string','string','struct'), + 'Gets a set of comments for a given post.'); + + $this->addCallback('wp.deleteComment',array($this,'wp_deleteComment'), + array('boolean','integer','string','string','integer'), + 'Delete a comment with given ID.'); + + $this->addCallback('wp.editComment',array($this,'wp_editComment'), + array('boolean','integer','string','string','integer','struct'), + 'Edit a comment with given ID.'); + + $this->addCallback('wp.newComment',array($this,'wp_newComment'), + array('integer','integer','string','string','integer','struct'), + 'Create a new comment for a given post ID.'); + + $this->addCallback('wp.getCommentStatusList',array($this,'wp_getCommentStatusList'), + array('array','integer','string','string'), + 'Retrieve all of the comment statuses.'); + } + + 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,array($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 ($this->core->auth->userID() == $user_id) { + return true; + } + + if ($this->core->auth->checkUser($user_id,$pwd) !== true) { + throw new Exception('Login error'); + } + + return true; + } + + private function setBlog() + { + 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 (!$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(array( + '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(array('cat_url' => $cat_url)); + + return $rs->isEmpty() ? null : $rs->cat_id; + } + + /* Generic methods + --------------------------------------------------- */ + private function newPost($blog_id,$user,$pwd,$content,$struct=array(),$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=array(),$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'] = array($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,array(&$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 = array(); + $params['limit'] = $nb_post; + + $posts = $this->core->blog->getPosts($params); + + $res = array(); + while ($posts->fetch()) + { + $tres = array(); + + $tres['dateCreated'] = new xmlrpcDate($posts->getTS()); + $tres['userid'] = $posts->user_id; + $tres['postid'] = $posts->post_id; + + if ($posts->cat_id) { + $tres['categories'] = array($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,array(&$tres)); + + $res[] = $tres; + } + + return $res; + } + + private function getUserBlogs($user,$pwd) + { + $this->setUser($user,$pwd); + $this->setBlog(); + + return array(array( + 'url' => $this->core->blog->url, + 'blogid' => '1', + 'blogName' => $this->core->blog->name + )); + } + + private function getUserInfo($user,$pwd) + { + $this->setUser($user,$pwd); + + return array( + '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 = array(); + + $l = $rs->level; + $stack = array('',$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[] = array( + '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 array( + array( + '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 array( + 'file' => $file_name, + 'url' => $f->file_url, + 'type' => files::getMimeType($file_name) + ); + } + + private function translateWpStatus($s) + { + $status = array( + '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 = array( + '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=array()) + { + $timezone = 0; + if ($this->core->blog->settings->system->blog_timezone) { + $timezone = dt::getTimeOffset($this->core->blog->settings->system->blog_timezone)/3600; + } + + $res = array ( + 'software_name' => array ( + 'desc' => 'Software Name', + 'readonly' => true, + 'value' => 'Dotclear' + ), + 'software_version' => array ( + 'desc' => 'Software Version', + 'readonly' => true, + 'value' => DC_VERSION + ), + 'blog_url' => array ( + 'desc' => 'Blog URL', + 'readonly' => true, + 'value' => $this->core->blog->url + ), + 'time_zone' => array ( + 'desc' => 'Time Zone', + 'readonly' => true, + 'value' => (string) $timezone + ), + 'blog_title' => array ( + 'desc' => 'Blog Title', + 'readonly' => false, + 'value' => $this->core->blog->name + ), + 'blog_tagline' => array ( + 'desc' => 'Blog Tagline', + 'readonly' => false, + 'value' => $this->core->blog->desc + ), + 'date_format' => array ( + 'desc' => 'Date Format', + 'readonly' => false, + 'value' => $this->core->blog->settings->system->date_format + ), + 'time_format' => array ( + 'desc' => 'Time Format', + 'readonly' => false, + 'value' => $this->core->blog->settings->system->time_format + ) + ); + + if (!empty($options)) + { + $r = array(); + 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 array( + '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 array( + '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 = array( + '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 = array(); + while ($posts->fetch()) + { + $tres = array( + "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" => array(), + "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" => array(), + "wp_page_template" => 'default' + ); + + # --BEHAVIOR-- xmlrpcGetPageInfo + $this->core->callBehavior('xmlrpcGetPageInfo',$this,array(&$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 = array(); + + foreach($rs as $k => $v) + { + $res[] = array( + '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 = array(); + $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[] = array( + '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(array('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 = array(); + while ($rs->fetch()) + { + $res[] = array( + '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 = array( + 'approved' => 0, + 'awaiting_moderation' => 0, + 'spam' => 0, + 'total' => 0 + ); + $rs = $this->core->blog->getComments(array('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 = array(); + + 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'] = array($offset,$limit); + + $rs = $this->core->blog->getComments($params); + $res = array(); + while ($rs->fetch()) + { + $res[] = array( + '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,array(),$publish); + } + + public function blogger_editPost($appkey,$postid,$username,$password,$content,$publish) + { + return $this->editPost($postid,$username,$password,$content,array(),$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 array(); + } + + /* 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 = array(); + foreach ($A as $v) { + $res[] = array( + '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 array('Default' => 'default'); + } + + public function wp_getOptions($blogid,$username,$password,$options=array()) + { + $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 = array(); + $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,array(),$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 array( + 'hold' => 'Unapproved', + 'approve' => 'Approved', + 'spam' => 'Spam' + ); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/core_error.php b/v2/dotclear/inc/core_error.php new file mode 100644 index 0000000..5bbbe73 --- /dev/null +++ b/v2/dotclear/inc/core_error.php @@ -0,0 +1,62 @@ + + + + + + + + + Dotclear - Error + + + + +
        +

        Dotclear

        +

        +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/dbschema/db-schema.php b/v2/dotclear/inc/dbschema/db-schema.php new file mode 100644 index 0000000..cb6bb22 --- /dev/null +++ b/v2/dotclear/inc/dbschema/db-schema.php @@ -0,0 +1,282 @@ +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', 40, 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_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 ('varchar', 255, false) + + ->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)'); +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/dbschema/upgrade-cli.php b/v2/dotclear/inc/dbschema/upgrade-cli.php new file mode 100644 index 0000000..6beaeea --- /dev/null +++ b/v2/dotclear/inc/dbschema/upgrade-cli.php @@ -0,0 +1,52 @@ +#!/usr/bin/env php +con->begin(); + try { + $changes = 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); +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/dbschema/upgrade.php b/v2/dotclear/inc/dbschema/upgrade.php new file mode 100644 index 0000000..fc9213b --- /dev/null +++ b/v2/dotclear/inc/dbschema/upgrade.php @@ -0,0 +1,331 @@ +getVersion('core'); + + if ($version === null) { + return false; + } + + if (version_compare($version,DC_VERSION,'<') == 1 || strpos(DC_VERSION,'dev')) + { + try + { + if ($core->con->driver() == 'sqlite') { + throw new Exception(__('SQLite Database Schema cannot be upgraded.')); + } + + # 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 + ------------------------------------ */ + # 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 = array ( + '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 = array(); + + $init_fav['new_post'] = array('new_post','New entry','post.php', + 'images/menu/edit.png','images/menu/edit-b.png', + 'usage,contentadmin',null,'menu-new-post'); + $init_fav['posts'] = array('posts','Entries','posts.php', + 'images/menu/entries.png','images/menu/entries-b.png', + 'usage,contentadmin',null,null); + $init_fav['comments'] = array('comments','Comments','comments.php', + 'images/menu/comments.png','images/menu/comments-b.png', + 'usage,contentadmin',null,null); + $init_fav['prefs'] = array('prefs','My preferences','preferences.php', + 'images/menu/user-pref.png','images/menu/user-pref-b.png', + '*',null,null); + $init_fav['blog_pref'] = array('blog_pref','Blog settings','blog_pref.php', + 'images/menu/blog-pref.png','images/menu/blog-pref-b.png', + 'admin',null,null); + $init_fav['blog_theme'] = array('blog_theme','Blog appearance','blog_theme.php', + 'images/menu/themes.png','images/menu/blog-theme-b.png', + 'admin',null,null); + + $init_fav['pages'] = array('pages','Pages','plugin.php?p=pages', + 'index.php?pf=pages/icon.png','index.php?pf=pages/icon-big.png', + 'contentadmin,pages',null,null); + $init_fav['blogroll'] = array('blogroll','Blogroll','plugin.php?p=blogroll', + 'index.php?pf=blogroll/icon-small.png','index.php?pf=blogroll/icon.png', + 'usage,contentadmin',null,null); + + $count = 0; + foreach ($init_fav as $k => $f) { + $t = array('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 = array ( + '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 = array ( + '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.4.0','<=')) + { + # setup media_exclusion + $strReq = 'UPDATE '.$core->prefix.'setting '. + "SET setting_value = '/\\.php\$/i' ". + "WHERE setting_id = 'media_exclusion' ". + "AND setting_value = '' "; + $core->con->execute($strReq); + } + + $core->setVersion('core',DC_VERSION); + $core->blogDefaults(); + + # Drop content from session table + $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; +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/digests b/v2/dotclear/inc/digests new file mode 100644 index 0000000..df4b068 --- /dev/null +++ b/v2/dotclear/inc/digests @@ -0,0 +1,694 @@ +3a1c6cc728dddc258091a601f28a9c12 ./inc/swf/swfupload.swf +362b9537b53c8d3eb363598e5f38fb8b ./inc/swf/player_mp3.swf +9dcb71aec92e94b1b905192412a1f2a5 ./inc/swf/player_flv.swf +d7b28c9c52cf01199f8c9fe230e8b43f ./inc/core/class.dc.media.php +68523099197e276833c84c6c2376bb08 ./inc/core/class.dc.workspace.php +112f70a6b6b4eec61ad405f990b60fbf ./inc/core/class.dc.update.php +cde424b7a01df820e8bb6b0f1d9939d4 ./inc/core/class.dc.log.php +3477103ce812b71a9d38e3cf1feadb3b ./inc/core/class.dc.namespace.php +949e4e5a3746a1ad8de75dbb5a90c35a ./inc/core/class.dc.meta.php +b2aada7c5fa56c7bcf32c6cb3fae59fa ./inc/core/class.dc.categories.php +bb44cb8fbecee45fca2709969b227f85 ./inc/core/class.dc.xmlrpc.php +fad7358243930c7f22df931aba38427c ./inc/core/class.dc.error.php +a3cf40511123ed66a4a14bcf4b79faa4 ./inc/core/class.dc.utils.php +ac779916ef9504dac7771143e1352529 ./inc/core/class.dc.rest.php +6d45dad8e0afba501538e8577c57c62e ./inc/core/class.dc.themes.php +284bfc24af7741ab9439a94e45553f22 ./inc/core/class.dc.auth.php +ac5f58c6a91756d730850e1f0054f0f4 ./inc/core/class.dc.rs.extensions.php +d8b464d462e8c26fdb8314991f030dd6 ./inc/core/class.dc.trackback.php +aaaf612c3c0ba092d99bea8b65882838 ./inc/core/class.dc.settings.php +bc3fdfd02070475f7d0d2dae9d2fef0b ./inc/core/class.dc.postmedia.php +57dbe830b06b44ab46c9d800e5e68abf ./inc/core/class.dc.prefs.php +ddcf7b8ae49896902f78012fc502f765 ./inc/core/class.dc.blog.php +6a959c862a9cdba82da5ba3b73059474 ./inc/core/class.dc.modules.php +05941079cd9db9368c3debd740a85217 ./inc/core/class.dc.core.php +cb95b315558ebcb254249210637abe4b ./inc/libs/clearbricks/ext/incutio.ixr_library.php +d867d4ebc95bf8cbf8b196925c4100fc ./inc/libs/clearbricks/net/class.net.socket.php +1b89f622435faa7a75fa58a1aca3d54d ./inc/libs/clearbricks/zip/class.unzip.php +dd4bebcf0c65f78f07f2c831c1d26aa3 ./inc/libs/clearbricks/zip/class.zip.php +a0439fc6efcb19e17cd01c8530ada840 ./inc/libs/clearbricks/diff/lib.tidy.diff.php +f770aaef9206d211e158c4e956064a04 ./inc/libs/clearbricks/diff/lib.diff.php +2edbef60bcab048afb2c250e59c082df ./inc/libs/clearbricks/mail/class.socket.mail.php +4c5c63f2bb1489e3dc3bfc534f63bcc8 ./inc/libs/clearbricks/mail/class.mail.php +c1920efdf8daa2fa28db711391763e95 ./inc/libs/clearbricks/rest/class.rest.php +7dabf49811c54cd7e2e00a755a95e2da ./inc/libs/clearbricks/dblayer/class.pgsql.php +1ba018c7314f5e38484a516a9e6efbf0 ./inc/libs/clearbricks/dblayer/class.mysql.php +7ee9856ad3e4fced1869b9067b7da3ae ./inc/libs/clearbricks/dblayer/class.sqlite.php +2e3c38caefed1891cc9c268060e88a32 ./inc/libs/clearbricks/dblayer/dblayer.php +ffe21c976b615f671ffc80430ec1dfe7 ./inc/libs/clearbricks/dblayer/class.cursor.php +e7b55e965efccd72eeddb344c085156c ./inc/libs/clearbricks/dblayer/class.mysqli.php +e94f8b559d0a6d3786332bb8b5740dd1 ./inc/libs/clearbricks/html.validator/class.html.validator.php +f2940f553ad376554d9fc6df6954f1ff ./inc/libs/clearbricks/html.filter/class.html.filter.php +14d99509858d7dc753c23b5c62b80e7e ./inc/libs/clearbricks/Makefile +f2813d658120f149b4e03613df0cd8c0 ./inc/libs/clearbricks/image/class.image.tools.php +dea8c1d155c6d3cf6071bd34d105c47b ./inc/libs/clearbricks/image/class.image.meta.php +c0eb7fef8da37112c003ba0baf93bf53 ./inc/libs/clearbricks/filemanager/class.filemanager.php +bcf108d62c14d6e654b33c998bd3d1fd ./inc/libs/clearbricks/pager/class.pager.php +8a14839615beb6693b6d171852660563 ./inc/libs/clearbricks/net.http.feed/class.feed.parser.php +252485faf7459053c3f7e811493e5f30 ./inc/libs/clearbricks/net.http.feed/class.feed.reader.php +1bf1a1a82994425f03ad9f19e935d3c3 ./inc/libs/clearbricks/README +3fab7ec6ad3bc37b72d19848a3950bd9 ./inc/libs/clearbricks/net.xmlrpc/class.net.xmlrpc.php +f59dc65e4c36b47c0243eae0a60da11f ./inc/libs/clearbricks/dbschema/class.dbschema.php +4dc4e0ccd516f43cc1058f41a32536c1 ./inc/libs/clearbricks/dbschema/class.mysql.dbschema.php +0e6a54cbad7faa258e7cf3bafe95b9ca ./inc/libs/clearbricks/dbschema/class.sqlite.dbschema.php +52cb3d63686c4202f8a3e1c28b72eaf5 ./inc/libs/clearbricks/dbschema/class.dbstruct.php +19a7b770183e06433645ba6a572a6675 ./inc/libs/clearbricks/dbschema/class.mysqli.dbschema.php +3a5afc640ccd46b0eef5bce89fad58b3 ./inc/libs/clearbricks/dbschema/class.pgsql.dbschema.php +5d58ee070707b9344b74c4e95a6d8e2d ./inc/libs/clearbricks/template/class.template.php +dbe26992e25c706eb7ea798e26697401 ./inc/libs/clearbricks/_common.php +b23a438a4183cfd323342b591e88be5f ./inc/libs/clearbricks/url.handler/class.url.handler.php +e969237c3afdcbd0524bfb67ff186c2d ./inc/libs/clearbricks/debian/dirs +a165b0c48efd38c3c2725a282922c6f1 ./inc/libs/clearbricks/debian/docs +09741ea306c67f178df496044fd7d3bf ./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 +33c1cd6baec7ef84b905dd6fb463b871 ./inc/libs/clearbricks/debian/copyright +057af760bd008ca95cac177c5137f54d ./inc/libs/clearbricks/common/lib.form.php +b55f8d044a595684bab9423bf26cb76e ./inc/libs/clearbricks/common/lib.text.php +53f8e535da21c1f1ec0246a643d01993 ./inc/libs/clearbricks/common/tz.dat +1dcce11946a8134fe2c42680102714b2 ./inc/libs/clearbricks/common/lib.l10n.php +64979fdd95ee50b64ced5588b815cd4b ./inc/libs/clearbricks/common/lib.crypt.php +a4f3f32c32831d8585f64d160c75e144 ./inc/libs/clearbricks/common/_main.php +570ad3fc526ff54e2f76eaef8121af2d ./inc/libs/clearbricks/common/lib.date.php +a611602796b3ad3bf56dcbec23751f61 ./inc/libs/clearbricks/common/lib.html.php +bb0f2a13c46323c8aa763d4b65a849f5 ./inc/libs/clearbricks/common/lib.files.php +1e89873bfe887d73fb70a46cfea1bb66 ./inc/libs/clearbricks/common/lib.http.php +9bb79146a6ee39380f1060d0300a3702 ./inc/libs/clearbricks/net.http/class.net.http.php +084a7d997891a5a676128d571ce54e24 ./inc/libs/clearbricks/net.nntp/class.net.nntp.php +f590c41a10e8115ce381b3ae03ab8bea ./inc/libs/clearbricks/net.nntp/class.nntp.message.php +cd276c4686d956fea1fd9a56096726ba ./inc/libs/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php +e75095764485b34dc522b6db59eae353 ./inc/libs/clearbricks/xmlsql/class.xmlsql.php +ca58e658d7976706bcb3bb885f229034 ./inc/libs/clearbricks/session.db/class.session.db.php +c3a41bf94dc11a1387a8f2d606111e64 ./inc/libs/clearbricks/mail.mime/class.mime.message.php +8bced09e0fb7e6fb368a62a7b2423bc3 ./inc/libs/clearbricks/mail.convert/class.mail.convert.php +d3b27e15bf5eafe2da90669e483ac645 ./inc/config.php.in +d8b13db427df79bf42abb6b11bf96c3d ./inc/admin/lib.pager.php +33e352171f3847b1545d0815e7865f53 ./inc/admin/prepend.php +f56f8e6149de6f8bef76d0b44a120826 ./inc/admin/class.dc.menu.php +e75011fa626fa363277471c4a16874e5 ./inc/admin/lib.dc.page.php +d067eb2122f08ecf9c70270a16d4c615 ./inc/dbschema/upgrade.php +9420fb50208df6b8ebf3c1430535f327 ./inc/dbschema/upgrade-cli.php +82bdc27069f8bcfe26f1d19d13098207 ./inc/dbschema/db-schema.php +209634bb0238704c4874c35d615ae59e ./inc/.htaccess +75290cb1920955fbde2e83ecdea3c7a6 ./inc/prepend.php +92f8d91038339aef6b6c2800aeffdd00 ./inc/load_plugin_file.php +f042e743a702f88bae04a0c9b4f232bc ./inc/public/lib.tpl.context.php +38032779b49d5297b2911e789e819e75 ./inc/public/prepend.php +c1a30a35f3bdc7282cd5b057a562d60c ./inc/public/lib.urlhandlers.php +2083a978f369df674fd9605344a078fb ./inc/public/rs.extension.php +9b72c7b38e24bcdb1a62b8caad592ef3 ./inc/public/class.dc.template.php +f406a856d0fef5ca16ab1fe5f6780d26 ./inc/public/default-templates/atom.xml +dc797422198ecf55648798926b333e34 ./inc/public/default-templates/404.html +cc388bc96e57726ed1e46a71de72822a ./inc/public/default-templates/rss2.xml +a7006baa75d1c133d57acbe1f559abce ./inc/public/default-templates/rss2.xsl +16bdc8783f0b0b181bca2922afa6707f ./inc/public/default-templates/_head.html +0ee80a5f6349fd4433f163e1ecd762aa ./inc/public/default-templates/category.html +94df989e651b3fab9ae5f889da8957a5 ./inc/public/default-templates/_top.html +252faf814965810e53ceaebca99110b8 ./inc/public/default-templates/rss2-comments.xml +7cbfaee006b71aa9e2ce9ff114bc30cf ./inc/public/default-templates/archive_month.html +4672a386be84c3b8aabe7f0f11eadd1f ./inc/public/default-templates/_footer.html +5e4dcd3dd0dfc2b5434ff4a47258c208 ./inc/public/default-templates/archive.html +5b02984ea641fdfafb3aeaa46d44094b ./inc/public/default-templates/_mp3_player.html +19a49738c54edd08f2551be928d263f1 ./inc/public/default-templates/atom-comments.xml +03a6321d766f1731d343db2aba6dce75 ./inc/public/default-templates/post.html +51c379d5e96b3cfff2410872ab2cdeb4 ./inc/public/default-templates/user_head.html +dbd6a409049296c74a829e3754628a01 ./inc/public/default-templates/password-form.html +89ea9bf9dc901c6f20861c915c410e33 ./inc/public/default-templates/search.html +ec603716f84156a51d557b1d4462ab05 ./inc/public/default-templates/home.html +0a7c11c048ad7c6d9acbb9a1e38c2120 ./inc/public/default-templates/_flv_player.html +d3edd069e027d9c3ad1d32595c66bf31 ./inc/core_error.php +751419260aa954499f7abaabaa882bbe ./LICENSE +2bfd05d14d13567c5d7b38b685e79bd7 ./admin/js/ie7/IE7.js +0da2f7d8d62695d582e1e8bfcb47a581 ./admin/js/ie7/IE8.js +45becbb1ba3486fa9961b9c47614d2a0 ./admin/js/ie7/blank.gif +3e8e99f729bba013ed74b23d442ce467 ./admin/js/ie7/ie7-recalc.js +1085be8f5d5a499858d18feee8d0ef8c ./admin/js/ie7/ie7-squish.js +379dffc9b9ed5ea513204c61057b77b3 ./admin/js/jsToolBar/jsToolBar.js +92c3d043218f868cf339b120dd6d51b7 ./admin/js/jsToolBar/popup_posts.js +fc39a873174acf0443ff17c8c577e929 ./admin/js/jsToolBar/popup_link.js +3446cbe53dada04cdf45882fb7952a77 ./admin/js/jsToolBar/popup_media.js +7e0a3f0bc1d42587db8637ce02838083 ./admin/js/jsToolBar/jsToolBar.dotclear.js +82d06b07d7e72577e99a8715eae90a0f ./admin/js/jsToolBar/jsToolBar.wysiwyg.js +592a3011c4e47a77856ad7df0d136bee ./admin/js/color-picker.js +9396d0f3e3ee1cefb5c2ada58f479a64 ./admin/js/_blog_pref.js +08451c09111e763c2354993025b07a66 ./admin/js/_comment.js +bb5fc73c7465d0e1e630560f39dbcc62 ./admin/js/_post.js +7f7f3ab2d5bcf53486a107df47cd5ba0 ./admin/js/common.js +db1cb95e3ef75253a1d1a31aeffe8daa ./admin/js/prelude.js +e3b5113a5cfed6a1d9dfa72834e5fb0d ./admin/js/_preferences.js +eab2d2bcceded304d2a923bc546869fc ./admin/js/_index.js +44c7923c51c9e8b5185d70078a1652cc ./admin/js/dragsort-tablerows.js +47acaf03194199a8b4421d1ef21532ec ./admin/js/_users.js +cf014d803ae8195a2473b48b1f2c92b6 ./admin/js/_langs.js +a4f58f2e708d54d966341654a1b9710f ./admin/js/filter-controls.js +c3de1b5c946d737887c495a081bc4fa3 ./admin/js/_media.js +d1c92505a8fd0fb4b2be1ccb944628e6 ./admin/js/_blog_theme.js +697881b2c6f4ba506df223db28db3a1c ./admin/js/_preferences-dragdrop.js +334a1345290da857abe41f3807c14967 ./admin/js/date-picker.js +81b61be64f338c2fd40da2bf47d2183e ./admin/js/_categories.js +49e972305f261af8830eac093dbed82f ./admin/js/confirm-close.js +dacda4414ed2d1db6b25ed16bdcc8ee9 ./admin/js/_media_item.js +10b1885ee8c7ad1c439bc3de868d4bb4 ./admin/js/_trackbacks.js +8f76e1f99874c7dd4705c1104625ca18 ./admin/js/jquery/jquery.pageTabs.js +b053c1e03164d2e6f92362ee298e5d0b ./admin/js/jquery/jquery.farbtastic.js +ce7a22089d470d15dc6a061bf7fe3d09 ./admin/js/jquery/jquery.bgFade.js +ca868f7e39985586a856b48b624c96d7 ./admin/js/jquery/jquery-ui-1.8.12.custom.min.js +33440de62785bd447fab76638ffe20cb ./admin/js/jquery/jquery.js +7f5f5bb62524a0ec4be1985031e7e141 ./admin/js/jquery/jquery.modal.js +93e11ef8585fbfa48f6dc595eb17a12c ./admin/js/jquery/jquery.biscuit.js +c9cd2dff00dac4624a45d36f42d9aa58 ./admin/js/jquery/jquery.candyUpload.js +aa314073e0ef73b815959563872f6c2e ./admin/js/_permissions.js +e6c6bad56b68af8402a30099a663ae65 ./admin/js/_posts_list.js +b46e51e327a0bb8f2711f230fc1467b0 ./admin/js/tool-man/dragsort.js +814404153ee1d350adf4bf7d1eae4cf8 ./admin/js/tool-man/core.js +6084f5f1fbcb14b085014ffa06961029 ./admin/js/tool-man/drag.js +02fba17f9a80f43ad07c55f2e092e7de ./admin/js/tool-man/coordinates.js +7d13dc9c59316624c051e6256c86bcb7 ./admin/js/tool-man/events.js +af77cd03cd87c611f1f495d6d29bab44 ./admin/js/tool-man/css.js +87aabde2f9d09bac13b8483e369cca38 ./admin/js/tool-man/cookies.js +3c34805dc9ff1c9863719c85dd804e5e ./admin/js/meta-editor.js +fd92ba025b34df5e3784ae9cca6c4f8d ./admin/js/_permissions_blog.js +eb8ddfafcf6aa8db9f55cd5f10dbe9aa ./admin/js/_comments.js +f0f861db8bed322c14508f592aeef119 ./admin/js/_plugins.js +29698c9e3ac842ca5c30abe9a69b338a ./admin/js/_category.js +d01327e2f55af880a2fb49813e48fb2e ./admin/blogs.php +ec14efc419c67fe3644ff904047b31c3 ./admin/posts_actions.php +fab2ceaf2ae5316dab402161aade8792 ./admin/blog_del.php +c9f5d46227866cadf6ebd3a05f550bd3 ./admin/update.php +ce12a0f1d02169e0e9f10a6ae88fcf82 ./admin/plugins.php +f418b7365dd285d4fca3e4c0435b4313 ./admin/popup_posts.php +a7699758a4d9927b23f8e2a62904e3cd ./admin/style/warning.png +d79076c50110cbdcc4cddf30f4df9873 ./admin/style/jsToolBar/jsToolBar.css +3e0f67a5296128dbe1857c1935f6da47 ./admin/style/jsToolBar/bt_link.png +64cdd7af332808399e65fa0538761c2d ./admin/style/jsToolBar/resize.png +6a388ba1cdeff25c79d540333b96ed27 ./admin/style/jsToolBar/bt_clean.png +a6ae01491523b9335334eea1b98c136f ./admin/style/jsToolBar/bt_img_select.png +828adda895735a8624fd819506078af7 ./admin/style/jsToolBar/bt_br.png +e5aae58c14ca63c366fe02acec03d928 ./admin/style/jsToolBar/bt_em.png +0c7181b5c37930f422fb9e6e7488c1d9 ./admin/style/jsToolBar/bt_img.png +ee7ee162173d1b91896415988e8cd006 ./admin/style/jsToolBar/bt_bquote.png +47c71f6ee3cf31296ff1f18348748754 ./admin/style/jsToolBar/bt_ins.png +e9f233c1c309337911b5b03fb9e1dbc4 ./admin/style/jsToolBar/bt_pre.png +22ad5189d9d94f9a24af5311ad06805b ./admin/style/jsToolBar/bt_paragraph.png +2ca06c1c3e80079ea378fe061e4cfdc3 ./admin/style/jsToolBar/bt_code.png +78d7a07a669055db5c3966edc31c94e4 ./admin/style/jsToolBar/bt_ol.png +a3a4c43798d387734737ed81a65eb7f9 ./admin/style/jsToolBar/bt_quote.png +94a2fd910832b31e18b4bb4e63f9aa79 ./admin/style/jsToolBar/bt_strong.png +e7e674fbd3612010f154475c449b3b79 ./admin/style/jsToolBar/bt_del.png +e43a1380a8ae14425ca9ce400dd336b7 ./admin/style/jsToolBar/bt_post.png +39f73a440303baed6920a769370091da ./admin/style/jsToolBar/bt_ul.png +5d2a95c6b6817027b7cc7413bf664991 ./admin/style/candyUpload/style.css +caa11e523e5a3ed38d90480bed5d5a73 ./admin/style/candyUpload/loader.png +0f4c059f8aceadfb2dd932e7a02ce22a ./admin/style/candyUpload/cancel.png +35a8ca87a425e1058f0f93d92a842e9a ./admin/style/msg-error.png +c85f133d357cdc134155416f5be2a5db ./admin/style/install.css +d8d0578b583b3879b3ee697fb4068648 ./admin/style/modal/modal.css +14a0936bd54fd18c22b5e7fb86ba26a3 ./admin/style/modal/loader.gif +4134330cedefac4a9075a7756ca496bd ./admin/style/modal/close.png +57d696efde30fe02acd30b46d87e930d ./admin/style/package.png +40be108b0a31181f7975eac193908b12 ./admin/style/default.css +2f7d618efdcae3859b36bbfa38fc75bf ./admin/style/page-bg.png +9c3e7b71f42e2d6c64b52fe7d46d5f16 ./admin/style/grid.png +45b045808b62976c5bfb7a701db2830b ./admin/style/dc_logo.png +003b8b1d1103cff11a707c5f80861a75 ./admin/style/default-rtl.css +5a7ae1d8c8b2343a9e5a4aebf4234392 ./admin/style/farbtastic/marker.png +2b6d304868ff398c17252b7b0a0414c4 ./admin/style/farbtastic/wheel.png +fbbd113ed6e6cf4adcab0f720b3b9b9c ./admin/style/farbtastic/farbtastic.css +c6dc921c0d6f2197793d9174b4267ca0 ./admin/style/farbtastic/mask.png +0716421681913b2cda5821b32b3360d4 ./admin/style/dotclear-logo2.png +57688345aa080a9a391efc40608ce98b ./admin/style/dotclear-logo.png +bed217c0f0423816fdfdae5f136a2aea ./admin/style/msg-std.png +b4459cd68df8a0ea2a5c12e6c655c71d ./admin/style/dc_logo_small.png +82244e6c9f6b8f3cefa0545468f40d65 ./admin/style/drag.png +a9d4db4f30e21559acf9fd825d72db27 ./admin/style/dc_bg.png +6fbf1d5cf4953ace09e5041a53d477ec ./admin/style/head-bg.png +e179e6f8a290c99a5ee77fca9c204863 ./admin/style/add.png +5625c7bc5997742b45925105fd9a4303 ./admin/style/magnifier.png +b8abd059ee85b073a27d38bba8998786 ./admin/style/iesucks.css +daa77b9f2795fce1510dd2d6b82e722a ./admin/style/info.png +37e482ed14e13a1a8a141deb0eea81f2 ./admin/style/date-picker.css +cc08cffba493e1114cc6709a7aadd2e5 ./admin/style/install/note.png +1141b61e9cea3d25ffebed4d4a2d6676 ./admin/style/install/process_warning.png +dff65577ffa614149552e2bd25800f76 ./admin/style/install/important.png +0716421681913b2cda5821b32b3360d4 ./admin/style/install/w-logo.png +5b036194b0772a23e73c88538c43737c ./admin/xmlrpc.php +cdede2ce1df3ba2299f00aeb2604a7cb ./admin/auth.php +666eb56b637e82e178a8a6bdfc2886b3 ./admin/post.php +b8d6dda0d3db7b147fe589151a1a4526 ./admin/permissions.php +860965f34e820c1ca4640b5ef9f92cae ./admin/blog.php +27619cd17f652e7d806586b6ccbbd3e2 ./admin/plugin.php +92f95f82fe97b88ccd42bd330c6844d1 ./admin/media.php +d43b3bbaf093f0e17f05269ee1d594e4 ./admin/media_item.php +f0adbe078cc42ac3ec356b1fa2cd67cd ./admin/images/plus.png +26500215140d1b0c0b947138316f00de ./admin/images/menu/blogs-b.png +3fc65469ad83784c3e11a0b0f5610fe1 ./admin/images/menu/edit.png +7b4259eae3ef9ce3c8e73cda28cdb848 ./admin/images/menu/blogs.png +ac90182e4a2ddbfec7308fd8e0e9f97f ./admin/images/menu/dashboard.png +098c84b3f910856868c291551092cdab ./admin/images/menu/favorite-b.png +6848117e186492db3baf129b00bda76a ./admin/images/menu/categories-b.png +7562fc3201a20137ca93b41a17ada1ab ./admin/images/menu/langs-b.png +accef94acd8231f145c05ad518f31953 ./admin/images/menu/update.png +76943b18b4e6486a549554a658e611c9 ./admin/images/menu/plugins.png +6c4b15fe4dec48c1f28e33706de6dae9 ./admin/images/menu/blog-theme-b.png +8aaa4c8fe97ac9c53b5e886a3bbfb971 ./admin/images/menu/favorite.png +b51b37a5d3ff0c0cb35414dea1b439b2 ./admin/images/menu/blog-pref.png +dc8df030063d8e45ad34b001274abf5b ./admin/images/menu/themes.png +a77a1960486bface242d029c7aa087d8 ./admin/images/menu/add_to_favorites.png +78a765be907724d7401647d808ca50bd ./admin/images/menu/search-b.png +304e702c190e373ab168f3e02a39bafb ./admin/images/menu/plugins-b.png +42af2e3a92f15d62697fb53fef595002 ./admin/images/menu/media.png +e724854eebb8a8050ab2948d374c8794 ./admin/images/menu/user-pref.png +223a95d65cf6eb77b3864eab41b0c205 ./admin/images/menu/entries.png +11dfd2df494996acaadd5144ef1b5f82 ./admin/images/menu/comments.png +3cdb945ceec71cece4535938d7e7b4da ./admin/images/menu/users-b.png +b88d849500404c5e1dd5e84d2cbfbdd7 ./admin/images/menu/media-b.png +6b05a6e5e8d6a802b596941de43d16b7 ./admin/images/menu/search.png +feab8bf7358c357f4cd537820fe62490 ./admin/images/menu/user-pref-b.png +1cf0eda014601a2de0ca6e2d65ce2e84 ./admin/images/menu/blog-pref-b.png +d5dcc36f898a1d7e114ee86c9706d32e ./admin/images/menu/entries-b.png +81b7b4adc7aa2f0369cf9fc17410b13d ./admin/images/menu/users.png +d7f03e538273c00dd945234ced5bc3e6 ./admin/images/menu/edit-b.png +1b080e9e7b5ddedd790423b661403a41 ./admin/images/menu/comments-b.png +b26e639aefd49983c1e519077f4982a7 ./admin/images/menu/categories.png +194eed635664d675736700fd51fb282d ./admin/images/menu/langs.png +86583b1fd690088cd78f91983aaf8575 ./admin/images/dotclear_pw.png +f8e8e34c81702d8cce1de96d628fcae3 ./admin/images/favicon.png +9f5f27a72f4c95790de10fb8acf75b37 ./admin/images/superadmin.png +50bf28433834e6d2f6f46d1b84421d2b ./admin/images/media/video.png +8e35fccfaa42070297cef723428183ba ./admin/images/media/blank.png +7e619d7762995a3c3ef9edfb3b6b4c92 ./admin/images/media/spreadsheet.png +c2b2d27914d57241d3d0e96a66ede1ca ./admin/images/media/package.png +487708f4b8f6fa0ecff78f26ded3806a ./admin/images/media/html.png +90864902424cf16cd85d2a6e54fe0272 ./admin/images/media/document.png +be57e76497791a2540d91f9149f38a6b ./admin/images/media/audio.png +6884619665a6a6a91996236dd26ad2fc ./admin/images/media/text.png +27013d32dfc30e5512ff184c2869bb3f ./admin/images/media/image.png +34f401c85ee26edfffb2c317ae2b1b54 ./admin/images/media/presentation.png +c6db076f177ccaf062d017c5db228085 ./admin/images/media/folder.png +4d8611170bd9173aa87cfbd4878ac588 ./admin/images/media/executable.png +61334007afc261a93d677bd3460ee36d ./admin/images/trash.png +21d31ef6dad45bbdd32bbe358169952b ./admin/images/selected.png +e7c196b87a5e022549b71e6f4e811d18 ./admin/images/menu_on.png +a2e73061f728066f2305c6aa5698687e ./admin/images/outgoing-blue.png +c3380a4422bb8a7e8756a6a167ddea22 ./admin/images/check-off.png +87e4a16e12e1e0761687d0cfdd7baacc ./admin/images/admin.png +355fbab6dc8dc6c779ba687bcc187fd7 ./admin/images/scheduled.png +913ff19cacf4d03fc835b4255c236c79 ./admin/images/picker.png +3ee61de988743c744af9b9224d35284b ./admin/images/minus.png +b742e51edacbf99451e12c5bb01531b0 ./admin/images/junk.png +f86e9d6679d8b27334bbb8df82b1c7d9 ./admin/images/menu_off.png +9b1093546f036f5e51069ffbef0ea7b8 ./admin/images/add.png +45c5ccf8249c5ec4800b9e0c3d34b7ca ./admin/images/check-wrn.png +02437c955382fa358b357632557a6f4e ./admin/images/attach.png +dfab5698cd3abbb30538331690413d30 ./admin/images/locker.png +c40960ead0cf335209336463cb1b37be ./admin/images/check-on.png +2b544dcc4b961ad990d0c27cce909585 ./admin/images/outgoing.png +07181f8c47f322410aea182338b031b3 ./admin/images/date-picker.png +fc080751a6ae822596cbdb54c97c4fd6 ./admin/images/edit-mini.png +93d023c510f85f951839c251814291ab ./admin/images/noscreenshot.png +1eaa6675e93550119cfee871b3fc5746 ./admin/images/logout.png +8776864d4c4505e2cc4040a418497bc9 ./admin/images/help.png +5df7e9507b256844f028f01d0716b203 ./admin/index.php +c41d2db8c410a3dfddda37a52ab14bc4 ./admin/permissions_blog.php +87c7183813aacc1661e19ec8ec601ec1 ./admin/posts.php +4ca3e3050137b781b379803b6ff8690d ./admin/comments.php +da4e5249ad11ec5f17019dc152bc8180 ./admin/popup_link.php +220ab6d14dffd7be6b77867669641327 ./admin/services.php +7e02c709544983f9a44a36d4bc853240 ./admin/search.php +80bb7019bb3ed2127bdff6545412ddbe ./admin/blog_theme.php +9b8715ae3022f2f771587bae2f7e5134 ./admin/comments_actions.php +d70e027e2672a6e22d2d84b3aabbdf63 ./admin/comment.php +12cc226c90afdea93ad0cd2057672c96 ./admin/user.php +711578a896d6ef538da5825511caa738 ./admin/dispatcher.php +04030cedae46cd04dfd74498c567bf3e ./admin/preferences.php +064eae06875333c399a798494f8e2e71 ./admin/users.php +7ec8ad91b731edcd5b5bec16f1e900b1 ./admin/install/index.php +60975a951783e28e7c4c86402a05f385 ./admin/install/check.php +64df84db3fb2189eef4b1852f58e6e17 ./admin/install/wizard.php +ed72c46169a159eff4e450c186f7bb40 ./admin/category.php +c67c2b5a1c0ad19020cc7a50c83284f7 ./admin/post_media.php +87e3874b7d8105f426d6ef7462f5f027 ./admin/blog_pref.php +39aa7e09b45b8333a8d5d8a3e3a79c22 ./admin/categories.php +ddfc2edef1e53ebc956f0d3dfd656b87 ./admin/trackbacks.php +6b9e293e1beb9a8e706c83822c9f381e ./admin/langs.php +af0e07462505de19ad85e6cd53906846 ./README +312530ddb6b46618848b0e34ebc0422c ./locales/en/date.po +fc51ec4fe28cadc83913b369fc77d65a ./locales/en/help/comments.html +018f76172ae892ae4810db02314b86fa ./locales/en/help/blog_pref.html +c25adc9f42a14527b538cb58554b1ad8 ./locales/en/help/media.html +5c9c46fede94f83bef0828264f18f596 ./locales/en/help/post.html +efc9a224297e6902a5f0efa196bc7c6f ./locales/en/help/wiki.html +a2c6ee6914d93c0f551ced12e55970f7 ./locales/en/help/categories.html +684790773f1f459104e1daf3e50fc94c ./locales/en/help/user_pref.html +649bda3d42114c8dfea967f005d304a1 ./locales/en/help/user.html +09352986157f34af6730061ce06bd8cd ./locales/en/help/posts.html +4c12ea0a98c5cd138d084ca0c91117f6 ./locales/en/public.lang.php +5a3e3ece8c26cfab6fb0a087f5a9d73a ./locales/en/date.lang.php +ccadbcf1eb0c0618b7176476b2869f5a ./locales/en/main.po +2fa421ee2b30c8d8d68682f09237a76c ./locales/en/plugins.po +ec25ff7f135839cd1cf3b1773a7b07bf ./locales/en/plugins.lang.php +166d2195addbd38c01c70151246b581c ./locales/en/resources.php +00991b192802456807b67cc5445abb82 ./locales/en/public.po +4c12ea0a98c5cd138d084ca0c91117f6 ./locales/en/main.lang.php +0b6a3799a53022c6140bec6b7fc5b2f7 ./locales/fr/date.po +0474ebce1d0524825e938908d4afa44f ./locales/fr/help/core_media.html +fdad4d76a79dabef426adbda07ad510a ./locales/fr/help/core_user_pref.html +a9f897d204180419de34dcfdc9b18f76 ./locales/fr/help/themeEditor.html +2bc53951d2998ab67979a5b1ea4f3df9 ./locales/fr/help/core_comments.html +996bbf4b20290530884d25844b1ee32d ./locales/fr/help/blowupConfig.html +ad33f702e9d1872395ac85dc8a9ff875 ./locales/fr/help/core_categories.html +6bc717490f197e5e7216b1853bf88b7d ./locales/fr/help/core_post.html +e38f790349f0accca7c63a7f76671c0c ./locales/fr/help/core_posts.html +590e7cdb6a0010defabfae0c5e1554c2 ./locales/fr/help/core_wiki.html +2150b2a08a45668b253ff05c9f74affe ./locales/fr/help/core_user.html +a02aaf58ce0bb12e3b88b828090cd9db ./locales/fr/help/core_blog_pref.html +632b94ebd8e9fa690469adea0b78cf65 ./locales/fr/public.lang.php +59b6966fb7e7945212d0daf6128628dc ./locales/fr/date.lang.php +01ba7f80a86519153e8f7c536ce8a9ee ./locales/fr/main.po +dbba5039396a650c96ee9e73e38f452f ./locales/fr/plugins.po +b85f6046043eecd1402d8201a9ccd2b7 ./locales/fr/plugins.lang.php +bfc28002a0ceb0ce48ca56781875de23 ./locales/fr/resources.php +878cd7e4ad4ec67cc8fd02072d6b1b84 ./locales/fr/public.po +38db3db98dbbe4ce8bedcbf459bb4070 ./locales/fr/main.lang.php +d4ff299dabd4b8334cc7259d0eb65b6e ./locales/README +daec700273b033301fd6c71dc29a19d7 ./index.php +33440de62785bd447fab76638ffe20cb ./themes/default/js/jquery.js +0399de1c63edf55deb1cffa811905d57 ./themes/default/js/jquery.cookie.js +b766b02a8f0e877f56c232dcd9ba736a ./themes/default/js/post.js +a802d1f382c8b22d1e6c6e28ae618498 ./themes/default/img/trackback.png +74eb29efade1edeff692446b5e6f8258 ./themes/default/img/commentmy-b.png +ef130b60877aad5a4cc8d4c5ead17317 ./themes/default/img/commentmy-t.png +f3b1ebaf5a27959228b736f87db11776 ./themes/default/img/page-bg.png +e199613e6ec2cead05b92a6f2c76e2ec ./themes/default/img/comment-b.png +90835fdf81ee294b89674795ee6ed71f ./themes/default/img/tag.png +3f7ae2737f49605199554ccac96dc748 ./themes/default/img/comment-t.png +c85f9d3ea9319236e67db78be1707478 ./themes/default/img/page-b.png +42fa213bf2a5b110afeb7552767700c6 ./themes/default/img/page-t.png +68f80507e1d6123f57c5f25f006a699f ./themes/default/img/feed.png +e4d37aac2d638c4a358a7fd4c76b2789 ./themes/default/img/attach.png +19741777bd991f387b6d01d187f9cacc ./themes/default/img/comment.png +dff6f47c5dcfce7c1a909f015f43a91b ./themes/default/img/body-bg.png +432d016389df9619e39213301f8b58d3 ./themes/default/_define.php +bb7dfb0da518903b3307a28f6c6d3a33 ./themes/default/style.css +acdc336f9249b84310c6bc541d6e66ed ./themes/default/smilies/laugh.png +e8cfd3101dc2a0cdb7a343e1b873a38f ./themes/default/smilies/cry.png +062fe26eb6105ce6bbc0d133b50c4fcc ./themes/default/smilies/confused.png +c8372d57625893f1c06a409cf1fb84d5 ./themes/default/smilies/sad.png +c1fa8d897b82f938e2abada9bfa7c2c1 ./themes/default/smilies/redface.png +f97fc26a57559afec46806aa42083d9c ./themes/default/smilies/eek.png +919a6951304ce994203f86edd3f5d359 ./themes/default/smilies/dizzy.png +5933cf36a8be5147933b6499d589eb01 ./themes/default/smilies/idea.png +d2855d2d68f6467db69219534638f4bb ./themes/default/smilies/exclam.png +93d6fed1604e9e479f65940e52884ac3 ./themes/default/smilies/lol.png +0d16e0502c576f32662ad14dfde3cf53 ./themes/default/smilies/rolleyes.png +18f5ac3609425f10e910e64d5620edda ./themes/default/smilies/cool.png +1808a6c8affe37c094a038dc983b0ffc ./themes/default/smilies/mrgreen.png +d3671aa8ccb54bbc9b89a83ba202b626 ./themes/default/smilies/smilies.txt +51b2f454563975cc4b2fcc07d31ce12d ./themes/default/smilies/alien.png +5b36ab1e0f2c2dde883a6e32cc3feebf ./themes/default/smilies/smile.png +a1650e5c1ff8e9f40ef90828011f91fa ./themes/default/smilies/normal.png +efe8138e2d0b366a9dcdbe1c6ab75eee ./themes/default/smilies/evil.png +987742e347ef66bbc2b6b9546a0aa360 ./themes/default/smilies/angry.png +83362893d53db01b5595a7e8021fcdac ./themes/default/smilies/wink.png +57a457e1818d6e9419ced9366d2e33dc ./themes/default/smilies/razz.png +9ae025a44deba07f3e3fe2c3992e5d9c ./themes/default/smilies/arrow.png +25adf8de879b3e83c12ab668bc3c983f ./themes/default/smilies/question.png +32f2a33008491d7c070d70cfc64585c0 ./themes/default/smilies/surprised.png +c75e685b085a7e188b42f995626ec3be ./themes/default/screenshot.jpg +9a32c297dc3b0deb2d6dd753003a34fa ./themes/default/print.css +45d00287e10a0f80755df82486fe5d9c ./themes/ductile/img/menu.png +a0015087cd48c18743ec0abe82093fe3 ./themes/ductile/img/sticker-contact.png +1b0f8f994562ca42560fcd2ada72c409 ./themes/ductile/img/filet.png +94d5758aa91ec3378e0cd48de4f3d479 ./themes/ductile/img/last-hline.png +6490586470558c14c3bbeb50b7aba76e ./themes/ductile/img/post-info.png +d5979fa0d9b2f8be5e1426d10ab324b0 ./themes/ductile/img/sticker-about.png +a9ba633e8a0c38c7799fddb21386bf1b ./themes/ductile/img/squares.png +f5a4ca7d53980b9f3599f44f1ee056dc ./themes/ductile/img/404.png +3addb79a8269aa09f6a481faed784cb3 ./themes/ductile/img/minus-plus.png +6b66cc73ab482165f05fa3681c3117fd ./themes/ductile/img/hline.png +f6d76b2ec5d0cc2f1fa0d7959e654659 ./themes/ductile/img/feed.png +2558233ed307b504ee5cec813ed37c9f ./themes/ductile/img/vline.png +54e2985a49ef5d1eafd019c7890d6c75 ./themes/ductile/img/sticker-feed.png +208f72649c79433f373bc2f345e31b1e ./themes/ductile/img/logo.png +9ea7bc2b06c8b9835fca914e45c3e9fd ./themes/ductile/img/menumobile.png +3830f6d401c112da3fec2d49598c110a ./themes/ductile/img/info-co.png +ee625571fee87ee28771814a7d990533 ./themes/ductile/img/download.png +d3a5600600c51388faca5764d78bb255 ./themes/ductile/tpl/_sidebar.html +5972bce8e97464802c9337e3f711a497 ./themes/ductile/tpl/404.html +5eca089dfae96ac2fbec83e0b8fecca8 ./themes/ductile/tpl/_head.html +82cac60aaee669c22cd12c2ea906120a ./themes/ductile/tpl/category.html +f0c5851b4f7864c4e867fc68422c1e7b ./themes/ductile/tpl/_top.html +646d08df01866f93c3f96e181f44f67d ./themes/ductile/tpl/page.html +04042fb7780b53434df1f3e08c188d0d ./themes/ductile/tpl/archive_month.html +acb7ffa595929e97cddd77c4e879122c ./themes/ductile/tpl/_footer.html +a1983537ec443b2820e3bcda28a2cdb6 ./themes/ductile/tpl/archive.html +34be6f864b5a87efca2c4629752ea8f5 ./themes/ductile/tpl/tag.html +f09ccea554a96b569cfd05c07b33dd4a ./themes/ductile/tpl/post.html +f4f70c7f5a9ebd5ba3deccfa85991248 ./themes/ductile/tpl/_entry-title.html +1b66d32c893660bee28650240a2a58b5 ./themes/ductile/tpl/_pagination.html +32da3358202c8a7f561e4de2c40602b9 ./themes/ductile/tpl/tags.html +1363f40218fbf611757fb0a77bf8b368 ./themes/ductile/tpl/_entry-short.html +423ed7b8edc6f7585fed258c27d8a189 ./themes/ductile/tpl/_entry-full.html +281cd5fecf15b910197cff4ce8c59c8c ./themes/ductile/tpl/search.html +a43fbe8596a6a7cea756a568e8b08418 ./themes/ductile/tpl/home.html +26bbe6713e4d3107acaf728d385f7846 ./themes/ductile/tpl/_simple-entry.html +1b496f7d9654a14a2c8fbf1048691ca5 ./themes/ductile/_define.php +f422cb4f4f3e78e9759c0f7df7e3aefe ./themes/ductile/style.css +32fb1656e65748b87fd80961720288e2 ./themes/ductile/_public.php +917671c8a29b46c280ecadc16b0c3aac ./themes/ductile/locales/en/help/help.html +464d1bec47c94bd1318a1d0b7591d69d ./themes/ductile/locales/en/main.po +f4ea1518ffbaab35d4c0a811a7a6f0e5 ./themes/ductile/locales/en/resources.php +05862e7f740b0d2693235af02dba8ae7 ./themes/ductile/locales/en/main.lang.php +60cd3466769a335e11f4ea6b6db00b32 ./themes/ductile/locales/es/main.po +85515ddcb067b706b5f96de3bd93fc7a ./themes/ductile/locales/es/main.lang.php +044617aacddd312e982a46868d5cd0c4 ./themes/ductile/locales/fr/help/help.html +21c5b93e0056a529b23a2a8100b6f131 ./themes/ductile/locales/fr/main.po +f4ea1518ffbaab35d4c0a811a7a6f0e5 ./themes/ductile/locales/fr/resources.php +a0eb02cf98df99cc5b72f1f9b85d03e4 ./themes/ductile/locales/fr/admin.lang.php +64ccfba3a4cc05b19bee205508d29de3 ./themes/ductile/locales/fr/admin.po +b0504f396485c4cdcc469bf7aa6ac72f ./themes/ductile/locales/fr/main.lang.php +b068880d9ebca0eb8ee939edf7a61fdb ./themes/ductile/_prepend.php +41268b2c9f4e7a588033689d7ff9f89c ./themes/ductile/screenshot.jpg +ac71d8654c2c0f52d9f23e5b6c70450d ./themes/ductile/ie.css +c3caa3fdd24ac661593757f9b454f291 ./themes/ductile/ductile.js +757317303623fbac21adbc8747f7b63e ./themes/ductile/_config.php +342e032b31de25bdb8d855712dfceddb ./themes/ductile/rebase.css +f1d67702b54ededa0c493fef0c7dc7f1 ./themes/ductile/mediaqueries.css +28e5e068b8b973e3d1997ba7c24205f2 ./themes/blueSilence/img/background.png +8ebf5666d9d8bac9fdeedbd17daaa348 ./themes/blueSilence/img/report.png +a58dc745b52f9bb33a44fb9ccd5a776d ./themes/blueSilence/img/rss.png +a56d97cb87cfd1970760fff3345af5d8 ./themes/blueSilence/img/tag.png +f7a34b6d7c3641b7afa0132a9723ece3 ./themes/blueSilence/img/top.jpg +f9e699b16c85dd103e725d70efdacd2f ./themes/blueSilence/img/li.png +ab2a5e87e0c395d12a221485be1ff821 ./themes/blueSilence/img/commentaire.png +4c8c6a8fe121912b6cf6a8edaa920aec ./themes/blueSilence/img/tags.png +e8bb6589fe0e7bc810acc4c350f2a54d ./themes/blueSilence/img/commentaire_bulle.png +851317b61d8b6b08d6bb58c06896df21 ./themes/blueSilence/img/attach.png +c1dda264fa5d4120717ba558f01fe652 ./themes/blueSilence/img/footer.png +986f44c4d95f0b4d30defec242f0f76d ./themes/blueSilence/img/retrolien.png +e40bfe45e815e751015bf122e16fb1f2 ./themes/blueSilence/img/sidebar_li.png +3cb85a1d74bceda7858cf0ba4431ada0 ./themes/blueSilence/_define.php +4c99f9d0bd6843a5484740269dd72770 ./themes/blueSilence/style.css +6c0c644258713357329a5bcac1f44768 ./themes/blueSilence/screenshot.jpg +449494368191ae4e7ab977c2a76ab009 ./themes/customCSS/_define.php +d41d8cd98f00b204e9800998ecf8427e ./themes/customCSS/style.css +1d1df50722b311cd831e82a6c49cef11 ./themes/customCSS/_public.php +440252bd94cc1bba46e7b660695d4bd4 ./themes/customCSS/locales/fr/main.po +59bc320d1152143f1519da7733d092ff ./themes/customCSS/locales/fr/main.lang.php +148208db11c31b1be2fa3ea96c5fe63e ./themes/customCSS/_config.php +7d3667a8583b161e345c97abf93b7d13 ./plugins/tags/js/posts_actions.js +8973e836ae4ccefef97120342b2e877c ./plugins/tags/js/jquery.autocomplete.js +8bf49851e4d1322827a052b061d0cc6a ./plugins/tags/js/post.js +deb5c791e787615f52941227f045b37e ./plugins/tags/img/loader.gif +ea424458edbdb1f6c4c01d4486019252 ./plugins/tags/img/tag-add.png +02310f9b96fbe3d9005db4c32c0edfde ./plugins/tags/_define.php +0ab37bb8c62958dadba1c01ce4daa0ae ./plugins/tags/style.css +efb60d58acef17a9f8f2f29b42b9641a ./plugins/tags/_widgets.php +769eb2e4000b8b871ef6a764e356e438 ./plugins/tags/_public.php +87d59c76efc5f658c8bdb77ad1683583 ./plugins/tags/icon.png +543aad10a48eca31afd4e8cd28b2b21e ./plugins/tags/_prepend.php +0984fac02a7059c3da6575a7f04f67bf ./plugins/tags/_admin.php +e1e298db61c4ec7e99161c9788111b4c ./plugins/tags/index.php +84845fa3256c8c15bf3a4ff613cc823b ./plugins/tags/_xmlrpc.php +c365a799f0c5c04facd507116ec18bce ./plugins/tags/tags.php +87ffbdb75c871db5411264cb4aa8c1fb ./plugins/tags/tag_posts.php +88af742960b518262cc6e6d662a408d3 ./plugins/tags/icon-big.png +b078a69a4fc6e58a804df40d6f9402ec ./plugins/tags/default-templates/tag.html +e238ea5276b7333593026d68b02f1ce5 ./plugins/tags/default-templates/tags.html +440acb22b11ba828e45fd27c60810adc ./plugins/maintenance/_define.php +2c604b5d65cc096c1f37906d596a2231 ./plugins/maintenance/icon.png +eaffdb154740265f9dae351b891e2d27 ./plugins/maintenance/locales/en/help/maintenance.html +61add6e9746aded7aab53aee9f91a147 ./plugins/maintenance/locales/en/resources.php +f8ed1b11e9fd0843d7ea40e010f3ae83 ./plugins/maintenance/locales/fr/help/maintenance.html +61add6e9746aded7aab53aee9f91a147 ./plugins/maintenance/locales/fr/resources.php +a60ed9fce76695b5d507e815555a8620 ./plugins/maintenance/_admin.php +5f391589b20df151de1e353e5d3f8253 ./plugins/maintenance/index.php +0bb6cc2229f017adedbc67642496a623 ./plugins/maintenance/icon-big.png +53fcb389435570d224759e1e87723154 ./plugins/pages/list.php +c20960611d6099a96accf85bbe5f7f67 ./plugins/pages/_define.php +dddced2207edb25a187bceb5f855bfa5 ./plugins/pages/page.php +c8744d50e0bf36cdc4613929be5f4f7a ./plugins/pages/_widgets.php +23cb01b0f5290900f359d81e0211b74d ./plugins/pages/_public.php +b1bc318e924e34522709312a12dcc5c2 ./plugins/pages/icon.png +1be0fb2eebf54bfad343c52177a80632 ./plugins/pages/locales/en/help/page.html +12a1a6fee37d25d40c03f9d7234a351c ./plugins/pages/locales/en/help/pages.html +3dce4e7363737b9d0a9b1cb80473a000 ./plugins/pages/locales/en/resources.php +cb15c560f1ce3ca28b218015de39c86e ./plugins/pages/locales/fr/help/page.html +14b6f95e1d372f013a59f022c59c28fc ./plugins/pages/locales/fr/help/pages.html +3dce4e7363737b9d0a9b1cb80473a000 ./plugins/pages/locales/fr/resources.php +bc20a848078bc49837802aa98cbdafa2 ./plugins/pages/_prepend.php +c85dc3b8e26a69a525c8d1af05b321a0 ./plugins/pages/_admin.php +e4d87532945c6948024c19d042e2b886 ./plugins/pages/index.php +d27a7d60e6ba80c4707dcffd3c2dbd5e ./plugins/pages/icon-np-big.png +31089648f09c9203bb9a9f08fa0f8d5c ./plugins/pages/icon-np.png +c14f9c44eb9edbc48e953cb1a21fb972 ./plugins/pages/icon-big.png +075d988db38c1cdfce6a1983067b57f9 ./plugins/pages/default-templates/page.html +51614d28203d0d30b54522c73020931b ./plugins/pings/lib.pings.php +b185af2e3eb0ce2a0bdb5cc86ee498c9 ./plugins/pings/_define.php +9752e15a58ed8ef93751e4db2826e4ac ./plugins/pings/icon.png +ac4b9c80e9f15d9b9f5219dfa44c1849 ./plugins/pings/_admin.php +ddbb5e077b8683fe7b5617c17c9d4520 ./plugins/pings/index.php +69ea448ef9b88ebfb3d055365de751e5 ./plugins/pings/icon-big.png +e074e70b8acc82f6b8353ae2dc240d7b ./plugins/pings/post.js +ce7e454ba349cf30c2180f82df6a0579 ./plugins/attachments/_define.php +0009b6fab4d355467d4752500c895689 ./plugins/attachments/_public.php +df2984cdf2e725a1190d4c4d1a22cd5c ./plugins/attachments/_admin.php +b0943867447d5997118b7bfc34768ce6 ./plugins/antispam/inc/class.dc.spamfilters.php +638c21bdabf86e609981ab1e511de0a5 ./plugins/antispam/inc/lib.dc.antispam.php +345e04b476c4b364769678147705bfd2 ./plugins/antispam/inc/class.dc.spamfilter.php +63d9349114f535ab0731d45302db1fa7 ./plugins/antispam/inc/lib.dc.antispam.url.php +77a77bd9ce6f79c562ecafb0f5dfff04 ./plugins/antispam/_define.php +6678a3fc8c6bcc54e7f2910e4aaeba21 ./plugins/antispam/_install.php +c28748b62667080cf3a0fbf5dd98f7b2 ./plugins/antispam/style.css +6fc18cd4a4dfa2928807c405a8b585fb ./plugins/antispam/_public.php +a48997d0567984d464981b4cde3ff125 ./plugins/antispam/icon.png +39a774a74f940ac72192d6c496a44478 ./plugins/antispam/_prepend.php +1e2deb4234391c489de45137d3e306d2 ./plugins/antispam/filters/class.dc.filter.linkslookup.php +cf86365f13c9e82063ef70f91d1b8963 ./plugins/antispam/filters/class.dc.filter.words.php +26fb77ae2996f1df9d884773aaae7f7a ./plugins/antispam/filters/class.dc.filter.ip.php +ebff2023b0e53322f6c908729df29015 ./plugins/antispam/filters/class.dc.filter.iplookup.php +b2626bad20ac045e3dcf448d9c7b32e0 ./plugins/antispam/_admin.php +316d16dea4cf1fe94cf6a8a7f149b8ec ./plugins/antispam/index.php +b82da4aa0a7b272ae5e2de8dd7359b3a ./plugins/antispam/feed.png +56dd843cb7ae5c0e71ea479c96136352 ./plugins/antispam/antispam.js +83716ca10d30c3fb0cd1c25c75518a72 ./plugins/antispam/icon-big.png +63f54624efa0c327dcc4e6a0f45e5a7b ./plugins/themeEditor/_define.php +df6d24d467b24cf0914f388864ff3000 ./plugins/themeEditor/style.css +597ea9521d22937dbce30b392c6b3d46 ./plugins/themeEditor/_admin.php +c4762effea48c7e75e20ee5936af0484 ./plugins/themeEditor/index.php +70e24bb343ebeb5c4bc427532d5866e0 ./plugins/themeEditor/class.themeEditor.php +740e05113c1d5f825eba114554e096d4 ./plugins/themeEditor/help.html +f75e3f88c0dea27c23c1b02486cf485d ./plugins/themeEditor/script.js +209634bb0238704c4874c35d615ae59e ./plugins/.htaccess +176f0818cbfc87abfd63ea4533dfa9ec ./plugins/blogroll/icon-small.png +ff5ac25482026b10a85a24dc5c95224a ./plugins/blogroll/edit.php +66eac420aa0fb1a5dd92f5f98fba453c ./plugins/blogroll/_define.php +cd90c7acf3cb672aba63312930055195 ./plugins/blogroll/class.dc.importblogroll.php +a7baf0626982efff0d58e3fda94d6aa5 ./plugins/blogroll/_install.php +4390f663a991dc993ba648786bba97f3 ./plugins/blogroll/_widgets.php +e075e330663bb96f131b7c294ba8c430 ./plugins/blogroll/_public.php +a8ff27e2e7e0e204fb06c6154c09a89d ./plugins/blogroll/icon.png +eff29112ce348cff22324fa93fccee39 ./plugins/blogroll/locales/en/help/blogroll.html +febf3b64152a0aefb523a8822998c02c ./plugins/blogroll/locales/en/resources.php +6bfc5a8bf8a197307a289f294d2b34f4 ./plugins/blogroll/locales/fr/help/blogroll.html +febf3b64152a0aefb523a8822998c02c ./plugins/blogroll/locales/fr/resources.php +f655145e14734c5eef2e40489227bc66 ./plugins/blogroll/_prepend.php +fc5215f1cfd1a630bc64516c243159c7 ./plugins/blogroll/_admin.php +a8199396024d0d87716b3e2c39290a19 ./plugins/blogroll/class.dc.blogroll.php +b5bb164e1e73648a5ebfbe91985953cf ./plugins/blogroll/index.php +a7a6b9941ea78fd2055e025f0e1a3abc ./plugins/akismet/_define.php +e77a818a2a09aaf0a7eed4d18b0d8236 ./plugins/akismet/_prepend.php +230313fe9f4089ff7f2ebb3a696be3b2 ./plugins/akismet/class.dc.filter.akismet.php +269e3aa0b8dadeee41f7c68ade7f12de ./plugins/aboutConfig/_define.php +4d78af1bbd7743a86da47683067b1eb7 ./plugins/aboutConfig/icon.png +7c9e3b0ef93fb178c44b0d22fe20e38b ./plugins/aboutConfig/_admin.php +9a9b2b40603945feafea068863a611ab ./plugins/aboutConfig/index.php +16eb7f5b6739a98fdd46606e87ceccb6 ./plugins/aboutConfig/icon-big.png +490168b1d58949a991352f3c8db587fb ./plugins/userPref/_define.php +309e06f46959a3a86b007218e5038e23 ./plugins/userPref/icon.png +c01675d280ae574977f25a082397ecbc ./plugins/userPref/_admin.php +dbec6e054ff4db968ec074772a473bd0 ./plugins/userPref/index.php +feab8bf7358c357f4cd537820fe62490 ./plugins/userPref/icon-big.png +f98d952cb1b5431498700fb3592f739d ./plugins/widgets/_define.php +d077c7c36df3ade673424af06c0b1133 ./plugins/widgets/_install.php +1c204b2811264f93c82dae9ed4e58930 ./plugins/widgets/style.css +5f05e70580892549e068ac89b0b9b168 ./plugins/widgets/widgets.js +5d2f7110de5b4de03cf013c7421e87cc ./plugins/widgets/_widgets_functions.php +7101653d4ef9b55ef29a2e5c7846d052 ./plugins/widgets/_public.php +ede434e0f2087fd062494db6cb0b0c58 ./plugins/widgets/_default_widgets.php +3ce023c7ee7afe5f25f710b11cca9a68 ./plugins/widgets/icon.png +8c9c71d3c0fcd5769bf1a8283749fe26 ./plugins/widgets/class.widgets.php +6c1794028932d3c3c7557d998ecacc40 ./plugins/widgets/_admin.php +926b5589c70fed8f09cd97025ed9e903 ./plugins/widgets/index.php +10b9dd1b33f5f2f74cc92ddac74f3423 ./plugins/widgets/icon-big.png +3b552f7e600dd6c98edc66e7e62ad1fb ./plugins/widgets/dragdrop.js +0fce40ecac55cf0fcdc7e534022bf878 ./plugins/fairTrackbacks/_define.php +15b05e4b999143220e3852ffbdaa15a6 ./plugins/fairTrackbacks/_public.php +bb7fb55037e7045678c3aa812ac4a2ec ./plugins/fairTrackbacks/_prepend.php +5c365c86f9ed6d4a9187c24a3d0b1b43 ./plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php +0fef5c3614e320bdaf2b1342c313e09a ./plugins/blowupConfig/lib/class.blowup.config.php +f57b92ae244d97ea5889240c600a23ac ./plugins/blowupConfig/_define.php +bd71a73523e30fe0d069c3813ad1bcbb ./plugins/blowupConfig/_install.php +8ac968bf6adbb5cfe1397f2e6e229ace ./plugins/blowupConfig/config.js +1a761c6c24723b5c4d5091d1b983a0eb ./plugins/blowupConfig/_public.php +80eb24ad26a2c981ede3a7c01d53b66a ./plugins/blowupConfig/_admin.php +81d1dde973bcadc1728a2eeb81e6c925 ./plugins/blowupConfig/index.php +f3b1ebaf5a27959228b736f87db11776 ./plugins/blowupConfig/alpha-img/page-bg.png +4e391773b51ea336fb0f647cb15379ef ./plugins/blowupConfig/alpha-img/comment-b.png +78e0f7cf458db47eef8b8ff7a241ac8e ./plugins/blowupConfig/alpha-img/comment-t.png +1c7448246376dc8cecee8f0479723099 ./plugins/blowupConfig/alpha-img/page-t/flamingo.png +f4c8777ac3a935e7b9f171a948a78280 ./plugins/blowupConfig/alpha-img/page-t/blank.png +68daa93bbe1ed00ff190cf851033fe93 ./plugins/blowupConfig/alpha-img/page-t/animals.png +a140e3ae9bf5dcc8c3152a315bd17cd3 ./plugins/blowupConfig/alpha-img/page-t/default.png +c1582964ea8ac500d8f1358cbc11b472 ./plugins/blowupConfig/alpha-img/page-t/typo.png +a13d367c5520f17ee7b3a2dd6cd8068f ./plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png +a33e8d4310696d64bb065cddd23b5292 ./plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png +c48b1a888ebd601e610a425c733367fa ./plugins/blowupConfig/alpha-img/page-t/rabbit.png +508813560e6465f057c07287ad40dd88 ./plugins/blowupConfig/alpha-img/page-t/flourish-1.png +7b79aa5c11177401d7b2cac032d09038 ./plugins/blowupConfig/alpha-img/page-t/flourish-2.png +722ad2b43138abc067ade1c4ed6d0297 ./plugins/blowupConfig/alpha-img/page-t/light-trails-1.png +ee440de20028785d2c0a4fd7b25ae060 ./plugins/blowupConfig/alpha-img/page-t/light-trails-2.png +3f9e394ed187b401c4446b77490944b6 ./plugins/blowupConfig/alpha-img/page-t/light-trails-3.png +ca76fc93ebbce051feb507869165ab6d ./plugins/blowupConfig/alpha-img/page-t/light-trails-4.png +4b3117cca2f73c47d5892684443c74fb ./plugins/blowupConfig/alpha-img/page-t/image-mask.png +ffacd6551c2bd0c863740e7c2fcc29e4 ./plugins/blowupConfig/alpha-img/page-t/butterflies.png +652d06f1c3f7057cd7ab6a01d22b72ba ./plugins/blowupConfig/alpha-img/page-b.png +52db59a23ae5743c614bf9457de0167a ./plugins/blowupConfig/alpha-img/gradient-d.png +bd908640438b6cc2088e81df0894c864 ./plugins/blowupConfig/alpha-img/gradient-l.png +d1e0bdff64011042f02910c6518b1d00 ./plugins/blowupConfig/alpha-img/gradient-m.png +81ef6980e71af45e03f56f66fa6fbd8e ./plugins/blowupConfig/help.html +b39d8c8adaaac48561c435648faa0d64 ./plugins/importExport/js/script.js +caa11e523e5a3ed38d90480bed5d5a73 ./plugins/importExport/inc/img/progress.png +425eef5d5e136de5596b0904fda02755 ./plugins/importExport/inc/flat/class.flat.export.php +ca602765dee0850af857ec8decf3a7f1 ./plugins/importExport/inc/flat/class.flat.import.php +0cadbc79aea5b10efd9ee289799cb46c ./plugins/importExport/inc/flat/class.flat.backup.php +d025a6a06fa8030c06782ec7af1d09f5 ./plugins/importExport/inc/class.dc.export.flat.php +5bb7442c4ca029a63f1897de32966115 ./plugins/importExport/inc/class.dc.ieModule.php +ddf1420b9d0a57351e8fff696c6c7580 ./plugins/importExport/inc/class.dc.import.dc1.php +e4cb3020c427f2cfa3ac0a07a5544684 ./plugins/importExport/inc/class.dc.import.wp.php +c3856ce629d23bc6c6093492250a2aa5 ./plugins/importExport/inc/class.dc.import.flat.php +5d4e8f35e0f9b124b627ccb695d050e7 ./plugins/importExport/inc/class.dc.import.feed.php +e34fedf924add536b694e3d19b67eb9e ./plugins/importExport/_define.php +ab75dcc3e4af8dfdf47a4d9144c45a15 ./plugins/importExport/style.css +cf150f4cab469116959f43e9995a3654 ./plugins/importExport/icon.png +e43b7373a9d43b4aa2025ed5b222aa3d ./plugins/importExport/locales/fr/main.po +e9a05d12aeb1c1882b57f4e3912daf76 ./plugins/importExport/locales/fr/main.lang.php +967d9452787afa167e8d4f7f08432da4 ./plugins/importExport/_prepend.php +4b3f4140098e0b3b96a34b0f4ced511d ./plugins/importExport/_admin.php +9169fd905a1a194849221947f99096f5 ./plugins/importExport/index.php +2959b999f06c95bc6582392d13a0b1f0 ./plugins/importExport/icon-big.png +a27e5d730ed8617782a740bf018f4889 ./plugins/simpleMenu/icon-small.png +823184eb803c7e35a800c23f6d11691f ./plugins/simpleMenu/_define.php +c30e0e1fa38e4ece85294b8f1e55c2b3 ./plugins/simpleMenu/_install.php +92128bc19b2eecc265dd09b9584dee88 ./plugins/simpleMenu/_widgets.php +1b09f23aec3d84b3c17f1c8a5fe2c771 ./plugins/simpleMenu/_public.php +f32140a88ecb3e8fd21ae2ed6652f07c ./plugins/simpleMenu/icon.png +5597505935e7696c6f12b180be70ee50 ./plugins/simpleMenu/locales/en/help/help.html +b4ea322bc7ec45b260c803e046d073fe ./plugins/simpleMenu/locales/en/resources.php +61d2df2c7abd41c0dbd9974596583d42 ./plugins/simpleMenu/locales/fr/help/help.html +b4ea322bc7ec45b260c803e046d073fe ./plugins/simpleMenu/locales/fr/resources.php +460286be8fc317eafd75a3ec72452690 ./plugins/simpleMenu/_admin.php +9734092f951bcf93a505bfa15e80960b ./plugins/simpleMenu/index.php +d85775470a54e974bd4a81fe9363f4b4 ./CREDITS diff --git a/v2/dotclear/inc/libs/clearbricks/Makefile b/v2/dotclear/inc/libs/clearbricks/Makefile new file mode 100644 index 0000000..f440738 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/Makefile @@ -0,0 +1,44 @@ +SHELL=/bin/sh + +DIST=_dist +CB=$(DIST)/clearbricks + +default: + @echo "make config or make dist" + +config: + mkdir -p ./$(CB) + + ## Copy needed files and folders + find ./ -mindepth 1 -maxdepth 1 -type d \ + -not -regex '.*svn.*' \ + -not -name '_dist' \ + -not -name 'debian' \ + -exec cp -r \{\} ./$(CB) \; + + ## Copy _common.php and README files + cp _common.php README ./$(CB)/ + + ## Remove .svn folders + find ./$(CB)/ -type d -name '.svn' -print0 | xargs -0 rm -rf + + touch config-stamp + +dist: config dist-tgz dist-zip + +deb: + cp ./README debian/README + dpkg-buildpackage -rfakeroot + +dist-tgz: + [ -f config-stamp ] + cd $(DIST) && tar cfz clearbricks-$$(grep CLEARBRICKS_VERSION clearbricks/common/_main.php | cut -d"'" -f4).tar.gz ./clearbricks + +dist-zip: + [ -f config-stamp ] + cd $(DIST) && zip -r9 clearbricks-$$(grep CLEARBRICKS_VERSION clearbricks/common/_main.php | cut -d"'" -f4).zip ./clearbricks + +clean: + [ -f config-stamp ] + rm -rf $(DIST) + rm -f config-stamp build-stamp configure-stamp diff --git a/v2/dotclear/inc/libs/clearbricks/README b/v2/dotclear/inc/libs/clearbricks/README new file mode 100644 index 0000000..4dadeec --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/README @@ -0,0 +1,67 @@ +Clearbricks +=========== + +What is this? +------------- + +No, Clearbricks is not yet another framework. Clearbricks is not cool at all and +does not even have a nice website. It won't make you smart neither have great +ideas for you. + +Clearbricks is only about code and efficiency, consider it as a toolbox. And +please, do me a favor, don't call it a framework :-) + +How do I use it? +---------------- + +Clearbricks is about "using what we need when we need it". Pick the module(s) +you need and add it to your code. The only thing you'll always need is the +"common" directory. + +Once you're ready, you have to create a _common.php file on top of your +Clearbricks path (or wherever you want) and add $__autoload entries for your +modules. + +Take a look at the _common.php file. + +Of course, you're encouraged to use the $__autoload magic for your own classes. + +Use it with Subversion +---------------------- + +You may think that all this subdirectories is a mess. You're quite right. But +one day, you'll find it cool to use Clearbricks modules in your subversion +repository as some external properties. This day, you'll love me :-) + +Clearbricks subersion repository is located at: +https://clearbricks.org/svn + +Here is an example of using Clearbricks and dbLayer in your own subversion +repository as an external property: + +First, we create a Clearbricks root. + +$ mkdir clearbricks +$ svn add clearbricks +$ svn ci + +And now, add externals definitions + +$ cd clearbricks +$ svn propedit svn:externals . + +Your editor fires up, type: + +common https://clearbricks.org/svn/trunk/common +dblayer https://clearbricks.org/svn/trunk/dblayer + +Save and "svn up". + +Then, you can create a _common.php file wich will contain: + + + +You're done! diff --git a/v2/dotclear/inc/libs/clearbricks/_common.php b/v2/dotclear/inc/libs/clearbricks/_common.php new file mode 100644 index 0000000..cf9e6d8 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/_common.php @@ -0,0 +1,123 @@ + \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/_main.php b/v2/dotclear/inc/libs/clearbricks/common/_main.php new file mode 100644 index 0000000..c3129b3 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/_main.php @@ -0,0 +1,58 @@ + dirname(__FILE__).'/lib.crypt.php', + 'dt' => dirname(__FILE__).'/lib.date.php', + 'files' => dirname(__FILE__).'/lib.files.php', + 'path' => dirname(__FILE__).'/lib.files.php', + 'form' => dirname(__FILE__).'/lib.form.php', + 'formSelectOption' => dirname(__FILE__).'/lib.form.php', + 'html' => dirname(__FILE__).'/lib.html.php', + 'http' => dirname(__FILE__).'/lib.http.php', + 'text' => dirname(__FILE__).'/lib.text.php' +); + +# autoload for clearbricks +function cb_autoload($name) +{ + global $__autoload; + + if (isset($__autoload[$name])) { + require_once $__autoload[$name]; + } +} + +# if php version >= 5.1.2, we can benefit from spl_autoload_register, +# so other libraries can define their own independent autoload too +if (function_exists("spl_autoload_register")) { + spl_autoload_register("cb_autoload"); +} else { + # otherwise we define a classic autoload function for older php... + function __autoload($name) { + cb_autoload($name); + } +} + +# We only need l10n __() function +require_once dirname(__FILE__).'/lib.l10n.php'; + +# We set default timezone to avoid warning +dt::setTZ('UTC'); +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.crypt.php b/v2/dotclear/inc/libs/clearbricks/common/lib.crypt.php new file mode 100644 index 0000000..5975697 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.crypt.php @@ -0,0 +1,77 @@ +$data, using the said $key + * and $hashfunc as hash method (sha1 or md5 are accepted.) + * + * @param string $key Hash key + * @param string $data Data + * @param string $hashfunc Hash function (md5 or sha1) + * @return string + */ + public static function hmac($key,$data,$hashfunc='sha1') + { + $blocksize=64; + if ($hashfunc != 'sha1') { + $hashfunc = 'md5'; + } + + if (strlen($key)>$blocksize) { + $key=pack('H*', $hashfunc($key)); + } + + $key=str_pad($key,$blocksize,chr(0x00)); + $ipad=str_repeat(chr(0x36),$blocksize); + $opad=str_repeat(chr(0x5c),$blocksize); + $hmac = pack('H*',$hashfunc(($key^$opad).pack('H*',$hashfunc(($key^$ipad).$data)))); + return bin2hex($hmac); + } + + /** + * Password generator + * + * Returns an 8 characters random password. + * + * @todo Add a length param + * + * @return string + */ + public static function createPassword() + { + $pwd = array(); + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; + $chars2 = '$!@'; + + foreach (range(0,7) as $i) { + $pwd[] = $chars[rand(0,strlen($chars)-1)]; + } + + $pos1 = array_rand(array(0,1,2,3)); + $pos2 = array_rand(array(4,5,6,7)); + $pwd[$pos1] = $chars2[rand(0,strlen($chars2)-1)]; + $pwd[$pos2] = $chars2[rand(0,strlen($chars2)-1)]; + + return implode('',$pwd); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.date.php b/v2/dotclear/inc/libs/clearbricks/common/lib.date.php new file mode 100644 index 0000000..de9b28a --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.date.php @@ -0,0 +1,260 @@ + $v) { + $g = explode('/',$k); + $tmp[$g[0]][$k] = $v; + } + $res = $tmp; + } + } + + return $res; + } + + private static function _callback($args) + { + $b = array(1=>'_Jan',2=>'_Feb',3=>'_Mar',4=>'_Apr',5=>'_May',6=>'_Jun', + 7=>'_Jul',8=>'_Aug',9=>'_Sep',10=>'_Oct',11=>'_Nov',12=>'_Dec'); + + $B = array(1=>'January',2=>'February',3=>'March',4=>'April', + 5=>'May',6=>'June',7=>'July',8=>'August',9=>'September', + 10=>'October',11=>'November',12=>'December'); + + $a = array(1=>'_Mon',2=>'_Tue',3=>'_Wed',4=>'_Thu',5=>'_Fri', + 6=>'_Sat',0=>'_Sun'); + + $A = array(1=>'Monday',2=>'Tuesday',3=>'Wednesday',4=>'Thursday', + 5=>'Friday',6=>'Saturday',0=>'Sunday'); + + return __(${$args[1]}[(integer) $args[2]]); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.files.php b/v2/dotclear/inc/libs/clearbricks/common/lib.files.php new file mode 100644 index 0000000..e957095 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.files.php @@ -0,0 +1,667 @@ + 'application/vnd.oasis.opendocument.text', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + + 'sxw' => 'application/vnd.sun.xml.writer', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxi' => 'application/vnd.sun.xml.impress', + + 'ppt' => 'application/mspowerpoint', + 'doc' => 'application/msword', + 'xls' => 'application/msexcel', + 'rtf' => 'application/rtf', + + 'pdf' => 'application/pdf', + 'ps' => 'application/postscript', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'json' => 'application/json', + 'xml' => 'application/xml', + + 'bin' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + + 'bz2' => 'application/x-bzip', + 'deb' => 'application/x-debian-package', + 'gz' => 'application/x-gzip', + 'jar' => 'application/x-java-archive', + 'rar' => 'application/rar', + 'rpm' => 'application/x-redhat-package-manager', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-gtar', + 'zip' => 'application/zip', + + 'aiff' => 'audio/x-aiff', + 'ua' => 'audio/basic', + 'mp3' => 'audio/mpeg3', + 'mid' => 'audio/x-midi', + 'midi' => 'audio/x-midi', + 'ogg' => 'application/ogg', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'wav' => 'audio/x-wav', + 'wma' => 'audio/x-ms-wma', + + 'swf' => 'application/x-shockwave-flash', + 'swfl' => 'application/x-shockwave-flash', + + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'ico' => 'image/vnd.microsoft.icon', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'xbm' => 'image/x-xbitmap', + + 'css' => 'text/css', + 'csv' => 'text/csv', + 'js' => 'text/javascript', + 'html' => 'text/html', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'rtf' => 'text/richtext', + 'rtx' => 'text/richtext', + + 'mpg' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'ogv' => 'video/ogg', + 'viv' => 'video/vnd.vivo', + 'vivo' => 'video/vnd.vivo', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mp4' => 'video/mp4', + 'm4v' => 'video/x-m4v', + 'flv' => 'video/x-flv', + 'avi' => 'video/x-msvideo', + 'wmv' => 'video/x-ms-wmv' + ); + + /** + * Directory scanning + * + * Returns a directory child files and directories. + * + * @param string $d Path to scan + * @param boolean $order Order results + * @return array + */ + public static function scandir($d,$order=false) + { + $res = array(); + $dh = @opendir($d); + + if ($dh === false) { + throw new Exception(__('Unable to open directory.')); + } + + while (($f = readdir($dh)) !== false) { + $res[] = $f; + } + closedir($dh); + + sort($res); + if ($order) { + rsort($res); + } + + return $res; + } + + /** + * File extension + * + * Returns a file extension. + * + * @param string $f File name + * @return string + */ + public static function getExtension($f) + { + $f = explode('.',basename($f)); + + if (count($f) <= 1) { return ''; } + + return strtolower($f[count($f)-1]); + } + + /** + * MIME type + * + * Returns a file MIME type, based on static var {@link $mimeType} + * + * @param string $f File name + * @return string + */ + public static function getMimeType($f) + { + $ext = self::getExtension($f); + $types = self::mimeTypes(); + + if (isset($types[$ext])) { + return $types[$ext]; + } else { + return 'text/plain'; + } + } + + /** + * MIME types + * + * Returns all defined MIME types. + * + * @return array + */ + public static function mimeTypes() + { + return self::$mimeType; + } + + /** + * New MIME types + * + * Append new MIME types to defined MIME types. + * + * @param array $tab New MIME types. + */ + public static function registerMimeTypes($tab) + { + self::$mimeType = array_merge(self::$mimeType, $tab); + } + + /** + * Is a file or directory deletable. + * + * Returns true if $f is a file or directory and is deletable. + * + * @param string $f File or directory + * @return boolean + */ + public static function isDeletable($f) + { + if (is_file($f)) { + return is_writable(dirname($f)); + } elseif (is_dir($f)) { + return (is_writable(dirname($f)) && count(files::scandir($f)) <= 2); + } + } + + /** + * Recursive removal + * + * Remove recursively a directory. + * + * @param string $dir Directory patch + * @return boolean + */ + public static function deltree($dir) + { + $current_dir = opendir($dir); + while($entryname = readdir($current_dir)) + { + if (is_dir($dir.'/'.$entryname) and ($entryname != '.' and $entryname!='..')) + { + if (!files::deltree($dir.'/'.$entryname)) { + return false; + } + } + elseif ($entryname != '.' and $entryname!='..') + { + if (!@unlink($dir.'/'.$entryname)) { + return false; + } + } + } + closedir($current_dir); + return @rmdir($dir); + } + + /** + * Touch file + * + * Set file modification time to now. + * + * @param string $f File to change + */ + public static function touch($f) + { + if (is_writable($f)) { + if (function_exists('touch')) { + @touch($f); + } else { + # Very bad hack + @file_put_contents($f,file_get_contents($f)); + } + } + } + + /** + * Directory creation. + * + * Creates directory $f. If $r is true, attempts to create needed parents + * directories. + * + * @param string $f Directory to create + * @param boolean $r Create parent directories + */ + public static function makeDir($f,$r=false) + { + if (empty($f)) { + return; + } + + if (DIRECTORY_SEPARATOR == '\\') { + $f = str_replace('/','\\',$f); + } + + if (is_dir($f)) { + return; + } + + if ($r) + { + $dir = path::real($f,false); + $dirs = array(); + + while (!is_dir($dir)) { + array_unshift($dirs,basename($dir)); + $dir = dirname($dir); + } + + foreach ($dirs as $d) + { + $dir .= DIRECTORY_SEPARATOR.$d; + if ($d != '' && !is_dir($dir)) { + self::makeDir($dir); + } + } + } + else + { + if (@mkdir($f) === false) { + throw new Exception(__('Unable to create directory.')); + } + self::inheritChmod($f); + } + } + + /** + * Mode inheritage + * + * Sets file or directory mode according to its parent. + * + * @param string $file File to change + */ + public static function inheritChmod($file) + { + if (!function_exists('fileperms') || !function_exists('chmod')) { + return false; + } + + if (self::$dir_mode != null) { + return @chmod($file,self::$dir_mode); + } else { + return @chmod($file,fileperms(dirname($file))); + } + } + + /** + * Changes file content. + * + * Writes $f_content into $f file. + * + * @param string $f File to edit + * @param string $f_content Content to write + */ + public static function putContent($f, $f_content) + { + if (file_exists($f) && !is_writable($f)) { + throw new Exception(__('File is not writable.')); + } + + $fp = @fopen($f, 'w'); + + if ($fp === false) { + throw new Exception(__('Unable to open file.')); + } + + fwrite($fp,$f_content,strlen($f_content)); + fclose($fp); + return true; + } + + /** + * Human readable file size. + * + * @param integer $size Bytes + * @return string + */ + public static function size($size) + { + $kb = 1024; + $mb = 1024 * $kb; + $gb = 1024 * $mb; + $tb = 1024 * $gb; + + if($size < $kb) { + return $size." B"; + } + else if($size < $mb) { + return round($size/$kb,2)." KB"; + } + else if($size < $gb) { + return round($size/$mb,2)." MB"; + } + else if($size < $tb) { + return round($size/$gb,2)." GB"; + } + else { + return round($size/$tb,2)." TB"; + } + } + + /** + * Converts a human readable file size to bytes. + * + * @param string $v Size + * @return integer + */ + public static function str2bytes($v) + { + $v = trim($v); + $last = strtolower(substr($v,-1,1)); + + switch($last) + { + case 'g': + $v *= 1024; + case 'm': + $v *= 1024; + case 'k': + $v *= 1024; + } + + return $v; + } + + /** + * Upload status + * + * Returns true if upload status is ok, throws an exception instead. + * + * @param array $file File array as found in $_FILES + * @return boolean + */ + public static function uploadStatus($file) + { + if (!isset($file['error'])) { + throw new Exception(__('Not an uploaded file.')); + } + + switch ($file['error']) { + case UPLOAD_ERR_OK: + return true; + case UPLOAD_ERR_INI_SIZE: + case UPLOAD_ERR_FORM_SIZE: + throw new Exception(__('The uploaded file exceeds the maximum file size allowed.')); + return false; + case UPLOAD_ERR_PARTIAL: + throw new Exception(__('The uploaded file was only partially uploaded.')); + return false; + case UPLOAD_ERR_NO_FILE: + throw new Exception(__('No file was uploaded.')); + return false; + case UPLOAD_ERR_NO_TMP_DIR: + throw new Exception(__('Missing a temporary folder.')); + return false; + case UPLOAD_ERR_CANT_WRITE: + throw new Exception(__('Failed to write file to disk.')); + return false; + default: + return true; + } + } + + # Packages generation methods + # + /** + * Recursive directory scanning + * + * Returns an array of a given directory's content. The array contains + * two arrays: dirs and files. Directory's content is fetched recursively. + * + * @param string $dirName Directory name + * @param array $contents Contents array. Leave it empty + * @return array + */ + public static function getDirList($dirName, &$contents = null) + { + if (!$contents) { + $contents = array('dirs'=> array(),'files' => array()); + } + + $exclude_list=array('.','..','.svn'); + + if (empty($res)) { + $res = array(); + } + + $dirName = preg_replace('|/$|','',$dirName); + + if (!is_dir($dirName)) { + throw new Exception(sprintf(__('%s is not a directory.'),$dirName)); + } + + $contents['dirs'][] = $dirName; + + $d = @dir($dirName); + + if ($d === false) { + throw new Exception(__('Unable to open directory.')); + } + + while($entry = $d->read()) + { + if (!in_array($entry,$exclude_list)) + { + if (is_dir($dirName.'/'.$entry)) + { + files::getDirList($dirName.'/'.$entry, $contents); + } + else + { + $contents['files'][] = $dirName.'/'.$entry; + } + } + } + $d->close(); + + return $contents; + } + + /** + * Filename cleanup + * + * Removes unwanted characters in a filename. + * + * @param string $n Filename + * @return string + */ + public static function tidyFileName($n) + { + $n = text::deaccent($n); + $n = preg_replace('/^[.]/u','',$n); + return preg_replace('/[^A-Za-z0-9._-]/u','_',$n); + } +} + +/** +* Path manipulation utilities +* +* @package Clearbricks +* @subpackage Common +*/ +class path +{ + /** + * Returns the real path of a file. + * + * If parameter $strict is true, file should exist. Returns false if + * file does not exist. + * + * @param string $p Filename + * @param boolean $strict File should exists + * @return string + */ + public static function real($p,$strict=true) + { + $os = (DIRECTORY_SEPARATOR == '\\') ? 'win' : 'nix'; + + # Absolute path? + if ($os == 'win') { + $_abs = preg_match('/^\w+:/',$p); + } else { + $_abs = substr($p,0,1) == '/'; + } + + # Standard path form + if ($os == 'win') { + $p = str_replace('\\','/',$p); + } + + # Adding root if !$_abs + if (!$_abs) { + $p = dirname($_SERVER['SCRIPT_FILENAME']).'/'.$p; + } + + # Clean up + $p = preg_replace('|/+|','/',$p); + + if (strlen($p) > 1) { + $p = preg_replace('|/$|','',$p); + } + + $_start = ''; + if ($os == 'win') { + list($_start,$p) = explode(':',$p); + $_start .= ':/'; + } else { + $_start = '/'; + } + $p = substr($p,1); + + # Go through + $P = explode('/',$p); + $res = array(); + + for ($i=0;$i 0) { + array_pop($res); + } + } else { + array_push($res,$P[$i]); + } + } + + $p = $_start.implode('/',$res); + + if ($strict && !@file_exists($p)) { + return false; + } + + return $p; + } + + /** + * Returns a clean file path + * + * @param string $p File path + * @return string + */ + public static function clean($p) + { + $p = str_replace('..','',$p); + $p = preg_replace('|/{2,}|','/',$p); + $p = preg_replace('|/$|','',$p); + + return $p; + } + + /** + * Path information + * + * Returns an array of information: + * - dirname + * - basename + * - extension + * - base (basename without extension) + * + * @param string $f File path + */ + public static function info($f) + { + $p = pathinfo($f); + $res = array(); + + $res['dirname'] = $p['dirname']; + $res['basename'] = $p['basename']; + $res['extension'] = isset($p['extension']) ? $p['extension'] : ''; + $res['base'] = preg_replace('/\.'.preg_quote($res['extension'],'/').'$/','',$res['basename']); + + return $res; + } + + /** + * Full path with root + * + * Returns a path with root concatenation unless path begins with a slash + * + * @param string $p File path + * @param string $root Root path + * @return string + */ + public static function fullFromRoot($p,$root) + { + if (substr($p,0,1) == '/') { + return $p; + } + + return $root.'/'.$p; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.form.php b/v2/dotclear/inc/libs/clearbricks/common/lib.form.php new file mode 100644 index 0000000..398fe5c --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.form.php @@ -0,0 +1,347 @@ +'."\n"; + + $res .= self::comboOptions($data,$default); + + $res .= ''."\n"; + + return $res; + } + + private static function comboOptions($data,$default) + { + $res = ''; + $option = ''."\n"; + $optgroup = ''."\n".'%2$s'."\n"; + + foreach($data as $k => $v) + { + if (is_array($v)) { + $res .= sprintf($optgroup,$k,self::comboOptions($v,$default)); + } elseif ($v instanceof formSelectOption) { + $res .= $v->render($default); + } else { + $s = ($v == $default) ? ' selected="selected"' : ''; + $res .= sprintf($option,$v,$k,$s); + } + } + + return $res; + } + + /** + * Radio button + * + * Returns HTML code for a radio button. $nid could be a string or an array of + * name and ID. + * + * @param string|array $nid Element ID and name + * @param string $value Element value + * @param boolean $checked True if checked + * @param string $class Element class name + * @param string $tabindex Element tabindex + * @param boolean $disabled True if disabled + * @param string $extra_html Extra HTML attributes + * + * @return string + */ + public static function radio($nid, $value, $checked='', $class='', $tabindex='', + $disabled=false, $extra_html='') + { + self::getNameAndId($nid,$name,$id); + + $res = ''; + $res .= $default; + $res .= ''; + + return $res; + } + + /** + * Hidden field + * + * Returns HTML code for an hidden field. $nid could be a string or an array of + * name and ID. + * + * @param string|array $nid Element ID and name + * @param string $value Element value + * + * @return string + */ + public static function hidden($nid,$value) + { + self::getNameAndId($nid,$name,$id); + + $res = 'name = $name; + $this->value = $value; + $this->class_name = $class_name; + $this->html = $html; + } + + /** + * Option renderer + * + * Returns option HTML code + * + * @param boolean $default Option is selected + * @return string + */ + public function render($default) + { + $attr = $this->html; + $attr .= $this->class_name ? ' class="'.$this->class_name.'"' : ''; + + if ($this->value == $default) { + $attr .= ' selected="selected"'; + } + + return sprintf($this->option,$this->value,$this->name,$attr)."\n"; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.html.php b/v2/dotclear/inc/libs/clearbricks/common/lib.html.php new file mode 100644 index 0000000..20aba63 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.html.php @@ -0,0 +1,185 @@ + "'" + ); + + $str = str_replace(array_keys($extra),array_values($extra),$str); + + return html_entity_decode($str,ENT_QUOTES,'UTF-8'); + } + + /** + * Remove markup + * + * Removes every tags, comments, cdata from string + * + * @param string $str String to clean + * @return string + */ + public static function clean($str) + { + $str = strip_tags($str); + return $str; + } + + /** + * Javascript escape + * + * Returns a protected JavaScript string + * + * @param string $str String to protect + * @return string + */ + public static function escapeJS($str) + { + $str = htmlspecialchars($str,ENT_NOQUOTES,'UTF-8'); + $str = str_replace("'","\'",$str); + $str = str_replace('"','\"',$str); + return $str; + } + + /** + * URL escape + * + * Returns an escaped URL string for HTML content + * + * @param string $str String to escape + * @return string + */ + public static function escapeURL($str) + { + return str_replace('&','&',$str); + } + + /** + * URL sanitize + * + * Encode every parts between / in url + * + * @param string $str String to satinyze + * @return string + */ + public static function sanitizeURL($str) + { + return str_replace('%2F','/',rawurlencode($str)); + } + + /** + * Remove host in URL + * + * Removes host part in URL + * + * @param string $str URL to transform + * @return string + */ + public static function stripHostURL($url) + { + return preg_replace('|^[a-z]{3,}://.*?(/.*$)|','$1',$url); + } + + /** + * Set links to absolute ones + * + * Appends $root URL to URIs attributes in $str. + * + * @param string $str HTML to transform + * @param string $root Base URL + * @return string + */ + public static function absoluteURLs($str,$root) + { + self::$url_root = $root; + $attr = 'action|background|cite|classid|codebase|data|href|longdesc|profile|src|usemap'; + + $str = preg_replace_callback('/((?:'.$attr.')=")(.*?)(")/msu',array('self','absoluteURLHandler'),$str); + + foreach (self::$absolute_regs as $r) { + $str = preg_replace_callback($r,array('self','absoluteURLHandler'),$str); + } + + self::$url_root = null; + return $str; + } + + private static function absoluteURLHandler($m) + { + $url = $m[2]; + + $link = str_replace('%','%%',$m[1]).'%s'.str_replace('%','%%',$m[3]); + $host = preg_replace('|^([a-z]{3,}://)(.*?)/(.*)$|','$1$2',self::$url_root); + + $parse = parse_url($m[2]); + if (empty($parse['scheme'])) + { + if (strpos($url,'/') === 0) { + $url = $host.$url; + } elseif (strpos($url,'#') === 0) { + $url = self::$url_root.$url; + } elseif (preg_match('|/$|',self::$url_root)) { + $url = self::$url_root.$url; + } else { + $url = dirname(self::$url_root).'/'.$url; + } + } + + return sprintf($link,$url); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.http.php b/v2/dotclear/inc/libs/clearbricks/common/lib.http.php new file mode 100644 index 0000000..7fec31a --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.http.php @@ -0,0 +1,452 @@ +[a-z]{2}(?:-[a-z]{2})?)(?:;q=(?P[.0-9]*))?/'; + if (preg_match_all($pattern,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$acclang) !== false) + { + foreach($acclang['priority'] as $i => $p) + { + if ($p == '') $acclang['priority'][$i]=1; + } + array_multisort($acclang['priority'], SORT_DESC,$acclang['lang']); + } + return $acclang['lang']; + } else { + return $acclang; + } + } + + /** + * HTTP Cache + * + * Sends HTTP cache headers (304) according to a list of files and an optionnal. + * list of timestamps. + * + * @param array $files Files on which check mtime + * @param array $mod_ts List of timestamps + */ + public static function cache($files,$mod_ts=array()) + { + if (empty($files) || !is_array($files)) { + return; + } + + array_walk($files,create_function('&$v','$v = filemtime($v);')); + + $array_ts = array_merge($mod_ts,$files); + + rsort($array_ts); + $now = time(); + $ts = min($array_ts[0],$now); + + $since = null; + if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + $since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + $since = preg_replace ('/^(.*)(Mon|Tue|Wed|Thu|Fri|Sat|Sun)(.*)(GMT)(.*)/', '$2$3 GMT', $since); + $since = strtotime($since); + $since = ($since <= $now) ? $since : null; + } + + # Common headers list + $headers[] = 'Last-Modified: '.gmdate('D, d M Y H:i:s',$ts).' GMT'; + $headers[] = 'Cache-Control: must-revalidate, max-age='.abs((integer) self::$cache_max_age); + $headers[] = 'Pragma:'; + + if ($since >= $ts) + { + self::head(304,'Not Modified'); + foreach ($headers as $v) { + header($v); + } + exit; + } + else + { + header('Date: '.gmdate('D, d M Y H:i:s',$now).' GMT'); + foreach ($headers as $v) { + header($v); + } + } + } + + /** + * HTTP Etag + * + * Sends HTTP cache headers (304) according to a list of etags in client request. + * + * @param string $p_content Response page content + */ + public static function etag() + { + # We create an etag from all arguments + $args = func_get_args(); + if (empty($args)) { + return; + } + + $etag = '"'.md5(implode('',$args)).'"'; + unset($args); + + header('ETag: '.$etag); + + # Do we have a previously sent content? + if (!empty($_SERVER['HTTP_IF_NONE_MATCH'])) + { + foreach (explode(',',$_SERVER['HTTP_IF_NONE_MATCH']) as $i) + { + if (stripslashes(trim($i)) == $etag) { + self::head(304,'Not Modified'); + exit; + } + } + } + } + + /** + * HTTP Header + * + * Sends an HTTP code and message to client. + * + * @param string $code HTTP code + * @param string $msg Message + */ + public static function head($code,$msg=null) + { + $status_mode = preg_match('/cgi/',PHP_SAPI); + + if (!$msg) + { + $msg_codes = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + + $msg = isset($msg_codes[$code]) ? $msg_codes[$code] : '-'; + } + + if ($status_mode) { + header('Status: '.$code.' '.$msg); + } else { + header($msg, true, $code); + } + } + + /** + * Trim request + * + * Trims every value in GET, POST, REQUEST and COOKIE vars. + * Removes magic quotes if magic_quote_gpc is on. + */ + public static function trimRequest() + { + if(!empty($_GET)) { + array_walk($_GET,array('self','trimRequestInVar')); + } + if(!empty($_POST)) { + array_walk($_POST,array('self','trimRequestInVar')); + } + if(!empty($_REQUEST)) { + array_walk($_REQUEST,array('self','trimRequestInVar')); + } + if(!empty($_COOKIE)) { + array_walk($_COOKIE,array('self','trimRequestInVar')); + } + } + + /* + * + * To be deleted? + * -- saymonz, 29.04.2011 + * + //* + + private static function trimRequestHandler(&$v,$key) + { + $v = self::trimRequestInVar($v); + } + + //*/ + + private static function trimRequestInVar(&$value,$key) + { + if (is_array($value)) + { + foreach ($value as $k => &$v) + { + if (is_array($v)) { + self::trimRequestInVar($v,$k); + } else { + if (get_magic_quotes_gpc()) { + $v = stripslashes($v); + } + $v = trim($v); + } + } + } + else + { + if (get_magic_quotes_gpc()) { + $value = stripslashes($value); + } + $value = trim($value); + } + } + + /** + * Unset global variables + * + * If register_globals is on, removes every GET, POST, COOKIE, REQUEST, SERVER, + * ENV, FILES vars from GLOBALS. + */ + public static function unsetGlobals() + { + if (!ini_get('register_globals')) { + return; + } + + if (isset($_REQUEST['GLOBALS'])) { + throw new Exception('GLOBALS overwrite attempt detected'); + } + + # Variables that shouldn't be unset + $no_unset = array('GLOBALS','_GET','_POST','_COOKIE','_REQUEST', + '_SERVER','_ENV','_FILES'); + + $input = array_merge($_GET,$_POST,$_COOKIE,$_SERVER,$_ENV,$_FILES, + (isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array())); + + foreach ($input as $k => $v) { + if (!in_array($k,$no_unset) && isset($GLOBALS[$k]) ) { + $GLOBALS[$k] = null; + unset($GLOBALS[$k]); + } + } + } +} +?> diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.l10n.php b/v2/dotclear/inc/libs/clearbricks/common/lib.l10n.php new file mode 100644 index 0000000..63543d0 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.l10n.php @@ -0,0 +1,453 @@ + 'Afaraf', + 'ab' => 'Аҧсуа', + 'ae' => 'Avesta', + 'af' => 'Afrikaans', + 'ak' => 'Akan', + 'am' => 'አማርኛ', + 'an' => 'Aragonés', + 'ar' => '‫العربية', + 'as' => 'অসমীয়া', + 'av' => 'авар мацӀ', + 'ay' => 'Aymar aru', + 'az' => 'Azərbaycan dili', + 'ba' => 'башҡорт теле', + 'be' => 'Беларуская', + 'bg' => 'български език', + 'bh' => 'भोजपुरी', + 'bi' => 'Bislama', + 'bm' => 'Bamanankan', + 'bn' => 'বাংলা', + 'bo' => 'བོད་ཡིག', + 'br' => 'Brezhoneg', + 'bs' => 'Bosanski jezik', + 'ca' => 'Català', + 'ce' => 'нохчийн мотт', + 'ch' => 'Chamoru', + 'co' => 'Corsu', + 'cr' => 'ᓀᐦᐃᔭᐍᐏᐣ', + 'cs' => 'Česky', + 'cu' => 'ѩзыкъ Словѣньскъ', + 'cv' => 'чӑваш чӗлхи', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch', + 'dv' => '‫ދިވެހި', + 'dz' => 'རྫོང་ཁ', + 'ee' => 'Ɛʋɛgbɛ', + 'el' => 'Ελληνικά', + 'en' => 'English', + 'eo' => 'Esperanto', + 'es' => 'español', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => '‫فارسی', + 'ff' => 'Fulfulde', + 'fi' => 'Suomen kieli', + 'fj' => 'Vosa Vakaviti', + 'fo' => 'Føroyskt', + 'fr' => 'Français', + 'fy' => 'Frysk', + 'ga' => 'Gaeilge', + 'gd' => 'Gàidhlig', + 'gl' => 'Galego', + 'gn' => "Avañe'ẽ", + 'gu' => 'ગુજરાતી', + 'gv' => 'Ghaelg', + 'ha' => '‫هَوُسَ', + 'he' => '‫עברית', + 'hi' => 'हिन्दी', + 'ho' => 'Hiri Motu', + 'hr' => 'Hrvatski', + 'ht' => 'Kreyòl ayisyen', + 'hu' => 'Magyar', + 'hy' => 'Հայերեն', + 'hz' => 'Otjiherero', + 'ia' => 'Interlingua', + 'id' => 'Bahasa Indonesia', + 'ie' => 'Interlingue', + 'ig' => 'Igbo', + 'ii' => 'ꆇꉙ', + 'ik' => 'Iñupiaq', + 'io' => 'Ido', + 'is' => 'Íslenska', + 'it' => 'Italiano', + 'iu' => 'ᐃᓄᒃᑎᑐᑦ', + 'ja' => '日本語', + 'jv' => 'Basa Jawa', + 'ka' => 'ქართული', + 'kg' => 'KiKongo', + 'ki' => 'Gĩkũyũ', + 'kj' => 'Kuanyama', + 'kk' => 'Қазақ тілі', + 'kl' => 'Kalaallisut', + 'km' => 'ភាសាខ្មែរ', + 'kn' => 'ಕನ್ನಡ', + 'ko' => '한국어', + 'kr' => 'Kanuri', + 'ks' => 'कश्मीरी', + 'ku' => 'Kurdî', + 'kv' => 'коми кыв', + 'kw' => 'Kernewek', + 'ky' => 'кыргыз тили', + 'la' => 'Latine', + 'lb' => 'Lëtzebuergesch', + 'lg' => 'Luganda', + 'li' => 'Limburgs', + 'ln' => 'Lingála', + 'lo' => 'ພາສາລາວ', + 'lt' => 'Lietuvių kalba', + 'lu' => 'Luba-Katanga ', + 'lv' => 'Latviešu valoda', + 'mg' => 'Malagasy fiteny', + 'mh' => 'Kajin M̧ajeļ', + 'mi' => 'Te reo Māori', + 'mk' => 'македонски јазик', + 'ml' => 'മലയാളം', + 'mn' => 'Монгол', + 'mo' => 'Limba moldovenească', + 'mr' => 'मराठी', + 'ms' => 'Bahasa Melayu', + 'mt' => 'Malti', + 'my' => 'ဗမာစာ', + 'na' => 'Ekakairũ Naoero', + 'nb' => 'Norsk bokmål', + 'nd' => 'isiNdebele', + 'ne' => 'नेपाली', + 'ng' => 'Owambo', + 'nl' => 'Nederlands', + 'nl-be' => 'Nederlands (Belgium)', + 'nn' => 'Norsk nynorsk', + 'no' => 'Norsk', + 'nr' => 'Ndébélé', + 'nv' => 'Diné bizaad', + 'ny' => 'ChiCheŵa', + 'oc' => 'Occitan', + 'oj' => 'ᐊᓂᔑᓈᐯᒧᐎᓐ', + 'om' => 'Afaan Oromoo', + 'or' => 'ଓଡ଼ିଆ', + 'os' => 'Ирон æвзаг', + 'pa' => 'ਪੰਜਾਬੀ', + 'pi' => 'पाऴि', + 'pl' => 'Polski', + 'ps' => '‫پښتو', + 'pt' => 'Português', + 'pt-br' => 'Português (Brasil)', + 'qu' => 'Runa Simi', + 'rm' => 'Rumantsch grischun', + 'rn' => 'kiRundi', + 'ro' => 'Română', + 'ru' => 'Русский', + 'rw' => 'IKinyarwanda', + 'sa' => 'संस्कृतम्', + 'sc' => 'sardu', + 'sd' => 'सिन्धी', + 'se' => 'Davvisámegiella', + 'sg' => 'Yângâ tî sängö', + 'sh' => 'SrpskoHrvatski', + 'si' => 'සිංහල', + 'sk' => 'Slovenčina', + 'sl' => 'Slovenščina', + 'sm' => "Gagana fa'a Samoa", + 'sn' => 'chiShona', + 'so' => 'Soomaaliga', + 'sq' => 'Shqip', + 'sr' => 'српски језик', + 'ss' => 'SiSwati', + 'st' => 'seSotho', + 'su' => 'Basa Sunda', + 'sv' => 'Svenska', + 'sw' => 'Kiswahili', + 'ta' => 'தமிழ்', + 'te' => 'తెలుగు', + 'tg' => 'тоҷикӣ', + 'th' => 'ไทย', + 'ti' => 'ትግርኛ', + 'tk' => 'Türkmen', + 'tl' => 'Tagalog', + 'tn' => 'seTswana', + 'to' => 'faka Tonga', + 'tr' => 'Türkçe', + 'ts' => 'xiTsonga', + 'tt' => 'татарча', + 'tw' => 'Twi', + 'ty' => 'Reo Mā`ohi', + 'ug' => 'Uyƣurqə', + 'uk' => 'Українська', + 'ur' => '‫اردو', + 'uz' => "O'zbek", + 've' => 'tshiVenḓa', + 'vi' => 'Tiếng Việt', + 'vo' => 'Volapük', + 'wa' => 'Walon', + 'wo' => 'Wollof', + 'xh' => 'isiXhosa', + 'yi' => '‫ייִדיש', + 'yo' => 'Yorùbá', + 'za' => 'Saɯ cueŋƅ', + 'zh' => '中文', + 'zh-hk' => '中文 (香港)', + 'zh-tw' => '中文 (臺灣)', + 'zu' => 'isiZulu' + ); + } + + $langs = self::$langs; + if ($name_with_code) { + foreach ($langs as $k => &$v) { + $v = $k.' - '.$v; + } + } + + if ($flip) { + return array_flip($langs); + } + + return $langs; + } + + /** + * Text direction + * + * Returns text direction for a given language. + * If text direction was forced with {@link $text_direction}, returns this + * value. + * + * @param string $lang Language code + * @return string + */ + public static function getTextDirection($lang) + { + if (self::$text_direction) { + return self::$text_direction; + } + + if (preg_match('/^(ar|dv|fa|ha|he|ps|ur|yi)$/i',$lang)) { + return 'rtl'; + } + return 'ltr'; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/lib.text.php b/v2/dotclear/inc/libs/clearbricks/common/lib.text.php new file mode 100644 index 0000000..f4483cf --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/lib.text.php @@ -0,0 +1,355 @@ + $p) { + $str = preg_replace('/['.$p.']/u',$r,$str); + } + + return $str; + } + + /** + * String to URL + * + * Transforms a string to a proper URL. + * + * @param string $str String to transform + * @param boolean $with_slashes Keep slashes in URL + * @return string + */ + public static function str2URL($str,$with_slashes=true) + { + $str = self::deaccent($str); + $str = preg_replace('/[^A-Za-z0-9_\s\'\:\/[\]-]/','',$str); + + return self::tidyURL($str,$with_slashes); + } + + /** + * URL cleanup + * + * @param string $str URL to tidy + * @param boolean $keep_slashes Keep slashes in URL + * @param boolean $keep_spaces Keep spaces in URL + * @return string + */ + public static function tidyURL($str,$keep_slashes=true,$keep_spaces=false) + { + $str = strip_tags($str); + $str = str_replace(array('?','&','#','=','+','<','>','"','%'),'',$str); + $str = str_replace("'",' ',$str); + $str = preg_replace('/[\s]+/u',' ',trim($str)); + + if (!$keep_slashes) { + $str = str_replace('/','-',$str); + } + + if (!$keep_spaces) { + $str = str_replace(' ','-',$str); + } + + $str = preg_replace('/[-]+/','-',$str); + + # Remove path changes in URL + $str = preg_replace('%^/%','',$str); + $str = preg_replace('%\.+/%','',$str); + + return $str; + } + + /** + * Cut string + * + * Returns a cuted string on spaced at given length $l. + * + * @param string $str String to cut + * @param integer $l Length to keep + * @return string + */ + public static function cutString($str,$l) + { + $s = preg_split('/([\s]+)/u',$str,-1,PREG_SPLIT_DELIM_CAPTURE); + + $res = ''; + $L = 0; + + if (mb_strlen($s[0]) >= $l) { + return mb_substr($s[0],0,$l); + } + + foreach ($s as $v) + { + $L = $L+mb_strlen($v); + + if ($L > $l) { + break; + } else { + $res .= $v; + } + } + + return trim($res); + } + + /** + * Split words + * + * Returns an array of words from a given string. + * + * @param string $str Words to split + * @return array + */ + public static function splitWords($str) + { + $non_word = '\x{0000}-\x{002F}\x{003A}-\x{0040}\x{005b}-\x{0060}\x{007B}-\x{007E}\x{00A0}-\x{00BF}\s'; + if (preg_match_all('/([^'.$non_word.']{3,})/msu',html::clean($str),$match)) { + foreach ($match[1] as $i => $v) { + $match[1][$i] = mb_strtolower($v); + } + return $match[1]; + } + return array(); + } + + /** + * Encoding detection + * + * Returns the encoding (in lowercase) of given $str. + * + * @param string $str String + * @return string + */ + public static function detectEncoding($str) + { + return strtolower(mb_detect_encoding($str.' ', + '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')); + } + + /** + * UTF8 conversions + * + * Returns an UTF-8 converted string. If $encoding is not specified, the + * function will try to detect encoding. + * + * @param string $str String to convert + * @param string $encoding Optionnal "from" encoding + * @return string + */ + public static function toUTF8($str,$encoding=null) + { + if (!$encoding) { + $encoding = self::detectEncoding($str); + } + + if ($encoding != 'utf-8') { + $str = iconv($encoding,'UTF-8',$str); + } + + return $str; + } + + /** + * Find bad UTF8 tokens + * + * Locates the first bad byte in a UTF-8 string returning it's + * byte index in the string + * PCRE Pattern to locate bad bytes in a UTF-8 string + * Comes from W3 FAQ: Multilingual Forms + * Note: modified to include full ASCII range including control chars + * + * @copyright Harry Fuecks + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU LGPL 2.1 + * @link http://phputf8.sourceforge.net + * + * @param string $str String to search + * @return integer|false + */ + public static function utf8badFind($str) + { + $UTF8_BAD = + '([\x00-\x7F]'. # ASCII (including control chars) + '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte + '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs + '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte + '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates + '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 + '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 + '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 + '|(.{1}))'; # invalid byte + $pos = 0; + $badList = array(); + + while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { + $bytes = strlen($matches[0]); + if ( isset($matches[2])) { + return $pos; + } + $pos += $bytes; + $str = substr($str,$bytes); + } + return false; + } + + /** + * UTF8 cleanup + * + * Replaces non utf8 bytes in $str by $repl. + * + * @copyright Harry Fuecks + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU LGPL 2.1 + * @link http://phputf8.sourceforge.net + * + * @param string $str String to clean + * @param string $repl Replacement string + * @return string + */ + public static function cleanUTF8($str,$repl='?') + { + while (($bad_index = self::utf8badFind($str)) !== false) { + $str = substr_replace($str,$repl,$bad_index,1); + } + + return $str; + } + + /** + * BOM removal + * + * Removes BOM from the begining of a string if present. + * + * @param string $str String to clean + * @return string + */ + public static function removeBOM($str) + { + if (substr_count($str,'')) { + return str_replace('','',$str); + } + + return $str; + } + + /** + * Quoted printable conversion + * + * Encodes given str to quoted printable + * + * @param string $str String to encode + * @return string + */ + public static function QPEncode($str) + { + $res = ''; + + foreach (preg_split("/\r?\n/msu", $str) as $line) + { + $l = ''; + preg_match_all('/./',$line,$m); + + foreach ($m[0] as $c) + { + $a = ord($c); + + if ($a < 32 || $a == 61 || $a > 126) { + $c = sprintf('=%02X',$a); + } + + $l .= $c; + } + + $res .= $l."\r\n"; + } + return $res; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/common/tz.dat b/v2/dotclear/inc/libs/clearbricks/common/tz.dat new file mode 100644 index 0000000..09cc367 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/common/tz.dat @@ -0,0 +1,407 @@ +Africa/Abidjan +Africa/Accra +Africa/Addis_Ababa +Africa/Algiers +Africa/Asmera +Africa/Bamako +Africa/Bangui +Africa/Banjul +Africa/Bissau +Africa/Blantyre +Africa/Brazzaville +Africa/Bujumbura +Africa/Cairo +Africa/Casablanca +Africa/Ceuta +Africa/Conakry +Africa/Dakar +Africa/Dar_es_Salaam +Africa/Djibouti +Africa/Douala +Africa/El_Aaiun +Africa/Freetown +Africa/Gaborone +Africa/Harare +Africa/Johannesburg +Africa/Kampala +Africa/Khartoum +Africa/Kigali +Africa/Kinshasa +Africa/Lagos +Africa/Libreville +Africa/Lome +Africa/Luanda +Africa/Lubumbashi +Africa/Lusaka +Africa/Malabo +Africa/Maputo +Africa/Maseru +Africa/Mbabane +Africa/Mogadishu +Africa/Monrovia +Africa/Nairobi +Africa/Ndjamena +Africa/Niamey +Africa/Nouakchott +Africa/Ouagadougou +Africa/Porto-Novo +Africa/Sao_Tome +Africa/Timbuktu +Africa/Tripoli +Africa/Tunis +Africa/Windhoek + + +America/Adak +America/Anchorage +America/Anguilla +America/Antigua +America/Araguaina +America/Argentina/Buenos_Aires +America/Argentina/Catamarca +America/Argentina/ComodRivadavia +America/Argentina/Cordoba +America/Argentina/Jujuy +America/Argentina/La_Rioja +America/Argentina/Mendoza +America/Argentina/Rio_Gallegos +America/Argentina/San_Juan +America/Argentina/Tucuman +America/Argentina/Ushuaia +America/Aruba +America/Asuncion +America/Bahia +America/Barbados +America/Belem +America/Belize +America/Boa_Vista +America/Bogota +America/Boise +America/Cambridge_Bay +America/Campo_Grande +America/Cancun +America/Caracas +America/Cayenne +America/Cayman +America/Chicago +America/Chihuahua +America/Costa_Rica +America/Cuiaba +America/Curacao +America/Danmarkshavn +America/Dawson +America/Dawson_Creek +America/Denver +America/Detroit +America/Dominica +America/Edmonton +America/Eirunepe +America/El_Salvador +America/Fortaleza +America/Glace_Bay +America/Godthab +America/Goose_Bay +America/Grand_Turk +America/Grenada +America/Guadeloupe +America/Guatemala +America/Guayaquil +America/Guyana +America/Halifax +America/Havana +America/Hermosillo +America/Indiana/Indianapolis +America/Indiana/Knox +America/Indiana/Marengo +America/Indiana/Vevay +America/Indianapolis +America/Inuvik +America/Iqaluit +America/Jamaica +America/Juneau +America/Kentucky/Louisville +America/Kentucky/Monticello +America/La_Paz +America/Lima +America/Los_Angeles +America/Louisville +America/Maceio +America/Managua +America/Manaus +America/Martinique +America/Mazatlan +America/Menominee +America/Merida +America/Mexico_City +America/Miquelon +America/Monterrey +America/Montevideo +America/Montreal +America/Montserrat +America/Nassau +America/New_York +America/Nipigon +America/Nome +America/Noronha +America/North_Dakota/Center +America/Panama +America/Pangnirtung +America/Paramaribo +America/Phoenix +America/Port-au-Prince +America/Port_of_Spain +America/Porto_Velho +America/Puerto_Rico +America/Rainy_River +America/Rankin_Inlet +America/Recife +America/Regina +America/Rio_Branco +America/Santiago +America/Santo_Domingo +America/Sao_Paulo +America/Scoresbysund +America/Shiprock +America/St_Johns +America/St_Kitts +America/St_Lucia +America/St_Thomas +America/St_Vincent +America/Swift_Current +America/Tegucigalpa +America/Thule +America/Thunder_Bay +America/Tijuana +America/Toronto +America/Tortola +America/Vancouver +America/Whitehorse +America/Winnipeg +America/Yakutat +America/Yellowknife + + +Antarctica/Casey +Antarctica/Davis +Antarctica/DumontDUrville +Antarctica/Mawson +Antarctica/McMurdo +Antarctica/Palmer +Antarctica/Rothera +Antarctica/South_Pole +Antarctica/Syowa +Antarctica/Vostok + + +Arctic/Longyearbyen + + +Asia/Aden +Asia/Almaty +Asia/Amman +Asia/Anadyr +Asia/Aqtau +Asia/Aqtobe +Asia/Ashgabat +Asia/Baghdad +Asia/Bahrain +Asia/Baku +Asia/Bangkok +Asia/Beirut +Asia/Bishkek +Asia/Brunei +Asia/Calcutta +Asia/Choibalsan +Asia/Chongqing +Asia/Colombo +Asia/Damascus +Asia/Dhaka +Asia/Dili +Asia/Dubai +Asia/Dushanbe +Asia/Gaza +Asia/Harbin +Asia/Hong_Kong +Asia/Hovd +Asia/Irkutsk +Asia/Istanbul +Asia/Jakarta +Asia/Jayapura +Asia/Jerusalem +Asia/Kabul +Asia/Kamchatka +Asia/Karachi +Asia/Kashgar +Asia/Katmandu +Asia/Krasnoyarsk +Asia/Kuala_Lumpur +Asia/Kuching +Asia/Kuwait +Asia/Macau +Asia/Magadan +Asia/Makassar +Asia/Manila +Asia/Muscat +Asia/Nicosia +Asia/Novosibirsk +Asia/Omsk +Asia/Oral +Asia/Phnom_Penh +Asia/Pontianak +Asia/Pyongyang +Asia/Qatar +Asia/Qyzylorda +Asia/Rangoon +Asia/Riyadh +Asia/Saigon +Asia/Sakhalin +Asia/Samarkand +Asia/Seoul +Asia/Shanghai +Asia/Singapore +Asia/Taipei +Asia/Tashkent +Asia/Tbilisi +Asia/Tehran +Asia/Thimphu +Asia/Tokyo +Asia/Ulaanbaatar +Asia/Urumqi +Asia/Vientiane +Asia/Vladivostok +Asia/Yakutsk +Asia/Yekaterinburg +Asia/Yerevan + + +Atlantic/Azores +Atlantic/Bermuda +Atlantic/Canary +Atlantic/Cape_Verde +Atlantic/Faeroe +Atlantic/Jan_Mayen +Atlantic/Madeira +Atlantic/Reykjavik +Atlantic/South_Georgia +Atlantic/St_Helena +Atlantic/Stanley + + +Australia/Adelaide +Australia/Brisbane +Australia/Broken_Hill +Australia/Darwin +Australia/Hobart +Australia/Lindeman +Australia/Lord_Howe +Australia/Melbourne +Australia/Perth +Australia/Sydney + + +Europe/Amsterdam +Europe/Andorra +Europe/Athens +Europe/Belfast +Europe/Belgrade +Europe/Berlin +Europe/Bratislava +Europe/Brussels +Europe/Bucharest +Europe/Budapest +Europe/Chisinau +Europe/Copenhagen +Europe/Dublin +Europe/Gibraltar +Europe/Helsinki +Europe/Istanbul +Europe/Kaliningrad +Europe/Kiev +Europe/Lisbon +Europe/Ljubljana +Europe/London +Europe/Luxembourg +Europe/Madrid +Europe/Malta +Europe/Mariehamn +Europe/Minsk +Europe/Monaco +Europe/Moscow +Europe/Nicosia +Europe/Oslo +Europe/Paris +Europe/Prague +Europe/Riga +Europe/Rome +Europe/Samara +Europe/San_Marino +Europe/Sarajevo +Europe/Simferopol +Europe/Skopje +Europe/Sofia +Europe/Stockholm +Europe/Tallinn +Europe/Tirane +Europe/Uzhgorod +Europe/Vaduz +Europe/Vatican +Europe/Vienna +Europe/Vilnius +Europe/Warsaw +Europe/Zagreb +Europe/Zaporozhye +Europe/Zurich + + +Indian/Antananarivo +Indian/Chagos +Indian/Christmas +Indian/Cocos +Indian/Comoro +Indian/Kerguelen +Indian/Mahe +Indian/Maldives +Indian/Mauritius +Indian/Mayotte +Indian/Reunion + + +Pacific/Apia +Pacific/Auckland +Pacific/Chatham +Pacific/Easter +Pacific/Efate +Pacific/Enderbury +Pacific/Fakaofo +Pacific/Fiji +Pacific/Funafuti +Pacific/Galapagos +Pacific/Gambier +Pacific/Guadalcanal +Pacific/Guam +Pacific/Honolulu +Pacific/Johnston +Pacific/Kiritimati +Pacific/Kosrae +Pacific/Kwajalein +Pacific/Majuro +Pacific/Marquesas +Pacific/Midway +Pacific/Nauru +Pacific/Niue +Pacific/Norfolk +Pacific/Noumea +Pacific/Pago_Pago +Pacific/Palau +Pacific/Pitcairn +Pacific/Ponape +Pacific/Port_Moresby +Pacific/Rarotonga +Pacific/Saipan +Pacific/Tahiti +Pacific/Tarawa +Pacific/Tongatapu +Pacific/Truk +Pacific/Wake +Pacific/Wallis +Pacific/Yap \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dblayer/class.cursor.php b/v2/dotclear/inc/libs/clearbricks/dblayer/class.cursor.php new file mode 100644 index 0000000..45b604f --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dblayer/class.cursor.php @@ -0,0 +1,258 @@ + + * openCursor('table'); + * $cur->field1 = 1; + * $cur->field2 = 'foo'; + * $cur->insert(); // Insert field ... + * + * $cur->update('WHERE field3 = 4'); // ... or update field + * ?> + *
        + * + * @see dbLayer::openCursor() + * @param dbLayer &$con Connection object + * @param string $table Table name + */ + public function __construct($con,$table) + { + $this->__con =& $con; + $this->setTable($table); + } + + /** + * Set table + * + * Changes working table and resets data + * + * @param string $table Table name + */ + public function setTable($table) + { + $this->__table = $table; + $this->__data = array(); + } + + /** + * Set field + * + * Set value $v to a field named $n. Value could be + * an string, an integer, a float, a null value or an array. + * + * If value is an array, its first value will be interpreted as a SQL + * command. String values will be automatically escaped. + * + * @see __set() + * @param string $n Field name + * @param mixed $v Field value + */ + public function setField($n,$v) + { + $this->__data[$n] = $v; + } + + /** + * Unset field + * + * Remove a field from data set. + * + * @param string $n Field name + */ + public function unsetField($n) + { + unset($this->__data[$n]); + } + + /** + * Field exists + * + * @return boolean true if field named $n exists + */ + public function isField($n) + { + return isset($this->__data[$n]); + } + + /** + * Field value + * + * @see __get() + * @return mixed value for a field named $n + */ + public function getField($n) + { + if (isset($this->__data[$n])) { + return $this->__data[$n]; + } + + return null; + } + + /** + * Set Field + * + * Magic alias for {@link setField()} + */ + public function __set($n,$v) + { + $this->setField($n,$v); + } + + /** + * Field value + * + * Magic alias for {@link getField()} + * + * @return mixed value for a field named $n + */ + public function __get($n) + { + return $this->getField($n); + } + + /** + * Empty data set + * + * Removes all data from data set + */ + public function clean() + { + $this->__data = array(); + } + + private function formatFields() + { + $data = array(); + + foreach ($this->__data as $k => $v) + { + $k = $this->__con->escapeSystem($k); + + if (is_null($v)) { + $data[$k] = 'NULL'; + } elseif (is_string($v)) { + $data[$k] = "'".$this->__con->escape($v)."'"; + } elseif (is_array($v)) { + $data[$k] = is_string($v[0]) ? "'".$this->__con->escape($v[0])."'" : $v[0]; + } else { + $data[$k] = $v; + } + } + + return $data; + } + + /** + * Get insert query + * + * Returns the generated INSERT query + * + * @return string + */ + public function getInsert() + { + $data = $this->formatFields(); + + $insReq = 'INSERT INTO '.$this->__con->escapeSystem($this->__table)." (\n". + implode(",\n",array_keys($data))."\n) VALUES (\n". + implode(",\n",array_values($data))."\n) "; + + return $insReq; + } + + /** + * Get update query + * + * Returns the generated UPDATE query + * + * @param string $where WHERE condition + * @return string + */ + public function getUpdate($where) + { + $data = $this->formatFields(); + $fields = array(); + + $updReq = 'UPDATE '.$this->__con->escapeSystem($this->__table)." SET \n"; + + foreach ($data as $k => $v) { + $fields[] = $k.' = '.$v.""; + } + + $updReq .= implode(",\n",$fields); + $updReq .= "\n".$where; + + return $updReq; + } + + /** + * Execute insert query + * + * Executes the generated INSERT query + */ + public function insert() + { + if (!$this->__table) { + throw new Exception('No table name.'); + } + + $insReq = $this->getInsert(); + + $this->__con->execute($insReq); + + return true; + } + + /** + * Execute update query + * + * Executes the generated UPDATE query + * + * @param string $where WHERE condition + */ + public function update($where) + { + if (!$this->__table) { + throw new Exception('No table name.'); + } + + $updReq = $this->getUpdate($where); + + $this->__con->execute($updReq); + + return true; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dblayer/class.mysql.php b/v2/dotclear/inc/libs/clearbricks/dblayer/class.mysql.php new file mode 100644 index 0000000..5e68130 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dblayer/class.mysql.php @@ -0,0 +1,253 @@ +db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + if (!function_exists('mysql_pconnect')) { + throw new Exception('PHP MySQL functions are not available'); + } + + if (($link = @mysql_pconnect($host,$user,$password)) === false) { + throw new Exception('Unable to connect to database'); + } + + $this->db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + private function db_post_connect($link,$database) + { + if (@mysql_select_db($database,$link) === false) { + throw new Exception('Unable to use database '.$database); + } + + if (version_compare($this->db_version($link),'4.1','>=')) + { + $this->db_query($link,'SET NAMES utf8'); + $this->db_query($link,'SET CHARACTER SET utf8'); + $this->db_query($link,"SET COLLATION_CONNECTION = 'utf8_general_ci'"); + $this->db_query($link,"SET COLLATION_SERVER = 'utf8_general_ci'"); + $this->db_query($link,"SET CHARACTER_SET_SERVER = 'utf8'"); + $this->db_query($link,"SET CHARACTER_SET_DATABASE = 'utf8'"); + } + } + + /** @ignore */ + public function db_close($handle) + { + if (is_resource($handle)) { + mysql_close($handle); + } + } + + /** @ignore */ + public function db_version($handle) + { + if (is_resource($handle)) { + return mysql_get_server_info(); + } + return null; + } + + /** @ignore */ + public function db_query($handle,$query) + { + if (is_resource($handle)) + { + $res = @mysql_query($query,$handle); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if (is_resource($res)) { + return mysql_num_fields($res); + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + if (is_resource($res)) { + return mysql_num_rows($res); + } + return 0; + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if (is_resource($res)) { + return mysql_field_name($res,$position); + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if (is_resource($res)) { + return mysql_field_type($res,$position); + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + if (is_resource($res)) { + return mysql_fetch_assoc($res); + } + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + if (is_resource($res)) { + return mysql_data_seek($res,$row); + } + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if (is_resource($handle)) { + return mysql_affected_rows($handle); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if (is_resource($handle)) + { + $e = mysql_error($handle); + if ($e) { + return $e.' ('.mysql_errno($handle).')'; + } + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + if (is_resource($handle)) { + return mysql_real_escape_string($str,$handle); + } else { + return mysql_escape_string($str); + } + } + + /** @ignore */ + public function db_write_lock($table) + { + try { + $this->execute('LOCK TABLES '.$this->escapeSystem($table).' WRITE'); + } catch (Exception $e) { + # As lock is a privilege in MySQL, we can avoid errors with weak_locks static var + if (!self::$weak_locks) { + throw $e; + } + } + } + + /** @ignore */ + public function db_unlock() + { + try { + $this->execute('UNLOCK TABLES'); + } catch (Exception $e) { + if (!self::$weak_locks) { + throw $e; + } + } + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('OPTIMIZE TABLE '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + $pattern = str_replace('%M','%i',$pattern); + + return 'DATE_FORMAT('.$field.','."'".$this->escape($pattern)."') "; + } + + /** @ignore */ + public function concat() + { + $args = func_get_args(); + return 'CONCAT('.implode(',',$args).')'; + } + + /** @ignore */ + public function escapeSystem($str) + { + return '`'.$str.'`'; + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dblayer/class.mysqli.php b/v2/dotclear/inc/libs/clearbricks/dblayer/class.mysqli.php new file mode 100644 index 0000000..cf6c9a7 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dblayer/class.mysqli.php @@ -0,0 +1,284 @@ +db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + // No pconnect wtih mysqli, below code is for comatibility + return $this->db_connect($host,$user,$password,$database); + } + + /** @ignore */ + private function db_post_connect($link,$database) + { + if (version_compare($this->db_version($link),'4.1','>=')) + { + $this->db_query($link,'SET NAMES utf8'); + $this->db_query($link,'SET CHARACTER SET utf8'); + $this->db_query($link,"SET COLLATION_CONNECTION = 'utf8_general_ci'"); + $this->db_query($link,"SET COLLATION_SERVER = 'utf8_general_ci'"); + $this->db_query($link,"SET CHARACTER_SET_SERVER = 'utf8'"); + $this->db_query($link,"SET CHARACTER_SET_DATABASE = 'utf8'"); + $link->set_charset("utf8"); + } + } + + /** @ignore */ + public function db_close($handle) + { + if ($handle instanceof MySQLi) { + mysqli_close($handle); + } + } + + /** @ignore */ + public function db_version($handle) + { + if ($handle instanceof MySQLi) { + return mysqli_get_server_info($handle); + } + return null; + } + + /** @ignore */ + public function db_query($handle,$query) + { + if ($handle instanceof MySQLi) + { + + $res = @mysqli_query($handle, $query); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if ($res instanceof MySQLi_Result) { + //return mysql_num_fields($res); + return $res->field_count; + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + if ($res instanceof MySQLi_Result) { + return $res->num_rows; + } + return 0; + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if ($res instanceof MySQLi_Result) { + $res->field_seek($position); + $finfo = $res->fetch_field(); + return $finfo->name; + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if ($res instanceof MySQLi_Result) { + $res->field_seek($position); + $finfo = $res->fetch_field(); + return $this->_convert_types($finfo->type); + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + if ($res instanceof MySQLi_Result) { + $v = $res->fetch_assoc(); + return($v === NULL) ? false : $v; + } + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + if ($res instanceof MySQLi_Result) { + return $res->data_seek($row); + } + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if ($handle instanceof MySQLi) { + return mysqli_affected_rows($handle); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if ($handle instanceof MySQLi) + { + $e = mysqli_error($handle); + if ($e) { + return $e.' ('.mysqli_errno($handle).')'; + } + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + if ($handle instanceof MySQLi) { + + return mysqli_real_escape_string($handle, $str); + } + return addslashes($str); + } + + /** @ignore */ + public function db_write_lock($table) + { + try { + $this->execute('LOCK TABLES '.$this->escapeSystem($table).' WRITE'); + } catch (Exception $e) { + # As lock is a privilege in MySQL, we can avoid errors with weak_locks static var + if (!self::$weak_locks) { + throw $e; + } + } + } + + /** @ignore */ + public function db_unlock() + { + try { + $this->execute('UNLOCK TABLES'); + } catch (Exception $e) { + if (!self::$weak_locks) { + throw $e; + } + } + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('OPTIMIZE TABLE '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + $pattern = str_replace('%M','%i',$pattern); + + return 'DATE_FORMAT('.$field.','."'".$this->escape($pattern)."') "; + } + + /** @ignore */ + public function concat() + { + $args = func_get_args(); + return 'CONCAT('.implode(',',$args).')'; + } + + /** @ignore */ + public function escapeSystem($str) + { + return '`'.$str.'`'; + } + + protected function _convert_types($id) { + $id2type = array( + '1'=>'int', + '2'=>'int', + '3'=>'int', + '8'=>'int', + '9'=>'int', + + '16'=>'int', //BIT type recognized as unknown with mysql adapter + + '4'=>'real', + '5'=>'real', + '246'=>'real', + + '253'=>'string', + '254'=>'string', + + '10'=>'date', + '11'=>'time', + '12'=>'datetime', + '13'=>'year', + + '7'=>'timestamp', + + '252'=>'blob' + + ); + $type = 'unknown'; + + if(isset($id2type[$id])) $type = $id2type[$id]; + + return $type; + } + + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dblayer/class.pgsql.php b/v2/dotclear/inc/libs/clearbricks/dblayer/class.pgsql.php new file mode 100644 index 0000000..0f16b32 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dblayer/class.pgsql.php @@ -0,0 +1,280 @@ +get_connection_string($host,$user,$password,$database); + + if (($link = @pg_connect($str)) === false) { + throw new Exception('Unable to connect to database'); + } + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + if (!function_exists('pg_pconnect')) { + throw new Exception('PHP PostgreSQL functions are not available'); + } + + $str = $this->get_connection_string($host,$user,$password,$database); + + if (($link = @pg_pconnect($str)) === false) { + throw new Exception('Unable to connect to database'); + } + + return $link; + } + + /** @ignore */ + public function db_close($handle) + { + if (is_resource($handle)) { + pg_close($handle); + } + } + + /** @ignore */ + public function db_version($handle) + { + if (is_resource($handle)) + { + return pg_parameter_status($handle,'server_version'); + } + return null; + } + + /** @ignore */ + public function db_query($handle,$query) + { + if (is_resource($handle)) + { + $res = @pg_query($handle,$query); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if (is_resource($res)) { + return pg_num_fields($res); + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + if (is_resource($res)) { + return pg_num_rows($res); + } + return 0; + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if (is_resource($res)) { + return pg_field_name($res,$position); + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if (is_resource($res)) { + return pg_field_type($res,$position); + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + if (is_resource($res)) { + return pg_fetch_assoc($res); + } + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + if (is_resource($res)) { + return pg_result_seek($res,(int) $row); + } + return false; + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if (is_resource($handle) && is_resource($res)) { + return pg_affected_rows($res); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if (is_resource($handle)) { + return pg_last_error($handle); + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + return pg_escape_string($str); + } + + /** @ignore */ + public function db_write_lock($table) + { + $this->execute('BEGIN'); + $this->execute('LOCK TABLE '.$this->escapeSystem($table).' IN EXCLUSIVE MODE'); + } + + /** @ignore */ + public function db_unlock() + { + $this->execute('END'); + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('VACUUM FULL '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + $rep = array( + '%d' => 'DD', + '%H' => 'HH24', + '%M' => 'MI', + '%m' => 'MM', + '%S' => 'SS', + '%Y' => 'YYYY' + ); + + $pattern = str_replace(array_keys($rep),array_values($rep),$pattern); + + return 'TO_CHAR('.$field.','."'".$this->escape($pattern)."') "; + } + + /** + * Function call + * + * Calls a PostgreSQL function an returns the result as a {@link record}. + * After $name, you can add any parameters you want to append + * them to the PostgreSQL function. You don't need to escape string in + * arguments. + * + * @param string $name Function name + * @return record + */ + public function callFunction($name) + { + $data = func_get_args(); + array_shift($data); + + foreach ($data as $k => $v) + { + if (is_null($v)) { + $data[$k] = 'NULL'; + } elseif (is_string($v)) { + $data[$k] = "'".$this->escape($v)."'"; + } elseif (is_array($v)) { + $data[$k] = $v[0]; + } else { + $data[$k] = $v; + } + } + + $req = + 'SELECT '.$name."(\n". + implode(",\n",array_values($data)). + "\n) "; + + return $this->select($req); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dblayer/class.sqlite.php b/v2/dotclear/inc/libs/clearbricks/dblayer/class.sqlite.php new file mode 100644 index 0000000..eea52eb --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dblayer/class.sqlite.php @@ -0,0 +1,278 @@ +db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + public function db_pconnect($host,$user,$password,$database) + { + if (!class_exists('PDO') || !in_array('sqlite',PDO::getAvailableDrivers())) { + throw new Exception('PDO SQLite class is not available'); + } + + $link = new PDO('sqlite:'.$database,null,null,array(PDO::ATTR_PERSISTENT => true)); + $this->db_post_connect($link,$database); + + return $link; + } + + /** @ignore */ + private function db_post_connect($handle,$database) + { + if ($handle instanceof PDO) { + $this->db_exec($handle,'PRAGMA short_column_names = 1'); + $this->db_exec($handle,'PRAGMA encoding = "UTF-8"'); + $handle->sqliteCreateFunction('now',array($this,'now'),0); + } + } + + /** @ignore */ + public function db_close($handle) + { + if ($handle instanceof PDO) { + $handle = null; + $this->__link = null; + } + } + + /** @ignore */ + public function db_version($handle) + { + if ($handle instanceof PDO) { + return $handle->getAttribute(PDO::ATTR_SERVER_VERSION); + } + } + + # There is no other way than get all selected data in a staticRecord + /** @ignore */ + public function select($sql) + { + $result = $this->db_query($this->__link,$sql); + $this->__last_result =& $result; + + $info = array(); + $info['con'] =& $this; + $info['cols'] = $this->db_num_fields($result); + $info['info'] = array(); + + for ($i=0; $i<$info['cols']; $i++) { + $info['info']['name'][] = $this->db_field_name($result,$i); + $info['info']['type'][] = $this->db_field_type($result,$i); + } + + $data = array(); + while ($r = $result->fetch(PDO::FETCH_ASSOC)) + { + $R = array(); + foreach ($r as $k => $v) { + $k = preg_replace('/^(.*)\./','',$k); + $R[$k] = $v; + $R[] =& $R[$k]; + } + $data[] = $R; + } + + $info['rows'] = count($data); + $result->closeCursor(); + + return new staticRecord($data,$info); + } + + /** @ignore */ + public function db_query($handle,$query) + { + if ($handle instanceof PDO) + { + $res = $handle->query($query); + if ($res === false) { + $e = new Exception($this->db_last_error($handle)); + $e->sql = $query; + throw $e; + } + + return $res; + } + } + + /** @ignore */ + public function db_exec($handle,$query) + { + return $this->db_query($handle,$query); + } + + /** @ignore */ + public function db_num_fields($res) + { + if ($res instanceof PDOStatement) { + return $res->columnCount(); + } + return 0; + } + + /** @ignore */ + public function db_num_rows($res) + { + } + + /** @ignore */ + public function db_field_name($res,$position) + { + if ($res instanceof PDOStatement) { + $m = $res->getColumnMeta($position); + return preg_replace('/^.+\./','',$m['name']); # we said short_column_names = 1 + } + } + + /** @ignore */ + public function db_field_type($res,$position) + { + if ($res instanceof PDOStatement) { + $m = $res->getColumnMeta($position); + switch ($m['pdo_type']) { + case PDO::PARAM_BOOL: + return 'boolean'; + case PDO::PARAM_NULL: + return 'null'; + case PDO::PARAM_INT: + return 'integer'; + default: + return 'varchar'; + } + } + } + + /** @ignore */ + public function db_fetch_assoc($res) + { + } + + /** @ignore */ + public function db_result_seek($res,$row) + { + } + + /** @ignore */ + public function db_changes($handle,$res) + { + if ($res instanceof PDOStatement) { + return $res->rowCount(); + } + } + + /** @ignore */ + public function db_last_error($handle) + { + if ($handle instanceof PDO) { + $err = $handle->errorInfo(); + return $err[2].' ('.$err[1].')'; + } + return false; + } + + /** @ignore */ + public function db_escape_string($str,$handle=null) + { + if ($handle instanceof PDO) { + return trim($handle->quote($str),"'"); + } + return $str; + } + + /** @ignore */ + public function escapeSystem($str) + { + return "'".$this->escape($str)."'"; + } + + /** @ignore */ + public function begin() + { + if ($this->__link instanceof PDO) { + $this->__link->beginTransaction(); + } + } + + /** @ignore */ + public function commit() + { + if ($this->__link instanceof PDO) { + $this->__link->commit(); + } + } + + /** @ignore */ + public function rollback() + { + if ($this->__link instanceof PDO) { + $this->__link->rollBack(); + } + } + + /** @ignore */ + public function db_write_lock($table) + { + $this->execute('BEGIN EXCLUSIVE TRANSACTION'); + } + + /** @ignore */ + public function db_unlock() + { + $this->execute('END'); + } + + /** @ignore */ + public function vacuum($table) + { + $this->execute('VACUUM '.$this->escapeSystem($table)); + } + + /** @ignore */ + public function dateFormat($field,$pattern) + { + return "strftime('".$this->escape($pattern)."',".$field.') '; + } + + # Internal SQLite function that adds NOW() SQL function. + /** @ignore */ + public function now() + { + return date('Y-m-d H:i:s'); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dblayer/dblayer.php b/v2/dotclear/inc/libs/clearbricks/dblayer/dblayer.php new file mode 100644 index 0000000..21f1b8d --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dblayer/dblayer.php @@ -0,0 +1,1208 @@ +$position. + * + * @param resource $res Resource result + * @param integer $position Field position + * @return string + */ + function db_field_name($res,$position); + + /** + * Field type + * + * This method should return the field type a the given position + * $position. + * + * @param resource $res Resource result + * @param integer $position Field position + * @return string + */ + function db_field_type($res,$position); + + /** + * Fetch result + * + * This method should fetch one line of result and return an associative array + * with field name as key and field value as value. + * + * @param resource $res Resource result + * @return array + */ + function db_fetch_assoc($res); + + /** + * Move result cursor + * + * This method should move result cursor on given row position $row + * and return true on success. + * + * @param resource $res Resource result + * @param integer $position Row position + * @return boolean + */ + function db_result_seek($res,$row); + + /** + * Affected rows + * + * This method should return number of rows affected by INSERT, UPDATE or + * DELETE queries. + * + * @param resource $handle Resource link + * @param resource $res Resource result + * @return integer + */ + function db_changes($handle,$res); + + /** + * Last error + * + * This method should return the last error string for the current connection. + * + * @param resource $handle Resource link + * @return string + */ + function db_last_error($handle); + + /** + * Escape string + * + * This method should return an escaped string for the current connection. + * + * @param string $str String to escape + * @param resource $handle Resource link + * @return string + */ + function db_escape_string($str,$handle=null); + + /** + * Acquiere Write lock + * + * This method should lock the given table in write access. + * + * @param string $table Table name + */ + function db_write_lock($table); + + /** + * Release lock + * + * This method should releases an acquiered lock. + */ + function db_unlock(); +} + +/** +* Database Abstraction Layer class +* +* Base class for database abstraction. Each driver extends this class and +* implements {@link i_dbLayer} interface. +* +* @package Clearbricks +* @subpackage DBLayer +*/ +class dbLayer +{ + /** @var string Driver name */ + protected $__driver = null; + + /** @var string Database version */ + protected $__version = null; + + /** @var resource Database resource link */ + protected $__link; + + /** @var resource Last result resource link */ + protected $__last_result; + + /** + * Start connection + * + * Static function to use to init database layer. Returns a object extending + * dbLayer. + * + * @param string $driver Driver name + * @param string $host Database hostname + * @param string $database Database name + * @param string $user User ID + * @param string $password Password + * @param string $persistent Persistent connection + * @return object + */ + public static function init($driver,$host,$database,$user='',$password='',$persistent=false) + { + if (file_exists(dirname(__FILE__).'/class.'.$driver.'.php')) { + require_once dirname(__FILE__).'/class.'.$driver.'.php'; + $driver_class = $driver.'Connection'; + } else { + trigger_error('Unable to load DB layer for '.$driver,E_USER_ERROR); + exit(1); + } + + return new $driver_class($host,$database,$user,$password,$persistent); + } + + /** + * @param string $host Database hostname + * @param string $database Database name + * @param string $user User ID + * @param string $password Password + * @param string $persistent Persistent connection + */ + public function __construct($host,$database,$user='',$password='',$persistent=false) + { + if ($persistent) { + $this->__link = $this->db_pconnect($host,$user,$password,$database); + } else { + $this->__link = $this->db_connect($host,$user,$password,$database); + } + + $this->__version = $this->db_version($this->__link); + $this->__database = $database; + } + + /** + * Closes database connection. + */ + public function close() + { + $this->db_close($this->__link); + } + + /** + * Returns database driver name + * + * @return string + */ + public function driver() + { + return $this->__driver; + } + + /** + * Returns database driver version + * + * @return string + */ + public function version() + { + return $this->__version; + } + + /** + * Returns current database name + * + * @return string + */ + public function database() + { + return $this->__database; + } + + /** + * Returns link resource + * + * @return resource + */ + public function link() + { + return $this->__link; + } + + /** + * Run query and get results + * + * Executes a query and return a {@link record} object. + * + * @param string $sql SQL query + * @return record + */ + public function select($sql) + { + $result = $this->db_query($this->__link,$sql); + + $this->__last_result =& $result; + + $info = array(); + $info['con'] =& $this; + $info['cols'] = $this->db_num_fields($result); + $info['rows'] = $this->db_num_rows($result); + $info['info'] = array(); + + for ($i=0; $i<$info['cols']; $i++) { + $info['info']['name'][] = $this->db_field_name($result,$i); + $info['info']['type'][] = $this->db_field_type($result,$i); + } + + return new record($result,$info); + } + + /** + * Run query + * + * Executes a query and return true if succeed + * + * @param string $sql SQL query + * @return true + */ + public function execute($sql) + { + $result = $this->db_exec($this->__link,$sql); + + $this->__last_result =& $result; + + return true; + } + + /** + * Begin transaction + * + * Begins a transaction. Transaction should be {@link commit() commited} + * or {@link rollback() rollbacked}. + */ + public function begin() + { + $this->execute('BEGIN'); + } + + /** + * Commit transaction + * + * Commits a previoulsy started transaction. + */ + public function commit() + { + $this->execute('COMMIT'); + } + + /** + * Rollback transaction + * + * Rollbacks a previously started transaction. + */ + public function rollback() + { + $this->execute('ROLLBACK'); + } + + /** + * Aquiere write lock + * + * This method lock the given table in write access. + * + * @param string $table Table name + */ + public function writeLock($table) + { + $this->db_write_lock($table); + } + + /** + * Release lock + * + * This method releases an acquiered lock. + */ + public function unlock() + { + $this->db_unlock(); + } + + /** + * Vacuum the table given in argument. + * + * @param string $table Table name + */ + public function vacuum($table) + { + } + + /** + * Changed rows + * + * Returns the number of lines affected by the last DELETE, INSERT or UPDATE + * query. + * + * @return integer + */ + public function changes() + { + return $this->db_changes($this->__link,$this->__last_result); + } + + /** + * Last error + * + * Returns the last database error or false if no error. + * + * @return string|false + */ + public function error() + { + $err = $this->db_last_error($this->__link); + + if (!$err) { + return false; + } + + return $err; + } + + /** + * Date formatting + * + * Returns a query fragment with date formater. + * + * The following modifiers are accepted: + * + * - %d : Day of the month, numeric + * - %H : Hour 24 (00..23) + * - %M : Minute (00..59) + * - %m : Month numeric (01..12) + * - %S : Seconds (00..59) + * - %Y : Year, numeric, four digits + * + * @param string $field Field name + * @param string $pattern Date format + * @return string + */ + public function dateFormat($field,$pattern) + { + return + 'TO_CHAR('.$field.','."'".$this->escape($pattern)."') "; + } + + /** + * Query Limit + * + * Returns a LIMIT query fragment. $arg1 could be an array of + * offset and limit or an integer which is only limit. If $arg2 + * is given and $arg1 is an integer, it would become limit. + * + * @param array|integer $arg1 array or integer with limit intervals + * @param array|null $arg2 integer or null + * @return string + */ + public function limit($arg1, $arg2=null) + { + if (is_array($arg1)) + { + $arg1 = array_values($arg1); + $arg2 = isset($arg1[1]) ? $arg1[1] : null; + $arg1 = $arg1[0]; + } + + if ($arg2 === null) { + $sql = ' LIMIT '.(integer) $arg1.' '; + } else { + $sql = ' LIMIT '.(integer) $arg2.' OFFSET '.(integer) $arg1.' '; + } + + return $sql; + } + + /** + * IN fragment + * + * Returns a IN query fragment where $in could be an array, a string, + * an integer or null + * + * @param array|string|integer|null $in "IN" values + * @return string + */ + public function in($in) + { + if (is_null($in)) + { + return ' IN (NULL) '; + } + elseif (is_string($in)) + { + return " IN ('".$this->escape($in)."') "; + } + elseif (is_array($in)) + { + foreach ($in as $i => $v) { + if (is_null($v)) { + $in[$i] = 'NULL'; + } elseif (is_string($v)) { + $in[$i] = "'".$this->escape($v)."'"; + } + } + return ' IN ('.implode(',',$in).') '; + } + else + { + return ' IN ( '.(integer) $in.') '; + } + } + + /** + * Concat strings + * + * Returns SQL concatenation of methods arguments. Theses arguments + * should be properly escaped when needed. + * + * @return string + */ + public function concat() + { + $args = func_get_args(); + return implode(' || ',$args); + } + + /** + * Escape string + * + * Returns SQL protected string or array values. + * + * @param string|array $i String or array to protect + * @return string|array + */ + public function escape($i) + { + if (is_array($i)) { + foreach ($i as $k => $s) { + $i[$k] = $this->db_escape_string($s,$this->__link); + } + return $i; + } + + return $this->db_escape_string($i,$this->__link); + } + + /** + * System escape string + * + * Returns SQL system protected string. + * + * @param string $str String to protect + * @return string + */ + public function escapeSystem($str) + { + return '"'.$str.'"'; + } + + /** + * Cursor object + * + * Returns a new instance of {@link cursor} class on $table for + * the current connection. + * + * @param string $table Target table + * @return cursor + */ + public function openCursor($table) + { + return new cursor($this,$table); + } +} + +/** +* Query Result Record Class +* +* This class acts as an iterator over database query result. It does not fetch +* all results on instantiation and thus, depending on database engine, should not +* fill PHP process memory. +* +* @package Clearbricks +* @subpackage DBLayer +*/ +class record implements Iterator, Countable +{ + /** @var resource Database resource link */ + protected $__link; + + /** @var resource Query result resource */ + protected $__result; + + /** @var array Result information array */ + protected $__info; + + /** @var array List of static functions that extend record */ + protected $__extend = array(); + + /** @var integer Current result position */ + protected $__index = 0; + + /** @var array Current result row content */ + protected $__row = false; + + private $__fetch = false; + + /** + * Constructor + * + * Creates class instance from result link and some informations. + * $info is an array with the following content: + * + * - con => database object instance + * - cols => number of columns + * - rows => number of rows + * - info[name] => an array with columns names + * - info[type] => an array with columns types + * + * @param resource $result Resource result + * @param array $info Information array + */ + public function __construct($result,$info) + { + $this->__result = $result; + $this->__info = $info; + $this->__link = $info['con']->link(); + $this->index(0); + } + + /** + * To staticRecord + * + * Converts this record to a {@link staticRecord} instance. + */ + public function toStatic() + { + if ($this instanceof staticRecord) { + return $this; + } + return new staticRecord($this->__result,$this->__info); + } + + /** + * Magic call + * + * Magic call function. Calls function added by {@link extend()} if exists, passing it + * self object and arguments. + * + * @return mixed + */ + public function __call($f,$args) + { + if (isset($this->__extend[$f])) + { + array_unshift($args,$this); + return call_user_func_array($this->__extend[$f],$args); + } + + trigger_error('Call to undefined method record::'.$f.'()',E_USER_ERROR); + } + + /** + * Magic get + * + * Alias for {@link field()}. + * + * @param string|integer $n Field name + * @return string + */ + public function __get($n) + { + return $this->field($n); + } + + /** + * Get field + * + * Alias for {@link field()}. + * + * @param string|integer $n Field name + * @return string + */ + public function f($n) + { + return $this->field($n); + } + + /** + * Get field + * + * Retrieve field value by its name or column position. + * + * @param string|integer $n Field name + * @return string + */ + public function field($n) + { + return $this->__row[$n]; + } + + /** + * Field exists + * + * Returns true if a field exists. + * + * @param string $n Field name + * @return string + */ + public function exists($n) + { + return isset($this->__row[$n]); + } + + /** + * Field isset + * + * Returns true if a field exists (magic method from PHP 5.1). + * + * @param string $n Field name + * @return string + */ + public function __isset($n) + { + return isset($this->__row[$n]); + } + + /** + * Extend record + * + * Extends this instance capabilities by adding all public static methods of + * $class to current instance. Class methods should take at least + * this record as first parameter. + * + * @see __call() + * + * @param string $class Class name + */ + public function extend($class) + { + if (!class_exists($class)) { + return; + } + + $c = new ReflectionClass($class); + foreach ($c->getMethods() as $m) { + if ($m->isStatic() && $m->isPublic()) { + $this->__extend[$m->name] = array($class,$m->name); + } + } + } + + /** + * Returns record extensions. + * + * @return array + */ + public function extensions() + { + return $this->__extend; + } + + private function setRow() + { + $this->__row = $this->__info['con']->db_fetch_assoc($this->__result); + + if ($this->__row !== false) + { + foreach ($this->__row as $k => $v) { + $this->__row[] =& $this->__row[$k]; + } + return true; + } + else + { + return false; + } + } + + /** + * Returns the current index position (0 is first) or move to $row if + * specified. + * + * @param integer $row Row number to move + * @return integer + */ + public function index($row=null) + { + if ($row === null) { + return $this->__index === null ? 0 : $this->__index; + } + + if ($row < 0 || $row+1 > $this->__info['rows']) { + return false; + } + + if ($this->__info['con']->db_result_seek($this->__result,(integer) $row)) + { + $this->__index = $row; + $this->setRow(); + $this->__info['con']->db_result_seek($this->__result,(integer) $row); + return true; + } + return false; + } + + /** + * One step move index + * + * This method moves index forward and return true until index is not + * the last one. You can use it to loop over record. Example: + * + * fetch()) { + * echo $rs->field1; + * } + * ?> + * + * + * @return boolean + */ + public function fetch() + { + if (!$this->__fetch) { + $this->__fetch = true; + $i = -1; + } else { + $i = $this->__index; + } + + if (!$this->index($i+1)) { + $this->__fetch = false; + $this->__index = 0; + return false; + } + + return true; + } + + /** + * Moves index to first position. + * + * @return boolean + */ + public function moveStart() + { + $this->__fetch = false; + return $this->index(0); + } + + /** + * Moves index to last position. + * + * @return boolean + */ + public function moveEnd() + { + return $this->index($this->__info['rows']-1); + } + + /** + * Moves index to next position. + * + * @return boolean + */ + public function moveNext() + { + return $this->index($this->__index+1); + } + + /** + * Moves index to previous position. + * + * @return boolean + */ + public function movePrev() + { + return $this->index($this->__index-1); + } + + /** + * @return boolean true if index is at last position + */ + public function isEnd() + { + return $this->__index+1 == $this->count(); + } + + /** + * @return boolean true if index is at first position. + */ + public function isStart() + { + return $this->__index <= 0; + } + + /** + * @return boolean true if record contains no result. + */ + public function isEmpty() + { + return $this->count() == 0; + } + + /** + * @return integer number of rows in record + */ + public function count() + { + return $this->__info['rows']; + } + + /** + * @return array array of columns, with name as key and type as value. + */ + public function columns() + { + return $this->__info['info']['name']; + } + + /** + * @return array all rows in record. + */ + public function rows() + { + return $this->getData(); + } + + /** + * All data + * + * Returns an array of all rows in record. This method is called by rows(). + * + * @return array + */ + protected function getData() + { + $res = array(); + + if ($this->count() == 0) { + return $res; + } + + $this->__info['con']->db_result_seek($this->__result,0); + while (($r = $this->__info['con']->db_fetch_assoc($this->__result)) !== false) { + foreach ($r as $k => $v) { + $r[] =& $r[$k]; + } + $res[] = $r; + } + $this->__info['con']->db_result_seek($this->__result,$this->__index); + + return $res; + } + + /** + * @return array current rows. + */ + public function row() + { + return $this->__row; + } + + /* Iterator methods */ + + /** + * @see Iterator::current + */ + public function current() { + return $this; + } + + /** + * @see Iterator::key + */ + public function key() + { + return $this->index(); + } + /** + * @see Iterator::next + */ + public function next() + { + $this->fetch(); + } + + /** + * @see Iterator::rewind + */ + public function rewind() + { + $this->moveStart(); + $this->fetch(); + } + + /** + * @see Iterator::valid + */ + public function valid () + { + return $this->__fetch; + } + +} + +/** +* Query Result Static Record Class +* +* Unlike record class, this one contains all results in an associative array. +* +* @package Clearbricks +* @subpackage DBLayer +*/ +class staticRecord extends record +{ + /** @var array Data array */ + public $__data = array(); + + private $__sortfield; + private $__sortsign; + + /** @ignore */ + public function __construct($result,$info) + { + if (is_array($result)) + { + $this->__info = $info; + $this->__data = $result; + } + else + { + parent::__construct($result,$info); + $this->__data = parent::getData(); + } + + unset($this->__link); + unset($this->__result); + } + + /** + * Static record from array + * + * Returns a new instance of object from an associative array. + * + * @param array $data Data array + * @return staticRecord + */ + public static function newFromArray($data) + { + if (!is_array($data)) { + $data = array(); + } + + $data = array_values($data); + + if (empty($data) || !is_array($data[0])) { + $cols = 0; + } else { + $cols = count($data[0]); + } + + $info = array( + 'con' => null, + 'info' => null, + 'cols' => $cols, + 'rows' => count($data) + ); + + return new self($data,$info); + } + + /** @ignore */ + public function field($n) + { + return $this->__data[$this->__index][$n]; + } + + /** @ignore */ + public function exists($n) + { + return isset($this->__data[$this->__index][$n]); + } + + /** @ignore */ + public function index($row=null) + { + if ($row === null) { + return $this->__index; + } + + if ($row < 0 || $row+1 > $this->__info['rows']) { + return false; + } + + $this->__index = $row; + return true; + } + + /** @ignore */ + public function rows() + { + return $this->__data; + } + + /** + * Changes value of a given field in the current row. + * + * @param string $n Field name + * @param string $v Field value + */ + public function set($n,$v) + { + if ($this->__index === null) { + return false; + } + + $this->__data[$this->__index][$n] = $v; + } + + /** + * Sorts values by a field in a given order. + * + * @param string $field Field name + * @param string $order Sort type (asc or desc) + */ + public function sort($field,$order='asc') + { + if (!isset($this->__data[0][$field])) { + return false; + } + + $this->__sortfield = $field; + $this->__sortsign = strtolower($order) == 'asc' ? 1 : -1; + + usort($this->__data,array($this,'sortCallback')); + + $this->__sortfield = null; + $this->__sortsign = null; + } + + private function sortCallback($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 strcmp($a,$b) * $this->__sortsign; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dbschema/class.dbschema.php b/v2/dotclear/inc/libs/clearbricks/dbschema/class.dbschema.php new file mode 100644 index 0000000..6a6bb47 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dbschema/class.dbschema.php @@ -0,0 +1,310 @@ +array + */ + function db_get_tables(); + + /** + This method should return an associative array of columns in given table + $table with column names in keys. Each line value is an array + with following values: + + - [type] data type (string) + - [len] data length (integer or null) + - [null] is null? (boolean) + - [default] default value (string) + + @param table string Table name + @return array + */ + function db_get_columns($table); + + /** + This method should return an array of keys in given table + $table. Each line value is an array with following values: + + - [name] index name (string) + - [primary] primary key (boolean) + - [unique] unique key (boolean) + - [cols] columns (array) + + @param table string Table name + @return array + */ + function db_get_keys($table); + + /** + This method should return an array of indexes in given table + $table. Each line value is an array with following values: + + - [name] index name (string) + - [type] index type (string) + - [cols] columns (array) + + @param table string Table name + @return array + */ + function db_get_indexes($table); + + /** + This method should return an array of foreign keys in given table + $table. Each line value is an array with following values: + + - [name] key name (string) + - [c_cols] child columns (array) + - [p_table] parent table (string) + - [p_cols] parent columns (array) + - [update] on update statement (string) + - [delete] on delete statement (string) + + @param table string Table name + @return array + */ + function db_get_references($table); + + function db_create_table($name,$fields); + + function db_create_field($table,$name,$type,$len,$null,$default); + + function db_create_primary($table,$name,$cols); + + function db_create_unique($table,$name,$cols); + + function db_create_index($table,$name,$type,$cols); + + function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + + function db_alter_field($table,$name,$type,$len,$null,$default); + + function db_alter_primary($table,$name,$newname,$cols); + + function db_alter_unique($table,$name,$newname,$cols); + + function db_alter_index($table,$name,$newname,$type,$cols); + + function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + + function db_drop_unique($table,$name); +} + +class dbSchema +{ + protected $con; + + public function __construct($con) + { + $this->con =& $con; + } + + public static function init($con) + { + $driver = $con->driver(); + $driver_class = $driver.'Schema'; + + if (!class_exists($driver_class)) + { + if (file_exists(dirname(__FILE__).'/class.'.$driver.'.dbschema.php')) { + require dirname(__FILE__).'/class.'.$driver.'.dbschema.php'; + } else { + trigger_error('Unable to load DB schema layer for '.$driver,E_USER_ERROR); + exit(1); + } + } + + return new $driver_class($con); + } + + /** + Database data type to universal data type conversion. + + @param type string Type name + @param leng integer Field length (in/out) + @param default string Default field value (in/out) + @return string + */ + public function dbt2udt($type,&$len,&$default) + { + $c = array( + 'bool' => 'boolean', + 'int2' => 'smallint', + 'int' => 'integer', + 'int4' => 'integer', + 'int8' => 'bigint', + 'float4' => 'real', + 'double precision' => 'float', + 'float8' => 'float', + 'decimal' => 'numeric', + 'character varying' => 'varchar', + 'character' => 'char' + ); + + if (isset($c[$type])) { + return $c[$type]; + } + + return $type; + } + + /** + Universal data type to database data tye conversion. + + @param type string Type name + @param leng integer Field length (in/out) + @param default string Default field value (in/out) + @return string + */ + public function udt2dbt($type,&$len,&$default) + { + return $type; + } + + /** + Returns an array of all table names. + + @see i_dbSchema::db_get_tables + @return array + */ + public function getTables() + { + return $this->db_get_tables(); + } + + /** + Returns an array of columns (name and type) of a given table. + + @see i_dbSchema::db_get_columns + @param table string Table name + @return array + */ + public function getColumns($table) + { + return $this->db_get_columns($table); + } + + /** + Returns an array of index of a given table. + + @see i_dbSchema::db_get_keys + @param table string Table name + @return array + */ + public function getKeys($table) + { + return $this->db_get_keys($table); + } + + /** + Returns an array of indexes of a given table. + + @see i_dbSchema::db_get_index + @param table string Table name + @return array + */ + public function getIndexes($table) + { + return $this->db_get_indexes($table); + } + + /** + Returns an array of foreign keys of a given table. + + @see i_dbSchema::db_get_references + @param table string Table name + @return array + */ + public function getReferences($table) + { + return $this->db_get_references($table); + } + + public function createTable($name,$fields) + { + return $this->db_create_table($name,$fields); + } + + public function createField($table,$name,$type,$len,$null,$default) + { + return $this->db_create_field($table,$name,$type,$len,$null,$default); + } + + public function createPrimary($table,$name,$cols) + { + return $this->db_create_primary($table,$name,$cols); + } + + public function createUnique($table,$name,$cols) + { + return $this->db_create_unique($table,$name,$cols); + } + + public function createIndex($table,$name,$type,$cols) + { + return $this->db_create_index($table,$name,$type,$cols); + } + + public function createReference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + return $this->db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function alterField($table,$name,$type,$len,$null,$default) + { + return $this->db_alter_field($table,$name,$type,$len,$null,$default); + } + + public function alterPrimary($table,$name,$newname,$cols) + { + return $this->db_alter_primary($table,$name,$newname,$cols); + } + + public function alterUnique($table,$name,$newname,$cols) + { + return $this->db_alter_unique($table,$name,$newname,$cols); + } + + public function alterIndex($table,$name,$newname,$type,$cols) + { + return $this->db_alter_index($table,$name,$newname,$type,$cols); + } + + public function alterReference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + return $this->db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function dropUnique($table,$name) + { + return $this->db_drop_unique($table,$name); + } + + public function flushStack() + { + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dbschema/class.dbstruct.php b/v2/dotclear/inc/libs/clearbricks/dbschema/class.dbstruct.php new file mode 100644 index 0000000..2b76012 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dbschema/class.dbstruct.php @@ -0,0 +1,639 @@ +con =& $con; + $this->prefix = $prefix; + } + + public function driver() + { + return $this->con->driver(); + } + + public function table($name) + { + $this->tables[$name] = new dbStructTable($name); + return $this->tables[$name]; + } + + public function __get($name) + { + if (!isset($this->tables[$name])) { + return $this->table($name); + } + + return $this->tables[$name]; + } + + public function reverse() + { + $schema = dbSchema::init($this->con); + + # Get tables + $tables = $schema->getTables(); + + foreach ($tables as $t_name) + { + if ($this->prefix && strpos($t_name,$this->prefix) !== 0) { + continue; + } + + $t = $this->table($t_name); + + # Get columns + $cols = $schema->getColumns($t_name); + + foreach ($cols as $c_name => $col) { + $type = $schema->dbt2udt($col['type'],$col['len'],$col['default']); + $t->field($c_name,$type,$col['len'],$col['null'],$col['default'],true); + } + + # Get keys + $keys = $schema->getKeys($t_name); + + foreach ($keys as $k) + { + $args = $k['cols']; + array_unshift($args,$k['name']); + + if ($k['primary']) { + call_user_func_array(array($t,'primary'),$args); + } elseif ($k['unique']) { + call_user_func_array(array($t,'unique'),$args); + } + } + + # Get indexes + $idx = $schema->getIndexes($t_name); + foreach ($idx as $i) + { + $args = array($i['name'],$i['type']); + $args = array_merge($args,$i['cols']); + + call_user_func_array(array($t,'index'),$args); + } + + # Get foreign keys + $ref = $schema->getReferences($t_name); + foreach ($ref as $r) { + $t->reference($r['name'],$r['c_cols'],$r['p_table'],$r['p_cols'],$r['update'],$r['delete']); + } + } + } + + /** + Synchronize this schema taken from database with $schema. + + @param s dbStruct Structure to synchronize with + */ + public function synchronize($s) + { + $this->tables = array(); + $this->reverse(); + + if (!($s instanceof self)) { + throw new Exception('Invalid database schema'); + } + + $tables = $s->getTables(); + + $table_create = array(); + $key_create = array(); + $index_create = array(); + $reference_create = array(); + + $field_create = array(); + $field_update = array(); + $key_update = array(); + $index_update = array(); + $reference_update = array(); + + $got_work = false; + + $schema = dbSchema::init($this->con); + + foreach ($tables as $tname => $t) + { + if (!$this->tableExists($tname)) + { + # Table does not exist, create table + $table_create[$tname] = $t->getFields(); + + # Add keys, indexes and references + $keys = $t->getKeys(); + $indexes = $t->getIndexes(); + $references = $t->getReferences(); + + foreach ($keys as $k => $v) { + $key_create[$tname][$this->prefix.$k] = $v; + } + foreach ($indexes as $k => $v) { + $index_create[$tname][$this->prefix.$k] = $v; + } + foreach ($references as $k => $v) { + $v['p_table'] = $this->prefix.$v['p_table']; + $reference_create[$tname][$this->prefix.$k] = $v; + } + + $got_work = true; + } + else # Table exists + { + # Check new fields to create + $fields = $t->getFields(); + $db_fields = $this->tables[$tname]->getFields(); + foreach ($fields as $fname => $f) + { + if (!$this->tables[$tname]->fieldExists($fname)) + { + # Field doest not exist, create it + $field_create[$tname][$fname] = $f; + $got_work = true; + } + elseif ($this->fieldsDiffer($db_fields[$fname],$f)) + { + # Field exists and differs from db version + $field_update[$tname][$fname] = $f; + $got_work = true; + } + } + + # Check keys to add or upgrade + $keys = $t->getKeys(); + $db_keys = $this->tables[$tname]->getKeys(); + + foreach ($keys as $kname => $k) + { + if ($k['type'] == 'primary' && $this->con->driver() == 'mysql') { + $kname = 'PRIMARY'; + } else { + $kname = $this->prefix.$kname; + } + + $db_kname = $this->tables[$tname]->keyExists($kname,$k['type'],$k['cols']); + if (!$db_kname) + { + # Key does not exist, create it + $key_create[$tname][$kname] = $k; + $got_work = true; + } + elseif ($this->keysDiffer($db_kname,$db_keys[$db_kname]['cols'],$kname,$k['cols'])) + { + # Key exists and differs from db version + $key_update[$tname][$db_kname] = array_merge(array('name'=>$kname),$k); + $got_work = true; + } + } + + # Check index to add or upgrade + $idx = $t->getIndexes(); + $db_idx = $this->tables[$tname]->getIndexes(); + + foreach ($idx as $iname => $i) + { + $iname = $this->prefix.$iname; + $db_iname = $this->tables[$tname]->indexExists($iname,$i['type'],$i['cols']); + + if (!$db_iname) + { + # Index does not exist, create it + $index_create[$tname][$iname] = $i; + $got_work = true; + } + elseif ($this->indexesDiffer($db_iname,$db_idx[$db_iname],$iname,$i)) + { + # Index exists and differs from db version + $index_update[$tname][$db_iname] = array_merge(array('name'=>$iname),$i); + $got_work = true; + } + } + + # Check references to add or upgrade + $ref = $t->getReferences(); + $db_ref = $this->tables[$tname]->getReferences(); + + foreach ($ref as $rname => $r) + { + $rname = $this->prefix.$rname; + $r['p_table'] = $this->prefix.$r['p_table']; + $db_rname = $this->tables[$tname]->referenceExists($rname,$r['c_cols'],$r['p_table'],$r['p_cols']); + + if (!$db_rname) + { + # Reference does not exist, create it + $reference_create[$tname][$rname] = $r; + $got_work = true; + } + elseif ($this->referencesDiffer($db_rname,$db_ref[$db_rname],$rname,$r)) + { + $reference_update[$tname][$db_rname] = array_merge(array('name'=>$rname),$r); + $got_work = true; + } + } + } + } + + if (!$got_work) { + return; + } + + # Create tables + foreach ($table_create as $table => $fields) + { + $schema->createTable($table,$fields); + } + + # Create new fields + foreach ($field_create as $tname => $fields) + { + foreach ($fields as $fname => $f) { + $schema->createField($tname,$fname,$f['type'],$f['len'],$f['null'],$f['default']); + } + } + + # Update fields + foreach ($field_update as $tname => $fields) + { + foreach ($fields as $fname => $f) { + $schema->alterField($tname,$fname,$f['type'],$f['len'],$f['null'],$f['default']); + } + } + + # Create new keys + foreach ($key_create as $tname => $keys) + { + foreach ($keys as $kname => $k) + { + if ($k['type'] == 'primary') { + $schema->createPrimary($tname,$kname,$k['cols']); + } elseif ($k['type'] == 'unique') { + $schema->createUnique($tname,$kname,$k['cols']); + } + } + } + + # Update keys + foreach ($key_update as $tname => $keys) + { + foreach ($keys as $kname => $k) + { + if ($k['type'] == 'primary') { + $schema->alterPrimary($tname,$kname,$k['name'],$k['cols']); + } elseif ($k['type'] == 'unique') { + $schema->alterUnique($tname,$kname,$k['name'],$k['cols']); + } + } + } + + # Create indexes + foreach ($index_create as $tname => $index) + { + foreach ($index as $iname => $i) { + $schema->createIndex($tname,$iname,$i['type'],$i['cols']); + } + } + + # Update indexes + foreach ($index_update as $tname => $index) + { + foreach ($index as $iname => $i) { + $schema->alterIndex($tname,$iname,$i['name'],$i['type'],$i['cols']); + } + } + + # Create references + foreach ($reference_create as $tname => $ref) + { + foreach ($ref as $rname => $r) + { + $schema->createReference($rname,$tname,$r['c_cols'],$r['p_table'],$r['p_cols'],$r['update'],$r['delete']); + } + } + + # Update references + foreach ($reference_update as $tname => $ref) + { + foreach ($ref as $rname => $r) { + $schema->alterReference($rname,$r['name'],$tname,$r['c_cols'],$r['p_table'],$r['p_cols'],$r['update'],$r['delete']); + } + } + + # Flush execution stack + $schema->flushStack(); + + return + count($table_create) + count($key_create) + count($index_create) + + count($reference_create) + count($field_create) + count($field_update) + + count($key_update) + count($index_update) + count($reference_update); + } + + public function getTables() + { + $res = array(); + foreach ($this->tables as $t => $v) + { + $res[$this->prefix.$t] = $v; + } + + return $res; + } + + public function tableExists($name) + { + return isset($this->tables[$name]); + } + + private function fieldsDiffer($db_field,$schema_field) + { + $d_type = $db_field['type']; + $d_len = (integer) $db_field['len']; + $d_default = $db_field['default']; + $d_null = $db_field['null']; + + $s_type = $schema_field['type']; + $s_len = (integer) $schema_field['len']; + $s_default = $schema_field['default']; + $s_null = $schema_field['null']; + + return $d_type != $s_type || $d_len != $s_len || $d_default != $s_default || $d_null != $s_null; + } + + private function keysDiffer($d_name,$d_cols,$s_name,$s_cols) + { + return $d_name != $s_name || $d_cols != $s_cols; + } + + private function indexesDiffer($d_name,$d_i,$s_name,$s_i) + { + return $d_name != $s_name || $d_i['cols'] != $s_i['cols'] || $d_i['type'] != $s_i['type']; + } + + private function referencesDiffer($d_name,$d_r,$s_name,$s_r) + { + return + $d_name != $s_name || $d_r['c_cols'] != $s_r['c_cols'] + || $d_r['p_table'] != $s_r['p_table'] || $d_r['p_cols'] != $s_r['p_cols'] + || $d_r['update'] != $s_r['update'] || $d_r['delete'] != $s_r['delete']; + } +} + +class dbStructTable +{ + protected $name; + protected $has_primary = false; + + protected $fields = array(); + protected $keys = array(); + protected $indexes = array(); + protected $references = array(); + + /** + Universal data types supported by dbSchema + + SMALLINT : signed 2 bytes integer + INTEGER : signed 4 bytes integer + BIGINT : signed 8 bytes integer + REAL : signed 4 bytes floating point number + FLOAT : signed 8 bytes floating point number + NUMERIC : exact numeric type + + DATE : Calendar date (day, month and year) + TIME : Time of day + TIMESTAMP : Date and time + + CHAR : A fixed n-length character string + VARCHAR : A variable length character string + TEXT : A variable length of text + */ + protected $allowed_types = array( + 'smallint','integer','bigint','real','float','numeric', + 'date','time','timestamp', + 'char','varchar','text' + ); + + public function __construct($name) + { + $this->name = $name; + return $this; + } + + public function getFields() + { + return $this->fields; + } + + public function getKeys($primary=null) + { + return $this->keys; + } + + public function getIndexes() + { + return $this->indexes; + } + + public function getReferences() + { + return $this->references; + } + + public function fieldExists($name) + { + return isset($this->fields[$name]); + } + + public function keyExists($name,$type,$cols) + { + # Look for key with the same name + if (isset($this->keys[$name])) { + return $name; + } + + # Look for key with the same columns list and type + foreach ($this->keys as $n => $k) + { + if ($k['cols'] == $cols && $k['type'] == $type) { + # Same columns and type, return new name + return $n; + } + } + + return false; + } + + public function indexExists($name,$type,$cols) + { + # Look for key with the same name + if (isset($this->indexes[$name])) { + return $name; + } + + # Look for index with the same columns list and type + foreach ($this->indexes as $n => $i) + { + if ($i['cols'] == $cols && $i['type'] == $type) { + # Same columns and type, return new name + return $n; + } + } + + return false; + } + + public function referenceExists($name,$c_cols,$p_table,$p_cols) + { + if (isset($this->references[$name])) { + return $name; + } + + # Look for reference with same chil columns, parent table and columns + foreach ($this->references as $n => $r) + { + if ($c_cols == $r['c_cols'] && $p_table == $r['p_table'] && $p_cols == $r['p_cols']) { + # Only name differs, return new name + return $n; + } + } + + return false; + } + + public function field($name,$type,$len,$null=true,$default=false,$to_null=false) + { + $type = strtolower($type); + + if (!in_array($type,$this->allowed_types)) + { + if ($to_null) { + $type = null; + } else { + throw new Exception('Invalid data type '.$type.' in schema'); + } + } + + $this->fields[$name] = array( + 'type' => $type, + 'len' => (integer) $len, + 'default' => $default, + 'null' => (boolean) $null + ); + + return $this; + } + + public function __call($name,$args) + { + array_unshift($args,$name); + return call_user_func_array(array($this,'field'),$args); + } + + public function primary($name,$col) + { + if ($this->has_primary) { + throw new Exception(sprintf('Table %s already has a primary key',$this->name)); + } + + $cols = func_get_args(); + array_shift($cols); + + return $this->newKey('primary',$name,$cols); + } + + public function unique($name,$col) + { + $cols = func_get_args(); + array_shift($cols); + + return $this->newKey('unique',$name,$cols); + } + + public function index($name,$type,$col) + { + $cols = func_get_args(); + array_shift($cols); + array_shift($cols); + + $this->checkCols($cols); + + $this->indexes[$name] = array( + 'type' => strtolower($type), + 'cols' => $cols + ); + + return $this; + } + + public function reference($name,$c_cols,$p_table,$p_cols,$update=false,$delete=false) + { + if (!is_array($p_cols)) { + $p_cols = array($p_cols); + } + if (!is_array($c_cols)) { + $c_cols = array($c_cols); + } + + $this->checkCols($c_cols); + + $this->references[$name] = array( + 'c_cols' => $c_cols, + 'p_table' => $p_table, + 'p_cols' => $p_cols, + 'update' => $update, + 'delete' => $delete + ); + } + + protected function newKey($type,$name,$cols) + { + $this->checkCols($cols); + + $this->keys[$name] = array( + 'type' => $type, + 'cols' => $cols + ); + + if ($type == 'primary') { + $this->has_primary = true; + } + + return $this; + } + + protected function checkCols($cols) + { + foreach ($cols as $v) { + if (!preg_match('/^\(.*?\)$/',$v) && !isset($this->fields[$v])) { + throw new Exception(sprintf('Field %s does not exist in table %s',$v,$this->name)); + } + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dbschema/class.mysql.dbschema.php b/v2/dotclear/inc/libs/clearbricks/dbschema/class.mysql.dbschema.php new file mode 100644 index 0000000..51b9ce2 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dbschema/class.mysql.dbschema.php @@ -0,0 +1,470 @@ +con->select($sql); + + $res = array(); + while ($rs->fetch()) { + $res[] = $rs->f(0); + } + return $res; + } + + public function db_get_columns($table) + { + $sql = 'SHOW COLUMNS FROM '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $field = trim($rs->f('Field')); + $type = trim($rs->f('Type')); + $null = strtolower($rs->f('Null')) == 'yes'; + $default = $rs->f('Default'); + + $len = null; + if (preg_match('/^(.+?)\(([\d,]+)\)$/si',$type,$m)) { + $type = $m[1]; + $len = (integer) $m[2]; + } + + if ($default != '' && !is_numeric($default)) { + $default = "'".$default."'"; + } + + $res[$field] = array( + 'type' => $type, + 'len' => $len, + 'null' => $null, + 'default' => $default + ); + } + return $res; + } + + public function db_get_keys($table) + { + $sql = 'SHOW INDEX FROM '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $t = array(); + $res = array(); + while ($rs->fetch()) + { + $key_name = $rs->f('Key_name'); + $unique = $rs->f('Non_unique') == 0; + $seq = $rs->f('Seq_in_index'); + $col_name = $rs->f('Column_name'); + + if ($key_name == 'PRIMARY' || $unique) { + $t[$key_name]['cols'][$seq] = $col_name; + $t[$key_name]['unique'] = $unique; + } + } + + foreach ($t as $name => $idx) + { + ksort($idx['cols']); + + $res[] = array( + 'name' => $name, + 'primary' => $name == 'PRIMARY', + 'unique' => $idx['unique'], + 'cols' => array_values($idx['cols']) + ); + } + + return $res; + } + + public function db_get_indexes($table) + { + $sql = 'SHOW INDEX FROM '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $t = array(); + $res = array(); + while ($rs->fetch()) + { + $key_name = $rs->f('Key_name'); + $unique = $rs->f('Non_unique') == 0; + $seq = $rs->f('Seq_in_index'); + $col_name = $rs->f('Column_name'); + $type = $rs->f('Index_type'); + + if ($key_name != 'PRIMARY' && !$unique) { + $t[$key_name]['cols'][$seq] = $col_name; + $t[$key_name]['type'] = $type; + } + } + + foreach ($t as $name => $idx) + { + ksort($idx['cols']); + + $res[] = array( + 'name' => $name, + 'type' => $idx['type'], + 'cols' => $idx['cols'] + ); + } + + return $res; + } + + public function db_get_references($table) + { + $sql = 'SHOW CREATE TABLE '.$this->con->escapeSystem($table); + $rs = $this->con->select($sql); + + $s = $rs->f(1); + + $res = array(); + + $n = preg_match_all('/^\s*CONSTRAINT\s+`(.+?)`\s+FOREIGN\s+KEY\s+\((.+?)\)\s+REFERENCES\s+`(.+?)`\s+\((.+?)\)(.*?)$/msi',$s,$match); + if ($n > 0) + { + foreach ($match[1] as $i => $name) + { + # Columns transformation + $t_cols = str_replace('`','',$match[2][$i]); + $t_cols = explode(',',$t_cols); + $r_cols = str_replace('`','',$match[4][$i]); + $r_cols = explode(',',$r_cols); + + # ON UPDATE|DELETE + $on = trim($match[5][$i],', '); + $on_delete = null; + $on_update = null; + if ($on != '') { + if (preg_match('/ON DELETE (.+?)(?:\s+ON|$)/msi',$on,$m)) { + $on_delete = strtolower(trim($m[1])); + } + if (preg_match('/ON UPDATE (.+?)(?:\s+ON|$)/msi',$on,$m)) { + $on_update = strtolower(trim($m[1])); + } + } + + $res[] = array ( + 'name' => $name, + 'c_cols' => $t_cols, + 'p_table' => $match[3][$i], + 'p_cols' => $r_cols, + 'update' => $on_update, + 'delete' => $on_delete + ); + } + } + return $res; + } + + public function db_create_table($name,$fields) + { + $a = array(); + + foreach ($fields as $n => $f) + { + $type = $f['type']; + $len = (integer) $f['len']; + $default = $f['default']; + $null = $f['null']; + + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $a[] = + $this->con->escapeSystem($n).' '. + $type.$len.' '.$null.' '.$default; + } + + $sql = + 'CREATE TABLE '.$this->con->escapeSystem($name)." (\n". + implode(",\n",$a). + "\n) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin "; + + $this->con->execute($sql); + } + + public function db_create_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = (integer) $len > 0 ? '('.(integer) $len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD COLUMN '.$this->con->escapeSystem($name).' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_create_primary($table,$name,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD CONSTRAINT PRIMARY KEY ('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_create_unique($table,$name,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD CONSTRAINT UNIQUE KEY '.$this->con->escapeSystem($name).' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_create_index($table,$name,$type,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD INDEX '.$this->con->escapeSystem($name).' USING '.$type.' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $c = array(); + $p = array(); + foreach ($c_cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + foreach ($p_cols as $v) { + $p[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($c_table).' '. + 'ADD CONSTRAINT '.$name.' FOREIGN KEY '. + '('.implode(',',$c).') '. + 'REFERENCES '.$this->con->escapeSystem($p_table).' '. + '('.implode(',',$p).') '; + + if ($update) { + $sql .= 'ON UPDATE '.$update.' '; + } + if ($delete) { + $sql .= 'ON DELETE '.$delete.' '; + } + + $this->con->execute($sql); + } + + public function db_alter_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = (integer) $len > 0 ? '('.(integer) $len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'CHANGE COLUMN '.$this->con->escapeSystem($name).' '.$this->con->escapeSystem($name).' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_alter_primary($table,$name,$newname,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP PRIMARY KEY, ADD PRIMARY KEY '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_alter_unique($table,$name,$newname,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP INDEX '.$this->con->escapeSystem($name).', '. + 'ADD UNIQUE '.$this->con->escapeSystem($newname).' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_alter_index($table,$name,$newname,$type,$cols) + { + $c = array(); + foreach ($cols as $v) { + $c[] = $this->con->escapeSystem($v); + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP INDEX '.$this->con->escapeSystem($name).', '. + 'ADD INDEX '.$this->con->escapeSystem($newname).' '. + 'USING '.$type.' '. + '('.implode(',',$c).') '; + + $this->con->execute($sql); + } + + public function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($c_table).' '. + 'DROP FOREIGN KEY '.$this->con->escapeSystem($name); + + $this->con->execute($sql); + $this->createReference($newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function db_drop_unique($table,$name) + { + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'DROP INDEX '.$this->con->escapeSystem($name); + $this->con->execute($sql); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dbschema/class.mysqli.dbschema.php b/v2/dotclear/inc/libs/clearbricks/dbschema/class.mysqli.dbschema.php new file mode 100644 index 0000000..719fb56 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dbschema/class.mysqli.dbschema.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dbschema/class.pgsql.dbschema.php b/v2/dotclear/inc/libs/clearbricks/dbschema/class.pgsql.dbschema.php new file mode 100644 index 0000000..bf6591f --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dbschema/class.pgsql.dbschema.php @@ -0,0 +1,400 @@ + 'no action', + 'r' => 'restrict', + 'c' => 'cascade', + 'n' => 'set null', + 'd' => 'set default' + ); + + public function dbt2udt($type,&$len,&$default) + { + $type = parent::dbt2udt($type,$len,$default); + + return $type; + } + + public function udt2dbt($type,&$len,&$default) + { + $type = parent::udt2dbt($type,$len,$default); + + return $type; + } + + public function db_get_tables() + { + $sql = + 'SELECT table_name '. + 'FROM information_schema.tables '. + "WHERE table_schema = current_schema() "; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) { + $res[] = $rs->f(0); + } + return $res; + } + + public function db_get_columns($table) + { + $sql = + 'SELECT column_name, udt_name, character_maximum_length, '. + 'is_nullable, column_default '. + 'FROM information_schema.columns '. + "WHERE table_name = '".$this->con->escape($table)."' "; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $field = trim($rs->column_name); + $type = trim($rs->udt_name); + $null = strtolower($rs->is_nullable) == 'yes'; + $default = $rs->column_default; + $len = $rs->character_maximum_length; + + if ($len == '') { + $len = null; + } + + $default = preg_replace('/::([\w\d\s]*)$/','',$default); + + $res[$field] = array( + 'type' => $type, + 'len' => $len, + 'null' => $null, + 'default' => $default + ); + } + + return $res; + } + + public function db_get_keys($table) + { + $sql = + 'SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as idxname, indisunique::integer, indisprimary::integer, '. + 'indnatts, tab.relname as tabname, contype, amname '. + 'FROM pg_index idx '. + 'JOIN pg_class cls ON cls.oid=indexrelid '. + 'JOIN pg_class tab ON tab.oid=indrelid '. + 'LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace '. + 'JOIN pg_namespace n ON n.oid=tab.relnamespace '. + 'JOIN pg_am am ON am.oid=cls.relam '. + "LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0') ". + 'LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) '. + 'LEFT OUTER JOIN pg_description des ON des.objoid=con.oid '. + 'LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) '. + "WHERE tab.relname = '".$this->con->escape($table)."' ". + "AND contype IN ('p','u') ". + 'ORDER BY cls.relname '; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $k = array( + 'name' => $rs->idxname, + 'primary' => (boolean) $rs->indisprimary, + 'unique' => (boolean) $rs->indisunique, + 'cols' => array() + ); + + for ($i=1; $i<=$rs->indnatts; $i++) { + $cols = $this->con->select('SELECT pg_get_indexdef('.$rs->oid.'::oid, '.$i.', true);'); + $k['cols'][] = $cols->f(0); + } + + $res[] = $k; + } + + return $res; + } + + public function db_get_indexes($table) + { + $sql = + 'SELECT DISTINCT ON(cls.relname) cls.oid, cls.relname as idxname, n.nspname, '. + 'indnatts, tab.relname as tabname, contype, amname '. + 'FROM pg_index idx '. + 'JOIN pg_class cls ON cls.oid=indexrelid '. + 'JOIN pg_class tab ON tab.oid=indrelid '. + 'LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace '. + 'JOIN pg_namespace n ON n.oid=tab.relnamespace '. + 'JOIN pg_am am ON am.oid=cls.relam '. + "LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0') ". + 'LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) '. + 'LEFT OUTER JOIN pg_description des ON des.objoid=con.oid '. + 'LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) '. + "WHERE tab.relname = '".$this->con->escape($table)."' ". + 'AND conname IS NULL '. + 'ORDER BY cls.relname '; + + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $k = array( + 'name' => $rs->idxname, + 'type' => $rs->amname, + 'cols' => array() + ); + + for ($i=1; $i<=$rs->indnatts; $i++) { + $cols = $this->con->select('SELECT pg_get_indexdef('.$rs->oid.'::oid, '.$i.', true);'); + $k['cols'][] = $cols->f(0); + } + + $res[] = $k; + } + + return $res; + } + + public function db_get_references($table) + { + $sql = + 'SELECT ct.oid, conname, condeferrable, condeferred, confupdtype, '. + 'confdeltype, confmatchtype, conkey, confkey, conrelid, confrelid, cl.relname as fktab, '. + 'cr.relname as reftab '. + 'FROM pg_constraint ct '. + 'JOIN pg_class cl ON cl.oid=conrelid '. + 'JOIN pg_namespace nl ON nl.oid=cl.relnamespace '. + 'JOIN pg_class cr ON cr.oid=confrelid '. + 'JOIN pg_namespace nr ON nr.oid=cr.relnamespace '. + "WHERE contype='f' ". + "AND cl.relname = '".$this->con->escape($table)."' ". + 'ORDER BY conname '; + + $rs = $this->con->select($sql); + + $cols_sql = + 'SELECT a1.attname as conattname, a2.attname as confattname '. + 'FROM pg_attribute a1, pg_attribute a2 '. + 'WHERE a1.attrelid=%1$s::oid AND a1.attnum=%2$s '. + 'AND a2.attrelid=%3$s::oid AND a2.attnum=%4$s '; + + $res = array(); + while ($rs->fetch()) + { + $conkey = preg_replace('/[^\d]/','',$rs->conkey); + $confkey = preg_replace('/[^\d]/','',$rs->confkey); + + $k = array( + 'name' => $rs->conname, + 'c_cols' => array(), + 'p_table' => $rs->reftab, + 'p_cols' => array(), + 'update' => $this->ref_actions_map[$rs->confupdtype], + 'delete' => $this->ref_actions_map[$rs->confdeltype] + ); + + $cols = $this->con->select(sprintf($cols_sql,$rs->conrelid,$conkey,$rs->confrelid,$confkey)); + while ($cols->fetch()) { + $k['c_cols'][] = $cols->conattname; + $k['p_cols'][] = $cols->confattname; + } + + $res[] = $k; + } + + return $res; + } + + public function db_create_table($name,$fields) + { + $a = array(); + + foreach ($fields as $n => $f) + { + $type = $f['type']; + $len = (integer) $f['len']; + $default = $f['default']; + $null = $f['null']; + + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $a[] = + $n.' '. + $type.$len.' '.$null.' '.$default; + } + + $sql = + 'CREATE TABLE '.$name." (\n". + implode(",\n",$a). + "\n)"; + + $this->con->execute($sql); + } + + public function db_create_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$table.' ADD COLUMN '.$name.' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_create_primary($table,$name,$cols) + { + $sql = + 'ALTER TABLE '.$table.' '. + 'ADD CONSTRAINT '.$name.' PRIMARY KEY ('.implode(",",$cols).') '; + + $this->con->execute($sql); + } + + public function db_create_unique($table,$name,$cols) + { + $sql = + 'ALTER TABLE '.$table.' '. + 'ADD CONSTRAINT '.$name.' UNIQUE ('.implode(',',$cols).') '; + + $this->con->execute($sql); + } + + public function db_create_index($table,$name,$type,$cols) + { + $sql = + 'CREATE INDEX '.$name.' ON '.$table.' USING '.$type. + '('.implode(',',$cols).') '; + + $this->con->execute($sql); + } + + public function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $sql = + 'ALTER TABLE '.$c_table.' '. + 'ADD CONSTRAINT '.$name.' FOREIGN KEY '. + '('.implode(',',$c_cols).') '. + 'REFERENCES '.$p_table.' '. + '('.implode(',',$p_cols).') '; + + if ($update) { + $sql .= 'ON UPDATE '.$update.' '; + } + if ($delete) { + $sql .= 'ON DELETE '.$delete.' '; + } + + $this->con->execute($sql); + } + + public function db_alter_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = (integer) $len > 0 ? '('.(integer) $len.')' : ''; + + $sql = 'ALTER TABLE '.$table.' ALTER COLUMN '.$name.' TYPE '.$type.$len; + $this->con->execute($sql); + + if ($default === null) { + $default = 'SET DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'SET DEFAULT '.$default; + } else { + $default = 'DROP DEFAULT'; + } + + $sql = 'ALTER TABLE '.$table.' ALTER COLUMN '.$name.' '.$default; + $this->con->execute($sql); + + $null = $null ? 'DROP NOT NULL' : 'SET NOT NULL'; + $sql = 'ALTER TABLE '.$table.' ALTER COLUMN '.$name.' '.$null; + $this->con->execute($sql); + } + + public function db_alter_primary($table,$name,$newname,$cols) + { + $sql = 'ALTER TABLE '.$table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + + $this->createPrimary($table,$newname,$cols); + } + + public function db_alter_unique($table,$name,$newname,$cols) + { + $sql = 'ALTER TABLE '.$table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + + $this->createUnique($table,$newname,$cols); + } + + public function db_alter_index($table,$name,$newname,$type,$cols) + { + $sql = 'DROP INDEX '.$name; + $this->con->execute($sql); + + $this->createIndex($table,$newname,$type,$cols); + } + + public function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $sql = 'ALTER TABLE '.$c_table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + + $this->createReference($newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function db_drop_unique($table,$name) + { + $sql = 'ALTER TABLE '.$table.' DROP CONSTRAINT '.$name; + $this->con->execute($sql); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/dbschema/class.sqlite.dbschema.php b/v2/dotclear/inc/libs/clearbricks/dbschema/class.sqlite.dbschema.php new file mode 100644 index 0000000..2ab7279 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/dbschema/class.sqlite.dbschema.php @@ -0,0 +1,514 @@ +table_stack as $table => $def) + { + $sql = 'CREATE TABLE '.$table." (\n".implode(",\n",$def)."\n)\n "; + $this->con->execute($sql); + } + + foreach ($this->x_stack as $x) + { + $this->con->execute($x); + } + } + + public function db_get_tables() + { + $res = array(); + $sql = "SELECT * FROM sqlite_master WHERE type = 'table'"; + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) { + $res[] = $rs->tbl_name; + } + + return $res; + } + + public function db_get_columns($table) + { + $sql = 'PRAGMA table_info('.$this->con->escapeSystem($table).')'; + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + $field = trim($rs->name); + $type = trim($rs->type); + $null = trim($rs->notnull) == 0; + $default = trim($rs->dflt_value); + + $len = null; + if (preg_match('/^(.+?)\(([\d,]+)\)$/si',$type,$m)) { + $type = $m[1]; + $len = (integer) $m[2]; + } + + $res[$field] = array( + 'type' => $type, + 'len' => $len, + 'null' => $null, + 'default' => $default + ); + } + return $res; + } + + public function db_get_keys($table) + { + $t = array(); + $res = array(); + + # Get primary keys first + $sql = "SELECT sql FROM sqlite_master WHERE type='table' AND name='".$this->con->escape($table)."'"; + $rs = $this->con->select($sql); + + if ($rs->isEmpty()) { + return array(); + } + + # Get primary keys + $n = preg_match_all('/^\s*CONSTRAINT\s+([^,]+?)\s+PRIMARY\s+KEY\s+\((.+?)\)/msi',$rs->sql,$match); + if ($n > 0) + { + foreach ($match[1] as $i => $name) + { + $cols = preg_split('/\s*,\s*/',$match[2][$i]); + $res[] = array( + 'name' => $name, + 'primary' => true, + 'unique' => false, + 'cols' => $cols + ); + } + } + + # Get unique keys + $n = preg_match_all('/^\s*CONSTRAINT\s+([^,]+?)\s+UNIQUE\s+\((.+?)\)/msi',$rs->sql,$match); + if ($n > 0) + { + foreach ($match[1] as $i => $name) + { + $cols = preg_split('/\s*,\s*/',$match[2][$i]); + $res[] = array( + 'name' => $name, + 'primary' => false, + 'unique' => true, + 'cols' => $cols + ); + } + } + + return $res; + } + + public function db_get_indexes($table) + { + $sql = 'PRAGMA index_list('.$this->con->escapeSystem($table).')'; + $rs = $this->con->select($sql); + + $res = array(); + while ($rs->fetch()) + { + if (preg_match('/^sqlite_/',$rs->name)) { + continue; + } + + $idx = $this->con->select('PRAGMA index_info('.$this->con->escapeSystem($rs->name).')'); + $cols = array(); + while ($idx->fetch()) { + $cols[] = $idx->name; + } + + $res[] = array( + 'name' => $rs->name, + 'type' => 'btree', + 'cols' => $cols + ); + } + + return $res; + } + + public function db_get_references($table) + { + $sql = 'SELECT * FROM sqlite_master WHERE type=\'trigger\' AND tbl_name = \'%1$s\' AND name LIKE \'%2$s_%%\' '; + $res = array(); + + # Find constraints on table + $bir = $this->con->select(sprintf($sql,$this->con->escape($table),'bir')); + $bur = $this->con->select(sprintf($sql,$this->con->escape($table),'bur')); + + if ($bir->isEmpty() || $bur->isempty()) { + return $res; + } + + while ($bir->fetch()) + { + # Find child column and parent table and column + if (!preg_match('/FROM\s+(.+?)\s+WHERE\s+(.+?)\s+=\s+NEW\.(.+?)\s*?\) IS\s+NULL/msi',$bir->sql,$m)) { + continue; + } + + $c_col = $m[3]; + $p_table = $m[1]; + $p_col = $m[2]; + + # Find on update + $on_update = 'restrict'; + $aur = $this->con->select(sprintf($sql,$this->con->escape($p_table),'aur')); + while ($aur->fetch()) + { + if (!preg_match('/AFTER\s+UPDATE/msi',$aur->sql)) { + continue; + } + + if (preg_match('/UPDATE\s+'.$table.'\s+SET\s+'.$c_col.'\s*=\s*NEW.'.$p_col. + '\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$aur->sql)) { + $on_update = 'cascade'; + break; + } + + if (preg_match('/UPDATE\s+'.$table.'\s+SET\s+'.$c_col.'\s*=\s*NULL'. + '\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$aur->sql)) { + $on_update = 'set null'; + break; + } + } + + # Find on delete + $on_delete = 'restrict'; + $bdr = $this->con->select(sprintf($sql,$this->con->escape($p_table),'bdr')); + while ($bdr->fetch()) + { + if (!preg_match('/BEFORE\s+DELETE/msi',$bdr->sql)) { + continue; + } + + if (preg_match('/DELETE\s+FROM\s+'.$table.'\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$bdr->sql)) { + $on_delete = 'cascade'; + break; + } + + if (preg_match('/UPDATE\s+'.$table.'\s+SET\s+'.$c_col.'\s*=\s*NULL'. + '\s+WHERE\s+'.$c_col.'\s*=\s*OLD\.'.$p_col.'/msi',$bdr->sql)) { + $on_update = 'set null'; + break; + } + } + + $res[] = array( + 'name' => substr($bir->name,4), + 'c_cols' => array($c_col), + 'p_table' => $p_table, + 'p_cols' => array($p_col), + 'update' => $on_update, + 'delete' => $on_delete + ); + } + + return $res; + } + + public function db_create_table($name,$fields) + { + $a = array(); + + foreach ($fields as $n => $f) + { + $type = $f['type']; + $len = (integer) $f['len']; + $default = $f['default']; + $null = $f['null']; + + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $a[] = $n.' '.$type.$len.' '.$null.' '.$default; + } + + $this->table_stack[$name][] = implode(",\n",$a); + $this->table_hist[$name] = $fields; + } + + public function db_create_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + $len = $len > 0 ? '('.$len.')' : ''; + $null = $null ? 'NULL' : 'NOT NULL'; + + if ($default === null) { + $default = 'DEFAULT NULL'; + } elseif ($default !== false) { + $default = 'DEFAULT '.$default.' '; + } else { + $default = ''; + } + + $sql = + 'ALTER TABLE '.$this->con->escapeSystem($table).' '. + 'ADD COLUMN '.$this->con->escapeSystem($name).' '. + $type.$len.' '.$null.' '.$default; + + $this->con->execute($sql); + } + + public function db_create_primary($table,$name,$cols) + { + $this->table_stack[$table][] = 'CONSTRAINT '.$name.' PRIMARY KEY ('.implode(',',$cols).') '; + } + + public function db_create_unique($table,$name,$cols) + { + $this->table_stack[$table][] = 'CONSTRAINT '.$name.' UNIQUE ('.implode(',',$cols).') '; + } + + public function db_create_index($table,$name,$type,$cols) + { + $this->x_stack[] = 'CREATE INDEX '.$name.' ON '.$table.' ('.implode(',',$cols).') '; + } + + public function db_create_reference($name,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + if (!isset($this->table_hist[$c_table])) { + return; + } + + if (count($c_cols) > 1 || count($p_cols) > 1) { + throw new Exception('SQLite UDBS does not support multiple columns foreign keys'); + } + + $c_col = $c_cols[0]; + $p_col = $p_cols[0]; + + $update = strtolower($update); + $delete = strtolower($delete); + + $cnull = $this->table_hist[$c_table][$c_col]['null']; + + # Create constraint + $this->x_stack[] = + 'CREATE TRIGGER bir_'.$name."\n". + 'BEFORE INSERT ON '.$c_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE(ROLLBACK,\'insert on table "'.$c_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE '. + ($cnull ? 'NEW.'.$c_col." IS NOT NULL\n AND " : ''). + '(SELECT '.$p_col.' FROM '.$p_table.' WHERE '.$p_col.' = NEW.'.$c_col.") IS NULL;\n". + "END;\n"; + + # Update constraint + $this->x_stack[] = + 'CREATE TRIGGER bur_'.$name."\n". + 'BEFORE UPDATE ON '.$c_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE(ROLLBACK,\'update on table "'.$c_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE '. + ($cnull ? 'NEW.'.$c_col." IS NOT NULL\n AND " : ''). + '(SELECT '.$p_col.' FROM '.$p_table.' WHERE '.$p_col.' = NEW.'.$c_col.") IS NULL;\n". + "END;\n"; + + # ON UPDATE + if ($update == 'cascade') + { + $this->x_stack[] = + 'CREATE TRIGGER aur_'.$name."\n". + 'AFTER UPDATE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' UPDATE '.$c_table.' SET '.$c_col.' = NEW.'.$p_col.' WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + elseif ($update == 'set null') + { + $this->x_stack[] = + 'CREATE TRIGGER aur_'.$name."\n". + 'AFTER UPDATE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' UPDATE '.$c_table.' SET '.$c_col.' = NULL WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + else # default on restrict + { + $this->x_stack[] = + 'CREATE TRIGGER burp_'.$name."\n". + 'BEFORE UPDATE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE (ROLLBACK,\'update on table "'.$p_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE (SELECT '.$c_col.' FROM '.$c_table.' WHERE '.$c_col.' = OLD.'.$p_col.") IS NOT NULL;\n". + "END;\n"; + } + + # ON DELETE + if ($delete == 'cascade') + { + $this->x_stack[] = + 'CREATE TRIGGER bdr_'.$name."\n". + 'BEFORE DELETE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' DELETE FROM '.$c_table.' WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + elseif ($delete == 'set null') + { + $this->x_stack[] = + 'CREATE TRIGGER bdr_'.$name."\n". + 'BEFORE DELETE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' UPDATE '.$c_table.' SET '.$c_col.' = NULL WHERE '.$c_col.' = OLD.'.$p_col.";\n". + "END;\n"; + } + else + { + $this->x_stack[] = + 'CREATE TRIGGER bdr_'.$name."\n". + 'BEFORE DELETE ON '.$p_table."\n". + "FOR EACH ROW BEGIN\n". + ' SELECT RAISE (ROLLBACK,\'delete on table "'.$p_table.'" violates foreign key constraint "'.$name.'"\')'."\n". + ' WHERE (SELECT '.$c_col.' FROM '.$c_table.' WHERE '.$c_col.' = OLD.'.$p_col.") IS NOT NULL;\n". + "END;\n"; + } + } + + public function db_alter_field($table,$name,$type,$len,$null,$default) + { + $type = $this->udt2dbt($type,$len,$default); + if ($type != 'integer' && $type != 'text' && $type != 'timestamp') { + throw new Exception('SQLite fields cannot be changed.'); + } + } + + public function db_alter_primary($table,$name,$newname,$cols) + { + throw new Exception('SQLite primary key cannot be changed.'); + } + + public function db_alter_unique($table,$name,$newname,$cols) + { + throw new Exception('SQLite unique index cannot be changed.'); + } + + public function db_alter_index($table,$name,$newname,$type,$cols) + { + $this->con->execute('DROP INDEX IF EXISTS '.$name); + $this->con->execute('CREATE INDEX '.$newname.' ON '.$table.' ('.implode(',',$cols).') '); + } + + public function db_alter_reference($name,$newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete) + { + $this->con->execute('DROP TRIGGER IF EXISTS bur_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS burp_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS bir_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS aur_'.$name); + $this->con->execute('DROP TRIGGER IF EXISTS bdr_'.$name); + + $this->table_hist[$c_table] = $this->db_get_columns($c_table); + $this->db_create_reference($newname,$c_table,$c_cols,$p_table,$p_cols,$update,$delete); + } + + public function db_drop_unique($table,$name) + { + throw new Exception('SQLite unique index cannot be removed.'); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/debian/changelog b/v2/dotclear/inc/libs/clearbricks/debian/changelog new file mode 100644 index 0000000..01eeb48 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/changelog @@ -0,0 +1,466 @@ +libclearbricks (0.8-22) unstable; urgency=low ; svnrev=165 + + * SVN Revision 152 - olivier, on Mon, 30 Jun 2008 15:04:18 +0200 + New debian changelog + + * SVN Revision 153 - olivier, on Thu, 03 Jul 2008 21:12:19 +0200 + SQLite with PDO and SQLite 3 only. + + * SVN Revision 154 - olivier, on Fri, 11 Jul 2008 14:48:43 +0200 + Bug fix with TZ file load. + + * SVN Revision 155 - olivier, on Wed, 16 Jul 2008 15:55:11 +0200 + Minor performances tweaks. + + * SVN Revision 156 - olivier, on Wed, 16 Jul 2008 16:11:17 +0200 + Added getTypes() in urlHandler. + + * SVN Revision 157 - olivier, on Wed, 16 Jul 2008 18:11:01 +0200 + Added support for inline HTML in wiki2xhtml + + * SVN Revision 158 - olivier, on Wed, 16 Jul 2008 18:22:00 +0200 + Description error in wiki2xhtml + + * SVN Revision 159 - olivier, on Thu, 17 Jul 2008 12:51:08 +0200 + Added ?> in lib.crypt.php + + * SVN Revision 160 - olivier, on Thu, 17 Jul 2008 16:13:25 +0200 + Always force a new connection for mysql dblayer. + + * SVN Revision 161 - olivier, on Sun, 20 Jul 2008 16:41:36 +0200 + Added l10n::getTextDirection() method. + + * SVN Revision 162 - olivier, on Sun, 20 Jul 2008 18:35:31 +0200 + l10n::getISOcodes returns now native langue names. + + * SVN Revision 163 - olivier, on Mon, 21 Jul 2008 15:02:59 +0200 + Init content in htmlFilter::aply() + + * SVN Revision 164 - olivier, on Mon, 21 Jul 2008 15:16:15 +0200 + Basic scheme instead of BASIC. + + * SVN Revision 165 - olivier, on Wed, 30 Jul 2008 18:35:08 +0200 + Typo fix in filemanager. + + -- Olivier Meunier Wed, 06 Aug 2008 14:36:17 +0200 + +libclearbricks (0.8-21) unstable; urgency=low ; svnrev=151 + + * SVN Revision 142 - olivier, on Tue, 13 May 2008 14:26:02 +0200 + New debian changelog + + * SVN Revision 143 - olivier, on Mon, 02 Jun 2008 14:45:13 +0200 + In imageTools::memoryAllocate, multiply by 8 instead of 5 + + * SVN Revision 144 - olivier, on Tue, 03 Jun 2008 14:57:26 +0200 + Fixed sorting bug in filemanager + + * SVN Revision 145 - olivier, on Wed, 04 Jun 2008 09:15:57 +0200 + Fixed a minor bug in imageTools when image size becomes zero. + + * SVN Revision 146 - olivier, on Fri, 06 Jun 2008 10:30:46 +0200 + "'" is a space in text::tidyURL + + * SVN Revision 147 - olivier, on Fri, 06 Jun 2008 10:54:59 +0200 + Added filemanager::getPwd() and filemanager::writable() methods. + + * SVN Revision 148 - olivier, on Fri, 06 Jun 2008 14:11:34 +0200 + Removed dead line in feedReader. + + * SVN Revision 149 - olivier, on Mon, 16 Jun 2008 09:25:37 +0200 + Changed longdesc by title in wiki2xhtml. + + * SVN Revision 150 - olivier, on Thu, 19 Jun 2008 17:50:31 +0200 + Added languages in lib.l10n.php + + * SVN Revision 151 - olivier, on Wed, 25 Jun 2008 18:04:43 +0200 + Fixed bugs in l10n and url.handler. + + -- Olivier Meunier Mon, 30 Jun 2008 15:03:33 +0200 + +libclearbricks (0.8-20) unstable; urgency=low ; svnrev=141 + + * SVN Revision 121 - olivier, on Tue, 26 Feb 2008 10:20:48 +0100 + New debian changelog + + * SVN Revision 122 - olivier, on Wed, 27 Feb 2008 09:13:52 +0100 + Fixed seconds reading in fileUnzip + + * SVN Revision 124 - olivier, on Wed, 27 Feb 2008 11:22:23 +0100 + Added fileZip class + + * SVN Revision 126 - olivier, on Wed, 27 Feb 2008 14:03:43 +0100 + More memory for fileUnzip + + * SVN Revision 128 - olivier, on Sun, 02 Mar 2008 14:10:07 +0100 + Fixed time offset output for RFC822 dates. + + * SVN Revision 129 - olivier, on Sun, 02 Mar 2008 14:10:56 +0100 + Tweak in fileUnzip to handle directories in central. + + * SVN Revision 131 - olivier, on Fri, 21 Mar 2008 11:25:28 +0100 + Changed all chmod($file,fileperms(dirname($file))) calls with new files::inheritChmod() static method. + Some minor fixes. + + * SVN Revision 132 - olivier, on Fri, 21 Mar 2008 11:26:33 +0100 + Added files::inheritChmod() calls in filemanager uploadFile() and uploadBits() methods. + Closes #177. + + * SVN Revision 133 - olivier, on Fri, 04 Apr 2008 15:23:51 +0200 + Added translation support in l10n::getISOcodes() + + * SVN Revision 134 - olivier, on Fri, 04 Apr 2008 17:45:23 +0200 + Added template::blockExists(), template::valueExists() and template::tagExists() methods. + + * SVN Revision 135 - olivier, on Thu, 17 Apr 2008 17:54:27 +0200 + filemanager::exclude_list is now a regexp list and handles file upload. + + * SVN Revision 136 - olivier, on Fri, 18 Apr 2008 10:29:17 +0200 + Reverted to revision 134 + + * SVN Revision 137 - olivier, on Fri, 18 Apr 2008 11:41:03 +0200 + Added fileManager::$exclude_pattern property. This works only for files. + + * SVN Revision 138 - pep, on Wed, 23 Apr 2008 02:10:08 +0200 + Should fix problems when sending mail (/r/n or /n header terminator) + + * SVN Revision 139 - olivier, on Wed, 23 Apr 2008 18:07:03 +0200 + memory_get_usage test. + + * SVN Revision 140 - olivier, on Thu, 24 Apr 2008 11:34:02 +0200 + Fix a bug in html::absoluteURLs() + + * SVN Revision 141 - olivier, on Wed, 07 May 2008 15:44:15 +0200 + Added X-Forwarded-For support in netHttp + + -- Olivier Meunier Tue, 13 May 2008 14:04:11 +0200 + +libclearbricks (0.8-18) unstable; urgency=low ; svnrev=120 + + * SVN Revision 109 - olivier, on Tue, 19 Feb 2008 12:50:10 +0100 + New debian changelog + + * SVN Revision 110 - olivier, on Thu, 21 Feb 2008 01:23:59 +0100 + Fixed a bug and added getRootDir method in fileUnzip + + * SVN Revision 112 - olivier, on Thu, 21 Feb 2008 02:18:58 +0100 + Added isEmpty and hasFile methods in fileUnzip + + * SVN Revision 114 - olivier, on Thu, 21 Feb 2008 12:03:01 +0100 + Created text::deaccent and applied this method in text::str2URL and files::tidyFileName + + * SVN Revision 116 - olivier, on Thu, 21 Feb 2008 16:47:25 +0100 + Try to handle memory usage in imageTools. + Add a grey background on alpha channel of PNG images for resize method. + Destroy image resource in close method. + + * SVN Revision 118 - olivier, on Sat, 23 Feb 2008 19:16:15 +0100 + Fix with files::registerMimeTypes + + * SVN Revision 119 - olivier, on Mon, 25 Feb 2008 11:36:41 +0100 + fileUnzip - Added a better way to recognize directories and tries + to increase memory limit if needed. + + -- Olivier Meunier Tue, 26 Feb 2008 10:19:58 +0100 + +libclearbricks (0.8-17) unstable; urgency=low ; svnrev=108 + + * SVN Revision 108 - olivier, on Tue, 19 Feb 2008 10:25:56 +0100 + New debian changelog + + -- Olivier Meunier Tue, 19 Feb 2008 12:47:49 +0100 + +libclearbricks (0.8-14) unstable; urgency=low ; svnrev=107 + + * SVN Revision 63 - mik, on Fri, 06 Jul 2007 13:19:43 +0200 + last version + + * SVN Revision 64 - olivier, on Fri, 13 Jul 2007 12:07:00 +0200 + May have fixed some bugs in UDBS synchronize process. + + * SVN Revision 65 - olivier, on Thu, 26 Jul 2007 10:08:11 +0200 + HTML validator class + + * SVN Revision 66 - olivier, on Thu, 26 Jul 2007 10:17:29 +0200 + HTML validator documentation update + + * SVN Revision 67 - olivier, on Fri, 27 Jul 2007 16:36:30 +0200 + Fixed a serious bug with tidy in HTML Filter + + * SVN Revision 68 - olivier, on Fri, 27 Jul 2007 16:40:40 +0200 + Better way to fix tidy issue in HTML Filter + + * SVN Revision 69 - olivier, on Sat, 28 Jul 2007 10:32:28 +0200 + You can now add some patterns for html::absoluteURLHandler() + + * SVN Revision 70 - olivier, on Mon, 30 Jul 2007 11:22:32 +0200 + Try to read php://input if HTTP_RAW_POST_DATA is empty in net.xmlrpc. + + * SVN Revision 71 - olivier, on Tue, 21 Aug 2007 10:00:58 +0200 + Allow some specific index to exist in DB and Schema. Typo fixes in comments and messages. + + * SVN Revision 72 - biou, on Sun, 02 Sep 2007 11:58:16 +0200 + text::cutString now works in utf-8 + (possible problems with trackbacks) + thx sasha for detecting the bug + + * SVN Revision 73 - olivier, on Thu, 20 Sep 2007 15:22:36 +0200 + Changed some properties scope + + * SVN Revision 76 - olivier, on Wed, 07 Nov 2007 15:06:38 +0100 + Fixed a bug with timezone offset in ISO8601 and RFC822 dates + + * SVN Revision 89 - olivier, on Sat, 01 Dec 2007 12:01:23 +0100 + Changed require by require_once in dbLayer::init + + * SVN Revision 92 - olivier, on Tue, 11 Dec 2007 22:53:56 +0100 + Fixed realIP() code. + + * SVN Revision 94 - biou, on Sat, 29 Dec 2007 11:41:47 +0100 + ability to register new mime types + + * SVN Revision 95 - biou, on Wed, 09 Jan 2008 11:10:26 +0100 + autoload upgraded to use spl_autoload_register (with backward compat) + + * SVN Revision 96 - olivier, on Tue, 15 Jan 2008 16:11:37 +0100 + Changed the way we sort items in filemanager + + * SVN Revision 98 - olivier, on Fri, 15 Feb 2008 15:43:35 +0100 + Added unzip class + + * SVN Revision 100 - olivier, on Fri, 15 Feb 2008 22:31:39 +0100 + Changed getFilesList() and added getDirsList() in fileUnzip class. + + * SVN Revision 101 - olivier, on Fri, 15 Feb 2008 22:50:25 +0100 + Don't append zip file basename to target in unzipAll (fileUnzip) + + * SVN Revision 102 - olivier, on Fri, 15 Feb 2008 23:12:12 +0100 + Moved exclude condition from unzipAll() to getList() in fileUnzip. + + * SVN Revision 103 - olivier, on Sat, 16 Feb 2008 00:40:16 +0100 + Removed files::makePackage and files::installPackage. + + * SVN Revision 104 - olivier, on Mon, 18 Feb 2008 14:00:42 +0100 + Fix in urlHandler class + + * SVN Revision 106 - olivier, on Mon, 18 Feb 2008 15:58:57 +0100 + Added cleanFileName() in fileUnzip class. + + -- Olivier Meunier Tue, 19 Feb 2008 10:24:09 +0100 + +libclearbricks (0.8-13) unstable; urgency=low ; svnrev=62 + + * SVN Revision 61 - mik, on Mon, 02 Jul 2007 11:10:18 +0200 + last version changelog + + * SVN Revision 62 - olivier, on Fri, 06 Jul 2007 13:14:27 +0200 + Fixed a bug in socketMail::getFrom() + + -- Olivier Meunier Fri, 06 Jul 2007 13:18:46 +0200 + +libclearbricks (0.8-12) unstable; urgency=low ; svnrev=60 + + * SVN Revision 53 - pep, on Sun, 03 Jun 2007 12:31:25 +0200 + Changed the way we obtain current TZ + + * SVN Revision 54 - olivier, on Mon, 11 Jun 2007 10:46:06 +0200 + Changed default values in sessionDB constructor + + * SVN Revision 55 - pep, on Tue, 12 Jun 2007 19:51:21 +0200 + Fixed a bug + + * SVN Revision 56 - biou, on Wed, 13 Jun 2007 14:24:02 +0200 + permits file creation with putContent + + * SVN Revision 57 - biou, on Thu, 21 Jun 2007 17:15:06 +0200 + added a consistency check on the moveFile method + + * SVN Revision 58 - olivier, on Thu, 28 Jun 2007 16:03:35 +0200 + Added video/x-flv in lib.files + + * SVN Revision 59 - olivier, on Mon, 02 Jul 2007 10:29:10 +0200 + Fixed bugs in socketMail::getFrom + + * SVN Revision 60 - mik, on Mon, 02 Jul 2007 11:07:16 +0200 + export LANG=C before svn commands + + -- Olivier Meunier Mon, 02 Jul 2007 11:09:40 +0200 + +libclearbricks (0.8-11) unstable; urgency=low ; svnrev=52 + + * SVN Revision 36 - olivier, on Thu, 15 Feb 2007 19:02:26 +0100 + Fixed redirect bug in netHttp + + * SVN Revision 37 - olivier, on Sun, 18 Feb 2007 18:08:32 +0100 + Added default timezone in common/_main.php + + * SVN Revision 38 - olivier, on Sun, 18 Feb 2007 18:17:22 +0100 + Changed template::getData() code. + + * SVN Revision 39 - olivier, on Sun, 18 Feb 2007 22:20:26 +0100 + template::getData() optim. + + * SVN Revision 40 - olivier, on Mon, 19 Feb 2007 11:02:48 +0100 + Added strict_check mode in xmlrpcServer. Default to false. + + * SVN Revision 41 - olivier, on Mon, 19 Feb 2007 11:24:51 +0100 + Removed l10n::getHtmlFile() and added l10n::getFilePath(). + + * SVN Revision 42 - olivier, on Fri, 23 Feb 2007 14:00:39 +0100 + Fixed a bug with starting dot lines in nntp class. + + * SVN Revision 43 - pep, on Fri, 23 Feb 2007 16:43:40 +0100 + Small fix : seems better ... + + * SVN Revision 44 - olivier, on Mon, 26 Feb 2007 14:35:56 +0100 + Forgot 'class' attribute for 'a' tag. + + * SVN Revision 45 - pep, on Wed, 28 Feb 2007 02:50:48 +0100 + Shouldn't inflate incoming content when use_gzip = false + + * SVN Revision 46 - olivier, on Wed, 21 Mar 2007 16:33:28 +0100 + * New dbSchema classes collection. + * Removed getTables() and getColumns() method from dbLayer(). + * Added a database() method in dbLayer returning current database in use. + + * SVN Revision 47 - olivier, on Wed, 21 Mar 2007 23:58:02 +0100 + Returns changes count in dbStruct::synchronize(). + + * SVN Revision 48 - olivier, on Fri, 30 Mar 2007 17:30:22 +0200 + Added a static property (https_scheme_on_443) to force https scheme if port is 443 + in http::getHost(). + + * SVN Revision 49 - olivier, on Mon, 02 Apr 2007 13:35:06 +0200 + added http::browserUID. + + * SVN Revision 50 - olivier, on Mon, 02 Apr 2007 14:52:44 +0200 + Allow secure session cookies + + * SVN Revision 51 - olivier, on Mon, 02 Apr 2007 14:57:20 +0200 + Added cookie_domain for session + + * SVN Revision 52 - olivier, on Tue, 17 Apr 2007 17:53:44 +0200 + Signature fix in mail.convert + + -- Olivier Meunier Thu, 19 Apr 2007 16:06:54 +0200 + +libclearbricks (0.8-10) unstable; urgency=low ; svnrev=35 + + * SVN Revision 33 - mik, on Thu, 01 Feb 2007 18:39:56 +0100 + Romuald:gruik pour display de la date en fonction de la timezone + + * SVN Revision 34 - olivier, on Mon, 05 Feb 2007 11:05:25 +0100 + Timezone display in dt::str and dt::dt2str + + * SVN Revision 35 - olivier, on Mon, 05 Feb 2007 16:22:59 +0100 + net.xmlrpc + + -- Olivier Meunier Mon, 05 Feb 2007 17:50:20 +0100 + +libclearbricks (0.8-9) unstable; urgency=low ; svnrev=32 + + * SVN Revision 30 - olivier, on Thu, 25 Jan 2007 16:53:01 +0100 + Implemented status_string in netHttp + + * SVN Revision 31 - olivier, on Wed, 31 Jan 2007 13:53:10 +0100 + NEWNEWS implementation in netNntp::getNewArticles(). + + * SVN Revision 32 - olivier, on Wed, 31 Jan 2007 19:32:05 +0100 + Fix in net.nntp + + -- Olivier Meunier Wed, 31 Jan 2007 19:36:08 +0100 + +libclearbricks (0.8-8) unstable; urgency=low ; svnrev=29 + + * SVN Revision 28 - olivier, on Thu, 25 Jan 2007 10:31:20 +0100 + Can now specify a port for postgresql connection. + + * SVN Revision 29 - olivier, on Thu, 25 Jan 2007 14:47:15 +0100 + Fix in text::QPEncode() static method + + -- Olivier Meunier Thu, 25 Jan 2007 15:31:25 +0100 + +libclearbricks (0.8-7) unstable; urgency=low ; svnrev=27 + + * SVN Revision 26 - olivier, on Wed, 17 Jan 2007 16:57:51 +0100 + Chmod on feed cache file + + * SVN Revision 27 - olivier, on Wed, 17 Jan 2007 16:58:09 +0100 + Chmod on template cache file + + -- Olivier Meunier Wed, 17 Jan 2007 16:59:11 +0100 + +libclearbricks (0.8-6) unstable; urgency=low ; svnrev=25 + + * SVN Revision 25 - olivier, on Wed, 17 Jan 2007 16:22:49 +0100 + Fixed chmod bug. + + -- Olivier Meunier Wed, 17 Jan 2007 16:23:25 +0100 + +libclearbricks (0.8-5) unstable; urgency=low ; svnrev=24 + + * SVN Revision 24 - olivier, on Tue, 16 Jan 2007 16:13:10 +0100 + New debian changelog + + -- Olivier Meunier Wed, 17 Jan 2007 14:26:32 +0100 + +libclearbricks (0.8-4) unstable; urgency=low ; svnrev=23 + + * SVN Revision 18 - mik, on Fri, 12 Jan 2007 13:33:59 +0100 + libclearbricks' readme in the package + + * SVN Revision 19 - mik, on Fri, 12 Jan 2007 13:36:13 +0100 + empty so removed + + * SVN Revision 20 - mik, on Mon, 15 Jan 2007 18:24:05 +0100 + README file copied in debian dir + + * SVN Revision 21 - olivier, on Tue, 16 Jan 2007 15:17:05 +0100 + Test .svn dir before mkdcl.php launch + + * SVN Revision 22 - olivier, on Tue, 16 Jan 2007 15:48:04 +0100 + Fix in files::touch() method + + * SVN Revision 23 - olivier, on Tue, 16 Jan 2007 16:12:02 +0100 + Zend hash del key or index fix. + + -- Olivier Meunier Tue, 16 Jan 2007 16:12:50 +0100 + +libclearbricks (0.8-3) unstable; urgency=low ; svnrev=17 + + * SVN Revision 13 - olivier, on Wed, 10 Jan 2007 00:51:10 +0100 + Makefile and Debian package for clearbricks + + * SVN Revision 14 - olivier, on Wed, 10 Jan 2007 16:27:08 +0100 + Clearbricks Debian packager + + * SVN Revision 15 - olivier, on Thu, 11 Jan 2007 14:44:42 +0100 + Test if database functions exist on dblayer driver load. + + * SVN Revision 16 - olivier, on Fri, 12 Jan 2007 01:30:36 +0100 + Fixed Windows bug in files::makeDir. + + * SVN Revision 17 - olivier, on Fri, 12 Jan 2007 01:57:56 +0100 + Fixed an issue with recursive files::makeDir and open_basedir. + + -- Olivier Meunier Fri, 12 Jan 2007 13:34:21 +0100 + +libclearbricks (0.8-2) unstable; urgency=low ; svnrev=12 + + * SVN Revision 10 - olivier, on Tue, 09 Jan 2007 13:53:03 +0100 + Added default HTTP Status Messages in http::head() + + * SVN Revision 11 - olivier, on Tue, 09 Jan 2007 16:46:09 +0100 + Added clearbricks version number. + + * SVN Revision 12 - olivier, on Wed, 10 Jan 2007 00:17:53 +0100 + Fixed PHP 5.0 compatibilty issue \o/ + + -- Olivier Meunier Wed, 10 Jan 2007 16:24:31 +0100 + +libclearbricks (0.8-1) unstable; urgency=low ; svnrev=9 + + First upstream release + + -- Olivier Meunier Tue, 09 Jan 2007 16:50:12 +0100 + diff --git a/v2/dotclear/inc/libs/clearbricks/debian/compat b/v2/dotclear/inc/libs/clearbricks/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/compat @@ -0,0 +1 @@ +4 diff --git a/v2/dotclear/inc/libs/clearbricks/debian/control b/v2/dotclear/inc/libs/clearbricks/debian/control new file mode 100644 index 0000000..f371d2c --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/control @@ -0,0 +1,14 @@ +Source: libclearbricks +Section: web +Priority: optional +Maintainer: Olivier Meunier +Build-Depends: debhelper (>= 4.0.0), php5-cli +Standards-Version: 3.6.1 + +Package: libclearbricks +Architecture: all +Depends: ${misc:Depends}, php5-cli | php5-cgi | libapache2-mod-php5 | libapache-mod-php5 +Suggests: php5-pgsql | php5-sqlite | php5-mysql, php5-gd, php5-tidy +Description: libclearbricks is a set of PHP classes and utilities + Clearbricks is not yet another PHP framework. Clearbricks is only + about code and efficiency, consider it as a toolbox. diff --git a/v2/dotclear/inc/libs/clearbricks/debian/copyright b/v2/dotclear/inc/libs/clearbricks/debian/copyright new file mode 100644 index 0000000..235ba0d --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/copyright @@ -0,0 +1,27 @@ +This is Clearbricks, written and maintained by Olivier Meunier <> +on Fri, 29 Dec 2006 16:55:20 +0200. + +The original source can always be found at: + http://clearbricks.org/ + +Copyright (C) 2006 Olivier Meunier + +License: + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. diff --git a/v2/dotclear/inc/libs/clearbricks/debian/dirs b/v2/dotclear/inc/libs/clearbricks/debian/dirs new file mode 100644 index 0000000..67ddb20 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/dirs @@ -0,0 +1 @@ +usr/lib/clearbricks diff --git a/v2/dotclear/inc/libs/clearbricks/debian/docs b/v2/dotclear/inc/libs/clearbricks/debian/docs new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/docs @@ -0,0 +1 @@ +README diff --git a/v2/dotclear/inc/libs/clearbricks/debian/mkdcl.php b/v2/dotclear/inc/libs/clearbricks/debian/mkdcl.php new file mode 100644 index 0000000..39688c7 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/mkdcl.php @@ -0,0 +1,198 @@ +#!/usr/bin/env php +f)) { + throw new Exception('No changelog file found'); + } + } + + private function readLastRevision() + { + $f = file($this->f); + $res = array(); + $done = false; + + foreach ($f as $v) + { + $v = rtrim($v,"\n"); + + # First line of a change + if (strpos($v,' ') !== 0 && trim($v) != '') + { + if ($done) { + break; + } + + $done = true; + $res = $this->getPackageInfo($v,$res[$i]); + } + # Maintainer information + elseif (strpos($v,' --') === 0) + { + $res['maintainer'] = $this->getMaintainerInfo($v); + } + # Changelog + elseif (strpos($v,' ') === 0) + { + $res['changelog'] .= $v."\n"; + } + } + + return $res; + } + + public function writeChangelog() + { + $ch = $this->readLastRevision(); + + # Get debian revision + $rev = 1; + if (preg_match('/^(.*)-(\d+)$/',$ch['version'],$m)) { + $ch['version'] = $m[1]; + $rev = $m[2]; + } + $rev++; + + # Get SVN revision + $svnrev = isset($ch['keywords']['svnrev']) ? (integer) $ch['keywords']['svnrev'] : 1; + + # Get current SVN revision + $currev = svnInfo::getCurrentRevision(); + if ($currev <= $svnrev) { + return; + } + + $changelog = ''; + $changes = svnInfo::getChangeLog($svnrev+1,$currev); + foreach ($changes as $k => $v) + { + $changelog .= + ' * SVN Revision '.$k.' - '.$v['author']. + ', on '.date('r',strtotime($v['date']))."\n". + ' '.trim(preg_replace('/\n/ms',"\n ",$v['msg']))."\n\n"; + + } + + $res = + $ch['package'].' ('.$ch['version'].'-'.$rev.') '.$ch['dist'].'; urgency='.$ch['keywords']['urgency']. + ' ; svnrev='.$currev. + "\n\n". + rtrim($changelog)."\n\n". + ' -- '.$ch['maintainer']['name'].' <'.$ch['maintainer']['email'].'> '.date('r')."\n". + "\n"; + + $old_changelog = file_get_contents($this->f); + $fp = fopen($this->f,'wb'); + fwrite($fp,$res.$old_changelog); + fclose($fp); + } + + private function getPackageInfo($l) + { + $res = array( + 'package' => '', + 'version' => '', + 'dist' => '', + 'keywords' => '', + 'changelog' => '', + 'maintainer' => array() + ); + + $l = explode(';',$l); + + # Info + $info = array_shift($l); + $res['package'] = strtok($info,' '); + $res['version'] = strtok('()'); + $res['dist'] = trim(strtok(';')); + + # Keywords + foreach ($l as $v) { + $v = explode('=',$v); + if (count($v) == 2) { + $res['keywords'][trim($v[0])] = trim($v[1]); + } + } + + return $res; + } + + private function getMaintainerInfo($l) + { + $res = array( + 'name' => '', + 'email' => '', + 'date' => '' + ); + + if (preg_match('/^ -- (.+?) <(.+?)> (.+?)$/',$l,$m)) { + $res['name'] = $m[1]; + $res['email'] = $m[2]; + $res['date'] = $m[3]; + } + + return $res; + } +} + +class svnInfo +{ + public static function getCurrentRevision() + { + $info = `LANG=C svn info --xml`; + + $x = @simplexml_load_string($info); + if (!$x) { + throw new Exception('Unable to get current SVN revision'); + } + + $rev = $x->entry->commit['revision']; + + if (!$rev) { + throw new Exception('Last revision number is invalid'); + } + + return (integer) $rev; + } + + public static function getChangeLog($fromrev,$torev) + { + $log = `LANG=C svn log --xml -r $fromrev:$torev`; + + $x = @simplexml_load_string($log); + if (!$x) { + throw new Exception('Unable to open SVN log'); + } + + $res = array(); + foreach ($x->logentry as $change) + { + $res[(integer) $change['revision']] = array( + 'author' => (string) $change->author, + 'date' => (string) $change->date, + 'msg' => trim((string) $change->msg) + ); + } + + return $res; + } +} + +# Main +try +{ + $ch = new debianChangelog(); + $ch->writeChangelog(); +} +catch (Exception $e) +{ + fwrite(STDERR,$e->getMessage()."\n"); + exit(1); +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/debian/rules b/v2/dotclear/inc/libs/clearbricks/debian/rules new file mode 100644 index 0000000..5f5f3e9 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/debian/rules @@ -0,0 +1,62 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +DEST=debian/dotclear/usr/share/dotclear + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + touch configure-stamp + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + $(MAKE) config + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + cp -af $(CURDIR)/_dist/clearbricks $(CURDIR)/debian/libclearbricks/usr/lib/ + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/v2/dotclear/inc/libs/clearbricks/diff/lib.diff.php b/v2/dotclear/inc/libs/clearbricks/diff/lib.diff.php new file mode 100644 index 0000000..ccc62f3 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/diff/lib.diff.php @@ -0,0 +1,366 @@ +0); + $end_reached = false; + + # Find LCS length + for ($D = 0; $D < $cx+$cy+1 && !$end_reached; $D++) + { + for ($k = -$D; $k <= $D; $k += 2) + { + $x = ($k == -$D || $k != $D && $V[$k-1] < $V[$k+1]) + ? $V[$k+1] : $V[$k-1]+1; + $y = $x-$k; + + while ($x < $cx && $y < $cy && $src[$x] == $dst[$y]) + { + $x++; $y++; + } + + $V[$k] = $x; + + if ($x == $cx && $y == $cy) { + $end_reached = true; + break; + } + } + $stack[] = $V; + } + $D--; + + # Recover edit path + $res = array(); + for ($D = $D; $D > 0; $D--) + { + $V = array_pop($stack); + $cx = $x; + $cy = $y; + + # Try right diagonal + $k++; + $x = array_key_exists($k,$V) ? $V[$k] : 0; + $y = $x-$k; + $y++; + + while ($x < $cx && $y < $cy + && isset($src[$x]) && isset($dst[$y]) && $src[$x] == $dst[$y]) + { + $x++; $y++; + } + + if ($x == $cx && $y == $cy) { + $x = $V[$k]; + $y = $x-$k; + + $res[] = array('i',$x,$y); + continue; + } + + # Right diagonal wasn't the solution, use left diagonal + $k -= 2; + $x = $V[$k]; + $y = $x-$k; + $res[] = array('d',$x,$y); + } + + return array_reverse($res); + } + + /** + * Returns unified diff from source $src to destination $dst. + * + * @param string $src Original data + * @param string $dst New data + * @param string $ctx Context length + * @return string + */ + public static function uniDiff($src,$dst,$ctx=2) + { + list($src,$dst) = array(explode("\n",$src),explode("\n",$dst)); + $cx = count($src); $cy = count($dst); + + $ses = diff::SES($src,$dst); + $res = ''; + + $pos_x = 0; + $pos_y = 0; + $old_lines = 0; + $new_lines = 0; + $buffer = ''; + + foreach ($ses as $cmd) + { + list($cmd,$x,$y) = array($cmd[0],$cmd[1],$cmd[2]); + + # New chunk + if ($x-$pos_x > 2*$ctx || $pos_x == 0 && $x > $ctx) + { + # Footer for current chunk + for ($i = 0; $buffer && $i < $ctx; $i++) + { + $buffer .= sprintf(self::$us_ctx,$src[$pos_x+$i]); + } + + # Header for current chunk + $res .= sprintf(self::$us_range, + $pos_x+1-$old_lines,$old_lines+$i, + $pos_y+1-$new_lines,$new_lines+$i + ).$buffer; + + $pos_x = $x; + $pos_y = $y; + $old_lines = 0; + $new_lines = 0; + $buffer = ''; + + # Header for next chunk + for ($i = $ctx; $i > 0 ; $i--) + { + $buffer .= sprintf(self::$us_ctx,$src[$pos_x-$i]); + $old_lines++; + $new_lines++; + } + } + + # Context + while ($x > $pos_x) + { + $old_lines++; + $new_lines++; + $buffer .= sprintf(self::$us_ctx,$src[$pos_x]); + $pos_x++; + $pos_y++; + } + # Deletion + if ($cmd == 'd') { + $old_lines++; + $buffer .= sprintf(self::$us_del,$src[$x]); + $pos_x++; + } + # Insertion + elseif ($cmd == 'i') { + $new_lines++; + $buffer .= sprintf(self::$us_ins,$dst[$y]); + $pos_y++; + } + } + + # Remaining chunk + if ($buffer) + { + # Footer + for ($i = 0; $i < $ctx; $i++) + { + if (!isset($src[$pos_x+$i])) { + break; + } + $buffer .= sprintf(self::$us_ctx,$src[$pos_x+$i]); + } + + # Header for current chunk + $res .= sprintf(self::$us_range, + $pos_x+1-$old_lines,$old_lines+$i, + $pos_y+1-$new_lines,$new_lines+$i + ).$buffer; + } + return $res; + } + + /** + * Applies a unified patch to a piece of text. + * Throws an exception on invalid or not applicable diff. + * + * @param string $src Source text + * @param string $diff Patch to apply + * @return string + */ + public static function uniPatch($src,$diff) + { + $dst = array(); + $src = explode("\n",$src); + $diff = explode("\n",$diff); + + $t = count($src); + $old_length = $new_length = 0; + + foreach ($diff as $line) + { + # New chunk + if (preg_match(self::$up_range,$line,$m)) { + $m[1]--; $m[3]--; + + if ($m[1] > $t) { + throw new Exception(__('Bad range')); + } + + if ($t - count($src) > $m[1]) { + throw new Exception(__('Invalid range')); + } + + while ($t - count($src) < $m[1]) + { + $dst[] = array_shift($src); + } + + if (count($dst) !== $m[3]) { + throw new Exception(__('Invalid line number')); + } + + if ($old_length || $new_length) { + throw new Exception(__('Chunk is out of range')); + } + + $old_length = (integer) $m[2]; + $new_length = (integer) $m[4]; + } + # Context + elseif (preg_match(self::$up_ctx,$line,$m)) { + if (array_shift($src) !== $m[1]) { + throw new Exception(__('Bad context')); + } + $dst[] = $m[1]; + $old_length--; + $new_length--; + } + # Addition + elseif (preg_match(self::$up_ins,$line,$m)) { + $dst[] = $m[1]; + $new_length--; + } + # Deletion + elseif (preg_match(self::$up_del,$line,$m)) { + if (array_shift($src) !== $m[1]) { + throw new Exception(__('Bad context (in deletion)')); + } + $old_length--; + } + elseif ($line == '') { + continue; + } + else { + throw new Exception(__('Invalid diff format')); + } + } + + if ($old_length || $new_length) { + throw new Exception(__('Chunk is out of range')); + } + + return implode("\n",array_merge($dst,$src)); + } + + /** + * Throws an exception on invalid unified diff. + * + * @param string $diff Diff text to check + */ + public static function uniCheck($diff) + { + $diff = explode("\n",$diff); + + $cur_line = 1; $ins_lines = 0; + + # Chunk length + $old_length = $new_length = 0; + + foreach ($diff as $line) + { + # New chunk + if (preg_match(self::$up_range,$line,$m)) { + if ($cur_line > $m[1]) { + throw new Exception(__('Invalid range')); + } + while ($cur_line < $m[1]) + { + $ins_lines++; $cur_line++; + } + if ($ins_lines+1 != $m[3]) { + throw new Exception(__('Invalid line number')); + } + + if ($old_length || $new_length) { + throw new Exception(__('Chunk is out of range')); + } + + $old_length = $m[2]; + $new_length = $m[4]; + } + # Context + elseif (preg_match(self::$up_ctx,$line,$m)) { + $ins_lines++; $cur_line++; + $old_length--; + $new_length--; + } + # Addition + elseif (preg_match(self::$up_ins,$line,$m)) { + $ins_lines++; + $new_length--; + } + # Deletion + elseif (preg_match(self::$up_del,$line,$m)) { + $cur_line++; + $old_length--; + } + # Skip empty lines + elseif ($line == '') { + continue; + } + # Unrecognized diff format + else { + throw new Exception(__('Invalid diff format')); + } + } + + if ($old_length || $new_length) { + throw new Exception(__('Chunk is out of range')); + } + } +} + +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/diff/lib.tidy.diff.php b/v2/dotclear/inc/libs/clearbricks/diff/lib.tidy.diff.php new file mode 100644 index 0000000..3885797 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/diff/lib.tidy.diff.php @@ -0,0 +1,353 @@ +up_range,$udiff,$context); + + $chunks = preg_split($this->up_range,$udiff,-1,PREG_SPLIT_NO_EMPTY); + + foreach ($chunks as $k => $chunk) + { + $tidy_chunk = new tidyDiffChunk(); + $tidy_chunk->setRange( + (int) $context[1][$k], + (int) $context[2][$k], + (int) $context[3][$k], + (int) $context[4][$k] + ); + + $old_line = (int) $context[1][$k]; + $new_line = (int) $context[3][$k]; + + foreach (explode("\n",$chunk) as $line) + { + # context + if (preg_match($this->up_ctx,$line,$m)) { + $tidy_chunk->addLine('context',array($old_line,$new_line),$m[1]); + $old_line++; + $new_line++; + } + # insertion + if (preg_match($this->up_ins,$line,$m)) { + $tidy_chunk->addLine('insert',array($old_line,$new_line),$m[1]); + $new_line++; + } + # deletion + if (preg_match($this->up_del,$line,$m)) { + $tidy_chunk->addLine('delete',array($old_line,$new_line),$m[1]); + $old_line++; + } + } + + if ($inline_changes) { + $tidy_chunk->findInsideChanges(); + } + + array_push($this->__data,$tidy_chunk); + } + } + + /** + * All chunks + * + * Returns all chunks defined. + * + * @return array + */ + public function getChunks() + { + return $this->__data; + } +} + +/** +* TIDY diff chunk +* +* A diff chunk representation. Used by a TIDY diff. +* +* @package Clearbricks +* @subpackage Diff +*/ +class tidyDiffChunk +{ + /** @var array Chunk information array */ + protected $__info; + /** @var array Chunk data array */ + protected $__data; + + /** + * Constructor + * + * Creates and initializes a chunk representation for a TIDY diff. + */ + public function __construct() + { + $this->__info = array( + 'context' => 0, + 'delete' => 0, + 'insert' => 0, + 'range' => array( + 'start' => array(), + 'end' => array() + ) + ); + $this->__data = array(); + } + + /** + * Set chunk range + * + * Sets chunk range in TIDY chunk object. + * + * @param integer $line_start Old start line number + * @param integer $offest_start Old offset number + * @param integer $line_end new start line number + * @param integer $offset_end New offset number + */ + public function setRange($line_start,$offest_start,$line_end,$offset_end) + { + if (is_int($line_start) && is_int($offest_start) && is_int($line_end) && is_int($offset_end)) + { + $this->__info['range']['start'] = array($line_start,$offest_start); + $this->__info['range']['end'] = array($line_end,$offset_end); + } + } + + /** + * Add line + * + * Adds TIDY line object for TIDY chunk object. + * + * @param string $type Tine type + * @param array $lines Line number for old and new context + * @param string $content Line content + */ + public function addLine($type,$lines,$content) + { + $tidy_line = new tidyDiffLine($type,$lines,$content); + + if (!is_null($tidy_line)) { + array_push($this->__data,$tidy_line); + $this->__info[$type]++; + } + } + + /** + * All lines + * + * Returns all lines defined. + * + * @return array + */ + public function getLines() + { + return $this->__data; + } + + /** + * Chunk information + * + * Returns chunk information according to the given name, null otherwise. + * + * @param string $n Info name + * @return string + */ + public function getInfo($n) + { + return array_key_exists($n,$this->__info) ? $this->__info[$n] : null; + } + + /** + * Find changes + * + * Finds changes inside lines for each groups of diff lines. Wraps changes + * by string \0 and \1 + */ + public function findInsideChanges() + { + $groups = $this->getGroups(); + + foreach ($groups as $group) { + $middle = count($group) / 2; + for ($i = 0; $i < $middle; $i++) { + $from = $group[$i]; + $to = $group[$i + $middle]; + $threshold = $this->getChangeExtent($from->content,$to->content); + + if ($threshold['start'] != 0 || $threshold['end'] != 0) + { + $start = $threshold['start']; + $end = $threshold['end'] + strlen($from->content); + $offset = $end - $start; + $from->overwrite( + substr($from->content,0,$start).'\0'. + substr($from->content,$start,$offset).'\1'. + substr($from->content,$end,strlen($from->content)) + ); + $end = $threshold['end'] + strlen($to->content); + $offset = $end - $start; + $to->overwrite( + substr($to->content,0,$start).'\0'. + substr($to->content,$start,$offset).'\1'. + substr($to->content,$end,strlen($to->content)) + ); + } + } + } + } + + private function getGroups() + { + $res = $group = array(); + $allowed_types = array('delete','insert'); + $delete = $insert = 0; + + foreach ($this->__data as $k => $line) { + if (in_array($line->type,$allowed_types)) { + array_push($group,$line); + ${$line->type}++; + } else { + if ($delete === $insert && count($group) > 0) { + array_push($res,$group); + } + $delete = $insert = 0; + $group = array(); + } + } + if ($delete === $insert && count($group) > 0) { + array_push($res,$group); + } + + return $res; + } + + private function getChangeExtent($str1,$str2) + { + $start = 0; + $limit = min(strlen($str1), strlen($str2)); + while ($start < $limit and $str1[$start] === $str2[$start]) { + $start++; + } + + $end = -1; + $limit = $limit - $start; + + while (-$end <= $limit && $str1[strlen($str1) + $end] === $str2[strlen($str2) + $end]) { + $end--; + } + + return array('start' => $start,'end' => $end +1); + } +} + +/** +* TIDY diff line +* +* A diff line representation. Used by a TIDY chunk. +* +* @package Clearbricks +* @subpackage Diff +*/ +class tidyDiffLine +{ + /** @var string Line type */ + protected $type; + /** @var array Line number for old and new context */ + protected $lines; + /** @var string Line content */ + protected $content; + + /** + * Constructor + * + * Creates a line representation for a tidy chunk. Returns a new object if + * all parameters are fine, null otherwise. + * + * @param string $type Tine type + * @param array $lines Line number for old and new context + * @param string $content Line content + * @return object + */ + public function __construct($type,$lines,$content) + { + $allowed_type = array('context','delete','insert'); + + if (in_array($type,$allowed_type) && is_array($lines) && is_string($content)) { + $this->type = $type; + $this->lines = $lines; + $this->content = $content; + + return $this; + } + + return null; + } + + /** + * Magic get + * + * Returns field content according to the given name, null otherwise. + * + * @param string $n Field name + * @return string + */ + public function __get($n) + { + return isset($n,$this) ? $this->{$n} : null; + } + + /** + * Overwrite + * + * Overwrites content for the current line. + * + * @param string $content Line content + */ + public function overwrite($content) + { + if (is_string($content)) { + $this->content = $content; + } + } +} + +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/ext/incutio.ixr_library.php b/v2/dotclear/inc/libs/clearbricks/ext/incutio.ixr_library.php new file mode 100644 index 0000000..7a5ead1 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/ext/incutio.ixr_library.php @@ -0,0 +1,856 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + + +class IXR_Value +{ + public $data; + public $type; + + function IXR_Value ($data, $type = false) { + $this->data = $data; + if (!$type) { + $type = $this->calculateType(); + } + $this->type = $type; + if ($type == 'struct') { + /* Turn all the values in the array in to new IXR_Value objects */ + foreach ($this->data as $key => $value) { + $this->data[$key] = new IXR_Value($value); + } + } + if ($type == 'array') { + for ($i = 0, $j = count($this->data); $i < $j; $i++) { + $this->data[$i] = new IXR_Value($this->data[$i]); + } + } + } + function calculateType() { + if ($this->data === true || $this->data === false) { + return 'boolean'; + } + if (is_integer($this->data)) { + return 'int'; + } + if (is_double($this->data)) { + return 'double'; + } + // Deal with IXR object types base64 and date + if (is_object($this->data) && $this->data instanceof IXR_Date) { + return 'date'; + } + if (is_object($this->data) && $this->data instanceof IXR_Base64) { + return 'base64'; + } + // If it is a normal PHP object convert it in to a struct + if (is_object($this->data)) { + + $this->data = get_object_vars($this->data); + return 'struct'; + } + if (!is_array($this->data)) { + return 'string'; + } + /* We have an array - is it an array or a struct ? */ + if ($this->isStruct($this->data)) { + return 'struct'; + } else { + return 'array'; + } + } + function getXml() { + /* Return XML for this value */ + switch ($this->type) { + case 'boolean': + return ''.(($this->data) ? '1' : '0').''; + break; + case 'int': + return ''.$this->data.''; + break; + case 'double': + return ''.$this->data.''; + break; + case 'string': + return ''.htmlspecialchars($this->data).''; + break; + case 'array': + $return = ''."\n"; + foreach ($this->data as $item) { + $return .= ' '.$item->getXml()."\n"; + } + $return .= ''; + return $return; + break; + case 'struct': + $return = ''."\n"; + foreach ($this->data as $name => $value) { + $return .= " $name"; + $return .= $value->getXml()."\n"; + } + $return .= ''; + return $return; + break; + case 'date': + case 'base64': + return $this->data->getXml(); + break; + } + return false; + } + function isStruct($array) { + /* Nasty function to check if an array is a struct or not */ + $expected = 0; + foreach ($array as $key => $value) { + if ((string)$key != (string)$expected) { + return true; + } + $expected++; + } + return false; + } +} + + +class IXR_Message +{ + public $brutxml; + public $message; + public $messageType; // methodCall / methodResponse / fault + public $faultCode; + public $faultString; + public $methodName; + public $params; + // Current variable stacks + public $_arraystructs = array(); // The stack used to keep track of the current array/struct + public $_arraystructstypes = array(); // Stack keeping track of if things are structs or array + public $_currentStructName = array(); // A stack as well + public $_param; + public $_value; + public $_currentTag; + public $_currentTagContents; + // The XML parser + public $_parser; + function IXR_Message ($message) { + $this->brutxml = $this->message = $message; + } + function parse() { + // first remove the XML declaration + $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message); + if (trim($this->message) == '') { + return false; + } + $this->_parser = xml_parser_create(); + // Set XML parser to take the case of tags in to account + xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); + // Set XML parser callback functions + xml_set_object($this->_parser, $this); + xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); + xml_set_character_data_handler($this->_parser, 'cdata'); + if (!xml_parse($this->_parser, $this->message)) { + /* die(sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($this->_parser)), + xml_get_current_line_number($this->_parser))); */ + return false; + } + xml_parser_free($this->_parser); + // Grab the error messages, if any + if ($this->messageType == 'fault') { + $this->faultCode = $this->params[0]['faultCode']; + $this->faultString = $this->params[0]['faultString']; + } + return true; + } + function tag_open($parser, $tag, $attr) { + $this->currentTag = $tag; + switch($tag) { + case 'methodCall': + case 'methodResponse': + case 'fault': + $this->messageType = $tag; + break; + /* Deal with stacks of arrays and structs */ + case 'data': // data is to all intents and puposes more interesting than array + $this->_arraystructstypes[] = 'array'; + $this->_arraystructs[] = array(); + break; + case 'struct': + $this->_arraystructstypes[] = 'struct'; + $this->_arraystructs[] = array(); + break; + } + } + function cdata($parser, $cdata) { + $this->_currentTagContents .= $cdata; + } + function tag_close($parser, $tag) { + $valueFlag = false; + switch($tag) { + case 'int': + case 'i4': + $value = (int)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'double': + $value = (double)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'string': + $value = (string)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'dateTime.iso8601': + $value = new IXR_Date(trim($this->_currentTagContents)); + // $value = $iso->getTimestamp(); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'value': + // "If no type is indicated, the type is string." + if (trim($this->_currentTagContents) != '') { + $value = (string)$this->_currentTagContents; + $this->_currentTagContents = ''; + $valueFlag = true; + } + break; + case 'boolean': + $value = (boolean)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'base64': + $value = base64_decode($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + /* Deal with stacks of arrays and structs */ + case 'data': + case 'struct': + $value = array_pop($this->_arraystructs); + array_pop($this->_arraystructstypes); + $valueFlag = true; + break; + case 'member': + array_pop($this->_currentStructName); + break; + case 'name': + $this->_currentStructName[] = trim($this->_currentTagContents); + $this->_currentTagContents = ''; + break; + case 'methodName': + $this->methodName = trim($this->_currentTagContents); + $this->_currentTagContents = ''; + break; + } + if ($valueFlag) { + /* + if (!is_array($value) && !is_object($value)) { + $value = trim($value); + } + */ + if (count($this->_arraystructs) > 0) { + // Add value to struct or array + if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { + // Add to struct + $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; + } else { + // Add to array + $this->_arraystructs[count($this->_arraystructs)-1][] = $value; + } + } else { + // Just add as a paramater + $this->params[] = $value; + } + } + } +} + + +class IXR_Server +{ + public $data; + public $callbacks = array(); + public $message; + public $capabilities; + function IXR_Server($callbacks = false, $data = false) { + $this->setCapabilities(); + if ($callbacks) { + $this->callbacks = $callbacks; + } + $this->setCallbacks(); + $this->serve($data); + } + function head($code,$msg) + { + $status_mode = preg_match('/cgi/',php_sapi_name()); + + if ($status_mode) { + header('Status: '.$code.' '.$msg); + } else { + if (version_compare(phpversion(),'4.3.0','>=')) { + header($msg, TRUE, $code); + } else { + header('HTTP/1.x '.$code.' '.$msg); + } + } + } + function serve($data = false, $encoding='ISO-8859-1') { + if (!$data) { + global $HTTP_RAW_POST_DATA; + if (!$HTTP_RAW_POST_DATA) { + $this->head(405,'Method Not Allowed'); + header('Content-Type: text/plain'); + echo 'XML-RPC server accepts POST requests only.'; + exit; + } + $data = $HTTP_RAW_POST_DATA; + } + $this->message = new IXR_Message($data); + if (!$this->message->parse()) { + $this->error(-32700, 'parse error. not well formed'); + } + if ($this->message->messageType != 'methodCall') { + $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); + } + $result = $this->call($this->message->methodName, $this->message->params); + // Is the result an error? + if ($result instanceof IXR_Error) { + $this->error($result); + } + // Encode the result + $r = new IXR_Value($result); + $resultxml = $r->getXml(); + // Create the XML + $xml = << + + + + $resultxml + + + + + +EOD; + // Send it + $this->output($xml,$encoding); + } + function call($methodname, $args) { + if (!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); + } + $method = $this->callbacks[$methodname]; + // Perform the callback and send the response + if (count($args) == 1) { + // If only one paramater just send that instead of the whole array + $args = $args[0]; + } + // Are we dealing with a function or a method? + if (substr($method, 0, 5) == 'this:') { + // It's a class method - check it exists + $method = substr($method, 5); + if (!method_exists($this, $method)) { + return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); + } + // Call the method + $result = $this->$method($args); + } else { + // It's a function - does it exist? + if (!function_exists($method)) { + return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); + } + // Call the function + $result = $method($args); + } + return $result; + } + + function error($error, $message = false) { + // Accepts either an error object or an error code and message + if ($message && !is_object($error)) { + $error = new IXR_Error($error, $message); + } + $this->output($error->getXml()); + } + function output($xml,$encoding='ISO-8859-1') { + $xml = ''."\n".$xml; + $length = strlen($xml); + header('Connection: close'); + header('Content-Length: '.$length); + header('Content-Type: text/xml'); + header('Date: '.date('r')); + echo $xml; + exit; + } + function hasMethod($method) { + return in_array($method, array_keys($this->callbacks)); + } + function setCapabilities() { + // Initialises capabilities array + $this->capabilities = array( + 'xmlrpc' => array( + 'specUrl' => 'http://www.xmlrpc.com/spec', + 'specVersion' => 1 + ), + 'faults_interop' => array( + 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', + 'specVersion' => 20010516 + ), + 'system.multicall' => array( + 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', + 'specVersion' => 1 + ) + ); + } + function getCapabilities($args) { + return $this->capabilities; + } + function setCallbacks() { + $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; + $this->callbacks['system.listMethods'] = 'this:listMethods'; + $this->callbacks['system.multicall'] = 'this:multiCall'; + } + function listMethods($args) { + // Returns a list of methods - uses array_reverse to ensure user defined + // methods are listed before server defined methods + return array_reverse(array_keys($this->callbacks)); + } + function multiCall($methodcalls) { + // See http://www.xmlrpc.com/discuss/msgReader$1208 + $return = array(); + foreach ($methodcalls as $call) { + $method = $call['methodName']; + $params = $call['params']; + if ($method == 'system.multicall') { + $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); + } else { + $result = $this->call($method, $params); + } + if ($result instanceof IXR_Error) { + $return[] = array( + 'faultCode' => $result->code, + 'faultString' => $result->message + ); + } else { + $return[] = array($result); + } + } + return $return; + } +} + +class IXR_Request { + public $method; + public $args; + public $xml; + function IXR_Request($method, $args) { + $this->method = $method; + $this->args = $args; + $this->xml = << + +{$this->method} + + +EOD; + foreach ($this->args as $arg) { + $this->xml .= ''; + $v = new IXR_Value($arg); + $this->xml .= $v->getXml(); + $this->xml .= "\n"; + } + $this->xml .= ''; + } + function getLength() { + return strlen($this->xml); + } + function getXml() { + return $this->xml; + } +} + + +class IXR_Client { + public $server; + public $port; + public $path; + public $useragent; + protected $proxy_host; + protected $proxy_port; + public $timeout = 10; + public $response; + public $message = false; + public $debug = false; + // Storage place for an error message + public $error = false; + function IXR_Client($server, $path = false, $port = 80) { + if (!$path) { + // Assume we have been given a URL instead + $bits = parse_url($server); + $this->server = $bits['host']; + $this->port = isset($bits['port']) ? $bits['port'] : 80; + $this->path = isset($bits['path']) ? $bits['path'] : '/'; + // Make absolutely sure we have a path + if (!$this->path) { + $this->path = '/'; + } + } else { + $this->server = $server; + $this->path = $path; + $this->port = $port; + } + $this->useragent = 'The Incutio XML-RPC PHP Library'; + + if (defined('HTTP_PROXY_HOST') && defined('HTTP_PROXY_PORT')) { + $this->setProxy(HTTP_PROXY_HOST,HTTP_PROXY_PORT); + } + } + function setProxy($host,$port='8080') + { + $this->proxy_host = $host; + $this->proxy_port = $port; + } + function query() { + $args = func_get_args(); + $method = array_shift($args); + $request = new IXR_Request($method, $args); + $length = $request->getLength(); + $xml = $request->getXml(); + $r = "\r\n"; + $request = "POST {$this->path} HTTP/1.0$r"; + $request .= "Host: {$this->server}$r"; + $request .= "Content-Type: text/xml$r"; + $request .= "User-Agent: {$this->useragent}$r"; + $request .= "Content-length: {$length}$r$r"; + $request .= $xml; + // Now send the request + if ($this->debug) { + echo '
        '.htmlspecialchars($request)."\n
        \n\n"; + } + + if ($this->proxy_host && $this->proxy_port) { + $host = $this->proxy_host; + $port = $this->proxy_port; + } else { + $host = $this->server; + $port = $this->port; + } + + $fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout); + if (!$fp) { + $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); + return false; + } + socket_set_timeout($fp, $this->timeout); + fputs($fp, $request); + $contents = ''; + $gotFirstLine = false; + $gettingHeaders = true; + while (!feof($fp)) { + $line = fgets($fp, 4096); + if (!$gotFirstLine) { + // Check line for '200' + if (strstr($line, '200') === false) { + $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); + return false; + } + $gotFirstLine = true; + } + if (trim($line) == '') { + $gettingHeaders = false; + } + if (!$gettingHeaders) { + $contents .= trim($line)."\n"; + } + } + if ($this->debug) { + echo '
        '.htmlspecialchars($contents)."\n
        \n\n"; + } + // Now parse what we've got back + $this->message = new IXR_Message($contents); + if (!$this->message->parse()) { + // XML error + $this->error = new IXR_Error(-32700, 'parse error. not well formed'); + return false; + } + // Is the message a fault? + if ($this->message->messageType == 'fault') { + $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); + return false; + } + // Message must be OK + return true; + } + function getResponse() { + // methodResponses can only have one param - return that + return $this->message->params[0]; + } + function isError() { + return (is_object($this->error)); + } + function getErrorCode() { + return $this->error->code; + } + function getErrorMessage() { + return $this->error->message; + } +} + + +class IXR_Error { + public $code; + public $message; + function IXR_Error($code, $message) { + $this->code = $code; + $this->message = $message; + } + function getXml() { + $xml = << + + + + + faultCode + {$this->code} + + + faultString + {$this->message} + + + + + + +EOD; + return $xml; + } +} + + +class IXR_Date { + public $year; + public $month; + public $day; + public $hour; + public $minute; + public $second; + function IXR_Date($time) { + // $time can be a PHP timestamp or an ISO one + if (is_numeric($time)) { + $this->parseTimestamp($time); + } else { + $this->parseTimestamp(strtotime($time)); + } + } + function parseTimestamp($timestamp) { + $this->year = date('Y', $timestamp); + $this->month = date('m', $timestamp); + $this->day = date('d', $timestamp); + $this->hour = date('H', $timestamp); + $this->minute = date('i', $timestamp); + $this->second = date('s', $timestamp); + $this->ts = $timestamp; + } + + function getIso() { + return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second; + } + function getXml() { + return ''.$this->getIso().''; + } + function getTimestamp() { + return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); + } +} + + +class IXR_Base64 { + public $data; + function IXR_Base64($data) { + $this->data = $data; + } + function getXml() { + return ''.base64_encode($this->data).''; + } +} + + +class IXR_IntrospectionServer extends IXR_Server { + public $signatures; + public $help; + function IXR_IntrospectionServer() { + $this->setCallbacks(); + $this->setCapabilities(); + $this->capabilities['introspection'] = array( + 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', + 'specVersion' => 1 + ); + $this->addCallback( + 'system.methodSignature', + 'this:methodSignature', + array('array', 'string'), + 'Returns an array describing the return type and required parameters of a method' + ); + $this->addCallback( + 'system.getCapabilities', + 'this:getCapabilities', + array('struct'), + 'Returns a struct describing the XML-RPC specifications supported by this server' + ); + $this->addCallback( + 'system.listMethods', + 'this:listMethods', + array('array'), + 'Returns an array of available methods on this server' + ); + $this->addCallback( + 'system.methodHelp', + 'this:methodHelp', + array('string', 'string'), + 'Returns a documentation string for the specified method' + ); + } + function addCallback($method, $callback, $args, $help) { + $this->callbacks[$method] = $callback; + $this->signatures[$method] = $args; + $this->help[$method] = $help; + } + function call($methodname, $args) { + // Make sure it's in an array + if ($args && !is_array($args)) { + $args = array($args); + } + // Over-rides default call method, adds signature check + if (!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); + } + $method = $this->callbacks[$methodname]; + $signature = $this->signatures[$methodname]; + $returnType = array_shift($signature); + // Check the number of arguments + if (count($args) != count($signature)) { + // print 'Num of args: '.count($args).' Num in signature: '.count($signature); + return new IXR_Error(-32602, 'server error. wrong number of method parameters'); + } + // Check the argument types + $ok = true; + $argsbackup = $args; + for ($i = 0, $j = count($args); $i < $j; $i++) { + $arg = array_shift($args); + $type = array_shift($signature); + switch ($type) { + case 'int': + case 'i4': + if (is_array($arg) || !is_int($arg)) { + $ok = false; + } + break; + case 'base64': + case 'string': + if (!is_string($arg)) { + $ok = false; + } + break; + case 'boolean': + if ($arg !== false && $arg !== true) { + $ok = false; + } + break; + case 'float': + case 'double': + if (!is_float($arg)) { + $ok = false; + } + break; + case 'date': + case 'dateTime.iso8601': + if ($arg instanceof IXR_Date) { + $ok = false; + } + break; + } + if (!$ok) { + return new IXR_Error(-32602, 'server error. invalid method parameters'); + } + } + // It passed the test - run the "real" method call + return parent::call($methodname, $argsbackup); + } + function methodSignature($method) { + if (!$this->hasMethod($method)) { + return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.'); + } + // We should be returning an array of types + $types = $this->signatures[$method]; + $return = array(); + foreach ($types as $type) { + switch ($type) { + case 'string': + $return[] = 'string'; + break; + case 'int': + case 'i4': + $return[] = 42; + break; + case 'double': + $return[] = 3.1415; + break; + case 'dateTime.iso8601': + $return[] = new IXR_Date(time()); + break; + case 'boolean': + $return[] = true; + break; + case 'base64': + $return[] = new IXR_Base64('base64'); + break; + case 'array': + $return[] = array('array'); + break; + case 'struct': + $return[] = array('struct' => 'struct'); + break; + } + } + return $return; + } + function methodHelp($method) { + return $this->help[$method]; + } +} + + +class IXR_ClientMulticall extends IXR_Client { + public $calls = array(); + function IXR_ClientMulticall($server, $path = false, $port = 80) { + parent::IXR_Client($server, $path, $port); + $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; + } + function addCall() { + $args = func_get_args(); + $methodName = array_shift($args); + $struct = array( + 'methodName' => $methodName, + 'params' => $args + ); + $this->calls[] = $struct; + } + function query() { + // Prepare multicall, then call the parent::query() method + return parent::query('system.multicall', $this->calls); + } +} + +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/filemanager/class.filemanager.php b/v2/dotclear/inc/libs/clearbricks/filemanager/class.filemanager.php new file mode 100644 index 0000000..103f1b2 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/filemanager/class.filemanager.php @@ -0,0 +1,618 @@ +array(),'files'=>array()); + + /** + * Constructor + * + * New filemanage istance. Note that filemanage is a jail in given root + * path. You won't be able to access files outside {@link $root} path with + * the object's methods. + * + * @param string $root Root path + * @param string $root_url Root URL + */ + public function __construct($root,$root_url='') + { + $this->root = $this->pwd = path::real($root); + $this->root_url = $root_url; + + if (!preg_match('#/$#',$this->root_url)) { + $this->root_url = $this->root_url.'/'; + } + + if (!$this->root) { + throw new Exception('Invalid root directory.'); + } + } + + /** + * Change directory + * + * Changes working directory. $dir is relative to instance {@link $root} + * directory. + * + * @param string $dir Directory + */ + public function chdir($dir) + { + $realdir = path::real($this->root.'/'.path::clean($dir)); + if (!$realdir || !is_dir($realdir)) { + throw new Exception('Invalid directory.'); + } + + if ($this->isExclude($realdir)) { + throw new Exception('Directory is excluded.'); + } + + $this->pwd = $realdir; + } + + /** + * Get working directory + * + * Returns working directory path. + * + * @return string + */ + public function getPwd() + { + return $this->pwd; + } + + /** + * Current directory is writable + * + * @return boolean true if working directory is writable + */ + public function writable() + { + if (!$this->pwd) { + return false; + } + + return is_writable($this->pwd); + } + + /** + * Add exclusion + * + * Appends an exclusion to exclusions list. $f should be a regexp. + * + * @see $exclude_list + * @param string $f Exclusion regexp + */ + public function addExclusion($f) + { + if (is_array($f)) + { + foreach ($f as $v) { + if (($V = path::real($v)) !== false) { + $this->exclude_list[] = $V; + } + } + } + elseif (($F = path::real($f)) !== false) + { + $this->exclude_list[] = $F; + } + } + + /** + * Path is excluded + * + * Returns true if path (file or directory) $f is excluded. $f path is + * relative to {@link $root} path. + * + * @see $exclude_list + * @param string $f Path to match + * @return boolean + */ + protected function isExclude($f) + { + foreach ($this->exclude_list as $v) + { + if (strpos($f,$v) === 0) { + return true; + } + } + + return false; + } + + /** + * File is excluded + * + * Returns true if file $f is excluded. $f path is relative to {@link $root} + * path. + * + * @see $exclude_pattern + * @param string $f File to match + * @return boolean + */ + protected function isFileExclude($f) + { + if (!$this->exclude_pattern) { + return false; + } + + return preg_match($this->exclude_pattern,$f); + } + + /** + * Item in jail + * + * Returns true if file or directory $f is in jail (ie. not outside the + * {@link $root} directory). + * + * @param string $f Path to match + * @return boolean + */ + protected function inJail($f) + { + $f = path::real($f); + + if ($f !== false) { + return preg_match('|^'.preg_quote($this->root,'|').'|',$f); + } + + return false; + } + + /** + * File in files + * + * Returns true if file $f is in files array of {@link $dir}. + * + * @param string $f File to match + * @return boolean + */ + public function inFiles($f) + { + foreach ($this->dir['files'] as $v) { + if ($v->relname == $f) { + return true; + } + } + return false; + } + + /** + * Directory list + * + * Creates list of items in working directory and append it to {@link $dir} + * + * @uses sortHandler() + * @uses fileItem + */ + public function getDir() + { + $dir = path::clean($this->pwd); + + $dh = @opendir($dir); + + if ($dh === false) { + throw new Exception('Unable to read directory.'); + } + + $d_res = $f_res = array(); + + while (($file = readdir($dh)) !== false) + { + $fname = $dir.'/'.$file; + + if ($this->inJail($fname) && !$this->isExclude($fname)) + { + if (is_dir($fname) && $file != '.') { + $tmp = new fileItem($fname,$this->root,$this->root_url); + if ($file == '..') { + $tmp->parent = true; + } + $d_res[] = $tmp; + } + + if (is_file($fname) && strpos($file,'.') !== 0 && !$this->isFileExclude($file)) { + $f_res[] = new fileItem($fname,$this->root,$this->root_url); + } + } + } + closedir($dh); + + $this->dir = array('dirs'=>$d_res,'files'=>$f_res); + usort($this->dir['dirs'],array($this,'sortHandler')); + usort($this->dir['files'],array($this,'sortHandler')); + } + + /** + * Root directories + * + * Returns an array of directory under {@link $root} directory. + * + * @uses fileItem + * @return array + */ + public function getRootDirs() + { + $d = files::getDirList($this->root); + + $dir = array(); + + foreach ($d['dirs'] as $v) { + $dir[] = new fileItem($v,$this->root,$this->root_url); + } + + return $dir; + } + + /** + * Upload file + * + * Move $tmp file to its final destination $dest and + * returns the destination file path. + * $dest should be in jail. This method will throw exception + * if the file cannot be written. + * + * You should first verify upload status, with {@link files::uploadStatus()} + * or PHP native functions. + * + * @see files::uploadStatus() + * @param string $tmp Temporary uploaded file path + * @param string $dest Destination file + * @param string $overwrite overwrite mode + * @return string Destination real path + */ + public function uploadFile($tmp,$dest,$overwrite=false) + { + $dest = $this->pwd.'/'.path::clean($dest); + + if ($this->isFileExclude($dest)) { + throw new Exception(__('Uploading this file is not allowed.')); + } + + if (!$this->inJail(dirname($dest))) { + throw new Exception(__('Destination directory is not in jail.')); + } + + if (!$overwrite && file_exists($dest)) { + throw new Exception(__('File already exists.')); + } + + if (!is_writable(dirname($dest))) { + throw new Exception(__('Cannot write in this directory.')); + } + + if (@move_uploaded_file($tmp,$dest) === false) { + throw new Exception(__('An error occurred while writing the file.')); + } + + files::inheritChmod($dest); + return path::real($dest); + } + + /** + * Upload file by bits + * + * Creates a new file $dest with contents of $bits and + * return the destination file path. + * $dest should be in jail. This method will throw exception + * if file cannot be written. + * + * @param string $bits Destination file content + * @param string $dest Destination file + * @return string Destination real path + */ + public function uploadBits($name,$bits) + { + $dest = $this->pwd.'/'.path::clean($name); + + if ($this->isFileExclude($dest)) { + throw new Exception(__('Uploading this file is not allowed.')); + } + + if (!$this->inJail(dirname($dest))) { + throw new Exception(__('Destination directory is not in jail.')); + } + + if (!is_writable(dirname($dest))) { + throw new Exception(__('Cannot write in this directory.')); + } + + $fp = @fopen($dest,'wb'); + if ($fp === false) { + throw new Exception(__('An error occurred while writing the file.')); + } + + fwrite($fp,$bits); + fclose($fp); + files::inheritChmod($dest); + + return path::real($dest); + } + + /** + * New directory + * + * Creates a new directory $d relative to working directory. + * + * @param string $d Directory name + */ + public function makeDir($d) + { + files::makeDir($this->pwd.'/'.path::clean($d)); + } + + /** + * Move file + * + * Moves a file $s to a new destination $d. Both + * $s and $d are relative to {@link $root}. + * + * @param string $s Source file + * @param string $d Destination file + */ + public function moveFile($s,$d) + { + $s = $this->root.'/'.path::clean($s); + $d = $this->root.'/'.path::clean($d); + + if (($s = path::real($s)) === false) { + throw new Exception(__('Source file does not exist.')); + } + + $dest_dir = path::real(dirname($d)); + + if (!$this->inJail($s)) { + throw new Exception(__('File is not in jail.')); + } + if (!$this->inJail($dest_dir)) { + throw new Exception(__('File is not in jail.')); + } + + if (!is_writable($dest_dir)) { + throw new Exception(__('Destination directory is not writable.')); + } + + if (@rename($s,$d) === false) { + throw new Exception(__('Unable to rename file.')); + } + } + + /** + * Remove item + * + * Removes a file or directory $f which is relative to working + * directory. + * + * @param string $f Path to remove + */ + public function removeItem($f) + { + $file = path::real($this->pwd.'/'.path::clean($f)); + + if (is_file($file)) { + $this->removeFile($f); + } elseif (is_dir($file)) { + $this->removeDir($f); + } + } + + /** + * Remove item + * + * Removes a file $f which is relative to working directory. + * + * @param string $f File to remove + */ + public function removeFile($f) + { + $f = path::real($this->pwd.'/'.path::clean($f)); + + if (!$this->inJail($f)) { + throw new Exception(__('File is not in jail.')); + } + + if (!files::isDeletable($f)) { + throw new Exception(__('File cannot be removed.')); + } + + if (@unlink($f) === false) { + throw new Exception(__('File cannot be removed.')); + } + } + + /** + * Remove item + * + * Removes a directory $d which is relative to working directory. + * + * @param string $d Directory to remove + */ + public function removeDir($d) + { + $d = path::real($this->pwd.'/'.path::clean($d)); + + if (!$this->inJail($d)) { + throw new Exception(__('Directory is not in jail.')); + } + + if (!files::isDeletable($d)) { + throw new Exception(__('Directory cannot be removed.')); + } + + if (@rmdir($d) === false) { + throw new Exception(__('Directory cannot be removed.')); + } + } + + /** + * SortHandler + * + * This method is called by {@link getDir()} to sort files. Can be overrided + * in inherited classes. + * + * @param fileItem $a fileItem object + * @param fileItem $b fileItem object + * @return integer + */ + protected function sortHandler($a,$b) + { + if ($a->parent && !$b->parent || !$a->parent && $b->parent) { + return ($a->parent) ? -1 : 1; + } + return strcasecmp($a->basename,$b->basename); + } +} + +/** +* File item +* +* File item class used by {@link filemanager}. In this class {@link $file} could +* be either a file or a directory. +* +* @package Clearbricks +* @subpackage Filemanager +*/ +class fileItem +{ + /** @var string Complete path to file */ + public $file; + + /** @var string File basename */ + public $basename; + + /** @var string File directory name */ + public $dir; + + /** @var string File URL */ + public $file_url; + + /** @var string File directory URL */ + public $dir_url; + + /** @var string File extension */ + public $extension; + + /** @var string File path relative to $root given in constructor */ + public $relname; + + /** @var boolean Parent directory (ie. "..") */ + public $parent = false; + + + /** @var string File MimeType. See {@link files::getMimeType()}. */ + public $type; + + /** @var integer File modification timestamp */ + public $mtime; + + /** @var integer File size */ + public $size; + + /** @var integer File permissions mode */ + public $mode; + + /** @var integer File owner ID */ + public $uid; + + /** @var integer File group ID */ + public $gid; + + /** @var boolean True if file or directory is writable */ + public $w; + + /** @var boolean True if file is a directory */ + public $d; + + /** @var boolean True if file file is executable or directory is traversable */ + public $x; + + /** @var boolean True if file is a file */ + public $f; + + /** @var boolean True if file or directory is deletable */ + public $del; + + /** + * Constructor + * + * Creates an instance of fileItem object. + * + * @param string $file Absolute file or directory path + * @param string $root File root path + * @param string $root_url File root URL + */ + public function __construct($file,$root,$root_url='') + { + $file = path::real($file); + $stat = stat($file); + $path = path::info($file); + + $rel = preg_replace('/^'.preg_quote($root,'/').'\/?/','',$file); + + $this->file = $file; + $this->basename = $path['basename']; + $this->dir = $path['dirname']; + $this->relname = $rel; + + $this->file_url = str_replace('%2F','/',rawurlencode($rel)); + $this->file_url = $root_url.$this->file_url; + + $this->dir_url = dirname($this->file_url); + $this->extension = $path['extension']; + + $this->mtime = $stat[9]; + $this->size = $stat[7]; + $this->mode = $stat[2]; + $this->uid = $stat[4]; + $this->gid = $stat[5]; + $this->w = is_writable($file); + $this->d = is_dir($file); + $this->f = is_file($file); + if ($this->d) { + $this->x = file_exists($file.'/.'); + } else { + $this->x = false; + } + $this->del = files::isDeletable($file); + + $this->type = $this->d ? null : files::getMimeType($file); + $this->type_prefix = preg_replace('/^(.+?)\/.+$/','$1',$this->type); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/html.filter/class.html.filter.php b/v2/dotclear/inc/libs/clearbricks/html.filter/class.html.filter.php new file mode 100644 index 0000000..cbe6c86 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/html.filter/class.html.filter.php @@ -0,0 +1,676 @@ +parser = xml_parser_create('UTF-8'); + xml_set_object($this->parser,$this); + xml_set_element_handler($this->parser, 'tag_open', 'tag_close'); + xml_set_character_data_handler($this->parser, 'cdata'); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); + + $this->removeTags( + 'applet','base','basefont','body','center','dir','font', + 'frame','frameset','head','html','isindex', + 'link','menu','meta','noframes','script','style' + ); + + $this->removeAttributes( + 'onclick','ondblclick','onfocus','onkeydown','onkeypress', + 'onkeyup','onload','onmousedown','onmousemove','onmouseout', + 'onmouseover','onmouseup','onreset','onselect','onsubmit', + 'onunload' + ); + } + + /** + * Append tags + * + * Appends tags to remove. Each method argument is a tag. Example: + * + * + * removeTags('frame','script'); + * ?> + * + */ + public function removeTags() + { + foreach ($this->argsArray(func_get_args()) as $tag) { + $this->removed_tags[] = $tag; + } + } + + /** + * Append attributes + * + * Appends attributes to remove. Each method argument is an attribute. Example: + * + * + * removeAttributes('onclick','onunload'); + * ?> + * + */ + public function removeAttributes() + { + foreach ($this->argsArray(func_get_args()) as $a) { + $this->removed_attrs[] = $a; + } + } + + /** + * Append attributes for tags + * + * Appends attributes to remove from specific tags. Each method argument is + * an array of tags with attributes. Example: + * + * + * removeTagAttributes(array('a' => array('src','title'))); + * ?> + * + */ + public function removeTagAttributes($tag) + { + $args = $this->argsArray(func_get_args()); + array_shift($args); + + foreach ($args as $a) { + $this->removed_tag_attrs[$tag][] = $a; + } + } + + /** + * Known tags + * + * Creates a list of known tags. + * + * @param array $t Tags array + */ + public function setTags($t) + { + if (is_array($t)) { + $this->tags = $t; + } + } + + /** + * Apply filter + * + * This method applies filter on given $str string. It will first + * try to use tidy extension if exists and then apply the filter. + * + * @param string $str String to filter + * @return string Filtered string + */ + public function apply($str) + { + if (extension_loaded('tidy') && class_exists('tidy')) + { + $config = array( + 'doctype' => 'strict', + 'drop-proprietary-attributes' => true, + 'drop-font-tags' => true, + 'escape-cdata' => true, + 'indent' => false, + 'join-classes' => false, + 'join-styles' => true, + 'lower-literals' => true, + 'output-xhtml' => true, + 'show-body-only' => true, + 'wrap' => 80 + ); + + $str = '

        tt

        '.$str; // Fixes a big issue + + $tidy = new tidy; + $tidy->parseString($str, $config, 'utf8'); + $tidy->cleanRepair(); + + $str = (string) $tidy; + + $str = preg_replace('#^

        tt

        \s?#','',$str); + } + else + { + $str = $this->miniTidy($str); + } + + # Removing open comments, open CDATA and processing instructions + $str = preg_replace('%%msu','',$str); + $str = str_replace(' + $fc = preg_replace('/(^\s*)?/ms','',$fc); + + # Compile blocks + foreach ($this->blocks as $b => $f) { + $pattern = sprintf($this->tag_block,preg_quote($b,'#')); + + $fc = preg_replace_callback('#'.$pattern.'#ms', + array($this,'compileBlock'),$fc); + } + + # Compile values + foreach ($this->values as $v => $f) { + $pattern = sprintf($this->tag_value,preg_quote($v,'#')); + + $fc = preg_replace_callback('#'.$pattern.'#ms', + array($this,'compileValue'),$fc); + } + + return $fc; + } + + protected function compileBlock($match) + { + $b = $match[1]; + $content = $match[3]; + $attr = $this->getAttrs($match[2]); + + # Call block function + return call_user_func($this->blocks[$b],$attr,$content); + } + + protected function compileValue($match) + { + $v = $match[1]; + $attr = isset($match[2]) ? $this->getAttrs($match[2]) : array(); + $str_attr = isset($match[2]) ? $match[2] : null; + + return call_user_func($this->values[$v],$attr,ltrim($str_attr)); + } + + protected function getAttrs($str) + { + $res = array(); + if (preg_match_all('|([a-zA-Z0-9_:-]+)="([^"]*)"|ms',$str,$m) > 0) { + foreach ($m[1] as $i => $v) { + $res[$v] = $m[2][$i]; + } + } + return $res; + } +} +?> diff --git a/v2/dotclear/inc/libs/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php b/v2/dotclear/inc/libs/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php new file mode 100644 index 0000000..5af2363 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/text.wiki2xhtml/class.wiki2xhtml.php @@ -0,0 +1,1305 @@ + Added ``inline html`` support +# +# 3.2.5 +# => Changed longdesc by title in images +# +# 3.2.4 +# => Auto links +# => Code cleanup +# +# 3.2.3 +# Olivier +# => PHP5 Strict +# +# 3.2.2 +# Olivier +# => Changement de la gestion des URL spéciales +# +# 3.2.1 +# Olivier +# => Changement syntaxe des macros +# +# 3.2 +# Olivier +# => Changement de fonctionnement des macros +# => Passage de fonctions externes pour les macros et les mots wiki +# +# 3.1d +# Jérôme Lipowicz +# => antispam +# Olivier +# => centrage d'image +# +# 3.1c +# Olivier +# => Possibilité d'échaper les | dans les marqueurs avec \ +# +# 3.1b +# Nicolas Chachereau +# => Changement de regexp pour la correction syntaxique +# +# 3.1a +# Olivier +# => Bug du Call-time pass-by-reference +# +# 3.1 +# Olivier +# => Ajout des macros «««..»»» +# => Ajout des blocs vides øøø +# => Ajout du niveau de titre paramétrable +# => Option de blocage du parseur dans les
        +#			=> Titres au format setext (experimental, désactivé)
        +#
        +# 3.0
        +# Olivier		=> Récriture du parseur inline, plus d'erreur XHTML
        +#			=> Ajout d'une vérification d'intégrité pour les listes
        +#			=> Les acronymes sont maintenant dans un fichier texte
        +#			=> Ajout d'un tag images ((..)), del --..-- et ins ++..++
        +#			=> Plus possible de faire des liens JS [lien|javascript:...]
        +#			=> Ajout des notes de bas de page §§...§§
        +#			=> Ajout des mots wiki
        +#
        +# 2.5
        +# Olivier		=> Récriture du code, plus besoin du saut de ligne entre blocs !=
        +#
        +# 2.0
        +# Stephanie	=> correction des PCRE et ajout de fonctionnalités
        +# Mathieu 	=> ajout du strip-tags, implementation des options, reconnaissance automatique d'url, etc.
        +# Olivier		=> chagement de active_link en active_urls
        +#			=> ajout des options pour les blocs
        +#			=> intégration de l'aide dans le code, avec les options
        +#			=> début de quelque chose pour la reconnaissance auto d'url (avec Mat)
        +
        +# TODO :
        +# Mathieu	=> active_wiki_urls (modifier wikiParseUrl ?)
        +# 		=> active_auto_urls
        +#
        +# *		=> ajouter des options.
        +# 		=> trouver un meilleur nom pour active_link ? (pour le jour ou ca sera tellement une usine
        +#		   a gaz que on generera des tags  :)
        +#
        +
        +# Wiki2xhtml
        +
        +class wiki2xhtml
        +{
        +	public $__version__ = '3.2.6';
        +	
        +	public $T;
        +	public $opt;
        +	public $line;
        +	public $acro_table;
        +	public $foot_notes;
        +	public $macros;
        +	public $functions;
        +	
        +	public $tags;
        +	public $open_tags;
        +	public $close_tags;
        +	public $custom_tags = array();
        +	public $all_tags;
        +	public $tag_pattern;
        +	public $escape_table;
        +	public $allowed_inline = array();
        +	
        +	function __construct()
        +	{
        +		# Mise en place des options
        +		$this->setOpt('active_title',1);		# Activation des titres !!!
        +		$this->setOpt('active_setext_title',0);	# Activation des titres setext (EXPERIMENTAL)
        +		$this->setOpt('active_hr',1);			# Activation des 
        + $this->setOpt('active_lists',1); # Activation des listes + $this->setOpt('active_quote',1); # Activation du
        + $this->setOpt('active_pre',1); # Activation du
        +		$this->setOpt('active_empty',1);		# Activation du bloc vide øøø
        +		$this->setOpt('active_auto_urls',0);	# Activation de la reconnaissance d'url
        +		$this->setOpt('active_auto_br',0);	# Activation du saut de ligne automatique (dans les paragraphes)
        +		$this->setOpt('active_antispam',1);	# Activation de l'antispam pour les emails
        +		$this->setOpt('active_urls',1);		# Activation des liens []
        +		$this->setOpt('active_auto_img',1);	# Activation des images automatiques dans les liens []
        +		$this->setOpt('active_img',1);		# Activation des images (())
        +		$this->setOpt('active_anchor',1);		# Activation des ancres ~...~
        +		$this->setOpt('active_em',1);			# Activation du  ''...''
        +		$this->setOpt('active_strong',1);		# Activation du  __...__
        +		$this->setOpt('active_br',1);			# Activation du 
        %%% + $this->setOpt('active_q',1); # Activation du {{...}} + $this->setOpt('active_code',1); # Activation du @@...@@ + $this->setOpt('active_acronym',1); # Activation des acronymes + $this->setOpt('active_ins',1); # Activation des ins ++..++ + $this->setOpt('active_del',1); # Activation des del --..-- + $this->setOpt('active_inline_html',1); # Activation du HTML inline ``...`` + $this->setOpt('active_footnotes',1); # Activation des notes de bas de page + $this->setOpt('active_wikiwords',0); # Activation des mots wiki + $this->setOpt('active_macros',1); # Activation des macros /// /// + + $this->setOpt('parse_pre',1); # Parser l'intérieur de blocs
         ?
        +		
        +		$this->setOpt('active_fr_syntax',1);	# Corrections syntaxe FR
        +		
        +		$this->setOpt('first_title_level',3);	# Premier niveau de titre 
        +		
        +		$this->setOpt('note_prefix','wiki-footnote');
        +		$this->setOpt('note_str','

        Notes

        %s
        '); + $this->setOpt('note_str_single','

        Note

        %s
        '); + $this->setOpt('words_pattern', + '((?setOpt('auto_url_pattern', + '%(?setOpt('acronyms_file',dirname(__FILE__).'/acronyms.txt'); + + $this->acro_table = $this->__getAcronyms(); + $this->foot_notes = array(); + $this->functions = array(); + $this->macros = array(); + + $this->registerFunction('macro:html',array($this,'__macroHTML')); + } + + function setOpt($option, $value) + { + $this->opt[$option] = $value; + } + + function setOpts($options) + { + if (!is_array($options)) { + return; + } + + foreach ($options as $k => $v) { + $this->opt[$k] = $v; + } + } + + function getOpt($option) + { + return (!empty($this->opt[$option])) ? $this->opt[$option] : false; + } + + function registerFunction($type,$name) + { + if (is_callable($name)) { + $this->functions[$type] = $name; + } + } + + function transform($in) + { + # Initialisation des tags + $this->__initTags(); + $this->foot_notes = array(); + + # Récupération des macros + if ($this->getOpt('active_macros')) { + $in = preg_replace('#^///(.*?)///($|\r)#mse',"\\\$this->__getMacro('\\1')",$in); + } + + # Vérification du niveau de titre + if ($this->getOpt('first_title_level') > 4) { + $this->setOpt('first_title_level',4); + } + + $res = str_replace("\r", '', $in); + + $escape_pattern = array(); + + # traitement des titres à la setext + if ($this->getOpt('active_setext_title') && $this->getOpt('active_title')) { + $res = preg_replace('/^(.*)\n[=]{5,}$/m','!!!$1',$res); + $res = preg_replace('/^(.*)\n[-]{5,}$/m','!!$1',$res); + } + + # Transformation des mots Wiki + if ($this->getOpt('active_wikiwords') && $this->getOpt('words_pattern')) { + $res = preg_replace('/'.$this->getOpt('words_pattern').'/msu','¶¶¶$1¶¶¶',$res); + } + + # Transformation des URLs automatiques + if ($this->getOpt('active_auto_urls')) + { + $active_urls = $this->getOpt('active_urls'); + + $this->setOpt('active_urls',1); + $this->__initTags(); + + # If urls are not active, escape URLs tags + if (!$active_urls) + { + $res = preg_replace( + '%(?tags['a'])).'])%msU', + '\\\$1',$res + ); + } + + # Transforms urls while preserving tags. + $tree = preg_split($this->tag_pattern,$res,-1,PREG_SPLIT_DELIM_CAPTURE); + foreach ($tree as &$leaf) { + $leaf = preg_replace($this->getOpt('auto_url_pattern'),'[$1$2]',$leaf); + } + unset($leaf); + $res = implode($tree); + + + } + + $this->T = explode("\n",$res); + $this->T[] = ''; + + # Parse les blocs + $res = $this->__parseBlocks(); + + # Line break + if ($this->getOpt('active_br')) { + $res = preg_replace('/(?', $res); + $escape_pattern[] = '%%%'; + } + + # Nettoyage des \s en trop + $res = preg_replace('/([\s]+)(<\/p>|<\/li>|<\/pre>)/u', '$2', $res); + $res = preg_replace('/(
      • )([\s]+)/u', '$1', $res); + + # On vire les escapes + if (!empty($escape_pattern)) { + $res = preg_replace('/\\\('.implode('|',$escape_pattern).')/','$1',$res); + } + + # On vire les ¶¶¶MotWiki¶¶¶ qui sont resté (dans les url...) + if ($this->getOpt('active_wikiwords') && $this->getOpt('words_pattern')) { + $res = preg_replace('/¶¶¶'.$this->getOpt('words_pattern').'¶¶¶/msu','$1',$res); + } + + # On remet les macros + if ($this->getOpt('active_macros')) { + $res = preg_replace('/^##########MACRO#([0-9]+)#$/mse','\$this->__putMacro("$1")',$res); + } + + # Auto line break dans les paragraphes + if ($this->getOpt('active_auto_br')) { + $res = preg_replace_callback('%(

        )(.*?)(

        )%msu',array($this,'__autoBR'),$res); + } + + # On ajoute les notes + if (count($this->foot_notes) > 0) + { + $res_notes = ''; + $i = 1; + foreach ($this->foot_notes as $k => $v) { + $res_notes .= "\n".'

        ['.$i.'] '.$v.'

        '; + $i++; + } + $res .= sprintf("\n".(count($this->foot_notes) > 1 ? $this->getOpt('note_str') : $this->getOpt('note_str_single'))."\n",$res_notes); + } + + return $res; + } + + /* PRIVATE + --------------------------------------------------- */ + + function __initTags() + { + $tags = array( + 'em' => array("''","''"), + 'strong' => array('__','__'), + 'acronym' => array('??','??'), + 'a' => array('[',']'), + 'img' => array('((','))'), + 'q' => array('{{','}}'), + 'code' => array('@@','@@'), + 'anchor' => array('~','~'), + 'del' => array('--','--'), + 'ins' => array('++','++'), + 'inline' => array('``','``'), + 'note' => array('$$','$$'), + 'word' => array('¶¶¶','¶¶¶') + ); + $this->linetags = array( + 'empty' => 'øøø', + 'title' => '([!]{1,4})', + 'hr' => '[-]{4}[- ]', + 'quote' => '(>|;:)', + 'lists' => '([*#]+)', + 'pre' => '[ ]{1}' + ); + + $this->tags = array_merge($tags,$this->custom_tags); + + # Suppression des tags selon les options + if (!$this->getOpt('active_urls')) { + unset($this->tags['a']); + } + if (!$this->getOpt('active_img')) { + unset($this->tags['img']); + } + if (!$this->getOpt('active_anchor')) { + unset($this->tags['anchor']); + } + if (!$this->getOpt('active_em')) { + unset($this->tags['em']); + } + if (!$this->getOpt('active_strong')) { + unset($this->tags['strong']); + } + if (!$this->getOpt('active_q')) { + unset($this->tags['q']); + } + if (!$this->getOpt('active_code')) { + unset($this->tags['code']); + } + if (!$this->getOpt('active_acronym')) { + unset($this->tags['acronym']); + } + if (!$this->getOpt('active_ins')) { + unset($this->tags['ins']); + } + if (!$this->getOpt('active_del')) { + unset($this->tags['del']); + } + if (!$this->getOpt('active_inline_html')) { + unset($this->tags['inline']); + } + if (!$this->getOpt('active_footnotes')) { + unset($this->tags['note']); + } + if (!$this->getOpt('active_wikiwords')) { + unset($this->tags['word']); + } + + # Suppression des tags de début de ligne selon les options + if (!$this->getOpt('active_empty')) { + unset($this->linetags['empty']); + } + if (!$this->getOpt('active_title')) { + unset($this->linetags['title']); + } + if (!$this->getOpt('active_hr')) { + unset($this->linetags['hr']); + } + if (!$this->getOpt('active_quote')) { + unset($this->linetags['quote']); + } + if (!$this->getOpt('active_lists')) { + unset($this->linetags['lists']); + } + if (!$this->getOpt('active_pre')) { + unset($this->linetags['pre']); + } + + $this->open_tags = $this->__getTags(); + $this->close_tags = $this->__getTags(false); + $this->all_tags = $this->__getAllTags(); + $this->tag_pattern = $this->__getTagsPattern(); + + $this->escape_table = $this->all_tags; + array_walk($this->escape_table,create_function('&$a','$a = \'\\\\\'.$a;')); + } + + function __getTags($open=true) + { + $res = array(); + foreach ($this->tags as $k => $v) { + $res[$k] = ($open) ? $v[0] : $v[1]; + } + return $res; + } + + function __getAllTags() + { + $res = array(); + foreach ($this->tags as $v) { + $res[] = $v[0]; + $res[] = $v[1]; + } + return array_values(array_unique($res)); + } + + function __getTagsPattern($escape=false) + { + $res = $this->all_tags; + array_walk($res,create_function('&$a','$a = preg_quote($a,"/");')); + + if (!$escape) { + return '/(?T); + + for ($i=0; $i<$max; $i++) + { + $pre_mode = $mode; + $pre_type = $type; + $end = ($i+1 == $max); + + $line = $this->__getLine($i,$type,$mode); + + if ($type != 'pre' || $this->getOpt('parse_pre')) { + $line = $this->__inlineWalk($line); + } + + $res .= $this->__closeLine($type,$mode,$pre_type,$pre_mode); + $res .= $this->__openLine($type,$mode,$pre_type,$pre_mode); + + # P dans les blockquotes + if ($type == 'blockquote' && trim($line) == '' && $pre_type == $type) { + $res .= "

        \n

        "; + } + + # Correction de la syntaxe FR dans tous sauf pre et hr + # Sur idée de Christophe Bonijol + # Changement de regex (Nicolas Chachereau) + if ($this->getOpt('active_fr_syntax') && $type != null && $type != 'pre' && $type != 'hr') { + $line = preg_replace('%[ ]+([:?!;\x{00BB}](\s|$))%u',' $1',$line); + $line = preg_replace('%(\x{00AB})[ ]+%u','$1 ',$line); + } + + $res .= $line; + } + + return trim($res); + } + + function __getLine($i,&$type,&$mode) + { + $pre_type = $type; + $pre_mode = $mode; + $type = $mode = null; + + if (empty($this->T[$i])) { + return false; + } + + $line = htmlspecialchars($this->T[$i],ENT_NOQUOTES); + + # Ligne vide + if (empty($line)) + { + $type = null; + } + elseif ($this->getOpt('active_empty') && preg_match('/^øøø(.*)$/',$line,$cap)) + { + $type = null; + $line = trim($cap[1]); + } + # Titre + elseif ($this->getOpt('active_title') && preg_match('/^([!]{1,4})(.*)$/',$line,$cap)) + { + $type = 'title'; + $mode = strlen($cap[1]); + $line = trim($cap[2]); + } + # Ligne HR + elseif ($this->getOpt('active_hr') && preg_match('/^[-]{4}[- ]*$/',$line)) + { + $type = 'hr'; + $line = null; + } + # Blockquote + elseif ($this->getOpt('active_quote') && preg_match('/^(>|;:)(.*)$/',$line,$cap)) + { + $type = 'blockquote'; + $line = trim($cap[2]); + } + # Liste + elseif ($this->getOpt('active_lists') && preg_match('/^([*#]+)(.*)$/',$line,$cap)) + { + $type = 'list'; + $mode = $cap[1]; + $valid = true; + + # Vérification d'intégrité + $dl = ($type != $pre_type) ? 0 : strlen($pre_mode); + $d = strlen($mode); + $delta = $d-$dl; + + if ($delta < 0 && strpos($pre_mode,$mode) !== 0) { + $valid = false; + } + if ($delta > 0 && $type == $pre_type && strpos($mode,$pre_mode) !== 0) { + $valid = false; + } + if ($delta == 0 && $mode != $pre_mode) { + $valid = false; + } + if ($delta > 1) { + $valid = false; + } + + if (!$valid) { + $type = 'p'; + $mode = null; + $line = '
        '.$line; + } else { + $line = trim($cap[2]); + } + } + # Préformaté + elseif ($this->getOpt('active_pre') && preg_match('/^[ ]{1}(.*)$/',$line,$cap)) + { + $type = 'pre'; + $line = $cap[1]; + } + # Paragraphe + else { + $type = 'p'; + if (preg_match('/^\\\((?:('.implode('|',$this->linetags).')).*)$/',$line,$cap)) { + $line = $cap[1]; + } + $line = trim($line); + } + + return $line; + } + + function __openLine($type,$mode,$pre_type,$pre_mode) + { + $open = ($type != $pre_type); + + if ($open && $type == 'p') + { + return "\n

        "; + } + elseif ($open && $type == 'blockquote') + { + return "\n

        "; + } + elseif (($open || $mode != $pre_mode) && $type == 'title') + { + $fl = $this->getOpt('first_title_level'); + $fl = $fl+3; + $l = $fl-$mode; + return "\n'; + } + elseif ($open && $type == 'pre') + { + return "\n

        ";
        +		}
        +		elseif ($open && $type == 'hr')
        +		{
        +			return "\n
        "; + } + elseif ($type == 'list') + { + $dl = ($open) ? 0 : strlen($pre_mode); + $d = strlen($mode); + $delta = $d-$dl; + $res = ''; + + if($delta > 0) { + if(substr($mode, -1, 1) == '*') { + $res .= "
          \n"; + } else { + $res .= "
            \n"; + } + } elseif ($delta < 0) { + $res .= "\n"; + for($j = 0; $j < abs($delta); $j++) { + if (substr($pre_mode,(0 - $j - 1), 1) == '*') { + $res .= "
        \n
      • \n"; + } else { + $res .= "\n\n"; + } + } + } else { + $res .= "\n"; + } + + return $res."
      • "; + } + else + { + return null; + } + } + + function __closeLine($type,$mode,$pre_type,$pre_mode) + { + $close = ($type != $pre_type); + + if ($close && $pre_type == 'p') + { + return "

        \n"; + } + elseif ($close && $pre_type == 'blockquote') + { + return "

      • \n"; + } + elseif (($close || $mode != $pre_mode) && $pre_type == 'title') + { + $fl = $this->getOpt('first_title_level'); + $fl = $fl+3; + $l = $fl-$pre_mode; + return '\n"; + } + elseif ($close && $pre_type == 'pre') + { + return "
        \n"; + } + elseif ($close && $pre_type == 'list') + { + $res = ''; + for($j = 0; $j < strlen($pre_mode); $j++) { + if(substr($pre_mode,(0 - $j - 1), 1) == '*') { + $res .= "\n"; + } else { + $res .= "\n"; + } + } + return $res; + } + else + { + return "\n"; + } + } + + + /* Inline + --------------------------------------------------- */ + function __inlineWalk($str,$allow_only=null) + { + $tree = preg_split($this->tag_pattern,$str,-1,PREG_SPLIT_DELIM_CAPTURE); + + $res = ''; + for ($i=0; $iopen_tags)) && + ($allow_only == null || in_array(array_search($tree[$i],$this->open_tags),$allow_only))) + { + $tag = array_search($tree[$i],$this->open_tags); + $tag_type = 'open'; + + if (($tidy = $this->__makeTag($tree,$tag,$i,$i,$attr,$tag_type)) !== false) + { + if ($tag != '') { + $res .= '<'.$tag.$attr; + $res .= ($tag_type == 'open') ? '>' : ' />'; + } + $res .= $tidy; + } + else + { + $res .= $tree[$i]; + } + } + else + { + $res .= $tree[$i]; + } + } + + # Suppression des echappements + $res = str_replace($this->escape_table,$this->all_tags,$res); + + return $res; + } + + function __makeTag(&$tree,&$tag,$position,&$j,&$attr,&$type) + { + $res = ''; + $closed = false; + + $itag = $this->close_tags[$tag]; + + # Recherche fermeture + for ($i=$position+1;$i__parseLink($res,$tag,$attr,$type); + break; + case 'img': + $type = 'close'; + $res = $this->__parseImg($res,$attr); + break; + case 'acronym': + $res = $this->__parseAcronym($res,$attr); + break; + case 'q': + $res = $this->__parseQ($res,$attr); + break; + case 'anchor': + $tag = 'a'; + $res = $this->__parseAnchor($res,$attr); + break; + case 'note': + $tag = ''; + $res = $this->__parseNote($res); + break; + case 'inline': + $tag = ''; + $res = $this->__parseInlineHTML($res); + break; + case 'word': + $res = $this->parseWikiWord($res,$tag,$attr,$type); + break; + default : + $res = $this->__inlineWalk($res); + break; + } + + if ($type == 'open' && $tag != '') { + $res .= ''; + } + $j = $i; + break; + } + } + + return $res; + } + else + { + return false; + } + } + + function __splitTagsAttr($str) + { + $res = preg_split('/(? $v) { + $res[$k] = str_replace("\|",'|',$v); + } + + return $res; + } + + # Antispam (Jérôme Lipowicz) + function __antiSpam($str) + { + $encoded = bin2hex($str); + $encoded = chunk_split($encoded, 2, '%'); + $encoded = '%'.substr($encoded, 0, strlen($encoded) - 1); + return $encoded; + } + + function __parseLink($str,&$tag,&$attr,&$type) + { + $n_str = $this->__inlineWalk($str,array('acronym','img')); + $data = $this->__splitTagsAttr($n_str); + $no_image = false; + + # Only URL in data + if (count($data) == 1) + { + $url = trim($str); + $content = strlen($url) > 35 ? substr($url,0,35).'...' : $url; + $lang = ''; + $title = $url; + } + elseif (count($data) > 1) + { + $url = trim($data[1]); + $content = $data[0]; + $lang = (!empty($data[2])) ? $this->protectAttr($data[2],true) : ''; + $title = (!empty($data[3])) ? $data[3] : ''; + $no_image = (!empty($data[4])) ? (boolean) $data[4] : false; + } + + # Remplacement si URL spéciale + $this->__specialUrls($url,$content,$lang,$title); + + # On vire les   dans l'url + $url = str_replace(' ',' ',$url); + + if (preg_match('/^(.+)[.](gif|jpg|jpeg|png)$/', $url) && !$no_image && $this->getOpt('active_auto_img')) + { + # On ajoute les dimensions de l'image si locale + # Idée de Stephanie + $img_size = null; + if (!preg_match('#[a-zA-Z]+://#', $url)) { + if (preg_match('#^/#',$url)) { + $path_img = $_SERVER['DOCUMENT_ROOT'] . $url; + } else { + $path_img = $url; + } + + $img_size = @getimagesize($path_img); + } + + $attr = ' src="'.$this->protectAttr($this->protectUrls($url)).'"'. + $attr .= (count($data) > 1) ? ' alt="'.$this->protectAttr($content).'"' : ' alt=""'; + $attr .= ($lang) ? ' lang="'.$lang.'"' : ''; + $attr .= ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; + $attr .= (is_array($img_size)) ? ' '.$img_size[3] : ''; + + $tag = 'img'; + $type = 'close'; + return null; + } + else + { + if ($this->getOpt('active_antispam') && preg_match('/^mailto:/',$url)) { + $content = $content == $url ? preg_replace('%^mailto:%','',$content) : $content; + $url = 'mailto:'.$this->__antiSpam(substr($url,7)); + + } + + $attr = ' href="'.$this->protectAttr($this->protectUrls($url)).'"'; + $attr .= ($lang) ? ' hreflang="'.$lang.'"' : ''; + $attr .= ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; + + return $content; + } + } + + function __specialUrls(&$url,&$content,&$lang,&$title) + { + foreach ($this->functions as $k => $v) + { + if (strpos($k,'url:') === 0 && strpos($url,substr($k,4)) === 0) + { + $res = call_user_func($v,$url,$content); + + $url = isset($res['url']) ? $res['url'] : $url; + $content = isset($res['content']) ? $res['content'] : $content; + $lang = isset($res['lang']) ? $res['lang'] : $lang; + $title = isset($res['title']) ? $res['title'] : $title; + + break; + } + } + } + + function __parseImg($str,&$attr) + { + $data = $this->__splitTagsAttr($str); + + $alt = ''; + $url = $data[0]; + if (!empty($data[1])) { + $alt = $data[1]; + } + + $attr = ' src="'.$this->protectAttr($this->protectUrls($url)).'"'; + $attr .= ' alt="'.$this->protectAttr($alt).'"'; + + if (!empty($data[2])) { + $data[2] = strtoupper($data[2]); + if ($data[2] == 'G' || $data[2] == 'L') { + $attr .= ' style="float:left; margin: 0 1em 1em 0;"'; + } elseif ($data[2] == 'D' || $data[2] == 'R') { + $attr .= ' style="float:right; margin: 0 0 1em 1em;"'; + } elseif ($data[2] == 'C') { + $attr .= ' style="display:block; margin:0 auto;"'; + } + } + + if (!empty($data[3])) { + $attr .= ' title="'.$this->protectAttr($data[3]).'"'; + } + + return null; + } + + function __parseQ($str,&$attr) + { + $str = $this->__inlineWalk($str); + $data = $this->__splitTagsAttr($str); + + $content = $data[0]; + $lang = (!empty($data[1])) ? $this->protectAttr($data[1],true) : ''; + + $attr .= (!empty($lang)) ? ' lang="'.$lang.'"' : ''; + $attr .= (!empty($data[2])) ? ' cite="'.$this->protectAttr($this->protectUrls($data[2])).'"' : ''; + + return $content; + } + + function __parseAnchor($str,&$attr) + { + $name = $this->protectAttr($str,true); + + if ($name != '') { + $attr = ' name="'.$name.'"'; + } + return null; + } + + function __parseNote($str) + { + $i = count($this->foot_notes)+1; + $id = $this->getOpt('note_prefix').'-'.$i; + $this->foot_notes[$id] = $this->__inlineWalk($str); + return '\['.$i.'\]'; + } + + function __parseInlineHTML($str) + { + return str_replace(array('>','<'),array('>','<'),$str); + } + + # Obtenir un acronyme + function __parseAcronym($str,&$attr) + { + $data = $this->__splitTagsAttr($str); + + $acronym = $data[0]; + $title = $lang = ''; + + if (count($data) > 1) + { + $title = $data[1]; + $lang = (!empty($data[2])) ? $this->protectAttr($data[2],true) : ''; + } + + if ($title == '' && !empty($this->acro_table[$acronym])) { + $title = $this->acro_table[$acronym]; + } + + $attr = ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; + $attr .= ($lang) ? ' lang="'.$lang.'"' : ''; + + return $acronym; + } + + # Définition des acronymes, dans le fichier acronyms.txt + function __getAcronyms() + { + $file = $this->getOpt('acronyms_file'); + $res = array(); + + if (file_exists($file)) + { + if (($fc = @file($file)) !== false) + { + foreach ($fc as $v) + { + $v = trim($v); + if ($v != '') + { + $p = strpos($v,':'); + $K = (string) trim(substr($v,0,$p)); + $V = (string) trim(substr($v,($p+1))); + + if ($K) { + $res[$K] = $V; + } + } + } + } + } + + return $res; + } + + # Mots wiki (pour héritage) + function parseWikiWord($str,&$tag,&$attr,&$type) + { + $tag = $attr = ''; + + if (isset($this->functions['wikiword'])) { + return call_user_func($this->functions['wikiword'],$str); + } + + return $str; + } + + /* Protection des attributs */ + function protectAttr($str,$name=false) + { + if ($name && !preg_match('/^[A-Za-z][A-Za-z0-9_:.-]*$/',$str)) { + return ''; + } + + return str_replace(array("'",'"'),array(''','"'),$str); + } + + /* Protection des urls */ + function protectUrls($str) + { + if (preg_match('/^javascript:/',$str)) { + $str = '#'; + } + + return $str; + } + + /* Auto BR */ + function __autoBR($m) + { + return $m[1].str_replace("\n","
        \n",$m[2]).$m[3]; + } + + /* Macro + --------------------------------------------------- */ + function __getMacro($s) + { + $this->macros[] = str_replace('\"','"',$s); + return 'øøø##########MACRO#'.(count($this->macros)-1).'#'; + } + + function __putMacro($id) + { + $id = (integer) $id; + if (isset($this->macros[$id])) + { + $content = str_replace("\r",'',$this->macros[$id]); + + $c = explode("\n",$content); + + # première ligne, premier mot + $fl = trim($c[0]); + $fw = $fl; + + if ($fl) { + if (strpos($fl,' ') !== false) { + $fw = substr($fl,0,strpos($fl,' ')); + } + $content = implode("\n",array_slice($c,1)); + } + + if ($c[0] == "\n") { + $content = implode("\n",array_slice($c,1)); + } + + if ($fw) + { + if (isset($this->functions['macro:'.$fw])) + { + return call_user_func($this->functions['macro:'.$fw],$content,$fl); + } + } + + # Si on n'a rien pu faire, on retourne le tout sous + # forme de
        +			return '
        '.htmlspecialchars($this->macros[$id]).'
        '; + } + + return null; + } + + function __macroHTML($s) + { + return $s; + } + + /* Aide et debug + --------------------------------------------------- */ + function help() + { + $help['b'] = array(); + $help['i'] = array(); + + $help['b'][] = 'Laisser une ligne vide entre chaque bloc de même nature.'; + $help['b'][] = 'Paragraphe : du texte et une ligne vide'; + + if ($this->getOpt('active_title')) { + $help['b'][] = 'Titre : !!!, !!, '. + '! pour des titres plus ou moins importants'; + } + + if ($this->getOpt('active_hr')) { + $help['b'][] = 'Trait horizontal : ----'; + } + + if ($this->getOpt('active_lists')) { + $help['b'][] = 'Liste : ligne débutant par * ou '. + '#. Il est possible de mélanger les listes '. + '(*#*) pour faire des listes de plusieurs niveaux. '. + 'Respecter le style de chaque niveau'; + } + + if ($this->getOpt('active_pre')) { + $help['b'][] = 'Texte préformaté : espace devant chaque ligne de texte'; + } + + if ($this->getOpt('active_quote')) { + $help['b'][] = 'Bloc de citation : > ou '. + ';: devant chaque ligne de texte'; + } + + if ($this->getOpt('active_fr_syntax')) { + $help['i'][] = 'La correction de ponctuation est active. Un espace '. + 'insécable remplacera automatiquement tout espace '. + 'précédant les marques ";","?",":" et "!".'; + } + + if ($this->getOpt('active_em')) { + $help['i'][] = 'Emphase : deux apostrophes \'\'texte\'\''; + } + + if ($this->getOpt('active_strong')) { + $help['i'][] = 'Forte emphase : deux soulignés __texte__'; + } + + if ($this->getOpt('active_br')) { + $help['i'][] = 'Retour forcé à la ligne : %%%'; + } + + if ($this->getOpt('active_ins')) { + $help['i'][] = 'Insertion : deux plus ++texte++'; + } + + if ($this->getOpt('active_del')) { + $help['i'][] = 'Suppression : deux moins --texte--'; + } + + if ($this->getOpt('active_urls')) { + $help['i'][] = 'Lien : [url], [nom|url], '. + '[nom|url|langue] ou [nom|url|langue|titre].'; + + $help['i'][] = 'Image : comme un lien mais avec une extension d\'image.'. + '
        Pour désactiver la reconnaissance d\'image mettez 0 dans un dernier '. + 'argument. Par exemple [image|image.gif||0] fera un lien vers l\'image au '. + 'lieu de l\'afficher.'. + '
        Il est conseillé d\'utiliser la nouvelle syntaxe.'; + } + + if ($this->getOpt('active_img')) { + $help['i'][] = 'Image (nouvelle syntaxe) : '. + '((url|texte alternatif)), '. + '((url|texte alternatif|position)) ou '. + '((url|texte alternatif|position|description longue)). '. + '
        La position peut prendre les valeur L ou G (gauche), R ou D (droite) ou C (centré).'; + } + + if ($this->getOpt('active_anchor')) { + $help['i'][] = 'Ancre : ~ancre~'; + } + + if ($this->getOpt('active_acronym')) { + $help['i'][] = 'Acronyme : ??acronyme?? ou '. + '??acronyme|titre??'; + } + + if ($this->getOpt('active_q')) { + $help['i'][] = 'Citation : {{citation}}, '. + '{{citation|langue}} ou {{citation|langue|url}}'; + } + + if ($this->getOpt('active_code')) { + $help['i'][] = 'Code : @@code ici@@'; + } + + if ($this->getOpt('active_footnotes')) { + $help['i'][] = 'Note de bas de page : $$Corps de la note$$'; + } + + $res = '
        '; + + $res .= '
        Blocs
        '; + if (count($help['b']) > 0) + { + $res .= '
        • '; + $res .= implode(' ;
        • ', $help['b']); + $res .= '.
        '; + } + $res .= '
        '; + + $res .= '
        Éléments en ligne
        '; + if (count($help['i']) > 0) + { + $res .= '
        • '; + $res .= implode(' ;
        • ', $help['i']); + $res .= '.
        '; + } + $res .= '
        '; + + $res .= '
        '; + + return $res; + } + + /* + function debug() + { + $mode = $type = null; + $max = count($this->T); + + $res = + ''. + ''; + + for ($i=0; $i<$max; $i++) + { + $pre_mode = $mode; + $pre_type = $type; + + $line = $this->__getLine($i,$type,$mode); + + $res .= + ''. + ''; + + } + $res .= '
        p-modep-typemodetypechaine
        '.$pre_mode.''.$pre_type.''.$mode.''.$type.''.$line.'
        '; + + return $res; + } + //*/ +} + +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/url.handler/class.url.handler.php b/v2/dotclear/inc/libs/clearbricks/url.handler/class.url.handler.php new file mode 100644 index 0000000..0c99b3c --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/url.handler/class.url.handler.php @@ -0,0 +1,229 @@ +mode = $mode; + } + + public function register($type,$url,$representation,$handler) + { + $this->types[$type] = array( + 'url' => $url, + 'representation' => $representation, + 'handler' => $handler + ); + } + + public function registerDefault($handler) + { + $this->default_handler = $handler; + } + + public function registerError($handler) + { + array_unshift($this->error_handlers,$handler); + } + + public function unregister($type) + { + if (isset($this->types[$type])) { + unset($this->types[$type]); + } + } + + public function getTypes() + { + return $this->types; + } + + public function getBase($type) + { + if (isset($this->types[$type])) { + return $this->types[$type]['url']; + } + return null; + } + + public function getDocument() + { + $type = $args = ''; + + if ($this->mode == 'path_info') + { + $part = substr($_SERVER['PATH_INFO'],1); + } + else + { + $part = ''; + + $qs = $this->parseQueryString(); + + # Recreates some _GET and _REQUEST pairs + if (!empty($qs)) + { + foreach ($_GET as $k => $v) { + if (isset($_REQUEST[$k])) { + unset($_REQUEST[$k]); + } + } + $_GET = $qs; + $_REQUEST = array_merge($qs,$_REQUEST); + + list($k,$v) = each($qs); + if ($v === null) { + $part = $k; + unset($_GET[$k]); + unset($_REQUEST[$k]); + } + } + } + + $_SERVER['URL_REQUEST_PART'] = $part; + + $this->getArgs($part,$type,$args); + + if (!$type) + { + $this->type = 'default'; + $this->callDefaultHandler($args); + } + else + { + $this->type = $type; + $this->callHandler($type,$args); + } + } + + public function getArgs($part,&$type,&$args) + { + if ($part == '') { + $type = null; + $args = null; + return; + } + + $this->sortTypes(); + + foreach ($this->types as $k => $v) + { + $repr = $v['representation']; + if ($repr == $part) { + $type = $k; + $args = null; + return; + } + elseif (preg_match('#'.$repr.'#',$part,$m)) + { + $type = $k; + $args = isset($m[1]) ? $m[1] : null; + return; + } + } + + # No type, pass args to default + $args = $part; + } + + public function callHandler($type,$args) + { + if (!isset($this->types[$type])) { + throw new Exception('Unknown URL type'); + } + + $handler = $this->types[$type]['handler']; + if (!is_callable($handler)) { + throw new Exception('Unable to call function'); + } + try { + call_user_func($handler,$args); + } catch (Exception $e) { + foreach ($this->error_handlers as $err_handler) { + if (call_user_func($err_handler,$args,$type,$e) === true) { + return; + } + } + # propagate exception, as it has not been processed by handlers + throw $e; + } + } + + public function callDefaultHandler($args) + { + if (!is_callable($this->default_handler)) { + throw new Exception('Unable to call function'); + } + + try { + call_user_func($this->default_handler,$args); + } catch (Exception $e) { + foreach ($this->error_handlers as $err_handler) { + if (call_user_func($err_handler,$args,'default',$e) === true) { + return; + } + } + # propagate exception, as it has not been processed by handlers + throw $e; + } + + } + + protected function parseQueryString() + { + if (!empty($_SERVER['QUERY_STRING'])) + { + $q = explode('&',$_SERVER['QUERY_STRING']); + $T = array(); + foreach ($q as $v) + { + $t = explode('=',$v,2); + + $t[0] = rawurldecode($t[0]); + if (!isset($t[1])) { + $T[$t[0]] = null; + } else { + $T[$t[0]] = urldecode($t[1]); + } + } + + return $T; + } + return array(); + } + + protected function sortTypes() + { + foreach ($this->types as $k => $v) { + $r[$k] = $v['url']; + } + array_multisort($r,SORT_DESC,$this->types); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/xmlsql/class.xmlsql.php b/v2/dotclear/inc/libs/clearbricks/xmlsql/class.xmlsql.php new file mode 100644 index 0000000..e69ed3a --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/xmlsql/class.xmlsql.php @@ -0,0 +1,180 @@ +con =& $con; + $this->xml = $xml; + + $schema = dbSchema::init($this->con); + $this->tables = $schema->getTables(); + unset($schema); + } + + public function replace($str,$rep) + { + $this->xml = str_replace($str,$rep,$this->xml); + } + + public function execute($version=0) + { + $this->test_version = $version; + + $x = @simplexml_load_string($this->xml); + if (!$x) { + throw new Exception('Unable to load XML file.'); + } + + $this->parseNode($x); + } + + protected function parseNode($node) + { + foreach ($node->children() as $n) + { + switch (dom_import_simplexml($n)->nodeName) + { + case 'test': + $this->performTest($n); + break; + case 'action': + $this->performAction($n); + break; + } + } + } + + protected function performTest($node) + { + /* Test like: + ... + */ + if (isset($node['type']) && (string) $node['type'] == 'table') + { + $test['result'] = in_array($node['name'],$this->tables); + $test['label'] = 'Table %s does not exists'; + $test['string'] = (string) $node['name']; + + $xtest = $node; + } + /* Test syntax: + ... + */ + elseif (isset($node['type']) && (string) $node['type'] == 'column') + { + $c = explode('.',$node['name']); + + if (count($c) != 2) { + return false; + + } + + list($table,$col) = $c; + + $rs = $this->con->getColumns($table); + + $test['result'] = isset($rs[$col]); + $test['label'] = 'Column %s does not exists'; + $test['string'] = (string) $node['name']; + + $xtest = $node; + } + /* Test syntax: + ... + */ + elseif (isset($node['type']) && (string) $node['type'] == 'version') + { + $comp = isset($node['comp']) ? $node['comp'] : '>'; + $test['result'] = version_compare($node['name'],$this->test_version,$comp); + $test['label'] = 'Version %s is too low'; + $test['string'] = (string) $node['name']; + + $xtest = $node; + } + + # End tests + if (isset($xtest)) + { + if ($xtest['eq'] == 'neq') { + $test['result'] = !$test['result']; + } + + if (isset($xtest['alert'])) { + $test['alert'] = (boolean) (integer) $xtest['alert']; + } else { + $test['alert'] = false; + } + + if (isset($xtest['label'])) { + $test['label'] = (string) $xtest['label']; + } + if (isset($xtest['string'])) { + $test['string'] = (string) $xtest['string']; + } + unset($xtest); + + # Test false + if (!$test['result']) + { + if ($test['alert']) { + throw new Exception(sprintf($test['label'],$test['string'])); + } + } + # Test true + else + { + $this->parseNode($node); + } + } + else + { + return false; + } + } + + protected function performAction($node) + { + $req = trim((string) $node); + if ($req) + { + try { + $this->con->execute($req); + } catch (Exception $e) { + if ($node['silent'] != 1) { + throw $e; + } + } + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/zip/class.unzip.php b/v2/dotclear/inc/libs/clearbricks/zip/class.unzip.php new file mode 100644 index 0000000..09cc081 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/zip/class.unzip.php @@ -0,0 +1,541 @@ +file_name = $file_name; + } + + public function __destruct() + { + $this->close(); + } + + public function close() + { + if ($this->fp) { + fclose($this->fp); + $this->fp = null; + } + + if ($this->memory_limit) { + ini_set('memory_limit',$this->memory_limit); + } + } + + public function getList($stop_on_file=false,$exclude=false) + { + if (!empty($this->compressed_list)) { + return $this->compressed_list; + } + + if (!$this->loadFileListByEOF($stop_on_file,$exclude)) { + if(!$this->loadFileListBySignatures($stop_on_file,$exclude)) { + return false; + } + } + + return $this->compressed_list; + } + + public function unzipAll($target) + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + foreach ($this->compressed_list as $k => $v) + { + if ($v['is_dir']) { + continue; + } + + $this->unzip($k,$target.'/'.$k); + } + } + + public function unzip($file_name,$target=false) + { + if (empty($this->compressed_list)) { + $this->getList($file_name); + } + + if (!isset($this->compressed_list[$file_name])) { + throw new Exception(sprintf(__('File %s is not compressed in the zip.'),$file_name)); + } + if ($this->isFileExcluded($file_name)) { + return; + } + $details =& $this->compressed_list[$file_name]; + + if ($details['is_dir']) { + throw new Exception(sprintf(__('Trying to unzip a folder name %s'),$file_name)); + } + + if (!$details['uncompressed_size']) { + return $this->putContent('',$target); + } + + if ($target) { + $this->testTargetDir(dirname($target)); + } + + fseek($this->fp(),$details['contents_start_offset']); + + $this->memoryAllocate($details['compressed_size']); + return $this->uncompress( + fread($this->fp(), $details['compressed_size']), + $details['compression_method'], + $details['uncompressed_size'], + $target + ); + } + + public function getFilesList() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + $res = array(); + foreach ($this->compressed_list as $k => $v) { + if (!$v['is_dir']) { + $res[] = $k; + } + } + return $res; + } + + public function getDirsList() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + $res = array(); + foreach ($this->compressed_list as $k => $v) { + if ($v['is_dir']) { + $res[] = substr($k,0,-1); + } + } + return $res; + } + + public function getRootDir() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + $files = $this->getFilesList(); + $dirs = $this->getDirsList(); + + $root_files = 0; + $root_dirs = 0; + foreach ($files as $v) { if (strpos($v,'/') === false) { $root_files++; }} + foreach ($dirs as $v) { if (strpos($v,'/') === false) { $root_dirs++; }} + + if ($root_files == 0 && $root_dirs == 1) { + return $dirs[0]; + } else { + return false; + } + } + + public function isEmpty() + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + return count($this->compressed_list) == 0; + } + + public function hasFile($f) + { + if (empty($this->compressed_list)) { + $this->getList(); + } + + return isset($this->compressed_list[$f]); + } + + public function setExcludePattern($pattern) + { + $this->exclude_pattern = $pattern; + } + + protected function fp() + { + if ($this->fp === null) { + $this->fp = @fopen($this->file_name,'rb'); + } + + if ($this->fp === false) { + throw new Exception('Unable to open file.'); + } + + return $this->fp; + } + + protected function isFileExcluded($f) + { + if (!$this->exclude_pattern) { + return false; + } + + return preg_match($this->exclude_pattern,$f); + } + + protected function putContent($content,$target=false) + { + if ($target) { + $r = @file_put_contents($target,$content); + if ($r === false) { + throw new Exception(__('Unable to write destination file.')); + } + files::inheritChmod($target); + return true; + } + return $content; + } + + protected function testTargetDir($dir) + { + if (is_dir($dir) && !is_writable($dir)) { + throw new Exception(__('Unable to write in target directory, permission denied.')); + } + + if (!is_dir($dir)) { + files::makeDir($dir,true); + } + } + + protected function uncompress($content,$mode,$size,$target=false) + { + switch ($mode) + { + case 0: + # Not compressed + $this->memoryAllocate($size*2); + return $this->putContent($content,$target); + case 1: + throw new Exception('Shrunk mode is not supported.'); + case 2: + case 3: + case 4: + case 5: + throw new Exception('Compression factor '.($mode-1).' is not supported.'); + case 6: + throw new Exception('Implode is not supported.'); + case 7: + throw new Exception('Tokenizing compression algorithm is not supported.'); + case 8: + # Deflate + if (!function_exists('gzinflate')) { + throw new Exception('Gzip functions are not available.'); + } + $this->memoryAllocate($size*2); + return $this->putContent(gzinflate($content,$size),$target); + case 9: + throw new Exception('Enhanced Deflating is not supported.'); + case 10: + throw new Exception('PKWARE Date Compression Library Impoloding is not supported.'); + case 12: + # Bzip2 + if (!function_exists('bzdecompress')) { + throw new Exception('Bzip2 functions are not available.'); + } + $this->memoryAllocate($size*2); + return $this->putContent(bzdecompress($content),$target); + case 18: + throw new Exception('IBM TERSE is not supported.'); + default: + throw new Exception('Unknown uncompress method'); + } + } + + protected function loadFileListByEOF($stop_on_file=false,$exclude=false) + { + $fp = $this->fp(); + + for ($x=0; $x<1024; $x++) + { + fseek($fp,-22-$x,SEEK_END); + $signature = fread($fp,4); + + if ($signature == $this->dir_sig_e) + { + $dir_list = array(); + + $eodir = array( + 'disk_number_this' => unpack('v', fread($fp,2)), + 'disk_number' => unpack('v', fread($fp,2)), + 'total_entries_this' => unpack('v', fread($fp,2)), + 'total_entries' => unpack('v', fread($fp,2)), + 'size_of_cd' => unpack('V', fread($fp,4)), + 'offset_start_cd' => unpack('V', fread($fp,4)) + ); + + $zip_comment_len = unpack('v', fread($fp,2)); + $eodir['zipfile_comment'] = $zip_comment_len[1] ? fread($fp,$zip_comment_len) : ''; + + $this->eo_central = array( + 'disk_number_this' => $eodir['disk_number_this'][1], + 'disk_number' => $eodir['disk_number'][1], + 'total_entries_this' => $eodir['total_entries_this'][1], + 'total_entries' => $eodir['total_entries'][1], + 'size_of_cd' => $eodir['size_of_cd'][1], + 'offset_start_cd' => $eodir['offset_start_cd'][1], + 'zipfile_comment' => $eodir['zipfile_comment'] + ); + + fseek($fp, $this->eo_central['offset_start_cd']); + $signature = fread($fp,4); + + while ($signature == $this->dir_sig) + { + $dir = array(); + $dir['version_madeby'] = unpack("v",fread($fp, 2)); # version made by + $dir['version_needed'] = unpack("v",fread($fp, 2)); # version needed to extract + $dir['general_bit_flag'] = unpack("v",fread($fp, 2)); # general purpose bit flag + $dir['compression_method'] = unpack("v",fread($fp, 2)); # compression method + $dir['lastmod_time'] = unpack("v",fread($fp, 2)); # last mod file time + $dir['lastmod_date'] = unpack("v",fread($fp, 2)); # last mod file date + $dir['crc-32'] = fread($fp,4); # crc-32 + $dir['compressed_size'] = unpack("V",fread($fp, 4)); # compressed size + $dir['uncompressed_size'] = unpack("V",fread($fp, 4)); # uncompressed size + + $file_name_len = unpack("v",fread($fp, 2)); # filename length + $extra_field_len = unpack("v",fread($fp, 2)); # extra field length + $file_comment_len = unpack("v",fread($fp, 2)); # file comment length + + $dir['disk_number_start'] = unpack("v",fread($fp, 2)); # disk number start + $dir['internal_attributes'] = unpack("v",fread($fp, 2)); # internal file attributes-byte1 + $dir['external_attributes1'] = unpack("v",fread($fp, 2)); # external file attributes-byte2 + $dir['external_attributes2'] = unpack("v",fread($fp, 2)); # external file attributes + $dir['relative_offset'] = unpack("V",fread($fp, 4)); # relative offset of local header + $dir['file_name'] = $this->cleanFileName(fread($fp, $file_name_len[1])); # filename + $dir['extra_field'] = $extra_field_len[1] ? fread($fp, $extra_field_len[1]) : ''; # extra field + $dir['file_comment'] = $file_comment_len[1] ? fread($fp, $file_comment_len[1]) : ''; # file comment + + $dir_list[$dir['file_name']] = array( + 'version_madeby' => $dir['version_madeby'][1], + 'version_needed' => $dir['version_needed'][1], + 'general_bit_flag' => str_pad(decbin($dir['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), + 'compression_method' => $dir['compression_method'][1], + 'lastmod_datetime' => $this->getTimeStamp($dir['lastmod_date'][1],$dir['lastmod_time'][1]), + 'crc-32' => str_pad(dechex(ord($dir['crc-32'][3])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($dir['crc-32'][2])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($dir['crc-32'][1])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($dir['crc-32'][0])), 2, '0', STR_PAD_LEFT), + 'compressed_size' => $dir['compressed_size'][1], + 'uncompressed_size' => $dir['uncompressed_size'][1], + 'disk_number_start' => $dir['disk_number_start'][1], + 'internal_attributes' => $dir['internal_attributes'][1], + 'external_attributes1' => $dir['external_attributes1'][1], + 'external_attributes2' => $dir['external_attributes2'][1], + 'relative_offset' => $dir['relative_offset'][1], + 'file_name' => $dir['file_name'], + 'extra_field' => $dir['extra_field'], + 'file_comment' => $dir['file_comment'] + ); + $signature = fread($fp, 4); + } + + foreach ($dir_list as $k => $v) + { + if ($exclude && preg_match($exclude,$k)) { + continue; + } + + $i = $this->getFileHeaderInformation($v['relative_offset']); + + $this->compressed_list[$k]['file_name'] = $k; + $this->compressed_list[$k]['is_dir'] = $v['external_attributes1'] == 16 || substr($k,-1,1) == '/'; + $this->compressed_list[$k]['compression_method'] = $v['compression_method']; + $this->compressed_list[$k]['version_needed'] = $v['version_needed']; + $this->compressed_list[$k]['lastmod_datetime'] = $v['lastmod_datetime']; + $this->compressed_list[$k]['crc-32'] = $v['crc-32']; + $this->compressed_list[$k]['compressed_size'] = $v['compressed_size']; + $this->compressed_list[$k]['uncompressed_size'] = $v['uncompressed_size']; + $this->compressed_list[$k]['lastmod_datetime'] = $v['lastmod_datetime']; + $this->compressed_list[$k]['extra_field'] = $i['extra_field']; + $this->compressed_list[$k]['contents_start_offset'] = $i['contents_start_offset']; + + if(strtolower($stop_on_file) == strtolower($k)) { + break; + } + } + return true; + } + } + return false; + } + + protected function loadFileListBySignatures($stop_on_file=false,$exclude=false) + { + $fp = $this->fp(); + fseek($fp,0); + + $return = false; + while(true) + { + $details = $this->getFileHeaderInformation(); + if (!$details) { + fseek($fp,12-4,SEEK_CUR); # 12: Data descriptor - 4: Signature (that will be read again) + $details = $this->getFileHeaderInformation(); + } + if (!$details) { + break; + } + $filename = $details['file_name']; + + if ($exclude && preg_match($exclude,$filename)) { + continue; + } + + $this->compressed_list[$filename] = $details; + $return = true; + + if (strtolower($stop_on_file) == strtolower($filename)) { + break; + } + } + + return $return; + } + + protected function getFileHeaderInformation($start_offset=false) + { + $fp = $this->fp(); + + if ($start_offset !== false) { + fseek($fp,$start_offset); + } + + $signature = fread($fp, 4); + if ($signature == $this->zip_sig) + { + # Get information about the zipped file + $file = array(); + $file['version_needed'] = unpack("v",fread($fp, 2)); # version needed to extract + $file['general_bit_flag'] = unpack("v",fread($fp, 2)); # general purpose bit flag + $file['compression_method'] = unpack("v",fread($fp, 2)); # compression method + $file['lastmod_time'] = unpack("v",fread($fp, 2)); # last mod file time + $file['lastmod_date'] = unpack("v",fread($fp, 2)); # last mod file date + $file['crc-32'] = fread($fp,4); # crc-32 + $file['compressed_size'] = unpack("V",fread($fp, 4)); # compressed size + $file['uncompressed_size'] = unpack("V",fread($fp, 4)); # uncompressed size + + $file_name_len = unpack("v",fread($fp, 2)); # filename length + $extra_field_len = unpack("v",fread($fp, 2)); # extra field length + + $file['file_name'] = $this->cleanFileName(fread($fp,$file_name_len[1])); # filename + $file['extra_field'] = $extra_field_len[1] ? fread($fp, $extra_field_len[1]) : ''; # extra field + $file['contents_start_offset'] = ftell($fp); + + # Look for the next file + fseek($fp, $file['compressed_size'][1], SEEK_CUR); + + # Mount file table + $i = array( + 'file_name' => $file['file_name'], + 'is_dir' => substr($file['file_name'],-1,1) == '/', + 'compression_method' => $file['compression_method'][1], + 'version_needed' => $file['version_needed'][1], + 'lastmod_datetime' => $this->getTimeStamp($file['lastmod_date'][1],$file['lastmod_time'][1]), + 'crc-32' => str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT), + 'compressed_size' => $file['compressed_size'][1], + 'uncompressed_size' => $file['uncompressed_size'][1], + 'extra_field' => $file['extra_field'], + 'general_bit_flag' => str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), + 'contents_start_offset'=>$file['contents_start_offset'] + ); + return $i; + } + return false; + } + + protected function getTimeStamp($date,$time) + { + $BINlastmod_date = str_pad(decbin($date), 16, '0', STR_PAD_LEFT); + $BINlastmod_time = str_pad(decbin($time), 16, '0', STR_PAD_LEFT); + $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980; + $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4)); + $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5)); + $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5)); + $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6)); + $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)) * 2; + + return mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY); + } + + protected function cleanFileName($n) + { + $n = str_replace('../','',$n); + $n = preg_replace('#^/+#','',$n); + return $n; + } + + protected function memoryAllocate($size) + { + $mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000; + $mem_limit = @ini_get('memory_limit'); + if ($mem_used && $mem_limit) + { + $mem_limit = files::str2bytes($mem_limit); + $mem_avail = $mem_limit-$mem_used-(512*1024); + $mem_needed = $size; + + if ($mem_needed > $mem_avail) + { + if (@ini_set('memory_limit',$mem_limit+$mem_needed+$mem_used) === false) { + throw new Exception(__('Not enough memory to open file.')); + } + + if (!$this->memory_limit) { + $this->memory_limit = $mem_limit; + } + } + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/libs/clearbricks/zip/class.zip.php b/v2/dotclear/inc/libs/clearbricks/zip/class.zip.php new file mode 100644 index 0000000..f5656e9 --- /dev/null +++ b/v2/dotclear/inc/libs/clearbricks/zip/class.zip.php @@ -0,0 +1,370 @@ +fp = $out_fp; + } + + public function __destruct() + { + $this->close(); + } + + public function close() + { + if ($this->memory_limit) { + ini_set('memory_limit',$this->memory_limit); + } + } + + public function addExclusion($reg) + { + $this->exclusions[] = $reg; + } + + public function addFile($file,$name=null) + { + $file = preg_replace('#[\\\/]+#','/',$file); + + if (!$name) { + $name = $file; + } + $name = $this->formatName($name); + + if ($this->isExcluded($name)) { + return; + } + + if (!file_exists($file) || !is_file($file)) { + throw new Exception(__('File does not exist')); + } + if (!is_readable($file)) { + throw new Exception(__('Cannot read file')); + } + + $info = stat($file); + + $this->entries[$name] = array( + 'file' => $file, + 'is_dir' => false, + 'mtime' => $info['mtime'], + 'size' => $info['size'] + ); + } + + public function addDirectory($dir,$name=null,$recursive=false) + { + $dir = preg_replace('#[\\\/]+#','/',$dir); + if (substr($dir,-1-1) != '/') { + $dir .= '/'; + } + + if (!$name && $name !== '') { + $name = $dir; + } + + if ($this->isExcluded($name)) { + return; + } + + if ($name !== '') + { + if (substr($name,-1,1) != '/') { + $name .= '/'; + } + + $name = $this->formatName($name); + + if ($name !== '') + { + $this->entries[$name] = array( + 'file' => null, + 'is_dir' => true, + 'mtime' => time(), + 'size' => 0 + ); + } + } + + if ($recursive) + { + if (!is_dir($dir)) { + throw new Exception(__('Directory does not exist')); + } + if (!is_readable($dir)) { + throw new Exception(__('Cannot read directory')); + } + + $D = dir($dir); + while (($e = $D->read()) !== false) { + if ($e == '.' || $e == '..') { + continue; + } + + if (is_dir($dir.'/'.$e)) { + $this->addDirectory($dir.$e,$name.$e,true); + } elseif (is_file($dir.'/'.$e)) { + $this->addFile($dir.$e,$name.$e); + } + } + } + } + + public function write() + { + foreach ($this->entries as $name => $v) + { + if ($v['is_dir']) { + $this->writeDirectory($name); + } else { + $this->writeFile($name,$v['file'],$v['size'],$v['mtime']); + } + } + + $ctrldir = implode('',$this->ctrl_dir); + + fwrite($this->fp, + $ctrldir. + $this->eof_ctrl_dir. + pack('v',sizeof($this->ctrl_dir)). # total # of entries "on this disk" + pack('v',sizeof($this->ctrl_dir)). # total # of entries overall + pack('V',strlen($ctrldir)). # size of central dir + pack('V',$this->old_offset). # offset to start of central dir + "\x00\x00" # .zip file comment length + ); + } + + protected function writeDirectory($name) + { + if (!isset($this->entries[$name])) { + return; + } + + $mdate = $this->makeDate(time()); + $mtime = $this->makeTime(time()); + + # Data descriptor + $data_desc = + "\x50\x4b\x03\x04". + "\x0a\x00". # ver needed to extract + "\x00\x00". # gen purpose bit flag + "\x00\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',0). # crc32 + pack('V',0). # compressed filesize + pack('V',0). # uncompressed filesize + pack('v',strlen($name)). # length of pathname + pack('v',0). # extra field length + $name. # end of "local file header" segment + pack('V',0). # crc32 + pack('V',0). # compressed filesize + pack('V',0); # uncompressed filesize + + $new_offset = $this->old_offset + strlen($data_desc); + fwrite($this->fp,$data_desc); + + # Add to central record + $cdrec = + "\x50\x4b\x01\x02". + "\x00\x00". # version made by + "\x0a\x00". # version needed to extract + "\x00\x00". # gen purpose bit flag + "\x00\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',0). # crc32 + pack('V',0). # compressed filesize + pack('V',0). # uncompressed filesize + pack('v',strlen($name)). # length of filename + pack('v',0). # extra field length + pack('v',0). # file comment length + pack('v',0). # disk number start + pack('v',0). # internal file attributes + pack('V',16). # external file attributes - 'directory' bit set + pack('V',$this->old_offset). # relative offset of local header + $name; + + $this->old_offset = $new_offset; + $this->ctrl_dir[] = $cdrec; + } + + protected function writeFile($name,$file,$size,$mtime) + { + if (!isset($this->entries[$name])) { + return; + } + + $size = filesize($file); + $this->memoryAllocate($size*3); + + $content = file_get_contents($file); + + $unc_len = strlen($content); + $crc = crc32($content); + $zdata = gzdeflate($content); + $c_len = strlen($zdata); + + unset($content); + + $mdate = $this->makeDate($mtime); + $mtime = $this->makeTime($mtime); + + # Data descriptor + $data_desc = + "\x50\x4b\x03\x04". + "\x14\x00". # ver needed to extract + "\x00\x00". # gen purpose bit flag + "\x08\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',$crc). # crc32 + pack('V',$c_len). # compressed filesize + pack('V',$unc_len). # uncompressed filesize + pack('v',strlen($name)). # length of filename + pack('v',0). # extra field length + $name. # end of "local file header" segment + $zdata. # "file data" segment + pack('V',$crc). # crc32 + pack('V',$c_len). # compressed filesize + pack('V',$unc_len); # uncompressed filesize + + fwrite($this->fp,$data_desc); + unset($zdata); + + $new_offset = $this->old_offset + strlen($data_desc); + + # Add to central directory record + $cdrec = + "\x50\x4b\x01\x02". + "\x00\x00". # version made by + "\x14\x00". # version needed to extract + "\x00\x00". # gen purpose bit flag + "\x08\x00". # compression method + pack('v',$mtime). # last mod time + pack('v',$mdate). # last mod date + pack('V',$crc). # crc32 + pack('V',$c_len). # compressed filesize + pack('V',$unc_len). # uncompressed filesize + pack('v',strlen($name)). # length of filename + pack('v',0). # extra field length + pack('v',0). # file comment length + pack('v',0). # disk number start + pack('v',0). # internal file attributes + pack('V',32). # external file attributes - 'archive' bit set + pack('V',$this->old_offset). # relative offset of local header + $name; + + $this->old_offset = $new_offset; + $this->ctrl_dir[] = $cdrec; + } + + protected function formatName($name) + { + if (substr($name,0,1) == '/') { + $name = substr($name,1); + } + + return $name; + } + + protected function isExcluded($name) + { + foreach ($this->exclusions as $reg) { + if (preg_match($reg,$name)) { + return true; + } + } + + return false; + } + + protected function makeDate($ts) + { + $year = date('Y',$ts)-1980; + if ($year < 0) { + $year = 0; + } + + $year = sprintf('%07b',$year); + $month = sprintf('%04b',date('n',$ts)); + $day = sprintf('%05b',date('j',$ts)); + + return bindec($year.$month.$day); + } + + protected function makeTime($ts) + { + $hour = sprintf('%05b',date('G',$ts)); + $minute = sprintf('%06b',date('i',$ts)); + $second = sprintf('%05b',ceil(date('s',$ts)/2)); + + return bindec($hour.$minute.$second); + } + + protected function memoryAllocate($size) + { + $mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000; + $mem_limit = @ini_get('memory_limit'); + if ($mem_used && $mem_limit) + { + $mem_limit = files::str2bytes($mem_limit); + $mem_avail = $mem_limit-$mem_used-(512*1024); + $mem_needed = $size; + + if ($mem_needed > $mem_avail) + { + if (@ini_set('memory_limit',$mem_limit+$mem_needed+$mem_used) === false) { + throw new Exception(__('Not enough memory to open file.')); + } + + if (!$this->memory_limit) { + $this->memory_limit = $mem_limit; + } + } + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/load_plugin_file.php b/v2/dotclear/inc/load_plugin_file.php new file mode 100644 index 0000000..033f17d --- /dev/null +++ b/v2/dotclear/inc/load_plugin_file.php @@ -0,0 +1,87 @@ + \ No newline at end of file diff --git a/v2/dotclear/inc/prepend.php b/v2/dotclear/inc/prepend.php new file mode 100644 index 0000000..bc48181 --- /dev/null +++ b/v2/dotclear/inc/prepend.php @@ -0,0 +1,286 @@ +getCode() == 0 ? + sprintf(__('

        This either means that the username and password information in '. + 'your config.php file is incorrect or we can\'t contact '. + 'the database server at "%s". This could mean your '. + 'host\'s database server is down.

        '. + '
        • Are you sure you have the correct username and password?
        • '. + '
        • Are you sure that you have typed the correct hostname?
        • '. + '
        • Are you sure that the database server is running?
        '. + '

        If you\'re unsure what these terms mean you should probably contact '. + 'your host. If you still need help you can always visit the '. + 'Dotclear Support Forums.

        '). + (DC_DEBUG ? + __('The following error was encountered while trying to read the database:').'

        • '.$e->getMessage().'
        ' : '') + ,(DC_DBHOST != '' ? DC_DBHOST : 'localhost') + ) + : '' + ,20); +} + +# If we have some __top_behaviors, we load them +if (isset($__top_behaviors) && is_array($__top_behaviors)) +{ + foreach ($__top_behaviors as $b) { + $core->addBehavior($b[0],$b[1]); + } + unset($b); +} + +http::trimRequest(); +try { + http::unsetGlobals(); +} catch (Exception $e) { + header('Content-Type: text/plain'); + echo $e->getMessage(); + exit; +} + +$core->url->registerDefault(array('dcUrlHandlers','home')); +$core->url->registerError(array('dcUrlHandlers','default404')); +$core->url->register('lang','','^([a-zA-Z]{2}(?:-[a-z]{2})?(?:/page/[0-9]+)?)$',array('dcUrlHandlers','lang')); +$core->url->register('post','post','^post/(.+)$',array('dcUrlHandlers','post')); +$core->url->register('preview','preview','^preview/(.+)$',array('dcUrlHandlers','preview')); +$core->url->register('category','category','^category/(.+)$',array('dcUrlHandlers','category')); +$core->url->register('archive','archive','^archive(/.+)?$',array('dcUrlHandlers','archive')); + +$core->url->register('feed','feed','^feed/(.+)$',array('dcUrlHandlers','feed')); +$core->url->register('trackback','trackback','^trackback/(.+)$',array('dcUrlHandlers','trackback')); +$core->url->register('rsd','rsd','^rsd$',array('dcUrlHandlers','rsd')); +$core->url->register('xmlrpc','xmlrpc','^xmlrpc/(.+)$',array('dcUrlHandlers','xmlrpc')); + +$core->setPostType('post','post.php?id=%d',$core->url->getURLFor('post','%s')); + +# Store upload_max_filesize in bytes +$u_max_size = files::str2bytes(ini_get('upload_max_filesize')); +$p_max_size = files::str2bytes(ini_get('post_max_size')); +if ($p_max_size < $u_max_size) { + $u_max_size = $p_max_size; +} +define('DC_MAX_UPLOAD_SIZE',$u_max_size); +unset($u_max_size); unset($p_max_size); + +# Shutdown +register_shutdown_function('__shutdown'); + +function __shutdown() +{ + global $__shutdown; + if (is_array($__shutdown)) { + foreach ($__shutdown as $f) { + if (is_callable($f)) { + call_user_func($f); + } + } + } + # Explicitly close session before DB connection + try { + if (session_id()) { + session_write_close(); + } + } catch (Exception $e) {} + $GLOBALS['core']->con->close(); +} + +function __error($summary,$message,$code=0) +{ + # Error codes + # 10 : no config file + # 20 : database issue + # 30 : blog is not defined + # 40 : template files creation + # 50 : no default theme + # 60 : template processing error + # 70 : blog is offline + + if (CLI_MODE) + { + trigger_error($summary,E_USER_ERROR); + exit(1); + } + else + { + if (defined('DC_ERRORFILE') && is_file(DC_ERRORFILE)) { + include DC_ERRORFILE; + } else { + include dirname(__FILE__).'/core_error.php'; + } + exit; + } +} + +function init_prepend_l10n() +{ + # Loading locales for detected language + $dlang = http::getAcceptLanguages(); + foreach($dlang as $l) + { + if ($l == 'en' || l10n::set(dirname(__FILE__).'/../locales/'.$l.'/main') !== false) { + break; + } + } +} +?> diff --git a/v2/dotclear/inc/public/class.dc.template.php b/v2/dotclear/inc/public/class.dc.template.php new file mode 100644 index 0000000..8bda8ce --- /dev/null +++ b/v2/dotclear/inc/public/class.dc.template.php @@ -0,0 +1,3095 @@ +remove_php = !$core->blog->settings->system->tpl_allow_php; + $this->use_cache = $core->blog->settings->system->tpl_use_cache; + + $this->tag_block = '|>)((?:[^<]|<(?!/?tpl:\1)|(?R))*)'; + $this->tag_value = '{{tpl:(\w+)(\s(.*?))?}}'; + + $this->core =& $core; + + # Transitional tags + $this->addValue('EntryTrackbackCount',array($this,'EntryPingCount')); + $this->addValue('EntryTrackbackData',array($this,'EntryPingData')); + $this->addValue('EntryTrackbackLink',array($this,'EntryPingLink')); + + # l10n + $this->addValue('lang',array($this,'l10n')); + + # Loops test tags + $this->addBlock('LoopPosition',array($this,'LoopPosition')); + + # Archives + $this->addBlock('Archives',array($this,'Archives')); + $this->addBlock('ArchivesHeader',array($this,'ArchivesHeader')); + $this->addBlock('ArchivesFooter',array($this,'ArchivesFooter')); + $this->addBlock('ArchivesYearHeader',array($this,'ArchivesYearHeader')); + $this->addBlock('ArchivesYearFooter',array($this,'ArchivesYearFooter')); + $this->addValue('ArchiveDate',array($this,'ArchiveDate')); + $this->addBlock('ArchiveNext',array($this,'ArchiveNext')); + $this->addBlock('ArchivePrevious',array($this,'ArchivePrevious')); + $this->addValue('ArchiveEntriesCount',array($this,'ArchiveEntriesCount')); + $this->addValue('ArchiveURL',array($this,'ArchiveURL')); + + # Blog + $this->addValue('BlogArchiveURL',array($this,'BlogArchiveURL')); + $this->addValue('BlogCopyrightNotice',array($this,'BlogCopyrightNotice')); + $this->addValue('BlogDescription',array($this,'BlogDescription')); + $this->addValue('BlogEditor',array($this,'BlogEditor')); + $this->addValue('BlogFeedID',array($this,'BlogFeedID')); + $this->addValue('BlogFeedURL',array($this,'BlogFeedURL')); + $this->addValue('BlogRSDURL',array($this,'BlogRSDURL')); + $this->addValue('BlogName',array($this,'BlogName')); + $this->addValue('BlogLanguage',array($this,'BlogLanguage')); + $this->addValue('BlogThemeURL',array($this,'BlogThemeURL')); + $this->addValue('BlogUpdateDate',array($this,'BlogUpdateDate')); + $this->addValue('BlogID',array($this,'BlogID')); + $this->addValue('BlogURL',array($this,'BlogURL')); + $this->addValue('BlogPublicURL',array($this,'BlogPublicURL')); + $this->addValue('BlogQmarkURL',array($this,'BlogQmarkURL')); + $this->addValue('BlogMetaRobots',array($this,'BlogMetaRobots')); + + # Categories + $this->addBlock('Categories',array($this,'Categories')); + $this->addBlock('CategoriesHeader',array($this,'CategoriesHeader')); + $this->addBlock('CategoriesFooter',array($this,'CategoriesFooter')); + $this->addBlock('CategoryIf',array($this,'CategoryIf')); + $this->addBlock('CategoryFirstChildren',array($this,'CategoryFirstChildren')); + $this->addBlock('CategoryParents',array($this,'CategoryParents')); + $this->addValue('CategoryFeedURL',array($this,'CategoryFeedURL')); + $this->addValue('CategoryURL',array($this,'CategoryURL')); + $this->addValue('CategoryShortURL',array($this,'CategoryShortURL')); + $this->addValue('CategoryDescription',array($this,'CategoryDescription')); + $this->addValue('CategoryTitle',array($this,'CategoryTitle')); + + # Comments + $this->addBlock('Comments',array($this,'Comments')); + $this->addValue('CommentAuthor',array($this,'CommentAuthor')); + $this->addValue('CommentAuthorDomain',array($this,'CommentAuthorDomain')); + $this->addValue('CommentAuthorLink',array($this,'CommentAuthorLink')); + $this->addValue('CommentAuthorMailMD5',array($this,'CommentAuthorMailMD5')); + $this->addValue('CommentAuthorURL',array($this,'CommentAuthorURL')); + $this->addValue('CommentContent',array($this,'CommentContent')); + $this->addValue('CommentDate',array($this,'CommentDate')); + $this->addValue('CommentTime',array($this,'CommentTime')); + $this->addValue('CommentEmail',array($this,'CommentEmail')); + $this->addValue('CommentEntryTitle',array($this,'CommentEntryTitle')); + $this->addValue('CommentFeedID',array($this,'CommentFeedID')); + $this->addValue('CommentID',array($this,'CommentID')); + $this->addBlock('CommentIf',array($this,'CommentIf')); + $this->addValue('CommentIfFirst',array($this,'CommentIfFirst')); + $this->addValue('CommentIfMe',array($this,'CommentIfMe')); + $this->addValue('CommentIfOdd',array($this,'CommentIfOdd')); + $this->addValue('CommentIP',array($this,'CommentIP')); + $this->addValue('CommentOrderNumber',array($this,'CommentOrderNumber')); + $this->addBlock('CommentsFooter',array($this,'CommentsFooter')); + $this->addBlock('CommentsHeader',array($this,'CommentsHeader')); + $this->addValue('CommentPostURL',array($this,'CommentPostURL')); + $this->addBlock('IfCommentAuthorEmail',array($this,'IfCommentAuthorEmail')); + $this->addValue('CommentHelp',array($this,'CommentHelp')); + + # Comment preview + $this->addBlock('IfCommentPreview',array($this,'IfCommentPreview')); + $this->addValue('CommentPreviewName',array($this,'CommentPreviewName')); + $this->addValue('CommentPreviewEmail',array($this,'CommentPreviewEmail')); + $this->addValue('CommentPreviewSite',array($this,'CommentPreviewSite')); + $this->addValue('CommentPreviewContent',array($this,'CommentPreviewContent')); + $this->addValue('CommentPreviewCheckRemember',array($this,'CommentPreviewCheckRemember')); + + # Entries + $this->addBlock('DateFooter',array($this,'DateFooter')); + $this->addBlock('DateHeader',array($this,'DateHeader')); + $this->addBlock('Entries',array($this,'Entries')); + $this->addBlock('EntriesFooter',array($this,'EntriesFooter')); + $this->addBlock('EntriesHeader',array($this,'EntriesHeader')); + $this->addValue('EntryExcerpt',array($this,'EntryExcerpt')); + $this->addValue('EntryAuthorCommonName',array($this,'EntryAuthorCommonName')); + $this->addValue('EntryAuthorDisplayName',array($this,'EntryAuthorDisplayName')); + $this->addValue('EntryAuthorEmail',array($this,'EntryAuthorEmail')); + $this->addValue('EntryAuthorID',array($this,'EntryAuthorID')); + $this->addValue('EntryAuthorLink',array($this,'EntryAuthorLink')); + $this->addValue('EntryAuthorURL',array($this,'EntryAuthorURL')); + $this->addValue('EntryBasename',array($this,'EntryBasename')); + $this->addValue('EntryCategory',array($this,'EntryCategory')); + $this->addBlock('EntryCategoriesBreadcrumb',array($this,'EntryCategoriesBreadcrumb')); + $this->addValue('EntryCategoryID',array($this,'EntryCategoryID')); + $this->addValue('EntryCategoryURL',array($this,'EntryCategoryURL')); + $this->addValue('EntryCategoryShortURL',array($this,'EntryCategoryShortURL')); + $this->addValue('EntryCommentCount',array($this,'EntryCommentCount')); + $this->addValue('EntryContent',array($this,'EntryContent')); + $this->addValue('EntryDate',array($this,'EntryDate')); + $this->addValue('EntryFeedID',array($this,'EntryFeedID')); + $this->addValue('EntryFirstImage',array($this,'EntryFirstImage')); + $this->addValue('EntryID',array($this,'EntryID')); + $this->addBlock('EntryIf',array($this,'EntryIf')); + $this->addValue('EntryIfFirst',array($this,'EntryIfFirst')); + $this->addValue('EntryIfOdd',array($this,'EntryIfOdd')); + $this->addValue('EntryIfSelected',array($this,'EntryIfSelected')); + $this->addValue('EntryLang',array($this,'EntryLang')); + $this->addBlock('EntryNext',array($this,'EntryNext')); + $this->addValue('EntryPingCount',array($this,'EntryPingCount')); + $this->addValue('EntryPingData',array($this,'EntryPingData')); + $this->addValue('EntryPingLink',array($this,'EntryPingLink')); + $this->addBlock('EntryPrevious',array($this,'EntryPrevious')); + $this->addValue('EntryTitle',array($this,'EntryTitle')); + $this->addValue('EntryTime',array($this,'EntryTime')); + $this->addValue('EntryURL',array($this,'EntryURL')); + + # Languages + $this->addBlock('Languages',array($this,'Languages')); + $this->addBlock('LanguagesHeader',array($this,'LanguagesHeader')); + $this->addBlock('LanguagesFooter',array($this,'LanguagesFooter')); + $this->addValue('LanguageCode',array($this,'LanguageCode')); + $this->addBlock('LanguageIfCurrent',array($this,'LanguageIfCurrent')); + $this->addValue('LanguageURL',array($this,'LanguageURL')); + + # Pagination + $this->addBlock('Pagination',array($this,'Pagination')); + $this->addValue('PaginationCounter',array($this,'PaginationCounter')); + $this->addValue('PaginationCurrent',array($this,'PaginationCurrent')); + $this->addBlock('PaginationIf',array($this,'PaginationIf')); + $this->addValue('PaginationURL',array($this,'PaginationURL')); + + # Trackbacks + $this->addValue('PingBlogName',array($this,'PingBlogName')); + $this->addValue('PingContent',array($this,'PingContent')); + $this->addValue('PingDate',array($this,'PingDate')); + $this->addValue('PingEntryTitle',array($this,'PingEntryTitle')); + $this->addValue('PingFeedID',array($this,'PingFeedID')); + $this->addValue('PingID',array($this,'PingID')); + $this->addValue('PingIfFirst',array($this,'PingIfFirst')); + $this->addValue('PingIfOdd',array($this,'PingIfOdd')); + $this->addValue('PingIP',array($this,'PingIP')); + $this->addValue('PingNoFollow',array($this,'PingNoFollow')); + $this->addValue('PingOrderNumber',array($this,'PingOrderNumber')); + $this->addValue('PingPostURL',array($this,'PingPostURL')); + $this->addBlock('Pings',array($this,'Pings')); + $this->addBlock('PingsFooter',array($this,'PingsFooter')); + $this->addBlock('PingsHeader',array($this,'PingsHeader')); + $this->addValue('PingTime',array($this,'PingTime')); + $this->addValue('PingTitle',array($this,'PingTitle')); + $this->addValue('PingAuthorURL',array($this,'PingAuthorURL')); + + # System + $this->addValue('SysBehavior',array($this,'SysBehavior')); + $this->addBlock('SysIf',array($this,'SysIf')); + $this->addBlock('SysIfCommentPublished',array($this,'SysIfCommentPublished')); + $this->addBlock('SysIfCommentPending',array($this,'SysIfCommentPending')); + $this->addBlock('SysIfFormError',array($this,'SysIfFormError')); + $this->addValue('SysFeedSubtitle',array($this,'SysFeedSubtitle')); + $this->addValue('SysFormError',array($this,'SysFormError')); + $this->addValue('SysPoweredBy',array($this,'SysPoweredBy')); + $this->addValue('SysSearchString',array($this,'SysSearchString')); + $this->addValue('SysSelfURI',array($this,'SysSelfURI')); + } + + public function getData($________) + { + # --BEHAVIOR-- tplBeforeData + if ($this->core->hasBehavior('tplBeforeData')) + { + self::$_r = $this->core->callBehavior('tplBeforeData',$this->core); + if (self::$_r) { + return self::$_r; + } + } + + parent::getData($________); + + # --BEHAVIOR-- tplAfterData + if ($this->core->hasBehavior('tplAfterData')) { + $this->core->callBehavior('tplAfterData',$this->core,self::$_r); + } + + return self::$_r; + } + + protected function compileFile($file) + { + $fc = file_get_contents($file); + + $this->compile_stack[] = $file; + + # Remove every PHP tags + if ($this->remove_php) + { + $fc = preg_replace('/<\?(?=php|=|\s).*?\?>/ms','',$fc); + } + + # Transform what could be considered as PHP short tags + $fc = preg_replace('/(<\?(?!php|=|\s))(.*?)(\?>)/ms', + '$2',$fc); + + # Remove template comments + $fc = preg_replace('/(^\s*)?/ms','',$fc); + + # Lexer part : split file into small pieces + # each array entry will be either a tag or plain text + $blocks = preg_split( + '#(]*>)|()|({{tpl:\w+[^}]*}})#msu',$fc,-1, + PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); + + # Next : build semantic tree from tokens. + $rootNode = new tplNode(); + $node = $rootNode; + $errors = array(); + foreach ($blocks as $id => $block) { + $isblock = preg_match('#|>)||{{tpl:(\w+)(\s(.*?))?}}#ms',$block,$match); + if ($isblock == 1) { + if (substr($match[0],1,1) == '/') { + // Closing tag, check if it matches current opened node + $tag = $match[3]; + if (($node instanceof tplNodeBlock) && $node->getTag() == $tag) { + $node->setClosing(); + $node = $node->getParent(); + } else { + // Closing tag does not match opening tag + // Search if it closes a parent tag + $search = $node; + while($search->getTag() != 'ROOT' && $search->getTag() != $tag) { + $search = $search->getParent(); + } + if ($search->getTag() == $tag) { + $errors[] = sprintf( + __('Did not find closing tag for block . Content has been ignored.'), + html::escapeHTML($node->getTag())); + $search->setClosing(); + $node = $search->getParent(); + } else { + $errors[]=sprintf( + __('Unexpected closing tag found.'), + $tag);; + } + } + } elseif (substr($match[0],0,1) == '{') { + // Value tag + $tag = $match[4]; + $str_attr = ''; + $attr = array(); + if (isset($match[6])) { + $str_attr = $match[6]; + $attr = $this->getAttrs($match[6]); + } + $node->addChild(new tplNodeValue($tag,$attr,$str_attr)); + } else { + // Opening tag, create new node and dive into it + $tag = $match[1]; + $newnode = new tplNodeBlock($tag,isset($match[2])?$this->getAttrs($match[2]):array()); + $node->addChild($newnode); + $node = $newnode; + } + } else { + // Simple text + $node->addChild(new tplNodeText($block)); + } + } + + if (($node instanceof tplNodeBlock) && !$node->isClosed()) { + $errors[] = sprintf( + __('Did not find closing tag for block . Content has been ignored.'), + html::escapeHTML($node->getTag())); + } + + $err = ""; + if (count($errors) > 0) { + $err = "\n\n\n"; + } + + return $rootNode->compile($this).$err; + } + + public function compileBlockNode($tag,$attr,$content) + { + $this->current_tag = $tag; + $attr = new ArrayObject($attr); + # --BEHAVIOR-- templateBeforeBlock + $res = $this->core->callBehavior('templateBeforeBlock',$this->core,$this->current_tag,$attr); + + # --BEHAVIOR-- templateInsideBlock + $this->core->callBehavior('templateInsideBlock',$this->core,$this->current_tag,$attr,array(&$content)); + + if (isset($this->blocks[$this->current_tag])) { + $res .= call_user_func($this->blocks[$this->current_tag],$attr,$content); + } elseif ($this->unknown_block_handler != null) { + $res .= call_user_func($this->unknown_block_handler,$this->current_tag,$attr,$content); + } + + # --BEHAVIOR-- templateAfterBlock + $res .= $this->core->callBehavior('templateAfterBlock',$this->core,$this->current_tag,$attr); + + return $res; + } + + public function compileValueNode($tag,$attr,$str_attr) + { + $this->current_tag = $tag; + + $attr = new ArrayObject($attr); + # --BEHAVIOR-- templateBeforeValue + $res = $this->core->callBehavior('templateBeforeValue',$this->core,$this->current_tag,$attr); + + if (isset($this->values[$this->current_tag])) { + $res .= call_user_func($this->values[$this->current_tag],$attr,ltrim($str_attr)); + } elseif ($this->unknown_value_handler != null) { + $res .= call_user_func($this->unknown_value_handler,$this->current_tag,$attr,$str_attr); + } + + # --BEHAVIOR-- templateAfterValue + $res .= $this->core->callBehavior('templateAfterValue',$this->core,$this->current_tag,$attr); + + return $res; + } + + public function setUnknownValueHandler($callback) + { + if (is_callable($callback)) { + $this->unknown_value_handler = $callback; + } + } + + public function setUnknownBlockHandler($callback) + { + if (is_callable($callback)) { + $this->unknown_block_handler = $callback; + } + } + + public function getFilters($attr) + { + $p[0] = '0'; # encode_xml + $p[1] = '0'; # remove_html + $p[2] = '0'; # cut_string + $p[3] = '0'; # lower_case + $p[4] = '0'; # upper_case + + $p[0] = (integer) (!empty($attr['encode_xml']) || !empty($attr['encode_html'])); + $p[1] = (integer) !empty($attr['remove_html']); + + if (!empty($attr['cut_string']) && (integer) $attr['cut_string'] > 0) { + $p[2] = (integer) $attr['cut_string']; + } + + $p[3] = (integer) !empty($attr['lower_case']); + $p[4] = (integer) !empty($attr['upper_case']); + + return "context::global_filter(%s,".implode(",",$p).",'".addslashes($this->current_tag)."')"; + } + + public static function getOperator($op) + { + switch (strtolower($op)) + { + case 'or': + case '||': + return '||'; + case 'and': + case '&&': + default: + return '&&'; + } + } + + public function getSortByStr($attr,$table = null) + { + $res = array(); + + $default_order = 'desc'; + + $default_alias = array( + 'post' => array( + 'title' => 'post_title', + 'selected' => 'post_selected', + 'author' => 'user_id', + 'date' => 'post_dt', + 'id' => 'post_id', + 'comment' => 'nb_comment', + 'trackback' => 'nb_trackback' + ), + 'comment' => array( + 'author' => 'comment_author', + 'date' => 'comment_dt', + 'id' => 'comment_id' + ) + ); + + $alias = new ArrayObject(); + + # --BEHAVIOR-- templateCustomSortByAlias + $this->core->callBehavior('templateCustomSortByAlias',$alias); + + $alias = $alias->getArrayCopy(); + + if (is_array($alias)) { + foreach ($alias as $k => $v) { + if (!is_array($v)) { + $alias[$k] = array(); + } + if (!is_array($v)) { + $default_alias[$k] = array(); + } + $default_alias[$k] = array_merge($default_alias[$k],$alias[$k]); + } + } + + if (!array_key_exists($table,$default_alias)) { + return implode(', ',$res); + } + + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $default_order = $attr['order']; + } + if (isset($attr['sortby'])) { + $sorts = explode(',',$attr['sortby']); + foreach ($sorts as $k => $sort) { + $order = $default_order; + if (preg_match('/([a-z]*)\s*\?(desc|asc)$/i',$sort,$matches)) { + $sort = $matches[1]; + $order = $matches[2]; + } + if (array_key_exists($sort,$default_alias[$table])) { + array_push($res,$default_alias[$table][$sort].' '.$order); + } + } + } + + if (count($res) === 0) { + array_push($res,$default_alias[$table]['date'].' '.$default_order); + } + + return implode(', ',$res); + } + + public function getAge($attr) + { + if (isset($attr['age']) && preg_match('/^(\-[0-9]+|last).*$/i',$attr['age'])) { + if (($ts = strtotime($attr['age'])) !== false) { + return dt::str('%Y-%m-%d %H:%m:%S',$ts); + } + } + return ''; + } + + /* TEMPLATE FUNCTIONS + ------------------------------------------------------- */ + + public function l10n($attr,$str_attr) + { + # Normalize content + $str_attr = preg_replace('/\s+/x',' ',$str_attr); + + return ""; + } + + public function LoopPosition($attr,$content) + { + $start = isset($attr['start']) ? (integer) $attr['start'] : '0'; + $length = isset($attr['length']) ? (integer) $attr['length'] : 'null'; + $even = isset($attr['even']) ? (integer) (boolean) $attr['even'] : 'null'; + + if ($start > 0) { + $start--; + } + + return + 'loopPosition('.$start.','.$length.','.$even.')) : ?>'. + $content. + ""; + } + + + /* Archives ------------------------------------------- */ + /*dtd + + + */ + public function Archives($attr,$content) + { + $p = "if (!isset(\$params)) \$params = array();\n"; + $p .= "\$params['type'] = 'month';\n"; + if (isset($attr['type'])) { + $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n"; + } + + if (isset($attr['category'])) { + $p .= "\$params['cat_url'] = '".addslashes($attr['category'])."';\n"; + } + + if (isset($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (isset($attr['post_lang'])) { + $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n"; + } + + if (empty($attr['no_context']) && !isset($attr['category'])) + { + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + } + + $order = 'desc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $p .= "\$params['order'] = '".$attr['order']."';\n "; + } + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "Archives","method" => "blog::getDates"), + $attr,$content); + $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'archives->fetch()) : ?>'.$content.'archives = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function ArchivesHeader($attr,$content) + { + return + "archives->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function ArchivesFooter($attr,$content) + { + return + "archives->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function ArchivesYearHeader($attr,$content) + { + return + "archives->yearHeader()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function ArchivesYearFooter($attr,$content) + { + return + "archives->yearFooter()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function ArchiveDate($attr) + { + $format = '%B %Y'; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $f = $this->getFilters($attr); + return 'archives->dt)").'; ?>'; + } + + /*dtd + + */ + public function ArchiveEntriesCount($attr) + { + $f = $this->getFilters($attr); + return 'archives->nb_post').'; ?>'; + } + + /*dtd + + + */ + public function ArchiveNext($attr,$content) + { + $p = "if (!isset(\$params)) \$params = array();\n"; + $p .= "\$params['type'] = 'month';\n"; + if (isset($attr['type'])) { + $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n"; + } + + if (isset($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (isset($attr['post_lang'])) { + $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n"; + } + + $p .= "\$params['next'] = \$_ctx->archives->dt;"; + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "ArchiveNext","method" => "blog::getDates"), + $attr, $content); + $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'archives->fetch()) : ?>'.$content.'archives = null; ?>'; + + return $res; + } + + /*dtd + + + */ + public function ArchivePrevious($attr,$content) + { + $p = 'if (!isset($params)) $params = array();'; + $p .= "\$params['type'] = 'month';\n"; + if (isset($attr['type'])) { + $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n"; + } + + if (isset($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (isset($attr['post_lang'])) { + $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n"; + } + + $p .= "\$params['previous'] = \$_ctx->archives->dt;"; + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "ArchivePrevious","method" => "blog::getDates"), + $attr, $content); + $res .= $p; + $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'archives->fetch()) : ?>'.$content.'archives = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function ArchiveURL($attr) + { + $f = $this->getFilters($attr); + return 'archives->url($core)').'; ?>'; + } + + + /* Blog ----------------------------------------------- */ + /*dtd + + */ + public function BlogArchiveURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getURLFor("archive")').'; ?>'; + } + + /*dtd + + */ + public function BlogCopyrightNotice($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->system->copyright_notice').'; ?>'; + } + + /*dtd + + */ + public function BlogDescription($attr) + { + $f = $this->getFilters($attr); + return 'blog->desc').'; ?>'; + } + + /*dtd + + */ + public function BlogEditor($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->system->editor').'; ?>'; + } + + /*dtd + + */ + public function BlogFeedID($attr) + { + $f = $this->getFilters($attr); + return 'blog->uid').'; ?>'; + } + + /*dtd + + + */ + public function BlogFeedURL($attr) + { + $type = !empty($attr['type']) ? $attr['type'] : 'atom'; + + if (!preg_match('#^(rss2|atom)$#',$type)) { + $type = 'atom'; + } + + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getURLFor("feed","'.$type.'")').'; ?>'; + } + + /*dtd + + */ + public function BlogName($attr) + { + $f = $this->getFilters($attr); + return 'blog->name').'; ?>'; + } + + /*dtd + + */ + public function BlogLanguage($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->system->lang').'; ?>'; + } + + /*dtd + + */ + public function BlogThemeURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->system->themes_url."/".$core->blog->settings->system->theme').'; ?>'; + } + + /*dtd + + */ + public function BlogPublicURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->settings->system->public_url').'; ?>'; + } + + /*dtd + + + */ + public function BlogUpdateDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } else { + $format = '%Y-%m-%d %H:%M:%S'; + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'blog->upddt,\$core->blog->settings->system->blog_timezone)").'; ?>'; + } elseif ($iso8601) { + return 'blog->upddt,\$core->blog->settings->system->blog_timezone)").'; ?>'; + } else { + return 'blog->upddt)").'; ?>'; + } + } + + /*dtd + + */ + public function BlogID($attr) + { + $f = $this->getFilters($attr); + return 'blog->id').'; ?>'; + } + + /*dtd + + */ + public function BlogRSDURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getURLFor(\'rsd\')').'; ?>'; + } + + /*dtd + + */ + public function BlogURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url').'; ?>'; + } + + /*dtd + + */ + public function BlogQmarkURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->getQmarkURL()').'; ?>'; + } + + /*dtd + + + */ + public function BlogMetaRobots($attr) + { + $robots = isset($attr['robots']) ? addslashes($attr['robots']) : ''; + return "blog->settings->system->robots_policy,'".$robots."'); ?>"; + } + + /* Categories ----------------------------------------- */ + + /*dtd + + */ + public function Categories($attr,$content) + { + $p = "if (!isset(\$params)) \$params = array();\n"; + + if (isset($attr['url'])) { + $p .= "\$params['cat_url'] = '".addslashes($attr['url'])."';\n"; + } + + if (!empty($attr['post_type'])) { + $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n"; + } + + if (!empty($attr['level'])) { + $p .= "\$params['level'] = ".(integer) $attr['level'].";\n"; + } + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "Categories","method" => "blog::getCategories"), + $attr,$content); + $res .= '$_ctx->categories = $core->blog->getCategories($params);'."\n"; + $res .= "?>\n"; + $res .= 'categories->fetch()) : ?>'.$content.'categories = null; unset($params); ?>'; + + return $res; + } + + /*dtd + + */ + public function CategoriesHeader($attr,$content) + { + return + "categories->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CategoriesFooter($attr,$content) + { + return + "categories->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function CategoryIf($attr,$content) + { + $if = new ArrayObject(); + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['url'])) { + $url = addslashes(trim($attr['url'])); + if (substr($url,0,1) == '!') { + $url = substr($url,1); + $if[] = '($_ctx->categories->cat_url != "'.$url.'")'; + } else { + $if[] = '($_ctx->categories->cat_url == "'.$url.'")'; + } + } + + if (isset($attr['has_entries'])) { + $sign = (boolean) $attr['has_entries'] ? '>' : '=='; + $if[] = '$_ctx->categories->nb_post '.$sign.' 0'; + } + + if (isset($attr['has_description'])) { + $sign = (boolean) $attr['has_description'] ? '!=' : '=='; + $if[] = '$_ctx->categories->cat_desc '.$sign.' ""'; + } + + $this->core->callBehavior('tplIfConditions','CategoryIf',$attr,$content,$if); + + if (count($if) != 0) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + */ + public function CategoryFirstChildren($attr,$content) + { + return + "categories = $core->blog->getCategoryFirstChildren($_ctx->categories->cat_id);'."\n". + 'while ($_ctx->categories->fetch()) : ?>'.$content.'categories = null; ?>'; + } + + /*dtd + + */ + public function CategoryParents($attr,$content) + { + return + "categories = $core->blog->getCategoryParents($_ctx->categories->cat_id);'."\n". + 'while ($_ctx->categories->fetch()) : ?>'.$content.'categories = null; ?>'; + } + + /*dtd + + + */ + public function CategoryFeedURL($attr) + { + $type = !empty($attr['type']) ? $attr['type'] : 'atom'; + + if (!preg_match('#^(rss2|atom)$#',$type)) { + $type = 'atom'; + } + + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getURLFor("feed","category/".'. + '$_ctx->categories->cat_url."/'.$type.'")').'; ?>'; + } + + /*dtd + + */ + public function CategoryURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getURLFor("category",'. + '$_ctx->categories->cat_url)').'; ?>'; + } + + /*dtd + + */ + public function CategoryShortURL($attr) + { + $f = $this->getFilters($attr); + return 'categories->cat_url').'; ?>'; + } + + /*dtd + + */ + public function CategoryDescription($attr) + { + $f = $this->getFilters($attr); + return 'categories->cat_desc').'; ?>'; + } + + /*dtd + + */ + public function CategoryTitle($attr) + { + $f = $this->getFilters($attr); + return 'categories->cat_title').'; ?>'; + } + + /* Entries -------------------------------------------- */ + /*dtd + + + */ + public function Entries($attr,$content) + { + $lastn = -1; + if (isset($attr['lastn'])) { + $lastn = abs((integer) $attr['lastn'])+0; + } + + $p = 'if (!isset($_page_number)) { $_page_number = 1; }'."\n"; + + if ($lastn != 0) { + if ($lastn > 0) { + $p .= "\$params['limit'] = ".$lastn.";\n"; + } else { + $p .= "\$params['limit'] = \$_ctx->nb_entry_per_page;\n"; + } + + if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == "0") { + $p .= "\$params['limit'] = array(((\$_page_number-1)*\$params['limit']),\$params['limit']);\n"; + } else { + $p .= "\$params['limit'] = array(0, \$params['limit']);\n"; + } + } + + if (isset($attr['author'])) { + $p .= "\$params['user_id'] = '".addslashes($attr['author'])."';\n"; + } + + if (isset($attr['category'])) { + $p .= "\$params['cat_url'] = '".addslashes($attr['category'])."';\n"; + $p .= "context::categoryPostParam(\$params);\n"; + } + + if (isset($attr['no_category']) && $attr['no_category']) { + $p .= "@\$params['sql'] .= ' AND P.cat_id IS NULL ';\n"; + $p .= "unset(\$params['cat_url']);\n"; + } + + if (!empty($attr['type'])) { + $p .= "\$params['post_type'] = preg_split('/\s*,\s*/','".addslashes($attr['type'])."',-1,PREG_SPLIT_NO_EMPTY);\n"; + } + + if (!empty($attr['url'])) { + $p .= "\$params['post_url'] = '".addslashes($attr['url'])."';\n"; + } + + if (empty($attr['no_context'])) + { + if (!isset($attr['author'])) + { + $p .= + 'if ($_ctx->exists("users")) { '. + "\$params['user_id'] = \$_ctx->users->user_id; ". + "}\n"; + } + + if (!isset($attr['category']) && (!isset($attr['no_category']) || !$attr['no_category'])) + { + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + } + + $p .= + 'if ($_ctx->exists("archives")) { '. + "\$params['post_year'] = \$_ctx->archives->year(); ". + "\$params['post_month'] = \$_ctx->archives->month(); "; + if (!isset($attr['lastn'])) { + $p .= "unset(\$params['limit']); "; + } + $p .= + "}\n"; + + $p .= + 'if ($_ctx->exists("langs")) { '. + "\$params['post_lang'] = \$_ctx->langs->post_lang; ". + "}\n"; + + $p .= + 'if (isset($_search)) { '. + "\$params['search'] = \$_search; ". + "}\n"; + } + + $p .= "\$params['order'] = '".$this->getSortByStr($attr,'post')."';\n"; + + if (isset($attr['no_content']) && $attr['no_content']) { + $p .= "\$params['no_content'] = true;\n"; + } + + if (isset($attr['selected'])) { + $p .= "\$params['post_selected'] = ".(integer) (boolean) $attr['selected'].";"; + } + + if (isset($attr['age'])) { + $age = $this->getAge($attr); + $p .= !empty($age) ? "@\$params['sql'] .= ' AND P.post_dt > \'".$age."\'';\n" : ''; + } + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "Entries","method" => "blog::getPosts"), + $attr,$content); + $res .= '$_ctx->post_params = $params;'."\n"; + $res .= '$_ctx->posts = $core->blog->getPosts($params); unset($params);'."\n"; + $res .= "?>\n"; + $res .= + 'posts->fetch()) : ?>'.$content.'posts = null; $_ctx->post_params = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function DateHeader($attr,$content) + { + return + "posts->firstPostOfDay()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function DateFooter($attr,$content) + { + return + "posts->lastPostOfDay()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function EntryIf($attr,$content) + { + $if = new ArrayObject(); + $extended = null; + $hascategory = null; + + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['type'])) { + $type = trim($attr['type']); + $type = !empty($type)?$type:'post'; + $if[] = '$_ctx->posts->post_type == "'.addslashes($type).'"'; + } + + if (isset($attr['url'])) { + $url = trim($attr['url']); + if (substr($url,0,1) == '!') { + $url = substr($url,1); + $if[] = '$_ctx->posts->post_url != "'.addslashes($url).'"'; + } else { + $if[] = '$_ctx->posts->post_url == "'.addslashes($url).'"'; + } + } + + if (isset($attr['category'])) { + $category = addslashes(trim($attr['category'])); + if (substr($category,0,1) == '!') { + $category = substr($category,1); + $if[] = '($_ctx->posts->cat_url != "'.$category.'")'; + } else { + $if[] = '($_ctx->posts->cat_url == "'.$category.'")'; + } + } + + if (isset($attr['first'])) { + $sign = (boolean) $attr['first'] ? '=' : '!'; + $if[] = '$_ctx->posts->index() '.$sign.'= 0'; + } + + if (isset($attr['odd'])) { + $sign = (boolean) $attr['odd'] ? '=' : '!'; + $if[] = '($_ctx->posts->index()+1)%2 '.$sign.'= 1'; + } + + if (isset($attr['extended'])) { + $sign = (boolean) $attr['extended'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->isExtended()'; + } + + if (isset($attr['selected'])) { + $sign = (boolean) $attr['selected'] ? '' : '!'; + $if[] = $sign.'(boolean)$_ctx->posts->post_selected'; + } + + if (isset($attr['has_category'])) { + $sign = (boolean) $attr['has_category'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->cat_id'; + } + + if (isset($attr['comments_active'])) { + $sign = (boolean) $attr['comments_active'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->commentsActive()'; + } + + if (isset($attr['pings_active'])) { + $sign = (boolean) $attr['pings_active'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->trackbacksActive()'; + } + + if (isset($attr['has_comment'])) { + $sign = (boolean) $attr['has_comment'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->hasComments()'; + } + + if (isset($attr['has_ping'])) { + $sign = (boolean) $attr['has_ping'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->hasTrackbacks()'; + } + + if (isset($attr['show_comments'])) { + if ((boolean) $attr['show_comments']) { + $if[] = '($_ctx->posts->hasComments() || $_ctx->posts->commentsActive())'; + } else { + $if[] = '(!$_ctx->posts->hasComments() && !$_ctx->posts->commentsActive())'; + } + } + + if (isset($attr['show_pings'])) { + if ((boolean) $attr['show_pings']) { + $if[] = '($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())'; + } else { + $if[] = '(!$_ctx->posts->hasTrackbacks() && !$_ctx->posts->trackbacksActive())'; + } + } + + $this->core->callBehavior('tplIfConditions','EntryIf',$attr,$content,$if); + + if (count($if) != 0) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + + */ + public function EntryIfFirst($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'first'; + $ret = html::escapeHTML($ret); + + return + 'posts->index() == 0) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function EntryIfOdd($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'odd'; + $ret = html::escapeHTML($ret); + + return + 'posts->index()+1)%2 == 1) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function EntryIfSelected($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'selected'; + $ret = html::escapeHTML($ret); + + return + 'posts->post_selected) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function EntryContent($attr) + { + $urls = '0'; + if (!empty($attr['absolute_urls'])) { + $urls = '1'; + } + + $f = $this->getFilters($attr); + + if (!empty($attr['full'])) { + return 'posts->getExcerpt('.$urls.')." ".$_ctx->posts->getContent('.$urls.')').'; ?>'; + } else { + return 'posts->getContent('.$urls.')').'; ?>'; + } + } + + /*dtd + + + */ + public function EntryExcerpt($attr) + { + $urls = '0'; + if (!empty($attr['absolute_urls'])) { + $urls = '1'; + } + + $f = $this->getFilters($attr); + return 'posts->getExcerpt('.$urls.')').'; ?>'; + } + + + /*dtd + + */ + public function EntryAuthorCommonName($attr) + { + $f = $this->getFilters($attr); + return 'posts->getAuthorCN()').'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorDisplayName($attr) + { + $f = $this->getFilters($attr); + return 'posts->user_displayname').'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorID($attr) + { + $f = $this->getFilters($attr); + return 'posts->user_id').'; ?>'; + } + + /*dtd + + + */ + public function EntryAuthorEmail($attr) + { + $p = 'true'; + if (isset($attr['spam_protected']) && !$attr['spam_protected']) { + $p = 'false'; + } + + $f = $this->getFilters($attr); + return 'posts->getAuthorEmail(".$p.")").'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorLink($attr) + { + $f = $this->getFilters($attr); + return 'posts->getAuthorLink()').'; ?>'; + } + + /*dtd + + */ + public function EntryAuthorURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->user_url').'; ?>'; + } + + /*dtd + + */ + public function EntryBasename($attr) + { + $f = $this->getFilters($attr); + return 'posts->post_url').'; ?>'; + } + + /*dtd + + */ + public function EntryCategory($attr) + { + $f = $this->getFilters($attr); + return 'posts->cat_title').'; ?>'; + } + + /*dtd + + */ + public function EntryCategoriesBreadcrumb($attr,$content) + { + return + "categories = $core->blog->getCategoryParents($_ctx->posts->cat_id);'."\n". + 'while ($_ctx->categories->fetch()) : ?>'.$content.'categories = null; ?>'; + } + + /*dtd + + */ + public function EntryCategoryID($attr) + { + $f = $this->getFilters($attr); + return 'posts->cat_id').'; ?>'; + } + + /*dtd + + */ + public function EntryCategoryURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->getCategoryURL()').'; ?>'; + } + + /*dtd + + */ + public function EntryCategoryShortURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->cat_url').'; ?>'; + } + + + /*dtd + + */ + public function EntryFeedID($attr) + { + $f = $this->getFilters($attr); + return 'posts->getFeedID()').'; ?>'; + } + + /*dtd + + + */ + public function EntryFirstImage($attr) + { + $size = !empty($attr['size']) ? $attr['size'] : ''; + $class = !empty($attr['class']) ? $attr['class'] : ''; + $with_category = !empty($attr['with_category']) ? 'true' : 'false'; + + return ""; + } + + /*dtd + + */ + public function EntryID($attr) + { + $f = $this->getFilters($attr); + return 'posts->post_id').'; ?>'; + } + + /*dtd + + */ + public function EntryLang($attr) + { + $f = $this->getFilters($attr); + return + 'posts->post_lang) { '. + 'echo '.sprintf($f,'$_ctx->posts->post_lang').'; '. + '} else {'. + 'echo '.sprintf($f,'$core->blog->settings->system->lang').'; '. + '} ?>'; + } + + /*dtd + + + */ + public function EntryNext($attr,$content) + { + $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0'; + $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0'; + + return + 'blog->getNextPost($_ctx->posts,1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n". + ''. + + 'posts = $next_post; unset($next_post);'."\n". + 'while ($_ctx->posts->fetch()) : ?>'. + $content. + 'posts = null; ?>'. + "\n"; + } + + /*dtd + + + */ + public function EntryPrevious($attr,$content) + { + $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0'; + $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0'; + + return + 'blog->getNextPost($_ctx->posts,-1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n". + ''. + + 'posts = $prev_post; unset($prev_post);'."\n". + 'while ($_ctx->posts->fetch()) : ?>'. + $content. + 'posts = null; ?>'. + "\n"; + } + + /*dtd + + */ + public function EntryTitle($attr) + { + $f = $this->getFilters($attr); + return 'posts->post_title').'; ?>'; + } + + /*dtd + + */ + public function EntryURL($attr) + { + $f = $this->getFilters($attr); + return 'posts->getURL()').'; ?>'; + } + + /*dtd + + + */ + public function EntryDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + $type = (!empty($attr['creadt']) ? 'creadt' : ''); + $type = (!empty($attr['upddt']) ? 'upddt' : $type); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'posts->getRFC822Date('".$type."')").'; ?>'; + } elseif ($iso8601) { + return 'posts->getISO8601Date('".$type."')").'; ?>'; + } else { + return 'posts->getDate('".$format."','".$type."')").'; ?>'; + } + } + + /*dtd + + + */ + public function EntryTime($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $type = (!empty($attr['creadt']) ? 'creadt' : ''); + $type = (!empty($attr['upddt']) ? 'upddt' : $type); + + $f = $this->getFilters($attr); + return 'posts->getTime('".$format."','".$type."')").'; ?>'; + } + + /*dtd + + */ + public function EntriesHeader($attr,$content) + { + return + "posts->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function EntriesFooter($attr,$content) + { + return + "posts->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public function EntryCommentCount($attr) + { + $none = 'no comment'; + $one = 'one comment'; + $more = '%d comments'; + + if (isset($attr['none'])) { + $none = addslashes($attr['none']); + } + if (isset($attr['one'])) { + $one = addslashes($attr['one']); + } + if (isset($attr['more'])) { + $more = addslashes($attr['more']); + } + + if (empty($attr['count_all'])) { + $operation = '$_ctx->posts->nb_comment'; + } else { + $operation = '($_ctx->posts->nb_comment + $_ctx->posts->nb_trackback)'; + } + + return + ""; + } + + /*dtd + + + */ + public function EntryPingCount($attr) + { + $none = 'no trackback'; + $one = 'one trackback'; + $more = '%d trackbacks'; + + if (isset($attr['none'])) { + $none = addslashes($attr['none']); + } + if (isset($attr['one'])) { + $one = addslashes($attr['one']); + } + if (isset($attr['more'])) { + $more = addslashes($attr['more']); + } + + return + "posts->nb_trackback == 0) {\n". + " printf(__('".$none."'),(integer) \$_ctx->posts->nb_trackback);\n". + "} elseif (\$_ctx->posts->nb_trackback == 1) {\n". + " printf(__('".$one."'),(integer) \$_ctx->posts->nb_trackback);\n". + "} else {\n". + " printf(__('".$more."'),(integer) \$_ctx->posts->nb_trackback);\n". + "} ?>"; + } + + /*dtd + + */ + public function EntryPingData($attr) + { + return "posts->trackbacksActive()) { echo \$_ctx->posts->getTrackbackData(); } ?>\n"; + } + + /*dtd + + */ + public function EntryPingLink($attr) + { + return "posts->trackbacksActive()) { echo \$_ctx->posts->getTrackbackLink(); } ?>\n"; + } + + /* Languages -------------------------------------- */ + /*dtd + + + */ + public function Languages($attr,$content) + { + $p = "if (!isset(\$params)) \$params = array();\n"; + + if (isset($attr['lang'])) { + $p = "\$params['lang'] = '".addslashes($attr['lang'])."';\n"; + } + + $order = 'desc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $p .= "\$params['order'] = '".$attr['order']."';\n "; + } + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "Languages","method" => "blog::getLangs"), + $attr,$content); + $res .= '$_ctx->langs = $core->blog->getLangs($params); unset($params);'."\n"; + $res .= "?>\n"; + + $res .= + 'langs->count() > 1) : '. + 'while ($_ctx->langs->fetch()) : ?>'.$content. + 'langs = null; endif; ?>'; + + return $res; + } + + /*dtd + + */ + public function LanguagesHeader($attr,$content) + { + return + "langs->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function LanguagesFooter($attr,$content) + { + return + "langs->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function LanguageCode($attr) + { + $f = $this->getFilters($attr); + return 'langs->post_lang').'; ?>'; + } + + /*dtd + + */ + public function LanguageIfCurrent($attr,$content) + { + return + "cur_lang == \$_ctx->langs->post_lang) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function LanguageURL($attr) + { + $f = $this->getFilters($attr); + return 'blog->url.$core->url->getURLFor("lang",'. + '$_ctx->langs->post_lang)').'; ?>'; + } + + /* Pagination ------------------------------------- */ + /*dtd + + + */ + public function Pagination($attr,$content) + { + $p = "post_params;'."\n"; + $p .= $this->core->callBehavior("templatePrepareParams", + array("tag" => "Pagination","method" => "blog::getPosts"), + $attr,$content); + $p .= '$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);'."\n"; + $p .= "?>\n"; + + if (isset($attr['no_context']) && $attr['no_context']) { + return $p.$content; + } + + return + $p. + 'pagination->f(0) > $_ctx->posts->count()) : ?>'. + $content. + ''; + } + + /*dtd + + */ + public function PaginationCounter($attr) + { + $f = $this->getFilters($attr); + return ''; + } + + /*dtd + + */ + public function PaginationCurrent($attr) + { + $offset = 0; + if (isset($attr['offset'])) { + $offset = (integer) $attr['offset']; + } + + $f = $this->getFilters($attr); + return ''; + } + + /*dtd + + + */ + public function PaginationIf($attr,$content) + { + $if = array(); + + if (isset($attr['start'])) { + $sign = (boolean) $attr['start'] ? '' : '!'; + $if[] = $sign.'context::PaginationStart()'; + } + + if (isset($attr['end'])) { + $sign = (boolean) $attr['end'] ? '' : '!'; + $if[] = $sign.'context::PaginationEnd()'; + } + + $this->core->callBehavior('tplIfConditions','PaginationIf',$attr,$content,$if); + + if (count($if) != 0) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + + */ + public function PaginationURL($attr) + { + $offset = 0; + if (isset($attr['offset'])) { + $offset = (integer) $attr['offset']; + } + + $f = $this->getFilters($attr); + return ''; + } + + /* Comments --------------------------------------- */ + /*dtd + + + */ + public function Comments($attr,$content) + { + $p = ""; + if (empty($attr['with_pings'])) { + $p .= "\$params['comment_trackback'] = false;\n"; + } + + $lastn = 0; + if (isset($attr['lastn'])) { + $lastn = abs((integer) $attr['lastn'])+0; + } + + if ($lastn > 0) { + $p .= "\$params['limit'] = ".$lastn.";\n"; + } else { + $p .= "if (\$_ctx->nb_comment_per_page !== null) { \$params['limit'] = \$_ctx->nb_comment_per_page; }\n"; + } + + if (empty($attr['no_context'])) + { + $p .= + "if (\$_ctx->posts !== null) { ". + "\$params['post_id'] = \$_ctx->posts->post_id; ". + "\$core->blog->withoutPassword(false);\n". + "}\n"; + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + + $p .= + 'if ($_ctx->exists("langs")) { '. + "\$params['sql'] = \"AND P.post_lang = '\".\$core->blog->con->escape(\$_ctx->langs->post_lang).\"' \"; ". + "}\n"; + } + + if (!isset($attr['order'])) { + $attr['order'] = 'asc'; + } + + $p .= "\$params['order'] = '".$this->getSortByStr($attr,'comment')."';\n"; + + if (isset($attr['no_content']) && $attr['no_content']) { + $p .= "\$params['no_content'] = true;\n"; + } + + if (isset($attr['age'])) { + $age = $this->getAge($attr); + $p .= !empty($age) ? "@\$params['sql'] .= ' AND P.post_dt > \'".$age."\'';\n" : ''; + } + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "Comments","method" => "blog::getComments"), + $attr,$content); + $res .= $p; + $res .= '$_ctx->comments = $core->blog->getComments($params); unset($params);'."\n"; + $res .= "if (\$_ctx->posts !== null) { \$core->blog->withoutPassword(true);}\n"; + + if (!empty($attr['with_pings'])) { + $res .= '$_ctx->pings = $_ctx->comments;'."\n"; + } + + $res .= "?>\n"; + + $res .= + 'comments->fetch()) : ?>'.$content.'comments = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function CommentAuthor($attr) + { + $f = $this->getFilters($attr); + return 'comments->comment_author").'; ?>'; + } + + /*dtd + + */ + public function CommentAuthorDomain($attr) + { + return 'comments->comment_site); ?>'; + } + + /*dtd + + */ + public function CommentAuthorLink($attr) + { + $f = $this->getFilters($attr); + return 'comments->getAuthorLink()').'; ?>'; + } + + /*dtd + + */ + public function CommentAuthorMailMD5($attr) + { + return 'comments->comment_email) ; ?>'; + } + + /*dtd + + */ + public function CommentAuthorURL($attr) + { + $f = $this->getFilters($attr); + return 'comments->getAuthorURL()').'; ?>'; + } + + /*dtd + + + */ + public function CommentContent($attr) + { + $urls = '0'; + if (!empty($attr['absolute_urls'])) { + $urls = '1'; + } + + $f = $this->getFilters($attr); + return 'comments->getContent('.$urls.')').'; ?>'; + } + + /*dtd + + + */ + public function CommentDate($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + $type = (!empty($attr['upddt']) ? 'upddt' : ''); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'comments->getRFC822Date('".$type."')").'; ?>'; + } elseif ($iso8601) { + return 'comments->getISO8601Date('".$type."')").'; ?>'; + } else { + return 'comments->getDate('".$format."','".$type."')").'; ?>'; + } + } + + /*dtd + + + */ + public function CommentTime($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + $type = (!empty($attr['upddt']) ? 'upddt' : ''); + + $f = $this->getFilters($attr); + return 'comments->getTime('".$format."','".$type."')").'; ?>'; + } + + /*dtd + + + */ + public function CommentEmail($attr) + { + $p = 'true'; + if (isset($attr['spam_protected']) && !$attr['spam_protected']) { + $p = 'false'; + } + + $f = $this->getFilters($attr); + return 'comments->getEmail(".$p.")").'; ?>'; + } + + /*dtd + + */ + public function CommentEntryTitle($attr) + { + $f = $this->getFilters($attr); + return 'comments->post_title').'; ?>'; + } + + /*dtd + + */ + public function CommentFeedID($attr) + { + $f = $this->getFilters($attr); + return 'comments->getFeedID()').'; ?>'; + } + + /*dtd + + */ + public function CommentID($attr) + { + return 'comments->comment_id; ?>'; + } + + /*dtd + + + */ + public function CommentIf($attr,$content) + { + $if = array(); + $is_ping = null; + + if (isset($attr['is_ping'])) { + $sign = (boolean) $attr['is_ping'] ? '' : '!'; + $if[] = $sign.'$_ctx->comments->comment_trackback'; + } + + $this->core->callBehavior('tplIfConditions','CommentIf',$attr,$content,$if); + + if (count($if) != 0) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + + */ + public function CommentIfFirst($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'first'; + $ret = html::escapeHTML($ret); + + return + 'comments->index() == 0) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function CommentIfMe($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'me'; + $ret = html::escapeHTML($ret); + + return + 'comments->isMe()) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function CommentIfOdd($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'odd'; + $ret = html::escapeHTML($ret); + + return + 'comments->index()+1)%2) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + */ + public function CommentIP($attr) + { + return 'comments->comment_ip; ?>'; + } + + /*dtd + + */ + public function CommentOrderNumber($attr) + { + return 'comments->index()+1; ?>'; + } + + /*dtd + + */ + public function CommentsFooter($attr,$content) + { + return + "comments->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CommentsHeader($attr,$content) + { + return + "comments->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CommentPostURL($attr) + { + $f = $this->getFilters($attr); + return 'comments->getPostURL()').'; ?>'; + } + + /*dtd + + */ + public function IfCommentAuthorEmail($attr,$content) + { + return + "comments->comment_email) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function CommentHelp($attr,$content) + { + return + "blog->settings->system->wiki_comments) {\n". + " echo __('Comments can be formatted using a simple wiki syntax.');\n". + "} else {\n". + " echo __('HTML code is displayed as text and web addresses are automatically converted.');\n". + "} ?>"; + } + + /* Comment preview -------------------------------- */ + /*dtd + + */ + public function IfCommentPreview($attr,$content) + { + return + 'comment_preview !== null && $_ctx->comment_preview["preview"]) : ?>'. + $content. + ''; + } + + /*dtd + + */ + public function CommentPreviewName($attr) + { + $f = $this->getFilters($attr); + return 'comment_preview["name"]').'; ?>'; + } + + /*dtd + + */ + public function CommentPreviewEmail($attr) + { + $f = $this->getFilters($attr); + return 'comment_preview["mail"]').'; ?>'; + } + + /*dtd + + */ + public function CommentPreviewSite($attr) + { + $f = $this->getFilters($attr); + return 'comment_preview["site"]').'; ?>'; + } + + /*dtd + + + */ + public function CommentPreviewContent($attr) + { + $f = $this->getFilters($attr); + + if (!empty($attr['raw'])) { + $co = '$_ctx->comment_preview["rawcontent"]'; + } else { + $co = '$_ctx->comment_preview["content"]'; + } + + return ''; + } + + /*dtd + + */ + public function CommentPreviewCheckRemember($attr) + { + return + "comment_preview['remember']) { echo ' checked=\"checked\"'; } ?>"; + } + + /* Trackbacks ------------------------------------- */ + /*dtd + + */ + public function PingBlogName($attr) + { + $f = $this->getFilters($attr); + return 'pings->comment_author').'; ?>'; + } + + /*dtd + + */ + public function PingContent($attr) + { + $f = $this->getFilters($attr); + return 'pings->getTrackbackContent()').'; ?>'; + } + + /*dtd + + + */ + public function PingDate($attr,$type='') + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + + $iso8601 = !empty($attr['iso8601']); + $rfc822 = !empty($attr['rfc822']); + $type = (!empty($attr['upddt']) ? 'upddt' : ''); + + $f = $this->getFilters($attr); + + if ($rfc822) { + return 'pings->getRFC822Date('".$type."')").'; ?>'; + } elseif ($iso8601) { + return 'pings->getISO8601Date('".$type."')").'; ?>'; + } else { + return 'pings->getDate('".$format."','".$type."')").'; ?>'; + } + } + + /*dtd + + + */ + public function PingTime($attr) + { + $format = ''; + if (!empty($attr['format'])) { + $format = addslashes($attr['format']); + } + $type = (!empty($attr['upddt']) ? 'upddt' : ''); + + $f = $this->getFilters($attr); + return 'pings->getTime('".$format."','".$type."')").'; ?>'; + } + + /*dtd + + */ + public function PingEntryTitle($attr) + { + $f = $this->getFilters($attr); + return 'pings->post_title').'; ?>'; + } + + /*dtd + + */ + public function PingFeedID($attr) + { + $f = $this->getFilters($attr); + return 'pings->getFeedID()').'; ?>'; + } + + /*dtd + + */ + public function PingID($attr) + { + return 'pings->comment_id; ?>'; + } + + /*dtd + + + */ + public function PingIfFirst($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'first'; + $ret = html::escapeHTML($ret); + + return + 'pings->index() == 0) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + + */ + public function PingIfOdd($attr) + { + $ret = isset($attr['return']) ? $attr['return'] : 'odd'; + $ret = html::escapeHTML($ret); + + return + 'pings->index()+1)%2) { '. + "echo '".addslashes($ret)."'; } ?>"; + } + + /*dtd + + */ + public function PingIP($attr) + { + return 'pings->comment_ip; ?>'; + } + + /*dtd + + */ + public function PingNoFollow($attr) + { + return + 'blog->settings->system->comments_nofollow) { '. + 'echo \' rel="nofollow"\';'. + '} ?>'; + } + + /*dtd + + */ + public function PingOrderNumber($attr) + { + return 'pings->index()+1; ?>'; + } + + /*dtd + + */ + public function PingPostURL($attr) + { + $f = $this->getFilters($attr); + return 'pings->getPostURL()').'; ?>'; + } + + /*dtd + + + */ + public function Pings($attr,$content) + { + $p = + "if (\$_ctx->posts !== null) { ". + "\$params['post_id'] = \$_ctx->posts->post_id; ". + "\$core->blog->withoutPassword(false);\n". + "}\n"; + + $p .= "\$params['comment_trackback'] = true;\n"; + + $lastn = 0; + if (isset($attr['lastn'])) { + $lastn = abs((integer) $attr['lastn'])+0; + } + + if ($lastn > 0) { + $p .= "\$params['limit'] = ".$lastn.";\n"; + } else { + $p .= "if (\$_ctx->nb_comment_per_page !== null) { \$params['limit'] = \$_ctx->nb_comment_per_page; }\n"; + } + + if (empty($attr['no_context'])) + { + $p .= + 'if ($_ctx->exists("categories")) { '. + "\$params['cat_id'] = \$_ctx->categories->cat_id; ". + "}\n"; + + $p .= + 'if ($_ctx->exists("langs")) { '. + "\$params['sql'] = \"AND P.post_lang = '\".\$core->blog->con->escape(\$_ctx->langs->post_lang).\"' \"; ". + "}\n"; + } + + $order = 'asc'; + if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) { + $order = $attr['order']; + } + + $p .= "\$params['order'] = 'comment_dt ".$order."';\n"; + + if (isset($attr['no_content']) && $attr['no_content']) { + $p .= "\$params['no_content'] = true;\n"; + } + + $res = "core->callBehavior("templatePrepareParams", + array("tag" => "Pings","method" => "blog::getComments"), + $attr,$content); + $res .= '$_ctx->pings = $core->blog->getComments($params); unset($params);'."\n"; + $res .= "if (\$_ctx->posts !== null) { \$core->blog->withoutPassword(true);}\n"; + $res .= "?>\n"; + + $res .= + 'pings->fetch()) : ?>'.$content.'pings = null; ?>'; + + return $res; + } + + /*dtd + + */ + public function PingsFooter($attr,$content) + { + return + "pings->isEnd()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function PingsHeader($attr,$content) + { + return + "pings->isStart()) : ?>". + $content. + ""; + } + + /*dtd + + */ + public function PingTitle($attr) + { + $f = $this->getFilters($attr); + return 'pings->getTrackbackTitle()').'; ?>'; + } + + /*dtd + + */ + public function PingAuthorURL($attr) + { + $f = $this->getFilters($attr); + return 'pings->getAuthorURL()').'; ?>'; + } + + # System + /*dtd + + + */ + public function SysBehavior($attr,$raw) + { + if (!isset($attr['behavior'])) { + return; + } + + $b = addslashes($attr['behavior']); + return + 'hasBehavior(\''.$b.'\')) { '. + '$core->callBehavior(\''.$b.'\',$core,$_ctx);'. + '} ?>'; + } + + /*dtd + + + */ + public function SysIf($attr,$content) + { + $if = new ArrayObject(); + $is_ping = null; + + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['categories'])) { + $sign = (boolean) $attr['categories'] ? '!' : '='; + $if[] = '$_ctx->categories '.$sign.'== null'; + } + + if (isset($attr['posts'])) { + $sign = (boolean) $attr['posts'] ? '!' : '='; + $if[] = '$_ctx->posts '.$sign.'== null'; + } + + if (isset($attr['blog_lang'])) { + $if[] = "\$core->blog->settings->system->lang == '".addslashes($attr['blog_lang'])."'"; + } + + if (isset($attr['current_tpl'])) { + $sign = '='; + if (substr($attr['current_tpl'],0,1) == '!') { + $sign = '!'; + $attr['current_tpl'] = substr($attr['current_tpl'],1); + } + $if[] = "\$_ctx->current_tpl ".$sign."= '".addslashes($attr['current_tpl'])."'"; + } + + if (isset($attr['current_mode'])) { + $sign = '='; + if (substr($attr['current_mode'],0,1) == '!') { + $sign = '!'; + $attr['current_mode'] = substr($attr['current_mode'],1); + } + $if[] = "\$core->url->type ".$sign."= '".addslashes($attr['current_mode'])."'"; + } + + if (isset($attr['has_tpl'])) { + $sign = ''; + if (substr($attr['has_tpl'],0,1) == '!') { + $sign = '!'; + $attr['has_tpl'] = substr($attr['has_tpl'],1); + } + $if[] = $sign."\$core->tpl->getFilePath('".addslashes($attr['has_tpl'])."') !== false"; + } + + if (isset($attr['blog_id'])) { + $sign = ''; + if (substr($attr['blog_id'],0,1) == '!') { + $sign = '!'; + $attr['blog_id'] = substr($attr['blog_id'],1); + } + $if[] = $sign."(\$core->blog->id == '".addslashes($attr['blog_id'])."')"; + } + + if (isset($attr['comments_active'])) { + $sign = (boolean) $attr['comments_active'] ? '' : '!'; + $if[] = $sign.'$core->blog->settings->system->allow_comments'; + } + + if (isset($attr['pings_active'])) { + $sign = (boolean) $attr['pings_active'] ? '' : '!'; + $if[] = $sign.'$core->blog->settings->system->allow_trackbacks'; + } + + if (isset($attr['wiki_comments'])) { + $sign = (boolean) $attr['wiki_comments'] ? '' : '!'; + $if[] = $sign.'$core->blog->settings->system->wiki_comments'; + } + + if (isset($attr['search_count']) && + preg_match('/^((=|!|>|<)=|(>|<))\s*[0-9]+$/',trim($attr['search_count']))) { + $if[] = '(isset($_search_count) && $_search_count '.html::decodeEntities($attr['search_count']).')'; + } + + $this->core->callBehavior('tplIfConditions','SysIf',$attr,$content,$if); + + if (count($if) != 0) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + */ + public function SysIfCommentPublished($attr,$content) + { + return + ''. + $content. + ''; + } + + /*dtd + + */ + public function SysIfCommentPending($attr,$content) + { + return + ''. + $content. + ''; + } + + /*dtd + + */ + public function SysFeedSubtitle($attr) + { + $f = $this->getFilters($attr); + return 'feed_subtitle !== null) { echo '.sprintf($f,'$_ctx->feed_subtitle').';} ?>'; + } + + /*dtd + + */ + public function SysIfFormError($attr,$content) + { + return + 'form_error !== null) : ?>'. + $content. + ''; + } + + /*dtd + + */ + public function SysFormError($attr) + { + return + 'form_error !== null) { echo $_ctx->form_error; } ?>'; + } + + public function SysPoweredBy($attr) + { + return + 'Dotclear"); ?>'; + } + + public function SysSearchString($attr) + { + $s = isset($attr['string']) ? $attr['string'] : '%1$s'; + + $f = $this->getFilters($attr); + return ''; + } + + public function SysSelfURI($attr) + { + $f = $this->getFilters($attr); + return ''; + } +} + +# Template nodes, for parsing purposes + +# Generic list node, this one may only be instanciated +# once for root element +class tplNode +{ + # Basic tree structure : links to parent, children forrest + protected $parentNode; + protected $children; + + public function __construct() { + $this->children = array(); + $this->parentNode = null; + } + + // Returns compiled block + public function compile($tpl) { + $res=''; + foreach ($this->children as $child) { + $res .= $child->compile($tpl); + } + return $res; + } + + # Add a children to current node + public function addChild ($child) { + $this->children[] = $child; + $child->setParent($this); + } + + # Defines parent for current node + protected function setParent($parent) { + $this->parentNode = $parent; + } + + # Retrieves current node parent. + # If parent is root node, null is returned + public function getParent() { + return $this->parentNode; + } + + # Current node tag + public function getTag() { + return "ROOT"; + } +} + +// Text node, for any non-tpl content +class tplNodeText extends tplNode +{ + // Simple text node, only holds its content + protected $content; + + public function __construct($text) { + parent::__construct(); + $this->content=$text; + } + + public function compile($tpl) { + return $this->content; + } + + public function getTag() { + return "TEXT"; + } +} + +// Block node, for all ... +class tplNodeBlock extends tplNode +{ + protected $attr; + protected $tag; + protected $closed; + + public function __construct($tag,$attr) { + parent::__construct(); + $this->content=''; + $this->tag = $tag; + $this->attr = $attr; + $this->closed=false; + } + public function setClosing() { + $this->closed = true; + } + public function isClosed() { + return $this->closed; + } + public function compile($tpl) { + if ($this->closed) { + $content = parent::compile($tpl); + return $tpl->compileBlockNode($this->tag,$this->attr,$content); + } else { + // if tag has not been closed, silently ignore its content... + return ''; + } + } + public function getTag() { + return $this->tag; + } +} + +// Value node, for all {{tpl:Tag}} +class tplNodeValue extends tplNode +{ + protected $attr; + protected $str_attr; + protected $tag; + + public function __construct($tag,$attr,$str_attr) { + parent::__construct(); + $this->content=''; + $this->tag = $tag; + $this->attr = $attr; + $this->str_attr = $str_attr; + } + + public function compile($tpl) { + return $tpl->compileValueNode($this->tag,$this->attr,$this->str_attr); + } + + public function getTag() { + return $this->tag; + } +} + +?> diff --git a/v2/dotclear/inc/public/default-templates/404.html b/v2/dotclear/inc/public/default-templates/404.html new file mode 100644 index 0000000..f2beeeb --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/404.html @@ -0,0 +1,62 @@ + + + + + + + {{tpl:lang Document not found}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
        +{{tpl:include src="_top.html"}} + +
        + +
        +
        + +
        +

        {{tpl:lang Document not found}}

        +
        + +
        +

        {{tpl:lang The document you are looking for does not exist.}}

        +
        + +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/_flv_player.html b/v2/dotclear/inc/public/default-templates/_flv_player.html new file mode 100644 index 0000000..7f5b9b6 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/_flv_player.html @@ -0,0 +1,8 @@ + + + + + +{{tpl:lang Embedded Audio Player}} + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/_footer.html b/v2/dotclear/inc/public/default-templates/_footer.html new file mode 100644 index 0000000..fac8db1 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/_footer.html @@ -0,0 +1,5 @@ + + +{{tpl:SysBehavior behavior="publicFooterContent"}} \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/_head.html b/v2/dotclear/inc/public/default-templates/_head.html new file mode 100644 index 0000000..a79816f --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/_head.html @@ -0,0 +1,12 @@ + + + + + + +{{tpl:include src="user_head.html"}} +{{tpl:SysBehavior behavior="publicHeadContent"}} \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/_mp3_player.html b/v2/dotclear/inc/public/default-templates/_mp3_player.html new file mode 100644 index 0000000..b1a1991 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/_mp3_player.html @@ -0,0 +1,6 @@ + + + + +{{tpl:lang Embedded Audio Player}} + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/_top.html b/v2/dotclear/inc/public/default-templates/_top.html new file mode 100644 index 0000000..829edae --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/_top.html @@ -0,0 +1,10 @@ +
        +

        {{tpl:BlogName encode_html="1"}}

        + + + {{tpl:SysBehavior behavior="publicTopAfterContent"}} +
        + +

        {{tpl:lang To content}} | +{{tpl:lang To menu}} | +{{tpl:lang To search}}

        \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/archive.html b/v2/dotclear/inc/public/default-templates/archive.html new file mode 100644 index 0000000..c551449 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/archive.html @@ -0,0 +1,79 @@ + + + + + + + {{tpl:lang Archives}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
        +{{tpl:include src="_top.html"}} + +
        + +
        +
        + +
        +

        {{tpl:lang Archives}}

        +
        + +
        + + +

        {{tpl:ArchiveDate format="%Y"}}

        + + +
        +
        + +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/archive_month.html b/v2/dotclear/inc/public/default-templates/archive_month.html new file mode 100644 index 0000000..0f4b745 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/archive_month.html @@ -0,0 +1,96 @@ + + + + + + + {{tpl:lang Archives}} - {{tpl:ArchiveDate}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
        +{{tpl:include src="_top.html"}} + +
        + +
        +
        + + + +
        +

        {{tpl:ArchiveDate}}

        +
        + +
        + + +

        {{tpl:EntryDate}}

        + +

        {{tpl:EntryTitle encode_html="1"}}

        + + +
        +
        +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/atom-comments.xml b/v2/dotclear/inc/public/default-templates/atom-comments.xml new file mode 100644 index 0000000..74b4970 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/atom-comments.xml @@ -0,0 +1,47 @@ + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + - {{tpl:lang Comments}} + {{tpl:BlogDescription encode_xml="1"}} + + + {{tpl:BlogUpdateDate iso8601="1"}} + + {{tpl:BlogEditor encode_xml="1"}} + + {{tpl:BlogFeedID}} + Dotclear + + + + + + [ping] {{tpl:PingEntryTitle encode_xml="1"}} - {{tpl:PingBlogName encode_xml="1"}} + + {{tpl:PingFeedID}} + {{tpl:PingDate iso8601="1"}} + {{tpl:PingDate iso8601="1" upddt="1"}} + {{tpl:PingBlogName encode_xml="1"}} + <p><a href="{{tpl:PingAuthorURL encode_xml="1"}}">{{tpl:PingTitle encode_xml="1"}}</a></p> {{tpl:PingContent encode_xml="1"}} + + + + + + + {{tpl:CommentEntryTitle encode_xml="1"}} - {{tpl:CommentAuthor encode_xml="1"}} + + {{tpl:CommentFeedID}} + {{tpl:CommentDate iso8601="1"}} + {{tpl:CommentDate iso8601="1" upddt="1"}} + {{tpl:CommentAuthor encode_xml="1"}} + {{tpl:CommentContent absolute_urls="1" encode_xml="1"}} + + + + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/atom.xml b/v2/dotclear/inc/public/default-templates/atom.xml new file mode 100644 index 0000000..5237103 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/atom.xml @@ -0,0 +1,50 @@ + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + {{tpl:BlogDescription encode_xml="1"}} + + + {{tpl:BlogUpdateDate iso8601="1"}} + + {{tpl:BlogEditor encode_xml="1"}} + + {{tpl:BlogFeedID}} + Dotclear + + + + + {{tpl:EntryTitle encode_xml="1"}} + + {{tpl:EntryFeedID}} + {{tpl:EntryDate iso8601="1"}} + {{tpl:EntryDate iso8601="1" upddt="1"}} + {{tpl:EntryAuthorCommonName encode_xml="1"}} + + {{tpl:EntryCategory encode_html="1"}} + + {{tpl:TagID}} + + {{tpl:EntryExcerpt absolute_urls="1" encode_xml="1"}} + {{tpl:EntryContent absolute_urls="1" encode_xml="1"}} + + + + + + + + {{tpl:EntryURL}}#comment-form + {{tpl:BlogFeedURL type="atom"}}/comments/{{tpl:EntryID}} + + + + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/category.html b/v2/dotclear/inc/public/default-templates/category.html new file mode 100644 index 0000000..0d43eea --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/category.html @@ -0,0 +1,167 @@ + + + + + + + {{tpl:CategoryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
        +{{tpl:include src="_top.html"}} + +
        + +
        +
        + +
        +

        {{tpl:CategoryTitle encode_html="1"}} + {{tpl:CategoryTitle encode_html="1"}}

        + {{tpl:CategoryDescription}} + + +

        {{tpl:lang Entries feed}} + + + - {{tpl:lang Comments feed}} + +

        +
        +
        + + + +
        +

        {{tpl:lang Subcategories}}

        + +
        + +
        + + +
        + +

        {{tpl:EntryDate}}

        + +

        {{tpl:EntryTitle encode_html="1"}}

        + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
        {{tpl:EntryExcerpt}}
        +

        {{tpl:lang Continue + reading}}...

        +
        + + + +
        {{tpl:EntryContent}}
        +
        + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
        + + + +

        - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

        +
        +
        +
        +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/home.html b/v2/dotclear/inc/public/default-templates/home.html new file mode 100644 index 0000000..df11fcd --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/home.html @@ -0,0 +1,144 @@ + + + + + + + {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
        +{{tpl:include src="_top.html"}} + +
        + +
        +
        + +
        + +

        {{tpl:EntryDate}}

        + +

        {{tpl:EntryTitle encode_html="1"}}

        + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
        {{tpl:EntryExcerpt}}
        +

        {{tpl:lang Continue + reading}}...

        +
        + + + +
        {{tpl:EntryContent}}
        +
        + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
        + + + +

        - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

        +
        +
        +
        +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/password-form.html b/v2/dotclear/inc/public/default-templates/password-form.html new file mode 100644 index 0000000..e462dc0 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/password-form.html @@ -0,0 +1,41 @@ + + + + + + {{tpl:lang Password needed}} - {{tpl:BlogName encode_html="1"}} + + + + + + +
        +

        {{tpl:lang Password needed}}

        + +

        {{tpl:lang You must give a password to access this area.}}

        +

        + +
        + + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/post.html b/v2/dotclear/inc/public/default-templates/post.html new file mode 100644 index 0000000..6ff773c --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/post.html @@ -0,0 +1,274 @@ + + + + + + + {{tpl:EntryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + + + + +
        +{{tpl:EntryPingData}} + +{{tpl:include src="_top.html"}} + +
        + +
        +
        + + + +
        +

        {{tpl:EntryTitle encode_html="1"}}

        + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
        {{tpl:EntryExcerpt}}
        +
        + +
        {{tpl:EntryContent}}
        + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} +
        + + + + +
        +

        {{tpl:lang Attachments}}

        +
          + +
        • + + {{tpl:include src="_mp3_player.html"/}} - + + + {{tpl:include src="_flv_player.html"/}} + + + {{tpl:AttachmentTitle}} + +
        • + +
        +
        + +
        + + + + + +
        +

        {{tpl:lang Comments}}

        +
        + +
        {{tpl:CommentOrderNumber}}. + {{tpl:lang On}} {{tpl:CommentDate}}, {{tpl:CommentTime}} + {{tpl:lang by}} {{tpl:CommentAuthorLink}}
        + +
        + + {{tpl:SysBehavior behavior="publicCommentBeforeContent"}} + + {{tpl:CommentContent}} + + + {{tpl:SysBehavior behavior="publicCommentAfterContent"}} +
        + +
        +
        + +
        +
        + + + +

        {{tpl:SysFormError}}

        +
        + + +

        {{tpl:lang Your comment has been published.}}

        +
        + + +

        {{tpl:lang Your comment has been submitted and + will be reviewed for publication.}}

        +
        + + +
        + +
        +

        {{tpl:lang Your comment}}

        +
        +
        {{tpl:CommentPreviewContent}}
        +
        +

        +
        +
        + +

        {{tpl:lang Add a comment}}

        +
        + + {{tpl:SysBehavior behavior="publicCommentFormBeforeContent"}} + +

        + +

        + +

        + +

        + +

        + +

        + +

        + +

        + +

        +

        {{tpl:CommentHelp}}

        + + + {{tpl:SysBehavior behavior="publicCommentFormAfterContent"}} +
        + +
        +

        +

        +
        +
        +
        + + + +
        +

        {{tpl:lang They posted on the same topic}}

        + + +
        + +
        {{tpl:PingOrderNumber}}. + {{tpl:lang On}} {{tpl:PingDate}}, {{tpl:PingTime}} + {{tpl:lang by}} {{tpl:PingBlogName encode_html="1"}}
        + +
        + + {{tpl:SysBehavior behavior="publicPingBeforeContent"}} + +

        {{tpl:PingTitle encode_html="1"}}

        + {{tpl:PingContent}} + + + {{tpl:SysBehavior behavior="publicPingAfterContent"}} +
        + +
        + +
        +
        +
        + + +

        {{tpl:lang Trackback URL}} : {{tpl:EntryPingLink}}

        +
        + + +

        {{tpl:lang This post's comments feed}}

        +
        +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + diff --git a/v2/dotclear/inc/public/default-templates/rss2-comments.xml b/v2/dotclear/inc/public/default-templates/rss2-comments.xml new file mode 100644 index 0000000..ccea31b --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/rss2-comments.xml @@ -0,0 +1,48 @@ + + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + - {{tpl:lang Comments}} + {{tpl:BlogURL}} + + {{tpl:BlogDescription encode_xml="1"}} + {{tpl:BlogLanguage}} + {{tpl:BlogUpdateDate rfc822="1"}} + {{tpl:BlogCopyrightNotice encode_xml="1"}} + http://blogs.law.harvard.edu/tech/rss + Dotclear + + + + + + [ping] {{tpl:PingEntryTitle encode_xml="1"}} - {{tpl:PingBlogName encode_xml="1"}} + {{tpl:PingPostURL encode_xml="1"}}#c{{tpl:PingID}} + {{tpl:PingFeedID}} + {{tpl:PingDate rfc822="1"}} + {{tpl:PingBlogName encode_xml="1"}} + + <p><a href="{{tpl:PingAuthorURL encode_xml="1"}}">{{tpl:PingTitle encode_xml="1"}}</a></p> + {{tpl:PingContent encode_xml="1"}} + + + + + + {{tpl:CommentEntryTitle encode_xml="1"}} - {{tpl:CommentAuthor encode_xml="1"}} + {{tpl:CommentPostURL encode_xml="1"}}#c{{tpl:CommentID}} + {{tpl:CommentFeedID}} + {{tpl:CommentDate rfc822="1"}} + {{tpl:CommentAuthor encode_xml="1"}} + + {{tpl:CommentContent absolute_urls="1" encode_xml="1"}} + + + + + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/rss2.xml b/v2/dotclear/inc/public/default-templates/rss2.xml new file mode 100644 index 0000000..c4ed4ba --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/rss2.xml @@ -0,0 +1,49 @@ + + + + + {{tpl:BlogName encode_xml="1"}}{{tpl:SysFeedSubtitle encode_xml="1"}} + {{tpl:BlogURL}} + + {{tpl:BlogDescription encode_xml="1"}} + {{tpl:BlogLanguage}} + {{tpl:BlogUpdateDate rfc822="1"}} + {{tpl:BlogCopyrightNotice encode_xml="1"}} + http://blogs.law.harvard.edu/tech/rss + Dotclear + + + + + {{tpl:EntryTitle encode_xml="1"}} + {{tpl:EntryURL}} + {{tpl:EntryFeedID}} + {{tpl:EntryDate rfc822="1"}} + {{tpl:EntryAuthorCommonName encode_xml="1"}} + + {{tpl:EntryCategory encode_html="1"}} + + {{tpl:TagID}} + + {{tpl:EntryExcerpt absolute_urls="1" encode_xml="1"}} + {{tpl:EntryContent absolute_urls="1" encode_xml="1"}} + + + + + + + {{tpl:EntryURL}}#comment-form + {{tpl:EntryURL}}#comment-form + {{tpl:BlogFeedURL}}/comments/{{tpl:EntryID}} + + + + + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/rss2.xsl b/v2/dotclear/inc/public/default-templates/rss2.xsl new file mode 100644 index 0000000..9dcd864 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/rss2.xsl @@ -0,0 +1,120 @@ + + + + + + + + + {{tpl:lang Subscribe to}} <xsl:value-of select="/rss/channel/title"/> + + + + +
        +
        +

        +

        +
        +
        +

        {{tpl:lang What is an RSS feed?}}

        +

        {{tpl:lang RSS feed is a free blog summary. It provides content + (either posts or comments) or summaries of content, together with links + to the full versions, and other metadata. The last published items may + then be read by your favorite RSS + aggregator.}}

        +

        {{tpl:lang Subscribe}}

        +

        {{tpl:lang Simply copy the following URL into your aggregator:}}

        +

        +
        +
        + +
        + +
        + + +
        + + + +
        +

        +
        +
        +
        + +
        \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/search.html b/v2/dotclear/inc/public/default-templates/search.html new file mode 100644 index 0000000..2d2ba90 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/search.html @@ -0,0 +1,152 @@ + + + + + + + {{tpl:lang Search}} - {{tpl:SysSearchString encode_html="1"}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf> + + + + + + - {{tpl:lang page}} {{tpl:PaginationCurrent}}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + +
        +{{tpl:include src="_top.html"}} + +
        + +
        +
        + +
        +

        {{tpl:lang Search}}

        + +

        {{tpl:SysSearchString encode_html="1" string="Your search for %1$s returned no result."}}

        +
        + +

        {{tpl:SysSearchString encode_html="1" string="Your search for %1$s returned %2$s result."}}

        +
        + +

        {{tpl:SysSearchString encode_html="1" string="Your search for %1$s returned %2$s results."}}

        +
        +
        + + +
        + +

        {{tpl:EntryDate}}

        + +

        {{tpl:EntryTitle encode_html="1"}}

        + + + + + + + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + + +
        {{tpl:EntryExcerpt}}
        +

        {{tpl:lang Continue + reading}}...

        +
        + + + +
        {{tpl:EntryContent}}
        +
        + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} + + + + + +
        + + + +

        - + {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}} + -

        +
        +
        +
        +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/inc/public/default-templates/user_head.html b/v2/dotclear/inc/public/default-templates/user_head.html new file mode 100644 index 0000000..4b380d4 --- /dev/null +++ b/v2/dotclear/inc/public/default-templates/user_head.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v2/dotclear/inc/public/lib.tpl.context.php b/v2/dotclear/inc/public/lib.tpl.context.php new file mode 100644 index 0000000..18780ad --- /dev/null +++ b/v2/dotclear/inc/public/lib.tpl.context.php @@ -0,0 +1,443 @@ +pop($name); + } else { + $this->stack[$name][] =& $var; + if ($var instanceof record) { + $this->stack['cur_loop'][] =& $var; + } + } + } + + public function __get($name) + { + if (!isset($this->stack[$name])) { + return null; + } + + $n = count($this->stack[$name]); + if ($n > 0) { + return $this->stack[$name][($n-1)]; + } + + return null; + } + + public function exists($name) + { + return isset($this->stack[$name][0]); + } + + public function pop($name) + { + if (isset($this->stack[$name])) { + $v = array_pop($this->stack[$name]); + if ($v instanceof record) { + array_pop($this->stack['cur_loop']); + } + unset($v); + } + } + + # Loop position tests + public function loopPosition($start,$length=null,$even=null) + { + if (!$this->cur_loop) { + return false; + } + + $index = $this->cur_loop->index(); + $size = $this->cur_loop->count(); + + $test = false; + if ($start >= 0) + { + $test = $index >= $start; + if ($length !== null) { + if ($length >= 0) { + $test = $test && $index < $start + $length; + } else { + $test = $test && $index < $size + $length; + } + } + } + else + { + $test = $index >= $size + $start; + if ($length !== null) { + if ($length >= 0) { + $test = $test && $index < $size + $start + $length; + } else { + $test = $test && $index < $size + $length; + } + } + } + + if ($even !== null) { + $test = $test && $index%2 == $even; + } + + return $test; + } + + + # Static methods + public static function global_filter($str, + $encode_xml, $remove_html, $cut_string, $lower_case, $upper_case ,$tag='') + { + $args = func_get_args(); + array_pop($args); + $args[0] =& $str; + + # --BEHAVIOR-- publicBeforeContentFilter + $res = $GLOBALS['core']->callBehavior('publicBeforeContentFilter',$GLOBALS['core'],$tag,$args); + + if ($remove_html) { + $str = self::remove_html($str); + $str = preg_replace('/\s+/',' ',$str); + } + + if ($encode_xml) { + $str = self::encode_xml($str); + } + + if ($cut_string) { + $str = self::cut_string($str,(integer) $cut_string); + } + + if ($lower_case) { + $str = self::lower_case($str); + } elseif ($upper_case) { + $str = self::upper_case($str); + } + + # --BEHAVIOR-- publicAfterContentFilter + $res = $GLOBALS['core']->callBehavior('publicAfterContentFilter',$GLOBALS['core'],$tag,$args); + + return $str; + } + + + public static function cut_string($str,$l) + { + return text::cutString($str,$l); + } + + public static function encode_xml($str) + { + return html::escapeHTML($str); + } + + public static function remove_html($str) + { + return html::decodeEntities(html::clean($str)); + } + + public static function lower_case($str) + { + return mb_strtolower($str); + } + + public static function upper_case($str) + { + return mb_strtoupper($str); + } + + public static function categoryPostParam(&$p) + { + $not = substr($p['cat_url'],0,1) == '!'; + if ($not) { + $p['cat_url'] = substr($p['cat_url'],1); + } + + $p['cat_url'] = preg_split('/\s*,\s*/',$p['cat_url'],-1,PREG_SPLIT_NO_EMPTY); + + foreach ($p['cat_url'] as &$v) + { + if ($not) { + $v .= ' ?not'; + } + if ($GLOBALS['_ctx']->exists('categories') && preg_match('/#self/',$v)) { + $v = preg_replace('/#self/',$GLOBALS['_ctx']->categories->cat_url,$v); + } elseif ($GLOBALS['_ctx']->exists('posts') && preg_match('/#self/',$v)) { + $v = preg_replace('/#self/',$GLOBALS['_ctx']->posts->cat_url,$v); + } + } + } + + # Static methods for pagination + public static function PaginationNbPages() + { + global $_ctx; + + if ($_ctx->pagination === null) { + return false; + } + + $nb_posts = $_ctx->pagination->f(0); + $nb_per_page = $_ctx->post_params['limit'][1]; + + $nb_pages = ceil($nb_posts/$nb_per_page); + + return $nb_pages; + } + + public static function PaginationPosition($offset=0) + { + if (isset($GLOBALS['_page_number'])) { + $p = $GLOBALS['_page_number']; + } else { + $p = 1; + } + + $p = $p+$offset; + + $n = self::PaginationNbPages(); + if (!$n) { + return $p; + } + + if ($p > $n || $p <= 0) { + return 1; + } else { + return $p; + } + } + + public static function PaginationStart() + { + if (isset($GLOBALS['_page_number'])) { + return self::PaginationPosition() == 1; + } + + return true; + } + + public static function PaginationEnd() + { + if (isset($GLOBALS['_page_number'])) { + return self::PaginationPosition() == self::PaginationNbPages(); + } + + return false; + } + + public static function PaginationURL($offset=0) + { + $args = $_SERVER['URL_REQUEST_PART']; + + $n = self::PaginationPosition($offset); + + $args = preg_replace('#(^|/)page/([0-9]+)$#','',$args); + + $url = $GLOBALS['core']->blog->url.$args; + + if ($n > 1) { + $url = preg_replace('#/$#','',$url); + $url .= '/page/'.$n; + } + + # If search param + if (!empty($_GET['q'])) { + $s = strpos($url,'?') !== false ? '&' : '?'; + $url .= $s.'q='.rawurlencode($_GET['q']); + } + return $url; + } + + # Robots policy + public static function robotsPolicy($base,$over) + { + $pol = array('INDEX' => 'INDEX','FOLLOW' => 'FOLLOW', 'ARCHIVE' => 'ARCHIVE'); + $base = array_flip(preg_split('/\s*,\s*/',$base)); + $over = array_flip(preg_split('/\s*,\s*/',$over)); + + foreach ($pol as $k => &$v) + { + if (isset($base[$k]) || isset($base['NO'.$k])) { + $v = isset($base['NO'.$k]) ? 'NO'.$k : $k; + } + if (isset($over[$k]) || isset($over['NO'.$k])) { + $v = isset($over['NO'.$k]) ? 'NO'.$k : $k; + } + } + + if ($pol['ARCHIVE'] == 'ARCHIVE') { + unset($pol['ARCHIVE']); + } + + return implode(', ',$pol); + } + + # Smilies static methods + public static function getSmilies($blog) + { + $path = array(); + if (isset($GLOBALS['__theme'])) { + $path[] = $GLOBALS['__theme']; + } + $path[] = 'default'; + $definition = $blog->themes_path.'/%s/smilies/smilies.txt'; + $base_url = $blog->settings->system->themes_url.'/%s/smilies/'; + + $res = array(); + + foreach ($path as $t) + { + if (file_exists(sprintf($definition,$t))) { + $base_url = sprintf($base_url,$t); + return self::smiliesDefinition(sprintf($definition,$t),$base_url); + } + } + return false; + } + + public static function smiliesDefinition($f,$url) + { + $def = file($f); + + $res = array(); + foreach($def as $v) + { + $v = trim($v); + if (preg_match('|^([^\t]*)[\t]+(.*)$|',$v,$matches)) + { + $r = '/(\A|[\s]+|>)('.preg_quote($matches[1],'/').')([\s]+|[<]|\Z)/ms'; + $s = '$1$3'; + $res[$r] = $s; + } + } + + return $res; + } + + public static function addSmilies($str) + { + if (!isset($GLOBALS['__smilies']) || !is_array($GLOBALS['__smilies'])) { + return $str; + } + + return preg_replace(array_keys($GLOBALS['__smilies']),array_values($GLOBALS['__smilies']),$str); + } + + # First post image helpers + public static function EntryFirstImageHelper($size,$with_category,$class="") + { + global $core, $_ctx; + + $media = new dcMedia($core); + $sizes = implode('|',array_keys($media->thumb_sizes)).'|o'; + if (!preg_match('/^'.$sizes.'$/',$size)) { + $size = 's'; + } + $p_url = $core->blog->settings->system->public_url; + $p_site = preg_replace('#^(.+?//.+?)/(.*)$#','$1',$core->blog->url); + $p_root = $core->blog->public_path; + + $pattern = '(?:'.preg_quote($p_site,'/').')?'.preg_quote($p_url,'/'); + $pattern = sprintf('/]+/msu',$pattern); + + $src = ''; + $alt = ''; + + # We first look in post content + if ($_ctx->posts) + { + $subject = $_ctx->posts->post_excerpt_xhtml.$_ctx->posts->post_content_xhtml; + if (preg_match_all($pattern,$subject,$m) > 0) + { + foreach ($m[1] as $i => $img) { + if (($src = self::ContentFirstImageLookup($p_root,$img,$size)) !== false) { + $src = $p_url.(dirname($img) != '/' ? dirname($img) : '').'/'.$src; + if (preg_match('/alt="([^"]+)"/',$m[0][$i],$malt)) { + $alt = $malt[1]; + } + break; + } + } + } + } + + # No src, look in category description if available + if (!$src && $with_category && $_ctx->categories) + { + if (preg_match_all($pattern,$_ctx->categories->cat_desc,$m) > 0) + { + foreach ($m[1] as $i => $img) { + if (($src = self::ContentFirstImageLookup($p_root,$img,$size)) !== false) { + $src = $p_url.(dirname($img) != '/' ? dirname($img) : '').'/'.$src; + if (preg_match('/alt="([^"]+)"/',$m[0][$i],$malt)) { + $alt = $malt[1]; + } + break; + } + } + }; + } + + if ($src) { + return ''.$alt.''; + } + } + + private static function ContentFirstImageLookup($root,$img,$size) + { + global $core; + + # Get base name and extension + $info = path::info($img); + $base = $info['base']; + + $media = new dcMedia($core); + $sizes = implode('|',array_keys($media->thumb_sizes)); + if (preg_match('/^\.(.+)_('.$sizes.')$/',$base,$m)) { + $base = $m[1]; + } + + $res = false; + if ($size != 'o' && file_exists($root.'/'.$info['dirname'].'/.'.$base.'_'.$size.'.jpg')) + { + $res = '.'.$base.'_'.$size.'.jpg'; + } + else + { + $f = $root.'/'.$info['dirname'].'/'.$base; + if (file_exists($f.'.'.$info['extension'])) { + $res = $base.'.'.$info['extension']; + } elseif (file_exists($f.'.jpg')) { + $res = $base.'.jpg'; + } elseif (file_exists($f.'.jpeg')) { + $res = $base.'.jpeg'; + } elseif (file_exists($f.'.png')) { + $res = $base.'.png'; + } elseif (file_exists($f.'.gif')) { + $res = $base.'.gif'; + } + } + + if ($res) { + return $res; + } + return false; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/public/lib.urlhandlers.php b/v2/dotclear/inc/public/lib.urlhandlers.php new file mode 100644 index 0000000..7ff1f87 --- /dev/null +++ b/v2/dotclear/inc/public/lib.urlhandlers.php @@ -0,0 +1,683 @@ +callBehavior("publicGetURLFor",$type,$value); + if (!$url) { + $url = $this->getBase($type); + if ($value) { + if ($url) { + $url .= '/'; + } + $url .= $value; + } + } + return $url; + } + + public function register($type,$url,$representation,$handler) + { + $core =& $GLOBALS['core']; + $t = new ArrayObject(array($type,$url,$representation,$handler)); + $core->callBehavior("publicRegisterURL",$t); + parent::register($t[0],$t[1],$t[2],$t[3]); + } + + public static function p404() + { + throw new Exception ("Page not found",404); + } + + public static function default404($args,$type,$e) + { + if ($e->getCode() != 404) { + throw $e; + } + $_ctx =& $GLOBALS['_ctx']; + $core = $GLOBALS['core']; + + header('Content-Type: text/html; charset=UTF-8'); + http::head(404,'Not Found'); + $core->url->type = '404'; + $_ctx->current_tpl = '404.html'; + $_ctx->content_type = 'text/html'; + + echo $core->tpl->getData($_ctx->current_tpl); + + # --BEHAVIOR-- publicAfterDocument + $core->callBehavior('publicAfterDocument',$core); + exit; + } + + protected static function getPageNumber(&$args) + { + if (preg_match('#(^|/)page/([0-9]+)$#',$args,$m)) { + $n = (integer) $m[2]; + if ($n > 0) { + $args = preg_replace('#(^|/)page/([0-9]+)$#','',$args); + return $n; + } + } + + return false; + } + + protected static function serveDocument($tpl,$content_type='text/html',$http_cache=true,$http_etag=true) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + if ($_ctx->nb_entry_per_page === null) { + $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_page; + } + + $tpl_file = $core->tpl->getFilePath($tpl); + + if (!$tpl_file) { + throw new Exception('Unable to find template '); + } + + $result = new ArrayObject; + + $_ctx->current_tpl = $tpl; + $_ctx->content_type = $content_type; + $_ctx->http_cache = $http_cache; + $_ctx->http_etag = $http_etag; + $core->callBehavior('urlHandlerBeforeGetData',$_ctx); + + if ($_ctx->http_cache) { + $GLOBALS['mod_files'][] = $tpl_file; + http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); + } + + header('Content-Type: '.$_ctx->content_type.'; charset=UTF-8'); + $result['content'] = $core->tpl->getData($_ctx->current_tpl); + $result['content_type'] = $_ctx->content_type; + $result['tpl'] = $_ctx->current_tpl; + $result['blogupddt'] = $core->blog->upddt; + + # --BEHAVIOR-- urlHandlerServeDocument + $core->callBehavior('urlHandlerServeDocument',$result); + + if ($_ctx->http_cache && $_ctx->http_etag) { + http::etag($result['content'],http::getSelfURI()); + } + echo $result['content']; + } + + public function getDocument() + { + $core =& $GLOBALS['core']; + + $type = $args = ''; + + if ($this->mode == 'path_info') + { + $part = substr($_SERVER['PATH_INFO'],1); + } + else + { + $part = ''; + + $qs = $this->parseQueryString(); + + # Recreates some _GET and _REQUEST pairs + if (!empty($qs)) + { + foreach ($_GET as $k => $v) { + if (isset($_REQUEST[$k])) { + unset($_REQUEST[$k]); + } + } + $_GET = $qs; + $_REQUEST = array_merge($qs,$_REQUEST); + + list($k,$v) = each($qs); + if ($v === null) { + $part = $k; + unset($_GET[$k]); + unset($_REQUEST[$k]); + } + } + } + + $_SERVER['URL_REQUEST_PART'] = $part; + + $this->getArgs($part,$type,$this->args); + + # --BEHAVIOR-- urlHandlerGetArgsDocument + $core->callBehavior('urlHandlerGetArgsDocument',$this); + + if (!$type) + { + $this->type = 'default'; + $this->callDefaultHandler($this->args); + } + else + { + $this->type = $type; + $this->callHandler($type,$this->args); + } + } + + public static function home($args) + { + $n = self::getPageNumber($args); + + if ($args && !$n) + { + # "Then specified URL went unrecognized by all URL handlers and + # defaults to the home page, but is not a page number. + self::p404(); + } + else + { + $core =& $GLOBALS['core']; + + if ($n) { + $GLOBALS['_page_number'] = $n; + $core->url->type = $n > 1 ? 'default-page' : 'default'; + } + + if (empty($_GET['q'])) { + self::serveDocument('home.html'); + $core->blog->publishScheduledEntries(); + } else { + self::search(); + } + } + } + + public static function search() + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $core->url->type='search'; + + $GLOBALS['_search'] = !empty($_GET['q']) ? rawurldecode($_GET['q']) : ''; + if ($GLOBALS['_search']) { + $params = new ArrayObject(array('search' => $GLOBALS['_search'])); + $core->callBehavior('publicBeforeSearchCount',$params); + $GLOBALS['_search_count'] = $core->blog->getPosts($params,true)->f(0); + } + + self::serveDocument('search.html'); + } + + public static function lang($args) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $n = self::getPageNumber($args); + $params = new ArrayObject(array( + 'lang' => $args)); + + $core->callBehavior('publicLangBeforeGetLangs',$params,$args); + + $_ctx->langs = $core->blog->getLangs($params); + + if ($_ctx->langs->isEmpty()) { + # The specified language does not exist. + self::p404(); + } + else + { + if ($n) { + $GLOBALS['_page_number'] = $n; + } + $_ctx->cur_lang = $args; + self::home(null); + } + } + + public static function category($args) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $n = self::getPageNumber($args); + + if ($args == '' && !$n) { + # No category was specified. + self::p404(); + } + else + { + $params = new ArrayObject(array( + 'cat_url' => $args, + 'post_type' => 'post')); + + $core->callBehavior('publicCategoryBeforeGetCategories',$params,$args); + + $_ctx->categories = $core->blog->getCategories($params); + + if ($_ctx->categories->isEmpty()) { + # The specified category does no exist. + self::p404(); + } + else + { + if ($n) { + $GLOBALS['_page_number'] = $n; + } + self::serveDocument('category.html'); + } + } + } + + public static function archive($args) + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $year = $month = $cat_url = null; + # Nothing or year and month + if ($args == '') + { + self::serveDocument('archive.html'); + } + elseif (preg_match('|^/([0-9]{4})/([0-9]{2})$|',$args,$m)) + { + $params = new ArrayObject(array( + 'year' => $m[1], + 'month' => $m[2], + 'type' => 'month')); + + $core->callBehavior('publicArchiveBeforeGetDates',$params,$args); + + $_ctx->archives = $core->blog->getDates($params); + + if ($_ctx->archives->isEmpty()) { + # There is no entries for the specified period. + self::p404(); + } + else + { + self::serveDocument('archive_month.html'); + } + } + else { + # The specified URL is not a date. + self::p404(); + } + } + + public static function post($args) + { + if ($args == '') { + # No entry was specified. + self::p404(); + } + else + { + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + $core->blog->withoutPassword(false); + + $params = new ArrayObject(array( + 'post_url' => $args)); + + $core->callBehavior('publicPostBeforeGetPosts',$params,$args); + + $_ctx->posts = $core->blog->getPosts($params); + + $_ctx->comment_preview = new ArrayObject(); + $_ctx->comment_preview['content'] = ''; + $_ctx->comment_preview['rawcontent'] = ''; + $_ctx->comment_preview['name'] = ''; + $_ctx->comment_preview['mail'] = ''; + $_ctx->comment_preview['site'] = ''; + $_ctx->comment_preview['preview'] = false; + $_ctx->comment_preview['remember'] = false; + + $core->blog->withoutPassword(true); + + if ($_ctx->posts->isEmpty()) + { + # The specified entry does not exist. + self::p404(); + } + else + { + $post_id = $_ctx->posts->post_id; + $post_password = $_ctx->posts->post_password; + + # Password protected entry + if ($post_password != '' && !$_ctx->preview) + { + # Get passwords cookie + if (isset($_COOKIE['dc_passwd'])) { + $pwd_cookie = unserialize($_COOKIE['dc_passwd']); + } else { + $pwd_cookie = array(); + } + + # Check for match + if ((!empty($_POST['password']) && $_POST['password'] == $post_password) + || (isset($pwd_cookie[$post_id]) && $pwd_cookie[$post_id] == $post_password)) + { + $pwd_cookie[$post_id] = $post_password; + setcookie('dc_passwd',serialize($pwd_cookie),0,'/'); + } + else + { + self::serveDocument('password-form.html','text/html',false); + return; + } + } + + $post_comment = + isset($_POST['c_name']) && isset($_POST['c_mail']) && + isset($_POST['c_site']) && isset($_POST['c_content']) && + $_ctx->posts->commentsActive(); + + # Posting a comment + if ($post_comment) + { + # Spam trap + if (!empty($_POST['f_mail'])) { + http::head(412,'Precondition Failed'); + header('Content-Type: text/plain'); + echo "So Long, and Thanks For All the Fish"; + # Exits immediately the application to preserve the server. + exit; + } + + $name = $_POST['c_name']; + $mail = $_POST['c_mail']; + $site = $_POST['c_site']; + $content = $_POST['c_content']; + $preview = !empty($_POST['preview']); + + if ($content != '') + { + if ($core->blog->settings->system->wiki_comments) { + $core->initWikiComment(); + } else { + $core->initWikiSimpleComment(); + } + $content = $core->wikiTransform($content); + $content = $core->HTMLfilter($content); + } + + $_ctx->comment_preview['content'] = $content; + $_ctx->comment_preview['rawcontent'] = $_POST['c_content']; + $_ctx->comment_preview['name'] = $name; + $_ctx->comment_preview['mail'] = $mail; + $_ctx->comment_preview['site'] = $site; + + if ($preview) + { + # --BEHAVIOR-- publicBeforeCommentPreview + $core->callBehavior('publicBeforeCommentPreview',$_ctx->comment_preview); + + $_ctx->comment_preview['preview'] = true; + } + else + { + # Post the comment + $cur = $core->con->openCursor($core->prefix.'comment'); + $cur->comment_author = $name; + $cur->comment_site = html::clean($site); + $cur->comment_email = html::clean($mail); + $cur->comment_content = $content; + $cur->post_id = $_ctx->posts->post_id; + $cur->comment_status = $core->blog->settings->system->comments_pub ? 1 : -1; + $cur->comment_ip = http::realIP(); + + $redir = $_ctx->posts->getURL(); + $redir .= $core->blog->settings->system->url_scan == 'query_string' ? '&' : '?'; + + try + { + if (!text::isEmail($cur->comment_email)) { + throw new Exception(__('You must provide a valid email address.')); + } + + # --BEHAVIOR-- publicBeforeCommentCreate + $core->callBehavior('publicBeforeCommentCreate',$cur); + if ($cur->post_id) { + $comment_id = $core->blog->addComment($cur); + + # --BEHAVIOR-- publicAfterCommentCreate + $core->callBehavior('publicAfterCommentCreate',$cur,$comment_id); + } + + if ($cur->comment_status == 1) { + $redir_arg = 'pub=1'; + } else { + $redir_arg = 'pub=0'; + } + + header('Location: '.$redir.$redir_arg); + } + catch (Exception $e) + { + $_ctx->form_error = $e->getMessage(); + $_ctx->form_error; + } + } + } + + # The entry + self::serveDocument('post.html'); + } + } + } + + public static function preview($args) + { + $core = $GLOBALS['core']; + $_ctx = $GLOBALS['_ctx']; + + if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#',$args,$m)) { + # The specified Preview URL is malformed. + self::p404(); + } + else + { + $user_id = $m[1]; + $user_key = $m[2]; + $post_url = $m[3]; + if (!$core->auth->checkUser($user_id,null,$user_key)) { + # The user has no access to the entry. + self::p404(); + } + else + { + $_ctx->preview = true; + self::post($post_url); + } + } + } + + public static function feed($args) + { + $type = null; + $comments = false; + $cat_url = false; + $post_id = null; + $subtitle = ''; + + $mime = 'application/xml'; + + $_ctx =& $GLOBALS['_ctx']; + $core =& $GLOBALS['core']; + + if (preg_match('!^([a-z]{2}(-[a-z]{2})?)/(.*)$!',$args,$m)) { + $params = new ArrayObject(array('lang' => $m[1])); + + $args = $m[3]; + + $core->callBehavior('publicFeedBeforeGetLangs',$params,$args); + + $_ctx->langs = $core->blog->getLangs($params); + + if ($_ctx->langs->isEmpty()) { + # The specified language does not exist. + self::p404(); + return; + } else { + $_ctx->cur_lang = $m[1]; + } + } + + if (preg_match('#^rss2/xslt$#',$args,$m)) + { + # RSS XSLT stylesheet + self::serveDocument('rss2.xsl','text/xml'); + return; + } + elseif (preg_match('#^(atom|rss2)/comments/([0-9]+)$#',$args,$m)) + { + # Post comments feed + $type = $m[1]; + $comments = true; + $post_id = (integer) $m[2]; + } + elseif (preg_match('#^(?:category/(.+)/)?(atom|rss2)(/comments)?$#',$args,$m)) + { + # All posts or comments feed + $type = $m[2]; + $comments = !empty($m[3]); + if (!empty($m[1])) { + $cat_url = $m[1]; + } + } + else + { + # The specified Feed URL is malformed. + self::p404(); + return; + } + + if ($cat_url) + { + $params = new ArrayObject(array( + 'cat_url' => $cat_url, + 'post_type' => 'post')); + + $core->callBehavior('publicFeedBeforeGetCategories',$params,$args); + + $_ctx->categories = $core->blog->getCategories($params); + + if ($_ctx->categories->isEmpty()) { + # The specified category does no exist. + self::p404(); + return; + } + + $subtitle = ' - '.$_ctx->categories->cat_title; + } + elseif ($post_id) + { + $params = new ArrayObject(array( + 'post_id' => $post_id, + 'post_type' => '')); + + $core->callBehavior('publicFeedBeforeGetPosts',$params,$args); + + $_ctx->posts = $core->blog->getPosts($params); + + if ($_ctx->posts->isEmpty()) { + # The specified post does not exist. + self::p404(); + return; + } + + $subtitle = ' - '.$_ctx->posts->post_title; + } + + $tpl = $type; + if ($comments) { + $tpl .= '-comments'; + $_ctx->nb_comment_per_page = $core->blog->settings->system->nb_comment_per_feed; + } else { + $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_feed; + $_ctx->short_feed_items = $core->blog->settings->system->short_feed_items; + } + $tpl .= '.xml'; + + if ($type == 'atom') { + $mime = 'application/atom+xml'; + } + + $_ctx->feed_subtitle = $subtitle; + + header('X-Robots-Tag: '.context::robotsPolicy($core->blog->settings->system->robots_policy,'')); + self::serveDocument($tpl,$mime); + if (!$comments && !$cat_url) { + $core->blog->publishScheduledEntries(); + } + } + + public static function trackback($args) + { + if (!preg_match('/^[0-9]+$/',$args)) { + # The specified trackback URL is not an number + self::p404(); + } else { + $tb = new dcTrackback($GLOBALS['core']); + $tb->receive($args); + } + } + + public static function rsd($args) + { + $core =& $GLOBALS['core']; + http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); + + header('Content-Type: text/xml; charset=UTF-8'); + echo + ''."\n". + ''."\n". + "\n". + " Dotclear\n". + " http://www.dotclear.org/\n". + ' '.html::escapeHTML($core->blog->url)."\n"; + + if ($core->blog->settings->system->enable_xmlrpc) + { + $u = sprintf(DC_XMLRPC_URL,$core->blog->url,$core->blog->id); + + echo + " \n". + ' '."\n". + ' '."\n". + ' '."\n". + ' '."\n". + " \n"; + } + + echo + "\n". + "\n"; + } + + public static function xmlrpc($args) + { + $core =& $GLOBALS['core']; + $blog_id = preg_replace('#^([^/]*).*#','$1',$args); + $server = new dcXmlRpc($core,$blog_id); + $server->serve(); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/public/prepend.php b/v2/dotclear/inc/public/prepend.php new file mode 100644 index 0000000..29bc3a9 --- /dev/null +++ b/v2/dotclear/inc/public/prepend.php @@ -0,0 +1,156 @@ +setBlog(DC_BLOG_ID); + } catch (Exception $e) { + init_prepend_l10n(); + __error(__('Database problem') + ,DC_DEBUG ? + __('The following error was encountered while trying to read the database:').'

        • '.$e->getMessage().'
        ' : + __('Something went wrong while trying to read the database.') + ,20); + } +} + +if ($core->blog->id == null) { + __error(__('Blog is not defined.') + ,__('Did you change your Blog ID?') + ,30); +} + +if ((boolean)!$core->blog->status) { + $core->unsetBlog(); + __error(__('Blog is offline.') + ,__('This blog is offline. Please try again later.') + ,70); +} + +# Loading media +try { + $core->media = new dcMedia($core); +} catch (Exception $e) {} + +# Creating template context +$_ctx = new context(); +try { + $core->tpl = new dcTemplate(DC_TPL_CACHE,'$core->tpl',$core); +} catch (Exception $e) { + __error(__('Can\'t create template files.') + ,$e->getMessage() + ,40); +} + +# Loading locales +$_lang = $core->blog->settings->system->lang; +$_lang = preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$_lang) ? $_lang : 'en'; + +if (l10n::set(dirname(__FILE__).'/../../locales/'.$_lang.'/date') === false && $_lang != 'en') { + l10n::set(dirname(__FILE__).'/../../locales/en/date'); +} +l10n::set(dirname(__FILE__).'/../../locales/'.$_lang.'/public'); +l10n::set(dirname(__FILE__).'/../../locales/'.$_lang.'/plugins'); + +# Loading plugins +try { + $core->plugins->loadModules(DC_PLUGINS_ROOT,'public',$_lang); +} catch (Exception $e) {} + +# Loading themes +$core->themes = new dcThemes($core); +$core->themes->loadModules($core->blog->themes_path); + +# Defining theme if not defined +if (!isset($__theme)) { + $__theme = $core->blog->settings->system->theme; +} + +if (!$core->themes->moduleExists($__theme)) { + $__theme = $core->blog->settings->system->theme = 'default'; +} + +$__parent_theme = $core->themes->moduleInfo($__theme,'parent'); +if ($__parent_theme) { + if (!$core->themes->moduleExists($__parent_theme)) { + $__theme = $core->blog->settings->system->theme = 'default'; + $__parent_theme = null; + } +} + +# If theme doesn't exist, stop everything +if (!$core->themes->moduleExists($__theme)) { + __error(__('Default theme not found.') + ,__('This either means you removed your default theme or set a wrong theme '. + 'path in your blog configuration. Please check theme_path value in '. + 'about:config module or reinstall default theme. ('.$__theme.')') + ,50); +} + +# Loading _public.php file for selected theme +$core->themes->loadNsFile($__theme,'public'); + +# Loading translations for selected theme +if ($__parent_theme) { + $core->themes->loadModuleL10N($__parent_theme,$_lang,'main'); +} +$core->themes->loadModuleL10N($__theme,$_lang,'main'); + +# --BEHAVIOR-- publicPrepend +$core->callBehavior('publicPrepend',$core); + +# Prepare the HTTP cache thing +$mod_files = get_included_files(); +$mod_ts = array(); +$mod_ts[] = $core->blog->upddt; + +$__theme_tpl_path = array( + $core->blog->themes_path.'/'.$__theme.'/tpl' +); +if ($__parent_theme) { + $__theme_tpl_path[] = $core->blog->themes_path.'/'.$__parent_theme.'/tpl'; +} + +$core->tpl->setPath( + $__theme_tpl_path, + dirname(__FILE__).'/default-templates', + $core->tpl->getPath()); + +$core->url->mode = $core->blog->settings->system->url_scan; + +try { + # --BEHAVIOR-- publicBeforeDocument + $core->callBehavior('publicBeforeDocument',$core); + + $core->url->getDocument(); + + # --BEHAVIOR-- publicAfterDocument + $core->callBehavior('publicAfterDocument',$core); +} catch (Exception $e) { + __error($e->getMessage() + ,__('Something went wrong while loading template file for your blog.') + ,60); +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/public/rs.extension.php b/v2/dotclear/inc/public/rs.extension.php new file mode 100644 index 0000000..4c58ae3 --- /dev/null +++ b/v2/dotclear/inc/public/rs.extension.php @@ -0,0 +1,93 @@ +addBehavior('coreBlogGetPosts',array('rsExtendPublic','coreBlogGetPosts')); +$core->addBehavior('coreBlogGetComments',array('rsExtendPublic','coreBlogGetComments')); + +class rsExtendPublic +{ + public static function coreBlogGetPosts($rs) + { + $rs->extend('rsExtPostPublic'); + } + + public static function coreBlogGetComments($rs) + { + $rs->extend('rsExtCommentPublic'); + } +} + +class rsExtPostPublic extends rsExtPost +{ + public static function getContent($rs,$absolute_urls=false) + { + # Not very nice hack but it does the job :) + if (isset($GLOBALS['_ctx']) && $GLOBALS['_ctx']->short_feed_items === true) { + $_ctx =& $GLOBALS['_ctx']; + $c = parent::getContent($rs,$absolute_urls); + $c = context::remove_html($c); + $c = context::cut_string($c,350); + + $c = + '

        '.$c.'... '. + ''.__('Read').' '. + html::escapeHTML($rs->post_title).'

        '; + + return $c; + } + + if ($rs->core->blog->settings->system->use_smilies) + { + return self::smilies(parent::getContent($rs,$absolute_urls),$rs->core->blog); + } + + return parent::getContent($rs,$absolute_urls); + } + + public static function getExcerpt($rs,$absolute_urls=false) + { + if ($rs->core->blog->settings->system->use_smilies) + { + return self::smilies(parent::getExcerpt($rs,$absolute_urls),$rs->core->blog); + } + + return parent::getExcerpt($rs,$absolute_urls); + } + + protected static function smilies($c,$blog) + { + if (!isset($GLOBALS['__smilies'])) { + $GLOBALS['__smilies'] = context::getSmilies($blog); + } + return context::addSmilies($c); + } +} + +class rsExtCommentPublic extends rsExtComment +{ + public static function getContent($rs,$absolute_urls=false) + { + if ($rs->core->blog->settings->system->use_smilies) + { + $c = parent::getContent($rs,$absolute_urls); + + if (!isset($GLOBALS['__smilies'])) { + $GLOBALS['__smilies'] = context::getSmilies($rs->core->blog); + } + return context::addSmilies($c); + } + + return parent::getContent($rs,$absolute_urls); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/inc/swf/player_flv.swf b/v2/dotclear/inc/swf/player_flv.swf new file mode 100644 index 0000000..01ec373 Binary files /dev/null and b/v2/dotclear/inc/swf/player_flv.swf differ diff --git a/v2/dotclear/inc/swf/player_mp3.swf b/v2/dotclear/inc/swf/player_mp3.swf new file mode 100644 index 0000000..487b7df Binary files /dev/null and b/v2/dotclear/inc/swf/player_mp3.swf differ diff --git a/v2/dotclear/inc/swf/swfupload.swf b/v2/dotclear/inc/swf/swfupload.swf new file mode 100644 index 0000000..e3f7670 Binary files /dev/null and b/v2/dotclear/inc/swf/swfupload.swf differ diff --git a/v2/dotclear/index.php b/v2/dotclear/index.php new file mode 100644 index 0000000..36de246 --- /dev/null +++ b/v2/dotclear/index.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/v2/dotclear/locales/README b/v2/dotclear/locales/README new file mode 100644 index 0000000..d021aa6 --- /dev/null +++ b/v2/dotclear/locales/README @@ -0,0 +1 @@ +Please see http://www.dotclear.org/translate diff --git a/v2/dotclear/locales/en/date.lang.php b/v2/dotclear/locales/en/date.lang.php new file mode 100644 index 0000000..d56522b --- /dev/null +++ b/v2/dotclear/locales/en/date.lang.php @@ -0,0 +1,38 @@ + \ No newline at end of file diff --git a/v2/dotclear/locales/en/date.po b/v2/dotclear/locales/en/date.po new file mode 100644 index 0000000..16faca8 --- /dev/null +++ b/v2/dotclear/locales/en/date.po @@ -0,0 +1,116 @@ +msgid "%Y-%m-%d %H:%M" +msgstr "" + +msgid "_Jan" +msgstr "Jan" + +msgid "_Feb" +msgstr "Feb" + +msgid "_Mar" +msgstr "Mar" + +msgid "_Apr" +msgstr "Apr" + +msgid "_May" +msgstr "May" + +msgid "_Jun" +msgstr "Jun" + +msgid "_Jul" +msgstr "Jul" + +msgid "_Aug" +msgstr "Aug" + +msgid "_Sep" +msgstr "Sep" + +msgid "_Oct" +msgstr "Oct" + +msgid "_Nov" +msgstr "Nov" + +msgid "_Dec" +msgstr "Dec" + +msgid "January" +msgstr "" + +msgid "February" +msgstr "" + +msgid "March" +msgstr "" + +msgid "April" +msgstr "" + +msgid "May" +msgstr "" + +msgid "June" +msgstr "" + +msgid "July" +msgstr "" + +msgid "August" +msgstr "" + +msgid "September" +msgstr "" + +msgid "October" +msgstr "" + +msgid "November" +msgstr "" + +msgid "December" +msgstr "" + +msgid "_Mon" +msgstr "Mon" + +msgid "_Tue" +msgstr "Tue" + +msgid "_Wed" +msgstr "Wed" + +msgid "_Thu" +msgstr "Thu" + +msgid "_Fri" +msgstr "Fri" + +msgid "_Sat" +msgstr "Sat" + +msgid "_Sun" +msgstr "Sun" + +msgid "Monday" +msgstr "" + +msgid "Tuesday" +msgstr "" + +msgid "Wednesday" +msgstr "" + +msgid "Thursday" +msgstr "" + +msgid "Friday" +msgstr "" + +msgid "Saturday" +msgstr "" + +msgid "Sunday" +msgstr "" diff --git a/v2/dotclear/locales/en/help/blog_pref.html b/v2/dotclear/locales/en/help/blog_pref.html new file mode 100644 index 0000000..487a4f2 --- /dev/null +++ b/v2/dotclear/locales/en/help/blog_pref.html @@ -0,0 +1,148 @@ + + + Blog settings + + + + +

        Blog details

        +
        +
        Blog ID
        +
        Unique ID of the blog. Mandatory, can only be modified by a super administrator.
        + +
        Blog name
        +
        Name of the blog, mandatory.
        + +
        Blog URL
        +
        Complete blog URL. Mandatory, can only be modified by a super administrator.
        + +
        Blog status
        +
        +
          +
        • online: blog open to visitors
        • +
        • offline: blog closed to visitors + but open to writers.
        • +
        • removed: blog closed to both visitors and writers.
        • +
        +
        + +
        Blog description
        +
        Here you can put any description you want for your blog. This is a + simple text, without formatting.
        +
        + +

        Blog configuration

        +
        +
        Blog editor's name
        +
        Name of the person responsible for the blog. It may be the owner or the + publisher if he exists.
        + +
        Copyright notice
        +
        Note about the reproduction rights for the blog.
        + +
        Default language
        +
        Language of the blog interface. Either this language is among the avalaible ones, + and the interface will be translated, or it is not, and the interface will then + default to English.
        + +
        Blog timezone
        +
        Defines the timezone that will be used for comments and trackbacks.
        + +
        Date formats
        +
        The date format of the blog posts. See the date and time + format reference section below for further details.
        + +
        Time format
        +
        The time format of the blog posts. See the date and time + format reference section below for further details.
        + +
        Display .. entries per page
        +
        The number will be used as the limit for the number of entries + on the homepage, the first page of each category and the rss feeds.
        + +
        Leave comments open for .. days
        +
        The number of days during wich comments and trackbacks will be open + after the post publication. When empty, the comments will be kept open + indefinitly.
        + +
        Display smilies on entries and comments
        +
        Display sequences of characters as :-) or ;-) as images.
        + +
        Accept comments
        +
        Globally accepts comments on the blog. Takes precedence over the + per post setting.
        + +
        Accept trackbacks
        +
        Globally accepts trackbacks on the blog. Takes precedence over the + per post setting.
        + +
        Moderate comments and trackbacks
        +
        With such an option, comments and trackbacks will be published after + editor agreement.
        + +
        Add "nofollow" relation on comments and trackbacks links
        +
        Adds an attribute on inks in comments and trackbacks, instructing the + search bots not to follow them. This antispam features has yet to + prove its value.
        + +
        Wiki syntax for comments
        +
        Allows a subset of the wiki syntax to be used in comments.
        +
        + +

        Format reference

        +

        Some fields can be configured to format the date and time to your needs. +Here is a reference:

        + +
          +
        • %a: abreviated week day (local).
        • +
        • %A: complete week day (local).
        • +
        • %b: abbreviated month name (local).
        • +
        • %B: complete month name (local).
        • +
        • %c: default local date and time display.
        • +
        • %C: century (the year, divided by 100 and + round up beetween 00 and 99)
        • +
        • %d: numeric month day (from 01 to 31)
        • +
        • %D: identical to %m/%d/%y
        • +
        • %e: numeric month day (from 1 to 31)
        • +
        • %g: identical to %G, 2 digits.
        • +
        • %G: The year on 4 digits, according to the week number + (cf. %V). Format and value identical to %Y, except that if the number of the + week belongs to the preceding or following year, the current year + will be used instead.
        • +
        • %h: identical to %b
        • +
        • %H: digital hour of the day, 24H mode + (from 00 to 23)
        • +
        • %I: digital hour of the day, 12H mode + (from 01 to 12)
        • +
        • %j: day of the year, digital (from 001 to 366)
        • +
        • %m: month, digital (from 1 à 12)
        • +
        • %M: minute, digital
        • +
        • %n: newline character
        • +
        • %p: either `am' or `pm', according to the absolute time, + or depending of the local settings.
        • +
        • %r: the hour, a.m. and p.m. format
        • +
        • %R: the hour, 24h format
        • +
        • %S: seconds, digital
        • +
        • %t: tab character
        • +
        • %T: the current time (identical to %H:%M:%S)
        • +
        • %u: day week, digit, from 1 to 7. (1 stands for monday)
        • +
        • %U: week number, the first sundqy of the year being the + first day of the first week.
        • +
        • %V: the week numpber as defined in ISO + 8601:1988, digital, from 01 to 53. The week 1 is the first week + with more than 4 days in the current year, whose monday is the first day. + (Use %G or %g for the year elements corresponding to the number of the + week for the considered timestamp.)
        • +
        • %W: number of the week in the year, the first monday of + the year being the start of the first week
        • +
        • %w: day of the week, digital, 0 stands for sunday
        • +
        • %x: prefered format for the date without the time
        • +
        • %X: prefered format for the time without the date
        • +
        • %y: the year, two digits (from 00 to 99)
        • +
        • %Y: the year, four digits
        • +
        • %Z ou %z: timezone, name or abbreviation
        • +
        • %%: a litteral `%' character
        • +
        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/en/help/categories.html b/v2/dotclear/locales/en/help/categories.html new file mode 100644 index 0000000..becd08f --- /dev/null +++ b/v2/dotclear/locales/en/help/categories.html @@ -0,0 +1,47 @@ + + + Categories + + + +

        Categories list

        +
        +
        Create a new category
        +
        Choose the name you wish to use for your new category + then click on Save to validate its creation. you may leave it at the first level or choose its parent. + To modify your category, click on its name in the list.
        + +
        Delete a category
        +
        Choose the category you wish to delete in the selection menu and click ok. + A category can be deleted only if it contains no posts.
        + +
        Reorder categories
        +
        Use this option to place all categories at the same level. + If you wish to reorder a category respectively to other categories, + click on its name and choose its parent and its position respectively to its siblings.
        +
        + +

        Modifying a category

        +
        +
        Title
        +
        Choose the name you wish to give to your category + the click on Save to validate.
        + +
        URL
        +
        DotClear creates a default url for the category, making it reachable + with a path similar to this one : + http://monblog.tld/category/My-category. You can click + on the small lock on the right to modify it according to your needs.
        + +
        Description
        +
        What's inside the description field will be shown when selecting + one of the blog's categories. You can fill it with any kind of content + (paragraphs, lists, etc.) The format used for this field is HTML.
        + +
        Move this category
        +
        You can move the category as a child of any other by choosing its parent. + You can also specify the category position respectively to its siblings by placing it before or after any of them.
        +
        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/en/help/comments.html b/v2/dotclear/locales/en/help/comments.html new file mode 100644 index 0000000..83338f2 --- /dev/null +++ b/v2/dotclear/locales/en/help/comments.html @@ -0,0 +1,58 @@ + + + Comments + + + +

        Filters for comment list

        +
        +
        Type:
        +
        None, comments or trackbacks.
        + +
        Status:
        +
          +
        • junk:
        • +
        • pending:
        • +
        • unpublished: offline ;
        • +
        • published: online.
        • +
        + +
        Order by:
        +
        Choose: date, entry title, author or status, then + choose the sort order.
        + +
        Sort
        +
        Choose whether the list will be sorted in ascending or descending order.
        + +
        Comment author.
        +
        The search is not case sensitive. If you want to search only a part of + the name, you can use the * generic character.
        + +
        Comments per page :
        +
        Allows you to choose the number of comments on each page of this list.
        +
        + +

        Modify or add a comment

        +
        +
        Author:
        +
        Author name. This field is mandatory.
        + +
        Email:
        +
        Author's email address.
        + +
        Web site:
        +
        URL of the comment author's blog or website.
        + +
        Status:
        +
          +
        • junk:
        • +
        • pending:
        • +
        • unpublished: offline
        • +
        • published: online
        • +
        + +
        Comment:
        +
        Comment's content. This field is html formated.
        +
        + + diff --git a/v2/dotclear/locales/en/help/media.html b/v2/dotclear/locales/en/help/media.html new file mode 100644 index 0000000..052f40f --- /dev/null +++ b/v2/dotclear/locales/en/help/media.html @@ -0,0 +1,90 @@ + + + Media manager + + + + +

        Media list

        +

        The media manager's main page displays a list of available files (medias) +for the currently selected blog.

        + +

        You can click the icons or names of the medias in the folder or any +subfolder to display them.

        + +

        Selecting a MP3 sound file displays a player to immediately play +the file. The Flash plugin must be installed on your browser for +this player to operate.

        + +

        The images are displayed with a thumbnail when possible.

        + +

        Add files

        +
        +
        Choose a file
        +
        Here you can select one of your local files to send it on your + blog. Should the file be bigger than the maximum size, it will not be sent.
        + +
        Title
        +
        Optional title for the file you want to send.
        + +
        Private
        +
        Check this if you want the file to be private, i.e. accessible only by + its owner and not accessible from the blog.
        +
        + +

        New folder

        +

        This form allows you to create an new subfolder in the currently displayed +folder. Just choose a name for it as you would do on your own computer.

        + +

        Media details

        +

        This page displays a set of informations about the currently selected +file and allows you to perform a few operations.

        + +
        +
        File name
        +
        Change the file name of the current file.
        + +
        File title
        +
        Give a title to the file, or change the current one.
        + +
        File date
        +
        Change the file date.
        + +
        Private
        +
        Specify that the file will be hidden to anyone but its owner in the media manager.
        + +
        New directory
        +
        Allows you to move the file in another folder.
        + +
        Change file
        +
        With this form, you can replace the file while keeping all the attributes + (name, title...). As always, you have to respect the maximum file size.
        +
        + +

        A specific treatment for images

        +

        When you add an image, depending of its size, up to four versions will be made available:

        +
          +
        • square: a 48x48 pixels square thumbnail,
        • +
        • thumbnail: the image reduced to 100 pixels on its longest side.
        • +
        • small: the longuest side is reduced to 240 pixels.
        • +
        • medium: the longuest side is reduced to 500 pixels.
        • +
        • original: the original, unchanged image.
        • +
        +

        Default size for the versions thumbnail, small and medium can be modified in your blog's parameters.

        + +

        Add a file to an entry

        +

        You can easily attach a file to one of your posts. First, create a new post +and save it. Then click on "Add files to this entry" to open the media manager.

        + +

        Just click on the [+] icon (Attach this file to entry). The file will then +be attached to the post.

        + +

        The attached files will be shown on the post page, as a list beside the post content. +The MP3 files will be made available through the Flash player, so that they can easily by read by visitors.

        + +

        Podcast and broadcasting of music files

        +

        All attached files will be included in your RSS feeds, allowing for easy +podcasting of any type of file.

        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/en/help/post.html b/v2/dotclear/locales/en/help/post.html new file mode 100644 index 0000000..8fa872b --- /dev/null +++ b/v2/dotclear/locales/en/help/post.html @@ -0,0 +1,125 @@ + + + Editing an entry + + + + +

        Editing an entry

        + +
        +
        Entry title
        +
        Type in the entry title. This field is mandatory.
        + +
        Excerpt
        +
        The content of this field will be shown in pages showing entries as + a list, such as the homepage or categories pages, followed by a "continue reading" + link. It will also be visible at the begining of the page showing the + whole post. Should you leave it blank, the content field will be shown + on the homepage and other entry list pages.
        + +
        Content
        +
        The content of your entry. This field is mandatory.
        + +
        Notes
        +
        This text area is for personal use, for notes or memos. What you enter here + will never be displayed on the blog..
        + +
        Category
        +
        Your entry's category. To create a new category, please see the + "categories" section. You can choose to leave + you entry without a category by selecting the blank line.
        + +
        Entry status
        +
        Allows you to choose your post's status: +
          +
        • pending: the publication status has not yet been decided.
        • +
        • scheduled: the post will be set online at the time and date + set in the Published on field.
        • +
        • unpublished: the post is offline.
        • +
        • published: the post is online.
        • +
        +
        + +
        Published on
        +
        Here you can change the post's publication date and time. If the post's + status is scheduled, it will come online at the said date and time.
        + +
        Text formating
        +
        To choose the post syntax. Wiki is a simplified syntax + and will be converted to valid xhtml ; Unless you have a perfect + understanding of xhtml, we advise you to choose the wiki syntax. + See the Wiki syntax reference for more information.
        + +
        Accept comments
        +
        Select this box to allow your visitors to comment the entry. The corresponding + global setting is to be found in the blog settings.
        + +
        Accept trackbacks
        +
        A trackback is a way to let a portion of your entry as a comment on + another blog. Select this box to allow others to trackback your entry. The + corresponding global setting is to be found in the blog settings.
        + +
        Selected entry
        +
        The selected entries will be listed on your blog menu, under the title "Best + of me".
        + +
        Entry password
        +
        You can enter a password for your entry. A password protected entry will + not be displayed on your blog, it will only be reachable to those you will + give the entry url (see the view entry link) and password.
        + +
        Basename
        +
        By clicking on its lock, you can unprotect this field and and choose another + URL for your entry. If the URL you're trying to use is already used by another + entry, a number will be added to it.
        + +
        Entry lang.
        +
        The two character language code of your entry. It defaults to your (as a + user) language but you can change it to whatever language code you want, ie. + "en" or "fr-qc". This code is used for the entry's display, no chack is made + on it.
        + +
        Attachments
        +
        The attachments are all the medias you joined to the entry. The link add + file to this entry allows you to attach other files to the entry. Remember + to save your changes before clicking on this link. For further information, + see the media manager help.
        +
        + +

        Trackback

        +

        To make a trackback, click on the Ping blogs link. +You will not see this link if your entry's status is not published.

        + +
        +
        URLs to ping
        +
        Paste here the trackback URLs you found in the posts you want to ping.
        + + +
        Send excerpt
        +
        This field defaults to your entry's first few sentences. That's wht will be + sent to the blog you're pinging, along with a link to your post. You can modify + the excerpt by editing this field.
        + + +
        Auto discover ping URLs
        +
        If your entries links specific posts and if the blog carrying those posts + is set to allow it, this tool will find the URLs to ping for you.
        +
        + +

        Comments

        +
        +
        Comment list
        +
        On the Comments tab, you can read and change the status of + your entry's comments. If you're allowed to by the blog administrator, + you will be able to set your comments online or offline, to delete them or + to mark them as junk.
        + +
        Add a comment
        +
        On the Add a comment tab, you can reply to one of your entry's + comments without leaving your blog's backend. The syntax to be used here is + plain, unlimited, xhtml. Use the fields as if you were modifying a comment.
        +
        + + + diff --git a/v2/dotclear/locales/en/help/posts.html b/v2/dotclear/locales/en/help/posts.html new file mode 100644 index 0000000..82730c6 --- /dev/null +++ b/v2/dotclear/locales/en/help/posts.html @@ -0,0 +1,65 @@ + + + Managing entries + + + + +

        Filters for the entry list

        + +
        +
        Author
        +
        Allows you to filter by author.
        + +
        Category
        +
        Filter by category.
        + +
        Status
        +
          +
        • pending: posts whose publication status has not yet + been decided.
        • +
        • scheduled: post to be set online automatically.
        • +
        • unpublished: offline posts.
        • +
        • published: online posts.
        • +
        + +
        Selected
        +
        Only the selected posts, or the non selected ones.
        + +
        Month
        +
        Filter on a specific month and year.
        + +
        Language
        +
        Filter on the different languages used on the blog.
        + +
        Order by
        +
        Defines the list order, either on the date, the title, the author, + the status or the selected status.
        + +
        Sort
        +
        Choose whether the list will be sorted in ascending or descending order.
        + +
        Entries per page
        +
        Number of entries that will be displayed on each page of the resulting + set.
        +
        + +

        Selected entry actions

        +

        If your user permission level allows it, you can perform batch operations on the +entries you select in the list.

        + +
          +
        • Publish: set the entries online.
        • +
        • Unpublish: set the entries offline.
        • +
        • Schedule: schedule the entries to be set online at their + publication date.
        • +
        • Mark as pending: erase other publication status.
        • +
        • Change category: directs you to the category list to choose a + new one for the entries.
        • +
        • Change author: allows you to change the posts' author by + entering the new author's ID.
        • +
        • Delete: suppress the posts (this operation cannot be undone).
        • +
        + + + diff --git a/v2/dotclear/locales/en/help/user.html b/v2/dotclear/locales/en/help/user.html new file mode 100644 index 0000000..fb9283c --- /dev/null +++ b/v2/dotclear/locales/en/help/user.html @@ -0,0 +1,71 @@ + + + User + + + + +

        User information

        +
        +
        User name
        +
        At least 2 characters (non accentuated characters, numbers or symbols, no white space. This field is mandatory.
        + +
        Password
        +
        Write down twice your password in the fields Password and Confirm password. The minimal password length is 6 characters. + These two fields are mandatory.
        + +
        Name, First name
        +
        If the display name is empty, the author's name will be displayed + as his Firstname and Name as set here.
        + +
        Display name
        +
        You can choose here a nickname that will be displayed in your posts instead of your name and first name.
        + +
        Email
        +
        Address that will be used to send a new password on dotclear's new password generation page.
        + +
        URL
        +
        The user's web site. If set, the author name will be displayed as + a link to the said site.
        + +
        Preferred format
        +
        To choose the prefered post syntax. Wiki is a simplified syntax + and will be converted to valid xhtml ; Unless you have a perfect + understanding of html, we advise you to choose the wiki syntax.
        + +
        Default entry status
        +
        The default entry status can be set to: +
          +
        • pending: the publication status has not yet been decided.
        • +
        • scheduled: the post will be set online at the time and date + set in the Published on field.
        • +
        • unpublished: the post is offline.
        • +
        • published: the post is online.
        • +
        +
        + +
        Entry edit field height
        +
        The height of the Entry edit field. Defaults to 24.
        + +
        User language
        +
        The language in wich one publishes his posts. If the translation exists, + the interface will also be displayed in that language.
        + +
        User timezone
        +
        That choice will define the time displayed on post publication.
        + +
        Password change required to connect
        +
        Check this box to force the new user to change his password on login.
        + +
        Super administrator
        +
        This choice will give all permissions to the user on all of your installation's blogs.
        +
        + +

        Tags

        +
        +
        Tags list format
        +
        This choice allows, in post editing page, to display all available tags + or only most used ones.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/locales/en/help/user_pref.html b/v2/dotclear/locales/en/help/user_pref.html new file mode 100644 index 0000000..a6de71b --- /dev/null +++ b/v2/dotclear/locales/en/help/user_pref.html @@ -0,0 +1,84 @@ + + + User preferences + + + + +

        My profile

        +
        +
        Name, First name
        +
        If the display name is empty, the author's name will be displayed + as his Firstname and Name as set here.
        + +
        Display name
        +
        You can choose here a nickname that will be displayed in your posts instead of your name and first name.
        + +
        Email
        +
        Address where will be sent the new comment notifications.
        + +
        URL
        +
        The user's web site. If set, the author name will be displayed as + a link to the said site.
        + +
        User language
        +
        The language in wich one publishes his posts. If the translation exists, + the interface will also be displayed in that language.
        + +
        User timezone
        +
        That choice will define the time displayed on post publication.
        + +
        Change your password
        +
        To change your password, write down twice your password in the fields + New password and Confirm password. The minimal password length is 6 characters. + If no new password is provided, the current one is kept.
        +
        +

        My options

        +
        +
        Preferred format
        +
        To choose the prefered post syntax. Wiki is a simplified syntax + and will be converted to valid xhtml ; Unless you have a perfect + understanding of html, we advise you to choose the wiki syntax.
        + +
        Default entry status
        +
        The default entry status can be set to: +
          +
        • pending: the publication status has not yet been decided.
        • +
        • scheduled: the post will be set online at the time and date + set in the Published on field.
        • +
        • unpublished: the post is offline.
        • +
        • published: the post is online.
        • +
        +
        + +
        Entry edit field height
        +
        The height of the Entry edit field. Defaults to 24.
        + +
        Enable WYSIWYG mode
        +
        Allow the use of the WYSIWYG editor for the posts and description fields.
        + +
        Activate enhanced uploader in media manager
        +
        This option allows you to upload several files at the same time on your server. + Needs the presence of Flash plugin in your browser.
        + +
        Dashboard modules
        +
        Choices made in this zone will be reflected on your blog's dashboard.
        + +
        Tags list format
        +
        This choice allows, in post editing page, to display all available tags + or only most used ones.
        +
        +

        My favorites

        +
        +
        My favorites
        +
        This zone displays the chosen favorites on the blog's dashboard. + They can be reordered with your mouse if javascript is activated in your browser. + Otherwise, a field allows to give them an ordering number. When you are finished, do not forget + to save your changes. You may also select favorites to be deleted from the list.
        + +
        Available favorites
        +
        All plugins allowing to be put as favorites are diplayed here. Check the corresponding boxes + and add them to the dashboard.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/locales/en/help/wiki.html b/v2/dotclear/locales/en/help/wiki.html new file mode 100644 index 0000000..3bf005e --- /dev/null +++ b/v2/dotclear/locales/en/help/wiki.html @@ -0,0 +1,85 @@ + + + Wiki syntax reference + + + + +

        Wiki syntax reference

        + +

        The wiki syntax is a way to enhance your text with a minimal set +of tags, studied to cover the basic needs (titles, paragraphs, quotes, +lists...)

        + +
        +
        Block elements
        +
        +
          +
        • Leave an empty line between two similar blocks.
        • +
        • Paragraph: free text, ended by an empty line if + another paragraph is to follow.
        • +
        • Title: !!! title, !! title + or ! title, allowing you to use three different levels of + heading.
        • +
        • Horizontal line: ----
        • +
        • Lists: Start each line with + * or # for unnumbered or numbered lists respectively. + List imbrication is done by mixing list markers this way: +
          +* item 1
          +** item 1.1
          +* item 2
          +*# item 2.1
          +...
          +
          +
        • +
        • Preformatted text: each line must start with a space.
        • +
        • Block quote: each line must start with a semicolon (>).
        • +
        +
        + +
        Formatting tags
        +
        +
          +
        • Emphasis: two quotes ''text''
        • +
        • Strong emphasis: two underscore __text__
        • +
        • New line: %%%
        • +
        • Insertion: two plusses ++text++
        • +
        • Deletion: two minuses --text--
        • +
        • Link: [url], [name|url], + [name|url|language] or [name|url|languagee|title]
        • +
        • Image: + ((url|alternative text)), + ((url|alternative text|position)) or + ((url|alternative text|position|long description)). +
          The position can be either L or G (left), R or D (right) or C (centered).
        • +
        • Anchor: ~anchor~
        • +
        • Acronym: ??acronym|title??
        • +
        • Inline HTML: two backquotes ``html code``
        • +
        • Inline quote: {{quote}}, + {{quote|language}} or {{quote|language|url}}
        • +
        • Code: @@code@@
        • +
        • Footnotes: $$footnote$$
        • +
        +
        + +
        Unformatted text
        +
        If you don't want one formatting character to be interpreted as such, add a +\ just before it. This way: +\[text in brackets without being a link\] +
        + +
        HTML Code
        +
        Even if you chose the wiki syntax, you may sometimes need a more +powerful formatting, i.e. HTML syntax. Do it the following way: +
        +///html
        +<p style="color:red">my text in red</p>
        +///
        +
        +
        +
        + + + + diff --git a/v2/dotclear/locales/en/main.lang.php b/v2/dotclear/locales/en/main.lang.php new file mode 100644 index 0000000..5b13df9 --- /dev/null +++ b/v2/dotclear/locales/en/main.lang.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/v2/dotclear/locales/en/main.po b/v2/dotclear/locales/en/main.po new file mode 100644 index 0000000..39c6cfd --- /dev/null +++ b/v2/dotclear/locales/en/main.po @@ -0,0 +1,2785 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-11-06 18:11+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Dotclear has been upgraded." +msgstr "" + +msgid "Password reset" +msgstr "" + +msgid "Someone has requested to reset the password for the following site and username." +msgstr "" + +msgid "Username:" +msgstr "" + +msgid "To reset your password visit the following address, otherwise just ignore this email and nothing will happen." +msgstr "" + +#, php-format +msgid "The e-mail was sent successfully to %s." +msgstr "" + +msgid "Your new password" +msgstr "" + +msgid "Password:" +msgstr "" + +msgid "Your new password is in your mailbox." +msgstr "" + +msgid "Passwords don't match" +msgstr "" + +msgid "You didn't change your password." +msgstr "" + +msgid "You have to change your password before you can login." +msgstr "" + +msgid "In order to login, you have to change your password now." +msgstr "" + +msgid "Safe Mode can only be used for super administrators." +msgstr "" + +msgid "Wrong username or password" +msgstr "" + +msgid "Back to login screen" +msgstr "" + +msgid "Request a new password" +msgstr "" + +msgid "Email:" +msgstr "" + +msgid "recover" +msgstr "" + +msgid "Change your password" +msgstr "" + +msgid "New password:" +msgstr "" + +msgid "Confirm password:" +msgstr "" + +msgid "change" +msgstr "" + +msgid "Safe mode login" +msgstr "" + +msgid "This mode allows you to login without activating any of your plugins. This may be useful to solve compatibility problems" +msgstr "" + +msgid "Disable or delete any plugin suspected to cause trouble, then log out and log back in normally." +msgstr "" + +msgid "Remember my ID on this computer" +msgstr "" + +msgid "log in" +msgstr "" + +msgid "You must accept cookies in order to use the private area." +msgstr "" + +msgid "Get back to normal authentication" +msgstr "" + +msgid "Connection issue?" +msgstr "" + +msgid "I forgot my password" +msgstr "" + +msgid "I want to log in in safe mode" +msgstr "" + +msgid "New blog" +msgstr "" + +msgid "Blogs" +msgstr "" + +msgid "Blog ID:" +msgstr "" + +msgid "Required field" +msgstr "" + +msgid "At least 2 characters using letters, numbers or symbols." +msgstr "" + +msgid "Please note that changing your blog ID may require changes in your public index.php file." +msgstr "" + +msgid "Blog name:" +msgstr "" + +msgid "Blog URL:" +msgstr "" + +msgid "Blog description:" +msgstr "" + +msgid "Create" +msgstr "" + +msgid "No such blog ID" +msgstr "" + +msgid "Password verification failed" +msgstr "" + +msgid "Delete a blog" +msgstr "" + +msgid "Warning" +msgstr "" + +#, php-format +msgid "You are about to delete the blog %s. Every entry, comment and category will be deleted." +msgstr "" + +msgid "Please give your password to confirm the blog deletion." +msgstr "" + +msgid "Your password:" +msgstr "" + +msgid "Delete this blog" +msgstr "" + +msgid "No given blog id." +msgstr "" + +msgid "No such blog." +msgstr "" + +msgid "year/month/day/title" +msgstr "" + +msgid "year/month/title" +msgstr "" + +msgid "year/title" +msgstr "" + +msgid "title" +msgstr "" + +msgid "Title" +msgstr "" + +msgid "Title, Date" +msgstr "" + +msgid "Title, Country, Date" +msgstr "" + +msgid "Title, City, Country, Date" +msgstr "" + +msgid "I would like search engines and archivers to index and archive my blog's content." +msgstr "" + +msgid "I would like search engines and archivers to index but not archive my blog's content." +msgstr "" + +msgid "I would like to prevent search engines and archivers from indexing or archiving my blog's content." +msgstr "" + +msgid "That blog Id is already in use." +msgstr "" + +msgid "Invalid language code" +msgstr "" + +msgid "Blog settings" +msgstr "" + +msgid "Warning: except for special configurations, it is generally advised to have a trailing \"/\" in your blog URL in PATH_INFO mode." +msgstr "" + +msgid "Warning: except for special configurations, it is generally advised to have a trailing \"?\" in your blog URL in QUERY_STRING mode." +msgstr "" + +msgid "Blog has been successfully created." +msgstr "" + +msgid "Blog has been successfully updated." +msgstr "" + +msgid "Parameters" +msgstr "" + +msgid "Blog details" +msgstr "" + +msgid "URL scan method:" +msgstr "" + +msgid "Blog status:" +msgstr "" + +msgid "Blog configuration" +msgstr "" + +msgid "Blog editor name:" +msgstr "" + +msgid "Default language:" +msgstr "" + +msgid "Blog timezone:" +msgstr "" + +msgid "Copyright notice:" +msgstr "" + +msgid "New post URL format:" +msgstr "" + +msgid "Enable XML/RPC interface" +msgstr "" + +msgid "more information" +msgstr "" + +msgid "Comments and trackbacks" +msgstr "" + +msgid "Accept comments" +msgstr "" + +msgid "Moderate comments" +msgstr "" + +#, php-format +msgid "Leave comments open for %s days" +msgstr "" + +msgid "Leave blank to disable this feature." +msgstr "" + +msgid "Wiki syntax for comments" +msgstr "" + +msgid "Accept trackbacks" +msgstr "" + +msgid "Moderate trackbacks" +msgstr "" + +#, php-format +msgid "Leave trackbacks open for %s days" +msgstr "" + +msgid "Add \"nofollow\" relation on comments and trackbacks links" +msgstr "" + +msgid "Blog presentation" +msgstr "" + +msgid "Date format:" +msgstr "" + +msgid "Time format:" +msgstr "" + +msgid "Display smilies on entries and comments" +msgstr "" + +#, php-format +msgid "Display %s entries per page" +msgstr "" + +#, php-format +msgid "Display %s entries per feed" +msgstr "" + +#, php-format +msgid "Display %s comments per feed" +msgstr "" + +msgid "Truncate feeds" +msgstr "" + +msgid "Media and images" +msgstr "" + +msgid "Generated image sizes (in pixels)" +msgstr "" + +msgid "Thumbnails:" +msgstr "" + +msgid "Small:" +msgstr "" + +msgid "Medium:" +msgstr "" + +msgid "Inserted image title" +msgstr "" + +msgid "This defines image tag title when you insert it in a post from the media manager. It is retrieved from the picture's metadata." +msgstr "" + +msgid "Search engines robots policy" +msgstr "" + +msgid "Save" +msgstr "" + +msgid "XML/RPC interface" +msgstr "" + +msgid "XML/RPC interface allows you to edit your blog with an external client." +msgstr "" + +msgid "XML/RPC interface is not active. Change settings to enable it." +msgstr "" + +msgid "XML/RPC interface is active. You should set the following parameters on your XML/RPC client:" +msgstr "" + +msgid "Server URL:" +msgstr "" + +msgid "Blogging system:" +msgstr "" + +msgid "User name:" +msgstr "" + +msgid "your password" +msgstr "" + +msgid "Users" +msgstr "" + +msgid "Users on this blog" +msgstr "" + +msgid "No users" +msgstr "" + +msgid "Change permissions" +msgstr "" + +msgid "Super administrator" +msgstr "" + +msgid "You can't remove default theme." +msgstr "" + +msgid "Theme does not exist." +msgstr "" + +msgid "Unable to move uploaded file." +msgstr "" + +msgid "An error occurred while downloading the file." +msgstr "" + +#, php-format +msgid "by %s" +msgstr "" + +#, php-format +msgid "version %s" +msgstr "" + +#, php-format +msgid "(built on \"%s\")" +msgstr "" + +#, php-format +msgid "(requires \"%s\")" +msgstr "" + +msgid "Stylesheet" +msgstr "" + +msgid "Configure theme" +msgstr "" + +msgid "Blog appearance" +msgstr "" + +msgid "Theme has been successfully changed." +msgstr "" + +msgid "Theme has been successfully installed." +msgstr "" + +msgid "Theme has been successfully upgraded" +msgstr "" + +msgid "Theme has been successfully deleted." +msgstr "" + +#, php-format +msgid "You can find additional themes for your blog on %s." +msgstr "" + +msgid "To install or upgrade a theme you generally just need to upload it in \"Install or upgrade a theme\" section." +msgstr "" + +msgid "Themes" +msgstr "" + +#, php-format +msgid "You are currently using \"%s\"" +msgstr "" + +msgid "Use selected theme" +msgstr "" + +msgid "Delete selected theme" +msgstr "" + +msgid "Install or upgrade a theme" +msgstr "" + +msgid "You can install themes by uploading or downloading zip files." +msgstr "" + +msgid "Upload a zip file" +msgstr "" + +msgid "Theme zip file:" +msgstr "" + +msgid "Upload theme" +msgstr "" + +msgid "Download a zip file" +msgstr "" + +msgid "Theme zip file URL:" +msgstr "" + +msgid "Download theme" +msgstr "" + +msgid "To enable this function, please give write access to your themes directory." +msgstr "" + +msgid "Theme configuration" +msgstr "" + +msgid "back" +msgstr "" + +msgid "Last update" +msgstr "" + +msgid "Blog name" +msgstr "" + +msgid "Blog ID" +msgstr "" + +msgid "Descending" +msgstr "" + +msgid "Ascending" +msgstr "" + +msgid "List of blogs" +msgstr "" + +msgid "Blog has been successfully deleted." +msgstr "" + +msgid "Create a new blog" +msgstr "" + +msgid "Filters" +msgstr "" + +msgid "Order by:" +msgstr "" + +msgid "Sort:" +msgstr "" + +msgid "Search:" +msgstr "" + +msgid "Blogs per page" +msgstr "" + +msgid "Apply filters" +msgstr "" + +msgid "No blog" +msgstr "" + +msgid "Page(s)" +msgstr "" + +msgid "Entries" +msgstr "" + +msgid "Status" +msgstr "" + +#, php-format +msgid "Edit blog %s" +msgstr "" + +msgid "edit" +msgstr "" + +#, php-format +msgid "Switch to blog %s" +msgstr "" + +msgid "This category does not exist." +msgstr "" + +msgid "Categories" +msgstr "" + +msgid "The category has been successfully created." +msgstr "" + +msgid "The category has been successfully removed." +msgstr "" + +msgid "Categories have been successfully reordered." +msgstr "" + +msgid "The category has been successfully moved." +msgstr "" + +msgid "No category yet." +msgstr "" + +msgid "Categories list" +msgstr "" + +#, php-format +msgid "%d entries" +msgstr "" + +#, php-format +msgid "%d entry" +msgstr "" + +msgid "total:" +msgstr "" + +msgid "URL:" +msgstr "" + +msgid "Add a new category" +msgstr "" + +msgid "Title:" +msgstr "" + +msgid "Parent:" +msgstr "" + +msgid "Top level" +msgstr "" + +msgid "Remove a category" +msgstr "" + +msgid "Choose a category to remove:" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Reorder categories" +msgstr "" + +msgid "This will relocate all categories on the top level" +msgstr "" + +msgid "Reorder" +msgstr "" + +msgid "New category" +msgstr "" + +msgid "Category has been successfully updated." +msgstr "" + +msgid "Category information" +msgstr "" + +msgid "Warning: If you set the URL manually, it may conflict with another category." +msgstr "" + +msgid "Description:" +msgstr "" + +msgid "Move this category" +msgstr "" + +msgid "Category parent" +msgstr "" + +msgid "Category sibling" +msgstr "" + +msgid "Move current category" +msgstr "" + +msgid "after" +msgstr "" + +msgid "before" +msgstr "" + +msgid "position: " +msgstr "" + +msgid "Entry does not exist." +msgstr "" + +msgid "No comment" +msgstr "" + +msgid "You can't edit this comment." +msgstr "" + +msgid "Edit comment" +msgstr "" + +msgid "Comment has been successfully updated." +msgstr "" + +#, php-format +msgid "Your comment on my blog %s" +msgstr "" + +#, php-format +msgid "" +"Hi!\n" +"\n" +"You wrote a comment on:\n" +"%s\n" +"\n" +"\n" +msgstr "" + +msgid "Send an e-mail" +msgstr "" + +msgid "IP address:" +msgstr "" + +msgid "Date:" +msgstr "" + +msgid "Author:" +msgstr "" + +msgid "Web site:" +msgstr "" + +msgid "Status:" +msgstr "" + +msgid "Comment:" +msgstr "" + +msgid "comment" +msgstr "" + +msgid "trackback" +msgstr "" + +msgid "Date" +msgstr "" + +msgid "Entry title" +msgstr "" + +msgid "Author" +msgstr "" + +msgid "publish" +msgstr "" + +msgid "unpublish" +msgstr "" + +msgid "mark as pending" +msgstr "" + +msgid "mark as junk" +msgstr "" + +msgid "Type:" +msgstr "" + +msgid "Comments per page" +msgstr "" + +msgid "Comment author:" +msgstr "" + +msgid "You have one spam comments." +msgstr "" + +msgid "Show it." +msgstr "" + +#, php-format +msgid "You have %s spam comments." +msgstr "" + +msgid "Show them." +msgstr "" + +msgid "Selected comments action:" +msgstr "" + +msgid "action: " +msgstr "" + +msgid "ok" +msgstr "" + +msgid "Comments" +msgstr "" + +#, php-format +msgid "%d comment" +msgstr "" + +#, php-format +msgid "%d comments" +msgstr "" + +msgid "New entry" +msgstr "" + +msgid "My preferences" +msgstr "" + +msgid "Documentation and support" +msgstr "" + +msgid "Latest news" +msgstr "" + +msgid "Dashboard" +msgstr "" + +msgid "Make this blog my default blog" +msgstr "" + +msgid "This blog is offline" +msgstr "" + +msgid "This blog is removed" +msgstr "" + +msgid "is not defined, you should edit your configuration file." +msgstr "" + +msgid "Following plugins have been installed:" +msgstr "" + +msgid "Following plugins have not been installed:" +msgstr "" + +#, php-format +msgid "Dotclear %s is available!" +msgstr "" + +msgid "Upgrade now" +msgstr "" + +msgid "Remind me later" +msgstr "" + +msgid "information about this version" +msgstr "" + +msgid "Some plugins are installed twice:" +msgstr "" + +msgid "Quick entry" +msgstr "" + +msgid "Content:" +msgstr "" + +msgid "Category:" +msgstr "" + +msgid "Save and publish" +msgstr "" + +#, php-format +msgid "PHP version is %s (5.0 or earlier needed)." +msgstr "" + +msgid "Multibyte string module (mbstring) is not available." +msgstr "" + +msgid "Iconv module is not available." +msgstr "" + +msgid "Output control functions are not available." +msgstr "" + +msgid "SimpleXML module is not available." +msgstr "" + +msgid "DOM XML module is not available." +msgstr "" + +msgid "PCRE engine does not support UTF-8 strings." +msgstr "" + +msgid "SPL module is not available." +msgstr "" + +#, php-format +msgid "MySQL version is %s (4.1 or earlier needed)." +msgstr "" + +msgid "MySQL InnoDB engine is not available." +msgstr "" + +#, php-format +msgid "PostgreSQL version is %s (8.0 or earlier needed)." +msgstr "" + +msgid "Please set a master key (DC_MASTER_KEY) in configuration file." +msgstr "" + +msgid "Dotclear is already installed." +msgstr "" + +msgid "Dotclear cannot be installed." +msgstr "" + +msgid "No user ID given" +msgstr "" + +msgid "User ID must contain at least 2 characters using letters, numbers or symbols." +msgstr "" + +msgid "Invalid email address" +msgstr "" + +msgid "No password given" +msgstr "" + +msgid "Password must contain at least 6 characters." +msgstr "" + +msgid "My first blog" +msgstr "" + +msgid "%A, %B %e %Y" +msgstr "" + +msgid "Welcome to Dotclear!" +msgstr "" + +msgid "This is your first entry. When you're ready to blog, log in to edit or delete it." +msgstr "" + +msgid "Dotclear Team" +msgstr "" + +msgid "" +"

        This is a comment.

        \n" +"

        To delete it, log in and view your blog's comments. Then you might remove or edit it.

        " +msgstr "" + +msgid "Dotclear Install" +msgstr "" + +msgid "show" +msgstr "" + +msgid "Dotclear installation" +msgstr "" + +#, php-format +msgid "Cache directory %s is not writable." +msgstr "" + +msgid "Errors:" +msgstr "" + +msgid "Configuration file has been successfully created." +msgstr "" + +msgid "User information" +msgstr "" + +msgid "Please provide the following information needed to create the first user." +msgstr "" + +msgid "First Name:" +msgstr "" + +msgid "Last Name:" +msgstr "" + +msgid "Username and password" +msgstr "" + +msgid "All done!" +msgstr "" + +msgid "Dotclear has been successfully installed. Here is some useful information you should keep." +msgstr "" + +msgid "Your account" +msgstr "" + +msgid "Your blog" +msgstr "" + +msgid "Blog address:" +msgstr "" + +msgid "Administration interface:" +msgstr "" + +msgid "Manage your blog now" +msgstr "" + +msgid "Installation can not be completed" +msgstr "" + +msgid "For the said reasons, Dotclear can not be installed. Please refer to the documentation to learn how to correct the problem." +msgstr "" + +#, php-format +msgid "File %s does not exist." +msgstr "" + +#, php-format +msgid "Cannot write %s file." +msgstr "" + +msgid "Dotclear installation wizard" +msgstr "" + +msgid "Welcome" +msgstr "" + +msgid "To complete your Dotclear installation and start writing on your blog, we just need to know how to access your database and who you are. Just fill this two steps wizard with this information and we will be done." +msgstr "" + +msgid "Attention:" +msgstr "" + +msgid "this wizard may not function on every host. If it does not work for you, please refer to the documentation to learn how to create the config.php file manually." +msgstr "" + +msgid "System information" +msgstr "" + +msgid "Please provide the following information needed to create your configuration file." +msgstr "" + +msgid "Database type:" +msgstr "" + +msgid "Database Host Name:" +msgstr "" + +msgid "Database Name:" +msgstr "" + +msgid "Database User Name:" +msgstr "" + +msgid "Database Password:" +msgstr "" + +msgid "Database Tables Prefix:" +msgstr "" + +msgid "Continue" +msgstr "" + +msgid "No such installed language" +msgstr "" + +msgid "You can't remove English language." +msgstr "" + +msgid "Permissions to delete language denied." +msgstr "" + +msgid "Invalid language file URL." +msgstr "" + +msgid "Languages management" +msgstr "" + +msgid "Language has been successfully deleted." +msgstr "" + +msgid "Language has been successfully installed." +msgstr "" + +msgid "Language has been successfully upgraded" +msgstr "" + +msgid "Here you can install, upgrade or remove languages for your Dotclear installation." +msgstr "" + +#, php-format +msgid "You can change your user language in your preferences or change your blog's main language in your blog settings." +msgstr "" + +msgid "Installed languages" +msgstr "" + +msgid "No additional language is installed." +msgstr "" + +msgid "Language" +msgstr "" + +msgid "Action" +msgstr "" + +msgid "Install or upgrade languages" +msgstr "" + +#, php-format +msgid "You can install or remove a language by adding or removing the relevant directory in your %s folder." +msgstr "" + +msgid "Available languages" +msgstr "" + +#, php-format +msgid "You can download and install a additional language directly from Dotclear.net. Proposed languages are based on your version: %s." +msgstr "" + +msgid "Language:" +msgstr "" + +msgid "Install language" +msgstr "" + +msgid "You can install languages by uploading zip files." +msgstr "" + +msgid "Language zip file:" +msgstr "" + +msgid "Upload language" +msgstr "" + +msgid "Invalid language zip file." +msgstr "" + +msgid "The zip file does not appear to be a valid Dotclear language pack." +msgstr "" + +msgid "An error occurred during language upgrade." +msgstr "" + +msgid "Error:" +msgstr "" + +msgid "By names, in ascending order" +msgstr "" + +msgid "By names, in descending order" +msgstr "" + +msgid "By dates, in ascending order" +msgstr "" + +msgid "By dates, in descending order" +msgstr "" + +msgid "Media manager" +msgstr "" + +msgid "confirm removal" +msgstr "" + +#, php-format +msgid "Are you sure you want to remove %s?" +msgstr "" + +msgid "Cancel" +msgstr "" + +msgid "Yes" +msgstr "" + +msgid "Directory has been successfully created." +msgstr "" + +msgid "Files have been successfully uploaded." +msgstr "" + +msgid "File has been successfully removed." +msgstr "" + +msgid "Directory has been successfully removed." +msgstr "" + +msgid "Directory has been successfully rebuilt." +msgstr "" + +msgid "Zip file has been successfully extracted." +msgstr "" + +#, php-format +msgid "Choose a file to attach to entry %s by clicking on %s." +msgstr "" + +msgid "Attach this file to entry" +msgstr "" + +#, php-format +msgid "Choose a file to insert into entry by clicking on %s." +msgstr "" + +msgid "No file." +msgstr "" + +msgid "Sort files:" +msgstr "" + +msgid "Sort" +msgstr "" + +msgid "Add files" +msgstr "" + +msgid "Please take care to publish media that you own and that are not protected by copyright." +msgstr "" + +msgid "Choose a file:" +msgstr "" + +#, php-format +msgid "Maximum size %s" +msgstr "" + +msgid "Private" +msgstr "" + +msgid "To send several files at the same time, you can activate the enhanced uploader in" +msgstr "" + +msgid "Send" +msgstr "" + +msgid "New directory" +msgstr "" + +msgid "Directory Name:" +msgstr "" + +msgid "Download this directory as a zip file" +msgstr "" + +msgid "open" +msgstr "" + +msgid "Insert this file into entry" +msgstr "" + +msgid "delete" +msgstr "" + +msgid "Not a valid file" +msgstr "" + +msgid "File has been successfully updated." +msgstr "" + +msgid "Thumbnails have been successfully updated." +msgstr "" + +msgid "Insert media item" +msgstr "" + +msgid "Image size:" +msgstr "" + +msgid "original" +msgstr "" + +msgid "Image alignment" +msgstr "" + +msgid "None" +msgstr "" + +msgid "Left" +msgstr "" + +msgid "Right" +msgstr "" + +msgid "Center" +msgstr "" + +msgid "Image insertion" +msgstr "" + +msgid "As a single image" +msgstr "" + +msgid "As a link to original image" +msgstr "" + +msgid "MP3 disposition" +msgstr "" + +msgid "Please note that you cannot insert mp3 files with visual editor." +msgstr "" + +msgid "Please note that you cannot insert video files with visual editor." +msgstr "" + +msgid "Video size" +msgstr "" + +msgid "Width:" +msgstr "" + +msgid "Height:" +msgstr "" + +msgid "Video disposition" +msgstr "" + +msgid "Media item will be inserted as a link." +msgstr "" + +msgid "Insert" +msgstr "" + +msgid "Media details" +msgstr "" + +msgid "Available sizes:" +msgstr "" + +msgid "File owner:" +msgstr "" + +msgid "File type:" +msgstr "" + +msgid "File size:" +msgstr "" + +msgid "File URL:" +msgstr "" + +msgid "Show entries containing this media" +msgstr "" + +msgid "Entries containing this media" +msgstr "" + +msgid "No entry seems contain this media." +msgstr "" + +msgid "Image details" +msgstr "" + +msgid "No detail" +msgstr "" + +msgid "Update thumbnails" +msgstr "" + +msgid "This will create or update thumbnails for this image." +msgstr "" + +msgid "Extract in a new directory" +msgstr "" + +msgid "Extract in current directory" +msgstr "" + +msgid "Extract archive" +msgstr "" + +msgid "This will extract archive in a new directory that should not exist yet." +msgstr "" + +msgid "This will extract archive in current directory and will overwrite existing files or directory." +msgstr "" + +msgid "Extract mode:" +msgstr "" + +msgid "Extract" +msgstr "" + +msgid "Change media properties" +msgstr "" + +msgid "File name:" +msgstr "" + +msgid "File title:" +msgstr "" + +msgid "File date:" +msgstr "" + +msgid "New directory:" +msgstr "" + +msgid "Change file" +msgstr "" + +msgid "No blog or user given." +msgstr "" + +msgid "permissions" +msgstr "" + +msgid "Permissions" +msgstr "" + +msgid "The permissions have been successfully updated." +msgstr "" + +#, php-format +msgid "You are about to change permissions on the following blogs for users %s." +msgstr "" + +msgid "Validate permissions" +msgstr "" + +msgid "choose a blog" +msgstr "" + +msgid "Choose a blog" +msgstr "" + +msgid "Entries per page" +msgstr "" + +#, php-format +msgid "Choose one or more blogs to which you want to give permissions to users %s." +msgstr "" + +msgid "Set permissions" +msgstr "" + +msgid "select" +msgstr "" + +msgid "No content found on this plugin." +msgstr "" + +msgid "Plugin not found" +msgstr "" + +msgid "The plugin you reached does not exist or does not have an admin page." +msgstr "" + +msgid "No such plugin." +msgstr "" + +msgid "You don't have permissions to delete this plugin." +msgstr "" + +msgid "You don't have permissions to deactivate this plugin." +msgstr "" + +msgid "Plugins management" +msgstr "" + +msgid "Plugin has been successfully deleted." +msgstr "" + +msgid "Plugin has been successfully installed." +msgstr "" + +msgid "Plugin has been successfully upgraded" +msgstr "" + +msgid "Plugins add new functionalities to Dotclear. Here you can activate or deactivate installed plugins." +msgstr "" + +#, php-format +msgid "You can find additional plugins for your blog on %s." +msgstr "" + +msgid "To install or upgrade a plugin you generally just need to upload it in \"Install or upgrade a plugin\" section." +msgstr "" + +msgid "To install or upgrade a plugin you just need to extract it in your plugins directory." +msgstr "" + +msgid "Plugins" +msgstr "" + +msgid "Activated plugins" +msgstr "" + +msgid "Plugin" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Details" +msgstr "" + +msgid "Deactivate" +msgstr "" + +msgid "Deactivated plugins" +msgstr "" + +msgid "Activate" +msgstr "" + +msgid "Install or upgrade a plugin" +msgstr "" + +msgid "You can install plugins by uploading or downloading zip files." +msgstr "" + +msgid "Plugin zip file:" +msgstr "" + +msgid "Upload plugin" +msgstr "" + +msgid "Plugin zip file URL:" +msgstr "" + +msgid "Download plugin" +msgstr "" + +msgid "To enable this function, please give write access to your plugins directory." +msgstr "" + +msgid "Add a link" +msgstr "" + +msgid "Available" +msgstr "" + +msgid "Most used" +msgstr "" + +msgid "Link URL:" +msgstr "" + +msgid "Link title:" +msgstr "" + +msgid "Link language:" +msgstr "" + +msgid "Add a link to an entry" +msgstr "" + +msgid "Search entry:" +msgstr "" + +msgid "Search" +msgstr "" + +msgid "cancel" +msgstr "" + +msgid "This entry does not exist." +msgstr "" + +msgid "Edit entry" +msgstr "" + +msgid "next entry" +msgstr "" + +msgid "previous entry" +msgstr "" + +msgid "Entry has been successfully updated." +msgstr "" + +msgid "Entry has been successfully created." +msgstr "" + +msgid "File has been successfully attached." +msgstr "" + +msgid "Attachment has been successfully removed." +msgstr "" + +msgid "Comment has been successfully created." +msgstr "" + +msgid "Don't forget to validate your XHTML conversion by saving your post." +msgstr "" + +msgid "Go to this entry on the site" +msgstr "" + +msgid "new window" +msgstr "" + +msgid "Excerpt:" +msgstr "" + +msgid "Notes:" +msgstr "" + +msgid "Preview" +msgstr "" + +msgid "Entry status:" +msgstr "" + +msgid "Published on:" +msgstr "" + +msgid "Text formating:" +msgstr "" + +msgid "Convert to XHTML" +msgstr "" + +msgid "Selected entry" +msgstr "" + +msgid "Entry lang:" +msgstr "" + +msgid "Entry password:" +msgstr "" + +msgid "Basename:" +msgstr "" + +msgid "Warning: If you set the URL manually, it may conflict with another entry." +msgstr "" + +msgid "Ping blogs" +msgstr "" + +msgid "Trackbacks" +msgstr "" + +msgid "No trackback" +msgstr "" + +msgid "Add a comment" +msgstr "" + +msgid "Name:" +msgstr "" + +msgid "IP address" +msgstr "" + +msgid "published" +msgstr "" + +msgid "unpublished" +msgstr "" + +msgid "pending" +msgstr "" + +msgid "junk" +msgstr "" + +msgid "select this comment" +msgstr "" + +msgid "select this trackback" +msgstr "" + +msgid "Edit this comment" +msgstr "" + +msgid "This attachment does not exist" +msgstr "" + +msgid "Remove attachment" +msgstr "" + +msgid "Attachment" +msgstr "" + +msgid "Are you sure you want to remove this attachment?" +msgstr "" + +msgid "selected" +msgstr "" + +msgid "not selected" +msgstr "" + +msgid "Category" +msgstr "" + +msgid "Selected" +msgstr "" + +msgid "Publish" +msgstr "" + +msgid "Unpublish" +msgstr "" + +msgid "Schedule" +msgstr "" + +msgid "Mark as pending" +msgstr "" + +msgid "Mark" +msgstr "" + +msgid "Mark as selected" +msgstr "" + +msgid "Mark as unselected" +msgstr "" + +msgid "Change" +msgstr "" + +msgid "Change category" +msgstr "" + +msgid "Change author" +msgstr "" + +msgid "Selected:" +msgstr "" + +msgid "Month:" +msgstr "" + +msgid "Lang:" +msgstr "" + +msgid "Selected entries action:" +msgstr "" + +msgid "This user does not exist" +msgstr "" + +msgid "Change category for entries" +msgstr "" + +msgid "Change author for entries" +msgstr "" + +msgid "Author ID:" +msgstr "" + +msgid "Default" +msgstr "" + +msgid "If you want to change your email or password you must provide your current password." +msgstr "" + +msgid "No favorite selected" +msgstr "" + +msgid "Personal information has been successfully updated." +msgstr "" + +msgid "Personal options has been successfully updated." +msgstr "" + +msgid "Favorites have been successfully added." +msgstr "" + +msgid "Favorites have been successfully updated." +msgstr "" + +msgid "Favorites have been successfully removed." +msgstr "" + +msgid "Default favorites have been successfully updated." +msgstr "" + +msgid "My profile" +msgstr "" + +msgid "Display name:" +msgstr "" + +msgid "User language:" +msgstr "" + +msgid "User timezone:" +msgstr "" + +msgid "If you have changed this user email or password you must provide your current password to save these modifications." +msgstr "" + +msgid "My options" +msgstr "" + +msgid "Preferred format:" +msgstr "" + +msgid "Default entry status:" +msgstr "" + +msgid "Entry edit field height:" +msgstr "" + +msgid "Enable WYSIWYG mode" +msgstr "" + +msgid "Activate enhanced uploader in media manager" +msgstr "" + +msgid "Iconset:" +msgstr "" + +msgid "Do not use standard favicon" +msgstr "" + +msgid "This will be applied for all users" +msgstr "" + +msgid "Accessibility options" +msgstr "" + +msgid "Disable javascript powered drag and drop for ordering items" +msgstr "" + +msgid "Numeric fields will allow to type the elements' ordering number." +msgstr "" + +msgid "Dashboard modules" +msgstr "" + +msgid "Display documentation links" +msgstr "" + +msgid "Display Dotclear news" +msgstr "" + +msgid "Display quick entry form" +msgstr "" + +msgid "My favorites" +msgstr "" + +#, php-format +msgid "position of %s" +msgstr "" + +msgid "Save order" +msgstr "" + +msgid "Delete selected favorites" +msgstr "" + +msgid "Are you sure you want to remove selected favorites?" +msgstr "" + +msgid "If you are a super administrator, you may define this set of favorites to be used by default on all blogs of this installation:" +msgstr "" + +msgid "Define as default favorites" +msgstr "" + +msgid "Currently no personal favorites." +msgstr "" + +msgid "Default favorites" +msgstr "" + +msgid "Those favorites are displayed when My Favorites list is empty." +msgstr "" + +msgid "Available favorites" +msgstr "" + +msgid "Add to my favorites" +msgstr "" + +msgid "Search options" +msgstr "" + +msgid "Query:" +msgstr "" + +msgid "Search entries" +msgstr "" + +msgid "Search comments" +msgstr "" + +msgid "schedule" +msgstr "" + +msgid "change category" +msgstr "" + +msgid "change author" +msgstr "" + +#, php-format +msgid "%d entries found" +msgstr "" + +#, php-format +msgid "%d entry found" +msgstr "" + +#, php-format +msgid "%d comment found" +msgstr "" + +#, php-format +msgid "%d comments found" +msgstr "" + +msgid "This entry does not exist or is not published" +msgstr "" + +msgid "All pings sent." +msgstr "" + +#, php-format +msgid "Back to \"%s\"" +msgstr "" + +msgid "Auto discover ping URLs" +msgstr "" + +msgid "URLs to ping:" +msgstr "" + +msgid "Send excerpt:" +msgstr "" + +msgid "Previously sent pings" +msgstr "" + +msgid "Dotclear update" +msgstr "" + +#, php-format +msgid "Unable to delete file %s" +msgstr "" + +#, php-format +msgid "Downloaded Dotclear archive seems to be corrupted. Try download it again." +msgstr "" + +msgid "The following files of your Dotclear installation have been modified so we won't try to update your installation. Please try to update manually." +msgstr "" + +#, php-format +msgid "The following files of your Dotclear installation are not readable. Please fix this or try to make a backup file named %s manually." +msgstr "" + +msgid "The following files of your Dotclear installation cannot be written. Please fix this or try to update manually." +msgstr "" + +msgid "No newer Dotclear version available." +msgstr "" + +#, php-format +msgid "Dotclear %s is available." +msgstr "" + +msgid "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." +msgstr "" + +msgid "Update Dotclear" +msgstr "" + +msgid "Update backup files" +msgstr "" + +msgid "The following files are backups of previously updates. You can revert your previous installation or delete theses files." +msgstr "" + +msgid "Please note that reverting your Dotclear version may have some unwanted side-effects. Consider reverting only if you experience strong issues with this new version." +msgstr "" + +#, php-format +msgid "You should not revert to version prior to last one (%s)." +msgstr "" + +msgid "Delete selected file" +msgstr "" + +msgid "Revert to selected file" +msgstr "" + +msgid "Congratulations, you're one click away from the end of the update." +msgstr "" + +msgid "Finish the update." +msgstr "" + +msgid "new user" +msgstr "" + +#, php-format +msgid "User \"%s\" already exists." +msgstr "" + +msgid "User has been successfully updated." +msgstr "" + +msgid "User has been successfully created." +msgstr "" + +msgid "Warning:" +msgstr "" + +msgid "If you change your username, you will have to log in again." +msgstr "" + +msgid "Mandatory for password recovering procedure." +msgstr "" + +msgid "Password change required to connect" +msgstr "" + +msgid "Save and create another" +msgstr "" + +msgid "No permissions." +msgstr "" + +msgid "Add new permissions" +msgstr "" + +msgid "Username" +msgstr "" + +msgid "Last Name" +msgstr "" + +msgid "First Name" +msgstr "" + +msgid "Display name" +msgstr "" + +msgid "Number of entries" +msgstr "" + +msgid "users" +msgstr "" + +msgid "User has been successfully removed." +msgstr "" + +msgid "Create a new user" +msgstr "" + +msgid "Users per page" +msgstr "" + +msgid "Selected users action:" +msgstr "" + +msgid "Blog:" +msgstr "" + +msgid "Change blog" +msgstr "" + +msgid "Blogs:" +msgstr "" + +msgid "Go to site" +msgstr "" + +msgid "My dashboard" +msgstr "" + +#, php-format +msgid "Logout %s" +msgstr "" + +msgid "Safe mode" +msgstr "" + +msgid "You are in safe mode. All plugins have been temporarily disabled. Remind to log out then log in again normally to get back all functionalities" +msgstr "" + +#, php-format +msgid "Thank you for using %s." +msgstr "" + +msgid "Help" +msgstr "" + +msgid "uncover" +msgstr "" + +msgid "hide" +msgstr "" + +msgid "help" +msgstr "" + +msgid "no selection" +msgstr "" + +msgid "select all" +msgstr "" + +msgid "invert selection" +msgstr "" + +msgid "view entry" +msgstr "" + +#, php-format +msgid "Are you sure you want to delete selected entries (%s)?" +msgstr "" + +msgid "Are you sure you want to delete this entry?" +msgstr "" + +#, php-format +msgid "Are you sure you want to delete selected comments (%s)?" +msgstr "" + +msgid "Are you sure you want to delete this comment?" +msgstr "" + +msgid "Users with posts cannot be deleted." +msgstr "" + +#, php-format +msgid "Are you sure you want to delete selected users (%s)?" +msgstr "" + +#, php-format +msgid "Are you sure you want to delete category \"%s\"?" +msgstr "" + +msgid "Are you sure you want to reorder all categories?" +msgstr "" + +#, php-format +msgid "Are you sure you want to remove media \"%s\"?" +msgstr "" + +msgid "Are you sure you want to extract archive in current directory?" +msgstr "" + +#, php-format +msgid "Are you sure you want to remove attachment \"%s\"?" +msgstr "" + +#, php-format +msgid "Are you sure you want to delete \"%s\" language?" +msgstr "" + +#, php-format +msgid "Are you sure you want to delete \"%s\" plugin?" +msgstr "" + +msgid "Use this theme" +msgstr "" + +msgid "Remove this theme" +msgstr "" + +#, php-format +msgid "Are you sure you want to delete \"%s\" theme?" +msgstr "" + +msgid "Zip file content" +msgstr "" + +msgid "XHTML markup validator" +msgstr "" + +msgid "XHTML content is valid." +msgstr "" + +msgid "There are XHTML markup errors." +msgstr "" + +msgid "You have unsaved changes. Switch post format will loose these changes. Proceed anyway?" +msgstr "" + +msgid "Loading enhanced uploader, please wait." +msgstr "" + +msgid "You have unsaved changes." +msgstr "" + +msgid "close" +msgstr "" + +msgid "now" +msgstr "" + +msgid "visual" +msgstr "" + +msgid "source" +msgstr "" + +msgid "You can use the following shortcuts to format your text." +msgstr "" + +msgid "-- none --" +msgstr "" + +msgid "-- block format --" +msgstr "" + +msgid "Paragraph" +msgstr "" + +msgid "Level 1 header" +msgstr "" + +msgid "Level 2 header" +msgstr "" + +msgid "Level 3 header" +msgstr "" + +msgid "Level 4 header" +msgstr "" + +msgid "Level 5 header" +msgstr "" + +msgid "Level 6 header" +msgstr "" + +msgid "Strong emphasis" +msgstr "" + +msgid "Emphasis" +msgstr "" + +msgid "Inserted" +msgstr "" + +msgid "Deleted" +msgstr "" + +msgid "Inline quote" +msgstr "" + +msgid "Code" +msgstr "" + +msgid "Line break" +msgstr "" + +msgid "Blockquote" +msgstr "" + +msgid "Preformated text" +msgstr "" + +msgid "Unordered list" +msgstr "" + +msgid "Ordered list" +msgstr "" + +msgid "Link" +msgstr "" + +msgid "URL?" +msgstr "" + +msgid "Language?" +msgstr "" + +msgid "External image" +msgstr "" + +msgid "Media chooser" +msgstr "" + +msgid "Link to an entry" +msgstr "" + +msgid "Activate enhanced uploader" +msgstr "" + +msgid "Disable enhanced uploader" +msgstr "" + +msgid "File successfully uploaded." +msgstr "" + +msgid "Maximum file size allowed:" +msgstr "" + +msgid "Limit exceeded." +msgstr "" + +msgid "File size exceeds allowed limit." +msgstr "" + +msgid "Canceled." +msgstr "" + +msgid "HTTP Error:" +msgstr "" + +msgid "Choose file" +msgstr "" + +msgid "Choose files" +msgstr "" + +msgid "Clean" +msgstr "" + +msgid "Upload" +msgstr "" + +msgid "No file in queue." +msgstr "" + +msgid "1 file in queue." +msgstr "" + +#, php-format +msgid "%d files in queue." +msgstr "" + +msgid "Queue error:" +msgstr "" + +msgid "«prev." +msgstr "" + +msgid "next»" +msgstr "" + +msgid "No entry" +msgstr "" + +msgid "scheduled" +msgstr "" + +msgid "protected" +msgstr "" + +#, php-format +msgid "%d attachment" +msgstr "" + +#, php-format +msgid "%d attachments" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "No user" +msgstr "" + +msgid "admin" +msgstr "" + +msgid "superadmin" +msgstr "" + +msgid "Database error" +msgstr "" + +msgid "There seems to be no Session table in your database. Is Dotclear completly installed?" +msgstr "" + +msgid "System" +msgstr "" + +msgid "Blog" +msgstr "" + +msgid "Updates" +msgstr "" + +msgid "Languages" +msgstr "" + +msgid "administrator" +msgstr "" + +msgid "manage their own entries and comments" +msgstr "" + +msgid "publish entries and comments" +msgstr "" + +msgid "delete entries and comments" +msgstr "" + +msgid "manage all entries and comments" +msgstr "" + +msgid "manage categories" +msgstr "" + +msgid "manage their own media items" +msgstr "" + +msgid "manage all media items" +msgstr "" + +msgid "That user does not exist in the database." +msgstr "" + +msgid "That key does not exist in the database." +msgstr "" + +msgid "You are not allowed to add categories" +msgstr "" + +msgid "You are not allowed to update categories" +msgstr "" + +msgid "You are not allowed to delete categories" +msgstr "" + +msgid "This category is not empty." +msgstr "" + +msgid "Category URL must be unique." +msgstr "" + +msgid "You must provide a category title" +msgstr "" + +msgid "You must provide a category URL" +msgstr "" + +msgid "You are not allowed to create an entry" +msgstr "" + +msgid "You are not allowed to update entries" +msgstr "" + +msgid "No such entry ID" +msgstr "" + +msgid "You are not allowed to edit this entry" +msgstr "" + +msgid "You are not allowed to change this entry status" +msgstr "" + +msgid "You are not allowed to change this entry category" +msgstr "" + +msgid "You are not allowed to mark this entry as selected" +msgstr "" + +msgid "You are not allowed to delete entries" +msgstr "" + +msgid "You are not allowed to delete this entry" +msgstr "" + +msgid "No entry title" +msgstr "" + +msgid "No entry content" +msgstr "" + +msgid "Empty entry URL" +msgstr "" + +msgid "You are not allowed to update comments" +msgstr "" + +msgid "No such comment ID" +msgstr "" + +msgid "You are not allowed to update this comment" +msgstr "" + +msgid "You are not allowed to change this comment's status" +msgstr "" + +msgid "You are not allowed to delete comments" +msgstr "" + +msgid "You are not allowed to delete this comment" +msgstr "" + +msgid "You must provide a comment" +msgstr "" + +msgid "You must provide an author name" +msgstr "" + +msgid "Email address is not valid." +msgstr "" + +msgid "online" +msgstr "" + +msgid "offline" +msgstr "" + +msgid "removed" +msgstr "" + +msgid "You are not an administrator" +msgstr "" + +msgid "Invalid user language code" +msgstr "" + +msgid "Blog ID must contain at least 2 characters using letters, numbers or symbols." +msgstr "" + +msgid "No blog name" +msgstr "" + +msgid "No blog URL" +msgstr "" + +msgid "No log message" +msgstr "" + +msgid "unknown" +msgstr "" + +msgid "No blog defined." +msgstr "" + +#, php-format +msgid "Directory %s does not exist." +msgstr "" + +msgid "You are not a super administrator." +msgstr "" + +msgid "Permission denied." +msgstr "" + +msgid "You are not the file owner." +msgstr "" + +msgid "This file is not allowed." +msgstr "" + +msgid "New file already exists." +msgstr "" + +msgid "File does not exist in the database." +msgstr "" + +#, php-format +msgid "Extract destination directory %s already exists." +msgstr "" + +msgid "Embedded Audio Player" +msgstr "" + +msgid "Embedded Video Player" +msgstr "" + +#, php-format +msgid "%s: in [%s] and [%s]" +msgstr "" + +msgid "Empty module zip file." +msgstr "" + +msgid "The zip file does not appear to be a valid Dotclear module." +msgstr "" + +msgid "An error occurred during module deletion." +msgstr "" + +#, php-format +msgid "Unable to upgrade \"%s\". (same version)" +msgstr "" + +msgid "Unable to read new _define.php file" +msgstr "" + +msgid "No such module." +msgstr "" + +msgid "Cannot remove module files" +msgstr "" + +msgid "Cannot deactivate plugin." +msgstr "" + +msgid "Cannot activate plugin." +msgstr "" + +#, php-format +msgid "Invalid setting dcNamespace: %s" +msgstr "" + +msgid "Unable to retrieve settings:" +msgstr "" + +#, php-format +msgid "%s is not a valid setting id" +msgstr "" + +msgid "No namespace specified" +msgstr "" + +msgid "Unable to retrieve workspaces:" +msgstr "" + +msgid "Unable to retrieve namespaces:" +msgstr "" + +#, php-format +msgid "Invalid setting namespace: %s" +msgstr "" + +#, php-format +msgid "%s has still been pinged" +msgstr "" + +msgid "Unable to ping URL" +msgstr "" + +#, php-format +msgid "%s is not a ping URL" +msgstr "" + +#, php-format +msgid "%s, ping error:" +msgstr "" + +msgid "Digests file not found." +msgstr "" + +msgid "No file to download" +msgstr "" + +msgid "Root directory is not writable." +msgstr "" + +msgid "An error occurred while downloading archive." +msgstr "" + +msgid "Archive not found." +msgstr "" + +msgid "Unable to read current digests file." +msgstr "" + +msgid "Downloaded file does not seem to be a valid archive." +msgstr "" + +msgid "Incomplete archive." +msgstr "" + +msgid "Unable to read digests file." +msgstr "" + +msgid "Invalid digests file." +msgstr "" + +#, php-format +msgid "Invalid dcWorkspace: %s" +msgstr "" + +msgid "Unable to retrieve prefs:" +msgstr "" + +#, php-format +msgid "%s is not a valid pref id" +msgstr "" + +msgid "No workspace specified" +msgstr "" + +msgid "SQLite Database Schema cannot be upgraded." +msgstr "" + +msgid "Something went wrong with auto upgrade:" +msgstr "" + +msgid "Unable to open directory." +msgstr "" + +msgid "Unable to create directory." +msgstr "" + +msgid "File is not writable." +msgstr "" + +msgid "Unable to open file." +msgstr "" + +msgid "Not an uploaded file." +msgstr "" + +msgid "The uploaded file exceeds the maximum file size allowed." +msgstr "" + +msgid "The uploaded file was only partially uploaded." +msgstr "" + +msgid "No file was uploaded." +msgstr "" + +msgid "Missing a temporary folder." +msgstr "" + +msgid "Failed to write file to disk." +msgstr "" + +#, php-format +msgid "%s is not a directory." +msgstr "" + +msgid "Bad range" +msgstr "" + +msgid "Invalid range" +msgstr "" + +msgid "Invalid line number" +msgstr "" + +msgid "Chunk is out of range" +msgstr "" + +msgid "Bad context" +msgstr "" + +msgid "Bad context (in deletion)" +msgstr "" + +msgid "Invalid diff format" +msgstr "" + +msgid "Uploading this file is not allowed." +msgstr "" + +msgid "Destination directory is not in jail." +msgstr "" + +msgid "File already exists." +msgstr "" + +msgid "Cannot write in this directory." +msgstr "" + +msgid "An error occurred while writing the file." +msgstr "" + +msgid "Source file does not exist." +msgstr "" + +msgid "File is not in jail." +msgstr "" + +msgid "Destination directory is not writable." +msgstr "" + +msgid "Unable to rename file." +msgstr "" + +msgid "File cannot be removed." +msgstr "" + +msgid "Directory is not in jail." +msgstr "" + +msgid "Directory cannot be removed." +msgstr "" + +msgid "Not enough memory to open image." +msgstr "" + +#, php-format +msgid "File %s is not compressed in the zip." +msgstr "" + +#, php-format +msgid "Trying to unzip a folder name %s" +msgstr "" + +msgid "Unable to write destination file." +msgstr "" + +msgid "Unable to write in target directory, permission denied." +msgstr "" + +msgid "Not enough memory to open file." +msgstr "" + +msgid "File does not exist" +msgstr "" + +msgid "Cannot read file" +msgstr "" + +msgid "Directory does not exist" +msgstr "" + +msgid "Cannot read directory" +msgstr "" + +msgid "Unable to connect to database" +msgstr "" + +#, php-format +msgid "

        This either means that the username and password information in your config.php file is incorrect or we can't contact the database server at \"%s\". This could mean your host's database server is down.

        • Are you sure you have the correct username and password?
        • Are you sure that you have typed the correct hostname?
        • Are you sure that the database server is running?

        If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the Dotclear Support Forums.

        " +msgstr "" + +msgid "The following error was encountered while trying to read the database:" +msgstr "" diff --git a/v2/dotclear/locales/en/plugins.lang.php b/v2/dotclear/locales/en/plugins.lang.php new file mode 100644 index 0000000..0bb1271 --- /dev/null +++ b/v2/dotclear/locales/en/plugins.lang.php @@ -0,0 +1,45 @@ + \ No newline at end of file diff --git a/v2/dotclear/locales/en/plugins.po b/v2/dotclear/locales/en/plugins.po new file mode 100644 index 0000000..55a7780 --- /dev/null +++ b/v2/dotclear/locales/en/plugins.po @@ -0,0 +1,1573 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-11-06 18:11+0100\n" +"PO-Revision-Date: 2011-10-25 15:52+0100\n" +"Last-Translator: Franck Paul \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "no" +msgstr "" + +msgid "yes" +msgstr "" + +msgid "Configuration successfully updated" +msgstr "" + +msgid "Settings definition successfully updated" +msgstr "" + +msgid "blog settings" +msgstr "" + +msgid "Value" +msgstr "" + +msgid "Description" +msgstr "" + +msgid "Goto:" +msgstr "" + +msgid "Ok" +msgstr "" + +msgid "global settings" +msgstr "" + +msgid "Akismet spam filter" +msgstr "" + +#, php-format +msgid "Filtered by %s." +msgstr "" + +msgid "Akismet API key:" +msgstr "" + +msgid "API key verified" +msgstr "" + +msgid "API key not verified" +msgstr "" + +msgid "Get your own API key" +msgstr "" + +msgid "Antispam" +msgstr "" + +msgid "Delete junk comments older than" +msgstr "" + +msgid "days" +msgstr "" + +msgid "IP Blacklist / Whitelist Filter" +msgstr "" + +#, php-format +msgid "Filtered by %1$s with rule %2$s." +msgstr "" + +msgid "IP address has been successfully added." +msgstr "" + +msgid "IP addresses have been successfully removed." +msgstr "" + +msgid "Blacklist" +msgstr "" + +msgid "Whitelist" +msgstr "" + +msgid "Add an IP address: " +msgstr "" + +msgid "Global IP" +msgstr "" + +msgid "Add" +msgstr "" + +msgid "No IP address in list." +msgstr "" + +msgid "IP list" +msgstr "" + +msgid "Checks sender IP address against DNSBL servers" +msgstr "" + +#, php-format +msgid "Filtered by %1$s with server %2$s." +msgstr "" + +msgid "IP Lookup servers" +msgstr "" + +msgid "Add here a coma separated list of servers." +msgstr "" + +msgid "Checks links in comments against surbl.org" +msgstr "" + +msgid "Words Blacklist" +msgstr "" + +#, php-format +msgid "Filtered by %1$s with word %2$s." +msgstr "" + +msgid "Words have been successfully added." +msgstr "" + +msgid "Word has been successfully added." +msgstr "" + +msgid "Words have been successfully removed." +msgstr "" + +msgid "Add a word " +msgstr "" + +msgid "Global word" +msgstr "" + +msgid "No word in list." +msgstr "" + +msgid "List of bad words" +msgstr "" + +msgid "Delete selected words" +msgstr "" + +msgid "Create default wordlist" +msgstr "" + +msgid "This word exists" +msgstr "" + +msgid "No description" +msgstr "" + +#, php-format +msgid "Filtered by %1$s (%2$s)" +msgstr "" + +msgid "Unknown filter." +msgstr "" + +msgid "This comment is a spam:" +msgstr "" + +#, php-format +msgid "(including %d spam comment)" +msgstr "" + +#, php-format +msgid "(including %d spam comments)" +msgstr "" + +msgid "Spam moderation" +msgstr "" + +msgid "Spam" +msgstr "" + +msgid "Ham" +msgstr "" + +msgid "Filter does not exist." +msgstr "" + +msgid "Filter has no user interface." +msgstr "" + +#, php-format +msgid "%s configuration" +msgstr "" + +msgid "Information" +msgstr "" + +msgid "Spam comments have been successfully deleted." +msgstr "" + +msgid "Junk comments:" +msgstr "" + +msgid "Published comments:" +msgstr "" + +msgid "Delete all spams" +msgstr "" + +#, php-format +msgid "All spam comments older than %s day(s) will be automatically deleted." +msgstr "" + +msgid "You can modify this duration in " +msgstr "" + +msgid "Blog preferences" +msgstr "" + +msgid "Filters configuration has been successfully saved." +msgstr "" + +msgid "Available spam filters" +msgstr "" + +msgid "Order" +msgstr "" + +msgid "Active" +msgstr "" + +msgid "Auto Del." +msgstr "" + +msgid "Filter name" +msgstr "" + +msgid "Filter configuration" +msgstr "" + +msgid "position" +msgstr "" + +msgid "Syndication" +msgstr "" + +msgid "Junk comments RSS feed" +msgstr "" + +msgid "Published comments RSS feed" +msgstr "" + +msgid "Attachments" +msgstr "" + +msgid "remove" +msgstr "" + +msgid "No attachment." +msgstr "" + +msgid "Add files to this entry" +msgstr "" + +msgid "Blogroll" +msgstr "" + +msgid "manage blogroll" +msgstr "" + +msgid "Links" +msgstr "" + +msgid "All categories" +msgstr "" + +msgid "Home page only" +msgstr "" + +msgid "You must provide a link title" +msgstr "" + +msgid "You must provide a link URL" +msgstr "" + +msgid "You need to provide a XBEL or OPML file." +msgstr "" + +msgid "File is not in XML format." +msgstr "" + +msgid "No such link or title" +msgstr "" + +msgid "Return to blogroll" +msgstr "" + +msgid "Category has been successfully updated" +msgstr "" + +msgid "Edit category" +msgstr "" + +msgid "Link has been successfully updated" +msgstr "" + +msgid "Edit link" +msgstr "" + +msgid "XFN" +msgstr "" + +msgid "_xfn_Me" +msgstr "Me" + +msgid "_xfn_Another link for myself" +msgstr "Another link for myself" + +msgid "_xfn_Friendship" +msgstr "Friendship" + +msgid "_xfn_Contact" +msgstr "Contact" + +msgid "_xfn_Acquaintance" +msgstr "Acquaintance" + +msgid "_xfn_Friend" +msgstr "Friend" + +msgid "_xfn_Physical" +msgstr "Physical" + +msgid "_xfn_Met" +msgstr "Met" + +msgid "_xfn_Professional" +msgstr "Professional" + +msgid "_xfn_Co-worker" +msgstr "Co-worker" + +msgid "_xfn_Colleague" +msgstr "Colleague" + +msgid "_xfn_Geographical" +msgstr "Geographical" + +msgid "_xfn_Co-resident" +msgstr "Co-resident" + +msgid "_xfn_Neighbor" +msgstr "Neighbor" + +msgid "_xfn_Family" +msgstr "Family" + +msgid "_xfn_Child" +msgstr "Child" + +msgid "_xfn_Parent" +msgstr "Parent" + +msgid "_xfn_Sibling" +msgstr "Sibling" + +msgid "_xfn_Spouse" +msgstr "Spouse" + +msgid "_xfn_Kin" +msgstr "Kin" + +msgid "_xfn_Romantic" +msgstr "Romantic" + +msgid "_xfn_Muse" +msgstr "Muse" + +msgid "_xfn_Crush" +msgstr "Crush" + +msgid "_xfn_Date" +msgstr "Date" + +msgid "_xfn_Sweetheart" +msgstr "Sweetheart" + +msgid "Nothing to import" +msgstr "" + +msgid "Import operation cancelled." +msgstr "" + +msgid "Items order has been successfully updated" +msgstr "" + +msgid "Items have been successfully removed." +msgstr "" + +msgid "Link has been successfully created." +msgstr "" + +msgid "category has been successfully created." +msgstr "" + +msgid "links have been successfully imported." +msgstr "" + +msgid "URL" +msgstr "" + +msgid "Lang" +msgstr "" + +msgid "select this link" +msgstr "" + +msgid "Delete selected links" +msgstr "" + +msgid "Are you sure you want to delete selected links?" +msgstr "" + +msgid "The link list is empty." +msgstr "" + +msgid "Add a new link" +msgstr "" + +msgid "Add a category" +msgstr "" + +msgid "Import links" +msgstr "" + +msgid "OPML or XBEL File:" +msgstr "" + +msgid "Import" +msgstr "" + +msgid "Light linear gradient" +msgstr "" + +msgid "Medium linear gradient" +msgstr "" + +msgid "Dark linear gradient" +msgstr "" + +msgid "Solid color" +msgstr "" + +msgid "Custom..." +msgstr "" + +msgid "Blowup configuration" +msgstr "" + +msgid "Predefined styles" +msgstr "" + +msgid "Apply code" +msgstr "" + +msgid "Choose a predefined style" +msgstr "" + +msgid "For the following reasons, images cannot be created. You won't be able to change some background properties." +msgstr "" + +msgid "Theme configuration has been successfully updated." +msgstr "" + +msgid "General" +msgstr "" + +msgid "Background color:" +msgstr "" + +msgid "Background color fill:" +msgstr "" + +msgid "Main text font:" +msgstr "" + +msgid "Main text font size:" +msgstr "" + +msgid "Main text color:" +msgstr "" + +msgid "Text line height:" +msgstr "" + +msgid "Links color:" +msgstr "" + +msgid "Visited links color:" +msgstr "" + +msgid "Focus links color:" +msgstr "" + +msgid "Page top" +msgstr "" + +msgid "Prelude color:" +msgstr "" + +msgid "Hide main title" +msgstr "" + +msgid "Main title font:" +msgstr "" + +msgid "Main title font size:" +msgstr "" + +msgid "Main title color:" +msgstr "" + +msgid "Main title alignment:" +msgstr "" + +msgid "center" +msgstr "" + +msgid "left" +msgstr "" + +msgid "right" +msgstr "" + +msgid "Main title position (x:y)" +msgstr "" + +msgid "Top image" +msgstr "" + +msgid "Choose \"Custom...\" to upload your own image." +msgstr "" + +msgid "Add your image:" +msgstr "" + +#, php-format +msgid "JPEG or PNG file, 800 pixels wide, maximum size %s" +msgstr "" + +msgid "Sidebar" +msgstr "" + +msgid "Sidebar position:" +msgstr "" + +msgid "Sidebar text font:" +msgstr "" + +msgid "Sidebar text font size:" +msgstr "" + +msgid "Sidebar text color:" +msgstr "" + +msgid "Sidebar titles font:" +msgstr "" + +msgid "Sidebar titles font size:" +msgstr "" + +msgid "Sidebar titles color:" +msgstr "" + +msgid "Sidebar 2nd level titles font:" +msgstr "" + +msgid "Sidebar 2nd level titles font size:" +msgstr "" + +msgid "Sidebar 2nd level titles color:" +msgstr "" + +msgid "Sidebar lines color:" +msgstr "" + +msgid "Sidebar links color:" +msgstr "" + +msgid "Sidebar visited links color:" +msgstr "" + +msgid "Sidebar focus links color:" +msgstr "" + +msgid "Date title font:" +msgstr "" + +msgid "Date title font size:" +msgstr "" + +msgid "Date title color:" +msgstr "" + +msgid "Entry title font:" +msgstr "" + +msgid "Entry title font size:" +msgstr "" + +msgid "Entry title color:" +msgstr "" + +msgid "Comment background color:" +msgstr "" + +msgid "Comment text color:" +msgstr "" + +msgid "My comment background color:" +msgstr "" + +msgid "My comment text color:" +msgstr "" + +msgid "Footer" +msgstr "" + +msgid "Footer font:" +msgstr "" + +msgid "Footer font size:" +msgstr "" + +msgid "Footer color:" +msgstr "" + +msgid "Footer links color:" +msgstr "" + +msgid "Footer background color:" +msgstr "" + +msgid "Additional CSS" +msgstr "" + +msgid "Configuration import / export" +msgstr "" + +msgid "You can share your configuration using the following code. To apply a configuration, paste the code, click on \"Apply code\" and save." +msgstr "" + +msgid "Copy this code:" +msgstr "" + +msgid "default" +msgstr "" + +msgid "At least one of the following functions is not available: imagecreatetruecolor, imagepng & imagecreatefrompng." +msgstr "" + +msgid "The 'public' directory does not exist." +msgstr "" + +#, php-format +msgid "The '%s' directory cannot be modified." +msgstr "" + +msgid "Unable to create images." +msgstr "" + +msgid "Invalid file type." +msgstr "" + +msgid "Uploaded image is not 800 pixels wide." +msgstr "" + +msgid "Unable to open image." +msgstr "" + +msgid "Checks trackback source for a link to the post" +msgstr "" + +msgid "Import/Export" +msgstr "" + +msgid "Flat file export" +msgstr "" + +msgid "Exports a blog or a full Dotclear installation to flat file." +msgstr "" + +msgid "Export file not found." +msgstr "" + +msgid "Export a blog" +msgstr "" + +#, php-format +msgid "This will create an export of your current blog: %s" +msgstr "" + +msgid "Export" +msgstr "" + +msgid "You may also want to download your media directory as a zip file" +msgstr "" + +msgid "Export all content" +msgstr "" + +msgid "Congratulation!" +msgstr "" + +msgid "Your blog has been successfully imported. Welcome on Dotclear 2!" +msgstr "" + +msgid "Why don't you blog this now?" +msgstr "" + +msgid "or" +msgstr "" + +msgid "visit your dashboard" +msgstr "" + +msgid "Dotclear 1.2 import" +msgstr "" + +msgid "Import a Dotclear 1.2 installation into your current blog." +msgstr "" + +#, php-format +msgid "This will import your Dotclear 1.2 content as new content in the current blog: %s." +msgstr "" + +msgid "Please note that this process will empty your categories, blogroll, entries and comments on the current blog." +msgstr "" + +msgid "Depending on the size of your blog, it could take a few minutes." +msgstr "" + +msgid "General information" +msgstr "" + +msgid "Import my blog now" +msgstr "" + +msgid "We first need some information about your old Dotclear 1.2 installation." +msgstr "" + +msgid "Entries import options" +msgstr "" + +msgid "Number of entries to import at once:" +msgstr "" + +msgid "Importing users" +msgstr "" + +msgid "Importing categories" +msgstr "" + +msgid "Importing blogroll" +msgstr "" + +#, php-format +msgid "Importing entries from %d to %d / %d" +msgstr "" + +msgid "Please read carefully" +msgstr "" + +msgid "Every newly imported user has received a random password and will need to ask for a new one by following the \"I forgot my password\" link on the login page (Their registered email address has to be valid.)" +msgstr "" + +#, php-format +msgid "Please note that Dotclear 2 has a new URL layout. You can avoid broken links by installing DC1 redirect plugin and activate it in your blog configuration." +msgstr "" + +msgid "next step" +msgstr "" + +msgid "Dotclear tables not found" +msgstr "" + +msgid "Feed import" +msgstr "" + +msgid "Imports a feed as new entries." +msgstr "" + +msgid "Cannot retrieve feed URL." +msgstr "" + +msgid "No items in feed." +msgstr "" + +msgid "Content successfully imported." +msgstr "" + +msgid "Import from a feed" +msgstr "" + +#, php-format +msgid "This will import a feed (RSS or Atom) a as new content in the current blog: %s." +msgstr "" + +msgid "Feed URL:" +msgstr "" + +msgid "Flat file import" +msgstr "" + +msgid "Imports a blog or a full Dotclear installation from flat file." +msgstr "" + +msgid "Single blog successfully imported." +msgstr "" + +msgid "Are you sure you want to import a full backup file?" +msgstr "" + +msgid "Import a single blog" +msgstr "" + +#, php-format +msgid "This will import a single blog backup as new content in the current blog: %s." +msgstr "" + +msgid "Upload a backup file" +msgstr "" + +msgid "or pick up a local file in your public directory" +msgstr "" + +msgid "Import a full backup file" +msgstr "" + +msgid "Warning: This will reset all the content of your database, except users." +msgstr "" + +msgid "WordPress import" +msgstr "" + +msgid "Import a WordPress installation into your current blog." +msgstr "" + +#, php-format +msgid "This will import your WordPress content as new content in the current blog: %s." +msgstr "" + +msgid "We first need some information about your old WordPress installation." +msgstr "" + +msgid "WordPress and Dotclear's handling of categories are quite different. You can assign several categories to a single post in WordPress. In the Dotclear world, we see it more like \"One category, several tags.\" Therefore Dotclear can only import one category per post and will chose the lowest numbered one. If you want to keep a trace of every category, you can import them as tags, with an optional prefix." +msgstr "" + +msgid "On the other hand, in WordPress, a post can not be uncategorized, and a default installation has a first category labelised \"Uncategorized\". If you did not change that category, you can just ignore it while importing your blog, as Dotclear allows you to actually keep your posts uncategorized." +msgstr "" + +msgid "Ignore the first category:" +msgstr "" + +msgid "Import lowest numbered category on posts:" +msgstr "" + +msgid "Import all categories as tags:" +msgstr "" + +msgid "Prefix such tags with:" +msgstr "" + +msgid "Content filters" +msgstr "" + +msgid "You may want to process your post and/or comment content with the following filters." +msgstr "" + +msgid "Post content formatter:" +msgstr "" + +msgid "Comment content formatter:" +msgstr "" + +msgid "WordPress tables not found" +msgstr "" + +msgid "No file to read." +msgstr "" + +msgid "File is not a DotClear backup." +msgstr "" + +msgid "File is not a single blog export." +msgstr "" + +msgid "File is not a full export." +msgstr "" + +msgid "The backup file does not appear to be well formed." +msgstr "" + +msgid "Please wait..." +msgstr "" + +msgid "Maintenance" +msgstr "" + +msgid "Optimization successful." +msgstr "" + +msgid "Comments and trackback counted." +msgstr "" + +msgid "Templates cache directory emptied." +msgstr "" + +msgid "Logs deleted." +msgstr "" + +#, php-format +msgid "Indexing entry %d to %d." +msgstr "" + +msgid "next" +msgstr "" + +msgid "Entries index done." +msgstr "" + +msgid "Back" +msgstr "" + +#, php-format +msgid "Indexing comment %d to %d." +msgstr "" + +msgid "Comments index done." +msgstr "" + +msgid "Optimize database room" +msgstr "" + +msgid "Vacuum tables" +msgstr "" + +msgid "Counters" +msgstr "" + +msgid "Reset comments and ping counters" +msgstr "" + +msgid "Search engine index" +msgstr "" + +msgid "This may take a very long time" +msgstr "" + +msgid "Index all posts" +msgstr "" + +msgid "Index all comments" +msgstr "" + +msgid "Vacuum logs" +msgstr "" + +msgid "Delete all logs" +msgstr "" + +msgid "Empty templates cache directory" +msgstr "" + +msgid "Empty directory" +msgstr "" + +msgid "Pages" +msgstr "" + +#, php-format +msgid "%d page" +msgstr "" + +#, php-format +msgid "%d pages" +msgstr "" + +msgid "manage pages" +msgstr "" + +msgid "Published on" +msgstr "" + +msgid "This page's comments feed" +msgstr "" + +msgid "You must provide a valid email address." +msgstr "" + +msgid "Page title" +msgstr "" + +msgid "Page position" +msgstr "" + +msgid "Publication date" +msgstr "" + +msgid "No page" +msgstr "" + +msgid "select this page" +msgstr "" + +msgid "Are you sure you want to delete selected pages?" +msgstr "" + +msgid "New page" +msgstr "" + +msgid "Selected pages action:" +msgstr "" + +msgid "This page does not exist." +msgstr "" + +msgid "Edit page" +msgstr "" + +msgid "next page" +msgstr "" + +msgid "previous page" +msgstr "" + +msgid "Are you sure you want to delete this page?" +msgstr "" + +msgid "Page has been successfully updated." +msgstr "" + +msgid "Page has been successfully created." +msgstr "" + +msgid "Go to this page on the site" +msgstr "" + +msgid "Page status:" +msgstr "" + +msgid "Page position:" +msgstr "" + +msgid "Page lang:" +msgstr "" + +msgid "Page password:" +msgstr "" + +msgid "Warning: If you set the URL manually, it may conflict with another page." +msgstr "" + +msgid "Add files to this page" +msgstr "" + +msgid "Pings" +msgstr "" + +msgid "Pings configuration" +msgstr "" + +msgid "Settings have been successfully updated." +msgstr "" + +msgid "Activate pings extension" +msgstr "" + +msgid "Service name:" +msgstr "" + +msgid "Service URI:" +msgstr "" + +msgid "error" +msgstr "" + +msgid "Test ping services" +msgstr "" + +msgid "Check all" +msgstr "" + +msgid "Pings:" +msgstr "" + +msgid "Simple menu" +msgstr "" + +msgid "Menu" +msgstr "" + +msgid "All months" +msgstr "" + +msgid "All tags" +msgstr "" + +msgid "Home" +msgstr "" + +msgid "Archive" +msgstr "" + +msgid "Page" +msgstr "" + +msgid "Tags" +msgstr "" + +msgid "User defined" +msgstr "" + +msgid "Label" +msgstr "" + +msgid "Recent posts" +msgstr "" + +#, php-format +msgid "Switch to %s language" +msgstr "" + +msgid "Recent Posts from this category" +msgstr "" + +msgid "Archives" +msgstr "" + +#, php-format +msgid "Posts from %s" +msgstr "" + +#, php-format +msgid "Recent posts for %s tag" +msgstr "" + +msgid "Label and URL of menu item are mandatory." +msgstr "" + +msgid "No menu items selected." +msgstr "" + +msgid "Label is mandatory." +msgstr "" + +msgid "URL is mandatory." +msgstr "" + +msgid "Menu item has been successfully added." +msgstr "" + +msgid "Menu items have been successfully removed." +msgstr "" + +msgid "Menu items have been successfully updated." +msgstr "" + +msgid "Add item" +msgstr "" + +msgid "Select type" +msgstr "" + +msgid "Type of item menu:" +msgstr "" + +msgid "Continue..." +msgstr "Continue…" + +msgid "Select language:" +msgstr "" + +msgid "Select category:" +msgstr "" + +msgid "Select month (if necessary):" +msgstr "" + +msgid "Select page:" +msgstr "" + +msgid "Select tag (if necessary):" +msgstr "" + +msgid "Label of item menu:" +msgstr "" + +msgid "Description of item menu:" +msgstr "" + +msgid "URL of item menu:" +msgstr "" + +msgid "Add this item" +msgstr "" + +msgid "Add an item" +msgstr "" + +msgid "Menu items list" +msgstr "" + +msgid "Update menu" +msgstr "" + +msgid "Delete selected menu items" +msgstr "" + +msgid "Are you sure you want to remove selected menu items?" +msgstr "" + +msgid "Currently no menu items" +msgstr "" + +msgid "Tags:" +msgstr "" + +#, php-format +msgid "Are you sure you want to remove this %s?" +msgstr "" + +#, php-format +msgid "Add a %s to this entry" +msgstr "" + +msgid "Choose from list" +msgstr "" + +msgid "all" +msgstr "" + +msgid "Tag" +msgstr "" + +msgid "used in %e - frequency %p%" +msgstr "" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "Enter tags separated by coma" +msgstr "" + +msgid "Add tags" +msgstr "" + +msgid "Remove tags" +msgstr "" + +msgid "Add tags to entries" +msgstr "" + +msgid "Tags to add:" +msgstr "" + +msgid "Remove selected tags from entries" +msgstr "" + +msgid "No tags for selected entries" +msgstr "" + +msgid "Following tags have been found in selected entries:" +msgstr "" + +msgid "short" +msgstr "" + +msgid "extended" +msgstr "" + +msgid "Tags list format:" +msgstr "" + +msgid "This tag's comments Atom feed" +msgstr "" + +msgid "This tag's entries Atom feed" +msgstr "" + +msgid "Limit (empty means no limit):" +msgstr "" + +msgid "Entries count" +msgstr "" + +msgid "Tag name" +msgstr "" + +msgid "Link to all tags:" +msgstr "" + +msgid "Edit tag" +msgstr "" + +msgid "Tag has been successfully renamed" +msgstr "" + +msgid "Back to tags list" +msgstr "" + +msgid "Actions for this tag" +msgstr "" + +msgid "Edit tag name:" +msgstr "" + +msgid "Rename" +msgstr "" + +msgid "Delete this tag:" +msgstr "" + +msgid "List of entries with this tag" +msgstr "" + +msgid "Tag has been successfully removed" +msgstr "" + +msgid "No tags on this blog." +msgstr "" + +msgid "Theme Editor" +msgstr "" + +msgid "No file" +msgstr "" + +msgid "File does not exist." +msgstr "" + +#, php-format +msgid "File %s is not readable" +msgstr "" + +#, php-format +msgid "Unable to write file %s. Please check your theme files and folders permissions." +msgstr "" + +msgid "Saving document..." +msgstr "" + +msgid "Document saved" +msgstr "" + +msgid "An error occurred:" +msgstr "" + +#, php-format +msgid "Your current theme on this blog is \"%s\"." +msgstr "" + +msgid "You can't edit default theme." +msgstr "" + +msgid "Please select a file to edit." +msgstr "" + +msgid "File editor" +msgstr "" + +#, php-format +msgid "Editing file %s" +msgstr "" + +msgid "This file is not writable. Please check your theme files permissions." +msgstr "" + +msgid "Templates files" +msgstr "" + +msgid "CSS files" +msgstr "" + +msgid "JavaScript files" +msgstr "" + +msgid "Preferences successfully updated" +msgstr "" + +msgid "Preferences definition successfully updated" +msgstr "" + +msgid "user preferences" +msgstr "" + +msgid "global preferences" +msgstr "" + +msgid "Presentation widgets" +msgstr "" + +msgid "Search engine" +msgstr "" + +msgid "Navigation links" +msgstr "" + +msgid "Selected entries" +msgstr "" + +msgid "Best of me" +msgstr "" + +msgid "Blog languages" +msgstr "" + +msgid "With entries counts" +msgstr "" + +msgid "Subscribe links" +msgstr "" + +msgid "Subscribe" +msgstr "" + +msgid "Feeds type:" +msgstr "" + +msgid "Feed reader" +msgstr "" + +msgid "Somewhere else" +msgstr "" + +msgid "Entries limit:" +msgstr "" + +msgid "Text" +msgstr "" + +msgid "Text:" +msgstr "" + +msgid "Last entries" +msgstr "" + +msgid "Uncategorized" +msgstr "" + +msgid "Tag:" +msgstr "" + +msgid "Last comments" +msgstr "" + +msgid "Comments limit:" +msgstr "" + +#, php-format +msgid "This blog's entries %s feed" +msgstr "" + +#, php-format +msgid "This blog's comments %s feed" +msgstr "" + +msgid "Entries feed" +msgstr "" + +msgid "Comments feed" +msgstr "" + +msgid "navigation" +msgstr "" + +msgid "extra" +msgstr "" + +msgid "custom" +msgstr "" + +msgid "Widgets" +msgstr "" + +msgid "Are you sure you want to reset sidebars?" +msgstr "" + +msgid "Available widgets" +msgstr "" + +msgid "Append to:" +msgstr "" + +msgid "Add widgets to sidebars" +msgstr "" + +msgid "Navigation sidebar" +msgstr "" + +msgid "Extra sidebar" +msgstr "" + +msgid "Custom sidebar" +msgstr "" + +msgid "Update sidebars" +msgstr "" + +msgid "Reset sidebars" +msgstr "" + +msgid "Use of widgets" +msgstr "" + +msgid "Widgets may be used to add various blocks of content to be displayed on your public pages. To add a widget, drag it from the Available widgets list on the left to one of the sidebars on the right of this page. You can order your widgets in a sidebar by dragging them up or down. You must update sidebars to apply your changes." +msgstr "" + +msgid "Once included in a sidebar, widgets have configuration options that you can reach by clicking on the + sign next to their name." +msgstr "" + +msgid "Reset sidebars to get back to default widgets installation." +msgstr "" + +msgid "Widget templates tags" +msgstr "" + +msgid "If you are allowed to edit your theme templates, you can directly add widgets as templates tags, with their own configuration." +msgstr "" + +msgid "To add a widget in your template, you need to write code like this:" +msgstr "" + +msgid "Widget ID" +msgstr "" + +msgid "Setting name" +msgstr "" + +msgid "Setting value" +msgstr "" + +msgid "Here are the following available widgets for your blog:" +msgstr "" + +msgid "Widget ID:" +msgstr "" + +msgid "No setting for this widget" +msgstr "" + +msgid "Setting name:" +msgstr "" + +msgid "No widget." +msgstr "" + +msgid "order" +msgstr "" + +msgid "Remove widget" +msgstr "" diff --git a/v2/dotclear/locales/en/public.lang.php b/v2/dotclear/locales/en/public.lang.php new file mode 100644 index 0000000..5b13df9 --- /dev/null +++ b/v2/dotclear/locales/en/public.lang.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/v2/dotclear/locales/en/public.po b/v2/dotclear/locales/en/public.po new file mode 100644 index 0000000..74431fb --- /dev/null +++ b/v2/dotclear/locales/en/public.po @@ -0,0 +1,243 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-10-13 05:22+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# Not found strings +msgid "To content" +msgstr "" + +msgid "To menu" +msgstr "" + +msgid "To search" +msgstr "" + +msgid "By" +msgstr "" + +msgid "by" +msgstr "" + +msgid "on" +msgstr "" + +msgid "On" +msgstr "" + +msgid "Continue reading" +msgstr "" + +msgid "no comment" +msgstr "" + +msgid "one comment" +msgstr "" + +msgid "%d comments" +msgstr "" + +msgid "no trackback" +msgstr "" + +msgid "one trackback" +msgstr "" + +msgid "%d trackbacks" +msgstr "" + +msgid "no attachment" +msgstr "" + +msgid "one attachment" +msgstr "" + +msgid "%d attachments" +msgstr "" + +msgid "previous entries" +msgstr "" + +msgid "page" +msgstr "" + +msgid "of" +msgstr "" + +msgid "next entries" +msgstr "" + +msgid "Search" +msgstr "" + +msgid "Your search for %1$s returned no result." +msgstr "" + +msgid "Your search for %1$s returned %2$s result." +msgstr "" + +msgid "Your search for %1$s returned %2$s results." +msgstr "" + +msgid "Home" +msgstr "" + +msgid "All keywords" +msgstr "" + +msgid "Best of me" +msgstr "" + +msgid "Languages" +msgstr "" + +msgid "Categories" +msgstr "" + +msgid "Subcategories" +msgstr "" + +msgid "Archives" +msgstr "" + +msgid "Links" +msgstr "" + +msgid "Subscribe" +msgstr "" + +msgid "Entries feed" +msgstr "" + +msgid "Comments feed" +msgstr "" + +msgid "This blog's comments Atom feed" +msgstr "" + +msgid "This category's entries Atom feed" +msgstr "" + +msgid "This category's comments Atom feed" +msgstr "" + +msgid "This post's comments feed" +msgstr "" + +msgid "This post's comments Atom feed" +msgstr "" + +msgid "Attachments" +msgstr "" + +msgid "Permalink" +msgstr "" + +msgid "Comments" +msgstr "" + +msgid "Your comment" +msgstr "" + +msgid "Your comment has been published." +msgstr "" + +msgid "Your comment has been submitted and will be reviewed for publication." +msgstr "" + +msgid "Add a comment" +msgstr "" + +msgid "Name or nickname" +msgstr "" + +msgid "Email address" +msgstr "" + +msgid "Website" +msgstr "" + +msgid "optional" +msgstr "" + +msgid "Comment" +msgstr "" + +msgid "Comments can be formatted using a simple wiki syntax." +msgstr "" + +msgid "HTML code is displayed as text and web addresses are automatically converted." +msgstr "" + +msgid "Remember me on this blog" +msgstr "" + +msgid "preview" +msgstr "" + +msgid "send" +msgstr "" + +msgid "They posted on the same topic" +msgstr "" + +msgid "Trackback URL" +msgstr "" + +msgid "You must provide an author name" +msgstr "" + +msgid "You must provide a comment" +msgstr "" + +msgid "Email address is not valid" +msgstr "" + +msgid "Document not found" +msgstr "" + +msgid "The document you are looking for does not exist." +msgstr "" + +msgid "Powered by %s" +msgstr "" + +msgid "Subscribe to" +msgstr "" + +msgid "What is an RSS feed?" +msgstr "" + +msgid "RSS feed is a free blog summary. It provides content (either posts or comments) or summaries of content, together with links to the full versions, and other metadata. The last published items may then be read by your favorite RSS aggregator." +msgstr "" + +msgid "Simply copy the following URL into your aggregator:" +msgstr "" + +msgid "Password needed" +msgstr "" + +msgid "You must give a password to access this area." +msgstr "" + +msgid "Password:" +msgstr "" + +msgid "You must provide a valid email address." +msgstr "" + +msgid "Read" +msgstr "" diff --git a/v2/dotclear/locales/en/resources.php b/v2/dotclear/locales/en/resources.php new file mode 100644 index 0000000..52ce5ec --- /dev/null +++ b/v2/dotclear/locales/en/resources.php @@ -0,0 +1,35 @@ + 'http://dotclear.org/documentation/2.0', + 'Dotclear 2 presentation' => 'http://dotclear.org/documentation/2.0/overview/tour', + "User manual" => 'http://dotclear.org/documentation/2.0/usage', + "Installation and administration guides" => 'http://dotclear.org/documentation/2.0/admin', + "Dotclear 2 support forum" => 'http://forum.dotclear.net/' +); + +$__resources['help'] = array( + 'core_blog_pref' => dirname(__FILE__).'/help/blog_pref.html', + 'core_categories' => dirname(__FILE__).'/help/categories.html', + 'core_comments' => dirname(__FILE__).'/help/comments.html', + 'core_media' => dirname(__FILE__).'/help/media.html', + 'core_post' => dirname(__FILE__).'/help/post.html', + 'core_posts' => dirname(__FILE__).'/help/posts.html', + 'core_user_pref' => dirname(__FILE__).'/help/user_pref.html', + 'core_user' => dirname(__FILE__).'/help/user.html', + 'core_wiki' => dirname(__FILE__).'/help/wiki.html' +); +?> \ No newline at end of file diff --git a/v2/dotclear/locales/fr/date.lang.php b/v2/dotclear/locales/fr/date.lang.php new file mode 100644 index 0000000..1fbb505 --- /dev/null +++ b/v2/dotclear/locales/fr/date.lang.php @@ -0,0 +1,58 @@ + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/date.po b/v2/dotclear/locales/fr/date.po new file mode 100644 index 0000000..4ea100b --- /dev/null +++ b/v2/dotclear/locales/fr/date.po @@ -0,0 +1,136 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-04-15 16:27+0200\n" +"PO-Revision-Date: 2008-04-15 16:34+0100\n" +"Last-Translator: Olivier Meunier \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" + +msgid "%Y-%m-%d %H:%M" +msgstr "%d/%m/%Y %H:%M" + +msgid "_Jan" +msgstr "janv." + +msgid "_Feb" +msgstr "fév." + +msgid "_Mar" +msgstr "mar." + +msgid "_Apr" +msgstr "avr." + +msgid "_May" +msgstr "mai" + +msgid "_Jun" +msgstr "juin" + +msgid "_Jul" +msgstr "juil." + +msgid "_Aug" +msgstr "août" + +msgid "_Sep" +msgstr "sept." + +msgid "_Oct" +msgstr "oct." + +msgid "_Nov" +msgstr "nov." + +msgid "_Dec" +msgstr "déc." + +msgid "January" +msgstr "janvier" + +msgid "February" +msgstr "février" + +msgid "March" +msgstr "mars" + +msgid "April" +msgstr "avril" + +msgid "May" +msgstr "mai" + +msgid "June" +msgstr "juin" + +msgid "July" +msgstr "juillet" + +msgid "August" +msgstr "août" + +msgid "September" +msgstr "septembre" + +msgid "October" +msgstr "octobre" + +msgid "November" +msgstr "novembre" + +msgid "December" +msgstr "décembre" + +msgid "_Mon" +msgstr "lun." + +msgid "_Tue" +msgstr "mar." + +msgid "_Wed" +msgstr "mer." + +msgid "_Thu" +msgstr "jeu." + +msgid "_Fri" +msgstr "ven." + +msgid "_Sat" +msgstr "sam." + +msgid "_Sun" +msgstr "dim." + +msgid "Monday" +msgstr "lundi" + +msgid "Tuesday" +msgstr "mardi" + +msgid "Wednesday" +msgstr "mercredi" + +msgid "Thursday" +msgstr "jeudi" + +msgid "Friday" +msgstr "vendredi" + +msgid "Saturday" +msgstr "samedi" + +msgid "Sunday" +msgstr "dimanche" + +#~ msgid "%A, %B %e %Y" +#~ msgstr "%A %e %B %Y" diff --git a/v2/dotclear/locales/fr/help/blowupConfig.html b/v2/dotclear/locales/fr/help/blowupConfig.html new file mode 100644 index 0000000..9d41c14 --- /dev/null +++ b/v2/dotclear/locales/fr/help/blowupConfig.html @@ -0,0 +1,71 @@ + + + + Configuration du thème Blowup + + + +

        En modifiant la configuration du thème Blowup, vous pouvez personnaliser votre +thème très facilement. Pour cela, renseignez simplement les champs de configuration +ou choisissez un style prédéfini.

        + +

        Couleurs

        + +

        Quand vous devez indiquer une valeur de couleur, celle-ci doit être au format +hexadécimal. Par exemple : "#FF0000" donnera du rouge. Vous pouvez vous aider +de la pipette à côté de chaque champs de couleur.

        +

        Si vous manquez d'inspiration vous pouvez consulter :

        + + +

        Unités de mesure

        + +

        Quand vous devez indiquer une taille, celle-ci doit être suivie d'une unité +de mesure. Par exemple : "1em". Si vous n'indiquez aucune unité, la valeur +donnée prendra des pixels comme unité par défaut.

        +

        Les unités possibles sont :

        +
          +
        • px
        • +
        • em
        • +
        • ex
        • +
        • pt
        • +
        • %
        • +
        + +

        Images d'en-tête

        + +

        Vous pouvez choisir une image d'en-tête parmi une liste de nombreuses images +afin de remplacer celle utilisée par défaut.

        + +

        En choisissant "Personnalisé..." parmi la liste d'images, vous pourrez +déposer votre propre image. Celle-ci doit être au format JPG ou PNG et +avoir une largeur exacte de 800 pixels. + +

        Si vous déposez une image au format JPG, un cadre sera ajouté autours de +l'image, ce qui n'est pas le cas avec une image au format PNG (dont la +transparence sera également préservée).

        + +

        Styles prédéfinis

        + +

        Vous pouvez choisir un style prédéfini dans la liste d'option "Styles prédéfinis". +Une fois le style choisi, vous devez valider le formulaire pour appliquer les +changements.

        + +

        Vous pouvez ensuite modifier le style prédéfini comme bon vous semble.

        + +

        Import / export de configuration

        + +

        À la fin des options de Blowup, vous pouvez afficher une zone appelée "Import +/ export de configuration". Dans cette zone de texte se trouve la configuration +en cours d'utilisation. Vous pouvez la copier pour la partager avec d'autres.

        + +

        Pour appliquer (importer) une configuration, il suffit simplement de remplacer +le contenu de la zone de texte par celui que vous voulez utiliser. N'oubliez pas +de cliquer sur "appliquer le code".

        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_blog_pref.html b/v2/dotclear/locales/fr/help/core_blog_pref.html new file mode 100644 index 0000000..b9a79c1 --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_blog_pref.html @@ -0,0 +1,191 @@ + + + Préférences du blog + + + + +

        Informations du blog

        +
        +
        Identifiant du blog
        +
        Identifiant unique du blog. Obligatoire, ne peut être modifié que par un + super administrateur.
        + +
        Nom du blog
        +
        Nom du blog, obligatoire.
        + +
        URL du blog
        +
        URL complète du blog. Obligatoire, ne peut être modifiée que par un super + administrateur.
        + +
        Méthode de lecture de l'URL
        +
        Définit le mode de lecture de l'URL. PATH_INFO est conseillé, QUERY_STRING + doit être utilisé si le premier ne fonctionne pas. En mode PATH_INFO, l'URL du + blog doit se terminer par un "/" et en mode QUERY_STRING, par un "?".
        + +
        État du blog
        +
        +
          +
        • en ligne : blog accessible aux visiteurs
        • +
        • hors ligne : blog inaccessible aux visiteurs + mais les rédacteurs peuvent se connecter
        • +
        • supprimé : blog inaccessible aux visiteurs + comme aux rédacteurs
        • +
        +
        + +
        Description du blog
        +
        Texte libre de description du blog. Il s'agit d'un texte simple, sans + formatage quelconque.
        +
        + +

        Configuration du blog

        +
        +
        Nom de l'éditeur du blog
        +
        Nom de la personne responsable du contenu du blog. Peut être le propriétaire + ou le directeur de publication s'il existe. Information affichée dans les + informations du flux de syndication.
        + +
        Note de copyright
        +
        Note indiquant les droits de reproduction autorisés pour le blog.
        + +
        Langue par défaut
        +
        Langue de l'interface du blog. Si cette langue existe parmi les + traductions, l'interface sera traduite dans celle-ci, sinon elle apparaîtra + en Anglais.
        + +
        Fuseau horaire du blog
        +
        Définit le fuseau horaire qui sera utilisé pour dater les commentaires et + trackbacks entrant sur le blog.
        + +
        Format des dates
        +
        Indique le format de la date d'un billet sur le blog. Voir la section + "formatage des dates" pour plus détails.
        + +
        Format des heures
        +
        Indique le format de l'heure d'un billet sur le blog. Voir la section + "formatage des dates" pour plus détails.
        + +
        Laisser les commentaires ouverts durant .. jours
        +
        Permet d'indiquer le nombre de jours durant lesquels les commentaires + sont possibles sur un billet après sa publication. Aucune valeur laissera + les commentaires toujours ouverts.
        + +
        Laisser les rétroliens ouverts durant .. jours
        +
        Permet d'indiquer le nombre de jours durant lesquels les rétroliens + sont possibles sur un billet après sa publication. Aucune valeur laissera + les rétroliens toujours ouverts.
        + +
        Accepter les commentaires
        +
        Accepter globalement les commentaires sur le blog. Ce paramètre est + supérieur à celui autorisant ou non les commentaires sur un billet.
        + +
        Accepter les rétroliens
        +
        Accepter globalement les rétroliens sur le blog. Ce paramètre est + supérieur à celui autorisant ou non les rétroliens sur un billet.
        + +
        Modérer les commentaires
        +
        Si cette option est activée, les commentaires ne seront publiés qu'après + l'approbation d'un rédacteur.
        + +
        Modérer les rétroliens
        +
        Si cette option est activée, les rétroliens ne seront publiés qu'après + l'approbation d'un rédacteur.
        + +
        Ajouter la relation "nofollow" aux liens des commentaires et rétroliens
        +
        Ajoute un attribut sur les liens des commentaires et rétroliens indiquant + aux robots des moteurs de recherche de ne pas les suivre. Cette mesure + est censée permettre de lutter contre le spam mais n'a pas encore vraiment + prouvé son efficacité.
        + +
        Syntaxe wiki pour les commentaires
        +
        Autoriser quelques éléments de la syntaxe wiki dans les commentaires.
        + +
        Afficher .. billets par page
        +
        Le nombre donné sera utilisé comme limite d'affichage des billets sur la + page d'accueil et la première page de chaque catégorie.
        + +
        Afficher des émoticônes dans les billets et commentaires
        +
        Remplacer certaines suites de caractères comme :-) ou ;-) par des images.
        + +
        Afficher .. billets par flux de syndication
        +
        Le nombre donné sera utilisé comme limite d'affichage des billets dans + les flux de syndication.
        + +
        Afficher .. commentaires par flux de syndication
        +
        Le nombre donné sera utilisé comme limite d'affichage des commentaires dans + les flux de syndication.
        + +
        Tronquer le flux de syndication
        +
        Cette option permet de ne fournir qu'un extrait des billets dans les flux + de syndication.
        +
        + +

        Formatage des dates

        +

        Certains champs permettent de formater les dates avec des caractères +particuliers dont voici la définition :

        + +
          +
        • %a : nom abrégé du jour de la semaine (local).
        • +
        • %A : nom complet du jour de la semaine (local).
        • +
        • %b : nom abrégé du mois (local).
        • +
        • %B : nom complet du mois (local).
        • +
        • %c : représentation préférée pour les dates et + heures, en local.
        • +
        • %C : numéro de siècle (l'année, divisée par 100 et + arrondie entre 00 et 99)
        • +
        • %d : jour du mois en numérique (intervalle 01 à + 31)
        • +
        • %D : identique à %m/%d/%y
        • +
        • %e : numéro du jour du mois. Les chiffres sont + précédés d'un espace (de ' 1' à '31')
        • +
        • %g : identique à %G, sur 2 chiffres.
        • +
        • %G : L'année sur 4 chiffres correspondant au numéro + de semaine (voir %V). Même format et valeur que %Y, excepté que si le numéro + de la semaine appartient à l'année précédente ou suivante, l'année courante + sera utilisé à la place.
        • +
        • %h : identique à %b
        • +
        • %H : heure de la journée en numérique, et sur + 24-heures (intervalle de 00 à 23)
        • +
        • %I : heure de la journée en numérique, et sur 12- + heures (intervalle 01 à 12)
        • +
        • %j : jour de l'année, en numérique (intervalle 001 à + 366)
        • +
        • %m : mois en numérique (intervalle 1 à 12)
        • +
        • %M : minute en numérique
        • +
        • %n : newline character
        • +
        • %p : soit `am' ou `pm' en fonction de l'heure + absolue, ou en fonction des valeurs enregistrées en local.
        • +
        • %r : l'heure au format a.m. et p.m.
        • +
        • %R : l'heure au format 24h
        • +
        • %S : secondes en numérique
        • +
        • %t : tabulation
        • +
        • %T : l'heure actuelle (égal à %H:%M:%S)
        • +
        • %u : le numéro de jour dans la semaine, de 1 à 7. (1 + représente Lundi)
        • +
        • %U : numéro de semaine dans l'année, en considérant + le premier dimanche de l'année comme le premier jour de la première + semaine.
        • +
        • %V : le numéro de semaine comme défini dans l'ISO + 8601:1988, sous forme décimale, de 01 à 53. La semaine 1 est la première + semaine qui a plus de 4 jours dans l'année courante, et dont Lundi est le + premier jour. (Utilisez %G ou %g pour les éléments de l'année qui + correspondent au numéro de la semaine pour le timestamp donné.)
        • +
        • %W : numéro de semaine dans l'année, en considérant + le premier lundi de l'année comme le premier jour de la première semaine
        • +
        • %w : jour de la semaine, numérique, avec Dimanche = + 0
        • +
        • %x : format préféré de représentation de la date + sans l'heure
        • +
        • %X : format préféré de représentation de l'heure + sans la date
        • +
        • %y : l'année, numérique, sur deux chiffres (de 00 à + 99)
        • +
        • %Y : l'année, numérique, sur quatre chiffres
        • +
        • %Z ou %z : fuseau horaire, ou nom ou + abréviation
        • +
        • %% : un caractère `%' littéral
        • +
        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_categories.html b/v2/dotclear/locales/fr/help/core_categories.html new file mode 100644 index 0000000..002800e --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_categories.html @@ -0,0 +1,50 @@ + + + Catégories + + + +

        Liste des categories

        +
        +
        Créer une nouvelle catégorie
        +
        Choisissez le nom que vous souhaitez donner à votre nouvelle catégorie + puis cliquer sur Enregistrer pour valider sa création. Vous pouvez choisir + sa catégorie parente ou la laisser au premier niveau. Pour modifier la catégorie, + cliquez sur son nom dans la liste.
        + +
        Supprimer une catégorie
        +
        Choisissez la catégorie que vous voulez supprimer et cliquez sur ok. + Une catégorie ne peut être supprimée que si elle ne contient aucun billet.
        + +
        Réordonner les catégories
        +
        Utilisez cette option pour placer toutes les catégories au même niveau. + Si vous voulez modifier l'ordre des catégories plus précisément, cliquez sur le nom de chacune d'entre elles + et choisissez sa catégorie parente ou sa position par rapport à ses voisines.
        +
        + +

        Modifier une catégorie

        +
        +
        Titre
        +
        Choisissez le nom que vous souhaitez donner à votre nouvelle catégorie + puis cliquer sur Enregistrer pour valider sa création.
        + +
        URL
        +
        DotClear crée une url par défaut de la catégorie, qui sera ainsi + accessible avec un chemin de la forme + http://monblog.tld/category/Ma-categorie. En cliquant sur le + petit verrou situé à la droite, vous pouvez la modifier comme il vous + plaira.
        + +
        Description
        +
        Le contenu de ce champ de description sera affiché lors de la sélection + d'une catégorie dans le blog. Vous pouvez le remplir avec toute forme de + contenu (paragraphes, listes, etc.) Le format du texte de la description est + HTML.
        + +
        Déplacer cette catégorie
        +
        Vous pouvez déplacer la catégorie dans n'importe quelle autre, qui deviendra sa parente. + Vous pouvez aussi spécifier sa position par rapport à ses voisines en la plaçant avant ou après l'une d'elles.
        +
        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_comments.html b/v2/dotclear/locales/fr/help/core_comments.html new file mode 100644 index 0000000..1fe0676 --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_comments.html @@ -0,0 +1,59 @@ + + + Commentaires + + + +

        Filtres de la liste des commentaires

        +
        +
        Type :
        +
        Aucun ou commentaires ou rétroliens.
        + +
        État :
        +
          +
        • indésirable : reconnu comme spam ;
        • +
        • en attente : en attente de modération ;
        • +
        • non publié : hors ligne ;
        • +
        • publié : en ligne.
        • +
        + +
        Trier par :
        +
        Choisir : date, ou titre du billet, ou auteur ou état, puis indiquez si vous + souhaitez que les résultats s'affichent par ordre croissant ou décroissant.
        + +
        Trier
        +
        Indique l'ordre dans lequel on souhaite effectuer le tri.
        + +
        Auteur du commentaire.
        +
        Recherche indifféremment avec des minuscules ou majuscules. On peut chercher + sur une partie du nom en utilisant le caractère joker + *.
        + +
        Commentaires par page :
        +
        Détermine le nombre de commentaires affichés par page de cette liste.
        +
        + +

        Modifier ou ajouter un commentaire

        +
        +
        Auteur :
        +
        Nom de l'auteur. Ce champ est obligatoire.
        + +
        Email :
        +
        Adresse mail de l'auteur du commmentaire.
        + +
        Site web :
        +
        URL du site ou du blog de l'auteur du commentaire.
        + +
        État :
        +
          +
        • indésirable : reconnu comme spam ;
        • +
        • en attente : en attente de modération ;
        • +
        • non publié : hors ligne
        • +
        • publié : en ligne
        • +
        + +
        Commentaire :
        +
        Contenu du commentaire. Ce champ utilise la syntaxe HTML.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_media.html b/v2/dotclear/locales/fr/help/core_media.html new file mode 100644 index 0000000..aeb6e85 --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_media.html @@ -0,0 +1,97 @@ + + + Gestionnaire de media + + + + +

        Liste des médias

        +

        La page principale du gestionnaire de médias présente la liste des fichiers +(médias) disponibles pour le blog en cours d'utilisation.

        + +

        Il est possible d'afficher les médias dans des répertoires ou sous +répertoires en cliquant sur leurs icônes ou leurs noms.

        + +

        Les fichiers son au format MP3 sont présentés avec un lecteur permettant +d'en prendre immédiatement connaissance. Ce lecteur nécessite un lecteur (ou +plugin) Flash sur votre navigateur.

        + +

        Les images sont affichées avec une miniature quand cela est possible.

        + +

        Ajouter des fichiers

        +
        +
        Choisissez un fichier
        +
        Permet de choisir un fichier sur son disque dur pour l'envoyer sur le + blog. Si le fichier dépasse la taille maximale, il ne pourra être envoyé.
        + +
        Titre
        +
        Titre optionnel du fichier à envoyer.
        + +
        Privé
        +
        Indique que le fichier envoyé n'est visible que par son propriétaire tant + qu'il n'est pas publié sur le blog.
        +
        + +

        Nouveau répertoire

        +

        Ce formulaire permet de créer un nouveau répertoire dans le répertoire en +cours d'utilisation. Indiquez simplement un nom comme vous le feriez sur votre +ordinateur.

        + +

        Détails du média

        +

        La page individuelle d'un média présente un ensemble d'informations sur +celui-ci et permet d'effectuer quelques opérations.

        + +
        +
        Nom du fichier
        +
        Changer le nom du fichier en cours.
        + +
        Titre du fichier
        +
        Changer ou donner un titre au fichier en cours.
        + +
        Date du fichier
        +
        Changer la date du fichier.
        + +
        Privé
        +
        Changer le caractère privé ou non du média.
        + +
        Nouveau répertoire
        +
        Permet de changer l'emplacement du fichier.
        + +
        Changer le fichier
        +
        Ce formulaire permet de changer le fichier tout en conservant ses + attributs (nom, titre...). Comme toujours, il ne peut dépasser la taille + maximalle indiquée.
        +
        + +

        Cas particulier des images

        +

        Lors de l'ajout d'une image, jusqu'à quatre versions de celle-ci peuvent +être disponibles (selon la taille de l'image d'origine) :

        +
          +
        • carrée : image carrée de 48 pixels de côté,
        • +
        • miniature : image réduite à 100 pixels sur son plus grand côté,
        • +
        • petite : image réduite à 240 pixels sur son plus grand côté,
        • +
        • moyenne : image réduite à 500 pixes sur son plus grand côté,
        • +
        • originale : image originale non transformée.
        • +
        +

        Les tailles par défaut des versions miniature, petite et moyenne, peuvent être modifiées dans les paramètres du blog

        + +

        Attacher un fichier à un billet

        +

        Vous pouvez attacher un fichier à un billet très facilement. Commencez par +créer un nouveau billet et enregistrez-le. Cliquez alors sur "ajouter un fichier +au billet" pour ouvrir le gestionnaire de médias.

        + +

        En cliquant sur l'icône [+] (Attacher un fichier au billet) vous pourrez +joindre le fichier à votre billet.

        + +

        Les fichiers attachés à un billet seront visibles sur la page individuelle +du billet, sous la forme d'une liste après le contenu du billet. Les fichiers +MP3 seront automatiquement accompagnés d'un lecteur en Flash permettant la +lecture directe du fichier.

        + +

        Podcast et diffusion de fichiers musicaux

        +

        Tous les fichiers attachés à un billet seront présents dans les fils RSS de +vos billets, vous permettant ainsi de réaliser des podcast de n'importe quel +type de fichier.

        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_post.html b/v2/dotclear/locales/fr/help/core_post.html new file mode 100644 index 0000000..418a7ec --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_post.html @@ -0,0 +1,137 @@ + + + Rédaction d'un billet + + + + +

        Rédaction du billet

        + +
        +
        Titre du billet
        +
        Inscrivez le titre du billet. Ce champ est obligatoire.
        + +
        Extrait
        +
        Le contenu de ce champ s'affichera dans les pages présentant les listes de + billets telles que la page d'accueil ou le tri sur une catégorie, suivi d'un + lien « Lire la suite ». Il figurera également au début du billet dans le + contexte de l'affichage du billet seul. Si vous ne remplissez pas ce champ, le + champ Contenu sera intégralement affiché dès la page d'accueil.
        + +
        Contenu
        +
        Le contenu du billet. Ce champ est obligatoire.
        + +
        Notes
        +
        Cette zone de texte sert à la prise de notes ou pense-bête divers. Elle ne + sera jamais affichée sur le blog.
        + +
        Catégorie
        +
        La catégorie de votre billet. Pour créer une nouvelle catégorie, rendez vous + dans la section "catégories". Vous pouvez n'affecter + votre billet à aucune catégorie en choisissant la ligne vide.
        + +
        État du billet
        +
        Permet de choisir l'état du billet après enregistrement : +
          +
        • en attente : en attente de publication.
        • +
        • programmé : le billet sera mis en ligne aux date et heure + indiquées dans le champ Publié le.
        • +
        • non publié : billet hors ligne.
        • +
        • publié : billet en ligne.
        • +
        +
        + +
        Publié le
        +
        Permet de modifier la date et l'heure de publication du billet. Si vous avez + choisi le statut programmé il sera mis en ligne aux date et heure + définis dans ce champ.
        + +
        Format du texte
        +
        Permet de choisir la syntaxe de saisie du billet. Le wiki est une syntaxe + simplifiée et sera converti en xhtml valide ; à moins que vous maîtrisiez + parfaitement le xhtml nous vous conseillons de la choisir de préférence. + Consultez la référence de la syntaxe Wiki pour plus + d'informations.
        + +
        Accepter les commentaires
        +
        Cochez ou décochez la case selon que vous souhaitez permettre ou interdire + les commentaires sur le billet en particulier. L'option pour permettre ou + autoriser de façon générale les commentaires se situe dans le menu Préférences + du blog.
        + +
        Accepter les rétroliens
        +
        Un rétrolien permet de signaler un billet dans les commentaires d'un autre + blog. Cochez ou décochez la case selon que vous souhaitez permettre ou interdire + les rétroliens sur le billet. L'option pour permettre ou autoriser de façon + générale les rétroliens se situe dans le menu Préférences du blog.
        + +
        Billet sélectionné
        +
        Les billets marqués comme sélectionnés apparaîtront dans le menu de votre + blog, sous l'intitulé « A retenir ».
        + +
        Mot de passe du billet
        +
        Permet de déterminer un mot de passe d'accès à un billet dans le blog. Un + billet protégé par mot de passe ne sera visible nulle part sur votre blog, vous + pourrez en donner l'adresse à vos lecteurs en vous rendant sur le lien + voir le billet.
        + +
        URL spécifique
        +
        Ce champ permet de choisir une URL pour un billet autre que celle par défaut + après avoir cliqué sur le petit verrou placé à sa droite. Si vous essayez + d'utiliser une URL déjà existante, celle-ci se verra incrémentée d'un chiffre.
        + +
        Langue du billet
        +
        Code langue de votre billet. Par défaut, il s'agit du code de votre + langue. Vous pouvez le changer, par le code d'une autre langue, par + exemple "en", "fr-qc". Ce code est libre, il sera utilisé lors de l'affichage + des billets.
        + +
        Pièces jointes
        +
        Les pièces jointes sont tous les médias attachés au billet. Le lien + d'ajout d'une pièce jointe permet de choisir un fichier à ajouter au billet. + Pensez à sauvegarder votre billet avant de choisir une pièce jointe. Consultez + l'aide du gestionnaire de médias pour plus de détails.
        +
        + +

        Rétroliens

        +

        Pour faire un rétrolien cliquez sur le lien Faire des rétroliens. +Si vous ne voyez pas ce lien, vérifiez que votre billet est bien dans l'état +publié.

        + +
        +
        URLs à rétrolier
        +
        Indiquez ici la ou les URL que vous aurez relevé dans le billet vers lequel + vous souhaitez envoyer un rétrolien.
        + + +
        Envoyer l'extrait
        +
        Par défaut, ce champ comporte le début de votre billet. C'est ce qui sera + envoyé vers le blog que vous rétroliez avec un lien vers votre billet complet. + Vous pouvez modifier le contenu de cette "accroche" en saisissant directement le + texte de votre choix dans ce champ.
        + + +
        Découverte automatique des URLs à rétrolier
        +
        Si votre billet comporte des liens vers des billets précis et si la + plate-forme du blog destinataire est configurée pour le permettre, cette + fonction découvrira automatiquement les URLs spécifiques d'envoi de + rétrolien.
        +
        + +

        Commentaires

        +
        +
        Liste les commentaires
        +
        Depuis l'onglets Commentaires vous pouvez lire et changer l'état + des commentaires de votre billet. Suivant vos permissions, vous pouvez modifier, + mettre en ligne ou hors ligne, supprimer ou classer comme commentaire + indésirable.
        + +
        Ajouter un commentaire
        +
        Depuis l'onglet Ajouter un commentaire vous pouvez répondre + directement à un commentaire sans passer par votre blog. Cette interface vous + permet également de saisir votre commentaire en xhtml, sans limitation. Vous + devrez remplir les champs de la même manière qu'en modifiant un commentaire
        +
        + + + diff --git a/v2/dotclear/locales/fr/help/core_posts.html b/v2/dotclear/locales/fr/help/core_posts.html new file mode 100644 index 0000000..69b8cf1 --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_posts.html @@ -0,0 +1,65 @@ + + + Gestion des billets + + + + +

        Filtres de la liste des billets

        + +
        +
        Auteur
        +
        Permet de filtrer les billets par auteur.
        + +
        Catégorie
        +
        Filtrer les billets par catégorie.
        + +
        État
        +
          +
        • en attente : en attente de publication.
        • +
        • programmé : le billet sera mis en ligne aux date et heure + indiquées dans le champ Publié le.
        • +
        • non publié : billet hors ligne.
        • +
        • publié : billet en ligne.
        • +
        + +
        Sélectionné
        +
        Aucun, billet marqué comme sélectionné ou non sélectionné.
        + +
        Mois
        +
        Filtre les billets d'un mois d'une année donné.
        + +
        Langue
        +
        Filtre les billets selon la langue indiquée dans le champ langue du + billet.
        + +
        Trier par
        +
        Permet de trier les résultats de filtrage selon la date, le titre, + la catégorie, l'auteur, l'état de publication ou l'état de sélection.
        + +
        Trier
        +
        Indique l'ordre dans lequel on souhaite effectuer le tri.
        + +
        Billets par page
        +
        Nombre de billets à afficher par page de résultat.
        +
        + +

        Actions par lot sur les billets

        +

        Il est possible d'effectuer un ensemble d'actions sur plusieurs billets, d'un +seul coup. Les actions possibles dépendent des permissions de l'utilisateur.

        + +
          +
        • Publier : mettre le billet en ligne,
        • +
        • Hors ligne : mettre le billet hors ligne,
        • +
        • Programmer : programmer le billet pour mise en ligne à la date de + publication,
        • +
        • En attente : en attente de publication,
        • +
        • Changer de catégorie : envoie sur la liste des catégories pour + changer celle des billets sélectionnés,
        • +
        • Changer l'auteur : permet de changer l'auteur du billet en indiquant + l'identifiant de l'utilisateur qui deviendra le nouvel auteur,
        • +
        • Supprimer : supprime le billet (cette opération est irréversible).
        • +
        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_user.html b/v2/dotclear/locales/fr/help/core_user.html new file mode 100644 index 0000000..9ebc52e --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_user.html @@ -0,0 +1,74 @@ + + + Utilisateur + + + + +

        Informations utilisateur

        +
        +
        Nom d'utilisateur
        +
        Il doit être composé d'au moins 2 caractères (lettres non accentuées, chiffres ou symboles, pas d'espace). Ce champ est obligatoire.
        + +
        Mot de passe
        +
        Indiquez deux fois le mot de passe dans les champs Mot de passe et Confirmer le mot de + passe. Le mot de passe doit être long d'au moins 6 caractères. + Ces deux champs sont obligatoires.
        + +
        Nom, Prénom
        +
        Si le pseudonyme n'est pas renseigné, le nom de l'auteur qui sera affiché + sera composé des noms et prénoms renseignés dans ces champs.
        + +
        Pseudonyme
        +
        Vous pouvez choisir ici le nom sous lequel vos billets seront signés. S'il est renseigné, le pseudonyme se substitue au nom et au prénom.
        + +
        Email
        +
        Cette adresse sera utilisée lors de la régénération du mot de passe depuis la fenêtre d'authentification de dotclear.
        + +
        URL
        +
        Indique le site web de l'utilisateur. S'il est indiqué, le nom du rédacteur + d'un billet sera présenté comme un lien vers le site donné.
        + +
        Format d'édition préféré
        +
        Choix de la syntaxe par défaut pour la saisie des billets. Le wiki est une + syntaxe simplifiée et sera converti en xhtml valide ; à moins que vous ne + maîtrisiez parfaitement le xhtml, nous vous conseillons le choix du wiki.
        + +
        État des billets par défaut
        +
        Le statut par défaut des billets peut être déterminé à : +
          +
        • en attente : le statut de publication n'a pas encore + été décidé.
        • +
        • programmé : le billet sera mis en ligne aux date et + heure indiquées dans le champ Publié le.
        • +
        • non publié : billet hors ligne.
        • +
        • publié : billet en ligne.
        • +
        +
        + +
        Taille de la zone d'édition
        +
        Fixe la hauteur de la zone d'édtion du billet. Par défaut ce paramètre est + réglé à la valeur 24.
        + +
        Langue de l'utilisateur
        +
        Indique la langue dans laquelle on souhaite publier ses billets. Si la + traduction existe, l'interface sera également traduite dans cette langue.
        + +
        Fuseau horaire de l'utilisateur
        +
        Ce choix déterminera l'affichage de l'heure de publication des billets.
        + +
        Changement de mot de passe requis pour la connexion
        +
        Cocher cette case permettra au nouvel utilisateur de choisir par lui-même son mot de passe de connexion.
        + +
        Super administrateur
        +
        Ce choix attribuera toutes les permissions au profil utilisateur sur l'ensemble des blogs de l'installation.
        +
        + +

        Tags

        +
        +
        Format de la liste des tags
        +
        Ce choix permet, dans la page d'édition d'un billet, d'afficher tous les tags + disponibles ou seulement les plus utilisés.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_user_pref.html b/v2/dotclear/locales/fr/help/core_user_pref.html new file mode 100644 index 0000000..f119686 --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_user_pref.html @@ -0,0 +1,91 @@ + + + Préférences utilisateur + + + + +

        Mon profil

        +
        +
        Nom, Prénom
        +
        Si le pseudonyme n'est pas renseigné, le nom de l'auteur qui sera affiché + sera composé des noms et prénoms renseignés dans ces champs.
        + +
        Pseudonyme
        +
        Vous pouvez choisir ici le nom sous lequel vos billets seront signés. S'il est renseigné, le pseudonyme se substitue au nom et au prénom.
        + +
        Email
        +
        Adresse à laquelle devront être envoyées les notifications de nouveaux + commentaires par email.
        + +
        URL
        +
        Indique le site web de l'utilisateur. S'il est indiqué, le nom du rédacteur + d'un billet sera présenté comme un lien vers le site donné.
        + +
        Langue de l'utilisateur
        +
        Indique la langue dans laquelle on souhaite publier ses billets. Si la + traduction existe, l'interface sera également traduite dans cette langue.
        + +
        Fuseau horaire de l'utilisateur
        +
        Ce choix déterminera l'affichage de l'heure de publication des billets.
        + +
        Changer le mot de passe
        +
        Pour changer votre mot de passe, indiquez deux fois le nouveau mot de passe + dans les champs Nouveau mot de passe et Confirmer le mot de + passe. Le mot de passe doit être long d'au moins 6 caractères. + Si aucun mot de passe n'est spécifié, il ne sera pas changé.
        +
        + +

        Mes options

        +
        +
        Format d'édition préféré
        +
        Choix de la syntaxe par défaut pour la saisie des billets. Le wiki est une + syntaxe simplifiée et sera converti en xhtml valide ; à moins que vous ne + maîtrisiez parfaitement le xhtml, nous vous conseillons le choix du wiki.
        + +
        État des billets par défaut
        +
        Le statut par défaut des billets peut être déterminé à : +
          +
        • en attente : le statut de publication n'a pas encore + été décidé.
        • +
        • programmé : le billet sera mis en ligne aux date et + heure indiquées dans le champ Publié le.
        • +
        • non publié : billet hors ligne.
        • +
        • publié : billet en ligne.
        • +
        +
        + +
        Taille de la zone d'édition
        +
        Fixe la hauteur de la zone d'édtion du billet. Par défaut ce paramètre est + réglé à la valeur 24.
        + +
        Activer l'éditeur visuel
        +
        Permet d'activer ou non l'éditeur visuel des billets et des catégories.
        + +
        Activer l'interface avancée du gestionnaire de médias
        +
        Cette option permet d'envoyer plusieurs fichiers à la fois sur le serveur. + Nécessite la présence du plugin Flash dans votre navigateur.
        + +
        Modules du tableau de bord
        +
        Les choix opérés dans cette zone détermineront l'affichage de + ces blocs sur le tableau de bord.
        + +
        Format de la liste de tags
        +
        Ce choix permet, dans la page d'édition d'un billet, d'afficher tous les tags + disponibles ou seulement les plus utilisés.
        +
        +

        Mes favoris

        +
        +
        Mes favoris
        +
        Cette zone affiche les favoris actuellement utilisés. Ils peuvent être réordonnés + au moyen de la souris si javascript est activé dans votre navigateur. Sinon, une case + permet d'attribuer à chaque favori un numéro d'ordre. Lorsque vous avez terminé, n'oubliez + pas d'enregistrer vos modifications. Vous pouvez aussi sélectionner les favoris à supprimer + du tableau de bord.
        + +
        Favoris disponibles
        +
        Tous les plugins permettant d'être mis en favoris sont listés ici. + Cochez les cases correspondantes aux favoris que vous souhaitez utiliser, puis ajoutez-les.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/core_wiki.html b/v2/dotclear/locales/fr/help/core_wiki.html new file mode 100644 index 0000000..1fb3b90 --- /dev/null +++ b/v2/dotclear/locales/fr/help/core_wiki.html @@ -0,0 +1,85 @@ + + + Référence de la syntaxe Wiki + + + + +

        Référence de la syntaxe Wiki

        + +

        La syntaxe Wiki est une manière d'écrire du texte avec un jeu de balises +réduit au minimum, permettant de couvrir les besoins les plus courants +(titres, paragraphes, citations, listes...).

        + +
        +
        Éléments de bloc
        +
        +
          +
        • Laissez une ligne vide entre chaque bloc de même nature.
        • +
        • Paragraphe : texte libre, terminé par une ligne + vide si suivi d'un second paragraphe.
        • +
        • Titre : !!! titre, !! titre + ou ! titre pour des titres plus ou moins importants.
        • +
        • Trait horizontal : ----
        • +
        • Listes : lignes débutant par * pour des + listes à puce ou # pour des listes numérotées. Vous pouvez faire + des listes imbriquées en mélangeant les codes de liste. Par exemple : +
          +* item 1
          +** item 1.1
          +* item 2
          +*# item 2.1
          +...
          +
          +
        • +
        • Texte préformaté : espace avant chaque ligne de texte.
        • +
        • Bloc de citation : > devant chaque + ligne de texte.
        • +
        +
        + +
        Éléments de formatage
        +
        +
          +
        • Emphase : deux apostrophes ''texte''
        • +
        • Forte emphase : deux soulignés __texte__
        • +
        • Retour forcé à la ligne : %%%
        • +
        • Insertion : deux plus ++texte++
        • +
        • Suppression : deux moins --texte--
        • +
        • Lien : [url], [nom|url], + [nom|url|langue] ou [nom|url|langue|titre]
        • +
        • Image : + ((url|texte alternatif)), + ((url|texte alternatif|position)) ou + ((url|texte alternatif|position|description longue)). +
          La position peut prendre les valeurs L ou G (gauche), R ou D (droite) ou C (centré).
        • +
        • Ancre : ~ancre~
        • +
        • Acronyme : ??acronyme|titre??
        • +
        • Code HTML en ligne: deux apostrophes inversées ``code html``
        • +
        • Citation en ligne : {{citation}}, + {{citation|langue}} ou {{citation|langue|url}}
        • +
        • Code : @@code ici@@
        • +
        • Note de bas de page : $$Corps de la note$$
        • +
        +
        + +
        Empêcher le formatage du texte
        +
        Pour insérer un caractère sans que celui-ci soit reconnu comme un caractère +de formatage, ajoutez le caractère \ avant celui-ci. Par exemple : +\[texte entre crochet qui n'est pas un lien\] +
        + +
        Insérer du code HTML
        +
        Vous pouvez ponctuellement avoir besoin d'insérer du code HTML dans votre +texte au format Wiki. Pour cela, utilisez le code suivant : +
        +///html
        +<p style="color:red">mon texte en rouge</p>
        +///
        +
        +
        +
        + + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/help/themeEditor.html b/v2/dotclear/locales/fr/help/themeEditor.html new file mode 100644 index 0000000..b79802b --- /dev/null +++ b/v2/dotclear/locales/fr/help/themeEditor.html @@ -0,0 +1,37 @@ + + + + Éditeur de thème + + + +

        L'éditeur de thème vous permet de modifier les fichiers template, les feuilles +de style et les fichiers JavaScript de votre thème en cours d'utilisation.

        + +

        La liste des fichiers est divisée en trois parties :

        +
          +
        • Fichiers template : les templates
        • +
        • Fichiers CSS : les feuilles de style
        • +
        • Fichiers JavaScript
        • +
        + +

        Une puce jaune contre le nom de chaque fichier indique que celui-ci fait partie +du thème. Une puce rouge indique qu'il se trouve dans le thème parent. +Une puce noire indique qu'il se trouve dans le thème "default".

        + +

        Si vous modifiez un fichier se trouvant dans le thème "default", celui-ci +sera copié dans votre thème en cours d'utilisation.

        + +

        Pour modifier un fichier, cliquez sur son nom, le contenu s'affichera alors +dans une zone d'édition. Si le fichier peut-être écrit vous pourrez sauvegarder +celui-ci en cliquant sur "enregistrer".

        + +

        Les modifications sont immédiatement appliquées lors de la sauvegarde, soyez +vigilants.

        + +

        Pour éditer vos templates, n'hésitez pas à consultez la +liste des marqueurs +de template.

        + + + \ No newline at end of file diff --git a/v2/dotclear/locales/fr/main.lang.php b/v2/dotclear/locales/fr/main.lang.php new file mode 100644 index 0000000..2609024 --- /dev/null +++ b/v2/dotclear/locales/fr/main.lang.php @@ -0,0 +1,923 @@ +This is a comment.

        +

        To delete it, log in and view your blog\'s comments. Then you might remove or edit it.

        '] = '

        Ceci est un commentaire

        Pour le supprimer, connectez-vous et affichez les commentaires de votre blog. Vous pourrez alors le supprimer ou le modifier.

        '; +$GLOBALS['__l10n']['Dotclear Install'] = 'Installation de Dotclear'; +$GLOBALS['__l10n']['show'] = 'voir'; +$GLOBALS['__l10n']['Dotclear installation'] = 'Installation de Dotclear'; +$GLOBALS['__l10n']['Cache directory %s is not writable.'] = 'Le répertoire de cache %s n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['Errors:'] = 'Erreurs :'; +$GLOBALS['__l10n']['Configuration file has been successfully created.'] = 'Le fichier de configuration a été créé avec succès.'; +$GLOBALS['__l10n']['User information'] = 'Informations utilisateur'; +$GLOBALS['__l10n']['Please provide the following information needed to create the first user.'] = 'Merci de fournir les informations suivantes pour créer le premier utilisateur.'; +$GLOBALS['__l10n']['First Name:'] = 'Prénom :'; +$GLOBALS['__l10n']['Last Name:'] = 'Nom :'; +$GLOBALS['__l10n']['Username and password'] = 'Identifiant et mot de passe'; +$GLOBALS['__l10n']['All done!'] = 'Terminé !'; +$GLOBALS['__l10n']['Dotclear has been successfully installed. Here is some useful information you should keep.'] = 'Dotclear a été installé avec succès. Conservez les informations suivantes précieusement.'; +$GLOBALS['__l10n']['Your account'] = 'Votre compte'; +$GLOBALS['__l10n']['Your blog'] = 'Votre blog'; +$GLOBALS['__l10n']['Blog address:'] = 'Adresse du blog :'; +$GLOBALS['__l10n']['Administration interface:'] = 'Interface d\'administration :'; +$GLOBALS['__l10n']['Manage your blog now'] = 'Gérez votre blog'; +$GLOBALS['__l10n']['Installation can not be completed'] = 'L\'installation ne peut pas être menée à bien'; +$GLOBALS['__l10n']['For the said reasons, Dotclear can not be installed. Please refer to the documentation to learn how to correct the problem.'] = 'Pour les raisons ci-dessus, Dotclear ne peut pas être installé. Référez-vous à la documentation pour savoir comment corriger le problème.'; +$GLOBALS['__l10n']['File %s does not exist.'] = 'Le fichier %s n\'existe pas.'; +$GLOBALS['__l10n']['Cannot write %s file.'] = 'Impossible d\'écrire le fichier %s.'; +$GLOBALS['__l10n']['Dotclear installation wizard'] = 'Assistant d\'installation de Dotclear'; +$GLOBALS['__l10n']['Welcome'] = 'Bienvenue'; +$GLOBALS['__l10n']['To complete your Dotclear installation and start writing on your blog, we just need to know how to access your database and who you are. Just fill this two steps wizard with this information and we will be done.'] = 'Pour achever votre installation de Dotclear, il ne manque plus que les informations concernant votre base de données, puis vos informations personnelles. Remplissez simplement les deux formulaires suivants et vous pourrez commencer à utiliser votre blog.'; +$GLOBALS['__l10n']['Attention:'] = 'Attention :'; +$GLOBALS['__l10n']['this wizard may not function on every host. If it does not work for you, please refer to the documentation to learn how to create the config.php file manually.'] = 'cet assistant peut ne pas fonctionner chez tous les hébergeurs. Si vous rencontrez un problème, vous trouverez comment créer le fichier de configuration dans la documentation.'; +$GLOBALS['__l10n']['System information'] = 'Informations système'; +$GLOBALS['__l10n']['Please provide the following information needed to create your configuration file.'] = 'Merci de fournir les informations suivantes :'; +$GLOBALS['__l10n']['Database type:'] = 'Type de base de données :'; +$GLOBALS['__l10n']['Database Host Name:'] = 'Nom d\'hôte de la base de données :'; +$GLOBALS['__l10n']['Database Name:'] = 'Nom de la base de données :'; +$GLOBALS['__l10n']['Database User Name:'] = 'Nom d\'utilisateur de la base de données :'; +$GLOBALS['__l10n']['Database Password:'] = 'Mot de passe de la base de données :'; +$GLOBALS['__l10n']['Database Tables Prefix:'] = 'Préfixe des tables de la base de données :'; +$GLOBALS['__l10n']['Continue'] = 'Continuer'; +$GLOBALS['__l10n']['No such installed language'] = 'Cette langue n\'est pas installée'; +$GLOBALS['__l10n']['You can\'t remove English language.'] = 'Vous ne pouvez pas supprimer la langue anglaise.'; +$GLOBALS['__l10n']['Permissions to delete language denied.'] = 'Permission de supprimer la langue refusée.'; +$GLOBALS['__l10n']['Invalid language file URL.'] = 'URL de fichier de langue invalide.'; +$GLOBALS['__l10n']['Languages management'] = 'Gestion des langues'; +$GLOBALS['__l10n']['Language has been successfully deleted.'] = 'La langue a été supprimée avec succès.'; +$GLOBALS['__l10n']['Language has been successfully installed.'] = 'La langue a été installée avec succès.'; +$GLOBALS['__l10n']['Language has been successfully upgraded'] = 'La langue a été mise à jour avec succès.'; +$GLOBALS['__l10n']['Here you can install, upgrade or remove languages for your Dotclear installation.'] = 'Sur cette page, vous pouvez installer, mettre à jour ou supprimer des langues de votre installation de Dotclear.'; +$GLOBALS['__l10n']['You can change your user language in your preferences or change your blog\'s main language in your blog settings.'] = 'Vous pouvez changer votre langue d\'utilisateur dans vos préférences ou changer la langue principale de votre blog dans vos paramètres de blog.'; +$GLOBALS['__l10n']['Installed languages'] = 'Langues installées'; +$GLOBALS['__l10n']['No additional language is installed.'] = 'Aucune langue supplémentaire n\'est installée.'; +$GLOBALS['__l10n']['Language'] = 'Langue'; +$GLOBALS['__l10n']['Action'] = 'Action'; +$GLOBALS['__l10n']['Install or upgrade languages'] = 'Installer ou mettre à jour une langue'; +$GLOBALS['__l10n']['You can install or remove a language by adding or removing the relevant directory in your %s folder.'] = 'Vous pouvez installer ou supprimer une langue en ajoutant ou supprimant le répertoire correspondant dans votre répertoire %s.'; +$GLOBALS['__l10n']['Available languages'] = 'Langues disponibles'; +$GLOBALS['__l10n']['You can download and install a additional language directly from Dotclear.net. Proposed languages are based on your version: %s.'] = 'Vous pouvez télécharger et installer une langue supplémentaire directement depuis Dotclear.net. Les langues proposées sont basées sur votre version : %s.'; +$GLOBALS['__l10n']['Language:'] = 'Langue :'; +$GLOBALS['__l10n']['Install language'] = 'Installer la langue'; +$GLOBALS['__l10n']['You can install languages by uploading zip files.'] = 'Vous pouvez installer des langues en déposant des fichiers zip.'; +$GLOBALS['__l10n']['Language zip file:'] = 'Fichier zip de la langue :'; +$GLOBALS['__l10n']['Upload language'] = 'Déposer la langue'; +$GLOBALS['__l10n']['Invalid language zip file.'] = 'Fichier zip de langue invalide.'; +$GLOBALS['__l10n']['The zip file does not appear to be a valid Dotclear language pack.'] = 'Le fichier zip ne semble pas être un fichier valide de langue Dotclear.'; +$GLOBALS['__l10n']['An error occurred during language upgrade.'] = 'Une erreur est survenue durant la mise à jour de la langue.'; +$GLOBALS['__l10n']['Error:'] = 'Erreur :'; +$GLOBALS['__l10n']['By names, in ascending order'] = 'Par noms, croissants'; +$GLOBALS['__l10n']['By names, in descending order'] = 'Par noms, décroissants'; +$GLOBALS['__l10n']['By dates, in ascending order'] = 'Par dates, croissantes'; +$GLOBALS['__l10n']['By dates, in descending order'] = 'Par dates, décroissantes'; +$GLOBALS['__l10n']['Media manager'] = 'Gestionnaire de médias'; +$GLOBALS['__l10n']['confirm removal'] = 'Confirmer la suppression'; +$GLOBALS['__l10n']['Are you sure you want to remove %s?'] = 'Êtes-vous certain de vouloir supprimer %s ?'; +$GLOBALS['__l10n']['Cancel'] = 'Annuler'; +$GLOBALS['__l10n']['Yes'] = 'oui'; +$GLOBALS['__l10n']['Directory has been successfully created.'] = 'Répertoire créé avec succès.'; +$GLOBALS['__l10n']['Files have been successfully uploaded.'] = 'Fichier chargé avec succès.'; +$GLOBALS['__l10n']['File has been successfully removed.'] = 'Fichier supprimé avec succès.'; +$GLOBALS['__l10n']['Directory has been successfully removed.'] = 'Répertoire supprimé avec succès.'; +$GLOBALS['__l10n']['Directory has been successfully rebuilt.'] = 'Répertoire reconstruit avec succès.'; +$GLOBALS['__l10n']['Zip file has been successfully extracted.'] = 'Le fichier zip a été extrait avec succès.'; +$GLOBALS['__l10n']['Choose a file to attach to entry %s by clicking on %s.'] = 'Choisissez un fichier à attacher au billet %s en cliquant sur %s.'; +$GLOBALS['__l10n']['Attach this file to entry'] = 'Attacher ce fichier au billet'; +$GLOBALS['__l10n']['Choose a file to insert into entry by clicking on %s.'] = 'Choisissez un fichier à insérer dans le billet en cliquant sur %s.'; +$GLOBALS['__l10n']['No file.'] = 'Aucun fichier.'; +$GLOBALS['__l10n']['Sort files:'] = 'Trier les fichiers :'; +$GLOBALS['__l10n']['Sort'] = 'Trier'; +$GLOBALS['__l10n']['Add files'] = 'Ajouter des fichiers'; +$GLOBALS['__l10n']['Please take care to publish media that you own and that are not protected by copyright.'] = 'Veuillez prendre garde à ne publier que des médias que vous possédez ou qui ne sont pas protégés contre la copie.'; +$GLOBALS['__l10n']['Choose a file:'] = 'Choisissez un fichier :'; +$GLOBALS['__l10n']['Maximum size %s'] = 'Taille maximale %s'; +$GLOBALS['__l10n']['Private'] = 'Privé'; +$GLOBALS['__l10n']['To send several files at the same time, you can activate the enhanced uploader in'] = 'Pour envoyer plusieurs fichiers à la fois, vous pouvez activer l\'interface avancée dans'; +$GLOBALS['__l10n']['Send'] = 'Envoyer'; +$GLOBALS['__l10n']['New directory'] = 'Nouveau répertoire'; +$GLOBALS['__l10n']['Directory Name:'] = 'Nom du répertoire :'; +$GLOBALS['__l10n']['Download this directory as a zip file'] = 'Télécharger ce répertoire dans un fichier zip'; +$GLOBALS['__l10n']['open'] = 'ouvrir'; +$GLOBALS['__l10n']['Insert this file into entry'] = 'Insérer ce fichier dans le billet'; +$GLOBALS['__l10n']['delete'] = 'supprimer'; +$GLOBALS['__l10n']['Not a valid file'] = 'Fichier invalide'; +$GLOBALS['__l10n']['File has been successfully updated.'] = 'Fichier mis à jour avec succès.'; +$GLOBALS['__l10n']['Thumbnails have been successfully updated.'] = 'Les miniatures ont été mises à jour avec succès.'; +$GLOBALS['__l10n']['Insert media item'] = 'Insérer un média'; +$GLOBALS['__l10n']['Image size:'] = 'Taille de l\'image :'; +$GLOBALS['__l10n']['original'] = 'originale'; +$GLOBALS['__l10n']['Image alignment'] = 'Alignement de l\'image'; +$GLOBALS['__l10n']['None'] = 'Aucun'; +$GLOBALS['__l10n']['Left'] = 'Gauche'; +$GLOBALS['__l10n']['Right'] = 'Droite'; +$GLOBALS['__l10n']['Center'] = 'Centre'; +$GLOBALS['__l10n']['Image insertion'] = 'Insertion de l\'image'; +$GLOBALS['__l10n']['As a single image'] = 'En tant qu\'image uniquement'; +$GLOBALS['__l10n']['As a link to original image'] = 'En tant que lien vers l\'image originale'; +$GLOBALS['__l10n']['MP3 disposition'] = 'Disposition du MP3'; +$GLOBALS['__l10n']['Please note that you cannot insert mp3 files with visual editor.'] = 'Merci de noter que vous ne pouvez pas insérer de fichier mp3 avec l\'éditeur visuel.'; +$GLOBALS['__l10n']['Please note that you cannot insert video files with visual editor.'] = 'Merci de noter que vous ne pouvez pas insérer de fichier vidéo avec l\'éditeur visuel.'; +$GLOBALS['__l10n']['Video size'] = 'Taille de la vidéo'; +$GLOBALS['__l10n']['Width:'] = 'Largeur :'; +$GLOBALS['__l10n']['Height:'] = 'Hauteur :'; +$GLOBALS['__l10n']['Video disposition'] = 'Disposition de la vidéo'; +$GLOBALS['__l10n']['Media item will be inserted as a link.'] = 'Le média sera inséré en tant que lien.'; +$GLOBALS['__l10n']['Insert'] = 'Insérer'; +$GLOBALS['__l10n']['Media details'] = 'Détails du média'; +$GLOBALS['__l10n']['Available sizes:'] = 'Tailles disponibles :'; +$GLOBALS['__l10n']['File owner:'] = 'Propriétaire du fichier :'; +$GLOBALS['__l10n']['File type:'] = 'Type de fichier :'; +$GLOBALS['__l10n']['File size:'] = 'Taille du fichier :'; +$GLOBALS['__l10n']['File URL:'] = 'URL du fichier :'; +$GLOBALS['__l10n']['Show entries containing this media'] = 'Afficher les billets contenant ce média'; +$GLOBALS['__l10n']['Entries containing this media'] = 'Billets contenant ce média'; +$GLOBALS['__l10n']['No entry seems contain this media.'] = 'Aucun billet ne semble contenir ce média.'; +$GLOBALS['__l10n']['Image details'] = 'Détails de l\'image'; +$GLOBALS['__l10n']['No detail'] = 'Aucun détail'; +$GLOBALS['__l10n']['Update thumbnails'] = 'Mettre à jour les miniatures'; +$GLOBALS['__l10n']['This will create or update thumbnails for this image.'] = 'Ceci va créer ou mettre à jour les miniatures pour cette image.'; +$GLOBALS['__l10n']['Extract in a new directory'] = 'Extraire dans un nouveau répertoire'; +$GLOBALS['__l10n']['Extract in current directory'] = 'Extraire dans le répertoire actuel'; +$GLOBALS['__l10n']['Extract archive'] = 'Extraire l\'archive'; +$GLOBALS['__l10n']['This will extract archive in a new directory that should not exist yet.'] = 'Ceci va extraire l\'archive dans un nouveau répertoire qui ne doit pas encore exister.'; +$GLOBALS['__l10n']['This will extract archive in current directory and will overwrite existing files or directory.'] = 'Ceci va extraire l\'archive dans le répertoire actuel et va écraser les fichiers ou répertoires existants.'; +$GLOBALS['__l10n']['Extract mode:'] = 'Mode d\'extraction :'; +$GLOBALS['__l10n']['Extract'] = 'Extrait'; +$GLOBALS['__l10n']['Change media properties'] = 'Changer les propriétés du média'; +$GLOBALS['__l10n']['File name:'] = 'Nom du fichier :'; +$GLOBALS['__l10n']['File title:'] = 'Titre du fichier :'; +$GLOBALS['__l10n']['File date:'] = 'Date du fichier :'; +$GLOBALS['__l10n']['New directory:'] = 'Nouveau répertoire :'; +$GLOBALS['__l10n']['Change file'] = 'Changer le fichier'; +$GLOBALS['__l10n']['No blog or user given.'] = 'Vous n\'avez pas indiqué de blog ou d\'utilisateur'; +$GLOBALS['__l10n']['permissions'] = 'permissions'; +$GLOBALS['__l10n']['Permissions'] = 'Permissions'; +$GLOBALS['__l10n']['The permissions have been successfully updated.'] = 'Permissions mises à jour avec succès.'; +$GLOBALS['__l10n']['You are about to change permissions on the following blogs for users %s.'] = 'Vous allez changer les permissions des utilisateurs %s pour ces blogs.'; +$GLOBALS['__l10n']['Validate permissions'] = 'Valider les permissions'; +$GLOBALS['__l10n']['choose a blog'] = 'choisissez un blog'; +$GLOBALS['__l10n']['Choose a blog'] = 'Choisissez un blog'; +$GLOBALS['__l10n']['Entries per page'] = 'Billets par page'; +$GLOBALS['__l10n']['Choose one or more blogs to which you want to give permissions to users %s.'] = 'Choisissez un ou plusieurs blogs pour lesquels les utilisateurs suivants auront des permissions : %s.'; +$GLOBALS['__l10n']['Set permissions'] = 'définir les permissions'; +$GLOBALS['__l10n']['select'] = 'sélectionner'; +$GLOBALS['__l10n']['No content found on this plugin.'] = 'Aucun contenu pour cette extension.'; +$GLOBALS['__l10n']['Plugin not found'] = 'Extension introuvable'; +$GLOBALS['__l10n']['The plugin you reached does not exist or does not have an admin page.'] = 'L\'extension que vous essayez d\'atteindre n\'existe pas ou n\'a pas de page d\'administration.'; +$GLOBALS['__l10n']['No such plugin.'] = 'Extension inexistante.'; +$GLOBALS['__l10n']['You don\'t have permissions to delete this plugin.'] = 'Vous n\'avez pas les permissions pour effacer cette extension'; +$GLOBALS['__l10n']['You don\'t have permissions to deactivate this plugin.'] = 'Vous n\'avez pas les permissions pour désactiver cette extension.'; +$GLOBALS['__l10n']['Plugins management'] = 'Gestion des extensions'; +$GLOBALS['__l10n']['Plugin has been successfully deleted.'] = 'L\'extension a été supprimée avec succès.'; +$GLOBALS['__l10n']['Plugin has been successfully installed.'] = 'L\'extension a été installée avec succès.'; +$GLOBALS['__l10n']['Plugin has been successfully upgraded'] = 'L\'extension a été mise à jour avec succès.'; +$GLOBALS['__l10n']['Plugins add new functionalities to Dotclear. Here you can activate or deactivate installed plugins.'] = 'Les extensions ajoutent de nouvelles fonctionnalités à Dotclear. Ici, vous pouvez activer ou désactiver les extensions installées.'; +$GLOBALS['__l10n']['You can find additional plugins for your blog on %s.'] = 'Vous pouvez trouver de nouvelles extensions pour votre blog sur %s.'; +$GLOBALS['__l10n']['To install or upgrade a plugin you generally just need to upload it in "Install or upgrade a plugin" section.'] = 'Tout ce que vous avez à faire pour installer ou mettre à jour une extension est généralement de la déposer dans la section "Installer ou mettre à jour une extension".'; +$GLOBALS['__l10n']['To install or upgrade a plugin you just need to extract it in your plugins directory.'] = 'Tout ce que vous avez à faire pour installer une extension est de l\'extraire dans votre répertoire d\'extensions.'; +$GLOBALS['__l10n']['Plugins'] = 'Extensions'; +$GLOBALS['__l10n']['Activated plugins'] = 'Extensions activées'; +$GLOBALS['__l10n']['Plugin'] = 'Extension'; +$GLOBALS['__l10n']['Version'] = 'Version'; +$GLOBALS['__l10n']['Details'] = 'Détails'; +$GLOBALS['__l10n']['Deactivate'] = 'Désactiver'; +$GLOBALS['__l10n']['Deactivated plugins'] = 'Extensions désactivées'; +$GLOBALS['__l10n']['Activate'] = 'Activer'; +$GLOBALS['__l10n']['Install or upgrade a plugin'] = 'Installer ou mettre à jour une extension'; +$GLOBALS['__l10n']['You can install plugins by uploading or downloading zip files.'] = 'Vous pouvez installer des extensions en déposant ou téléchargeant des fichiers zip.'; +$GLOBALS['__l10n']['Plugin zip file:'] = 'Fichier zip de l\'extension :'; +$GLOBALS['__l10n']['Upload plugin'] = 'Déposer l\'extension'; +$GLOBALS['__l10n']['Plugin zip file URL:'] = 'URL du fichier zip de l\'extension :'; +$GLOBALS['__l10n']['Download plugin'] = 'Télécharger l\'extension'; +$GLOBALS['__l10n']['To enable this function, please give write access to your plugins directory.'] = 'Pour activer cette fonction, donnez un accès en écriture à votre répertoire d\'extensions.'; +$GLOBALS['__l10n']['Add a link'] = 'Ajouter un lien'; +$GLOBALS['__l10n']['Available'] = 'Disponible'; +$GLOBALS['__l10n']['Most used'] = 'Plus utilisées'; +$GLOBALS['__l10n']['Link URL:'] = 'URL du lien :'; +$GLOBALS['__l10n']['Link title:'] = 'Titre du lien :'; +$GLOBALS['__l10n']['Link language:'] = 'Langue du lien :'; +$GLOBALS['__l10n']['Add a link to an entry'] = 'Ajouter un lien vers un billet'; +$GLOBALS['__l10n']['Search entry:'] = 'Rechercher un billet :'; +$GLOBALS['__l10n']['Search'] = 'Rechercher'; +$GLOBALS['__l10n']['cancel'] = 'Annuler'; +$GLOBALS['__l10n']['This entry does not exist.'] = 'Ce billet n\'existe pas.'; +$GLOBALS['__l10n']['Edit entry'] = 'Modifier le billet'; +$GLOBALS['__l10n']['next entry'] = 'billet suivant'; +$GLOBALS['__l10n']['previous entry'] = 'billet précédent'; +$GLOBALS['__l10n']['Entry has been successfully updated.'] = 'Billet mis à jour avec succès.'; +$GLOBALS['__l10n']['Entry has been successfully created.'] = 'Billet créé avec succès.'; +$GLOBALS['__l10n']['File has been successfully attached.'] = 'Fichier attaché avec succès.'; +$GLOBALS['__l10n']['Attachment has been successfully removed.'] = 'Pièce jointe retirée avec succès.'; +$GLOBALS['__l10n']['Comment has been successfully created.'] = 'Commentaire créé avec succès.'; +$GLOBALS['__l10n']['Don\'t forget to validate your XHTML conversion by saving your post.'] = 'Enregistrez votre billet pour valider la transformation en XHTML.'; +$GLOBALS['__l10n']['Go to this entry on the site'] = 'Voir ce billet sur le site'; +$GLOBALS['__l10n']['new window'] = 'nouvelle fenêtre'; +$GLOBALS['__l10n']['Excerpt:'] = 'Extrait :'; +$GLOBALS['__l10n']['Notes:'] = 'Notes :'; +$GLOBALS['__l10n']['Preview'] = 'Prévisualiser'; +$GLOBALS['__l10n']['Entry status:'] = 'État du billet :'; +$GLOBALS['__l10n']['Published on:'] = 'Publié le :'; +$GLOBALS['__l10n']['Text formating:'] = 'Format du texte :'; +$GLOBALS['__l10n']['Convert to XHTML'] = 'Convertir en XHTML'; +$GLOBALS['__l10n']['Selected entry'] = 'Billet sélectionné'; +$GLOBALS['__l10n']['Entry lang:'] = 'Langue du billet :'; +$GLOBALS['__l10n']['Entry password:'] = 'Mot de passe du billet :'; +$GLOBALS['__l10n']['Basename:'] = 'URL spécifique :'; +$GLOBALS['__l10n']['Warning: If you set the URL manually, it may conflict with another entry.'] = 'Attention : si vous indiquez l\'URL manuellement, celle-ci peut entrer en conflit avec un autre billet.'; +$GLOBALS['__l10n']['Ping blogs'] = 'Faire des rétroliens'; +$GLOBALS['__l10n']['Trackbacks'] = 'Rétroliens'; +$GLOBALS['__l10n']['No trackback'] = 'Aucun rétrolien'; +$GLOBALS['__l10n']['Add a comment'] = 'Ajouter un commentaire'; +$GLOBALS['__l10n']['Name:'] = 'Nom :'; +$GLOBALS['__l10n']['IP address'] = 'Adresse IP'; +$GLOBALS['__l10n']['published'] = 'publié'; +$GLOBALS['__l10n']['unpublished'] = 'non publié'; +$GLOBALS['__l10n']['pending'] = 'en attente'; +$GLOBALS['__l10n']['junk'] = 'indésirable'; +$GLOBALS['__l10n']['select this comment'] = 'Sélectionner ce commentaire'; +$GLOBALS['__l10n']['select this trackback'] = 'Sélectionner ce rétrolien'; +$GLOBALS['__l10n']['Edit this comment'] = 'Modifier ce commentaire'; +$GLOBALS['__l10n']['This attachment does not exist'] = 'Cette pièce jointe n\'existe pas'; +$GLOBALS['__l10n']['Remove attachment'] = 'Supprimer la pièce jointe'; +$GLOBALS['__l10n']['Attachment'] = 'Pièce jointe'; +$GLOBALS['__l10n']['Are you sure you want to remove this attachment?'] = 'Êtes-vous certain de vouloir supprimer cette pièce jointe ?'; +$GLOBALS['__l10n']['selected'] = 'sélectionné'; +$GLOBALS['__l10n']['not selected'] = 'non sélectionné'; +$GLOBALS['__l10n']['Category'] = 'Catégorie'; +$GLOBALS['__l10n']['Selected'] = 'Sélectionné'; +$GLOBALS['__l10n']['Publish'] = 'Publier'; +$GLOBALS['__l10n']['Unpublish'] = 'Mettre hors ligne'; +$GLOBALS['__l10n']['Schedule'] = 'Programmer'; +$GLOBALS['__l10n']['Mark as pending'] = 'Mettre en attente'; +$GLOBALS['__l10n']['Mark'] = 'Marquer'; +$GLOBALS['__l10n']['Mark as selected'] = 'Sélectionner'; +$GLOBALS['__l10n']['Mark as unselected'] = 'Désélectionner'; +$GLOBALS['__l10n']['Change'] = 'Changer'; +$GLOBALS['__l10n']['Change category'] = 'Changer la catégorie'; +$GLOBALS['__l10n']['Change author'] = 'Changer l\'auteur'; +$GLOBALS['__l10n']['Selected:'] = 'Sélectionné :'; +$GLOBALS['__l10n']['Month:'] = 'Mois :'; +$GLOBALS['__l10n']['Lang:'] = 'Langue :'; +$GLOBALS['__l10n']['Selected entries action:'] = 'Action sur les billets sélectionnés :'; +$GLOBALS['__l10n']['This user does not exist'] = 'Cet utilisateur n\'existe pas'; +$GLOBALS['__l10n']['Change category for entries'] = 'Changer de catégorie pour les billets'; +$GLOBALS['__l10n']['Change author for entries'] = 'Changer l\'auteur des billets'; +$GLOBALS['__l10n']['Author ID:'] = 'Identifiant de l\'utilisateur :'; +$GLOBALS['__l10n']['Default'] = 'Défaut'; +$GLOBALS['__l10n']['If you want to change your email or password you must provide your current password.'] = 'Si vous voulez changer votre adresse email ou votre mot de passe, vous devez indiquer votre mot de passe actuel.'; +$GLOBALS['__l10n']['No favorite selected'] = 'Aucun favori sélectionné'; +$GLOBALS['__l10n']['Personal information has been successfully updated.'] = 'Informations personnelles mises à jour avec succès.'; +$GLOBALS['__l10n']['Personal options has been successfully updated.'] = 'Vos options personnelles ont été enregistrées avec succès.'; +$GLOBALS['__l10n']['Favorites have been successfully added.'] = 'Les favoris ont été ajoutés avec succès.'; +$GLOBALS['__l10n']['Favorites have been successfully updated.'] = 'Les favoris ont été mis à jour avec succès.'; +$GLOBALS['__l10n']['Favorites have been successfully removed.'] = 'Les favoris ont été retirés avec succès.'; +$GLOBALS['__l10n']['Default favorites have been successfully updated.'] = 'Les favoris par défaut ont été enregistrés avec succès.'; +$GLOBALS['__l10n']['My profile'] = 'Mon profil'; +$GLOBALS['__l10n']['Display name:'] = 'Pseudonyme :'; +$GLOBALS['__l10n']['User language:'] = 'Langue de l\'utilisateur :'; +$GLOBALS['__l10n']['User timezone:'] = 'Fuseau horaire de l\'utilisateur :'; +$GLOBALS['__l10n']['If you have changed this user email or password you must provide your current password to save these modifications.'] = 'Si vous voulez changer votre adresse email ou votre mot de passe, vous devez indiquer votre mot de passe.'; +$GLOBALS['__l10n']['My options'] = 'Mes options'; +$GLOBALS['__l10n']['Preferred format:'] = 'Format d\'édition préféré :'; +$GLOBALS['__l10n']['Default entry status:'] = 'État des billets par défaut :'; +$GLOBALS['__l10n']['Entry edit field height:'] = 'Taille de la zone d\'édition :'; +$GLOBALS['__l10n']['Enable WYSIWYG mode'] = 'Activer l\'éditeur visuel'; +$GLOBALS['__l10n']['Activate enhanced uploader in media manager'] = 'Activer l\'interface avancée du gestionnaire de médias'; +$GLOBALS['__l10n']['Hide My favorites menu'] = 'Cacher le menu Mes Favoris'; +$GLOBALS['__l10n']['Iconset:'] = 'Jeu d\'icônes :'; +$GLOBALS['__l10n']['Do not use standard favicon'] = 'Ne pas utiliser le favicon standard'; +$GLOBALS['__l10n']['This will be applied for all users'] = 'Ce choix sera actif pour tous les utilisateurs'; +$GLOBALS['__l10n']['Accessibility options'] = 'Options d\'accessibilité'; +$GLOBALS['__l10n']['Disable javascript powered drag and drop for ordering items'] = 'Désactiver le drag and drop javascript pour ordonnancer les éléments'; +$GLOBALS['__l10n']['Numeric fields will allow to type the elements\' ordering number.'] = 'Des champs numériques permettront d\'indiquer la position des éléments.'; +$GLOBALS['__l10n']['Dashboard modules'] = 'Modules du tableau de bord'; +$GLOBALS['__l10n']['Display documentation links'] = 'Afficher les liens de documentation'; +$GLOBALS['__l10n']['Display Dotclear news'] = 'Afficher les nouvelles de Dotclear'; +$GLOBALS['__l10n']['Display quick entry form'] = 'Afficher le formulaire de billet rapide'; +$GLOBALS['__l10n']['My favorites'] = 'Mes favoris'; +$GLOBALS['__l10n']['position of %s'] = 'position de %s'; +$GLOBALS['__l10n']['Save order'] = 'Enregistrer l\'ordre'; +$GLOBALS['__l10n']['Delete selected favorites'] = 'Retirer les favoris sélectionnés'; +$GLOBALS['__l10n']['Are you sure you want to remove selected favorites?'] = 'Êtes-vous sûr de vouloir retirer les favoris sélectionnés ?'; +$GLOBALS['__l10n']['If you are a super administrator, you may define this set of favorites to be used by default on all blogs of this installation:'] = 'Si vous êtes super administrateur, vous pouvez définir ce jeu de favoris comme l\'ensemble par défaut pour tous les blogs de l\'installation :'; +$GLOBALS['__l10n']['Define as default favorites'] = 'Définir comme favoris par défaut'; +$GLOBALS['__l10n']['Currently no personal favorites.'] = 'La liste de vos favoris est vide pour le moment.'; +$GLOBALS['__l10n']['Default favorites'] = 'Favoris par défaut'; +$GLOBALS['__l10n']['Those favorites are displayed when My Favorites list is empty.'] = 'Favoris affichés quand la liste Mes Favoris est vide.'; +$GLOBALS['__l10n']['Available favorites'] = 'Favoris disponibles'; +$GLOBALS['__l10n']['Add to my favorites'] = 'Ajouter à mes favoris'; +$GLOBALS['__l10n']['Search options'] = 'Options de recherche'; +$GLOBALS['__l10n']['Query:'] = 'Requête :'; +$GLOBALS['__l10n']['Search entries'] = 'Rechercher des billets'; +$GLOBALS['__l10n']['Search comments'] = 'Rechercher des commentaires'; +$GLOBALS['__l10n']['schedule'] = 'programmer'; +$GLOBALS['__l10n']['change category'] = 'changer la catégorie'; +$GLOBALS['__l10n']['change author'] = 'changer l\'auteur'; +$GLOBALS['__l10n']['%d entries found'] = '%d billets trouvés'; +$GLOBALS['__l10n']['%d entry found'] = '%d billet trouvé'; +$GLOBALS['__l10n']['%d comment found'] = '%d commentaire trouvé'; +$GLOBALS['__l10n']['%d comments found'] = '%d commentaires trouvés'; +$GLOBALS['__l10n']['This entry does not exist or is not published'] = 'Ce billet n\'existe pas ou n\'est pas publié'; +$GLOBALS['__l10n']['All pings sent.'] = 'Tous les rétroliens ont été envoyés.'; +$GLOBALS['__l10n']['Back to "%s"'] = 'Retour à "%s"'; +$GLOBALS['__l10n']['Auto discover ping URLs'] = 'Découverte automatique des URL à rétrolier'; +$GLOBALS['__l10n']['URLs to ping:'] = 'URLs à rétrolier :'; +$GLOBALS['__l10n']['Send excerpt:'] = 'Envoyer l\'extrait :'; +$GLOBALS['__l10n']['Previously sent pings'] = 'Rétroliens déjà envoyés'; +$GLOBALS['__l10n']['Dotclear update'] = 'Mise à jour de Dotclear'; +$GLOBALS['__l10n']['Unable to delete file %s'] = 'Impossible de supprimer le fichier %s'; +$GLOBALS['__l10n']['Downloaded Dotclear archive seems to be corrupted. Try download it again.'] = 'L\'archive téléchargée de Dotclear semble être corrompue. Essayer de la télécharger à nouveau.'; +$GLOBALS['__l10n']['The following files of your Dotclear installation have been modified so we won\'t try to update your installation. Please try to update manually.'] = 'Comme les fichiers suivants de votre installation de Dotclear ont été modifiés, votre installation ne peut être mise à jour. Merci de mettre à jour manuellement.'; +$GLOBALS['__l10n']['The following files of your Dotclear installation are not readable. Please fix this or try to make a backup file named %s manually.'] = 'Les fichiers suivants de votre installation de Dotclear ne peuvent pas être lus. Veuillez corriger ceci ou créer un fichier de backup nommé %s manuellement.'; +$GLOBALS['__l10n']['The following files of your Dotclear installation cannot be written. Please fix this or try to update manually.'] = 'Les fichiers suivants de votre installation de Dotclear ne peuvent pas être écrits. Veuillez corriger ceci ou mettre à jour manuellement.'; +$GLOBALS['__l10n']['No newer Dotclear version available.'] = 'Aucune nouvelle version de Dotclear n\'est disponible.'; +$GLOBALS['__l10n']['Dotclear %s is available.'] = 'Dotclear %s est disponible.'; +$GLOBALS['__l10n']['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.'] = 'Pour mettre à jour votre installation de Dotclear, cliquez sur le bouton suivant. Un fichier de sauvegarde de votre installation actuelle sera créé dans votre répertoire principal.'; +$GLOBALS['__l10n']['Update Dotclear'] = 'Mettre à jour Dotclear'; +$GLOBALS['__l10n']['Update backup files'] = 'Sauvegardes des mises à jour'; +$GLOBALS['__l10n']['The following files are backups of previously updates. You can revert your previous installation or delete theses files.'] = 'Les fichiers suivants sont des sauvegardes de mises à jour précédentes. Vous pouvez rétablir votre installation précédente ou supprimer ces fichiers.'; +$GLOBALS['__l10n']['Please note that reverting your Dotclear version may have some unwanted side-effects. Consider reverting only if you experience strong issues with this new version.'] = 'Merci de noter que rétablir votre version de Dotclear peut avoir des effets non désirés. N\'envisagez ceci que si vous rencontrez d\'importantes difficultés avec cette nouvelle version.'; +$GLOBALS['__l10n']['You should not revert to version prior to last one (%s).'] = 'Vous ne devez pas rétablir une version précédant la dernière (%s).'; +$GLOBALS['__l10n']['Delete selected file'] = 'Supprimer le fichier sélectionné'; +$GLOBALS['__l10n']['Revert to selected file'] = 'Rétablir le fichier sélectionné'; +$GLOBALS['__l10n']['Congratulations, you\'re one click away from the end of the update.'] = 'Félicitations, vous êtes à un clic de la fin de la mise à jour.'; +$GLOBALS['__l10n']['Finish the update.'] = 'Finir la mise à jour.'; +$GLOBALS['__l10n']['new user'] = 'nouvel utilisateur'; +$GLOBALS['__l10n']['User "%s" already exists.'] = 'L\'utilisateur "%s" existe déjà.'; +$GLOBALS['__l10n']['User has been successfully updated.'] = 'Utilisateur mis à jour avec succès.'; +$GLOBALS['__l10n']['User has been successfully created.'] = 'Utilisateur créé avec succès.'; +$GLOBALS['__l10n']['Warning:'] = 'Attention :'; +$GLOBALS['__l10n']['If you change your username, you will have to log in again.'] = 'Si vous changez votre login, vous devrez vous identifier à nouveau.'; +$GLOBALS['__l10n']['Mandatory for password recovering procedure.'] = 'Obligatoire pour la procédure de récupération de mot de passe.'; +$GLOBALS['__l10n']['Password change required to connect'] = 'Changement de mot de passe requis pour la connexion'; +$GLOBALS['__l10n']['Save and create another'] = 'Enregistrer et créer un nouveau'; +$GLOBALS['__l10n']['No permissions.'] = 'Aucune permission.'; +$GLOBALS['__l10n']['Add new permissions'] = 'Ajouter de nouvelles permissions'; +$GLOBALS['__l10n']['Username'] = 'Identifiant'; +$GLOBALS['__l10n']['Last Name'] = 'Nom'; +$GLOBALS['__l10n']['First Name'] = 'Prénom'; +$GLOBALS['__l10n']['Display name'] = 'Pseudonyme'; +$GLOBALS['__l10n']['Number of entries'] = 'Nombre de billets'; +$GLOBALS['__l10n']['users'] = 'utilisateurs'; +$GLOBALS['__l10n']['User has been successfully removed.'] = 'Utilisateur supprimé avec succès.'; +$GLOBALS['__l10n']['Create a new user'] = 'Créer un nouvel utilisateur'; +$GLOBALS['__l10n']['Users per page'] = 'Utilisateurs par page'; +$GLOBALS['__l10n']['Selected users action:'] = 'Action sur les utilisateurs sélectionnés :'; +$GLOBALS['__l10n']['Blog:'] = 'Blog :'; +$GLOBALS['__l10n']['Change blog'] = 'Changer de blog'; +$GLOBALS['__l10n']['Blogs:'] = 'Blogs :'; +$GLOBALS['__l10n']['Go to the content'] = 'Aller au contenu'; +$GLOBALS['__l10n']['Go to the menu'] = 'Aller au menu'; +$GLOBALS['__l10n']['Go to site'] = 'Aller sur le site'; +$GLOBALS['__l10n']['My dashboard'] = 'Mon tableau de bord'; +$GLOBALS['__l10n']['Logout %s'] = 'Déconnecter %s'; +$GLOBALS['__l10n']['Safe mode'] = 'Mode de secours'; +$GLOBALS['__l10n']['You are in safe mode. All plugins have been temporarily disabled. Remind to log out then log in again normally to get back all functionalities'] = 'Vous êtes en mode de secours. Tous les plugins ont été temporairement désactivés. N\'oubliez-pas de vous déconnecter puis de vous reconnecter normalement pour retrouver toutes les fonctionnalités'; +$GLOBALS['__l10n']['Thank you for using %s.'] = 'Merci d\'utiliser %s.'; +$GLOBALS['__l10n']['Help'] = 'Aide'; +$GLOBALS['__l10n']['uncover'] = 'dévoiler'; +$GLOBALS['__l10n']['hide'] = 'cacher'; +$GLOBALS['__l10n']['help'] = 'aide'; +$GLOBALS['__l10n']['no selection'] = 'aucune sélection'; +$GLOBALS['__l10n']['select all'] = 'tout sélectionner'; +$GLOBALS['__l10n']['invert selection'] = 'inverser la sélection'; +$GLOBALS['__l10n']['view entry'] = 'voir le billet'; +$GLOBALS['__l10n']['Are you sure you want to delete selected entries (%s)?'] = 'Êtes-vous certain de vouloir supprimer les billets sélectionnés (%s) ?'; +$GLOBALS['__l10n']['Are you sure you want to delete this entry?'] = 'Êtes-vous certain de vouloir supprimer ce billet ?'; +$GLOBALS['__l10n']['Are you sure you want to delete selected comments (%s)?'] = 'Êtes-vous certain de vouloir supprimer les commentaires sélectionnés (%s) ?'; +$GLOBALS['__l10n']['Are you sure you want to delete this comment?'] = 'Êtes-vous certain de vouloir supprimer ce commentaire ?'; +$GLOBALS['__l10n']['Users with posts cannot be deleted.'] = 'Les utilisateurs ayant écrit des billets ne peuvent être effacées.'; +$GLOBALS['__l10n']['Are you sure you want to delete selected users (%s)?'] = 'Êtes-vous certain de vouloir supprimer les utilisateurs sélectionnés (%s) ?'; +$GLOBALS['__l10n']['Are you sure you want to delete category "%s"?'] = 'Êtes-vous certain de vouloir supprimer la catégorie "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to reorder all categories?'] = 'Êtes-vous certain de vouloir réinitialiser l\'ordre des catégories ?'; +$GLOBALS['__l10n']['Are you sure you want to remove media "%s"?'] = 'Êtes-vous certain de vouloir supprimer le média "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to extract archive in current directory?'] = 'Êtes-vous certain de vouloir extraire l\'archive dans le répertoire actuel ?'; +$GLOBALS['__l10n']['Are you sure you want to remove attachment "%s"?'] = 'Êtes-vous certain de vouloir supprimer la pièce jointe "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to delete "%s" language?'] = 'Êtes-vous certain de vouloir supprimer la langue "%s" ?'; +$GLOBALS['__l10n']['Are you sure you want to delete "%s" plugin?'] = 'Êtes-vous certain de vouloir supprimer l\'extension "%s" ?'; +$GLOBALS['__l10n']['Use this theme'] = 'Utiliser ce thème'; +$GLOBALS['__l10n']['Remove this theme'] = 'Supprimer ce thème'; +$GLOBALS['__l10n']['Are you sure you want to delete "%s" theme?'] = 'Êtes-vous certain de vouloir supprimer le thème "%s" ?'; +$GLOBALS['__l10n']['Zip file content'] = 'Contenu du fichier zip'; +$GLOBALS['__l10n']['XHTML markup validator'] = 'Validation XHTML'; +$GLOBALS['__l10n']['XHTML content is valid.'] = 'Le contenu XHTML est valide.'; +$GLOBALS['__l10n']['There are XHTML markup errors.'] = 'Il y a des erreurs XHTML.'; +$GLOBALS['__l10n']['You have unsaved changes. Switch post format will loose these changes. Proceed anyway?'] = 'Vous avez des modifications non sauvegardées. Changer de format vous fera perdre ces modifications. Continuer ?'; +$GLOBALS['__l10n']['Loading enhanced uploader, please wait.'] = 'Chargement de l\'interface avancée.'; +$GLOBALS['__l10n']['You have unsaved changes.'] = 'Vous n\'avez pas enregistré vos modifications.'; +$GLOBALS['__l10n']['close'] = 'fermer'; +$GLOBALS['__l10n']['now'] = 'maintenant'; +$GLOBALS['__l10n']['visual'] = 'visuel'; +$GLOBALS['__l10n']['source'] = 'source'; +$GLOBALS['__l10n']['You can use the following shortcuts to format your text.'] = 'Vous pouvez utiliser les raccourcis suivants pour formater votre texte.'; +$GLOBALS['__l10n']['-- none --'] = '-- aucun --'; +$GLOBALS['__l10n']['-- block format --'] = '-- format bloc --'; +$GLOBALS['__l10n']['Paragraph'] = 'Paragraphe'; +$GLOBALS['__l10n']['Level 1 header'] = 'Titre de niveau 1'; +$GLOBALS['__l10n']['Level 2 header'] = 'Titre de niveau 2'; +$GLOBALS['__l10n']['Level 3 header'] = 'Titre de niveau 3'; +$GLOBALS['__l10n']['Level 4 header'] = 'Titre de niveau 4'; +$GLOBALS['__l10n']['Level 5 header'] = 'Titre de niveau 5'; +$GLOBALS['__l10n']['Level 6 header'] = 'Titre de niveau 6'; +$GLOBALS['__l10n']['Strong emphasis'] = 'Forte emphase'; +$GLOBALS['__l10n']['Emphasis'] = 'Emphase'; +$GLOBALS['__l10n']['Inserted'] = 'Insertion'; +$GLOBALS['__l10n']['Deleted'] = 'Suppression'; +$GLOBALS['__l10n']['Inline quote'] = 'Citation en ligne'; +$GLOBALS['__l10n']['Code'] = 'Code'; +$GLOBALS['__l10n']['Line break'] = 'Passage à la ligne'; +$GLOBALS['__l10n']['Blockquote'] = 'Bloc de citation'; +$GLOBALS['__l10n']['Preformated text'] = 'Texte préformaté'; +$GLOBALS['__l10n']['Unordered list'] = 'Liste à puces'; +$GLOBALS['__l10n']['Ordered list'] = 'Liste numérotée'; +$GLOBALS['__l10n']['Link'] = 'Lien'; +$GLOBALS['__l10n']['URL?'] = 'URL ?'; +$GLOBALS['__l10n']['Language?'] = 'Langue ?'; +$GLOBALS['__l10n']['External image'] = 'Image externe'; +$GLOBALS['__l10n']['Media chooser'] = 'Sélecteur de média'; +$GLOBALS['__l10n']['Link to an entry'] = 'Lien vers un billet'; +$GLOBALS['__l10n']['Activate enhanced uploader'] = 'Activer l\'interface avancée'; +$GLOBALS['__l10n']['Disable enhanced uploader'] = 'Désactiver l\'interface avancée'; +$GLOBALS['__l10n']['File successfully uploaded.'] = 'Fichier envoyé avec succès.'; +$GLOBALS['__l10n']['Maximum file size allowed:'] = 'Taille maximale de fichier autorisée :'; +$GLOBALS['__l10n']['Limit exceeded.'] = 'Limite dépassée.'; +$GLOBALS['__l10n']['File size exceeds allowed limit.'] = 'La taille du fichier dépasse la limite autorisée.'; +$GLOBALS['__l10n']['Canceled.'] = 'Annulé.'; +$GLOBALS['__l10n']['HTTP Error:'] = 'Erreur HTTP :'; +$GLOBALS['__l10n']['Choose file'] = 'Choisir un fichier'; +$GLOBALS['__l10n']['Choose files'] = 'Choisir des fichiers'; +$GLOBALS['__l10n']['Clean'] = 'Nettoyer'; +$GLOBALS['__l10n']['Upload'] = 'Envoyer'; +$GLOBALS['__l10n']['No file in queue.'] = 'Aucun fichier en file d\'attente.'; +$GLOBALS['__l10n']['1 file in queue.'] = '1 fichier en attente.'; +$GLOBALS['__l10n']['%d files in queue.'] = '%d fichiers en attente.'; +$GLOBALS['__l10n']['Queue error:'] = 'Erreur de file d\'attente :'; +$GLOBALS['__l10n']['«prev.'] = '«préc.'; +$GLOBALS['__l10n']['next»'] = 'suiv.»'; +$GLOBALS['__l10n']['No entry'] = 'Pas de billet'; +$GLOBALS['__l10n']['scheduled'] = 'programmé'; +$GLOBALS['__l10n']['protected'] = 'protégé'; +$GLOBALS['__l10n']['%d attachment'] = '%d annexe'; +$GLOBALS['__l10n']['%d attachments'] = '%d annexes'; +$GLOBALS['__l10n']['Type'] = 'Type'; +$GLOBALS['__l10n']['No user'] = 'Pas d\'utilisateur'; +$GLOBALS['__l10n']['admin'] = 'Administrateur'; +$GLOBALS['__l10n']['superadmin'] = 'Super administrateur'; +$GLOBALS['__l10n']['Database error'] = 'Erreur de base de données :'; +$GLOBALS['__l10n']['There seems to be no Session table in your database. Is Dotclear completly installed?'] = 'Il semble de la table Session n\'existe pas dans votre base de données. Dotclear est-il bien installé correctement?'; +$GLOBALS['__l10n']['System'] = 'Système'; +$GLOBALS['__l10n']['Blog'] = 'Blog'; +$GLOBALS['__l10n']['Updates'] = 'Mises à jour'; +$GLOBALS['__l10n']['Languages'] = 'Langues'; +$GLOBALS['__l10n']['administrator'] = 'administrateur'; +$GLOBALS['__l10n']['manage their own entries and comments'] = 'gérer ses propres billets et commentaires'; +$GLOBALS['__l10n']['publish entries and comments'] = 'publier des billets et des commentaires'; +$GLOBALS['__l10n']['delete entries and comments'] = 'supprimer des billets et des commentaires'; +$GLOBALS['__l10n']['manage all entries and comments'] = 'gérer tous les billets et commentaires'; +$GLOBALS['__l10n']['manage categories'] = 'gérer les catégories'; +$GLOBALS['__l10n']['manage their own media items'] = 'gérer ses propres médias'; +$GLOBALS['__l10n']['manage all media items'] = 'gérer tous les médias'; +$GLOBALS['__l10n']['That user does not exist in the database.'] = 'Cet utilisateur n\'existe pas dans la base de données.'; +$GLOBALS['__l10n']['That key does not exist in the database.'] = 'Cette clé n\'existe pas dans la base de données.'; +$GLOBALS['__l10n']['You are not allowed to add categories'] = 'Vous n\'êtes pas autorisé à créer des catégories'; +$GLOBALS['__l10n']['You are not allowed to update categories'] = 'Vous n\'êtes pas autorisé à modifier des catégories'; +$GLOBALS['__l10n']['You are not allowed to delete categories'] = 'Vous n\'êtes pas autorisé à supprimer des catégories'; +$GLOBALS['__l10n']['This category is not empty.'] = 'Cette catégorie n\'est pas vide.'; +$GLOBALS['__l10n']['You are not allowed to reset categories order'] = 'Vous n\'êtes pas autorisé à modifier l\'ordre des catégories'; +$GLOBALS['__l10n']['Category URL must be unique.'] = 'L\'URL de chaque catégorie doit être unique.'; +$GLOBALS['__l10n']['You must provide a category title'] = 'Vous devez indiquer un titre de catégorie'; +$GLOBALS['__l10n']['You must provide a category URL'] = 'Vous devez indiquer une URL de catégorie'; +$GLOBALS['__l10n']['You are not allowed to create an entry'] = 'Vous n\'êtes pas autorisé à créer des billets'; +$GLOBALS['__l10n']['You are not allowed to update entries'] = 'Vous n\'êtes pas autorisé à modifier les billets'; +$GLOBALS['__l10n']['No such entry ID'] = 'Identifiant du billet inconnu'; +$GLOBALS['__l10n']['You are not allowed to edit this entry'] = 'Vous n\'êtes pas autorisé à modifier ce billet'; +$GLOBALS['__l10n']['You are not allowed to change this entry status'] = 'Vous n\'êtes pas autorisé à modifier l\'état de ce billet'; +$GLOBALS['__l10n']['You are not allowed to change this entry category'] = 'Vous n\'êtes pas autorisé à modifier la catégorie de ce billet'; +$GLOBALS['__l10n']['You are not allowed to mark this entry as selected'] = 'Vous n\'êtes pas autorisé à marquer ce billet comme sélectionné'; +$GLOBALS['__l10n']['You are not allowed to delete entries'] = 'Vous n\'êtes pas autorisé à supprimer des billets'; +$GLOBALS['__l10n']['You are not allowed to delete this entry'] = 'Vous n\'êtes pas autorisé à supprimer ce billet'; +$GLOBALS['__l10n']['No entry title'] = 'Pas de titre de billet'; +$GLOBALS['__l10n']['No entry content'] = 'Pas de contenu de billet'; +$GLOBALS['__l10n']['Empty entry URL'] = 'URL du billet vide'; +$GLOBALS['__l10n']['You are not allowed to update comments'] = 'Vous n\'êtes pas autorisé à modifier des commentaires'; +$GLOBALS['__l10n']['No such comment ID'] = 'Identifiant du commentaire inconnu'; +$GLOBALS['__l10n']['You are not allowed to update this comment'] = 'Vous n\'êtes pas autorisé à modifier ce commentaire'; +$GLOBALS['__l10n']['You are not allowed to change this comment\'s status'] = 'Vous n\'êtes pas autorisé à changer l\'état de ce commentaire'; +$GLOBALS['__l10n']['You are not allowed to delete comments'] = 'Vous n\'êtes pas autorisé à supprimer des commentaires'; +$GLOBALS['__l10n']['You are not allowed to delete this comment'] = 'Vous n\'êtes pas autorisé à supprimer ce commentaire'; +$GLOBALS['__l10n']['You must provide a comment'] = 'Vous devez indiquer un commentaire'; +$GLOBALS['__l10n']['You must provide an author name'] = 'Vous devez indiquer un nom d\'auteur'; +$GLOBALS['__l10n']['Email address is not valid.'] = 'Adresse email invalide.'; +$GLOBALS['__l10n']['online'] = 'en ligne'; +$GLOBALS['__l10n']['offline'] = 'hors ligne'; +$GLOBALS['__l10n']['removed'] = 'supprimé'; +$GLOBALS['__l10n']['You are not an administrator'] = 'Vous n\'êtes pas administrateur'; +$GLOBALS['__l10n']['Invalid user language code'] = 'Code langue de l\'utilisateur invalide'; +$GLOBALS['__l10n']['Blog ID must contain at least 2 characters using letters, numbers or symbols.'] = 'L\'identifiant du blog doit contenir au moins 2 caractères composés de lettres, chiffres ou symboles.'; +$GLOBALS['__l10n']['No blog name'] = 'Pas de nom de blog'; +$GLOBALS['__l10n']['No blog URL'] = 'Pas d\'URL de blog'; +$GLOBALS['__l10n']['No log message'] = 'Pas de message dans le journal'; +$GLOBALS['__l10n']['unknown'] = 'inconnu'; +$GLOBALS['__l10n']['No blog defined.'] = 'Aucun blog défini.'; +$GLOBALS['__l10n']['Directory %s does not exist.'] = 'Le répertoire %s n\'existe pas.'; +$GLOBALS['__l10n']['You are not a super administrator.'] = 'Vous n\'êtes pas super administrateur.'; +$GLOBALS['__l10n']['Permission denied.'] = 'Permission refusée.'; +$GLOBALS['__l10n']['You are not the file owner.'] = 'Vous n\'êtes pas le propriétaire de ce fichier.'; +$GLOBALS['__l10n']['This file is not allowed.'] = 'Ce fichier n\'est pas autorisé.'; +$GLOBALS['__l10n']['New file already exists.'] = 'Le nouveau fichier existe déjà.'; +$GLOBALS['__l10n']['File does not exist in the database.'] = 'Ce fichier n\'existe pas dans la base de données.'; +$GLOBALS['__l10n']['Extract destination directory %s already exists.'] = 'Le répertoire de destination d\'extraction %s existe déjà.'; +$GLOBALS['__l10n']['Embedded Audio Player'] = 'Fichier audio intégré'; +$GLOBALS['__l10n']['Embedded Video Player'] = 'Fichier vidéo intégré'; +$GLOBALS['__l10n']['%s: in [%s] and [%s]'] = '%s: dans [%s] et [%s]'; +$GLOBALS['__l10n']['Empty module zip file.'] = 'Fichier zip de module vide.'; +$GLOBALS['__l10n']['The zip file does not appear to be a valid Dotclear module.'] = 'Le fichier zip ne semble pas être un fichier valide de module Dotclear.'; +$GLOBALS['__l10n']['An error occurred during module deletion.'] = 'Une erreur est survenue durant la suppression du module.'; +$GLOBALS['__l10n']['Unable to upgrade "%s". (same version)'] = 'Impossible de mettre à jour "%s" (même version).'; +$GLOBALS['__l10n']['Unable to read new _define.php file'] = 'Impossible de lire le nouveau fichier _define.php.'; +$GLOBALS['__l10n']['No such module.'] = 'Module inexistant.'; +$GLOBALS['__l10n']['Cannot remove module files'] = 'Impossible de supprimer les fichiers du module'; +$GLOBALS['__l10n']['Cannot deactivate plugin.'] = 'L\'extension ne peut pas être désactivée.'; +$GLOBALS['__l10n']['Cannot activate plugin.'] = 'L\'extension ne peut pas être activée.'; +$GLOBALS['__l10n']['Invalid setting dcNamespace: %s'] = 'Espace de nommage du paramètre invalide : %s'; +$GLOBALS['__l10n']['Unable to retrieve settings:'] = 'Impossible d\'obtenir les paramètres :'; +$GLOBALS['__l10n']['%s is not a valid setting id'] = '%s n\'est pas un identifiant de paramètre valide'; +$GLOBALS['__l10n']['No namespace specified'] = 'Aucun espace de nommage spécifié'; +$GLOBALS['__l10n']['Unable to retrieve workspaces:'] = 'Impossible d\'obtenir les espaces de travail :'; +$GLOBALS['__l10n']['Unable to retrieve namespaces:'] = 'Impossible d\'obtenir les espaces de nommage :'; +$GLOBALS['__l10n']['Invalid setting namespace: %s'] = 'Espace de nommage du paramètre invalide : %s'; +$GLOBALS['__l10n']['%s has still been pinged'] = 'Un rétrolien vers %s a déjà été fait'; +$GLOBALS['__l10n']['Unable to ping URL'] = 'Impossible de réaliser le rétrolien'; +$GLOBALS['__l10n']['%s is not a ping URL'] = '%s n\'est pas une URL de rétrolien'; +$GLOBALS['__l10n']['%s, ping error:'] = '%s, erreur de rétrolien :'; +$GLOBALS['__l10n']['Digests file not found.'] = 'Fichier de contrôle introuvable.'; +$GLOBALS['__l10n']['No file to download'] = 'Aucun fichier à télécharger.'; +$GLOBALS['__l10n']['Root directory is not writable.'] = 'Le répertoire principal n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['An error occurred while downloading archive.'] = 'Une erreur est survenue lors du téléchargement de l\'archive.'; +$GLOBALS['__l10n']['Archive not found.'] = 'L\'archive n\'a pas été trouvée.'; +$GLOBALS['__l10n']['Unable to read current digests file.'] = 'Impossible de lire le fichier de contrôle actuel.'; +$GLOBALS['__l10n']['Downloaded file does not seem to be a valid archive.'] = 'Le fichier téléchargé ne semble pas être une archive valide.'; +$GLOBALS['__l10n']['Incomplete archive.'] = 'Archive incomplète.'; +$GLOBALS['__l10n']['Unable to read digests file.'] = 'Impossible de lire le fichier de contrôle.'; +$GLOBALS['__l10n']['Invalid digests file.'] = 'Fichier de contrôle invalide.'; +$GLOBALS['__l10n']['Invalid dcWorkspace: %s'] = 'Espace de travail de la préférence invalide : %s'; +$GLOBALS['__l10n']['Unable to retrieve prefs:'] = 'Impossible d\'obtenir les préférences :'; +$GLOBALS['__l10n']['%s is not a valid pref id'] = '%s n\'est pas un identifiant de préférence valide'; +$GLOBALS['__l10n']['No workspace specified'] = 'Aucun espace de travail spécifié'; +$GLOBALS['__l10n']['SQLite Database Schema cannot be upgraded.'] = 'Le schema de base de données SQLite ne peut pas être mis à jour.'; +$GLOBALS['__l10n']['Something went wrong with auto upgrade:'] = 'Une erreur est survenue durant la mise à jour automatique :'; +$GLOBALS['__l10n']['Unable to open directory.'] = 'Impossible d\'ouvrir le répertoire.'; +$GLOBALS['__l10n']['Unable to create directory.'] = 'Impossible de créer le répertoire.'; +$GLOBALS['__l10n']['File is not writable.'] = 'Le fichier n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['Unable to open file.'] = 'Impossible d\'ouvrir le fichier.'; +$GLOBALS['__l10n']['Not an uploaded file.'] = 'Pas un fichier déposé.'; +$GLOBALS['__l10n']['The uploaded file exceeds the maximum file size allowed.'] = 'Le fichier déposé est plus grand que la taille maximale autorisée.'; +$GLOBALS['__l10n']['The uploaded file was only partially uploaded.'] = 'Le fichier n\'a été chargé qu\'en partie.'; +$GLOBALS['__l10n']['No file was uploaded.'] = 'Aucun fichier chargé.'; +$GLOBALS['__l10n']['Missing a temporary folder.'] = 'Il manque un répertoire temporaire.'; +$GLOBALS['__l10n']['Failed to write file to disk.'] = 'Impossible d\'écrire le fichier.'; +$GLOBALS['__l10n']['%s is not a directory.'] = '%s n\'est pas un répertoire.'; +$GLOBALS['__l10n']['Bad range'] = 'Mauvaises limites'; +$GLOBALS['__l10n']['Invalid range'] = 'Sélection invalide'; +$GLOBALS['__l10n']['Invalid line number'] = 'Numéro de ligne invalide invalide'; +$GLOBALS['__l10n']['Chunk is out of range'] = 'L\'extrait est hors limite'; +$GLOBALS['__l10n']['Bad context'] = 'Contexte invalide'; +$GLOBALS['__l10n']['Bad context (in deletion)'] = 'Contexte invalide (lors de la suppression)'; +$GLOBALS['__l10n']['Invalid diff format'] = 'Format de fichier diff invalide.'; +$GLOBALS['__l10n']['Uploading this file is not allowed.'] = 'L\'envoi de ce fichier n\'est pas autorisé.'; +$GLOBALS['__l10n']['Destination directory is not in jail.'] = 'Le répertoire cible n\'est pas en jail.'; +$GLOBALS['__l10n']['File already exists.'] = 'Le nouveau fichier existe déjà.'; +$GLOBALS['__l10n']['Cannot write in this directory.'] = 'Impossible d\'écrire dans ce répertoire.'; +$GLOBALS['__l10n']['An error occurred while writing the file.'] = 'Une erreur est survenue pendant l\'écriture du fichier.'; +$GLOBALS['__l10n']['Source file does not exist.'] = 'Le fichier source n\'existe pas'; +$GLOBALS['__l10n']['File is not in jail.'] = 'Le fichier n\'est pas en jail.'; +$GLOBALS['__l10n']['Destination directory is not writable.'] = 'Le répertoire cible n\'est pas accessible en écriture.'; +$GLOBALS['__l10n']['Unable to rename file.'] = 'Impossible de renommer le fichier.'; +$GLOBALS['__l10n']['File cannot be removed.'] = 'Ce fichier ne peut pas être supprimé.'; +$GLOBALS['__l10n']['Directory is not in jail.'] = 'Le répertoire n\'est pas en jail.'; +$GLOBALS['__l10n']['Directory cannot be removed.'] = 'Ce répertoire ne peut pas être supprimé.'; +$GLOBALS['__l10n']['Not enough memory to open image.'] = 'Mémoire insuffisante pour ouvrir l\'image.'; +$GLOBALS['__l10n']['File %s is not compressed in the zip.'] = 'Le fichier %s n\'est pas compressé dans le zip.'; +$GLOBALS['__l10n']['Trying to unzip a folder name %s'] = 'Tentative de décompresser un répertoire %s'; +$GLOBALS['__l10n']['Unable to write destination file.'] = 'Impossible d\'écrire le fichier de destination'; +$GLOBALS['__l10n']['Unable to write in target directory, permission denied.'] = 'Impossible d\'écrire dans le répertoire cible, permission refusée.'; +$GLOBALS['__l10n']['Not enough memory to open file.'] = 'Mémoire insuffisante pour ouvrir le fichier.'; +$GLOBALS['__l10n']['File does not exist'] = 'Le fichier n\'existe pas'; +$GLOBALS['__l10n']['Cannot read file'] = 'Impossible de lire le fichier'; +$GLOBALS['__l10n']['Directory does not exist'] = 'Le répertoire n\'existe pas'; +$GLOBALS['__l10n']['Cannot read directory'] = 'Impossible de lire le répertoire'; +$GLOBALS['__l10n']['Unable to connect to database'] = 'Connexion à la base de données impossible'; +$GLOBALS['__l10n']['

        This either means that the username and password information in your config.php file is incorrect or we can\'t contact the database server at "%s". This could mean your host\'s database server is down.

        • Are you sure you have the correct username and password?
        • Are you sure that you have typed the correct hostname?
        • Are you sure that the database server is running?

        If you\'re unsure what these terms mean you should probably contact your host. If you still need help you can always visit the Dotclear Support Forums.

        '] = '

        Cela signifie soit que les informations d\'identifiant ou de mot de passe de votre fichier config.php sont incorrects, soit que nous ne pouvons pas contacter le serveur de base de données à l\'adresse "%s". Cela peut vouloir dire que le serveur en question est éteint.

        • Êtes-vous sûr que l\'identifiant et le mot de passe sont corrects ?
        • Êtes-vous sûr d\'avoir entré le bon nom de serveur ?
        • Êtes-vous sûr que le serveur fonctionne ?

        S vous n\'êtes pas sûr de la signification de ces termes, vous devriez probablement contacter votre hébergeur. Si vous avez besoin d\'une aide supplémentaire, vous pouvez vous rendre sur le forum d\'entraide Dotclear.

        '; +$GLOBALS['__l10n']['The following error was encountered while trying to read the database:'] = 'L\'erreur suivante a été rencontrée lors de la tentative d\'accès à la base de données :'; +?> \ No newline at end of file diff --git a/v2/dotclear/locales/fr/main.po b/v2/dotclear/locales/fr/main.po new file mode 100644 index 0000000..2cceae3 --- /dev/null +++ b/v2/dotclear/locales/fr/main.po @@ -0,0 +1,2896 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-13 10:46+0200\n" +"PO-Revision-Date: 2011-10-13 19:26+0100\n" +"Last-Translator: xave \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: \n" +"X-Poedit-Language: French\n" + +msgid "Dotclear has been upgraded." +msgstr "Dotclear a été mis à jour." + +msgid "Password reset" +msgstr "Réinitialisation du mot de passe" + +msgid "Someone has requested to reset the password for the following site and username." +msgstr "Quelqu'un a demandé la réinitialisation du mot de passe pour le site et l'utilisateur suivants." + +msgid "Username:" +msgstr "Nom d'utilisateur :" + +msgid "To reset your password visit the following address, otherwise just ignore this email and nothing will happen." +msgstr "Pour réinitialiser votre mot de passe, rendez-vous à l'adresse suivante. Sinon ignorez simplement ce message et rien ne se passera." + +#, php-format +msgid "The e-mail was sent successfully to %s." +msgstr "Le message a été envoyé avec succès à %s." + +msgid "Your new password" +msgstr "Votre nouveau mot de passe" + +msgid "Password:" +msgstr "Mot de passe :" + +msgid "Your new password is in your mailbox." +msgstr "Votre nouveau mot de passe est dans votre boîte à lettres." + +msgid "Passwords don't match" +msgstr "Les mots de passe ne correspondent pas" + +msgid "You didn't change your password." +msgstr "Vous n'avez pas changé votre mot de passe." + +msgid "You have to change your password before you can login." +msgstr "Vous devez changer de mot de passe avant de vous connecter." + +msgid "In order to login, you have to change your password now." +msgstr "Afin de vous connecter, vous devez changer votre mot de passe." + +msgid "Safe Mode can only be used for super administrators." +msgstr "Le mode sans échec ne peut être utilisé que par un super administrateur." + +msgid "Wrong username or password" +msgstr "Nom d'utilisateur ou mot de passe incorrect" + +msgid "Back to login screen" +msgstr "Retour à l'écran de connexion" + +msgid "Request a new password" +msgstr "Demander un nouveau mot de passe" + +msgid "Email:" +msgstr "Email :" + +msgid "recover" +msgstr "récupérer" + +msgid "Change your password" +msgstr "Changer votre mot de passe" + +msgid "New password:" +msgstr "Nouveau mot de passe :" + +msgid "Confirm password:" +msgstr "Confirmez le mot de passe :" + +msgid "change" +msgstr "changer" + +msgid "Safe mode login" +msgstr "Connexion en mode sans échec" + +msgid "This mode allows you to login without activating any of your plugins. This may be useful to solve compatibility problems" +msgstr "Ce mode vous permet de vous connecter sans activer de plugins. Il peut être utile pour résoudre un problème de compatibilité" + +msgid "Disable or delete any plugin suspected to cause trouble, then log out and log back in normally." +msgstr "Désactivez ou supprimez les plugins semblant en être la cause, puis déconnectez-vous et connectez-vous à nouveau normalement." + +msgid "Remember my ID on this computer" +msgstr "Se souvenir de mon identifiant sur cet ordinateur" + +msgid "log in" +msgstr "Se connecter" + +msgid "You must accept cookies in order to use the private area." +msgstr "Vous devez accepter les cookies pour utiliser l'interface privée." + +msgid "Get back to normal authentication" +msgstr "Retour à l'écran de connexion normal" + +msgid "Connection issue?" +msgstr "Problème de connexion ?" + +msgid "I forgot my password" +msgstr "J'ai oublié mon mot de passe" + +msgid "I want to log in in safe mode" +msgstr "Me connecter en mode sans échec" + +msgid "New blog" +msgstr "Nouveau blog" + +msgid "Blogs" +msgstr "Blogs" + +msgid "Blog ID:" +msgstr "Identifiant du blog :" + +msgid "Required field" +msgstr "Champ obligatoire" + +msgid "At least 2 characters using letters, numbers or symbols." +msgstr "Au moins 2 caractères, composés de lettres non accentuées, chiffres ou symboles." + +msgid "Please note that changing your blog ID may require changes in your public index.php file." +msgstr "Veuillez noter que changer l'identifiant de votre blog peut nécessiter des modifications dans votre fichier index.php public." + +msgid "Blog name:" +msgstr "Nom du blog :" + +msgid "Blog URL:" +msgstr "URL du blog :" + +msgid "Blog description:" +msgstr "Description du blog :" + +msgid "Create" +msgstr "Créer" + +msgid "No such blog ID" +msgstr "Identifiant de blog inconnu" + +msgid "Password verification failed" +msgstr "La vérification du mot de passe a échoué" + +msgid "Delete a blog" +msgstr "Supprimer un blog" + +msgid "Warning" +msgstr "Attention" + +#, php-format +msgid "You are about to delete the blog %s. Every entry, comment and category will be deleted." +msgstr "Vous êtes sur le point de supprimer le blog %s. Tous ses billets, commentaires et catégories seront supprimés." + +msgid "Please give your password to confirm the blog deletion." +msgstr "Veuillez indiquer votre mot de passe pour confirmer la suppression du blog." + +msgid "Your password:" +msgstr "Votre mot de passe :" + +msgid "Delete this blog" +msgstr "Supprimer ce blog" + +msgid "No given blog id." +msgstr "Pas d'identifiant de blog." + +msgid "No such blog." +msgstr "Blog inexistant." + +msgid "year/month/day/title" +msgstr "année/mois/jour/titre" + +msgid "year/month/title" +msgstr "année/mois/titre" + +msgid "year/title" +msgstr "année/titre" + +msgid "title" +msgstr "titre" + +msgid "Title" +msgstr "Titre" + +msgid "Title, Date" +msgstr "Titre, date" + +msgid "Title, Country, Date" +msgstr "Titre, pays, date" + +msgid "Title, City, Country, Date" +msgstr "Titre, ville, pays, date" + +msgid "I would like search engines and archivers to index and archive my blog's content." +msgstr "Je souhaite que mon blog soit indexé et archivé par les moteurs de recherche et archiveurs." + +msgid "I would like search engines and archivers to index but not archive my blog's content." +msgstr "Je souhaite que mon blog soit indexé mais pas archivé par les moteurs de recherche et archiveurs." + +msgid "I would like to prevent search engines and archivers from indexing or archiving my blog's content." +msgstr "Je souhaite que mon blog ne soit ni indexé ni archivé par les moteurs de recherche et archiveurs." + +msgid "That blog Id is already in use." +msgstr "Cet identifiant est déjà utilisé" + +msgid "Invalid language code" +msgstr "Code langue invalide" + +msgid "Blog settings" +msgstr "Paramètres du blog" + +msgid "Warning: except for special configurations, it is generally advised to have a trailing \"/\" in your blog URL in PATH_INFO mode." +msgstr "Attention: sauf cas particulier, il est généralement conseillé de terminer l'URL de votre blog par \"/\" en mode PATH_INFO." + +msgid "Warning: except for special configurations, it is generally advised to have a trailing \"?\" in your blog URL in QUERY_STRING mode." +msgstr "Attention: sauf cas particulier, il est généralement conseillé de terminer l'URL de votre blog par \"?\" en mode QUERY_STRING." + +msgid "Blog has been successfully created." +msgstr "Blog créé avec succès." + +msgid "Blog has been successfully updated." +msgstr "Blog mis à jour avec succès." + +msgid "Parameters" +msgstr "Paramètres" + +msgid "Blog details" +msgstr "Informations du blog" + +msgid "URL scan method:" +msgstr "Méthode de lecture de l'URL :" + +msgid "Blog status:" +msgstr "État du blog :" + +msgid "Blog configuration" +msgstr "Configuration du blog" + +msgid "Blog editor name:" +msgstr "Nom de l'éditeur du blog :" + +msgid "Default language:" +msgstr "Langue par défaut :" + +msgid "Blog timezone:" +msgstr "Fuseau horaire du blog :" + +msgid "Copyright notice:" +msgstr "Note de copyright :" + +msgid "New post URL format:" +msgstr "Format d'URL des nouveaux billets :" + +msgid "Enable XML/RPC interface" +msgstr "Activer l'interface XML/RPC" + +msgid "more information" +msgstr "plus d'informations" + +msgid "Comments and trackbacks" +msgstr "Commentaires et rétroliens" + +msgid "Accept comments" +msgstr "Accepter les commentaires" + +msgid "Moderate comments" +msgstr "Modérer les commentaires" + +#, php-format +msgid "Leave comments open for %s days" +msgstr "Laisser les commentaires ouverts durant %s jours" + +msgid "Leave blank to disable this feature." +msgstr "Laissez vide pour annuler ce comportement." + +msgid "Wiki syntax for comments" +msgstr "Syntaxe wiki pour les commentaires" + +msgid "Accept trackbacks" +msgstr "Accepter les rétroliens" + +msgid "Moderate trackbacks" +msgstr "Modérer les rétroliens" + +#, php-format +msgid "Leave trackbacks open for %s days" +msgstr "Laisser les rétroliens ouverts durant %s jours" + +msgid "Add \"nofollow\" relation on comments and trackbacks links" +msgstr "Ajouter la relation \"nofollow\" aux liens des commentaires et rétroliens" + +msgid "Blog presentation" +msgstr "Présentation du blog" + +msgid "Date format:" +msgstr "Format des dates :" + +msgid "Time format:" +msgstr "Format des heures :" + +msgid "Display smilies on entries and comments" +msgstr "Afficher des émoticones dans les billets et commentaires" + +#, php-format +msgid "Display %s entries per page" +msgstr "Afficher %s billets par page" + +#, php-format +msgid "Display %s entries per feed" +msgstr "Afficher %s billets par flux de syndication" + +#, php-format +msgid "Display %s comments per feed" +msgstr "Afficher %s commentaires par flux de syndication" + +msgid "Truncate feeds" +msgstr "Tronquer les flux de syndication" + +msgid "Media and images" +msgstr "Médias et images" + +msgid "Generated image sizes (in pixels)" +msgstr "Tailles des images générées (en pixels)" + +msgid "Thumbnails:" +msgstr "Miniatures :" + +msgid "Small:" +msgstr "Petites :" + +msgid "Medium:" +msgstr "Moyennes :" + +msgid "Inserted image title" +msgstr "Titres des images insérées" + +msgid "This defines image tag title when you insert it in a post from the media manager. It is retrieved from the picture's metadata." +msgstr "Ceci définit le titre de la balise d'une image insérée depuis le gestionnaire de media. Les informations sont obtenues depuis les métadonnées de l'image." + +msgid "Search engines robots policy" +msgstr "Paramètres d'indexation par les moteurs de recherche" + +msgid "Save" +msgstr "Enregistrer" + +msgid "XML/RPC interface" +msgstr "Interface XML/RPC" + +msgid "XML/RPC interface allows you to edit your blog with an external client." +msgstr "L'interface XML/RPC vous permet de publier sur votre blog avec un client externe." + +msgid "XML/RPC interface is not active. Change settings to enable it." +msgstr "L'interface XML/RPC n'est pas active. Changez vos paramètres pour l'activer." + +msgid "XML/RPC interface is active. You should set the following parameters on your XML/RPC client:" +msgstr "L'interface XML/RPC est active. Vous êtes invités à indiquer les paramètres suivants dans votre client XML/RPC :" + +msgid "Server URL:" +msgstr "URL du serveur :" + +msgid "Blogging system:" +msgstr "Système de blog :" + +msgid "User name:" +msgstr "Nom d'utilisateur :" + +msgid "your password" +msgstr "votre mot de passe" + +msgid "Users" +msgstr "Utilisateurs" + +msgid "Users on this blog" +msgstr "Utilisateurs de ce blog" + +msgid "No users" +msgstr "Aucun utilisateur" + +msgid "Change permissions" +msgstr "Changer les permissions" + +msgid "Super administrator" +msgstr "Super administrateur" + +msgid "You can't remove default theme." +msgstr "Vous ne pouvez pas supprimer le thème par défaut." + +msgid "Theme does not exist." +msgstr "Ce thème n'existe pas." + +msgid "Unable to move uploaded file." +msgstr "Impossible de déplacer le fichier téléchargé." + +msgid "An error occurred while downloading the file." +msgstr "Une erreur est survenue lors du téléchargement du fichier." + +#, php-format +msgid "by %s" +msgstr "par %s" + +#, php-format +msgid "version %s" +msgstr "version %s" + +#, php-format +msgid "(built on \"%s\")" +msgstr "(basé sur \"%s\")" + +#, php-format +msgid "(requires \"%s\")" +msgstr "(nécessite \"%s\")" + +msgid "Stylesheet" +msgstr "Feuille de style" + +msgid "Configure theme" +msgstr "Configurer le thème" + +msgid "Blog appearance" +msgstr "Apparence du blog" + +msgid "Theme has been successfully changed." +msgstr "Thème changé avec succès." + +msgid "Theme has been successfully installed." +msgstr "Le thème a été installé avec succès." + +msgid "Theme has been successfully upgraded" +msgstr "Le thème a été mis à jour avec succès." + +msgid "Theme has been successfully deleted." +msgstr "Le thème a été supprimé avec succès." + +#, php-format +msgid "You can find additional themes for your blog on %s." +msgstr "Vous pouvez trouver de nouveaux thèmes pour votre blog sur %s." + +msgid "To install or upgrade a theme you generally just need to upload it in \"Install or upgrade a theme\" section." +msgstr "Tout ce que vous avez à faire pour installer ou mettre à jour un thème est généralement de le déposer dans la section \"Installer ou mettre à jour un thème\"." + +msgid "Themes" +msgstr "Thèmes" + +#, php-format +msgid "You are currently using \"%s\"" +msgstr "Vous utilisez actuellement \"%s\"" + +msgid "Use selected theme" +msgstr "Utiliser le thème sélectionné" + +msgid "Delete selected theme" +msgstr "Supprimer le thème sélectionné" + +msgid "Install or upgrade a theme" +msgstr "Installer ou mettre à jour un thème" + +msgid "You can install themes by uploading or downloading zip files." +msgstr "Vous pouvez installer des thèmes en déposant ou téléchargeant des fichiers zip." + +msgid "Upload a zip file" +msgstr "Déposer un fichier zip" + +msgid "Theme zip file:" +msgstr "Fichier zip du thème :" + +msgid "Upload theme" +msgstr "Déposer un thème" + +msgid "Download a zip file" +msgstr "Télécharger un fichier zip" + +msgid "Theme zip file URL:" +msgstr "URL du fichier zip du thème :" + +msgid "Download theme" +msgstr "Télécharger le thème" + +msgid "To enable this function, please give write access to your themes directory." +msgstr "Pour activer cette fonction, donnez un accès en écriture à votre répertoire de thèmes." + +msgid "Theme configuration" +msgstr "Configurer le thème" + +msgid "back" +msgstr "retour" + +msgid "Last update" +msgstr "Dernière mise à jour" + +msgid "Blog name" +msgstr "Nom du blog" + +msgid "Blog ID" +msgstr "Identifiant du blog" + +msgid "Descending" +msgstr "Décroissant" + +msgid "Ascending" +msgstr "Croissant" + +msgid "List of blogs" +msgstr "Liste des blogs" + +msgid "Blog has been successfully deleted." +msgstr "Blog supprimé avec succès." + +msgid "Create a new blog" +msgstr "Créer un nouveau blog" + +msgid "Filters" +msgstr "Filtres" + +msgid "Order by:" +msgstr "Trier par :" + +msgid "Sort:" +msgstr "Ordre :" + +msgid "Search:" +msgstr "Chercher :" + +msgid "Blogs per page" +msgstr "Blogs par page" + +msgid "Apply filters" +msgstr "Appliquer les filtres" + +msgid "No blog" +msgstr "Pas de blog" + +msgid "Page(s)" +msgstr "Page(s)" + +msgid "Entries" +msgstr "Billets" + +msgid "Status" +msgstr "État" + +#, php-format +msgid "Edit blog %s" +msgstr "Modifier le blog %s" + +msgid "edit" +msgstr "modifier" + +#, php-format +msgid "Switch to blog %s" +msgstr "Passer au blog %s" + +msgid "This category does not exist." +msgstr "Cette catégorie n'existe pas." + +msgid "Categories" +msgstr "Catégories" + +msgid "The category has been successfully created." +msgstr "Catégorie créée avec succès." + +msgid "The category has been successfully removed." +msgstr "Catégorie supprimée avec succès." + +msgid "Categories have been successfully reordered." +msgstr "Catégories réordonnées avec succès." + +msgid "The category has been successfully moved." +msgstr "Catégorie déplacée avec succès." + +msgid "No category yet." +msgstr "Pas encore de catégorie." + +msgid "Categories list" +msgstr "Liste des catégories" + +#, php-format +msgid "%d entries" +msgstr "%d billets" + +#, php-format +msgid "%d entry" +msgstr "%d billet" + +msgid "total:" +msgstr "total :" + +msgid "URL:" +msgstr "URL :" + +msgid "Add a new category" +msgstr "Créer une nouvelle catégorie" + +msgid "Title:" +msgstr "Titre :" + +msgid "Parent:" +msgstr "Parent :" + +msgid "Top level" +msgstr "Premier niveau" + +msgid "Remove a category" +msgstr "Supprimer une catégorie" + +msgid "Choose a category to remove:" +msgstr "Choisissez une catégorie à supprimer :" + +msgid "Delete" +msgstr "Supprimer" + +msgid "Reorder categories" +msgstr "Réordonner les catégories" + +msgid "This will relocate all categories on the top level" +msgstr "Ceci va déplacer toutes les catégories au premier niveau" + +msgid "Reorder" +msgstr "Réordonner" + +msgid "New category" +msgstr "Nouvelle catégorie" + +msgid "Category has been successfully updated." +msgstr "Catégorie mise à jour avec succès." + +msgid "Category information" +msgstr "Détails de la catégorie" + +msgid "Warning: If you set the URL manually, it may conflict with another category." +msgstr "Attention : si vous indiquez l'URL manuellement, celle-ci peut entrer en conflit avec une autre catégorie." + +msgid "Description:" +msgstr "Description :" + +msgid "Move this category" +msgstr "Déplacer cette catégorie" + +msgid "Category parent" +msgstr "Catégorie parente" + +msgid "Category sibling" +msgstr "Catégorie voisine" + +msgid "Move current category" +msgstr "Déplacer la catégorie" + +msgid "after" +msgstr "après" + +msgid "before" +msgstr "avant" + +msgid "position: " +msgstr "position : " + +msgid "Entry does not exist." +msgstr "Ce billet n'existe pas." + +msgid "No comment" +msgstr "Aucun commentaire" + +msgid "You can't edit this comment." +msgstr "Vous ne pouvez pas modifier ce commentaire." + +msgid "Edit comment" +msgstr "Modifier le commentaire" + +msgid "Comment has been successfully updated." +msgstr "Commentaire mis à jour avec succès." + +#, php-format +msgid "Your comment on my blog %s" +msgstr "Votre commentaire sur mon blog %s" + +#, php-format +msgid "" +"Hi!\n" +"\n" +"You wrote a comment on:\n" +"%s\n" +"\n" +"\n" +msgstr "" +"Bonjour,\n" +"\n" +"Vous avez déposé un commentaire sur \n" +"%s\n" +"\n" +"\n" + +msgid "Send an e-mail" +msgstr "Envoyer un email" + +msgid "IP address:" +msgstr "Adresse IP :" + +msgid "Date:" +msgstr "Date :" + +msgid "Author:" +msgstr "Auteur :" + +msgid "Web site:" +msgstr "Site web :" + +msgid "Status:" +msgstr "État :" + +msgid "Comment:" +msgstr "Commentaire :" + +msgid "comment" +msgstr "commentaire" + +msgid "trackback" +msgstr "rétrolien" + +msgid "Date" +msgstr "Date" + +msgid "Entry title" +msgstr "Titre du billet" + +msgid "Author" +msgstr "Auteur" + +msgid "publish" +msgstr "publier" + +msgid "unpublish" +msgstr "hors ligne" + +msgid "mark as pending" +msgstr "en attente" + +msgid "mark as junk" +msgstr "indésirable" + +msgid "Type:" +msgstr "Type :" + +msgid "Comments per page" +msgstr "Commentaires par page" + +msgid "Comment author:" +msgstr "Auteur du commentaire :" + +msgid "You have one spam comments." +msgstr "Vous avez un commentaire indésirable." + +msgid "Show it." +msgstr "L'afficher" + +#, php-format +msgid "You have %s spam comments." +msgstr "Vous avez %s commentaires indésirables." + +msgid "Show them." +msgstr "Les afficher." + +msgid "Selected comments action:" +msgstr "Action sur les commentaires sélectionnés :" + +msgid "action: " +msgstr "action : " + +msgid "ok" +msgstr "ok" + +msgid "Comments" +msgstr "Commentaires" + +#, php-format +msgid "%d comment" +msgstr "%d commentaire" + +#, php-format +msgid "%d comments" +msgstr "%d commentaires" + +msgid "New entry" +msgstr "Nouveau billet" + +msgid "My preferences" +msgstr "Mes préférences" + +msgid "Documentation and support" +msgstr "Documentation et support" + +msgid "Latest news" +msgstr "Actualités" + +msgid "Dashboard" +msgstr "Tableau de bord" + +msgid "Make this blog my default blog" +msgstr "Définir comme blog par défaut" + +msgid "This blog is offline" +msgstr "Ce blog est hors ligne" + +msgid "This blog is removed" +msgstr "Ce blog est supprimé" + +msgid "is not defined, you should edit your configuration file." +msgstr "n'est pas défini, vous devriez corriger votre fichier de configuration." + +msgid "Following plugins have been installed:" +msgstr "Les extensions suivantes ont été installées :" + +msgid "Following plugins have not been installed:" +msgstr "Les extensions suivantes n'ont pas été installées :" + +#, php-format +msgid "Dotclear %s is available!" +msgstr "Dotclear %s est disponible !" + +msgid "Upgrade now" +msgstr "Mettre à jour maintenant" + +msgid "Remind me later" +msgstr "Me le rappeler plus tard" + +msgid "information about this version" +msgstr "Information à propos de cette version" + +msgid "Some plugins are installed twice:" +msgstr "Ces extensions sont installées en double :" + +msgid "Quick entry" +msgstr "Billet rapide" + +msgid "Content:" +msgstr "Contenu :" + +msgid "Category:" +msgstr "Catégorie :" + +msgid "Save and publish" +msgstr "Enregister et publier" + +#, php-format +msgid "PHP version is %s (5.0 or earlier needed)." +msgstr "La version de PHP est %s (5.0 ou plus récent nécessaire.)" + +msgid "Multibyte string module (mbstring) is not available." +msgstr "Le support des chaînes multi-octets (mbstring) n'est pas disponible." + +msgid "Iconv module is not available." +msgstr "Le module iconv n'est pas disponible." + +msgid "Output control functions are not available." +msgstr "Les fonctions de bufferisation de sortie ne sont pas disponibles." + +msgid "SimpleXML module is not available." +msgstr "Le module SimpleXML n'est pas disponible." + +msgid "DOM XML module is not available." +msgstr "Le module DOM XML n'est pas disponible." + +msgid "PCRE engine does not support UTF-8 strings." +msgstr "Le moteur d'expressions rationnelles PCRE ne gère pas les chaînes UTF-8." + +msgid "SPL module is not available." +msgstr "Le module SPL n'est pas disponible." + +#, php-format +msgid "MySQL version is %s (4.1 or earlier needed)." +msgstr "La version de MySQL est %s (4.1 ou plus récent nécessaire)." + +msgid "MySQL InnoDB engine is not available." +msgstr "Le gestionnaire de stockage InnoDB de MySQL n'est pas disponible." + +#, php-format +msgid "PostgreSQL version is %s (8.0 or earlier needed)." +msgstr "La version de PostgreSQL est %s (8.0 ou plus récent nécessaire)." + +msgid "Please set a master key (DC_MASTER_KEY) in configuration file." +msgstr "Veuillez indiquer une clé de référence (DC_MASTER_KEY) dans le fichier de configuration." + +msgid "Dotclear is already installed." +msgstr "Dotclear est déjà installé." + +msgid "Dotclear cannot be installed." +msgstr "Dotclear ne peut pas être installé." + +msgid "No user ID given" +msgstr "Aucun identifiant utilisateur spécifié" + +msgid "User ID must contain at least 2 characters using letters, numbers or symbols." +msgstr "L'identifiant utilisateur doit contenir au moins 2 caractères composés de lettres, chiffres ou symboles." + +msgid "Invalid email address" +msgstr "Adresse email incorrecte" + +msgid "No password given" +msgstr "Aucun mot de passe spécifié" + +msgid "Password must contain at least 6 characters." +msgstr "Le mot de passe doit contenir au moins 6 caractères." + +msgid "My first blog" +msgstr "Mon premier blog" + +msgid "%A, %B %e %Y" +msgstr "%A %e %B %Y" + +msgid "Welcome to Dotclear!" +msgstr "Bienvenue sur Dotclear !" + +msgid "This is your first entry. When you're ready to blog, log in to edit or delete it." +msgstr "Ceci est votre premier billet. Quand vous serez prêt à bloguer, connectez-vous pour le modifier ou le supprimer." + +msgid "Dotclear Team" +msgstr "L'équipe Dotclear" + +msgid "" +"

        This is a comment.

        \n" +"

        To delete it, log in and view your blog's comments. Then you might remove or edit it.

        " +msgstr "

        Ceci est un commentaire

        Pour le supprimer, connectez-vous et affichez les commentaires de votre blog. Vous pourrez alors le supprimer ou le modifier.

        " + +msgid "Dotclear Install" +msgstr "Installation de Dotclear" + +msgid "show" +msgstr "voir" + +msgid "Dotclear installation" +msgstr "Installation de Dotclear" + +#, php-format +msgid "Cache directory %s is not writable." +msgstr "Le répertoire de cache %s n'est pas accessible en écriture." + +msgid "Errors:" +msgstr "Erreurs :" + +msgid "Configuration file has been successfully created." +msgstr "Le fichier de configuration a été créé avec succès." + +msgid "User information" +msgstr "Informations utilisateur" + +msgid "Please provide the following information needed to create the first user." +msgstr "Merci de fournir les informations suivantes pour créer le premier utilisateur." + +msgid "First Name:" +msgstr "Prénom :" + +msgid "Last Name:" +msgstr "Nom :" + +msgid "Username and password" +msgstr "Identifiant et mot de passe" + +msgid "All done!" +msgstr "Terminé !" + +msgid "Dotclear has been successfully installed. Here is some useful information you should keep." +msgstr "Dotclear a été installé avec succès. Conservez les informations suivantes précieusement." + +msgid "Your account" +msgstr "Votre compte" + +msgid "Your blog" +msgstr "Votre blog" + +msgid "Blog address:" +msgstr "Adresse du blog :" + +msgid "Administration interface:" +msgstr "Interface d'administration :" + +msgid "Manage your blog now" +msgstr "Gérez votre blog" + +msgid "Installation can not be completed" +msgstr "L'installation ne peut pas être menée à bien" + +msgid "For the said reasons, Dotclear can not be installed. Please refer to the documentation to learn how to correct the problem." +msgstr "Pour les raisons ci-dessus, Dotclear ne peut pas être installé. Référez-vous à la documentation pour savoir comment corriger le problème." + +#, php-format +msgid "File %s does not exist." +msgstr "Le fichier %s n'existe pas." + +#, php-format +msgid "Cannot write %s file." +msgstr "Impossible d'écrire le fichier %s." + +msgid "Dotclear installation wizard" +msgstr "Assistant d'installation de Dotclear" + +msgid "Welcome" +msgstr "Bienvenue" + +msgid "To complete your Dotclear installation and start writing on your blog, we just need to know how to access your database and who you are. Just fill this two steps wizard with this information and we will be done." +msgstr "Pour achever votre installation de Dotclear, il ne manque plus que les informations concernant votre base de données, puis vos informations personnelles. Remplissez simplement les deux formulaires suivants et vous pourrez commencer à utiliser votre blog." + +msgid "Attention:" +msgstr "Attention :" + +msgid "this wizard may not function on every host. If it does not work for you, please refer to the documentation to learn how to create the config.php file manually." +msgstr "cet assistant peut ne pas fonctionner chez tous les hébergeurs. Si vous rencontrez un problème, vous trouverez comment créer le fichier de configuration dans la documentation." + +msgid "System information" +msgstr "Informations système" + +msgid "Please provide the following information needed to create your configuration file." +msgstr "Merci de fournir les informations suivantes :" + +msgid "Database type:" +msgstr "Type de base de données :" + +msgid "Database Host Name:" +msgstr "Nom d'hôte de la base de données :" + +msgid "Database Name:" +msgstr "Nom de la base de données :" + +msgid "Database User Name:" +msgstr "Nom d'utilisateur de la base de données :" + +msgid "Database Password:" +msgstr "Mot de passe de la base de données :" + +msgid "Database Tables Prefix:" +msgstr "Préfixe des tables de la base de données :" + +msgid "Continue" +msgstr "Continuer" + +msgid "No such installed language" +msgstr "Cette langue n'est pas installée" + +msgid "You can't remove English language." +msgstr "Vous ne pouvez pas supprimer la langue anglaise." + +msgid "Permissions to delete language denied." +msgstr "Permission de supprimer la langue refusée." + +msgid "Invalid language file URL." +msgstr "URL de fichier de langue invalide." + +msgid "Languages management" +msgstr "Gestion des langues" + +msgid "Language has been successfully deleted." +msgstr "La langue a été supprimée avec succès." + +msgid "Language has been successfully installed." +msgstr "La langue a été installée avec succès." + +msgid "Language has been successfully upgraded" +msgstr "La langue a été mise à jour avec succès." + +msgid "Here you can install, upgrade or remove languages for your Dotclear installation." +msgstr "Sur cette page, vous pouvez installer, mettre à jour ou supprimer des langues de votre installation de Dotclear." + +#, php-format +msgid "You can change your user language in your preferences or change your blog's main language in your blog settings." +msgstr "Vous pouvez changer votre langue d'utilisateur dans vos préférences ou changer la langue principale de votre blog dans vos paramètres de blog." + +msgid "Installed languages" +msgstr "Langues installées" + +msgid "No additional language is installed." +msgstr "Aucune langue supplémentaire n'est installée." + +msgid "Language" +msgstr "Langue" + +msgid "Action" +msgstr "Action" + +msgid "Install or upgrade languages" +msgstr "Installer ou mettre à jour une langue" + +#, php-format +msgid "You can install or remove a language by adding or removing the relevant directory in your %s folder." +msgstr "Vous pouvez installer ou supprimer une langue en ajoutant ou supprimant le répertoire correspondant dans votre répertoire %s." + +msgid "Available languages" +msgstr "Langues disponibles" + +#, php-format +msgid "You can download and install a additional language directly from Dotclear.net. Proposed languages are based on your version: %s." +msgstr "Vous pouvez télécharger et installer une langue supplémentaire directement depuis Dotclear.net. Les langues proposées sont basées sur votre version : %s." + +msgid "Language:" +msgstr "Langue :" + +msgid "Install language" +msgstr "Installer la langue" + +msgid "You can install languages by uploading zip files." +msgstr "Vous pouvez installer des langues en déposant des fichiers zip." + +msgid "Language zip file:" +msgstr "Fichier zip de la langue :" + +msgid "Upload language" +msgstr "Déposer la langue" + +msgid "Invalid language zip file." +msgstr "Fichier zip de langue invalide." + +msgid "The zip file does not appear to be a valid Dotclear language pack." +msgstr "Le fichier zip ne semble pas être un fichier valide de langue Dotclear." + +msgid "An error occurred during language upgrade." +msgstr "Une erreur est survenue durant la mise à jour de la langue." + +msgid "Error:" +msgstr "Erreur :" + +msgid "By names, in ascending order" +msgstr "Par noms, croissants" + +msgid "By names, in descending order" +msgstr "Par noms, décroissants" + +msgid "By dates, in ascending order" +msgstr "Par dates, croissantes" + +msgid "By dates, in descending order" +msgstr "Par dates, décroissantes" + +msgid "Media manager" +msgstr "Gestionnaire de médias" + +msgid "confirm removal" +msgstr "Confirmer la suppression" + +#, php-format +msgid "Are you sure you want to remove %s?" +msgstr "Êtes-vous certain de vouloir supprimer %s ?" + +msgid "Cancel" +msgstr "Annuler" + +msgid "Yes" +msgstr "oui" + +msgid "Directory has been successfully created." +msgstr "Répertoire créé avec succès." + +msgid "Files have been successfully uploaded." +msgstr "Fichier chargé avec succès." + +msgid "File has been successfully removed." +msgstr "Fichier supprimé avec succès." + +msgid "Directory has been successfully removed." +msgstr "Répertoire supprimé avec succès." + +msgid "Directory has been successfully rebuilt." +msgstr "Répertoire reconstruit avec succès." + +msgid "Zip file has been successfully extracted." +msgstr "Le fichier zip a été extrait avec succès." + +#, php-format +msgid "Choose a file to attach to entry %s by clicking on %s." +msgstr "Choisissez un fichier à attacher au billet %s en cliquant sur %s." + +msgid "Attach this file to entry" +msgstr "Attacher ce fichier au billet" + +#, php-format +msgid "Choose a file to insert into entry by clicking on %s." +msgstr "Choisissez un fichier à insérer dans le billet en cliquant sur %s." + +msgid "No file." +msgstr "Aucun fichier." + +msgid "Sort files:" +msgstr "Trier les fichiers :" + +msgid "Sort" +msgstr "Trier" + +msgid "Add files" +msgstr "Ajouter des fichiers" + +msgid "Please take care to publish media that you own and that are not protected by copyright." +msgstr "Veuillez prendre garde à ne publier que des médias que vous possédez ou qui ne sont pas protégés contre la copie." + +msgid "Choose a file:" +msgstr "Choisissez un fichier :" + +#, php-format +msgid "Maximum size %s" +msgstr "Taille maximale %s" + +msgid "Private" +msgstr "Privé" + +msgid "To send several files at the same time, you can activate the enhanced uploader in" +msgstr "Pour envoyer plusieurs fichiers à la fois, vous pouvez activer l'interface avancée dans" + +msgid "Send" +msgstr "Envoyer" + +msgid "New directory" +msgstr "Nouveau répertoire" + +msgid "Directory Name:" +msgstr "Nom du répertoire :" + +msgid "Download this directory as a zip file" +msgstr "Télécharger ce répertoire dans un fichier zip" + +msgid "open" +msgstr "ouvrir" + +msgid "Insert this file into entry" +msgstr "Insérer ce fichier dans le billet" + +msgid "delete" +msgstr "supprimer" + +msgid "Not a valid file" +msgstr "Fichier invalide" + +msgid "File has been successfully updated." +msgstr "Fichier mis à jour avec succès." + +msgid "Thumbnails have been successfully updated." +msgstr "Les miniatures ont été mises à jour avec succès." + +msgid "Insert media item" +msgstr "Insérer un média" + +msgid "Image size:" +msgstr "Taille de l'image :" + +msgid "original" +msgstr "originale" + +msgid "Image alignment" +msgstr "Alignement de l'image" + +msgid "None" +msgstr "Aucun" + +msgid "Left" +msgstr "Gauche" + +msgid "Right" +msgstr "Droite" + +msgid "Center" +msgstr "Centre" + +msgid "Image insertion" +msgstr "Insertion de l'image" + +msgid "As a single image" +msgstr "En tant qu'image uniquement" + +msgid "As a link to original image" +msgstr "En tant que lien vers l'image originale" + +msgid "MP3 disposition" +msgstr "Disposition du MP3" + +msgid "Please note that you cannot insert mp3 files with visual editor." +msgstr "Merci de noter que vous ne pouvez pas insérer de fichier mp3 avec l'éditeur visuel." + +msgid "Please note that you cannot insert video files with visual editor." +msgstr "Merci de noter que vous ne pouvez pas insérer de fichier vidéo avec l'éditeur visuel." + +msgid "Video size" +msgstr "Taille de la vidéo" + +msgid "Width:" +msgstr "Largeur :" + +msgid "Height:" +msgstr "Hauteur :" + +msgid "Video disposition" +msgstr "Disposition de la vidéo" + +msgid "Media item will be inserted as a link." +msgstr "Le média sera inséré en tant que lien." + +msgid "Insert" +msgstr "Insérer" + +msgid "Media details" +msgstr "Détails du média" + +msgid "Available sizes:" +msgstr "Tailles disponibles :" + +msgid "File owner:" +msgstr "Propriétaire du fichier :" + +msgid "File type:" +msgstr "Type de fichier :" + +msgid "File size:" +msgstr "Taille du fichier :" + +msgid "File URL:" +msgstr "URL du fichier :" + +msgid "Show entries containing this media" +msgstr "Afficher les billets contenant ce média" + +msgid "Entries containing this media" +msgstr "Billets contenant ce média" + +msgid "No entry seems contain this media." +msgstr "Aucun billet ne semble contenir ce média." + +msgid "Image details" +msgstr "Détails de l'image" + +msgid "No detail" +msgstr "Aucun détail" + +msgid "Update thumbnails" +msgstr "Mettre à jour les miniatures" + +msgid "This will create or update thumbnails for this image." +msgstr "Ceci va créer ou mettre à jour les miniatures pour cette image." + +msgid "Extract in a new directory" +msgstr "Extraire dans un nouveau répertoire" + +msgid "Extract in current directory" +msgstr "Extraire dans le répertoire actuel" + +msgid "Extract archive" +msgstr "Extraire l'archive" + +msgid "This will extract archive in a new directory that should not exist yet." +msgstr "Ceci va extraire l'archive dans un nouveau répertoire qui ne doit pas encore exister." + +msgid "This will extract archive in current directory and will overwrite existing files or directory." +msgstr "Ceci va extraire l'archive dans le répertoire actuel et va écraser les fichiers ou répertoires existants." + +msgid "Extract mode:" +msgstr "Mode d'extraction :" + +msgid "Extract" +msgstr "Extrait" + +msgid "Change media properties" +msgstr "Changer les propriétés du média" + +msgid "File name:" +msgstr "Nom du fichier :" + +msgid "File title:" +msgstr "Titre du fichier :" + +msgid "File date:" +msgstr "Date du fichier :" + +msgid "New directory:" +msgstr "Nouveau répertoire :" + +msgid "Change file" +msgstr "Changer le fichier" + +msgid "No blog or user given." +msgstr "Vous n'avez pas indiqué de blog ou d'utilisateur" + +msgid "permissions" +msgstr "permissions" + +msgid "Permissions" +msgstr "Permissions" + +msgid "The permissions have been successfully updated." +msgstr "Permissions mises à jour avec succès." + +#, php-format +msgid "You are about to change permissions on the following blogs for users %s." +msgstr "Vous allez changer les permissions des utilisateurs %s pour ces blogs." + +msgid "Validate permissions" +msgstr "Valider les permissions" + +msgid "choose a blog" +msgstr "choisissez un blog" + +msgid "Choose a blog" +msgstr "Choisissez un blog" + +msgid "Entries per page" +msgstr "Billets par page" + +#, php-format +msgid "Choose one or more blogs to which you want to give permissions to users %s." +msgstr "Choisissez un ou plusieurs blogs pour lesquels les utilisateurs suivants auront des permissions : %s." + +msgid "Set permissions" +msgstr "définir les permissions" + +msgid "select" +msgstr "sélectionner" + +msgid "No content found on this plugin." +msgstr "Aucun contenu pour cette extension." + +msgid "Plugin not found" +msgstr "Extension introuvable" + +msgid "The plugin you reached does not exist or does not have an admin page." +msgstr "L'extension que vous essayez d'atteindre n'existe pas ou n'a pas de page d'administration." + +msgid "No such plugin." +msgstr "Extension inexistante." + +msgid "You don't have permissions to delete this plugin." +msgstr "Vous n'avez pas les permissions pour effacer cette extension" + +msgid "You don't have permissions to deactivate this plugin." +msgstr "Vous n'avez pas les permissions pour désactiver cette extension." + +msgid "Plugins management" +msgstr "Gestion des extensions" + +msgid "Plugin has been successfully deleted." +msgstr "L'extension a été supprimée avec succès." + +msgid "Plugin has been successfully installed." +msgstr "L'extension a été installée avec succès." + +msgid "Plugin has been successfully upgraded" +msgstr "L'extension a été mise à jour avec succès." + +msgid "Plugins add new functionalities to Dotclear. Here you can activate or deactivate installed plugins." +msgstr "Les extensions ajoutent de nouvelles fonctionnalités à Dotclear. Ici, vous pouvez activer ou désactiver les extensions installées." + +#, php-format +msgid "You can find additional plugins for your blog on %s." +msgstr "Vous pouvez trouver de nouvelles extensions pour votre blog sur %s." + +msgid "To install or upgrade a plugin you generally just need to upload it in \"Install or upgrade a plugin\" section." +msgstr "Tout ce que vous avez à faire pour installer ou mettre à jour une extension est généralement de la déposer dans la section \"Installer ou mettre à jour une extension\"." + +msgid "To install or upgrade a plugin you just need to extract it in your plugins directory." +msgstr "Tout ce que vous avez à faire pour installer une extension est de l'extraire dans votre répertoire d'extensions." + +msgid "Plugins" +msgstr "Extensions" + +msgid "Activated plugins" +msgstr "Extensions activées" + +msgid "Plugin" +msgstr "Extension" + +msgid "Version" +msgstr "Version" + +msgid "Details" +msgstr "Détails" + +msgid "Deactivate" +msgstr "Désactiver" + +msgid "Deactivated plugins" +msgstr "Extensions désactivées" + +msgid "Activate" +msgstr "Activer" + +msgid "Install or upgrade a plugin" +msgstr "Installer ou mettre à jour une extension" + +msgid "You can install plugins by uploading or downloading zip files." +msgstr "Vous pouvez installer des extensions en déposant ou téléchargeant des fichiers zip." + +msgid "Plugin zip file:" +msgstr "Fichier zip de l'extension :" + +msgid "Upload plugin" +msgstr "Déposer l'extension" + +msgid "Plugin zip file URL:" +msgstr "URL du fichier zip de l'extension :" + +msgid "Download plugin" +msgstr "Télécharger l'extension" + +msgid "To enable this function, please give write access to your plugins directory." +msgstr "Pour activer cette fonction, donnez un accès en écriture à votre répertoire d'extensions." + +msgid "Add a link" +msgstr "Ajouter un lien" + +msgid "Available" +msgstr "Disponible" + +msgid "Most used" +msgstr "Plus utilisées" + +msgid "Link URL:" +msgstr "URL du lien :" + +msgid "Link title:" +msgstr "Titre du lien :" + +msgid "Link language:" +msgstr "Langue du lien :" + +msgid "Add a link to an entry" +msgstr "Ajouter un lien vers un billet" + +msgid "Search entry:" +msgstr "Rechercher un billet :" + +msgid "Search" +msgstr "Rechercher" + +msgid "cancel" +msgstr "Annuler" + +msgid "This entry does not exist." +msgstr "Ce billet n'existe pas." + +msgid "Edit entry" +msgstr "Modifier le billet" + +msgid "next entry" +msgstr "billet suivant" + +msgid "previous entry" +msgstr "billet précédent" + +msgid "Entry has been successfully updated." +msgstr "Billet mis à jour avec succès." + +msgid "Entry has been successfully created." +msgstr "Billet créé avec succès." + +msgid "File has been successfully attached." +msgstr "Fichier attaché avec succès." + +msgid "Attachment has been successfully removed." +msgstr "Pièce jointe retirée avec succès." + +msgid "Comment has been successfully created." +msgstr "Commentaire créé avec succès." + +msgid "Don't forget to validate your XHTML conversion by saving your post." +msgstr "Enregistrez votre billet pour valider la transformation en XHTML." + +msgid "Go to this entry on the site" +msgstr "Voir ce billet sur le site" + +msgid "new window" +msgstr "nouvelle fenêtre" + +msgid "Excerpt:" +msgstr "Extrait :" + +msgid "Notes:" +msgstr "Notes :" + +msgid "Preview" +msgstr "Prévisualiser" + +msgid "Entry status:" +msgstr "État du billet :" + +msgid "Published on:" +msgstr "Publié le :" + +msgid "Text formating:" +msgstr "Format du texte :" + +msgid "Convert to XHTML" +msgstr "Convertir en XHTML" + +msgid "Selected entry" +msgstr "Billet sélectionné" + +msgid "Entry lang:" +msgstr "Langue du billet :" + +msgid "Entry password:" +msgstr "Mot de passe du billet :" + +msgid "Basename:" +msgstr "URL spécifique :" + +msgid "Warning: If you set the URL manually, it may conflict with another entry." +msgstr "Attention : si vous indiquez l'URL manuellement, celle-ci peut entrer en conflit avec un autre billet." + +msgid "Ping blogs" +msgstr "Faire des rétroliens" + +msgid "Trackbacks" +msgstr "Rétroliens" + +msgid "No trackback" +msgstr "Aucun rétrolien" + +msgid "Add a comment" +msgstr "Ajouter un commentaire" + +msgid "Name:" +msgstr "Nom :" + +msgid "IP address" +msgstr "Adresse IP" + +msgid "published" +msgstr "publié" + +msgid "unpublished" +msgstr "non publié" + +msgid "pending" +msgstr "en attente" + +msgid "junk" +msgstr "indésirable" + +msgid "select this comment" +msgstr "Sélectionner ce commentaire" + +msgid "select this trackback" +msgstr "Sélectionner ce rétrolien" + +msgid "Edit this comment" +msgstr "Modifier ce commentaire" + +msgid "This attachment does not exist" +msgstr "Cette pièce jointe n'existe pas" + +msgid "Remove attachment" +msgstr "Supprimer la pièce jointe" + +msgid "Attachment" +msgstr "Pièce jointe" + +msgid "Are you sure you want to remove this attachment?" +msgstr "Êtes-vous certain de vouloir supprimer cette pièce jointe ?" + +msgid "selected" +msgstr "sélectionné" + +msgid "not selected" +msgstr "non sélectionné" + +msgid "Category" +msgstr "Catégorie" + +msgid "Selected" +msgstr "Sélectionné" + +msgid "Publish" +msgstr "Publier" + +msgid "Unpublish" +msgstr "Mettre hors ligne" + +msgid "Schedule" +msgstr "Programmer" + +msgid "Mark as pending" +msgstr "Mettre en attente" + +msgid "Mark" +msgstr "Marquer" + +msgid "Mark as selected" +msgstr "Sélectionner" + +msgid "Mark as unselected" +msgstr "Désélectionner" + +msgid "Change" +msgstr "Changer" + +msgid "Change category" +msgstr "Changer la catégorie" + +msgid "Change author" +msgstr "Changer l'auteur" + +msgid "Selected:" +msgstr "Sélectionné :" + +msgid "Month:" +msgstr "Mois :" + +msgid "Lang:" +msgstr "Langue :" + +msgid "Selected entries action:" +msgstr "Action sur les billets sélectionnés :" + +msgid "This user does not exist" +msgstr "Cet utilisateur n'existe pas" + +msgid "Change category for entries" +msgstr "Changer de catégorie pour les billets" + +msgid "Change author for entries" +msgstr "Changer l'auteur des billets" + +msgid "Author ID:" +msgstr "Identifiant de l'utilisateur :" + +msgid "Default" +msgstr "Défaut" + +msgid "If you want to change your email or password you must provide your current password." +msgstr "Si vous voulez changer votre adresse email ou votre mot de passe, vous devez indiquer votre mot de passe actuel." + +msgid "No favorite selected" +msgstr "Aucun favori sélectionné" + +msgid "Personal information has been successfully updated." +msgstr "Informations personnelles mises à jour avec succès." + +msgid "Personal options has been successfully updated." +msgstr "Vos options personnelles ont été enregistrées avec succès." + +msgid "Favorites have been successfully added." +msgstr "Les favoris ont été ajoutés avec succès." + +msgid "Favorites have been successfully updated." +msgstr "Les favoris ont été mis à jour avec succès." + +msgid "Favorites have been successfully removed." +msgstr "Les favoris ont été retirés avec succès." + +msgid "Default favorites have been successfully updated." +msgstr "Les favoris par défaut ont été enregistrés avec succès." + +msgid "My profile" +msgstr "Mon profil" + +msgid "Display name:" +msgstr "Pseudonyme :" + +msgid "User language:" +msgstr "Langue de l'utilisateur :" + +msgid "User timezone:" +msgstr "Fuseau horaire de l'utilisateur :" + +msgid "If you have changed this user email or password you must provide your current password to save these modifications." +msgstr "Si vous voulez changer votre adresse email ou votre mot de passe, vous devez indiquer votre mot de passe." + +msgid "My options" +msgstr "Mes options" + +msgid "Preferred format:" +msgstr "Format d'édition préféré :" + +msgid "Default entry status:" +msgstr "État des billets par défaut :" + +msgid "Entry edit field height:" +msgstr "Taille de la zone d'édition :" + +msgid "Enable WYSIWYG mode" +msgstr "Activer l'éditeur visuel" + +msgid "Activate enhanced uploader in media manager" +msgstr "Activer l'interface avancée du gestionnaire de médias" + +msgid "Hide My favorites menu" +msgstr "Cacher le menu Mes Favoris" + +msgid "Iconset:" +msgstr "Jeu d'icônes :" + +msgid "Do not use standard favicon" +msgstr "Ne pas utiliser le favicon standard" + +msgid "This will be applied for all users" +msgstr "Ce choix sera actif pour tous les utilisateurs" + +msgid "Accessibility options" +msgstr "Options d'accessibilité" + +msgid "Disable javascript powered drag and drop for ordering items" +msgstr "Désactiver le drag and drop javascript pour ordonnancer les éléments" + +msgid "Numeric fields will allow to type the elements' ordering number." +msgstr "Des champs numériques permettront d'indiquer la position des éléments." + +msgid "Dashboard modules" +msgstr "Modules du tableau de bord" + +msgid "Display documentation links" +msgstr "Afficher les liens de documentation" + +msgid "Display Dotclear news" +msgstr "Afficher les nouvelles de Dotclear" + +msgid "Display quick entry form" +msgstr "Afficher le formulaire de billet rapide" + +msgid "My favorites" +msgstr "Mes favoris" + +#, php-format +msgid "position of %s" +msgstr "position de %s" + +msgid "Save order" +msgstr "Enregistrer l'ordre" + +msgid "Delete selected favorites" +msgstr "Retirer les favoris sélectionnés" + +msgid "Are you sure you want to remove selected favorites?" +msgstr "Êtes-vous sûr de vouloir retirer les favoris sélectionnés ?" + +msgid "If you are a super administrator, you may define this set of favorites to be used by default on all blogs of this installation:" +msgstr "Si vous êtes super administrateur, vous pouvez définir ce jeu de favoris comme l'ensemble par défaut pour tous les blogs de l'installation :" + +msgid "Define as default favorites" +msgstr "Définir comme favoris par défaut" + +msgid "Currently no personal favorites." +msgstr "La liste de vos favoris est vide pour le moment." + +msgid "Default favorites" +msgstr "Favoris par défaut" + +msgid "Those favorites are displayed when My Favorites list is empty." +msgstr "Favoris affichés quand la liste Mes Favoris est vide." + +msgid "Available favorites" +msgstr "Favoris disponibles" + +msgid "Add to my favorites" +msgstr "Ajouter à mes favoris" + +msgid "Search options" +msgstr "Options de recherche" + +msgid "Query:" +msgstr "Requête :" + +msgid "Search entries" +msgstr "Rechercher des billets" + +msgid "Search comments" +msgstr "Rechercher des commentaires" + +msgid "schedule" +msgstr "programmer" + +msgid "change category" +msgstr "changer la catégorie" + +msgid "change author" +msgstr "changer l'auteur" + +#, php-format +msgid "%d entries found" +msgstr "%d billets trouvés" + +#, php-format +msgid "%d entry found" +msgstr "%d billet trouvé" + +#, php-format +msgid "%d comment found" +msgstr "%d commentaire trouvé" + +#, php-format +msgid "%d comments found" +msgstr "%d commentaires trouvés" + +msgid "This entry does not exist or is not published" +msgstr "Ce billet n'existe pas ou n'est pas publié" + +msgid "All pings sent." +msgstr "Tous les rétroliens ont été envoyés." + +#, php-format +msgid "Back to \"%s\"" +msgstr "Retour à \"%s\"" + +msgid "Auto discover ping URLs" +msgstr "Découverte automatique des URL à rétrolier" + +msgid "URLs to ping:" +msgstr "URLs à rétrolier :" + +msgid "Send excerpt:" +msgstr "Envoyer l'extrait :" + +msgid "Previously sent pings" +msgstr "Rétroliens déjà envoyés" + +msgid "Dotclear update" +msgstr "Mise à jour de Dotclear" + +#, php-format +msgid "Unable to delete file %s" +msgstr "Impossible de supprimer le fichier %s" + +#, php-format +msgid "Downloaded Dotclear archive seems to be corrupted. Try download it again." +msgstr "L'archive téléchargée de Dotclear semble être corrompue. Essayer de la télécharger à nouveau." + +msgid "The following files of your Dotclear installation have been modified so we won't try to update your installation. Please try to update manually." +msgstr "Comme les fichiers suivants de votre installation de Dotclear ont été modifiés, votre installation ne peut être mise à jour. Merci de mettre à jour manuellement." + +#, php-format +msgid "The following files of your Dotclear installation are not readable. Please fix this or try to make a backup file named %s manually." +msgstr "Les fichiers suivants de votre installation de Dotclear ne peuvent pas être lus. Veuillez corriger ceci ou créer un fichier de backup nommé %s manuellement." + +msgid "The following files of your Dotclear installation cannot be written. Please fix this or try to update manually." +msgstr "Les fichiers suivants de votre installation de Dotclear ne peuvent pas être écrits. Veuillez corriger ceci ou mettre à jour manuellement." + +msgid "No newer Dotclear version available." +msgstr "Aucune nouvelle version de Dotclear n'est disponible." + +#, php-format +msgid "Dotclear %s is available." +msgstr "Dotclear %s est disponible." + +msgid "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." +msgstr "Pour mettre à jour votre installation de Dotclear, cliquez sur le bouton suivant. Un fichier de sauvegarde de votre installation actuelle sera créé dans votre répertoire principal." + +msgid "Update Dotclear" +msgstr "Mettre à jour Dotclear" + +msgid "Update backup files" +msgstr "Sauvegardes des mises à jour" + +msgid "The following files are backups of previously updates. You can revert your previous installation or delete theses files." +msgstr "Les fichiers suivants sont des sauvegardes de mises à jour précédentes. Vous pouvez rétablir votre installation précédente ou supprimer ces fichiers." + +msgid "Please note that reverting your Dotclear version may have some unwanted side-effects. Consider reverting only if you experience strong issues with this new version." +msgstr "Merci de noter que rétablir votre version de Dotclear peut avoir des effets non désirés. N'envisagez ceci que si vous rencontrez d'importantes difficultés avec cette nouvelle version." + +#, php-format +msgid "You should not revert to version prior to last one (%s)." +msgstr "Vous ne devez pas rétablir une version précédant la dernière (%s)." + +msgid "Delete selected file" +msgstr "Supprimer le fichier sélectionné" + +msgid "Revert to selected file" +msgstr "Rétablir le fichier sélectionné" + +msgid "Congratulations, you're one click away from the end of the update." +msgstr "Félicitations, vous êtes à un clic de la fin de la mise à jour." + +msgid "Finish the update." +msgstr "Finir la mise à jour." + +msgid "new user" +msgstr "nouvel utilisateur" + +#, php-format +msgid "User \"%s\" already exists." +msgstr "L'utilisateur \"%s\" existe déjà." + +msgid "User has been successfully updated." +msgstr "Utilisateur mis à jour avec succès." + +msgid "User has been successfully created." +msgstr "Utilisateur créé avec succès." + +msgid "Warning:" +msgstr "Attention :" + +msgid "If you change your username, you will have to log in again." +msgstr "Si vous changez votre login, vous devrez vous identifier à nouveau." + +msgid "Mandatory for password recovering procedure." +msgstr "Obligatoire pour la procédure de récupération de mot de passe." + +msgid "Password change required to connect" +msgstr "Changement de mot de passe requis pour la connexion" + +msgid "Save and create another" +msgstr "Enregistrer et créer un nouveau" + +msgid "No permissions." +msgstr "Aucune permission." + +msgid "Add new permissions" +msgstr "Ajouter de nouvelles permissions" + +msgid "Username" +msgstr "Identifiant" + +msgid "Last Name" +msgstr "Nom" + +msgid "First Name" +msgstr "Prénom" + +msgid "Display name" +msgstr "Pseudonyme" + +msgid "Number of entries" +msgstr "Nombre de billets" + +msgid "users" +msgstr "utilisateurs" + +msgid "User has been successfully removed." +msgstr "Utilisateur supprimé avec succès." + +msgid "Create a new user" +msgstr "Créer un nouvel utilisateur" + +msgid "Users per page" +msgstr "Utilisateurs par page" + +msgid "Selected users action:" +msgstr "Action sur les utilisateurs sélectionnés :" + +msgid "Blog:" +msgstr "Blog :" + +msgid "Change blog" +msgstr "Changer de blog" + +msgid "Blogs:" +msgstr "Blogs :" + +msgid "Go to the content" +msgstr "Aller au contenu" + +msgid "Go to the menu" +msgstr "Aller au menu" + +msgid "Go to site" +msgstr "Aller sur le site" + +msgid "My dashboard" +msgstr "Mon tableau de bord" + +#, php-format +msgid "Logout %s" +msgstr "Déconnecter %s" + +msgid "Safe mode" +msgstr "Mode de secours" + +msgid "You are in safe mode. All plugins have been temporarily disabled. Remind to log out then log in again normally to get back all functionalities" +msgstr "Vous êtes en mode de secours. Tous les plugins ont été temporairement désactivés. N'oubliez-pas de vous déconnecter puis de vous reconnecter normalement pour retrouver toutes les fonctionnalités" + +#, php-format +msgid "Thank you for using %s." +msgstr "Merci d'utiliser %s." + +msgid "Help" +msgstr "Aide" + +msgid "uncover" +msgstr "dévoiler" + +msgid "hide" +msgstr "cacher" + +msgid "help" +msgstr "aide" + +msgid "no selection" +msgstr "aucune sélection" + +msgid "select all" +msgstr "tout sélectionner" + +msgid "invert selection" +msgstr "inverser la sélection" + +msgid "view entry" +msgstr "voir le billet" + +#, php-format +msgid "Are you sure you want to delete selected entries (%s)?" +msgstr "Êtes-vous certain de vouloir supprimer les billets sélectionnés (%s) ?" + +msgid "Are you sure you want to delete this entry?" +msgstr "Êtes-vous certain de vouloir supprimer ce billet ?" + +#, php-format +msgid "Are you sure you want to delete selected comments (%s)?" +msgstr "Êtes-vous certain de vouloir supprimer les commentaires sélectionnés (%s) ?" + +msgid "Are you sure you want to delete this comment?" +msgstr "Êtes-vous certain de vouloir supprimer ce commentaire ?" + +msgid "Users with posts cannot be deleted." +msgstr "Les utilisateurs ayant écrit des billets ne peuvent être effacées." + +#, php-format +msgid "Are you sure you want to delete selected users (%s)?" +msgstr "Êtes-vous certain de vouloir supprimer les utilisateurs sélectionnés (%s) ?" + +#, php-format +msgid "Are you sure you want to delete category \"%s\"?" +msgstr "Êtes-vous certain de vouloir supprimer la catégorie \"%s\" ?" + +msgid "Are you sure you want to reorder all categories?" +msgstr "Êtes-vous certain de vouloir réinitialiser l'ordre des catégories ?" + +#, php-format +msgid "Are you sure you want to remove media \"%s\"?" +msgstr "Êtes-vous certain de vouloir supprimer le média \"%s\" ?" + +msgid "Are you sure you want to extract archive in current directory?" +msgstr "Êtes-vous certain de vouloir extraire l'archive dans le répertoire actuel ?" + +#, php-format +msgid "Are you sure you want to remove attachment \"%s\"?" +msgstr "Êtes-vous certain de vouloir supprimer la pièce jointe \"%s\" ?" + +#, php-format +msgid "Are you sure you want to delete \"%s\" language?" +msgstr "Êtes-vous certain de vouloir supprimer la langue \"%s\" ?" + +#, php-format +msgid "Are you sure you want to delete \"%s\" plugin?" +msgstr "Êtes-vous certain de vouloir supprimer l'extension \"%s\" ?" + +msgid "Use this theme" +msgstr "Utiliser ce thème" + +msgid "Remove this theme" +msgstr "Supprimer ce thème" + +#, php-format +msgid "Are you sure you want to delete \"%s\" theme?" +msgstr "Êtes-vous certain de vouloir supprimer le thème \"%s\" ?" + +msgid "Zip file content" +msgstr "Contenu du fichier zip" + +msgid "XHTML markup validator" +msgstr "Validation XHTML" + +msgid "XHTML content is valid." +msgstr "Le contenu XHTML est valide." + +msgid "There are XHTML markup errors." +msgstr "Il y a des erreurs XHTML." + +msgid "You have unsaved changes. Switch post format will loose these changes. Proceed anyway?" +msgstr "Vous avez des modifications non sauvegardées. Changer de format vous fera perdre ces modifications. Continuer ?" + +msgid "Loading enhanced uploader, please wait." +msgstr "Chargement de l'interface avancée." + +msgid "You have unsaved changes." +msgstr "Vous n'avez pas enregistré vos modifications." + +msgid "close" +msgstr "fermer" + +msgid "now" +msgstr "maintenant" + +msgid "visual" +msgstr "visuel" + +msgid "source" +msgstr "source" + +msgid "You can use the following shortcuts to format your text." +msgstr "Vous pouvez utiliser les raccourcis suivants pour formater votre texte." + +msgid "-- none --" +msgstr "-- aucun --" + +msgid "-- block format --" +msgstr "-- format bloc --" + +msgid "Paragraph" +msgstr "Paragraphe" + +msgid "Level 1 header" +msgstr "Titre de niveau 1" + +msgid "Level 2 header" +msgstr "Titre de niveau 2" + +msgid "Level 3 header" +msgstr "Titre de niveau 3" + +msgid "Level 4 header" +msgstr "Titre de niveau 4" + +msgid "Level 5 header" +msgstr "Titre de niveau 5" + +msgid "Level 6 header" +msgstr "Titre de niveau 6" + +msgid "Strong emphasis" +msgstr "Forte emphase" + +msgid "Emphasis" +msgstr "Emphase" + +msgid "Inserted" +msgstr "Insertion" + +msgid "Deleted" +msgstr "Suppression" + +msgid "Inline quote" +msgstr "Citation en ligne" + +msgid "Code" +msgstr "Code" + +msgid "Line break" +msgstr "Passage à la ligne" + +msgid "Blockquote" +msgstr "Bloc de citation" + +msgid "Preformated text" +msgstr "Texte préformaté" + +msgid "Unordered list" +msgstr "Liste à puces" + +msgid "Ordered list" +msgstr "Liste numérotée" + +msgid "Link" +msgstr "Lien" + +msgid "URL?" +msgstr "URL ?" + +msgid "Language?" +msgstr "Langue ?" + +msgid "External image" +msgstr "Image externe" + +msgid "Media chooser" +msgstr "Sélecteur de média" + +msgid "Link to an entry" +msgstr "Lien vers un billet" + +msgid "Activate enhanced uploader" +msgstr "Activer l'interface avancée" + +msgid "Disable enhanced uploader" +msgstr "Désactiver l'interface avancée" + +msgid "File successfully uploaded." +msgstr "Fichier envoyé avec succès." + +msgid "Maximum file size allowed:" +msgstr "Taille maximale de fichier autorisée :" + +msgid "Limit exceeded." +msgstr "Limite dépassée." + +msgid "File size exceeds allowed limit." +msgstr "La taille du fichier dépasse la limite autorisée." + +msgid "Canceled." +msgstr "Annulé." + +msgid "HTTP Error:" +msgstr "Erreur HTTP :" + +msgid "Choose file" +msgstr "Choisir un fichier" + +msgid "Choose files" +msgstr "Choisir des fichiers" + +msgid "Clean" +msgstr "Nettoyer" + +msgid "Upload" +msgstr "Envoyer" + +msgid "No file in queue." +msgstr "Aucun fichier en file d'attente." + +msgid "1 file in queue." +msgstr "1 fichier en attente." + +#, php-format +msgid "%d files in queue." +msgstr "%d fichiers en attente." + +msgid "Queue error:" +msgstr "Erreur de file d'attente :" + +msgid "«prev." +msgstr "«préc." + +msgid "next»" +msgstr "suiv.»" + +msgid "No entry" +msgstr "Pas de billet" + +msgid "scheduled" +msgstr "programmé" + +msgid "protected" +msgstr "protégé" + +#, php-format +msgid "%d attachment" +msgstr "%d annexe" + +#, php-format +msgid "%d attachments" +msgstr "%d annexes" + +msgid "Type" +msgstr "Type" + +msgid "No user" +msgstr "Pas d'utilisateur" + +msgid "admin" +msgstr "Administrateur" + +msgid "superadmin" +msgstr "Super administrateur" + +msgid "Database error" +msgstr "Erreur de base de données :" + +msgid "There seems to be no Session table in your database. Is Dotclear completly installed?" +msgstr "Il semble de la table Session n'existe pas dans votre base de données. Dotclear est-il bien installé correctement?" + +msgid "System" +msgstr "Système" + +msgid "Blog" +msgstr "Blog" + +msgid "Updates" +msgstr "Mises à jour" + +msgid "Languages" +msgstr "Langues" + +msgid "administrator" +msgstr "administrateur" + +msgid "manage their own entries and comments" +msgstr "gérer ses propres billets et commentaires" + +msgid "publish entries and comments" +msgstr "publier des billets et des commentaires" + +msgid "delete entries and comments" +msgstr "supprimer des billets et des commentaires" + +msgid "manage all entries and comments" +msgstr "gérer tous les billets et commentaires" + +msgid "manage categories" +msgstr "gérer les catégories" + +msgid "manage their own media items" +msgstr "gérer ses propres médias" + +msgid "manage all media items" +msgstr "gérer tous les médias" + +msgid "That user does not exist in the database." +msgstr "Cet utilisateur n'existe pas dans la base de données." + +msgid "That key does not exist in the database." +msgstr "Cette clé n'existe pas dans la base de données." + +msgid "You are not allowed to add categories" +msgstr "Vous n'êtes pas autorisé à créer des catégories" + +msgid "You are not allowed to update categories" +msgstr "Vous n'êtes pas autorisé à modifier des catégories" + +msgid "You are not allowed to delete categories" +msgstr "Vous n'êtes pas autorisé à supprimer des catégories" + +msgid "This category is not empty." +msgstr "Cette catégorie n'est pas vide." + +msgid "You are not allowed to reset categories order" +msgstr "Vous n'êtes pas autorisé à modifier l'ordre des catégories" + +msgid "Category URL must be unique." +msgstr "L'URL de chaque catégorie doit être unique." + +msgid "You must provide a category title" +msgstr "Vous devez indiquer un titre de catégorie" + +msgid "You must provide a category URL" +msgstr "Vous devez indiquer une URL de catégorie" + +msgid "You are not allowed to create an entry" +msgstr "Vous n'êtes pas autorisé à créer des billets" + +msgid "You are not allowed to update entries" +msgstr "Vous n'êtes pas autorisé à modifier les billets" + +msgid "No such entry ID" +msgstr "Identifiant du billet inconnu" + +msgid "You are not allowed to edit this entry" +msgstr "Vous n'êtes pas autorisé à modifier ce billet" + +msgid "You are not allowed to change this entry status" +msgstr "Vous n'êtes pas autorisé à modifier l'état de ce billet" + +msgid "You are not allowed to change this entry category" +msgstr "Vous n'êtes pas autorisé à modifier la catégorie de ce billet" + +msgid "You are not allowed to mark this entry as selected" +msgstr "Vous n'êtes pas autorisé à marquer ce billet comme sélectionné" + +msgid "You are not allowed to delete entries" +msgstr "Vous n'êtes pas autorisé à supprimer des billets" + +msgid "You are not allowed to delete this entry" +msgstr "Vous n'êtes pas autorisé à supprimer ce billet" + +msgid "No entry title" +msgstr "Pas de titre de billet" + +msgid "No entry content" +msgstr "Pas de contenu de billet" + +msgid "Empty entry URL" +msgstr "URL du billet vide" + +msgid "You are not allowed to update comments" +msgstr "Vous n'êtes pas autorisé à modifier des commentaires" + +msgid "No such comment ID" +msgstr "Identifiant du commentaire inconnu" + +msgid "You are not allowed to update this comment" +msgstr "Vous n'êtes pas autorisé à modifier ce commentaire" + +msgid "You are not allowed to change this comment's status" +msgstr "Vous n'êtes pas autorisé à changer l'état de ce commentaire" + +msgid "You are not allowed to delete comments" +msgstr "Vous n'êtes pas autorisé à supprimer des commentaires" + +msgid "You are not allowed to delete this comment" +msgstr "Vous n'êtes pas autorisé à supprimer ce commentaire" + +msgid "You must provide a comment" +msgstr "Vous devez indiquer un commentaire" + +msgid "You must provide an author name" +msgstr "Vous devez indiquer un nom d'auteur" + +msgid "Email address is not valid." +msgstr "Adresse email invalide." + +msgid "online" +msgstr "en ligne" + +msgid "offline" +msgstr "hors ligne" + +msgid "removed" +msgstr "supprimé" + +msgid "You are not an administrator" +msgstr "Vous n'êtes pas administrateur" + +msgid "Invalid user language code" +msgstr "Code langue de l'utilisateur invalide" + +msgid "Blog ID must contain at least 2 characters using letters, numbers or symbols." +msgstr "L'identifiant du blog doit contenir au moins 2 caractères composés de lettres, chiffres ou symboles." + +msgid "No blog name" +msgstr "Pas de nom de blog" + +msgid "No blog URL" +msgstr "Pas d'URL de blog" + +msgid "No log message" +msgstr "Pas de message dans le journal" + +msgid "unknown" +msgstr "inconnu" + +msgid "No blog defined." +msgstr "Aucun blog défini." + +#, php-format +msgid "Directory %s does not exist." +msgstr "Le répertoire %s n'existe pas." + +msgid "You are not a super administrator." +msgstr "Vous n'êtes pas super administrateur." + +msgid "Permission denied." +msgstr "Permission refusée." + +msgid "You are not the file owner." +msgstr "Vous n'êtes pas le propriétaire de ce fichier." + +msgid "This file is not allowed." +msgstr "Ce fichier n'est pas autorisé." + +msgid "New file already exists." +msgstr "Le nouveau fichier existe déjà." + +msgid "File does not exist in the database." +msgstr "Ce fichier n'existe pas dans la base de données." + +#, php-format +msgid "Extract destination directory %s already exists." +msgstr "Le répertoire de destination d'extraction %s existe déjà." + +msgid "Embedded Audio Player" +msgstr "Fichier audio intégré" + +msgid "Embedded Video Player" +msgstr "Fichier vidéo intégré" + +#, php-format +msgid "%s: in [%s] and [%s]" +msgstr "%s: dans [%s] et [%s]" + +msgid "Empty module zip file." +msgstr "Fichier zip de module vide." + +msgid "The zip file does not appear to be a valid Dotclear module." +msgstr "Le fichier zip ne semble pas être un fichier valide de module Dotclear." + +msgid "An error occurred during module deletion." +msgstr "Une erreur est survenue durant la suppression du module." + +#, php-format +msgid "Unable to upgrade \"%s\". (same version)" +msgstr "Impossible de mettre à jour \"%s\" (même version)." + +msgid "Unable to read new _define.php file" +msgstr "Impossible de lire le nouveau fichier _define.php." + +msgid "No such module." +msgstr "Module inexistant." + +msgid "Cannot remove module files" +msgstr "Impossible de supprimer les fichiers du module" + +msgid "Cannot deactivate plugin." +msgstr "L'extension ne peut pas être désactivée." + +msgid "Cannot activate plugin." +msgstr "L'extension ne peut pas être activée." + +#, php-format +msgid "Invalid setting dcNamespace: %s" +msgstr "Espace de nommage du paramètre invalide : %s" + +msgid "Unable to retrieve settings:" +msgstr "Impossible d'obtenir les paramètres :" + +#, php-format +msgid "%s is not a valid setting id" +msgstr "%s n'est pas un identifiant de paramètre valide" + +msgid "No namespace specified" +msgstr "Aucun espace de nommage spécifié" + +msgid "Unable to retrieve workspaces:" +msgstr "Impossible d'obtenir les espaces de travail :" + +msgid "Unable to retrieve namespaces:" +msgstr "Impossible d'obtenir les espaces de nommage :" + +#, php-format +msgid "Invalid setting namespace: %s" +msgstr "Espace de nommage du paramètre invalide : %s" + +#, php-format +msgid "%s has still been pinged" +msgstr "Un rétrolien vers %s a déjà été fait" + +msgid "Unable to ping URL" +msgstr "Impossible de réaliser le rétrolien" + +#, php-format +msgid "%s is not a ping URL" +msgstr "%s n'est pas une URL de rétrolien" + +#, php-format +msgid "%s, ping error:" +msgstr "%s, erreur de rétrolien :" + +msgid "Digests file not found." +msgstr "Fichier de contrôle introuvable." + +msgid "No file to download" +msgstr "Aucun fichier à télécharger." + +msgid "Root directory is not writable." +msgstr "Le répertoire principal n'est pas accessible en écriture." + +msgid "An error occurred while downloading archive." +msgstr "Une erreur est survenue lors du téléchargement de l'archive." + +msgid "Archive not found." +msgstr "L'archive n'a pas été trouvée." + +msgid "Unable to read current digests file." +msgstr "Impossible de lire le fichier de contrôle actuel." + +msgid "Downloaded file does not seem to be a valid archive." +msgstr "Le fichier téléchargé ne semble pas être une archive valide." + +msgid "Incomplete archive." +msgstr "Archive incomplète." + +msgid "Unable to read digests file." +msgstr "Impossible de lire le fichier de contrôle." + +msgid "Invalid digests file." +msgstr "Fichier de contrôle invalide." + +#, php-format +msgid "Invalid dcWorkspace: %s" +msgstr "Espace de travail de la préférence invalide : %s" + +msgid "Unable to retrieve prefs:" +msgstr "Impossible d'obtenir les préférences :" + +#, php-format +msgid "%s is not a valid pref id" +msgstr "%s n'est pas un identifiant de préférence valide" + +msgid "No workspace specified" +msgstr "Aucun espace de travail spécifié" + +msgid "SQLite Database Schema cannot be upgraded." +msgstr "Le schema de base de données SQLite ne peut pas être mis à jour." + +msgid "Something went wrong with auto upgrade:" +msgstr "Une erreur est survenue durant la mise à jour automatique :" + +msgid "Unable to open directory." +msgstr "Impossible d'ouvrir le répertoire." + +msgid "Unable to create directory." +msgstr "Impossible de créer le répertoire." + +msgid "File is not writable." +msgstr "Le fichier n'est pas accessible en écriture." + +msgid "Unable to open file." +msgstr "Impossible d'ouvrir le fichier." + +# pas compris +msgid "Not an uploaded file." +msgstr "Pas un fichier déposé." + +msgid "The uploaded file exceeds the maximum file size allowed." +msgstr "Le fichier déposé est plus grand que la taille maximale autorisée." + +msgid "The uploaded file was only partially uploaded." +msgstr "Le fichier n'a été chargé qu'en partie." + +msgid "No file was uploaded." +msgstr "Aucun fichier chargé." + +msgid "Missing a temporary folder." +msgstr "Il manque un répertoire temporaire." + +msgid "Failed to write file to disk." +msgstr "Impossible d'écrire le fichier." + +#, php-format +msgid "%s is not a directory." +msgstr "%s n'est pas un répertoire." + +msgid "Bad range" +msgstr "Mauvaises limites" + +msgid "Invalid range" +msgstr "Sélection invalide" + +msgid "Invalid line number" +msgstr "Numéro de ligne invalide invalide" + +msgid "Chunk is out of range" +msgstr "L'extrait est hors limite" + +msgid "Bad context" +msgstr "Contexte invalide" + +msgid "Bad context (in deletion)" +msgstr "Contexte invalide (lors de la suppression)" + +msgid "Invalid diff format" +msgstr "Format de fichier diff invalide." + +msgid "Uploading this file is not allowed." +msgstr "L'envoi de ce fichier n'est pas autorisé." + +# "en jail" ? c'est du geek ? +msgid "Destination directory is not in jail." +msgstr "Le répertoire cible n'est pas en jail." + +msgid "File already exists." +msgstr "Le nouveau fichier existe déjà." + +msgid "Cannot write in this directory." +msgstr "Impossible d'écrire dans ce répertoire." + +msgid "An error occurred while writing the file." +msgstr "Une erreur est survenue pendant l'écriture du fichier." + +msgid "Source file does not exist." +msgstr "Le fichier source n'existe pas" + +# geek ? +msgid "File is not in jail." +msgstr "Le fichier n'est pas en jail." + +msgid "Destination directory is not writable." +msgstr "Le répertoire cible n'est pas accessible en écriture." + +msgid "Unable to rename file." +msgstr "Impossible de renommer le fichier." + +msgid "File cannot be removed." +msgstr "Ce fichier ne peut pas être supprimé." + +# décidément ! mais que veut dire "jail" ? +msgid "Directory is not in jail." +msgstr "Le répertoire n'est pas en jail." + +msgid "Directory cannot be removed." +msgstr "Ce répertoire ne peut pas être supprimé." + +msgid "Not enough memory to open image." +msgstr "Mémoire insuffisante pour ouvrir l'image." + +#, php-format +msgid "File %s is not compressed in the zip." +msgstr "Le fichier %s n'est pas compressé dans le zip." + +#, php-format +msgid "Trying to unzip a folder name %s" +msgstr "Tentative de décompresser un répertoire %s" + +msgid "Unable to write destination file." +msgstr "Impossible d'écrire le fichier de destination" + +msgid "Unable to write in target directory, permission denied." +msgstr "Impossible d'écrire dans le répertoire cible, permission refusée." + +msgid "Not enough memory to open file." +msgstr "Mémoire insuffisante pour ouvrir le fichier." + +msgid "File does not exist" +msgstr "Le fichier n'existe pas" + +msgid "Cannot read file" +msgstr "Impossible de lire le fichier" + +msgid "Directory does not exist" +msgstr "Le répertoire n'existe pas" + +msgid "Cannot read directory" +msgstr "Impossible de lire le répertoire" + +msgid "Unable to connect to database" +msgstr "Connexion à la base de données impossible" + +#, php-format +msgid "

        This either means that the username and password information in your config.php file is incorrect or we can't contact the database server at \"%s\". This could mean your host's database server is down.

        • Are you sure you have the correct username and password?
        • Are you sure that you have typed the correct hostname?
        • Are you sure that the database server is running?

        If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the Dotclear Support Forums.

        " +msgstr "

        Cela signifie soit que les informations d'identifiant ou de mot de passe de votre fichier config.php sont incorrects, soit que nous ne pouvons pas contacter le serveur de base de données à l'adresse \"%s\". Cela peut vouloir dire que le serveur en question est éteint.

        • Êtes-vous sûr que l'identifiant et le mot de passe sont corrects ?
        • Êtes-vous sûr d'avoir entré le bon nom de serveur ?
        • Êtes-vous sûr que le serveur fonctionne ?

        S vous n'êtes pas sûr de la signification de ces termes, vous devriez probablement contacter votre hébergeur. Si vous avez besoin d'une aide supplémentaire, vous pouvez vous rendre sur le forum d'entraide Dotclear.

        " + +msgid "The following error was encountered while trying to read the database:" +msgstr "L'erreur suivante a été rencontrée lors de la tentative d'accès à la base de données :" + +#~ msgid "medium" +#~ msgstr "moyenne" + +#~ msgid "small" +#~ msgstr "petite" + +#~ msgid "thumbnail" +#~ msgstr "miniature" + +#~ msgid "square" +#~ msgstr "carrée" + +#~ msgid "Blog themes" +#~ msgstr "Thèmes du blog" + +#~ msgid "Pages" +#~ msgstr "Pages" + +#~ msgid "Blogroll" +#~ msgstr "Liens / Blogroll" + +#~ msgid "update thumbnails" +#~ msgstr "mettre à jour les miniatures" + +#~ msgid "insert" +#~ msgstr "insérer" + +#~ msgid "View entry" +#~ msgstr "Voir le billet" + +#~ msgid "Go to this page on the site" +#~ msgstr "Voir cette page sur le site" + +#~ msgid "Attachments" +#~ msgstr "Pièces jointes" + +#~ msgid "remove" +#~ msgstr "supprimer" + +#~ msgid "Remove" +#~ msgstr "Supprimer" + +#~ msgid "No attachment." +#~ msgstr "Aucune pièce jointe" + +#~ msgid "Add files to this entry" +#~ msgstr "Ajouter un fichier au billet" + +#~ msgid "User:" +#~ msgstr "Utilisateur :" + +#~ msgid "Logout" +#~ msgstr "Déconnexion" + +#~ msgid "login" +#~ msgstr "login" + +#~ msgid "filter" +#~ msgstr "filtre" + +#~ msgid "User preferences" +#~ msgstr "Préférences utilisateur" + +#~ msgid "Documentation" +#~ msgstr "Documentation" + +#~ msgid "View site" +#~ msgstr "Voir le site" + +#~ msgid "The file %s already exists. If you need to reset any of the configuration items in this file, please delete it first or you may continue to install." +#~ msgstr "Le fichier %s existe déjà. Pour réinitialiser votre configuration, supprimez d'abord ce fichier ou bien continuez l'installation." + +#~ msgid "Login:" +#~ msgstr "Login :" + +#~ msgid "Login and password" +#~ msgstr "Identifiant et mot de passe" + +#~ msgid "User ID" +#~ msgstr "Identifiant utilisateur" + +#~ msgid "Name" +#~ msgstr "Nom" + +#~ msgid "Add this page to my favorites" +#~ msgstr "Ajouter cette page à mes favoris" + +#~ msgid "Modify numbers in fields to change favorites order." +#~ msgstr "Modifiez les nombres dans les champs pour changer l'ordre des favoris." diff --git a/v2/dotclear/locales/fr/plugins.lang.php b/v2/dotclear/locales/fr/plugins.lang.php new file mode 100644 index 0000000..526dd3e --- /dev/null +++ b/v2/dotclear/locales/fr/plugins.lang.php @@ -0,0 +1,530 @@ +DC1 redirect plugin and activate it in your blog configuration.'] = 'Veuillez noter que Dotclear 2 a un nouveau format d\'URL. Vous pouvez éviter les liens morts en installant le plugin DC1 redirect et en l\'activant dans la configuration de votre blog.'; +$GLOBALS['__l10n']['next step'] = 'étape suivante'; +$GLOBALS['__l10n']['Dotclear tables not found'] = 'Tables Dotclear non trouvées'; +$GLOBALS['__l10n']['Feed import'] = 'Importer depuis un flux'; +$GLOBALS['__l10n']['Imports a feed as new entries.'] = 'Importe un flux comme nouveaux billets.'; +$GLOBALS['__l10n']['Cannot retrieve feed URL.'] = 'Impossible d\'atteindre l\'URL du fil.'; +$GLOBALS['__l10n']['No items in feed.'] = 'Aucun élément dans le feed.'; +$GLOBALS['__l10n']['Content successfully imported.'] = 'Contenu importé avec succès.'; +$GLOBALS['__l10n']['This will import a feed (RSS or Atom) a as new content in the current blog: %s.'] = 'Ceci va importer un fil (RSS ou Atom) comme un nouveau contenu dans le blog en cours : %s.'; +$GLOBALS['__l10n']['Feed URL:'] = 'URL du fil'; +$GLOBALS['__l10n']['Flat file import'] = 'Importer depuis un fichier texte'; +$GLOBALS['__l10n']['Imports a blog or a full Dotclear installation from flat file.'] = 'Importe un blog ou toutes les données depuis un fichier texte.'; +$GLOBALS['__l10n']['Single blog successfully imported.'] = 'Blog importé avec succès.'; +$GLOBALS['__l10n']['Are you sure you want to import a full backup file?'] = 'Êtes-vous certain de vouloir charger un fichier de sauvegarde complet ?'; +$GLOBALS['__l10n']['This will import a single blog backup as new content in the current blog: %s.'] = 'Ceci va charger une sauvegarde de blog comme un nouveau contenu dans le blog en cours : %s.'; +$GLOBALS['__l10n']['Upload a backup file'] = 'Charger un fichier de sauvegarde'; +$GLOBALS['__l10n']['or pick up a local file in your public directory'] = 'ou choisissez un fichier local dans votre répertoire public'; +$GLOBALS['__l10n']['This will reset all the content of your database, except users.'] = 'Ceci va remettre à zéro tout le contenu, sauf les utilisateurs.'; +$GLOBALS['__l10n']['Another file with same name exists.'] = 'Il existe déjà un fichier portant ce nom.'; +$GLOBALS['__l10n']['Failed to extract backup file.'] = 'Erreur à l\'extraction du fichier de sauvegarde.'; +$GLOBALS['__l10n']['No backup in compressed file.'] = 'Pas de sauvegarde dans le fichier compressé.'; +$GLOBALS['__l10n']['WordPress import'] = 'Importer depuis WordPress'; +$GLOBALS['__l10n']['Import a WordPress installation into your current blog.'] = 'Importe un blog WordPress dans votre blog en cours.'; +$GLOBALS['__l10n']['This will import your WordPress content as new content in the current blog: %s.'] = 'Ceci va importer votre blog WordPress comme un nouveau contenu dans le blog en cours : %s.'; +$GLOBALS['__l10n']['We first need some information about your old WordPress installation.'] = 'Nous avons d\'abord besoin de renseignements à propos de votre ancienne installation WordPress.'; +$GLOBALS['__l10n']['WordPress and Dotclear\'s handling of categories are quite different. You can assign several categories to a single post in WordPress. In the Dotclear world, we see it more like "One category, several tags." Therefore Dotclear can only import one category per post and will chose the lowest numbered one. If you want to keep a trace of every category, you can import them as tags, with an optional prefix.'] = 'WordPress et Dotclear gèrent les catégories différemment. Dans WordPress, un article peut être classé dans plusieurs catégories, alors que dans Dotclear, on voit ça plutôt comme "Une catégorie, plusieurs mots-clefs." Dotclear ne peut donc importer qu\'une seule catégorie par article et choisira dans ce cas la première créée. Si vous désirez garder une trace de chaque catégorie, vous pouvez les importer en tant que mots-clefs, avec un préfixe optionnel.'; +$GLOBALS['__l10n']['On the other hand, in WordPress, a post can not be uncategorized, and a default installation has a first category labelised "Uncategorized". If you did not change that category, you can just ignore it while importing your blog, as Dotclear allows you to actually keep your posts uncategorized.'] = 'Par contre, il est impossible dans WordPress de ne pas assigner une catégorie à un article, c\'est pourquoi lors de l\'installation est créée une première catégorie appelée "Non classée". Si vous n\'avez pas modifié cette catégorie, l\'importation peut simplement l\'ignorer. En effet, Dotclear vous permet d\'avoir des articles sans catégorie.'; +$GLOBALS['__l10n']['Ignore the first category:'] = 'Ignorer la première catégorie :'; +$GLOBALS['__l10n']['Import lowest numbered category on posts:'] = 'Importer la catégorie la plus ancienne sur les articles :'; +$GLOBALS['__l10n']['Import all categories as tags:'] = 'Importer les catégories comme des mots-clés :'; +$GLOBALS['__l10n']['Prefix such tags with:'] = 'Préfixer ces mots-clés avec :'; +$GLOBALS['__l10n']['Content filters'] = 'Filtres de contenu'; +$GLOBALS['__l10n']['You may want to process your post and/or comment content with the following filters.'] = 'Vous pouvez utiliser les filtres suivants sur vos articles et/ou commentaires.'; +$GLOBALS['__l10n']['Post content formatter:'] = 'Formatage des articles :'; +$GLOBALS['__l10n']['Comment content formatter:'] = 'Formatage des commentaires :'; +$GLOBALS['__l10n']['WordPress tables not found'] = 'Tables WordPress non trouvées'; +$GLOBALS['__l10n']['No file to read.'] = 'Aucun fichier lisible.'; +$GLOBALS['__l10n']['File is not a DotClear backup.'] = 'Le fichier n\'est pas une sauvegarde DotClear.'; +$GLOBALS['__l10n']['File is not a single blog export.'] = 'Le fichier n\'est pas un fichier d\'export simple.'; +$GLOBALS['__l10n']['File is not a full export.'] = 'Le fichier n\'est pas un fichier d\'export complet.'; +$GLOBALS['__l10n']['ID of "%3$s" does not match on record "%1$s" at line %2$s of backup file.'] = 'L\'ID de "%3$s" ne correspond pas sur l\'enregistrement "%1$s", ligne %2$s du fichier de sauvegarde.'; +$GLOBALS['__l10n']['Please wait...'] = 'Veuillez patienter...'; +$GLOBALS['__l10n']['Maintenance'] = 'Maintenance'; +$GLOBALS['__l10n']['Optimization successful.'] = 'Optimisation réalisée avec succès.'; +$GLOBALS['__l10n']['Comments and trackback counted.'] = 'Commentaires et rétroliens comptés.'; +$GLOBALS['__l10n']['Templates cache directory emptied.'] = 'Répertoire du cache des templates vidé.'; +$GLOBALS['__l10n']['Logs deleted.'] = 'Journaux effacés.'; +$GLOBALS['__l10n']['Indexing entry %d to %d.'] = 'Index des billets %d à %d'; +$GLOBALS['__l10n']['next'] = 'suivant'; +$GLOBALS['__l10n']['Entries index done.'] = 'Index des billets complet.'; +$GLOBALS['__l10n']['Back'] = 'Retour'; +$GLOBALS['__l10n']['Indexing comment %d to %d.'] = 'Index des commentaires %d à %d'; +$GLOBALS['__l10n']['Comments index done.'] = 'Index des commentaires complet.'; +$GLOBALS['__l10n']['Optimize database room'] = 'Optimiser l\'espace de la base de données'; +$GLOBALS['__l10n']['Vacuum tables'] = 'Nettoyer les tables'; +$GLOBALS['__l10n']['Counters'] = 'Compteurs'; +$GLOBALS['__l10n']['Reset comments and ping counters'] = 'Réinitialiser les compteurs des commentaires et rétroliens'; +$GLOBALS['__l10n']['Search engine index'] = 'Index du moteur de recherche'; +$GLOBALS['__l10n']['This may take a very long time'] = 'Ceci peut prendre beaucoup de temps'; +$GLOBALS['__l10n']['Index all posts'] = 'Indexer tous les billets'; +$GLOBALS['__l10n']['Index all comments'] = 'Indexer tous les commentaires'; +$GLOBALS['__l10n']['Vacuum logs'] = 'Vider les journaux'; +$GLOBALS['__l10n']['Delete all logs'] = 'Supprimer tous les journaux'; +$GLOBALS['__l10n']['Empty templates cache directory'] = 'Vider le répertoire du cache des templates'; +$GLOBALS['__l10n']['Empty directory'] = 'Vider le répertoire'; +$GLOBALS['__l10n']['Pages'] = 'Pages'; +$GLOBALS['__l10n']['%d page'] = '%d page'; +$GLOBALS['__l10n']['%d pages'] = '%d pages'; +$GLOBALS['__l10n']['manage pages'] = 'gérer les pages'; +$GLOBALS['__l10n']['Published on'] = 'Publié le'; +$GLOBALS['__l10n']['This page\'s comments feed'] = 'Fil des commentaires de cette page'; +$GLOBALS['__l10n']['You must provide a valid email address.'] = 'Vous devez indiquer une adresse email valide.'; +$GLOBALS['__l10n']['Page title'] = 'Titre de la page'; +$GLOBALS['__l10n']['Page position'] = 'Position de la page'; +$GLOBALS['__l10n']['Publication date'] = 'Date de publication'; +$GLOBALS['__l10n']['No page'] = 'Aucune page'; +$GLOBALS['__l10n']['select this page'] = 'sélectionner cette page'; +$GLOBALS['__l10n']['Are you sure you want to delete selected pages?'] = 'Êtes-vous certain de vouloir supprimer les pages sélectionnées ?'; +$GLOBALS['__l10n']['New page'] = 'Nouvelle page'; +$GLOBALS['__l10n']['Selected pages action:'] = 'Action sur les pages sélectionnées :'; +$GLOBALS['__l10n']['This page does not exist.'] = 'Cette page n\'existe pas.'; +$GLOBALS['__l10n']['Edit page'] = 'Modifier la page'; +$GLOBALS['__l10n']['next page'] = 'page suivante'; +$GLOBALS['__l10n']['previous page'] = 'page précédente'; +$GLOBALS['__l10n']['Are you sure you want to delete this page?'] = 'Êtes-vous certain de vouloir supprimer cette page ?'; +$GLOBALS['__l10n']['Page has been successfully updated.'] = 'La page a été mise à jour avec succès.'; +$GLOBALS['__l10n']['Page has been successfully created.'] = 'La page a été créée avec succès.'; +$GLOBALS['__l10n']['Go to this page on the site'] = 'Aller sur cette page'; +$GLOBALS['__l10n']['Page status:'] = 'État de la page :'; +$GLOBALS['__l10n']['Page position:'] = 'Position de la page :'; +$GLOBALS['__l10n']['Page lang:'] = 'Langue de la page :'; +$GLOBALS['__l10n']['Page password:'] = 'Mot de passe de la page :'; +$GLOBALS['__l10n']['Warning: If you set the URL manually, it may conflict with another page.'] = 'Attention : si vous indiquez l\'URL manuellement, celle-ci peut entrer en conflit avec une autre page.'; +$GLOBALS['__l10n']['Add files to this page'] = 'Ajouter un fichier à la page'; +$GLOBALS['__l10n']['Pings'] = 'Pings'; +$GLOBALS['__l10n']['Pings configuration'] = 'Configuration des pings'; +$GLOBALS['__l10n']['Settings have been successfully updated.'] = 'Paramètres enregistrés avec succès.'; +$GLOBALS['__l10n']['Activate pings extension'] = 'Activer le module de ping'; +$GLOBALS['__l10n']['Service name:'] = 'Nom du service :'; +$GLOBALS['__l10n']['Service URI:'] = 'URI du service :'; +$GLOBALS['__l10n']['error'] = 'erreur'; +$GLOBALS['__l10n']['Test ping services'] = 'Tester les services de ping'; +$GLOBALS['__l10n']['Check all'] = 'Tout cocher.'; +$GLOBALS['__l10n']['Pings:'] = 'Pings :'; +$GLOBALS['__l10n']['Simple menu'] = 'Menu simple'; +$GLOBALS['__l10n']['Menu'] = 'Menu'; +$GLOBALS['__l10n']['All months'] = 'Tous les mois'; +$GLOBALS['__l10n']['All tags'] = 'Tous les mots-clés'; +$GLOBALS['__l10n']['Home'] = 'Accueil'; +$GLOBALS['__l10n']['Archive'] = 'Archive'; +$GLOBALS['__l10n']['Page'] = 'Page'; +$GLOBALS['__l10n']['Tags'] = 'Mots-clés'; +$GLOBALS['__l10n']['User defined'] = 'Saisie libre'; +$GLOBALS['__l10n']['Label'] = 'Libellé'; +$GLOBALS['__l10n']['Recent posts'] = 'Billets récents'; +$GLOBALS['__l10n']['Switch to %s language'] = 'Basculer vers le %s'; +$GLOBALS['__l10n']['Recent Posts from this category'] = 'Billets récents de cette catégorie'; +$GLOBALS['__l10n']['Archives'] = 'Archives'; +$GLOBALS['__l10n']['Posts from %s'] = 'Billets de %s'; +$GLOBALS['__l10n']['Recent posts for %s tag'] = 'Billets récent pour le mot-clé %s'; +$GLOBALS['__l10n']['Label and URL of menu item are mandatory.'] = 'Les libellés et URL sont obligatoires.'; +$GLOBALS['__l10n']['No menu items selected.'] = 'Aucun item de menu sélectionné.'; +$GLOBALS['__l10n']['Label is mandatory.'] = 'Le libellé est obligatoire.'; +$GLOBALS['__l10n']['URL is mandatory.'] = 'l’URL est obligatoire.'; +$GLOBALS['__l10n']['Menu item has been successfully added.'] = 'L\'item de menu a été ajouté avec succès.'; +$GLOBALS['__l10n']['Menu items have been successfully removed.'] = 'Le ou les items de menu ont été supprimés avec succès.'; +$GLOBALS['__l10n']['Menu items have been successfully updated.'] = 'Le ou les items de menu ont été mis à jour avec succès.'; +$GLOBALS['__l10n']['Add item'] = 'Ajouter un item de menu'; +$GLOBALS['__l10n']['Select type'] = 'Sélection du type'; +$GLOBALS['__l10n']['Type of item menu:'] = 'Type d\'item de menu :'; +$GLOBALS['__l10n']['Continue...'] = 'Continuer…'; +$GLOBALS['__l10n']['Select language:'] = 'Sélectionnez la langue :'; +$GLOBALS['__l10n']['Select category:'] = 'Sélectionnez la catégorie :'; +$GLOBALS['__l10n']['Select month (if necessary):'] = 'Sélectionnez le mois (si nécessaire) :'; +$GLOBALS['__l10n']['Select page:'] = 'Sélectionnez la page :'; +$GLOBALS['__l10n']['Select tag (if necessary):'] = 'Sélectionnez le mot clé (si nécessaire) :'; +$GLOBALS['__l10n']['Label of item menu:'] = 'Libellé de l\'item de menu :'; +$GLOBALS['__l10n']['Description of item menu:'] = 'Description de l\'item de menu :'; +$GLOBALS['__l10n']['URL of item menu:'] = 'URL de l\'item de menu :'; +$GLOBALS['__l10n']['Add this item'] = 'Ajouter cet item'; +$GLOBALS['__l10n']['Add an item'] = 'Ajouter un item de menu'; +$GLOBALS['__l10n']['Menu items list'] = 'Liste des items de menu'; +$GLOBALS['__l10n']['Update menu'] = 'Mettre à jour le menu'; +$GLOBALS['__l10n']['Delete selected menu items'] = 'Supprimer les items de menu sélectionnés'; +$GLOBALS['__l10n']['Are you sure you want to remove selected menu items?'] = 'Êtes-vous sûr de vouloir supprimer les items de menu sélectionnés ?'; +$GLOBALS['__l10n']['Currently no menu items'] = 'Aucun item de menu pour l\'instant'; +$GLOBALS['__l10n']['Tags:'] = 'Mots-clés :'; +$GLOBALS['__l10n']['Are you sure you want to remove this %s?'] = 'Êtes vous certain de vouloir supprimer ce %s ?'; +$GLOBALS['__l10n']['Add a %s to this entry'] = 'Ajouter un %s à ce billet'; +$GLOBALS['__l10n']['Choose from list'] = 'Choisir depuis la liste'; +$GLOBALS['__l10n']['all'] = 'tous'; +$GLOBALS['__l10n']['Tag'] = 'Mot-clé'; +$GLOBALS['__l10n']['used in %e - frequency %p%'] = 'utilisé dans %e - fréquence %p%'; +$GLOBALS['__l10n']['entry'] = 'billet'; +$GLOBALS['__l10n']['entries'] = 'billets'; +$GLOBALS['__l10n']['Enter tags separated by coma'] = 'Entrer les mots-clés séparés par une virgule'; +$GLOBALS['__l10n']['Add tags'] = 'Ajouter des mots-clés'; +$GLOBALS['__l10n']['Remove tags'] = 'Supprimer des mots-clés'; +$GLOBALS['__l10n']['Add tags to entries'] = 'Ajouter des mots-clés aux billets'; +$GLOBALS['__l10n']['Tags to add:'] = 'Mots-clés à ajouter :'; +$GLOBALS['__l10n']['Remove selected tags from entries'] = 'Supprimer les mots-clés sélectionnés des billets'; +$GLOBALS['__l10n']['No tags for selected entries'] = 'Aucun mot-clé pour les billets sélectionnés'; +$GLOBALS['__l10n']['Following tags have been found in selected entries:'] = 'Les mots-clés suivants ont été trouvés dans les billets sélectionnés :'; +$GLOBALS['__l10n']['short'] = 'court'; +$GLOBALS['__l10n']['extended'] = 'étendu'; +$GLOBALS['__l10n']['Tags list format:'] = 'Format de la liste des mots-clés :'; +$GLOBALS['__l10n']['This tag\'s comments Atom feed'] = 'Fil Atom des commentaires de ce mot-clé'; +$GLOBALS['__l10n']['This tag\'s entries Atom feed'] = 'Fil Atom des billets de ce mot-clé'; +$GLOBALS['__l10n']['Limit (empty means no limit):'] = 'Limite (laisser vide pour aucune limite)'; +$GLOBALS['__l10n']['Entries count'] = 'Nombre de billets'; +$GLOBALS['__l10n']['Tag name'] = 'Nom du mot-clé'; +$GLOBALS['__l10n']['Link to all tags:'] = 'Lien vers tous les mots-clés :'; +$GLOBALS['__l10n']['Edit tag'] = 'Modifier le mot-clé'; +$GLOBALS['__l10n']['Tag has been successfully renamed'] = 'Mot-clé renommé avec succès'; +$GLOBALS['__l10n']['Back to tags list'] = 'Retour à la liste des mots-clés'; +$GLOBALS['__l10n']['Actions for this tag'] = 'Actions pour ce mot-clé'; +$GLOBALS['__l10n']['Edit tag name:'] = 'Renommer ce mot-clé :'; +$GLOBALS['__l10n']['Rename'] = 'Renommer'; +$GLOBALS['__l10n']['Delete this tag:'] = 'Supprimer ce mot-clé :'; +$GLOBALS['__l10n']['List of entries with this tag'] = 'Liste des billets avec ce mot-clé'; +$GLOBALS['__l10n']['Tag has been successfully removed'] = 'Mot-clé supprimé avec succès'; +$GLOBALS['__l10n']['No tags on this blog.'] = 'Aucun mot-clé sur ce blog'; +$GLOBALS['__l10n']['Theme Editor'] = 'Éditeur de thème'; +$GLOBALS['__l10n']['No file'] = 'Aucun fichier'; +$GLOBALS['__l10n']['File does not exist.'] = 'Le fichier n\'existe pas.'; +$GLOBALS['__l10n']['File %s is not readable'] = 'Le fichier %s n\'est pas lisible'; +$GLOBALS['__l10n']['Unable to write file %s. Please check your theme files and folders permissions.'] = 'Impossible d\'écrire le fichier %s. Veuillez vérifier les permissions des fichiers et répertoires de votre thème.'; +$GLOBALS['__l10n']['Saving document...'] = 'Sauvegarde du document...'; +$GLOBALS['__l10n']['Document saved'] = 'Document sauvegardé'; +$GLOBALS['__l10n']['An error occurred:'] = 'Une erreur s\'est produite :'; +$GLOBALS['__l10n']['Your current theme on this blog is "%s".'] = 'Le thème utilisé actuellement sur votre blog est "%s".'; +$GLOBALS['__l10n']['You can\'t edit default theme.'] = 'Vous ne pouvez pas modifier le thème par défaut.'; +$GLOBALS['__l10n']['Please select a file to edit.'] = 'Veuillez sélectionner un fichier à modifier.'; +$GLOBALS['__l10n']['File editor'] = 'Éditeur de fichier'; +$GLOBALS['__l10n']['Editing file %s'] = 'Modification du fichier %s'; +$GLOBALS['__l10n']['This file is not writable. Please check your theme files permissions.'] = 'Ce fichier ne peut pas être modifié. Veuillez vérifier les permissions des fichiers de votre thème.'; +$GLOBALS['__l10n']['Templates files'] = 'Fichiers template'; +$GLOBALS['__l10n']['CSS files'] = 'Fichiers CSS'; +$GLOBALS['__l10n']['JavaScript files'] = 'Fichiers JavaScript'; +$GLOBALS['__l10n']['Preferences successfully updated'] = 'Préférences mises à jour avec succès'; +$GLOBALS['__l10n']['Preferences definition successfully updated'] = 'Définition des préférences mise à jour avec succès'; +$GLOBALS['__l10n']['user preferences'] = 'préférences utilisateur'; +$GLOBALS['__l10n']['global preferences'] = 'préférences globales'; +$GLOBALS['__l10n']['Presentation widgets'] = 'Widgets de présentation'; +$GLOBALS['__l10n']['Search engine'] = 'Moteur de recherche'; +$GLOBALS['__l10n']['Navigation links'] = 'Liens de navigation'; +$GLOBALS['__l10n']['Selected entries'] = 'Billets sélectionnés'; +$GLOBALS['__l10n']['Best of me'] = 'À retenir'; +$GLOBALS['__l10n']['Blog languages'] = 'Langues du blog'; +$GLOBALS['__l10n']['With entries counts'] = 'Afficher le nombre de billets'; +$GLOBALS['__l10n']['Subscribe links'] = 'Liens d\'abonnement'; +$GLOBALS['__l10n']['Subscribe'] = 'S\'abonner'; +$GLOBALS['__l10n']['Feeds type:'] = 'Types de fil :'; +$GLOBALS['__l10n']['Feed reader'] = 'Lecteur de fils de nouvelles'; +$GLOBALS['__l10n']['Somewhere else'] = 'Ailleurs'; +$GLOBALS['__l10n']['Entries limit:'] = 'Nombre de billets maximum :'; +$GLOBALS['__l10n']['Text'] = 'Texte'; +$GLOBALS['__l10n']['Text:'] = 'Texte :'; +$GLOBALS['__l10n']['Last entries'] = 'Derniers billets'; +$GLOBALS['__l10n']['Uncategorized'] = 'Non catégorisé'; +$GLOBALS['__l10n']['Tag:'] = 'Mot-clé :'; +$GLOBALS['__l10n']['Last comments'] = 'Derniers commentaires'; +$GLOBALS['__l10n']['Comments limit:'] = 'Nombre de commentaires maximum :'; +$GLOBALS['__l10n']['This blog\'s entries %s feed'] = 'Fil %s des billets de ce blog'; +$GLOBALS['__l10n']['This blog\'s comments %s feed'] = 'Fil %s des commentaires de ce blog'; +$GLOBALS['__l10n']['Entries feed'] = 'Fil des billets'; +$GLOBALS['__l10n']['Comments feed'] = 'Fil des commentaires'; +$GLOBALS['__l10n']['navigation'] = 'navigation'; +$GLOBALS['__l10n']['extra'] = 'extra'; +$GLOBALS['__l10n']['custom'] = 'supplémentaire'; +$GLOBALS['__l10n']['Widgets'] = 'Widgets'; +$GLOBALS['__l10n']['Are you sure you want to reset sidebars?'] = 'Êtes-vous certain de vouloir réinitialiser les bandeaux ?'; +$GLOBALS['__l10n']['Available widgets'] = 'Widgets disponibles'; +$GLOBALS['__l10n']['Append to:'] = 'Ajouter à :'; +$GLOBALS['__l10n']['Add widgets to sidebars'] = 'Ajouter les widgets aux bandeaux'; +$GLOBALS['__l10n']['Navigation sidebar'] = 'Bandeau de navigation'; +$GLOBALS['__l10n']['Extra sidebar'] = 'Bandeau d\'extra'; +$GLOBALS['__l10n']['Custom sidebar'] = 'Bandeau supplémentaire'; +$GLOBALS['__l10n']['Update sidebars'] = 'Mettre à jour les bandeaux'; +$GLOBALS['__l10n']['Reset sidebars'] = 'Réinitialiser les bandeaux'; +$GLOBALS['__l10n']['Use of widgets'] = 'Utilisation des widgets'; +$GLOBALS['__l10n']['Widgets may be used to add various blocks of content to be displayed on your public pages. To add a widget, drag it from the Available widgets list on the left to one of the sidebars on the right of this page. You can order your widgets in a sidebar by dragging them up or down. You must update sidebars to apply your changes.'] = 'Les widgets sont utilisés pour ajouter des blocs de contenus divers dans vos pages publiques. Pour ajouter un widget, faites-le glisser depuis la liste des Widgets disponibles à gauche jusqu\'à l\'un des bandeaux sur la droite de la page. Vous pouvez ré-ordonner vos widgets dans un bandeau en le faisant glisser vers le haut ou vers le bas. Vous devez mettre à jour les bandeaux pour enregistrer vos modifications.'; +$GLOBALS['__l10n']['Once included in a sidebar, widgets have configuration options that you can reach by clicking on the + sign next to their name.'] = 'Une fois inclus dans un bandeau, les widgets ont généralement des options que vous pouvez configurer. Cliquez sur le signe + à côté de leur nom pour y accéder.'; +$GLOBALS['__l10n']['Reset sidebars to get back to default widgets installation.'] = 'Réinitialisez les bandeaux pour retrouver les widgets par défaut de votre installation.'; +$GLOBALS['__l10n']['Widget templates tags'] = 'Marqueurs de template des widgets'; +$GLOBALS['__l10n']['If you are allowed to edit your theme templates, you can directly add widgets as templates tags, with their own configuration.'] = 'Si vous avez le droit de modifier les templates de votre thème, vous pouvez directement ajouter des widgets à l\'aide de marqueurs de template, avec leur propre configuration.'; +$GLOBALS['__l10n']['To add a widget in your template, you need to write code like this:'] = 'Pour ajouter un widget dans votre template, vous devez écrire un code comme ceci :'; +$GLOBALS['__l10n']['Widget ID'] = 'Identifiant du widget'; +$GLOBALS['__l10n']['Setting name'] = 'Nom du paramètre'; +$GLOBALS['__l10n']['Setting value'] = 'Valeur du paramètre'; +$GLOBALS['__l10n']['Here are the following available widgets for your blog:'] = 'Voici les widgets disponibles pour votre blog :'; +$GLOBALS['__l10n']['Widget ID:'] = 'Identifiant du widget :'; +$GLOBALS['__l10n']['No setting for this widget'] = 'Aucun paramètre pour ce widget'; +$GLOBALS['__l10n']['Setting name:'] = 'Nom du paramètre :'; +$GLOBALS['__l10n']['No widget.'] = 'Aucun widget.'; +$GLOBALS['__l10n']['order'] = 'ordre'; +$GLOBALS['__l10n']['Remove widget'] = 'Supprimer le widget'; +?> \ No newline at end of file diff --git a/v2/dotclear/locales/fr/plugins.po b/v2/dotclear/locales/fr/plugins.po new file mode 100644 index 0000000..586b32e --- /dev/null +++ b/v2/dotclear/locales/fr/plugins.po @@ -0,0 +1,1769 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-13 10:46+0200\n" +"PO-Revision-Date: \n" +"Last-Translator: xave \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" + +msgid "no" +msgstr "non" + +msgid "yes" +msgstr "oui" + +msgid "Configuration successfully updated" +msgstr "Configuration mise à jour avec succès" + +msgid "Settings definition successfully updated" +msgstr "Définition des paramètres mise à jour avec succès" + +msgid "blog settings" +msgstr "paramètres du blog" + +msgid "Value" +msgstr "Valeur" + +msgid "Description" +msgstr "Description" + +msgid "Goto:" +msgstr "Aller à :" + +msgid "Ok" +msgstr "Valider" + +msgid "global settings" +msgstr "paramètres globaux" + +msgid "Akismet spam filter" +msgstr "Filtre de spam Akismet" + +#, php-format +msgid "Filtered by %s." +msgstr "Filtré par %s" + +msgid "Akismet API key:" +msgstr "Clé de l'interface Akismet :" + +msgid "API key verified" +msgstr "Clé vérifiée" + +msgid "API key not verified" +msgstr "Clé non vérifiée" + +msgid "Get your own API key" +msgstr "Obtenez votre clé Akismet" + +msgid "Antispam" +msgstr "Antispam" + +msgid "Delete junk comments older than" +msgstr "Supprimer les commentaires indésirables après" + +msgid "days" +msgstr "jours" + +msgid "IP Blacklist / Whitelist Filter" +msgstr "Liste noire / Liste blanche IP" + +#, php-format +msgid "Filtered by %1$s with rule %2$s." +msgstr "Filtré par %1$s avec la règle %2$s." + +msgid "IP address has been successfully added." +msgstr "L'adresse IP a été ajoutée avec succès." + +msgid "IP addresses have been successfully removed." +msgstr "L'adresse IP a été supprimée avec succès." + +msgid "Blacklist" +msgstr "Liste noire" + +msgid "Whitelist" +msgstr "Liste blanche" + +msgid "Add an IP address: " +msgstr "Ajouter une adresse IP" + +msgid "Global IP" +msgstr "IP globale" + +msgid "Add" +msgstr "Ajouter" + +msgid "No IP address in list." +msgstr "Aucune adresse IP dans la liste." + +msgid "IP list" +msgstr "Liste d'IP" + +msgid "Checks sender IP address against DNSBL servers" +msgstr "Vérifie l'adresse IP de l'auteur sur des serveurs DNSBL" + +#, php-format +msgid "Filtered by %1$s with server %2$s." +msgstr "Filtré par %1$s avec le serveur %2$s." + +msgid "IP Lookup servers" +msgstr "Serveurs de résolution d'IP" + +msgid "Add here a coma separated list of servers." +msgstr "Ajoutez ici une liste de serveurs, séparés par des virgules." + +msgid "Checks links in comments against surbl.org" +msgstr "Vérifie les liens dans les commentaires sur surbl.org" + +msgid "Words Blacklist" +msgstr "Liste de termes interdits" + +#, php-format +msgid "Filtered by %1$s with word %2$s." +msgstr "Filtré par %1$s avec le terme %2$s." + +msgid "Words have been successfully added." +msgstr "Les mots ont été ajouté avec succès." + +msgid "Word has been successfully added." +msgstr "Le mot a été ajouté avec succès." + +msgid "Words have been successfully removed." +msgstr "Les mots ont été supprimés avec succès." + +msgid "Add a word " +msgstr "Ajouter un mot" + +msgid "Global word" +msgstr "Mot global" + +msgid "No word in list." +msgstr "Aucun mot dans la liste." + +msgid "List of bad words" +msgstr "Liste de mots interdits" + +msgid "Delete selected words" +msgstr "Supprimer les mots sélectionnés" + +msgid "Create default wordlist" +msgstr "Créer une liste par défaut" + +msgid "This word exists" +msgstr "Ce mot existe" + +msgid "No description" +msgstr "Aucune description" + +#, php-format +msgid "Filtered by %1$s (%2$s)" +msgstr "Filtré par %1$s (%2$s)" + +msgid "Unknown filter." +msgstr "Filtre inconnu" + +msgid "This comment is a spam:" +msgstr "Ce commentaire est un indésirable :" + +#, php-format +msgid "(including %d spam comment)" +msgstr "(dont %d commentaire indésirable)" + +#, php-format +msgid "(including %d spam comments)" +msgstr "(dont %d commentaires indésirables)" + +msgid "Spam moderation" +msgstr "Modération des indésirables" + +msgid "Spam" +msgstr "Spam" + +msgid "Ham" +msgstr "Non spam" + +msgid "Filter does not exist." +msgstr "Le filtre n'existe pas." + +msgid "Filter has no user interface." +msgstr "Le filtre n'a pas d'interface utilisateur." + +#, php-format +msgid "%s configuration" +msgstr "%s configuration" + +msgid "Information" +msgstr "Informations" + +msgid "Spam comments have been successfully deleted." +msgstr "Les commentaires indésirables ont été supprimés avec succès." + +msgid "Junk comments:" +msgstr "Commentaires indésirables :" + +msgid "Published comments:" +msgstr "Commentaires publiés :" + +msgid "Delete all spams" +msgstr "Supprimer tous les indésirables" + +#, php-format +msgid "All spam comments older than %s day(s) will be automatically deleted." +msgstr "" +"Tous les indésirables de plus de %s jour(s) seront automatiquement supprimés." + +msgid "You can modify this duration in " +msgstr "Vous pouvez modifier cet intervalle dans" + +msgid "Blog preferences" +msgstr "Préférences du blog" + +msgid "Filters configuration has been successfully saved." +msgstr "La configuration des filtres a été enregistrée avec succès." + +msgid "Available spam filters" +msgstr "Filtres disponibles" + +msgid "Order" +msgstr "Ordre" + +msgid "Active" +msgstr "Actif" + +msgid "Auto Del." +msgstr "Suppr. Auto" + +msgid "Filter name" +msgstr "Nom du filtre" + +msgid "Filter configuration" +msgstr "Configuration du filtre" + +msgid "position" +msgstr "position" + +msgid "Syndication" +msgstr "Syndication" + +msgid "Junk comments RSS feed" +msgstr "Fil RSS des commentaires indésirables" + +msgid "Published comments RSS feed" +msgstr "Fil RSS des commentaires publiés" + +msgid "Attachments" +msgstr "Fichiers attachés" + +msgid "remove" +msgstr "supprimer" + +msgid "No attachment." +msgstr "Pas de fichier attaché" + +msgid "Add files to this entry" +msgstr "Ajouter des fichiers à la page" + +msgid "Blogroll" +msgstr "Liens / Blogroll" + +msgid "manage blogroll" +msgstr "gérer la liste de liens" + +msgid "Links" +msgstr "Liens" + +msgid "All categories" +msgstr "Toutes les catégories" + +msgid "Home page only" +msgstr "Page d'accueil uniquement" + +msgid "You must provide a link title" +msgstr "Vous devez indiquer un titre de lien" + +msgid "You must provide a link URL" +msgstr "Vous devez indiquer une URL" + +msgid "You need to provide a XBEL or OPML file." +msgstr "Vous devez fournir un fichier XBEL ou OPML." + +msgid "File is not in XML format." +msgstr "Le fichier n'est pas au format XML." + +msgid "No such link or title" +msgstr "Lien ou catégorie inexistant" + +msgid "Return to blogroll" +msgstr "Retour à la liste des liens" + +msgid "Category has been successfully updated" +msgstr "Catégorie mise à jour avec succès" + +msgid "Edit category" +msgstr "Modifier une catégorie" + +msgid "Link has been successfully updated" +msgstr "Lien mis à jour avec succès" + +msgid "Edit link" +msgstr "Modifier le lien" + +msgid "XFN" +msgstr "XFN" + +msgid "_xfn_Me" +msgstr "Moi" + +msgid "_xfn_Another link for myself" +msgstr "Autre lien m'appartenant" + +msgid "_xfn_Friendship" +msgstr "Amitié" + +msgid "_xfn_Contact" +msgstr "Contact" + +msgid "_xfn_Acquaintance" +msgstr "Connaissance" + +msgid "_xfn_Friend" +msgstr "Ami(e)" + +msgid "_xfn_Physical" +msgstr "Physique" + +msgid "_xfn_Met" +msgstr "Rencontré(e)" + +msgid "_xfn_Professional" +msgstr "Professionnel" + +msgid "_xfn_Co-worker" +msgstr "Camarade de travail" + +msgid "_xfn_Colleague" +msgstr "Collègue" + +msgid "_xfn_Geographical" +msgstr "Géographique" + +msgid "_xfn_Co-resident" +msgstr "Colocataire" + +msgid "_xfn_Neighbor" +msgstr "Voisin(e)" + +msgid "_xfn_Family" +msgstr "Famille" + +msgid "_xfn_Child" +msgstr "Enfant" + +msgid "_xfn_Parent" +msgstr "Parent" + +msgid "_xfn_Sibling" +msgstr "Frère/Soeur" + +msgid "_xfn_Spouse" +msgstr "Époux/Épouse" + +msgid "_xfn_Kin" +msgstr "Famille" + +msgid "_xfn_Romantic" +msgstr "Romantique" + +msgid "_xfn_Muse" +msgstr "Muse" + +msgid "_xfn_Crush" +msgstr "Bluette" + +msgid "_xfn_Date" +msgstr "Petit(e) ami(e)" + +msgid "_xfn_Sweetheart" +msgstr "Ami(e)" + +msgid "Nothing to import" +msgstr "Rien à importer" + +msgid "Import operation cancelled." +msgstr "Importation annulée." + +msgid "Items order has been successfully updated" +msgstr "L'ordre des éléments a été mis à jour avec succès" + +msgid "Items have been successfully removed." +msgstr "Éléments supprimés avec succès." + +msgid "Link has been successfully created." +msgstr "Lien créé avec succès." + +msgid "category has been successfully created." +msgstr "Catégorie mise à jour avec succès." + +msgid "links have been successfully imported." +msgstr "Liens importés avec succès." + +msgid "URL" +msgstr "URL" + +msgid "Lang" +msgstr "Langue" + +msgid "select this link" +msgstr "sélectionner ce lien" + +msgid "Delete selected links" +msgstr "Supprimer les liens sélectionnés" + +msgid "Are you sure you want to delete selected links?" +msgstr "Êtes-vous certain de vouloir supprimer les liens sélectionnés ?" + +msgid "The link list is empty." +msgstr "La liste des liens est vide." + +msgid "Add a new link" +msgstr "Ajouter un nouveau lien" + +msgid "Add a category" +msgstr "Ajouter une catégorie" + +msgid "Import links" +msgstr "Importer des liens" + +msgid "OPML or XBEL File:" +msgstr "Fichier OPML ou XBEL :" + +msgid "Import" +msgstr "Importer" + +msgid "Light linear gradient" +msgstr "Dégradé linéaire clair" + +msgid "Medium linear gradient" +msgstr "Dégradé linéaire moyen" + +msgid "Dark linear gradient" +msgstr "Dégradé linéaire foncé" + +msgid "Solid color" +msgstr "Couleur unie" + +msgid "Custom..." +msgstr "Personnalisé..." + +msgid "Blowup configuration" +msgstr "Configuration Blowup" + +msgid "Predefined styles" +msgstr "Styles prédéfinis" + +msgid "Apply code" +msgstr "Appliquer le code" + +msgid "Choose a predefined style" +msgstr "Sélectionnez un style prédéfini" + +msgid "" +"For the following reasons, images cannot be created. You won't be able to " +"change some background properties." +msgstr "" +"Pour les raisons qui suivent, les images ne pouvant être créées, vous ne " +"pourrez pas changer certaines propriétés." + +msgid "Theme configuration has been successfully updated." +msgstr "La configuration du thème a été mise à jour avec succès." + +msgid "General" +msgstr "Général" + +msgid "Background color:" +msgstr "Couleur de fond :" + +msgid "Background color fill:" +msgstr "Remplissage de la couleur de fond :" + +msgid "Main text font:" +msgstr "Police du texte principal :" + +msgid "Main text font size:" +msgstr "Taille du texte principal :" + +msgid "Main text color:" +msgstr "Couleur du texte principal :" + +msgid "Text line height:" +msgstr "Hauteur des lignes :" + +msgid "Links color:" +msgstr "Couleur des liens :" + +msgid "Visited links color:" +msgstr "Couleurs des liens visités :" + +msgid "Focus links color:" +msgstr "Couleur des liens survolés :" + +msgid "Page top" +msgstr "Haut de page" + +msgid "Prelude color:" +msgstr "Couleur du prélude :" + +msgid "Hide main title" +msgstr "Cacher le titre principal" + +msgid "Main title font:" +msgstr "Police du titre principal :" + +msgid "Main title font size:" +msgstr "Taille du titre principal :" + +msgid "Main title color:" +msgstr "Couleur du titre principal :" + +msgid "Main title alignment:" +msgstr "Alignement du titre principal :" + +msgid "center" +msgstr "centre" + +msgid "left" +msgstr "gauche" + +msgid "right" +msgstr "droite" + +msgid "Main title position (x:y)" +msgstr "Position du titre principal (x:y)" + +msgid "Top image" +msgstr "Image d'en-tête" + +msgid "Choose \"Custom...\" to upload your own image." +msgstr "Choisissez \"Personnalisé...\" pour déposer votre propre image." + +msgid "Add your image:" +msgstr "Ajouter votre image :" + +#, php-format +msgid "JPEG or PNG file, 800 pixels wide, maximum size %s" +msgstr "Fichier JPEG ou PNG, 800 pixels de large et de taille maximale %s" + +msgid "Sidebar" +msgstr "Bandeau" + +msgid "Sidebar position:" +msgstr "Position du bandeau" + +msgid "Sidebar text font:" +msgstr "Police du texte du bandeau :" + +msgid "Sidebar text font size:" +msgstr "Taille du texte du bandeau :" + +msgid "Sidebar text color:" +msgstr "Couleur du texte du bandeau :" + +msgid "Sidebar titles font:" +msgstr "Police des titres du bandeau :" + +msgid "Sidebar titles font size:" +msgstr "Taille des titres du bandeau :" + +msgid "Sidebar titles color:" +msgstr "Couleur des titres du bandeau :" + +msgid "Sidebar 2nd level titles font:" +msgstr "Police des titres de niveau 2 du bandeau :" + +msgid "Sidebar 2nd level titles font size:" +msgstr "Taille des titres de niveau 2 du bandeau :" + +msgid "Sidebar 2nd level titles color:" +msgstr "Couleur des titres de niveau 2 du bandeau :" + +msgid "Sidebar lines color:" +msgstr "Couleur des lignes du bandeau :" + +msgid "Sidebar links color:" +msgstr "Couleur des liens du bandeau :" + +msgid "Sidebar visited links color:" +msgstr "Couleurs des liens vistés du bandeau :" + +msgid "Sidebar focus links color:" +msgstr "Couleurs des liens survolés du bandeau :" + +msgid "Date title font:" +msgstr "Police des titres de dates :" + +msgid "Date title font size:" +msgstr "Taille des titres de dates :" + +msgid "Date title color:" +msgstr "Couleur des titres de date :" + +msgid "Entry title font:" +msgstr "Police des titres de billet :" + +msgid "Entry title font size:" +msgstr "Taille des titres des billets :" + +msgid "Entry title color:" +msgstr "Couleur des titres des billets :" + +msgid "Comment background color:" +msgstr "Couleur du fond des commentaires :" + +msgid "Comment text color:" +msgstr "Couleur du texte des commentaires :" + +msgid "My comment background color:" +msgstr "Couleur du fond de mes commentaires :" + +msgid "My comment text color:" +msgstr "Couleur du texte de mes commentaires :" + +msgid "Footer" +msgstr "Pied de page" + +msgid "Footer font:" +msgstr "Police du pied de page :" + +msgid "Footer font size:" +msgstr "Taille du texte du pied de page :" + +msgid "Footer color:" +msgstr "Couleur du texte du pied de page :" + +msgid "Footer links color:" +msgstr "Couleur des liens du pied de page :" + +msgid "Footer background color:" +msgstr "Couleur de fond du pied de page :" + +msgid "Additional CSS" +msgstr "Style additionnel" + +msgid "Configuration import / export" +msgstr "Import / export de configuration" + +msgid "" +"You can share your configuration using the following code. To apply a " +"configuration, paste the code, click on \"Apply code\" and save." +msgstr "" +"Vous pouvez partager votre configuration en utilisant le code suivant. Pour " +"appliquer une configuration, copiez le code, cliquez sur \"Appliquer le code" +"\" et enregistrez." + +msgid "Copy this code:" +msgstr "Copier ce code :" + +msgid "default" +msgstr "par défaut" + +msgid "" +"At least one of the following functions is not available: " +"imagecreatetruecolor, imagepng & imagecreatefrompng." +msgstr "" +"L'une de ces fonctions au moins est indisponible : imagecreatetruecolor, " +"imagepng & imagecreatefrompng." + +msgid "The 'public' directory does not exist." +msgstr "Le répertoire 'public' n'existe pas." + +#, php-format +msgid "The '%s' directory cannot be modified." +msgstr "Le répertoire '%s' ne peut pas être modifié." + +msgid "Unable to create images." +msgstr "Impossible de créer les images." + +msgid "Invalid file type." +msgstr "Type de fichier non valide." + +msgid "Uploaded image is not 800 pixels wide." +msgstr "L'image fournie de fait pas 800 pixels de large." + +msgid "Unable to open image." +msgstr "Impossible d'ouvrir l'image." + +msgid "Checks trackback source for a link to the post" +msgstr "Vérifie si la source du rétrolien possède un lien vers le billet" + +msgid "Import/Export" +msgstr "Import/Export" + +msgid "Flat file export" +msgstr "Exporter un fichier texte" + +msgid "Exports a blog or a full Dotclear installation to flat file." +msgstr "" +"Exporte un blog ou toutes les données de Dotclear dans un fichier texte." + +msgid "Export file not found." +msgstr "Fichier d'export non trouvé." + +msgid "Failed to compress export file." +msgstr "Erreur à la compression du fichier d'exportation" + +msgid "Single blog" +msgstr "Un seul blog" + +#, php-format +msgid "This will create an export of your current blog: %s" +msgstr "Ceci va exporter le contenu du blog en cours : %s." + +msgid "Compress file" +msgstr "Compresser le fichier" + +msgid "You may also want to download your media directory as a zip file" +msgstr "" +"Vous pouvez également télécharger votre répertoire de médias au format zip." + +msgid "Export" +msgstr "Exporter" + +msgid "Multiple blogs" +msgstr "Plusieurs blogs" + +msgid "This will create an export of all the content of your database." +msgstr "L'intégralité de votre base de données sera exportée." + +msgid "Congratulation!" +msgstr "Félicitations !" + +msgid "Your blog has been successfully imported. Welcome on Dotclear 2!" +msgstr "Votre blog a été importé avec succès. Bienvenue sur Dotclear 2 !" + +msgid "Why don't you blog this now?" +msgstr "Pourquoi ne pas le bloguer maintenant ?" + +msgid "or" +msgstr "ou" + +msgid "visit your dashboard" +msgstr "vous rendre sur votre tableau de bord" + +msgid "Dotclear 1.2 import" +msgstr "Importer depuis Dotclear 1.2" + +msgid "Import a Dotclear 1.2 installation into your current blog." +msgstr "Importe un blog Dotclear 1.2 dans votre blog en cours." + +#, php-format +msgid "" +"This will import your Dotclear 1.2 content as new content in the current " +"blog: %s." +msgstr "" +"Ceci va importer votre blog Dotclear 1.2 comme un nouveau contenu dans le " +"blog en cours : %s." + +msgid "" +"Please note that this process will empty your categories, blogroll, entries " +"and comments on the current blog." +msgstr "" +"Veuillez noter que cette opération va vider vos catégories, liens, billets " +"et commentaires sur le blog en cours." + +msgid "Depending on the size of your blog, it could take a few minutes." +msgstr "Selon la taille de votre blog, ceci peut prendre quelques minutes." + +msgid "General information" +msgstr "Informations générales" + +msgid "Import my blog now" +msgstr "Importer mon blog" + +msgid "" +"We first need some information about your old Dotclear 1.2 installation." +msgstr "" +"Nous avons d'abord besoin de renseignements à propos de votre ancienne " +"installation Dotclear 1.2." + +msgid "Entries import options" +msgstr "Options d'importation des billets" + +msgid "Number of entries to import at once:" +msgstr "Nombre de billets à importer à chaque étape :" + +msgid "Importing users" +msgstr "Importe les utilisateurs" + +msgid "Importing categories" +msgstr "Importe les catégories" + +msgid "Importing blogroll" +msgstr "Importe les liens" + +#, php-format +msgid "Importing entries from %d to %d / %d" +msgstr "Importation des billets %d à %d sur %d" + +msgid "Please read carefully" +msgstr "Merci de lire attentivement" + +msgid "" +"Every newly imported user has received a random password and will need to " +"ask for a new one by following the \"I forgot my password\" link on the " +"login page (Their registered email address has to be valid.)" +msgstr "" +"Chaque utilisateur nouvellement importé a reçu un mot de passe aléatoire et " +"devra en demander un nouveau en suivant le lien \"J'ai oublié mon mot de " +"passe\" sur la page de connexion à l'interface d'aministration." + +#, php-format +msgid "" +"Please note that Dotclear 2 has a new URL layout. You can avoid broken links " +"by installing DC1 redirect plugin and activate it in your " +"blog configuration." +msgstr "" +"Veuillez noter que Dotclear 2 a un nouveau format d'URL. Vous pouvez éviter " +"les liens morts en installant le plugin DC1 redirect et " +"en l'activant dans la configuration de votre blog." + +msgid "next step" +msgstr "étape suivante" + +msgid "Dotclear tables not found" +msgstr "Tables Dotclear non trouvées" + +msgid "Feed import" +msgstr "Importer depuis un flux" + +msgid "Imports a feed as new entries." +msgstr "Importe un flux comme nouveaux billets." + +msgid "Cannot retrieve feed URL." +msgstr "Impossible d'atteindre l'URL du fil." + +msgid "No items in feed." +msgstr "Aucun élément dans le feed." + +msgid "Content successfully imported." +msgstr "Contenu importé avec succès." + +#, php-format +msgid "" +"This will import a feed (RSS or Atom) a as new content in the current blog: " +"%s." +msgstr "" +"Ceci va importer un fil (RSS ou Atom) comme un nouveau contenu dans le blog " +"en cours : %s." + +msgid "Feed URL:" +msgstr "URL du fil" + +msgid "Flat file import" +msgstr "Importer depuis un fichier texte" + +msgid "Imports a blog or a full Dotclear installation from flat file." +msgstr "Importe un blog ou toutes les données depuis un fichier texte." + +msgid "Single blog successfully imported." +msgstr "Blog importé avec succès." + +msgid "Are you sure you want to import a full backup file?" +msgstr "" +"Êtes-vous certain de vouloir charger un fichier de sauvegarde complet ?" + +#, php-format +msgid "" +"This will import a single blog backup as new content in the current blog: %s." +msgstr "" +"Ceci va charger une sauvegarde de blog comme un nouveau contenu dans le blog " +"en cours : %s." + +msgid "Upload a backup file" +msgstr "Charger un fichier de sauvegarde" + +msgid "or pick up a local file in your public directory" +msgstr "ou choisissez un fichier local dans votre répertoire public" + +msgid "This will reset all the content of your database, except users." +msgstr "Ceci va remettre à zéro tout le contenu, sauf les utilisateurs." + +msgid "Another file with same name exists." +msgstr "Il existe déjà un fichier portant ce nom." + +msgid "Failed to extract backup file." +msgstr "Erreur à l'extraction du fichier de sauvegarde." + +msgid "No backup in compressed file." +msgstr "Pas de sauvegarde dans le fichier compressé." + +msgid "WordPress import" +msgstr "Importer depuis WordPress" + +msgid "Import a WordPress installation into your current blog." +msgstr "Importe un blog WordPress dans votre blog en cours." + +#, php-format +msgid "" +"This will import your WordPress content as new content in the current blog: " +"%s." +msgstr "" +"Ceci va importer votre blog WordPress comme un nouveau contenu dans le blog " +"en cours : %s." + +msgid "We first need some information about your old WordPress installation." +msgstr "" +"Nous avons d'abord besoin de renseignements à propos de votre ancienne " +"installation WordPress." + +msgid "" +"WordPress and Dotclear's handling of categories are quite different. You can " +"assign several categories to a single post in WordPress. In the Dotclear " +"world, we see it more like \"One category, several tags.\" Therefore " +"Dotclear can only import one category per post and will chose the lowest " +"numbered one. If you want to keep a trace of every category, you can import " +"them as tags, with an optional prefix." +msgstr "" +"WordPress et Dotclear gèrent les catégories différemment. Dans WordPress, un " +"article peut être classé dans plusieurs catégories, alors que dans Dotclear, " +"on voit ça plutôt comme \"Une catégorie, plusieurs mots-clefs.\" Dotclear ne " +"peut donc importer qu'une seule catégorie par article et choisira dans ce " +"cas la première créée. Si vous désirez garder une trace de chaque catégorie, " +"vous pouvez les importer en tant que mots-clefs, avec un préfixe optionnel." + +msgid "" +"On the other hand, in WordPress, a post can not be uncategorized, and a " +"default installation has a first category labelised \"Uncategorized\". If you did not change that category, you can just ignore it while " +"importing your blog, as Dotclear allows you to actually keep your posts " +"uncategorized." +msgstr "" +"Par contre, il est impossible dans WordPress de ne pas assigner une " +"catégorie à un article, c'est pourquoi lors de l'installation est créée une " +"première catégorie appelée \"Non classée\". Si vous n'avez pas " +"modifié cette catégorie, l'importation peut simplement l'ignorer. En effet, " +"Dotclear vous permet d'avoir des articles sans catégorie." + +msgid "Ignore the first category:" +msgstr "Ignorer la première catégorie :" + +msgid "Import lowest numbered category on posts:" +msgstr "Importer la catégorie la plus ancienne sur les articles :" + +msgid "Import all categories as tags:" +msgstr "Importer les catégories comme des mots-clés :" + +msgid "Prefix such tags with:" +msgstr "Préfixer ces mots-clés avec :" + +msgid "Content filters" +msgstr "Filtres de contenu" + +msgid "" +"You may want to process your post and/or comment content with the following " +"filters." +msgstr "" +"Vous pouvez utiliser les filtres suivants sur vos articles et/ou " +"commentaires." + +msgid "Post content formatter:" +msgstr "Formatage des articles :" + +msgid "Comment content formatter:" +msgstr "Formatage des commentaires :" + +msgid "WordPress tables not found" +msgstr "Tables WordPress non trouvées" + +msgid "No file to read." +msgstr "Aucun fichier lisible." + +msgid "File is not a DotClear backup." +msgstr "Le fichier n'est pas une sauvegarde DotClear." + +msgid "File is not a single blog export." +msgstr "Le fichier n'est pas un fichier d'export simple." + +msgid "File is not a full export." +msgstr "Le fichier n'est pas un fichier d'export complet." + +#, php-format +msgid "" +"ID of \"%3$s\" does not match on record \"%1$s\" at line %2$s of backup file." +msgstr "" +"L'ID de \"%3$s\" ne correspond pas sur l'enregistrement \"%1$s\", ligne %2$s " +"du fichier de sauvegarde." + +msgid "Please wait..." +msgstr "Veuillez patienter..." + +# French translation of DotClea" +msgid "Maintenance" +msgstr "Maintenance" + +msgid "Optimization successful." +msgstr "Optimisation réalisée avec succès." + +msgid "Comments and trackback counted." +msgstr "Commentaires et rétroliens comptés." + +msgid "Templates cache directory emptied." +msgstr "Répertoire du cache des templates vidé." + +msgid "Logs deleted." +msgstr "Journaux effacés." + +#, php-format +msgid "Indexing entry %d to %d." +msgstr "Index des billets %d à %d" + +msgid "next" +msgstr "suivant" + +msgid "Entries index done." +msgstr "Index des billets complet." + +msgid "Back" +msgstr "Retour" + +#, php-format +msgid "Indexing comment %d to %d." +msgstr "Index des commentaires %d à %d" + +msgid "Comments index done." +msgstr "Index des commentaires complet." + +msgid "Optimize database room" +msgstr "Optimiser l'espace de la base de données" + +msgid "Vacuum tables" +msgstr "Nettoyer les tables" + +msgid "Counters" +msgstr "Compteurs" + +msgid "Reset comments and ping counters" +msgstr "Réinitialiser les compteurs des commentaires et rétroliens" + +msgid "Search engine index" +msgstr "Index du moteur de recherche" + +msgid "This may take a very long time" +msgstr "Ceci peut prendre beaucoup de temps" + +msgid "Index all posts" +msgstr "Indexer tous les billets" + +msgid "Index all comments" +msgstr "Indexer tous les commentaires" + +msgid "Vacuum logs" +msgstr "Vider les journaux" + +msgid "Delete all logs" +msgstr "Supprimer tous les journaux" + +msgid "Empty templates cache directory" +msgstr "Vider le répertoire du cache des templates" + +msgid "Empty directory" +msgstr "Vider le répertoire" + +msgid "Pages" +msgstr "Pages" + +#, php-format +msgid "%d page" +msgstr "%d page" + +#, php-format +msgid "%d pages" +msgstr "%d pages" + +msgid "manage pages" +msgstr "gérer les pages" + +msgid "Published on" +msgstr "Publié le" + +msgid "This page's comments feed" +msgstr "Fil des commentaires de cette page" + +msgid "You must provide a valid email address." +msgstr "Vous devez indiquer une adresse email valide." + +msgid "Page title" +msgstr "Titre de la page" + +msgid "Page position" +msgstr "Position de la page" + +msgid "Publication date" +msgstr "Date de publication" + +msgid "No page" +msgstr "Aucune page" + +msgid "select this page" +msgstr "sélectionner cette page" + +msgid "Are you sure you want to delete selected pages?" +msgstr "Êtes-vous certain de vouloir supprimer les pages sélectionnées ?" + +msgid "New page" +msgstr "Nouvelle page" + +msgid "Selected pages action:" +msgstr "Action sur les pages sélectionnées :" + +msgid "This page does not exist." +msgstr "Cette page n'existe pas." + +msgid "Edit page" +msgstr "Modifier la page" + +msgid "next page" +msgstr "page suivante" + +msgid "previous page" +msgstr "page précédente" + +msgid "Are you sure you want to delete this page?" +msgstr "Êtes-vous certain de vouloir supprimer cette page ?" + +msgid "Page has been successfully updated." +msgstr "La page a été mise à jour avec succès." + +msgid "Page has been successfully created." +msgstr "La page a été créée avec succès." + +msgid "Go to this page on the site" +msgstr "Aller sur cette page" + +msgid "Page status:" +msgstr "État de la page :" + +msgid "Page position:" +msgstr "Position de la page :" + +msgid "Page lang:" +msgstr "Langue de la page :" + +msgid "Page password:" +msgstr "Mot de passe de la page :" + +msgid "" +"Warning: If you set the URL manually, it may conflict with another page." +msgstr "" +"Attention : si vous indiquez l'URL manuellement, celle-ci peut entrer en " +"conflit avec une autre page." + +msgid "Add files to this page" +msgstr "Ajouter un fichier à la page" + +msgid "Pings" +msgstr "Pings" + +msgid "Pings configuration" +msgstr "Configuration des pings" + +msgid "Settings have been successfully updated." +msgstr "Paramètres enregistrés avec succès." + +msgid "Activate pings extension" +msgstr "Activer le module de ping" + +msgid "Service name:" +msgstr "Nom du service :" + +msgid "Service URI:" +msgstr "URI du service :" + +msgid "error" +msgstr "erreur" + +msgid "Test ping services" +msgstr "Tester les services de ping" + +msgid "Check all" +msgstr "Tout cocher." + +msgid "Pings:" +msgstr "Pings :" + +msgid "Simple menu" +msgstr "Menu simple" + +msgid "Menu" +msgstr "Menu" + +msgid "All months" +msgstr "Tous les mois" + +msgid "All tags" +msgstr "Tous les mots-clés" + +msgid "Home" +msgstr "Accueil" + +msgid "Archive" +msgstr "Archive" + +msgid "Page" +msgstr "Page" + +msgid "Tags" +msgstr "Mots-clés" + +msgid "User defined" +msgstr "Saisie libre" + +msgid "Label" +msgstr "Libellé" + +msgid "Recent posts" +msgstr "Billets récents" + +#, php-format +msgid "Switch to %s language" +msgstr "Basculer vers le %s" + +msgid "Recent Posts from this category" +msgstr "Billets récents de cette catégorie" + +msgid "Archives" +msgstr "Archives" + +#, php-format +msgid "Posts from %s" +msgstr "Billets de %s" + +#, php-format +msgid "Recent posts for %s tag" +msgstr "Billets récent pour le mot-clé %s" + +msgid "Label and URL of menu item are mandatory." +msgstr "Les libellés et URL sont obligatoires." + +msgid "No menu items selected." +msgstr "Aucun item de menu sélectionné." + +msgid "Label is mandatory." +msgstr "Le libellé est obligatoire." + +msgid "URL is mandatory." +msgstr "l’URL est obligatoire." + +msgid "Menu item has been successfully added." +msgstr "L'item de menu a été ajouté avec succès." + +msgid "Menu items have been successfully removed." +msgstr "Le ou les items de menu ont été supprimés avec succès." + +msgid "Menu items have been successfully updated." +msgstr "Le ou les items de menu ont été mis à jour avec succès." + +msgid "Add item" +msgstr "Ajouter un item de menu" + +msgid "Select type" +msgstr "Sélection du type" + +msgid "Type of item menu:" +msgstr "Type d'item de menu :" + +msgid "Continue..." +msgstr "Continuer…" + +msgid "Select language:" +msgstr "Sélectionnez la langue :" + +msgid "Select category:" +msgstr "Sélectionnez la catégorie :" + +msgid "Select month (if necessary):" +msgstr "Sélectionnez le mois (si nécessaire) :" + +msgid "Select page:" +msgstr "Sélectionnez la page :" + +msgid "Select tag (if necessary):" +msgstr "Sélectionnez le mot clé (si nécessaire) :" + +msgid "Label of item menu:" +msgstr "Libellé de l'item de menu :" + +msgid "Description of item menu:" +msgstr "Description de l'item de menu :" + +msgid "URL of item menu:" +msgstr "URL de l'item de menu :" + +msgid "Add this item" +msgstr "Ajouter cet item" + +msgid "Add an item" +msgstr "Ajouter un item de menu" + +msgid "Menu items list" +msgstr "Liste des items de menu" + +msgid "Update menu" +msgstr "Mettre à jour le menu" + +msgid "Delete selected menu items" +msgstr "Supprimer les items de menu sélectionnés" + +msgid "Are you sure you want to remove selected menu items?" +msgstr "Êtes-vous sûr de vouloir supprimer les items de menu sélectionnés ?" + +msgid "Currently no menu items" +msgstr "Aucun item de menu pour l'instant" + +msgid "Tags:" +msgstr "Mots-clés :" + +#, php-format +msgid "Are you sure you want to remove this %s?" +msgstr "Êtes vous certain de vouloir supprimer ce %s ?" + +#, php-format +msgid "Add a %s to this entry" +msgstr "Ajouter un %s à ce billet" + +msgid "Choose from list" +msgstr "Choisir depuis la liste" + +msgid "all" +msgstr "tous" + +msgid "Tag" +msgstr "Mot-clé" + +msgid "used in %e - frequency %p%" +msgstr "utilisé dans %e - fréquence %p%" + +msgid "entry" +msgstr "billet" + +msgid "entries" +msgstr "billets" + +msgid "Enter tags separated by coma" +msgstr "Entrer les mots-clés séparés par une virgule" + +msgid "Add tags" +msgstr "Ajouter des mots-clés" + +msgid "Remove tags" +msgstr "Supprimer des mots-clés" + +msgid "Add tags to entries" +msgstr "Ajouter des mots-clés aux billets" + +msgid "Tags to add:" +msgstr "Mots-clés à ajouter :" + +msgid "Remove selected tags from entries" +msgstr "Supprimer les mots-clés sélectionnés des billets" + +msgid "No tags for selected entries" +msgstr "Aucun mot-clé pour les billets sélectionnés" + +msgid "Following tags have been found in selected entries:" +msgstr "Les mots-clés suivants ont été trouvés dans les billets sélectionnés :" + +msgid "short" +msgstr "court" + +msgid "extended" +msgstr "étendu" + +msgid "Tags list format:" +msgstr "Format de la liste des mots-clés :" + +msgid "This tag's comments Atom feed" +msgstr "Fil Atom des commentaires de ce mot-clé" + +msgid "This tag's entries Atom feed" +msgstr "Fil Atom des billets de ce mot-clé" + +msgid "Limit (empty means no limit):" +msgstr "Limite (laisser vide pour aucune limite)" + +msgid "Entries count" +msgstr "Nombre de billets" + +msgid "Tag name" +msgstr "Nom du mot-clé" + +msgid "Link to all tags:" +msgstr "Lien vers tous les mots-clés :" + +msgid "Edit tag" +msgstr "Modifier le mot-clé" + +msgid "Tag has been successfully renamed" +msgstr "Mot-clé renommé avec succès" + +msgid "Back to tags list" +msgstr "Retour à la liste des mots-clés" + +msgid "Actions for this tag" +msgstr "Actions pour ce mot-clé" + +msgid "Edit tag name:" +msgstr "Renommer ce mot-clé :" + +msgid "Rename" +msgstr "Renommer" + +msgid "Delete this tag:" +msgstr "Supprimer ce mot-clé :" + +msgid "List of entries with this tag" +msgstr "Liste des billets avec ce mot-clé" + +msgid "Tag has been successfully removed" +msgstr "Mot-clé supprimé avec succès" + +msgid "No tags on this blog." +msgstr "Aucun mot-clé sur ce blog" + +msgid "Theme Editor" +msgstr "Éditeur de thème" + +msgid "No file" +msgstr "Aucun fichier" + +msgid "File does not exist." +msgstr "Le fichier n'existe pas." + +#, php-format +msgid "File %s is not readable" +msgstr "Le fichier %s n'est pas lisible" + +#, php-format +msgid "" +"Unable to write file %s. Please check your theme files and folders " +"permissions." +msgstr "" +"Impossible d'écrire le fichier %s. Veuillez vérifier les permissions des " +"fichiers et répertoires de votre thème." + +msgid "Saving document..." +msgstr "Sauvegarde du document..." + +msgid "Document saved" +msgstr "Document sauvegardé" + +msgid "An error occurred:" +msgstr "Une erreur s'est produite :" + +#, php-format +msgid "Your current theme on this blog is \"%s\"." +msgstr "Le thème utilisé actuellement sur votre blog est \"%s\"." + +msgid "You can't edit default theme." +msgstr "Vous ne pouvez pas modifier le thème par défaut." + +msgid "Please select a file to edit." +msgstr "Veuillez sélectionner un fichier à modifier." + +msgid "File editor" +msgstr "Éditeur de fichier" + +#, php-format +msgid "Editing file %s" +msgstr "Modification du fichier %s" + +msgid "This file is not writable. Please check your theme files permissions." +msgstr "" +"Ce fichier ne peut pas être modifié. Veuillez vérifier les permissions des " +"fichiers de votre thème." + +msgid "Templates files" +msgstr "Fichiers template" + +msgid "CSS files" +msgstr "Fichiers CSS" + +msgid "JavaScript files" +msgstr "Fichiers JavaScript" + +msgid "Preferences successfully updated" +msgstr "Préférences mises à jour avec succès" + +msgid "Preferences definition successfully updated" +msgstr "Définition des préférences mise à jour avec succès" + +msgid "user preferences" +msgstr "préférences utilisateur" + +msgid "global preferences" +msgstr "préférences globales" + +msgid "Presentation widgets" +msgstr "Widgets de présentation" + +msgid "Search engine" +msgstr "Moteur de recherche" + +msgid "Navigation links" +msgstr "Liens de navigation" + +msgid "Selected entries" +msgstr "Billets sélectionnés" + +msgid "Best of me" +msgstr "À retenir" + +msgid "Blog languages" +msgstr "Langues du blog" + +msgid "With entries counts" +msgstr "Afficher le nombre de billets" + +msgid "Subscribe links" +msgstr "Liens d'abonnement" + +msgid "Subscribe" +msgstr "S'abonner" + +msgid "Feeds type:" +msgstr "Types de fil :" + +msgid "Feed reader" +msgstr "Lecteur de fils de nouvelles" + +msgid "Somewhere else" +msgstr "Ailleurs" + +msgid "Entries limit:" +msgstr "Nombre de billets maximum :" + +msgid "Text" +msgstr "Texte" + +msgid "Text:" +msgstr "Texte :" + +msgid "Last entries" +msgstr "Derniers billets" + +msgid "Uncategorized" +msgstr "Non catégorisé" + +msgid "Tag:" +msgstr "Mot-clé :" + +msgid "Last comments" +msgstr "Derniers commentaires" + +msgid "Comments limit:" +msgstr "Nombre de commentaires maximum :" + +#, php-format +msgid "This blog's entries %s feed" +msgstr "Fil %s des billets de ce blog" + +#, php-format +msgid "This blog's comments %s feed" +msgstr "Fil %s des commentaires de ce blog" + +msgid "Entries feed" +msgstr "Fil des billets" + +msgid "Comments feed" +msgstr "Fil des commentaires" + +msgid "navigation" +msgstr "navigation" + +msgid "extra" +msgstr "extra" + +msgid "custom" +msgstr "supplémentaire" + +msgid "Widgets" +msgstr "Widgets" + +msgid "Are you sure you want to reset sidebars?" +msgstr "Êtes-vous certain de vouloir réinitialiser les bandeaux ?" + +msgid "Available widgets" +msgstr "Widgets disponibles" + +msgid "Append to:" +msgstr "Ajouter à :" + +msgid "Add widgets to sidebars" +msgstr "Ajouter les widgets aux bandeaux" + +msgid "Navigation sidebar" +msgstr "Bandeau de navigation" + +msgid "Extra sidebar" +msgstr "Bandeau d'extra" + +msgid "Custom sidebar" +msgstr "Bandeau supplémentaire" + +msgid "Update sidebars" +msgstr "Mettre à jour les bandeaux" + +msgid "Reset sidebars" +msgstr "Réinitialiser les bandeaux" + +msgid "Use of widgets" +msgstr "Utilisation des widgets" + +msgid "" +"Widgets may be used to add various blocks of content to be displayed on your " +"public pages. To add a widget, drag it from the Available widgets list on " +"the left to one of the sidebars on the right of this page. You can order " +"your widgets in a sidebar by dragging them up or down. You must update " +"sidebars to apply your changes." +msgstr "" +"Les widgets sont utilisés pour ajouter des blocs de contenus divers dans vos " +"pages publiques. Pour ajouter un widget, faites-le glisser depuis la liste " +"des Widgets disponibles à gauche jusqu'à l'un des bandeaux sur la droite de " +"la page. Vous pouvez ré-ordonner vos widgets dans un bandeau en le faisant " +"glisser vers le haut ou vers le bas. Vous devez mettre à jour les bandeaux " +"pour enregistrer vos modifications." + +msgid "" +"Once included in a sidebar, widgets have configuration options that you can " +"reach by clicking on the + sign next to their name." +msgstr "" +"Une fois inclus dans un bandeau, les widgets ont généralement des options " +"que vous pouvez configurer. Cliquez sur le signe + à côté de leur nom pour y " +"accéder." + +msgid "Reset sidebars to get back to default widgets installation." +msgstr "" +"Réinitialisez les bandeaux pour retrouver les widgets par défaut de votre " +"installation." + +msgid "Widget templates tags" +msgstr "Marqueurs de template des widgets" + +msgid "" +"If you are allowed to edit your theme templates, you can directly add " +"widgets as templates tags, with their own configuration." +msgstr "" +"Si vous avez le droit de modifier les templates de votre thème, vous pouvez " +"directement ajouter des widgets à l'aide de marqueurs de template, avec leur " +"propre configuration." + +msgid "To add a widget in your template, you need to write code like this:" +msgstr "" +"Pour ajouter un widget dans votre template, vous devez écrire un code comme " +"ceci :" + +msgid "Widget ID" +msgstr "Identifiant du widget" + +msgid "Setting name" +msgstr "Nom du paramètre" + +msgid "Setting value" +msgstr "Valeur du paramètre" + +msgid "Here are the following available widgets for your blog:" +msgstr "Voici les widgets disponibles pour votre blog :" + +msgid "Widget ID:" +msgstr "Identifiant du widget :" + +msgid "No setting for this widget" +msgstr "Aucun paramètre pour ce widget" + +msgid "Setting name:" +msgstr "Nom du paramètre :" + +msgid "No widget." +msgstr "Aucun widget." + +msgid "order" +msgstr "ordre" + +msgid "Remove widget" +msgstr "Supprimer le widget" + +#~ msgid "Export a blog" +#~ msgstr "Exporter un blog" + +#~ msgid "Export all content" +#~ msgstr "Exporter tout le contenu" + +#~ msgid "Import from a feed" +#~ msgstr "Importer depuis un fil de nouvelles" + +#~ msgid "Import a full backup file" +#~ msgstr "Import d'un fichier de sauvegarde complet" + +#~ msgid "The backup file does not appear to be well formed." +#~ msgstr "Le fichier de sauvegarde ne semble pas être correctement formaté" + +#~ msgid "List" +#~ msgstr "Liste" + +#~ msgid "Return to filters" +#~ msgstr "Retour aux filtres" + +#~ msgid "import" +#~ msgstr "importer" + +#~ msgid "Preview" +#~ msgstr "Prévisualiser" + +#~ msgid "Send" +#~ msgstr "Envoyer" + +#~ msgid "View page" +#~ msgstr "Voir la page" + +#~ msgid "Preview page" +#~ msgstr "Prévisualiser la page" + +#~ msgid "Save order" +#~ msgstr "Enregistrer l'ordre" + +#~ msgid "External media" +#~ msgstr "Média externe" + +#~ msgid "External media selector" +#~ msgstr "Sélecteur de médias externes" + +#~ msgid "Supported media services" +#~ msgstr "Services de média supportés" + +#~ msgid "" +#~ "Please enter the URL of the page containing the video you want to include " +#~ "in your post." +#~ msgstr "" +#~ "Entrez l'URL de la page contenant la video que vous voulez intégrer à " +#~ "votre billet." + +#~ msgid "Page URL:" +#~ msgstr "URL de la page :" + +#~ msgid "Media alignment" +#~ msgstr "Alignement du média" + +#~ msgid "Media title" +#~ msgstr "Titre du média" + +#~ msgid "Unsupported service" +#~ msgstr "Service non pris en charge" + +#~ msgid "Invalid page URL" +#~ msgstr "URL de la page non valide" diff --git a/v2/dotclear/locales/fr/public.lang.php b/v2/dotclear/locales/fr/public.lang.php new file mode 100644 index 0000000..fac7047 --- /dev/null +++ b/v2/dotclear/locales/fr/public.lang.php @@ -0,0 +1,94 @@ +%1$s returned no result.'] = 'Votre recherche de %1$s n\'a donné aucun résultat.'; +$GLOBALS['__l10n']['Your search for %1$s returned %2$s result.'] = 'Votre recherche de %1$s a donné %2$s résultat.'; +$GLOBALS['__l10n']['Your search for %1$s returned %2$s results.'] = 'Votre recherche de %1$s a donné %2$s résultats.'; +$GLOBALS['__l10n']['Home'] = 'Accueil'; +$GLOBALS['__l10n']['All keywords'] = 'Tous les mots clés'; +$GLOBALS['__l10n']['Best of me'] = 'À retenir'; +$GLOBALS['__l10n']['Languages'] = 'Langues'; +$GLOBALS['__l10n']['Categories'] = 'Catégories'; +$GLOBALS['__l10n']['Subcategories'] = 'Sous-catégories'; +$GLOBALS['__l10n']['Archives'] = 'Archives'; +$GLOBALS['__l10n']['Links'] = 'Liens'; +$GLOBALS['__l10n']['Subscribe'] = 'S\'abonner'; +$GLOBALS['__l10n']['Entries feed'] = 'Fil des billets'; +$GLOBALS['__l10n']['Comments feed'] = 'Fil des commentaires'; +$GLOBALS['__l10n']['This blog\'s comments Atom feed'] = 'Fil Atom des commentaires de ce blog'; +$GLOBALS['__l10n']['This category\'s entries Atom feed'] = 'Fil Atom des billets de cette catégorie'; +$GLOBALS['__l10n']['This category\'s comments Atom feed'] = 'Fil Atom des commentaires de cette catégorie'; +$GLOBALS['__l10n']['This post\'s comments feed'] = 'Fil des commentaires de ce billet'; +$GLOBALS['__l10n']['This post\'s comments Atom feed'] = 'Fil Atom des commentaires de ce billet'; +$GLOBALS['__l10n']['Attachments'] = 'Annexes'; +$GLOBALS['__l10n']['Permalink'] = 'Lien permanent'; +$GLOBALS['__l10n']['Comments'] = 'Commentaires'; +$GLOBALS['__l10n']['Your comment'] = 'Votre commentaire'; +$GLOBALS['__l10n']['Your comment has been published.'] = 'Votre commentaire a été publié.'; +$GLOBALS['__l10n']['Your comment has been submitted and will be reviewed for publication.'] = 'Votre commentaire a été enregistré et sera publié après validation.'; +$GLOBALS['__l10n']['Add a comment'] = 'Ajouter un commentaire'; +$GLOBALS['__l10n']['Name or nickname'] = 'Nom ou pseudo'; +$GLOBALS['__l10n']['Email address'] = 'Adresse email'; +$GLOBALS['__l10n']['Website'] = 'Site web'; +$GLOBALS['__l10n']['optional'] = 'facultatif'; +$GLOBALS['__l10n']['Comment'] = 'Commentaire'; +$GLOBALS['__l10n']['Comments can be formatted using a simple wiki syntax.'] = 'Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.'; +$GLOBALS['__l10n']['HTML code is displayed as text and web addresses are automatically converted.'] = 'Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.'; +$GLOBALS['__l10n']['Remember me on this blog'] = 'Se souvenir de moi sur ce blog'; +$GLOBALS['__l10n']['preview'] = 'prévisualiser'; +$GLOBALS['__l10n']['send'] = 'envoyer'; +$GLOBALS['__l10n']['They posted on the same topic'] = 'La discussion continue ailleurs'; +$GLOBALS['__l10n']['Trackback URL'] = 'URL de rétrolien'; +$GLOBALS['__l10n']['You must provide an author name'] = 'Vous devez indiquer un nom'; +$GLOBALS['__l10n']['You must provide a comment'] = 'Vous devez écrire un commentaire'; +$GLOBALS['__l10n']['Email address is not valid'] = 'Adresse email incorrecte'; +$GLOBALS['__l10n']['Document not found'] = 'Document non trouvé'; +$GLOBALS['__l10n']['The document you are looking for does not exist.'] = 'Le document que vous cherchez n\'existe pas.'; +$GLOBALS['__l10n']['Powered by %s'] = 'Propulsé par %s'; +$GLOBALS['__l10n']['Subscribe to'] = 'S\'abonner à'; +$GLOBALS['__l10n']['What is an RSS feed?'] = 'Qu\'est ce qu\'un fil RSS ?'; +$GLOBALS['__l10n']['RSS feed is a free blog summary. It provides content (either posts or comments) or summaries of content, together with links to the full versions, and other metadata. The last published items may then be read by your favorite RSS aggregator.'] = 'Un fil RSS recueille les informations de mise à jour d\'un site. Il fournit le contenu des billets ou des commentaires ou un extrait de ceux-ci, ainsi qu\'un lien vers les versions complètes et quelques autres informations. Ce fil a pour vocation d\'être lu par un agrégateur RSS.'; +$GLOBALS['__l10n']['Simply copy the following URL into your aggregator:'] = 'Copier simplement l\'adresse suivante dans votre agrégateur :'; +$GLOBALS['__l10n']['Password needed'] = 'Mot de passe nécessaire'; +$GLOBALS['__l10n']['You must give a password to access this area.'] = 'Vous devez indiquer un mot de passe pour accéder à cette partie.'; +$GLOBALS['__l10n']['Password:'] = 'Mot de passe :'; +$GLOBALS['__l10n']['You must provide a valid email address.'] = 'Vous devez indiquer une adresse e-mail valide.'; +$GLOBALS['__l10n']['Read'] = 'Lire'; +?> \ No newline at end of file diff --git a/v2/dotclear/locales/fr/public.po b/v2/dotclear/locales/fr/public.po new file mode 100644 index 0000000..ce50b8e --- /dev/null +++ b/v2/dotclear/locales/fr/public.po @@ -0,0 +1,241 @@ +# French translation of DotClear +# Copyright (C) 2006. +# Olivier Meunier , 2006. +msgid "" +msgstr "" +"Project-Id-Version: Dotclear 2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-10-13 05:22+0200\n" +"PO-Revision-Date: 2011-01-15 22:47+0100\n" +"Last-Translator: xave \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" + +msgid "To content" +msgstr "Aller au contenu" + +msgid "To menu" +msgstr "Aller au menu" + +msgid "To search" +msgstr "Aller à la recherche" + +msgid "By" +msgstr "Par" + +msgid "by" +msgstr "par" + +msgid "on" +msgstr "le" + +msgid "On" +msgstr "Le" + +msgid "Continue reading" +msgstr "Lire la suite" + +msgid "no comment" +msgstr "aucun commentaire" + +msgid "one comment" +msgstr "un commentaire" + +msgid "%d comments" +msgstr "%d commentaires" + +msgid "no trackback" +msgstr "aucun rétrolien" + +msgid "one trackback" +msgstr "un rétrolien" + +msgid "%d trackbacks" +msgstr "%d rétroliens" + +msgid "no attachment" +msgstr "aucune annexe" + +msgid "one attachment" +msgstr "une annexe" + +msgid "%d attachments" +msgstr "%d annexes" + +msgid "previous entries" +msgstr "billets précédents" + +msgid "page" +msgstr "page" + +msgid "of" +msgstr "de" + +msgid "next entries" +msgstr "billets suivants" + +msgid "Search" +msgstr "Recherche" + +msgid "Your search for %1$s returned no result." +msgstr "Votre recherche de %1$s n'a donné aucun résultat." + +msgid "Your search for %1$s returned %2$s result." +msgstr "Votre recherche de %1$s a donné %2$s résultat." + +msgid "Your search for %1$s returned %2$s results." +msgstr "Votre recherche de %1$s a donné %2$s résultats." + +msgid "Home" +msgstr "Accueil" + +msgid "All keywords" +msgstr "Tous les mots clés" + +msgid "Best of me" +msgstr "À retenir" + +msgid "Languages" +msgstr "Langues" + +msgid "Categories" +msgstr "Catégories" + +msgid "Subcategories" +msgstr "Sous-catégories" + +msgid "Archives" +msgstr "Archives" + +msgid "Links" +msgstr "Liens" + +msgid "Subscribe" +msgstr "S'abonner" + +msgid "Entries feed" +msgstr "Fil des billets" + +msgid "Comments feed" +msgstr "Fil des commentaires" + +msgid "This blog's comments Atom feed" +msgstr "Fil Atom des commentaires de ce blog" + +msgid "This category's entries Atom feed" +msgstr "Fil Atom des billets de cette catégorie" + +msgid "This category's comments Atom feed" +msgstr "Fil Atom des commentaires de cette catégorie" + +msgid "This post's comments feed" +msgstr "Fil des commentaires de ce billet" + +msgid "This post's comments Atom feed" +msgstr "Fil Atom des commentaires de ce billet" + +msgid "Attachments" +msgstr "Annexes" + +msgid "Permalink" +msgstr "Lien permanent" + +msgid "Comments" +msgstr "Commentaires" + +msgid "Your comment" +msgstr "Votre commentaire" + +msgid "Your comment has been published." +msgstr "Votre commentaire a été publié." + +msgid "Your comment has been submitted and will be reviewed for publication." +msgstr "Votre commentaire a été enregistré et sera publié après validation." + +msgid "Add a comment" +msgstr "Ajouter un commentaire" + +msgid "Name or nickname" +msgstr "Nom ou pseudo" + +msgid "Email address" +msgstr "Adresse email" + +msgid "Website" +msgstr "Site web" + +msgid "optional" +msgstr "facultatif" + +msgid "Comment" +msgstr "Commentaire" + +msgid "Comments can be formatted using a simple wiki syntax." +msgstr "Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée." + +msgid "HTML code is displayed as text and web addresses are automatically converted." +msgstr "Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées." + +msgid "Remember me on this blog" +msgstr "Se souvenir de moi sur ce blog" + +msgid "preview" +msgstr "prévisualiser" + +msgid "send" +msgstr "envoyer" + +msgid "They posted on the same topic" +msgstr "La discussion continue ailleurs" + +msgid "Trackback URL" +msgstr "URL de rétrolien" + +msgid "You must provide an author name" +msgstr "Vous devez indiquer un nom" + +msgid "You must provide a comment" +msgstr "Vous devez écrire un commentaire" + +msgid "Email address is not valid" +msgstr "Adresse email incorrecte" + +msgid "Document not found" +msgstr "Document non trouvé" + +msgid "The document you are looking for does not exist." +msgstr "Le document que vous cherchez n'existe pas." + +msgid "Powered by %s" +msgstr "Propulsé par %s" + +msgid "Subscribe to" +msgstr "S'abonner à" + +msgid "What is an RSS feed?" +msgstr "Qu'est ce qu'un fil RSS ?" + +msgid "RSS feed is a free blog summary. It provides content (either posts or comments) or summaries of content, together with links to the full versions, and other metadata. The last published items may then be read by your favorite RSS aggregator." +msgstr "Un fil RSS recueille les informations de mise à jour d'un site. Il fournit le contenu des billets ou des commentaires ou un extrait de ceux-ci, ainsi qu'un lien vers les versions complètes et quelques autres informations. Ce fil a pour vocation d'être lu par un agrégateur RSS." + +msgid "Simply copy the following URL into your aggregator:" +msgstr "Copier simplement l'adresse suivante dans votre agrégateur :" + +msgid "Password needed" +msgstr "Mot de passe nécessaire" + +msgid "You must give a password to access this area." +msgstr "Vous devez indiquer un mot de passe pour accéder à cette partie." + +msgid "Password:" +msgstr "Mot de passe :" + +msgid "You must provide a valid email address." +msgstr "Vous devez indiquer une adresse e-mail valide." + +msgid "Read" +msgstr "Lire" diff --git a/v2/dotclear/locales/fr/resources.php b/v2/dotclear/locales/fr/resources.php new file mode 100644 index 0000000..d0e57b7 --- /dev/null +++ b/v2/dotclear/locales/fr/resources.php @@ -0,0 +1,23 @@ + 'http://doc.dotclear.net/2.0', + 'Présentation de Dotclear 2' => 'http://doc.dotclear.net/2.0/overview/tour', + "Manuel de l'utilisateur" => 'http://doc.dotclear.net/2.0/usage', + "Guide d'installation et d'administration" => 'http://doc.dotclear.net/2.0/admin', + "Forum de support de Dotclear 2" => 'http://forum.dotclear.net/' +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/.htaccess b/v2/dotclear/plugins/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/v2/dotclear/plugins/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/v2/dotclear/plugins/aboutConfig/_admin.php b/v2/dotclear/plugins/aboutConfig/_admin.php new file mode 100644 index 0000000..7a161a0 --- /dev/null +++ b/v2/dotclear/plugins/aboutConfig/_admin.php @@ -0,0 +1,17 @@ +addItem('about:config','plugin.php?p=aboutConfig','index.php?pf=aboutConfig/icon.png', + preg_match('/plugin.php\?p=aboutConfig(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/aboutConfig/_define.php b/v2/dotclear/plugins/aboutConfig/_define.php new file mode 100644 index 0000000..271c26e --- /dev/null +++ b/v2/dotclear/plugins/aboutConfig/_define.php @@ -0,0 +1,20 @@ +registerModule( + /* Name */ "about:config", + /* Description*/ "Manage every blog configuration directive", + /* Author */ "Olivier Meunier", + /* Version */ '0.4' +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/aboutConfig/icon-big.png b/v2/dotclear/plugins/aboutConfig/icon-big.png new file mode 100644 index 0000000..f5e9c5d Binary files /dev/null and b/v2/dotclear/plugins/aboutConfig/icon-big.png differ diff --git a/v2/dotclear/plugins/aboutConfig/icon.png b/v2/dotclear/plugins/aboutConfig/icon.png new file mode 100644 index 0000000..adbbf0f Binary files /dev/null and b/v2/dotclear/plugins/aboutConfig/icon.png differ diff --git a/v2/dotclear/plugins/aboutConfig/index.php b/v2/dotclear/plugins/aboutConfig/index.php new file mode 100644 index 0000000..dda5716 --- /dev/null +++ b/v2/dotclear/plugins/aboutConfig/index.php @@ -0,0 +1,240 @@ + $s) + { + $core->blog->settings->addNamespace($ns); + + foreach ($s as $k => $v) { + $core->blog->settings->$ns->put($k,$v); + } + + $core->blog->triggerBlog(); + } + + http::redirect($p_url.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +# Global settings update +if (!empty($_POST['gs']) && is_array($_POST['gs'])) +{ + try + { + foreach ($_POST['gs'] as $ns => $s) + { + $core->blog->settings->addNamespace($ns); + + foreach ($s as $k => $v) { + $core->blog->settings->$ns->put($k,$v,null,null,true,true); + } + + $core->blog->triggerBlog(); + } + + http::redirect($p_url.'&upd=1&part=global'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +$part = !empty($_GET['part']) && $_GET['part'] == 'global' ? 'global' : 'local'; + +function settingLine($id,$s,$ns,$field_name,$strong_label) +{ + if ($s['type'] == 'boolean') { + $field = form::combo(array($field_name.'['.$ns.']['.$id.']',$field_name.'_'.$id), + array(__('yes') => 1, __('no') => 0),$s['value']); + } else { + $field = form::field(array($field_name.'['.$ns.']['.$id.']',$field_name.'_'.$id),40,null, + html::escapeHTML($s['value'])); + } + + $slabel = $strong_label ? '%s' : '%s'; + + return + ''. + ''. + ''.$field.''. + ''.$s['type'].''. + ''.html::escapeHTML($s['label']).''. + ''; +} +?> + + + about:config + + + + + + +'.__('Configuration successfully updated').'

        '; +} + +if (!empty($_GET['upda'])) { + echo '

        '.__('Settings definition successfully updated').'

        '; +} +?> +

        blog->name); ?> › about:config

        + +
        + +%s'. +''. +''."\n". +' Setting ID'."\n". +' '.__('Value').''."\n". +' '.__('Type').''."\n". +' '.__('Description').''."\n". +''."\n". +''."\n". +''; +$table_footer = ''; + +$settings = array(); +foreach ($core->blog->settings->dumpNamespaces() as $ns => $namespace) { + foreach ($namespace->dumpSettings() as $k => $v) { + $settings[$ns][$k] = $v; + } +} +ksort($settings); +if (count($settings) > 0) { + $ns_combo = array(); + foreach ($settings as $ns => $s) { + $ns_combo[$ns] = '#l_'.$ns; + } + echo + '
        '. + '

        '. + ' '.form::combo('ls_nav',$ns_combo). + ' '. + ''. + $core->formNonce().'

        '; +} +?> + +
        + + $s) +{ + ksort($s); + echo sprintf($table_header,'l_'.$ns,$ns); + foreach ($s as $k => $v) + { + echo settingLine($k,$v,$ns,'s',!$v['global']); + } + echo $table_footer; +} +?> + +

        + +formNonce(); ?>

        +
        +
        + +
        + +blog->settings->dumpNamespaces() as $ns => $namespace) { + foreach ($namespace->dumpGlobalSettings() as $k => $v) { + $settings[$ns][$k] = $v; + } +} + +ksort($settings); + +if (count($settings) > 0) { + $ns_combo = array(); + foreach ($settings as $ns => $s) { + $ns_combo[$ns] = '#g_'.$ns; + } + echo + '
        '. + '

        '. + ' '.form::combo('gs_nav',$ns_combo). + ' '. + ''. + $core->formNonce().'

        '; +} +?> + +
        + + $s) +{ + ksort($s); + echo sprintf($table_header,'g_'.$ns,$ns); + foreach ($s as $k => $v) + { + echo settingLine($k,$v,$ns,'gs',false); + } + echo $table_footer; +} +?> + +

        + +formNonce(); ?>

        +
        +
        + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/akismet/_define.php b/v2/dotclear/plugins/akismet/_define.php new file mode 100644 index 0000000..599cf82 --- /dev/null +++ b/v2/dotclear/plugins/akismet/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "Akismet", + /* Description*/ "Akismet interface for Dotclear", + /* Author */ "Olivier Meunier", + /* Version */ '1.1', + array( + 'permissions' => 'usage,contentadmin', + 'priority' => 200 + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/akismet/_prepend.php b/v2/dotclear/plugins/akismet/_prepend.php new file mode 100644 index 0000000..c65fb2d --- /dev/null +++ b/v2/dotclear/plugins/akismet/_prepend.php @@ -0,0 +1,17 @@ +spamfilters[] = 'dcFilterAkismet'; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/akismet/class.dc.filter.akismet.php b/v2/dotclear/plugins/akismet/class.dc.filter.akismet.php new file mode 100644 index 0000000..44530a4 --- /dev/null +++ b/v2/dotclear/plugins/akismet/class.dc.filter.akismet.php @@ -0,0 +1,260 @@ +auth->isSuperAdmin()) { + $this->has_gui = false; + } + } + + protected function setInfo() + { + $this->description = __('Akismet spam filter'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %s.'),$this->guiLink()); + } + + private function akInit() + { + $blog =& $this->core->blog; + + if (!$blog->settings->akismet->ak_key) { + return false; + } + + return new akismet($blog->url,$blog->settings->akismet->ak_key); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (($ak = $this->akInit()) === false) { + return; + } + + $blog =& $this->core->blog; + + try + { + if ($ak->verify()) + { + $post = $blog->getPosts(array('post_id' => $post_id)); + + $c = $ak->comment_check( + $post->getURL(), + $type, + $author, + $email, + $site, + $content + ); + + if ($c) { + $status = 'Filtered by Akismet'; + return true; + } + } + } catch (Exception $e) {} # If http or akismet is dead, we don't need to know it + } + + public function trainFilter($status,$filter,$type,$author,$email,$site,$ip,$content,$rs) + { + # We handle only false positive from akismet + if ($status == 'spam' && $filter != 'dcFilterAkismet') + { + return; + } + + $f = $status == 'spam' ? 'submit_spam' : 'submit_ham'; + + if (($ak = $this->akInit()) === false) { + return; + } + + try + { + if ($ak->verify()) { + $ak->{$f}($rs->getPostURL(),$type,$author,$email,$site,$content); + } + } catch (Exception $e) {} # If http or akismet is dead, we don't need to know it + } + + public function gui($url) + { + $blog =& $this->core->blog; + + $ak_key = $blog->settings->akismet->ak_key; + $ak_verified = null; + + if (isset($_POST['ak_key'])) + { + try + { + $ak_key = $_POST['ak_key']; + + $blog->settings->addNamespace('akismet'); + $blog->settings->akismet->put('ak_key',$ak_key,'string'); + + http::redirect($url.'&up=1'); + } + catch (Exception $e) + { + $this->core->error->add($e->getMessage()); + } + } + + if ($blog->settings->akismet->ak_key) + { + try { + $ak = new akismet($blog->url,$blog->settings->akismet->ak_key); + $ak_verified = $ak->verify(); + } catch (Exception $e) { + $this->core->error->add($e->getMessage()); + } + } + + $res = + '
        '. + '

        '; + + if ($ak_verified !== null) { + if ($ak_verified) { + $res .= ' '.__('API key verified'); + } else { + $res .= ' '.__('API key not verified'); + } + } + + $res .= '

        '; + + $res .= + '

        '.__('Get your own API key').'

        '. + '

        '. + $this->core->formNonce().'

        '. + '
        '; + + return $res; + } +} + +class akismet extends netHttp +{ + protected $base_host = 'rest.akismet.com'; + protected $ak_host = ''; + protected $ak_version = '1.1'; + protected $ak_path = '/%s/%s'; + + protected $ak_key = null; + protected $blog_url; + + protected $timeout = 3; + + public function __construct($blog_url,$api_key) + { + $this->blog_url = $blog_url; + $this->ak_key = $api_key; + + $this->ak_path = sprintf($this->ak_path,$this->ak_version,'%s'); + $this->ak_host = $this->ak_key.'.'.$this->base_host; + + parent::__construct($this->ak_host,80); + } + + public function verify() + { + $this->host = $this->base_host; + $path = sprintf($this->ak_path,'verify-key'); + + $data = array( + 'key' => $this->ak_key, + 'blog' => $this->blog_url + ); + + if ($this->post($path,$data,'UTF-8')) + { + return $this->getContent() == 'valid'; + } + + return false; + } + + public function comment_check($permalink,$type,$author,$email,$url,$content) + { + $info_ignore = array('HTTP_COOKIE'); + $info = array(); + + foreach ($_SERVER as $k => $v) { + if (strpos($k,'HTTP_') === 0 && !in_array($k,$info_ignore)) { + $info[$k] = $v; + } + } + + return $this->callFunc('comment-check',$permalink,$type,$author,$email,$url,$content,$info); + } + + public function submit_spam($permalink,$type,$author,$email,$url,$content) + { + $this->callFunc('submit-spam',$permalink,$type,$author,$email,$url,$content); + return true; + } + + public function submit_ham($permalink,$type,$author,$email,$url,$content) + { + $this->callFunc('submit-ham',$permalink,$type,$author,$email,$url,$content); + return true; + } + + protected function callFunc($function,$permalink,$type,$author,$email,$url,$content,$info=array()) + { + $ua = isset($info['HTTP_USER_AGENT']) ? $info['HTTP_USER_AGENT'] : ''; + $referer = isset($info['HTTP_REFERER']) ? $info['HTTP_REFERER'] : ''; + + # Prepare comment data + $data = array( + 'blog' => $this->blog_url, + 'user_ip' => http::realIP(), + 'user_agent' => $ua, + 'referrer' => $referer, + 'permalink' => $permalink, + 'comment_type' => $type, + 'comment_author' => $author, + 'comment_author_email' => $email, + 'comment_author_url' => $url, + 'comment_content' => $content + ); + + $data = array_merge($data,$info); + + $this->host = $this->ak_host; + $path = sprintf($this->ak_path,$function); + + if (!$this->post($path,$data,'UTF-8')) { + throw new Exception('HTTP error: '.$this->getError()); + } + + return $this->getContent() == 'true'; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/_admin.php b/v2/dotclear/plugins/antispam/_admin.php new file mode 100644 index 0000000..838301d --- /dev/null +++ b/v2/dotclear/plugins/antispam/_admin.php @@ -0,0 +1,73 @@ +addItem(__('Antispam'),'plugin.php?p=antispam','index.php?pf=antispam/icon.png', + preg_match('/plugin.php\?p=antispam(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id)); + +$core->addBehavior('coreAfterCommentUpdate',array('dcAntispam','trainFilters')); +$core->addBehavior('adminAfterCommentDesc',array('dcAntispam','statusMessage')); +$core->addBehavior('adminDashboardIcons',array('dcAntispam','dashboardIcon')); + +$core->addBehavior('adminDashboardFavs','antispamDashboardFavs'); +$core->addBehavior('adminDashboardFavsIcon','antispamDashboardFavsIcon'); + +function antispamDashboardFavs($core,$favs) +{ + $favs['antispam'] = new ArrayObject(array('antispam','Antispam','plugin.php?p=antispam', + 'index.php?pf=antispam/icon.png','index.php?pf=antispam/icon-big.png', + 'admin',null,null)); +} + +function antispamDashboardFavsIcon($core,$name,$icon) +{ + // Check if it is comments favs + if ($name == 'comments') { + // Hack comments title if there is at least one spam + $str = dcAntispam::dashboardIconTitle($core); + if ($str != '') { + $icon[0] .= $str; + } + } +} + +if (!DC_ANTISPAM_CONF_SUPER || $core->auth->isSuperAdmin()) { + $core->addBehavior('adminBlogPreferencesForm',array('antispamBehaviors','adminBlogPreferencesForm')); + $core->addBehavior('adminBeforeBlogSettingsUpdate',array('antispamBehaviors','adminBeforeBlogSettingsUpdate')); +} + +class antispamBehaviors +{ + public static function adminBlogPreferencesForm($core,$settings) + { + $ttl = $settings->antispam->antispam_moderation_ttl; + echo + '
        Antispam'. + '

        '. + '
        '; + } + + public static function adminBeforeBlogSettingsUpdate($settings) + { + $settings->addNamespace('antispam'); + $settings->antispam->put('antispam_moderation_ttl',(integer)$_POST['antispam_moderation_ttl']); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/_define.php b/v2/dotclear/plugins/antispam/_define.php new file mode 100644 index 0000000..554194a --- /dev/null +++ b/v2/dotclear/plugins/antispam/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "Antispam", + /* Description*/ "Generic antispam plugin for Dotclear", + /* Author */ "Alain Vagner", + /* Version */ '1.3.1', + array( + 'permissions' => 'usage,contentadmin', + 'priority' => 10 + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/_install.php b/v2/dotclear/plugins/antispam/_install.php new file mode 100644 index 0000000..ca13f6b --- /dev/null +++ b/v2/dotclear/plugins/antispam/_install.php @@ -0,0 +1,56 @@ +plugins->moduleInfo('antispam','version'); +if (version_compare($core->getVersion('antispam'),$version,'>=')) { + return; +} + +/* Database schema +-------------------------------------------------------- */ +$s = new dbStruct($core->con,$core->prefix); + +$s->spamrule + ->rule_id ('bigint', 0, false) + ->blog_id ('varchar', 32, true) + ->rule_type ('varchar', 16, false, "'word'") + ->rule_content ('varchar', 128, false) + + ->primary('pk_spamrule','rule_id') + ; + +$s->spamrule->index('idx_spamrule_blog_id','btree','blog_id'); +$s->spamrule->reference('fk_spamrule_blog','blog_id','blog','blog_id','cascade','cascade'); + +if ($s->driver() == 'pgsql') { + $s->spamrule->index('idx_spamrule_blog_id_null','btree','(blog_id IS NULL)'); +} + +# Schema installation +$si = new dbStruct($core->con,$core->prefix); +$changes = $si->synchronize($s); + +# Creating default wordslist +if ($core->getVersion('antispam') === null) { + $_o = new dcFilterWords($core); + $_o->defaultWordsList(); + unset($_o); +} + +$settings = new dcSettings($core,null); +$settings->addNamespace('antispam'); +$settings->antispam->put('antispam_moderation_ttl',0,'integer','Antispam Moderation TTL (days)',false); + +$core->setVersion('antispam',$version); +return true; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/_prepend.php b/v2/dotclear/plugins/antispam/_prepend.php new file mode 100644 index 0000000..1dba929 --- /dev/null +++ b/v2/dotclear/plugins/antispam/_prepend.php @@ -0,0 +1,30 @@ +spamfilters = array('dcFilterIP','dcFilterWords','dcFilterIpLookup','dcFilterLinksLookup'); + +$core->url->register('spamfeed','spamfeed','^spamfeed/(.+)$',array('dcAntispamURL','spamFeed')); +$core->url->register('hamfeed','hamfeed','^hamfeed/(.+)$',array('dcAntispamURL','hamFeed')); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/_public.php b/v2/dotclear/plugins/antispam/_public.php new file mode 100644 index 0000000..c3c3d97 --- /dev/null +++ b/v2/dotclear/plugins/antispam/_public.php @@ -0,0 +1,17 @@ +addBehavior('publicBeforeCommentCreate',array('dcAntispam','isSpam')); +$core->addBehavior('publicBeforeTrackbackCreate',array('dcAntispam','isSpam')); +$core->addBehavior('publicBeforeDocument',array('dcAntispam','purgeOldSpam')); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/antispam.js b/v2/dotclear/plugins/antispam/antispam.js new file mode 100644 index 0000000..8f243e9 --- /dev/null +++ b/v2/dotclear/plugins/antispam/antispam.js @@ -0,0 +1,3 @@ + +var dragsort=ToolMan.dragsort();$(function(){$("#filters-list").each(function(){dragsort.makeTableSortable(this,dotclear.sortable.setHandle,dotclear.sortable.saveOrder);});});dotclear.sortable={setHandle:function(item){var handle=$(item).find('td.handle').get(0);while(handle.firstChild){handle.removeChild(handle.firstChild);} +item.toolManDragGroup.setHandle(handle);$(handle).addClass('handler');},saveOrder:function(item){var group=item.toolManDragGroup;var order=$('#filters_order').get(0);group.register('dragend',function(){order.value='';items=item.parentNode.getElementsByTagName('tr');for(var i=0;icon =& $core->con; + $this->table = $core->prefix.'spamrule'; + } + + protected function setInfo() + { + $this->description = __('IP Blacklist / Whitelist Filter'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with rule %2$s.'),$this->guiLink(),$status); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (!$ip) { + return; + } + + # White list check + if ($this->checkIP($ip,'white') !== false) { + return false; + } + + # Black list check + if (($s = $this->checkIP($ip,'black')) !== false) { + $status = $s; + return true; + } + } + + public function gui($url) + { + global $default_tab; + $core =& $this->core; + + # Set current type and tab + $ip_type = 'black'; + if (!empty($_REQUEST['ip_type']) && $_REQUEST['ip_type'] == 'white') { + $ip_type = 'white'; + } + $default_tab = 'tab_'.$ip_type; + + # Add IP to list + if (!empty($_POST['addip'])) + { + try + { + $global = !empty($_POST['globalip']) && $core->auth->isSuperAdmin(); + + $this->addIP($ip_type,$_POST['addip'],$global); + http::redirect($url.'&added=1&ip_type='.$ip_type); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + + # Remove IP from list + if (!empty($_POST['delip']) && is_array($_POST['delip'])) + { + try { + $this->removeRule($_POST['delip']); + http::redirect($url.'&removed=1&ip_type='.$ip_type); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + /* DISPLAY + ---------------------------------------------- */ + $res = ''; + + if (!empty($_GET['added'])) { + $res .= '

        '.__('IP address has been successfully added.').'

        '; + } + if (!empty($_GET['removed'])) { + $res .= '

        '.__('IP addresses have been successfully removed.').'

        '; + } + + $res .= + $this->displayForms($url,'black',__('Blacklist')). + $this->displayForms($url,'white',__('Whitelist')); + + return $res; + } + + private function displayForms($url,$type,$title) + { + $core =& $this->core; + + $res = + '
        '. + + '
        '. + form::hidden(array('ip_type'),$type). + ''; + if ($core->auth->isSuperAdmin()) { + $res .= ' '; + } + + $res .= + $core->formNonce(). + '

        '. + '

        '. + '
        '; + + $rs = $this->getRules($type); + + if ($rs->isEmpty()) + { + $res .= '

        '.__('No IP address in list.').'

        '; + } + else + { + $res .= + '
        '. + '

        ' . __('IP list') . '

        '. + '
        '; + + while ($rs->fetch()) + { + $bits = explode(':',$rs->rule_content); + $pattern = $bits[0]; + $ip = $bits[1]; + $bitmask = $bits[2]; + + $disabled_ip = false; + $p_style = $this->style_p; + if (!$rs->blog_id) { + $disabled_ip = !$core->auth->isSuperAdmin(); + $p_style .= $this->style_global; + } + + $res .= + '

        '; + } + $res .= + '
        '. + '

        '. + $core->formNonce(). + form::hidden(array('ip_type'),$type). + '

        '. + '
        '; + } + + $res .= '
        '; + + return $res; + } + + private function ipmask($pattern,&$ip,&$mask) + { + $bits = explode('/',$pattern); + + # Set IP + $bits[0] .= str_repeat(".0", 3 - substr_count($bits[0], ".")); + $ip = ip2long($bits[0]); + + if (!$ip || $ip == -1) { + throw new Exception('Invalid IP address'); + } + + # Set mask + if (!isset($bits[1])) { + $mask = -1; + } elseif (strpos($bits[1],'.')) { + $mask = ip2long($bits[1]); + if (!$mask) { + $mask = -1; + } + } else { + $mask = ~((1 << (32 - $bits[1])) - 1); + } + } + + private function addIP($type,$pattern,$global) + { + $this->ipmask($pattern,$ip,$mask); + $pattern = long2ip($ip).($mask != -1 ? '/'.long2ip($mask) : ''); + $content = $pattern.':'.$ip.':'.$mask; + + $old = $this->getRuleCIDR($type,$global,$ip,$mask); + $cur = $this->con->openCursor($this->table); + + if ($old->isEmpty()) + { + $id = $this->con->select('SELECT MAX(rule_id) FROM '.$this->table)->f(0) + 1; + + $cur->rule_id = $id; + $cur->rule_type = (string) $type; + $cur->rule_content = (string) $content; + + if ($global && $this->core->auth->isSuperAdmin()) { + $cur->blog_id = null; + } else { + $cur->blog_id = $this->core->blog->id; + } + + $cur->insert(); + } + else + { + $cur->rule_type = (string) $type; + $cur->rule_content = (string) $content; + $cur->update('WHERE rule_id = '.(integer) $old->rule_id); + } + } + + private function getRules($type='all') + { + $strReq = + 'SELECT rule_id, rule_type, blog_id, rule_content '. + 'FROM '.$this->table.' '. + "WHERE rule_type = '".$this->con->escape($type)."' ". + "AND (blog_id = '".$this->core->blog->id."' OR blog_id IS NULL) ". + 'ORDER BY blog_id ASC, rule_content ASC '; + + return $this->con->select($strReq); + } + + private function getRuleCIDR($type,$global,$ip,$mask) + { + $strReq = + 'SELECT * FROM '.$this->table.' '. + "WHERE rule_type = '".$this->con->escape($type)."' ". + "AND rule_content LIKE '%:".(integer) $ip.":".(integer) $mask."' ". + 'AND blog_id '.($global ? 'IS NULL ' : "= '".$this->core->blog->id."' "); + + return $this->con->select($strReq); + } + + private function checkIP($cip,$type) + { + $core =& $this->core; + + $strReq = + 'SELECT DISTINCT(rule_content) '. + 'FROM '.$this->table.' '. + "WHERE rule_type = '".$this->con->escape($type)."' ". + "AND (blog_id = '".$this->core->blog->id."' OR blog_id IS NULL) ". + 'ORDER BY rule_content ASC '; + + $rs = $this->con->select($strReq); + while ($rs->fetch()) + { + list($pattern,$ip,$mask) = explode(':',$rs->rule_content); + if ((ip2long($cip) & (integer) $mask) == ((integer) $ip & (integer) $mask)) { + return $pattern; + } + } + return false; + } + + private function removeRule($ids) + { + $strReq = 'DELETE FROM '.$this->table.' '; + + if (is_array($ids)) { + foreach ($ids as $i => $v) { + $ids[$i] = (integer) $v; + } + $strReq .= 'WHERE rule_id IN ('.implode(',',$ids).') '; + } else { + $ids = (integer) $ids; + $strReq .= 'WHERE rule_id = '.$ids.' '; + } + + if (!$this->core->auth->isSuperAdmin()) { + $strReq .= "AND blog_id = '".$this->core->blog->id."' "; + } + + $this->con->execute($strReq); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/filters/class.dc.filter.iplookup.php b/v2/dotclear/plugins/antispam/filters/class.dc.filter.iplookup.php new file mode 100644 index 0000000..540ae7b --- /dev/null +++ b/v2/dotclear/plugins/antispam/filters/class.dc.filter.iplookup.php @@ -0,0 +1,121 @@ +auth->isSuperAdmin()) { + $this->has_gui = false; + } + } + + protected function setInfo() + { + $this->description = __('Checks sender IP address against DNSBL servers'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with server %2$s.'),$this->guiLink(),$status); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (!$ip || long2ip(ip2long($ip)) != $ip) { + return; + } + + $match = array(); + + $bls = $this->getServers(); + $bls = preg_split('/\s*,\s*/',$bls); + + foreach ($bls as $bl) + { + if ($this->dnsblLookup($ip,$bl)) { + $match[] = $bl; + } + } + + if (!empty($match)) { + $status = substr(implode(', ',$match),0,128); + return true; + } + } + + public function gui($url) + { + $bls = $this->getServers(); + + if (isset($_POST['bls'])) + { + try { + $this->core->blog->settings->addNamespace('antispam'); + $this->core->blog->settings->antispam->put('antispam_dnsbls',$_POST['bls'],'string','Antispam DNSBL servers',true,false); + http::redirect($url.'&upd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + /* DISPLAY + ---------------------------------------------- */ + $res = ''; + + $res .= + '
        '. + '
        ' . __('IP Lookup servers') . ''. + '

        '. + '

        '. + $this->core->formNonce().'

        '. + '
        '. + '
        '; + + return $res; + } + + private function getServers() + { + $bls = $this->core->blog->settings->antispam->antispam_dnsbls; + if ($bls === null) { + $this->core->blog->settings->addNamespace('antispam'); + $this->core->blog->settings->antispam->put('antispam_dnsbls',$this->default_bls,'string','Antispam DNSBL servers',true,false); + return $this->default_bls; + } + + return $bls; + } + + private function dnsblLookup($ip,$bl) + { + $revIp = implode('.',array_reverse(explode('.',$ip))); + + $host = $revIp.'.'.$bl.'.'; + if (gethostbyname($host) != $host) { + return true; + } + + return false; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/filters/class.dc.filter.linkslookup.php b/v2/dotclear/plugins/antispam/filters/class.dc.filter.linkslookup.php new file mode 100644 index 0000000..7bd87a0 --- /dev/null +++ b/v2/dotclear/plugins/antispam/filters/class.dc.filter.linkslookup.php @@ -0,0 +1,74 @@ +description = __('Checks links in comments against surbl.org'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with server %2$s.'),$this->guiLink(),$status); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if (!$ip || long2ip(ip2long($ip)) != $ip) { + return; + } + + $urls = $this->getLinks($content); + array_unshift($urls,$site); + + foreach ($urls as $u) + { + $b = parse_url($u); + if (!isset($b['host']) || !$b['host']) { + continue; + } + + $domain = preg_replace('/^(.*\.)([^.]+\.[^.]+)$/','$2',$b['host']); + $host = $domain.'.'.$this->server; + + if (gethostbyname($host) != $host) { + $status = substr($domain,0,128); + return true; + } + } + } + + private function getLinks($text) + { + $res = array(); + + # href attribute on "a" tags + if (preg_match_all('/]+)>/ms', $text, $match, PREG_SET_ORDER)) + { + for ($i = 0; $i \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/filters/class.dc.filter.words.php b/v2/dotclear/plugins/antispam/filters/class.dc.filter.words.php new file mode 100644 index 0000000..5444bce --- /dev/null +++ b/v2/dotclear/plugins/antispam/filters/class.dc.filter.words.php @@ -0,0 +1,356 @@ +con =& $core->con; + $this->table = $core->prefix.'spamrule'; + } + + protected function setInfo() + { + $this->description = __('Words Blacklist'); + } + + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s with word %2$s.'),$this->guiLink(),''.$status.''); + } + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + $str = $author.' '.$email.' '.$site.' '.$content; + + $rs = $this->getRules(); + + while ($rs->fetch()) + { + $word = $rs->rule_content; + + if (substr($word,0,1) == '/' && substr($word,-1,1) == '/') { + $reg = substr(substr($word,1),0,-1); + } else { + $reg = preg_quote($word, '/'); + $reg = '(^|\s+|>|<)'.$reg.'(>|<|\s+|\.|$)'; + } + + if (preg_match('/'.$reg.'/msiu',$str)) { + $status = $word; + return true; + } + } + } + + public function gui($url) + { + $core =& $this->core; + + # Create list + if (!empty($_POST['createlist'])) + { + try { + $this->defaultWordsList(); + http::redirect($url.'&list=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Adding a word + if (!empty($_POST['swa'])) + { + $globalsw = !empty($_POST['globalsw']) && $core->auth->isSuperAdmin(); + + try { + $this->addRule($_POST['swa'],$globalsw); + http::redirect($url.'&added=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Removing spamwords + if (!empty($_POST['swd']) && is_array($_POST['swd'])) + { + try { + $this->removeRule($_POST['swd']); + http::redirect($url.'&removed=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + /* DISPLAY + ---------------------------------------------- */ + $res = ''; + + if (!empty($_GET['list'])) { + $res .= '

        '.__('Words have been successfully added.').'

        '; + } + if (!empty($_GET['added'])) { + $res .= '

        '.__('Word has been successfully added.').'

        '; + } + if (!empty($_GET['removed'])) { + $res .= '

        '.__('Words have been successfully removed.').'

        '; + } + + $res .= + '
        '. + '

        '; + + if ($core->auth->isSuperAdmin()) { + $res .= ' '; + } + + $res .= + $core->formNonce(). + '

        '. + '

        '. + '
        '; + + $rs = $this->getRules(); + if ($rs->isEmpty()) + { + $res .= '

        '.__('No word in list.').'

        '; + } + else + { + $res .= + '
        '. + '

        ' . __('List of bad words') . '

        '. + '
        '; + + while ($rs->fetch()) + { + $disabled_word = false; + $p_style = $this->style_p; + if (!$rs->blog_id) { + $disabled_word = !$core->auth->isSuperAdmin(); + $p_style .= $this->style_global; + } + + $res .= + '

        '; + } + + $res .= + '
        '. + '

        '.form::hidden(array('spamwords'),1). + $core->formNonce(). + '

        '. + '
        '; + } + + if ($core->auth->isSuperAdmin()) + { + $res .= + '
        '. + '

        '. + form::hidden(array('spamwords'),1). + form::hidden(array('createlist'),1). + $core->formNonce().'

        '. + '
        '; + } + + return $res; + } + + private function getRules() + { + $strReq = 'SELECT rule_id, blog_id, rule_content '. + 'FROM '.$this->table.' '. + "WHERE rule_type = 'word' ". + "AND ( blog_id = '".$this->con->escape($this->core->blog->id)."' ". + "OR blog_id IS NULL ) ". + 'ORDER BY blog_id ASC, rule_content ASC '; + + return $this->con->select($strReq); + } + + private function addRule($content,$general=false) + { + $strReq = 'SELECT rule_id FROM '.$this->table.' '. + "WHERE rule_type = 'word' ". + "AND rule_content = '".$this->con->escape($content)."' "; + $rs = $this->con->select($strReq); + + if (!$rs->isEmpty()) { + throw new Exception(__('This word exists')); + } + + $rs = $this->con->select('SELECT MAX(rule_id) FROM '.$this->table); + $id = (integer) $rs->f(0) + 1; + + $cur = $this->con->openCursor($this->table); + $cur->rule_id = $id; + $cur->rule_type = 'word'; + $cur->rule_content = (string) $content; + + if ($general && $this->core->auth->isSuperAdmin()) { + $cur->blog_id = null; + } else { + $cur->blog_id = $this->core->blog->id; + } + + $cur->insert(); + } + + private function removeRule($ids) + { + $strReq = 'DELETE FROM '.$this->table.' '; + + if (is_array($ids)) { + foreach ($ids as &$v) { + $v = (integer) $v; + } + $strReq .= 'WHERE rule_id IN ('.implode(',',$ids).') '; + } else { + $ids = (integer) $ids; + $strReq .= 'WHERE rule_id = '.$ids.' '; + } + + if (!$this->core->auth->isSuperAdmin()) { + $strReq .= "AND blog_id = '".$this->con->escape($this->core->blog->id)."' "; + } + + $this->con->execute($strReq); + } + + public function defaultWordsList() + { + $words = array( + '/-credit(\s+|$)/', + '/-digest(\s+|$)/', + '/-loan(\s+|$)/', + '/-online(\s+|$)/', + '4u', + 'adipex', + 'advicer', + 'ambien', + 'baccarat', + 'baccarrat', + 'blackjack', + 'bllogspot', + 'bolobomb', + 'booker', + 'byob', + 'car-rental-e-site', + 'car-rentals-e-site', + 'carisoprodol', + 'cash', + 'casino', + 'casinos', + 'chatroom', + 'cialis', + 'craps', + 'credit-card', + 'credit-report-4u', + 'cwas', + 'cyclen', + 'cyclobenzaprine', + 'dating-e-site', + 'day-trading', + 'debt', + 'digest-', + 'discount', + 'discreetordering', + 'duty-free', + 'dutyfree', + 'estate', + 'favourits', + 'fioricet', + 'flowers-leading-site', + 'freenet', + 'freenet-shopping', + 'gambling', + 'gamias', + 'health-insurancedeals-4u', + 'holdem', + 'holdempoker', + 'holdemsoftware', + 'holdemtexasturbowilson', + 'hotel-dealse-site', + 'hotele-site', + 'hotelse-site', + 'incest', + 'insurance-quotesdeals-4u', + 'insurancedeals-4u', + 'jrcreations', + 'levitra', + 'macinstruct', + 'mortgage', + 'online-gambling', + 'onlinegambling-4u', + 'ottawavalleyag', + 'ownsthis', + 'palm-texas-holdem-game', + 'paxil', + 'pharmacy', + 'phentermine', + 'pills', + 'poker', + 'poker-chip', + 'poze', + 'prescription', + 'rarehomes', + 'refund', + 'rental-car-e-site', + 'roulette', + 'shemale', + 'slot', + 'slot-machine', + 'soma', + 'taboo', + 'tamiflu', + 'texas-holdem', + 'thorcarlson', + 'top-e-site', + 'top-site', + 'tramadol', + 'trim-spa', + 'ultram', + 'v1h', + 'vacuum', + 'valeofglamorganconservatives', + 'viagra', + 'vicodin', + 'vioxx', + 'xanax', + 'zolus' + ); + + foreach ($words as $w) { + try { + $this->addRule($w,true); + } catch (Exception $e) {} + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/icon-big.png b/v2/dotclear/plugins/antispam/icon-big.png new file mode 100644 index 0000000..5a9f0f4 Binary files /dev/null and b/v2/dotclear/plugins/antispam/icon-big.png differ diff --git a/v2/dotclear/plugins/antispam/icon.png b/v2/dotclear/plugins/antispam/icon.png new file mode 100644 index 0000000..bf069cc Binary files /dev/null and b/v2/dotclear/plugins/antispam/icon.png differ diff --git a/v2/dotclear/plugins/antispam/inc/class.dc.spamfilter.php b/v2/dotclear/plugins/antispam/inc/class.dc.spamfilter.php new file mode 100644 index 0000000..59f0fe2 --- /dev/null +++ b/v2/dotclear/plugins/antispam/inc/class.dc.spamfilter.php @@ -0,0 +1,161 @@ +dcCore Dotclear core object + */ + public function __construct($core) + { + $this->core =& $core; + $this->setInfo(); + + if (!$this->name) { + $this->name = get_class($this); + } + + $this->gui_url = 'plugin.php?p=antispam&f='.get_class($this); + } + + /** + This method is called by the constructor and allows you to change some + object properties without overloading object constructor. + */ + protected function setInfo() + { + $this->description = __('No description'); + } + + /** + This method should return if a comment is a spam or not. If it returns true + or false, execution of next filters will be stoped. If should return nothing + to let next filters apply. + + Your filter should also fill $status variable with its own information if + comment is a spam. + + @param type string Comment type (comment or trackback) + @param author string Comment author + @param email string Comment author email + @param site string Comment author website + @param ip string Comment author IP address + @param content string Comment content + @param post_id integer Comment post_id + @param[out] status integer Comment status + @return boolean + */ + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + } + + /** + This method is called when a non-spam (ham) comment becomes spam or when a + spam becomes a ham. + + @param type string Comment type (comment or trackback) + @param filter string Filter name + @param author string Comment author + @param email string Comment author email + @param site string Comment author website + @param ip string Comment author IP address + @param content string Comment content + @param post_url string Post URL + @param rs record Comment record + @return boolean + */ + public function trainFilter($status,$filter,$type,$author,$email,$site,$ip,$content,$rs) + { + } + + /** + This method returns filter status message. You can overload this method to + return a custom message. Message is shown in comment details and in + comments list. + + @param status string Filter status. + @param comment_id record Comment record + @return string + */ + public function getStatusMessage($status,$comment_id) + { + return sprintf(__('Filtered by %1$s (%2$s)'),$this->guiLink(),$status); + } + + /** + This method is called when you enter filter configuration. Your class should + have $has_gui property set to "true" to enable GUI. + + In this method you should put everything related to filter configuration. + $url variable is the URL of GUI unescaped. + + @param url string GUI URL. + */ + public function gui($url) + { + } + + public function hasGUI() + { + if (!$this->core->auth->check('admin',$this->core->blog->id)) { + return false; + } + + if (!$this->has_gui) { + return false; + } + + return true; + } + + public function guiURL() + { + if (!$this->hasGui()) { + return false; + } + + return $this->gui_url; + } + + /** + Returns a link to filter GUI if exists or only filter name if has_gui + property is false. + + @return string + */ + public function guiLink() + { + if (($url = $this->guiURL()) !== false) { + $url = html::escapeHTML($url); + $link = '
        %1$s'; + } else { + $link = '%1$s'; + } + + return sprintf($link,$this->name,$url); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/inc/class.dc.spamfilters.php b/v2/dotclear/plugins/antispam/inc/class.dc.spamfilters.php new file mode 100644 index 0000000..2983aaa --- /dev/null +++ b/v2/dotclear/plugins/antispam/inc/class.dc.spamfilters.php @@ -0,0 +1,164 @@ +core =& $core; + } + + public function init($filters) + { + foreach ($filters as $f) + { + if (!class_exists($f)) { + continue; + } + + $r = new ReflectionClass($f); + $p = $r->getParentClass(); + + if (!$p || $p->name != 'dcSpamFilter') { + continue; + } + + $this->filters[$f] = new $f($this->core); + } + + $this->setFilterOpts(); + if (!empty($this->filters_opt)) { + uasort($this->filters,array($this,'orderCallBack')); + } + } + + public function getFilters() + { + return $this->filters; + } + + public function isSpam($cur) + { + foreach ($this->filters as $fid => $f) + { + if (!$f->active) { + continue; + } + + $type = $cur->comment_trackback ? 'trackback' : 'comment'; + $author = $cur->comment_author; + $email = $cur->comment_email; + $site = $cur->comment_site; + $ip = $cur->comment_ip; + $content = $cur->comment_content; + $post_id = $cur->post_id; + + $is_spam = $f->isSpam($type,$author,$email,$site,$ip,$content,$post_id,$status); + + if ($is_spam === true) { + if ($f->auto_delete) { + $cur->clean(); + } else { + $cur->comment_status = -2; + $cur->comment_spam_status = $status; + $cur->comment_spam_filter = $fid; + } + return true; + } elseif ($is_spam === false) { + return false; + } + } + + return false; + } + + public function trainFilters($rs,$status,$filter_name) + { + foreach ($this->filters as $fid => $f) + { + if (!$f->active) { + continue; + } + + $type = $rs->comment_trackback ? 'trackback' : 'comment'; + $author = $rs->comment_author; + $email = $rs->comment_email; + $site = $rs->comment_site; + $ip = $rs->comment_ip; + $content = $rs->comment_content; + + $f->trainFilter($status,$filter_name,$type,$author,$email,$site,$ip,$content,$rs); + } + } + + public function statusMessage($rs,$filter_name) + { + $f = isset($this->filters[$filter_name]) ? $this->filters[$filter_name] : null; + + if ($f === null) + { + return __('Unknown filter.'); + } + else + { + $status = $rs->exists('comment_spam_status') ? $rs->comment_spam_status : null; + + return $f->getStatusMessage($status,$rs->comment_id); + } + } + + public function saveFilterOpts($opts,$global=false) + { + $this->core->blog->settings->addNamespace('antispam'); + if ($global) { + $this->core->blog->settings->antispam->drop('antispam_filters'); + } + $this->core->blog->settings->antispam->put('antispam_filters',serialize($opts),'string','Antispam Filters',true,$global); + } + + private function setFilterOpts() + { + if ($this->core->blog->settings->antispam->antispam_filters !== null) { + $this->filters_opt = @unserialize($this->core->blog->settings->antispam->antispam_filters); + } + + # Create default options if needed + if (!is_array($this->filters_opt)) { + $this->saveFilterOpts(array(),true); + $this->filters_opt = array(); + } + + foreach ($this->filters_opt as $k => $o) + { + if (isset($this->filters[$k]) && is_array($o)) { + $this->filters[$k]->active = isset($o[0])?$o[0]:false; + $this->filters[$k]->order = isset($o[1])?$o[1]:0; + $this->filters[$k]->auto_delete = isset($o[2])?$o[2]:false; + } + } + } + + private function orderCallBack($a,$b) + { + if ($a->order == $b->order) { + return 0; + } + + return $a->order > $b->order ? 1 : -1; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/inc/lib.dc.antispam.php b/v2/dotclear/plugins/antispam/inc/lib.dc.antispam.php new file mode 100644 index 0000000..fe788a1 --- /dev/null +++ b/v2/dotclear/plugins/antispam/inc/lib.dc.antispam.php @@ -0,0 +1,211 @@ +spamfilters)) { + return; + } + + self::$filters = new dcSpamFilters($core); + self::$filters->init($core->spamfilters); + } + + public static function isSpam($cur) + { + self::initFilters(); + self::$filters->isSpam($cur); + } + + public static function trainFilters($blog,$cur,$rs) + { + $status = null; + # From ham to spam + if ($rs->comment_status != -2 && $cur->comment_status == -2) { + $status = 'spam'; + } + + # From spam to ham + if ($rs->comment_status == -2 && $cur->comment_status == 1) { + $status = 'ham'; + } + + # the status of this comment has changed + if ($status) + { + $filter_name = $rs->exists('comment_spam_filter') ? $rs->comment_spam_filter : null; + + self::initFilters(); + self::$filters->trainFilters($rs,$status,$filter_name); + } + } + + public static function statusMessage($rs) + { + if ($rs->exists('comment_status') && $rs->comment_status == -2) + { + $filter_name = $rs->exists('comment_spam_filter') ? $rs->comment_spam_filter : null; + + self::initFilters(); + + return + '

        '.__('This comment is a spam:').' '. + self::$filters->statusMessage($rs,$filter_name).'

        '; + } + } + + public static function dashboardIcon($core, $icons) + { + if (($count = self::countSpam($core)) > 0) { + $str = ($count > 1) ? __('(including %d spam comments)') : __('(including %d spam comment)'); + $icons['comments'][0] .= '
        '.sprintf($str,$count).''; + } + } + + public static function dashboardIconTitle($core) + { + if (($count = self::countSpam($core)) > 0) { + $str = ($count > 1) ? __('(including %d spam comments)') : __('(including %d spam comment)'); + return '
        '.sprintf($str,$count).''; + } else { + return ''; + } + } + + public static function countSpam($core) + { + return $core->blog->getComments(array('comment_status'=>-2),true)->f(0); + } + + public static function countPublishedComments($core) + { + return $core->blog->getComments(array('comment_status'=>1),true)->f(0); + } + + public static function delAllSpam($core, $beforeDate = null) + { + $strReq = + 'SELECT comment_id '. + 'FROM '.$core->prefix.'comment C '. + 'JOIN '.$core->prefix.'post P ON P.post_id = C.post_id '. + "WHERE blog_id = '".$core->con->escape($core->blog->id)."' ". + 'AND comment_status = -2 '; + if ($beforeDate) { + $strReq .= 'AND comment_dt < \''.$beforeDate.'\' '; + } + + $rs = $core->con->select($strReq); + $r = array(); + while ($rs->fetch()) { + $r[] = (integer) $rs->comment_id; + } + + if (empty($r)) { + return; + } + + $strReq = + 'DELETE FROM '.$core->prefix.'comment '. + 'WHERE comment_id '.$core->con->in($r).' '; + + $core->con->execute($strReq); + } + + public static function getUserCode($core) + { + $code = + pack('a32',$core->auth->userID()). + pack('H*',crypt::hmac(DC_MASTER_KEY,$core->auth->getInfo('user_pwd'))); + return bin2hex($code); + } + + public static function checkUserCode($core,$code) + { + $code = pack('H*',$code); + + $user_id = trim(@pack('a32',substr($code,0,32))); + $pwd = @unpack('H40hex',substr($code,32,40)); + + if ($user_id === false || $pwd === false) { + return false; + } + + $pwd = $pwd['hex']; + + $strReq = 'SELECT user_id, user_pwd '. + 'FROM '.$core->prefix.'user '. + "WHERE user_id = '".$core->con->escape($user_id)."' "; + + $rs = $core->con->select($strReq); + + if ($rs->isEmpty()) { + return false; + } + + if (crypt::hmac(DC_MASTER_KEY,$rs->user_pwd) != $pwd) { + return false; + } + + $permissions = $core->getBlogPermissions($core->blog->id); + + if ( empty($permissions[$rs->user_id]) ) { + return false; + } + + return $rs->user_id; + } + + public static function purgeOldSpam($core) + { + $defaultDateLastPurge = time(); + $defaultModerationTTL = '7'; + $init = false; + + // settings + $core->blog->settings->addNamespace('antispam'); + + $dateLastPurge = $core->blog->settings->antispam->antispam_date_last_purge; + if ($dateLastPurge === null) { + $init = true; + $core->blog->settings->antispam->put('antispam_date_last_purge',$defaultDateLastPurge,'integer','Antispam Date Last Purge (unix timestamp)',true,false); + $dateLastPurge = $defaultDateLastPurge; + } + $moderationTTL = $core->blog->settings->antispam->antispam_moderation_ttl; + if ($moderationTTL === null) { + $core->blog->settings->antispam->put('antispam_moderation_ttl',$defaultModerationTTL,'integer','Antispam Moderation TTL (days)',true,false); + $moderationTTL = $defaultModerationTTL; + } + + if ($moderationTTL < 0) { + // disabled + return; + } + + // we call the purge every day + if ((time()-$dateLastPurge) > (86400)) { + // update dateLastPurge + if (!$init) { + $core->blog->settings->antispam->put('antispam_date_last_purge',time(),null,null,true,false); + } + $date = date('Y-m-d H:i:s', time() - $moderationTTL*86400); + dcAntispam::delAllSpam($core, $date); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/inc/lib.dc.antispam.url.php b/v2/dotclear/plugins/antispam/inc/lib.dc.antispam.url.php new file mode 100644 index 0000000..4161fb2 --- /dev/null +++ b/v2/dotclear/plugins/antispam/inc/lib.dc.antispam.url.php @@ -0,0 +1,99 @@ +auth->checkUser($user_id,null,null); + + header('Content-Type: application/xml; charset=UTF-8'); + + $title = $core->blog->name.' - '.__('Spam moderation'). ' - '; + $params = array(); + $end_url = ''; + if ($type == 'spam') { + $title .= __('Spam'); + $params['comment_status'] = -2; + $end_url = '?status=-2'; + } else { + $title .= __('Ham'); + $params['sql'] = ' AND comment_status IN (1,-1) '; + } + + echo + ''."\n". + ''."\n". + ''."\n". + ''.html::escapeHTML($title).''."\n". + ''.(DC_ADMIN_URL ? DC_ADMIN_URL.'comments.php'.$end_url : 'about:blank').''."\n". + ''."\n"; + + $rs = $core->blog->getComments($params); + $maxitems = 20; + $nbitems = 0; + + while ($rs->fetch() && ($nbitems < $maxitems)) + { + $nbitems++; + $uri = DC_ADMIN_URL ? DC_ADMIN_URL.'comment.php?id='.$rs->comment_id : 'about:blank'; + $author = $rs->comment_author; + $title = $rs->post_title.' - '.$author; + if ($type == 'spam') { + $title .= '('.$rs->comment_spam_filter.')'; + } + $id = $rs->getFeedID(); + + $content = '

        IP: '.$rs->comment_ip; + + if (trim($rs->comment_site)) { + $content .= '
        URL:
        '.$rs->comment_site.''; + } + $content .= "


        \n"; + $content .= $rs->comment_content; + + echo + ''."\n". + ' '.html::escapeHTML($title).''."\n". + ' '.$uri.''."\n". + ' '.$id.''."\n". + ' '.$rs->getRFC822Date().''."\n". + ' '.html::escapeHTML($author).''."\n". + ' '.html::escapeHTML($content).''."\n". + ''; + } + + echo "\n"; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/index.php b/v2/dotclear/plugins/antispam/index.php new file mode 100644 index 0000000..a13eb1b --- /dev/null +++ b/v2/dotclear/plugins/antispam/index.php @@ -0,0 +1,233 @@ +getFilters(); + +$page_name = __('Antispam'); +$filter_gui = false; +$default_tab = null; + +try +{ + # Show filter configuration GUI + if (!empty($_GET['f'])) + { + if (!isset($filters[$_GET['f']])) { + throw new Exception(__('Filter does not exist.')); + } + + if (!$filters[$_GET['f']]->hasGUI()) { + throw new Exception(__('Filter has no user interface.')); + } + + $filter = $filters[$_GET['f']]; + $filter_gui = $filter->gui($filter->guiURL()); + } + + # Remove all spam + if (!empty($_POST['delete_all'])) + { + $ts = dt::str('%Y-%m-%d %H:%M:%S',$_POST['ts'],$core->blog->settings->system->blog_timezone); + + dcAntispam::delAllSpam($core,$ts); + http::redirect($p_url.'&del=1'); + } + + # Update filters + if (isset($_POST['filters_upd'])) + { + $filters_opt = array(); + $i = 0; + foreach ($filters as $fid => $f) { + $filters_opt[$fid] = array(false,$i); + $i++; + } + + # Enable active filters + if (isset($_POST['filters_active']) && is_array($_POST['filters_active'])) { + foreach ($_POST['filters_active'] as $v) { + $filters_opt[$v][0] = true; + } + } + + # Order filters + if (!empty($_POST['f_order']) && empty($_POST['filters_order'])) + { + $order = $_POST['f_order']; + asort($order); + $order = array_keys($order); + } + elseif (!empty($_POST['filters_order'])) + { + $order = explode(',',trim($_POST['filters_order'],',')); + } + + if (isset($order)) { + foreach ($order as $i => $f) { + $filters_opt[$f][1] = $i; + } + } + + # Set auto delete flag + if (isset($_POST['filters_auto_del']) && is_array($_POST['filters_auto_del'])) { + foreach ($_POST['filters_auto_del'] as $v) { + $filters_opt[$v][2] = true; + } + } + + dcAntispam::$filters->saveFilterOpts($filters_opt); + http::redirect($p_url.'&upd=1'); + } +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} +?> + + + <?php echo $page_name; ?> + + + + +'.html::escapeHTML($core->blog->name).' › '.$page_name.''. + ' › '.sprintf(__('%s configuration'),$filter->name).''; + + echo $filter_gui; +} +else +{ + echo '

        '.html::escapeHTML($core->blog->name).' › '.$page_name.'

        '; + + # Information + $spam_count = dcAntispam::countSpam($core); + $published_count = dcAntispam::countPublishedComments($core); + $moderationTTL = $core->blog->settings->antispam->antispam_moderation_ttl; + + echo + '
        '. + '

        '.__('Information').'

        '; + + if (!empty($_GET['del'])) { + echo '

        '.__('Spam comments have been successfully deleted.').'

        '; + } + + echo + ''; + + if ($spam_count > 0) + { + echo + '

        '.$core->formNonce(). + form::hidden('ts',time()). + '

        '; + } + if ($moderationTTL != null && $moderationTTL >=0) { + echo '

        '.sprintf(__('All spam comments older than %s day(s) will be automatically deleted.'), $moderationTTL).' '. + __('You can modify this duration in '). + ' '.__('Blog preferences').'

        '; + } + echo '
        '; + + + # Filters + echo + '
        '; + + if (!empty($_GET['upd'])) { + echo '

        '.__('Filters configuration has been successfully saved.').'

        '; + } + + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + $i = 0; + foreach ($filters as $fid => $f) + { + $gui_link = ' '; + if ($f->hasGUI()) { + $gui_link = + ''. + ''.__('Filter configuration').''; + } + + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''; + $i++; + } + echo + '
        '.__('Available spam filters').'
        '.__('Order').''.__('Active').''.__('Auto Del.').''.__('Filter name').''.__('Description').'
        '.form::field(array('f_order['.$fid.']'),2,5,(string) $i, '', '', false, 'title="'.__('position').'"').''.form::checkbox(array('filters_active[]'),$fid,$f->active, '', '', false, 'title="'.__('Active').'"').''.form::checkbox(array('filters_auto_del[]'),$fid,$f->auto_delete, '', '', false, 'title="'.__('Auto Del.').'"').''.$f->name.''.$f->description.''.$gui_link.'
        '. + '

        '.form::hidden('filters_order',''). + $core->formNonce(). + '

        '. + '
        '; + + + # Syndication + if (DC_ADMIN_URL) + { + $ham_feed = $core->blog->url.$core->url->getURLFor( + 'hamfeed', + $code = dcAntispam::getUserCode($core) + ); + $spam_feed = $core->blog->url.$core->url->getURLFor( + 'spamfeed', + $code = dcAntispam::getUserCode($core) + ); + + echo + '

        '.__('Syndication').'

        '. + ''; + } +} +?> + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/antispam/style.css b/v2/dotclear/plugins/antispam/style.css new file mode 100644 index 0000000..c93cf1a --- /dev/null +++ b/v2/dotclear/plugins/antispam/style.css @@ -0,0 +1,23 @@ +ul.spaminfo { + display: block; + list-style: none; + margin: 1em 0; + padding: 0; +} +ul.spaminfo li { + margin: 0.5em 0; + padding-left: 16px; + /*background: url(index.php?pf=antispam/feed.png) no-repeat center left;*/ + background-color: transparent; + background-position: center left; + background-repeat: no-repeat; +} +ul.spaminfo .spamcount { + background-image: url(images/junk.png); +} +ul.spaminfo .hamcount { + background-image: url(images/check-on.png); +} +ul.spaminfo .feed { + background-image: url(index.php?pf=antispam/feed.png); +} \ No newline at end of file diff --git a/v2/dotclear/plugins/attachments/_admin.php b/v2/dotclear/plugins/attachments/_admin.php new file mode 100644 index 0000000..70ca40f --- /dev/null +++ b/v2/dotclear/plugins/attachments/_admin.php @@ -0,0 +1,75 @@ +addBehavior ('adminPostFormSidebar',array('attachmentAdmin','adminPostFormSidebar')); +$core->addBehavior ('adminPostAfterForm',array('attachmentAdmin','adminPostAfterForm')); + +class attachmentAdmin +{ + public static function adminPostFormSidebar($post) + { + if ($post !== null) + { + $core =& $GLOBALS['core']; + $post_media = $core->media->getPostMedia($post->post_id); + echo + '

        '.__('Attachments').'

        '; + foreach ($post_media as $f) + { + $ftitle = $f->media_title; + if (strlen($ftitle) > 18) { + $ftitle = substr($ftitle,0,16).'...'; + } + echo + '
        '. + ''. + ''. + ''. + '
        '; + } + unset($f); + + if (empty($post_media)) { + echo '

        '.__('No attachment.').'

        '; + } else { + } + echo '

        '.__('Add files to this entry').'

        '; + } + } + + public static function adminPostAfterForm($post) { + if ($post !== null) + { + $core =& $GLOBALS['core']; + echo + '
        '. + '
        '.form::hidden(array('post_id'),$post->post_id). + form::hidden(array('media_id'),''). + form::hidden(array('remove'),1). + $core->formNonce().'
        '; + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/attachments/_define.php b/v2/dotclear/plugins/attachments/_define.php new file mode 100644 index 0000000..99b4d2f --- /dev/null +++ b/v2/dotclear/plugins/attachments/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "attachments", + /* Description*/ "Manage post attachments", + /* Author */ "Dotclear Team", + /* Version */ '1.0', + array( + 'permissions' => 'usage,contentadmin,pages', + 'priority' => 999 + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/attachments/_public.php b/v2/dotclear/plugins/attachments/_public.php new file mode 100644 index 0000000..b08c450 --- /dev/null +++ b/v2/dotclear/plugins/attachments/_public.php @@ -0,0 +1,248 @@ +tpl->addBlock('Attachments',array('attachmentTpl','Attachments')); +$core->tpl->addBlock('AttachmentsHeader',array('attachmentTpl','AttachmentsHeader')); +$core->tpl->addBlock('AttachmentsFooter',array('attachmentTpl','AttachmentsFooter')); +$core->tpl->addValue('AttachmentMimeType',array('attachmentTpl','AttachmentMimeType')); +$core->tpl->addValue('AttachmentType',array('attachmentTpl','AttachmentType')); +$core->tpl->addValue('AttachmentFileName',array('attachmentTpl','AttachmentFileName')); +$core->tpl->addValue('AttachmentSize',array('attachmentTpl','AttachmentSize')); +$core->tpl->addValue('AttachmentTitle',array('attachmentTpl','AttachmentTitle')); +$core->tpl->addValue('AttachmentThumbnailURL',array('attachmentTpl','AttachmentThumbnailURL')); +$core->tpl->addValue('AttachmentURL',array('attachmentTpl','AttachmentURL')); +$core->tpl->addValue('MediaURL',array('attachmentTpl','MediaURL')); +$core->tpl->addBlock('AttachmentIf',array('attachmentTpl','AttachmentIf')); + +$core->tpl->addValue('EntryAttachmentCount',array('attachmentTpl','EntryAttachmentCount')); + +$core->addBehavior('tplIfConditions',array('attachmentBehavior','tplIfConditions')); + +class attachmentTpl { + + /*dtd + + */ + public static function Attachments($attr,$content) + { + $res = + "posts !== null && $core->media) {'."\n". + '$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id));'."\n". + "?>\n". + + 'attachments as $attach_i => $attach_f) : '. + '$GLOBALS[\'attach_i\'] = $attach_i; $GLOBALS[\'attach_f\'] = $attach_f;'. + '$_ctx->file_url = $attach_f->file_url; ?>'. + $content. + 'attachments = null; unset($attach_i,$attach_f,$_ctx->file_url); ?>'. + + "\n"; + + return $res; + } + + /*dtd + + */ + public static function AttachmentsHeader($attr,$content) + { + return + "". + $content. + ""; + } + + /*dtd + + */ + public static function AttachmentsFooter($attr,$content) + { + return + "attachments)) : ?>". + $content. + ""; + } + + /*dtd + + + */ + public static function AttachmentIf($attr,$content) + { + $if = array(); + + $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&'; + + if (isset($attr['is_image'])) { + $sign = (boolean) $attr['is_image'] ? '' : '!'; + $if[] = $sign.'$attach_f->media_image'; + } + + if (isset($attr['has_thumb'])) { + $sign = (boolean) $attr['has_thumb'] ? '' : '!'; + $if[] = $sign.'isset($attach_f->media_thumb[\'sq\'])'; + } + + if (isset($attr['is_mp3'])) { + $sign = (boolean) $attr['is_mp3'] ? '==' : '!='; + $if[] = '$attach_f->type '.$sign.' "audio/mpeg3"'; + } + + if (isset($attr['is_flv'])) { + $sign = (boolean) $attr['is_flv'] ? '' : '!'; + $if[] = $sign. + '($attach_f->type == "video/x-flv" || '. + '$attach_f->type == "video/mp4" || '. + '$attach_f->type == "video/x-m4v")'; + } + + if (count($if) != 0) { + return ''.$content.''; + } else { + return $content; + } + } + + /*dtd + + */ + public static function AttachmentMimeType($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'type').'; ?>'; + } + + /*dtd + + */ + public static function AttachmentType($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'media_type').'; ?>'; + } + + /*dtd + + */ + public static function AttachmentFileName($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'basename').'; ?>'; + } + + /*dtd + + + */ + public static function AttachmentSize($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + if (!empty($attr['full'])) { + return 'size').'; ?>'; + } + return 'size)').'; ?>'; + } + + /*dtd + + */ + public static function AttachmentTitle($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'media_title').'; ?>'; + } + + /*dtd + + */ + public static function AttachmentThumbnailURL($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return + 'media_thumb[\'sq\'])) {'. + 'echo '.sprintf($f,'$attach_f->media_thumb[\'sq\']').';'. + '}'. + '?>'; + } + + /*dtd + + */ + public static function AttachmentURL($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'file_url').'; ?>'; + } + + public static function MediaURL($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'file_url').'; ?>'; + } + + /*dtd + + + */ + public static function EntryAttachmentCount($attr) + { + $none = 'no attachment'; + $one = 'one attachment'; + $more = '%d attachments'; + + if (isset($attr['none'])) { + $none = addslashes($attr['none']); + } + if (isset($attr['one'])) { + $one = addslashes($attr['one']); + } + if (isset($attr['more'])) { + $more = addslashes($attr['more']); + } + + return + "posts->countMedia() == 0) {\n". + " printf(__('".$none."'),(integer) \$_ctx->posts->countMedia());\n". + "} elseif (\$_ctx->posts->countMedia() == 1) {\n". + " printf(__('".$one."'),(integer) \$_ctx->posts->countMedia());\n". + "} else {\n". + " printf(__('".$more."'),(integer) \$_ctx->posts->countMedia());\n". + "} ?>"; + } +} + +class attachmentBehavior +{ + public static function tplIfConditions($tag,$attr,$content,$if) + { + if ($tag == "EntryIf" && isset($attr['has_attachment'])) { + $sign = (boolean) $attr['has_attachment'] ? '' : '!'; + $if[] = $sign.'$_ctx->posts->countMedia()'; + } + } +} +?> diff --git a/v2/dotclear/plugins/blogroll/_admin.php b/v2/dotclear/plugins/blogroll/_admin.php new file mode 100644 index 0000000..cc14260 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/_admin.php @@ -0,0 +1,34 @@ +addBehavior('adminDashboardIcons','blogroll_dashboard'); +$core->addBehavior('adminDashboardFavs','blogroll_dashboard_favs'); +function blogroll_dashboard($core,$icons) +{ + $icons['blogroll'] = new ArrayObject(array(__('Blogroll'),'plugin.php?p=blogroll','index.php?pf=blogroll/icon.png')); +} +function blogroll_dashboard_favs($core,$favs) +{ + $favs['blogroll'] = new ArrayObject(array('blogroll','Blogroll','plugin.php?p=blogroll', + 'index.php?pf=blogroll/icon-small.png','index.php?pf=blogroll/icon.png', + 'usage,contentadmin',null,null)); +} + +$_menu['Plugins']->addItem(__('Blogroll'),'plugin.php?p=blogroll','index.php?pf=blogroll/icon-small.png', + preg_match('/plugin.php\?p=blogroll(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + +$core->auth->setPermissionType('blogroll',__('manage blogroll')); + +require dirname(__FILE__).'/_widgets.php'; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/_define.php b/v2/dotclear/plugins/blogroll/_define.php new file mode 100644 index 0000000..f0f6b22 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/_define.php @@ -0,0 +1,23 @@ +registerModule( + /* Name */ "Blogroll", + /* Description*/ "Manage your blogroll", + /* Author */ "Olivier Meunier", + /* Version */ '1.2', + array( + 'permissions' => 'blogroll' + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/_install.php b/v2/dotclear/plugins/blogroll/_install.php new file mode 100644 index 0000000..b5857b1 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/_install.php @@ -0,0 +1,46 @@ +plugins->moduleInfo('blogroll','version'); + +if (version_compare($core->getVersion('blogroll'),$version,'>=')) { + return; +} + +/* Database schema +-------------------------------------------------------- */ +$s = new dbStruct($core->con,$core->prefix); + +$s->link + ->link_id ('bigint', 0, false) + ->blog_id ('varchar', 32, false) + ->link_href ('varchar', 255, false) + ->link_title ('varchar', 255, false) + ->link_desc ('varchar', 255, true) + ->link_lang ('varchar', 5, true) + ->link_xfn ('varchar', 255, true) + ->link_position ('integer', 0, false, 0) + + ->primary('pk_link','link_id') + ; + +$s->link->index('idx_link_blog_id','btree','blog_id'); +$s->link->reference('fk_link_blog','blog_id','blog','blog_id','cascade','cascade'); + +# Schema installation +$si = new dbStruct($core->con,$core->prefix); +$changes = $si->synchronize($s); + +$core->setVersion('blogroll',$version); +return true; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/_prepend.php b/v2/dotclear/plugins/blogroll/_prepend.php new file mode 100644 index 0000000..d39e7bd --- /dev/null +++ b/v2/dotclear/plugins/blogroll/_prepend.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/_public.php b/v2/dotclear/plugins/blogroll/_public.php new file mode 100644 index 0000000..7f24029 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/_public.php @@ -0,0 +1,233 @@ +tpl->addValue('Blogroll',array('tplBlogroll','blogroll')); +$core->tpl->addValue('BlogrollXbelLink',array('tplBlogroll','blogrollXbelLink')); + +$core->url->register('xbel','xbel','^xbel(?:/?)$',array('urlBlogroll','xbel')); + +class tplBlogroll +{ + public static function blogroll($attr) + { + $category='

        %s

        '; + $block='
          %s
        '; + $item='%1$s'; + + if (isset($attr['category'])) { + $category = addslashes($attr['category']); + } + + if (isset($attr['block'])) { + $block = addslashes($attr['block']); + } + + if (isset($attr['item'])) { + $item = addslashes($attr['item']); + } + + $only_cat = 'null'; + if (!empty($attr['only_category'])) { + $only_cat = "'".addslashes($attr['only_category'])."'"; + } + + return + ''; + } + + public static function blogrollXbelLink($attr) + { + $f = $GLOBALS['core']->tpl->getFilters($attr); + return 'blog->url.$core->url->getURLFor("xbel")').'; ?>'; + } + + public static function getList($cat_title='

        %s

        ',$block='
          %s
        ',$item='
      • %s
      • ',$category=null) + { + $blogroll = new dcBlogroll($GLOBALS['core']->blog); + + try { + $links = $blogroll->getLinks(); + } catch (Exception $e) { + return false; + } + + $res = ''; + + $hierarchy = $blogroll->getLinksHierarchy($links); + + if ($category) { + if (!isset($hierarchy[$category])) { + return ''; + } + $hierarchy = array($hierarchy[$category]); + } + + foreach ($hierarchy as $k => $v) + { + if ($k != '') { + $res .= sprintf($cat_title,html::escapeHTML($k))."\n"; + } + + $res .= self::getLinksList($v,$block,$item); + } + + return $res; + } + + private static function getLinksList($links,$block='
          %s
        ',$item='%1$s') + { + $list = ''; + + # Find current link item if any + $current = -1; + $current_size = 0; + $self_uri = http::getSelfURI(); + + foreach ($links as $k => $v) + { + if (!preg_match('$^([a-z][a-z0-9.+-]+://)$',$v['link_href'])) { + $url = http::concatURL($self_uri,$v['link_href']); + if (strlen($url) > $current_size && preg_match('/^'.preg_quote($url,'/').'/',$self_uri)) { + $current = $k; + $current_size = strlen($url); + } + } + } + + foreach ($links as $k => $v) + { + $title = $v['link_title']; + $href = $v['link_href']; + $desc = $v['link_desc']; + $lang = $v['link_lang']; + $xfn = $v['link_xfn']; + + $link = + ''. + html::escapeHTML($title). + ''; + + $current_class = $current == $k ? ' class="active"' : ''; + + $list .= sprintf($item,$link,$current_class)."\n"; + } + + return sprintf($block,$list)."\n"; + } + + # Widget function + public static function linksWidget($w) + { + global $core; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $links = self::getList('

        %s

        ','
          %s
        ','%1$s',$w->category); + + if (empty($links)) { + return; + } + + return + ''; + } +} + +class urlBlogroll extends dcUrlHandlers +{ + public static function xbel($args) + { + $blogroll = new dcBlogroll($GLOBALS['core']->blog); + + try { + $links = $blogroll->getLinks(); + } catch (Exception $e) { + self::p404(); + return; + } + + if ($args) { + self::p404(); + return; + } + + http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); + + header('Content-Type: text/xml; charset=UTF-8'); + + echo + ''."\n". + ''."\n". + ''."\n". + ''.html::escapeHTML($GLOBALS['core']->blog->name)." blogroll\n"; + + $i = 1; + foreach ($blogroll->getLinksHierarchy($links) as $cat_title => $links) + { + if ($cat_title != '') { + echo + ''."\n". + "".html::escapeHTML($cat_title)."\n"; + } + + foreach ($links as $k => $v) + { + $lang = $v['link_lang'] ? ' xml:lang="'.$v['link_lang'].'"' : ''; + + echo + ''."\n". + ''.html::escapeHTML($v['link_title'])."\n"; + + if ($v['link_desc']) { + echo ''.html::escapeHTML($v['link_desc'])."\n"; + } + + if ($v['link_xfn']) { + echo + "\n". + ''.$v['link_xfn']."\n". + "\n"; + } + + echo + "\n"; + } + + if ($cat_title != '') { + echo "\n"; + } + + $i++; + } + + echo + ''; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/_widgets.php b/v2/dotclear/plugins/blogroll/_widgets.php new file mode 100644 index 0000000..b0f54ed --- /dev/null +++ b/v2/dotclear/plugins/blogroll/_widgets.php @@ -0,0 +1,44 @@ +addBehavior('initWidgets',array('blogrollWidgets','initWidgets')); +$core->addBehavior('initDefaultWidgets',array('blogrollWidgets','initDefaultWidgets')); + +class blogrollWidgets +{ + public static function initWidgets($w) + { + $w->create('links',__('Blogroll'),array('tplBlogroll','linksWidget')); + $w->links->setting('title',__('Title:'),__('Links')); + + $br = new dcBlogroll($GLOBALS['core']->blog); + $h = $br->getLinksHierarchy($br->getLinks()); + $h = array_keys($h); + $categories = array(__('All categories') => ''); + foreach ($h as $v) { + if ($v) { + $categories[$v] = $v; + } + } + unset($br,$h); + $w->links->setting('category',__('Category'),'','combo',$categories); + + $w->links->setting('homeonly',__('Home page only'),1,'check'); + } + + public static function initDefaultWidgets($w,$d) + { + $d['extra']->append($w->links); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/class.dc.blogroll.php b/v2/dotclear/plugins/blogroll/class.dc.blogroll.php new file mode 100644 index 0000000..da0b8e0 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/class.dc.blogroll.php @@ -0,0 +1,198 @@ +blog =& $blog; + $this->con =& $blog->con; + $this->table = $this->blog->prefix.'link'; + } + + public function getLinks($params=array()) + { + $strReq = 'SELECT link_id, link_title, link_desc, link_href, '. + 'link_lang, link_xfn, link_position '. + 'FROM '.$this->table.' '. + "WHERE blog_id = '".$this->con->escape($this->blog->id)."' "; + + if (isset($params['link_id'])) { + $strReq .= 'AND link_id = '.(integer) $params['link_id'].' '; + } + + $strReq .= 'ORDER BY link_position '; + + $rs = $this->con->select($strReq); + $rs = $rs->toStatic(); + + $this->setLinksData($rs); + + return $rs; + } + + public function getLink($id) + { + $params['link_id'] = $id; + + $rs = $this->getLinks($params); + + return $rs; + } + + public function addLink($title,$href,$desc='',$lang='', $xfn='') + { + $cur = $this->con->openCursor($this->table); + + $cur->blog_id = (string) $this->blog->id; + $cur->link_title = (string) $title; + $cur->link_href = (string) $href; + $cur->link_desc = (string) $desc; + $cur->link_lang = (string) $lang; + $cur->link_xfn = (string) $xfn; + + if ($cur->link_title == '') { + throw new Exception(__('You must provide a link title')); + } + + if ($cur->link_href == '') { + throw new Exception(__('You must provide a link URL')); + } + + $strReq = 'SELECT MAX(link_id) FROM '.$this->table; + $rs = $this->con->select($strReq); + $cur->link_id = (integer) $rs->f(0) + 1; + + $cur->insert(); + $this->blog->triggerBlog(); + } + + public function updateLink($id,$title,$href,$desc='',$lang='', $xfn='') + { + $cur = $this->con->openCursor($this->table); + + $cur->link_title = (string) $title; + $cur->link_href = (string) $href; + $cur->link_desc = (string) $desc; + $cur->link_lang = (string) $lang; + $cur->link_xfn = (string) $xfn; + + if ($cur->link_title == '') { + throw new Exception(__('You must provide a link title')); + } + + if ($cur->link_href == '') { + throw new Exception(__('You must provide a link URL')); + } + + $cur->update('WHERE link_id = '.(integer) $id. + " AND blog_id = '".$this->con->escape($this->blog->id)."'"); + $this->blog->triggerBlog(); + } + + public function updateCategory($id,$desc) + { + $cur = $this->con->openCursor($this->table); + + $cur->link_desc = (string) $desc; + + if ($cur->link_desc == '') { + throw new Exception(__('You must provide a category title')); + } + + $cur->update('WHERE link_id = '.(integer) $id. + " AND blog_id = '".$this->con->escape($this->blog->id)."'"); + $this->blog->triggerBlog(); + } + + public function addCategory($title) + { + $cur = $this->con->openCursor($this->table); + + $cur->blog_id = (string) $this->blog->id; + $cur->link_desc = (string) $title; + $cur->link_href = ''; + $cur->link_title = ''; + + if ($cur->link_desc == '') { + throw new Exception(__('You must provide a category title')); + } + + $strReq = 'SELECT MAX(link_id) FROM '.$this->table; + $rs = $this->con->select($strReq); + $cur->link_id = (integer) $rs->f(0) + 1; + + $cur->insert(); + $this->blog->triggerBlog(); + + return $cur->link_id; + } + + public function delItem($id) + { + $id = (integer) $id; + + $strReq = 'DELETE FROM '.$this->table.' '. + "WHERE blog_id = '".$this->con->escape($this->blog->id)."' ". + 'AND link_id = '.$id.' '; + + $this->con->execute($strReq); + $this->blog->triggerBlog(); + } + + public function updateOrder($id,$position) + { + $cur = $this->con->openCursor($this->table); + $cur->link_position = (integer) $position; + + $cur->update('WHERE link_id = '.(integer) $id. + " AND blog_id = '".$this->con->escape($this->blog->id)."'"); + $this->blog->triggerBlog(); + } + + + private function setLinksData($rs) + { + $cat_title = null; + while ($rs->fetch()) { + $rs->set('is_cat',!$rs->link_title && !$rs->link_href); + + if ($rs->is_cat) { + $cat_title = $rs->link_desc; + $rs->set('cat_title',null); + } else { + $rs->set('cat_title',$cat_title); + } + } + $rs->moveStart(); + } + + public function getLinksHierarchy($rs) + { + $res = array(); + + foreach ($rs->rows() as $k => $v) + { + if (!$v['is_cat']) { + $res[$v['cat_title']][] = $v; + } + } + + return $res; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/class.dc.importblogroll.php b/v2/dotclear/plugins/blogroll/class.dc.importblogroll.php new file mode 100644 index 0000000..a7317e3 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/class.dc.importblogroll.php @@ -0,0 +1,101 @@ +_parseXBEL($data); + } + elseif (preg_match('!_parseOPML($data); + } + else { + throw new Exception(__('You need to provide a XBEL or OPML file.')); + } + } + + + protected function _parseOPML($data) + { + $xml = @simplexml_load_string($data); + if (!$xml) throw new Exception(__('File is not in XML format.')); + + $outlines = $xml->xpath("//outline"); + + $this->entries = array(); + foreach ($outlines as $outline) { + if (isset($outline['htmlUrl'])) { + $link = $outline['htmlUrl']; + } + elseif (isset($outline['url'])) { + $link = $outline['url']; + } + else continue; + $entry = new StdClass(); + $entry->link = $link; + $entry->title = (!empty($outline['title']))?$outline['title']:''; + if (empty($entry->title)) { + $entry->title = (!empty($outline['text']))?$outline['text']:$entry->link; + } + $entry->desc = (!empty($outline['description']))?$outline['description']:''; + $this->entries[] = $entry; + } + } + + protected function _parseXBEL($data) + { + $xml = @simplexml_load_string($data); + if (!$xml) throw new Exception(__('File is not in XML format.')); + + $outlines = $xml->xpath("//bookmark"); + + $this->entries = array(); + foreach ($outlines as $outline) { + if (!isset($outline['href'])) continue; + $entry = new StdClass(); + $entry->link = $outline['href']; + $entry->title = (!empty($outline->title))?$outline->title:''; + if (empty($entry->title)) { + $entry->title = $entry->link; + } + $entry->desc = (!empty($outline->desc))?$outline->desc:''; + $this->entries[] = $entry; + } + } + + + public function getAll() + { + if (!$this->entries) return null; + return $this->entries; + } + +} + +class dcImportBlogroll { + + public static function loadFile($file) + { + if (file_exists($file) && is_readable($file)) { + $importer = new linksImporter(); + $importer->parse(file_get_contents($file)); + return $importer->getAll(); + } + return false; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/edit.php b/v2/dotclear/plugins/blogroll/edit.php new file mode 100644 index 0000000..6a6aa39 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/edit.php @@ -0,0 +1,241 @@ +getLink($id); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +if (!$core->error->flag() && $rs->isEmpty()) { + $core->error->add(__('No such link or title')); +} else { + $link_title = $rs->link_title; + $link_href = $rs->link_href; + $link_desc = $rs->link_desc; + $link_lang = $rs->link_lang; + $link_xfn = $rs->link_xfn; +} + +# Update a link +if (isset($rs) && !$rs->is_cat && !empty($_POST['edit_link'])) +{ + $link_title = $_POST['link_title']; + $link_href = $_POST['link_href']; + $link_desc = $_POST['link_desc']; + $link_lang = $_POST['link_lang']; + + $link_xfn = ''; + + if (!empty($_POST['identity'])) + { + $link_xfn .= $_POST['identity']; + } + else + { + if(!empty($_POST['friendship'])) { + $link_xfn .= ' '.$_POST['friendship']; + } + if(!empty($_POST['physical'])) { + $link_xfn .= ' met'; + } + if(!empty($_POST['professional'])) { + $link_xfn .= ' '.implode(' ',$_POST['professional']); + } + if(!empty($_POST['geographical'])) { + $link_xfn .= ' '.$_POST['geographical']; + } + if(!empty($_POST['family'])) { + $link_xfn .= ' '.$_POST['family']; + } + if(!empty($_POST['romantic'])) { + $link_xfn .= ' '.implode(' ',$_POST['romantic']); + } + } + + try { + $blogroll->updateLink($id,$link_title,$link_href,$link_desc,$link_lang,trim($link_xfn)); + http::redirect($p_url.'&edit=1&id='.$id.'&upd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + + +# Update a category +if (isset($rs) && $rs->is_cat && !empty($_POST['edit_cat'])) +{ + $link_desc = $_POST['link_desc']; + + try { + $blogroll->updateCategory($id,$link_desc); + http::redirect($p_url.'&edit=1&id='.$id.'&upd=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +?> + + + Blogroll + + + +'.__('Return to blogroll').'

        '; ?> + +is_cat) +{ + if (!empty($_GET['upd'])) { + echo '

        '.__('Category has been successfully updated').'

        '; + } + + echo + '
        '. + '
        '.__('Edit category').''. + + '

        '. + + form::hidden('edit',1). + form::hidden('id',$id). + $core->formNonce(). + '

        '. + '
        '. + '
        '; +} +if (isset($rs) && !$rs->is_cat) +{ + if (!empty($_GET['upd'])) { + echo '

        '.__('Link has been successfully updated').'

        '; + } + + echo + '
        '. + '
        '.__('Edit link').''. + + '

        '. + + '

        '. + + '

        '. + + '

        '. + + '

        '.form::hidden('p','blogroll'). + form::hidden('edit',1). + form::hidden('id',$id). + $core->formNonce(). + '

        '. + '
        '. + + + # XFN nightmare + '
        '.__('XFN').''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + + ''. + ''. + ''. + ''. + '
        '.__('_xfn_Me').'

        '.'

        '.__('_xfn_Friendship').'

        '. + ' '. + ' '. + ' '. + ''. + '

        '.__('_xfn_Physical').'

        '. + ''. + '

        '.__('_xfn_Professional').'

        '. + ' '. + ''. + '

        '.__('_xfn_Geographical').'

        '. + ' '. + ' '. + ''. + '

        '.__('_xfn_Family').'

        '. + ' '. + ' '. + ' '. + ' '. + ' '. + ''. + '

        '.__('_xfn_Romantic').'

        '. + ' '. + ' '. + ' '. + ' '. + '

        '. + + '
        '. + + '
        '; +} +?> + + \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/icon-small.png b/v2/dotclear/plugins/blogroll/icon-small.png new file mode 100644 index 0000000..935a9e4 Binary files /dev/null and b/v2/dotclear/plugins/blogroll/icon-small.png differ diff --git a/v2/dotclear/plugins/blogroll/icon.png b/v2/dotclear/plugins/blogroll/icon.png new file mode 100644 index 0000000..bc426a4 Binary files /dev/null and b/v2/dotclear/plugins/blogroll/icon.png differ diff --git a/v2/dotclear/plugins/blogroll/index.php b/v2/dotclear/plugins/blogroll/index.php new file mode 100644 index 0000000..8aaee2c --- /dev/null +++ b/v2/dotclear/plugins/blogroll/index.php @@ -0,0 +1,414 @@ +blog); + +if (!empty($_REQUEST['edit']) && !empty($_REQUEST['id'])) { + include dirname(__FILE__).'/edit.php'; + return; +} + +$default_tab = ''; +$link_title = $link_href = $link_desc = $link_lang = ''; +$cat_title = ''; + +# Import links +if (!empty($_POST['import_links']) && !empty($_FILES['links_file'])) +{ + $default_tab = 'import-links'; + + try + { + files::uploadStatus($_FILES['links_file']); + $ifile = DC_TPL_CACHE.'/'.md5(uniqid()); + if (!move_uploaded_file($_FILES['links_file']['tmp_name'],$ifile)) { + throw new Exception(__('Unable to move uploaded file.')); + } + + require_once dirname(__FILE__).'/class.dc.importblogroll.php'; + try { + $imported = dcImportBlogroll::loadFile($ifile); + @unlink($ifile); + } catch (Exception $e) { + @unlink($ifile); + throw $e; + } + + + if (empty($imported)) { + unset($imported); + throw new Exception(__('Nothing to import')); + } + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} + +if (!empty($_POST['import_links_do'])) { + foreach ($_POST['entries'] as $idx) { + $link_title = $_POST['title'][$idx]; + $link_href = $_POST['url'][$idx]; + $link_desc = $_POST['desc'][$idx]; + try { + $blogroll->addLink($link_title,$link_href,$link_desc,''); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'import-links'; + } + } + http::redirect($p_url.'&importlinks=1'); +} + +if (!empty($_POST['cancel_import'])) { + $core->error->add(__('Import operation cancelled.')); + $default_tab = 'import-links'; +} + +# Add link +if (!empty($_POST['add_link'])) +{ + $link_title = $_POST['link_title']; + $link_href = $_POST['link_href']; + $link_desc = $_POST['link_desc']; + $link_lang = $_POST['link_lang']; + + try { + $blogroll->addLink($link_title,$link_href,$link_desc,$link_lang); + http::redirect($p_url.'&addlink=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'add-link'; + } +} + +# Add category +if (!empty($_POST['add_cat'])) +{ + $cat_title = $_POST['cat_title']; + + try { + $blogroll->addCategory($cat_title); + http::redirect($p_url.'&addcat=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + $default_tab = 'add-cat'; + } +} + +# Delete link +if (!empty($_POST['removeaction']) && !empty($_POST['remove'])) { + foreach ($_POST['remove'] as $k => $v) + { + try { + $blogroll->delItem($v); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + break; + } + } + + if (!$core->error->flag()) { + http::redirect($p_url.'&removed=1'); + } +} + +# Order links +$order = array(); +if (empty($_POST['links_order']) && !empty($_POST['order'])) { + $order = $_POST['order']; + asort($order); + $order = array_keys($order); +} elseif (!empty($_POST['links_order'])) { + $order = explode(',',$_POST['links_order']); +} + +if (!empty($_POST['saveorder']) && !empty($order)) +{ + foreach ($order as $pos => $l) { + $pos = ((integer) $pos)+1; + + try { + $blogroll->updateOrder($l,$pos); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + if (!$core->error->flag()) { + http::redirect($p_url.'&neworder=1'); + } +} + + +# Get links +try { + $rs = $blogroll->getLinks(); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +?> + + + <?php echo __('Blogroll'); ?> + + + auth->user_prefs->addWorkspace('accessibility'); + $user_dm_nodragdrop = $core->auth->user_prefs->accessibility->nodragdrop; + ?> + + + + + + + +

        blog->name); ?> ›

        + +'.__('Items order has been successfully updated').'

        '; +} + +if (!empty($_GET['removed'])) { + echo '

        '.__('Items have been successfully removed.').'

        '; +} + +if (!empty($_GET['addlink'])) { + echo '

        '.__('Link has been successfully created.').'

        '; +} + +if (!empty($_GET['addcat'])) { + echo '

        '.__('category has been successfully created.').'

        '; +} + +if (!empty($_GET['importlinks'])) { + echo '

        '.__('links have been successfully imported.').'

        '; +} +?> + +
        + +
        + +'. +''. +'
        '; + +echo +'
        '. +'
        '. +'
        '.__('Add a new category').''. +'

        '. +form::hidden(array('p'),'blogroll'). +$core->formNonce(). +'

        '. +'
        '. +'
        '. +'
        '; + +echo +''; + +dcPage::helpBlock('blogroll'); +?> + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/locales/en/help/blogroll.html b/v2/dotclear/plugins/blogroll/locales/en/help/blogroll.html new file mode 100644 index 0000000..b1eeb8f --- /dev/null +++ b/v2/dotclear/plugins/blogroll/locales/en/help/blogroll.html @@ -0,0 +1,41 @@ + + + Blogroll + + + + +

        Blogroll

        +

        Blogroll allows you to display on your blog a list of links to other blogs or websites that +you wish to suggest to your visitors.

        +
        +
        Links
        +
        Here are displayed your links and categories. Categories are displayed in bold characters. + You can reorder this list by dragging its items upwards or downwards. Once done, clic on "save order". + The order chosen here will be applied to links displayed on the blog with the Blogroll widget. + Clic on a link or category to edit it.
        + +
        Delete selected links
        +
        Allows you to delete links or categories by checking the boxes next to each of them.
        + +
        Add a new link
        +
        +
          +
        • Title : Type here the anchor text. For example "Jules and Julie". This field is mandatory.
        • +
        • URL : Type here the link's destination. It must begin with http://. This field is mandatory.
        • +
        • Description : Type here a short description for the link (will be displayed in a tooltip). For example "Jules and Julie world trip".
        • +
        • Language : Type here a two characters code for the destination language (for example en for english).
        • +
        +
        + + +
        Add a category
        +
        Links categories allow you to classify links diplayed by the Blogroll widget. They are displayed in bold characters. + Type here the category's name.
        + +
        Import links
        +
        This option allows you to import links from an OPML or XBEL file.
        + +
        + + \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/locales/en/resources.php b/v2/dotclear/plugins/blogroll/locales/en/resources.php new file mode 100644 index 0000000..f690ced --- /dev/null +++ b/v2/dotclear/plugins/blogroll/locales/en/resources.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/locales/fr/help/blogroll.html b/v2/dotclear/plugins/blogroll/locales/fr/help/blogroll.html new file mode 100644 index 0000000..0d48812 --- /dev/null +++ b/v2/dotclear/plugins/blogroll/locales/fr/help/blogroll.html @@ -0,0 +1,38 @@ + + + Liens + + + + +

        Liens

        +

        La gestion des liens vous permet d'afficher sur votre blog un "blogroll" ou liste de liens vers d'autres blogs ou sites que vous souhaitez porter à l'attention de vos visiteurs.

        +
        +
        Liens
        +
        Sur cet onglet s'affichent les liens que vous avez ajoutés, ainsi que les catégories de lien que vous avez créées. Les catégories apparaissent en gras. Vous pouvez réordonner votre liste en cliquant sur les flèches bleues à gauche et en faisant glisser le lien vers le haut ou vers le bas afin de le placer à l'endroit désiré. Une fois vos liens ordonnés, cliquez sur "enregistrer l'ordre" afin d'appliquer les changements. L'ordre que vous choisissez sur cet écran sera l'ordre dans lequel s'afficheront les liens sur vos bandeaux de navigation par le biais du widget 'liens'.
        +Cliquer sur un lien vous mènera à sa fenêtre d'édition
        + +
        Supprimer les liens sélectionnés
        +
        Vous permet de supprimer les liens que vous avez sélectionnés au moyen de la case à cocher à gauche de chaque lien.
        + +
        Ajouter un lien
        +
        +
          +
        • Titre : Entrez ici le texte que vous voulez afficher sur votre blog. Par exemple "Jules et Julie". Ce champ est obligatoire.
        • +
        • URL : Saisissez ici l'adresse du lien. L'url doit commencer avec http://. Ce champ est obligatoire.
        • +
        • Description : Saisissez ici une courte description du lien (permet l'affichage d'info-bulles par survol de la souris). Par exemple "Le blog du voyage de Jules et Julie à travers le monde".
        • +
        • Langue : Saisissez ici le code à deux lettres de la langue du lien (par exemple fr pour le français).
        • +
        +
        + + +
        Ajouter une catégorie
        +
        Il s'agit là de catégories de liens, qui vous permettent d'agencer l'affichage de vos liens par le biais du widget "Liens". + Les catégories s'affichent en gras dans la liste.Saisissez le titre de la catégorie. Par exemple "Blogs des copains"
        + +
        Importer des liens
        +
        Permet d'importer des liens à partir d'un fichier externe au format OPML (format des listes de flux RSS) ou XBEL (format de partage de marque-pages ou favoris).
        + +
        + + \ No newline at end of file diff --git a/v2/dotclear/plugins/blogroll/locales/fr/resources.php b/v2/dotclear/plugins/blogroll/locales/fr/resources.php new file mode 100644 index 0000000..f690ced --- /dev/null +++ b/v2/dotclear/plugins/blogroll/locales/fr/resources.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/blowupConfig/_admin.php b/v2/dotclear/plugins/blowupConfig/_admin.php new file mode 100644 index 0000000..193844a --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/_admin.php @@ -0,0 +1,26 @@ +addBehavior('adminCurrentThemeDetails','blowup_config_details'); + +if (!isset($__resources['help']['blowupConfig'])) { + $__resources['help']['blowupConfig'] = dirname(__FILE__).'/help.html'; +} + +function blowup_config_details($core,$id) +{ + if ($id == 'default' && $core->auth->check('admin',$core->blog->id)) { + return '

        '.__('Theme configuration').'

        '; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blowupConfig/_define.php b/v2/dotclear/plugins/blowupConfig/_define.php new file mode 100644 index 0000000..4cb2b9c --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/_define.php @@ -0,0 +1,23 @@ +registerModule( + /* Name */ "Blowup Config", + /* Description*/ "Configure your Blowup Theme", + /* Author */ "Olivier Meunier", + /* Version */ '1.1', + array( + 'permissions' => 'admin' + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blowupConfig/_install.php b/v2/dotclear/plugins/blowupConfig/_install.php new file mode 100644 index 0000000..9fb9499 --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/_install.php @@ -0,0 +1,25 @@ +plugins->moduleInfo('blowupConfig','version'); +if (version_compare($core->getVersion('blowupConfig'),$version,'>=')) { + return; +} + +$settings = new dcSettings($core,null); +$settings->addNamespace('themes'); +$settings->themes->put('blowup_style','','string','Blow Up custom style',false); + +$core->setVersion('blowupConfig',$version); +return true; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/blowupConfig/_public.php b/v2/dotclear/plugins/blowupConfig/_public.php new file mode 100644 index 0000000..ad7967a --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/_public.php @@ -0,0 +1,196 @@ +blog->settings->system->theme != 'default') { + return; +} + +require dirname(__FILE__).'/lib/class.blowup.config.php'; +$core->addBehavior('publicHeadContent',array('tplBlowupTheme','publicHeadContent')); + +class tplBlowUpTheme +{ + public static function publicHeadContent($core) + { + echo '\n"; + } + + public static function blowUpStyleHelper() + { + $s = $GLOBALS['core']->blog->settings->themes->blowup_style; + + if ($s === null) { + return; + } + + $s = @unserialize($s); + if (!is_array($s)) { + return; + } + + $css = array(); + + /* Sidebar position + ---------------------------------------------- */ + if ($s['sidebar_position'] == 'left') { + $css['#wrapper']['background-position'] = '-300px 0'; + $css['#main']['float'] = 'right'; + $css['#sidebar']['float'] = 'left'; + } + + /* Properties + ---------------------------------------------- */ + self::prop($css,'body','background-color',$s['body_bg_c']); + + self::prop($css,'body','color',$s['body_txt_c']); + self::prop($css,'.post-tags li a:link, .post-tags li a:visited, .post-info-co a:link, .post-info-co a:visited','color',$s['body_txt_c']); + self::prop($css,'#page','font-size',$s['body_txt_s']); + self::prop($css,'body','font-family',blowupConfig::fontDef($s['body_txt_f'])); + + self::prop($css,'.post-content, .post-excerpt, #comments dd, #pings dd, dd.comment-preview','line-height',$s['body_line_height']); + + if (!$s['blog_title_hide']) + { + self::prop($css,'#top h1 a','color',$s['blog_title_c']); + self::prop($css,'#top h1','font-size',$s['blog_title_s']); + self::prop($css,'#top h1','font-family',blowupConfig::fontDef($s['blog_title_f'])); + + if ($s['blog_title_a'] == 'right' || $s['blog_title_a'] == 'left') { + $css['#top h1'][$s['blog_title_a']] = '0px'; + $css['#top h1']['width'] = 'auto'; + } + + if ($s['blog_title_p']) + { + $_p = explode(':',$s['blog_title_p']); + $css['#top h1']['top'] = $_p[1].'px'; + if ($s['blog_title_a'] != 'center') { + $_a = $s['blog_title_a'] == 'right' ? 'right' : 'left'; + $css['#top h1'][$_a] = $_p[0].'px'; + } + } + } + else + { + self::prop($css,'#top h1 span','text-indent','-5000px'); + self::prop($css,'#top h1','top','0px'); + $css['#top h1 a'] = array( + 'display' => 'block', + 'height' => $s['top_height'] ? ($s['top_height']-10).'px' : '120px', + 'width' => '800px' + ); + } + self::prop($css,'#top','height',$s['top_height']); + + self::prop($css,'.day-date','color',$s['date_title_c']); + self::prop($css,'.day-date','font-family',blowupConfig::fontDef($s['date_title_f'])); + self::prop($css,'.day-date','font-size',$s['date_title_s']); + + self::prop($css,'a','color',$s['body_link_c']); + self::prop($css,'a:visited','color',$s['body_link_v_c']); + self::prop($css,'a:hover, a:focus, a:active','color',$s['body_link_f_c']); + + self::prop($css,'#comment-form input, #comment-form textarea','color',$s['body_link_c']); + self::prop($css,'#comment-form input.preview','color',$s['body_link_c']); + self::prop($css,'#comment-form input.preview:hover','background',$s['body_link_f_c']); + self::prop($css,'#comment-form input.preview:hover','border-color',$s['body_link_f_c']); + self::prop($css,'#comment-form input.submit','color',$s['body_link_c']); + self::prop($css,'#comment-form input.submit:hover','background',$s['body_link_f_c']); + self::prop($css,'#comment-form input.submit:hover','border-color',$s['body_link_f_c']); + + self::prop($css,'#sidebar','font-family',blowupConfig::fontDef($s['sidebar_text_f'])); + self::prop($css,'#sidebar','font-size',$s['sidebar_text_s']); + self::prop($css,'#sidebar','color',$s['sidebar_text_c']); + + self::prop($css,'#sidebar h2','font-family',blowupConfig::fontDef($s['sidebar_title_f'])); + self::prop($css,'#sidebar h2','font-size',$s['sidebar_title_s']); + self::prop($css,'#sidebar h2','color',$s['sidebar_title_c']); + + self::prop($css,'#sidebar h3','font-family',blowupConfig::fontDef($s['sidebar_title2_f'])); + self::prop($css,'#sidebar h3','font-size',$s['sidebar_title2_s']); + self::prop($css,'#sidebar h3','color',$s['sidebar_title2_c']); + + self::prop($css,'#sidebar ul','border-top-color',$s['sidebar_line_c']); + self::prop($css,'#sidebar li','border-bottom-color',$s['sidebar_line_c']); + self::prop($css,'#topnav ul','border-bottom-color',$s['sidebar_line_c']); + + self::prop($css,'#sidebar li a','color',$s['sidebar_link_c']); + self::prop($css,'#sidebar li a:visited','color',$s['sidebar_link_v_c']); + self::prop($css,'#sidebar li a:hover, #sidebar li a:focus, #sidebar li a:active','color',$s['sidebar_link_f_c']); + self::prop($css,'#search input','color',$s['sidebar_link_c']); + self::prop($css,'#search .submit','color',$s['sidebar_link_c']); + self::prop($css,'#search .submit:hover','background',$s['sidebar_link_f_c']); + self::prop($css,'#search .submit:hover','border-color',$s['sidebar_link_f_c']); + + self::prop($css,'.post-title','color',$s['post_title_c']); + self::prop($css,'.post-title a, .post-title a:visited','color',$s['post_title_c']); + self::prop($css,'.post-title','font-family',blowupConfig::fontDef($s['post_title_f'])); + self::prop($css,'.post-title','font-size',$s['post_title_s']); + + self::prop($css,'#comments dd','background-color',$s['post_comment_bg_c']); + self::prop($css,'#comments dd','color',$s['post_comment_c']); + self::prop($css,'#comments dd.me','background-color',$s['post_commentmy_bg_c']); + self::prop($css,'#comments dd.me','color',$s['post_commentmy_c']); + + self::prop($css,'#prelude, #prelude a','color',$s['prelude_c']); + + self::prop($css,'#footer p','background-color',$s['footer_bg_c']); + self::prop($css,'#footer p','color',$s['footer_c']); + self::prop($css,'#footer p','font-size',$s['footer_s']); + self::prop($css,'#footer p','font-family',blowupConfig::fontDef($s['footer_f'])); + self::prop($css,'#footer p a','color',$s['footer_l_c']); + + /* Images + ------------------------------------------------------ */ + self::backgroundImg($css,'body',$s['body_bg_c'],'body-bg.png'); + self::backgroundImg($css,'body',$s['body_bg_g'] != 'light','body-bg.png'); + self::backgroundImg($css,'body',$s['prelude_c'],'body-bg.png'); + self::backgroundImg($css,'#top',$s['body_bg_c'],'page-t.png'); + self::backgroundImg($css,'#top',$s['body_bg_g'] != 'light','page-t.png'); + self::backgroundImg($css,'#top',$s['uploaded'] || $s['top_image'],'page-t.png'); + self::backgroundImg($css,'#footer',$s['body_bg_c'],'page-b.png'); + self::backgroundImg($css,'#comments dt',$s['post_comment_bg_c'],'comment-t.png'); + self::backgroundImg($css,'#comments dd',$s['post_comment_bg_c'],'comment-b.png'); + self::backgroundImg($css,'#comments dt.me',$s['post_commentmy_bg_c'],'commentmy-t.png'); + self::backgroundImg($css,'#comments dd.me',$s['post_commentmy_bg_c'],'commentmy-b.png'); + + $res = ''; + foreach ($css as $selector => $values) { + $res .= $selector." {\n"; + foreach ($values as $k => $v) { + $res .= $k.':'.$v.";\n"; + } + $res .= "}\n"; + } + + $res .= $s['extra_css']; + + return $res; + } + + protected static function prop(&$css,$selector,$prop,$value) + { + if ($value) { + $css[$selector][$prop] = $value; + } + } + + protected static function backgroundImg(&$css,$selector,$value,$image) + { + $file = blowupConfig::imagesPath().'/'.$image; + if ($value && file_exists($file)){ + $css[$selector]['background-image'] = 'url('.blowupConfig::imagesURL().'/'.$image.')'; + } + } +} +?> diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/comment-b.png b/v2/dotclear/plugins/blowupConfig/alpha-img/comment-b.png new file mode 100644 index 0000000..b1d37c6 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/comment-b.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/comment-t.png b/v2/dotclear/plugins/blowupConfig/alpha-img/comment-t.png new file mode 100644 index 0000000..b320da3 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/comment-t.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-d.png b/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-d.png new file mode 100644 index 0000000..74bf9cf Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-d.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-l.png b/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-l.png new file mode 100644 index 0000000..c395679 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-l.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-m.png b/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-m.png new file mode 100644 index 0000000..b25c6ba Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/gradient-m.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-b.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-b.png new file mode 100644 index 0000000..1c05ea4 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-b.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-bg.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-bg.png new file mode 100644 index 0000000..6033125 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-bg.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/animals.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/animals.png new file mode 100644 index 0000000..4db1e88 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/animals.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/blank.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/blank.png new file mode 100644 index 0000000..18ae6e8 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/blank.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/butterflies.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/butterflies.png new file mode 100644 index 0000000..2a790c5 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/butterflies.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/default.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/default.png new file mode 100644 index 0000000..1ea0620 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/default.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flamingo.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flamingo.png new file mode 100644 index 0000000..71388ee Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flamingo.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flourish-1.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flourish-1.png new file mode 100644 index 0000000..48c01e3 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flourish-1.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flourish-2.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flourish-2.png new file mode 100644 index 0000000..2707e5a Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/flourish-2.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/image-mask.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/image-mask.png new file mode 100644 index 0000000..998613b Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/image-mask.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-1.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-1.png new file mode 100644 index 0000000..c1e58c6 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-1.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-2.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-2.png new file mode 100644 index 0000000..81acee6 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-2.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-3.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-3.png new file mode 100644 index 0000000..d1dacc2 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-3.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-4.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-4.png new file mode 100644 index 0000000..901b6e7 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/light-trails-4.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/rabbit.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/rabbit.png new file mode 100644 index 0000000..272e406 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/rabbit.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png new file mode 100644 index 0000000..64f15f6 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/roadrunner-1.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png new file mode 100644 index 0000000..4c363d2 Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/roadrunner-2.png differ diff --git a/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/typo.png b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/typo.png new file mode 100644 index 0000000..ef7689d Binary files /dev/null and b/v2/dotclear/plugins/blowupConfig/alpha-img/page-t/typo.png differ diff --git a/v2/dotclear/plugins/blowupConfig/config.js b/v2/dotclear/plugins/blowupConfig/config.js new file mode 100644 index 0000000..b13bca2 --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/config.js @@ -0,0 +1,11 @@ + +$(function(){if($('#blog_title_hide').attr('checked')){toggleDisable($('#blog_title_f'));toggleDisable($('#blog_title_s'));toggleDisable($('#blog_title_c'));toggleDisable($('#blog_title_a'));toggleDisable($('#blog_title_p'));} +$('#blog_title_hide').click(function(){toggleDisable($('#blog_title_f'));toggleDisable($('#blog_title_s'));toggleDisable($('#blog_title_c'));toggleDisable($('#blog_title_a'));toggleDisable($('#blog_title_p'));});if($('#top_image').val()=='custom'){$('#uploader').show();}else{$('#uploader').hide();} +$('#top_image').change(function(){if(this.value=='custom'){$('#uploader').show();$('#image-preview').attr('src',dotclear.blowup_public_url+'/page-t.png');}else{$('#uploader').hide();$('#uploader input').val('');$('#image-preview').attr('src','index.php?pf=blowupConfig/alpha-img/page-t/'+this.value+'.png');}});var styles_combo=document.createElement('select');$(styles_combo).append('');$(styles_combo).append('');$(styles_combo).attr('title',dotclear.msg.predefined_style_title);for(var style in dotclear.blowup_styles){styles_option=document.createElement('option');styles_option.value=dotclear.blowup_styles[style];$(styles_option).append(style);$(styles_combo).append(styles_option);} +$('#theme_config').prepend(styles_combo);$(styles_combo).wrap('
        ').before(''+dotclear.msg.predefined_styles+'').wrap('

        ');$(styles_combo).change(function(){if(this.value=='none'){$(this.form).find('input[type=text]').val('').css({backgroundColor:'#FFF',color:'#000'});$(this.form).find('select').not($(this)).val('');$('#top_image').val('default');}else{applyBlowupValues(this.value);}});var e=$('#bu_export ~ fieldset:first');e.toggle();var img=document.createElement('img');img.src=dotclear.img_plus_src;img.alt=dotclear.img_plus_alt;img.className='expand';$(img).css('cursor','pointer');$(document.createTextNode(' ')).prependTo('#bu_export');$(img).prependTo('#bu_export');$(img).click(function(){if(e.css('display')=='none'){this.src=dotclear.img_minus_src;this.alt=dotclear.img_minus_alt;}else{this.src=dotclear.img_plus_src;this.alt=dotclear.img_plus_alt;} +e.toggle();});var a=document.createElement('a');a.href='#';$(a).text(dotclear.msg.apply_code);e.append(a);$(a).click(function(){var code=e.find('#export_code');if(code.size()==0){return false;} +applyBlowupValues(code.val());return false;});function toggleDisable(e){if(e.attr('disabled')){e.removeAttr('disabled');}else{e.attr('disabled','disabled');}} +function applyBlowupValues(code){code=code.replace("\n","");var re=/(^| )([a-zA-Z0-9_]+):"(.*?)"(;|$)/g;var s=code.match(re);if(typeof(s)=='object'&&s.length>0){var member,target,value,t_e;var reg=new RegExp('^(.+):"(.*)"(;?)\s*$');for(var i=0;i0.5?'#000':'#fff'});}} +function getColorLum(color) +{var rgb=[parseInt('0x'+color.substring(1,3))/255,parseInt('0x'+color.substring(3,5))/255,parseInt('0x'+color.substring(5,7))/255];return(Math.min(rgb[0],Math.min(rgb[1],rgb[2]))+Math.max(rgb[0],Math.max(rgb[1],rgb[2])))/2;}});dotclear.blowup_styles={'Spring Time':'body_bg_c:"#E0E0E0"; body_bg_g:"light"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#6B6B6B"; body_line_height:"1.4em"; top_image:"light-trails-1"; blog_title_hide:"0"; blog_title_f:""; blog_title_s:"3.5em"; blog_title_c:"#9AC528"; blog_title_a:"center"; blog_title_p:""; body_link_c:"#279AC4"; body_link_f_c:"#6D8824"; body_link_v_c:"#279AC4"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#6B6B6B"; sidebar_title_f:""; sidebar_title_s:""; sidebar_title_c:"#8FB22F"; sidebar_title2_f:""; sidebar_title2_s:""; sidebar_title2_c:"#279AC4"; sidebar_line_c:"#FFD02C"; sidebar_link_c:"#6B6B6B"; sidebar_link_f_c:"#9AC528"; sidebar_link_v_c:"#6B6B6B"; date_title_f:""; date_title_s:"1em"; date_title_c:"#279AC4"; post_title_f:""; post_title_s:"1.7em"; post_title_c:"#9AC528"; post_comment_bg_c:"#FFFAD1"; post_comment_c:"#6B6B6B"; post_commentmy_bg_c:"#F5F9D9"; post_commentmy_c:"#6B6B6B"; prelude_c:"#EDEDED"; footer_f:""; footer_s:"1.2em"; footer_c:"#9AC528"; footer_l_c:"#6D8824"; footer_bg_c:"#E0E0E0"','Forest':'body_bg_c:"#80661A"; body_bg_g:"light"; body_txt_f:""; body_txt_s:""; body_txt_c:"#0A0A00"; body_line_height:"1.4em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s3"; blog_title_s:"4em"; blog_title_c:"#D9D9BF"; blog_title_a:""; blog_title_p:""; body_link_c:"#666600"; body_link_f_c:"#CC9933"; body_link_v_c:"#8D8D40"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#0A0A00"; sidebar_title_f:"s2"; sidebar_title_s:"1.6em"; sidebar_title_c:"#4D4D00"; sidebar_title2_f:"s2"; sidebar_title2_s:""; sidebar_title2_c:"#575700"; sidebar_line_c:"#D9D9BF"; sidebar_link_c:"#40330D"; sidebar_link_f_c:"#666600"; sidebar_link_v_c:"#40330D"; date_title_f:""; date_title_s:""; date_title_c:"#B3B380"; post_title_f:"s2"; post_title_s:"2em"; post_title_c:"#4D4D00"; post_comment_bg_c:"#F0F0E6"; post_comment_c:"#0A0A00"; prelude_c:"#140F05"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:"#D9AD2B"; footer_bg_c:"#33260D"','Flamingo':'body_bg_c:"#CC9999"; body_bg_g:"light"; body_txt_f:"ss3"; body_txt_s:"1.2em"; body_txt_c:"#1A1A00"; body_line_height:"1.5em"; top_image:"flamingo"; blog_title_hide:"0"; blog_title_f:"ss1"; blog_title_s:"3.5em"; blog_title_c:"#FFFFFF"; blog_title_a:""; blog_title_p:""; body_link_c:"#AD8282"; body_link_f_c:"#8282D9"; body_link_v_c:"#997373"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss4"; sidebar_title_s:"1.4em"; sidebar_title_c:"#8282D9"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#AD8282"; sidebar_line_c:"#CDCDFF"; sidebar_link_c:"#262640"; sidebar_link_f_c:"#AD8282"; sidebar_link_v_c:"#262640"; date_title_f:"ss4"; date_title_s:""; date_title_c:"#D9B3B3"; post_title_f:"ss4"; post_title_s:"1.8em"; post_title_c:"#8282D9"; post_comment_bg_c:"#F2E5E5"; post_comment_c:""; prelude_c:"#140F0F"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#140F0F"','Iceberg':'body_bg_c:"#5280A3"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#757575"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3em"; blog_title_c:"#FFFFFF"; blog_title_a:""; blog_title_p:""; body_link_c:"#BDB000"; body_link_f_c:"#F3E66D"; body_link_v_c:"#BDB000"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.4em"; sidebar_title_c:"#689B9C"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#66664D"; sidebar_link_f_c:"#5280A3"; sidebar_link_v_c:"#66664D"; date_title_f:""; date_title_s:""; date_title_c:"#000000"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#6F6800"; post_comment_bg_c:"#E4E4E2"; post_comment_c:""; prelude_c:"#0E2734"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#0E2734"','Night':'body_bg_c:"#0D1A26"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#050A0F"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3.5em"; blog_title_c:"#F2F2E5"; blog_title_a:""; body_link_c:"#336699"; body_link_f_c:"#66664D"; body_link_v_c:"#2B5782"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.4em"; sidebar_title_c:"#336699"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#66664D"; sidebar_link_f_c:"#336699"; sidebar_link_v_c:"#66664D"; date_title_f:""; date_title_s:""; date_title_c:"#ADAD82"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#737300"; post_comment_bg_c:"#E6E6CD"; post_comment_c:""; prelude_c:"#070E14"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#14140F"; blog_title_p:""','Peas & Carrots':'body_bg_c:"#9DCA25"; body_bg_g:"light"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#383839"; body_line_height:"1.5em"; top_image:"butterflies"; blog_title_hide:"0"; blog_title_f:"ss4"; blog_title_s:"3em"; blog_title_c:"#DBDB9D"; blog_title_a:"left"; blog_title_p:""; body_link_c:"#646B10"; body_link_f_c:"#DF6C01"; body_link_v_c:"#919924"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss4"; sidebar_title_s:""; sidebar_title_c:"#FE9017"; sidebar_title2_f:"s2"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#826228"; sidebar_line_c:"#D3EB8B"; sidebar_link_c:"#858547"; sidebar_link_f_c:"#FE9017"; sidebar_link_v_c:"#8F9645"; date_title_f:""; date_title_s:""; date_title_c:"#826228"; post_title_f:"ss4"; post_title_s:"1.8em"; post_title_c:"#806432"; post_comment_bg_c:"#EFFDCC"; post_comment_c:"#826228"; prelude_c:"#C8E186"; footer_f:""; footer_s:"1em"; footer_c:"#FFFFFF"; footer_l_c:"#FFFFFF"; footer_bg_c:"#484432"','Rabbit':'body_bg_c:"#8F9645"; body_bg_g:"solid"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#625D47"; body_line_height:"1.4em"; top_image:"rabbit"; blog_title_hide:"0"; blog_title_f:"ss1"; blog_title_s:"3.5em"; blog_title_c:"#DBDB9D"; blog_title_a:""; blog_title_p:"130:70"; body_link_c:"#646B10"; body_link_f_c:"#484C12"; body_link_v_c:"#919924"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#858547"; sidebar_title_f:""; sidebar_title_s:""; sidebar_title_c:"#8F9645"; sidebar_title2_f:"s2"; sidebar_title2_s:""; sidebar_title2_c:"#826228"; sidebar_line_c:"#95956B"; sidebar_link_c:"#858547"; sidebar_link_f_c:"#826228"; sidebar_link_v_c:"#8F9645"; date_title_f:"s2"; date_title_s:"1em"; date_title_c:"#826228"; post_title_f:"s2"; post_title_s:"1.6em"; post_title_c:"#806432"; post_comment_bg_c:"#D6DE91"; post_comment_c:"#826228"; prelude_c:"#484432"; footer_f:""; footer_s:"1em"; footer_c:"#A6AF50"; footer_l_c:"#DBDB9D"; footer_bg_c:"#484432"','Rec Room':'body_bg_c:"#9B5E1C"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#757575"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3em"; blog_title_c:"#F9FAD6"; blog_title_a:""; blog_title_p:""; body_link_c:"#D1BF1D"; body_link_f_c:"#EEE168"; body_link_v_c:"#D1BF1D"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.2em"; sidebar_title_c:"#689B9C"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#66664D"; sidebar_link_f_c:"#689B9C"; sidebar_link_v_c:"#66664D"; date_title_f:""; date_title_s:""; date_title_c:"#000000"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#689B9C"; post_comment_bg_c:"#E4E4E2"; post_comment_c:""; prelude_c:"#3B2C16"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#3B2C16"','Seville':'body_bg_c:"#B51A0D"; body_bg_g:"dark"; body_txt_f:"ss3"; body_txt_s:"1.1em"; body_txt_c:"#383839"; body_line_height:"1.5em"; top_image:"default"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3em"; blog_title_c:"#FFFFFF"; blog_title_a:""; blog_title_p:""; body_link_c:"#F18A32"; body_link_f_c:"#F1B232"; body_link_v_c:"#F18A32"; sidebar_position:"left"; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:""; sidebar_title_f:"ss3"; sidebar_title_s:"1.4em"; sidebar_title_c:"#97471C"; sidebar_title2_f:"ss3"; sidebar_title2_s:"1.2em"; sidebar_title2_c:"#737300"; sidebar_line_c:"#E6E6CD"; sidebar_link_c:"#6E6E72"; sidebar_link_f_c:"#F18A32"; sidebar_link_v_c:"#6E6E72"; date_title_f:""; date_title_s:""; date_title_c:"#97471C"; post_title_f:"s2"; post_title_s:"1.8em"; post_title_c:"#F18A32"; post_comment_bg_c:"#E4E4E2"; post_comment_c:""; prelude_c:"#381A1A"; footer_f:""; footer_s:""; footer_c:"#FFFFFF"; footer_l_c:""; footer_bg_c:"#381A1A"','Typo':'body_bg_c:"#FFFFFF"; body_bg_g:"solid"; body_txt_f:"ss1"; body_txt_s:"1.2em"; body_txt_c:"#000000"; body_line_height:"1.4em"; top_image:"typo"; blog_title_hide:"0"; blog_title_f:"s2"; blog_title_s:"3.5em"; blog_title_c:"#B11508"; blog_title_a:"left"; blog_title_p:"140:50"; body_link_c:"#B11508"; body_link_f_c:"#000000"; body_link_v_c:"#4D4D4D"; sidebar_position:""; sidebar_text_f:""; sidebar_text_s:""; sidebar_text_c:"#000000"; sidebar_title_f:"s2"; sidebar_title_s:""; sidebar_title_c:"#B11508"; sidebar_title2_f:"s2"; sidebar_title2_s:""; sidebar_title2_c:"#000000"; sidebar_line_c:"#000000"; sidebar_link_c:"#000000"; sidebar_link_f_c:"#B11508"; sidebar_link_v_c:"#000000"; date_title_f:"s2"; date_title_s:"1em"; date_title_c:"#000000"; post_title_f:"s2"; post_title_s:"1.6em"; post_title_c:"#B11508"; post_comment_bg_c:"#FFFFFF"; post_comment_c:"#000000"; prelude_c:"#FFFFFF"; footer_f:""; footer_s:"1em"; footer_c:"#000000"; footer_l_c:"#B11508"; footer_bg_c:"#FFFFFF"'}; \ No newline at end of file diff --git a/v2/dotclear/plugins/blowupConfig/help.html b/v2/dotclear/plugins/blowupConfig/help.html new file mode 100644 index 0000000..8442326 --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/help.html @@ -0,0 +1,55 @@ + + + + Blowup theme configuration + + + +

        Modifying Blowup theme configuration allows you to customize your theme very easily. To do so, simply fill in the configuration fields or choose a predefined style.

        + +

        Colors

        + +

        When you have to enter a color value, you should use the hexadecimal format. For example: "#FF0000" will give you red. You may also use the color picker located next to every color field.

        +

        If you lack inspiration, try and visit the following pages:

        + + +

        Measurement units

        + +

        When you have to indicate a size, it must be followed by a measurement unit. For example: "1em". If you do not include the unit, pixels will be used as default measurement unit.

        +

        The following measurement units are available:

        +
          +
        • px
        • +
        • em
        • +
        • ex
        • +
        • pt
        • +
        • %
        • +
        + +

        Header images

        + +

        You can choose a header image among the list to use in place of the default one.

        + +

        When you choose "Custom..." in the image list, you will be able to upload your own image. The image file must be in JPG or PNG format and must be precisely 800px wide. + +

        If you upload a JPG image, a border will be added to the image, which is not the case with PNG images (the transparency of which will also be kept).

        + +

        Predefined styles

        + +

        You can choose a predefined style in the "Predefinite styles" drop-down list. +Once you have chosen the style, you have to submit the form to apply the modifications.

        + +

        You may then modify the predefined style according to your taste and needs.

        + +

        Configuration import / export

        + +

        At the end of Blowup options, you can display an area names "Configuration import/export". This text area contains the configuration you are currently using. You can copy it to share it with others.

        + +

        To apply (import) a configuration, you simply need to replace the content of the text area with the configuration you want to use. Don't forget to click on "apply code".

        + + + diff --git a/v2/dotclear/plugins/blowupConfig/index.php b/v2/dotclear/plugins/blowupConfig/index.php new file mode 100644 index 0000000..34e7248 --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/index.php @@ -0,0 +1,464 @@ +error->flag()) { + $notices = $core->error->toHTML(); + $core->error->reset(); +} + +$blowup_base = array( + 'body_bg_c' => null, + 'body_bg_g' => 'light', + + 'body_txt_f' => null, + 'body_txt_s' => null, + 'body_txt_c' => null, + 'body_line_height' => null, + + 'top_image' => 'default', + 'top_height' => null, + 'uploaded' => null, + + 'blog_title_hide' => null, + 'blog_title_f' => null, + 'blog_title_s' => null, + 'blog_title_c' => null, + 'blog_title_a' => null, + 'blog_title_p' => null, + + 'body_link_c' => null, + 'body_link_f_c' => null, + 'body_link_v_c' => null, + + 'sidebar_position' => null, + 'sidebar_text_f' => null, + 'sidebar_text_s' => null, + 'sidebar_text_c' => null, + 'sidebar_title_f' => null, + 'sidebar_title_s' => null, + 'sidebar_title_c' => null, + 'sidebar_title2_f' => null, + 'sidebar_title2_s' => null, + 'sidebar_title2_c' => null, + 'sidebar_line_c' => null, + 'sidebar_link_c' => null, + 'sidebar_link_f_c' => null, + 'sidebar_link_v_c' => null, + + 'date_title_f' => null, + 'date_title_s' => null, + 'date_title_c' => null, + + 'post_title_f' => null, + 'post_title_s' => null, + 'post_title_c' => null, + 'post_comment_bg_c' => null, + 'post_comment_c' => null, + 'post_commentmy_bg_c' => null, + 'post_commentmy_c' => null, + + 'prelude_c' => null, + 'footer_f' => null, + 'footer_s' => null, + 'footer_c' => null, + 'footer_l_c' => null, + 'footer_bg_c' => null, + + 'extra_css' => null +); + +$blowup_user = $core->blog->settings->themes->blowup_style; + +$blowup_user = @unserialize($blowup_user); +if (!is_array($blowup_user)) { + $blowup_user = array(); +} + +$blowup_user = array_merge($blowup_base,$blowup_user); + +$gradient_types = array( + __('Light linear gradient') => 'light', + __('Medium linear gradient') => 'medium', + __('Dark linear gradient') => 'dark', + __('Solid color') => 'solid' +); + +$top_images = array(__('Custom...') => 'custom'); +$top_images = array_merge($top_images,array_flip(blowupConfig::$top_images)); + + +if (!empty($_POST)) +{ + try + { + $blowup_user['body_txt_f'] = $_POST['body_txt_f']; + $blowup_user['body_txt_s'] = blowupConfig::adjustFontSize($_POST['body_txt_s']); + $blowup_user['body_txt_c'] = blowupConfig::adjustColor($_POST['body_txt_c']); + $blowup_user['body_line_height'] = blowupConfig::adjustFontSize($_POST['body_line_height']); + + $blowup_user['blog_title_hide'] = (integer) !empty($_POST['blog_title_hide']); + $update_blog_title = !$blowup_user['blog_title_hide'] && ( + !empty($_POST['blog_title_f']) || !empty($_POST['blog_title_s']) || + !empty($_POST['blog_title_c']) || !empty($_POST['blog_title_a']) || + !empty($_POST['blog_title_p']) + ); + + if ($update_blog_title) + { + $blowup_user['blog_title_f'] = $_POST['blog_title_f']; + $blowup_user['blog_title_s'] = blowupConfig::adjustFontSize($_POST['blog_title_s']); + $blowup_user['blog_title_c'] = blowupConfig::adjustColor($_POST['blog_title_c']); + $blowup_user['blog_title_a'] = preg_match('/^(left|center|right)$/',$_POST['blog_title_a']) ? $_POST['blog_title_a'] : null; + $blowup_user['blog_title_p'] = blowupConfig::adjustPosition($_POST['blog_title_p']); + } + + $blowup_user['body_link_c'] = blowupConfig::adjustColor($_POST['body_link_c']); + $blowup_user['body_link_f_c'] = blowupConfig::adjustColor($_POST['body_link_f_c']); + $blowup_user['body_link_v_c'] = blowupConfig::adjustColor($_POST['body_link_v_c']); + + $blowup_user['sidebar_text_f'] = $_POST['sidebar_text_f']; + $blowup_user['sidebar_text_s'] = blowupConfig::adjustFontSize($_POST['sidebar_text_s']); + $blowup_user['sidebar_text_c'] = blowupConfig::adjustColor($_POST['sidebar_text_c']); + $blowup_user['sidebar_title_f'] = $_POST['sidebar_title_f']; + $blowup_user['sidebar_title_s'] = blowupConfig::adjustFontSize($_POST['sidebar_title_s']); + $blowup_user['sidebar_title_c'] = blowupConfig::adjustColor($_POST['sidebar_title_c']); + $blowup_user['sidebar_title2_f'] = $_POST['sidebar_title2_f']; + $blowup_user['sidebar_title2_s'] = blowupConfig::adjustFontSize($_POST['sidebar_title2_s']); + $blowup_user['sidebar_title2_c'] = blowupConfig::adjustColor($_POST['sidebar_title2_c']); + $blowup_user['sidebar_line_c'] = blowupConfig::adjustColor($_POST['sidebar_line_c']); + $blowup_user['sidebar_link_c'] = blowupConfig::adjustColor($_POST['sidebar_link_c']); + $blowup_user['sidebar_link_f_c'] = blowupConfig::adjustColor($_POST['sidebar_link_f_c']); + $blowup_user['sidebar_link_v_c'] = blowupConfig::adjustColor($_POST['sidebar_link_v_c']); + + $blowup_user['sidebar_position'] = ($_POST['sidebar_position'] == 'left') ? 'left' : null; + + $blowup_user['date_title_f'] = $_POST['date_title_f']; + $blowup_user['date_title_s'] = blowupConfig::adjustFontSize($_POST['date_title_s']); + $blowup_user['date_title_c'] = blowupConfig::adjustColor($_POST['date_title_c']); + + $blowup_user['post_title_f'] = $_POST['post_title_f']; + $blowup_user['post_title_s'] = blowupConfig::adjustFontSize($_POST['post_title_s']); + $blowup_user['post_title_c'] = blowupConfig::adjustColor($_POST['post_title_c']); + $blowup_user['post_comment_c'] = blowupConfig::adjustColor($_POST['post_comment_c']); + $blowup_user['post_commentmy_c'] = blowupConfig::adjustColor($_POST['post_commentmy_c']); + + + $blowup_user['footer_f'] = $_POST['footer_f']; + $blowup_user['footer_s'] = blowupConfig::adjustFontSize($_POST['footer_s']); + $blowup_user['footer_c'] = blowupConfig::adjustColor($_POST['footer_c']); + $blowup_user['footer_l_c'] = blowupConfig::adjustColor($_POST['footer_l_c']); + $blowup_user['footer_bg_c'] = blowupConfig::adjustColor($_POST['footer_bg_c']); + + + $blowup_user['extra_css'] = blowupConfig::cleanCSS($_POST['extra_css']); + + if ($can_write_images) + { + $uploaded = null; + if ($blowup_user['uploaded'] && is_file(blowupConfig::imagesPath().'/'.$blowup_user['uploaded'])) { + $uploaded = blowupConfig::imagesPath().'/'.$blowup_user['uploaded']; + } + + if (!empty($_FILES['upfile']) && !empty($_FILES['upfile']['name'])) { + files::uploadStatus($_FILES['upfile']); + $uploaded = blowupConfig::uploadImage($_FILES['upfile']); + $blowup_user['uploaded'] = basename($uploaded); + } + + $blowup_user['top_image'] = in_array($_POST['top_image'],$top_images) ? $_POST['top_image'] : 'default'; + + $blowup_user['body_bg_c'] = blowupConfig::adjustColor($_POST['body_bg_c']); + $blowup_user['body_bg_g'] = in_array($_POST['body_bg_g'],$gradient_types) ? $_POST['body_bg_g'] : ''; + $blowup_user['post_comment_bg_c'] = blowupConfig::adjustColor($_POST['post_comment_bg_c']); + $blowup_user['post_commentmy_bg_c'] = blowupConfig::adjustColor($_POST['post_commentmy_bg_c']); + $blowup_user['prelude_c'] = blowupConfig::adjustColor($_POST['prelude_c']); + blowupConfig::createImages($blowup_user,$uploaded); + } + + $core->blog->settings->addNamespace('themes'); + $core->blog->settings->themes->put('blowup_style',serialize($blowup_user)); + $core->blog->triggerBlog(); + + http::redirect($p_url.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +?> + + + <?php echo __('Blowup configuration'); ?> + + + + + + +'.html::escapeHTML($core->blog->name). +' › '.__('Blog appearance').''.__('Blowup configuration').''. +'

        '.__('back').'

        '; + + +if (!$can_write_images) { + echo '
        '. + __('For the following reasons, images cannot be created. You won\'t be able to change some background properties.'). + $notices.'
        '; +} + +if (!empty($_GET['upd'])) { + echo '

        '.__('Theme configuration has been successfully updated.').'

        '; +} + +echo '
        '; + +echo '
        '.__('General').''; + +if ($can_write_images) { + echo + '

        '. + + '

        '; +} + +echo +'

        '. + +'

        '. + +'

        '. + +'

        '. +'
        '. + +'
        '.__('Links').''. +'

        '. + +'

        '. + +'

        '. +'
        '. + +'
        '.__('Page top').''; + +if ($can_write_images) { + echo + '

        '; +} + +echo +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. +'
        '; + +if ($can_write_images) { + if ($blowup_user['top_image'] == 'custom' && $blowup_user['uploaded']) { + $preview_image = http::concatURL($core->blog->url,blowupConfig::imagesURL().'/page-t.png'); + } else { + $preview_image = 'index.php?pf=blowupConfig/alpha-img/page-t/'.$blowup_user['top_image'].'.png'; + } + + echo + '
        '.__('Top image').''. + '

        '. + '

        '.__('Choose "Custom..." to upload your own image.').'

        '. + + '

        '. + + '

        '.__('Preview').'

        '. + '
        '. + ''. + '
        '. + '
        '; +} + +echo +'
        '.__('Sidebar').''. +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. +'
        '. + +'
        '.__('Entries').''. +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '; + +if ($can_write_images) { + echo + '

        '; +} + +echo +'

        '; + +if ($can_write_images) { + echo + '

        '; +} + +echo +'

        '. +'
        '. + +'
        '.__('Footer').''. +'

        '. + +'

        '. + +'

        '. + +'

        '. + +'

        '. +'
        '; + +echo +'
        '.__('Additional CSS').''. +'

        '.form::textarea('extra_css',72,5,html::escapeHTML($blowup_user['extra_css']),'maximal','',false,'title="'.__('Additional CSS').'"').'

        '. +'
        '; + + + +// Import / Export configuration +$tmp_array = array(); +$tmp_exclude = array('uploaded','top_height'); +if ($blowup_user['top_image'] == 'custom') { + $tmp_exclude[] = 'top_image'; +} +foreach ($blowup_user as $k => $v) { + if (!in_array($k,$tmp_exclude)) { + $tmp_array[] = $k.':'.'"'.$v.'"'; + } +} +echo +'

        '.__('Configuration import / export').'

        '. +'

        '.__('You can share your configuration using the following code. To apply a configuration, paste the code, click on "Apply code" and save.').'

        '. +'

        '.form::textarea('export_code',72,5,implode('; ',$tmp_array),'maximal','',false,'title="'.__('Copy this code:').'"').'

        '. +'
        '; + +echo +'

        '. +$core->formNonce().'

        '. +'
        '; + +dcPage::helpBlock('blowupConfig'); +?> + + diff --git a/v2/dotclear/plugins/blowupConfig/lib/class.blowup.config.php b/v2/dotclear/plugins/blowupConfig/lib/class.blowup.config.php new file mode 100644 index 0000000..a7fe577 --- /dev/null +++ b/v2/dotclear/plugins/blowupConfig/lib/class.blowup.config.php @@ -0,0 +1,429 @@ + array( + 'ss1' => 'Arial, Helvetica, sans-serif', + 'ss2' => 'Verdana,Geneva, Arial, Helvetica, sans-serif', + 'ss3' => '"Lucida Grande", "Lucida Sans Unicode", sans-serif', + 'ss4' => '"Trebuchet MS", Helvetica, sans-serif', + 'ss5' => 'Impact, Charcoal, sans-serif' + ), + + 'serif' => array( + 's1' => 'Times, "Times New Roman", serif', + 's2' => 'Georgia, serif', + 's3' => 'Baskerville, "Palatino Linotype", serif' + ), + + 'monospace' => array( + 'm1' => '"Andale Mono", "Courier New", monospace', + 'm2' => '"Courier New", Courier, mono, monospace' + ) + ); + + protected static $fonts_combo = array(); + protected static $fonts_list = array(); + + public static $top_images = array( + 'default' => 'Default', + 'blank' => 'Blank', + 'light-trails-1' => 'Light Trails 1', + 'light-trails-2' => 'Light Trails 2', + 'light-trails-3' => 'Light Trails 3', + 'light-trails-4' => 'Light Trails 4', + 'butterflies' => 'Butterflies', + 'flourish-1' => 'Flourished 1', + 'flourish-2' => 'Flourished 2', + 'animals' => 'Animals', + 'flamingo' => 'Flamingo', + 'rabbit' => 'Rabbit', + 'roadrunner-1' => 'Road Runner 1', + 'roadrunner-2' => 'Road Runner 2', + 'typo' => 'Typo' + ); + + public static function fontsList() + { + if (empty(self::$fonts_combo)) + { + self::$fonts_combo[__('default')] = ''; + foreach (self::$fonts as $family => $g) + { + $fonts = array(); + foreach ($g as $code => $font) { + $fonts[str_replace('"','',$font)] = $code; + } + self::$fonts_combo[$family] = $fonts; + } + } + + return self::$fonts_combo; + } + + public static function fontDef($c) + { + if (empty(self::$fonts_list)) + { + foreach (self::$fonts as $family => $g) + { + foreach ($g as $code => $font) { + self::$fonts_list[$code] = $font; + } + } + } + + return isset(self::$fonts_list[$c]) ? self::$fonts_list[$c] : null; + } + + public static function adjustFontSize($s) + { + if (preg_match('/^([0-9.]+)\s*(%|pt|px|em|ex)?$/',$s,$m)) { + if (empty($m[2])) { + $m[2] = 'px'; + } + return $m[1].$m[2]; + } + + return null; + } + + public static function adjustPosition($p) + { + if (!preg_match('/^[0-9]+(:[0-9]+)?$/',$p)) { + return null; + } + + $p = explode(':',$p); + + return $p[0].(count($p) == 1 ? ':0' : ':'.$p[1]); + } + + 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 ''; + } + + public static function cleanCSS($css) + { + // TODO ? + return $css; + } + + public static function imagesPath() + { + global $core; + return path::real($core->blog->public_path).'/blowup-images'; + } + + public static function imagesURL() + { + global $core; + return $core->blog->settings->system->public_url.'/blowup-images'; + } + + public static function canWriteImages($create=false) + { + global $core; + + $public = path::real($core->blog->public_path); + $imgs = self::imagesPath(); + + 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/blowup-images')); + return false; + } + + return true; + } + + public static function uploadImage($f) + { + if (!self::canWriteImages(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().'/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.')); + } + + $s = getimagesize($dest); + if ($s[0] != 800) { + throw new Exception(__('Uploaded image is not 800 pixels wide.')); + } + + return $dest; + } + + public static function createImages(&$config,$uploaded) + { + $body_color = $config['body_bg_c']; + $prelude_color = $config['prelude_c']; + $gradient = $config['body_bg_g']; + $comment_color = $config['post_comment_bg_c']; + $comment_color_my = $config['post_commentmy_bg_c']; + $top_image = $config['top_image']; + + $config['top_height'] = null; + + if ($top_image != 'custom' && !isset(self::$top_images[$top_image])) { + $top_image = 'default'; + } + if ($uploaded && !is_file($uploaded)) { + $uploaded = null; + } + + if (!self::canWriteImages(true)) { + throw new Exception(__('Unable to create images.')); + } + + $body_fill = array( + 'light' => dirname(__FILE__).'/../alpha-img/gradient-l.png', + 'medium' => dirname(__FILE__).'/../alpha-img/gradient-m.png', + 'dark' => dirname(__FILE__).'/../alpha-img/gradient-d.png' + ); + + $body_g = isset($body_fill[$gradient]) ? $body_fill[$gradient] : false; + + if ($top_image == 'custom' && $uploaded) { + $page_t = $uploaded; + } else { + $page_t = dirname(__FILE__).'/../alpha-img/page-t/'.$top_image.'.png'; + } + + $body_bg = dirname(__FILE__).'/../alpha-img/body-bg.png'; + $page_t_mask = dirname(__FILE__).'/../alpha-img/page-t/image-mask.png'; + $page_b = dirname(__FILE__).'/../alpha-img/page-b.png'; + $comment_t = dirname(__FILE__).'/../alpha-img/comment-t.png'; + $comment_b = dirname(__FILE__).'/../alpha-img/comment-b.png'; + $default_bg = '#e0e0e0'; + $default_prelude = '#ededed'; + + self::dropImage(basename($body_bg)); + self::dropImage('page-t.png'); + self::dropImage(basename($page_b)); + self::dropImage(basename($comment_t)); + self::dropImage(basename($comment_b)); + + $body_color = self::adjustColor($body_color); + $prelude_color = self::adjustColor($prelude_color); + $comment_color = self::adjustColor($comment_color); + + if ($top_image || $body_color || $gradient != 'light' || $prelude_color || $uploaded) + { + if (!$body_color) { + $body_color = $default_bg; + } + $body_color = sscanf($body_color,'#%2X%2X%2X'); + + # Create body gradient with color + $d_body_bg = imagecreatetruecolor(50,180); + $fill = imagecolorallocate($d_body_bg,$body_color[0],$body_color[1],$body_color[2]); + imagefill($d_body_bg,0,0,$fill); + + # User choosed a gradient + if ($body_g) { + $s_body_bg = imagecreatefrompng($body_g); + imagealphablending($s_body_bg,true); + imagecopy($d_body_bg,$s_body_bg,0,0,0,0,50,180); + imagedestroy($s_body_bg); + } + + if (!$prelude_color) { + $prelude_color = $default_prelude; + } + $prelude_color = sscanf($prelude_color,'#%2X%2X%2X'); + + $s_prelude = imagecreatetruecolor(50,30); + $fill = imagecolorallocate($s_prelude,$prelude_color[0],$prelude_color[1],$prelude_color[2]); + imagefill($s_prelude,0,0,$fill); + imagecopy($d_body_bg,$s_prelude,0,0,0,0,50,30); + + imagepng($d_body_bg,self::imagesPath().'/'.basename($body_bg)); + } + + if ($top_image || $body_color || $gradient != 'light') + { + # Create top image from uploaded image + $size = getimagesize($page_t); + $size = $size[1]; + $type = files::getMimeType($page_t); + + $d_page_t = imagecreatetruecolor(800,$size); + + if ($type == 'image/png') { + $s_page_t = @imagecreatefrompng($page_t); + } else { + $s_page_t = @imagecreatefromjpeg($page_t); + } + + if (!$s_page_t) { + throw new exception(__('Unable to open image.')); + } + + $fill = imagecolorallocate($d_page_t,$body_color[0],$body_color[1],$body_color[2]); + imagefill($d_page_t,0,0,$fill); + + if ($type == 'image/png') + { + # PNG, we only add body gradient and image + imagealphablending($s_page_t,true); + imagecopyresized($d_page_t,$d_body_bg,0,0,0,50,800,130,50,130); + imagecopy($d_page_t,$s_page_t,0,0,0,0,800,$size); + } + else + { + # JPEG, we add image and a frame with rounded corners + imagecopy($d_page_t,$s_page_t,0,0,0,0,800,$size); + + imagecopy($d_page_t,$d_body_bg,0,0,0,50,8,4); + imagecopy($d_page_t,$d_body_bg,0,4,0,54,4,4); + imagecopy($d_page_t,$d_body_bg,792,0,0,50,8,4); + imagecopy($d_page_t,$d_body_bg,796,4,0,54,4,4); + + $mask = imagecreatefrompng($page_t_mask); + imagealphablending($mask,true); + imagecopy($d_page_t,$mask,0,0,0,0,800,11); + imagedestroy($mask); + + $fill = imagecolorallocate($d_page_t,255,255,255); + imagefilledrectangle($d_page_t,0,11,3,$size-1,$fill); + imagefilledrectangle($d_page_t,796,11,799,$size-1,$fill); + imagefilledrectangle($d_page_t,0,$size-9,799,$size-1,$fill); + } + + $config['top_height'] = ($size).'px'; + + imagepng($d_page_t,self::imagesPath().'/page-t.png'); + + imagedestroy($d_body_bg); + imagedestroy($d_page_t); + imagedestroy($s_page_t); + + # Create bottom image with color + $d_page_b = imagecreatetruecolor(800,8); + $fill = imagecolorallocate($d_page_b,$body_color[0],$body_color[1],$body_color[2]); + imagefill($d_page_b,0,0,$fill); + + $s_page_b = imagecreatefrompng($page_b); + imagealphablending($s_page_b,true); + imagecopy($d_page_b,$s_page_b,0,0,0,0,800,160); + + imagepng($d_page_b,self::imagesPath().'/'.basename($page_b)); + + imagedestroy($d_page_b); + imagedestroy($s_page_b); + } + + if ($comment_color) { + self::commentImages($comment_color,$comment_t,$comment_b,basename($comment_t),basename($comment_b)); + } + if ($comment_color_my) { + self::commentImages($comment_color_my,$comment_t,$comment_b,'commentmy-t.png','commentmy-b.png'); + } + } + + protected static function commentImages($comment_color,$comment_t,$comment_b,$dest_t,$dest_b) + { + $comment_color = sscanf($comment_color,'#%2X%2X%2X'); + + $d_comment_t = imagecreatetruecolor(500,25); + $fill = imagecolorallocate($d_comment_t,$comment_color[0],$comment_color[1],$comment_color[2]); + imagefill($d_comment_t,0,0,$fill); + + $s_comment_t = imagecreatefrompng($comment_t); + imagealphablending($s_comment_t,true); + imagecopy($d_comment_t,$s_comment_t,0,0,0,0,500,25); + + imagepng($d_comment_t,self::imagesPath().'/'.$dest_t); + imagedestroy($d_comment_t); + imagedestroy($s_comment_t); + + $d_comment_b = imagecreatetruecolor(500,7); + $fill = imagecolorallocate($d_comment_b,$comment_color[0],$comment_color[1],$comment_color[2]); + imagefill($d_comment_b,0,0,$fill); + + $s_comment_b = imagecreatefrompng($comment_b); + imagealphablending($s_comment_b,true); + imagecopy($d_comment_b,$s_comment_b,0,0,0,0,500,7); + + imagepng($d_comment_b,self::imagesPath().'/'.$dest_b); + imagedestroy($d_comment_b); + imagedestroy($s_comment_b); + } + + public static function dropImage($img) + { + $img = path::real(self::imagesPath().'/'.$img); + if (is_writable(dirname($img))) { + @unlink($img); + @unlink(dirname($img).'/.'.basename($img,'.png').'_sq.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_m.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_s.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_sq.jpg'); + @unlink(dirname($img).'/.'.basename($img,'.png').'_t.jpg'); + } + } +} +?> diff --git a/v2/dotclear/plugins/fairTrackbacks/_define.php b/v2/dotclear/plugins/fairTrackbacks/_define.php new file mode 100644 index 0000000..6b4197d --- /dev/null +++ b/v2/dotclear/plugins/fairTrackbacks/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "Fair Trackbacks", + /* Description*/ "Trackback validity check", + /* Author */ "Olivier Meunier", + /* Version */ '1.1', + array( + 'permissions' => 'usage,contentadmin', + 'priority' => 200 + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/fairTrackbacks/_prepend.php b/v2/dotclear/plugins/fairTrackbacks/_prepend.php new file mode 100644 index 0000000..9b6f2fd --- /dev/null +++ b/v2/dotclear/plugins/fairTrackbacks/_prepend.php @@ -0,0 +1,22 @@ +spamfilters[] = 'dcFilterFairTrackbacks'; +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/fairTrackbacks/_public.php b/v2/dotclear/plugins/fairTrackbacks/_public.php new file mode 100644 index 0000000..4fd2644 --- /dev/null +++ b/v2/dotclear/plugins/fairTrackbacks/_public.php @@ -0,0 +1,18 @@ +spamfilters[] = 'dcFilterFairTrackbacks'; +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php b/v2/dotclear/plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php new file mode 100644 index 0000000..1248ea1 --- /dev/null +++ b/v2/dotclear/plugins/fairTrackbacks/class.dc.filter.fairtrackbacks.php @@ -0,0 +1,85 @@ +description = __('Checks trackback source for a link to the post'); + } + + + public function isSpam($type,$author,$email,$site,$ip,$content,$post_id,&$status) + { + if ($type != 'trackback') { + return; + } + + try + { + $default_parse = array('scheme'=>'','host'=>'','path'=>'','query'=>''); + $S = array_merge($default_parse,parse_url($site)); + + if ($S['scheme'] != 'http' || !$S['host'] || !$S['path']) { + throw new Exception('Invalid URL'); + } + + # Check incomink link page + $post = $this->core->blog->getPosts(array('post_id' => $post_id)); + $post_url = $post->getURL(); + $P = array_merge($default_parse,parse_url($post_url)); + + if ($post_url == $site) { + throw new Exception('Same source and destination'); + } + + $o = netHttp::initClient($site,$path); + $o->setTimeout(3); + $o->get($path); + + # Trackback source does not return 200 status code + if ($o->getStatus() != 200) { + throw new Exception('Invalid Status Code'); + } + + $tb_page = $o->getContent(); + + # Do we find a link to post in trackback source? + if ($S['host'] == $P['host']) { + $pattern = $P['path'].($P['query'] ? '?'.$P['query'] : ''); + } else { + $pattern = $post_url; + } + $pattern = preg_quote($pattern,'/'); + + if (!preg_match('/'.$pattern.'/',$tb_page)) { + throw new Exception('Unfair'); + } + } + catch (Exception $e) + { + throw new Exception('Trackback not allowed for this URL.'); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/_admin.php b/v2/dotclear/plugins/importExport/_admin.php new file mode 100644 index 0000000..ec5dbae --- /dev/null +++ b/v2/dotclear/plugins/importExport/_admin.php @@ -0,0 +1,35 @@ +addItem( + __('Import/Export'), + 'plugin.php?p=importExport', + 'index.php?pf=importExport/icon.png', + preg_match('/plugin.php\?p=importExport(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('admin',$core->blog->id) +); + +$core->addBehavior('adminDashboardFavs','importExportDashboardFavs'); + +function importExportDashboardFavs($core,$favs) +{ + $favs['importExport'] = new ArrayObject(array( + 'importExport', + __('Import/Export'), + 'plugin.php?p=importExport', + 'index.php?pf=importExport/icon.png', + 'index.php?pf=importExport/icon-big.png', + 'admin',null,null + )); +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/_define.php b/v2/dotclear/plugins/importExport/_define.php new file mode 100644 index 0000000..0a45722 --- /dev/null +++ b/v2/dotclear/plugins/importExport/_define.php @@ -0,0 +1,21 @@ +registerModule( + /* Name */ "Import / Export", + /* Description*/ "Import and Export your blog", + /* Author */ "Olivier Meunier & Contributors", + /* Version */ '3.1', + /* Perm */ 'admin' +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/_prepend.php b/v2/dotclear/plugins/importExport/_prepend.php new file mode 100644 index 0000000..34c66df --- /dev/null +++ b/v2/dotclear/plugins/importExport/_prepend.php @@ -0,0 +1,42 @@ +addBehavior('importExportModules','registerIeModules'); + +function registerIeModules($modules) +{ + $modules['import'][] = 'dcImportFlat'; + $modules['import'][] = 'dcImportFeed'; + + $modules['export'][] = 'dcExportFlat'; + + if ($GLOBALS['core']->auth->isSuperAdmin()) { + $modules['import'][] = 'dcImportDC1'; + $modules['import'][] = 'dcImportWP'; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/icon-big.png b/v2/dotclear/plugins/importExport/icon-big.png new file mode 100644 index 0000000..e4a6303 Binary files /dev/null and b/v2/dotclear/plugins/importExport/icon-big.png differ diff --git a/v2/dotclear/plugins/importExport/icon.png b/v2/dotclear/plugins/importExport/icon.png new file mode 100644 index 0000000..274b67c Binary files /dev/null and b/v2/dotclear/plugins/importExport/icon.png differ diff --git a/v2/dotclear/plugins/importExport/inc/class.dc.export.flat.php b/v2/dotclear/plugins/importExport/inc/class.dc.export.flat.php new file mode 100644 index 0000000..06b9bcf --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/class.dc.export.flat.php @@ -0,0 +1,241 @@ +type = 'export'; + $this->name = __('Flat file export'); + $this->description = __('Exports a blog or a full Dotclear installation to flat file.'); + } + + public function process($do) + { + # Export a blog + if ($do == 'export_blog' && $this->core->auth->check('admin',$this->core->blog->id)) + { + $fullname = $this->core->blog->public_path.'/.backup_'.sha1(uniqid()); + $blog_id = $this->core->con->escape($this->core->blog->id); + + try + { + $exp = new flatExport($this->core->con,$fullname,$this->core->prefix); + fwrite($exp->fp,'///DOTCLEAR|'.DC_VERSION."|single\n"); + + $exp->export('category', + 'SELECT * FROM '.$this->core->prefix.'category '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('link', + 'SELECT * FROM '.$this->core->prefix.'link '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('setting', + 'SELECT * FROM '.$this->core->prefix.'setting '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('post', + 'SELECT * FROM '.$this->core->prefix.'post '. + "WHERE blog_id = '".$blog_id."'" + ); + $exp->export('meta', + 'SELECT meta_id, meta_type, M.post_id '. + 'FROM '.$this->core->prefix.'meta M, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = M.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + $exp->export('media', + 'SELECT * FROM '.$this->core->prefix."media WHERE media_path = '". + $this->core->con->escape($this->core->blog->settings->system->public_path)."'" + ); + $exp->export('post_media', + 'SELECT media_id, M.post_id '. + 'FROM '.$this->core->prefix.'post_media M, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = M.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + $exp->export('ping', + 'SELECT ping.post_id, ping_url, ping_dt '. + 'FROM '.$this->core->prefix.'ping ping, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = ping.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + $exp->export('comment', + 'SELECT C.* '. + 'FROM '.$this->core->prefix.'comment C, '.$this->core->prefix.'post P '. + 'WHERE P.post_id = C.post_id '. + "AND P.blog_id = '".$blog_id."'" + ); + + # --BEHAVIOR-- exportSingle + $this->core->callBehavior('exportSingle',$this->core,$exp,$blog_id); + + $_SESSION['export_file'] = $fullname; + $_SESSION['export_filename'] = $_POST['file_name']; + $_SESSION['export_filezip'] = !empty($_POST['file_zip']); + http::redirect($this->getURL().'&do=ok'); + } + catch (Exception $e) + { + @unlink($fullname); + throw $e; + } + } + + # Export all content + if ($do == 'export_all' && $this->core->auth->isSuperAdmin()) + { + $fullname = $this->core->blog->public_path.'/.backup_'.sha1(uniqid()); + try + { + $exp = new flatExport($this->core->con,$fullname,$this->core->prefix); + fwrite($exp->fp,'///DOTCLEAR|'.DC_VERSION."|full\n"); + $exp->exportTable('blog'); + $exp->exportTable('category'); + $exp->exportTable('link'); + $exp->exportTable('setting'); + $exp->exportTable('user'); + $exp->exportTable('pref'); + $exp->exportTable('permissions'); + $exp->exportTable('post'); + $exp->exportTable('meta'); + $exp->exportTable('media'); + $exp->exportTable('post_media'); + $exp->exportTable('log'); + $exp->exportTable('ping'); + $exp->exportTable('comment'); + $exp->exportTable('spamrule'); + $exp->exportTable('version'); + + # --BEHAVIOR-- exportFull + $this->core->callBehavior('exportFull',$this->core,$exp); + + $_SESSION['export_file'] = $fullname; + $_SESSION['export_filename'] = $_POST['file_name']; + $_SESSION['export_filezip'] = !empty($_POST['file_zip']); + http::redirect($this->getURL().'&do=ok'); + } + catch (Exception $e) + { + @unlink($fullname); + throw $e; + } + } + + # Send file content + if ($do == 'ok') + { + if (!file_exists($_SESSION['export_file'])) { + throw new Exception(__('Export file not found.')); + } + + ob_end_clean(); + + if (substr($_SESSION['export_filename'],-4) == '.zip') { + $_SESSION['export_filename'] = substr($_SESSION['export_filename'],0,-4);//.'.txt'; + } + + # Flat export + if (empty($_SESSION['export_filezip'])) { + + header('Content-Disposition: attachment;filename='.$_SESSION['export_filename']); + header('Content-Type: text/plain; charset=UTF-8'); + readfile($_SESSION['export_file']); + + unlink($_SESSION['export_file']); + unset($_SESSION['export_file'],$_SESSION['export_filename'],$_SESSION['export_filezip']); + exit; + } + # Zip export + else { + try + { + $file_zipname = $_SESSION['export_filename'].'.zip'; + + $fp = fopen('php://output','wb'); + $zip = new fileZip($fp); + $zip->addFile($_SESSION['export_file'],$_SESSION['export_filename']); + + header('Content-Disposition: attachment;filename='.$file_zipname); + header('Content-Type: application/x-zip'); + + $zip->write(); + + unlink($_SESSION['export_file']); + unset($zip,$_SESSION['export_file'],$_SESSION['export_filename'],$file_zipname); + exit; + } + catch (Exception $e) + { + unset($zip,$_SESSION['export_file'],$_SESSION['export_filename'],$file_zipname); + @unlink($_SESSION['export_file']); + + throw new Exception(__('Failed to compress export file.')); + } + } + } + } + + public function gui() + { + echo + '
        '. + '
        '.__('Single blog').''. + '

        '.sprintf(__('This will create an export of your current blog: %s'),html::escapeHTML($this->core->blog->name)).'

        '. + + '

        '. + form::field(array('file_name','file_name'),50,255,date('Y-m-d-').html::escapeHTML($this->core->blog->id.'-backup.txt')). + '

        '. + + '

        '. + '

        '. + + '

        '. + __('You may also want to download your media directory as a zip file').'

        '. + + '

        '. + form::hidden(array('do'),'export_blog'). + $this->core->formNonce().'

        '. + + '
        '. + '
        '; + + if ($this->core->auth->isSuperAdmin()) + { + echo + '
        '. + '
        '.__('Multiple blogs').''. + '

        '.__('This will create an export of all the content of your database.').'

        '. + + '

        '. + form::field(array('file_name','file_name2'),50,255,date('Y-m-d-').'dotclear-backup.txt'). + '

        '. + + '

        '. + '

        '. + + '

        '. + form::hidden(array('do'),'export_all'). + $this->core->formNonce().'

        '. + + '
        '. + '
        '; + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/class.dc.ieModule.php b/v2/dotclear/plugins/importExport/inc/class.dc.ieModule.php new file mode 100644 index 0000000..bfd9f2a --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/class.dc.ieModule.php @@ -0,0 +1,80 @@ +core =& $core; + $this->setInfo(); + + if (!in_array($this->type,array('import','export'))) { + throw new Exception(sprintf('Unknow type for module %s',get_class($this))); + } + + if (!$this->name) { + $this->name = get_class($this); + } + + $this->id = get_class($this); + $this->url = sprintf('plugin.php?p=importExport&type=%s&module=%s',$this->type,$this->id); + } + + public function init() + { + } + + abstract protected function setInfo(); + + final public function getURL($escape=false) + { + return $escape ? html::escapeHTML($this->url) : $this->url; + } + + abstract public function process($do); + + abstract public function gui(); + + protected function progressBar($percent) + { + $percent = ceil($percent); + if ($percent > 100) { + $percent = 100; + } + return '
        '.$percent.' %
        '; + } + + protected function autoSubmit() + { + return form::hidden(array('autosubmit'),1); + } + + protected function congratMessage() + { + return + '

        '.__('Congratulation!').'

        '. + '

        '.__('Your blog has been successfully imported. Welcome on Dotclear 2!').'

        '. + ''; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/class.dc.import.dc1.php b/v2/dotclear/plugins/importExport/inc/class.dc.import.dc1.php new file mode 100644 index 0000000..7ddb23e --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/class.dc.import.dc1.php @@ -0,0 +1,625 @@ + '', + 'db_name' => '', + 'db_user' => '', + 'db_pwd' => '', + 'db_prefix' => 'dc_', + 'post_limit' => 20, + 'cat_ids' => array() + ); + + protected function setInfo() + { + $this->type = 'import'; + $this->name = __('Dotclear 1.2 import'); + $this->description = __('Import a Dotclear 1.2 installation into your current blog.'); + } + + public function init() + { + $this->con =& $this->core->con; + $this->prefix = $this->core->prefix; + $this->blog_id = $this->core->blog->id; + + if (!isset($_SESSION['dc1_import_vars'])) { + $_SESSION['dc1_import_vars'] = $this->base_vars; + } + $this->vars =& $_SESSION['dc1_import_vars']; + + if ($this->vars['post_limit'] > 0) { + $this->post_limit = $this->vars['post_limit']; + } + } + + public function resetVars() + { + $this->vars = $this->base_vars;; + unset($_SESSION['dc1_import_vars']); + } + + public function process($do) + { + $this->action = $do; + } + + # We handle process in another way to always display something to + # user + protected function guiprocess($do) + { + switch ($do) + { + case 'step1': + $this->vars['db_host'] = $_POST['db_host']; + $this->vars['db_name'] = $_POST['db_name']; + $this->vars['db_user'] = $_POST['db_user']; + $this->vars['db_pwd'] = $_POST['db_pwd']; + $this->vars['post_limit'] = abs((integer) $_POST['post_limit']) > 0 ? $_POST['post_limit'] : 0; + $this->vars['db_prefix'] = $_POST['db_prefix']; + $db = $this->db(); + $db->close(); + $this->step = 2; + echo $this->progressBar(1); + break; + case 'step2': + $this->step = 2; + $this->importUsers(); + $this->step = 3; + echo $this->progressBar(3); + break; + case 'step3': + $this->step = 3; + $this->importCategories(); + if ($this->core->plugins->moduleExists('blogroll')) { + $this->step = 4; + echo $this->progressBar(5); + } else { + $this->step = 5; + echo $this->progressBar(7); + } + break; + case 'step4': + $this->step = 4; + $this->importLinks(); + $this->step = 5; + echo $this->progressBar(7); + break; + case 'step5': + $this->step = 5; + $this->post_offset = !empty($_REQUEST['offset']) ? abs((integer) $_REQUEST['offset']) : 0; + if ($this->importPosts($percent) === -1) { + http::redirect($this->getURL().'&do=ok'); + } else { + echo $this->progressBar(ceil($percent*0.93)+7); + } + break; + case 'ok': + $this->resetVars(); + $this->core->blog->triggerBlog(); + $this->step = 6; + echo $this->progressBar(100); + break; + } + } + + public function gui() + { + try { + $this->guiprocess($this->action); + } catch (Exception $e) { + $this->error($e); + } + + switch ($this->step) + { + case 1: + echo + '

        '.sprintf(__('This will import your Dotclear 1.2 content as new content in the current blog: %s.'), + ''.html::escapeHTML($this->core->blog->name).'').'

        '. + '

        '.__('Please note that this process '. + 'will empty your categories, blogroll, entries and comments on the current blog.').'

        '. + '

        '.__('Depending on the size of your blog, it could take a few minutes.').'

        '; + + printf($this->imForm(1,__('General information'),__('Import my blog now')), + '

        '.__('We first need some information about your old Dotclear 1.2 installation.').'

        '. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '.__('Entries import options').'

        '. + '

        ' + ); + break; + case 2: + printf($this->imForm(2,__('Importing users')), + $this->autoSubmit() + ); + break; + case 3: + printf($this->imForm(3,__('Importing categories')), + $this->autoSubmit() + ); + break; + case 4: + printf($this->imForm(4,__('Importing blogroll')), + $this->autoSubmit() + ); + break; + case 5: + $t = sprintf(__('Importing entries from %d to %d / %d'),$this->post_offset, + min(array($this->post_offset+$this->post_limit,$this->post_count)),$this->post_count); + printf($this->imForm(5,$t), + form::hidden(array('offset'),$this->post_offset). + $this->autoSubmit() + ); + break; + case 6: + echo + '

        '.__('Please read carefully').'

        '. + '
          '. + '
        • '.__('Every newly imported user has received a random password '. + 'and will need to ask for a new one by following the "I forgot my password" link on the login page '. + '(Their registered email address has to be valid.)').'
        • '. + + '
        • '.sprintf(__('Please note that Dotclear 2 has a new URL layout. You can avoid broken '. + 'links by installing DC1 redirect plugin and activate it in your blog configuration.'), + 'http://plugins.dotaddict.org/dc2/details/dc1redirect').'
        • '. + '
        '. + + $this->congratMessage(); + + break; + } + } + + # Simple form for step by step process + protected function imForm($step,$legend,$submit_value=null) + { + if (!$submit_value) { + $submit_value = __('next step').' >'; + } + + return + '
        '. + '
        '.$legend.''. + $this->core->formNonce(). + form::hidden(array('do'),'step'.$step). + '%s'. + '

        '. + '
        '. + '
        '; + } + + # Error display + protected function error($e) + { + echo '
        '.__('Errors:').''. + '

        '.$e->getMessage().'

        '; + } + + # Database init + protected function db() + { + $db = dbLayer::init('mysql',$this->vars['db_host'],$this->vars['db_name'],$this->vars['db_user'],$this->vars['db_pwd']); + + $rs = $db->select("SHOW TABLES LIKE '".$this->vars['db_prefix']."%'"); + if ($rs->isEmpty()) { + throw new Exception(__('Dotclear tables not found')); + } + + while ($rs->fetch()) { + $this->has_table[$rs->f(0)] = true; + } + + # Set this to read data as they were written in Dotclear 1 + try { + $db->execute('SET NAMES DEFAULT'); + } catch (Exception $e) {} + + $db->execute('SET CHARACTER SET DEFAULT'); + $db->execute("SET COLLATION_CONNECTION = DEFAULT"); + $db->execute("SET COLLATION_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_DATABASE = DEFAULT"); + + $this->post_count = $db->select( + 'SELECT COUNT(post_id) FROM '.$this->vars['db_prefix'].'post ' + )->f(0); + + return $db; + } + + protected function cleanStr($str) + { + return text::cleanUTF8(@text::toUTF8($str)); + } + + # Users import + protected function importUsers() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'user'); + + try + { + $this->con->begin(); + + while ($rs->fetch()) + { + if (!$this->core->userExists($rs->user_id)) + { + $cur = $this->con->openCursor($this->prefix.'user'); + $cur->user_id = $rs->user_id; + $cur->user_name = $rs->user_nom; + $cur->user_firstname = $rs->user_prenom; + $cur->user_displayname = $rs->user_pseudo; + $cur->user_pwd = crypt::createPassword(); + $cur->user_email = $rs->user_email; + $cur->user_lang = $rs->user_lang; + $cur->user_tz = $this->core->blog->settings->system->blog_timezone; + $cur->user_post_status = $rs->user_post_pub ? 1 : -2; + $cur->user_options = new ArrayObject(array( + 'edit_size' => (integer) $rs->user_edit_size, + 'post_format' => $rs->user_post_format + )); + + $permissions = array(); + switch ($rs->user_level) + { + case '0': + $cur->user_status = 0; + break; + case '1': # editor + $permissions['usage'] = true; + break; + case '5': # advanced editor + $permissions['contentadmin'] = true; + $permissions['categories'] = true; + $permissions['media_admin'] = true; + break; + case '9': # admin + $permissions['admin'] = true; + break; + } + + $this->core->addUser($cur); + $this->core->setUserBlogPermissions( + $rs->user_id, + $this->blog_id, + $permissions + ); + } + } + + $this->con->commit(); + $db->close(); + } + catch (Exception $e) + { + $this->con->rollback(); + $db->close(); + throw $e; + } + } + + # Categories import + protected function importCategories() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'categorie ORDER BY cat_ord ASC'); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + $ord = 2; + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'category'); + $cur->blog_id = $this->blog_id; + $cur->cat_title = $this->cleanStr(htmlspecialchars_decode($rs->cat_libelle)); + $cur->cat_desc = $this->cleanStr($rs->cat_desc); + $cur->cat_url = $this->cleanStr($rs->cat_libelle_url); + $cur->cat_lft = $ord++; + $cur->cat_rgt = $ord++; + + $cur->cat_id = $this->con->select( + 'SELECT MAX(cat_id) FROM '.$this->prefix.'category' + )->f(0) + 1; + $this->vars['cat_ids'][$rs->cat_id] = $cur->cat_id; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Blogroll import + protected function importLinks() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'link ORDER BY link_id ASC'); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'link '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'link'); + $cur->blog_id = $this->blog_id; + $cur->link_href = $this->cleanStr($rs->href); + $cur->link_title = $this->cleanStr($rs->label); + $cur->link_desc = $this->cleanStr($rs->title); + $cur->link_lang = $this->cleanStr($rs->lang); + $cur->link_xfn = $this->cleanStr($rs->rel); + $cur->link_position = (integer) $rs->position; + + $cur->link_id = $this->con->select( + 'SELECT MAX(link_id) FROM '.$this->prefix.'link' + )->f(0) + 1; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Entries import + protected function importPosts(&$percent) + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + + $count = $db->select('SELECT COUNT(post_id) FROM '.$prefix.'post')->f(0); + + $rs = $db->select( + 'SELECT * FROM '.$prefix.'post ORDER BY post_id ASC '. + $db->limit($this->post_offset,$this->post_limit) + ); + + try + { + if ($this->post_offset == 0) + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'post '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + } + + while ($rs->fetch()) { + $this->importPost($rs,$db); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + + if ($rs->count() < $this->post_limit) { + return -1; + } else { + $this->post_offset += $this->post_limit; + } + + if ($this->post_offset > $this->post_count) { + $percent = 100; + } else { + $percent = $this->post_offset * 100 / $this->post_count; + } + } + + protected function importPost($rs,$db) + { + $cur = $this->con->openCursor($this->prefix.'post'); + $cur->blog_id = $this->blog_id; + $cur->user_id = $rs->user_id; + $cur->cat_id = (integer) $this->vars['cat_ids'][$rs->cat_id]; + $cur->post_dt = $rs->post_dt; + $cur->post_creadt = $rs->post_creadt; + $cur->post_upddt = $rs->post_upddt; + $cur->post_title = html::decodeEntities($this->cleanStr($rs->post_titre)); + + $cur->post_url = date('Y/m/d/',strtotime($cur->post_dt)).$rs->post_id.'-'.$rs->post_titre_url; + $cur->post_url = substr($cur->post_url,0,255); + + $cur->post_format = $rs->post_content_wiki == '' ? 'xhtml' : 'wiki'; + $cur->post_content_xhtml = $this->cleanStr($rs->post_content); + $cur->post_excerpt_xhtml = $this->cleanStr($rs->post_chapo); + + if ($cur->post_format == 'wiki') { + $cur->post_content = $this->cleanStr($rs->post_content_wiki); + $cur->post_excerpt = $this->cleanStr($rs->post_chapo_wiki); + } else { + $cur->post_content = $this->cleanStr($rs->post_content); + $cur->post_excerpt = $this->cleanStr($rs->post_chapo); + } + + $cur->post_notes = $this->cleanStr($rs->post_notes); + $cur->post_status = (integer) $rs->post_pub; + $cur->post_selected = (integer) $rs->post_selected; + $cur->post_open_comment = (integer) $rs->post_open_comment; + $cur->post_open_tb = (integer) $rs->post_open_tb; + $cur->post_lang = $rs->post_lang; + + $cur->post_words = implode(' ',text::splitWords( + $cur->post_title.' '. + $cur->post_excerpt_xhtml.' '. + $cur->post_content_xhtml + )); + + $cur->post_id = $this->con->select( + 'SELECT MAX(post_id) FROM '.$this->prefix.'post' + )->f(0) + 1; + + $cur->insert(); + $this->importComments($rs->post_id,$cur->post_id,$db); + $this->importPings($rs->post_id,$cur->post_id,$db); + + # Load meta if we have some in DC1 + if (isset($this->has_table[$this->vars['db_prefix'].'post_meta'])) { + $this->importMeta($rs->post_id,$cur->post_id,$db); + } + } + + # Comments import + protected function importComments($post_id,$new_post_id,$db) + { + $count_c = $count_t = 0; + + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'comment '. + 'WHERE post_id = '.(integer) $post_id.' ' + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'comment'); + $cur->post_id = (integer) $new_post_id; + $cur->comment_author = $this->cleanStr($rs->comment_auteur); + $cur->comment_status = (integer) $rs->comment_pub; + $cur->comment_dt = $rs->comment_dt; + $cur->comment_upddt = $rs->comment_upddt; + $cur->comment_email = $this->cleanStr($rs->comment_email); + $cur->comment_content = $this->cleanStr($rs->comment_content); + $cur->comment_ip = $rs->comment_ip; + $cur->comment_trackback = (integer) $rs->comment_trackback; + + $cur->comment_site = $this->cleanStr($rs->comment_site); + if ($cur->comment_site != '' && !preg_match('!^http://.*$!',$cur->comment_site)) { + $cur->comment_site = substr('http://'.$cur->comment_site,0,255); + } + + if ($rs->exists('spam') && $rs->spam && $rs->comment_status = 0) { + $cur->comment_status = -2; + } + + $cur->comment_words = implode(' ',text::splitWords($cur->comment_content)); + + $cur->comment_id = $this->con->select( + 'SELECT MAX(comment_id) FROM '.$this->prefix.'comment' + )->f(0) + 1; + + $cur->insert(); + + if ($cur->comment_trackback && $cur->comment_status == 1) { + $count_t++; + } elseif ($cur->comment_status == 1) { + $count_c++; + } + } + + if ($count_t > 0 || $count_c > 0) + { + $this->con->execute( + 'UPDATE '.$this->prefix.'post SET '. + 'nb_comment = '.$count_c.', '. + 'nb_trackback = '.$count_t.' '. + 'WHERE post_id = '.(integer) $new_post_id.' ' + ); + } + } + + # Pings import + protected function importPings($post_id,$new_post_id,$db) + { + $urls = array(); + + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'ping '. + 'WHERE post_id = '.(integer) $post_id + ); + + while ($rs->fetch()) + { + $url = $this->cleanStr($rs->ping_url); + if (isset($urls[$url])) { + continue; + } + + $cur = $this->con->openCursor($this->prefix.'ping'); + $cur->post_id = (integer) $new_post_id; + $cur->ping_url = $url; + $cur->ping_dt = $rs->ping_dt; + $cur->insert(); + + $urls[$url] = true; + } + } + + # Meta import + protected function importMeta($post_id,$new_post_id,$db) + { + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'post_meta '. + 'WHERE post_id = '.(integer) $post_id.' ' + ); + + if ($rs->isEmpty()) { + return; + } + + while ($rs->fetch()) { + $this->core->meta->setPostMeta($new_post_id,$this->cleanStr($rs->meta_key),$this->cleanStr($rs->meta_value)); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/class.dc.import.feed.php b/v2/dotclear/plugins/importExport/inc/class.dc.import.feed.php new file mode 100644 index 0000000..cca76c1 --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/class.dc.import.feed.php @@ -0,0 +1,99 @@ +type = 'import'; + $this->name = __('Feed import'); + $this->description = __('Imports a feed as new entries.'); + } + + public function process($do) + { + if ($do == 'ok') { + $this->status = true; + return; + } + + if (empty($_POST['feed_url'])) { + return; + } + + $this->feed_url = $_POST['feed_url']; + + $feed = feedReader::quickParse($this->feed_url); + if ($feed === false) { + throw new Exception(__('Cannot retrieve feed URL.')); + } + if (count($feed->items) == 0) { + throw new Exception(__('No items in feed.')); + } + + $cur = $this->core->con->openCursor($this->core->prefix.'post'); + $this->core->con->begin(); + foreach ($feed->items as $item) + { + $cur->clean(); + $cur->user_id = $this->core->auth->userID(); + $cur->post_content = $item->content ? $item->content : $item->description; + $cur->post_title = $item->title ? $item->title : text::cutString(html::clean($cur->post_content),60); + $cur->post_format = 'xhtml'; + $cur->post_status = -2; + $cur->post_dt = strftime('%Y-%m-%d %H:%M:%S',$item->TS); + + try { + $post_id = $this->core->blog->addPost($cur); + } catch (Exception $e) { + $this->core->con->rollback(); + throw $e; + } + + foreach ($item->subject as $subject) { + $this->core->meta->setPostMeta($post_id,'tag',dcMeta::sanitizeMetaID($subject)); + } + } + + $this->core->con->commit(); + http::redirect($this->getURL().'&do=ok'); + + } + + public function gui() + { + if ($this->status) { + echo '

        '.__('Content successfully imported.').'

        '; + } + + echo + '
        '. + '
        '.__('Single blog').''. + '

        '.sprintf(__('This will import a feed (RSS or Atom) a as new content in the current blog: %s.'),html::escapeHTML($this->core->blog->name)).'

        '. + + '

        '. + form::field('feed_url',50,300,html::escapeHTML($this->feed_url)).'

        '. + + '

        '. + $this->core->formNonce(). + form::hidden(array('do'),1). + '

        '. + + '
        '. + '
        '; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/class.dc.import.flat.php b/v2/dotclear/plugins/importExport/inc/class.dc.import.flat.php new file mode 100644 index 0000000..96f2c6c --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/class.dc.import.flat.php @@ -0,0 +1,325 @@ +type = 'import'; + $this->name = __('Flat file import'); + $this->description = __('Imports a blog or a full Dotclear installation from flat file.'); + } + + public function process($do) + { + if ($do == 'single' || $do == 'full') { + $this->status = $do; + return; + } + + $to_unlink = false; + + # Single blog import + $files = $this->getPublicFiles(); + $single_upl = null; + if (!empty($_POST['public_single_file']) && in_array($_POST['public_single_file'],$files)) { + $single_upl = false; + } elseif (!empty($_FILES['up_single_file'])) { + $single_upl = true; + } + + if ($single_upl !== null) + { + if ($single_upl) { + files::uploadStatus($_FILES['up_single_file']); + $file = DC_TPL_CACHE.'/'.md5(uniqid()); + if (!move_uploaded_file($_FILES['up_single_file']['tmp_name'],$file)) { + throw new Exception(__('Unable to move uploaded file.')); + } + $to_unlink = true; + } else { + $file = $_POST['public_single_file']; + } + + try { + # Try to unzip file + $unzip_file = $this->unzip($file); + if (false !== $unzip_file) { + $bk = new flatImport($this->core,$unzip_file); + } + # Else this is a normal file + else { + $bk = new flatImport($this->core,$file); + } + + $bk->importSingle(); + } catch (Exception $e) { + @unlink($unzip_file); + if ($to_unlink) { + @unlink($file); + } + throw $e; + } + @unlink($unzip_file); + if ($to_unlink) { + @unlink($file); + } + http::redirect($this->getURL().'&do=single'); + } + + # Full import + $full_upl = null; + if (!empty($_POST['public_full_file']) && in_array($_POST['public_full_file'],$files)) { + $full_upl = false; + } elseif (!empty($_FILES['up_full_file'])) { + $full_upl = true; + } + + if ($full_upl !== null && $this->core->auth->isSuperAdmin()) + { + if (empty($_POST['your_pwd']) || !$this->core->auth->checkPassword(crypt::hmac(DC_MASTER_KEY,$_POST['your_pwd']))) { + throw new Exception(__('Password verification failed')); + } + + if ($full_upl) { + files::uploadStatus($_FILES['up_full_file']); + $file = DC_TPL_CACHE.'/'.md5(uniqid()); + if (!move_uploaded_file($_FILES['up_full_file']['tmp_name'],$file)) { + throw new Exception(__('Unable to move uploaded file.')); + } + $to_unlink = true; + } else { + $file = $_POST['public_full_file']; + } + + try { + # Try to unzip file + $unzip_file = $this->unzip($file); + if (false !== $unzip_file) { + $bk = new flatImport($this->core,$unzip_file); + } + # Else this is a normal file + else { + $bk = new flatImport($this->core,$file); + } + + $bk->importFull(); + } catch (Exception $e) { + @unlink($unzip_file); + if ($to_unlink) { + @unlink($file); + } + throw $e; + } + @unlink($unzip_file); + if ($to_unlink) { + @unlink($file); + } + http::redirect($this->getURL().'&do=full'); + } + + header('content-type:text/plain'); + var_dump($_POST); + exit; + + $this->status = true; + } + + public function gui() + { + if ($this->status == 'single') + { + echo '

        '.__('Single blog successfully imported.').'

        '; + return; + } + if ($this->status == 'full') + { + echo '

        '.__('Content successfully imported.').'

        '; + return; + } + + $public_files = array_merge(array('-' => ''),$this->getPublicFiles()); + $has_files = (boolean) (count($public_files) - 1); + + echo + '\n"; + + echo + '
        '. + '
        '.__('Single blog').''. + '

        '.sprintf(__('This will import a single blog backup as new content in the current blog: %s.'),html::escapeHTML($this->core->blog->name)).'

        '. + + '

        '. + ''. + '

        '; + + if ($has_files) { + echo + '

        '; + } + + echo + '

        '. + $this->core->formNonce(). + form::hidden(array('do'),1). + form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE). + '

        '. + + '
        '. + '
        '; + + if ($this->core->auth->isSuperAdmin()) + { + echo + '
        '. + '
        '.__('Multiple blogs').''. + '

        '.__('This will reset all the content of your database, except users.').'

        '. + + '

        '. + ''. + '

        '; + + if ($has_files) { + echo + '

        '. + form::combo('public_full_file',$public_files). + '

        '; + } + + echo + '

        '. + form::password('your_pwd',20,255).'

        '. + + '

        '. + $this->core->formNonce(). + form::hidden(array('do'),1). + form::hidden(array('MAX_FILE_SIZE'),DC_MAX_UPLOAD_SIZE). + '

        '. + + '
        '. + '
        '; + } + } + + protected function getPublicFiles() + { + $public_files = array(); + $dir = @dir($this->core->blog->public_path); + if ($dir) + { + while (($entry = $dir->read()) !== false) { + $entry_path = $dir->path.'/'.$entry; + + if (is_file($entry_path) && is_readable($entry_path)) + { + # Do not test each zip file content here, its too long + if (substr($entry_path,-4) == '.zip') { + $public_files[$entry] = $entry_path; + } + elseif (self::checkFileContent($entry_path)) { + $public_files[$entry] = $entry_path; + } + } + } + } + return $public_files; + } + + protected static function checkFileContent($entry_path) + { + $ret = false; + + $fp = fopen($entry_path,'rb'); + $ret = strpos(fgets($fp),'///DOTCLEAR|') === 0; + fclose($fp); + + return $ret; + } + + private function unzip($file) + { + $zip = new fileUnzip($file); + + if ($zip->isEmpty()) { + $zip->close(); + return false;//throw new Exception(__('File is empty or not a compressed file.')); + } + + foreach($zip->getFilesList() as $zip_file) + { + # Check zipped file name + if (substr($zip_file,-4) != '.txt') { + continue; + } + + # Check zipped file contents + $content = $zip->unzip($zip_file); + if (strpos($content,'///DOTCLEAR|') !== 0) { + unset($content); + continue; + } + + $target = path::fullFromRoot($zip_file,dirname($file)); + + # Check existing files with same name + if (file_exists($target)) { + $zip->close(); + unset($content); + throw new Exception(__('Another file with same name exists.')); + } + + # Extract backup content + if (file_put_contents($target,$content) === false) { + $zip->close(); + unset($content); + throw new Exception(__('Failed to extract backup file.')); + } + + $zip->close(); + unset($content); + + # Return extracted file name + return $target; + } + + $zip->close(); + throw new Exception(__('No backup in compressed file.')); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/class.dc.import.wp.php b/v2/dotclear/plugins/importExport/inc/class.dc.import.wp.php new file mode 100644 index 0000000..3336f11 --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/class.dc.import.wp.php @@ -0,0 +1,788 @@ + '', + 'db_name' => '', + 'db_user' => '', + 'db_pwd' => '', + 'db_prefix' => 'wp_', + 'ignore_first_cat' => 1, + 'cat_import' => 1, + 'cat_as_tags' => '', + 'cat_tags_prefix' => 'cat: ', + 'post_limit' => 20, + 'post_formater' => 'xhtml', + 'comment_formater' => 'xhtml', + 'user_ids' => array(), + 'cat_ids' => array(), + 'permalink_template' => 'p=%post_id%', + 'permalink_tags' => array( + '%year%', + '%monthnum%', + '%day%', + '%hour%', + '%minute%', + '%second%', + '%postname%', + '%post_id%', + '%category%', + '%author%' + ) + ); + protected $formaters; + + protected function setInfo() + { + $this->type = 'import'; + $this->name = __('WordPress import'); + $this->description = __('Import a WordPress installation into your current blog.'); + } + + public function init() + { + $this->con =& $this->core->con; + $this->prefix = $this->core->prefix; + $this->blog_id = $this->core->blog->id; + + if (!isset($_SESSION['wp_import_vars'])) { + $_SESSION['wp_import_vars'] = $this->base_vars; + } + $this->vars =& $_SESSION['wp_import_vars']; + + if ($this->vars['post_limit'] > 0) { + $this->post_limit = $this->vars['post_limit']; + } + + foreach ($this->core->getFormaters() as $v) { + $this->formaters[$v] = $v; + } + } + + public function resetVars() + { + $this->vars = $this->base_vars;; + unset($_SESSION['wp_import_vars']); + } + + public function process($do) + { + $this->action = $do; + } + + # We handle process in another way to always display something to + # user + protected function guiprocess($do) + { + switch ($do) + { + case 'step1': + $this->vars['db_host'] = $_POST['db_host']; + $this->vars['db_name'] = $_POST['db_name']; + $this->vars['db_user'] = $_POST['db_user']; + $this->vars['db_pwd'] = $_POST['db_pwd']; + $this->vars['db_prefix'] = $_POST['db_prefix']; + $this->vars['ignore_first_cat'] = isset($_POST['ignore_first_cat']); + $this->vars['cat_import'] = isset($_POST['cat_import']); + $this->vars['cat_as_tags'] = isset($_POST['cat_as_tags']); + $this->vars['cat_tags_prefix'] = $_POST['cat_tags_prefix']; + $this->vars['post_limit'] = abs((integer) $_POST['post_limit']) > 0 ? $_POST['post_limit'] : 0; + $this->vars['post_formater'] = isset($this->formaters[$_POST['post_formater']]) ? $_POST['post_formater'] : 'xhtml'; + $this->vars['comment_formater'] = isset($this->formaters[$_POST['comment_formater']]) ? $_POST['comment_formater'] : 'xhtml'; + $db = $this->db(); + $db->close(); + $this->step = 2; + echo $this->progressBar(1); + break; + case 'step2': + $this->step = 2; + $this->importUsers(); + $this->step = 3; + echo $this->progressBar(3); + break; + case 'step3': + $this->step = 3; + $this->importCategories(); + if ($this->core->plugins->moduleExists('blogroll')) { + $this->step = 4; + echo $this->progressBar(5); + } else { + $this->step = 5; + echo $this->progressBar(7); + } + break; + case 'step4': + $this->step = 4; + $this->importLinks(); + $this->step = 5; + echo $this->progressBar(7); + break; + case 'step5': + $this->step = 5; + $this->post_offset = !empty($_REQUEST['offset']) ? abs((integer) $_REQUEST['offset']) : 0; + if ($this->importPosts($percent) === -1) { + http::redirect($this->getURL().'&do=ok'); + } else { + echo $this->progressBar(ceil($percent*0.93)+7); + } + break; + case 'ok': + $this->resetVars(); + $this->core->blog->triggerBlog(); + $this->step = 6; + echo $this->progressBar(100); + break; + } + } + + public function gui() + { + try { + $this->guiprocess($this->action); + } catch (Exception $e) { + $this->error($e); + } + + switch ($this->step) + { + case 1: + echo + '

        '.sprintf(__('This will import your WordPress content as new content in the current blog: %s.'), + ''.html::escapeHTML($this->core->blog->name).'').'

        '. + '

        '.__('Please note that this process '. + 'will empty your categories, blogroll, entries and comments on the current blog.').'

        '. + '

        '.__('Depending on the size of your blog, it could take a few minutes.').'

        '; + + printf($this->imForm(1,__('General information'),__('Import my blog now')), + '

        '.__('We first need some information about your old WordPress installation.').'

        '. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '.__('Entries import options').'

        '. + '

        '.__('WordPress and Dotclear\'s handling of categories are quite different. '. + 'You can assign several categories to a single post in WordPress. In the Dotclear world, '. + 'we see it more like "One category, several tags." Therefore Dotclear can only import one '. + 'category per post and will chose the lowest numbered one. If you want to keep a trace of '. + 'every category, you can import them as tags, with an optional prefix.').'

        '. + '

        '.__('On the other hand, in WordPress, a post can not be uncategorized, and a default '. + 'installation has a first category labelised "Uncategorized". If you did not change that '. + 'category, you can just ignore it while importing your blog, as Dotclear allows you to '. + 'actually keep your posts uncategorized.').'

        '. + '

        '. + '

        '. + '

        '. + '

        '. + '

        '. + + '

        '.__('Content filters').'

        '. + '

        '.__('You may want to process your post and/or comment content with the following filters.').'

        '. + '

        '. + '

        ' + ); + break; + case 2: + printf($this->imForm(2,__('Importing users')), + $this->autoSubmit() + ); + break; + case 3: + printf($this->imForm(3,__('Importing categories')), + $this->autoSubmit() + ); + break; + case 4: + printf($this->imForm(4,__('Importing blogroll')), + $this->autoSubmit() + ); + break; + case 5: + $t = sprintf(__('Importing entries from %d to %d / %d'),$this->post_offset, + min(array($this->post_offset+$this->post_limit,$this->post_count)),$this->post_count); + printf($this->imForm(5,$t), + form::hidden(array('offset'),$this->post_offset). + $this->autoSubmit() + ); + break; + case 6: + echo + '

        '.__('Every newly imported user has received a random password '. + 'and will need to ask for a new one by following the "I forgot my password" link on the login page '. + '(Their registered email address has to be valid.)').'

        '. + $this->congratMessage(); + break; + } + } + + # Simple form for step by step process + protected function imForm($step,$legend,$submit_value=null) + { + if (!$submit_value) { + $submit_value = __('next step').' >'; + } + + return + '
        '. + '
        '.$legend.''. + $this->core->formNonce(). + form::hidden(array('do'),'step'.$step). + '%s'. + '

        '. + '
        '. + '
        '; + } + + # Error display + protected function error($e) + { + echo '
        '.__('Errors:').''. + '

        '.$e->getMessage().'

        '; + } + + # Database init + protected function db() + { + $db = dbLayer::init('mysql',$this->vars['db_host'],$this->vars['db_name'],$this->vars['db_user'],$this->vars['db_pwd']); + + $rs = $db->select("SHOW TABLES LIKE '".$this->vars['db_prefix']."%'"); + if ($rs->isEmpty()) { + throw new Exception(__('WordPress tables not found')); + } + + while ($rs->fetch()) { + $this->has_table[$rs->f(0)] = true; + } + + # Set this to read data as they were written + try { + $db->execute('SET NAMES DEFAULT'); + } catch (Exception $e) {} + + $db->execute('SET CHARACTER SET DEFAULT'); + $db->execute("SET COLLATION_CONNECTION = DEFAULT"); + $db->execute("SET COLLATION_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_SERVER = DEFAULT"); + $db->execute("SET CHARACTER_SET_DATABASE = DEFAULT"); + + $this->post_count = $db->select( + 'SELECT COUNT(ID) FROM '.$this->vars['db_prefix'].'posts '. + 'WHERE post_type = \'post\' OR post_type = \'page\'' + )->f(0); + + return $db; + } + + protected function cleanStr($str) + { + return text::cleanUTF8(@text::toUTF8($str)); + } + + # Users import + protected function importUsers() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'users'); + + try + { + $this->con->begin(); + + while ($rs->fetch()) + { + $user_login = preg_replace('/[^A-Za-z0-9@._-]/','-',$rs->user_login); + $this->vars['user_ids'][$rs->ID] = $user_login; + if (!$this->core->userExists($user_login)) + { + $cur = $this->con->openCursor($this->prefix.'user'); + $cur->user_id = $user_login; + $cur->user_pwd = crypt::createPassword(); + $cur->user_displayname = $rs->user_nicename; + $cur->user_email = $rs->user_email; + $cur->user_url = $rs->user_url; + $cur->user_creadt = $rs->user_registered; + $cur->user_lang = $this->core->blog->settings->system->lang; + $cur->user_tz = $this->core->blog->settings->system->blog_timezone; + $permissions = array(); + + $rs_meta = $db->select('SELECT * FROM '.$prefix.'usermeta WHERE user_id = '.$rs->ID); + while ($rs_meta->fetch()) + { + switch ($rs_meta->meta_key) + { + case 'first_name': + $cur->user_firstname = $this->cleanStr($rs_meta->meta_value); + break; + case 'last_name': + $cur->user_name = $this->cleanStr($rs_meta->meta_value); + break; + case 'description': + $cur->user_desc = $this->cleanStr($rs_meta->meta_value); + break; + case 'rich_editing': + $cur->user_options = new ArrayObject(array( + 'enable_wysiwyg' => $rs_meta->meta_value == 'true' ? true : false + )); + break; + case 'wp_user_level': + switch ($rs_meta->meta_value) + { + case '0': # Subscriber + $cur->user_status = 0; + break; + case '1': # Contributor + $permissions['usage'] = true; + $permissions['publish'] = true; + $permissions['delete'] = true; + break; + case '2': # Author + case '3': + case '4': + $permissions['contentadmin'] = true; + $permissions['media'] = true; + break; + case '5': # Editor + case '6': + case '7': + $permissions['contentadmin'] = true; + $permissions['categories'] = true; + $permissions['media_admin'] = true; + $permissions['pages'] = true; + $permissions['blogroll'] = true; + break; + case '8': # Administrator + case '9': + case '10': + $permissions['admin'] = true; + break; + } + break; + } + } + $this->core->addUser($cur); + $this->core->setUserBlogPermissions( + $cur->user_id, + $this->blog_id, + $permissions + ); + } + } + $this->con->commit(); + $db->close(); + } + catch (Exception $e) + { + $this->con->rollback(); + $db->close(); + throw $e; + } + } + + # Categories import + protected function importCategories() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select( + 'SELECT * FROM '.$prefix.'terms AS t, '.$prefix.'term_taxonomy AS x '. + 'WHERE x.taxonomy = \'category\' '. + 'AND t.term_id = x.term_id '. + ($this->vars['ignore_first_cat'] ? 'AND t.term_id <> 1 ' : ''). + 'ORDER BY t.term_id ASC' + ); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + $ord = 2; + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'category'); + $cur->blog_id = $this->blog_id; + $cur->cat_title = $this->cleanStr($rs->name); + $cur->cat_desc = $this->cleanStr($rs->description); + $cur->cat_url = $this->cleanStr($rs->slug); + $cur->cat_lft = $ord++; + $cur->cat_rgt = $ord++; + + $cur->cat_id = $this->con->select( + 'SELECT MAX(cat_id) FROM '.$this->prefix.'category' + )->f(0) + 1; + $this->vars['cat_ids'][$rs->term_id] = $cur->cat_id; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Blogroll import + protected function importLinks() + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + $rs = $db->select('SELECT * FROM '.$prefix.'links ORDER BY link_id ASC'); + + try + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'link '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'link'); + $cur->blog_id = $this->blog_id; + $cur->link_href = $this->cleanStr($rs->link_url); + $cur->link_title = $this->cleanStr($rs->link_name); + $cur->link_desc = $this->cleanStr($rs->link_description); + $cur->link_xfn = $this->cleanStr($rs->link_rel); + + $cur->link_id = $this->con->select( + 'SELECT MAX(link_id) FROM '.$this->prefix.'link' + )->f(0) + 1; + $cur->insert(); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + } + + # Entries import + protected function importPosts(&$percent) + { + $db = $this->db(); + $prefix = $this->vars['db_prefix']; + + $plink = $db->select( + 'SELECT option_value FROM '.$prefix.'options '. + "WHERE option_name = 'permalink_structure'" + )->option_value; + if ($plink) { + $this->vars['permalink_template'] = substr($plink,1); + } + + $rs = $db->select( + 'SELECT * FROM '.$prefix.'posts '. + 'WHERE post_type = \'post\' OR post_type = \'page\' '. + 'ORDER BY ID ASC '. + $db->limit($this->post_offset,$this->post_limit) + ); + + try + { + if ($this->post_offset == 0) + { + $this->con->execute( + 'DELETE FROM '.$this->prefix.'post '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + } + + while ($rs->fetch()) { + $this->importPost($rs,$db); + } + + $db->close(); + } + catch (Exception $e) + { + $db->close(); + throw $e; + } + + if ($rs->count() < $this->post_limit) { + return -1; + } else { + $this->post_offset += $this->post_limit; + } + + if ($this->post_offset > $this->post_count) { + $percent = 100; + } else { + $percent = $this->post_offset * 100 / $this->post_count; + } + } + + protected function importPost($rs,$db) + { + $post_date = !@strtotime($rs->post_date) ? '1970-01-01 00:00' : $rs->post_date; + if (!isset($this->vars['user_ids'][$rs->post_author])) { + $user_id = $this->core->auth->userID(); + } else { + $user_id = $this->vars['user_ids'][$rs->post_author]; + } + + $cur = $this->con->openCursor($this->prefix.'post'); + $cur->blog_id = $this->blog_id; + $cur->user_id = $user_id; + $cur->post_dt = $post_date; + $cur->post_creadt = $post_date; + $cur->post_upddt = $rs->post_modified; + $cur->post_title = $this->cleanStr($rs->post_title); + + if (!$cur->post_title) { + $cur->post_title = 'No title'; + } + + if ($this->vars['cat_import'] || $this->vars['cat_as_tags']) + { + $old_cat_ids = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'terms AS t, '. + $this->vars['db_prefix'].'term_taxonomy AS x, '. + $this->vars['db_prefix'].'term_relationships AS r '. + 'WHERE t.term_id = x.term_id '. + ($this->vars['ignore_first_cat'] ? 'AND t.term_id <> 1 ' : ''). + 'AND x.taxonomy = \'category\' '. + 'AND t.term_id = r.term_taxonomy_id '. + 'AND r.object_id ='.$rs->ID. + ' ORDER BY t.term_id ASC ' + ); + if (!$old_cat_ids->isEmpty() && $this->vars['cat_import']) + { + $cur->cat_id = $this->vars['cat_ids'][(integer) $old_cat_ids->term_id]; + } + } + + $permalink_infos = array( + date('Y',strtotime($cur->post_dt)), + date('m',strtotime($cur->post_dt)), + date('d',strtotime($cur->post_dt)), + date('H',strtotime($cur->post_dt)), + date('i',strtotime($cur->post_dt)), + date('s',strtotime($cur->post_dt)), + $rs->post_name, + $rs->ID, + $cur->cat_id, + $cur->user_id + ); + $cur->post_url = str_replace( + $this->vars['permalink_tags'], + $permalink_infos, + $rs->post_type== 'post' ? $this->vars['permalink_template'] : '%postname%' + ); + $cur->post_url = substr($cur->post_url,0,255); + + if (!$cur->post_url) { + $cur->post_url = $rs->ID; + } + + $cur->post_format = $this->vars['post_formater']; + $_post_content = explode('',$rs->post_content,2); + if (count($_post_content) == 1) { + $cur->post_excerpt = NULL; + $cur->post_content = $this->cleanStr(array_shift($_post_content)); + } else { + $cur->post_excerpt = $this->cleanStr(array_shift($_post_content)); + $cur->post_content = $this->cleanStr(array_shift($_post_content)); + } + + $cur->post_content_xhtml = $this->core->callFormater($this->vars['post_formater'],$cur->post_content); + $cur->post_excerpt_xhtml = $this->core->callFormater($this->vars['post_formater'],$cur->post_excerpt); + + switch ($rs->post_status) + { + case 'publish': + $cur->post_status = 1; + break; + case 'draft': + $cur->post_status = 0; + break; + case 'pending': + $cur->post_status = -2; + break; + default: + $cur->post_status = -2; + } + $cur->post_type = $rs->post_type; + $cur->post_password = $rs->post_password ? $rs->post_password : NULL; + $cur->post_open_comment = $rs->comment_status == 'open' ? 1 : 0; + $cur->post_open_tb = $rs->ping_status == 'open' ? 1 : 0; + + $cur->post_words = implode(' ',text::splitWords( + $cur->post_title.' '. + $cur->post_excerpt_xhtml.' '. + $cur->post_content_xhtml + )); + + $cur->post_id = $this->con->select( + 'SELECT MAX(post_id) FROM '.$this->prefix.'post' + )->f(0) + 1; + + $cur->post_url = $this->core->blog->getPostURL($cur->post_url,$cur->post_dt,$cur->post_title,$cur->post_id); + + $cur->insert(); + $this->importComments($rs->ID,$cur->post_id,$db); + $this->importPings($rs->ID,$cur->post_id,$db); + + # Create tags + $this->importTags($rs->ID,$cur->post_id,$db); + + if (!$old_cat_ids->isEmpty() && $this->vars['cat_as_tags']) + { + $old_cat_ids->moveStart(); + while ($old_cat_ids->fetch()) { + $this->core->meta->setPostMeta($cur->post_id,'tag',$this->cleanStr($this->vars['cat_tags_prefix'].$old_cat_ids->name)); + } + } + } + + # Comments import + protected function importComments($post_id,$new_post_id,$db) + { + $count_c = $count_t = 0; + + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'comments '. + 'WHERE comment_post_ID = '.(integer) $post_id.' ' + ); + + while ($rs->fetch()) + { + $cur = $this->con->openCursor($this->prefix.'comment'); + $cur->post_id = (integer) $new_post_id; + $cur->comment_author = $this->cleanStr($rs->comment_author); + $cur->comment_status = (integer) $rs->comment_approved ; + $cur->comment_dt = $rs->comment_date; + $cur->comment_email = $this->cleanStr($rs->comment_author_email); + $cur->comment_content = $this->core->callFormater($this->vars['comment_formater'],$this->cleanStr($rs->comment_content)); + $cur->comment_ip = $rs->comment_author_IP; + $cur->comment_trackback = $rs->comment_type == 'trackback' ? 1 : 0; + $cur->comment_site = substr($this->cleanStr($rs->comment_author_url),0,255); + if ($cur->comment_site == '') $cur->comment_site = NULL; + + if ($rs->comment_approved == 'spam') { + $cur->comment_status = -2; + } + + $cur->comment_words = implode(' ',text::splitWords($cur->comment_content)); + + $cur->comment_id = $this->con->select( + 'SELECT MAX(comment_id) FROM '.$this->prefix.'comment' + )->f(0) + 1; + + $cur->insert(); + + if ($cur->comment_trackback && $cur->comment_status == 1) { + $count_t++; + } elseif ($cur->comment_status == 1) { + $count_c++; + } + } + + if ($count_t > 0 || $count_c > 0) + { + $this->con->execute( + 'UPDATE '.$this->prefix.'post SET '. + 'nb_comment = '.$count_c.', '. + 'nb_trackback = '.$count_t.' '. + 'WHERE post_id = '.(integer) $new_post_id.' ' + ); + } + } + + # Pings import + protected function importPings($post_id,$new_post_id,$db) + { + $urls = array(); + $pings = array(); + + $rs = $db->select( + 'SELECT pinged FROM '.$this->vars['db_prefix'].'posts '. + 'WHERE ID = '.(integer) $post_id + ); + $pings = explode ("\n",$rs->pinged); + unset ($pings[0]); + + foreach($pings as $ping_url) + { + $url = $this->cleanStr($ping_url); + if (isset($urls[$url])) { + continue; + } + + $cur = $this->con->openCursor($this->prefix.'ping'); + $cur->post_id = (integer) $new_post_id; + $cur->ping_url = $url; + $cur->insert(); + + $urls[$url] = true; + } + } + + # Meta import + protected function importTags($post_id,$new_post_id,$db) + { + $rs = $db->select( + 'SELECT * FROM '.$this->vars['db_prefix'].'terms AS t, '. + $this->vars['db_prefix'].'term_taxonomy AS x, '. + $this->vars['db_prefix'].'term_relationships AS r '. + 'WHERE t.term_id = x.term_id '. + 'AND x.taxonomy = \'post_tag\' '. + 'AND t.term_id = r.term_taxonomy_id '. + 'AND r.object_id ='.$post_id. + ' ORDER BY t.term_id ASC' + ); + + if ($rs->isEmpty()) { + return; + } + + while ($rs->fetch()) { + $this->core->meta->setPostMeta($new_post_id,'tag',$this->cleanStr($rs->name)); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/flat/class.flat.backup.php b/v2/dotclear/plugins/importExport/inc/flat/class.flat.backup.php new file mode 100644 index 0000000..7e61cad --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/flat/class.flat.backup.php @@ -0,0 +1,148 @@ +(\\\\\\\\)*+)(\\\\n)/u' => "\$1\n", + '/(?(\\\\\\\\)*+)(\\\\r)/u' => "\$1\r", + '/(?(\\\\\\\\)*+)(\\\\")/u' => '$1"', + '/(\\\\\\\\)/' => '\\' + ); + + public function __construct($file) + { + if (file_exists($file) && is_readable($file)) { + $this->fp = fopen($file,'rb'); + $this->line_num = 1; + } else { + throw new Exception(__('No file to read.')); + } + } + + public function __destruct() + { + if ($this->fp) { + fclose($this->fp); + } + } + + public function getLine() + { + if (($line = $this->nextLine()) === false) { + return false; + } + + if (substr($line,0,1) == '[') + { + $this->line_name = substr($line,1,strpos($line,' ')-1); + + $line = substr($line,strpos($line,' ')+1,-1); + $this->line_cols = explode(',',$line); + + return $this->getLine(); + } + elseif (substr($line,0,1) == '"') + { + $line = preg_replace('/^"|"$/','',$line); + $line = preg_split('/(^"|","|"$)/m',$line); + + if (count($this->line_cols) != count($line)) { + throw new Exception(sprintf('Invalid row count at line %s',$this->line_num)); + } + + $res = array(); + + for ($i=0; $iline_cols[$i]] = + preg_replace(array_keys($this->replacement),array_values($this->replacement),$line[$i]); + } + + return new flatBackupItem($this->line_name,$res,$this->line_num); + } + else + { + return $this->getLine(); + } + } + + private function nextLine() + { + if (feof($this->fp)) { + return false; + } + $this->line_num++; + + $line = fgets($this->fp); + $line = trim($line); + + return empty($line) ? $this->nextLine() : $line; + } +} + +class flatBackupItem +{ + public $__name; + public $__line; + private $__data = array(); + + public function __construct($name,$data,$line) + { + $this->__name = $name; + $this->__data = $data; + $this->__line = $line; + } + + public function f($name) + { + return iconv('UTF-8','UTF-8//IGNORE',$this->__data[$name]); + } + + public function __get($name) + { + return $this->f($name); + } + + public function __set($n,$v) + { + $this->__data[$n] = $v; + } + + public function exists($n) + { + return isset($this->__data[$n]); + } + + public function drop() + { + foreach (func_get_args() as $n) { + if (isset($this->__data[$n])) { + unset($this->__data[$n]); + } + } + } + + public function substitute($old,$new) + { + if (isset($this->__data[$old])) { + $this->__data[$new] = $this->__data[$old]; + unset($this->__data[$old]); + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/flat/class.flat.export.php b/v2/dotclear/plugins/importExport/inc/flat/class.flat.export.php new file mode 100644 index 0000000..eec2b75 --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/flat/class.flat.export.php @@ -0,0 +1,106 @@ +con =& $con; + $this->prefix = $prefix; + + if (($this->fp = fopen($out,'w')) === false) { + return false; + } + @set_time_limit(300); + } + + function __destruct() + { + if (is_resource($this->fp)) { + fclose($this->fp); + } + } + + function export($name,$sql) + { + $rs = $this->con->select($sql); + + if (!$rs->isEmpty()) + { + fwrite($this->fp,"\n[".$name.' '.implode(',',$rs->columns())."]\n"); + while ($rs->fetch()) { + fwrite($this->fp,$this->getLine($rs)); + } + fflush($this->fp); + } + } + + function exportAll() + { + $tables = $this->getTables(); + + foreach ($tables as $table) + { + $this->exportTable($table); + } + } + + function exportTable($table) + { + $req = 'SELECT * FROM '.$this->con->escapeSystem($this->prefix.$table); + + $this->export($table,$req); + } + + function getTables() + { + $schema = dbSchema::init($this->con); + $db_tables = $schema->getTables(); + + $tables = array(); + foreach ($db_tables as $t) + { + if ($this->prefix) { + if (strpos($t,$this->prefix) === 0) { + $tables[] = $t; + } + } else { + $tables[] = $t; + } + } + + return $tables; + } + + function getLine($rs) + { + $l = array(); + $cols = $rs->columns(); + foreach ($cols as $i => &$c) { + $s = $rs->f($c); + $s = preg_replace($this->line_reg,$this->line_rep,$s); + $s = '"'.$s.'"'; + $l[$i] = $s; + } + return implode(',',$l)."\n"; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/flat/class.flat.import.php b/v2/dotclear/plugins/importExport/inc/flat/class.flat.import.php new file mode 100644 index 0000000..bdbd55b --- /dev/null +++ b/v2/dotclear/plugins/importExport/inc/flat/class.flat.import.php @@ -0,0 +1,869 @@ + array(), + 'post' => array(), + 'media' => array() + ); + + public $stack = array( + 'categories'=>null, + 'cat_id'=>1, + 'cat_lft'=>array(), + 'post_id'=>1, + 'media_id'=>1, + 'comment_id'=>1, + 'link_id'=>1 + ); + + public $has_categories = false; + + public function __construct($core,$file) + { + parent::__construct($file); + + $first_line = fgets($this->fp); + if (strpos($first_line,'///DOTCLEAR|') !== 0) { + throw new Exception(__('File is not a DotClear backup.')); + } + + @set_time_limit(300); + + $l = explode('|',$first_line); + + if (isset($l[1])) { + $this->dc_version = $l[1]; + } + + $this->mode = isset($l[2]) ? strtolower(trim($l[2])) : 'single'; + if ($this->mode != 'full' && $this->mode != 'single') { + $this->mode = 'single'; + } + + if (version_compare('1.2',$this->dc_version,'<=') && + version_compare('1.3',$this->dc_version,'>')) { + $this->dc_major_version = '1.2'; + } else { + $this->dc_major_version = '2.0'; + } + + $this->core =& $core; + $this->con =& $core->con; + $this->prefix = $core->prefix; + + $this->cur_blog = $this->con->openCursor($this->prefix.'blog'); + $this->cur_category = $this->con->openCursor($this->prefix.'category'); + $this->cur_link = $this->con->openCursor($this->prefix.'link'); + $this->cur_setting = $this->con->openCursor($this->prefix.'setting'); + $this->cur_user = $this->con->openCursor($this->prefix.'user'); + $this->cur_pref = $this->con->openCursor($this->prefix.'pref'); + $this->cur_permissions = $this->con->openCursor($this->prefix.'permissions'); + $this->cur_post = $this->con->openCursor($this->prefix.'post'); + $this->cur_meta = $this->con->openCursor($this->prefix.'meta'); + $this->cur_media = $this->con->openCursor($this->prefix.'media'); + $this->cur_post_media = $this->con->openCursor($this->prefix.'post_media'); + $this->cur_log = $this->con->openCursor($this->prefix.'log'); + $this->cur_ping = $this->con->openCursor($this->prefix.'ping'); + $this->cur_comment = $this->con->openCursor($this->prefix.'comment'); + $this->cur_spamrule = $this->con->openCursor($this->prefix.'spamrule'); + $this->cur_version = $this->con->openCursor($this->prefix.'version'); + + # --BEHAVIOR-- importInit + $this->core->callBehavior('importInit',$this,$this->core); + } + + public function importSingle() + { + if ($this->mode != 'single') { + throw new Exception(__('File is not a single blog export.')); + } + + if (!$this->core->auth->check('admin',$this->core->blog->id)) { + throw new Exception(__('Permission denied.')); + } + + $this->blog_id = $this->core->blog->id; + + $this->stack['categories'] = $this->con->select( + 'SELECT cat_id, cat_title, cat_url '. + 'FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->blog_id)."' " + ); + + $rs = $this->con->select('SELECT MAX(cat_id) FROM '.$this->prefix.'category'); + $this->stack['cat_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(link_id) FROM '.$this->prefix.'link'); + $this->stack['link_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(post_id) FROM '.$this->prefix.'post'); + $this->stack['post_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(media_id) FROM '.$this->prefix.'media'); + $this->stack['media_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select('SELECT MAX(comment_id) FROM '.$this->prefix.'comment'); + $this->stack['comment_id'] = ((integer) $rs->f(0))+1; + + $rs = $this->con->select( + 'SELECT MAX(cat_rgt) AS cat_rgt FROM '.$this->prefix.'category '. + "WHERE blog_id = '".$this->con->escape($this->core->blog->id)."'" + ); + + if ((integer) $rs->cat_rgt > 0) { + $this->has_categories = true; + $this->stack['cat_lft'][$this->core->blog->id] = (integer) $rs->cat_rgt + 1; + } + + $this->con->begin(); + + try + { + $last_line_name = ''; + $constrained = array('post', 'meta', 'post_media', 'ping', 'comment'); + + while (($line = $this->getLine()) !== false) + { + # import DC 1.2.x, we fix lines before insert + if ($this->dc_major_version == '1.2') { + $this->prepareDC12line($line); + } + + if ($last_line_name != $line->__name) { + if (in_array($last_line_name,$constrained)) { + # UNDEFER + if ($this->con->driver() == 'mysql') $this->con->execute('SET foreign_key_checks = 1'); + if ($this->con->driver() == 'pgsql') $this->con->execute('SET CONSTRAINTS ALL DEFERRED'); + } + + if (in_array($line->__name,$constrained)) { + # DEFER + if ($this->con->driver() == 'mysql') $this->con->execute('SET foreign_key_checks = 0'); + if ($this->con->driver() == 'pgsql') $this->con->execute('SET CONSTRAINTS ALL IMMEDIATE'); + } + + $last_line_name = $line->__name; + } + + switch ($line->__name) + { + case 'category': + $this->insertCategorySingle($line); + break; + case 'link': + $this->insertLinkSingle($line); + break; + case 'post': + $this->insertPostSingle($line); + break; + case 'meta': + $this->insertMetaSingle($line); + break; + case 'media': + $this->insertMediaSingle($line); + break; + case 'post_media': + $this->insertPostMediaSingle($line); + break; + case 'ping': + $this->insertPingSingle($line); + break; + case 'comment': + $this->insertCommentSingle($line); + break; + } + + # --BEHAVIOR-- importSingle + $this->core->callBehavior('importSingle',$line,$this,$this->core); + } + + if ($this->con->driver() == 'mysql') $this->con->execute('SET foreign_key_checks = 1'); + if ($this->con->driver() == 'pgsql') $this->con->execute('SET CONSTRAINTS ALL DEFERRED'); + } + catch (Exception $e) + { + $this->con->rollback(); + throw $e; + } + + $this->con->commit(); + } + + public function importFull() + { + if ($this->mode != 'full') { + throw new Exception(__('File is not a full export.')); + } + + if (!$this->core->auth->isSuperAdmin()) { + throw new Exception(__('Permission denied.')); + } + + $this->con->begin(); + $this->con->execute('DELETE FROM '.$this->prefix.'blog'); + $this->con->execute('DELETE FROM '.$this->prefix.'media'); + $this->con->execute('DELETE FROM '.$this->prefix.'spamrule'); + $this->con->execute('DELETE FROM '.$this->prefix.'setting'); + + try + { + while (($line = $this->getLine()) !== false) + { + switch ($line->__name) + { + case 'blog': + $this->insertBlog($line); + break; + case 'category': + $this->insertCategory($line); + break; + case 'link': + $this->insertLink($line); + break; + case 'setting': + $this->insertSetting($line); + break; + case 'user': + $this->insertUser($line); + break; + case 'pref': + $this->insertPref($line); + break; + case 'permissions': + $this->insertPermissions($line); + break; + case 'post': + $this->insertPost($line); + break; + case 'meta': + $this->insertMeta($line); + break; + case 'media': + $this->insertMedia($line); + break; + case 'post_media': + $this->insertPostMedia($line); + break; + case 'log'; + $this->insertLog($line); + break; + case 'ping': + $this->insertPing($line); + break; + case 'comment': + $this->insertComment($line); + break; + case 'spamrule': + $this->insertSpamRule($line); + break; + } + # --BEHAVIOR-- importFull + $this->core->callBehavior('importFull',$line,$this,$this->core); + } + } + catch (Exception $e) + { + $this->con->rollback(); + throw $e; + } + + $this->con->commit(); + } + + private function insertBlog($blog) + { + $this->cur_blog->clean(); + + $this->cur_blog->blog_id = (string) $blog->blog_id; + $this->cur_blog->blog_uid = (string) $blog->blog_uid; + $this->cur_blog->blog_creadt = (string) $blog->blog_creadt; + $this->cur_blog->blog_upddt = (string) $blog->blog_upddt; + $this->cur_blog->blog_url = (string) $blog->blog_url; + $this->cur_blog->blog_name = (string) $blog->blog_name; + $this->cur_blog->blog_desc = (string) $blog->blog_desc; + + $this->cur_blog->blog_status = $blog->exists('blog_status') ? (integer) $blog->blog_status : 1; + + $this->cur_blog->insert(); + } + + private function insertCategory($category) + { + $this->cur_category->clean(); + + $this->cur_category->cat_id = (string) $category->cat_id; + $this->cur_category->blog_id = (string) $category->blog_id; + $this->cur_category->cat_title = (string) $category->cat_title; + $this->cur_category->cat_url = (string) $category->cat_url; + $this->cur_category->cat_desc = (string) $category->cat_desc; + + if (!$this->has_categories && $category->exists('cat_lft') && $category->exists('cat_rgt')) { + $this->cur_category->cat_lft = (integer) $category->cat_lft; + $this->cur_category->cat_rgt = (integer) $category->cat_rgt; + } else { + if (!isset($this->stack['cat_lft'][$category->blog_id])) { + $this->stack['cat_lft'][$category->blog_id] = 2; + } + $this->cur_category->cat_lft = $this->stack['cat_lft'][$category->blog_id]++; + $this->cur_category->cat_rgt = $this->stack['cat_lft'][$category->blog_id]++; + } + + $this->cur_category->insert(); + } + + private function insertLink($link) + { + $this->cur_link->clean(); + + $this->cur_link->link_id = (integer) $link->link_id; + $this->cur_link->blog_id = (string) $link->blog_id; + $this->cur_link->link_href = (string) $link->link_href; + $this->cur_link->link_title = (string) $link->link_title; + $this->cur_link->link_desc = (string) $link->link_desc; + $this->cur_link->link_lang = (string) $link->link_lang; + $this->cur_link->link_xfn = (string) $link->link_xfn; + $this->cur_link->link_position = (integer) $link->link_position; + + $this->cur_link->insert(); + } + + private function insertSetting($setting) + { + $this->cur_setting->clean(); + + $this->cur_setting->setting_id = (string) $setting->setting_id; + $this->cur_setting->blog_id = !$setting->blog_id ? null : (string) $setting->blog_id; + $this->cur_setting->setting_ns = (string) $setting->setting_ns; + $this->cur_setting->setting_value = (string) $setting->setting_value; + $this->cur_setting->setting_type = (string) $setting->setting_type; + $this->cur_setting->setting_label = (string) $setting->setting_label; + + $this->cur_setting->insert(); + } + + private function insertPref($pref) + { + if ($this->prefExists($pref->pref_ws,$pref->pref_id,$pref->user_id)) { + return; + } + + $this->cur_pref->clean(); + + $this->cur_pref->pref_id = (string) $pref->pref_id; + $this->cur_pref->user_id = !$pref->user_id ? null : (string) $pref->user_id; + $this->cur_pref->pref_ws = (string) $pref->pref_ws; + $this->cur_pref->pref_value = (string) $pref->pref_value; + $this->cur_pref->pref_type = (string) $pref->pref_type; + $this->cur_pref->pref_label = (string) $pref->pref_label; + + $this->cur_pref->insert(); + } + + private function insertUser($user) + { + if ($this->userExists($user->user_id)) { + return; + } + + $this->cur_user->clean(); + + $this->cur_user->user_id = (string) $user->user_id; + $this->cur_user->user_super = (integer) $user->user_super; + $this->cur_user->user_pwd = (string) $user->user_pwd; + $this->cur_user->user_recover_key = (string) $user->user_recover_key; + $this->cur_user->user_name = (string) $user->user_name; + $this->cur_user->user_firstname = (string) $user->user_firstname; + $this->cur_user->user_displayname = (string) $user->user_displayname; + $this->cur_user->user_email = (string) $user->user_email; + $this->cur_user->user_url = (string) $user->user_url; + $this->cur_user->user_default_blog = !$user->user_default_blog ? null : (string) $user->user_default_blog; + $this->cur_user->user_lang = (string) $user->user_lang; + $this->cur_user->user_tz = (string) $user->user_tz; + $this->cur_user->user_post_status = (integer) $user->user_post_status; + $this->cur_user->user_creadt = (string) $user->user_creadt; + $this->cur_user->user_upddt = (string) $user->user_upddt; + + $this->cur_user->user_desc = $user->exists('user_desc') ? (string) $user->user_desc : null; + $this->cur_user->user_options = $user->exists('user_options') ? (string) $user->user_options : null; + $this->cur_user->user_status = $user->exists('user_status') ? (integer) $user->user_status : 1; + + $this->cur_user->insert(); + + $this->stack['users'][$user->user_id] = true; + } + + private function insertPermissions($permissions) + { + $this->cur_permissions->clean(); + + $this->cur_permissions->user_id = (string) $permissions->user_id; + $this->cur_permissions->blog_id = (string) $permissions->blog_id; + $this->cur_permissions->permissions = (string) $permissions->permissions; + + $this->cur_permissions->insert(); + } + + private function insertPost($post) + { + $this->cur_post->clean(); + + $cat_id = (integer) $post->cat_id; + if (!$cat_id) { + $cat_id = null; + } + + $post_password = $post->post_password ? (string) $post->post_password : null; + + $this->cur_post->post_id = (integer) $post->post_id; + $this->cur_post->blog_id = (string) $post->blog_id; + $this->cur_post->user_id = (string) $this->getUserId($post->user_id); + $this->cur_post->cat_id = $cat_id; + $this->cur_post->post_dt = (string) $post->post_dt; + $this->cur_post->post_creadt = (string) $post->post_creadt; + $this->cur_post->post_upddt = (string) $post->post_upddt; + $this->cur_post->post_password = $post_password; + $this->cur_post->post_type = (string) $post->post_type; + $this->cur_post->post_format = (string) $post->post_format; + $this->cur_post->post_url = (string) $post->post_url; + $this->cur_post->post_lang = (string) $post->post_lang; + $this->cur_post->post_title = (string) $post->post_title; + $this->cur_post->post_excerpt = (string) $post->post_excerpt; + $this->cur_post->post_excerpt_xhtml = (string) $post->post_excerpt_xhtml; + $this->cur_post->post_content = (string) $post->post_content; + $this->cur_post->post_content_xhtml = (string) $post->post_content_xhtml; + $this->cur_post->post_notes = (string) $post->post_notes; + $this->cur_post->post_words = (string) $post->post_words; + $this->cur_post->post_meta = (string) $post->post_meta; + $this->cur_post->post_status = (integer) $post->post_status; + $this->cur_post->post_selected = (integer) $post->post_selected; + $this->cur_post->post_open_comment = (integer) $post->post_open_comment; + $this->cur_post->post_open_tb = (integer) $post->post_open_tb; + $this->cur_post->nb_comment = (integer) $post->nb_comment; + $this->cur_post->nb_trackback = (integer) $post->nb_trackback; + + $this->cur_post->post_tz = $post->exists('post_tz') ? (string) $post->post_tz : 'UTC'; + + $this->cur_post->insert(); + } + + private function insertMeta($meta) + { + $this->cur_meta->clean(); + + $this->cur_meta->meta_id = (string) $meta->meta_id; + $this->cur_meta->meta_type = (string) $meta->meta_type; + $this->cur_meta->post_id = (integer) $meta->post_id; + + $this->cur_meta->insert(); + } + + private function insertMedia($media) + { + $this->cur_media->clean(); + + $this->cur_media->media_id = (integer) $media->media_id; + $this->cur_media->user_id = (string) $media->user_id; + $this->cur_media->media_path = (string) $media->media_path; + $this->cur_media->media_title = (string) $media->media_title; + $this->cur_media->media_file = (string) $media->media_file; + $this->cur_media->media_meta = (string) $media->media_meta; + $this->cur_media->media_dt = (string) $media->media_dt; + $this->cur_media->media_creadt = (string) $media->media_creadt; + $this->cur_media->media_upddt = (string) $media->media_upddt; + $this->cur_media->media_private = (integer) $media->media_private; + + $this->cur_media->media_dir = $media->exists('media_dir') ? (string) $media->media_dir : dirname($media->media_file); + + if (!$this->mediaExists()) { + $this->cur_media->insert(); + } + } + + private function insertPostMedia($post_media) + { + $this->cur_post_media->clean(); + + $this->cur_post_media->media_id = (integer) $post_media->media_id; + $this->cur_post_media->post_id = (integer) $post_media->post_id; + + $this->cur_post_media->insert(); + } + + private function insertLog($log) + { + $this->cur_log->clean(); + + $this->cur_log->log_id = (integer) $log->log_id; + $this->cur_log->user_id = (string) $log->user_id; + $this->cur_log->log_table = (string) $log->log_table; + $this->cur_log->log_dt = (string) $log->log_dt; + $this->cur_log->log_ip = (string) $log->log_ip; + $this->cur_log->log_msg = (string) $log->log_msg; + + $this->cur_log->insert(); + } + + private function insertPing($ping) + { + $this->cur_ping->clean(); + + $this->cur_ping->post_id = (integer) $ping->post_id; + $this->cur_ping->ping_url = (string) $ping->ping_url; + $this->cur_ping->ping_dt = (string) $ping->ping_dt; + + $this->cur_ping->insert(); + } + + private function insertComment($comment) + { + $this->cur_comment->clean(); + + $this->cur_comment->comment_id = (integer) $comment->comment_id; + $this->cur_comment->post_id = (integer) $comment->post_id; + $this->cur_comment->comment_dt = (string) $comment->comment_dt; + $this->cur_comment->comment_upddt = (string) $comment->comment_upddt; + $this->cur_comment->comment_author = (string) $comment->comment_author; + $this->cur_comment->comment_email = (string) $comment->comment_email; + $this->cur_comment->comment_site = (string) $comment->comment_site; + $this->cur_comment->comment_content = (string) $comment->comment_content; + $this->cur_comment->comment_words = (string) $comment->comment_words; + $this->cur_comment->comment_ip = (string) $comment->comment_ip; + $this->cur_comment->comment_status = (integer) $comment->comment_status; + $this->cur_comment->comment_spam_status = (integer) $comment->comment_spam_status; + $this->cur_comment->comment_trackback = (integer) $comment->comment_trackback; + + $this->cur_comment->comment_tz = $comment->exists('comment_tz') ? (string) $comment->comment_tz : 'UTC'; + $this->cur_comment->comment_spam_filter = $comment->exists('comment_spam_filter') ? (integer) $comment->comment_spam_filter : null; + + $this->cur_comment->insert(); + } + + private function insertSpamRule($spamrule) + { + $this->cur_spamrule->clean(); + + $this->cur_spamrule->rule_id = (integer) $spamrule->rule_id; + $this->cur_spamrule->blog_id = !$spamrule->blog_id ? null : (string) $spamrule->blog_id; + $this->cur_spamrule->rule_type = (string) $spamrule->rule_type; + $this->cur_spamrule->rule_content = (string) $spamrule->rule_content; + + $this->cur_spamrule->insert(); + } + + private function insertCategorySingle($category) + { + $this->cur_category->clean(); + + $m = $this->searchCategory($this->stack['categories'],$category->cat_url); + + $old_id = $category->cat_id; + if ($m !== false) + { + $cat_id = $m; + } + else + { + $cat_id = $this->stack['cat_id']; + $category->cat_id = $cat_id; + $category->blog_id = $this->blog_id; + + $this->insertCategory($category); + $this->stack['cat_id']++; + } + + $this->old_ids['category'][(integer) $old_id] = $cat_id; + } + + private function insertLinkSingle($link) + { + $link->blog_id = $this->blog_id; + $link->link_id = $this->stack['link_id']; + + $this->insertLink($link); + $this->stack['link_id']++; + } + + private function insertPostSingle($post) + { + if (!$post->cat_id || isset($this->old_ids['category'][(integer) $post->cat_id])) { + $post_id = $this->stack['post_id']; + $this->old_ids['post'][(integer) $post->post_id] = $post_id; + + $cat_id = $post->cat_id ? $this->old_ids['category'][(integer) $post->cat_id] : null; + + $post->post_id = $post_id; + $post->cat_id = $cat_id; + $post->blog_id = $this->blog_id; + + $post->post_url = $this->core->blog->getPostURL( + $post->post_url,$post->post_dt,$post->post_title,$post->post_id + ); + + $this->insertPost($post); + $this->stack['post_id']++; + } else { + self::throwIdError($post->__name,$post->__line,'category'); + } + } + + private function insertMetaSingle($meta) + { + if (isset($this->old_ids['post'][(integer) $meta->post_id])) { + $meta->post_id = $this->old_ids['post'][(integer) $meta->post_id]; + $this->insertMeta($meta); + } else { + self::throwIdError($meta->__name,$meta->__line,'post'); + } + } + + private function insertMediaSingle($media) + { + $media_id = $this->stack['media_id']; + $old_id = $media->media_id; + + $media->media_id = $media_id; + $media->media_path = $this->core->blog->settings->system->public_path; + $media->user_id = $this->getUserId($media->user_id); + + $this->insertMedia($media); + $this->stack['media_id']++; + $this->old_ids['media'][(integer) $old_id] = $media_id; + } + + private function insertPostMediaSingle($post_media) + { + if (isset($this->old_ids['media'][(integer) $post_media->media_id]) && + isset($this->old_ids['post'][(integer) $post_media->post_id])) { + $post_media->media_id = $this->old_ids['media'][(integer) $post_media->media_id]; + $post_media->post_id = $this->old_ids['post'][(integer) $post_media->post_id]; + + $this->insertPostMedia($post_media); + } elseif (!isset($this->old_ids['media'][(integer) $post_media->media_id])) { + self::throwIdError($post_media->__name,$post_media->__line,'media'); + }else { + self::throwIdError($post_media->__name,$post_media->__line,'post'); + } + } + + private function insertPingSingle($ping) + { + if (isset($this->old_ids['post'][(integer) $ping->post_id])) { + $ping->post_id = $this->old_ids['post'][(integer) $ping->post_id]; + + $this->insertPing($ping); + } else { + self::throwIdError($ping->__name,$ping->__line,'post'); + } + } + + private function insertCommentSingle($comment) + { + if (isset($this->old_ids['post'][(integer) $comment->post_id])) { + $comment_id = $this->stack['comment_id']; + + $comment->comment_id = $comment_id; + $comment->post_id = $this->old_ids['post'][(integer) $comment->post_id]; + + $this->insertComment($comment); + $this->stack['comment_id']++; + } else { + self::throwIdError($comment->__name,$comment->__line,'post'); + } + } + + private static function throwIdError($name,$line,$related) + { + throw new Exception(sprintf( + __('ID of "%3$s" does not match on record "%1$s" at line %2$s of backup file.'), + html::escapeHTML($name), + html::escapeHTML($line), + html::escapeHTML($related) + )); + } + + public function searchCategory($rs,$url) + { + while ($rs->fetch()) + { + if ($rs->cat_url == $url) { + return $rs->cat_id; + } + } + + return false; + } + + public function getUserId($user_id) + { + if (!$this->userExists($user_id)) + { + if ($this->core->auth->isSuperAdmin()) + { + # Sanitizes user_id and create a lambda user + $user_id = preg_replace('/[^A-Za-z0-9]$/','',$user_id); + $user_id .= strlen($user_id) < 2 ? '-a' : ''; + + # We change user_id, we need to check again + if (!$this->userExists($user_id)) + { + $this->cur_user->clean(); + $this->cur_user->user_id = (string) $user_id; + $this->cur_user->user_pwd = md5(uniqid()); + + $this->core->addUser($this->cur_user); + + $this->stack['users'][$user_id] = true; + } + } + else + { + # Returns current user id + $user_id = $this->core->auth->userID(); + } + } + + return $user_id; + } + + private function userExists($user_id) + { + if (isset($this->stack['users'][$user_id])) { + return $this->stack['users'][$user_id]; + } + + $strReq = 'SELECT user_id '. + 'FROM '.$this->prefix.'user '. + "WHERE user_id = '".$this->con->escape($user_id)."' "; + + $rs = $this->con->select($strReq); + + $this->stack['users'][$user_id] = !$rs->isEmpty(); + return $this->stack['users'][$user_id]; + } + + private function prefExists($pref_ws,$pref_id,$user_id) + { + $strReq = 'SELECT pref_id,pref_ws,user_id '. + 'FROM '.$this->prefix.'pref '. + "WHERE pref_id = '".$this->con->escape($pref_id)."' ". + "AND pref_ws = '".$this->con->escape($pref_ws)."' "; + if (!$user_id) { + $strReq .= "AND user_id IS NULL "; + } else { + $strReq .= "AND user_id = '".$this->con->escape($user_id)."' "; + } + + $rs = $this->con->select($strReq); + + return !$rs->isEmpty(); + } + + private function mediaExists() + { + $strReq = 'SELECT media_id '. + 'FROM '.$this->prefix.'media '. + "WHERE media_path = '".$this->cur_media->media_path."' ". + "AND media_file = '".$this->cur_media->media_file."' "; + + $rs = $this->con->select($strReq); + + return !$rs->isEmpty(); + } + + private function prepareDC12line(&$line) + { + $settings = array('dc_theme','dc_nb_post_per_page','dc_allow_comments', + 'dc_allow_trackbacks','dc_comment_pub','dc_comments_ttl', + 'dc_wiki_comments','dc_use_smilies','dc_date_format','dc_time_format', + 'dc_url_scan'); + + switch ($line->__name) + { + case 'categorie': + $line->substitute('cat_libelle','cat_title'); + $line->substitute('cat_libelle_url','cat_url'); + $line->__name = 'category'; + $line->blog_id = 'default'; + break; + case 'link': + $line->substitute('href','link_href'); + $line->substitute('label','link_title'); + $line->substitute('title','link_desc'); + $line->substitute('lang','link_lang'); + $line->substitute('rel','link_xfn'); + $line->substitute('position','link_position'); + $line->blog_id = 'default'; + break; + case 'post': + $line->substitute('post_titre','post_title'); + $line->post_title = html::decodeEntities($line->post_title); + $line->post_url = date('Y/m/d/',strtotime($line->post_dt)).$line->post_id.'-'.$line->post_titre_url; + $line->post_url = substr($line->post_url,0,255); + $line->post_format = $line->post_content_wiki == '' ? 'xhtml' : 'wiki'; + $line->post_content_xhtml = $line->post_content; + $line->post_excerpt_xhtml = $line->post_chapo; + + if ($line->post_format == 'wiki') { + $line->post_content = $line->post_content_wiki; + $line->post_excerpt = $line->post_chapo_wiki; + } else { + $line->post_content = $line->post_content; + $line->post_excerpt = $line->post_chapo; + } + + $line->post_status = (integer) $line->post_pub; + $line->post_type = 'post'; + $line->blog_id = 'default'; + + $line->drop('post_titre_url','post_content_wiki','post_chapo','post_chapo_wiki','post_pub'); + + break; + case 'post_meta': + $line->drop('meta_id'); + $line->substitute('meta_key','meta_type'); + $line->substitute('meta_value','meta_id'); + $line->__name = 'meta'; + $line->blog_id = 'default'; + break; + case 'comment': + $line->substitute('comment_auteur','comment_author'); + if ($line->comment_site != '' && !preg_match('!^http://.*$!', $line->comment_site,$m)) { + $line->comment_site = 'http://'.$line->comment_site; + } + $line->comment_status = (integer) $line->comment_pub; + $line->drop('comment_pub'); + break; + } + + # --BEHAVIOR-- importPrepareDC12 + $this->core->callBehavior('importPrepareDC12',$line,$this,$this->core); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/inc/img/progress.png b/v2/dotclear/plugins/importExport/inc/img/progress.png new file mode 100644 index 0000000..267f541 Binary files /dev/null and b/v2/dotclear/plugins/importExport/inc/img/progress.png differ diff --git a/v2/dotclear/plugins/importExport/index.php b/v2/dotclear/plugins/importExport/index.php new file mode 100644 index 0000000..6e58452 --- /dev/null +++ b/v2/dotclear/plugins/importExport/index.php @@ -0,0 +1,95 @@ +'.html::escapeHTML($o->name).''. + '
        '.html::escapeHTML($o->description).'
        '; + + unset($o); + } + return '
        '.$res.'
        '; +} + +$modules = new ArrayObject(array('import' => array(),'export' => array())); + +# --BEHAVIOR-- importExportModules +$core->callBehavior('importExportModules',$modules); + +$type = null; +if (!empty($_REQUEST['type']) && in_array($_REQUEST['type'],array('export','import'))) { + $type = $_REQUEST['type']; +} + +$module = null; +if ($type && !empty($_REQUEST['module'])) { + + if (isset($modules[$type]) && in_array($_REQUEST['module'],$modules[$type])) { + + $module = new $_REQUEST['module']($core); + $module->init(); + } +} + +if ($type && $module !== null && !empty($_REQUEST['do'])) +{ + try { + $module->process($_REQUEST['do']); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +$title = __('Import/Export'); + +echo ' + + + '.$title.' + + '.dcPage::jsLoad('index.php?pf=importExport/js/script.js').' + + +'; + +if ($type && $module !== null) { + echo + '

        '.$title.''. + ' › '.html::escapeHTML($module->name).'

        '. + '
        '; + + $module->gui(); + + echo '
        '; +} +else { + echo + '

        '.$title.'

        '. + '

        '.__('Import').'

        '.listImportExportModules($core,$modules['import']). + '

        '.__('Export').'

        '.listImportExportModules($core,$modules['export']); +} + +echo ' + +'; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/js/script.js b/v2/dotclear/plugins/importExport/js/script.js new file mode 100644 index 0000000..82892fd --- /dev/null +++ b/v2/dotclear/plugins/importExport/js/script.js @@ -0,0 +1,3 @@ + +$(function(){if($('*.error').length>0){return;} +$('#ie-gui form[method=post]:has(input[type=hidden][name=autosubmit])').each(function(){$('input[type=submit]',this).remove();$(this).after('

        '+dotclear.msg.please_wait+'

        ');$(this).submit();});}); \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/locales/fr/main.lang.php b/v2/dotclear/plugins/importExport/locales/fr/main.lang.php new file mode 100644 index 0000000..7d8c291 --- /dev/null +++ b/v2/dotclear/plugins/importExport/locales/fr/main.lang.php @@ -0,0 +1,92 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/importExport/locales/fr/main.po b/v2/dotclear/plugins/importExport/locales/fr/main.po new file mode 100644 index 0000000..d0049f1 --- /dev/null +++ b/v2/dotclear/plugins/importExport/locales/fr/main.po @@ -0,0 +1,333 @@ +# Language: Français +# Module: importExport - 3.1 +# Date: 2012-07-09 14:37:32 +# Translated with translater 1.5 + +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: importExport 3.1\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2012-07-09T14:37:32+00:00\n" +"Last-Translator: Jean-Christian Denis\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" + +#: _admin.php:15 +#: _admin.php:28 +#: index.php:59 +msgid "Import/Export" +msgstr "Import/Export" + +#: inc/class.dc.export.flat.php:19 +msgid "Flat file export" +msgstr "Exporter un fichier texte" + +#: inc/class.dc.export.flat.php:20 +msgid "Exports a blog or a full Dotclear installation to flat file." +msgstr "Exporte un blog ou toutes les données de Dotclear dans un fichier texte." + +#: inc/class.dc.export.flat.php:140 +msgid "Export file not found." +msgstr "Fichier d'export non trouvé." + +#: inc/class.dc.export.flat.php:184 +msgid "Failed to compress export file." +msgstr "Impossible de compresser le fichier d'export." + +#: inc/class.dc.export.flat.php:194 +#: inc/class.dc.import.feed.php:84 +#: inc/class.dc.import.flat.php:183 +msgid "Single blog" +msgstr "Un seul blog" + +#: inc/class.dc.export.flat.php:195 +msgid "This will create an export of your current blog: %s" +msgstr "Ceci va exporter le contenu du blog en cours : %s." + +#: inc/class.dc.export.flat.php:197 +#: inc/class.dc.export.flat.php:223 +msgid "File name:" +msgstr "Nom du fichier :" + +#: inc/class.dc.export.flat.php:203 +#: inc/class.dc.export.flat.php:229 +msgid "Compress file" +msgstr "Compresser le fichier" + +#: inc/class.dc.export.flat.php:207 +msgid "You may also want to download your media directory as a zip file" +msgstr "Vous pouvez également télécharger votre répertoire de médias au format zip." + +#: inc/class.dc.export.flat.php:209 +#: inc/class.dc.export.flat.php:232 +#: index.php:89 +msgid "Export" +msgstr "Exporter" + +#: inc/class.dc.export.flat.php:220 +#: inc/class.dc.import.flat.php:211 +msgid "Multiple blogs" +msgstr "Tous les blogs" + +#: inc/class.dc.export.flat.php:221 +msgid "This will create an export of all the content of your database." +msgstr "Ceci va exporter le contenu complet de votre base de données." + +#: inc/class.dc.ieModule.php:74 +msgid "Congratulation!" +msgstr "Félicitations !" + +#: inc/class.dc.ieModule.php:75 +msgid "Your blog has been successfully imported. Welcome on Dotclear 2!" +msgstr "Votre blog a été importé avec succès. Bienvenue sur Dotclear 2 !" + +#: inc/class.dc.ieModule.php:76 +msgid "Why don't you blog this now?" +msgstr "Pourquoi ne pas le bloguer maintenant ?" + +#: inc/class.dc.ieModule.php:77 +msgid "or" +msgstr "ou" + +#: inc/class.dc.ieModule.php:77 +msgid "visit your dashboard" +msgstr "vous rendre sur votre tableau de bord" + +#: inc/class.dc.import.dc1.php:43 +msgid "Dotclear 1.2 import" +msgstr "Importer depuis Dotclear 1.2" + +#: inc/class.dc.import.dc1.php:44 +msgid "Import a Dotclear 1.2 installation into your current blog." +msgstr "Importe un blog Dotclear 1.2 dans votre blog en cours." + +#: inc/class.dc.import.dc1.php:145 +msgid "This will import your Dotclear 1.2 content as new content in the current blog: %s." +msgstr "Ceci va importer votre blog Dotclear 1.2 comme un nouveau contenu dans le blog en cours : %s." + +#: inc/class.dc.import.dc1.php:149 +#: inc/class.dc.import.wp.php:180 +msgid "Depending on the size of your blog, it could take a few minutes." +msgstr "Selon la taille de votre blog, ceci peut prendre quelques minutes." + +#: inc/class.dc.import.dc1.php:151 +#: inc/class.dc.import.wp.php:182 +msgid "General information" +msgstr "Informations générales" + +#: inc/class.dc.import.dc1.php:151 +#: inc/class.dc.import.wp.php:182 +msgid "Import my blog now" +msgstr "Importer mon blog" + +#: inc/class.dc.import.dc1.php:152 +msgid "We first need some information about your old Dotclear 1.2 installation." +msgstr "Nous avons d'abord besoin de renseignements à propos de votre ancienne installation Dotclear 1.2." + +#: inc/class.dc.import.dc1.php:163 +#: inc/class.dc.import.wp.php:194 +msgid "Entries import options" +msgstr "Options d'importation des billets" + +#: inc/class.dc.import.dc1.php:164 +#: inc/class.dc.import.wp.php:212 +msgid "Number of entries to import at once:" +msgstr "Nombre de billets à importer à chaque étape :" + +#: inc/class.dc.import.dc1.php:169 +#: inc/class.dc.import.wp.php:222 +msgid "Importing users" +msgstr "Importe les utilisateurs" + +#: inc/class.dc.import.dc1.php:174 +#: inc/class.dc.import.wp.php:227 +msgid "Importing categories" +msgstr "Importe les catégories" + +#: inc/class.dc.import.dc1.php:179 +#: inc/class.dc.import.wp.php:232 +msgid "Importing blogroll" +msgstr "Importe les liens" + +#: inc/class.dc.import.dc1.php:184 +#: inc/class.dc.import.wp.php:237 +msgid "Importing entries from %d to %d / %d" +msgstr "Importation des billets %d à %d" + +#: inc/class.dc.import.dc1.php:193 +msgid "Please read carefully" +msgstr "Merci de lire attentivement" + +#: inc/class.dc.import.dc1.php:214 +#: inc/class.dc.import.wp.php:258 +msgid "next step" +msgstr "étape suivante" + +#: inc/class.dc.import.dc1.php:242 +msgid "Dotclear tables not found" +msgstr "Tables Dotclear non trouvées" + +#: inc/class.dc.import.feed.php:22 +msgid "Feed import" +msgstr "Importer depuis un flux" + +#: inc/class.dc.import.feed.php:23 +msgid "Imports a feed as new entries." +msgstr "Importe un flux comme nouveaux billets." + +#: inc/class.dc.import.feed.php:41 +msgid "Cannot retrieve feed URL." +msgstr "Impossible d'atteindre l'URL du fil." + +#: inc/class.dc.import.feed.php:44 +msgid "No items in feed." +msgstr "Aucun élément dans le fil." + +#: inc/class.dc.import.feed.php:79 +#: inc/class.dc.import.flat.php:149 +msgid "Content successfully imported." +msgstr "Contenu importé avec succès." + +#: inc/class.dc.import.feed.php:85 +msgid "This will import a feed (RSS or Atom) a as new content in the current blog: %s." +msgstr "Ceci va importer un fil (RSS ou Atom) comme un nouveau contenu dans le blog en cours : %s." + +#: inc/class.dc.import.feed.php:87 +msgid "Feed URL:" +msgstr "URL du fil :" + +#: inc/class.dc.import.feed.php:93 +#: inc/class.dc.import.flat.php:202 +#: inc/class.dc.import.flat.php:233 +#: index.php:88 +msgid "Import" +msgstr "Importer" + +#: inc/class.dc.import.flat.php:21 +msgid "Flat file import" +msgstr "Importer depuis un fichier texte" + +#: inc/class.dc.import.flat.php:22 +msgid "Imports a blog or a full Dotclear installation from flat file." +msgstr "Importe un blog ou toutes les données depuis un fichier texte." + +#: inc/class.dc.import.flat.php:144 +msgid "Single blog successfully imported." +msgstr "Blog importé avec succès." + +#: inc/class.dc.import.flat.php:160 +msgid "Are you sure you want to import a full backup file?" +msgstr "Êtes-vous certain de vouloir charger un fichier de sauvegarde complet ?" + +#: inc/class.dc.import.flat.php:184 +msgid "This will import a single blog backup as new content in the current blog: %s." +msgstr "Ceci va charger une sauvegarde de blog comme un nouveau contenu dans le blog en cours : %s." + +#: inc/class.dc.import.flat.php:186 +#: inc/class.dc.import.flat.php:214 +msgid "Upload a backup file" +msgstr "Charger un fichier de sauvegarde" + +#: inc/class.dc.import.flat.php:192 +#: inc/class.dc.import.flat.php:220 +msgid "or pick up a local file in your public directory" +msgstr "ou choisissez un fichier local dans votre répertoire public" + +#: inc/class.dc.import.flat.php:212 +msgid "This will reset all the content of your database, except users." +msgstr "Ceci va remettre à zéro tout le contenu, sauf les utilisateurs." + +#: inc/class.dc.import.flat.php:281 +msgid "File is empty or not a compressed file." +msgstr "Le Fichier est vide ou n'est pas un fichier compressé." + +#: inc/class.dc.import.flat.php:304 +msgid "Another file with same name exists." +msgstr "Un autre fichier du même nom existe déjà." + +#: inc/class.dc.import.flat.php:311 +msgid "Failed to extract backup file." +msgstr "Impossible d'extraire le fichier de sauvegarde." + +#: inc/class.dc.import.flat.php:322 +msgid "No backup in compressed file." +msgstr "Pas de sauvegarde dans le fichier compressé." + +#: inc/class.dc.import.wp.php:64 +msgid "WordPress import" +msgstr "Importer depuis WordPress" + +#: inc/class.dc.import.wp.php:65 +msgid "Import a WordPress installation into your current blog." +msgstr "Importe un blog WordPress dans votre blog en cours." + +#: inc/class.dc.import.wp.php:176 +msgid "This will import your WordPress content as new content in the current blog: %s." +msgstr "Ceci va importer votre blog WordPress comme un nouveau contenu dans le blog en cours : %s." + +#: inc/class.dc.import.wp.php:183 +msgid "We first need some information about your old WordPress installation." +msgstr "Nous avons d'abord besoin de renseignements à propos de votre ancienne installation WordPress." + +#: inc/class.dc.import.wp.php:205 +msgid "Ignore the first category:" +msgstr "Ignorer la première catégorie :" + +#: inc/class.dc.import.wp.php:207 +msgid "Import lowest numbered category on posts:" +msgstr "Importer la catégorie la plus ancienne sur les articles :" + +#: inc/class.dc.import.wp.php:209 +msgid "Import all categories as tags:" +msgstr "Importer les catégories comme des mots-clés :" + +#: inc/class.dc.import.wp.php:210 +msgid "Prefix such tags with:" +msgstr "Préfixer ces mots-clés avec :" + +#: inc/class.dc.import.wp.php:215 +msgid "Content filters" +msgstr "Filtres de contenu" + +#: inc/class.dc.import.wp.php:216 +msgid "You may want to process your post and/or comment content with the following filters." +msgstr "Vous pouvez utiliser les filtres suivants sur vos articles et/ou commentaires." + +#: inc/class.dc.import.wp.php:217 +msgid "Post content formatter:" +msgstr "Formatage des articles :" + +#: inc/class.dc.import.wp.php:218 +msgid "Comment content formatter:" +msgstr "Formatage des commentaires :" + +#: inc/class.dc.import.wp.php:286 +msgid "WordPress tables not found" +msgstr "Tables WordPress non trouvées" + +#: inc/flat/class.flat.backup.php:34 +msgid "No file to read." +msgstr "Aucun fichier lisible." + +#: inc/flat/class.flat.import.php:54 +msgid "File is not a DotClear backup." +msgstr "Le fichier n'est pas une sauvegarde DotClear." + +#: inc/flat/class.flat.import.php:105 +msgid "File is not a single blog export." +msgstr "Le fichier n'est pas un fichier d'export simple." + +#: inc/flat/class.flat.import.php:222 +msgid "File is not a full export." +msgstr "Le fichier n'est pas un fichier d'export complet." + +#: inc/flat/class.flat.import.php:704 +msgid "ID of \"%3$s\" does not match on record \"%1$s\" at line %2$s of backup file." +msgstr "L'ID de \"%3$s\" ne correspond pas dans l'enregistrement de \"%1$s\" à la ligne %2$s du fichier de sauvegarde." + +#: index.php:69 +msgid "Please wait..." +msgstr "Veuillez patienter..." + diff --git a/v2/dotclear/plugins/importExport/style.css b/v2/dotclear/plugins/importExport/style.css new file mode 100644 index 0000000..7aeb3c4 --- /dev/null +++ b/v2/dotclear/plugins/importExport/style.css @@ -0,0 +1,25 @@ +dl.modules dt { + font-weight: bold; + font-size: 1.1em; + margin: 1em 0 0 0; +} +dl.modules dd { + margin: 0 0 1.5em 0; +} +div.ie-progress { + background: #eee; + margin: 1em 0; +} +div.ie-progress div { + height: 10px; + width: 0; + font-size: 0.8em; + line-height: 1em; + height: 1em; + padding: 2px 0; + text-align: right; + background: green url(index.php?pf=importExport/progress.png) repeat-x top left; + color: white; + font-weight: bold; + -moz-border-radius: 2px; +} \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/_admin.php b/v2/dotclear/plugins/maintenance/_admin.php new file mode 100644 index 0000000..c74b5c5 --- /dev/null +++ b/v2/dotclear/plugins/maintenance/_admin.php @@ -0,0 +1,26 @@ +addItem(__('Maintenance'),'plugin.php?p=maintenance','index.php?pf=maintenance/icon.png', + preg_match('/plugin.php\?p=maintenance(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + +$core->addBehavior('adminDashboardFavs','maintenanceDashboardFavs'); + +function maintenanceDashboardFavs($core,$favs) +{ + $favs['maintenance'] = new ArrayObject(array('maintenance','Maintenance','plugin.php?p=maintenance', + 'index.php?pf=maintenance/icon.png','index.php?pf=maintenance/icon-big.png', + null,null,null)); +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/_define.php b/v2/dotclear/plugins/maintenance/_define.php new file mode 100644 index 0000000..3dab8db --- /dev/null +++ b/v2/dotclear/plugins/maintenance/_define.php @@ -0,0 +1,20 @@ +registerModule( + /* Name */ "Maintenance", + /* Description*/ "Maintain your database", + /* Author */ "Olivier Meunier", + /* Version */ '1.1' +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/icon-big.png b/v2/dotclear/plugins/maintenance/icon-big.png new file mode 100644 index 0000000..eeb8306 Binary files /dev/null and b/v2/dotclear/plugins/maintenance/icon-big.png differ diff --git a/v2/dotclear/plugins/maintenance/icon.png b/v2/dotclear/plugins/maintenance/icon.png new file mode 100644 index 0000000..0ade7d9 Binary files /dev/null and b/v2/dotclear/plugins/maintenance/icon.png differ diff --git a/v2/dotclear/plugins/maintenance/index.php b/v2/dotclear/plugins/maintenance/index.php new file mode 100644 index 0000000..edc511f --- /dev/null +++ b/v2/dotclear/plugins/maintenance/index.php @@ -0,0 +1,186 @@ +con); + $db_tables = $schema->getTables(); + + foreach ($db_tables as $t) { + if (strpos($t,$core->prefix) === 0) { + $core->con->vacuum($t); + } + } + http::redirect($p_url.'&vacuum=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } +} +elseif ($action == 'commentscount') +{ + try { + $core->countAllComments(); + http::redirect($p_url.'&commentscount=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} +elseif ($action == 'empty_cache') +{ + try { + $core->emptyTemplatesCache(); + http::redirect($p_url.'&empty_cache=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} +elseif ($action == 'log') +{ + try { + $core->log->delLogs(null,true); + http::redirect($p_url.'&delete_logs=1'); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +?> + + + <?php echo __('Maintenance'); ?> + + + +

        + +'.__('Optimization successful.').'

        '; +} +if (!empty($_GET['commentscount'])) { + echo '

        '.__('Comments and trackback counted.').'

        '; +} +if (!empty($_GET['empty_cache'])) { + echo '

        '.__('Templates cache directory emptied.').'

        '; +} +if (!empty($_GET['delete_logs'])) { + echo '

        '.__('Logs deleted.').'

        '; +} + +if ($action == 'index' && !empty($_GET['indexposts'])) +{ + $limit = 1000; + echo '

        '.sprintf(__('Indexing entry %d to %d.'),$start,$start+$limit).'

        '; + + $new_start = $core->indexAllPosts($start,$limit); + + if ($new_start) + { + $new_url = $p_url.'&action=index&indexposts=1&start='.$new_start; + echo + ''. + ''; + } + else + { + echo '

        '.__('Entries index done.').'

        '; + echo '

        '.__('Back').'

        '; + } +} +elseif ($action == 'index' && !empty($_GET['indexcomments'])) +{ + $limit = 1000; + echo '

        '.sprintf(__('Indexing comment %d to %d.'),$start,$start+$limit).'

        '; + + $new_start = $core->indexAllComments($start,$limit); + + if ($new_start) + { + $new_url = $p_url.'&action=index&indexcomments=1&start='.$new_start; + echo + ''. + ''; + } + else + { + echo '

        '.__('Comments index done.').'

        '; + echo '

        '.__('Back').'

        '; + } +} +else +{ + echo + '

        '.__('Optimize database room').'

        '. + '
        '. + '

        '. + $core->formNonce(). + form::hidden(array('action'),'vacuum'). + form::hidden(array('p'),'maintenance').'

        '. + '
        '; + + echo + '

        '.__('Counters').'

        '. + '
        '. + '

        '. + $core->formNonce(). + form::hidden(array('action'),'commentscount'). + form::hidden(array('p'),'maintenance').'

        '. + '
        '; + + echo + '

        '.__('Search engine index').' ('.__('This may take a very long time').')

        '. + '
        '. + '

        '. + ' '. + form::hidden(array('action'),'index'). + form::hidden(array('p'),'maintenance').'

        '. + '
        '; + + echo + '

        '.__('Vacuum logs').'

        '. + '
        '. + '

        '. + $core->formNonce(). + form::hidden(array('action'),'log'). + form::hidden(array('p'),'maintenance').'

        '. + '
        '; + + echo + '

        '.__('Empty templates cache directory').'

        '. + '
        '. + '

        '. + $core->formNonce(). + form::hidden(array('action'),'empty_cache'). + form::hidden(array('p'),'maintenance').'

        '. + '
        '; +} +dcPage::helpBlock('maintenance'); +?> + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/locales/en/help/maintenance.html b/v2/dotclear/plugins/maintenance/locales/en/help/maintenance.html new file mode 100644 index 0000000..98e69c6 --- /dev/null +++ b/v2/dotclear/plugins/maintenance/locales/en/help/maintenance.html @@ -0,0 +1,41 @@ + + + Maintenance + + + + +

        Maintenance

        + +
        +
        Optimize database room
        +
        After numerous delete or update operations on Dotclear's database, it gets fragmented. + Optimizing will allow to defragment it.
        + It has no incidence on your data's integrity.
        + It is recommended to optimize before any blog export.
        + +
        Counters
        +
        Initializing comments and trackbacks counters allows to check their exact numbers. + This operation can be useful when importing from another blog platform + (or when migrating from dotclear 1 to dotclear 2).
        + +
        Search engine index
        +
        These operations are necessary, after importing content in your blog, to use internal search engine, on public and private pages. +
          +
        • Index all posts: allows to index all posts
        • +
        • Index all comments: allows to index all comments and trackbacks
        • +
        +
        + +
        Vacuum logs
        +
        Logs record all activity and connection to your blog history. Unless you need to keep this history, + consider deleting these logs from time to time.
        + +
        Empty templates cache directory
        +
        It may be useful to empty this cache when modifying a theme's .html or .css files (or when updating + a theme or plugin).
        + Notice : with some hosters, the templates cache cannot be emptied with this extension. + You may then have to delete the directory /cbtpl/ directly on the server with your FTP software.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/locales/en/resources.php b/v2/dotclear/plugins/maintenance/locales/en/resources.php new file mode 100644 index 0000000..4c0cad4 --- /dev/null +++ b/v2/dotclear/plugins/maintenance/locales/en/resources.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/locales/fr/help/maintenance.html b/v2/dotclear/plugins/maintenance/locales/fr/help/maintenance.html new file mode 100644 index 0000000..5fa0864 --- /dev/null +++ b/v2/dotclear/plugins/maintenance/locales/fr/help/maintenance.html @@ -0,0 +1,39 @@ + + + Maintenance + + + + +

        Maintenance

        + +
        +
        Optimiser l'espace de la base de données
        +
        Au fur at à mesure des suppressions ou modifications dans vos billets, les tables de la + base de données se fragmentent. L'opération d'optimisation permet de compacter celles-ci.
        + Cette opération n'a aucun impact sur l'intégrité de vos données.
        + Il est fortement conseillé d'optimiser la base de données avant tout export de blog
        + +
        Compteurs
        +
        Réinitialiser les compteurs des commentaires et des rétroliens a pour effet + de donner le nombre exact de ceux-ci pour chaque billet. Cette opération peut se + révéler utile lors de l'import d'un blog d'une autre plateforme (ou d'une migration + de dotclear 1 vers dotclear 2).
        + +
        Index du moteur de recherche
        +
        Ces opérations s'effectuent généralement suite à un import afin d'obtenir des résultats lors des recherches, aussi bien depuis l'administration du blog qu'en partie publique. +
          +
        • Indexer tous les billets : permet d'indexer les billets
        • +
        • Indexer tous les commentaires : permet d'indexer les commentaires
        • +
        +
        + +
        Vider les journaux
        +
        Sauf si vous avez un besoin précis de conservation des activités de connexion sur votre blog, pensez à vider régulièrement la table des journaux : elle contient l'enregistrement de chaque connexion à l'administration et des opérations qui s'y sont déroulées.
        + +
        Vider le répertoire du cache des templates
        +
        La suppression du répertoire du cache des templates peut se révéler nécessaire lors de modifications dans les fichiers .html ou .css d'un thème (voire lors d'un changement de thème ou de mise à jour de plugin).
        + Attention : chez certains hébergeurs (Free par exemple), le cache du répertoire des templates ne peut pas être vidé grâce à l'extension Maintenance, il faut supprimer le répertoire cbtpl situé dans le répertoire cache (dotclear/cache par défaut) avec un logiciel FTP.
        +
        + + \ No newline at end of file diff --git a/v2/dotclear/plugins/maintenance/locales/fr/resources.php b/v2/dotclear/plugins/maintenance/locales/fr/resources.php new file mode 100644 index 0000000..4c0cad4 --- /dev/null +++ b/v2/dotclear/plugins/maintenance/locales/fr/resources.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/_admin.php b/v2/dotclear/plugins/pages/_admin.php new file mode 100644 index 0000000..0183cfc --- /dev/null +++ b/v2/dotclear/plugins/pages/_admin.php @@ -0,0 +1,51 @@ +addBehavior('adminDashboardIcons','pages_dashboard'); +$core->addBehavior('adminDashboardFavs','pages_dashboard_favs'); +$core->addBehavior('adminDashboardFavsIcon','pages_dashboard_favs_icon'); +function pages_dashboard($core,$icons) +{ + $icons['pages'] = new ArrayObject(array(__('Pages'),'plugin.php?p=pages','index.php?pf=pages/icon-big.png')); +} +function pages_dashboard_favs($core,$favs) +{ + $favs['pages'] = new ArrayObject(array('pages','Pages','plugin.php?p=pages', + 'index.php?pf=pages/icon.png','index.php?pf=pages/icon-big.png', + 'contentadmin,pages',null,null)); + $favs['newpage'] = new ArrayObject(array('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)); +} +function pages_dashboard_favs_icon($core,$name,$icon) +{ + // Check if it is one of my own favs + if ($name == 'pages') { + $params = new ArrayObject(); + $params['post_type'] = 'page'; + $page_count = $core->blog->getPosts($params,true)->f(0); + if ($page_count > 0) { + $str_pages = ($page_count > 1) ? __('%d pages') : __('%d page'); + $icon[0] = sprintf($str_pages,$page_count); + } + } +} + +$_menu['Blog']->addItem(__('Pages'),'plugin.php?p=pages','index.php?pf=pages/icon.png', + preg_match('/plugin.php\?p=pages(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('contentadmin,pages',$core->blog->id)); + +$core->auth->setPermissionType('pages',__('manage pages')); + +require dirname(__FILE__).'/_widgets.php'; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/_define.php b/v2/dotclear/plugins/pages/_define.php new file mode 100644 index 0000000..421fe2a --- /dev/null +++ b/v2/dotclear/plugins/pages/_define.php @@ -0,0 +1,24 @@ +registerModule( + /* Name */ "Pages", + /* Description*/ "Serve entries as simple web pages", + /* Author */ "Olivier Meunier", + /* Version */ '1.1.1', + array( + 'permissions' => 'contentadmin,pages', + 'priority' => 999 + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/_prepend.php b/v2/dotclear/plugins/pages/_prepend.php new file mode 100644 index 0000000..72bba20 --- /dev/null +++ b/v2/dotclear/plugins/pages/_prepend.php @@ -0,0 +1,21 @@ +url->register('pages','pages','^pages/(.+)$',array('urlPages','pages')); +$core->url->register('pagespreview','pagespreview','^pagespreview/(.+)$',array('urlPages','pagespreview')); + +$core->setPostType('page','plugin.php?p=pages&act=page&id=%d',$core->url->getURLFor('pages','%s')); + +# We should put this as settings later +$GLOBALS['page_url_format'] = '{t}'; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/_public.php b/v2/dotclear/plugins/pages/_public.php new file mode 100644 index 0000000..0d59325 --- /dev/null +++ b/v2/dotclear/plugins/pages/_public.php @@ -0,0 +1,267 @@ +blog->withoutPassword(false); + + $params = new ArrayObject(array( + 'post_type' => 'page', + 'post_url' => $args)); + + $core->callBehavior('publicPagesBeforeGetPosts',$params,$args); + + $_ctx->posts = $core->blog->getPosts($params); + + $_ctx->comment_preview = new ArrayObject(); + $_ctx->comment_preview['content'] = ''; + $_ctx->comment_preview['rawcontent'] = ''; + $_ctx->comment_preview['name'] = ''; + $_ctx->comment_preview['mail'] = ''; + $_ctx->comment_preview['site'] = ''; + $_ctx->comment_preview['preview'] = false; + $_ctx->comment_preview['remember'] = false; + + $core->blog->withoutPassword(true); + + + if ($_ctx->posts->isEmpty()) + { + # The specified page does not exist. + self::p404(); + } + else + { + $post_id = $_ctx->posts->post_id; + $post_password = $_ctx->posts->post_password; + + # Password protected entry + if ($post_password != '' && !$_ctx->preview) + { + # Get passwords cookie + if (isset($_COOKIE['dc_passwd'])) { + $pwd_cookie = unserialize($_COOKIE['dc_passwd']); + } else { + $pwd_cookie = array(); + } + + # Check for match + if ((!empty($_POST['password']) && $_POST['password'] == $post_password) + || (isset($pwd_cookie[$post_id]) && $pwd_cookie[$post_id] == $post_password)) + { + $pwd_cookie[$post_id] = $post_password; + setcookie('dc_passwd',serialize($pwd_cookie),0,'/'); + } + else + { + self::serveDocument('password-form.html','text/html',false); + return; + } + } + + $post_comment = + isset($_POST['c_name']) && isset($_POST['c_mail']) && + isset($_POST['c_site']) && isset($_POST['c_content']) && + $_ctx->posts->commentsActive(); + + # Posting a comment + if ($post_comment) + { + # Spam trap + if (!empty($_POST['f_mail'])) { + http::head(412,'Precondition Failed'); + header('Content-Type: text/plain'); + echo "So Long, and Thanks For All the Fish"; + # Exits immediately the application to preserve the server. + exit; + } + + $name = $_POST['c_name']; + $mail = $_POST['c_mail']; + $site = $_POST['c_site']; + $content = $_POST['c_content']; + $preview = !empty($_POST['preview']); + + if ($content != '') + { + if ($core->blog->settings->system->wiki_comments) { + $core->initWikiComment(); + } else { + $core->initWikiSimpleComment(); + } + $content = $core->wikiTransform($content); + $content = $core->HTMLfilter($content); + } + + $_ctx->comment_preview['content'] = $content; + $_ctx->comment_preview['rawcontent'] = $_POST['c_content']; + $_ctx->comment_preview['name'] = $name; + $_ctx->comment_preview['mail'] = $mail; + $_ctx->comment_preview['site'] = $site; + + if ($preview) + { + # --BEHAVIOR-- publicBeforeCommentPreview + $core->callBehavior('publicBeforeCommentPreview',$_ctx->comment_preview); + + $_ctx->comment_preview['preview'] = true; + } + else + { + # Post the comment + $cur = $core->con->openCursor($core->prefix.'comment'); + $cur->comment_author = $name; + $cur->comment_site = html::clean($site); + $cur->comment_email = html::clean($mail); + $cur->comment_content = $content; + $cur->post_id = $_ctx->posts->post_id; + $cur->comment_status = $core->blog->settings->system->comments_pub ? 1 : -1; + $cur->comment_ip = http::realIP(); + + $redir = $_ctx->posts->getURL(); + $redir .= $core->blog->settings->system->url_scan == 'query_string' ? '&' : '?'; + + try + { + if (!text::isEmail($cur->comment_email)) { + throw new Exception(__('You must provide a valid email address.')); + } + + # --BEHAVIOR-- publicBeforeCommentCreate + $core->callBehavior('publicBeforeCommentCreate',$cur); + if ($cur->post_id) { + $comment_id = $core->blog->addComment($cur); + + # --BEHAVIOR-- publicAfterCommentCreate + $core->callBehavior('publicAfterCommentCreate',$cur,$comment_id); + } + + if ($cur->comment_status == 1) { + $redir_arg = 'pub=1'; + } else { + $redir_arg = 'pub=0'; + } + + header('Location: '.$redir.$redir_arg); + } + catch (Exception $e) + { + $_ctx->form_error = $e->getMessage(); + $_ctx->form_error; + } + } + } + + # The entry + $core->tpl->setPath($core->tpl->getPath(), dirname(__FILE__).'/default-templates'); + self::serveDocument('page.html'); + } + } + } + + public static function pagespreview($args) + { + $core = $GLOBALS['core']; + $_ctx = $GLOBALS['_ctx']; + + if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#',$args,$m)) { + # The specified Preview URL is malformed. + self::p404(); + } + else + { + $user_id = $m[1]; + $user_key = $m[2]; + $post_url = $m[3]; + if (!$core->auth->checkUser($user_id,null,$user_key)) { + # The user has no access to the entry. + self::p404(); + } + else + { + $_ctx->preview = true; + self::pages($post_url); + } + } + } +} + +class tplPages +{ + # Widget function + public static function pagesWidget($w) + { + global $core, $_ctx; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $params['post_type'] = 'page'; + $params['limit'] = abs((integer) $w->limit); + $params['no_content'] = true; + + $sort = $w->sortby; + if (!in_array($sort,array('post_title','post_position','post_dt'))) { + $sort = 'post_title'; + } + + $order = $w->orderby; + if ($order != 'asc') { + $order = 'desc'; + } + $params['order'] = $sort.' '.$order; + + $rs = $core->blog->getPosts($params); + + if ($rs->isEmpty()) { + return; + } + + $res = + '
        '. + ($w->title ? '

        '.html::escapeHTML($w->title).'

        ' : ''). + '
          '; + + while ($rs->fetch()) { + $class = ''; + if (($core->url->type == 'pages' && $_ctx->posts instanceof record && $_ctx->posts->post_id == $rs->post_id)) { + $class = ' class="page-current"'; + } + $res .= ''. + html::escapeHTML($rs->post_title).''; + } + + $res .= '
        '; + + return $res; + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/_widgets.php b/v2/dotclear/plugins/pages/_widgets.php new file mode 100644 index 0000000..a6ba820 --- /dev/null +++ b/v2/dotclear/plugins/pages/_widgets.php @@ -0,0 +1,41 @@ +addBehavior('initWidgets',array('pagesWidgets','initWidgets')); +$core->addBehavior('initDefaultWidgets',array('pagesWidgets','initDefaultWidgets')); + +class pagesWidgets +{ + public static function initWidgets($w) + { + $w->create('pages',__('Pages'),array('tplPages','pagesWidget')); + $w->pages->setting('title',__('Title:'),__('Pages')); + $w->pages->setting('homeonly',__('Home page only'),1,'check'); + $w->pages->setting('sortby',__('Order by:'),'post_title','combo', + array( + __('Page title') => 'post_title', + __('Page position') => 'post_position', + __('Publication date') => 'post_dt' + ) + ); + $w->pages->setting('orderby',__('Sort:'),'asc','combo', + array(__('Ascending') => 'asc', __('Descending') => 'desc') + ); + } + + public static function initDefaultWidgets($w,$d) + { + $d['extra']->append($w->pages); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/default-templates/page.html b/v2/dotclear/plugins/pages/default-templates/page.html new file mode 100644 index 0000000..1a79823 --- /dev/null +++ b/v2/dotclear/plugins/pages/default-templates/page.html @@ -0,0 +1,247 @@ + + + + + + + {{tpl:EntryTitle encode_html="1"}} - {{tpl:BlogName encode_html="1"}} + + + + + + + + + + + + + + + + + + + + + + {{tpl:include src="_head.html"}} + + + + + + +
        +{{tpl:EntryPingData}} + +{{tpl:include src="_top.html"}} + +
        + +
        +
        + +
        +

        {{tpl:EntryTitle encode_html="1"}}

        + + + {{tpl:SysBehavior behavior="publicEntryBeforeContent"}} + + +
        {{tpl:EntryExcerpt}}
        +
        +
        {{tpl:EntryContent}}
        + +

        {{tpl:lang Published on}} {{tpl:EntryDate}} + {{tpl:lang by}} {{tpl:EntryAuthorLink}}

        + + + {{tpl:SysBehavior behavior="publicEntryAfterContent"}} +
        + + + + +
        +

        {{tpl:lang Attachments}}

        +
          + +
        • + + {{tpl:include src="_mp3_player.html"/}} - + + + {{tpl:include src="_flv_player.html"/}} + + + {{tpl:AttachmentTitle}} + +
        • + +
        +
        + +
        + + + + + +
        +

        {{tpl:lang Comments}}

        +
        + +
        {{tpl:CommentOrderNumber}}. + {{tpl:lang On}} {{tpl:CommentDate}}, {{tpl:CommentTime}} + {{tpl:lang by}} {{tpl:CommentAuthorLink}}
        + +
        + + {{tpl:SysBehavior behavior="publicCommentBeforeContent"}} + + {{tpl:CommentContent}} + + + {{tpl:SysBehavior behavior="publicCommentAfterContent"}} +
        + +
        +
        + +
        +
        + + + +

        {{tpl:SysFormError}}

        +
        + + +

        {{tpl:lang Your comment has been published.}}

        +
        + + +

        {{tpl:lang Your comment has been submitted and + will be reviewed for publication.}}

        +
        + + +
        + +
        +

        {{tpl:lang Your comment}}

        +
        +
        {{tpl:CommentPreviewContent}}
        +
        +

        +
        +
        + +

        {{tpl:lang Add a comment}}

        +
        + + {{tpl:SysBehavior behavior="publicCommentFormBeforeContent"}} + +

        + +

        + +

        + +

        + +

        + +

        + +

        + +

        + +

        + +

        {{tpl:CommentHelp}}

        + + + {{tpl:SysBehavior behavior="publicCommentFormAfterContent"}} +
        + +
        +

        +

        +
        +
        +
        + + + +
        +

        {{tpl:lang They posted on the same topic}}

        + + +
        + +
        {{tpl:PingOrderNumber}}. + {{tpl:lang On}} {{tpl:PingDate}}, {{tpl:PingTime}} + {{tpl:lang by}} {{tpl:PingBlogName encode_html="1"}}
        + +
        + + {{tpl:SysBehavior behavior="publicPingBeforeContent"}} + +

        {{tpl:PingTitle encode_html="1"}}

        + {{tpl:PingContent}} + + + {{tpl:SysBehavior behavior="publicPingAfterContent"}} +
        + +
        + +
        +
        +
        + + +

        {{tpl:lang Trackback URL}} : {{tpl:EntryPingLink}}

        +
        + + +

        {{tpl:lang This page's comments feed}}

        +
        +
        +
        + + + +
        + +{{tpl:include src="_footer.html"}} +
        + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/icon-big.png b/v2/dotclear/plugins/pages/icon-big.png new file mode 100644 index 0000000..b11cfb5 Binary files /dev/null and b/v2/dotclear/plugins/pages/icon-big.png differ diff --git a/v2/dotclear/plugins/pages/icon-np-big.png b/v2/dotclear/plugins/pages/icon-np-big.png new file mode 100644 index 0000000..d4ffd60 Binary files /dev/null and b/v2/dotclear/plugins/pages/icon-np-big.png differ diff --git a/v2/dotclear/plugins/pages/icon-np.png b/v2/dotclear/plugins/pages/icon-np.png new file mode 100644 index 0000000..6f7f7de Binary files /dev/null and b/v2/dotclear/plugins/pages/icon-np.png differ diff --git a/v2/dotclear/plugins/pages/icon.png b/v2/dotclear/plugins/pages/icon.png new file mode 100644 index 0000000..26f64b8 Binary files /dev/null and b/v2/dotclear/plugins/pages/icon.png differ diff --git a/v2/dotclear/plugins/pages/index.php b/v2/dotclear/plugins/pages/index.php new file mode 100644 index 0000000..5260502 --- /dev/null +++ b/v2/dotclear/plugins/pages/index.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/list.php b/v2/dotclear/plugins/pages/list.php new file mode 100644 index 0000000..695f548 --- /dev/null +++ b/v2/dotclear/plugins/pages/list.php @@ -0,0 +1,205 @@ +rs->isEmpty()) + { + echo '

        '.__('No page').'

        '; + } + else + { + $pager = new pager($page,$this->rs_count,$nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
        '.__('Title').''.__('Date').''.__('Author').''.__('Comments').''.__('Trackbacks').''.__('Status').'
        '; + + if ($enclose_block) { + $html_block = sprintf($enclose_block,$html_block); + } + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + + $blocks = explode('%s',$html_block); + + echo $blocks[0]; + + while ($this->rs->fetch()) + { + echo $this->postLine(); + } + + echo $blocks[1]; + + echo '

        '.__('Page(s)').' : '.$pager->getLinks().'

        '; + } + } + + private function postLine() + { + $img = '%1$s'; + switch ($this->rs->post_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('scheduled'),'scheduled.png'); + break; + case -2: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + 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 = ''; + + $res .= + ''. + form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable(),'title="'.__('select this page').'"').''. + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).''. + + ''.$this->rs->user_id.''. + ''.$this->rs->nb_comment.''. + ''.$this->rs->nb_trackback.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''; + + return $res; + } +} + +/* Getting pages +-------------------------------------------------------- */ +$params = array( + 'post_type' => 'page' +); + +$page = !empty($_GET['page']) ? (integer) $_GET['page'] : 1; +$nb_per_page = 30; + +if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) { + $nb_per_page = (integer) $_GET['nb']; +} + +$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page); +$params['no_content'] = true; +$params['order'] = 'post_position ASC, post_title ASC'; + +try { + $pages = $core->blog->getPosts($params); + $counter = $core->blog->getPosts($params,true); + $post_list = new adminPageList($core,$pages,$counter->f(0)); +} catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +# Actions combo box +$combo_action = array(); +if ($core->auth->check('publish,contentadmin',$core->blog->id)) +{ + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('schedule')] = 'schedule'; + $combo_action[__('mark as pending')] = 'pending'; +} +if ($core->auth->check('admin',$core->blog->id)) { + $combo_action[__('change author')] = 'author'; +} +if ($core->auth->check('delete,contentadmin',$core->blog->id)) +{ + $combo_action[__('delete')] = 'delete'; +} + +# --BEHAVIOR-- adminPagesActionsCombo +$core->callBehavior('adminPagesActionsCombo',array(&$combo_action)); + +/* Display +-------------------------------------------------------- */ +?> + + + <?php echo __('Pages'); ?> + + + + + +'.html::escapeHTML($core->blog->name).' › '.__('Pages').''. +'

        '.__('New page').'

        '; + +if (!$core->error->flag()) +{ + # Show pages + $post_list->display($page,$nb_per_page, + '
        '. + + '%s'. + + '
        '. + '

        '. + + '

        '. + form::combo('action',$combo_action). + '

        '. + form::hidden(array('post_type'),'page'). + form::hidden(array('redir'),html::escapeHTML($_SERVER['REQUEST_URI'])). + $core->formNonce(). + '
        '. + '
        '); +} +dcPage::helpBlock('pages'); +?> + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/locales/en/help/page.html b/v2/dotclear/plugins/pages/locales/en/help/page.html new file mode 100644 index 0000000..b91793f --- /dev/null +++ b/v2/dotclear/plugins/pages/locales/en/help/page.html @@ -0,0 +1,165 @@ + + + Editing a page + + + + +

        Editing a page

        + +
        +
        Page title
        +
        Type in the page title. This field is mandatory.
        + +
        Excerpt
        +
        This field is optional.
        + +
        Content
        +
        The content of your page. This field is mandatory.
        + +
        Notes
        +
        This text area is for personal use, for notes or memos. What you enter here + will never be displayed on the blog..
        + +
        Page status
        +
        Allows you to choose your page's status: +
          +
        • pending: the publication status has not yet been decided.
        • +
        • scheduled: the page will be set online at the time and date + set in the Published on field.
        • +
        • unpublished: the page is offline.
        • +
        • published: the page is online.
        • +
        +
        + +
        Published on
        +
        Here you can change the page's publication date and time. If the page's + status is scheduled, it will come online at the said date and time.
        + +
        Text formating
        +
        To choose the page syntax. Wiki is a simplified syntax + and will be converted to valid xhtml ; Unless you have a perfect + understanding of xhtml, we advise you to choose the wiki syntax. + See the Wiki syntax reference for more information.
        + +
        Accept comments
        +
        Select this box to allow your visitors to comment the page. The corresponding + global setting is to be found in the blog settings.
        + +
        Accept trackbacks
        +
        A trackback is a way to let a portion of your page as a comment on + another blog. Select this box to allow others to trackback your page. The + corresponding global setting is to be found in the blog settings.
        + +
        Page position
        +
        Allows to order pages according to this number. + This field is used by Pages widget (Order by page position).
        + +
        Page language
        +
        The two character language code of your page. It defaults to your (as a + user) language but you can change it to whatever language code you want, ie. + "en" or "fr-qc". This code is used for the page's display, no check is made + on it.
        + +
        Page password
        +
        You can enter a password for your page. A password protected page will + not be displayed on your blog, it will only be reachable to those you will + give the page url (see the view page link) and password.
        + +
        Basename
        +
        By clicking on its lock, you can unprotect this field and and choose another + URL for your page. If the URL you're trying to use is already used by another + page, a number will be added to it.
        +
        + +

        Comments

        +
        +
        Comment list
        +
        On the Comments tab, you can read and change the status of + your page's comments. If you're allowed to by the blog administrator, + you will be able to set your comments online or offline, to delete them or + to mark them as junk.
        + +
        Add a comment
        +
        On the Add a comment tab, you can reply to one of your page's + comments without leaving your blog's backend. The syntax to be used here is + plain, unlimited, xhtml. Use the fields as if you were modifying a comment.
        +
        + +

        Wiki syntax reference

        + +

        The wiki syntax is a way to enhance your text with a minimal set +of tags, studied to cover the basic needs (titles, paragraphs, quotes, +lists...)

        + +
        +
        Block elements
        +
        +
          +
        • Leave an empty line between two similar blocks.
        • +
        • Paragraph: free text, ended by an empty line if + another paragraph is to follow.
        • +
        • Title: !!! title, !! title + or ! title, allowing you to use three different levels of + heading.
        • +
        • Horizontal line: ----
        • +
        • Lists: Start each line with + * or # for unnumbered or numbered lists respectively. + List imbrication is done by mixing list markers this way: +
          +* item 1
          +** item 1.1
          +* item 2
          +*# item 2.1
          +...
          +
          +
        • +
        • Preformatted text: each line must start with a space.
        • +
        • Block quote: each line must start with a semicolon (>).
        • +
        +
        + +
        Formatting tags
        +
        +
          +
        • Emphasis: two quotes ''text''
        • +
        • Strong emphasis: two underscore __text__
        • +
        • New line: %%%
        • +
        • Insertion: two plusses ++text++
        • +
        • Deletion: two minuses --text--
        • +
        • Link: [url], [name|url], + [name|url|language] or [name|url|languagee|title]
        • +
        • Image: + ((url|alternative text)), + ((url|alternative text|position)) or + ((url|alternative text|position|long description)). +
          The position can be either L or G (left), R or D (right) or C (centered).
        • +
        • Anchor: ~anchor~
        • +
        • Acronym: ??acronym|title??
        • +
        • Inline HTML: two backquotes ``html code``
        • +
        • Inline quote: {{quote}}, + {{quote|language}} or {{quote|language|url}}
        • +
        • Code: @@code@@
        • +
        • Footnotes: $$footnote$$
        • +
        +
        + +
        Unformatted text
        +
        If you don't want one formatting character to be interpreted as such, add a +\ just before it. This way: +\[text in brackets without being a link\] +
        + +
        HTML Code
        +
        Even if you chose the wiki syntax, you may sometimes need a more +powerful formatting, i.e. HTML syntax. Do it the following way: +
        +///html
        +<p style="color:red">my text in red</p>
        +///
        +
        +
        +
        + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/locales/en/help/pages.html b/v2/dotclear/plugins/pages/locales/en/help/pages.html new file mode 100644 index 0000000..1afd515 --- /dev/null +++ b/v2/dotclear/plugins/pages/locales/en/help/pages.html @@ -0,0 +1,22 @@ + + + Managing pages + + + + +

        Selected pages actions

        +

        If your user permission level allows it, you can perform batch operations on the +pages you select in the list.

        + +
          +
        • Publish : set pages online,
        • +
        • Unpublish : set pages offline,
        • +
        • Mark as pending : erase other publication status,
        • +
        • Change author : allows you to change the pages' author by + entering the new author's ID,
        • +
        • Delete : suppress the pages (this operation cannot be undone).
        • +
        + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/locales/en/resources.php b/v2/dotclear/plugins/pages/locales/en/resources.php new file mode 100644 index 0000000..1df1128 --- /dev/null +++ b/v2/dotclear/plugins/pages/locales/en/resources.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/locales/fr/help/page.html b/v2/dotclear/plugins/pages/locales/fr/help/page.html new file mode 100644 index 0000000..1b17b7d --- /dev/null +++ b/v2/dotclear/plugins/pages/locales/fr/help/page.html @@ -0,0 +1,172 @@ + + + Rédaction d'une page + + + + +

        Rédaction de la page

        + +
        +
        Titre de la page
        +
        Inscrivez le titre de la page. Ce champ est obligatoire.
        + +
        Extrait
        +
        Ce champ est optionnel.
        + +
        Contenu
        +
        Le contenu de la page. Ce champ est obligatoire.
        + +
        Notes
        +
        Cette zone de texte sert à la prise de notes ou pense-bête divers. Elle ne + sera jamais affichée sur le blog.
        + +
        Etat de la page
        +
        Permet de choisir l'état de la page après enregistrement : +
          +
        • en attente : en attente de publication.
        • +
        • programmée : la page sera mise en ligne à la date et à l'heure + indiquées dans le champ Publiée le.
        • +
        • non publiée : page hors ligne.
        • +
        • publiée : page en ligne.
        • +
        +
        + +
        Publiée le
        +
        Permet de modifier la date et l'heure de publication de la page. Si vous avez + choisi le statut programmée elle sera mise en ligne aux date et heure + définis dans ce champ.
        + +
        Format du texte
        +
        Permet de choisir la syntaxe de saisie de la page. Le wiki est une syntaxe + simplifiée et sera converti en xhtml valide ; à moins que vous maîtrisiez + parfaitement le xhtml nous vous conseillons de la choisir de préférence. + Consultez la référence de la syntaxe Wiki pour plus + d'informations.
        + +
        Accepter les commentaires
        +
        Cochez ou décochez la case selon que vous souhaitez permettre ou interdire + les commentaires sur la page en particulier. L'option pour permettre ou + autoriser de façon générale les commentaires se situe dans le menu Préférences + du blog.
        + +
        Accepter les rétroliens
        +
        Un rétrolien permet de signaler une page dans les commentaires d'un autre + blog. Cochez ou décochez la case selon que vous souhaitez permettre ou interdire + les rétroliens sur la page. L'option pour permettre ou autoriser de façon + générale les rétroliens se situe dans le menu Préférences du blog.
        + +
        Position de la page
        +
        Permet de classer les différentes pages en fonction de leur numéro d'ordre. + Ce champ est utilisé par le widget Pages (Trier par position de la page).
        + +
        Langue de la page
        +
        Code langue de votre page. Par défaut, il s'agit du code de votre + langue. Vous pouvez le changer, par le code d'une autre langue, par + exemple "en", "fr-qc". Ce code est libre, il sera utilisé lors de l'affichage + des pages.
        + +
        Mot de passe de la page
        +
        Permet de déterminer un mot de passe d'accès à une page dans le blog. Une + page protégée par mot de passe ne sera visible nulle part sur votre blog, vous + pourrez en donner l'adresse à vos lecteurs en vous rendant sur le lien + Voir la page.
        + +
        URL spécifique
        +
        Ce champ permet de choisir une URL pour une page autre que celle par défaut + après avoir cliqué sur le petit verrou placé à sa droite. Si vous essayez + d'utiliser une URL déjà existante, celle-ci se verra incrémentée d'un chiffre.
        +
        + +

        Commentaires

        +
        +
        Liste les commentaires
        +
        Depuis l'onglets Commentaires vous pouvez lire et changer l'état + des commentaires de votre page. Suivant vos permissions, vous pouvez modifier, + mettre en ligne ou hors ligne, supprimer ou classer comme commentaire + indésirable.
        + +
        Ajouter un commentaire
        +
        Depuis l'onglet Ajouter un commentaire vous pouvez répondre + directement à un commentaire sans passer par votre blog. Cette interface vous + permet également de saisir votre commentaire en XHTML, sans limitation. Vous + devrez remplir les champs de la même manière qu'en modifiant un commentaire.
        +
        + +

        Référence de la syntaxe Wiki

        + +

        La syntaxe Wiki est une manière d'écrire du texte avec un jeu de balises +réduit au minimum, permettant de couvrir les besoins les plus courants +(titres, paragraphes, citations, listes...).

        + +
        +
        Éléments de bloc
        +
        +
          +
        • Laissez une ligne vide entre chaque bloc de même nature.
        • +
        • Paragraphe : texte libre, terminé par une ligne + vide si suivi d'un second paragraphe.
        • +
        • Titre : !!! titre, !! titre + ou ! titre pour des titres plus ou moins importants.
        • +
        • Trait horizontal : ----
        • +
        • Listes : lignes débutant par * pour des + listes à puce ou # pour des listes numérotées. Vous pouvez faire + des listes imbriquées en mélangeant les codes de liste. Par exemple : +
          +* item 1
          +** item 1.1
          +* item 2
          +*# item 2.1
          +...
          +
          +
        • +
        • Texte préformaté : espace avant chaque ligne de texte.
        • +
        • Bloc de citation : > devant chaque + ligne de texte.
        • +
        +
        + +
        Éléments de formatage
        +
        +
          +
        • Emphase : deux apostrophes ''texte''
        • +
        • Forte emphase : deux soulignés __texte__
        • +
        • Retour forcé à la ligne : %%%
        • +
        • Insertion : deux plus ++texte++
        • +
        • Suppression : deux moins --texte--
        • +
        • Lien : [url], [nom|url], + [nom|url|langue] ou [nom|url|langue|titre]
        • +
        • Image : + ((url|texte alternatif)), + ((url|texte alternatif|position)) ou + ((url|texte alternatif|position|description longue)). +
          La position peut prendre les valeurs L ou G (gauche), R ou D (droite) ou C (centré).
        • +
        • Ancre : ~ancre~
        • +
        • Acronyme : ??acronyme|titre??
        • +
        • Code HTML en ligne: deux apostrophes inversées ``code html``
        • +
        • Citation en ligne : {{citation}}, + {{citation|langue}} ou {{citation|langue|url}}
        • +
        • Code : @@code ici@@
        • +
        • Note de bas de page : $$Corps de la note$$
        • +
        +
        + +
        Empêcher le formatage du texte
        +
        Pour insérer un caractère sans que celui-ci soit reconnu comme un caractère +de formatage, ajoutez le caractère \ avant celui-ci. Par exemple : +\[texte entre crochet qui n'est pas un lien\] +
        + +
        Insérer du code HTML
        +
        Vous pouvez ponctuellement avoir besoin d'insérer du code HTML dans votre +texte au format Wiki. Pour cela, utilisez le code suivant : +
        +///html
        +<p style="color:red">mon texte en rouge</p>
        +///
        +
        +
        +
        + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/locales/fr/help/pages.html b/v2/dotclear/plugins/pages/locales/fr/help/pages.html new file mode 100644 index 0000000..1036b19 --- /dev/null +++ b/v2/dotclear/plugins/pages/locales/fr/help/pages.html @@ -0,0 +1,22 @@ + + + Gestion des pages + + + + +

        Actions par lot sur les pages

        +

        Il est possible d'effectuer un ensemble d'actions sur plusieurs pages, d'un +seul coup. Les actions possibles dépendent des permissions de l'utilisateur.

        + +
          +
        • Publier : mettre la page en ligne,
        • +
        • Hors ligne : mettre la page hors ligne,
        • +
        • En attente : en attente de publication,
        • +
        • Changer l'auteur : permet de changer l'auteur de la page en indiquant + l'identifiant de l'utilisateur qui deviendra le nouvel auteur,
        • +
        • Supprimer : supprime la page (cette opération est irréversible).
        • +
        + + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/locales/fr/resources.php b/v2/dotclear/plugins/pages/locales/fr/resources.php new file mode 100644 index 0000000..1df1128 --- /dev/null +++ b/v2/dotclear/plugins/pages/locales/fr/resources.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/v2/dotclear/plugins/pages/page.php b/v2/dotclear/plugins/pages/page.php new file mode 100644 index 0000000..a123b8c --- /dev/null +++ b/v2/dotclear/plugins/pages/page.php @@ -0,0 +1,659 @@ +auth->getOption('post_format'); +$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_position = 0; +$post_open_comment = false; +$post_open_tb = false; + +$post_media = array(); + +$page_title = __('New page'); + +$can_view_page = true; +$can_edit_page = $core->auth->check('pages,usage',$core->blog->id); +$can_publish = $core->auth->check('pages,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; +} + +# Status combo +foreach ($core->blog->getAllPostStatus() as $k => $v) { + $status_combo[$v] = (string) $k; +} + +# Formaters combo +foreach ($core->getFormaters() as $v) { + $formaters_combo[$v] = $v; +} + +# Languages combo +$rs = $core->blog->getLangs(array('order'=>'asc')); +$all_langs = l10n::getISOcodes(0,1); +$lang_combo = array('' => '', __('Most used') => array(), __('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); + + +# Get page informations +if (!empty($_REQUEST['id'])) +{ + $params['post_type'] = 'page'; + $params['post_id'] = $_REQUEST['id']; + + $post = $core->blog->getPosts($params); + + if ($post->isEmpty()) + { + $core->error->add(__('This page does not exist.')); + $can_view_page = false; + } + else + { + $post_id = $post->post_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_position = (integer) $post->post_position; + $post_open_comment = (boolean) $post->post_open_comment; + $post_open_tb = (boolean) $post->post_open_tb; + + $page_title = __('Edit page'); + + $can_edit_page = $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 page').' »'); + $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 page')); + $prev_headlink = sprintf($post_headlink,'previous', + html::escapeHTML($prev_rs->post_title),$prev_rs->post_id); + } + + try { + $core->media = new dcMedia($core); + $post_media = $core->media->getPostMedia($post_id); + } catch (Exception $e) {} + } +} + +# Format content +if (!empty($_POST) && $can_edit_page) +{ + $post_format = $_POST['post_format']; + $post_excerpt = $_POST['post_excerpt']; + $post_content = $_POST['post_content']; + + $post_title = $_POST['post_title']; + + if (isset($_POST['post_status'])) { + $post_status = (integer) $_POST['post_status']; + } + + if (empty($_POST['post_dt'])) { + $post_dt = ''; + } else { + $post_dt = strtotime($_POST['post_dt']); + $post_dt = date('Y-m-d H:i',$post_dt); + } + + $post_open_comment = !empty($_POST['post_open_comment']); + $post_open_tb = !empty($_POST['post_open_tb']); + $post_lang = $_POST['post_lang']; + $post_password = !empty($_POST['post_password']) ? $_POST['post_password'] : null; + $post_position = (integer) $_POST['post_position']; + + $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 + ); +} + +# Create or update post +if (!empty($_POST) && !empty($_POST['save']) && $can_edit_page) +{ + $cur = $core->con->openCursor($core->prefix.'post'); + + # Magic tweak :) + $core->blog->settings->system->post_url_format = $page_url_format; + + $cur->post_type = 'page'; + $cur->post_title = $post_title; + $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_position = $post_position; + $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-- adminBeforePageUpdate + $core->callBehavior('adminBeforePageUpdate',$cur,$post_id); + + $core->blog->updPost($post_id,$cur); + + # --BEHAVIOR-- adminAfterPageUpdate + $core->callBehavior('adminAfterPageUpdate',$cur,$post_id); + + http::redirect($redir_url.'&id='.$post_id.'&upd=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } + else + { + $cur->user_id = $core->auth->userID(); + + try + { + # --BEHAVIOR-- adminBeforePageCreate + $core->callBehavior('adminBeforePageCreate',$cur); + + $return_id = $core->blog->addPost($cur); + + # --BEHAVIOR-- adminAfterPageCreate + $core->callBehavior('adminAfterPageCreate',$cur,$return_id); + + http::redirect($redir_url.'&id='.$return_id.'&crea=1'); + } + catch (Exception $e) + { + $core->error->add($e->getMessage()); + } + } +} + +if (!empty($_POST['delete']) && $can_delete) +{ + try { + # --BEHAVIOR-- adminBeforePageDelete + $core->callBehavior('adminBeforePageDelete',$post_id); + $core->blog->delPost($post_id); + http::redirect($p_url); + } catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +/* DISPLAY +-------------------------------------------------------- */ +$default_tab = 'edit-entry'; +if (!$can_edit_page) { + $default_tab = ''; +} +if (!empty($_GET['co'])) { + $default_tab = 'comments'; +} + +?> + + + <?php echo $page_title.' - '.__('Pages'); ?> + + callBehavior('adminPageHeaders'). + dcPage::jsPageTabs($default_tab). + $next_headlink."\n".$prev_headlink; + ?> + + + + +'.__('Page has been successfully updated.').'

        '; +} +elseif (!empty($_GET['crea'])) { + echo '

        '.__('Page has been successfully created.').'

        '; +} +elseif (!empty($_GET['attached'])) { + echo '

        '.__('File has been successfully attached.').'

        '; +} +elseif (!empty($_GET['rmattach'])) { + echo '

        '.__('Attachment has been successfully removed.').'

        '; +} + +# XHTML conversion +if (!empty($_GET['xconv'])) +{ + $post_excerpt = $post_excerpt_xhtml; + $post_content = $post_content_xhtml; + $post_format = 'xhtml'; + + echo '

        '.__('Don\'t forget to validate your XHTML conversion by saving your post.').'

        '; +} + +echo '

        '.html::escapeHTML($core->blog->name). +' › '.__('Pages').''.$page_title; + if ($post_id) { + echo ' “'.$post_title.'”'; + } +echo '

        '; + +if ($post_id && $post->post_status == 1) { + echo '

        '.__('Go to this page on the site').'

        '; +} + +echo ''; + +if ($post_id) +{ + echo '

        '; + if ($prev_link) { echo $prev_link; } + if ($next_link && $prev_link) { echo ' - '; } + if ($next_link) { echo $next_link; } + + # --BEHAVIOR-- adminPageNavLinks + $core->callBehavior('adminPageNavLinks',isset($post) ? $post : null); + + echo '

        '; +} + +# Exit if we cannot view page +if (!$can_view_page) { + echo ''; + return; +} + + +/* Post form if we can edit post +-------------------------------------------------------- */ +if ($can_edit_page) +{ + echo '
        '; + echo '
        '; + + echo '
        '; + echo '
        '; + + echo + '

        '. + + '

        '. + form::textarea('post_excerpt',50,5,html::escapeHTML($post_excerpt)). + '

        '. + + '

        '. + form::textarea('post_content',50,$core->auth->getOption('edit_size'),html::escapeHTML($post_content)). + '

        '; + + echo + '

        '. + form::textarea('post_notes',50,5,html::escapeHTML($post_notes)). + '

        '; + + # --BEHAVIOR-- adminPageForm + $core->callBehavior('adminPageForm',isset($post) ? $post : null); + + echo + '

        '. + ($post_id ? form::hidden('id',$post_id) : ''). + ' '; + + if ($post_id) { + $preview_url = $core->blog->url. + $core->url->getURLFor('pagespreview', + $core->auth->userID().'/'. + http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')). + '/'.$post->post_url); + echo ''.__('Preview').''; + } + + echo + ($can_delete ? '' : ''). + $core->formNonce(). + '

        '; + + echo '
        '; // End #entry-content + echo '
        '; // End #entry-wrapper + + echo '
        '; + + echo + '

        '. + + '

        '. + + '

        '. + + '

        '. + '

        '. + + '

        '. + + '

        '. + + '

        '. + + '
        '. + '

        '. + '

        '. + __('Warning: If you set the URL manually, it may conflict with another page.'). + '

        '. + '
        '; + + if ($post_id) + { + echo + '

        '.__('Attachments').'

        '; + foreach ($post_media as $f) + { + $ftitle = $f->media_title; + if (strlen($ftitle) > 18) { + $ftitle = substr($ftitle,0,16).'...'; + } + echo + '
        '. + ''. + ''. + ''. + '
        '; + } + unset($f); + + if (empty($post_media)) { + echo '

        '.__('No attachment.').'

        '; + } + echo '

        '.__('Add files to this page').'

        '; + } + + # --BEHAVIOR-- adminPageFormSidebar + $core->callBehavior('adminPageFormSidebar',isset($post) ? $post : null); + + echo '
        '; // End #entry-sidebar + + echo '
        '; + echo '
        '; + + if ($post_id && !empty($post_media)) + { + echo + '
        '. + '
        '.form::hidden(array('post_id'),$post_id). + form::hidden(array('media_id'),''). + form::hidden(array('remove'),1). + $core->formNonce().'
        '; + } +} + + +/* Comments and trackbacks +-------------------------------------------------------- */ +if ($post_id) +{ + $params = array('post_id' => $post_id, 'order' => 'comment_dt ASC'); + + $comments = $core->blog->getComments(array_merge($params,array('comment_trackback'=>0))); + $trackbacks = $core->blog->getComments(array_merge($params,array('comment_trackback'=>1))); + + # Actions combo box + $combo_action = array(); + if ($can_edit_page && $core->auth->check('publish,contentadmin',$core->blog->id)) + { + $combo_action[__('publish')] = 'publish'; + $combo_action[__('unpublish')] = 'unpublish'; + $combo_action[__('mark as pending')] = 'pending'; + $combo_action[__('mark as junk')] = 'junk'; + } + + if ($can_edit_page && $core->auth->check('delete,contentadmin',$core->blog->id)) + { + $combo_action[__('delete')] = 'delete'; + } + + $has_action = !empty($combo_action) && (!$trackbacks->isEmpty() || !$comments->isEmpty()); + + echo + '
        '; + + if ($has_action) { + echo '
        '; + } + + echo '

        '.__('Trackbacks').'

        '; + + if (!$trackbacks->isEmpty()) { + showComments($trackbacks,$has_action); + } else { + echo '

        '.__('No trackback').'

        '; + } + + echo '

        '.__('Comments').'

        '; + if (!$comments->isEmpty()) { + showComments($comments,$has_action); + } else { + echo '

        '.__('No comment').'

        '; + } + + if ($has_action) { + echo + '
        '. + '

        '. + + '

        '. + form::combo('action',$combo_action). + form::hidden('redir',html::escapeURL($redir_url).'&id='.$post_id.'&co=1'). + $core->formNonce(). + '

        '. + '
        '. + '
        '; + } + + echo '
        '; +} + +/* Add a comment +-------------------------------------------------------- */ +if ($post_id) +{ + echo + '
        '. + '

        '.__('Add a comment').'

        '. + + '
        '. + '
        '. + '

        '. + + '

        '. + + '

        '. + + '

        '. + form::textarea('comment_content',50,8,html::escapeHTML('')). + '

        '. + + '

        '.form::hidden('post_id',$post_id). + $core->formNonce(). + '

        '. + '
        '. + '
        '. + '
        '; +} + + +# Show comments or trackbacks +function showComments($rs,$has_action) +{ + echo + ''. + ''. + ''. + ''. + ''. + ''. + ''; + + while($rs->fetch()) + { + $comment_url = 'comment.php?id='.$rs->comment_id; + + $img = '%1$s'; + switch ($rs->comment_status) { + case 1: + $img_status = sprintf($img,__('published'),'check-on.png'); + break; + case 0: + $img_status = sprintf($img,__('unpublished'),'check-off.png'); + break; + case -1: + $img_status = sprintf($img,__('pending'),'check-wrn.png'); + break; + case -2: + $img_status = sprintf($img,__('junk'),'junk.png'); + break; + } + + echo + ''. + + ''. + ''. + ''. + ''. + ''. + ''. + + ''; + } + + echo '
        '.__('Author').''.__('Date').''.__('IP address').''.__('Status').' 
        '. + ($has_action ? form::checkbox(array('comments[]'),$rs->comment_id,'','','',0,'title="'.__('select this comment').'"') : '').''.$rs->comment_author.''.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->comment_dt).''.$rs->comment_ip.''.$img_status.''. + '
        '; +} +dcPage::helpBlock('page'); +?> + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pings/_admin.php b/v2/dotclear/plugins/pings/_admin.php new file mode 100644 index 0000000..86ea58c --- /dev/null +++ b/v2/dotclear/plugins/pings/_admin.php @@ -0,0 +1,47 @@ +addItem(__('Pings'),'plugin.php?p=pings','index.php?pf=pings/icon.png', + preg_match('/plugin.php\?p=pings/',$_SERVER['REQUEST_URI']), + $core->auth->isSuperAdmin()); + +$__autoload['pingsAPI'] = dirname(__FILE__).'/lib.pings.php'; +$__autoload['pingsBehaviors'] = dirname(__FILE__).'/lib.pings.php'; + +# Create settings if they don't exist +if (!array_key_exists('pings',$core->blog->settings->dumpNamespaces())) +{ + $default_pings_uris = array( + 'Ping-o-Matic!' => 'http://rpc.pingomatic.com/', + 'Google Blog Search' => 'http://blogsearch.google.com/ping/RPC2' + ); + + $core->blog->settings->addNamespace('pings'); + $core->blog->settings->pings->put('pings_active',1,'boolean','Activate pings plugin',true,true); + $core->blog->settings->pings->put('pings_uris',serialize($default_pings_uris),'string','Pings services URIs',true,true); +} + +$core->addBehavior('adminPostHeaders',array('pingsBehaviors','pingJS')); +$core->addBehavior('adminPostFormSidebar',array('pingsBehaviors','pingsForm')); +$core->addBehavior('adminAfterPostCreate',array('pingsBehaviors','doPings')); +$core->addBehavior('adminAfterPostUpdate',array('pingsBehaviors','doPings')); + +$core->addBehavior('adminDashboardFavs','pingDashboardFavs'); + +function pingDashboardFavs($core,$favs) +{ + $favs['pings'] = new ArrayObject(array('pings','Pings','plugin.php?p=pings', + 'index.php?pf=pings/icon.png','index.php?pf=pings/icon-big.png', + null,null,null)); +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pings/_define.php b/v2/dotclear/plugins/pings/_define.php new file mode 100644 index 0000000..8e7a8a9 --- /dev/null +++ b/v2/dotclear/plugins/pings/_define.php @@ -0,0 +1,23 @@ +registerModule( + /* Name */ "Pings", + /* Description*/ "Ping services", + /* Author */ "Olivier Meunier", + /* Version */ '1.1', + array( + 'permissions' => 'usage,contentadmin' + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pings/icon-big.png b/v2/dotclear/plugins/pings/icon-big.png new file mode 100644 index 0000000..19dd23f Binary files /dev/null and b/v2/dotclear/plugins/pings/icon-big.png differ diff --git a/v2/dotclear/plugins/pings/icon.png b/v2/dotclear/plugins/pings/icon.png new file mode 100644 index 0000000..4324dd5 Binary files /dev/null and b/v2/dotclear/plugins/pings/icon.png differ diff --git a/v2/dotclear/plugins/pings/index.php b/v2/dotclear/plugins/pings/index.php new file mode 100644 index 0000000..2679675 --- /dev/null +++ b/v2/dotclear/plugins/pings/index.php @@ -0,0 +1,101 @@ +blog->settings->pings->pings_uris); + if (!$pings_uris) { + $pings_uris = array(); + } + + if (isset($_POST['pings_srv_name'])) + { + $pings_srv_name = is_array($_POST['pings_srv_name']) ? $_POST['pings_srv_name'] : array(); + $pings_srv_uri = is_array($_POST['pings_srv_uri']) ? $_POST['pings_srv_uri'] : array(); + $pings_uris = array(); + + foreach ($pings_srv_name as $k => $v) { + if (trim($v) && trim($pings_srv_uri[$k])) { + $pings_uris[trim($v)] = trim($pings_srv_uri[$k]); + } + } + + $core->blog->settings->addNamespace('pings'); + $core->blog->settings->pings->put('pings_active',!empty($_POST['pings_active']),null,null,true,true); + $core->blog->settings->pings->put('pings_uris',serialize($pings_uris),null,null,true,true); + http::redirect($p_url.'&up=1'); + } +} +catch (Exception $e) +{ + $core->error->add($e->getMessage()); +} +?> + + + <?php echo __('Pings'); ?> + + + +'.__('Pings configuration').''; + +if (!empty($_GET['up'])) { + echo '

        '.__('Settings have been successfully updated.').'

        '; +} + +echo +'
        '. +'

        '; + +$i = 0; +foreach ($pings_uris as $n => $u) +{ + echo + '

        '. + ''; + + if (!empty($_GET['test'])) + { + try { + pingsAPI::doPings($u,'Example site','http://example.com'); + echo ' ok'; + } catch (Exception $e) { + echo ' '.__('error').' '.$e->getMessage(); + } + } + + echo '

        '; + $i++; +} + +echo +'

        '. +''. +'

        '. + +'

        '. +$core->formNonce().'

        '. +'
        '; + +echo '

        '.__('Test ping services').'

        '; +?> + + \ No newline at end of file diff --git a/v2/dotclear/plugins/pings/lib.pings.php b/v2/dotclear/plugins/pings/lib.pings.php new file mode 100644 index 0000000..7b3cbdb --- /dev/null +++ b/v2/dotclear/plugins/pings/lib.pings.php @@ -0,0 +1,99 @@ +timeout = 3; + + $rsp = $o->query('weblogUpdates.ping',$site_name,$site_url); + + if (isset($rsp['flerror']) && $rsp['flerror']) { + throw new Exception($rsp['message']); + } + + return true; + } +} + +class pingsBehaviors +{ + public static function pingJS() + { + $res = + "\n".dcPage::jsLoad('index.php?pf=pings/post.js'); + + return $res; + } + + public static function pingsForm($post) + { + $core =& $GLOBALS['core']; + if (!$core->blog->settings->pings->pings_active) { + return; + } + + $pings_uris = @unserialize($core->blog->settings->pings->pings_uris); + if (empty($pings_uris) || !is_array($pings_uris)) { + return; + } + + if (!empty($_POST['pings_do']) && is_array($_POST['pings_do'])) { + $pings_do = $_POST['pings_do']; + } else { + $pings_do = array(); + } + + echo '

        '.__('Pings:').'

        '; + $i = 0; + foreach ($pings_uris as $k => $v) + { + echo + '

        '; + $i++; + } + } + + public static function doPings($cur,$post_id) + { + if (empty($_POST['pings_do']) || !is_array($_POST['pings_do'])) { + return; + } + + $core =& $GLOBALS['core']; + if (!$core->blog->settings->pings->pings_active) { + return; + } + + $pings_uris = @unserialize($core->blog->settings->pings->pings_uris); + if (empty($pings_uris) || !is_array($pings_uris)) { + return; + } + + foreach ($_POST['pings_do'] as $uri) + { + if (in_array($uri,$pings_uris)) { + try { + pingsAPI::doPings($uri,$core->blog->name,$core->blog->url); + } catch (Exception $e) {} + } + } + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/pings/post.js b/v2/dotclear/plugins/pings/post.js new file mode 100644 index 0000000..8be054a --- /dev/null +++ b/v2/dotclear/plugins/pings/post.js @@ -0,0 +1,3 @@ + +$(function(){$('#edit-entry').onetabload(function(){if($('p.ping-services').length>0){p=$('

        ');p.addClass('ping-services');a=$('');a.text(dotclear.msg.check_all);a.click(function(){$('p.ping-services input[type="checkbox"]').attr('checked','checked');return false;});$('p.ping-services:last').after(p.append(a));} +$('h3.ping-services').toggleWithLegend($('p.ping-services'),{cookie:'dcx_ping_services'});});}); \ No newline at end of file diff --git a/v2/dotclear/plugins/simpleMenu/_admin.php b/v2/dotclear/plugins/simpleMenu/_admin.php new file mode 100644 index 0000000..f838125 --- /dev/null +++ b/v2/dotclear/plugins/simpleMenu/_admin.php @@ -0,0 +1,32 @@ +addBehavior('adminDashboardIcons','simpleMenu_dashboard'); +$core->addBehavior('adminDashboardFavs','simpleMenu_dashboard_favs'); +function simpleMenu_dashboard($core,$icons) +{ + $icons['simpleMenu'] = new ArrayObject(array(__('Simple menu'),'plugin.php?p=simpleMenu','index.php?pf=simpleMenu/icon.png')); +} +function simpleMenu_dashboard_favs($core,$favs) +{ + $favs['simpleMenu'] = new ArrayObject(array('simpleMenu','Simple menu','plugin.php?p=simpleMenu', + 'index.php?pf=simpleMenu/icon-small.png','index.php?pf=simpleMenu/icon.png', + 'usage,contentadmin',null,null)); +} + +$_menu['Plugins']->addItem(__('Simple menu'),'plugin.php?p=simpleMenu','index.php?pf=simpleMenu/icon-small.png', + preg_match('/plugin.php\?p=simpleMenu(&.*)?$/',$_SERVER['REQUEST_URI']), + $core->auth->check('usage,contentadmin',$core->blog->id)); + +require dirname(__FILE__).'/_widgets.php'; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/simpleMenu/_define.php b/v2/dotclear/plugins/simpleMenu/_define.php new file mode 100644 index 0000000..c76d76d --- /dev/null +++ b/v2/dotclear/plugins/simpleMenu/_define.php @@ -0,0 +1,23 @@ +registerModule( + /* Name */ "simpleMenu", + /* Description*/ "Simple menu for Dotclear", + /* Author */ "Franck Paul", + /* Version */ '1.0', + array( + 'permissions' => 'admin' + ) +); +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/simpleMenu/_install.php b/v2/dotclear/plugins/simpleMenu/_install.php new file mode 100644 index 0000000..f6722e7 --- /dev/null +++ b/v2/dotclear/plugins/simpleMenu/_install.php @@ -0,0 +1,29 @@ +plugins->moduleInfo('simpleMenu','version'); +if (version_compare($core->getVersion('simpleMenu'),$version,'>=')) { + return; +} + +# Menu par défaut +$blog_url = html::stripHostURL($core->blog->url); +$menu_default = array( + array('label' => 'Home', 'descr' => 'Recent posts', 'url' => $blog_url), + array('label' => 'Archives', 'descr' => '', 'url' => $blog_url.$core->url->getURLFor('archive')) +); +$core->blog->settings->system->put('simpleMenu',serialize($menu_default),'string','simpleMenu default menu',false,true); + +$core->setVersion('simpleMenu',$version); +return true; +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/simpleMenu/_public.php b/v2/dotclear/plugins/simpleMenu/_public.php new file mode 100644 index 0000000..5737447 --- /dev/null +++ b/v2/dotclear/plugins/simpleMenu/_public.php @@ -0,0 +1,116 @@ +tpl->addValue('SimpleMenu',array('tplSimpleMenu','simpleMenu')); + +class tplSimpleMenu +{ + # Template function + public static function simpleMenu($attr) + { + $class = isset($attr['class']) ? trim($attr['class']) : ''; + $id = isset($attr['id']) ? trim($attr['id']) : ''; + $description = isset($attr['description']) ? trim($attr['description']) : ''; + + if (!preg_match('#^(title|span)$#',$description)) { + $description = ''; + } + + return ''; + } + + # Widget function + public static function simpleMenuWidget($w) + { + global $core, $_ctx; + + if ($w->homeonly && $core->url->type != 'default') { + return; + } + + $menu = tplSimpleMenu::displayMenu('','','title'); + if ($menu == '') { + return; + } + + return '
        '.($w->title ? '

        '.html::escapeHTML($w->title).'

        ' : '').$menu.'
        '; + } + + public static function displayMenu($class='',$id='',$description='') + { + $ret = ''; + + $menu = $GLOBALS['core']->blog->settings->system->get('simpleMenu'); + $menu = @unserialize($menu); + + if (is_array($menu)) + { + // Current relative URL + $url = $_SERVER['REQUEST_URI']; + $abs_url = http::getHost().$url; + + // Home recognition var + $home_url = html::stripHostURL($GLOBALS['core']->blog->url); + $home_directory = dirname($home_url); + if ($home_directory != '/') + $home_directory = $home_directory.'/'; + + // Menu items loop + foreach ($menu as $i => $m) { + # $href = lien de l'item de menu + $href = $m['url']; + $href = html::escapeHTML($href); + + # Active item test + $active = false; + if (($url == $href) || + ($abs_url == $href) || + ($_SERVER['URL_REQUEST_PART'] == $href) || + (($_SERVER['URL_REQUEST_PART'] == '') && (($href == $home_url) || ($href == $home_directory)))) { + $active = true; + } + $title = $span = ''; + if ($m['descr']) { + if ($description == 'title') { + $title = ' title="'.__($m['descr']).'"'; + } else { + $span = ' '.__($m['descr']).''; + } + } + $ret .= '
      • '. + ''.__($m['label']).$span.''. + '
      • '; + } + + // Final rendering + if ($ret) { + $ret = '
          '."\n".$ret."\n".'
        '; + } + } + + return $ret; + } +} + +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/simpleMenu/_widgets.php b/v2/dotclear/plugins/simpleMenu/_widgets.php new file mode 100644 index 0000000..72f9507 --- /dev/null +++ b/v2/dotclear/plugins/simpleMenu/_widgets.php @@ -0,0 +1,25 @@ +addBehavior('initWidgets',array('simpleMenuWidgets','initWidgets')); + +class simpleMenuWidgets +{ + public static function initWidgets($w) + { + $w->create('simplemenu',__('Simple menu'),array('tplSimpleMenu','simpleMenuWidget')); + $w->simplemenu->setting('title',__('Title:'),__('Menu')); + $w->simplemenu->setting('homeonly',__('Home page only'),1,'check'); + } +} +?> \ No newline at end of file diff --git a/v2/dotclear/plugins/simpleMenu/icon-small.png b/v2/dotclear/plugins/simpleMenu/icon-small.png new file mode 100644 index 0000000..aea1775 Binary files /dev/null and b/v2/dotclear/plugins/simpleMenu/icon-small.png differ diff --git a/v2/dotclear/plugins/simpleMenu/icon.png b/v2/dotclear/plugins/simpleMenu/icon.png new file mode 100644 index 0000000..07f5de7 Binary files /dev/null and b/v2/dotclear/plugins/simpleMenu/icon.png differ diff --git a/v2/dotclear/plugins/simpleMenu/index.php b/v2/dotclear/plugins/simpleMenu/index.php new file mode 100644 index 0000000..8eda38f --- /dev/null +++ b/v2/dotclear/plugins/simpleMenu/index.php @@ -0,0 +1,569 @@ +blog->url); + +# Liste des catégories +$categories_combo = array(); +$categories_label = array(); +try { + $rs = $core->blog->getCategories(array('post_type'=>'post')); + while ($rs->fetch()) { + $categories_combo[] = new formSelectOption( + str_repeat('  ',$rs->level-1).($rs->level-1 == 0 ? '' : '• ').html::escapeHTML($rs->cat_title), + $rs->cat_url + ); + $categories_label[$rs->cat_url] = html::escapeHTML($rs->cat_title); + } +} catch (Exception $e) { } + +# Liste des langues utilisées +$langs_combo = array(); +try { + $rs = $core->blog->getLangs(array('order'=>'asc')); + if ($rs->count() > 1) + { + $all_langs = l10n::getISOcodes(0,1); + while ($rs->fetch()) { + $lang_name = isset($all_langs[$rs->post_lang]) ? $all_langs[$rs->post_lang] : $rs->post_lang; + $langs_combo[$lang_name] = $rs->post_lang; + } + unset($all_langs); + } + unset($rs); +} catch (Exception $e) { } + +# Liste des mois d'archive +$months_combo = array(); +try { + $rs = $core->blog->getDates(array('type'=>'month')); + $months_combo[__('All months')] = '-'; + $first_year = $last_year = 0; + while ($rs->fetch()) { + $months_combo[dt::str('%B %Y',$rs->ts())] = $rs->year().$rs->month(); + if (($first_year == 0) || ($rs->year() < $first_year)) $first_year = $rs->year(); + if (($last_year == 0) || ($rs->year() > $last_year)) $last_year = $rs->year(); + } + unset($rs); +} catch (Exception $e) { } + +# Liste des pages -- Doit être pris en charge plus tard par le plugin ? +$pages_combo = array(); +try { + $rs = $core->blog->getPosts(array('post_type'=>'page')); + while ($rs->fetch()) { + $pages_combo[$rs->post_title] = $rs->getURL(); + } + unset($rs); +} catch (Exception $e) { } + +# Liste des tags -- Doit être pris en charge plus tard par le plugin ? +$tags_combo = array(); +try { + $rs = $core->meta->getMetadata(array('meta_type' => 'tag')); + $tags_combo[__('All tags')] = '-'; + while ($rs->fetch()) { + $tags_combo[$rs->meta_id] = $rs->meta_id; + } + unset($rs); +} catch (Exception $e) { } + +# Liste des types d'item de menu +$items = new ArrayObject(); +$items['home'] = new ArrayObject(array(__('Home'),false)); + +if (count($langs_combo) > 1) { + $items['lang'] = new ArrayObject(array(__('Language'),true)); +} +if (count($categories_combo)) { + $items['category'] = new ArrayObject(array(__('Category'),true)); +} +if (count($months_combo) > 1) { + $items['archive'] = new ArrayObject(array(__('Archive'),true)); +} +if ($core->plugins->moduleExists('pages')) { + if(count($pages_combo)) + $items['pages'] = new ArrayObject(array(__('Page'),true)); +} +if ($core->plugins->moduleExists('tags')) { + if (count($tags_combo) > 1) + $items['tags'] = new ArrayObject(array(__('Tags'),true)); +} + +# --BEHAVIOR-- adminSimpleMenuAddType +# Should add an item to $items[] as an array(