From 83b93f2bfeb31f962cb95736dec04b9fe1bc8096 Mon Sep 17 00:00:00 2001 From: Jean-Christian Denis Date: Mon, 23 Aug 2021 13:24:19 +0200 Subject: [PATCH] initial commit --- CHANGELOG | 45 ++ LICENSE | 339 +++++++++++ README.md | 31 + _admin.php | 395 ++++++++++++ _config.php | 110 ++++ _define.php | 34 ++ _install.php | 76 +++ _prepend.php | 44 ++ _public.php | 174 ++++++ icon-big.png | Bin 0 -> 1823 bytes icon.png | Bin 0 -> 438 bytes inc/class.periodical.php | 422 +++++++++++++ inc/lib.index.pager.php | 217 +++++++ inc/lib.periodical.socialmewriter.php | 93 +++ index.php | 845 ++++++++++++++++++++++++++ js/dates.js | 1 + js/periodsfilter.js | 44 ++ js/postsfilter.js | 44 ++ js/toggle.js | 9 + locales/en/help/help.html | 19 + locales/en/resources.php | 20 + locales/fr/help/help.html | 19 + locales/fr/main.lang.php | 179 ++++++ locales/fr/main.po | 240 ++++++++ locales/fr/resources.php | 20 + 25 files changed, 3420 insertions(+) create mode 100644 CHANGELOG create mode 100644 LICENSE create mode 100644 README.md create mode 100644 _admin.php create mode 100644 _config.php create mode 100644 _define.php create mode 100644 _install.php create mode 100644 _prepend.php create mode 100644 _public.php create mode 100644 icon-big.png create mode 100644 icon.png create mode 100644 inc/class.periodical.php create mode 100644 inc/lib.index.pager.php create mode 100644 inc/lib.periodical.socialmewriter.php create mode 100644 index.php create mode 100644 js/dates.js create mode 100644 js/periodsfilter.js create mode 100644 js/postsfilter.js create mode 100644 js/toggle.js create mode 100644 locales/en/help/help.html create mode 100644 locales/en/resources.php create mode 100644 locales/fr/help/help.html create mode 100644 locales/fr/main.lang.php create mode 100644 locales/fr/main.po create mode 100644 locales/fr/resources.php diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..717fae8 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,45 @@ +periodical 2013.11.11 +=========================================================== + * Switch to Dotclear 2.6 + +periodical 2013.06.30 +=========================================================== + * Small fix and typo + +periodical 0.5 - 2011-01-30 +=========================================================== + * Fixed install on nightly buid + * Fixed call to blog object on prepend + * Removed mesenger fonctions (this is to another plugin to do that) + * Added behavior on post update + * Added periodical to plugin soCialMe + * New year copyright + +periodical 0.4 - 2010-09-09 +=========================================================== + * Removed old Twitter functions + * Added StatusNet small functions (Identica) + * Required plugin Tac for Twitter ability + +periodical 0.3.1 - 2010-06-25 +=========================================================== + * Fixed postgreSQL compatibility + * Fixed php 5.3 compatibility on post action combo + * Fixed admin crash on non DC 2.2 + * Fixed users rights + +periodical 0.3 - 2010-06-08 +=========================================================== + * Switched to DC 2.2 + * Fixed simultaneous updates (uses flock) + * Added twitter option when posts are published + +periodical 0.2 - 2010-04-11 +=========================================================== + * Fixed some bugs + * Added DC 2.2 compatibility (new setting) + * closes #415 + +periodical 0.1 - 2010-04-05 +=========================================================== + * First lab release \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/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/README.md b/README.md new file mode 100644 index 0000000..62d91be --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# README + +## WHAT IS PERIODICAL ? + +"Periodical" is a plugin for the open-source +web publishing software called Dotclear. + +You can manage and publish automatically lists of posts +at regular periods. For exemple, you just need to add posts to a period +and they will be publish ones after the others every week. + +## REQUIREMENTS + + periodical requires: + + * admin permissions to configure plugin + * usage,contentadmin permissions to link feeds + * Dotclear 2.6 + +## USAGE + +First install periodical, manualy from a zip package or from +Dotaddict repository. (See Dotclear's documentation to know how do this) + +When enabled you have a menu in main sidebar menu called "Periodical" +under "Plugins" group, follow it to manage periods. + +When you edit a post you have a new sidebar option called "Periodical" +to link this post to a period. + +You can also add or remove period to multiple posts from posts actions page. diff --git a/_admin.php b/_admin.php new file mode 100644 index 0000000..9f2d9c5 --- /dev/null +++ b/_admin.php @@ -0,0 +1,395 @@ +blog->settings->addNamespace('periodical'); + +if ($core->blog->settings->periodical->periodical_active) { + + $_menu['Plugins']->addItem( + __('Periodical'), + 'plugin.php?p=periodical', + 'index.php?pf=periodical/icon.png', + preg_match( + '/plugin.php\?p=periodical(&.*)?$/', + $_SERVER['REQUEST_URI'] + ), + $core->auth->check('usage,contentadmin', $core->blog->id) + ); + + $core->addBehavior( + 'adminDashboardFavorites', + array('adminPeriodical', 'adminDashboardFavorites') + ); + $core->addBehavior( + 'adminPostHeaders', + array('adminPeriodical', 'adminPostHeaders') + ); + $core->addBehavior( + 'adminPostsActionsPage', + array('adminPeriodical', 'adminPostsActionsPage') + ); + $core->addBehavior( + 'adminPostFormItems', + array('adminPeriodical', 'adminPostFormItems') + ); + $core->addBehavior( + 'adminAfterPostUpdate', + array('adminPeriodical', 'adminAfterPostSave') + ); + $core->addBehavior( + 'adminAfterPostCreate', + array('adminPeriodical', 'adminAfterPostSave') + ); +} + +$core->addBehavior( + 'adminBeforePostDelete', + array('adminPeriodical', 'adminBeforePostDelete') +); + +/** + * @ingroup DC_PLUGIN_PERIODICAL + * @brief Periodical - admin methods. + * @since 2.6 + */ +class adminPeriodical +{ + public static $combo_period = null; + + /** + * Favorites. + * + * @param dcCore $core dcCore instance + * @param arrayObject $favs Array of favorites + */ + public static function adminDashboardFavorites(dcCore $core, $favs) + { + $favs->register('periodical', array( + 'title' => __('Periodical'), + 'url' => 'plugin.php?p=periodical', + 'small-icon' => 'index.php?pf=periodical/icon.png', + 'large-icon' => 'index.php?pf=periodical/icon-big.png', + 'permissions' => $core->auth->check( + 'usage,contentadmin', + $core->blog->id + ), + 'active_cb' => array( + 'adminPeriodical', + 'adminDashboardFavoritesActive' + ) + )); + } + + /** + * Favorites selection. + * + * @param string $request Requested page + * @param array $params Requested parameters + */ + public static function adminDashboardFavoritesActive($request, $params) + { + return $request == 'plugin.php' + && isset($params['p']) + && $params['p'] == 'periodical'; + } + + /** + * Add javascript for toggle + * + * @return string HTML head + */ + public static function adminPostHeaders() + { + return dcPage::jsLoad('index.php?pf=periodical/js/toggle.js'); + } + + /** + * Delete relation between post and period + * + * @param integer $post_id Post id + */ + public static function adminBeforePostDelete($post_id) + { + self::delPeriod($GLOBALS['core'], $post_id); + } + + /** + * Add actions to posts page combo + * + * @param dcCore $core dcCore instance + * @param dcPostsActionsPage $ap dcPostsActionsPage instance + */ + public static function adminPostsActionsPage(dcCore $core, dcPostsActionsPage $pa) + { + $pa->addAction( + array( + __('Periodical') => array( + __('Add to periodical') => 'periodical_add' + ) + ), + array('adminPeriodical', 'callbackAdd') + ); + + if (!$core->auth->check('delete,contentadmin', $core->blog->id)) { + + return null; + } + $pa->addAction( + array( + __('Periodical') => array( + __('Remove from periodical') => 'periodical_remove' + ) + ), + array('adminPeriodical', 'callbackRemove') + ); + } + + /** + * Posts actions callback to remove period + * + * @param dcCore $core dcCore instance + * @param dcPostsActionsPage $pa dcPostsActionsPage instance + * @param ArrayObject $post _POST actions + */ + public static function callbackRemove(dcCore $core, dcPostsActionsPage $pa, ArrayObject $post) + { + # No entry + $posts_ids = $pa->getIDs(); + if (empty($posts_ids)) { + throw new Exception(__('No entry selected')); + } + + # No right + if (!$core->auth->check('delete,contentadmin', $core->blog->id)) { + throw new Exception(__('No enough right')); + } + + # Remove linked period + foreach($posts_ids as $post_id) { + self::delPeriod($core, $post_id); + } + + dcPage::addSuccessNotice(__('Posts have been removed from periodical.')); + $pa->redirect(true); + } + + /** + * Posts actions callback to add period + * + * @param dcCore $core dcCore instance + * @param dcPostsActionsPage $pa dcPostsActionsPage instance + * @param ArrayObject $post _POST actions + */ + public static function callbackAdd(dcCore $core, dcPostsActionsPage $pa, ArrayObject $post) + { + # No entry + $posts_ids = $pa->getIDs(); + if (empty($posts_ids)) { + throw new Exception(__('No entry selected')); + } + + //todo: check if selected posts is unpublished + + # Save action + if (!empty($post['periodical'])) { + foreach($posts_ids as $post_id) { + self::delPeriod($core, $post_id); + self::addPeriod($core, $post_id, $post['periodical']); + } + + dcPage::addSuccessNotice(__('Posts have been added to periodical.')); + $pa->redirect(true); + } + + # Display form + else { + $pa->beginPage( + dcPage::breadcrumb(array( + html::escapeHTML($core->blog->name) => '', + $pa->getCallerTitle() => $pa->getRedirection(true), + __('Add a period to this selection') => '' + )) + ); + + echo + '
'. + $pa->getCheckboxes(). + + self::formPeriod($core). + + '

'. + $core->formNonce(). + $pa->getHiddenFields(). + form::hidden(array('action'), 'periodical_add'). + '

'. + '
'; + + $pa->endPage(); + } + } + + /** + * Add form to post sidebar + * + * @param ArrayObject $main_items Main items + * @param ArrayObject $sidebar_items Sidebar items + * @param record $post Post record or null + */ + public static function adminPostFormItems(ArrayObject $main_items, ArrayObject $sidebar_items, $post) + { + global $core; + + # Get existing linked period + $period = ''; + if ($post) { + $per = new periodical($core); + $rs = $per->getPosts(array('post_id' => $post->post_id)); + $period = $rs->isEmpty() ? '' : $rs->periodical_id; + } + + # Set linked period form items + $sidebar_items['options-box']['items']['period'] = + self::formPeriod($core, $period); + } + + /** + * Save linked period + * + * @param cursor $cur Current post cursor + * @param integer $post_id Post id + */ + public static function adminAfterPostSave(cursor $cur, $post_id) + { + global $core; + + if (!isset($_POST['periodical'])) { + + return null; + } + + # Delete old linked period + self::delPeriod($core, $post_id); + + # Add new linked period + self::addPeriod($core, $post_id, $_POST['periodical']); + } + + /** + * Posts period form field + * + * @param dcCore $core dcCore instance + * @param string $period Period + * @return string Period form content + */ + protected static function formPeriod(dcCore $core, $period='') + { + $combo = self::comboPeriod($core); + + if (empty($combo)) { + + return null; + } + + return + '

'. + form::combo('periodical', $combo, $period). + '

'; + } + + /** + * Combo of available periods + * + * @param dcCore $core dcCore instance + * @return array List of period + */ + protected static function comboPeriod(dcCore $core) + { + if (adminPeriodical::$combo_period === null) { + + $per = new periodical($core); + $periods = $per->getPeriods(); + + if ($periods->isEmpty()) { + + adminPeriodical::$combo_period = array(); + } + else { + $combo = array('-' => ''); + while ($periods->fetch()) { + $combo[html::escapeHTML($periods->periodical_title)] = $periods->periodical_id; + } + } + adminPeriodical::$combo_period = $combo; + } + + return adminPeriodical::$combo_period; + } + + /** + * Remove period from posts. + * + * @param dcCore $core dcCore instance + * @param integer $post_id Post id + */ + protected static function delPeriod(dcCore $core, $post_id) + { + if ($post_id === null) { + + return null; + } + + $post_id = (integer) $post_id; + $per = new periodical($core); + $per->delPost($post_id); + } + + /** + * Add period to posts + * + * @param dcCore $core dcCore instance + * @param integer $post_id Post id + * @param array $period Period + */ + protected static function addPeriod($core, $post_id, $period) + { + # Not saved + if ($post_id === null || empty($period)) { + + return null; + } + + # Period object + $per = new periodical($core); + + # Get periods + $period = $per->getPeriods(array('periodical_id' => $period)); + + # No period + if ($period->isEmpty()) { + + return null; + } + + $post_id = (integer) $post_id; + + # Add relation + $per->addPost($period->periodical_id, $post_id); + } +} diff --git a/_config.php b/_config.php new file mode 100644 index 0000000..561f9c1 --- /dev/null +++ b/_config.php @@ -0,0 +1,110 @@ +getURL().'#plugins' : $_REQUEST['redir']; + +# -- Combos -- +$sortby_combo = array( + __('Create date') => 'post_creadt', + __('Date') => 'post_dt', + __('Id') => 'post_id' +); +$order_combo = array( + __('Descending') => 'desc', + __('Ascending') => 'asc' +); + +# -- Get settings -- +$core->blog->settings->addNamespace('periodical'); +$s = $core->blog->settings->periodical; + +$s_active = (boolean) $s->periodical_active; +$s_upddate = (boolean) $s->periodical_upddate; +$s_updurl = (boolean) $s->periodical_updurl; +$e_order = (string) $s->periodical_pub_order; +$e_order = explode(' ', $e_order); +$s_sortby = in_array($e_order[0], $sortby_combo) ? $e_order[0] : 'post_dt'; +$s_order = isset($e_order[1]) && strtolower($e_order[1]) == 'desc' ? 'desc' : 'asc'; + +# -- Set settings -- +if (!empty($_POST['save'])) { + + try { + $s_active = !empty($_POST['s_active']); + $s_upddate = !empty($_POST['s_upddate']); + $s_updurl = !empty($_POST['s_updurl']); + $s_sortby = $_POST['s_sortby']; + $s_order = $_POST['s_order']; + + $s->put('periodical_active', $s_active); + $s->put('periodical_upddate', $s_upddate); + $s->put('periodical_updurl', $s_updurl); + $s->put('periodical_pub_order', $s_sortby.' '.$s_order); + + $core->blog->triggerBlog(); + + dcPage::addSuccessNotice( + __('Configuration has been successfully updated.') + ); + http::redirect( + $list->getURL('module=periodical&conf=1&redir='. + $list->getRedir()) + ); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } +} + +# -- Display form -- +echo ' + +
+

'.__('Activation').'

+ +

+ +
+ +
+

'.__('Dates of published entries').'

+ +

+ +

+ +
+ +
+

'.__('Order of publication of entries').'

+ +

'. +form::combo('s_sortby', $sortby_combo, $s_sortby).'

+ +

'. +form::combo('s_order', $order_combo, $s_order).'

+ +
'; diff --git a/_define.php b/_define.php new file mode 100644 index 0000000..edf5511 --- /dev/null +++ b/_define.php @@ -0,0 +1,34 @@ +registerModule( + /* Name */ + "Periodical", + /* Description*/ + "Published periodically entries", + /* Author */ + "Jean-Christian Denis", + /* Version */ + '2013.11.11', + /* Properies */ + array( + 'permissions' => 'usage,contentadmin', + 'type' => 'plugin', + 'dc_min' => '2.6', + 'support' => 'http://jcd.lv/q=periodical', + 'details' => 'http://plugins.dotaddict.org/dc2/details/periodical' + ) +); diff --git a/_install.php b/_install.php new file mode 100644 index 0000000..cff237d --- /dev/null +++ b/_install.php @@ -0,0 +1,76 @@ +plugins->moduleInfo('periodical', 'version'); +$old_version = $core->getVersion('periodical'); + +if (version_compare($old_version, $new_version, '>=')) { + + return null; +} + +try { + # Check Dotclear version + if (!method_exists('dcUtils', 'versionsCompare') + || dcUtils::versionsCompare(DC_VERSION, $dc_min, '<', false)) { + throw new Exception(sprintf( + '%s requires Dotclear %s', 'periodical', $dc_min + )); + } + + # Tables + $t = new dbStruct($core->con,$core->prefix); + + # Table principale des sondages + $t->periodical + ->periodical_id ('bigint', 0, false) + ->blog_id('varchar', 32, false) + ->periodical_type ('varchar', 32, false, "'post'") + ->periodical_title ('varchar', 255, false, "''") + ->periodical_tz ('varchar', 128, false, "'UTC'") + ->periodical_curdt ('timestamp', 0, false,' now()') + ->periodical_enddt ('timestamp', 0, false, 'now()') + ->periodical_pub_int ('varchar', 32, false, "'day'") + ->periodical_pub_nb ('smallint', 0, false, 1) + + ->primary('pk_periodical', 'periodical_id') + ->index('idx_periodical_type', 'btree', 'periodical_type'); + + $ti = new dbStruct($core->con, $core->prefix); + $changes = $ti->synchronize($t); + + # Settings + $core->blog->settings->addNamespace('periodical'); + $s = $core->blog->settings->periodical; + $s->put('periodical_active', false, 'boolean', 'Enable extension', false, true); + $s->put('periodical_upddate', true, 'boolean', 'Update post date', false, true); + $s->put('periodical_updurl', false, 'boolean', 'Update post url', false, true); + $s->put('periodical_pub_order', 'post_dt asc', 'string', 'Order of publication', false, true); + + # Version + $core->setVersion('periodical', $new_version); + + return true; +} +catch (Exception $e) { + $core->error->add($e->getMessage()); +} + +return false; diff --git a/_prepend.php b/_prepend.php new file mode 100644 index 0000000..b61777e --- /dev/null +++ b/_prepend.php @@ -0,0 +1,44 @@ +addBehavior( + 'soCialMeWriterMarker', + array('periodicalSoCialMeWriter', 'soCialMeWriterMarker') +); +$core->addBehavior( + 'periodicalAfterPublishedPeriodicalEntry', + array('periodicalSoCialMeWriter', 'periodicalAfterPublishedPeriodicalEntry') +); diff --git a/_public.php b/_public.php new file mode 100644 index 0000000..1c04cfe --- /dev/null +++ b/_public.php @@ -0,0 +1,174 @@ +url->type, array('default', 'feed'))) { + + return null; +} + +$core->blog->settings->addNamespace('periodical'); + +$core->addBehavior( + 'publicBeforeDocument', + array('publicPeriodical', 'publicBeforeDocument') +); + +/** + * @ingroup DC_PLUGIN_PERIODICAL + * @brief Periodical - public methods. + * @since 2.6 + */ +class publicPeriodical +{ + /** + * Publish periodical + * + * @param dcCore $core dcCore instance + */ + public static function publicBeforeDocument(dcCore $core) + { + try { + $per = new periodical($core); + $s = $core->blog->settings->periodical; + + $per->lockUpdate(); + + # Get periods + $periods = $core->auth->sudo(array($per, 'getPeriods')); + + # No period + if ($periods->isEmpty()) { + $per->unlockUpdate(); + + return null; + } + + $now = dt::toUTC(time()); + $posts_order = $s->periodical_pub_order; + if (!preg_match('/^(post_dt|post_creadt|post_id) (asc|desc)$/', $posts_order)) { + $posts_order = 'post_dt asc'; + } + $cur_period = $core->con->openCursor($core->prefix.'periodical'); + + while($periods->fetch()) { + + # Check if period is ongoing + $cur_tz = strtotime($periods->periodical_curdt); + $end_tz = strtotime($periods->periodical_enddt); + $now_tz = $now + dt::getTimeOffset($periods->periodical_tz, $now); + + if ($now_tz > $cur_tz && $now_tz < $end_tz) { + + $last_nb = 0; + $last_tz = $cur_tz; + + $max_nb = $periods->periodical_pub_nb; + $max_tz = $end_tz < $now_tz ? $end_tz : $now_tz; + + # Calculate nb of posts to get + $loop_tz = $cur_tz; + $limit = 0; + try { + while(1) { + if ($loop_tz > $max_tz) { + break; + } + $loop_tz = $per->getNextTime($loop_tz, $periods->periodical_pub_int); + $limit += 1; + } + } + catch (Exception $e) { } + + # If period need update + if ($limit > 0) { + + # Get posts to publish related to this period + $posts_params = array(); + $posts_params['periodical_id'] = $periods->periodical_id; + $posts_params['post_status'] = '-2'; + $posts_params['order'] = $posts_order; + $posts_params['limit'] = $limit * $max_nb; + $posts_params['no_content'] = true; + $posts = $core->auth->sudo(array($per, 'getPosts'), $posts_params); + + if (!$posts->isEmpty()) { + $cur_post = $core->con->openCursor($core->prefix.'post'); + + while($posts->fetch()) { + + # Publish post with right date + $cur_post->clean(); + $cur_post->post_status = 1; + + # Update post date with right date + if ($s->periodical_upddate) { + $cur_post->post_dt = date('Y-m-d H:i:s', $last_tz); + $cur_post->post_tz = $periods->periodical_tz; + } + else { + $cur_post->post_dt = $posts->post_dt; + } + + # Also update post url with right date + if ($s->periodical_updurl) { + $cur_post->post_url = $core->blog->getPostURL('', $cur_post->post_dt, $posts->post_title, $posts->post_id); + } + + $cur_post->update( + 'WHERE post_id = '.$posts->post_id.' '. + "AND blog_id = '".$core->con->escape($core->blog->id)."' " + ); + + # Delete post relation to this period + $per->delPost($posts->post_id); + + $last_nb++; + + # Increment upddt if nb of publishing is to the max + if ($last_nb == $max_nb) { + $last_tz = $per->getNextTime($last_tz, $periods->periodical_pub_int); + $last_nb = 0; + } + + # --BEHAVIOR-- periodicalAfterPublishedPeriodicalEntry + $core->callBehavior('periodicalAfterPublishedPeriodicalEntry', $core, $posts, $periods); + + } + $core->blog->triggerBlog(); + } + } + + # Update last published date of this period even if there's no post to publish + $cur_period->clean(); + $cur_period->periodical_curdt = date('Y-m-d H:i:s', $loop_tz); + $cur_period->update( + 'WHERE periodical_id = '.$periods->periodical_id.' '. + "AND blog_id = '".$core->con->escape($core->blog->id)."' " + ); + } + } + $per->unlockUpdate(); + } + catch (Exception $e) { + $per->unlockUpdate(); + + return null; + } + } +} diff --git a/icon-big.png b/icon-big.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb5527e6353e76a3f494a4431cccd8db925a37f GIT binary patch literal 1823 zcmZuydpHwp8=s3DH$!up#V$Tk7;Bpx%P=ByrZ*8wY8!?rF}$tTkcwWCe3*uF4K!pEOsBKXEz{08ka| zhRi@*&3S1NdI|T-rGU5#m#z?~Q2<{8iE;_yjSGY)p%54Z66wC?d=vnX&+%}@1}2|f znLm|<4ubZ79iHi_y!sF0(C+y$mYm`CXvNF zI7ft?fyg#0$1!sSudWqs@c&1$7@`-@8Wti8^naB+Daw^GcAtH!g+H){WQAwGIhm99 z;Z)NFs}^h3(o73lj7HZ}a?vn=*)T?(NgDPXG_GDCKaT>i(Q!~u16rQoGcC5Sl`tKV zu$5}lI^ajBX4QZ)byqVv={62KMzB*#9jYqSdgOBel-HfO8Eu-r>=8$sj5Y%vI}{3| zD7G=rFcMdUgl5*Jm8`X*LdKe4M4aO}_#SQ21{DBVzXg|&rWMv79(DZGqN$;?ytuTr z>Y03K$D|C}-WsfPzGu4^@)j+$yz%ptGs5a-(54Ap*J<2Z>cdkZ) zyAxkTEtR_<>5M+WhsASr+=uXM6WRA2ZsB&tDs@;%NLHxAo}ffhIpE`B_7r?vPWI|2 zF=k@4jP9ZsyO+e&#!x-jJZ-X4vo!_+ALGYdj@Z}p3hwV0OVnK%;l&Q}SwL!_XwR>Ia z-QkXVr6&5tjb}GvMNbz;_oenME~dqlba*8d)7&_4+ix9|A=Up!bBNJut%z2G6b^Q* zO+_%fP1Fc+rkhS2Q5r;%-62t-4zV--dyCwR*l6&)5_=gMcsGk4$9P9^vgvCd^X1GY zk2t>Z5>s-8HM?PXE?25jV|3G}L*YL?DNwmO?4hZWfp+gdGveUA!WccFBOVJf@2t zMIh~@o-9ke%!c&J&;_uIRRtvLXUkYi8u>+D@V%qvhF|2SryIM|6^qZWv$P#Q#@5Kd zs6V=nx7WYjo-^TixPfWH{MSR~L>yA|Dn30veAKuqw>||8@4E`}mx5B`=6(uq#6lxf0Z{OJ^8J{_)-%_Uo@91p! zS2LLZ@BV@&GsWRirrSzL5YY5`+R!KMfKA+qFOu;7J4C0wBJ(y$nKTZOyPB(E?9L9E z0-6GqQ(0MmCEnv%lBYv7WXTvQ)=}u!t6-i9-TNmYm_iQLuk+ zvun_n{tNW4%%#`FoCC%s^V2m!uK+^J+cl51w+9gD?Y8h3c9RY~o3o^^9u}#s5jJmG z^>PNNb_3!GgxbH+;eM}asnhLAk1WA;T2=j7>&H*RLR(HjR`bx|=<`dTg0w1;cP zUt%H)El6&k&w=n#-S`{|eq*nyftdE6r3WABwtsP}gz!{YO!U;bvWzCRic2 zFl7QxDL#@@x@Xr!sraVIz~YR!`w&G*-woZq#hvPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qRNAp5A0003cNklHE7}N_f(Rm(Ha>v%*7^z-f{l3j|1rDFX4A}pFFQH6gs>rr z0;?E{ckN-#9(xDEM2Q56-ZFMZDp+;T6_puQSfZiwz%m#0z|4bAdT)_SkE#zu*(^nVg9W z4$;L#CTEFlq6U5Mh+~_aZ!(E-o;mi)g gRDEz;4rapSA3G0d5-N^|yZ`_I07*qoM6N<$g5YJff&c&j literal 0 HcmV?d00001 diff --git a/inc/class.periodical.php b/inc/class.periodical.php new file mode 100644 index 0000000..20e8835 --- /dev/null +++ b/inc/class.periodical.php @@ -0,0 +1,422 @@ +core = $core; + $this->con = $core->con; + + $this->table = $core->con->escape($core->prefix.'periodical'); + $this->blog = $core->con->escape($core->blog->id); + } + + public function openCursor() + { + return $this->con->openCursor($this->table); + } + + # Get periods + public function getPeriods($params=array(),$count_only=false) + { + if ($count_only) { + $q = 'SELECT count(T.periodical_id) '; + } + else + { + $q = 'SELECT T.periodical_id, T.periodical_type, '; + + if (!empty($params['columns']) && is_array($params['columns'])) { + $q .= implode(', ',$params['columns']).', '; + } + $q .= + 'T.periodical_title, T.periodical_tz, '. + 'T.periodical_curdt, T.periodical_enddt, '. + 'T.periodical_pub_int, T.periodical_pub_nb '; + } + + $q .= 'FROM '.$this->table.' T '; + + if (!empty($params['from'])) { + $q .= $params['from'].' '; + } + $q .= "WHERE T.blog_id = '".$this->blog."' "; + + if (isset($params['periodical_type'])) { + if (is_array($params['periodical_type']) && !empty($params['periodical_type'])) { + $q .= 'AND T.periodical_type '.$this->con->in($params['periodical_type']); + } + elseif ($params['periodical_type'] != '') { + $q .= "AND T.periodical_type = '".$this->con->escape($params['periodical_type'])."' "; + } + } + else { + $q .= "AND T.periodical_type = 'post' "; + } + if (!empty($params['periodical_id'])) { + if (is_array($params['periodical_id'])) { + array_walk($params['periodical_id'],create_function('&$v,$k','if($v!==null){$v=(integer)$v;}')); + } + else { + $params['periodical_id'] = array((integer) $params['periodical_id']); + } + $q .= 'AND T.periodical_id '.$this->con->in($params['periodical_id']); + } + if (!empty($params['periodical_title'])) { + $q .= "AND T.periodical_title = '".$this->con->escape($params['periodical_title'])."' "; + } + if (!empty($params['sql'])) { + $q .= $params['sql'].' '; + } + if (!$count_only) { + if (!empty($params['order'])) { + $q .= 'ORDER BY '.$this->con->escape($params['order']).' '; + } + else { + $q .= 'ORDER BY T.periodical_id ASC '; + } + } + if (!$count_only && !empty($params['limit'])) { + $q .= $this->con->limit($params['limit']); + } + $rs = $this->con->select($q); + $rs->core = $this->core; + $rs->periodical = $this; + + return $rs; + } + + public function addPeriod($cur) + { + $this->con->writeLock($this->table); + + try + { + $id = $this->con->select( + 'SELECT MAX(periodical_id) FROM '.$this->table + )->f(0) + 1; + + $cur->periodical_id = $id; + $cur->blog_id = $this->blog; + $cur->periodical_type = 'post'; + $cur->periodical_tz = $this->core->auth->getInfo('user_tz'); + $cur->insert(); + $this->con->unlock(); + } + catch (Exception $e) + { + $this->con->unlock(); + throw $e; + } + return $cur->periodical_id; + } + + public function updPeriod($period_id,$cur) + { + $period_id = (integer) $period_id; + + if ($cur->periodical_tz == '' + && ($cur->periodical_curdt != '' || $cur->periodical_enddt != '')) { + $cur->periodical_tz = $this->core->auth->getInfo('user_tz'); + } + $cur->update( + "WHERE blog_id = '".$this->blog."' ". + "AND periodical_id = ".$period_id." " + ); + } + + # Delete a period + public function delPeriod($period_id) + { + $period_id = (integer) $period_id; + + $params = array(); + $params['periodical_id'] = $period_id; + $params['post_status'] = ''; + $rs = $this->getPosts($params); + + if (!$rs->isEmpty()) { + throw new Exception('Periodical is not empty'); + } + + $this->con->execute( + 'DELETE FROM '.$this->table.' '. + "WHERE blog_id = '".$this->blog."' ". + "AND periodical_id = ".$period_id." " + ); + } + + # Remove all posts related to a period + public function delPeriodPosts($period_id) + { + $params = array(); + $params['post_status'] = ''; + $params['periodical_id'] = (integer) $period_id; + + $rs = $this->getPosts($params); + + if ($rs->isEmpty()) return; + + $ids = array(); + while($rs->fetch()) + { + $ids[] = $rs->post_id; + } + + if (empty($ids)) return; + + $this->con->execute( + 'DELETE FROM '.$this->core->prefix.'meta '. + "WHERE meta_type = 'periodical' ". + "AND post_id ".$this->con->in($ids) + ); + } + + # Get posts related to periods + public function getPosts($params=array(),$count_only=false) + { + if (!isset($params['columns'])) $params['columns'] = array(); + if (!isset($params['from'])) $params['from'] = ''; + if (!isset($params['sql'])) $params['sql'] = ''; + + $params['columns'][] = 'T.periodical_id'; + $params['columns'][] = 'T.periodical_title'; + $params['columns'][] = 'T.periodical_type'; + $params['columns'][] = 'T.periodical_tz'; + $params['columns'][] = 'T.periodical_curdt'; + $params['columns'][] = 'T.periodical_enddt'; + $params['columns'][] = 'T.periodical_pub_int'; + $params['columns'][] = 'T.periodical_pub_nb'; + + $params['from'] .= 'LEFT JOIN '.$this->core->prefix.'meta R ON P.post_id = R.post_id '; + $params['from'] .= 'LEFT JOIN '.$this->table.' T ON CAST(T.periodical_id as char)=R.meta_id '; + + $params['sql'] .= "AND R.meta_type = 'periodical' "; + $params['sql'] .= "AND T.periodical_type = 'post' "; + + if (!empty($params['periodical_id'])) { + if (is_array($params['periodical_id'])) { + array_walk($params['periodical_id'],create_function('&$v,$k','if($v!==null){$v=(integer)$v;}')); + } + else { + $params['periodical_id'] = array((integer) $params['periodical_id']); + } + $params['sql'] .= 'AND T.periodical_id '.$this->con->in($params['periodical_id']); + unset($params['periodical_id']); + } + if ($this->core->auth->check('admin',$this->core->blog->id)) { + if (isset($params['post_status'])) { + if ($params['post_status'] != '') { + $params['sql'] .= 'AND P.post_status = '.(integer) $params['post_status'].' '; + } + unset($params['post_status']); + } + } + else { + $params['sql'] .= 'AND P.post_status = -2 '; + } + + $rs = $this->core->blog->getPosts($params,$count_only); + $rs->periodical = $this; + + return $rs; + } + + # Add post to periodical + public function addPost($period_id,$post_id) + { + $period_id = (integer) $period_id; + $post_id = (integer) $post_id; + + # Check if exists + $rs = $this->getPosts(array('post_id' => $post_id,'periodical_id' => $period_id)); + if (!$rs->isEmpty()) return; + + $cur = $this->con->openCursor($this->core->prefix.'meta'); + $this->con->writeLock($this->core->prefix.'meta'); + + try + { + $cur->post_id = $post_id; + $cur->meta_id = $period_id; + $cur->meta_type = 'periodical'; + $cur->insert(); + $this->con->unlock(); + } + catch (Exception $e) + { + $this->con->unlock(); + throw $e; + } + } + + # Delete post from periodical + public function delPost($post_id) + { + $post_id = (integer) $post_id; + + $this->con->execute( + 'DELETE FROM '.$this->core->prefix.'meta '. + "WHERE meta_type = 'periodical' ". + "AND post_id = '".$post_id."' " + ); + return true; + } + + # Remove all posts without pending status from periodical + public function cleanPosts($period_id=null) + { + $params = array(); + $params['post_status'] = ''; + $params['sql'] = 'AND post_status != -2 '; + if ($period_id !== null) { + $params['periodical_id'] = (integer) $period_id; + } + $rs = $this->getPosts($params); + + if ($rs->isEmpty()) return; + + $ids = array(); + while($rs->fetch()) + { + $ids[] = $rs->post_id; + } + + if (empty($ids)) return; + + $this->con->execute( + 'DELETE FROM '.$this->core->prefix.'meta '. + "WHERE meta_type = 'periodical' ". + "AND post_id ".$this->con->in($ids) + ); + } + + public static function getTimesCombo() + { + return array( + __('Hourly') => 'hour', + __('twice a day') => 'halfday', + __('Daily') => 'day', + __('Weekly') => 'week', + __('Monthly') => 'month' + ); + } + + public static function getNextTime($ts,$period) + { + $ts = (integer) $ts; + $e = explode(',',date('H,i,s,n,j,Y',$ts)); + switch($period) + { + case 'hour': + $new_ts = mktime($e[0] + 1,$e[1],$e[2],$e[3],$e[4],$e[5]); + break; + + case 'halfday': + $new_ts = mktime($e[0],$e[1] + 12,$e[2],$e[3],$e[4],$e[5]); + break; + + case 'day': + $new_ts = mktime($e[0],$e[1],$e[2],$e[3],$e[4] + 1,$e[5]); + break; + + case 'week': + $new_ts = mktime($e[0],$e[1],$e[2],$e[3],$e[4] + 7,$e[5]); + break; + + case 'month': + $new_ts = mktime($e[0],$e[1],$e[2],$e[3] + 1,$e[4],$e[5]); + break; + + default: + $new_ts = 0; + throw new Exception(__('Unknow frequence')); + break; + } + return $new_ts; + } + + # Lock a file to see if an update is ongoing + public function lockUpdate() + { + try + { + # Need flock function + if (!function_exists('flock')) { + throw New Exception("Can't call php function named flock"); + } + # Cache writable ? + if (!is_writable(DC_TPL_CACHE)) { + throw new Exception("Can't write in cache fodler"); + } + # Set file path + $f_md5 = md5($this->blog); + $cached_file = sprintf('%s/%s/%s/%s/%s.txt', + DC_TPL_CACHE, + 'periodical', + substr($f_md5,0,2), + substr($f_md5,2,2), + $f_md5 + ); + # Real path + $cached_file = path::real($cached_file,false); + # Make dir + if (!is_dir(dirname($cached_file))) { + + files::makeDir(dirname($cached_file),true); + } + # Make file + if (!file_exists($cached_file)) { + !$fp = @fopen($cached_file, 'w'); + if ($fp === false) { + throw New Exception("Can't create file"); + } + fwrite($fp,'1',strlen('1')); + fclose($fp); + } + # Open file + if (!($fp = @fopen($cached_file, 'r+'))) { + throw New Exception("Can't open file"); + } + # Lock file + if (!flock($fp,LOCK_EX)) { + throw New Exception("Can't lock file"); + } + $this->lock = $fp; + return true; + } + catch (Exception $e) + { + throw $e; + } + return false; + } + + public function unlockUpdate() + { + @fclose($this->lock); + $this->lock = null; + } +} +?> \ No newline at end of file diff --git a/inc/lib.index.pager.php b/inc/lib.index.pager.php new file mode 100644 index 0000000..46f96eb --- /dev/null +++ b/inc/lib.index.pager.php @@ -0,0 +1,217 @@ +rs->isEmpty()) { + $echo .= '

'.__('No period').'

'; + } + else { + $pager = new dcPager($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
'.__('Name').''.__('Next update').''.__('Frequency').''.__('Publications').''.__('Entries').''.__('End date').'
'. + '
'; + + if ($enclose_block) { + $html_block = sprintf($enclose_block, $html_block); + } + + $echo .= $pager->getLinks(); + + $blocks = explode('%s', $html_block); + + $echo .= $blocks[0]; + + while ($this->rs->fetch()) { + $echo .= $this->periodLine(); + } + + $echo .= $blocks[1]; + + $echo .= $pager->getLinks(); + } + + return $echo; + } + + private function periodLine() + { + $nb_posts = $this->rs->periodical->getPosts(array('periodical_id' => $this->rs->periodical_id), true); + $nb_posts = $nb_posts->f(0); + $style = !$nb_posts ? ' offline' : ''; + $posts_links = !$nb_posts ? + '0' : + ''.$nb_posts.''; + + $pub_int = in_array($this->rs->periodical_pub_int, $this->rs->periodical->getTimesCombo()) ? + __(array_search($this->rs->periodical_pub_int, $this->rs->periodical->getTimesCombo())) : __('Unknow frequence'); + + $res = + ''. + ''.form::checkbox(array('periods[]'), $this->rs->periodical_id).''. + ''.html::escapeHTML($this->rs->periodical_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->periodical_curdt).''. + ''.$pub_int.''. + ''.$this->rs->periodical_pub_nb.''. + ''.$posts_links.''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->periodical_enddt).''. + ''; + + return $res; + } + + public function postDisplay($page, $nb_per_page, $base_url, $enclose_block='') + { + $echo = ''; + if ($this->rs->isEmpty()) { + $echo .= '

'.__('No entry').'

'; + } + else { + $pager = new dcPager($page, $this->rs_count, $nb_per_page,10); + $pager->html_prev = $this->html_prev; + $pager->html_next = $this->html_next; + $pager->base_url = $base_url; + $pager->var_page = 'page'; + + $html_block = + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '%s
'.__('Title').''.__('Date').''.__('Category').''.__('Author').''.__('Status').''.__('Create date').'
'; + + if ($enclose_block) { + $html_block = sprintf($enclose_block, $html_block); + } + + $echo .= $pager->getLinks(); + + $blocks = explode('%s', $html_block); + + $echo .= $blocks[0]; + + while ($this->rs->fetch()) { + $echo .= $this->postLine(); + } + + $echo .= $blocks[1]; + + $echo .= $pager->getLinks(); + } + + return $echo; + } + + 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 = + ''. + ''.form::checkbox(array('periodical_entries[]'), $this->rs->post_id,0).''. + ''. + html::escapeHTML($this->rs->post_title).''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->post_dt).''. + ''.$cat_title.''. + ''.$this->rs->user_id.''. + ''.$img_status.' '.$selected.' '.$protected.' '.$attach.''. + ''.dt::dt2str(__('%Y-%m-%d %H:%M'), $this->rs->post_creadt, $this->rs->core->auth->getInfo('user_tz')).''. + ''; + + return $res; + } +} diff --git a/inc/lib.periodical.socialmewriter.php b/inc/lib.periodical.socialmewriter.php new file mode 100644 index 0000000..c6ea0ae --- /dev/null +++ b/inc/lib.periodical.socialmewriter.php @@ -0,0 +1,93 @@ + __('New periodical publication'), + 'description' => __('When an entry is published on a period'), + 'action' => array('Message','Link'), + 'format' => array('Message'), + 'wildcards' => array('Message' => array('%posttitle%','%posturl%','%shortposturl%','%postauthor%','%sitetitle%','%siteurl%','%shortsiteurl%')) + ); + } + + public static function periodicalAfterPublishedPeriodicalEntry($core,$post,$period) + { + $key = 'periodicalcreate'; + + # Is install + if (!$core->plugins->moduleExists('soCialMe')) return; + + # Is active + if (!$core->blog->settings->soCialMeWriter->active) return; + + # Load services + $soCialMeWriter = new soCialMeWriter($core); + + # List of service per action + $actions = $soCialMeWriter->getMarker('action'); + + # List of format per type + $formats = $soCialMeWriter->getMarker('format'); + + # prepare data + $shortposturl = soCialMeWriter::reduceURL($post->getURL()); + $shortposturl = $shortposturl ? $shortposturl : $post->getURL(); + + $shortsiteurl = soCialMeWriter::reduceURL($core->blog->url); + $shortsiteurl = $shortsiteurl ? $shortsiteurl : $core->blog->url; + + # sendMessage + if (!empty($formats[$key]['Message']) && !empty($actions[$key]['Message'])) + { + // parse message + $message_txt = str_replace( + array('%posttitle%','%posturl%','%shortposturl%','%postauthor%','%sitetitle%','%siteurl%','%shortsiteurl%'), + array($post->post_title,$post->getURL(),$shortposturl,$post->getUserCN(),$core->blog->name,$core->blog->url,$shortsiteurl), + $formats[$key]['Message'] + ); + + // send message + if (!empty($message_txt)) + { + foreach($actions[$key]['Message'] as $service_id) + { + $soCialMeWriter->play($service_id,'Message','Content',$message_txt); + } + } + } + + # sendLink + if (!empty($actions[$key]['Link'])) + { + foreach($actions[$key]['Link'] as $service_id) + { + $soCialMeWriter->play($service_id,'Link','Content',$post->post_title,$shortposturl); + } + } + + # sendData + // not yet implemented + + #sendArticle + // not yet implemented + } +} +?> \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..cc7f7b0 --- /dev/null +++ b/index.php @@ -0,0 +1,845 @@ +blog->settings->periodical; +$per = new periodical($core); + +# Default values +$action = isset($_POST['action']) ? $_POST['action'] : ''; +$part = isset($_REQUEST['part']) && $_REQUEST['part'] == 'period' ? 'period' : 'periods'; + + +############################################################ +# +# One period +# +############################################################ + +if ($part == 'period') { + + $starting_script = ''; + + # Default value for period + $period_id = null; + $period_title = __('One post per day'); + $period_pub_nb = 1; + $period_pub_int = 'day'; + $period_curdt = date('Y-m-d H:i:00', time()); + $period_enddt = date('Y-m-d H:i:00', time() + 31536000); //one year + + # Get period + if (!empty($_REQUEST['period_id'])) { + $rs = $per->getPeriods(array( + 'periodical_id' => $_REQUEST['period_id'] + )); + if ($rs->isEmpty()) { + $core->error->add(__('This period does not exist.')); + $period_id = null; + } + else { + $period_id = $rs->periodical_id; + $period_title = $rs->periodical_title; + $period_pub_nb = $rs->periodical_pub_nb; + $period_pub_int = $rs->periodical_pub_int; + $period_curdt = date('Y-m-d H:i', strtotime($rs->periodical_curdt)); + $period_enddt = date('Y-m-d H:i', strtotime($rs->periodical_enddt)); + + //todo load related posts + } + } + + # Set period + if ($action == 'setperiod') { + + # Get POST values + if (!empty($_POST['period_title'])) { + $period_title = $_POST['period_title']; + } + if (!empty($_POST['period_pub_nb'])) { + $period_pub_nb = abs((integer) $_POST['period_pub_nb']); + } + if (!empty($_POST['period_pub_int']) + && in_array($_POST['period_pub_int'], $per->getTimesCombo()) + ) { + $period_pub_int = $_POST['period_pub_int']; + } + if (!empty($_POST['period_curdt'])) { + $period_curdt = date('Y-m-d H:i:00', strtotime($_POST['period_curdt'])); + } + if (!empty($_POST['period_enddt'])) { + $period_enddt = date('Y-m-d H:i:00', strtotime($_POST['period_enddt'])); + } + + # Check period title and dates + $old_titles = $per->getPeriods(array( + 'periodical_title' => $period_title + )); + if (!$old_titles->isEmpty()) { + while($old_titles->fetch()) { + if (!$period_id || $old_titles->periodical_id != $period_id) { + $core->error->add(__('Period title is already taken')); + } + } + } + if (empty($period_title)) { + $core->error->add(__('Period title is required')); + } + if (strtotime($period_curdt) > strtotime($period_enddt)) { + $core->error->add(__('Start date must be older than end date')); + } + + # If no error, set period + if (!$core->error->flag()) { + + $cur = $per->openCursor(); + $cur->periodical_title = $period_title; + $cur->periodical_curdt = $period_curdt; + $cur->periodical_enddt = $period_enddt; + $cur->periodical_pub_int = $period_pub_int; + $cur->periodical_pub_nb = $period_pub_nb; + + # Update period + if ($period_id) { + + $per->updPeriod($period_id, $cur); + + dcPage::addSuccessNotice( + __('Period successfully updated.') + ); + } + # Create period + else { + + $period_id = $per->addPeriod($cur); + + dcPage::addSuccessNotice( + __('Period successfully created.') + ); + } + + http::redirect(empty($_POST['redir']) ? + $p_url.'&part=period&period_id='.$period_id.'#period' : + $_POST['redir'] + ); + } + } + + # Actions on related posts + if (!$core->error->flag() && $period_id && $action && !empty($_POST['periodical_entries'])) { + + # Publish posts + if ($action == 'publish') { + try { + foreach($_POST['periodical_entries'] as $id) { + $id = (integer) $id; + $core->blog->updPostStatus($id, 1); + $per->delPost($id); + } + + dcPage::addSuccessNotice( + __('Entries successfully published.') + ); + + http::redirect(empty($_POST['redir']) ? + $p_url.'&part=period&period_id='.$period_id.'#posts' : + $_POST['redir'] + ); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Unpublish posts + if ($action == 'unpublish') { + try { + foreach($_POST['periodical_entries'] as $id) { + $id = (integer) $id; + $core->blog->updPostStatus($id,0); + $per->delPost($id); + } + + dcPage::addSuccessNotice( + __('Entries successfully unpublished.') + ); + + http::redirect(empty($_POST['redir']) ? + $p_url.'&part=period&period_id='.$period_id.'#posts' : + $_POST['redir'] + ); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Remove posts from periodical + if ($action == 'remove_post_periodical') { + try { + foreach($_POST['periodical_entries'] as $id) { + $id = (integer) $id; + $per->delPost($id); + } + + dcPage::addSuccessNotice( + __('Entries successfully removed.') + ); + + http::redirect(empty($_POST['redir']) ? + $p_url.'&part=period&period_id='.$period_id.'#posts' : + $_POST['redir'] + ); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + } + + # Prepare combos for posts list + if ($period_id) { + + try { + # Getting categories + $categories = $core->blog->getCategories(array('post_type' => 'post')); + + # Getting authors + $users = $core->blog->getPostsUsers(); + + # Getting dates + $dates = $core->blog->getDates(array('type' => 'month')); + + # Getting langs + $langs = $core->blog->getLangs(); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Creating filter combo boxes + if ($period_id && !$core->error->flag()) { + + # Users combo + $users_combo = array_merge( + array('-' => ''), + dcAdminCombos::getUsersCombo($users) + ); + + # Categories combo + $categories_combo = array_merge( + array( + new formSelectOption('-', ''), + new formSelectOption(__('(No cat)'), 'NULL')), + dcAdminCombos::getCategoriesCombo($categories, false) + ); + $categories_values = array(); + foreach ($categories_combo as $cat) { + if (isset($cat->value)) { + $categories_values[$cat->value] = true; + } + } + + # Status combo + $status_combo = array_merge( + array('-' => ''), + dcAdminCombos::getPostStatusesCombo() + ); + + # Selection combo + $selected_combo = array( + '-' => '', + __('Selected') => '1', + __('Not selected') => '0' + ); + + # Attachments combo + $attachment_combo = array( + '-' => '', + __('With attachments') => '1', + __('Without attachments') => '0' + ); + + # Months combo + $dt_m_combo = array_merge( + array('-' => ''), + dcAdminCombos::getDatesCombo($dates) + ); + + # Langs combo + $lang_combo = array_merge( + array('-' => ''), + dcAdminCombos::getLangsCombo($langs, false) + ); + + # Sort_by combo + $sortby_combo = array( + __('Date') => 'post_dt', + __('Title') => 'post_title', + __('Category') => 'cat_title', + __('Author') => 'user_id', + __('Status') => 'post_status', + __('Selected') => 'post_selected', + __('Number of comments') => 'nb_comment', + __('Number of trackbacks') => 'nb_trackback' + ); + + # order combo + $order_combo = array( + __('Descending') => 'desc', + __('Ascending') => 'asc' + ); + + # parse filters + $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'] : ''; + $attachment = isset($_GET['attachment']) ? $_GET['attachment'] : ''; + $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']) ? max(1, (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; + $params['periodical_id'] = $period_id; + + # - User filter + if ($user_id !== '' && in_array($user_id, $users_combo)) { + $params['user_id'] = $user_id; + $show_filters = true; + } + else { + $user_id=''; + } + + # - Categories filter + if ($cat_id !== '' && isset($categories_values[$cat_id])) { + $params['cat_id'] = $cat_id; + $show_filters = true; + } + else { + $cat_id=''; + } + + # - Status filter + if ($status !== '' && in_array($status, $status_combo)) { + $params['post_status'] = $status; + $show_filters = true; + } + else { + $status=''; + } + + # - Selected filter + if ($selected !== '' && in_array($selected, $selected_combo)) { + $params['post_selected'] = $selected; + $show_filters = true; + } + else { + $selected=''; + } + + # - Selected filter + if ($attachment !== '' && in_array($attachment, $attachment_combo)) { + $params['media'] = $attachment; + $params['link_type'] = 'attachment'; + $show_filters = true; + } + else { + $attachment=''; + } + + # - Month filter + if ($month !== '' && in_array($month, $dt_m_combo)) { + $params['post_month'] = substr($month, 4, 2); + $params['post_year'] = substr($month, 0, 4); + $show_filters = true; + } + else { + $month=''; + } + + # - Lang filter + if ($lang !== '' && in_array($lang, $lang_combo)) { + $params['post_lang'] = $lang; + $show_filters = true; + } + else { + $lang=''; + } + + # - 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 = $per->getPosts($params); + $counter = $per->getPosts($params, true); + $post_list = new adminPeriodicalList($core, $posts, $counter->f(0)); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + $starting_script = + dcPage::jsLoad( + 'index.php?pf=periodical/js/postsfilter.js' + ). + '\n"; + } + + # Display + echo ' + '.__('Periodical').''. + dcPage::jsLoad('index.php?pf=periodical/js/dates.js'). + $starting_script. + dcPage::jsDatePicker(). + dcPage::jsPageTabs(). + ' + '; + + echo + dcPage::breadcrumb( + array( + html::escapeHTML($core->blog->name) => '', + __('Periodical') => $p_url.'&part=periods', + (null === $period_id ? __('New period') : __('Edit period')) => '' + ) + ). + dcPage::notices(); + + # Period form + echo ' +
+
+ +

'. + form::field('period_title', 60, 255, html::escapeHTML($period_title), 'maximal').'

+ +
+ +

'. + form::field('period_curdt', 16, 16, date('Y-m-d H:i', strtotime($period_curdt))).'

+ +

'. + form::field('period_enddt', 16, 16, date('Y-m-d H:i', strtotime($period_enddt))).'

+ +
+ +

'. + form::combo('period_pub_int',$per->getTimesCombo(),$period_pub_int).'

+ +

'. + form::field('period_pub_nb', 10, 3, html::escapeHTML($period_pub_nb)).'

+ +
+ +
+

'. + $core->formNonce(). + form::hidden(array('action'), 'setperiod'). + form::hidden(array('period_id'), $period_id). + form::hidden(array('part'), 'period').' +

+
+
+
'; + + if ($period_id && !$core->error->flag()) { + + # Actions combo box + $combo_action = array(); + $combo_action[__('Entries')][__('Publish')] = 'publish'; + $combo_action[__('Entries')][__('Unpublish')] = 'unpublish'; + $combo_action[__('Periodical')][__('Remove from periodical')] = 'remove_post_periodical'; + + $base_url = $p_url. + '&period_id='.$period_id. + '&part=period'. + '&user_id='.$user_id. + '&cat_id='.$cat_id. + '&status='.$status. + '&selected='.$selected. + '&attachment='.$attachment. + '&month='.$month. + '&lang='.$lang. + '&sortby='.$sortby. + '&order='.$order. + '&nb='.$nb_per_page. + '&page=%s'. + '#posts'; + + echo ' +
'; + + # Filters + echo + '
'. + + '

'. + __('Cancel filters and display options'). + '

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

'.__('Filters').'

'. + '

'. + form::combo('user_id',$users_combo,$user_id).'

'. + '

'. + form::combo('cat_id',$categories_combo,$cat_id).'

'. + '

' . + form::combo('status',$status_combo,$status).'

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

'. + form::combo('selected',$selected_combo,$selected).'

'. + '

'. + form::combo('attachment',$attachment_combo,$attachment).'

'. + '

'. + form::combo('month',$dt_m_combo,$month).'

'. + '

'. + form::combo('lang',$lang_combo,$lang).'

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

'.__('Display options').'

'. + '

'. + form::combo('sortby',$sortby_combo,$sortby).'

'. + '

'. + form::combo('order',$order_combo,$order).'

'. + '

'.__('Show').'

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

'. + form::hidden(array('p'), 'periodical'). + form::hidden(array('part'), 'period'). + form::hidden(array('period_id'), $period_id). + '

'. //Opera sucks + '
'; + + # Posts list + echo + $post_list->postDisplay($page, $nb_per_page, $base_url, + '
'. + + '%s'. + + '
'. + '

'. + + '

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

'. + form::hidden(array('period_id'), $period_id). + 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('attachment'), $attachment). + 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). + form::hidden(array('p'), 'periodical'). + form::hidden(array('part'), 'period'). + form::hidden(array('redir'), sprintf($base_url, $page)). + $core->formNonce(). + '
'. + '
' + ); + + echo + '
'; + } + +} + +############################################################ +# +# All periods +# +############################################################ + +else { + + # Delete periods and related posts links + if ($action == 'deleteperiods' && !empty($_POST['periods'])) { + try { + foreach($_POST['periods'] as $id) { + $id = (integer) $id; + $per->delPeriodPosts($id); + $per->delPeriod($id); + } + + dcPage::addSuccessNotice( + __('Periods removed.') + ); + + http::redirect(empty($_POST['redir']) ? + $p_url.'&part=periods' : + $_POST['redir'] + ); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + # Delete periods related posts links (without delete periods) + if ($action == 'emptyperiods' && !empty($_POST['periods'])) { + try { + foreach($_POST['periods'] as $id) { + $id = (integer) $id; + $per->delPeriodPosts($id); + } + + dcPage::addSuccessNotice( + __('Periods emptied.') + ); + + http::redirect(empty($_POST['redir']) ? + $p_url.'&part=periods' : + $_POST['redir'] + ); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + } + + # Combos + $sortby_combo = array( + __('Next update') => 'periodical_curdt', + __('End date') => 'periodical_enddt', + __('Frequence') => 'periodical_pub_int' + ); + + $order_combo = array( + __('Descending') => 'desc', + __('Ascending') => 'asc' + ); + + $combo_action = array(); + $combo_action[__('empty periods')] = 'emptyperiods'; + $combo_action[__('delete periods')] = 'deleteperiods'; + + # Filters + $sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'periodical_curdt'; + $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); + + if ($sortby !== '' && in_array($sortby, $sortby_combo)) { + if ($order !== '' && in_array($order, $order_combo)) { + $params['order'] = $sortby.' '.$order; + } + + if ($sortby != 'periodical_curdt' || $order != 'desc') { + $show_filters = true; + } + } + + # Get periods + try { + $periods = $per->getPeriods($params); + $counter = $per->getPeriods($params,true); + $period_list = new adminPeriodicalList($core,$periods,$counter->f(0)); + } + catch (Exception $e) { + $core->error->add($e->getMessage()); + } + + # Display + echo + ''.__('Periodical').''. + dcPage::jsLoad( + 'index.php?pf=periodical/js/periodsfilter.js' + ). + '\n". + ''. + ''. + + dcPage::breadcrumb( + array( + html::escapeHTML($core->blog->name) => '', + __('Periodical') => '' + ) + ). + dcPage::notices(). + + '

+ '.__('New period').' +

'; + + # Filter + echo + '
'. + + '

'. + __('Show filters and display options'). + '

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

'. + form::combo('sortby', $sortby_combo, $sortby).'

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

'. + form::combo('order', $order_combo, $order).'

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

'. + form::field('nb', 3, 3, $nb_per_page).'

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

'. + ''. + form::hidden(array('p'), 'periodical'). + form::hidden(array('part'), 'periods'). + '
'. //Opera sucks + '

'. + + '
'; + + # Posts list + echo $period_list->periodDisplay($page, $nb_per_page, + '
'. + + '%s'. + + '
'. + '

'. + + '

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

'. + form::hidden(array('sortby'), $sortby). + form::hidden(array('order'), $order). + form::hidden(array('page'), $page). + form::hidden(array('nb'), $nb_per_page). + form::hidden(array('p'), 'periodical'). + form::hidden(array('part'), 'periods'). + $core->formNonce(). + '
'. + '
' + ); + +} + +dcPage::helpBlock('periodical'); + +# Page footer +echo +'

+'.__('Configuration').' - +periodical - '.$core->plugins->moduleInfo('periodical', 'version').'  +'.__('periodical').' +

+'; diff --git a/js/dates.js b/js/dates.js new file mode 100644 index 0000000..597a031 --- /dev/null +++ b/js/dates.js @@ -0,0 +1 @@ +/* -- BEGIN LICENSE BLOCK ---------------------------------- * * This file is part of periodical, a plugin for Dotclear 2. * * Copyright (c) 2009-2013 Jean-Christian Denis and contributors * contact@jcdenis.fr http://jcd.lv * * Licensed under the GPL version 2.0 license. * A copy of this license is available in LICENSE file or at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * -- END LICENSE BLOCK ------------------------------------*/ $(function(){ var periodicalstart=document.getElementById('period_curdt'); if(periodicalstart!=undefined){ var periodicalstart_dtPick=new datePicker(periodicalstart); periodicalstart_dtPick.img_top='1.5em'; periodicalstart_dtPick.draw(); } var periodicalend=document.getElementById('period_enddt'); if(periodicalend!=undefined){ var periodicalend_dtPick=new datePicker(periodicalend); periodicalend_dtPick.img_top='1.5em'; periodicalend_dtPick.draw(); } }); \ No newline at end of file diff --git a/js/periodsfilter.js b/js/periodsfilter.js new file mode 100644 index 0000000..e3d983c --- /dev/null +++ b/js/periodsfilter.js @@ -0,0 +1,44 @@ +/* -- BEGIN LICENSE BLOCK ---------------------------------- + * + * This file is part of periodical, a plugin for Dotclear 2. + * + * Copyright (c) 2009-2013 Jean-Christian Denis and contributors + * contact@jcdenis.fr http://jcd.lv + * + * Licensed under the GPL version 2.0 license. + * A copy of this license is available in LICENSE file or at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * -- END LICENSE BLOCK ------------------------------------*/ + +$(function(){ + $('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);}); + + $filtersform = $('#filters-form'); + $filtersform.before('

'+dotclear.msg.filter_posts_list+'

') + + if( dotclear.msg.show_filters == 'false' ) { + $filtersform.hide(); + } else { + $('#filter-control') + .addClass('open') + .text(dotclear.msg.cancel_the_filter); + } + + $('#filter-control').click(function() { + if( $(this).hasClass('open') ) { + if( dotclear.msg.show_filters == 'true' ) { + return true; + } else { + $filtersform.hide(); + $(this).removeClass('open') + .text(dotclear.msg.filter_posts_list); + } + } else { + $filtersform.show(); + $(this).addClass('open') + .text(dotclear.msg.cancel_the_filter); + } + return false; + }); +}); \ No newline at end of file diff --git a/js/postsfilter.js b/js/postsfilter.js new file mode 100644 index 0000000..e11c92f --- /dev/null +++ b/js/postsfilter.js @@ -0,0 +1,44 @@ +/* -- BEGIN LICENSE BLOCK ---------------------------------- + * + * This file is part of periodical, a plugin for Dotclear 2. + * + * Copyright (c) 2009-2013 Jean-Christian Denis and contributors + * contact@jcdenis.fr http://jcd.lv + * + * Licensed under the GPL version 2.0 license. + * A copy of this license is available in LICENSE file or at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * -- END LICENSE BLOCK ------------------------------------*/ + +$(function(){ + $('.checkboxes-helpers').each(function(){dotclear.checkboxesHelpers(this);}); + + $filtersform = $('#filters-form'); + $filtersform.before('

'+dotclear.msg.filter_posts_list+'

') + + if( dotclear.msg.show_filters == 'false' ) { + $filtersform.hide(); + } else { + $('#filter-control') + .addClass('open') + .text(dotclear.msg.cancel_the_filter); + } + + $('#filter-control').click(function() { + if( $(this).hasClass('open') ) { + if( dotclear.msg.show_filters == 'true' ) { + return true; + } else { + $filtersform.hide(); + $(this).removeClass('open') + .text(dotclear.msg.filter_posts_list); + } + } else { + $filtersform.show(); + $(this).addClass('open') + .text(dotclear.msg.cancel_the_filter); + } + return false; + }); +}); \ No newline at end of file diff --git a/js/toggle.js b/js/toggle.js new file mode 100644 index 0000000..75f459c --- /dev/null +++ b/js/toggle.js @@ -0,0 +1,9 @@ +$(function() { + $('#periodical').parent().children('label').toggleWithLegend( + $('#periodical'), + { + user_pref: 'dcx_post_periodical', + legend_click: true + } + ); +}); \ No newline at end of file diff --git a/locales/en/help/help.html b/locales/en/help/help.html new file mode 100644 index 0000000..319e121 --- /dev/null +++ b/locales/en/help/help.html @@ -0,0 +1,19 @@ + + + + + periodical + + + +

If you want some help or contribute to the plugin periodical, follow these links.

+ + + + \ No newline at end of file diff --git a/locales/en/resources.php b/locales/en/resources.php new file mode 100644 index 0000000..45b86f1 --- /dev/null +++ b/locales/en/resources.php @@ -0,0 +1,20 @@ + + + + + periodical + + + +

Si vous souhaitez plus d'aide ou apporter votre contribution à l'extension periodical, voici quelques liens utiles.

+ + + + \ 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..f5e84d7 --- /dev/null +++ b/locales/fr/main.lang.php @@ -0,0 +1,179 @@ + \ No newline at end of file diff --git a/locales/fr/main.po b/locales/fr/main.po new file mode 100644 index 0000000..52ea9dc --- /dev/null +++ b/locales/fr/main.po @@ -0,0 +1,240 @@ +# Language: Français +# Module: periodical - 2013.11.11 +# Date: 2013-11-12 08:11:55 +# Translated with translater 2013.05.11 + +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: periodical 2013.11.11\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2013-11-12T08:11:55+00:00\n" +"Last-Translator: Jean-Christian Denis\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" + +#: _admin.php:25 +#: _admin.php:84 +#: _admin.php:142 +#: _admin.php:155 +#: _admin.php:311 +#: index.php:456 +#: index.php:468 +#: index.php:519 +#: index.php:738 +#: index.php:764 +msgid "Periodical" +msgstr "Publications périodiques" + +#: _admin.php:143 +msgid "Add to periodical" +msgstr "Ajouter aux publications périodiques" + +#: _admin.php:156 +#: index.php:519 +msgid "Remove from periodical" +msgstr "Retirer des publications périodiques" + +#: _admin.php:188 +msgid "Posts have been removed from periodical." +msgstr "Billets retirés des publications périodiques." + +#: _admin.php:216 +msgid "Posts have been added to periodical." +msgstr "Billets ajoutés aux publications périodiques." + +#: _admin.php:226 +msgid "Add a period to this selection" +msgstr "Ajouter une période à cette liste" + +#: _config.php:25 +#: inc/lib.index.pager.php:120 +msgid "Create date" +msgstr "Date de création" + +#: _config.php:64 +msgid "Configuration has been successfully updated." +msgstr "La configuration a été mise à jour avec succés." + +#: _config.php:84 +msgid "Enable plugin" +msgstr "Activer le plugin" + +#: _config.php:89 +msgid "Dates of published entries" +msgstr "Dates de billets publiés" + +#: _config.php:93 +msgid "Update post date" +msgstr "Mettre à jour la date du billet" + +#: _config.php:97 +msgid "Update post url" +msgstr "Mettre à jour l'URL du billet" + +#: _config.php:102 +msgid "Order of publication of entries" +msgstr "Ordre de publication des billets" + +#: inc/class.periodical.php:318 +msgid "Hourly" +msgstr "Toutes les heures" + +#: inc/class.periodical.php:319 +msgid "twice a day" +msgstr "Deux fois par jour" + +#: inc/class.periodical.php:320 +msgid "Daily" +msgstr "Une fois par jour" + +#: inc/class.periodical.php:321 +msgid "Weekly" +msgstr "Une fois par semaine" + +#: inc/class.periodical.php:322 +msgid "Monthly" +msgstr "Un fois par mois" + +#: inc/class.periodical.php:354 +#: inc/lib.index.pager.php:84 +msgid "Unknow frequence" +msgstr "Fréquence inconnue" + +#: inc/lib.index.pager.php:31 +msgid "No period" +msgstr "Pas de période" + +#: inc/lib.index.pager.php:44 +#: index.php:684 +msgid "Next update" +msgstr "Prochaine mise à jour" + +#: inc/lib.index.pager.php:46 +msgid "Publications" +msgstr "Publications" + +#: inc/lib.index.pager.php:48 +#: index.php:685 +msgid "End date" +msgstr "Date de fin" + +#: inc/lib.index.pager.php:81 +msgid "view related entries" +msgstr "voir les billets liés" + +#: inc/lib.index.pager.php:89 +msgid "edit period" +msgstr "modifier la période" + +#: inc/lib.periodical.socialmewriter.php:23 +msgid "New periodical publication" +msgstr "Nouvelle publication périodique" + +#: inc/lib.periodical.socialmewriter.php:24 +msgid "When an entry is published on a period" +msgstr "Lorsque qu'un billet st marqué comme publié par une période" + +#: index.php:43 +msgid "One post per day" +msgstr "Un billet par jour" + +#: index.php:55 +msgid "This period does not exist." +msgstr "Cette période n'existe pas." + +#: index.php:99 +msgid "Period title is already taken" +msgstr "Le titre de la période est déjà pris" + +#: index.php:104 +msgid "Period title is required" +msgstr "Le titre de la période est requis" + +#: index.php:107 +msgid "Start date must be older than end date" +msgstr "La date de début doit être plus ancienne que la date de fin" + +#: index.php:126 +msgid "Period successfully updated." +msgstr "Période mise à jour." + +#: index.php:135 +msgid "Period successfully created." +msgstr "Période crée." + +#: index.php:159 +msgid "Entries successfully published." +msgstr "Billets publiés." + +#: index.php:182 +msgid "Entries successfully unpublished." +msgstr "Billets mis hors ligne." + +#: index.php:204 +msgid "Entries successfully removed." +msgstr "Billets retirés." + +#: index.php:469 +#: index.php:477 +#: index.php:770 +msgid "New period" +msgstr "Nouvelle période" + +#: index.php:469 +#: index.php:477 +msgid "Edit period" +msgstr "Modifier la période" + +#: index.php:486 +msgid "Next update:" +msgstr "Prochaine mise à jour :" + +#: index.php:489 +msgid "End date:" +msgstr "Date de fin :" + +#: index.php:494 +msgid "Publication frequency:" +msgstr "Fréquence de publication :" + +#: index.php:497 +msgid "Number of entries to publish every time:" +msgstr "Nombre de billets à publier à chaque fois :" + +#: index.php:539 +msgid "Entries linked to this period" +msgstr "Billets liés à cette période" + +#: index.php:648 +msgid "Periods removed." +msgstr "Périodes retirées." + +#: index.php:669 +msgid "Periods emptied." +msgstr "Périodes vidées." + +#: index.php:686 +msgid "Frequence" +msgstr "Fréquence" + +#: index.php:695 +msgid "empty periods" +msgstr "vider les périodes" + +#: index.php:696 +msgid "delete periods" +msgstr "effacer les périodes" + +#: index.php:794 +msgid "Results per page :" +msgstr "Résultats par page :" + +#: index.php:818 +msgid "Selected periods action:" +msgstr "Action sur les périodes selectionnées :" + +msgid "Published periodically entries" +msgstr "Publier périodiquement des billets" + diff --git a/locales/fr/resources.php b/locales/fr/resources.php new file mode 100644 index 0000000..45b86f1 --- /dev/null +++ b/locales/fr/resources.php @@ -0,0 +1,20 @@ +