diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..e69de29
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..1369744
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,211 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991
+Free Software Foundation, Inc., 51 Franklin St, 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 Library
+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 copy-
+right 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 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 execu-
+table 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 auto-
+matically 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
\ No newline at end of file
diff --git a/gallery.php b/gallery.php
new file mode 100644
index 0000000..5b0398e
--- /dev/null
+++ b/gallery.php
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..ed5bfe8
--- /dev/null
+++ b/index.php
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plog-admin/_install.php b/plog-admin/_install.php
new file mode 100644
index 0000000..18f8125
--- /dev/null
+++ b/plog-admin/_install.php
@@ -0,0 +1,107 @@
+
+
+
+
+ Plogger
+
+
+
+
+
+
+
+
+'.plog_tr('Plogger Configuration Complete').'';
+ echo "\n\n\t" . ''. "\n";
+ // Otherwise, do the install
+ } else {
+ $errors = array();
+ $mysql = check_mysql(PLOGGER_DB_HOST, PLOGGER_DB_USER, PLOGGER_DB_PW, PLOGGER_DB_NAME);
+ if (empty($mysql)) {
+ create_tables();
+ configure_plogger($_SESSION['install_values']); // undefined index install_values
+ include_once(PLOGGER_DIR.'plog-load-config.php');
+ // If open permissions, have Plogger fix them
+ if (isset($_SESSION['plogger_close_perms'])) {
+ fix_open_perms($_SESSION['plogger_close_perms'], 'delete');
+ }
+ $col = add_collection(plog_tr('Plogger Test Collection'), plog_tr('Feel free to delete it'));
+ // Only attempt to create an album if the collection was created - sloppy fix for multiple installs
+ if (!empty($col['id'])) {
+ $alb = add_album(plog_tr('Plogger Test Album'), plog_tr('Feel free to delete it'), $col['id']);
+ }
+ } else {
+ echo plog_tr('There was an error with the MySQL connection').'!';
+ }
+ // If no errors, tell the user their login and password and link them to the login
+ if (empty($errors)) {
+ echo "\n\t" . ''.plog_tr('Plogger Install Complete').' ';
+ echo "\n\n\t" . ''.plog_tr('You have successfully installed Plogger!').' ';
+ echo "\n\t" . sprintf(plog_tr('Your username is %s and your password is %s'), ''.$_SESSION['install_values']['admin_username'].' ', ''.$_SESSION['install_values']['admin_password'].' ');
+ echo '
';
+ if (is_open_perms(PLOGGER_DIR.'plog-content/')) {
+ echo "\n\n\t" . ''.sprintf(plog_tr('You can now CHMOD the %s directory back to 0755'), 'plog-content/ ').'.
';
+ }
+ echo "\n\n\t" . ''. "\n";
+ unset($_SESSION['plogger_config']);
+ unset($_SESSION['install_values']);
+ } else {
+ // Else display the errors
+ }
+ }
+ }
+} else {
+ // Otherwise it's installed
+ echo ''.plog_tr('Plogger is already installed').'.
';
+}
+
+close_db();
+close_ftp();
+?>
+
+
+
\ No newline at end of file
diff --git a/plog-admin/_upgrade.php b/plog-admin/_upgrade.php
new file mode 100644
index 0000000..c76a5b2
--- /dev/null
+++ b/plog-admin/_upgrade.php
@@ -0,0 +1,330 @@
+
+
+
+
+ Upgrade Plogger
+
+
+
+
+
+
+
+ 0) {
+ echo "\n\t" . ''.plog_tr('Plogger cannot be upgraded until the following problems are resolved').':
';
+ echo "\n\n\t\t" . '';
+ foreach($errors as $error) {
+ echo "\n\t\t\t" . ''.$error.' ';
+ }
+ echo "\n\t\t" . ' ';
+ echo "\n\n\t\t" . '' . "\n";
+} else { // End of requirement check
+ $errors = "";
+
+ echo "\n" . ''.plog_tr('Upgrading Plogger').' ';
+
+ switch ($step) {
+ // Step 0 - gather any information needed
+ case 0:
+ if ($beta1) {
+ // Include the old sql database info and create a new plog-config.php file with it
+ include_once(PLOGGER_DIR.'plog-connect.php');
+ $conf = create_config_file($DB_HOST, $DB_USER, $DB_PW, $DB_NAME);
+ // Serve the config file and ask user to upload it to webhost
+ $_SESSION['plogger_config'] = $conf;
+ echo "\n\n\t" . ''.plog_tr('Updating Configuration').' ';
+ echo "\n\n\t\t" . ''.plog_tr('It appears you are updating from Plogger 1.0beta1. Your configuration file needs to be updated.').'
';
+ echo "\n\n\t\t" . '' . "\n";
+ break;
+ } else if ($needs_ftp) {
+ // If we need to collect ftp information for safe_mode workaround
+ // Handle errors and include the information form
+ if (!empty($ftp_errors)) {
+ echo "\n\n\t\t" . '';
+ foreach ($ftp_errors as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ include(PLOGGER_DIR.'plog-admin/includes/install-form-setup.php');
+ break;
+ }
+
+ // Step 1 - update the database
+ case 1:
+ $return = upgrade_database();
+ if (!empty($return)) {
+ echo "\n\n\t" . ''.plog_tr('Updating Database').' ';
+ echo "\n\n\t\t" . '';
+ foreach ($return as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ echo "\n\n\t" . ''.plog_tr('Done with database upgrade!').' ';
+ echo "\n\n\t" . '' . "\n";
+ break;
+ }
+
+ // Step 2 - move images, albums, collections, and uploads to new locations
+ case 2:
+ // Load the config file
+ include_once(PLOGGER_DIR.'plog-load-config.php');
+ // Check if we need to rename the directories due to permissions to force the re-creation of images/ and thumbs/
+ if (isset($_SESSION['plogger_close_perms'])) {
+ fix_open_perms($_SESSION['plogger_close_perms']);
+ }
+ $upgrade_images = upgrade_image_list();
+ if ($upgrade_images['total'] > 0 || isset($_POST['upgrade-images'])) {
+ $selects = array('5' => 5, '10' => 10, '25' => 25, '50' => 50, '75' => 75, '100' => 100, '150' => 150, '200' => 200, '250' => 250, '0' => plog_tr('All at once'));
+ echo "\n\n\t" . ''.plog_tr('Updating Images').' ';
+ if (!isset($_POST['upgrade-images'])) {
+ echo "\n\n\t" . ''.sprintf(plog_tr('Plogger needs to restructure %s items'), ''.$upgrade_images['total'].' ') . '
';
+ echo "\n\n\t" . '' . "\n";
+ } else {
+ $num_images = (isset($_POST['num-images']) && $_POST['num-images'] > 0) ? $_POST['num-images'] : $upgrade_images['total'];
+ $return = upgrade_images($num_images, $upgrade_images);
+ if (!empty($return['errors'])) {
+ echo "\n\n\t" . ''.plog_tr('Plogger was unable to move the following images. Please check your permissions.').'
';
+ echo "\n\n\t\t" . '';
+ foreach ($return['errors'] as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ if (!empty($return['output'])) {
+ echo "\n\n\t" . ''.plog_tr('Plogger was able to move the following images').':
';
+ echo "\n\n\t\t" . '';
+ foreach ($return['output'] as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ if ($return['count'] == $upgrade_images['total']) {
+ echo "\n\n\t" . ''.plog_tr('Done with image restructure').'! ';
+ echo "\n\n\t" . '' . "\n";
+ if (isset($_SESSION['plogger_close_perms'])) {
+ unset($_SESSION['plogger_close_perms']);
+ }
+ } else {
+ echo "\n\n\t" . ''.sprintf(plog_tr('Plogger needs to restructure %s more images'), ''.( $upgrade_images['total'] - $return['count'] ).' ').'
';
+ echo "\n\n\t" . '' . "\n";
+ }
+ }
+ break;
+ }
+
+ // Step 3 - check for old themes & translation files
+ case 3:
+ $check_list = check_list();
+ if (!empty($check_list['themes']) || !empty($check_list['translations'])) {
+ if (!empty($check_list['themes'])) {
+ echo "\n\n\t" . ''.sprintf(plog_tr('Plogger has found old %s files'), plog_tr('theme') ).'. '.sprintf( plog_tr('If you have customized a theme listed below, please verify that you have a copy located in %s before moving on to the next step'), 'plog-content/themes/ ' ).':
';
+ echo "\n\n\t\t" . '';
+ foreach ($check_list['themes'] as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ if (!empty($check_list['translations'])) {
+ echo "\n\n\t" . ''.sprintf(plog_tr('Plogger has found old %s files'), plog_tr('translation') ).'. '.sprintf(plog_tr('Please verify that you have a copy located in %s before moving on to the next step'), 'plog-content/translations/ ' ).':
';
+ echo "\n\n\t\t" . '';
+ foreach ($check_list['translations'] as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ echo "\n\n\t" . '' . "\n";
+ break;
+ }
+
+ // Step 4 - clean up the old files
+ case 4:
+ // Load the config file
+ include_once(PLOGGER_DIR.'plog-load-config.php');
+ $cleanup_list = cleanup_list();
+ if (!empty($cleanup_list['files']) || !empty($cleanup_list['folders']) || isset($_POST['do-cleanup'])) {
+ echo "\n\n\t" . ''.plog_tr('Cleaning Up Files').' ';
+ if (!isset($_POST['do-cleanup'])) {
+ echo "\n\n\t" . '';
+ } else {
+ $return = cleanup_files($cleanup_list['files'], $cleanup_list['folders']);
+ if (!empty($return['errors'])) {
+ echo "\n\n\t" . ''.plog_tr('Plogger could not delete the following files/folders. Please check your permissions or delete them manually.').'
';
+ echo "\n\n\t\t" . '';
+ foreach ($return['errors'] as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ if (!empty($return['output'])) {
+ echo "\n\n\t" . ''.plog_tr('Plogger was able to delete the following files/folders').':
';
+ echo "\n\n\t\t" . '';
+ foreach ($return['output'] as $value) {
+ echo "\n\t\t\t" . ''.$value.' ';
+ }
+ echo "\n\t\t" . ' ';
+ }
+ if (!empty($return['errors'])) {
+ echo "\n\t" . ''. "\n";
+ } else {
+ echo "\n\n\t" . ''.plog_tr('Done with cleanup!').' ';
+ }
+ }
+ echo "\n\n\t" . ''. "\n";
+ echo "\n\t" . '
'. "\n";
+ break;
+ }
+
+ // Finished!
+ case 5:
+ echo "\n\n\t" . ''.plog_tr('Upgrade complete!').' ';
+ echo "\n\n\t" . ''.plog_tr('You have successfully upgraded Plogger!').'
';
+ if (is_open_perms(PLOGGER_DIR.'plog-content/')) {
+ echo "\n\n\t" . ''.sprintf(plog_tr('You can now CHMOD the %s directory back to 0755'), 'plog-content/ ').'.
';
+ }
+ echo "\n\n\t" . ''. "\n";
+ break;
+ }
+}
+
+if (!$beta1) {
+ close_db();
+ close_ftp();
+}
+
+?>
+
+
+
\ No newline at end of file
diff --git a/plog-admin/css/admin.css b/plog-admin/css/admin.css
new file mode 100644
index 0000000..719148c
--- /dev/null
+++ b/plog-admin/css/admin.css
@@ -0,0 +1,927 @@
+/* Plogger gallery admin UI stylesheet */
+
+/* Body and general styles */
+
+html {
+ margin: 0;
+ padding: 0 15px 15px 15px;
+ border-top: 10px solid #365d95;
+}
+
+html, body, p, form, table, td, tr {
+ font-family: tahoma, verdana, arial, sans-serif;
+ font-size: 14px;
+ color: #333;
+}
+
+a:link, a:visited, a:active {
+ color: #24496c;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #800;
+ text-decoration: underline;
+}
+
+a:focus {
+ outline: none;
+ -moz-outline: none;
+}
+
+acronym, abbr {
+ cursor: help;
+}
+
+h1 {
+ font-family: verdana, arial, sans-serif;
+ font-size: 1.3em;
+ margin-top: 20px;
+ margin-bottom: 15px;
+ font-weight: bold;
+ color: #264b71;
+}
+
+h2 {
+ font-family: verdana, arial, sans-serif;
+ font-size: 1.1em;
+ margin-top: 20px;
+ margin-bottom: 15px;
+ text-decoration: underline;
+}
+
+h3 {
+ font-family: verdana, arial, sans-serif;
+ font-size: 1.0em;
+ margin-top: 15px;
+ margin-bottom: 10px;
+}
+
+img {
+ border: 0;
+ padding: 5px
+ display: inline;
+}
+
+input, select, textarea {
+ margin: 5px;
+ font-family: verdana, arial, sans-serif;
+ font-size: 13px;
+ letter-spacing: normal;
+ color: #333;
+}
+
+input.submit, input.submit-create, input.submit-delete, input.submit-cancel {
+ border: none;
+ -moz-border-radius: 8px;
+ -khtml-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ border-radius: 8px;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ -khtml-box-sizing: content-box;
+ box-sizing: content-box;
+ color: #fff;
+ font-family: verdana, arial, sans-serif;
+ font-size: 13px !important;
+ font-weight: bold;
+ padding: 2px 5px;
+ cursor: pointer;
+ width: 140px;
+}
+
+input.submit, input.submit-delete, input.submit-cancel {
+ background-color: #369;
+ background-image: url("../images/blue-button-bg.gif");
+ background-repeat: repeat-x;
+ margin-left: 0;
+}
+
+input.submit[type="submit"]:hover {
+ color: #a0cca0;
+}
+
+input.submit-delete[type="submit"]:hover, input.submit-cancel[type="submit"]:hover {
+ color: #f00;
+}
+
+input.submit-create {
+ background-color: #65bc85;
+ background-image: url("../images/green-button-bg.gif");
+ background-repeat: repeat-x;
+ margin: 0;
+}
+
+input.submit-create[type="button"]:hover {
+ color: #24496c;
+}
+
+input.submit-inline {
+ display: inline;
+ background-color: transparent;
+ color: #61a861;
+ font-size: 90%;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ border: 0 none;
+ cursor: pointer;
+}
+
+label {
+ padding: 8px;
+}
+
+label em {
+ font-style: normal;
+ text-decoration: underline;
+}
+
+p {
+ padding: 3px;
+}
+
+ul {
+ margin-bottom: 5px;
+}
+
+/* Header elements */
+
+#logo {
+ float: left;
+ margin-top: -3px;
+ padding: 0;
+}
+
+#logo img {
+ border: 0 none;
+}
+
+#plogger-version {
+ float: right;
+ width: 375px;
+ font-family: verdana, arial, sans-serif;
+ font-size: 11px;
+ color: #777;
+ margin: 0;
+ padding: 0;
+}
+
+#server-info {
+ margin: 5px 0 0 0;
+ padding: 5px 5px 5px 10px;
+ background-color: #f9f9f9;
+ border: 1px solid #d9d9d9;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ white-space: wrap;
+}
+
+/* Navigation elements (tabs) */
+
+#tab-nav {
+ clear: both;
+ float: left;
+ width: 100%;
+ background: transparent url("../images/bg.gif") repeat-x bottom;
+ font-family: "Lucida Sans Unicode", verdana, arial, sans-serif;
+ font-size: 13px;
+ letter-spacing: normal;
+ line-height: normal;
+ margin-bottom: 15px;
+ border-bottom: 1px solid #369;
+}
+
+#tab-nav ul {
+ margin: 0;
+ padding: 10px 0 0 3px;
+ list-style: none;
+}
+
+#tab-nav li {
+ float: left;
+ background: url("../images/left.gif") no-repeat left top;
+ margin: 0;
+ padding: 0 0 0 9px;
+}
+
+#tab-nav a {
+ float: left;
+ display: block;
+ background: url("../images/right.gif") no-repeat right top;
+ padding: 5px 13px 4px 4px;
+ text-decoration: none;
+ font-weight: normal;
+ color: #765;
+}
+
+/* Commented Backslash Hack hides rule from IE5-Mac \*/
+#tab-nav a {
+ float: none;
+}
+/* End IE5-Mac hack */
+
+#tab-nav a:hover {
+ color: green;
+}
+
+#tab-nav a:focus {
+ outline: none;
+ -moz-outline: none;
+}
+
+#tab-nav #current {
+ background-image: url("../images/left_on.gif");
+ border-bottom: 1px solid #369;
+}
+
+#tab-nav #current a {
+ background-image: url("../images/right_on.gif");
+ color: #fff;
+ padding-bottom: 4px;
+ font-weight: bold;
+}
+
+#tab-nav #current a:focus {
+ outline: none;
+ -moz-outline: none;
+}
+
+#tab-nav a em {
+ text-decoration: underline;
+ font-style: normal;
+}
+
+#tab-subnav {
+ background: #369;
+ padding: 8px;
+ clear: both;
+}
+
+/* Upload page elements */
+
+#uploadForm form {
+ margin: 0;
+ padding: 0;
+}
+
+#uploadForm label {
+ display: block;
+ white-space: nowrap;
+}
+
+/* Import and Manage page elements */
+
+a.folder {
+ background-image: url("../images/folder_open.gif");
+ background-repeat: no-repeat;
+ background-position: left 1px;
+ padding-left: 25px;
+}
+
+.add {
+ background-image: url("../images/new_file.gif");
+ background-repeat: no-repeat;
+ background-position: left center;
+ padding-left: 20px;
+ margin-left: -12px;
+ text-decoration: underline;
+}
+
+.import {
+ margin-bottom: 15px;
+ padding: 8px;
+ margin: 5px;
+ margin-left: 0;
+ background-color: #f9f9f9;
+ border: 1px solid #d9d9d9;
+ -moz-border-radius: 8px;
+ -khtml-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ border-radius: 8px;
+ width: 350px;
+ clear: both;
+}
+
+/* Manage and Feedback page elements */
+
+#contentList {
+}
+
+#contentList #breadcrumb_links, #contentList #comment-count {
+ font-size: 1.0em;
+ padding: 3px;
+}
+
+#contentList .pagination {
+ font-size: 0.9em;
+ margin-right: 5px;
+ padding: 10px 3px 0 3px;
+ font-weight: normal;
+ text-align: right;
+}
+
+#contentList .pagination span.page-link, #contentList .pagination a.page-link {
+ border: 1px solid #bbb;
+ -moz-border-radius: 3px;
+ -khtml-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ padding: 1px 5px;
+ margin: 0 3px;
+ color: #bbb;
+ text-decoration: none;
+}
+
+#contentList .pagination .page-link:hover, #contentList .pagination .page-link a:hover {
+ color: #777;
+ border: 1px solid #777;
+ background-color: #f9f9f9;
+}
+
+#contentList .pagination .page-link a:focus {
+ outline: none;
+ -moz-outline: none;
+}
+
+#contentList .pagination a.pagPrev, #contentList .pagination a.pagNext {
+ color: #bbb;
+ text-decoration: none;
+ margin: 0 2px;
+}
+
+#contentList .pagination a.pagPrev:hover, #contentList .pagination a.pagNext:hover {
+ color: #777;
+ text-decoration: none;
+}
+
+#contentList .pagination span.page-link-current {
+ border: 1px solid #369;
+ -moz-border-radius: 3px;
+ -khtml-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ padding: 1px 5px;
+ margin: 0 3px;
+ background-color: #369;
+ color: #fff;
+}
+
+#contentList label, #contentList select, #contentList option {
+ font-weight: normal;
+}
+
+#contentList .entries-page {
+ text-align: right;
+}
+
+#contentList .entries-page label, #contentList .entries-page select {
+ display: block inline;
+ margin-bottom: auto;
+}
+
+#contentList .entries-page select {
+ width: 60px;
+}
+
+#contentList .move-del-manage input, #contentList .move-del-manage select {
+ display: block inline;
+ margin-bottom: auto;
+ font-family: verdana, arial, sans-serif;
+ font-size: 13px;
+ letter-spacing: normal;
+ clear: both;
+}
+
+a#show-collection, a#show-album {
+ padding-left: 20px;
+ background: #ffc url("../images/new_file.gif") no-repeat left;
+ font-size: 1.1em;
+}
+
+a#show-collection:hover, a#show-album:hover {
+ text-decoration: none;
+ color: white;
+ background: #9c9 url("../images/new_file.gif") no-repeat left;
+}
+
+.edit {
+ background-image: url("../images/edit.gif");
+ background-repeat: no-repeat;
+ background-position: 5px 8px;
+ margin-bottom: 15px;
+ padding: 8px;
+ padding-left: 28px;
+ margin: 15px;
+ margin-left: 0;
+ background-color: #f9f9f9;
+ border: 1px solid #d9d9d9;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ width: auto;
+ clear: both;
+}
+
+.edit img {
+ border: 1px solid #d9d9d9;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 3px;
+ background-color: #fff;
+}
+
+.editable {
+ color: #000;
+ background: #fff url("../images/diag-bg.gif") repeat top left;
+ padding: 2px;
+ border: 1px solid #efefef;
+ display: block;
+ cursor: pointer;
+}
+
+.stats, .stats-info {
+ background: #f9f9f9;
+ background-image: url("../images/info.gif");
+ background-repeat: no-repeat;
+ background-position: 5px 8px;
+ margin-right: 5px;
+ margin-bottom: 15px;
+ border: 1px solid #d9d9d9;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 8px 5px 8px 28px;
+ clear: both;
+ width: auto;
+}
+
+/* Options page elements */
+
+#options-section label {
+ font-weight: bold;
+}
+
+.option-table table {
+ width: 665px;
+}
+
+.option-table label {
+ padding: 8px 2px 8px 8px;
+}
+
+.option-table td.left {
+ padding: 7px;
+ text-align: right;
+ width: 330px;
+}
+
+.option-table td.right {
+ padding: 7px;
+ text-align: left;
+ width: 330px;
+}
+
+tr.alt td.left {
+ background: #f7f7f7;
+ border-bottom: 1px solid #f3f3f3;
+ -moz-border-radius: 8px 0 0 8px;
+ -webkit-border-top-left-radius: 8px;
+ -webkit-border-bottom-left-radius: 8px;
+ -khtml-border-top-left-radius: 8px;
+ -khtml-border-bottom-left-radius: 8px;
+ border-top-left-radius: 8px;
+ border-bottom-left-radius: 8px;
+}
+
+tr.alt td.right {
+ background: #f7f7f7;
+ border-bottom: 1px solid #f3f3f3;
+ -moz-border-radius: 0 8px 8px 0;
+ -webkit-border-top-right-radius: 8px;
+ -khtml-border-top-right-radius: 8px;
+ -webkit-border-bottom-right-radius: 8px;
+ -khtml-border-bottom-right-radius: 8px;
+ border-top-right-radius: 8px;
+ border-bottom-right-radius: 8px;
+}
+
+/* Themes and Plugins page elements */
+
+#theme-table td, #plugin-table td {
+ padding: 8px;
+ vertical-align: top;
+}
+
+#theme-table tr.header th, #plugin-table tr.header th {
+ padding: 8px;
+}
+
+tr.activated td, tr.enabled td {
+ background: #bed6d7;
+}
+
+td.active, td.on {
+ color: #000;
+ font-weight: bold;
+}
+
+/* Upload and Manage page elements */
+
+/* Blue/green boxes on Upload page */
+
+table.cssbox-upload {
+ width: 750px;
+ margin: 0;
+ padding: 0;
+}
+
+td.cssbox-upload-blue, td.cssbox-upload-green {
+ width: 375px !important;
+ margin: 2px 0 0 0;
+ padding: 10px;
+ vertical-align: top;
+}
+
+th.cssbox-upload-head-blue h2, th.cssbox-upload-head-green h2 {
+ color: #fff;
+ font-weight: bold;
+ font-size: 14px;
+ text-align: center;
+ vertical-align: top;
+ text-decoration: none;
+ margin: 0;
+ border: 0;
+ padding: 4px 18px;
+ height: 17px;
+ width: auto;
+ -moz-border-radius: 12px 12px 0 0;
+ -webkit-border-top-right-radius: 12px;
+ -webkit-border-top-left-radius: 12px;
+ -khtml-border-top-right-radius: 12px;
+ -khtml-border-top-left-radius: 12px;
+ border-top-right-radius: 12px;
+ border-top-left-radius: 12px;
+}
+
+th.cssbox-upload-head-blue h2 {
+ background: transparent url("../images/blue-button-bg.gif") repeat-x top left;
+}
+
+th.cssbox-upload-head-green h2 {
+ background: transparent url("../images/green-button-bg.gif") repeat-x top left;
+}
+
+td.cssbox-upload-body {
+ margin: 0;
+ padding: 15px 10px 15px 15px;
+ vertical-align: top;
+ width: 350px;
+ background-color: #f9f9f9;
+ border-right: 1px solid #d9d9d9;
+ border-bottom: 1px solid #d9d9d9;
+ border-left: 1px solid #d9d9d9;
+ -moz-border-radius: 0 0 12px 12px;
+ -webkit-border-bottom-right-radius: 12px;
+ -webkit-border-bottom-left-radius: 12px;
+ -khtml-border-bottom-right-radius: 12px;
+ -khtml-border-bottom-left-radius: 12px;
+ border-bottom-right-radius: 12px;
+ border-bottom-left-radius: 12px;
+}
+
+.cssbox-upload-body label {
+ font-family: tahoma, verdana, arial, sans-serif;
+ font-size: 14px;
+ display: inline;
+ font-weight: bold;
+}
+
+/* Green box on Manage page */
+
+.cssbox-green {
+ width: 385px !important;
+ margin: 10px 0 2px 0;
+ padding: 0;
+}
+
+.cssbox-green em {
+ text-decoration: underline;
+ font-style: normal;
+}
+
+.cssbox-head-green h2 {
+ color: #fff;
+ font-weight: bold;
+ font-size: 13px;
+ text-align: left;
+ text-decoration: none;
+ width: auto;
+ background: transparent url("../images/green-button-bg.gif") repeat-x top left;
+ margin: 0;
+ border: 0;
+ padding: 3px 15px 3px 15px;
+ height: 17px;
+ -moz-border-radius: 8px 8px 0 0;
+ -webkit-border-top-right-radius: 8px;
+ -webkit-border-top-left-radius: 8px;
+ -khtml-border-top-right-radius: 8px;
+ -khtml-border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ border-top-left-radius: 8px;
+}
+
+.cssbox-head-green h2:hover {
+ color: #24496c;
+}
+
+.cssbox-head-green h2.manage {
+ cursor: pointer;
+}
+
+.cssbox-body-green {
+ margin: 0;
+ padding: 15px 10px 15px 15px;
+ width: auto;
+ background-color: #f9f9f9;
+ border-right: 1px solid #d9d9d9;
+ border-bottom: 1px solid #d9d9d9;
+ border-left: 1px solid #d9d9d9;
+ -moz-border-radius: 0 0 8px 8px;
+ -webkit-border-bottom-right-radius: 8px;
+ -webkit-border-bottom-left-radius: 8px;
+ -khtml-border-bottom-right-radius: 8px;
+ -khtml-border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ border-bottom-left-radius: 8px;
+}
+
+.cssbox-body-green label {
+ font-family: tahoma, verdana, arial, sans-serif;
+ font-size: 14px;
+ display: inline;
+ font-weight: bold;
+}
+
+.cssbox-body-green p {
+ margin: 0 0 20px;
+}
+
+/* Action/Error messages */
+
+.success, .actions, .errors, .info, .plugins {
+ background-repeat: no-repeat;
+ background-position: 5px 8px;
+ margin-right: 5px;
+ margin-bottom: 15px;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 8px 8px 8px 28px;
+ clear: both;
+}
+
+.success {
+ background-image: url("../images/success.gif");
+ border: 1px solid #8fc25c;
+ background-color: #d4edc9;
+}
+
+.actions {
+ background-image: url("../images/alert.gif");
+ border: 1px solid #bb0;
+ background-color: #ffffe0;
+}
+
+.errors {
+ background-image: url("../images/x.gif");
+ border: 1px solid maroon;
+ background-color: #fcc;
+}
+
+.info {
+ border: 1px solid #d9d9d9;
+ background-color: #f9f9f9;
+}
+
+.plugins {
+ border: 1px solid #d9d9d9;
+ background-color: #fff;
+}
+
+/* Miscellaneous - used throughout admin panel */
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+.breadcrumb-header {
+ height: 16px;
+ vertical-align: middle;
+ background-color: #ffc;
+ padding: 3px;
+}
+
+.img-shadow {
+ float: left;
+ background: url("../images/shadowAlpha.png") no-repeat bottom right !important;
+ background: url("../images/shadow.gif") no-repeat bottom right;
+ margin: 10px 0 0 10px !important;
+ margin: 10px 0 0 5px;
+}
+
+.img-shadow img {
+ display: block;
+ position: relative;
+ background-color: #fff;
+ border: 1px solid #a9a9a9;
+ margin: -6px 6px 6px -6px;
+ padding: 4px;
+}
+
+.margin-5 {
+ margin: 5px;
+}
+
+.no-margin-top {
+ margin-top: 0;
+}
+
+.no-margin-bottom {
+ margin-bottom: 0;
+}
+
+.strong {
+ font-weight: bold;
+}
+
+.table-header-middle {
+ background: url("../images/table-middle.gif") repeat-x top left;
+}
+
+.table-header-left {
+ background: url("../images/table-top-left.gif") no-repeat top left;
+}
+
+.table-header-right {
+ background: url("../images/table-top-right.gif") no-repeat top right;
+}
+
+tr.color-1 {
+ background-color: #eaeaea;
+ text-align: left;
+}
+
+tr.color-2 {
+ background-color: #f9f9f9;
+ text-align: left;
+}
+
+tr.header {
+ color: #fff;
+ background-color: #264e75;
+ font-weight: bold;
+}
+
+tr.header th {
+ padding: 6px;
+}
+
+tr.breadcrumb {
+ background-color: #ffffe0;
+ height: 16px;
+ vertical-align: middle;
+}
+
+tr.footer, tr.footer td {
+ -moz-border-radius: 0 0 8px 8px;
+ -webkit-border-bottom-right-radius: 8px;
+ -webkit-border-bottom-left-radius: 8px;
+ -khtml-border-bottom-right-radius: 8px;
+ -khtml-border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ border-bottom-left-radius: 8px;
+}
+
+tr.footer {
+ background-color: #264e75;
+}
+
+tr.footer td {
+ padding: 5px;
+}
+
+tr.footer td.invert-selection a, tr.footer td.invert-selection a:link, tr.footer td.invert-selection a:visited, tr.footer td.invert-selection a:active {
+ font-size: 0.9em;
+ font-weight: normal;
+ color: #fff;
+ text-decoration: none;
+ margin-left: 10px;
+}
+
+tr.footer td.invert-selection a:hover {
+ color: #ddd;
+}
+
+.thumbselect {
+ height: 100px;
+ padding-left: 100px;
+ background-repeat: no-repeat;
+ background-position: center left;
+}
+
+.thumboption {
+ height: 50px;
+ padding-left: 100px;
+ margin-bottom: 2px;
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+.upgrade {
+ color: #61a861;
+ text-decoration: none;
+}
+
+.vertical-top {
+ vertical-align: top;
+}
+
+.width-15 {
+ width: 15px;
+}
+
+.width-50 {
+ width: 50px;
+}
+
+.width-75 {
+ width: 75px;
+}
+
+.width-100 {
+ width: 100px;
+}
+
+.width-125 {
+ width: 125px;
+}
+
+.width-150 {
+ width: 150px;
+}
+
+.width-175 {
+ width: 175px;
+}
+
+.width-200 {
+ width: 200px;
+}
+
+.width-275 {
+ width: 275px;
+}
+
+.width-400 {
+ width: 400px;
+}
+
+.width-450 {
+ width: 450px;
+}
+
+.width-500 {
+ width: 500px;
+}
+
+.width-600 {
+ width: 600px;
+}
+
+.width-700 {
+ width: 700px;
+}
+
+.width-750 {
+ width: 750px;
+}
+
+td.width-15, td.width-50, td.width-75, td.width-100, td.width-125, td.width-150, td.width-175, td.width-200, td.width-275, td.width-400, td.width-450 {
+ vertical-align: top;
+}
diff --git a/plog-admin/css/lightbox.css b/plog-admin/css/lightbox.css
new file mode 100644
index 0000000..a04ef08
--- /dev/null
+++ b/plog-admin/css/lightbox.css
@@ -0,0 +1,44 @@
+#lightbox {
+ background-color: #eee;
+ padding: 10px;
+ border-right: 2px solid #666;
+ border-bottom: 2px solid #666;
+}
+
+#lightboxDetails {
+ font-size: 0.8em;
+ padding-top: 0.4em;
+}
+
+#lightboxCaption {
+ float: left;
+}
+
+#keyboardMsg {
+ float: right;
+}
+
+#closeButton {
+ top: 5px;
+ right: 5px;
+}
+
+#lightbox img {
+ border: none;
+ clear: both;
+}
+
+#overlay img {
+ border: none;
+}
+
+#overlay {
+ background-image: url("../images/overlay.png");
+}
+
+* html #overlay {
+ background-color: #333;
+ back\ground-color: transparent;
+ background-image: url("blank.gif");
+ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../images/overlay.png", sizingMethod="scale");
+}
diff --git a/plog-admin/css/login.css b/plog-admin/css/login.css
new file mode 100644
index 0000000..7961e42
--- /dev/null
+++ b/plog-admin/css/login.css
@@ -0,0 +1,103 @@
+/* Plogger gallery admin login stylesheet */
+
+/* Body and general styles */
+
+html, body, p, form {
+ font-family: tahoma, verdana, arial, sans-serif;
+ font-size: 14px;
+}
+
+a, a:link, a:visited, a:active {
+ color: #369;
+ font-size: 13px;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #519e51;
+ text-decoration: underline;
+}
+
+label {
+ font-weight: normal;
+}
+
+input {
+ margin: 1px;
+ font-family: verdana, arial, sans-serif;
+ font-size: 13px;
+ width: 180px;
+}
+
+input.submit {
+ border: none;
+ background-color: #369;
+ background-image: url("../images/blue-button-bg.gif");
+ background-repeat: repeat-x;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ -khtml-box-sizing: content-box;
+ box-sizing: content-box;
+ color: #fff;
+ font-family: verdana, arial, sans-serif;
+ font-size: 12px !important;
+ font-weight: bold;
+ padding: 3px 8px;
+ cursor: pointer;
+ margin-left: 0;
+ width: auto;
+}
+
+input.submit:hover {
+ color: #a0cca0;
+}
+
+/* Login page structure/elements */
+
+#login-page {
+ border-top: 20px solid #369;
+ background: #fff;
+ padding-top: 80px;
+ margin: auto;
+}
+
+#login-logo, #login-nav {
+ text-align: center;
+ margin: auto;
+}
+
+#login-box, #login-error, #login-action {
+ width: 285px;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ text-align: center;
+}
+
+#login-error, #login-action {
+ color: #000;
+ margin: 15px auto;
+ padding: 10px 15px;
+}
+
+#login-box {
+ background-color: #f9f9f9;
+ border: 1px solid #d9d9d9;
+ padding: 20px 15px 5px;
+ margin: 10px auto;
+}
+
+#login-error {
+ border: 1px solid maroon;
+ background-color: #fcc;
+}
+
+#login-action {
+ border: 1px solid #bb0;
+ background-color: #ffffe0;
+}
diff --git a/plog-admin/images/ajax-loader.gif b/plog-admin/images/ajax-loader.gif
new file mode 100644
index 0000000..766ff7a
Binary files /dev/null and b/plog-admin/images/ajax-loader.gif differ
diff --git a/plog-admin/images/alert.gif b/plog-admin/images/alert.gif
new file mode 100644
index 0000000..b346745
Binary files /dev/null and b/plog-admin/images/alert.gif differ
diff --git a/plog-admin/images/auto.gif b/plog-admin/images/auto.gif
new file mode 100644
index 0000000..49fdcbd
Binary files /dev/null and b/plog-admin/images/auto.gif differ
diff --git a/plog-admin/images/bg.gif b/plog-admin/images/bg.gif
new file mode 100644
index 0000000..21480b7
Binary files /dev/null and b/plog-admin/images/bg.gif differ
diff --git a/plog-admin/images/blue-button-bg.gif b/plog-admin/images/blue-button-bg.gif
new file mode 100644
index 0000000..8fd2c52
Binary files /dev/null and b/plog-admin/images/blue-button-bg.gif differ
diff --git a/plog-admin/images/diag-bg.gif b/plog-admin/images/diag-bg.gif
new file mode 100644
index 0000000..12ae989
Binary files /dev/null and b/plog-admin/images/diag-bg.gif differ
diff --git a/plog-admin/images/edit.gif b/plog-admin/images/edit.gif
new file mode 100644
index 0000000..6982be0
Binary files /dev/null and b/plog-admin/images/edit.gif differ
diff --git a/plog-admin/images/folder_open.gif b/plog-admin/images/folder_open.gif
new file mode 100644
index 0000000..c6b9745
Binary files /dev/null and b/plog-admin/images/folder_open.gif differ
diff --git a/plog-admin/images/green-button-bg.gif b/plog-admin/images/green-button-bg.gif
new file mode 100644
index 0000000..c14d214
Binary files /dev/null and b/plog-admin/images/green-button-bg.gif differ
diff --git a/plog-admin/images/info.gif b/plog-admin/images/info.gif
new file mode 100644
index 0000000..59ff9e5
Binary files /dev/null and b/plog-admin/images/info.gif differ
diff --git a/plog-admin/images/left.gif b/plog-admin/images/left.gif
new file mode 100644
index 0000000..c06be20
Binary files /dev/null and b/plog-admin/images/left.gif differ
diff --git a/plog-admin/images/left_on.gif b/plog-admin/images/left_on.gif
new file mode 100644
index 0000000..af674f7
Binary files /dev/null and b/plog-admin/images/left_on.gif differ
diff --git a/plog-admin/images/loading.gif b/plog-admin/images/loading.gif
new file mode 100644
index 0000000..915c198
Binary files /dev/null and b/plog-admin/images/loading.gif differ
diff --git a/plog-admin/images/login-logo.gif b/plog-admin/images/login-logo.gif
new file mode 100644
index 0000000..3850a57
Binary files /dev/null and b/plog-admin/images/login-logo.gif differ
diff --git a/plog-admin/images/new_file.gif b/plog-admin/images/new_file.gif
new file mode 100644
index 0000000..7928c09
Binary files /dev/null and b/plog-admin/images/new_file.gif differ
diff --git a/plog-admin/images/overlay.png b/plog-admin/images/overlay.png
new file mode 100644
index 0000000..7cee298
Binary files /dev/null and b/plog-admin/images/overlay.png differ
diff --git a/plog-admin/images/plogger-sm.gif b/plog-admin/images/plogger-sm.gif
new file mode 100644
index 0000000..5d0122d
Binary files /dev/null and b/plog-admin/images/plogger-sm.gif differ
diff --git a/plog-admin/images/plogger.gif b/plog-admin/images/plogger.gif
new file mode 100644
index 0000000..2d24668
Binary files /dev/null and b/plog-admin/images/plogger.gif differ
diff --git a/plog-admin/images/right.gif b/plog-admin/images/right.gif
new file mode 100644
index 0000000..4148e2e
Binary files /dev/null and b/plog-admin/images/right.gif differ
diff --git a/plog-admin/images/right_on.gif b/plog-admin/images/right_on.gif
new file mode 100644
index 0000000..63ba5ab
Binary files /dev/null and b/plog-admin/images/right_on.gif differ
diff --git a/plog-admin/images/rss.gif b/plog-admin/images/rss.gif
new file mode 100644
index 0000000..b0e4adf
Binary files /dev/null and b/plog-admin/images/rss.gif differ
diff --git a/plog-admin/images/shadow.gif b/plog-admin/images/shadow.gif
new file mode 100644
index 0000000..af7f537
Binary files /dev/null and b/plog-admin/images/shadow.gif differ
diff --git a/plog-admin/images/shadowAlpha.png b/plog-admin/images/shadowAlpha.png
new file mode 100644
index 0000000..a2561df
Binary files /dev/null and b/plog-admin/images/shadowAlpha.png differ
diff --git a/plog-admin/images/success.gif b/plog-admin/images/success.gif
new file mode 100644
index 0000000..5568972
Binary files /dev/null and b/plog-admin/images/success.gif differ
diff --git a/plog-admin/images/table-middle.gif b/plog-admin/images/table-middle.gif
new file mode 100644
index 0000000..8f7a45e
Binary files /dev/null and b/plog-admin/images/table-middle.gif differ
diff --git a/plog-admin/images/table-top-left.gif b/plog-admin/images/table-top-left.gif
new file mode 100644
index 0000000..9717087
Binary files /dev/null and b/plog-admin/images/table-top-left.gif differ
diff --git a/plog-admin/images/table-top-right.gif b/plog-admin/images/table-top-right.gif
new file mode 100644
index 0000000..2e47efc
Binary files /dev/null and b/plog-admin/images/table-top-right.gif differ
diff --git a/plog-admin/images/x.gif b/plog-admin/images/x.gif
new file mode 100644
index 0000000..c184cfb
Binary files /dev/null and b/plog-admin/images/x.gif differ
diff --git a/plog-admin/includes/install-form-setup.php b/plog-admin/includes/install-form-setup.php
new file mode 100644
index 0000000..d29659e
--- /dev/null
+++ b/plog-admin/includes/install-form-setup.php
@@ -0,0 +1,147 @@
+
+
+
+
+
+
diff --git a/plog-admin/includes/install-functions.php b/plog-admin/includes/install-functions.php
new file mode 100644
index 0000000..97dff83
--- /dev/null
+++ b/plog-admin/includes/install-functions.php
@@ -0,0 +1,1145 @@
+'.$column.' .'plog_tr('already exists, ignoring.').'';
+ return 'Field '.$column.' already exists, ignoring.';
+ }
+ }
+}
+
+function maybe_drop_column($table, $column) {
+ $sql = "DESCRIBE $table";
+ $res = mysql_query($sql);
+ $found = false;
+ while($row = mysql_fetch_array($res, MYSQL_NUM)) {
+ if ($row[0] == $column) $found = true;
+ }
+ if ($found) {
+ $sql = "ALTER TABLE $table DROP `$column`";
+ mysql_query($sql);
+ return plog_tr('Dropped column').': '.$column;
+ } else {
+ if (defined('PLOGGER_DEBUG')) {
+// return $column.' '.plog_tr('does not exist').'';
+ return $column.' does not exist';
+ }
+ }
+}
+
+function maybe_add_table($table, $add_sql, $options = '') {
+ $sql = "DESCRIBE $table";
+ $res = mysql_query($sql);
+ if (!$res) {
+ $q = "CREATE table `$table` ($add_sql) $options";
+ mysql_query($q);
+ if (mysql_error()) {
+ var_dump(mysql_error());
+ } else {
+ return true;
+ }
+ } else {
+ if (defined('PLOGGER_DEBUG')) {
+// return plog_tr('Table').' '.$table.' .'plog_tr('already exists, ignoring.').'';
+ return 'Table '.$table.' already exists, ignoring.';
+ }
+ }
+}
+
+function get_default_charset() {
+ // Since 4.1 MySQL has support for specifying character encoding for tables
+ // and I really want to use it if available. So we need figure out what version
+ // we are running on and to the right thing
+ $mysql_version = mysql_get_server_info();
+ $mysql_charset_support = '4.1';
+ $default_charset = '';
+
+ if (1 == version_compare($mysql_version, $mysql_charset_support)) {
+ $default_charset = 'DEFAULT CHARACTER SET UTF8';
+ }
+ return $default_charset;
+}
+
+function gd_missing() {
+ require_once(PLOGGER_DIR.'/plog-includes/lib/phpthumb/phpthumb.functions.php');
+ // This is copied over from phpthumb
+ return phpthumb_functions::gd_version() < 1;
+}
+
+function check_requirements() {
+ $errors = array();
+
+ // Check that the session variable can be read
+ if (!isset($_SESSION['plogger_session'])) {
+ $save_path = ini_get('session.save_path');
+ // Check that session.save_path is set (not set by default on PHP5)
+ if (empty($save_path)) {
+ if (!defined('SESSION_SAVE_PATH')) {
+ $sample_text = ' ('.sprintf(plog_tr('see %s if your %s does not contain this variable'), 'plog-config-sample.php', 'plog-config.php').')';
+ } else {
+ $sample_text = '';
+ }
+ $errors[] = sprintf( plog_tr('The PHP %s variable is not set in your php.ini file.'), 'session.save_path ').' '.sprintf(plog_tr('You can attempt to set this by adding a writable directory path to the %s variable in %s or contact your webhost on how to set this system variable.'), 'SESSION_SAVE_PATH ', 'plog-config.php'.$sample_text);
+ } else {
+ $errors[] = sprintf(plog_tr('PHP session cookies are not being set. Please check that session cookies are enabled on your browser or verify that your %s variable is set up correctly.'), 'session.save_path ').' '.sprintf(plog_tr('You can attempt to set this by adding a writable directory path to the %s variable in %s or contact your webhost on how to set this system variable.'), 'SESSION_SAVE_PATH ', 'plog-config.php'.$sample_text);
+ }
+ }
+
+ // Check that the GD library is available
+ if (gd_missing()) {
+ $errors[] = plog_tr('PHP GD module was not detected.');
+ }
+
+ // Check that MySQL functions are available
+ if (!function_exists('mysql_connect')) {
+ $errors[] = plog_tr('PHP MySQL module was not detected.');
+ }
+
+ // Make sure we have permission to read these folders/files
+ $files_to_read = array('./plog-admin', './plog-admin/css', './plog-admin/images', './plog-content/images', './plog-content/thumbs', './plog-content/uploads', './plog-includes', './plog-includes/lib');
+ foreach($files_to_read as $file) {
+ if (!is_readable(PLOGGER_DIR.$file)) {
+ $errors[] = sprintf(plog_tr('The path %s is not readable by the web server.'), ''.realpath(PLOGGER_DIR.$file).' ');
+ }
+ }
+
+ // Workaround for upgrading from beta1 since there are conflicting function in plog-functions.php and beta1 plog-connect.php
+ if (function_exists('is_safe_mode')) {
+ // If safe mode enabled, we will use the FTP workarounds to deal with folder permissions
+ if (!is_safe_mode()) {
+ // Make sure we have permission to write to these folders
+ $files_to_write = array('./plog-content/images', './plog-content/thumbs');
+ $i = 0;
+ foreach($files_to_write as $file) {
+ if (!is_writable(PLOGGER_DIR.$file)) {
+ $errors[] = sprintf(plog_tr('The path %s is not writable by the web server.'), ''.realpath(PLOGGER_DIR.$file).' ');
+ } else if (is_open_perms(realpath(PLOGGER_DIR.$file))) {
+ $_SESSION['plogger_close_perms'][basename($file)] = realpath(PLOGGER_DIR.$file);
+ }
+ }
+ if (isset($_SESSION['plogger_close_perms'])) {
+ if (!is_writable(PLOGGER_DIR.'plog-content/')) {
+ $errors[] = sprintf(plog_tr('Please temporarily CHMOD the %s directory to 0777 to allow Plogger to create initial directories for increased security. You will be prompted to CHMOD the directory back to 0755 after installation is complete.'), 'plog-content/ ');
+ }
+ }
+ }
+ }
+
+ return $errors;
+}
+
+function check_mysql_form($form) {
+ $errors = array();
+
+ if (empty($form['db_host'])) {
+ $errors[] = plog_tr('Please enter the name of your MySQL host.');
+ }
+
+ if (empty($form['db_user'])) {
+ $errors[] = plog_tr('Please enter the MySQL username.');
+ }
+
+ if (empty($form['db_name'])) {
+ $errors[] = plog_tr('Please enter the MySQL database name.');
+ }
+
+ return $errors;
+}
+
+function check_ftp_form($form) {
+ $errors = array();
+
+ if (empty($form['ftp_host'])) {
+ $errors[] = plog_tr('Please enter the name of your FTP host.');
+ }
+
+ if (empty($form['ftp_user'])) {
+ $errors[] = plog_tr('Please enter the FTP username.');
+ }
+
+ if (empty($form['ftp_pass'])) {
+ $errors[] = plog_tr('Please enter the FTP password.');
+ }
+
+ if (!empty($form['ftp_path'])) {
+ if (substr($form['ftp_path'], 0, 1) != '/'){
+ $form['ftp_path'] = '/'.$form['ftp_path'];
+ }
+ if (substr($form['ftp_path'], -1) != '/'){
+ $form['ftp_path'] = $form['ftp_path'].'/';
+ }
+ }
+
+ return array('errors' => $errors, 'form' => $form);
+}
+
+function check_ftp($host, $user, $pass, $path) {
+ $errors = array();
+
+ $connection = @ftp_connect($host);
+ if (!$connection) {
+ $errors[] = sprintf(plog_tr('Cannot connect to FTP host %s. Please check your FTP Host:'), ''.$host.' ');
+ } else {
+ $login = @ftp_login($connection, $user, $pass);
+ if (!$login) {
+ $errors[] = sprintf( plog_tr('Cannot login to FTP host %s with username %s and password %s. Please check your FTP Username: and FTP Password:'), ''.$host.' ', ''.$user.' ', ''.$pass.' ');
+ } else {
+ $checkdir = @ftp_chdir($connection, $path.'plog-content/images/'); // Check to see if the plog-content/images/ folder is accessible
+ if (!$checkdir) {
+ $errors[] = sprintf(plog_tr('Cannot find the Plogger %s directory along the path %s. Please check your FTP path to Plogger base folder (from FTP login):'), 'plog-content/images/ ', ''.$path.' ');
+ }
+ }
+ }
+ @ftp_close($connection);
+ return $errors;
+}
+
+/**** Install Functions ****/
+
+function do_install($form) {
+ $form = array_map('stripslashes', $form);
+ $form = array_map('trim', $form);
+
+ // First check the requirements
+ $errors = check_requirements();
+ if (sizeof($errors) > 0) {
+ echo "\t" . ''.plog_tr('Plogger cannot be installed until the following problems are resolved').':
';
+ echo "\n\n\t\t" . '';
+ foreach($errors as $error) {
+ echo "\n\t\t\t" . ''.$error.' ';
+ }
+ echo "\n\t\t" . ' ';
+ echo "\n\n\t" . '' . "\n";
+ return false;
+ }
+
+ $ok = false;
+ $errors = array();
+
+ // If we've already defined the database information, pass the values and skip them on the form
+ if (defined('PLOGGER_DB_HOST')) {
+ $mysql = check_mysql(PLOGGER_DB_HOST, PLOGGER_DB_USER, PLOGGER_DB_PW, PLOGGER_DB_NAME);
+ if (!empty($mysql)) {
+ $mysql_fail = true;
+ } else {
+ unset($_SESSION['plogger_config']);
+ }
+ // Set the form values equal to config values if already set
+ if (empty($form['db_host'])) {
+ $form['db_host'] = PLOGGER_DB_HOST;
+ }
+ if (empty($form['db_user'])) {
+ $form['db_user'] = PLOGGER_DB_USER;
+ }
+ if (empty($form['db_pass'])) {
+ $form['db_pass'] = PLOGGER_DB_PW;
+ }
+ if (empty($form['db_name'])) {
+ $form['db_name'] = PLOGGER_DB_NAME;
+ }
+ }
+
+ if (isset($form['action']) && $form['action'] == 'install') {
+ if (!defined('PLOGGER_DB_HOST') || isset($mysql_fail)) {
+ $mysql_form_check = check_mysql_form($form);
+ if (!empty($mysql_form_check)) {
+ $errors = array_merge($errors, $mysql_form_check);
+ }
+ }
+
+ if (empty($form['gallery_name'])) {
+ $errors[] = plog_tr('Please enter the name for your gallery.');
+ }
+
+ if (empty($form['admin_email'])) {
+ $errors[] = plog_tr('Please enter your email address.');
+ }
+
+ if (empty($form['admin_username'])) {
+ $errors[] = plog_tr('Please enter a username.');
+ }
+
+ if (empty($form['admin_password'])) {
+ $errors[] = plog_tr('Please enter a password.');
+ }
+
+ if ($form['admin_password'] != $form['admin_password_confirm']) {
+ $errors[] = plog_tr('Your passwords do not match. Please try again.');
+ }
+
+ if (is_safe_mode()) {
+ // If safe_mode enabled, check the FTP information form inputs
+ $ftp_form_check = check_ftp_form($form);
+ $form = $ftp_form_check['form'];
+ if (!empty($ftp_form_check['form']['errors'])) {
+ $errors = array_merge($errors, $ftp_form_check['form']['errors']);
+ }
+ }
+
+ if (empty($errors)) {
+ $mysql_errors = check_mysql($form['db_host'], $form['db_user'], $form['db_pass'], $form['db_name']);
+ if (is_safe_mode()) {
+ $ftp_errors = check_ftp($form['ftp_host'], $form['ftp_user'], $form['ftp_pass'], $form['ftp_path']);
+ } else {
+ $ftp_errors = array();
+ }
+ $errors = array_merge($mysql_errors, $ftp_errors);
+ $ok = empty($errors);
+ }
+
+ if (!$ok) {
+ echo '' . "\n\t" . '';
+ echo join(" \n\t", $errors);
+ echo " \n \n\n";
+ } else {
+ $_SESSION['install_values'] = array(
+ 'gallery_name' => $form['gallery_name'],
+ 'admin_email' => $form['admin_email'],
+ 'admin_password' => $form['admin_password'],
+ 'admin_username' => $form['admin_username']
+ );
+ if (is_safe_mode()) {
+ $_SESSION['ftp_values'] = array(
+ 'ftp_host' => $form['ftp_host'],
+ 'ftp_user' => $form['ftp_user'],
+ 'ftp_pass' => $form['ftp_pass'],
+ 'ftp_path' => $form['ftp_path']
+ );
+ }
+
+ if (!defined('PLOGGER_DB_HOST') || isset($mysql_fail)) {
+ // Serve the config file and ask user to upload it to webhost
+ $_SESSION['plogger_config'] = create_config_file($form['db_host'], $form['db_user'], $form['db_pass'], $form['db_name']);
+ }
+ return true;
+ }
+ }
+
+ include(PLOGGER_DIR.'plog-admin/includes/install-form-setup.php');
+ return false;
+}
+
+function create_tables() {
+ $default_charset = get_default_charset();
+
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'collections'
+ ,"`name` varchar(128) NOT NULL default '',
+ `description` varchar(255) NOT NULL default '',
+ `path` varchar(255) NOT NULL default '',
+ `id` int(11) NOT NULL auto_increment,
+ `thumbnail_id` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`)"
+ ,"Type=MyISAM $default_charset");
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'albums'
+ ," `name` varchar(128) NOT NULL default '',
+ `id` int(11) NOT NULL auto_increment,
+ `description` varchar(255) NOT NULL default '',
+ `path` varchar(255) NOT NULL default '',
+ `parent_id` int(11) NOT NULL default '0',
+ `thumbnail_id` int(11) NOT NULL default '0',
+ PRIMARY KEY (`id`),
+ INDEX pid_idx (`parent_id`)"
+ ," Type=MyISAM $default_charset");
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'pictures'
+ ,"`path` varchar(255) NOT NULL default '',
+ `parent_album` int(11) NOT NULL default '0',
+ `parent_collection` int(11) NOT NULL default '0',
+ `caption` mediumtext NOT NULL,
+ `description` text NOT NULL,
+ `id` int(11) NOT NULL auto_increment,
+ `date_modified` timestamp(14) NOT NULL,
+ `date_submitted` timestamp(14) NOT NULL,
+ `EXIF_date_taken` varchar(64) NOT NULL default '',
+ `EXIF_camera` varchar(64) NOT NULL default '',
+ `EXIF_shutterspeed` varchar(64) NOT NULL default '',
+ `EXIF_focallength` varchar(64) NOT NULL default '',
+ `EXIF_flash` varchar(64) NOT NULL default '',
+ `EXIF_aperture` varchar(64) NOT NULL default '',
+ `EXIF_iso` varchar(64) NOT NULL default '',
+ `allow_comments` int(11) NOT NULL default '1',
+ PRIMARY KEY (`id`),
+ INDEX pa_idx (`parent_album`),
+ INDEX pc_idx (`parent_collection`)"
+ ,"Type=MyISAM $default_charset");
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'comments'
+ ,"`id` int(11) NOT NULL auto_increment,
+ `parent_id` int(11) NOT NULL default '0',
+ `author` varchar(64) NOT NULL default '',
+ `email` varchar(64) NOT NULL default '',
+ `url` varchar(64) NOT NULL default '',
+ `date` datetime NOT NULL,
+ `comment` longtext NOT NULL,
+ `ip` char(64),
+ `approved` tinyint default '1',
+ PRIMARY KEY (`id`),
+ INDEX pid_idx (`parent_id`),
+ INDEX approved_idx (`approved`)"
+ ,"Type=MyISAM $default_charset");
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'config'
+ ,"`gallery_name` varchar(255) NOT NULL default '',
+ `gallery_url` varchar(255) NOT NULL default '',
+ `admin_username` varchar(64) NOT NULL default '',
+ `admin_email` varchar(50) NOT NULL default '',
+ `admin_password` varchar(64) NOT NULL default '',
+ `activation_key` varchar(64) NOT NULL default '',
+ `date_format` varchar(64) NOT NULL default '',
+ `compression` int(11) NOT NULL default '75',
+ `thumb_num` int(11) NOT NULL default '0',
+ `default_sortby` varchar(20) NOT NULL default '',
+ `default_sortdir` varchar(5) NOT NULL default '',
+ `album_sortby` varchar(20) NOT NULL default '',
+ `album_sortdir` varchar(5) NOT NULL default '',
+ `collection_sortby` varchar(20) NOT NULL default '',
+ `collection_sortdir` varchar(5) NOT NULL default '',
+ `allow_dl` smallint(1) NOT NULL default '0',
+ `allow_comments` smallint(1) NOT NULL default '1',
+ `allow_print` smallint(1) NOT NULL default '1',
+ `truncate` int(11) NOT NULL default '0',
+ `feed_num_entries` int(15) NOT NULL default '15',
+ `feed_title` text NOT NULL,
+ `feed_content` tinyint NOT NULL default '1',
+ `use_mod_rewrite` tinyint NOT NULL default '0',
+ `comments_notify` tinyint NOT NULL default '1',
+ `comments_moderate` tinyint NOT NULL default '0',
+ `theme_dir` varchar(128) NOT NULL default '',
+ `thumb_nav_range` int(11) NOT NULL default '0',
+ `allow_fullpic` tinyint default '1',
+ PRIMARY KEY (`thumb_num`)"
+ ,"Type=MyISAM $default_charset");
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'thumbnail_config'
+ ,"`id` int(10) unsigned NOT NULL auto_increment,
+ `update_timestamp` int(10) unsigned default NULL,
+ `max_size` int(10) unsigned default NULL,
+ `disabled` tinyint default '0',
+ `resize_option` tinyint default '2',
+ PRIMARY KEY (`id`)"
+ ,"Type=MyISAM $default_charset");
+
+ /*maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'tag2picture'
+ ,"`tag_id` bigint(20) unsigned NOT NULL default '0',
+ `picture_id` bigint(20) unsigned NOT NULL default '0',
+ `tagdate` datetime default NULL,
+ KEY `tag_id` (`tag_id`),
+ KEY `picture_id` (`picture_id`)"
+ ,"Type=MyISAM $default_charset");
+
+ maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'tags'
+ ,"`id` bigint(20) unsigned NOT NULL auto_increment,
+ `tag` char(50) NOT NULL default '',
+ `tagdate` datetime NOT NULL default '0000-00-00 00:00:00',
+ `urlified` char(50) NOT NULL default '',
+ PRIMARY KEY (`id`),
+ UNIQUE `tag` (`tag`),
+ UNIQUE `urlified` (`urlified`)"
+ ,"Type=MyISAM $default_charset");*/
+
+}
+
+function configure_plogger($form) {
+ // Use a random timestamp from the past to keep the existing thumbnails
+ $long_ago = 1096396500;
+
+ $thumbnail_sizes = array(
+ THUMB_SMALL => 100,
+ THUMB_LARGE => 500,
+ THUMB_RSS => 400,
+ THUMB_NAV => 60
+ );
+
+ foreach($thumbnail_sizes as $key => $size) {
+ $resize = ($key == THUMB_SMALL || $key == THUMB_NAV) ? 3: 2;
+ $sql = "INSERT INTO `".PLOGGER_TABLE_PREFIX."thumbnail_config` (`id`, `update_timestamp`, `max_size`, `resize_option`)
+ VALUES('$key', '$long_ago', '$size', '$resize')";
+ mysql_query($sql);
+ }
+
+ $config['gallery_url'] = 'http://'.$_SERVER['SERVER_NAME'].dirname(dirname($_SERVER['PHP_SELF']));
+ // Remove plog-admin/ from the end, if present .. is there a better way to determine the full url?
+ if (strpos($config['gallery_url'], 'plog-admin/')) {
+ $config['gallery_url'] = substr($config['gallery_url'], 0, strpos($config['gallery_url'], 'plog-admin/'));
+ }
+ // Verify that gallery URL contains a trailing slash. if not, add one.
+ if ($config['gallery_url']{strlen($config['gallery_url'])-1} != '/') {
+ $config['gallery_url'] .= '/';
+ }
+ // Verify that the gallery URL begins with 'http://' for mod_rewrite 301 redirects
+ if (strpos($config['gallery_url'], 'http://') === false) {
+ $config['gallery_url'] = 'http://'.$config['gallery_url'];
+ }
+ $config['admin_username'] = $form['admin_username'];
+ $config['admin_password'] = $form['admin_password'];
+ $config['admin_email'] = $form['admin_email'];
+ $config['gallery_name'] = $form['gallery_name'];
+
+ $config = array_map('mysql_real_escape_string', $config);
+
+ $row_exist = mysql_query("SELECT * FROM `".PLOGGER_TABLE_PREFIX."config`");
+ $row_exist_num = mysql_num_rows($row_exist);
+
+ if ($row_exist_num == 0) {
+ $query = "INSERT INTO `".PLOGGER_TABLE_PREFIX."config`
+ (`theme_dir`,
+ `compression`,
+ `thumb_num`,
+ `admin_username`,
+ `admin_email`,
+ `admin_password`,
+ `date_format`,
+ `feed_title`,
+ `gallery_name`,
+ `gallery_url`)
+ VALUES
+ ('default',
+ 75,
+ 20,
+ '${config['admin_username']}',
+ '${config['admin_email']}',
+ MD5('${config['admin_password']}'),
+ 'n.j.Y',
+ 'Plogger Photo Feed',
+ '${config['gallery_name']}',
+ '${config['gallery_url']}')";
+ } else {
+ $query = "UPDATE `".PLOGGER_TABLE_PREFIX."config` SET
+ `theme_dir` = 'default',
+ `compression` = 75,
+ `thumb_num` = 20,
+ `admin_username` = '${config['admin_username']}',
+ `admin_email` = '${config['admin_email']}',
+ `admin_password` = MD5('${config['admin_password']}'),
+ `date_format` = 'n.j.Y',
+ `feed_title` = 'Plogger Photo Feed',
+ `gallery_name` = '${config['gallery_name']}',
+ `gallery_url` = '${config['gallery_url']}'";
+ }
+ mysql_query($query);
+
+ // Create the FTP columns in the config table if safe_mode enabled/
+ if (is_safe_mode() && isset($_SESSION['ftp_values'])) {
+ configure_ftp($_SESSION['ftp_values']);
+ }
+
+ // Send an email with the username and password
+ $from = str_replace('www.', '', $_SERVER['HTTP_HOST']);
+ ini_set('sendmail_from', 'noreply@'.$from); // Set for Windows machines
+ @mail(
+ $config['admin_email'],
+ plog_tr('[Plogger] Your new gallery'),
+ plog_tr('You have successfully installed your new Plogger gallery.') . "\n\n" .sprintf(plog_tr('You can log in and manage it at %s'), $config['gallery_url'].'plog-admin/') . "\n\n" .plog_tr('Username').': '.$config['admin_username']. "\n" .plog_tr('Password').': '.$config['admin_password'],
+ 'From: Plogger '
+ );
+}
+
+function configure_ftp($form) {
+ maybe_add_column(PLOGGER_TABLE_PREFIX.'config', 'ftp_host', "varchar(64) NOT NULL default ''");
+ maybe_add_column(PLOGGER_TABLE_PREFIX.'config', 'ftp_user', "varchar(64) NOT NULL default ''");
+ maybe_add_column(PLOGGER_TABLE_PREFIX.'config', 'ftp_pass', "varchar(64) NOT NULL default ''");
+ maybe_add_column(PLOGGER_TABLE_PREFIX.'config', 'ftp_path', "varchar(255) NOT NULL default ''");
+ $query = "UPDATE `".PLOGGER_TABLE_PREFIX."config` SET
+ `ftp_host` = '".mysql_real_escape_string($form['ftp_host'])."',
+ `ftp_user` = '".mysql_real_escape_string($form['ftp_user'])."',
+ `ftp_pass` = '".mysql_real_escape_string($form['ftp_pass'])."',
+ `ftp_path` = '".mysql_real_escape_string($form['ftp_path'])."'";
+ mysql_query($query);
+}
+
+function fix_open_perms($dirs, $action = 'rename') {
+ if (!empty($dirs)) {
+ foreach ($dirs as $key => $dir) {
+ if ($action == 'delete') {
+ kill_dir(PLOGGER_DIR.'plog-content/'.$key);
+ } else {
+ @rename(PLOGGER_DIR.'plog-content/'.$key, PLOGGER_DIR.'plog-content/'.$key.'-old');
+ }
+ makeDirs(PLOGGER_DIR.'plog-content/'.$key);
+ }
+ }
+}
+
+function create_config_file($db_host, $db_user, $db_pass, $db_name) {
+ $cfg_file = "";
+ return $cfg_file;
+}
+
+/**** Upgrade Functions ****/
+
+function upgrade_database() {
+ global $config, $thumbnail_config;
+ $default_charset = get_default_charset();
+ $output = array();
+
+/** plogger_thumbnail_config **/
+ $thumb_table = maybe_add_table(
+ PLOGGER_TABLE_PREFIX.'thumbnail_config'
+ ,"`id` int(10) unsigned NOT NULL auto_increment,
+ `update_timestamp` int(10) unsigned default NULL,
+ `max_size` int(10) unsigned default NULL,
+ `disabled` tinyint default 0,
+ PRIMARY KEY (`id`)"
+ );
+
+ if ($thumb_table === true) {
+ $output[] = plog_tr('Added new table').': '.PLOGGER_TABLE_PREFIX.'thumbnail_config';
+ // Use a random timestamp from the past to keep the existing thumbnails
+ $long_ago = 1096396500;
+
+ if (!isset($config['max_thumbnail_size'])) {
+ $config['max_thumbnail_size'] = 100;
+ }
+ if (!isset($thumbnail_config[THUMB_SMALL]) || empty($thumbnail_config[THUMB_SMALL]['size'])) {
+ $sql = "INSERT INTO `".PLOGGER_TABLE_PREFIX."thumbnail_config` (id, update_timestamp, max_size)
+ VALUES('".THUMB_SMALL."', '".$long_ago."', '".$config['max_thumbnail_size']."')";
+ mysql_query($sql);
+ }
+
+ if (!isset($config['max_display_size'])) {
+ $config['max_display_size'] = 500;
+ }
+ if (!isset($thumbnail_config[THUMB_LARGE]) || empty($thumbnail_config[THUMB_LARGE]['size'])) {
+ $sql = "INSERT INTO `".PLOGGER_TABLE_PREFIX."thumbnail_config` (id, update_timestamp, max_size)
+ VALUES('".THUMB_LARGE."', '".$long_ago."', '".$config['max_display_size']."')";
+ mysql_query($sql);
+ }
+
+ if (!isset($config['rss_thumbsize'])) {
+ $config['rss_thumbsize'] = 400;
+ }
+ if (!isset($thumbnail_config[THUMB_RSS]) || empty($thumbnail_config[THUMB_RSS]['size'])) {
+ $sql = "INSERT INTO `".PLOGGER_TABLE_PREFIX."thumbnail_config` (id, update_timestamp, max_size)
+ VALUES('".THUMB_RSS."', '".$long_ago."', '".$config['rss_thumbsize']."')";
+ mysql_query($sql);
+ }
+
+ if (!isset($config['nav_thumbsize'])) {
+ $config['nav_thumbsize'] = 60;
+ }
+ if (!isset($thumbnail_config[THUMB_NAV]) || empty($thumbnail_config[THUMB_NAV]['size'])) {
+ $sql = "INSERT INTO `".PLOGGER_TABLE_PREFIX."thumbnail_config` (id, update_timestamp, max_size)
+ VALUES('".THUMB_NAV."', '".$long_ago."', '".$config['nav_thumbsize']."')";
+ mysql_query($sql);
+ }
+ }
+
+ $thumbnail_add_list = array(
+ 'disabled' => "tinyint default 0",
+ 'resize_option' => "tinyint default 2"
+ );
+ foreach ($thumbnail_add_list as $key => $value) {
+ $result = maybe_add_column(PLOGGER_TABLE_PREFIX.'thumbnail_config', $key, $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+ // Make sure to set the resize_option to square for small thumbs if previously set
+ if (isset($config['square_thumbs']) && $config['square_thumbs'] == 1) {
+ $sql = "UPDATE `".PLOGGER_TABLE_PREFIX."thumbnail_config` SET `resize_option` = '3' WHERE `id` = '".THUMB_SMALL."'";
+ mysql_query($sql);
+ }
+
+ // Move enable_thumb_nav setting to plogger_thumbnail_config table
+ if (isset($config['enable_thumb_nav'])) {
+ $disabled = ($config['enable_thumb_nav'] == 0) ? 1 : 0;
+ $sql = "UPDATE `".PLOGGER_TABLE_PREFIX."thumbnail_config` SET `disabled` = '$disabled' WHERE `id` = '".THUMB_NAV."'";
+ mysql_query($sql);
+ }
+
+ // set navigation thumbnails to square
+ $sql = "UPDATE `".PLOGGER_TABLE_PREFIX."thumbnail_config` SET `resize_option` = '3' WHERE `id` = '".THUMB_NAV."'";
+ mysql_query($sql);
+
+
+/** plogger_config **/
+ $config_drop_list = array(
+ 'max_thumbnail_size',
+ 'max_display_size',
+ 'rss_thumbsize',
+ 'feed_language',
+ 'enable_thumb_nav',
+ 'square_thumbs'
+ );
+ foreach ($config_drop_list as $value) {
+ $result = maybe_drop_column(PLOGGER_TABLE_PREFIX.'config', $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+ $config_add_list = array(
+ 'gallery_url' => "varchar(255) NOT NULL",
+ // RSS config
+ 'feed_num_entries' => "int(15) NOT NULL default '15'",
+ 'feed_title' => "varchar(255) NOT NULL default 'Plogger Photo Feed'",
+ 'feed_content' => "tinyint default '1'",
+ // Cruft-free URLs
+ 'use_mod_rewrite' => "smallint NOT NULL default '0'",
+ // Default sort order
+ 'default_sortdir' => "varchar(5) NOT NULL",
+ 'default_sortby' => "varchar(20) NOT NULL",
+ // Add field for admin email
+ 'admin_email' => "varchar(50) NOT NULL",
+ // Disable link to full size pic
+ 'allow_fullpic' => "tinyint NOT NULL default '1'",
+ // Comment notify
+ 'comments_notify' => "tinyint NOT NULL",
+ // Comment moderation
+ 'comments_moderate' => "tinyint NOT NULL default 0",
+ // User definable theme directory
+ 'theme_dir' => "varchar(128) NOT NULL",
+ // Add support for user defined sort order for albums and collections
+ 'album_sortby' => "varchar(20) NOT NULL default 'id'",
+ 'album_sortdir' => "varchar(5) NOT NULL default 'DESC'",
+ 'collection_sortby' => "varchar(20) NOT NULL default 'id'",
+ 'collection_sortdir' => "varchar(5) NOT NULL default 'DESC'",
+ // Add support for thumbnail configuration
+ 'thumb_nav_range' => "int(11) NOT NULL default 0",
+ // Add reset password activation key
+ 'activation_key' => "varchar(64) NOT NULL default ''"
+ );
+ foreach ($config_add_list as $key => $value) {
+ $result = maybe_add_column(PLOGGER_TABLE_PREFIX.'config', $key, $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+ // Insert the gallery_url if not already set
+ if (!isset($config['gallery_url']) || empty($config['gallery_url'])) {
+ $config['baseurl'] = 'http://'.$_SERVER['HTTP_HOST'].dirname(dirname($_SERVER['PHP_SELF'])).'/';
+ $output[] = plog_tr('Setting gallery url to ').$config['baseurl'];
+ $sql = "UPDATE `".PLOGGER_TABLE_PREFIX."config` SET gallery_url = '".$config['baseurl']."'";
+ mysql_query($sql);
+ }
+
+ // Insert default theme directory if not already set
+ if (!isset($config['theme_dir']) || empty($config['theme_dir'])) {
+ $output[] = plog_tr('Setting default theme directory to \'default\'');
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."config SET `theme_dir` = 'default' WHERE 1";
+ mysql_query($sql);
+ }
+
+/** plogger_collections **/
+ $collections_add_list = array(
+ // Selectable thumbnails
+ 'thumbnail_id' => "int(11) NOT NULL default 0",
+ // Add the path column
+ 'path' => "varchar(255) NOT NULL"
+ );
+ foreach ($collections_add_list as $key => $value) {
+ $result = maybe_add_column(PLOGGER_TABLE_PREFIX.'collections', $key, $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+/** plogger_albums **/
+ $albums_add_list = array(
+ // Selectable thumbnails
+ 'thumbnail_id' => "int(11) NOT NULL default 0",
+ // Add the path column
+ 'path' => "varchar(255) NOT NULL"
+ );
+ foreach ($albums_add_list as $key => $value) {
+ $result = maybe_add_column(PLOGGER_TABLE_PREFIX.'albums', $key, $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+/** plogger_pictures **/
+ $pictures_add_list = array(
+ // Add description
+ 'description' => "text",
+ 'EXIF_iso' => "varchar(64) NOT NULL default ''"
+ );
+ foreach ($pictures_add_list as $key => $value) {
+ $result = maybe_add_column(PLOGGER_TABLE_PREFIX.'pictures', $key, $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+/** plogger_comments **/
+ $comments_add_list = array(
+ // Add ip and approved fields to comments table
+ 'ip' => "char(64)",
+ 'approved' => "tinyint default 1"
+ );
+ foreach ($comments_add_list as $key => $value) {
+ $result = maybe_add_column(PLOGGER_TABLE_PREFIX.'comments', $key, $value);
+ if (!empty($result)) {
+ $output[] = $result;
+ }
+ }
+
+ /*$output[] = maybe_add_table(PLOGGER_TABLE_PREFIX.'tag2picture',"
+ `tag_id` bigint(20) unsigned NOT NULL default '0',
+ `picture_id` bigint(20) unsigned NOT NULL default '0',
+ `tagdate` datetime default NULL,
+ KEY `tag_id` (`tag_id`),
+ KEY `picture_id` (`picture_id`)
+ ");
+
+ $output[] = maybe_add_table(PLOGGER_TABLE_PREFIX.'tags',"
+ `id` bigint(20) unsigned NOT NULL auto_increment,
+ `tag` char(50) NOT NULL default '',
+ `tagdate` datetime NOT NULL default '0000-00-00 00:00:00',
+ `urlified` char(50) NOT NULL default '',
+ PRIMARY KEY (`id`),
+ UNIQUE `tag` (`tag`),
+ UNIQUE `urlified` (`urlified`)
+ ");*/
+
+ $sql = 'ALTER TABLE '.PLOGGER_TABLE_PREFIX.'comments ADD INDEX approved_idx (`approved`)';
+ mysql_query($sql);
+
+ // Add ip and approved fields to comments table
+ $sql = 'ALTER TABLE '.PLOGGER_TABLE_PREFIX.'comments CHANGE `date` `date` datetime';
+ mysql_query($sql);
+
+ // Convert charsets
+ // Since 4.1 MySQL has support for specifying character encoding for tables
+ // and I really want to use it if available. So we need figure out what version
+ // we are running on and to the right hting
+ $mysql_version = mysql_get_server_info();
+ $mysql_charset_support = '4.1';
+ $default_charset = '';
+
+ if (1 == version_compare($mysql_version,$mysql_charset_support)) {
+ $charset = 'utf8';
+ $tables = array('collections', 'albums', 'pictures', 'comments', 'config', 'thumbnail_config');
+ foreach($tables as $table) {
+ $tablename = PLOGGER_TABLE_PREFIX.$table;
+ $sql = "ALTER TABLE $tablename DEFAULT CHARACTER SET $charset";
+ if (!mysql_query($sql)) {
+ $output[] = "failed to convert $tablename to $charset ".mysql_error();
+ }
+ }
+ }
+
+ return $output;
+}
+
+function upgrade_image_list() {
+ $list = array();
+ $total = 0;
+
+ // Strip 'images/' prefix from pictures table
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET path = SUBSTRING(path,8) WHERE SUBSTRING(path,1,7) = 'images/'";
+ mysql_query($sql);
+
+ // Update 'path' for collections table
+ $sql = "SELECT id,name FROM ".PLOGGER_TABLE_PREFIX."collections";
+ $result = mysql_query($sql);
+ while($row = mysql_fetch_assoc($result)) {
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."collections SET path = '".strtolower(sanitize_filename($row['name']))."' WHERE id = ".$row['id'];
+ mysql_query($sql);
+ if (!file_exists(PLOGGER_DIR.'plog-content/images/'.strtolower(sanitize_filename($row['name'])))) {
+ $list[$total] = array('container' => 1, 'new_path' => 'plog-content/images/'.strtolower(sanitize_filename($row['name'])));
+ $total++;
+ }
+ }
+
+ // Update 'path' for albums table
+ $sql = "SELECT a.id AS id, a.name AS name, c.path AS collection_path
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE a.parent_id = c.id";
+ $result = mysql_query($sql);
+ while($row = mysql_fetch_assoc($result)) {
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."albums SET path = '".strtolower(sanitize_filename($row['name']))."' WHERE id = ".$row['id'];
+ mysql_query($sql);
+ if (!file_exists(PLOGGER_DIR.'plog-content/images/'.$row['collection_path'].'/'.strtolower(sanitize_filename($row['name'])))) {
+ $list[$total] = array('container' => 1, 'new_path' => 'plog-content/images/'.$row['collection_path'].'/'.strtolower(sanitize_filename($row['name'])));
+ $total++;
+ }
+ }
+
+ // Loop through each image from the pictures table, get its parent album name and parent collection
+ $sql = "SELECT p.path AS path, p.id AS pid,c.path AS collection_path, a.path AS album_path
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."pictures p, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE p.parent_album = a.id AND p.parent_collection = c.id";
+ $result = mysql_query($sql);
+
+ while($row = mysql_fetch_assoc($result)) {
+ $filename = sanitize_filename(basename($row['path']));
+ $c_directory = $row['collection_path'].'/';
+ $a_directory = $row['collection_path'].'/'.$row['album_path'].'/';
+ $new_path = $row['collection_path'].'/'.$row['album_path'].'/'.$filename;
+ // If the file exists, grab the information and add to the total
+ if (!file_exists(PLOGGER_DIR.'plog-content/images/'.$new_path)) {
+ // First see if it's in the old directory structure
+ if (file_exists(PLOGGER_DIR.'images/'.$row['path'])) {
+ $path = 'images/';
+ // Next check the temporary folder location for closing folder permissions
+ } else if (file_exists(PLOGGER_DIR.'plog-content/images-old/'.$row['path'])) {
+ $path = 'plog-content/images-old/';
+ // Otherwise check if it's in the new structure, but set up without new sanitized paths
+ } else if (file_exists(PLOGGER_DIR.'plog-content/images/'.$row['path'])) {
+ $path = 'plog-content/images/';
+ } else {
+ // Have no idea where the old image is
+ $path = '';
+ }
+ $list[$total] = array('id' => $row['pid'], 'old_path' => $path.$row['path'], 'new_path' => $new_path);
+ $total++;
+ }
+ }
+
+ // Add any photos from the uploads directory
+ if (file_exists(PLOGGER_DIR.'uploads/')) {
+ $old_uploads = get_files(PLOGGER_DIR.'uploads/', false, false, dirname(dirname(dirname(__FILE__))).'/uploads/');
+ $new_uploads = get_files(PLOGGER_DIR.'plog-content/uploads/', false, false, dirname(dirname(dirname(__FILE__))).'/plog-content/uploads/');
+
+ // Compare the two paths for differences
+ $compare_uploads = array_diff($old_uploads, $new_uploads);
+ foreach ($compare_uploads as $uploads) {
+ $list[$total] = array('uploads' => 1, 'old_path' => 'uploads/'.$uploads, 'new_path' => 'plog-content/uploads/'.$uploads);
+ $total++;
+ }
+ }
+
+ $list['total'] = $total;
+ return $list;
+}
+
+function upgrade_images($num, $list) {
+ $output = array();
+ $errors = array();
+ $count = 0;
+
+ $list = array_slice($list, 0, $num);
+
+ foreach ($list as $image) {
+ if (!empty($image['id'])) {
+ // Work on the images - move physical file, create directory if necessary and update path in database
+ if (!makeDirs(PLOGGER_DIR.'plog-content/images/'.dirname($image['new_path'].'/'))) {
+ $errors[] = plog_tr('Could not create directory').': '.PLOGGER_DIR.'plog-content/images/'.$image['new_path'];
+ } else {
+ if (!move_this(PLOGGER_DIR.$image['old_path'], PLOGGER_DIR.'plog-content/images/'.$image['new_path'])) {
+ $errors[] = plog_tr('Could not move file').': '.PLOGGER_DIR.$image['old_path'];
+ } else {
+ @chmod(PLOGGER_DIR.$new_path, PLOGGER_CHMOD_DIR);
+ $output[] = sprintf(plog_tr('Moved file %s -> %s'), ''.$image['old_path'].' ', ''.'plog-content/images/'.$image['new_path'].' ');
+ // Update database
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET path = '".mysql_real_escape_string($image['new_path'])."' WHERE id = '".$image['id']."'";
+ run_query($sql);
+ // Generate a new small thumbnail after database has been updated in case script times out
+ $thumbpath = generate_thumb($image['new_path'], $image['id'], THUMB_SMALL);
+ $count++;
+ }
+ }
+ } else if (!empty($image['uploads'])) {
+ // Work on the uploads - move physical file and create directory in the uploads folder if necessary and update path in database
+ if (!makeDirs(PLOGGER_DIR.dirname($image['new_path'].'/'))) {
+ $errors[] = plog_tr('Could not create directory').': '.PLOGGER_DIR.$image['new_path'];
+ } else {
+ if (!move_this(PLOGGER_DIR.$image['old_path'], PLOGGER_DIR.$image['new_path'])) {
+ $errors[] = plog_tr('Could not move file').': '.PLOGGER_DIR.$image['old_path'];
+ } else {
+ @chmod(PLOGGER_DIR.$new_path, PLOGGER_CHMOD_DIR);
+ $output[] = sprintf(plog_tr('Moved file %s -> %s'), ''.$image['old_path'].' ', ''.$image['new_path'].' ');
+ $count++;
+ }
+ }
+ } else if (!empty($image['container'])) {
+ // Create the collection and album directory structure
+ if (!makeDirs(PLOGGER_DIR.$image['new_path'].'/')) {
+ $errors[] = plog_tr('Could not create directory').': '.PLOGGER_DIR.$image['new_path'];
+ } else {
+ $output[] = sprintf(plog_tr('Created directory %s'), ''.$image['new_path'].' ');
+ $count++;
+ }
+ }
+ }
+
+ return array('errors' => $errors, 'output' => $output, 'count' => $count);
+}
+
+function check_list() {
+ $themes = array();
+ $translations = array();
+
+ // See if there are any old themes
+ if (file_exists(PLOGGER_DIR.'themes/')) {
+ $themes_old = get_files(PLOGGER_DIR.'themes/', true, false, dirname(dirname(dirname(__FILE__))).'/themes/');
+ if (!empty($themes_old)) {
+ foreach ($themes_old as $theme) {
+ if (!empty($theme) && $theme != 'index.php') {
+ $theme_parts = explode('/', $theme);
+ $themes[] = $theme_parts[0].'/';
+ }
+ }
+ $themes = array_unique($themes);
+ }
+ }
+
+ // See if there are any old translations
+ if (file_exists(PLOGGER_DIR.'plog-translations/')) {
+ $translations_old = get_files(PLOGGER_DIR.'plog-translations/', true, false, dirname(dirname(dirname(__FILE__))).'/plog-translations/');
+ if (!empty($translations_old)) {
+ foreach ($translations_old as $trans) {
+ if (!empty($trans)) {
+ $translations[] = $trans;
+ }
+ }
+ $translations = array_unique($translations);
+ }
+ }
+
+ return array('themes' => $themes, 'translations' => $translations);
+}
+
+function cleanup_list() {
+ $files = array();
+ $folders = array();
+
+ $file_list = array(
+ '_install.php',
+ '_upgrade.php',
+ 'plog-captcha.php',
+ 'plog-connect.php',
+ 'plog-functions.php',
+ 'plog-load_config.php',
+ 'plog-tag-functions.php',
+ 'set_session_var.php',
+ 'dynamics.js',
+ 'slideshow.js',
+ 'captcha.ttf',
+ 'plog-includes/plog-comment.php',
+ 'plog-includes/plog-tag-functions.php'
+ );
+ foreach ($file_list as $file) {
+ if (file_exists(PLOGGER_DIR.$file)) {
+ $files[] = PLOGGER_DIR.$file;
+ }
+ }
+
+ $folder_list = array(
+ 'admin/',
+ 'css/',
+ 'graphics/',
+ 'images/',
+ 'lib/',
+ 'plog-translations/',
+ 'themes/',
+ 'thumbs/',
+ 'uploads/',
+ 'summary/',
+ 'plog-content/images-old/',
+ 'plog-content/thumbs-old/'
+ );
+ foreach ($folder_list as $folder) {
+ if (file_exists(PLOGGER_DIR.$folder)) {
+ $folders[] = PLOGGER_DIR.$folder;
+ }
+ }
+
+ return array('files' => $files, 'folders' => $folders);
+}
+
+function cleanup_files($files, $folders) {
+ global $config;
+ $output = array();
+ $errors = array();
+
+ // Delete the files first
+ foreach ($files as $file) {
+ if (file_exists($file)) {
+ if (kill_file($file)) {
+ $output[] = plog_tr('Plogger found and deleted the file').': '.$file;
+ } else {
+ $errors[] = plog_tr('Plogger could not delete the file').': '.$file;
+ }
+ }
+ }
+
+ // Remove the folders since there should be no files in them
+ foreach ($folders as $folder) {
+ if (file_exists($folder)) {
+ if (kill_dir($folder)) {
+ $output[] = plog_tr('Plogger found and deleted the folder').': '.$folder;
+ } else {
+ $errors[] = plog_tr('Plogger could not delete the folder').': '.$folder;
+ }
+ }
+ }
+
+ return array('errors' => $errors, 'output' => $output);
+}
+
+?>
\ No newline at end of file
diff --git a/plog-admin/index.php b/plog-admin/index.php
new file mode 100644
index 0000000..bb2832f
--- /dev/null
+++ b/plog-admin/index.php
@@ -0,0 +1,114 @@
+'.plog_tr('You have been successfully logged out').'' . "\n\n";
+}
+
+// Reset password messages
+if (isset($_REQUEST['checkemail'])) {
+ switch($_REQUEST['checkemail']) {
+
+ case 1: // Reset password request completed, check email for confirmation link
+ $output .= "\t" . ''.plog_tr('Please check your email for the confirmation link').'.
' . "\n\n";
+ break;
+
+ case 2: // Verification successful, password has been reset, check email
+ $output .= "\t" . ''.plog_tr('Verification successful. Please check your email for the new password').'.
' . "\n\n";
+ break;
+
+ case 3: // Password has been reset, login and reset password to permanent one
+ $output .= "\t" . ''.plog_tr('Please change your password after logging in').'.
' . "\n\n";
+ break;
+
+ }
+}
+
+/* Error messages */
+// Login error message
+if (isset($_REQUEST['loginerror'])) {
+ // Invalid login info, either username or password didn't match
+ $output .= "\t" . ''.plog_tr('Invalid username or password').'
' . "\n\n";
+}
+
+// Reset password errors
+if (isset($_REQUEST['reseterror'])) {
+ switch($_REQUEST['reseterror']) {
+
+ case 1: // Password reset - invalid username or email address
+ $output .= "\t" . ''.plog_tr('Invalid admin username or email address').'
' . "\n\n";
+ break;
+
+ case 2: // Password reset - Verification link is invalid or expired
+ $output .= "\t" . ''.plog_tr('Sorry, that verification key does not appear to be valid').'.
' . "\n\n";
+ break;
+
+ case 3: // Password reset - email could not be sent
+ $output .= "\t" . ''.plog_tr('The email could not be sent. Possible reason: your host may have disabled the mail() function.').'
' . "\n\n";
+ break;
+ }
+}
+
+// Create the form information
+if(isset($_REQUEST['resetpassword'])) {
+ $output .= "\t" . ''.plog_tr('Please enter your username or email address and you will be notified via email the password reset process.').'
' . "\n\n";
+ $form_content = ''.plog_tr('Username or Email').'
+
+
' . "\n";
+ $link = ''.plog_tr('Log in').' ' . "\n";
+} else {
+ $form_content = ''.plog_tr('Username').':
+ '.plog_tr('Password').':
+
' . "\n";
+ $link = ''.plog_tr('Reset password').' ' . "\n";
+}
+
+close_db();
+?>
+
+
+
+ Plogger
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plog-admin/js/ajax_editing.js b/plog-admin/js/ajax_editing.js
new file mode 100644
index 0000000..2995a0d
--- /dev/null
+++ b/plog-admin/js/ajax_editing.js
@@ -0,0 +1,61 @@
+function makeEditable(id){
+ Event.observe(id, 'click', function(){edit($(id))}, false);
+ Event.observe(id, 'mouseover', function(){showAsEditable($(id))}, false);
+ Event.observe(id, 'mouseout', function(){showAsEditable($(id), true)}, false);
+}
+
+function showAsEditable(obj, clear){
+ if (!clear){
+ Element.addClassName(obj, 'editable');
+ }else{
+ Element.removeClassName(obj, 'editable');
+ }
+}
+
+function edit(obj){
+ Element.hide(obj);
+
+ var textarea ='';
+
+ var button = '
';
+
+ new Insertion.After(obj, textarea+button);
+
+ Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);
+ Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);
+
+ document.getElementById(obj.id+'_edit').focus();
+
+}
+
+function cleanUp(obj, keepEditable){
+ Element.remove(obj.id+'_editor');
+ Element.show(obj);
+ if (!keepEditable) showAsEditable(obj, true);
+}
+
+function saveChanges(obj){
+ var new_content = encodeURIComponent($F(obj.id+'_edit'));
+ new_content = new_content.replace(/%C2%A0/gi, '%20');
+
+ obj.innerHTML = ' ';
+ cleanUp(obj, true);
+
+ var success = function(t){editComplete(t, obj);}
+ var failure = function(t){editFailed(t, obj);}
+
+ var url = 'plog-rpc.php';
+ var pars = 'action=update&field=' + obj.id + '&content=' + new_content;
+ var myAjax = new Ajax.Request(url, {method:'post',
+ postBody:pars, onSuccess:success, onFailure:failure});
+}
+
+function editComplete(t, obj){
+ obj.innerHTML = t.responseText;
+ showAsEditable(obj, true);
+}
+
+function editFailed(t, obj){
+ obj.innerHTML = 'Sorry, the update failed.';
+ cleanUp(obj);
+}
diff --git a/plog-admin/js/lightbox.js b/plog-admin/js/lightbox.js
new file mode 100644
index 0000000..efe97c4
--- /dev/null
+++ b/plog-admin/js/lightbox.js
@@ -0,0 +1,409 @@
+/*
+ Lightbox JS: Fullsize Image Overlays
+ by Lokesh Dhakar - http://www.huddletogether.com
+
+ For more information on this script, visit:
+ http://huddletogether.com/projects/lightbox/
+
+ Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
+ (basically, do anything you want, just leave my name and link)
+
+ Table of Contents
+ -----------------
+ Configuration
+
+ Functions
+ - getPageScroll()
+ - getPageSize()
+ - pause()
+ - getKey()
+ - listenKey()
+ - showLightbox()
+ - hideLightbox()
+ - initLightbox()
+ - addLoadEvent()
+
+ Function Calls
+ - addLoadEvent(initLightbox)
+
+*/
+
+//
+// Configuration
+//
+
+// If you would like to use a custom loading image or close button reference them in the next two lines.
+var loadingImage = 'images/loading.gif';
+var closeButton = '';
+
+//
+// getPageScroll()
+// Returns array with x,y page scroll values.
+// Core code from - quirksmode.org
+//
+function getPageScroll(){
+
+ var yScroll;
+
+ if (self.pageYOffset) {
+ yScroll = self.pageYOffset;
+ } else if (document.documentElement && document.documentElement.scrollTop){ // Explorer 6 Strict
+ yScroll = document.documentElement.scrollTop;
+ } else if (document.body) {// all other Explorers
+ yScroll = document.body.scrollTop;
+ }
+
+ arrayPageScroll = new Array('',yScroll)
+ return arrayPageScroll;
+}
+
+//
+// getPageSize()
+// Returns array with page width, height and window width, height
+// Core code from - quirksmode.org
+// Edit for Firefox by pHaez
+//
+function getPageSize(){
+
+ var xScroll, yScroll;
+
+ if (window.innerHeight && window.scrollMaxY) {
+ xScroll = document.body.scrollWidth;
+ yScroll = window.innerHeight + window.scrollMaxY;
+ } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
+ xScroll = document.body.scrollWidth;
+ yScroll = document.body.scrollHeight;
+ } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
+ xScroll = document.body.offsetWidth;
+ yScroll = document.body.offsetHeight;
+ }
+
+ var windowWidth, windowHeight;
+ if (self.innerHeight) { // all except Explorer
+ windowWidth = self.innerWidth;
+ windowHeight = self.innerHeight;
+ } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
+ windowWidth = document.documentElement.clientWidth;
+ windowHeight = document.documentElement.clientHeight;
+ } else if (document.body) { // other Explorers
+ windowWidth = document.body.clientWidth;
+ windowHeight = document.body.clientHeight;
+ }
+
+ // for small pages with total height less then height of the viewport
+ if(yScroll < windowHeight){
+ pageHeight = windowHeight;
+ } else {
+ pageHeight = yScroll;
+ }
+
+ // for small pages with total width less then width of the viewport
+ if(xScroll < windowWidth){
+ pageWidth = windowWidth;
+ } else {
+ pageWidth = xScroll;
+ }
+
+ arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight)
+ return arrayPageSize;
+}
+
+//
+// pause(numberMillis)
+// Pauses code execution for specified time. Uses busy code, not good.
+// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
+//
+function pause(numberMillis) {
+ var now = new Date();
+ var exitTime = now.getTime() + numberMillis;
+ while (true) {
+ now = new Date();
+ if (now.getTime() > exitTime)
+ return;
+ }
+}
+
+//
+// getKey(key)
+// Gets keycode. If 'x' is pressed then it hides the lightbox.
+//
+
+function getKey(e){
+ if (e == null) { // ie
+ keycode = event.keyCode;
+ } else { // mozilla
+ keycode = e.which;
+ }
+ key = String.fromCharCode(keycode).toLowerCase();
+
+ if(key == 'x'){ hideLightbox(); }
+}
+
+//
+// listenKey()
+//
+function listenKey () { document.onkeypress = getKey; }
+
+//
+// showLightbox()
+// Preloads images. Pleaces new image in lightbox then centers and displays.
+//
+function showLightbox(objLink)
+{
+ // prep objects
+ var objOverlay = document.getElementById('overlay');
+ var objLightbox = document.getElementById('lightbox');
+ var objCaption = document.getElementById('lightboxCaption');
+ var objImage = document.getElementById('lightboxImage');
+ var objLoadingImage = document.getElementById('loadingImage');
+ var objLightboxDetails = document.getElementById('lightboxDetails');
+
+ var arrayPageSize = getPageSize();
+ var arrayPageScroll = getPageScroll();
+
+ // center loadingImage if it exists
+ if (objLoadingImage) {
+ objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageSize[3] - 35 - objLoadingImage.height) / 2) + 'px');
+ objLoadingImage.style.left = (((arrayPageSize[0] - 20 - objLoadingImage.width) / 2) + 'px');
+ objLoadingImage.style.display = 'block';
+ }
+
+ // set height of Overlay to take up whole page and show
+ objOverlay.style.height = (arrayPageSize[1] + 'px');
+ objOverlay.style.display = 'block';
+
+ // preload image
+ imgPreload = new Image();
+
+ imgPreload.onload=function(){
+ objImage.src = objLink.href;
+
+ // center lightbox and make sure that the top and left values are not negative
+ // and the image placed outside the viewport
+ var lightboxTop = arrayPageScroll[1] + ((arrayPageSize[3] - 35 - imgPreload.height) / 2);
+ var lightboxLeft = ((arrayPageSize[0] - 20 - imgPreload.width) / 2);
+
+ objLightbox.style.top = (lightboxTop < 0) ? "0px" : lightboxTop + "px";
+ objLightbox.style.left = (lightboxLeft < 0) ? "0px" : lightboxLeft + "px";
+
+ objLightboxDetails.style.width = imgPreload.width + 'px';
+
+ if(objLink.getAttribute('title')){
+ objCaption.style.display = 'block';
+ //objCaption.style.width = imgPreload.width + 'px';
+ objCaption.innerHTML = objLink.getAttribute('title');
+ } else {
+ objCaption.style.display = 'none';
+ }
+
+ // A small pause between the image loading and displaying is required with IE,
+ // this prevents the previous image displaying for a short burst causing flicker.
+ if (navigator.appVersion.indexOf("MSIE")!=-1){
+ pause(250);
+ }
+
+ if (objLoadingImage) { objLoadingImage.style.display = 'none'; }
+
+ // Hide select boxes as they will 'peek' through the image in IE
+ selects = document.getElementsByTagName("select");
+ for (i = 0; i != selects.length; i++) {
+ selects[i].style.visibility = "hidden";
+ }
+
+ objLightbox.style.display = 'block';
+
+ // After image is loaded, update the overlay height as the new image might have
+ // increased the overall page height.
+ arrayPageSize = getPageSize();
+ objOverlay.style.height = (arrayPageSize[1] + 'px');
+
+ // Check for 'x' keypress
+ listenKey();
+
+ return false;
+ }
+
+ imgPreload.src = objLink.href;
+
+}
+
+//
+// hideLightbox()
+//
+function hideLightbox()
+{
+ // get objects
+ objOverlay = document.getElementById('overlay');
+ objLightbox = document.getElementById('lightbox');
+
+ // hide lightbox and overlay
+ objOverlay.style.display = 'none';
+ objLightbox.style.display = 'none';
+
+ // make select boxes visible
+ selects = document.getElementsByTagName("select");
+ for (i = 0; i != selects.length; i++) {
+ selects[i].style.visibility = "visible";
+ }
+
+ // disable keypress listener
+ document.onkeypress = '';
+}
+
+//
+// initLightbox()
+// Function runs on window load, going through link tags looking for rel="lightbox".
+// These links receive onclick events that enable the lightbox display for their targets.
+// The function also inserts html markup at the top of the page which will be used as a
+// container for the overlay pattern and the inline image.
+//
+function initLightbox()
+{
+
+ if (!document.getElementsByTagName){ return; }
+ var anchors = document.getElementsByTagName("a");
+
+ // loop through all anchor tags
+ for (var i=0; i
+ //
+ //
+ //
+
+ var objBody = document.getElementsByTagName("body").item(0);
+
+ // create overlay div and hardcode some functional styles (aesthetic styles are in CSS file)
+ var objOverlay = document.createElement("div");
+ objOverlay.setAttribute('id','overlay');
+ objOverlay.onclick = function () {hideLightbox(); return false;}
+ objOverlay.style.display = 'none';
+ objOverlay.style.position = 'absolute';
+ objOverlay.style.top = '0';
+ objOverlay.style.left = '0';
+ objOverlay.style.zIndex = '90';
+ objOverlay.style.width = '100%';
+ objBody.insertBefore(objOverlay, objBody.firstChild);
+
+ var arrayPageSize = getPageSize();
+ var arrayPageScroll = getPageScroll();
+
+ // preload and create loader image
+ var imgPreloader = new Image();
+
+ // if loader image found, create link to hide lightbox and create loadingimage
+ imgPreloader.onload=function(){
+
+ var objLoadingImageLink = document.createElement("a");
+ objLoadingImageLink.setAttribute('href','#');
+ objLoadingImageLink.onclick = function () {hideLightbox(); return false;}
+ objOverlay.appendChild(objLoadingImageLink);
+
+ var objLoadingImage = document.createElement("img");
+ objLoadingImage.src = loadingImage;
+ objLoadingImage.setAttribute('id','loadingImage');
+ objLoadingImage.style.position = 'absolute';
+ objLoadingImage.style.zIndex = '150';
+ objLoadingImageLink.appendChild(objLoadingImage);
+
+ imgPreloader.onload=function(){}; // clear onLoad, as IE will flip out w/animated gifs
+
+ return false;
+ }
+
+ imgPreloader.src = loadingImage;
+
+ // create lightbox div, same note about styles as above
+ var objLightbox = document.createElement("div");
+ objLightbox.setAttribute('id','lightbox');
+ objLightbox.style.display = 'none';
+ objLightbox.style.position = 'absolute';
+ objLightbox.style.zIndex = '100';
+ objBody.insertBefore(objLightbox, objOverlay.nextSibling);
+
+ // create link
+ var objLink = document.createElement("a");
+ objLink.setAttribute('href','#');
+ objLink.setAttribute('title','Click to close');
+ objLink.onclick = function () {hideLightbox(); return false;}
+ objLightbox.appendChild(objLink);
+
+ // preload and create close button image
+ var imgPreloadCloseButton = new Image();
+
+ // if close button image found,
+ imgPreloadCloseButton.onload=function(){
+
+ var objCloseButton = document.createElement("img");
+ objCloseButton.src = closeButton;
+ objCloseButton.setAttribute('id','closeButton');
+ objCloseButton.style.position = 'absolute';
+ objCloseButton.style.zIndex = '200';
+ objLink.appendChild(objCloseButton);
+
+ return false;
+ }
+
+ imgPreloadCloseButton.src = closeButton;
+
+ // create image
+ var objImage = document.createElement("img");
+ objImage.setAttribute('id','lightboxImage');
+ objLink.appendChild(objImage);
+
+ // create details div, a container for the caption and keyboard message
+ var objLightboxDetails = document.createElement("div");
+ objLightboxDetails.setAttribute('id','lightboxDetails');
+ objLightbox.appendChild(objLightboxDetails);
+
+ // create caption
+ var objCaption = document.createElement("div");
+ objCaption.setAttribute('id','lightboxCaption');
+ objCaption.style.display = 'none';
+ objLightboxDetails.appendChild(objCaption);
+
+ // create keyboard message
+ var objKeyboardMsg = document.createElement("div");
+ objKeyboardMsg.setAttribute('id','keyboardMsg');
+ objKeyboardMsg.innerHTML = 'press x to close';
+ objLightboxDetails.appendChild(objKeyboardMsg);
+
+}
+
+//
+// addLoadEvent()
+// Adds event to window.onload without overwriting currently assigned onload functions.
+// Function found at Simon Willison's weblog - http://simon.incutio.com/
+//
+function addLoadEvent(func)
+{
+ var oldonload = window.onload;
+ if (typeof window.onload != 'function'){
+ window.onload = func;
+ } else {
+ window.onload = function(){
+ oldonload();
+ func();
+ }
+ }
+
+}
+
+addLoadEvent(initLightbox); // run initLightbox onLoad
\ No newline at end of file
diff --git a/plog-admin/js/plogger.js b/plog-admin/js/plogger.js
new file mode 100644
index 0000000..2dd9b79
--- /dev/null
+++ b/plog-admin/js/plogger.js
@@ -0,0 +1,113 @@
+function checkAll(form) {
+ for (var i=0;i 0) {
+ fields[0].focus();
+ }
+}
+
+function updateThumbPreview(selectObj) {
+ var thumb = selectObj.options[selectObj.selectedIndex].style.backgroundImage;
+ selectObj.style.backgroundImage = thumb;
+}
+
+var importThumbCounter = 0;
+
+function onImportThumbComplete(request) {
+ var picDic = 'pic_' + importThumbs[importThumbCounter];
+ Element.update(picDic,request.responseText);
+ var progress = (importThumbCounter + 1)/ importThumbs.length * 100;
+ Element.update('progress',Math.round(progress) + '%');
+ if (importThumbCounter < importThumbs.length) {
+ importThumbCounter++;
+ requestImportThumb();
+ }
+};
+
+function requestImportThumb() {
+ new Ajax.Request('plog-thumb.php', {method: 'get', onComplete: onImportThumbComplete, parameters: 'img=' + importThumbs[importThumbCounter]});
+};
+
+function checkArchive(fileInput) {
+
+ // check the extension of the chosen file, if it is a zip file
+ // we want to disable the caption and description fields because
+ // these are going to be set on the import page.
+
+ var filePath = fileInput.value;
+ var fileParts = new Array();
+ var zipAlert = document.getElementById('zip-alert');
+
+ fileParts = filePath.split('.');
+ var fileExtension = fileParts[fileParts.length-1];
+
+ if (fileExtension.toLowerCase() == 'zip') {
+ document.getElementById('caption').value = '';
+ document.getElementById('description').value = '';
+ document.getElementById('caption').disabled = true;
+ document.getElementById('description').disabled = true;
+ document.getElementById('caption').style.background = "#fafafa";
+ document.getElementById('description').style.background = "#fafafa";
+ if (zipAlert != null) {
+ zipAlert.style.display = '';
+ }
+ } else {
+ document.getElementById('caption').disabled = false;
+ document.getElementById('description').disabled = false;
+ document.getElementById('caption').style.background = "#fff";
+ document.getElementById('description').style.background = "#fff";
+ if (zipAlert != null) {
+ zipAlert.style.display = 'none';
+ }
+ }
+
+}
+
+function toggle(obj) {
+ var objarray = obj.split(', ');
+ while (objarray.length > 0) {
+ var el = document.getElementById(objarray.shift());
+ if ( el.style.display != 'none' ) {
+ el.style.display = 'none';
+ } else {
+ el.style.display = '';
+ }
+ }
+
+};
\ No newline at end of file
diff --git a/plog-admin/js/prototype.js b/plog-admin/js/prototype.js
new file mode 100644
index 0000000..fa8576e
--- /dev/null
+++ b/plog-admin/js/prototype.js
@@ -0,0 +1,1781 @@
+/* Prototype JavaScript framework, version 1.4.0
+ * (c) 2005 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.4.0',
+ ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.inspect = function(object) {
+ try {
+ if (object == undefined) return 'undefined';
+ if (object == null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+}
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+Object.extend(String.prototype, {
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(eval);
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ },
+
+ toQueryParams: function() {
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
+ return pairs.inject({}, function(params, pairString) {
+ var pair = pairString.split('=');
+ params[pair[0]] = pair[1];
+ return params;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, len = oStringList.length; i < len; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ inspect: function() {
+ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+ }
+});
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value >= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value <= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ iterator(value = collections.pluck(index));
+ return value;
+ });
+ },
+
+ inspect: function() {
+ return '#';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0; i < this.length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ shift: function() {
+ var result = this[0];
+ for (var i = 0; i < this.length - 1; i++)
+ this[i] = this[i + 1];
+ this.length--;
+ return result;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+var Hash = {
+ _each: function(iterator) {
+ for (key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ do {
+ iterator(value);
+ value = value.succ();
+ } while (this.include(value));
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+ function() {return new XMLHttpRequest()}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responderToAdd) {
+ if (!this.include(responderToAdd))
+ this.responders.push(responderToAdd);
+ },
+
+ unregister: function(responderToRemove) {
+ this.responders = this.responders.without(responderToRemove);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (responder[callback] && typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ if (parameters.length > 0) parameters += '&_=';
+
+ try {
+ this.url = url;
+ if (this.options.method == 'get' && parameters.length > 0)
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method, this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ this.setRequestHeaders();
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
+
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type',
+ 'application/x-www-form-urlencoded');
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ header: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {}
+ },
+
+ evalJSON: function() {
+ try {
+ return eval(this.header('X-JSON'));
+ } catch (e) {}
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (event == 'Complete') {
+ try {
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, object) {
+ this.updateContent();
+ onComplete(transport, object);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts)
+ response = response.stripScripts();
+
+ if (receiver) {
+ if (this.options.insertion) {
+ new this.options.insertion(receiver, response);
+ } else {
+ Element.update(receiver, response);
+ }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ return $A(children).inject([], function(elements, child) {
+ if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ elements.push(child);
+ return elements;
+ });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) {
+ var Element = new Object();
+}
+
+Object.extend(Element, {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ update: function(element, html) {
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).include(className);
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).add(className);
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).remove(className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ }
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (name in style)
+ element.style[name.camelize()] = style[name];
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element._overflow = element.style.overflow;
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element.style.overflow = element._overflow;
+ element._overflow = undefined;
+ }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ if (this.element.tagName.toLowerCase() == 'tbody') {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set(this.select(function(className) {
+ return className != classNameToRemove;
+ }).join(' '));
+ },
+
+ toString: function() {
+ return this.toArray().join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ },
+
+ select: function(element) {
+ $(element).select();
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select)
+ element.select();
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.blur();
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
+ }
+ },
+
+ findFirstElement: function(form) {
+ return Form.getElements(form).find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ Field.activate(Form.findFirstElement(form));
+ },
+
+ reset: function(form) {
+ $(form).reset();
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'submit':
+ case 'hidden':
+ case 'password':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = element.options[index];
+ value = opt.value;
+ if (!value && !('value' in opt))
+ value = opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = new Array();
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) {
+ var optValue = opt.value;
+ if (!optValue && !('value' in opt))
+ optValue = opt.text;
+ value.push(optValue);
+ }
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+}
\ No newline at end of file
diff --git a/plog-admin/plog-admin-functions.php b/plog-admin/plog-admin-functions.php
new file mode 100644
index 0000000..19f15c3
--- /dev/null
+++ b/plog-admin/plog-admin-functions.php
@@ -0,0 +1,2010 @@
+ $qval) {
+ if ($qkey != 'entries_per_page' && $qkey != 'plog_page') {
+ $url_query .= $qkey.'='.$qval.'&';
+ }
+ }
+ } else {
+ $url_query .= str_replace('&', '&', $url_parts['query']).'&';
+ }
+ }
+
+ $java = 'document.location.href=\''.$url_parts['path'].$url_query.'entries_per_page=\'+this.options[this.selectedIndex].value';
+
+ $possible_values = array('1'=>1, '5'=>5, '10'=>10, '20'=>20, '50'=>50, '100'=>100, '250'=>250, '500'=>500);
+ $output= "\n\t\t\t" . ''.plog_tr('E ntries per page').'
+ ';
+ foreach ($possible_values as $key => $value) {
+ if ($_SESSION['entries_per_page'] == $key) {
+ $output .= "\n\t\t\t\t" . ''.$key.' ';
+ } else {
+ $output .= "\n\t\t\t\t" . ''.$key.' ';
+ }
+ }
+ $output.= "\n\t\t\t" . ' ';
+ $output.= "\n\t\t\t" . '';
+ $output.= "\n\t\t\t";
+ return $output;
+}
+
+function add_picture($album_id, $tmpname, $filename, $caption, $desc, $allow_comm = 1) {
+ global $config;
+
+ $filename_parts = explode('.', strrev($filename), 2);
+ $filename_base = strrev($filename_parts[1]);
+ $filename_ext = strtolower(strrev($filename_parts[0]));
+
+ $ext_array = array('jpg', 'jpeg', 'gif', 'png', 'bmp');
+
+ $result = array(
+ 'output' => '',
+ 'errors' => '',
+ 'picture_id' => false,
+ );
+
+ $i = 0;
+
+ $unique_filename_base = strtolower(sanitize_filename(SmartStripSlashes($filename_base), true));
+
+ // Now get the name of the collection
+ $sql = "SELECT c.path AS collection_path, c.id AS collection_id,
+ a.path AS album_path, a.id AS album_id
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE c.id = a.parent_id AND a.id = '$album_id'";
+
+ $sql_result = run_query($sql);
+ $albumdata = mysql_fetch_assoc($sql_result);
+
+ // This shouldn't happen in normal cases
+ if (empty($albumdata)) {
+ $result['errors'] .= plog_tr('No such album!');
+ return $result;
+ }
+
+ $dest_album_name = SmartStripSlashes($albumdata['album_path']);
+ $dest_collection_name = SmartStripSlashes($albumdata['collection_path']);
+
+ $create_path = $dest_collection_name.'/'.$dest_album_name;
+
+ foreach ($ext_array as $ext) {
+ while (is_file($config['basedir'].'plog-content/images/'.$create_path.'/'.$unique_filename_base.'.'.$ext)) {
+ $unique_filename_base = SmartStripSlashes($filename_base).'-'.++$i;
+ }
+ }
+
+ $final_filename = sanitize_filename($unique_filename_base).'.'.$filename_ext;
+
+ // Final fully qualified filename
+ $final_fqfn = $config['basedir'].'plog-content/images/'.$create_path.'/'.$final_filename;
+
+ if (!makeDirs($config['basedir'].'plog-content/images/'.$create_path)) {
+ $result['errors'] .= sprintf(plog_tr('Could not create directory %s!'), ''.$create_path.' ');
+ return $result;
+ }
+
+ if (is_uploaded_file($tmpname)) {
+ // If safe_mode enabled, open the permissions if the destination path
+ if (is_safe_mode()) {
+ $parent_path = $config['basedir'].'plog-content/images/'.$create_path;
+ chmod_ftp($parent_path, 0777);
+ }
+ if (!move_uploaded_file($tmpname, $final_fqfn)) {
+ $result['errors'] .= sprintf(plog_tr('Could not move uploaded file: %s to %s'), ''.$tmpname.' ', ''.$final_fqfn.' ');
+ }
+ // If safe_mode enabled, close the permissions back down to the default
+ if (is_safe_mode()) {
+ chmod_ftp($parent_path);
+ }
+ } else {
+ if (!move_this($tmpname, $final_fqfn)) {
+ $result['errors'] .= sprintf(plog_tr('Could not move file: %s to %s'), ''.$tmpname.' ', ''.$final_fqfn.' ');
+ }
+ }
+
+ if (empty($result['errors'])) {
+ if (is_file($tmpname)) {
+ kill_file($tmpname);
+ }
+ $res = @chmod($final_fqfn, PLOGGER_CHMOD_FILE);
+
+ // Get the EXIF data.
+ require_once(PLOGGER_DIR.'plog-includes/lib/exifer1_7/exif.php');
+ $exif_raw = read_exif_data_raw($final_fqfn, false);
+ $exif = array();
+
+ $exif['date_taken'] = (isset($exif_raw['SubIFD']['DateTimeOriginal'])) ? trim($exif_raw['SubIFD']['DateTimeOriginal']) : '';
+ $exif['camera'] = (isset($exif_raw['IFD0']['Make']) && isset($exif_raw['IFD0']['Model'])) ? trim($exif_raw['IFD0']['Make']).' '.trim($exif_raw['IFD0']['Model']) : '';
+ $exif['shutter_speed'] = (isset($exif_raw['SubIFD']['ExposureTime'])) ? $exif_raw['SubIFD']['ExposureTime'] : '';
+ $exif['focal_length'] = (isset($exif_raw['SubIFD']['FocalLength'])) ? $exif_raw['SubIFD']['FocalLength'] : '';
+ $exif['flash'] = (isset($exif_raw['SubIFD']['Flash'])) ? $exif_raw['SubIFD']['Flash'] : '';
+ $exif['aperture'] = (isset($exif_raw['SubIFD']['FNumber'])) ? $exif_raw['SubIFD']['FNumber'] : '';
+ $exif['iso'] = (isset($exif_raw['SubIFD']['ISOSpeedRatings'])) ? $exif_raw['SubIFD']['ISOSpeedRatings'] : '';
+
+ $picture_path = $create_path.'/'.$final_filename;
+
+ $query = "INSERT INTO `".PLOGGER_TABLE_PREFIX."pictures`
+ (`parent_collection`,
+ `parent_album`,
+ `path`,
+ `date_modified`,
+ `date_submitted`,
+ `allow_comments`,
+ `EXIF_date_taken`,
+ `EXIF_camera`,
+ `EXIF_shutterspeed`,
+ `EXIF_focallength`,
+ `EXIF_flash`,
+ `EXIF_aperture`,
+ `EXIF_iso`,
+ `caption`,
+ `description`)
+ VALUES
+ ('".$albumdata['collection_id']."',
+ '".$albumdata['album_id']."',
+ '".mysql_real_escape_string($picture_path)."',
+ NOW(),
+ NOW(),
+ ".intval($allow_comm).",
+ '".mysql_real_escape_string($exif['date_taken'])."',
+ '".mysql_real_escape_string($exif['camera'])."',
+ '".mysql_real_escape_string($exif['shutter_speed'])."',
+ '".mysql_real_escape_string($exif['focal_length'])."',
+ '".mysql_real_escape_string($exif['flash'])."',
+ '".mysql_real_escape_string($exif['aperture'])."',
+ '".mysql_real_escape_string($exif['iso'])."',
+ '".mysql_real_escape_string($caption)."',
+ '".mysql_real_escape_string($desc)."')";
+
+ $sql_result = run_query($query);
+
+ $result['output'] .= sprintf(plog_tr('Your image %s was uploaded successfully.'), ''.$filename.' ');
+ $result['picture_id'] = mysql_insert_id();
+
+ // Let's generate the thumbnail and the large thumbnail right away.
+ // This way, the user won't see any latency from the thumbnail generation
+ // when viewing the gallery for the first time
+ // This also helps with the image pre-loading problem introduced
+ // by a javascript slideshow.
+
+ $thumbpath = generate_thumb($picture_path, $result['picture_id'], THUMB_SMALL);
+ //$thumbpath = generate_thumb($picture_path, $result['picture_id'],THUMB_LARGE);
+ }
+
+ return $result;
+}
+
+function update_picture($id, $caption, $allow_comments, $description) {
+ $id = intval($id);
+ $caption = mysql_real_escape_string($caption);
+ $description = mysql_real_escape_string($description);
+ $allow_comments = intval($allow_comments);
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET
+ caption = '$caption',
+ description = '$description',
+ allow_comments = '$allow_comments'
+ WHERE id='$id'";
+ $result = mysql_query($query);
+ if ($result) {
+ return array('output' => plog_tr('You have successfully modified the selected picture.'));
+ } else {
+ return array('errors' => mysql_error());
+ }
+}
+
+function update_picture_field($picture_id, $field, $value) {
+ $fields = array('caption', 'description');
+ if (!in_array($field, $fields)) {
+ return array('errors' => plog_tr('Invalid action'));
+ }
+
+ $errors = $output = '';
+
+ $picture_id = intval($picture_id);
+ $value = mysql_real_escape_string(trim($value));
+
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET $field = '$value' WHERE id='$picture_id'";
+
+ $result = mysql_query($query);
+ if ($result) {
+ return array('output' => plog_tr('You have successfully modified the selected picture.'));
+ } else {
+ return array('errors' => plog_tr('Could not modify selected picture.'));
+ }
+
+}
+
+function move_picture($pic_id, $to_album) {
+ global $config, $thumbnail_config;
+ // We need the parent_id from the album we're changing to
+ $to_album = intval($to_album);
+ $pic_id = intval($pic_id);
+
+ $query = "SELECT * FROM ".PLOGGER_TABLE_PREFIX."albums WHERE `id` = '".$to_album."'";
+ $result = run_query($query);
+ $row = mysql_fetch_assoc($result);
+
+ if (!is_array($row)) {
+ return array('errors' => sprintf(plog_tr('There is no album with id %s.'), ''.$to_album.' '));
+ }
+
+ $new_collection = $row['parent_id'];
+
+ // Move picture to new location
+ // We need to query to get collection names and album names to find new directory path
+
+ $picture = get_picture_by_id($pic_id);
+ // If attempting to move within the same album, abort
+ if ($picture['parent_album'] == $to_album) {
+ return;
+ }
+ $album = get_album_by_id($to_album);
+
+ $filename = SmartStripSlashes(basename($picture['path']));
+ $target_path = SmartStripSlashes($album['collection_path']).'/'.SmartStripSlashes($album['album_path']);
+
+ $filename_parts = explode('.', strrev($filename), 2);
+ $filename_base = strrev($filename_parts[1]);
+ $filename_ext = strrev($filename_parts[0]);
+ $unique_filename_base = strtolower(SmartStripSlashes($filename_base));
+
+ $i = 0;
+ while ($to_album != $picture['parent_album'] && is_file($config['basedir'].'plog-content/images/'.$target_path.'/'.$unique_filename_base.'.'.$filename_ext)) {
+ $unique_filename_base = $filename_base.'('.++$i.')';
+ }
+
+ // Final fully qualified file name
+ $picture_path = $target_path.'/'.sanitize_filename($unique_filename_base).'.'.$filename_ext;
+ $final_fqfn = $config['basedir'].'plog-content/images/'.$picture_path;
+
+ $rename = move_this($config['basedir'].'plog-content/images/'.$picture['path'], $final_fqfn);
+ @chmod($final_fqfn, PLOGGER_CHMOD_FILE);
+
+ // Delete thumbnails
+ foreach($thumbnail_config as $tval) {
+ $thumbpath = $config['basedir'].'plog-content/thumbs/'.dirname($picture['path']).'/'.$tval['type'].'/'.$picture['id'].'-'.$filename;
+ if (file_exists($thumbpath)) {
+ kill_file($thumbpath);
+ }
+ }
+
+ if (!$rename) {
+ return array('errors' => sprintf(plog_tr('Could not move file: %s to %s'), ''.$picture['path'].' ', ''.$final_fqfn.' '));
+ }
+
+ // Check if collection thumbnail = picture moved to different collection and set to default if so
+ if ($picture['parent_collection'] != $new_collection) {
+ $collection = get_collection_by_id($picture['parent_collection']);
+ if ($collection['thumbnail_id'] == $picture['id']) {
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."collections SET `thumbnail_id`='0' WHERE id='".$collection['id']."'";
+ run_query($query);
+ }
+ }
+ // Check if album thumbnail = moved picture and set to default if so
+ $album = get_album_by_id($picture['parent_album']);
+ if ($album['thumbnail_id'] == $picture['id']) {
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."albums SET `thumbnail_id`='0' WHERE id='".$album['id']."'";
+ run_query($query);
+ }
+
+ // Update database
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET
+ path = '".mysql_real_escape_string($picture_path)."',
+ parent_album = '".$to_album."',
+ parent_collection = '".$new_collection."'
+ WHERE id = '".$pic_id."'";
+ if (!mysql_query($sql)) {
+ return array('errors' => mysql_error());
+ }
+ return array('output' => plog_tr('Success'));
+}
+
+function delete_picture($del_id) {
+ global $config, $thumbnail_config;
+ $del_id = intval($del_id);
+ $picture = get_picture_by_id($del_id);
+ if ($picture) {
+ // Check if collection thumbnail = deleted picture and set to default if so
+ $collection = get_collection_by_id($picture['parent_collection']);
+ if ($collection['thumbnail_id'] == $picture['id']) {
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."collections SET `thumbnail_id`='0' WHERE id='".$collection['id']."'";
+ run_query($query);
+ }
+ // Check if album thumbnail = deleted picture and set to default if so
+ $album = get_album_by_id($picture['parent_album']);
+ if ($album['thumbnail_id'] == $picture['id']) {
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."albums SET `thumbnail_id`='0' WHERE id='".$album['id']."'";
+ run_query($query);
+ }
+
+ $query = "DELETE FROM ".PLOGGER_TABLE_PREFIX."pictures WHERE `id`= '".$picture['id']."'";
+ run_query($query);
+
+ // Delete all comments for the picture
+ $query = "DELETE FROM ".PLOGGER_TABLE_PREFIX."comments WHERE `parent_id`= '".$picture['id']."'";
+ run_query($query);
+
+ // Make sure that the file is actually located inside our 'plog-content/images/' directory
+ $full_path = $config['basedir'].'plog-content/images/'.SmartStripSlashes($picture['path']);
+ // Also check whether this image is in the correct folder
+ $relative_path = substr($full_path, 0, strlen($config['basedir']));
+ $basename = SmartStripSlashes(basename($picture['path']));
+ if ($relative_path == $config['basedir']) {
+ foreach($thumbnail_config as $tval) {
+ $thumbpath = $config['basedir'].'plog-content/thumbs/'.dirname($picture['path']).'/'.$tval['type'].'/'.$picture['id'].'-'.$basename;
+ if (file_exists($thumbpath)) {
+ kill_file($thumbpath);
+ }
+ }
+ if (is_file($full_path)) {
+ if (!kill_file($full_path)) {
+ $errors = plog_tr('Could not physically delete file from disk!');
+ }
+ }
+ } else {
+ $errors = plog_tr('Picture has invalid path, ignoring delete request.');
+ }
+ } else {
+ $errors = sprintf(plog_tr('There is no picture with id %s.'), ''.$del_id.' ');
+ }
+ if (isset($errors)) {
+ return array('errors' => $errors);
+ }
+ return true;
+}
+
+function add_collection($collection_name, $description) {
+ global $config;
+ $output = $errors = '';
+ $id = 0;
+ $collection_name = trim(SmartStripSlashes($collection_name));
+ if (empty($collection_name)) {
+ return array('errors' => plog_tr('Please enter a valid name for the collection.'));
+ }
+
+ $collection_folder = strtolower(sanitize_filename($collection_name));
+
+ // First try to create the directory, and only if that succeeds, then insert a new
+ // row into collections table, otherwise the collection will not be usable anyway
+ $create_path = $config['basedir'].'plog-content/images/'.$collection_folder;
+
+ // Do not allow collections with duplicate names, otherwise mod_rewritten links will start
+ // to behave weird.
+ if (is_dir($create_path)) {
+ // If there is already a directory, check to see if it's in the database
+ $collection_data = get_collection_by_name($collection_name);
+ if ($collection_data) {
+ // It's in the database, so throw duplicate collection error
+ return array('errors' => sprintf(plog_tr('New collection could not be created, because there is already one named %s!'), ''.$collection_name.' '));
+ } else {
+ // It's not in the database so attempt to delete the directory
+ if (!kill_dir($create_path)) {
+ // Could not delete the directory, so prompt the user to delete it manually
+ return array('errors' => sprintf(plog_tr('Collection directory %s exists, but no collection exists in the database. Attempt to delete automatically failed. Please delete folder via FTP manually and try again.'), ''.$create_path.' '));
+ }
+ }
+ }
+
+ // Create directory
+ if (!makeDirs($create_path)) {
+ $errors .= sprintf(plog_tr('Could not create directory %s!'), ''.$create_path.' ');
+ } else {
+ $sql_name = mysql_real_escape_string($collection_name);
+ $description = mysql_real_escape_string($description);
+ $collection_folder = mysql_real_escape_string($collection_folder);
+ $query = "INSERT INTO ".PLOGGER_TABLE_PREFIX."collections (`name`,`description`,`path`) VALUES ('$sql_name', '$description', '$collection_folder')";
+ $result = run_query($query);
+ $id = mysql_insert_id();
+
+ $output .= sprintf(plog_tr('You have successfully created the collection %s.'), ''.$collection_name.' ');
+ }
+
+ // Caller can check the value of id, if it is zero, then collection creation failed
+ // errors and output are separate, because this way the caller can format the return value
+ // as it needs
+ $result = array(
+ 'output' => $output,
+ 'errors' => $errors,
+ 'id' => $id,
+ );
+ return $result;
+
+}
+
+function update_collection($collection_id, $name, $description, $thumbnail_id = 0) {
+ global $config;
+
+ $errors = $output = '';
+
+ $name = trim(SmartStripSlashes($name));
+ if (empty($name)) {
+ return array('errors' => plog_tr('Please enter a valid name for the collection.'));
+ }
+
+ $target_name = strtolower(sanitize_filename($name));
+
+ $errors = $output = '';
+
+ $collection_id = intval($collection_id);
+ $thumbnail_id = intval($thumbnail_id);
+
+ $name = mysql_real_escape_string($name);
+ $description = mysql_real_escape_string($description);
+
+ // Rename the directory
+ // First, get the collection name of our source collection
+ $sql = "SELECT c.path as collection_path, name
+ FROM ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE c.id = '$collection_id'";
+
+ $result = run_query($sql);
+ $row = mysql_fetch_assoc($result);
+
+ $source_collection_name = SmartStripSlashes($row['collection_path']);
+ $source_path = $config['basedir'].'plog-content/images/'.$source_collection_name;
+ $target_path = $config['basedir'].'plog-content/images/'.$target_name;
+
+ // Check for self-renaming collection instance
+ if ($source_path != $target_path) {
+ // Do not allow collections with duplicate names, otherwise mod_rewritten links will start
+ // to behave weird.
+ if (is_dir($target_path)) {
+ // If there is already a directory, check to see if it's in the database
+ $collection_data = get_collection_by_name($name);
+ if ($collection_data) {
+ // It's in the database, so throw duplicate collection error
+ return array('errors' => sprintf(plog_tr('Collection %s could not be renamed to %s, because there is another collection with that name.'), ''.$row['name'].' ', ''.$name.' '));
+ } else {
+ // It's not in the database so attempt to delete the directory
+ if (!kill_dir($target_path)) {
+ // Could not delete the directory, so prompt the user to delete it manually
+ return array('errors' => sprintf(plog_tr('Collection directory %s exists, but no collection exists in the database. Attempt to delete automatically failed. Please delete folder via FTP manually and try again.'), ''.$target_path.' '));
+ }
+ }
+ }
+
+ // Perform the rename on the directory
+ if (!move_this($source_path, $target_path)) {
+ return array('errors' => sprintf(plog_tr('Error renaming directory: %s to %s'), ''.$source_path.' ', ''.$target_path.' '));
+ }
+ }
+
+ $target_name = mysql_real_escape_string($target_name);
+
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."collections SET name = '$name', path = '$target_name', description = '$description', thumbnail_id = '$thumbnail_id' WHERE id='$collection_id'";
+ $result = mysql_query($query);
+ if (!$result) {
+ return array('errors' => mysql_error());
+ }
+
+ $output = plog_tr('You have successfully modified the selected collection.');
+
+ // XXX: Update the path only if a collection was actually renamed
+
+ // Update the path field for all pictures within that collection
+ // Now we need to update the database paths of all pictures within source album
+ $sql = "SELECT p.id AS id, p.path AS path, c.name AS collection_name, a.path AS album_path
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."pictures p, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE p.parent_album = a.id AND p.parent_collection = c.id AND p.parent_collection = '$collection_id'";
+
+ $result = run_query($sql);
+
+ while($row = mysql_fetch_assoc($result)) {
+
+ $filename = basename($row['path']);
+ $album_path = $row['album_path'];
+
+ $new_path = mysql_real_escape_string(SmartStripSlashes($target_name.'/'.$album_path.'/'.$filename));
+
+ // Update database
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET path = '$new_path' WHERE id = '$row[id]'";
+ mysql_query($sql) or ($output .= mysql_error());
+ }
+
+ return array(
+ 'errors' => $errors,
+ 'output' => $output,
+ );
+}
+
+function update_collection_field($collection_id, $field, $value) {
+ $fields = array('name', 'description');
+ if (!in_array($field, $fields)) {
+ return array('errors' => plog_tr('Invalid action'));
+ }
+
+ $errors = $output = '';
+
+ $collection_id = intval($collection_id);
+ $value = mysql_real_escape_string(trim($value));
+
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."collections SET $field = '$value' WHERE id='$collection_id'";
+
+ $result = mysql_query($query);
+ if ($result) {
+ return array('output' => plog_tr('You have successfully modified the selected collection.'));
+ } else {
+ return array('errors' => plog_tr('Could not modify selected collection.'));
+ }
+
+}
+
+function delete_collection($del_id) {
+ global $config;
+ $sql = "SELECT c.name AS collection_name, c.path AS collection_path, c.id AS collection_id
+ FROM ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE c.id = '$del_id'";
+
+ $result = run_query($sql);
+ $collection = mysql_fetch_assoc($result);
+
+ if (!$collection) {
+ return array('errors' => plog_tr('No such collection.'));
+ }
+
+ // First delete all albums registered with this album
+ $sql = 'SELECT * FROM '.PLOGGER_TABLE_PREFIX.'albums WHERE parent_id = '.$collection['collection_id'];
+ $result = run_query($sql);
+ while ($row = mysql_fetch_assoc($result)) {
+ delete_album($row['id']);
+ }
+
+ // XXX: un-register collection
+ $query = "DELETE FROM ".PLOGGER_TABLE_PREFIX."collections WHERE `id`= '".$collection['collection_id']."'";
+ run_query($query);
+
+ // Finally try to delete the directory itself. It will succeed, if there are no files left inside it ..
+ // If there are then .. how did they get there? Probably not through Plogger and in this case do we
+ // really want to delete those?
+ $source_collection_name = SmartStripSlashes($collection['collection_path']);
+
+ // Delete any thumbnails for the collection
+ $collection_thumb_directory = $config['basedir'].'plog-content/thumbs/'.$source_collection_name;
+ if (file_exists($collection_thumb_directory)) {
+ kill_dir($collection_thumb_directory);
+ }
+ // Check to see if the collection_directory is a real directory and then try to delete it
+ $collection_directory = $config['basedir'].'plog-content/images/'.$source_collection_name;
+ if (is_dir($collection_directory)) {
+ if (!kill_dir($collection_directory)) {
+ return array('errors' => plog_tr('Collection directory still contains files after all albums have been deleted.'));
+ }
+ } else {
+ return array('errors' => plog_tr('Collection has invalid path, not deleting directory.'));
+ }
+ return array();
+}
+
+function add_album($album_name, $description, $pid) {
+ global $config;
+ $output = $errors = '';
+ $id = 0;
+ $album_name = trim(SmartStripSlashes($album_name));
+ if (empty($album_name)) {
+ return array('errors' => plog_tr('Please enter a valid name for the album.'));
+ }
+ // Get the parent collection name
+ $query = "SELECT c.path as collection_path FROM ". PLOGGER_TABLE_PREFIX."collections c WHERE id = '$pid'";
+
+ $result = run_query($query);
+ $row = mysql_fetch_assoc($result);
+
+ // This shouldn't happen
+ if (empty($row)) {
+ return array('errors' => plog_tr('No such collection.'));
+ }
+
+ $album_folder = strtolower(sanitize_filename($album_name));
+
+ // First try to create the directory to hold the images, if that fails, then the album
+ // will be unusable anyway
+ $create_path = $config['basedir'].'plog-content/images/'.SmartStripSlashes($row['collection_path']).'/'.$album_folder;
+
+ // Check path so we are not creating duplicate albums within the same collection
+ if (is_dir($create_path)) {
+ // If there is already a directory, check to see if it's in the database
+ $album_data = get_album_by_name($album_name, $pid);
+ if ($album_data) {
+ // It's in the database, so throw duplicate album error
+ return array('output' => 'existing', 'id' => $album_data['id'], 'errors' => sprintf(plog_tr('New album could not be created, because there is already one named %s in the collection %s'), ''.$album_folder.' ', ''.ucfirst(SmartStripSlashes($row['collection_path']).' ')));
+ } else {
+ // It's not in the database so attempt to delete the directory
+ if (!kill_dir($create_path)) {
+ // Could not delete the directory, so prompt the user to delete it manually
+ return array('errors' => sprintf(plog_tr('Album directory %s exists, but no album exists in the database. Attempt to delete automatically failed. Please delete folder via FTP manually and try again.'), ''.$create_path.' '));
+ }
+ }
+ }
+
+ if (!makeDirs($create_path)) {
+ $errors .= sprintf(plog_tr('Could not create directory %s!'), ''.$path.' ');
+ } else {
+ $sql_name = mysql_real_escape_string($album_name);
+ $description = mysql_real_escape_string($description);
+ $album_folder = mysql_real_escape_string($album_folder);
+ $query = "INSERT INTO ".PLOGGER_TABLE_PREFIX."albums (`name`,`description`,`parent_id`,`path`) VALUES ('$sql_name', '$description', '$pid', '$album_folder')";
+ $result = run_query($query);
+ $id = mysql_insert_id();
+
+ $output .= sprintf(plog_tr('You have successfully created the album %s.'), ''.$album_name.' ');
+ }
+ // Caller can check the value of id, if it is zero, then album creation failed
+ // errors and output are separate, because this way the caller can format the return value
+ // as it needs
+ $result = array(
+ 'output' => $output,
+ 'errors' => $errors,
+ 'id' => $id,
+ );
+ return $result;
+}
+
+function update_album($album_id, $name, $description, $thumbnail_id = 0) {
+ global $config;
+ $errors = $output = '';
+
+ $album_id = intval($album_id);
+ $thumbnail_id = intval($thumbnail_id);
+ $name = mysql_real_escape_string(SmartStripSlashes(trim($name)));
+ $description = mysql_real_escape_string(SmartStripSlashes($description));
+ if (empty($name)) {
+ return array('errors' => plog_tr('Please enter a valid name for the album.'));
+ }
+
+ $target_name = strtolower(sanitize_filename(SmartStripSlashes($name)));
+
+ // First, get the album name and collection name of our source album
+ $sql = "SELECT c.path AS collection_path, a.path AS album_path, a.parent_id AS collection_id
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE c.id = a.parent_id AND a.id = ".$album_id;
+
+ $result = run_query($sql);
+ $row = mysql_fetch_assoc($result);
+
+ $source_album_name = SmartStripSlashes($row['album_path']);
+ $source_collection_name = SmartStripSlashes($row['collection_path']);
+
+ $source_path = $config['basedir'].'plog-content/images/'.$source_collection_name.'/'.$source_album_name;
+ $target_path = $config['basedir'].'plog-content/images/'.$source_collection_name.'/'.$target_name;
+
+ // Check for self-renaming album instance
+ if ($source_path != $target_path) {
+ // Check path so we are not creating duplicate albums within the same collection
+ if (is_dir($target_path)) {
+ // If there is already a directory, check to see if it's in the database
+ $album_data = get_album_by_name($name, $row['collection_id']);
+ if ($album_data) {
+ // It's in the database, so throw duplicate album error
+ return array('errors' => sprintf(plog_tr('New album could not be created, because there is already one named %s in the collection %s'), ''.$target_name.' ', ''.$source_collection_name.' '));
+ } else {
+ // It's not in the database so attempt to delete the directory
+ if (!kill_dir($target_path)) {
+ // Could not delete the directory, so prompt the user to delete it manually
+ return array('errors' => sprintf(plog_tr('Album directory %s exists, but no album exists in the database. Attempt to delete automatically failed. Please delete folder via FTP manually and try again.'), ''.$target_path.' '));
+ }
+ }
+ }
+
+ // Perform the rename on the directory
+ if (!move_this($source_path, $target_path)) {
+ return array(
+ 'errors' => sprintf(plog_tr('Error renaming directory: %s to %s'), ''.$source_path.' ', ''.$target_path.' '));
+ }
+ }
+
+ $target_name = mysql_real_escape_string($target_name);
+
+ // Proceed only if rename succeeded
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."albums SET
+ name = '$name',
+ description = '$description',
+ thumbnail_id = '$thumbnail_id',
+ path = '$target_name'
+ WHERE id='$album_id'";
+
+ $result = mysql_query($query);
+ if (!$result) {
+ return array('errors' => mysql_error());
+ }
+
+ $output .= plog_tr('You have successfully modified the selected album.');
+
+ // Update the path field for all pictures within that album
+ $sql = "SELECT p.path AS path, p.id AS id, c.name AS collection_name, a.name AS album_name
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."pictures p, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE p.parent_album = a.id AND p.parent_collection = c.id AND p.parent_album = '$album_id'";
+
+ $result = run_query($sql);
+
+ while($row = mysql_fetch_assoc($result)) {
+
+ $filename = basename($row['path']);
+ $new_path = mysql_real_escape_string(SmartStripSlashes($source_collection_name.'/'.$target_name.'/'.$filename));
+
+ // Update database
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET path = '$new_path' WHERE id = '$row[id]'";
+ mysql_query($sql) or ($errors .= mysql_error());
+ }
+
+ return array(
+ 'errors' => $errors,
+ 'output' => $output,
+ );
+}
+
+function update_album_field($album_id, $field, $value) {
+ $fields = array('name', 'description');
+ if (!in_array($field, $fields)) {
+ return array('errors' => plog_tr('Invalid action'));
+ }
+
+ $value = mysql_real_escape_string(trim(SmartStripSlashes($value)));
+ $errors = $output = '';
+ $album_id = intval($album_id);
+
+ // Proceed only if rename succeeded
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."albums SET
+ $field = '$value'
+ WHERE id='$album_id'";
+
+ $result = mysql_query($query);
+
+ if ($result) {
+ return array('output' => plog_tr('You have successfully modified the selected album.'));
+ } else {
+ return array('errors' => plog_tr('Could not modify selected album.'));
+ }
+}
+
+function move_album($album_id, $to_collection) {
+ global $config;
+
+ $res = array(
+ 'errors' => '',
+ 'output' => '',
+ );
+
+ $album_id = intval($album_id);
+ $to_collection = intval($to_collection);
+
+ $sql = "SELECT
+ c.path as collection_path,
+ c.thumbnail_id as collection_thumb,
+ c.id as collection_id,
+ a.path as album_path
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE c.id = a.parent_id AND a.id = '$album_id'";
+
+ $result = run_query($sql);
+ $row = mysql_fetch_assoc($result);
+
+ $source_album_name = SmartStripSlashes($row['album_path']);
+ $source_collection_name = SmartStripSlashes($row['collection_path']);
+ $source_collection_thumb = $row['collection_thumb'];
+ $source_collection_id = $row['collection_id'];
+
+ // If moving to same collection, abort
+ if ($to_collection == $source_collection_id) {
+ return;
+ }
+
+ // Next, get the collection name of our destination collection
+ $sql = "SELECT c.path as collection_path FROM ".PLOGGER_TABLE_PREFIX."collections c WHERE c.id = '$to_collection'";
+
+ $result = run_query($sql);
+ $row = mysql_fetch_assoc($result);
+
+ $target_collection_name = SmartStripSlashes($row['collection_path']);
+ $source_path = $config['basedir'].'plog-content/images/'.$source_collection_name.'/'.$source_album_name.'/';
+ $target_path = $config['basedir'].'plog-content/images/'.$target_collection_name.'/'.$source_album_name.'/';
+ $thumb_path = $config['basedir'].'plog-content/thumbs/'.$source_collection_name.'/'.$source_album_name.'/';
+
+ // Check path so we are not creating duplicate albums within the same collection
+ if (is_dir($target_path)) {
+ // If there is already a directory, check to see if it's in the database
+ $album_data = get_album_by_name($source_album_name, $to_collection);
+ if ($album_data) {
+ // It's in the database, so throw duplicate album error
+ return array('errors' => sprintf(plog_tr('New album could not be created, because there is already one named %s in the collection %s'), ''.$source_album_name.' ', ''.$target_collection_name.' '));
+ } else {
+ // It's not in the database so attempt to delete the directory
+ if (!kill_dir($target_path)) {
+ // Could not delete the directory, so prompt the user to delete it manually
+ return array('errors' => sprintf(plog_tr('Album directory %s exists, but no album exists in the database. Attempt to delete automatically failed. Please delete folder via FTP manually and try again.'), ''.$target_path.' '));
+ }
+ }
+ }
+
+ // Attempt to make new album directory in target collection
+ if (!makeDirs($target_path)) {
+ return array('errors' => sprintf(plog_tr('Could not create directory %s!'), ''.$target_path.' '));
+ }
+
+ // Now we need to update the database paths of all pictures within source album
+ $sql = "SELECT p.path as path, p.id as picture_id, c.name as collection_name, a.name as album_name
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."pictures p, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE p.parent_album = a.id AND p.parent_collection = c.id AND p.parent_album = '$album_id'";
+
+ $result = run_query($sql);
+ $pic_ids = array();
+
+ while($row = mysql_fetch_assoc($result)) {
+ $filename = SmartStripSlashes(basename($row['path']));
+ $pic_ids[] = $row['picture_id'];
+ $old_path = $source_path.$filename;
+ $new_path = $target_path.$filename;
+
+ if (!move_this($old_path, $new_path)) {
+ $res['errors'] .= sprintf(plog_tr('Could not move file: %s to %s'), ''.$old_path.' ', ''.$new_path.' ');
+ } else {
+ @chmod($new_path, PLOGGER_CHMOD_FILE);
+ }
+
+ $path_insert = mysql_real_escape_string($target_collection_name.'/'.$source_album_name.'/'.$filename);
+
+ $sql = "UPDATE ".PLOGGER_TABLE_PREFIX."pictures SET
+ parent_collection = '$to_collection',
+ path = '$path_insert'
+ WHERE id = '$row[picture_id]'";
+ mysql_query($sql) or ($res['errors'] .= mysql_error());
+ }
+
+ // Check if collection thumbnail = picture moved to different collection and set to default if so
+ if (in_array($source_collection_thumb, $pic_ids)) {
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."collections SET `thumbnail_id`='0' WHERE id='".$source_collection_id."'";
+ run_query($query);
+ }
+
+ // Update the parent id of the moved album
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."albums SET `parent_id` = '$to_collection' WHERE `id`='$album_id'";
+ $result = run_query($query);
+
+ // Attempt to delete the old folder and thumbnails if there were no errors moving the files
+ if ($res['errors'] == '') {
+ kill_dir($thumb_path);
+ $remove = kill_dir($source_path);
+ if (!$remove) {
+ $res['errors'] .= sprintf(plog_tr('Could not remove album from collection %s. Album still contains files after all pictures have been moved.'), ''.$source_collection_name.' ');
+ }
+ }
+ return $res;
+}
+
+function delete_album($del_id) {
+ global $config;
+ $sql = "SELECT c.name AS collection_name, a.name AS album_name, a.id AS album_id, c.path AS collection_path, a.path AS album_path
+ FROM ".PLOGGER_TABLE_PREFIX."albums a, ".PLOGGER_TABLE_PREFIX."collections c
+ WHERE c.id = a.parent_id AND a.id = '$del_id'";
+
+ $result = run_query($sql);
+ $album = mysql_fetch_assoc($result);
+
+ if (!$album) {
+ return array('errors' => plog_tr('No such album'));
+ }
+
+ // First delete all pictures registered with this album
+ $sql = 'SELECT * FROM '.PLOGGER_TABLE_PREFIX.'pictures WHERE parent_album = '.$album['album_id'];
+ $result = run_query($sql);
+ while ($row = mysql_fetch_assoc($result)) {
+ delete_picture($row['id']);
+ }
+
+ // XXX: un-register album
+ $query = "DELETE FROM ".PLOGGER_TABLE_PREFIX."albums WHERE `id`= '".$album['album_id']."'";
+ run_query($query);
+
+ // Finally try to delete the directory itself. It will succeed, if there are no files left inside it ..
+ // If there are then .. how did they get there? Probably not through Plogger and in this case do we
+ // really want to delete those?
+ $source_album_name = SmartStripSlashes($album['album_path']);
+ $source_collection_name = SmartStripSlashes($album['collection_path']);
+
+ // Delete any thumbnails for the album
+ $album_thumb_directory = $config['basedir'].'plog-content/thumbs/'.$source_collection_name.'/'.$source_album_name;
+ if (file_exists($album_thumb_directory)) {
+ kill_dir($album_thumb_directory);
+ }
+ // Check to see if the album_directory is a real directory and then try to delete it
+ $album_directory = $config['basedir'].'plog-content/images/'.$source_collection_name.'/'.$source_album_name;
+ if (is_dir($album_directory)) {
+ if (!kill_dir($album_directory)) {
+ return array('errors' => plog_tr('Album directory still contains files after all pictures have been deleted.'));
+ }
+
+ } else {
+ return array('errors' => plog_tr('Album has invalid path, not deleting directory.'));
+ }
+ return array();
+}
+
+function update_comment($id, $author, $email, $url, $comment) {
+ $id = intval($id);
+ $author = mysql_real_escape_string($author);
+ $email = mysql_real_escape_string($email);
+ $url = mysql_real_escape_string($url);
+ $comment = mysql_real_escape_string(trim($comment));
+
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."comments SET author = '$author', comment = '$comment', url = '$url', email = '$email' WHERE id='$id'";
+ $result = mysql_query($query);
+ if ($result) {
+ return array('output' => plog_tr('You have successfully modified the selected comment.'));
+ } else {
+ return array('errors' => plog_tr('Could not modify selected comment.'));
+ }
+}
+
+function update_comment_field($id, $field, $value) {
+ $allowed_fields = array('author', 'email', 'url', 'comment');
+ if (!in_array($field, $allowed_fields)) {
+ return array('errors' => plog_tr('Invalid action'));
+ }
+
+ $id = intval($id);
+ $value = mysql_real_escape_string($value);
+
+ $query = "UPDATE ".PLOGGER_TABLE_PREFIX."comments SET $field = '$value' WHERE id='$id'";
+ $result = mysql_query($query);
+ if ($result) {
+ return array('output' => plog_tr('You have successfully modified the selected comment.'));
+ } else {
+ return array('errors' => plog_tr('Could not modify selected comment.'));
+ }
+}
+
+function count_albums($parent_id = 0) {
+ if (!$parent_id)
+ $numquery = "SELECT COUNT(*) AS `num_albums` FROM `".PLOGGER_TABLE_PREFIX."albums`";
+ else
+ $numquery = "SELECT COUNT(*) AS `num_albums` FROM `".PLOGGER_TABLE_PREFIX."albums` WHERE parent_id = '$parent_id'";
+
+ $numresult = run_query($numquery);
+ $num_albums = mysql_result($numresult, 0, 'num_albums');
+ return $num_albums;
+}
+
+function count_collections() {
+
+ $numquery = "SELECT COUNT(*) AS `num_collections` FROM `".PLOGGER_TABLE_PREFIX."collections`";
+
+ $numresult = run_query($numquery);
+ $num_albums = mysql_result($numresult, 0, 'num_collections');
+ return $num_albums;
+}
+
+function count_pictures($parent_id = 0) {
+ if (!$parent_id)
+ $numquery = "SELECT COUNT(*) AS `num_pics` FROM `".PLOGGER_TABLE_PREFIX."pictures`";
+ else
+ $numquery = "SELECT COUNT(*) AS `num_pics` FROM `".PLOGGER_TABLE_PREFIX."pictures` WHERE parent_album = '$parent_id'";
+
+ $numresult = run_query($numquery);
+ $num_pics = mysql_result($numresult, 0, 'num_pics');
+ return $num_pics;
+}
+
+function count_comments($parent_id = false) {
+ $numquery = "SELECT COUNT(*) AS `num_comments` FROM `".PLOGGER_TABLE_PREFIX."comments` WHERE approved = 1";
+ if ($parent_id !== false) {
+ $numquery .= " AND parent_id = '".$parent_id."'";
+ }
+
+ $numresult = run_query($numquery);
+ $num_comments = mysql_result($numresult, 0, 'num_comments');
+ return $num_comments;
+}
+
+function plog_edit_comment_form($comment_id) {
+ $output = '';
+ $comment_id = intval($comment_id);
+ $sql = "SELECT * FROM ".PLOGGER_TABLE_PREFIX."comments c WHERE c.id = '$comment_id'";
+ $result = run_query($sql);
+ $comment = mysql_fetch_assoc($result);
+ if (!is_array($comment)) {
+ // XXX: return an error message instead
+ return false;
+ }
+ $query = '';
+ if (strpos($_SERVER['PHP_SELF'], 'plog-manage') !== false) {
+ $query = '?level=comments&id='.$comment['parent_id'];
+ }
+
+ $output .= "\n\t" . '' . "\n";
+ return $output;
+}
+
+function makeDirs($path, $mode = PLOGGER_CHMOD_DIR) { // Creates directory tree recursively
+ if (is_safe_mode()) {
+ return is_dir($path) or (makeDirs(dirname($path), $mode) and makeDirs_ftp($path));
+ } else {
+ return is_dir($path) or (makeDirs(dirname($path), $mode) and mkdir($path, $mode) and configure_blank_index($path) and chmod($path, $mode));
+ }
+}
+
+/** These functions are for safe_mode enabled servers **/
+function connect_ftp() {
+ global $config, $PLOGGER_FTP;
+
+ $ftp_server = $config['ftp_host'];
+ $ftp_user = $config['ftp_user'];
+ $ftp_pass = $config['ftp_pass'];
+
+ // Create connection
+ $PLOGGER_FTP = ftp_connect($ftp_server);
+ // Login to ftp server
+ $ftp_result = ftp_login($PLOGGER_FTP, $ftp_user, $ftp_pass);
+
+ // Check if connection was made
+ if ((!$PLOGGER_FTP) || (!$ftp_result)) {
+ return false;
+ }
+ return true;
+}
+
+function close_ftp() {
+ global $PLOGGER_FTP;
+
+ if (isset($PLOGGER_FTP)) {
+ ftp_close($PLOGGER_FTP);
+ }
+}
+
+function makeDirs_ftp($path) {
+ global $config, $PLOGGER_FTP;
+ $return = false;
+
+ $ftp_path = str_replace($config['basedir'], '', $path);
+ $ftp_dir = dirname($ftp_path);
+ $ftp_new_dir = str_replace($ftp_dir.'/', '', $ftp_path);
+
+ if (!isset($PLOGGER_FTP)) {
+ // Check if connection was made
+ $ftp_connection = connect_ftp();
+ if ($ftp_connection === false) {
+ return $return;
+ }
+ }
+ ftp_chdir($PLOGGER_FTP, $config['ftp_path'].$ftp_dir); // Go to destination dir
+ $ftp_create_dir = ftp_mkdir($PLOGGER_FTP, $ftp_new_dir); // Create directory
+ if ($ftp_create_dir) {
+ chmod_ftp($path, 0777);
+ configure_blank_index($path);
+ $chmod = decoct(PLOGGER_CHMOD_DIR);
+ $ftp_exec_dir = ftp_site($PLOGGER_FTP, 'CHMOD '.$chmod.' '.$ftp_new_dir.'/');
+ }
+ if ($ftp_exec_dir) {
+ $return = true;
+ } else {
+ echo 'could not chmod!';
+ }
+ return $return;
+}
+
+function chmod_ftp($path, $mode = PLOGGER_CHMOD_DIR) {
+ global $config, $PLOGGER_FTP;
+ $return = false;
+
+ $ftp_chmod_dir = str_replace($config['basedir'], $config['ftp_path'], $path);
+
+ if (!isset($PLOGGER_FTP)) {
+ // Check if connection was made
+ $ftp_connection = connect_ftp();
+ if ($ftp_connection === false) {
+ return $return;
+ }
+ }
+ $chmod = decoct($mode);
+ $ftp_exec_dir = @ftp_site($PLOGGER_FTP, 'CHMOD '.$chmod.' '.$ftp_chmod_dir);
+ if ($ftp_exec_dir) {
+ $return = true;
+ }
+ return $return;
+}
+/** END functions for safe_mode enabled servers **/
+
+function configure_htaccess_fullpic($allow = false) {
+ $cfg = '';
+ $placeholder_start = '# BEGIN Plogger';
+ $placeholder_end = '# END Plogger';
+ $thisfile = '/plog-admin/'.basename(__FILE__);
+ $adm = strpos($_SERVER['PHP_SELF'], '/plog-admin');
+ $rewritebase = substr($_SERVER['PHP_SELF'], 0, $adm);
+ if (!$allow) {
+ $cfg .= "deny from all\n";
+ }
+ // Read the file
+ global $config;
+ $fpath = $config['basedir'].'plog-content/images/.htaccess';
+ $htaccess_lines = (is_file($fpath)) ? @file($fpath) : array();
+
+ $output = '';
+ $configuration_placed = false;
+ $between_placeholders = false;
+ foreach($htaccess_lines as $line) {
+ $tline = trim($line);
+ if ($placeholder_start == $tline) {
+ $between_placeholders = true;
+ $output .= $line.$cfg;
+ $configuration_placed = true;
+ continue;
+ }
+ if ($placeholder_end == $tline) {
+ $between_placeholders = false;
+ $output .= $line;
+ continue;
+ }
+ if ($between_placeholders) continue;
+
+ $output .= $line;
+ }
+
+ // No placeholders? Append to the end
+ if (!$configuration_placed) {
+ $output .= "\n\n" .$placeholder_start. "\n" .$cfg.$placeholder_end. "\n";
+ }
+
+ $fh = @fopen($fpath, 'w');
+ // Write changes out if the file can be opened.
+ // XXX: perhaps plog-options.php should check whether settings can be written and warn the user if not?
+ $success = false;
+ if ($fh) {
+ $success = true;
+ fwrite($fh, $output);
+ fclose($fh);
+ }
+ return $success;
+}
+
+function configure_mod_rewrite($enable = false) {
+ global $config;
+
+ if (file_exists($config['basedir'].'.htaccess') && is_writable($config['basedir'].'.htaccess')) {
+ $cfg = '';
+ $placeholder_start = '# BEGIN Plogger';
+ $placeholder_end = '# END Plogger';
+ $thisfile = '/plog-admin/'.basename(__FILE__);
+ $adm = strpos($_SERVER['PHP_SELF'], '/plog-admin');
+ $rewritebase = substr($_SERVER['PHP_SELF'], 0, $adm);
+ if ($enable) {
+ if (empty($rewritebase)) {
+ $rewritebase = '/';
+ }
+ $cfg .= "\n";
+ $cfg .= "RewriteEngine on\n";
+ $cfg .= "RewriteBase $rewritebase\n";
+ $cfg .= "RewriteCond %{REQUEST_URI} !(\.|/\$)\n";
+ $cfg .= "RewriteRule ^.*\$ http://".parse_url($config['gallery_url'], PHP_URL_HOST)."%{REQUEST_URI}/ [R=301,L]\n";
+ if (strpos($config['gallery_url'], 'www.')) {
+ $cfg .= "RewriteCond %{HTTP_HOST} !^www [NC]\n";
+ $cfg .= "RewriteRule ^(.*)\$ ".$config['gallery_url']."\$1 [R=301,L]\n";
+ }
+ $cfg .= "RewriteCond %{REQUEST_FILENAME} -d [OR]\n";
+ $cfg .= "RewriteCond %{REQUEST_FILENAME} -f\n";
+ $cfg .= "RewriteRule ^.*$ - [S=2]\n";
+ $cfg .= "RewriteRule feed/$ plog-rss.php?path=%{REQUEST_URI} [L]\n";
+ $cfg .= "RewriteRule ^.*$ index.php?path=%{REQUEST_URI} [L]\n";
+ $cfg .= " \n";
+ }
+ // Read the file
+ global $config;
+ $fpath = $config['basedir'].'.htaccess';
+ $htaccess_lines = @file($fpath);
+
+ $output = '';
+ $configuration_placed = false;
+ $between_placeholders = false;
+ foreach($htaccess_lines as $line) {
+ $tline = trim($line);
+ if ($placeholder_start == $tline) {
+ $between_placeholders = true;
+ $output .= $line.$cfg;
+ $configuration_placed = true;
+ continue;
+ }
+ if ($placeholder_end == $tline) {
+ $between_placeholders = false;
+ $output .= $line;
+ continue;
+ }
+ if ($between_placeholders) continue;
+
+ $output .= $line;
+ }
+
+ // No placeholders? Append to the end
+ if (!$configuration_placed) {
+ $output .= "\n\n" .$placeholder_start. "\n" .$cfg.$placeholder_end. "\n";
+ }
+
+ $fh = @fopen($fpath, 'w');
+ // Write changes out if the file can be opened.
+ // XXX: perhaps plog-options.php should check whether settings can be written and warn the user if not?
+ $success = false;
+ if ($fh) {
+ $success = true;
+ fwrite($fh, $output);
+ fclose($fh);
+ }
+ return $success;
+ } else {
+ return false;
+ }
+}
+
+function configure_blank_index($fpath = '') {
+ if (substr($fpath, -1) !== '/') {
+ $fpath = $fpath.'/';
+ }
+ // Write out the default blank index.php
+ if (!empty($fpath) && !file_exists($fpath.'index.php') && is_writable($fpath)) {
+ $output = "";
+ $fh = @fopen($fpath.'index.php', 'w');
+ if ($fh) {
+ fwrite($fh, $output);
+ fclose($fh);
+ }
+ }
+ // Always return true because a blank index is not required
+ return true;
+}
+
+// Makes sure that argument does not contain characters that cannot be allowed, like . or /, which
+// could be used to point to directory or filenames outside the Plogger directory
+function is_valid_directory($str) {
+ // Allow only alfanumeric characters, hyphen, [, ], dot, apostrophe and space in collection names
+ return !preg_match("/[^\w|\.|'|\-|\[|\] ]/", $str);
+}
+
+/// XXX: Something for the future: perhaps hooks for plugins should be implemented,
+// so plugins could add new fields to all those forms.
+function plog_add_collection_form() {
+ $output = "\n\t\t" . '
+
+ ' . "\n";
+ return $output;
+}
+
+function plog_add_album_form($parent_collection) {
+ $parent_collection = intval($parent_collection);
+ $output = "\n\t\t" . '
+
+ ' . "\n";
+ return $output;
+}
+
+function plog_edit_collection_form($collection_id) {
+ global $config, $thumbnail_config;
+ $output = '';
+ $collection_id = intval($collection_id);
+
+ $output .= "\n\t\t" . '' . "\n";
+ return $output;
+}
+
+function plog_edit_album_form($album_id) {
+ global $config, $thumbnail_config;
+
+ $album_id = intval($album_id);
+
+ $album = get_album_by_id($album_id);
+ $auto_graphic = $config['gallery_url'].'plog-admin/images/auto.gif';
+
+ $page = isset($_GET['plog_page']) ? '&plog_page='.intval($_GET['plog_page']) : '';
+
+ $output = "\n\t\t" . '' . "\n";
+ return $output;
+}
+
+function plog_picture_manager($id, $from, $limit) {
+ global $config, $empty;
+ $output = '';
+
+ plogger_init_pictures(array(
+ 'type' => 'album',
+ 'value' => $id,
+ 'from' => $from,
+ 'limit' => $limit,
+ 'sortby' => !empty($config['default_sortby']) ? $config['default_sortby'] : 'id',
+ 'sortdir' => !empty($config['default_sortdir']) ? $config['default_sortdir'] : 'ASC'
+ ));
+
+ // Create javascript initiation function for editable elements
+ if (plogger_has_pictures()) {
+ $output .= "\n\t\t" . '';
+ }
+
+ // Reset the picture array
+ plogger_init_pictures(array(
+ 'type' => 'album',
+ 'value' => $id,
+ 'from' => $from,
+ 'limit' => $limit,
+ 'sortby' => !empty($config['default_sortby']) ? $config['default_sortby'] : 'id',
+ 'sortdir' => !empty($config['default_sortdir']) ? $config['default_sortdir'] : 'ASC'
+ ));
+
+ if (plogger_has_pictures()) {
+ $allow_comment = ($config['allow_comments']) ? plog_tr('Allow Comments') : ' ';
+ $output .= "\n\t\t" . '
+
+ ';
+ $counter = 0;
+
+ while(plogger_has_pictures()) {
+ if ($counter%2 == 0) $table_row_color = 'color-1';
+ else $table_row_color = 'color-2';
+ $counter++;
+ plogger_load_picture();
+
+ $id = plogger_get_picture_id();
+ $output .= "\n\t\t\t" . '';
+ $output .= "\n\t\t\t\t" . '
';
+ $thumbpath = plogger_get_picture_thumb();
+ $imgtag = ' ';
+ $output .= "\n\t\t\t\t" . ' ';
+ $output .= "\n\t\t\t\t" . ''.basename(plogger_get_source_picture_path()).' '.sprintf(plog_tr('Comments: %d'), plogger_picture_comment_count()).'
';
+ $output .= "\n\t\t\t\t" . '
+ '.plog_tr('Caption').':
+ '.plogger_get_picture_caption().'
+ '.plog_tr('Description').':
+ '.plogger_get_picture_description().'
+ ';
+ if ($config['allow_comments']) {
+ $allow_comments = (1 == plogger_picture_allows_comments()) ? plog_tr('Yes') : plog_tr('No');
+ } else {
+ $allow_comments = ' ';
+ }
+ $output .= "\n\t\t\t\t" . ''.$allow_comments.'
';
+ $output .= "\n\t\t\t\t" . ' ';
+ $parent_id = $_REQUEST['id'];
+ $output .= '
';
+ $output .= "\n\t\t\t" . ' ';
+ }
+
+ $output .= "\n\t\t\t" . '
+
' . "\n";
+ } else {
+ $output .= "\n\n\t\t" . ''.sprintf(plog_tr('Sadly, there are no pictures yet. Why don\'t you upload some ?'), 'plog-upload.php').'
' . "\n";
+ $empty = true;
+ }
+ return $output;
+}
+
+function plog_album_manager($id, $from, $limit) {
+ global $config, $empty;
+ $output = '';
+
+ plogger_init_albums(array(
+ 'from' => $from,
+ 'collection_id' => $id,
+ 'limit' => $limit,
+ 'all_albums' => 1,
+ 'sortby' => !empty($config['album_sortby']) ? $config['album_sortby'] : 'id',
+ 'sortdir' => !empty($config['album_sortdir']) ? $config['album_sortdir'] : 'ASC'
+ ));
+
+ // Create javascript initiation function for editable elements
+ if (plogger_has_albums()) {
+ $output .= "\n\t\t" . '';
+ }
+
+ plogger_init_albums(array(
+ 'from' => $from,
+ 'collection_id' => $id,
+ 'limit' => $limit,
+ 'all_albums' => 1,
+ 'sortby' => !empty($config['album_sortby']) ? $config['album_sortby'] : 'id',
+ 'sortdir' => !empty($config['album_sortdir']) ? $config['album_sortdir'] : 'ASC'
+ ));
+
+ if (plogger_has_albums()) {
+ $output .= "\n\t\t" . '
+
+ ';
+ $counter = 0;
+
+ while(plogger_has_albums()) {
+ plogger_load_album();
+ $id = plogger_get_album_id();
+ if ($counter%2 == 0) $table_row_color = 'color-1';
+ else $table_row_color = 'color-2';
+ $counter++;
+
+ $text = (plogger_album_picture_count() == 1) ? plog_tr('image') : plog_tr('images');
+ $output .= "\n\t\t\t" . '';
+ $output .= "\n\t\t\t\t" . '
';
+ $output .= "\n\t\t\t\t" . ''.plogger_get_album_name().' - '.sprintf(plog_tr('%d'), plogger_album_picture_count()).' '.$text.'
';
+ $output .= "\n\t\t\t\t" . ''.plogger_get_album_description().'
';
+ $page = (isset($_GET['plog_page'])) ? '&plog_page='.intval($_GET['plog_page']) : '';
+ $output .= "\n\t\t\t\t" . ' ';
+ $output .= '
';
+ $output .= "\n\t\t\t" . ' ';
+ }
+
+ $output .= "\n\t\t\t" . '
+
' . "\n";
+ } else {
+ $output .= "\n\n\t\t" . ''.plog_tr('There are no albums in this collection yet, why don\'t you create one?').'
' . "\n";
+ $empty = true;
+ }
+ return $output;
+}
+
+function plog_collection_manager($from, $limit) {
+ global $config, $empty;
+ $output = '';
+
+ plogger_init_collections(array(
+ 'from' => $from,
+ 'limit' => $limit,
+ 'all_collections' => 1,
+ 'sortby' => !empty($config['collection_sortby']) ? $config['collection_sortby'] : 'id',
+ 'sortdir' => !empty($config['collection_sortdir']) ? $config['collection_sortdir'] : 'ASC'
+ ));
+
+ // Create javascript initiation function for editable elements
+ if (plogger_has_collections()) {
+ $output .= "\n\t\t" . '';
+ }
+
+ plogger_init_collections(array(
+ 'from' => $from,
+ 'limit' => $limit,
+ 'all_collections' => 1,
+ 'sortby' => !empty($config['collection_sortby']) ? $config['collection_sortby'] : 'id',
+ 'sortdir' => !empty($config['collection_sortdir']) ? $config['collection_sortdir'] : 'ASC'
+ ));
+
+ if (plogger_has_collections()) {
+ $output .= "\n\t\t" . '
+
+ ';
+ $counter = 0;
+
+ while(plogger_has_collections()) {
+ plogger_load_collection();
+ if ($counter%2 == 0) $table_row_color = 'color-1';
+ else $table_row_color = 'color-2';
+ $counter++;
+
+ $id = plogger_get_collection_id();
+ $text = (plogger_collection_album_count() == 1) ? plog_tr('album') : plog_tr('albums');
+ $output .= "\n\t\t\t" . '';
+ $output .= "\n\t\t\t\t" . '
';
+ $output .= "\n\t\t\t\t" . ''.plogger_get_collection_name().' - '.sprintf(plog_tr('%d'), plogger_collection_album_count()).' '.$text.'
';
+ $output .= "\n\t\t\t\t" . ''.plogger_get_collection_description().'
';
+ $output .= "\n\t\t\t\t" . ' ';
+ $output .= '
';
+ $output .= "\n\t\t\t" . ' ';
+ }
+
+ $output .= "\n\t\t\t" . '
+
' . "\n";
+ } else {
+ $output .= "\n\n\t\t" . ''.plog_tr('There are no collections yet').'.
' . "\n";
+ $empty = true;
+ }
+ return $output;
+}
+
+function plog_comment_manager($id, $from, $limit) {
+ global $config, $empty;
+ $output = '';
+
+ plogger_init_picture(array(
+ 'id' => $id,
+ 'from' => $from,
+ 'limit' => $limit
+ ));
+
+ // Create javascript initiation function for editable elements
+ if (plogger_picture_has_comments()) {
+ $output .= "\n\t\t" . '';
+ }
+
+ plogger_init_picture(array(
+ 'id' => $id,
+ 'from' => $from,
+ 'limit' => $limit
+ ));
+
+ if (plogger_picture_has_comments()) {
+ $output .= "\n\t\t" . '
+
+
+ ';
+ $counter = 0;
+
+ while(plogger_picture_has_comments()) {
+ plogger_load_comment();
+ if ($counter%2 == 0) $table_row_color = 'color-1';
+ else $table_row_color = 'color-2';
+ $counter++;
+
+ $id = plogger_get_comment_id();
+ $output .= "\n\t\t\t\t" . '';
+ $output .= "\n\t\t\t\t\t" .'
';
+ $output .= "\n\t\t\t\t\t" . '
+ '.plog_tr('Author').':
+
+ '.plog_tr('Email').':
+
+ '.plog_tr('Website').':
+
+ ';
+ $output .= "\n\t\t\t\t\t" . ''.plogger_get_comment_date('n/j/Y g:i a').'
';
+ $output .= "\n\t\t\t\t\t" . ' ';
+ $output .= "\n\t\t\t\t\t" . ' ';
+ $output .= '
';
+ $output .= "\n\t\t\t\t" . ' ';
+ }
+
+ $output .= "\n\t\t\t\t" . '
+
+
' . "\n";
+ } else {
+ $output .= "\n\n\t\t" . ''.plog_tr('This picture has no comments.').'
' . "\n";
+ $empty = true;
+ }
+ return $output;
+}
+
+function generate_ajax_picture_editing_init() {
+ $output = '
+
+
+ '.$inHead.'
+
+
+
+
+
+
+
+'.$string.'
+
';
+
+if (defined('PLOGGER_DEBUG') && PLOGGER_DEBUG == '1') {
+ $output .= trace('Queries: '.$GLOBALS['query_count'], false);
+ foreach ($GLOBALS['queries'] as $q) {
+ $output .= trace($q, false);
+ }
+ $output .= trace(plog_timer('end'), false);
+}
+
+$output .= "\n\n" . '
+