summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2019-11-17 20:57:39 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2019-11-17 20:57:39 +0100
commit3b06ee0d381dc1be5f40ca98ad4278046d869d21 (patch)
treed31e79fc57d882b8267f40c3434480bb58a3ca73
downloadfluxbb-3b06ee0d381dc1be5f40ca98ad4278046d869d21.tar.xz
checked in initial customized verison for Archlinux32HEADmaster
-rw-r--r--.gitattributes2
-rw-r--r--.gitignore11
-rw-r--r--COPYING340
-rw-r--r--addons/copyable_captcha.php104
-rw-r--r--addons/funnyquestion.php160
-rw-r--r--addons/index.html1
-rw-r--r--addons/recaptcha.php136
-rw-r--r--admin_bans.php602
-rw-r--r--admin_categories.php266
-rw-r--r--admin_censoring.php168
-rw-r--r--admin_forums.php501
-rw-r--r--admin_groups.php645
-rw-r--r--admin_index.php110
-rw-r--r--admin_loader.php55
-rw-r--r--admin_maintenance.php361
-rw-r--r--admin_options.php884
-rw-r--r--admin_permissions.php191
-rw-r--r--admin_reports.php183
-rw-r--r--admin_statistics.php139
-rw-r--r--admin_users.php1095
-rw-r--r--cache/.htaccess4
-rw-r--r--cache/index.html1
-rw-r--r--common.js38
-rw-r--r--config.php17
-rw-r--r--db_update.php1911
-rw-r--r--delete.php140
-rw-r--r--docker/docker-compose.yml30
-rw-r--r--docker/mariadb/Dockerfile4
-rw-r--r--docker/php/Dockerfile4
-rw-r--r--docker/php/etc/php/conf.d/extensions.ini4
-rwxr-xr-xdocker/restart6
-rwxr-xr-xdocker/start12
-rwxr-xr-xdocker/stop5
-rw-r--r--edit.php293
-rw-r--r--extern.php547
l---------favicon.ico1
-rw-r--r--footer.php165
-rw-r--r--header.php332
-rw-r--r--help.php154
-rw-r--r--img/avatars/index.html1
-rw-r--r--img/index.html1
-rw-r--r--img/smilies/big_smile.pngbin0 -> 373 bytes
-rw-r--r--img/smilies/cool.pngbin0 -> 380 bytes
-rw-r--r--img/smilies/hmm.pngbin0 -> 422 bytes
-rw-r--r--img/smilies/index.html1
-rw-r--r--img/smilies/lol.pngbin0 -> 364 bytes
-rw-r--r--img/smilies/mad.pngbin0 -> 409 bytes
-rw-r--r--img/smilies/neutral.pngbin0 -> 415 bytes
-rw-r--r--img/smilies/roll.pngbin0 -> 386 bytes
-rw-r--r--img/smilies/sad.pngbin0 -> 420 bytes
-rw-r--r--img/smilies/smile.pngbin0 -> 426 bytes
-rw-r--r--img/smilies/tongue.pngbin0 -> 416 bytes
-rw-r--r--img/smilies/wink.pngbin0 -> 428 bytes
-rw-r--r--img/smilies/yikes.pngbin0 -> 406 bytes
-rw-r--r--img/test.pngbin0 -> 2055 bytes
-rw-r--r--include/addons.php84
-rw-r--r--include/cache.php263
-rw-r--r--include/common.php209
-rw-r--r--include/common_admin.php174
-rw-r--r--include/dblayer/common_db.php48
-rw-r--r--include/dblayer/index.html1
-rw-r--r--include/dblayer/mysql.php378
-rw-r--r--include/dblayer/mysql_innodb.php392
-rw-r--r--include/dblayer/mysqli.php385
-rw-r--r--include/dblayer/mysqli_innodb.php398
-rw-r--r--include/dblayer/pgsql.php442
-rw-r--r--include/dblayer/sqlite.php601
-rw-r--r--include/email.php364
-rw-r--r--include/functions.php2227
-rw-r--r--include/index.html1
-rw-r--r--include/parser.php987
-rw-r--r--include/search_idx.php316
-rw-r--r--include/srand.php151
-rw-r--r--include/template/admin.tpl38
-rw-r--r--include/template/help.tpl23
-rw-r--r--include/template/index.html1
-rw-r--r--include/template/main.tpl38
-rw-r--r--include/template/maintenance.tpl23
-rw-r--r--include/template/redirect.tpl25
-rw-r--r--include/user/index.html1
-rw-r--r--include/utf8/index.html1
-rw-r--r--include/utf8/mbstring/core.php144
-rw-r--r--include/utf8/mbstring/index.html1
-rw-r--r--include/utf8/native/core.php422
-rw-r--r--include/utf8/native/index.html1
-rw-r--r--include/utf8/ord.php78
-rw-r--r--include/utf8/str_ireplace.php72
-rw-r--r--include/utf8/str_pad.php59
-rw-r--r--include/utf8/str_split.php33
-rw-r--r--include/utf8/strcasecmp.php27
-rw-r--r--include/utf8/strcspn.php36
-rw-r--r--include/utf8/stristr.php34
-rw-r--r--include/utf8/strrev.php22
-rw-r--r--include/utf8/strspn.php32
-rw-r--r--include/utf8/substr_replace.php27
-rw-r--r--include/utf8/trim.php74
-rw-r--r--include/utf8/ucfirst.php35
-rw-r--r--include/utf8/ucwords.php46
-rw-r--r--include/utf8/utf8.php72
-rw-r--r--include/utf8/utils/ascii.php221
-rw-r--r--include/utf8/utils/bad.php430
-rw-r--r--include/utf8/utils/index.html1
-rw-r--r--include/utf8/utils/patterns.php67
-rw-r--r--include/utf8/utils/position.php171
-rw-r--r--include/utf8/utils/specials.php131
-rw-r--r--include/utf8/utils/unicode.php241
-rw-r--r--include/utf8/utils/validation.php186
-rw-r--r--index.php278
-rw-r--r--lang/English/admin_bans.php69
-rw-r--r--lang/English/admin_categories.php29
-rw-r--r--lang/English/admin_censoring.php21
-rw-r--r--lang/English/admin_common.php44
-rw-r--r--lang/English/admin_forums.php53
-rw-r--r--lang/English/admin_groups.php91
-rw-r--r--lang/English/admin_index.php63
-rw-r--r--lang/English/admin_maintenance.php40
-rw-r--r--lang/English/admin_options.php227
-rw-r--r--lang/English/admin_permissions.php36
-rw-r--r--lang/English/admin_reports.php21
-rw-r--r--lang/English/admin_users.php110
-rw-r--r--lang/English/common.php169
-rw-r--r--lang/English/copyable_captcha.php10
-rw-r--r--lang/English/delete.php16
-rw-r--r--lang/English/forum.php17
-rw-r--r--lang/English/funnyquestion.php8
-rw-r--r--lang/English/help.php66
-rw-r--r--lang/English/index.html1
-rw-r--r--lang/English/index.php20
-rw-r--r--lang/English/install.php100
-rw-r--r--lang/English/login.php28
-rw-r--r--lang/English/mail_templates/activate_email.tpl12
-rw-r--r--lang/English/mail_templates/activate_password.tpl14
-rw-r--r--lang/English/mail_templates/banned_email_change.tpl9
-rw-r--r--lang/English/mail_templates/banned_email_post.tpl9
-rw-r--r--lang/English/mail_templates/banned_email_register.tpl9
-rw-r--r--lang/English/mail_templates/dupe_email_change.tpl9
-rw-r--r--lang/English/mail_templates/dupe_email_register.tpl9
-rw-r--r--lang/English/mail_templates/form_email.tpl13
-rw-r--r--lang/English/mail_templates/index.html1
-rw-r--r--lang/English/mail_templates/new_reply.tpl11
-rw-r--r--lang/English/mail_templates/new_reply_full.tpl18
-rw-r--r--lang/English/mail_templates/new_report.tpl9
-rw-r--r--lang/English/mail_templates/new_topic.tpl11
-rw-r--r--lang/English/mail_templates/new_topic_full.tpl18
-rw-r--r--lang/English/mail_templates/new_user.tpl12
-rw-r--r--lang/English/mail_templates/rename.tpl12
-rw-r--r--lang/English/mail_templates/welcome.tpl12
-rw-r--r--lang/English/misc.php93
-rw-r--r--lang/English/post.php38
-rw-r--r--lang/English/prof_reg.php79
-rw-r--r--lang/English/profile.php143
-rw-r--r--lang/English/recaptcha_addon.php20
-rw-r--r--lang/English/register.php37
-rw-r--r--lang/English/search.php64
-rw-r--r--lang/English/stopwords.txt175
-rw-r--r--lang/English/topic.php33
-rw-r--r--lang/English/update.php76
-rw-r--r--lang/English/userlist.php13
-rw-r--r--lang/German/admin_bans.php69
-rw-r--r--lang/German/admin_categories.php29
-rw-r--r--lang/German/admin_censoring.php21
-rw-r--r--lang/German/admin_common.php44
-rw-r--r--lang/German/admin_forums.php53
-rw-r--r--lang/German/admin_groups.php92
-rw-r--r--lang/German/admin_index.php64
-rw-r--r--lang/German/admin_maintenance.php40
-rw-r--r--lang/German/admin_options.php227
-rw-r--r--lang/German/admin_permissions.php36
-rw-r--r--lang/German/admin_reports.php21
-rw-r--r--lang/German/admin_users.php110
-rw-r--r--lang/German/common.php169
-rw-r--r--lang/German/delete.php16
-rw-r--r--lang/German/forum.php17
-rw-r--r--lang/German/funnyquestion.php8
-rw-r--r--lang/German/help.php66
-rw-r--r--lang/German/index.html1
-rw-r--r--lang/German/index.php20
-rw-r--r--lang/German/install.php105
-rw-r--r--lang/German/login.php28
-rw-r--r--lang/German/mail_templates/activate_email.tpl12
-rw-r--r--lang/German/mail_templates/activate_password.tpl14
-rw-r--r--lang/German/mail_templates/banned_email_change.tpl9
-rw-r--r--lang/German/mail_templates/banned_email_post.tpl9
-rw-r--r--lang/German/mail_templates/banned_email_register.tpl9
-rw-r--r--lang/German/mail_templates/dupe_email_change.tpl9
-rw-r--r--lang/German/mail_templates/dupe_email_register.tpl9
-rw-r--r--lang/German/mail_templates/form_email.tpl13
-rw-r--r--lang/German/mail_templates/index.html1
-rw-r--r--lang/German/mail_templates/new_reply.tpl11
-rw-r--r--lang/German/mail_templates/new_reply_full.tpl18
-rw-r--r--lang/German/mail_templates/new_report.tpl9
-rw-r--r--lang/German/mail_templates/new_topic.tpl11
-rw-r--r--lang/German/mail_templates/new_topic_full.tpl18
-rw-r--r--lang/German/mail_templates/new_user.tpl12
-rw-r--r--lang/German/mail_templates/rename.tpl12
-rw-r--r--lang/German/mail_templates/welcome.tpl12
-rw-r--r--lang/German/misc.php93
-rw-r--r--lang/German/post.php38
-rw-r--r--lang/German/prof_reg.php79
-rw-r--r--lang/German/profile.php143
-rw-r--r--lang/German/register.php37
-rw-r--r--lang/German/search.php63
-rw-r--r--lang/German/stopwords.txt238
-rw-r--r--lang/German/topic.php33
-rw-r--r--lang/German/update.php76
-rw-r--r--lang/German/userlist.php13
-rw-r--r--lang/index.html1
-rw-r--r--login.php335
-rw-r--r--misc.php414
-rw-r--r--moderate.php1020
-rw-r--r--plugins/AP_reCAPTCHA.php123
-rw-r--r--plugins/index.html1
-rw-r--r--post.php781
-rw-r--r--profile.php1882
-rw-r--r--readme.md28
-rw-r--r--register.php448
-rw-r--r--robots.txt3
-rw-r--r--search.php914
-rw-r--r--style/Air.css1651
-rw-r--r--style/Air/base_admin.css177
-rw-r--r--style/Air/img/asterisk.pngbin0 -> 168 bytes
-rw-r--r--style/Air/img/bull.pngbin0 -> 107 bytes
-rw-r--r--style/Air/img/email.pngbin0 -> 390 bytes
-rw-r--r--style/Air/img/exclaim.pngbin0 -> 432 bytes
-rw-r--r--style/Air/img/ext.pngbin0 -> 130 bytes
-rw-r--r--style/Air/img/feed.pngbin0 -> 439 bytes
-rw-r--r--style/Air/img/help.pngbin0 -> 394 bytes
-rw-r--r--style/Air/img/index.html1
-rw-r--r--style/Air/index.html1
l---------style/ArchLinux.css1
-rw-r--r--style/ArchLinux/admin.tpl40
-rw-r--r--style/ArchLinux/arch.css162
-rw-r--r--style/ArchLinux/archfooter.php11
-rw-r--r--style/ArchLinux/archicon.svg1
-rw-r--r--style/ArchLinux/archlogo.svg1
-rw-r--r--style/ArchLinux/archnavbar.css76
-rw-r--r--style/ArchLinux/archnavbar.php17
l---------style/ArchLinux/base_admin.css1
-rw-r--r--style/ArchLinux/css.php7
-rw-r--r--style/ArchLinux/favicon.icobin0 -> 501 bytes
-rw-r--r--style/ArchLinux/help.tpl29
-rw-r--r--style/ArchLinux/index.html1
-rw-r--r--style/ArchLinux/main.tpl40
-rw-r--r--style/ArchLinux/maintenance.tpl29
-rw-r--r--style/ArchLinux/redirect.tpl31
l---------style/ArchLinux32.css1
-rw-r--r--style/ArchLinux32/admin.tpl40
-rw-r--r--style/ArchLinux32/arch.css162
-rw-r--r--style/ArchLinux32/arch32logo.pngbin0 -> 5508 bytes
-rw-r--r--style/ArchLinux32/archfooter.php11
-rw-r--r--style/ArchLinux32/archicon.svg1
-rw-r--r--style/ArchLinux32/archlogo.svg1
-rw-r--r--style/ArchLinux32/archnavbar.css76
-rw-r--r--style/ArchLinux32/archnavbar.php18
-rw-r--r--style/ArchLinux32/base_admin.css177
-rw-r--r--style/ArchLinux32/css.php7
-rw-r--r--style/ArchLinux32/favicon.icobin0 -> 501 bytes
-rw-r--r--style/ArchLinux32/help.tpl29
-rw-r--r--style/ArchLinux32/index.html1
-rw-r--r--style/ArchLinux32/main.tpl40
-rw-r--r--style/ArchLinux32/maintenance.tpl29
-rw-r--r--style/ArchLinux32/redirect.tpl31
-rw-r--r--style/Cobalt.css1150
-rw-r--r--style/Earth.css1650
-rw-r--r--style/Earth/base_admin.css177
-rw-r--r--style/Earth/img/asterisk.pngbin0 -> 168 bytes
-rw-r--r--style/Earth/img/bull.pngbin0 -> 107 bytes
-rw-r--r--style/Earth/img/email.pngbin0 -> 388 bytes
-rw-r--r--style/Earth/img/exclaim.pngbin0 -> 432 bytes
-rw-r--r--style/Earth/img/ext.pngbin0 -> 130 bytes
-rw-r--r--style/Earth/img/feed.pngbin0 -> 439 bytes
-rw-r--r--style/Earth/img/help.pngbin0 -> 379 bytes
-rw-r--r--style/Earth/img/index.html1
-rw-r--r--style/Earth/index.html1
-rw-r--r--style/Fire.css1650
-rw-r--r--style/Fire/base_admin.css177
-rw-r--r--style/Fire/img/asterisk.pngbin0 -> 168 bytes
-rw-r--r--style/Fire/img/bull.pngbin0 -> 107 bytes
-rw-r--r--style/Fire/img/email.pngbin0 -> 371 bytes
-rw-r--r--style/Fire/img/exclaim.pngbin0 -> 432 bytes
-rw-r--r--style/Fire/img/ext.pngbin0 -> 130 bytes
-rw-r--r--style/Fire/img/feed.pngbin0 -> 439 bytes
-rw-r--r--style/Fire/img/help.pngbin0 -> 380 bytes
-rw-r--r--style/Fire/img/index.html1
-rw-r--r--style/Fire/index.html1
-rw-r--r--style/Lithium.css1149
-rw-r--r--style/Mercury.css1150
-rw-r--r--style/Oxygen.css1150
-rw-r--r--style/Radium.css1150
-rw-r--r--style/Sulfur.css1149
-rw-r--r--style/Technetium.css1373
-rw-r--r--style/Technetium/bg.pngbin0 -> 578 bytes
-rw-r--r--style/Technetium/dark-shade.pngbin0 -> 157 bytes
-rw-r--r--style/Technetium/darker-shade.pngbin0 -> 172 bytes
-rw-r--r--style/Technetium/feed.pngbin0 -> 439 bytes
-rw-r--r--style/Technetium/icon-closed-sticky.pngbin0 -> 457 bytes
-rw-r--r--style/Technetium/icon-closed.pngbin0 -> 255 bytes
-rw-r--r--style/Technetium/icon-moved.pngbin0 -> 423 bytes
-rw-r--r--style/Technetium/icon-new-sticky.pngbin0 -> 505 bytes
-rw-r--r--style/Technetium/icon-new.pngbin0 -> 324 bytes
-rw-r--r--style/Technetium/icon-nonew-sticky.pngbin0 -> 443 bytes
-rw-r--r--style/Technetium/icon-nonew.pngbin0 -> 265 bytes
-rw-r--r--style/Technetium/index.html1
-rw-r--r--style/Technetium/inv-shade.pngbin0 -> 117 bytes
-rw-r--r--style/Technetium/light-shade.pngbin0 -> 120 bytes
-rw-r--r--style/imports/base_admin.css54
-rw-r--r--style/imports/index.html1
-rw-r--r--style/index.html1
-rw-r--r--userlist.php183
-rw-r--r--viewforum.php311
-rw-r--r--viewtopic.php486
311 files changed, 46389 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..8359151
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+.gitattributes export-ignore
+.gitignore export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea07bb6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+/cache/*
+!/cache/index.html
+!/cache/.htaccess
+/include/user/*
+!/include/user/index.html
+/img/avatars/*
+!/img/avatars/index.html
+/.kateproject.d/
+/nbproject/
+/.idea/
+*~
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/addons/copyable_captcha.php b/addons/copyable_captcha.php
new file mode 100644
index 0000000..8f136d9
--- /dev/null
+++ b/addons/copyable_captcha.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * Copyable Captha plugin
+ * Copyright (C) 2016 artoodetoo
+ * Special thanks to Visman for his help
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+class addon_copyable_captcha extends flux_addon
+{
+ var $lang;
+ var $styles;
+ var $spans;
+
+ function register($manager)
+ {
+ global $pun_user;
+
+ if (!$pun_user['is_guest']) return;
+
+ $manager->bind('register_after_validation', array($this, 'hook_register_after_validation'));
+ $manager->bind('register_before_header', array($this, 'hook_register_before_header'));
+ $manager->bind('register_before_submit', array($this, 'hook_register_before_submit'));
+ }
+
+ function load_lang()
+ {
+ global $pun_user;
+
+ if (isset($this->lang)) return;
+
+ $user_lang = file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/copyable_captcha.php')
+ ? $pun_user['language']
+ : 'English';
+ require PUN_ROOT.'lang/'.$user_lang.'/copyable_captcha.php';
+
+ $this->lang = $lang_copyable_captcha;
+ }
+
+ function hook_register_after_validation()
+ {
+ global $errors, $cookie_name, $cookie_seed;
+
+ if (isset($_POST['req_word']) && isset($_COOKIE[$cookie_name.'_captcha']) && substr_count($_COOKIE[$cookie_name.'_captcha'], '-') === 1) {
+ list($hash, $time) = explode('-', $_COOKIE[$cookie_name.'_captcha']);
+ $word = $_POST['req_word'];
+ if ((int)$time <= time() - 120 || $hash !== sha1(strtolower($word).$cookie_seed.'secret'.$time)) {
+ $this->load_lang();
+ $errors[] = $this->lang['Captcha error'];
+ }
+ } else {
+ $this->load_lang();
+ $errors[] = $this->lang['Captcha error'];
+ }
+ }
+
+
+ function hook_register_before_header()
+ {
+ global $required_fields, $errors, $cookie_name, $cookie_seed;
+
+ $this->load_lang();
+ $required_fields['req_word'] = $this->lang['Captcha'];
+
+ $time = time();
+ $word = random_pass(mt_rand(4, 6));
+ $hash = sha1(strtolower($word).$cookie_seed.'secret'.$time);
+ forum_setcookie($cookie_name.'_captcha', $hash.'-'.$time, $time + 120);
+
+ $array = str_split($word);
+ $mixin = random_pass(mt_rand(1, 3));
+ $i = -1;
+ $this->styles = '';
+ foreach (str_split($mixin) as $ch) {
+ $i = mt_rand($i+1, count($array));
+ array_splice($array, $i, 0, $ch);
+ $this->styles .= '.masq i:nth-child('.($i + 1).'){display:none;} ';
+ }
+ $this->spans = '<i>'.implode('</i><i>', $array).'</i>';
+ }
+
+
+ function hook_register_before_submit()
+ {
+ global $lang_common;
+
+ $this->load_lang();
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $this->lang['Captcha legend'] ?></legend>
+ <div class="infldset">
+ <style> .masq i {font-style:normal;} <?php echo $this->styles ?></style>
+ <p><?php echo sprintf($this->lang['Captcha info'], $this->spans) ?></p>
+ <label class="required"><strong><?php echo $this->lang['Captcha'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_word" size="25" maxlength="25" /><br /></label>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ }
+}
diff --git a/addons/funnyquestion.php b/addons/funnyquestion.php
new file mode 100644
index 0000000..e3cd4e9
--- /dev/null
+++ b/addons/funnyquestion.php
@@ -0,0 +1,160 @@
+<?php
+
+if (!defined('PUN')) {
+ exit;
+}
+
+class addon_funnyquestion extends flux_addon
+{
+
+ public function __construct()
+ {
+ global $funnyquestion_disabled, $funnyquestion_hash, $funny_questions, $funnyquestion_timeout, $funnyquestion_remember, $funnyquestion_wait, $pun_user, $lang_funnyquestion;
+
+ !isset($funnyquestion_disabled) && $funnyquestion_disabled = false;
+ !isset($funnyquestion_hash) && $funnyquestion_hash = dirname(__FILE__);
+ !isset($funny_questions) && $funny_questions = array(
+ 'What is the Ultimate Answer to the Ultimate Question of Life, The Universe, and Everything?' => '42'
+ );
+ !isset($funnyquestion_timeout) && $funnyquestion_timeout = 3600;
+ !isset($funnyquestion_remember) && $funnyquestion_remember = 3600 * 24;
+ !isset($funnyquestion_wait) && $funnyquestion_wait = 2;
+
+ if (file_exists(PUN_ROOT . 'lang/' . $pun_user['language'] . '/funnyquestion.php')) {
+ require PUN_ROOT . 'lang/' . $pun_user['language'] . '/funnyquestion.php';
+ } else {
+ require PUN_ROOT . 'lang/English/funnyquestion.php';
+ }
+ }
+
+ /**
+ * @param flux_addon_manager $manager
+ */
+ public function register($manager)
+ {
+ $manager->bind('register_before_submit', array($this, 'print_funnyquestion'));
+ $manager->bind('quickpost_before_submit', array($this, 'print_funnyquestion'));
+ $manager->bind('post_before_submit', array($this, 'print_funnyquestion'));
+ $manager->bind('register_before_validation', array($this, 'set_error_on_check_funnyquestion'));
+ $manager->bind('post_before_validation', array($this, 'set_error_on_check_funnyquestion'));
+ }
+
+ public function print_funnyquestion()
+ {
+ echo $this->get_funnyquestion();
+ }
+
+ public function set_error_on_check_funnyquestion()
+ {
+ global $errors, $lang_funnyquestion;
+ $this->check_funnyquestion() || $errors[] = $lang_funnyquestion['wrong-answer'];
+ }
+
+ /**
+ * @param string $answer
+ * @return string
+ */
+ private function normalize_funnyanswer($answer)
+ {
+ return preg_replace('/[^a-z0-9]/', '', strtolower($answer));
+ }
+
+ private function set_funnycookie()
+ {
+ global $funnyquestion_hash, $funnyquestion_remember;
+
+ $time = time();
+ forum_setcookie('funnyquestion_hash', sha1($time . get_remote_address() . $funnyquestion_hash),
+ $time + $funnyquestion_remember);
+ forum_setcookie('funnyquestion_time', $time, $time + $funnyquestion_remember);
+ }
+
+ /**
+ * @return bool
+ */
+ private function has_funnycookie()
+ {
+ global $funnyquestion_hash, $funnyquestion_remember;
+
+ return (!empty($_COOKIE['funnyquestion_hash']) && !empty($_COOKIE['funnyquestion_time'])
+ && time() - $funnyquestion_remember <= $_COOKIE['funnyquestion_time']
+ && sha1($_COOKIE['funnyquestion_time'] . get_remote_address() . $funnyquestion_hash) == $_COOKIE['funnyquestion_hash']);
+ }
+
+ /**
+ * @return string
+ */
+ private function get_funnyquestion()
+ {
+ global $funnyquestion_disabled, $funnyquestion_hash, $funny_questions, $lang_funnyquestion, $lang_common, $pun_user;
+
+ if ($funnyquestion_disabled || !$pun_user['is_guest'] || $this->has_funnycookie()) {
+ return '';
+ }
+
+ $time = time();
+ $question = array_rand($funny_questions);
+ # make sure the user is not able to tell us the question to answer
+ $hash = sha1($time . $question . $funnyquestion_hash);
+
+ return '<div class="inform">
+ <fieldset>
+ <legend>' . $lang_funnyquestion['question-label'] . '</legend>
+ <div class="infldset">
+ <input type="hidden" name="funnyquestion_time" value="' . $time . '" />
+ <input type="hidden" name="funnyquestion_hash" value="' . $hash . '" />
+ <label class="required">
+ <strong>' . $question . '<span>' . $lang_common['Required'] . '</span></strong><br />
+ <input type="text" name="funny_answer" value="" size="50" /><br />
+ </label>
+ </div>
+ </fieldset>
+ </div>';
+ }
+
+ /**
+ * @return bool
+ */
+ private function check_funnyquestion()
+ {
+ global $funnyquestion_disabled, $funnyquestion_hash, $funnyquestion_timeout, $funnyquestion_wait, $funny_questions, $pun_user;
+
+ if ($funnyquestion_disabled || !$pun_user['is_guest'] || $this->has_funnycookie()) {
+ return true;
+ }
+
+ if (!empty($_POST['funnyquestion_time'])
+ && !empty($_POST['funnyquestion_hash'])
+ && !empty($_POST['funny_answer'])
+ ) {
+ $now = time();
+ $time = $_POST['funnyquestion_time'];
+ $hash = $_POST['funnyquestion_hash'];
+ $user_answer = $this->normalize_funnyanswer($_POST['funny_answer']);
+ } else {
+ return false;
+ }
+
+ if ($now - $time > $funnyquestion_timeout) {
+ return false;
+ } elseif ($now - $time < $funnyquestion_wait) {
+ return false;
+ }
+
+ foreach ($funny_questions as $question => $answers) {
+ if (!is_array($answers)) {
+ $answers = array($answers);
+ }
+ foreach ($answers as $answer) {
+ if ($this->normalize_funnyanswer($answer) == $user_answer
+ && $hash == sha1($time . $question . $funnyquestion_hash)
+ ) {
+ $this->set_funnycookie();
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/addons/index.html b/addons/index.html
new file mode 100644
index 0000000..cf1a99a
--- /dev/null
+++ b/addons/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/addons/recaptcha.php b/addons/recaptcha.php
new file mode 100644
index 0000000..668de1c
--- /dev/null
+++ b/addons/recaptcha.php
@@ -0,0 +1,136 @@
+<?php
+
+class addon_recaptcha extends flux_addon
+{
+ function register($manager)
+ {
+ global $pun_user;
+
+ if (!$this->is_configured()) return;
+
+ $this->get_language();
+
+ if ($this->enabled_location('register'))
+ {
+ $manager->bind('register_after_validation', array($this, 'hook_after_validation'));
+ $manager->bind('register_before_submit', array($this, 'hook_before_submit'));
+ }
+
+ if ($this->enabled_location('login'))
+ {
+ $manager->bind('login_after_validation', array($this, 'hook_after_validation'));
+ $manager->bind('login_before_submit', array($this, 'hook_before_submit'));
+ }
+
+ if ($this->enabled_location('guestpost') && $pun_user['is_guest'])
+ {
+ $manager->bind('post_after_validation', array($this, 'hook_after_validation'));
+ $manager->bind('post_before_submit', array($this, 'hook_before_submit'));
+ $manager->bind('quickpost_before_submit', array($this, 'hook_before_submit'));
+ }
+ }
+
+ function is_configured()
+ {
+ global $pun_config;
+
+ return !empty($pun_config['recaptcha_enabled']) && !empty($pun_config['recaptcha_site_key']) && !empty($pun_config['recaptcha_secret_key']);
+ }
+
+ function enabled_location($page)
+ {
+ global $pun_config;
+
+ return !empty($pun_config['recaptcha_location_'.$page]);
+ }
+
+ function get_language()
+ {
+ global $pun_user;
+
+ if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/recaptcha_addon.php'))
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/recaptcha_addon.php';
+ else
+ require PUN_ROOT.'lang/English/recaptcha_addon.php';
+ }
+
+ function hook_after_validation()
+ {
+ global $errors, $lang_recaptcha;
+
+ if (empty($errors) && !$this->verify_user_response())
+ {
+ $errors[] = $lang_recaptcha['Error'];
+ }
+ }
+
+ function hook_before_submit()
+ {
+ global $pun_config, $lang_recaptcha;
+
+ $site_key = $pun_config['recaptcha_site_key'];
+
+ ?>
+ <div class="inform">
+ <fieldset>
+ <legend><?= $lang_recaptcha['Human']; ?></legend>
+ <div class="infldset">
+ <p><?= $lang_recaptcha['Prove']; ?></p>
+ <script src="https://www.google.com/recaptcha/api.js"></script>
+ <div class="g-recaptcha" data-sitekey="<?php echo pun_htmlspecialchars($site_key) ?>"></div>
+ </div>
+ </fieldset>
+ </div>
+ <?php
+ }
+
+ function verify_user_response()
+ {
+ global $pun_config;
+
+ if (empty($_POST['g-recaptcha-response'])) return false;
+
+ $secret = $pun_config['recaptcha_secret_key'];
+ $response = $_POST['g-recaptcha-response'];
+ $ip = get_remote_address();
+
+ $query = "secret=$secret&response=$response&remoteip=$ip";
+ $url = "https://www.google.com/recaptcha/api/siteverify?$query";
+
+ $response = $this->send_request($url);
+
+ return strpos($response, '"success": true') !== false;
+ }
+
+ function send_request($url)
+ {
+ if (function_exists('curl_version'))
+ return $this->send_curl_request($url);
+ else
+ return $this->get_remote_file($url);
+ }
+
+ function send_curl_request($url)
+ {
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ return $response;
+ }
+
+ function get_remote_file($url)
+ {
+ global $lang_recaptcha;
+
+ $response = file_get_contents($url);
+
+ if ($response === false)
+ throw new Exception($lang_recaptcha['API error']);
+
+ return $response;
+ }
+}
diff --git a/admin_bans.php b/admin_bans.php
new file mode 100644
index 0000000..6ee8c58
--- /dev/null
+++ b/admin_bans.php
@@ -0,0 +1,602 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_bans.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_bans.php';
+
+// Add/edit a ban (stage 1)
+if (isset($_REQUEST['add_ban']) || isset($_GET['edit_ban']))
+{
+ if (isset($_GET['add_ban']) || isset($_POST['add_ban']))
+ {
+ // If the ID of the user to ban was provided through GET (a link from profile.php)
+ if (isset($_GET['add_ban']))
+ {
+ $user_id = intval($_GET['add_ban']);
+ if ($user_id < 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT group_id, username, email FROM '.$db->prefix.'users WHERE id='.$user_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ list($group_id, $ban_user, $ban_email) = $db->fetch_row($result);
+ else
+ message($lang_admin_bans['No user ID message']);
+ }
+ else // Otherwise the username is in POST
+ {
+ $ban_user = pun_trim($_POST['new_ban_user']);
+
+ if ($ban_user != '')
+ {
+ $result = $db->query('SELECT id, group_id, username, email FROM '.$db->prefix.'users WHERE username=\''.$db->escape($ban_user).'\' AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ list($user_id, $group_id, $ban_user, $ban_email) = $db->fetch_row($result);
+ else
+ message($lang_admin_bans['No user message']);
+ }
+ }
+
+ // Make sure we're not banning an admin or moderator
+ if (isset($group_id))
+ {
+ if ($group_id == PUN_ADMIN)
+ message(sprintf($lang_admin_bans['User is admin message'], pun_htmlspecialchars($ban_user)));
+
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
+ $is_moderator_group = $db->result($result);
+
+ if ($is_moderator_group)
+ message(sprintf($lang_admin_bans['User is mod message'], pun_htmlspecialchars($ban_user)));
+ }
+
+ // If we have a $user_id, we can try to find the last known IP of that user
+ if (isset($user_id))
+ {
+ $result = $db->query('SELECT poster_ip FROM '.$db->prefix.'posts WHERE poster_id='.$user_id.' ORDER BY posted DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $ban_ip = ($db->num_rows($result)) ? $db->result($result) : '';
+
+ if ($ban_ip == '')
+ {
+ $result = $db->query('SELECT registration_ip FROM '.$db->prefix.'users WHERE id='.$user_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ $ban_ip = ($db->num_rows($result)) ? $db->result($result) : '';
+ }
+ }
+
+ $mode = 'add';
+ }
+ else // We are editing a ban
+ {
+ $ban_id = intval($_GET['edit_ban']);
+ if ($ban_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT username, ip, email, message, expire FROM '.$db->prefix.'bans WHERE id='.$ban_id) or error('Unable to fetch ban info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ list($ban_user, $ban_ip, $ban_email, $ban_message, $ban_expire) = $db->fetch_row($result);
+ else
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+ $ban_expire = ($ban_expire != '') ? gmdate('Y-m-d', $ban_expire + $diff) : '';
+
+ $mode = 'edit';
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans']);
+ $focus_element = array('bans2', 'ban_user');
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('bans');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_bans['Ban advanced head'] ?></span></h2>
+ <div class="box">
+ <form id="bans2" method="post" action="admin_bans.php">
+ <div class="inform">
+ <input type="hidden" name="mode" value="<?php echo $mode ?>" />
+<?php if ($mode == 'edit'): ?> <input type="hidden" name="ban_id" value="<?php echo $ban_id ?>" />
+<?php endif; ?> <fieldset>
+ <legend><?php echo $lang_admin_bans['Ban advanced subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Username label'] ?></th>
+ <td>
+ <input type="text" name="ban_user" size="25" maxlength="25" value="<?php if (isset($ban_user)) echo pun_htmlspecialchars($ban_user); ?>" tabindex="1" />
+ <span><?php echo $lang_admin_bans['Username help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['IP label'] ?></th>
+ <td>
+ <input type="text" name="ban_ip" size="45" maxlength="255" value="<?php if (isset($ban_ip)) echo pun_htmlspecialchars($ban_ip); ?>" tabindex="2" />
+ <span><?php echo $lang_admin_bans['IP help'] ?><?php if ($ban_user != '' && isset($user_id)) printf(' '.$lang_admin_bans['IP help link'], '<a href="admin_users.php?ip_stats='.$user_id.'">'.$lang_admin_common['here'].'</a>') ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['E-mail label'] ?></th>
+ <td>
+ <input type="text" name="ban_email" size="40" maxlength="80" value="<?php if (isset($ban_email)) echo pun_htmlspecialchars($ban_email); ?>" tabindex="3" />
+ <span><?php echo $lang_admin_bans['E-mail help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ <p class="topspace"><strong class="warntext"><?php echo $lang_admin_bans['Ban IP range info'] ?></strong></p>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_bans['Message expiry subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Ban message label'] ?></th>
+ <td>
+ <input type="text" name="ban_message" size="50" maxlength="255" value="<?php if (isset($ban_message)) echo pun_htmlspecialchars($ban_message); ?>" tabindex="4" />
+ <span><?php echo $lang_admin_bans['Ban message help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Expire date label'] ?></th>
+ <td>
+ <input type="text" name="ban_expire" size="17" maxlength="10" value="<?php if (isset($ban_expire)) echo $ban_expire; ?>" tabindex="5" />
+ <span><?php echo $lang_admin_bans['Expire date help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="add_edit_ban" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="6" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+// Add/edit a ban (stage 2)
+else if (isset($_POST['add_edit_ban']))
+{
+ confirm_referrer('admin_bans.php');
+
+ $ban_user = pun_trim($_POST['ban_user']);
+ $ban_ip = pun_trim($_POST['ban_ip']);
+ $ban_email = strtolower(pun_trim($_POST['ban_email']));
+ $ban_message = pun_trim($_POST['ban_message']);
+ $ban_expire = pun_trim($_POST['ban_expire']);
+
+ if ($ban_user == '' && $ban_ip == '' && $ban_email == '')
+ message($lang_admin_bans['Must enter message']);
+ else if (strtolower($ban_user) == 'guest')
+ message($lang_admin_bans['Cannot ban guest message']);
+
+ // Make sure we're not banning an admin or moderator
+ if (!empty($ban_user))
+ {
+ $result = $db->query('SELECT group_id FROM '.$db->prefix.'users WHERE username=\''.$db->escape($ban_user).'\' AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ $group_id = $db->result($result);
+
+ if ($group_id == PUN_ADMIN)
+ message(sprintf($lang_admin_bans['User is admin message'], pun_htmlspecialchars($ban_user)));
+
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
+ $is_moderator_group = $db->result($result);
+
+ if ($is_moderator_group)
+ message(sprintf($lang_admin_bans['User is mod message'], pun_htmlspecialchars($ban_user)));
+ }
+ }
+
+ // Validate IP/IP range (it's overkill, I know)
+ if ($ban_ip != '')
+ {
+ $ban_ip = preg_replace('%\s{2,}%S', ' ', $ban_ip);
+ $addresses = explode(' ', $ban_ip);
+ $addresses = array_map('pun_trim', $addresses);
+
+ for ($i = 0; $i < count($addresses); ++$i)
+ {
+ if (strpos($addresses[$i], ':') !== false)
+ {
+ $octets = explode(':', $addresses[$i]);
+
+ for ($c = 0; $c < count($octets); ++$c)
+ {
+ $octets[$c] = ltrim($octets[$c], "0");
+
+ if ($c > 7 || (!empty($octets[$c]) && !ctype_xdigit($octets[$c])) || intval($octets[$c], 16) > 65535)
+ message($lang_admin_bans['Invalid IP message']);
+ }
+
+ $cur_address = implode(':', $octets);
+ $addresses[$i] = $cur_address;
+ }
+ else
+ {
+ $octets = explode('.', $addresses[$i]);
+
+ for ($c = 0; $c < count($octets); ++$c)
+ {
+ $octets[$c] = (strlen($octets[$c]) > 1) ? ltrim($octets[$c], "0") : $octets[$c];
+
+ if ($c > 3 || preg_match('%[^0-9]%', $octets[$c]) || intval($octets[$c]) > 255)
+ message($lang_admin_bans['Invalid IP message']);
+ }
+
+ $cur_address = implode('.', $octets);
+ $addresses[$i] = $cur_address;
+ }
+ }
+
+ $ban_ip = implode(' ', $addresses);
+ }
+
+ require PUN_ROOT.'include/email.php';
+ if ($ban_email != '')
+ {
+ // Validate email or domain format
+ if (!is_valid_email($ban_email) && !preg_match('%^[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,63})$%', $ban_email))
+ message($lang_admin_bans['Invalid e-mail message']);
+
+ // Let's ensure we are not adding a duplicate ban
+ $dup_conditions = array('(expire IS NULL OR expire > '.time().')');
+
+ // If we're adding an email address, we can also check for the domain
+ $domain_index = strpos($ban_email, '@');
+
+ if ($domain_index !== false && $_POST['mode'] == 'add')
+ {
+ // We are not checking for domains when editing bans, as that might
+ // prevent editing other fields of already existing email bans for
+ // which a domain ban was added later.
+ $ban_domain = substr($ban_email, $domain_index + 1);
+ $dup_conditions[] = 'email IN (\''.$db->escape($ban_email).'\', \''.$db->escape($ban_domain).'\')';
+ }
+ else
+ $dup_conditions[] = 'email = \''.$db->escape($ban_email).'\'';
+
+ // When editing, we also need to exclude the current ban
+ if ($_POST['mode'] == 'edit')
+ $dup_conditions[] = 'id != '.intval($_POST['ban_id']);
+
+ $result = $db->query('SELECT email FROM '.$db->prefix.'bans WHERE '.implode(' AND ', $dup_conditions)) or error('Unable to check for duplicate bans', __FILE__, __LINE__, $db->error());
+ if ($match = $db->result($result))
+ {
+ $is_domain = strpos($match, '@') === false;
+
+ if ($is_domain)
+ message(sprintf($lang_admin_bans['Duplicate domain message'], $match));
+ else
+ message(sprintf($lang_admin_bans['Duplicate e-mail message'], $match));
+ }
+ }
+
+ if ($ban_expire != '' && $ban_expire != 'Never')
+ {
+ $ban_expire = strtotime($ban_expire.' GMT');
+
+ if ($ban_expire == -1 || !$ban_expire)
+ message($lang_admin_bans['Invalid date message'].' '.$lang_admin_bans['Invalid date reasons']);
+
+ $diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+ $ban_expire -= $diff;
+
+ if ($ban_expire <= time())
+ message($lang_admin_bans['Invalid date message'].' '.$lang_admin_bans['Invalid date reasons']);
+ }
+ else
+ $ban_expire = 'NULL';
+
+ $ban_user = ($ban_user != '') ? '\''.$db->escape($ban_user).'\'' : 'NULL';
+ $ban_ip = ($ban_ip != '') ? '\''.$db->escape($ban_ip).'\'' : 'NULL';
+ $ban_email = ($ban_email != '') ? '\''.$db->escape($ban_email).'\'' : 'NULL';
+ $ban_message = ($ban_message != '') ? '\''.$db->escape($ban_message).'\'' : 'NULL';
+
+ if ($_POST['mode'] == 'add')
+ $db->query('INSERT INTO '.$db->prefix.'bans (username, ip, email, message, expire, ban_creator) VALUES('.$ban_user.', '.$ban_ip.', '.$ban_email.', '.$ban_message.', '.$ban_expire.', '.$pun_user['id'].')') or error('Unable to add ban', __FILE__, __LINE__, $db->error());
+ else
+ $db->query('UPDATE '.$db->prefix.'bans SET username='.$ban_user.', ip='.$ban_ip.', email='.$ban_email.', message='.$ban_message.', expire='.$ban_expire.' WHERE id='.intval($_POST['ban_id'])) or error('Unable to update ban', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the bans cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_bans_cache();
+
+ if ($_POST['mode'] == 'edit')
+ redirect('admin_bans.php', $lang_admin_bans['Ban edited redirect']);
+ else
+ redirect('admin_bans.php', $lang_admin_bans['Ban added redirect']);
+}
+
+// Remove a ban
+else if (isset($_GET['del_ban']))
+{
+ confirm_referrer('admin_bans.php');
+
+ $ban_id = intval($_GET['del_ban']);
+ if ($ban_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $db->query('DELETE FROM '.$db->prefix.'bans WHERE id='.$ban_id) or error('Unable to delete ban', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the bans cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_bans_cache();
+
+ redirect('admin_bans.php', $lang_admin_bans['Ban removed redirect']);
+}
+
+// Find bans
+else if (isset($_GET['find_ban']))
+{
+ $form = isset($_GET['form']) ? $_GET['form'] : array();
+
+ // trim() all elements in $form
+ $form = array_map('pun_trim', $form);
+ $conditions = $query_str = array();
+
+ $expire_after = isset($_GET['expire_after']) ? pun_trim($_GET['expire_after']) : '';
+ $expire_before = isset($_GET['expire_before']) ? pun_trim($_GET['expire_before']) : '';
+ $order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'ip', 'email', 'expire')) ? 'b.'.$_GET['order_by'] : 'b.username';
+ $direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
+
+ $query_str[] = 'order_by='.$order_by;
+ $query_str[] = 'direction='.$direction;
+
+ // Try to convert date/time to timestamps
+ if ($expire_after != '')
+ {
+ $query_str[] = 'expire_after='.$expire_after;
+
+ $expire_after = strtotime($expire_after);
+ if ($expire_after === false || $expire_after == -1)
+ message($lang_admin_bans['Invalid date message']);
+
+ $conditions[] = 'b.expire>'.$expire_after;
+ }
+ if ($expire_before != '')
+ {
+ $query_str[] = 'expire_before='.$expire_before;
+
+ $expire_before = strtotime($expire_before);
+ if ($expire_before === false || $expire_before == -1)
+ message($lang_admin_bans['Invalid date message']);
+
+ $conditions[] = 'b.expire<'.$expire_before;
+ }
+
+ $like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
+ foreach ($form as $key => $input)
+ {
+ if ($input != '' && in_array($key, array('username', 'ip', 'email', 'message')))
+ {
+ $conditions[] = 'b.'.$db->escape($key).' '.$like_command.' \''.$db->escape(str_replace(array('*', '_'), array('%', '\\_'), $input)).'\'';
+ $query_str[] = 'form%5B'.$key.'%5D='.urlencode($input);
+ }
+ }
+
+ // Fetch ban count
+ $result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'bans as b WHERE b.id>0'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '')) or error('Unable to fetch ban list', __FILE__, __LINE__, $db->error());
+ $num_bans = $db->result($result);
+
+ // Determine the ban offset (based on $_GET['p'])
+ $num_pages = ceil($num_bans / 50);
+
+ $p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+ $start_from = 50 * ($p - 1);
+
+ // Generate paging links
+ $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_bans.php?find_ban=&amp;'.implode('&amp;', $query_str));
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans'], $lang_admin_bans['Results head']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_bans.php"><?php echo $lang_admin_common['Bans'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_bans['Results head'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+
+<div id="bans1" class="blocktable">
+ <h2><span><?php echo $lang_admin_bans['Results head'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_bans['Results username head'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_admin_bans['Results e-mail head'] ?></th>
+ <th class="tc3" scope="col"><?php echo $lang_admin_bans['Results IP address head'] ?></th>
+ <th class="tc4" scope="col"><?php echo $lang_admin_bans['Results expire head'] ?></th>
+ <th class="tc5" scope="col"><?php echo $lang_admin_bans['Results message head'] ?></th>
+ <th class="tc6" scope="col"><?php echo $lang_admin_bans['Results banned by head'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_admin_bans['Results actions head'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $result = $db->query('SELECT b.id, b.username, b.ip, b.email, b.message, b.expire, b.ban_creator, u.username AS ban_creator_username FROM '.$db->prefix.'bans AS b LEFT JOIN '.$db->prefix.'users AS u ON b.ban_creator=u.id WHERE b.id>0'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '').' ORDER BY '.$db->escape($order_by).' '.$db->escape($direction).' LIMIT '.$start_from.', 50') or error('Unable to fetch ban list', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ while ($ban_data = $db->fetch_assoc($result))
+ {
+
+ $actions = '<a href="admin_bans.php?edit_ban='.$ban_data['id'].'">'.$lang_admin_common['Edit'].'</a> | <a href="admin_bans.php?del_ban='.$ban_data['id'].'">'.$lang_admin_common['Remove'].'</a>';
+ $expire = format_time($ban_data['expire'], true);
+
+?>
+ <tr>
+ <td class="tcl"><?php echo ($ban_data['username'] != '') ? pun_htmlspecialchars($ban_data['username']) : '&#160;' ?></td>
+ <td class="tc2"><?php echo ($ban_data['email'] != '') ? pun_htmlspecialchars($ban_data['email']) : '&#160;' ?></td>
+ <td class="tc3"><?php echo ($ban_data['ip'] != '') ? pun_htmlspecialchars($ban_data['ip']) : '&#160;' ?></td>
+ <td class="tc4"><?php echo $expire ?></td>
+ <td class="tc5"><?php echo ($ban_data['message'] != '') ? pun_htmlspecialchars($ban_data['message']) : '&#160;' ?></td>
+ <td class="tc6"><?php echo ($ban_data['ban_creator_username'] != '') ? '<a href="profile.php?id='.$ban_data['ban_creator'].'">'.pun_htmlspecialchars($ban_data['ban_creator_username']).'</a>' : $lang_admin_bans['Unknown'] ?></td>
+ <td class="tcr"><?php echo $actions ?></td>
+ </tr>
+<?php
+
+ }
+ }
+ else
+ echo "\t\t\t\t".'<tr><td class="tcl" colspan="7">'.$lang_admin_bans['No match'].'</td></tr>'."\n";
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_bans.php"><?php echo $lang_admin_common['Bans'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_bans['Results head'] ?></strong></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans']);
+$focus_element = array('bans', 'new_ban_user');
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('bans');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_bans['New ban head'] ?></span></h2>
+ <div class="box">
+ <form id="bans" method="post" action="admin_bans.php?action=more">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_bans['Add ban subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Username label'] ?><div><input type="submit" name="add_ban" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="2" /></div></th>
+ <td>
+ <input type="text" name="new_ban_user" size="25" maxlength="25" tabindex="1" />
+ <span><?php echo $lang_admin_bans['Username advanced help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+
+ <h2 class="block2"><span><?php echo $lang_admin_bans['Ban search head'] ?></span></h2>
+ <div class="box">
+ <form id="find_bans" method="get" action="admin_bans.php">
+ <p class="submittop"><input type="submit" name="find_ban" value="<?php echo $lang_admin_bans['Submit search'] ?>" tabindex="3" /></p>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_bans['Ban search subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_bans['Ban search info'] ?></p>
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Username label'] ?></th>
+ <td><input type="text" name="form[username]" size="25" maxlength="25" tabindex="4" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['IP label'] ?></th>
+ <td><input type="text" name="form[ip]" size="30" maxlength="255" tabindex="5" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['E-mail label'] ?></th>
+ <td><input type="text" name="form[email]" size="30" maxlength="80" tabindex="6" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Message label'] ?></th>
+ <td><input type="text" name="form[message]" size="30" maxlength="255" tabindex="7" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Expire after label'] ?></th>
+ <td><input type="text" name="expire_after" size="10" maxlength="10" tabindex="8" />
+ <span><?php echo $lang_admin_bans['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Expire before label'] ?></th>
+ <td><input type="text" name="expire_before" size="10" maxlength="10" tabindex="9" />
+ <span><?php echo $lang_admin_bans['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_bans['Order by label'] ?></th>
+ <td>
+ <select name="order_by" tabindex="10">
+ <option value="username" selected="selected"><?php echo $lang_admin_bans['Order by username'] ?></option>
+ <option value="ip"><?php echo $lang_admin_bans['Order by ip'] ?></option>
+ <option value="email"><?php echo $lang_admin_bans['Order by e-mail'] ?></option>
+ <option value="expire"><?php echo $lang_admin_bans['Order by expire'] ?></option>
+ </select>&#160;&#160;&#160;<select name="direction" tabindex="11">
+ <option value="ASC" selected="selected"><?php echo $lang_admin_bans['Ascending'] ?></option>
+ <option value="DESC"><?php echo $lang_admin_bans['Descending'] ?></option>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="find_ban" value="<?php echo $lang_admin_bans['Submit search'] ?>" tabindex="12" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_categories.php b/admin_categories.php
new file mode 100644
index 0000000..756540a
--- /dev/null
+++ b/admin_categories.php
@@ -0,0 +1,266 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_categories.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_categories.php';
+
+// Add a new category
+if (isset($_POST['add_cat']))
+{
+ confirm_referrer('admin_categories.php');
+
+ $new_cat_name = pun_trim($_POST['new_cat_name']);
+ if ($new_cat_name == '')
+ message($lang_admin_categories['Must enter name message']);
+
+ $db->query('INSERT INTO '.$db->prefix.'categories (cat_name) VALUES(\''.$db->escape($new_cat_name).'\')') or error('Unable to create category', __FILE__, __LINE__, $db->error());
+
+ redirect('admin_categories.php', $lang_admin_categories['Category added redirect']);
+}
+
+// Delete a category
+else if (isset($_POST['del_cat']) || isset($_POST['del_cat_comply']))
+{
+ confirm_referrer('admin_categories.php');
+
+ $cat_to_delete = intval($_POST['cat_to_delete']);
+ if ($cat_to_delete < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if (isset($_POST['del_cat_comply'])) // Delete a category with all forums and posts
+ {
+ @set_time_limit(0);
+
+ $result = $db->query('SELECT id FROM '.$db->prefix.'forums WHERE cat_id='.$cat_to_delete) or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+ $num_forums = $db->num_rows($result);
+
+ for ($i = 0; $i < $num_forums; ++$i)
+ {
+ $cur_forum = $db->result($result, $i);
+
+ // Prune all posts and topics
+ prune($cur_forum, 1, -1);
+
+ // Delete the forum
+ $db->query('DELETE FROM '.$db->prefix.'forums WHERE id='.$cur_forum) or error('Unable to delete forum', __FILE__, __LINE__, $db->error());
+ }
+
+ // Locate any "orphaned redirect topics" and delete them
+ $result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+ $num_orphans = $db->num_rows($result);
+
+ if ($num_orphans)
+ {
+ for ($i = 0; $i < $num_orphans; ++$i)
+ $orphans[] = $db->result($result, $i);
+
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+ }
+
+ // Delete the category
+ $db->query('DELETE FROM '.$db->prefix.'categories WHERE id='.$cat_to_delete) or error('Unable to delete category', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_categories.php', $lang_admin_categories['Category deleted redirect']);
+ }
+ else // If the user hasn't confirmed the delete
+ {
+ $result = $db->query('SELECT cat_name FROM '.$db->prefix.'categories WHERE id='.$cat_to_delete) or error('Unable to fetch category info', __FILE__, __LINE__, $db->error());
+ $cat_name = $db->result($result);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Categories']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('categories');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_categories['Delete category head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_categories.php">
+ <div class="inform">
+ <input type="hidden" name="cat_to_delete" value="<?php echo $cat_to_delete ?>" />
+ <fieldset>
+ <legend><?php echo $lang_admin_categories['Confirm delete subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_categories['Confirm delete info'], pun_htmlspecialchars($cat_name)) ?></p>
+ <p class="warntext"><?php echo $lang_admin_categories['Delete category warn'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="del_cat_comply" value="<?php echo $lang_admin_common['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ }
+}
+
+else if (isset($_POST['update'])) // Change position and name of the categories
+{
+ confirm_referrer('admin_categories.php');
+
+ $categories = $_POST['cat'];
+ if (empty($categories))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ foreach ($categories as $cat_id => $cur_cat)
+ {
+ $cur_cat['name'] = pun_trim($cur_cat['name']);
+ $cur_cat['order'] = pun_trim($cur_cat['order']);
+
+ if ($cur_cat['name'] == '')
+ message($lang_admin_categories['Must enter name message']);
+
+ if ($cur_cat['order'] == '' || preg_match('%[^0-9]%', $cur_cat['order']))
+ message($lang_admin_categories['Must enter integer message']);
+
+ $db->query('UPDATE '.$db->prefix.'categories SET cat_name=\''.$db->escape($cur_cat['name']).'\', disp_position='.$cur_cat['order'].' WHERE id='.intval($cat_id)) or error('Unable to update category', __FILE__, __LINE__, $db->error());
+ }
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_categories.php', $lang_admin_categories['Categories updated redirect']);
+}
+
+// Generate an array with all categories
+$result = $db->query('SELECT id, cat_name, disp_position FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+$num_cats = $db->num_rows($result);
+
+for ($i = 0; $i < $num_cats; ++$i)
+ $cat_list[] = $db->fetch_assoc($result);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Categories']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('categories');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_categories['Add categories head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_categories.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_categories['Add categories subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_categories['Add category label'] ?><div><input type="submit" name="add_cat" value="<?php echo $lang_admin_categories['Add new submit'] ?>" tabindex="2" /></div></th>
+ <td>
+ <input type="text" name="new_cat_name" size="35" maxlength="80" tabindex="1" />
+ <span><?php printf($lang_admin_categories['Add category help'], '<a href="admin_forums.php">'.$lang_admin_common['Forums'].'</a>') ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+
+<?php if ($num_cats): ?> <h2 class="block2"><span><?php echo $lang_admin_categories['Delete categories head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_categories.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_categories['Delete categories subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_categories['Delete category label'] ?><div><input type="submit" name="del_cat" value="<?php echo $lang_admin_common['Delete'] ?>" tabindex="4" /></div></th>
+ <td>
+ <select name="cat_to_delete" tabindex="3">
+<?php
+
+ foreach ($cat_list as $cur_cat)
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'">'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+
+?>
+ </select>
+ <span><?php echo $lang_admin_categories['Delete category help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+<?php endif; ?>
+
+<?php if ($num_cats): ?> <h2 class="block2"><span><?php echo $lang_admin_categories['Edit categories head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_categories.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_categories['Edit categories subhead'] ?></legend>
+ <div class="infldset">
+ <table id="categoryedit">
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_categories['Category name label'] ?></th>
+ <th scope="col"><?php echo $lang_admin_categories['Category position label'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ foreach ($cat_list as $cur_cat)
+ {
+
+?>
+ <tr>
+ <td class="tcl"><input type="text" name="cat[<?php echo $cur_cat['id'] ?>][name]" value="<?php echo pun_htmlspecialchars($cur_cat['cat_name']) ?>" size="35" maxlength="80" /></td>
+ <td><input type="text" name="cat[<?php echo $cur_cat['id'] ?>][order]" value="<?php echo $cur_cat['disp_position'] ?>" size="3" maxlength="3" /></td>
+ </tr>
+<?php
+
+ }
+
+?>
+ </tbody>
+ </table>
+ <div class="fsetsubmit"><input type="submit" name="update" value="<?php echo $lang_admin_common['Update'] ?>" /></div>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+<?php endif; ?> </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_censoring.php b/admin_censoring.php
new file mode 100644
index 0000000..94a4f0a
--- /dev/null
+++ b/admin_censoring.php
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_censoring.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_censoring.php';
+
+// Add a censor word
+if (isset($_POST['add_word']))
+{
+ confirm_referrer('admin_censoring.php');
+
+ $search_for = pun_trim($_POST['new_search_for']);
+ $replace_with = pun_trim($_POST['new_replace_with']);
+
+ if ($search_for == '')
+ message($lang_admin_censoring['Must enter word message']);
+
+ $db->query('INSERT INTO '.$db->prefix.'censoring (search_for, replace_with) VALUES (\''.$db->escape($search_for).'\', \''.$db->escape($replace_with).'\')') or error('Unable to add censor word', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the censoring cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_censoring_cache();
+
+ redirect('admin_censoring.php', $lang_admin_censoring['Word added redirect']);
+}
+
+// Update a censor word
+else if (isset($_POST['update']))
+{
+ confirm_referrer('admin_censoring.php');
+
+ $id = intval(key($_POST['update']));
+
+ $search_for = pun_trim($_POST['search_for'][$id]);
+ $replace_with = pun_trim($_POST['replace_with'][$id]);
+
+ if ($search_for == '')
+ message($lang_admin_censoring['Must enter word message']);
+
+ $db->query('UPDATE '.$db->prefix.'censoring SET search_for=\''.$db->escape($search_for).'\', replace_with=\''.$db->escape($replace_with).'\' WHERE id='.$id) or error('Unable to update censor word', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the censoring cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_censoring_cache();
+
+ redirect('admin_censoring.php', $lang_admin_censoring['Word updated redirect']);
+}
+
+// Remove a censor word
+else if (isset($_POST['remove']))
+{
+ confirm_referrer('admin_censoring.php');
+
+ $id = intval(key($_POST['remove']));
+
+ $db->query('DELETE FROM '.$db->prefix.'censoring WHERE id='.$id) or error('Unable to delete censor word', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the censoring cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_censoring_cache();
+
+ redirect('admin_censoring.php', $lang_admin_censoring['Word removed redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Censoring']);
+$focus_element = array('censoring', 'new_search_for');
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('censoring');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_censoring['Censoring head'] ?></span></h2>
+ <div class="box">
+ <form id="censoring" method="post" action="admin_censoring.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_censoring['Add word subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_censoring['Add word info'].' '.($pun_config['o_censoring'] == '1' ? sprintf($lang_admin_censoring['Censoring enabled'], '<a href="admin_options.php#censoring">'.$lang_admin_common['Options'].'</a>') : sprintf($lang_admin_censoring['Censoring disabled'], '<a href="admin_options.php#censoring">'.$lang_admin_common['Options'].'</a>')) ?></p>
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_censoring['Censored word label'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_admin_censoring['Replacement label'] ?></th>
+ <th class="hidehead" scope="col"><?php echo $lang_admin_censoring['Action label'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="tcl"><input type="text" name="new_search_for" size="24" maxlength="60" tabindex="1" /></td>
+ <td class="tc2"><input type="text" name="new_replace_with" size="24" maxlength="60" tabindex="2" /></td>
+ <td><input type="submit" name="add_word" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="3" /></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_censoring['Edit remove subhead'] ?></legend>
+ <div class="infldset">
+<?php
+
+$result = $db->query('SELECT id, search_for, replace_with FROM '.$db->prefix.'censoring ORDER BY id') or error('Unable to fetch censor word list', __FILE__, __LINE__, $db->error());
+if ($db->num_rows($result))
+{
+
+?>
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_censoring['Censored word label'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_admin_censoring['Replacement label'] ?></th>
+ <th class="hidehead" scope="col"><?php echo $lang_admin_censoring['Action label'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ while ($cur_word = $db->fetch_assoc($result))
+ echo "\t\t\t\t\t\t\t\t".'<tr><td class="tcl"><input type="text" name="search_for['.$cur_word['id'].']" value="'.pun_htmlspecialchars($cur_word['search_for']).'" size="24" maxlength="60" /></td><td class="tc2"><input type="text" name="replace_with['.$cur_word['id'].']" value="'.pun_htmlspecialchars($cur_word['replace_with']).'" size="24" maxlength="60" /></td><td><input type="submit" name="update['.$cur_word['id'].']" value="'.$lang_admin_common['Update'].'" />&#160;<input type="submit" name="remove['.$cur_word['id'].']" value="'.$lang_admin_common['Remove'].'" /></td></tr>'."\n";
+
+?>
+ </tbody>
+ </table>
+<?php
+
+}
+else
+ echo "\t\t\t\t\t\t\t".'<p>'.$lang_admin_censoring['No words in list'].'</p>'."\n";
+
+?>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_forums.php b/admin_forums.php
new file mode 100644
index 0000000..c39bf74
--- /dev/null
+++ b/admin_forums.php
@@ -0,0 +1,501 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_forums.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_forums.php';
+
+// Add a "default" forum
+if (isset($_POST['add_forum']))
+{
+ confirm_referrer('admin_forums.php');
+
+ $add_to_cat = intval($_POST['add_to_cat']);
+ if ($add_to_cat < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $db->query('INSERT INTO '.$db->prefix.'forums (forum_name, cat_id) VALUES(\''.$db->escape($lang_admin_forums['New forum']).'\', '.$add_to_cat.')') or error('Unable to create forum', __FILE__, __LINE__, $db->error());
+ $new_fid = $db->insert_id();
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_forums.php?edit_forum='.$new_fid, $lang_admin_forums['Forum added redirect']);
+}
+
+// Delete a forum
+else if (isset($_GET['del_forum']))
+{
+ confirm_referrer('admin_forums.php');
+
+ $forum_id = intval($_GET['del_forum']);
+ if ($forum_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if (isset($_POST['del_forum_comply'])) // Delete a forum with all posts
+ {
+ @set_time_limit(0);
+
+ // Prune all posts and topics
+ prune($forum_id, 1, -1);
+
+ // Locate any "orphaned redirect topics" and delete them
+ $result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+ $num_orphans = $db->num_rows($result);
+
+ if ($num_orphans)
+ {
+ for ($i = 0; $i < $num_orphans; ++$i)
+ $orphans[] = $db->result($result, $i);
+
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+ }
+
+ // Delete the forum and any forum specific group permissions
+ $db->query('DELETE FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to delete forum', __FILE__, __LINE__, $db->error());
+ $db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+
+ // Delete any subscriptions for this forum
+ $db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE forum_id='.$forum_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_forums.php', $lang_admin_forums['Forum deleted redirect']);
+ }
+ else // If the user hasn't confirmed the delete
+ {
+ $result = $db->query('SELECT forum_name FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+ $forum_name = pun_htmlspecialchars($db->result($result));
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Forums']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('forums');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_forums['Confirm delete head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_forums.php?del_forum=<?php echo $forum_id ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_forums['Confirm delete subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_forums['Confirm delete info'], $forum_name) ?></p>
+ <p class="warntext"><?php echo $lang_admin_forums['Confirm delete warn'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="del_forum_comply" value="<?php echo $lang_admin_common['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ }
+}
+
+// Update forum positions
+else if (isset($_POST['update_positions']))
+{
+ confirm_referrer('admin_forums.php');
+
+ foreach ($_POST['position'] as $forum_id => $disp_position)
+ {
+ $disp_position = trim($disp_position);
+ if ($disp_position == '' || preg_match('%[^0-9]%', $disp_position))
+ message($lang_admin_forums['Must be integer message']);
+
+ $db->query('UPDATE '.$db->prefix.'forums SET disp_position='.$disp_position.' WHERE id='.intval($forum_id)) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_forums.php', $lang_admin_forums['Forums updated redirect']);
+}
+
+else if (isset($_GET['edit_forum']))
+{
+ $forum_id = intval($_GET['edit_forum']);
+ if ($forum_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Update group permissions for $forum_id
+ if (isset($_POST['save']))
+ {
+ confirm_referrer('admin_forums.php');
+
+ // Start with the forum details
+ $forum_name = pun_trim($_POST['forum_name']);
+ $forum_desc = pun_linebreaks(pun_trim($_POST['forum_desc']));
+ $cat_id = intval($_POST['cat_id']);
+ $sort_by = intval($_POST['sort_by']);
+ $redirect_url = isset($_POST['redirect_url']) ? pun_trim($_POST['redirect_url']) : null;
+
+ if ($forum_name == '')
+ message($lang_admin_forums['Must enter name message']);
+
+ if ($cat_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $forum_desc = ($forum_desc != '') ? '\''.$db->escape($forum_desc).'\'' : 'NULL';
+ $redirect_url = ($redirect_url != '') ? '\''.$db->escape($redirect_url).'\'' : 'NULL';
+
+ $db->query('UPDATE '.$db->prefix.'forums SET forum_name=\''.$db->escape($forum_name).'\', forum_desc='.$forum_desc.', redirect_url='.$redirect_url.', sort_by='.$sort_by.', cat_id='.$cat_id.' WHERE id='.$forum_id) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+
+ // Now let's deal with the permissions
+ if (isset($_POST['read_forum_old']))
+ {
+ $result = $db->query('SELECT g_id, g_read_board, g_post_replies, g_post_topics FROM '.$db->prefix.'groups WHERE g_id!='.PUN_ADMIN) or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+ while ($cur_group = $db->fetch_assoc($result))
+ {
+ $read_forum_new = ($cur_group['g_read_board'] == '1') ? isset($_POST['read_forum_new'][$cur_group['g_id']]) ? '1' : '0' : intval($_POST['read_forum_old'][$cur_group['g_id']]);
+ $post_replies_new = isset($_POST['post_replies_new'][$cur_group['g_id']]) ? '1' : '0';
+ $post_topics_new = isset($_POST['post_topics_new'][$cur_group['g_id']]) ? '1' : '0';
+
+ // Check if the new settings differ from the old
+ if ($read_forum_new != $_POST['read_forum_old'][$cur_group['g_id']] || $post_replies_new != $_POST['post_replies_old'][$cur_group['g_id']] || $post_topics_new != $_POST['post_topics_old'][$cur_group['g_id']])
+ {
+ // If the new settings are identical to the default settings for this group, delete its row in forum_perms
+ if ($read_forum_new == '1' && $post_replies_new == $cur_group['g_post_replies'] && $post_topics_new == $cur_group['g_post_topics'])
+ $db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE group_id='.$cur_group['g_id'].' AND forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+ else
+ {
+ // Run an UPDATE and see if it affected a row, if not, INSERT
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET read_forum='.$read_forum_new.', post_replies='.$post_replies_new.', post_topics='.$post_topics_new.' WHERE group_id='.$cur_group['g_id'].' AND forum_id='.$forum_id) or error('Unable to insert group forum permissions', __FILE__, __LINE__, $db->error());
+ if (!$db->affected_rows())
+ $db->query('INSERT INTO '.$db->prefix.'forum_perms (group_id, forum_id, read_forum, post_replies, post_topics) VALUES('.$cur_group['g_id'].', '.$forum_id.', '.$read_forum_new.', '.$post_replies_new.', '.$post_topics_new.')') or error('Unable to insert group forum permissions', __FILE__, __LINE__, $db->error());
+ }
+ }
+ }
+ }
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_forums.php', $lang_admin_forums['Forum updated redirect']);
+ }
+ else if (isset($_POST['revert_perms']))
+ {
+ confirm_referrer('admin_forums.php');
+
+ $db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache();
+
+ redirect('admin_forums.php?edit_forum='.$forum_id, $lang_admin_forums['Perms reverted redirect']);
+ }
+
+ // Fetch forum info
+ $result = $db->query('SELECT id, forum_name, forum_desc, redirect_url, num_topics, sort_by, cat_id FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $cur_forum = $db->fetch_assoc($result);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Forums']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('forums');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_forums['Edit forum head'] ?></span></h2>
+ <div class="box">
+ <form id="edit_forum" method="post" action="admin_forums.php?edit_forum=<?php echo $forum_id ?>">
+ <p class="submittop"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" tabindex="6" /></p>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_forums['Edit details subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_forums['Forum name label'] ?></th>
+ <td><input type="text" name="forum_name" size="35" maxlength="80" value="<?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?>" tabindex="1" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_forums['Forum description label'] ?></th>
+ <td><textarea name="forum_desc" rows="3" cols="50" tabindex="2"><?php echo pun_htmlspecialchars($cur_forum['forum_desc']) ?></textarea></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_forums['Category label'] ?></th>
+ <td>
+ <select name="cat_id" tabindex="3">
+<?php
+
+ $result = $db->query('SELECT id, cat_name FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+ while ($cur_cat = $db->fetch_assoc($result))
+ {
+ $selected = ($cur_cat['id'] == $cur_forum['cat_id']) ? ' selected="selected"' : '';
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'"'.$selected.'>'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+ }
+
+?>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_forums['Sort by label'] ?></th>
+ <td>
+ <select name="sort_by" tabindex="4">
+ <option value="0"<?php if ($cur_forum['sort_by'] == '0') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Last post'] ?></option>
+ <option value="1"<?php if ($cur_forum['sort_by'] == '1') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Topic start'] ?></option>
+ <option value="2"<?php if ($cur_forum['sort_by'] == '2') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Subject'] ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_forums['Redirect label'] ?></th>
+ <td><?php echo ($cur_forum['num_topics']) ? $lang_admin_forums['Redirect help'] : '<input type="text" name="redirect_url" size="45" maxlength="100" value="'.pun_htmlspecialchars($cur_forum['redirect_url']).'" tabindex="5" />'; ?></td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_forums['Group permissions subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_forums['Group permissions info'], '<a href="admin_groups.php">'.$lang_admin_common['User groups'].'</a>') ?></p>
+ <table id="forumperms">
+ <thead>
+ <tr>
+ <th class="atcl">&#160;</th>
+ <th><?php echo $lang_admin_forums['Read forum label'] ?></th>
+ <th><?php echo $lang_admin_forums['Post replies label'] ?></th>
+ <th><?php echo $lang_admin_forums['Post topics label'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $result = $db->query('SELECT g.g_id, g.g_title, g.g_read_board, g.g_post_replies, g.g_post_topics, fp.read_forum, fp.post_replies, fp.post_topics FROM '.$db->prefix.'groups AS g LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (g.g_id=fp.group_id AND fp.forum_id='.$forum_id.') WHERE g.g_id!='.PUN_ADMIN.' ORDER BY g.g_id') or error('Unable to fetch group forum permission list', __FILE__, __LINE__, $db->error());
+
+ $cur_index = 7;
+
+ while ($cur_perm = $db->fetch_assoc($result))
+ {
+ $read_forum = ($cur_perm['read_forum'] != '0') ? true : false;
+ $post_replies = (($cur_perm['g_post_replies'] == '0' && $cur_perm['post_replies'] == '1') || ($cur_perm['g_post_replies'] == '1' && $cur_perm['post_replies'] != '0')) ? true : false;
+ $post_topics = (($cur_perm['g_post_topics'] == '0' && $cur_perm['post_topics'] == '1') || ($cur_perm['g_post_topics'] == '1' && $cur_perm['post_topics'] != '0')) ? true : false;
+
+ // Determine if the current settings differ from the default or not
+ $read_forum_def = ($cur_perm['read_forum'] == '0') ? false : true;
+ $post_replies_def = (($post_replies && $cur_perm['g_post_replies'] == '0') || (!$post_replies && ($cur_perm['g_post_replies'] == '' || $cur_perm['g_post_replies'] == '1'))) ? false : true;
+ $post_topics_def = (($post_topics && $cur_perm['g_post_topics'] == '0') || (!$post_topics && ($cur_perm['g_post_topics'] == '' || $cur_perm['g_post_topics'] == '1'))) ? false : true;
+
+?>
+ <tr>
+ <th class="atcl"><?php echo pun_htmlspecialchars($cur_perm['g_title']) ?></th>
+ <td<?php if (!$read_forum_def) echo ' class="nodefault"'; ?>>
+ <input type="hidden" name="read_forum_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($read_forum) ? '1' : '0'; ?>" />
+ <input type="checkbox" name="read_forum_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($read_forum) ? ' checked="checked"' : ''; ?><?php echo ($cur_perm['g_read_board'] == '0') ? ' disabled="disabled"' : ''; ?> tabindex="<?php echo $cur_index++ ?>" />
+ </td>
+ <td<?php if (!$post_replies_def && $cur_forum['redirect_url'] == '') echo ' class="nodefault"'; ?>>
+ <input type="hidden" name="post_replies_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($post_replies) ? '1' : '0'; ?>" />
+ <input type="checkbox" name="post_replies_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_replies) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> tabindex="<?php echo $cur_index++ ?>" />
+ </td>
+ <td<?php if (!$post_topics_def && $cur_forum['redirect_url'] == '') echo ' class="nodefault"'; ?>>
+ <input type="hidden" name="post_topics_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($post_topics) ? '1' : '0'; ?>" />
+ <input type="checkbox" name="post_topics_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_topics) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> tabindex="<?php echo $cur_index++ ?>" />
+ </td>
+ </tr>
+<?php
+
+ }
+
+?>
+ </tbody>
+ </table>
+ <div class="fsetsubmit"><input type="submit" name="revert_perms" value="<?php echo $lang_admin_forums['Revert to default'] ?>" tabindex="<?php echo $cur_index++ ?>" /></div>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" tabindex="<?php echo $cur_index++ ?>" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Forums']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('forums');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_forums['Add forum head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_forums.php?action=adddel">
+<?php
+
+$result = $db->query('SELECT id, cat_name FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result) > 0)
+{
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_forums['Create new subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_forums['Add forum label'] ?><div><input type="submit" name="add_forum" value="<?php echo $lang_admin_forums['Add forum'] ?>" tabindex="2" /></div></th>
+ <td>
+ <select name="add_to_cat" tabindex="1">
+<?php
+
+ while ($cur_cat = $db->fetch_assoc($result))
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'">'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+
+?>
+ </select>
+ <span><?php echo $lang_admin_forums['Add forum help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+}
+else
+{
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_common['None'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_forums['No categories exist'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+}
+
+?>
+ </form>
+ </div>
+<?php
+
+// Display all the categories and forums
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.disp_position FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result) > 0)
+{
+
+?>
+ <h2 class="block2"><span><?php echo $lang_admin_forums['Edit forums head'] ?></span></h2>
+ <div class="box">
+ <form id="edforum" method="post" action="admin_forums.php?action=edit">
+ <p class="submittop"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="3" /></p>
+<?php
+
+$cur_index = 4;
+
+$cur_category = 0;
+while ($cur_forum = $db->fetch_assoc($result))
+{
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category != 0)
+ echo "\t\t\t\t\t\t\t".'</tbody>'."\n\t\t\t\t\t\t\t".'</table>'."\n\t\t\t\t\t\t".'</div>'."\n\t\t\t\t\t".'</fieldset>'."\n\t\t\t\t".'</div>'."\n";
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_forums['Category subhead'] ?> <?php echo pun_htmlspecialchars($cur_forum['cat_name']) ?></legend>
+ <div class="infldset">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl"><?php echo $lang_admin_common['Action'] ?></th>
+ <th class="tc2"><?php echo $lang_admin_forums['Position label'] ?></th>
+ <th class="tcr"><?php echo $lang_admin_forums['Forum label'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $cur_category = $cur_forum['cid'];
+ }
+
+?>
+ <tr>
+ <td class="tcl"><a href="admin_forums.php?edit_forum=<?php echo $cur_forum['fid'] ?>" tabindex="<?php echo $cur_index++ ?>"><?php echo $lang_admin_forums['Edit link'] ?></a> | <a href="admin_forums.php?del_forum=<?php echo $cur_forum['fid'] ?>" tabindex="<?php echo $cur_index++ ?>"><?php echo $lang_admin_forums['Delete link'] ?></a></td>
+ <td class="tc2"><input type="text" name="position[<?php echo $cur_forum['fid'] ?>]" size="3" maxlength="3" value="<?php echo $cur_forum['disp_position'] ?>" tabindex="<?php echo $cur_index++ ?>" /></td>
+ <td class="tcr"><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></td>
+ </tr>
+<?php
+
+}
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="<?php echo $cur_index++ ?>" /></p>
+ </form>
+ </div>
+<?php
+
+}
+
+?>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_groups.php b/admin_groups.php
new file mode 100644
index 0000000..db205d7
--- /dev/null
+++ b/admin_groups.php
@@ -0,0 +1,645 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_censoring.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_groups.php';
+
+
+// Fetch all groups
+$result = $db->query('SELECT * FROM '.$db->prefix.'groups ORDER BY g_id') or error('Unable to fetch user groups', __FILE__, __LINE__, $db->error());
+$groups = array();
+while ($cur_group = $db->fetch_assoc($result))
+ $groups[$cur_group['g_id']] = $cur_group;
+
+// Add/edit a group (stage 1)
+if (isset($_POST['add_group']) || isset($_GET['edit_group']))
+{
+ if (isset($_POST['add_group']))
+ {
+ $base_group = intval($_POST['base_group']);
+ $group = $groups[$base_group];
+
+ $mode = 'add';
+ }
+ else // We are editing a group
+ {
+ $group_id = intval($_GET['edit_group']);
+ if ($group_id < 1 || !isset($groups[$group_id]))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $group = $groups[$group_id];
+
+ $mode = 'edit';
+ }
+
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+ $required_fields = array('req_title' => $lang_admin_groups['Group title label']);
+ $focus_element = array('groups2', 'req_title');
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('groups');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_groups['Group settings head'] ?></span></h2>
+ <div class="box">
+ <form id="groups2" method="post" action="admin_groups.php" onsubmit="return process_form(this)">
+ <p class="submittop"><input type="submit" name="add_edit_group" value="<?php echo $lang_admin_common['Save'] ?>" /></p>
+ <div class="inform">
+ <input type="hidden" name="mode" value="<?php echo $mode ?>" />
+<?php if ($mode == 'edit'): ?> <input type="hidden" name="group_id" value="<?php echo $group_id ?>" />
+<?php endif; ?><?php if ($mode == 'add'): ?> <input type="hidden" name="base_group" value="<?php echo $base_group ?>" />
+<?php endif; ?> <fieldset>
+ <legend><?php echo $lang_admin_groups['Group settings subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_groups['Group settings info'] ?></p>
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Group title label'] ?></th>
+ <td>
+ <input type="text" name="req_title" size="25" maxlength="50" value="<?php if ($mode == 'edit') echo pun_htmlspecialchars($group['g_title']); ?>" tabindex="1" />
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['User title label'] ?></th>
+ <td>
+ <input type="text" name="user_title" size="25" maxlength="50" value="<?php echo pun_htmlspecialchars($group['g_user_title']) ?>" tabindex="2" />
+ <span><?php printf($lang_admin_groups['User title help'], ($group['g_id'] != PUN_GUEST ? $lang_common['Member'] : $lang_common['Guest'])) ?></span>
+ </td>
+ </tr>
+<?php if ($group['g_id'] != PUN_ADMIN): if ($group['g_id'] != PUN_GUEST): ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Promote users label'] ?></th>
+ <td>
+ <select name="promote_next_group" tabindex="3">
+ <option value="0"><?php echo $lang_admin_groups['Disable promotion'] ?></option>
+<?php
+
+foreach ($groups as $cur_group)
+{
+ if (($cur_group['g_id'] != $group['g_id'] || $mode == 'add') && $cur_group['g_id'] != PUN_ADMIN && $cur_group['g_id'] != PUN_GUEST)
+ {
+ if ($cur_group['g_id'] == $group['g_promote_next_group'])
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ }
+}
+
+?>
+ </select>
+ <input type="text" name="promote_min_posts" size="5" maxlength="10" value="<?php echo pun_htmlspecialchars($group['g_promote_min_posts']) ?>" tabindex="4" />
+ <span><?php printf($lang_admin_groups['Promote users help'], $lang_admin_groups['Disable promotion']) ?></span>
+ </td>
+ </tr>
+<?php if ($mode != 'edit' || $pun_config['o_default_user_group'] != $group['g_id']): ?> <tr>
+ <th scope="row"> <?php echo $lang_admin_groups['Mod privileges label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="moderator" value="1"<?php if ($group['g_moderator'] == '1') echo ' checked="checked"' ?> tabindex="5" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="moderator" value="0"<?php if ($group['g_moderator'] == '0') echo ' checked="checked"' ?> tabindex="6" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Mod privileges help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Edit profile label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="mod_edit_users" value="1"<?php if ($group['g_mod_edit_users'] == '1') echo ' checked="checked"' ?> tabindex="7" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="mod_edit_users" value="0"<?php if ($group['g_mod_edit_users'] == '0') echo ' checked="checked"' ?> tabindex="8" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Edit profile help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Rename users label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="mod_rename_users" value="1"<?php if ($group['g_mod_rename_users'] == '1') echo ' checked="checked"' ?> tabindex="9" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="mod_rename_users" value="0"<?php if ($group['g_mod_rename_users'] == '0') echo ' checked="checked"' ?> tabindex="10" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Rename users help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Change passwords label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="mod_change_passwords" value="1"<?php if ($group['g_mod_change_passwords'] == '1') echo ' checked="checked"' ?> tabindex="11" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="mod_change_passwords" value="0"<?php if ($group['g_mod_change_passwords'] == '0') echo ' checked="checked"' ?> tabindex="12" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Change passwords help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Mod promote users label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="mod_promote_users" value="1"<?php if ($group['g_mod_promote_users'] == '1') echo ' checked="checked"' ?> tabindex="13" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="mod_promote_users" value="0"<?php if ($group['g_mod_promote_users'] == '0') echo ' checked="checked"' ?> tabindex="14" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Mod promote users help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Ban users label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="mod_ban_users" value="1"<?php if ($group['g_mod_ban_users'] == '1') echo ' checked="checked"' ?> tabindex="15" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="mod_ban_users" value="0"<?php if ($group['g_mod_ban_users'] == '0') echo ' checked="checked"' ?> tabindex="16" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Ban users help'] ?></span>
+ </td>
+ </tr>
+<?php endif; endif; ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Read board label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="read_board" value="1"<?php if ($group['g_read_board'] == '1') echo ' checked="checked"' ?> tabindex="17" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="read_board" value="0"<?php if ($group['g_read_board'] == '0') echo ' checked="checked"' ?> tabindex="18" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Read board help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['View user info label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="view_users" value="1"<?php if ($group['g_view_users'] == '1') echo ' checked="checked"' ?> tabindex="19" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="view_users" value="0"<?php if ($group['g_view_users'] == '0') echo ' checked="checked"' ?> tabindex="20" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['View user info help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Post replies label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="post_replies" value="1"<?php if ($group['g_post_replies'] == '1') echo ' checked="checked"' ?> tabindex="21" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="post_replies" value="0"<?php if ($group['g_post_replies'] == '0') echo ' checked="checked"' ?> tabindex="22" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Post replies help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Post topics label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="post_topics" value="1"<?php if ($group['g_post_topics'] == '1') echo ' checked="checked"' ?> tabindex="23" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="post_topics" value="0"<?php if ($group['g_post_topics'] == '0') echo ' checked="checked"' ?> tabindex="24" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Post topics help'] ?></span>
+ </td>
+ </tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Edit posts label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="edit_posts" value="1"<?php if ($group['g_edit_posts'] == '1') echo ' checked="checked"' ?> tabindex="25" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="edit_posts" value="0"<?php if ($group['g_edit_posts'] == '0') echo ' checked="checked"' ?> tabindex="26" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Edit posts help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Delete posts label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="delete_posts" value="1"<?php if ($group['g_delete_posts'] == '1') echo ' checked="checked"' ?> tabindex="27" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="delete_posts" value="0"<?php if ($group['g_delete_posts'] == '0') echo ' checked="checked"' ?> tabindex="28" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Delete posts help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Delete topics label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="delete_topics" value="1"<?php if ($group['g_delete_topics'] == '1') echo ' checked="checked"' ?> tabindex="29" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="delete_topics" value="0"<?php if ($group['g_delete_topics'] == '0') echo ' checked="checked"' ?> tabindex="30" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Delete topics help'] ?></span>
+ </td>
+ </tr>
+<?php endif; ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Post links label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="post_links" value="1"<?php if ($group['g_post_links'] == '1') echo ' checked="checked"' ?> tabindex="31" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="post_links" value="0"<?php if ($group['g_post_links'] == '0') echo ' checked="checked"' ?> tabindex="32" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Post links help'] ?></span>
+ </td>
+ </tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Set own title label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="set_title" value="1"<?php if ($group['g_set_title'] == '1') echo ' checked="checked"' ?> tabindex="33" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="set_title" value="0"<?php if ($group['g_set_title'] == '0') echo ' checked="checked"' ?> tabindex="34" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Set own title help'] ?></span>
+ </td>
+ </tr>
+<?php endif; ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['User search label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="search" value="1"<?php if ($group['g_search'] == '1') echo ' checked="checked"' ?> tabindex="35" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="search" value="0"<?php if ($group['g_search'] == '0') echo ' checked="checked"' ?> tabindex="36" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['User search help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['User list search label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="search_users" value="1"<?php if ($group['g_search_users'] == '1') echo ' checked="checked"' ?> tabindex="37" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="search_users" value="0"<?php if ($group['g_search_users'] == '0') echo ' checked="checked"' ?> tabindex="38" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['User list search help'] ?></span>
+ </td>
+ </tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Send e-mails label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="send_email" value="1"<?php if ($group['g_send_email'] == '1') echo ' checked="checked"' ?> tabindex="39" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="send_email" value="0"<?php if ($group['g_send_email'] == '0') echo ' checked="checked"' ?> tabindex="40" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_groups['Send e-mails help'] ?></span>
+ </td>
+ </tr>
+<?php endif; ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Post flood label'] ?></th>
+ <td>
+ <input type="text" name="post_flood" size="5" maxlength="4" value="<?php echo $group['g_post_flood'] ?>" tabindex="41" />
+ <span><?php echo $lang_admin_groups['Post flood help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Search flood label'] ?></th>
+ <td>
+ <input type="text" name="search_flood" size="5" maxlength="4" value="<?php echo $group['g_search_flood'] ?>" tabindex="42" />
+ <span><?php echo $lang_admin_groups['Search flood help'] ?></span>
+ </td>
+ </tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?> <tr>
+ <th scope="row"><?php echo $lang_admin_groups['E-mail flood label'] ?></th>
+ <td>
+ <input type="text" name="email_flood" size="5" maxlength="4" value="<?php echo $group['g_email_flood'] ?>" tabindex="43" />
+ <span><?php echo $lang_admin_groups['E-mail flood help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Report flood label'] ?></th>
+ <td>
+ <input type="text" name="report_flood" size="5" maxlength="4" value="<?php echo $group['g_report_flood'] ?>" tabindex="44" />
+ <span><?php echo $lang_admin_groups['Report flood help'] ?></span>
+ </td>
+ </tr>
+<?php endif; endif; ?> </table>
+<?php if ($group['g_moderator'] == '1' ): ?> <p class="warntext"><?php echo $lang_admin_groups['Moderator info'] ?></p>
+<?php endif; ?> </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="add_edit_group" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="45" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+// Add/edit a group (stage 2)
+else if (isset($_POST['add_edit_group']))
+{
+ confirm_referrer('admin_groups.php');
+
+ // Is this the admin group? (special rules apply)
+ $is_admin_group = (isset($_POST['group_id']) && $_POST['group_id'] == PUN_ADMIN) ? true : false;
+
+ $title = pun_trim($_POST['req_title']);
+ $user_title = pun_trim($_POST['user_title']);
+
+ $promote_min_posts = isset($_POST['promote_min_posts']) ? intval($_POST['promote_min_posts']) : '0';
+ if (isset($_POST['promote_next_group']) &&
+ isset($groups[$_POST['promote_next_group']]) &&
+ !in_array($_POST['promote_next_group'], array(PUN_ADMIN, PUN_GUEST)) &&
+ (!isset($_POST['group_id']) || $_POST['promote_next_group'] != $_POST['group_id']))
+ $promote_next_group = $_POST['promote_next_group'];
+ else
+ $promote_next_group = '0';
+
+ $moderator = isset($_POST['moderator']) && $_POST['moderator'] == '1' ? '1' : '0';
+ $mod_edit_users = $moderator == '1' && isset($_POST['mod_edit_users']) && $_POST['mod_edit_users'] == '1' ? '1' : '0';
+ $mod_rename_users = $moderator == '1' && isset($_POST['mod_rename_users']) && $_POST['mod_rename_users'] == '1' ? '1' : '0';
+ $mod_change_passwords = $moderator == '1' && isset($_POST['mod_change_passwords']) && $_POST['mod_change_passwords'] == '1' ? '1' : '0';
+ $mod_ban_users = $moderator == '1' && isset($_POST['mod_ban_users']) && $_POST['mod_ban_users'] == '1' ? '1' : '0';
+ $mod_promote_users = $moderator == '1' && isset($_POST['mod_promote_users']) && $_POST['mod_promote_users'] == '1' ? '1' : '0';
+ $read_board = isset($_POST['read_board']) ? intval($_POST['read_board']) : '1';
+ $view_users = (isset($_POST['view_users']) && $_POST['view_users'] == '1') || $is_admin_group ? '1' : '0';
+ $post_replies = isset($_POST['post_replies']) ? intval($_POST['post_replies']) : '1';
+ $post_topics = isset($_POST['post_topics']) ? intval($_POST['post_topics']) : '1';
+ $edit_posts = isset($_POST['edit_posts']) ? intval($_POST['edit_posts']) : ($is_admin_group) ? '1' : '0';
+ $delete_posts = isset($_POST['delete_posts']) ? intval($_POST['delete_posts']) : ($is_admin_group) ? '1' : '0';
+ $delete_topics = isset($_POST['delete_topics']) ? intval($_POST['delete_topics']) : ($is_admin_group) ? '1' : '0';
+ $post_links = isset($_POST['post_links']) ? intval($_POST['post_links']) : '1';
+ $set_title = isset($_POST['set_title']) ? intval($_POST['set_title']) : ($is_admin_group) ? '1' : '0';
+ $search = isset($_POST['search']) ? intval($_POST['search']) : '1';
+ $search_users = isset($_POST['search_users']) ? intval($_POST['search_users']) : '1';
+ $send_email = (isset($_POST['send_email']) && $_POST['send_email'] == '1') || $is_admin_group ? '1' : '0';
+ $post_flood = (isset($_POST['post_flood']) && $_POST['post_flood'] >= 0) ? intval($_POST['post_flood']) : '0';
+ $search_flood = (isset($_POST['search_flood']) && $_POST['search_flood'] >= 0) ? intval($_POST['search_flood']) : '0';
+ $email_flood = (isset($_POST['email_flood']) && $_POST['email_flood'] >= 0) ? intval($_POST['email_flood']) : '0';
+ $report_flood = (isset($_POST['report_flood']) && $_POST['report_flood'] >= 0) ? intval($_POST['report_flood']) : '0';
+
+ if ($title == '')
+ message($lang_admin_groups['Must enter title message']);
+
+ $user_title = ($user_title != '') ? '\''.$db->escape($user_title).'\'' : 'NULL';
+
+ if ($_POST['mode'] == 'add')
+ {
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'groups WHERE g_title=\''.$db->escape($title).'\'') or error('Unable to check group title collision', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ message(sprintf($lang_admin_groups['Title already exists message'], pun_htmlspecialchars($title)));
+
+ $db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_promote_min_posts, g_promote_next_group, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_mod_promote_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_post_links, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES(\''.$db->escape($title).'\', '.$user_title.', '.$promote_min_posts.', '.$promote_next_group.', '.$moderator.', '.$mod_edit_users.', '.$mod_rename_users.', '.$mod_change_passwords.', '.$mod_ban_users.', '.$mod_promote_users.', '.$read_board.', '.$view_users.', '.$post_replies.', '.$post_topics.', '.$edit_posts.', '.$delete_posts.', '.$delete_topics.', '.$post_links.', '.$set_title.', '.$search.', '.$search_users.', '.$send_email.', '.$post_flood.', '.$search_flood.', '.$email_flood.', '.$report_flood.')') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+ $new_group_id = $db->insert_id();
+
+ // Now lets copy the forum specific permissions from the group which this group is based on
+ $result = $db->query('SELECT forum_id, read_forum, post_replies, post_topics FROM '.$db->prefix.'forum_perms WHERE group_id='.intval($_POST['base_group'])) or error('Unable to fetch group forum permission list', __FILE__, __LINE__, $db->error());
+ while ($cur_forum_perm = $db->fetch_assoc($result))
+ $db->query('INSERT INTO '.$db->prefix.'forum_perms (group_id, forum_id, read_forum, post_replies, post_topics) VALUES('.$new_group_id.', '.$cur_forum_perm['forum_id'].', '.$cur_forum_perm['read_forum'].', '.$cur_forum_perm['post_replies'].', '.$cur_forum_perm['post_topics'].')') or error('Unable to insert group forum permissions', __FILE__, __LINE__, $db->error());
+ }
+ else
+ {
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'groups WHERE g_title=\''.$db->escape($title).'\' AND g_id!='.intval($_POST['group_id'])) or error('Unable to check group title collision', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ message(sprintf($lang_admin_groups['Title already exists message'], pun_htmlspecialchars($title)));
+
+ $db->query('UPDATE '.$db->prefix.'groups SET g_title=\''.$db->escape($title).'\', g_user_title='.$user_title.', g_promote_min_posts='.$promote_min_posts.', g_promote_next_group='.$promote_next_group.', g_moderator='.$moderator.', g_mod_edit_users='.$mod_edit_users.', g_mod_rename_users='.$mod_rename_users.', g_mod_change_passwords='.$mod_change_passwords.', g_mod_ban_users='.$mod_ban_users.', g_mod_promote_users='.$mod_promote_users.', g_read_board='.$read_board.', g_view_users='.$view_users.', g_post_replies='.$post_replies.', g_post_topics='.$post_topics.', g_edit_posts='.$edit_posts.', g_delete_posts='.$delete_posts.', g_delete_topics='.$delete_topics.', g_post_links='.$post_links.', g_set_title='.$set_title.', g_search='.$search.', g_search_users='.$search_users.', g_send_email='.$send_email.', g_post_flood='.$post_flood.', g_search_flood='.$search_flood.', g_email_flood='.$email_flood.', g_report_flood='.$report_flood.' WHERE g_id='.intval($_POST['group_id'])) or error('Unable to update group', __FILE__, __LINE__, $db->error());
+
+ // Promote all users who would be promoted to this group on their next post
+ if ($promote_next_group)
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$promote_next_group.' WHERE group_id = '.intval($_POST['group_id']).' AND num_posts >= '.$promote_min_posts) or error('Unable to auto-promote existing users', __FILE__, __LINE__, $db->error());
+ }
+
+ // Regenerate the quick jump cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ $group_id = $_POST['mode'] == 'add' ? $new_group_id : intval($_POST['group_id']);
+ generate_quickjump_cache($group_id);
+
+ if ($_POST['mode'] == 'edit')
+ redirect('admin_groups.php', $lang_admin_groups['Group edited redirect']);
+ else
+ redirect('admin_groups.php', $lang_admin_groups['Group added redirect']);
+}
+
+
+// Set default group
+else if (isset($_POST['set_default_group']))
+{
+ confirm_referrer('admin_groups.php');
+
+ $group_id = intval($_POST['default_group']);
+
+ // Make sure it's not the admin or guest groups
+ if ($group_id == PUN_ADMIN || $group_id == PUN_GUEST)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Make sure it's not a moderator group
+ if ($groups[$group_id]['g_moderator'] != 0)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value='.$group_id.' WHERE conf_name=\'o_default_user_group\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the config cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_config_cache();
+
+ redirect('admin_groups.php', $lang_admin_groups['Default group redirect']);
+}
+
+
+// Remove a group
+else if (isset($_GET['del_group']))
+{
+ confirm_referrer('admin_groups.php');
+
+ $group_id = isset($_POST['group_to_delete']) ? intval($_POST['group_to_delete']) : intval($_GET['del_group']);
+ if ($group_id < 5)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Make sure we don't remove the default group
+ if ($group_id == $pun_config['o_default_user_group'])
+ message($lang_admin_groups['Cannot remove default message']);
+
+ // Check if this group has any members
+ $result = $db->query('SELECT g.g_title, COUNT(u.id) FROM '.$db->prefix.'groups AS g INNER JOIN '.$db->prefix.'users AS u ON g.g_id=u.group_id WHERE g.g_id='.$group_id.' GROUP BY g.g_id, g_title') or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
+
+ // If the group doesn't have any members or if we've already selected a group to move the members to
+ if (!$db->num_rows($result) || isset($_POST['del_group']))
+ {
+ if (isset($_POST['del_group_comply']) || isset($_POST['del_group']))
+ {
+ if (isset($_POST['del_group']))
+ {
+ $move_to_group = intval($_POST['move_to_group']);
+ $db->query('UPDATE '.$db->prefix.'users SET group_id='.$move_to_group.' WHERE group_id='.$group_id) or error('Unable to move users into group', __FILE__, __LINE__, $db->error());
+ }
+
+ // Delete the group and any forum specific permissions
+ $db->query('DELETE FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to delete group', __FILE__, __LINE__, $db->error());
+ $db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE group_id='.$group_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+
+ // Don't let users be promoted to this group
+ $db->query('UPDATE '.$db->prefix.'groups SET g_promote_next_group=0 WHERE g_promote_next_group='.$group_id) or error('Unable to remove group as promotion target', __FILE__, __LINE__, $db->error());
+
+ redirect('admin_groups.php', $lang_admin_groups['Group removed redirect']);
+ }
+ else
+ {
+ $result = $db->query('SELECT g_title FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group title', __FILE__, __LINE__, $db->error());
+ $group_title = $db->result($result);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('groups');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_groups['Group delete head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_groups.php?del_group=<?php echo $group_id ?>">
+ <div class="inform">
+ <input type="hidden" name="group_to_delete" value="<?php echo $group_id ?>" />
+ <fieldset>
+ <legend><?php echo $lang_admin_groups['Confirm delete subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_groups['Confirm delete info'], pun_htmlspecialchars($group_title)) ?></p>
+ <p class="warntext"><?php echo $lang_admin_groups['Confirm delete warn'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="del_group_comply" value="<?php echo $lang_admin_common['Delete'] ?>" tabindex="1" /><a href="javascript:history.go(-1)" tabindex="2"><?php echo $lang_admin_common['Go back'] ?></a></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ }
+ }
+
+ list($group_title, $group_members) = $db->fetch_row($result);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('groups');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_groups['Delete group head'] ?></span></h2>
+ <div class="box">
+ <form id="groups" method="post" action="admin_groups.php?del_group=<?php echo $group_id ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_groups['Move users subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_groups['Move users info'], pun_htmlspecialchars($group_title), forum_number_format($group_members)) ?></p>
+ <label><?php echo $lang_admin_groups['Move users label'] ?>
+ <select name="move_to_group">
+<?php
+
+ $result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' AND g_id!='.$group_id.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_group = $db->fetch_assoc($result))
+ {
+ if ($cur_group['g_id'] == PUN_MEMBER) // Pre-select the pre-defined Members group
+ echo "\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ }
+
+?>
+ </select>
+ <br /></label>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="del_group" value="<?php echo $lang_admin_groups['Delete group'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('groups');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_groups['Add groups head'] ?></span></h2>
+ <div class="box">
+ <form id="groups" method="post" action="admin_groups.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_groups['Add group subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['New group label'] ?><div><input type="submit" name="add_group" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="2" /></div></th>
+ <td>
+ <select id="base_group" name="base_group" tabindex="1">
+<?php
+
+foreach ($groups as $cur_group)
+{
+ if ($cur_group['g_id'] != PUN_ADMIN && $cur_group['g_id'] != PUN_GUEST)
+ {
+ if ($cur_group['g_id'] == $pun_config['o_default_user_group'])
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ }
+}
+
+?>
+ </select>
+ <span><?php echo $lang_admin_groups['New group help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_groups['Default group subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_groups['Default group label'] ?><div><input type="submit" name="set_default_group" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="4" /></div></th>
+ <td>
+ <select id="default_group" name="default_group" tabindex="3">
+<?php
+
+foreach ($groups as $cur_group)
+{
+ if ($cur_group['g_id'] > PUN_GUEST && $cur_group['g_moderator'] == 0)
+ {
+ if ($cur_group['g_id'] == $pun_config['o_default_user_group'])
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ }
+}
+
+?>
+ </select>
+ <span><?php echo $lang_admin_groups['Default group help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+
+ <h2 class="block2"><span><?php echo $lang_admin_groups['Existing groups head'] ?></span></h2>
+ <div class="box">
+ <div class="fakeform">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_groups['Edit groups subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_groups['Edit groups info'] ?></p>
+ <table>
+<?php
+
+$cur_index = 5;
+
+foreach ($groups as $cur_group)
+ echo "\t\t\t\t\t\t\t\t".'<tr><th scope="row"><a href="admin_groups.php?edit_group='.$cur_group['g_id'].'" tabindex="'.$cur_index++.'">'.$lang_admin_groups['Edit link'].'</a>'.(($cur_group['g_id'] > PUN_MEMBER) ? ' | <a href="admin_groups.php?del_group='.$cur_group['g_id'].'" tabindex="'.$cur_index++.'">'.$lang_admin_groups['Delete link'].'</a>' : '').'</th><td>'.pun_htmlspecialchars($cur_group['g_title']).'</td></tr>'."\n";
+
+?>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_index.php b/admin_index.php
new file mode 100644
index 0000000..7692bf2
--- /dev/null
+++ b/admin_index.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_index.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_index.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+
+// Check for upgrade
+if ($action == 'check_upgrade')
+{
+ if (!ini_get('allow_url_fopen'))
+ message($lang_admin_index['fopen disabled message']);
+
+ $latest_version = trim(@file_get_contents('http://fluxbb.org/latest_version'));
+ if (empty($latest_version))
+ message($lang_admin_index['Upgrade check failed message']);
+
+ if (version_compare($pun_config['o_cur_version'], $latest_version, '>='))
+ message($lang_admin_index['Running latest version message']);
+ else
+ message(sprintf($lang_admin_index['New version available message'], '<a href="http://fluxbb.org/">FluxBB.org</a>'));
+}
+// Remove install.php
+else if ($action == 'remove_install_file')
+{
+ $deleted = @unlink(PUN_ROOT.'install.php');
+
+ if ($deleted)
+ redirect('admin_index.php', $lang_admin_index['Deleted install.php redirect']);
+ else
+ message($lang_admin_index['Delete install.php failed']);
+}
+
+$install_file_exists = is_file(PUN_ROOT.'install.php');
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Index']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('index');
+
+?>
+ <div class="block">
+ <h2><span><?php echo $lang_admin_index['Forum admin head'] ?></span></h2>
+ <div id="adintro" class="box">
+ <div class="inbox">
+ <p><?php echo $lang_admin_index['Welcome to admin'] ?></p>
+ <ul>
+ <li><span><?php echo $lang_admin_index['Welcome 1'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 2'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 3'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 4'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 5'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 6'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 7'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 8'] ?></span></li>
+ <li><span><?php echo $lang_admin_index['Welcome 9'] ?></span></li>
+ </ul>
+ </div>
+ </div>
+
+<?php if ($install_file_exists) : ?>
+ <h2 class="block2"><span><?php echo $lang_admin_index['Alerts head'] ?></span></h2>
+ <div id="adalerts" class="box">
+ <p><?php printf($lang_admin_index['Install file exists'], '<a href="admin_index.php?action=remove_install_file">'.$lang_admin_index['Delete install file'].'</a>') ?></p>
+ </div>
+<?php endif; ?>
+
+ <h2 class="block2"><span><?php echo $lang_admin_index['About head'] ?></span></h2>
+ <div id="adstats" class="box">
+ <div class="inbox">
+ <dl>
+ <dt><?php echo $lang_admin_index['FluxBB version label'] ?></dt>
+ <dd>
+ <?php printf($lang_admin_index['FluxBB version data']."\n", $pun_config['o_cur_version'], '<a href="admin_index.php?action=check_upgrade">'.$lang_admin_index['Check for upgrade'].'</a>') ?>
+ </dd>
+ <dt><?php echo $lang_admin_index['Server statistics label'] ?></dt>
+ <dd>
+ <a href="admin_statistics.php"><?php echo $lang_admin_index['View server statistics'] ?></a>
+ </dd>
+ <dt><?php echo $lang_admin_index['Support label'] ?></dt>
+ <dd>
+ <a href="http://fluxbb.org/forums/index.php"><?php echo $lang_admin_index['Forum label'] ?></a> - <a href="http://fluxbb.org/community/irc.html"><?php echo $lang_admin_index['IRC label'] ?></a>
+ </dd>
+ </dl>
+ </div>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_loader.php b/admin_loader.php
new file mode 100644
index 0000000..a505e26
--- /dev/null
+++ b/admin_loader.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// The plugin to load should be supplied via GET
+$plugin = isset($_GET['plugin']) ? $_GET['plugin'] : '';
+if (!preg_match('%^AM?P_(\w*?)\.php$%i', $plugin))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// AP_ == Admins only, AMP_ == admins and moderators
+$prefix = substr($plugin, 0, strpos($plugin, '_'));
+if ($pun_user['g_moderator'] == '1' && $prefix == 'AP')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Make sure the file actually exists
+if (!file_exists(PUN_ROOT.'plugins/'.$plugin))
+ message(sprintf($lang_admin_common['No plugin message'], $plugin));
+
+// Construct REQUEST_URI if it isn't set
+if (!isset($_SERVER['REQUEST_URI']))
+ $_SERVER['REQUEST_URI'] = (isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '').'?'.(isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '');
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], str_replace('_', ' ', substr($plugin, strpos($plugin, '_') + 1, -4)));
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+// Attempt to load the plugin. We don't use @ here to suppress error messages,
+// because if we did and a parse error occurred in the plugin, we would only
+// get the "blank page of death"
+include PUN_ROOT.'plugins/'.$plugin;
+if (!defined('PUN_PLUGIN_LOADED'))
+ message(sprintf($lang_admin_common['Plugin failed message'], $plugin));
+
+// Output the clearer div
+?>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_maintenance.php b/admin_maintenance.php
new file mode 100644
index 0000000..45a6376
--- /dev/null
+++ b/admin_maintenance.php
@@ -0,0 +1,361 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+// Tell common.php that we don't want output buffering
+define('PUN_DISABLE_BUFFERING', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_maintenance.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_maintenance.php';
+
+$action = isset($_REQUEST['action']) ? pun_trim($_REQUEST['action']) : '';
+
+if ($action == 'rebuild')
+{
+ confirm_referrer('admin_maintenance.php');
+
+ check_csrf($_GET['csrf_token']);
+
+ $per_page = isset($_GET['i_per_page']) ? intval($_GET['i_per_page']) : 0;
+ $start_at = isset($_GET['i_start_at']) ? intval($_GET['i_start_at']) : 0;
+
+ // Check per page is > 0
+ if ($per_page < 1)
+ message($lang_admin_maintenance['Posts must be integer message']);
+
+ @set_time_limit(0);
+
+ // If this is the first cycle of posts we empty the search index before we proceed
+ if (isset($_GET['i_empty_index']))
+ {
+ $db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+ $db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
+
+ // Reset the sequence for the search words (not needed for SQLite)
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ $result = $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
+ break;
+
+ case 'pgsql';
+ $result = $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_maintenance['Rebuilding search index']);
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo generate_page_title($page_title) ?></title>
+<style type="text/css">
+body {
+ font: 12px Verdana, Arial, Helvetica, sans-serif;
+ color: #333333;
+ background-color: #FFFFFF
+}
+
+h1 {
+ font-size: 16px;
+ font-weight: normal;
+}
+</style>
+</head>
+<body>
+
+<h1><?php echo $lang_admin_maintenance['Rebuilding index info'] ?></h1>
+<hr />
+
+<?php
+
+ $query_str = '';
+
+ require PUN_ROOT.'include/search_idx.php';
+
+ // Fetch posts to process this cycle
+ $result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id >= '.$start_at.' ORDER BY p.id ASC LIMIT '.$per_page) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+ $end_at = 0;
+ while ($cur_item = $db->fetch_assoc($result))
+ {
+ echo '<p><span>'.sprintf($lang_admin_maintenance['Processing post'], $cur_item['id']).'</span></p>'."\n";
+
+ if ($cur_item['id'] == $cur_item['first_post_id'])
+ update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
+ else
+ update_search_index('post', $cur_item['id'], $cur_item['message']);
+
+ $end_at = $cur_item['id'];
+ }
+
+ // Check if there is more work to do
+ if ($end_at > 0)
+ {
+ $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result) > 0)
+ $query_str = '?action=rebuild&csrf_token='.pun_csrf_token().'&i_per_page='.$per_page.'&i_start_at='.$db->result($result);
+ }
+
+ $db->end_transaction();
+ $db->close();
+
+ exit('<meta http-equiv="refresh" content="0;url=admin_maintenance.php'.$query_str.'" /><hr /><p>'.sprintf($lang_admin_maintenance['Javascript redirect failed'], '<a href="admin_maintenance.php'.$query_str.'">'.$lang_admin_maintenance['Click here'].'</a>').'</p>');
+}
+
+if ($action == 'prune')
+{
+ $prune_from = pun_trim($_POST['prune_from']);
+ $prune_sticky = intval($_POST['prune_sticky']);
+
+ if (isset($_POST['prune_comply']))
+ {
+ confirm_referrer('admin_maintenance.php');
+
+ $prune_days = intval($_POST['prune_days']);
+ $prune_date = ($prune_days) ? time() - ($prune_days * 86400) : -1;
+
+ @set_time_limit(0);
+
+ if ($prune_from == 'all')
+ {
+ $result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+ $num_forums = $db->num_rows($result);
+
+ for ($i = 0; $i < $num_forums; ++$i)
+ {
+ $fid = $db->result($result, $i);
+
+ prune($fid, $prune_sticky, $prune_date);
+ update_forum($fid);
+ }
+ }
+ else
+ {
+ $prune_from = intval($prune_from);
+ prune($prune_from, $prune_sticky, $prune_date);
+ update_forum($prune_from);
+ }
+
+ // Locate any "orphaned redirect topics" and delete them
+ $result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+ $num_orphans = $db->num_rows($result);
+
+ if ($num_orphans)
+ {
+ for ($i = 0; $i < $num_orphans; ++$i)
+ $orphans[] = $db->result($result, $i);
+
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+ }
+
+ redirect('admin_maintenance.php', $lang_admin_maintenance['Posts pruned redirect']);
+ }
+
+ $prune_days = pun_trim($_POST['req_prune_days']);
+ if ($prune_days == '' || preg_match('%[^0-9]%', $prune_days))
+ message($lang_admin_maintenance['Days must be integer message']);
+
+ $prune_date = time() - ($prune_days * 86400);
+
+ // Concatenate together the query for counting number of topics to prune
+ $sql = 'SELECT COUNT(id) FROM '.$db->prefix.'topics WHERE last_post<'.$prune_date.' AND moved_to IS NULL';
+
+ if ($prune_sticky == '0')
+ $sql .= ' AND sticky=0';
+
+ if ($prune_from != 'all')
+ {
+ $prune_from = intval($prune_from);
+ $sql .= ' AND forum_id='.$prune_from;
+
+ // Fetch the forum name (just for cosmetic reasons)
+ $result = $db->query('SELECT forum_name FROM '.$db->prefix.'forums WHERE id='.$prune_from) or error('Unable to fetch forum name', __FILE__, __LINE__, $db->error());
+ $forum = '"'.pun_htmlspecialchars($db->result($result)).'"';
+ }
+ else
+ $forum = $lang_admin_maintenance['All forums'];
+
+ $result = $db->query($sql) or error('Unable to fetch topic prune count', __FILE__, __LINE__, $db->error());
+ $num_topics = $db->result($result);
+
+ if (!$num_topics)
+ message(sprintf($lang_admin_maintenance['No old topics message'], $prune_days));
+
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Prune']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('maintenance');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_maintenance['Prune head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_maintenance.php">
+ <div class="inform">
+ <input type="hidden" name="action" value="prune" />
+ <input type="hidden" name="prune_days" value="<?php echo $prune_days ?>" />
+ <input type="hidden" name="prune_sticky" value="<?php echo $prune_sticky ?>" />
+ <input type="hidden" name="prune_from" value="<?php echo $prune_from ?>" />
+ <fieldset>
+ <legend><?php echo $lang_admin_maintenance['Confirm prune subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_maintenance['Confirm prune info'], $prune_days, $forum, forum_number_format($num_topics)) ?></p>
+ <p class="warntext"><?php echo $lang_admin_maintenance['Confirm prune warn'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="prune_comply" value="<?php echo $lang_admin_common['Prune'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ exit;
+}
+
+
+// Get the first post ID from the db
+$result = $db->query('SELECT id FROM '.$db->prefix.'posts ORDER BY id ASC LIMIT 1') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+if ($db->num_rows($result))
+ $first_id = $db->result($result);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Maintenance']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('maintenance');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_maintenance['Maintenance head'] ?></span></h2>
+ <div class="box">
+ <form method="get" action="admin_maintenance.php">
+ <div class="inform">
+ <input type="hidden" name="action" value="rebuild" />
+ <input type="hidden" name="csrf_token" value="<?php echo pun_csrf_token() ?>" />
+ <fieldset>
+ <legend><?php echo $lang_admin_maintenance['Rebuild index subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_admin_maintenance['Rebuild index info'], '<a href="admin_options.php#maintenance">'.$lang_admin_common['Maintenance mode'].'</a>') ?></p>
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_maintenance['Posts per cycle label'] ?></th>
+ <td>
+ <input type="text" name="i_per_page" size="7" maxlength="7" value="300" tabindex="1" />
+ <span><?php echo $lang_admin_maintenance['Posts per cycle help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_maintenance['Starting post label'] ?></th>
+ <td>
+ <input type="text" name="i_start_at" size="7" maxlength="7" value="<?php echo (isset($first_id)) ? $first_id : 0 ?>" tabindex="2" />
+ <span><?php echo $lang_admin_maintenance['Starting post help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_maintenance['Empty index label'] ?></th>
+ <td class="inputadmin">
+ <label><input type="checkbox" name="i_empty_index" value="1" tabindex="3" checked="checked" />&#160;&#160;<?php echo $lang_admin_maintenance['Empty index help'] ?></label>
+ </td>
+ </tr>
+ </table>
+ <p class="topspace"><?php echo $lang_admin_maintenance['Rebuild completed info'] ?></p>
+ <div class="fsetsubmit"><input type="submit" name="rebuild_index" value="<?php echo $lang_admin_maintenance['Rebuild index'] ?>" tabindex="4" /></div>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+
+ <form method="post" action="admin_maintenance.php" onsubmit="return process_form(this)">
+ <div class="inform">
+ <input type="hidden" name="action" value="prune" />
+ <fieldset>
+ <legend><?php echo $lang_admin_maintenance['Prune subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_maintenance['Days old label'] ?></th>
+ <td>
+ <input type="text" name="req_prune_days" size="3" maxlength="3" tabindex="5" />
+ <span><?php echo $lang_admin_maintenance['Days old help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_maintenance['Prune sticky label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="prune_sticky" value="1" tabindex="6" checked="checked" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="prune_sticky" value="0" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_maintenance['Prune sticky help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_maintenance['Prune from label'] ?></th>
+ <td>
+ <select name="prune_from" tabindex="7">
+ <option value="all"><?php echo $lang_admin_maintenance['All forums'] ?></option>
+<?php
+
+ $result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id WHERE f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+ $cur_category = 0;
+ while ($forum = $db->fetch_assoc($result))
+ {
+ if ($forum['cid'] != $cur_category) // Are we still in the same category?
+ {
+ if ($cur_category)
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($forum['cat_name']).'">'."\n";
+ $cur_category = $forum['cid'];
+ }
+
+ echo "\t\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$forum['fid'].'">'.pun_htmlspecialchars($forum['forum_name']).'</option>'."\n";
+ }
+
+?>
+ </optgroup>
+ </select>
+ <span><?php echo $lang_admin_maintenance['Prune from help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ <p class="topspace"><?php printf($lang_admin_maintenance['Prune info'], '<a href="admin_options.php#maintenance">'.$lang_admin_common['Maintenance mode'].'</a>') ?></p>
+ <div class="fsetsubmit"><input type="submit" name="prune" value="<?php echo $lang_admin_common['Prune'] ?>" tabindex="8" /></div>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_options.php b/admin_options.php
new file mode 100644
index 0000000..2cb687a
--- /dev/null
+++ b/admin_options.php
@@ -0,0 +1,884 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_options.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_options.php';
+
+if (isset($_POST['form_sent']))
+{
+ confirm_referrer('admin_options.php', $lang_admin_options['Bad HTTP Referer message']);
+
+ $form = array(
+ 'board_title' => pun_trim($_POST['form']['board_title']),
+ 'board_desc' => pun_trim($_POST['form']['board_desc']),
+ 'base_url' => pun_trim($_POST['form']['base_url']),
+ 'default_timezone' => floatval($_POST['form']['default_timezone']),
+ 'default_dst' => $_POST['form']['default_dst'] != '1' ? '0' : '1',
+ 'default_lang' => pun_trim($_POST['form']['default_lang']),
+ 'default_style' => pun_trim($_POST['form']['default_style']),
+ 'time_format' => pun_trim($_POST['form']['time_format']),
+ 'date_format' => pun_trim($_POST['form']['date_format']),
+ 'timeout_visit' => (intval($_POST['form']['timeout_visit']) > 0) ? intval($_POST['form']['timeout_visit']) : 1,
+ 'timeout_online' => (intval($_POST['form']['timeout_online']) > 0) ? intval($_POST['form']['timeout_online']) : 1,
+ 'redirect_delay' => (intval($_POST['form']['redirect_delay']) >= 0) ? intval($_POST['form']['redirect_delay']) : 0,
+ 'show_version' => $_POST['form']['show_version'] != '1' ? '0' : '1',
+ 'show_user_info' => $_POST['form']['show_user_info'] != '1' ? '0' : '1',
+ 'show_post_count' => $_POST['form']['show_post_count'] != '1' ? '0' : '1',
+ 'smilies' => $_POST['form']['smilies'] != '1' ? '0' : '1',
+ 'smilies_sig' => $_POST['form']['smilies_sig'] != '1' ? '0' : '1',
+ 'make_links' => $_POST['form']['make_links'] != '1' ? '0' : '1',
+ 'topic_review' => (intval($_POST['form']['topic_review']) >= 0) ? intval($_POST['form']['topic_review']) : 0,
+ 'disp_topics_default' => intval($_POST['form']['disp_topics_default']),
+ 'disp_posts_default' => intval($_POST['form']['disp_posts_default']),
+ 'indent_num_spaces' => (intval($_POST['form']['indent_num_spaces']) >= 0) ? intval($_POST['form']['indent_num_spaces']) : 0,
+ 'quote_depth' => (intval($_POST['form']['quote_depth']) > 0) ? intval($_POST['form']['quote_depth']) : 1,
+ 'quickpost' => $_POST['form']['quickpost'] != '1' ? '0' : '1',
+ 'users_online' => $_POST['form']['users_online'] != '1' ? '0' : '1',
+ 'censoring' => $_POST['form']['censoring'] != '1' ? '0' : '1',
+ 'signatures' => $_POST['form']['signatures'] != '1' ? '0' : '1',
+ 'show_dot' => $_POST['form']['show_dot'] != '1' ? '0' : '1',
+ 'topic_views' => $_POST['form']['topic_views'] != '1' ? '0' : '1',
+ 'quickjump' => $_POST['form']['quickjump'] != '1' ? '0' : '1',
+ 'gzip' => $_POST['form']['gzip'] != '1' ? '0' : '1',
+ 'search_all_forums' => $_POST['form']['search_all_forums'] != '1' ? '0' : '1',
+ 'additional_navlinks' => pun_trim($_POST['form']['additional_navlinks']),
+ 'feed_type' => intval($_POST['form']['feed_type']),
+ 'feed_ttl' => intval($_POST['form']['feed_ttl']),
+ 'report_method' => intval($_POST['form']['report_method']),
+ 'mailing_list' => pun_trim($_POST['form']['mailing_list']),
+ 'avatars' => $_POST['form']['avatars'] != '1' ? '0' : '1',
+ 'avatars_dir' => pun_trim($_POST['form']['avatars_dir']),
+ 'avatars_width' => (intval($_POST['form']['avatars_width']) > 0) ? intval($_POST['form']['avatars_width']) : 1,
+ 'avatars_height' => (intval($_POST['form']['avatars_height']) > 0) ? intval($_POST['form']['avatars_height']) : 1,
+ 'avatars_size' => (intval($_POST['form']['avatars_size']) > 0) ? intval($_POST['form']['avatars_size']) : 1,
+ 'admin_email' => strtolower(pun_trim($_POST['form']['admin_email'])),
+ 'webmaster_email' => strtolower(pun_trim($_POST['form']['webmaster_email'])),
+ 'forum_subscriptions' => $_POST['form']['forum_subscriptions'] != '1' ? '0' : '1',
+ 'topic_subscriptions' => $_POST['form']['topic_subscriptions'] != '1' ? '0' : '1',
+ 'smtp_host' => pun_trim($_POST['form']['smtp_host']),
+ 'smtp_user' => pun_trim($_POST['form']['smtp_user']),
+ 'smtp_ssl' => $_POST['form']['smtp_ssl'] != '1' ? '0' : '1',
+ 'regs_allow' => $_POST['form']['regs_allow'] != '1' ? '0' : '1',
+ 'regs_verify' => $_POST['form']['regs_verify'] != '1' ? '0' : '1',
+ 'regs_report' => $_POST['form']['regs_report'] != '1' ? '0' : '1',
+ 'rules' => $_POST['form']['rules'] != '1' ? '0' : '1',
+ 'rules_message' => pun_trim($_POST['form']['rules_message']),
+ 'default_email_setting' => intval($_POST['form']['default_email_setting']),
+ 'announcement' => $_POST['form']['announcement'] != '1' ? '0' : '1',
+ 'announcement_message' => pun_trim($_POST['form']['announcement_message']),
+ 'maintenance' => $_POST['form']['maintenance'] != '1' ? '0' : '1',
+ 'maintenance_message' => pun_trim($_POST['form']['maintenance_message']),
+ );
+
+ if ($form['board_title'] == '')
+ message($lang_admin_options['Must enter title message']);
+
+ // Make sure base_url doesn't end with a slash
+ if (substr($form['base_url'], -1) == '/')
+ $form['base_url'] = substr($form['base_url'], 0, -1);
+
+ // Convert IDN to Punycode if needed
+ if (preg_match('/[^\x00-\x7F]/', $form['base_url']))
+ {
+ if (!function_exists('idn_to_ascii'))
+ message($lang_admin_options['Base URL problem']);
+ else
+ $form['base_url'] = idn_to_ascii($form['base_url']);
+ }
+
+ $languages = forum_list_langs();
+ if (!in_array($form['default_lang'], $languages))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $styles = forum_list_styles();
+ if (!in_array($form['default_style'], $styles))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($form['time_format'] == '')
+ $form['time_format'] = 'H:i:s';
+
+ if ($form['date_format'] == '')
+ $form['date_format'] = 'Y-m-d';
+
+
+ require PUN_ROOT.'include/email.php';
+
+ if (!is_valid_email($form['admin_email']))
+ message($lang_admin_options['Invalid e-mail message']);
+
+ if (!is_valid_email($form['webmaster_email']))
+ message($lang_admin_options['Invalid webmaster e-mail message']);
+
+ if ($form['mailing_list'] != '')
+ $form['mailing_list'] = strtolower(preg_replace('%\s%S', '', $form['mailing_list']));
+
+ // Make sure avatars_dir doesn't end with a slash
+ if (substr($form['avatars_dir'], -1) == '/')
+ $form['avatars_dir'] = substr($form['avatars_dir'], 0, -1);
+
+ if ($form['additional_navlinks'] != '')
+ $form['additional_navlinks'] = pun_trim(pun_linebreaks($form['additional_navlinks']));
+
+ // Change or enter a SMTP password
+ if (isset($_POST['form']['smtp_change_pass']))
+ {
+ $smtp_pass1 = isset($_POST['form']['smtp_pass1']) ? pun_trim($_POST['form']['smtp_pass1']) : '';
+ $smtp_pass2 = isset($_POST['form']['smtp_pass2']) ? pun_trim($_POST['form']['smtp_pass2']) : '';
+
+ if ($smtp_pass1 == $smtp_pass2)
+ $form['smtp_pass'] = $smtp_pass1;
+ else
+ message($lang_admin_options['SMTP passwords did not match']);
+ }
+
+ if ($form['announcement_message'] != '')
+ $form['announcement_message'] = pun_linebreaks($form['announcement_message']);
+ else
+ {
+ $form['announcement_message'] = $lang_admin_options['Enter announcement here'];
+ $form['announcement'] = '0';
+ }
+
+ if ($form['rules_message'] != '')
+ $form['rules_message'] = pun_linebreaks($form['rules_message']);
+ else
+ {
+ $form['rules_message'] = $lang_admin_options['Enter rules here'];
+ $form['rules'] = '0';
+ }
+
+ if ($form['maintenance_message'] != '')
+ $form['maintenance_message'] = pun_linebreaks($form['maintenance_message']);
+ else
+ {
+ $form['maintenance_message'] = $lang_admin_options['Default maintenance message'];
+ $form['maintenance'] = '0';
+ }
+
+ // Make sure the number of displayed topics and posts is between 3 and 75
+ if ($form['disp_topics_default'] < 3)
+ $form['disp_topics_default'] = 3;
+ else if ($form['disp_topics_default'] > 75)
+ $form['disp_topics_default'] = 75;
+
+ if ($form['disp_posts_default'] < 3)
+ $form['disp_posts_default'] = 3;
+ else if ($form['disp_posts_default'] > 75)
+ $form['disp_posts_default'] = 75;
+
+ if ($form['feed_type'] < 0 || $form['feed_type'] > 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($form['feed_ttl'] < 0)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($form['report_method'] < 0 || $form['report_method'] > 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($form['default_email_setting'] < 0 || $form['default_email_setting'] > 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($form['timeout_online'] >= $form['timeout_visit'])
+ message($lang_admin_options['Timeout error message']);
+
+ foreach ($form as $key => $input)
+ {
+ // Only update values that have changed
+ if (array_key_exists('o_'.$key, $pun_config) && $pun_config['o_'.$key] != $input)
+ {
+ if ($input != '' || is_int($input))
+ $value = '\''.$db->escape($input).'\'';
+ else
+ $value = 'NULL';
+
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value='.$value.' WHERE conf_name=\'o_'.$db->escape($key).'\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ // Regenerate the config cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_config_cache();
+ clear_feed_cache();
+
+ redirect('admin_options.php', $lang_admin_options['Options updated redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Options']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('options');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_options['Options head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_options.php">
+ <p class="submittop"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+ <div class="inform">
+ <input type="hidden" name="form_sent" value="1" />
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Essentials subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Board title label'] ?></th>
+ <td>
+ <input type="text" name="form[board_title]" size="50" maxlength="255" value="<?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?>" />
+ <span><?php echo $lang_admin_options['Board title help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Board desc label'] ?></th>
+ <td>
+ <textarea name="form[board_desc]" cols="60" rows="3"><?php echo pun_htmlspecialchars($pun_config['o_board_desc']) ?></textarea>
+ <span><?php echo $lang_admin_options['Board desc help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Base URL label'] ?></th>
+ <td>
+ <input type="text" name="form[base_url]" size="50" maxlength="100" value="<?php echo pun_htmlspecialchars($pun_config['o_base_url']) ?>" />
+ <span><?php echo $lang_admin_options['Base URL help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Timezone label'] ?></th>
+ <td>
+ <select name="form[default_timezone]">
+ <option value="-12"<?php if ($pun_config['o_default_timezone'] == -12) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-12:00'] ?></option>
+ <option value="-11"<?php if ($pun_config['o_default_timezone'] == -11) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-11:00'] ?></option>
+ <option value="-10"<?php if ($pun_config['o_default_timezone'] == -10) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-10:00'] ?></option>
+ <option value="-9.5"<?php if ($pun_config['o_default_timezone'] == -9.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-09:30'] ?></option>
+ <option value="-9"<?php if ($pun_config['o_default_timezone'] == -9) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-09:00'] ?></option>
+ <option value="-8.5"<?php if ($pun_config['o_default_timezone'] == -8.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-08:30'] ?></option>
+ <option value="-8"<?php if ($pun_config['o_default_timezone'] == -8) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-08:00'] ?></option>
+ <option value="-7"<?php if ($pun_config['o_default_timezone'] == -7) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-07:00'] ?></option>
+ <option value="-6"<?php if ($pun_config['o_default_timezone'] == -6) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-06:00'] ?></option>
+ <option value="-5"<?php if ($pun_config['o_default_timezone'] == -5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-05:00'] ?></option>
+ <option value="-4"<?php if ($pun_config['o_default_timezone'] == -4) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-04:00'] ?></option>
+ <option value="-3.5"<?php if ($pun_config['o_default_timezone'] == -3.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-03:30'] ?></option>
+ <option value="-3"<?php if ($pun_config['o_default_timezone'] == -3) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-03:00'] ?></option>
+ <option value="-2"<?php if ($pun_config['o_default_timezone'] == -2) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-02:00'] ?></option>
+ <option value="-1"<?php if ($pun_config['o_default_timezone'] == -1) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-01:00'] ?></option>
+ <option value="0"<?php if ($pun_config['o_default_timezone'] == 0) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC'] ?></option>
+ <option value="1"<?php if ($pun_config['o_default_timezone'] == 1) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+01:00'] ?></option>
+ <option value="2"<?php if ($pun_config['o_default_timezone'] == 2) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+02:00'] ?></option>
+ <option value="3"<?php if ($pun_config['o_default_timezone'] == 3) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+03:00'] ?></option>
+ <option value="3.5"<?php if ($pun_config['o_default_timezone'] == 3.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+03:30'] ?></option>
+ <option value="4"<?php if ($pun_config['o_default_timezone'] == 4) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+04:00'] ?></option>
+ <option value="4.5"<?php if ($pun_config['o_default_timezone'] == 4.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+04:30'] ?></option>
+ <option value="5"<?php if ($pun_config['o_default_timezone'] == 5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+05:00'] ?></option>
+ <option value="5.5"<?php if ($pun_config['o_default_timezone'] == 5.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+05:30'] ?></option>
+ <option value="5.75"<?php if ($pun_config['o_default_timezone'] == 5.75) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+05:45'] ?></option>
+ <option value="6"<?php if ($pun_config['o_default_timezone'] == 6) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+06:00'] ?></option>
+ <option value="6.5"<?php if ($pun_config['o_default_timezone'] == 6.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+06:30'] ?></option>
+ <option value="7"<?php if ($pun_config['o_default_timezone'] == 7) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+07:00'] ?></option>
+ <option value="8"<?php if ($pun_config['o_default_timezone'] == 8) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+08:00'] ?></option>
+ <option value="8.75"<?php if ($pun_config['o_default_timezone'] == 8.75) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+08:45'] ?></option>
+ <option value="9"<?php if ($pun_config['o_default_timezone'] == 9) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+09:00'] ?></option>
+ <option value="9.5"<?php if ($pun_config['o_default_timezone'] == 9.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+09:30'] ?></option>
+ <option value="10"<?php if ($pun_config['o_default_timezone'] == 10) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+10:00'] ?></option>
+ <option value="10.5"<?php if ($pun_config['o_default_timezone'] == 10.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+10:30'] ?></option>
+ <option value="11"<?php if ($pun_config['o_default_timezone'] == 11) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+11:00'] ?></option>
+ <option value="11.5"<?php if ($pun_config['o_default_timezone'] == 11.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+11:30'] ?></option>
+ <option value="12"<?php if ($pun_config['o_default_timezone'] == 12) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+12:00'] ?></option>
+ <option value="12.75"<?php if ($pun_config['o_default_timezone'] == 12.75) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+12:45'] ?></option>
+ <option value="13"<?php if ($pun_config['o_default_timezone'] == 13) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+13:00'] ?></option>
+ <option value="14"<?php if ($pun_config['o_default_timezone'] == 14) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+14:00'] ?></option>
+ </select>
+ <span><?php echo $lang_admin_options['Timezone help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['DST label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[default_dst]" value="1"<?php if ($pun_config['o_default_dst'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[default_dst]" value="0"<?php if ($pun_config['o_default_dst'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['DST help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Language label'] ?></th>
+ <td>
+ <select name="form[default_lang]">
+<?php
+
+ $languages = forum_list_langs();
+
+ foreach ($languages as $temp)
+ {
+ if ($pun_config['o_default_lang'] == $temp)
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+ }
+
+?>
+ </select>
+ <span><?php echo $lang_admin_options['Language help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Default style label'] ?></th>
+ <td>
+ <select name="form[default_style]">
+<?php
+
+ $styles = forum_list_styles();
+
+ foreach ($styles as $temp)
+ {
+ if ($pun_config['o_default_style'] == $temp)
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.str_replace('_', ' ', $temp).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.str_replace('_', ' ', $temp).'</option>'."\n";
+ }
+
+?>
+ </select>
+ <span><?php echo $lang_admin_options['Default style help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ $diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+ $timestamp = time() + $diff;
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Timeouts subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Time format label'] ?></th>
+ <td>
+ <input type="text" name="form[time_format]" size="25" maxlength="25" value="<?php echo pun_htmlspecialchars($pun_config['o_time_format']) ?>" />
+ <span><?php printf($lang_admin_options['Time format help'], gmdate($pun_config['o_time_format'], $timestamp), '<a href="http://www.php.net/manual/en/function.date.php">'.$lang_admin_options['PHP manual'].'</a>') ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Date format label'] ?></th>
+ <td>
+ <input type="text" name="form[date_format]" size="25" maxlength="25" value="<?php echo pun_htmlspecialchars($pun_config['o_date_format']) ?>" />
+ <span><?php printf($lang_admin_options['Date format help'], gmdate($pun_config['o_date_format'], $timestamp), '<a href="http://www.php.net/manual/en/function.date.php">'.$lang_admin_options['PHP manual'].'</a>') ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Visit timeout label'] ?></th>
+ <td>
+ <input type="text" name="form[timeout_visit]" size="5" maxlength="5" value="<?php echo $pun_config['o_timeout_visit'] ?>" />
+ <span><?php echo $lang_admin_options['Visit timeout help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Online timeout label'] ?></th>
+ <td>
+ <input type="text" name="form[timeout_online]" size="5" maxlength="5" value="<?php echo $pun_config['o_timeout_online'] ?>" />
+ <span><?php echo $lang_admin_options['Online timeout help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Redirect time label'] ?></th>
+ <td>
+ <input type="text" name="form[redirect_delay]" size="3" maxlength="3" value="<?php echo $pun_config['o_redirect_delay'] ?>" />
+ <span><?php echo $lang_admin_options['Redirect time help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Display subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Version number label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[show_version]" value="1"<?php if ($pun_config['o_show_version'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[show_version]" value="0"<?php if ($pun_config['o_show_version'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Version number help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Info in posts label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[show_user_info]" value="1"<?php if ($pun_config['o_show_user_info'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[show_user_info]" value="0"<?php if ($pun_config['o_show_user_info'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Info in posts help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Post count label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[show_post_count]" value="1"<?php if ($pun_config['o_show_post_count'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[show_post_count]" value="0"<?php if ($pun_config['o_show_post_count'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Post count help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Smilies label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[smilies]" value="1"<?php if ($pun_config['o_smilies'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[smilies]" value="0"<?php if ($pun_config['o_smilies'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Smilies help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Smilies sigs label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[smilies_sig]" value="1"<?php if ($pun_config['o_smilies_sig'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[smilies_sig]" value="0"<?php if ($pun_config['o_smilies_sig'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Smilies sigs help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Clickable links label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[make_links]" value="1"<?php if ($pun_config['o_make_links'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[make_links]" value="0"<?php if ($pun_config['o_make_links'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Clickable links help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Topic review label'] ?></th>
+ <td>
+ <input type="text" name="form[topic_review]" size="3" maxlength="3" value="<?php echo $pun_config['o_topic_review'] ?>" />
+ <span><?php echo $lang_admin_options['Topic review help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Topics per page label'] ?></th>
+ <td>
+ <input type="text" name="form[disp_topics_default]" size="3" maxlength="2" value="<?php echo $pun_config['o_disp_topics_default'] ?>" />
+ <span><?php echo $lang_admin_options['Topics per page help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Posts per page label'] ?></th>
+ <td>
+ <input type="text" name="form[disp_posts_default]" size="3" maxlength="2" value="<?php echo $pun_config['o_disp_posts_default'] ?>" />
+ <span><?php echo $lang_admin_options['Posts per page help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Indent label'] ?></th>
+ <td>
+ <input type="text" name="form[indent_num_spaces]" size="3" maxlength="3" value="<?php echo $pun_config['o_indent_num_spaces'] ?>" />
+ <span><?php echo $lang_admin_options['Indent help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Quote depth label'] ?></th>
+ <td>
+ <input type="text" name="form[quote_depth]" size="3" maxlength="3" value="<?php echo $pun_config['o_quote_depth'] ?>" />
+ <span><?php echo $lang_admin_options['Quote depth help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Features subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Quick post label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[quickpost]" value="1"<?php if ($pun_config['o_quickpost'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[quickpost]" value="0"<?php if ($pun_config['o_quickpost'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Quick post help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Users online label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[users_online]" value="1"<?php if ($pun_config['o_users_online'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[users_online]" value="0"<?php if ($pun_config['o_users_online'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Users online help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><a name="censoring"></a><?php echo $lang_admin_options['Censor words label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[censoring]" value="1"<?php if ($pun_config['o_censoring'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[censoring]" value="0"<?php if ($pun_config['o_censoring'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php printf($lang_admin_options['Censor words help'], '<a href="admin_censoring.php">'.$lang_admin_common['Censoring'].'</a>') ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><a name="signatures"></a><?php echo $lang_admin_options['Signatures label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[signatures]" value="1"<?php if ($pun_config['o_signatures'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[signatures]" value="0"<?php if ($pun_config['o_signatures'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Signatures help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['User has posted label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[show_dot]" value="1"<?php if ($pun_config['o_show_dot'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[show_dot]" value="0"<?php if ($pun_config['o_show_dot'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['User has posted help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Topic views label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[topic_views]" value="1"<?php if ($pun_config['o_topic_views'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[topic_views]" value="0"<?php if ($pun_config['o_topic_views'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Topic views help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Quick jump label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[quickjump]" value="1"<?php if ($pun_config['o_quickjump'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[quickjump]" value="0"<?php if ($pun_config['o_quickjump'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Quick jump help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['GZip label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[gzip]" value="1"<?php if ($pun_config['o_gzip'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[gzip]" value="0"<?php if ($pun_config['o_gzip'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['GZip help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Search all label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[search_all_forums]" value="1"<?php if ($pun_config['o_search_all_forums'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[search_all_forums]" value="0"<?php if ($pun_config['o_search_all_forums'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Search all help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Menu items label'] ?></th>
+ <td>
+ <textarea name="form[additional_navlinks]" rows="3" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_additional_navlinks']) ?></textarea>
+ <span><?php echo $lang_admin_options['Menu items help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Feed subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Default feed label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[feed_type]" value="0"<?php if ($pun_config['o_feed_type'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['None'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[feed_type]" value="1"<?php if ($pun_config['o_feed_type'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['RSS'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[feed_type]" value="2"<?php if ($pun_config['o_feed_type'] == '2') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Atom'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Default feed help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Feed TTL label'] ?></th>
+ <td>
+ <select name="form[feed_ttl]">
+ <option value="0"<?php if ($pun_config['o_feed_ttl'] == '0') echo ' selected="selected"'; ?>><?php echo $lang_admin_options['No cache'] ?></option>
+<?php
+
+ $times = array(5, 15, 30, 60);
+
+ foreach ($times as $time)
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$time.'"'.($pun_config['o_feed_ttl'] == $time ? ' selected="selected"' : '').'>'.sprintf($lang_admin_options['Minutes'], $time).'</option>'."\n";
+
+?>
+ </select>
+ <span><?php echo $lang_admin_options['Feed TTL help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Reports subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Reporting method label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[report_method]" value="0"<?php if ($pun_config['o_report_method'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Internal'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[report_method]" value="1"<?php if ($pun_config['o_report_method'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['By e-mail'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[report_method]" value="2"<?php if ($pun_config['o_report_method'] == '2') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Both'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Reporting method help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Mailing list label'] ?></th>
+ <td>
+ <textarea name="form[mailing_list]" rows="5" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_mailing_list']) ?></textarea>
+ <span><?php echo $lang_admin_options['Mailing list help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Avatars subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Use avatars label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[avatars]" value="1"<?php if ($pun_config['o_avatars'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[avatars]" value="0"<?php if ($pun_config['o_avatars'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Use avatars help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Upload directory label'] ?></th>
+ <td>
+ <input type="text" name="form[avatars_dir]" size="35" maxlength="50" value="<?php echo pun_htmlspecialchars($pun_config['o_avatars_dir']) ?>" />
+ <span><?php echo $lang_admin_options['Upload directory help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Max width label'] ?></th>
+ <td>
+ <input type="text" name="form[avatars_width]" size="5" maxlength="5" value="<?php echo $pun_config['o_avatars_width'] ?>" />
+ <span><?php echo $lang_admin_options['Max width help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Max height label'] ?></th>
+ <td>
+ <input type="text" name="form[avatars_height]" size="5" maxlength="5" value="<?php echo $pun_config['o_avatars_height'] ?>" />
+ <span><?php echo $lang_admin_options['Max height help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Max size label'] ?></th>
+ <td>
+ <input type="text" name="form[avatars_size]" size="6" maxlength="6" value="<?php echo $pun_config['o_avatars_size'] ?>" />
+ <span><?php echo $lang_admin_options['Max size help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['E-mail subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Admin e-mail label'] ?></th>
+ <td>
+ <input type="text" name="form[admin_email]" size="50" maxlength="80" value="<?php echo pun_htmlspecialchars($pun_config['o_admin_email']) ?>" />
+ <span><?php echo $lang_admin_options['Admin e-mail help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Webmaster e-mail label'] ?></th>
+ <td>
+ <input type="text" name="form[webmaster_email]" size="50" maxlength="80" value="<?php echo pun_htmlspecialchars($pun_config['o_webmaster_email']) ?>" />
+ <span><?php echo $lang_admin_options['Webmaster e-mail help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Forum subscriptions label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[forum_subscriptions]" value="1"<?php if ($pun_config['o_forum_subscriptions'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[forum_subscriptions]" value="0"<?php if ($pun_config['o_forum_subscriptions'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Forum subscriptions help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Topic subscriptions label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[topic_subscriptions]" value="1"<?php if ($pun_config['o_topic_subscriptions'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[topic_subscriptions]" value="0"<?php if ($pun_config['o_topic_subscriptions'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Topic subscriptions help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['SMTP address label'] ?></th>
+ <td>
+ <input type="text" name="form[smtp_host]" size="30" value="<?php echo pun_htmlspecialchars($pun_config['o_smtp_host']) ?>" />
+ <span><?php echo $lang_admin_options['SMTP address help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['SMTP username label'] ?></th>
+ <td>
+ <input type="text" name="form[smtp_user]" size="25" value="<?php echo pun_htmlspecialchars($pun_config['o_smtp_user']) ?>" />
+ <span><?php echo $lang_admin_options['SMTP username help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['SMTP password label'] ?></th>
+ <td>
+ <label><input type="checkbox" name="form[smtp_change_pass]" value="1" />&#160;<?php echo $lang_admin_options['SMTP change password help'] ?></label>
+<?php $smtp_pass = !empty($pun_config['o_smtp_pass']) ? random_key(pun_strlen($pun_config['o_smtp_pass']), true) : ''; ?>
+ <input type="password" name="form[smtp_pass1]" size="25" value="<?php echo $smtp_pass ?>" />
+ <input type="password" name="form[smtp_pass2]" size="25" value="<?php echo $smtp_pass ?>" />
+ <span><?php echo $lang_admin_options['SMTP password help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['SMTP SSL label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[smtp_ssl]" value="1"<?php if ($pun_config['o_smtp_ssl'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[smtp_ssl]" value="0"<?php if ($pun_config['o_smtp_ssl'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['SMTP SSL help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Registration subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Allow new label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[regs_allow]" value="1"<?php if ($pun_config['o_regs_allow'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[regs_allow]" value="0"<?php if ($pun_config['o_regs_allow'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Allow new help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Verify label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[regs_verify]" value="1"<?php if ($pun_config['o_regs_verify'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[regs_verify]" value="0"<?php if ($pun_config['o_regs_verify'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Verify help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Report new label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[regs_report]" value="1"<?php if ($pun_config['o_regs_report'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[regs_report]" value="0"<?php if ($pun_config['o_regs_report'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Report new help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Use rules label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[rules]" value="1"<?php if ($pun_config['o_rules'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[rules]" value="0"<?php if ($pun_config['o_rules'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Use rules help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Rules label'] ?></th>
+ <td>
+ <textarea name="form[rules_message]" rows="10" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_rules_message']) ?></textarea>
+ <span><?php echo $lang_admin_options['Rules help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['E-mail default label'] ?></th>
+ <td>
+ <span><?php echo $lang_admin_options['E-mail default help'] ?></span>
+ <label><input type="radio" name="form[default_email_setting]" id="form_default_email_setting_0" value="0"<?php if ($pun_config['o_default_email_setting'] == '0') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Display e-mail label'] ?></label>
+ <label><input type="radio" name="form[default_email_setting]" id="form_default_email_setting_1" value="1"<?php if ($pun_config['o_default_email_setting'] == '1') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Hide allow form label'] ?></label>
+ <label><input type="radio" name="form[default_email_setting]" id="form_default_email_setting_2" value="2"<?php if ($pun_config['o_default_email_setting'] == '2') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Hide both label'] ?></label>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Announcement subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Display announcement label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[announcement]" value="1"<?php if ($pun_config['o_announcement'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[announcement]" value="0"<?php if ($pun_config['o_announcement'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Display announcement help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Announcement message label'] ?></th>
+ <td>
+ <textarea name="form[announcement_message]" rows="5" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_announcement_message']) ?></textarea>
+ <span><?php echo $lang_admin_options['Announcement message help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_options['Maintenance subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><a name="maintenance"></a><?php echo $lang_admin_options['Maintenance mode label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[maintenance]" value="1"<?php if ($pun_config['o_maintenance'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[maintenance]" value="0"<?php if ($pun_config['o_maintenance'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_options['Maintenance mode help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_options['Maintenance message label'] ?></th>
+ <td>
+ <textarea name="form[maintenance_message]" rows="5" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_maintenance_message']) ?></textarea>
+ <span><?php echo $lang_admin_options['Maintenance message help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_permissions.php b/admin_permissions.php
new file mode 100644
index 0000000..806b651
--- /dev/null
+++ b/admin_permissions.php
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_permissions.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_permissions.php';
+
+if (isset($_POST['form_sent']))
+{
+ confirm_referrer('admin_permissions.php');
+
+ $form = array_map('intval', $_POST['form']);
+
+ foreach ($form as $key => $input)
+ {
+ // Make sure the input is never a negative value
+ if($input < 0)
+ $input = 0;
+
+ // Only update values that have changed
+ if (array_key_exists('p_'.$key, $pun_config) && $pun_config['p_'.$key] != $input)
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value='.$input.' WHERE conf_name=\'p_'.$db->escape($key).'\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+ }
+
+ // Regenerate the config cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_config_cache();
+
+ redirect('admin_permissions.php', $lang_admin_permissions['Perms updated redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Permissions']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('permissions');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_permissions['Permissions head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_permissions.php">
+ <p class="submittop"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+ <div class="inform">
+ <input type="hidden" name="form_sent" value="1" />
+ <fieldset>
+ <legend><?php echo $lang_admin_permissions['Posting subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['BBCode label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[message_bbcode]" value="1"<?php if ($pun_config['p_message_bbcode'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[message_bbcode]" value="0"<?php if ($pun_config['p_message_bbcode'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['BBCode help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Image tag label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[message_img_tag]" value="1"<?php if ($pun_config['p_message_img_tag'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[message_img_tag]" value="0"<?php if ($pun_config['p_message_img_tag'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['Image tag help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['All caps message label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[message_all_caps]" value="1"<?php if ($pun_config['p_message_all_caps'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[message_all_caps]" value="0"<?php if ($pun_config['p_message_all_caps'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['All caps message help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['All caps subject label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[subject_all_caps]" value="1"<?php if ($pun_config['p_subject_all_caps'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[subject_all_caps]" value="0"<?php if ($pun_config['p_subject_all_caps'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['All caps subject help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Require e-mail label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[force_guest_email]" value="1"<?php if ($pun_config['p_force_guest_email'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[force_guest_email]" value="0"<?php if ($pun_config['p_force_guest_email'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['Require e-mail help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_permissions['Signatures subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['BBCode sigs label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[sig_bbcode]" value="1"<?php if ($pun_config['p_sig_bbcode'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[sig_bbcode]" value="0"<?php if ($pun_config['p_sig_bbcode'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['BBCode sigs help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Image tag sigs label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[sig_img_tag]" value="1"<?php if ($pun_config['p_sig_img_tag'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[sig_img_tag]" value="0"<?php if ($pun_config['p_sig_img_tag'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['Image tag sigs help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['All caps sigs label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[sig_all_caps]" value="1"<?php if ($pun_config['p_sig_all_caps'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[sig_all_caps]" value="0"<?php if ($pun_config['p_sig_all_caps'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['All caps sigs help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Max sig length label'] ?></th>
+ <td>
+ <input type="text" name="form[sig_length]" size="5" maxlength="5" value="<?php echo $pun_config['p_sig_length'] ?>" />
+ <span class="clearb"><?php echo $lang_admin_permissions['Max sig length help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Max sig lines label'] ?></th>
+ <td>
+ <input type="text" name="form[sig_lines]" size="3" maxlength="3" value="<?php echo $pun_config['p_sig_lines'] ?>" />
+ <span class="clearb"><?php echo $lang_admin_permissions['Max sig lines help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_permissions['Registration subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Banned e-mail label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[allow_banned_email]" value="1"<?php if ($pun_config['p_allow_banned_email'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[allow_banned_email]" value="0"<?php if ($pun_config['p_allow_banned_email'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['Banned e-mail help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_permissions['Duplicate e-mail label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="form[allow_dupe_email]" value="1"<?php if ($pun_config['p_allow_dupe_email'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="form[allow_dupe_email]" value="0"<?php if ($pun_config['p_allow_dupe_email'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_permissions['Duplicate e-mail help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_reports.php b/admin_reports.php
new file mode 100644
index 0000000..77f2163
--- /dev/null
+++ b/admin_reports.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_reports.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_reports.php';
+
+// Zap a report
+if (isset($_POST['zap_id']))
+{
+ confirm_referrer('admin_reports.php');
+
+ $zap_id = intval(key($_POST['zap_id']));
+
+ $result = $db->query('SELECT zapped FROM '.$db->prefix.'reports WHERE id='.$zap_id) or error('Unable to fetch report info', __FILE__, __LINE__, $db->error());
+ $zapped = $db->result($result);
+
+ if ($zapped == '')
+ $db->query('UPDATE '.$db->prefix.'reports SET zapped='.time().', zapped_by='.$pun_user['id'].' WHERE id='.$zap_id) or error('Unable to zap report', __FILE__, __LINE__, $db->error());
+
+ // Delete old reports (which cannot be viewed anyway)
+ $result = $db->query('SELECT zapped FROM '.$db->prefix.'reports WHERE zapped IS NOT NULL ORDER BY zapped DESC LIMIT 10,1') or error('Unable to fetch read reports to delete', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result) > 0)
+ {
+ $zapped_threshold = $db->result($result);
+ $db->query('DELETE FROM '.$db->prefix.'reports WHERE zapped <= '.$zapped_threshold) or error('Unable to delete old read reports', __FILE__, __LINE__, $db->error());
+ }
+
+ redirect('admin_reports.php', $lang_admin_reports['Report zapped redirect']);
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Reports']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('reports');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_reports['New reports head'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="admin_reports.php?action=zap">
+<?php
+
+$result = $db->query('SELECT r.id, r.topic_id, r.forum_id, r.reported_by, r.created, r.message, p.id AS pid, t.subject, f.forum_name, u.username AS reporter FROM '.$db->prefix.'reports AS r LEFT JOIN '.$db->prefix.'posts AS p ON r.post_id=p.id LEFT JOIN '.$db->prefix.'topics AS t ON r.topic_id=t.id LEFT JOIN '.$db->prefix.'forums AS f ON r.forum_id=f.id LEFT JOIN '.$db->prefix.'users AS u ON r.reported_by=u.id WHERE r.zapped IS NULL ORDER BY created DESC') or error('Unable to fetch report list', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result))
+{
+ while ($cur_report = $db->fetch_assoc($result))
+ {
+ $reporter = ($cur_report['reporter'] != '') ? '<a href="profile.php?id='.$cur_report['reported_by'].'">'.pun_htmlspecialchars($cur_report['reporter']).'</a>' : $lang_admin_reports['Deleted user'];
+ $forum = ($cur_report['forum_name'] != '') ? '<span><a href="viewforum.php?id='.$cur_report['forum_id'].'">'.pun_htmlspecialchars($cur_report['forum_name']).'</a></span>' : '<span>'.$lang_admin_reports['Deleted'].'</span>';
+ $topic = ($cur_report['subject'] != '') ? '<span>»&#160;<a href="viewtopic.php?id='.$cur_report['topic_id'].'">'.pun_htmlspecialchars($cur_report['subject']).'</a></span>' : '<span>»&#160;'.$lang_admin_reports['Deleted'].'</span>';
+ $post = str_replace("\n", '<br />', pun_htmlspecialchars($cur_report['message']));
+ $post_id = ($cur_report['pid'] != '') ? '<span>»&#160;<a href="viewtopic.php?pid='.$cur_report['pid'].'#p'.$cur_report['pid'].'">'.sprintf($lang_admin_reports['Post ID'], $cur_report['pid']).'</a></span>' : '<span>»&#160;'.$lang_admin_reports['Deleted'].'</span>';
+ $report_location = array($forum, $topic, $post_id);
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php printf($lang_admin_reports['Report subhead'], format_time($cur_report['created'])) ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php printf($lang_admin_reports['Reported by'], $reporter) ?></th>
+ <td class="location"><?php echo implode(' ', $report_location) ?></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_reports['Reason'] ?><div><input type="submit" name="zap_id[<?php echo $cur_report['id'] ?>]" value="<?php echo $lang_admin_reports['Zap'] ?>" /></div></th>
+ <td><?php echo $post ?></td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ }
+}
+else
+{
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_common['None'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_reports['No new reports'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+}
+
+?>
+ </form>
+ </div>
+ </div>
+
+ <div class="blockform block2">
+ <h2><span><?php echo $lang_admin_reports['Last 10 head'] ?></span></h2>
+ <div class="box">
+ <div class="fakeform">
+<?php
+
+$result = $db->query('SELECT r.id, r.topic_id, r.forum_id, r.reported_by, r.message, r.zapped, r.zapped_by AS zapped_by_id, p.id AS pid, t.subject, f.forum_name, u.username AS reporter, u2.username AS zapped_by FROM '.$db->prefix.'reports AS r LEFT JOIN '.$db->prefix.'posts AS p ON r.post_id=p.id LEFT JOIN '.$db->prefix.'topics AS t ON r.topic_id=t.id LEFT JOIN '.$db->prefix.'forums AS f ON r.forum_id=f.id LEFT JOIN '.$db->prefix.'users AS u ON r.reported_by=u.id LEFT JOIN '.$db->prefix.'users AS u2 ON r.zapped_by=u2.id WHERE r.zapped IS NOT NULL ORDER BY zapped DESC LIMIT 10') or error('Unable to fetch report list', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result))
+{
+ while ($cur_report = $db->fetch_assoc($result))
+ {
+ $reporter = ($cur_report['reporter'] != '') ? '<a href="profile.php?id='.$cur_report['reported_by'].'">'.pun_htmlspecialchars($cur_report['reporter']).'</a>' : $lang_admin_reports['Deleted user'];
+ $forum = ($cur_report['forum_name'] != '') ? '<span><a href="viewforum.php?id='.$cur_report['forum_id'].'">'.pun_htmlspecialchars($cur_report['forum_name']).'</a></span>' : '<span>'.$lang_admin_reports['Deleted'].'</span>';
+ $topic = ($cur_report['subject'] != '') ? '<span>»&#160;<a href="viewtopic.php?id='.$cur_report['topic_id'].'">'.pun_htmlspecialchars($cur_report['subject']).'</a></span>' : '<span>»&#160;'.$lang_admin_reports['Deleted'].'</span>';
+ $post = str_replace("\n", '<br />', pun_htmlspecialchars($cur_report['message']));
+ $post_id = ($cur_report['pid'] != '') ? '<span>»&#160;<a href="viewtopic.php?pid='.$cur_report['pid'].'#p'.$cur_report['pid'].'">'.sprintf($lang_admin_reports['Post ID'], $cur_report['pid']).'</a></span>' : '<span>»&#160;'.$lang_admin_reports['Deleted'].'</span>';
+ $zapped_by = ($cur_report['zapped_by'] != '') ? '<strong>'.pun_htmlspecialchars($cur_report['zapped_by']).'</strong>' : $lang_admin_reports['NA'];
+ $report_location = array($forum, $topic, $post_id);
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php printf($lang_admin_reports['Zapped subhead'], format_time($cur_report['zapped']), $zapped_by) ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php printf($lang_admin_reports['Reported by'], $reporter) ?></th>
+ <td class="location"><?php echo implode(' ', $report_location) ?></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_reports['Reason'] ?></th>
+ <td><?php echo $post ?></td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ }
+}
+else
+{
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_common['None'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_reports['No zapped reports'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+}
+
+?>
+ </div>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_statistics.php b/admin_statistics.php
new file mode 100644
index 0000000..901e14c
--- /dev/null
+++ b/admin_statistics.php
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_index.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_index.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+
+
+// Show phpinfo() output
+if ($action == 'phpinfo' && $pun_user['g_id'] == PUN_ADMIN)
+{
+ // Is phpinfo() a disabled function?
+ if (strpos(strtolower((string) ini_get('disable_functions')), 'phpinfo') !== false)
+ message($lang_admin_index['PHPinfo disabled message']);
+
+ phpinfo();
+ exit;
+}
+
+
+// Get the server load averages (if possible)
+if (@file_exists('/proc/loadavg') && is_readable('/proc/loadavg'))
+{
+ // We use @ just in case
+ $fh = @fopen('/proc/loadavg', 'r');
+ $load_averages = @fread($fh, 64);
+ @fclose($fh);
+
+ if (($fh = @fopen('/proc/loadavg', 'r')))
+ {
+ $load_averages = fread($fh, 64);
+ fclose($fh);
+ }
+ else
+ $load_averages = '';
+
+ $load_averages = @explode(' ', $load_averages);
+ $server_load = isset($load_averages[2]) ? $load_averages[0].' '.$load_averages[1].' '.$load_averages[2] : $lang_admin_index['Not available'];
+}
+else if (!in_array(PHP_OS, array('WINNT', 'WIN32')) && preg_match('%averages?: ([0-9\.]+),?\s+([0-9\.]+),?\s+([0-9\.]+)%i', @exec('uptime'), $load_averages))
+ $server_load = $load_averages[1].' '.$load_averages[2].' '.$load_averages[3];
+else
+ $server_load = $lang_admin_index['Not available'];
+
+
+// Get number of current visitors
+$result = $db->query('SELECT COUNT(user_id) FROM '.$db->prefix.'online WHERE idle=0') or error('Unable to fetch online count', __FILE__, __LINE__, $db->error());
+$num_online = $db->result($result);
+
+
+// Collect some additional info about MySQL
+if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+{
+ // Calculate total db size/row count
+ $result = $db->query('SHOW TABLE STATUS LIKE \''.$db->prefix.'%\'') or error('Unable to fetch table status', __FILE__, __LINE__, $db->error());
+
+ $total_records = $total_size = 0;
+ while ($status = $db->fetch_assoc($result))
+ {
+ $total_records += $status['Rows'];
+ $total_size += $status['Data_length'] + $status['Index_length'];
+ }
+
+ $total_size = file_size($total_size);
+}
+
+
+// Check for the existence of various PHP opcode caches/optimizers
+if (function_exists('mmcache'))
+ $php_accelerator = '<a href="http://'.$lang_admin_index['Turck MMCache link'].'">'.$lang_admin_index['Turck MMCache'].'</a>';
+else if (isset($_PHPA))
+ $php_accelerator = '<a href="http://'.$lang_admin_index['ionCube PHP Accelerator link'].'">'.$lang_admin_index['ionCube PHP Accelerator'].'</a>';
+else if (ini_get('apc.enabled'))
+ $php_accelerator ='<a href="http://'.$lang_admin_index['Alternative PHP Cache (APC) link'].'">'.$lang_admin_index['Alternative PHP Cache (APC)'].'</a>';
+else if (ini_get('zend_optimizer.optimization_level'))
+ $php_accelerator = '<a href="http://'.$lang_admin_index['Zend Optimizer link'].'">'.$lang_admin_index['Zend Optimizer'].'</a>';
+else if (ini_get('eaccelerator.enable'))
+ $php_accelerator = '<a href="http://'.$lang_admin_index['eAccelerator link'].'">'.$lang_admin_index['eAccelerator'].'</a>';
+else if (ini_get('xcache.cacher'))
+ $php_accelerator = '<a href="http://'.$lang_admin_index['XCache link'].'">'.$lang_admin_index['XCache'].'</a>';
+else
+ $php_accelerator = $lang_admin_index['NA'];
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Server statistics']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('index');
+
+?>
+ <div class="block">
+ <h2><span><?php echo $lang_admin_index['Server statistics head'] ?></span></h2>
+ <div id="adstats" class="box">
+ <div class="inbox">
+ <dl>
+ <dt><?php echo $lang_admin_index['Server load label'] ?></dt>
+ <dd>
+ <?php printf($lang_admin_index['Server load data']."\n", $server_load, $num_online) ?>
+ </dd>
+<?php if ($pun_user['g_id'] == PUN_ADMIN): ?> <dt><?php echo $lang_admin_index['Environment label'] ?></dt>
+ <dd>
+ <?php printf($lang_admin_index['Environment data OS'], PHP_OS) ?><br />
+ <?php printf($lang_admin_index['Environment data version'], phpversion(), '<a href="admin_statistics.php?action=phpinfo">'.$lang_admin_index['Show info'].'</a>') ?><br />
+ <?php printf($lang_admin_index['Environment data acc']."\n", $php_accelerator) ?>
+ </dd>
+ <dt><?php echo $lang_admin_index['Database label'] ?></dt>
+ <dd>
+ <?php echo implode(' ', $db->get_version())."\n" ?>
+<?php if (isset($total_records) && isset($total_size)): ?> <br /><?php printf($lang_admin_index['Database data rows']."\n", forum_number_format($total_records)) ?>
+ <br /><?php printf($lang_admin_index['Database data size']."\n", $total_size) ?>
+<?php endif; ?> </dd>
+<?php endif; ?>
+ </dl>
+ </div>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_users.php b/admin_users.php
new file mode 100644
index 0000000..ac076d8
--- /dev/null
+++ b/admin_users.php
@@ -0,0 +1,1095 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the admin_users.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_users.php';
+
+// Show IP statistics for a certain user ID
+if (isset($_GET['ip_stats']))
+{
+ $ip_stats = intval($_GET['ip_stats']);
+ if ($ip_stats < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Fetch ip count
+ $result = $db->query('SELECT poster_ip, MAX(posted) AS last_used FROM '.$db->prefix.'posts WHERE poster_id='.$ip_stats.' GROUP BY poster_ip') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $num_ips = $db->num_rows($result);
+
+ // Determine the ip offset (based on $_GET['p'])
+ $num_pages = ceil($num_ips / 50);
+
+ $p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+ $start_from = 50 * ($p - 1);
+
+ // Generate paging links
+ $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?ip_stats='.$ip_stats );
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Results head']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<div id="users1" class="blocktable">
+ <h2><span><?php echo $lang_admin_users['Results head'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_users['Results IP address head'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_admin_users['Results last used head'] ?></th>
+ <th class="tc3" scope="col"><?php echo $lang_admin_users['Results times found head'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_admin_users['Results action head'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $result = $db->query('SELECT poster_ip, MAX(posted) AS last_used, COUNT(id) AS used_times FROM '.$db->prefix.'posts WHERE poster_id='.$ip_stats.' GROUP BY poster_ip ORDER BY last_used DESC LIMIT '.$start_from.', 50') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ while ($cur_ip = $db->fetch_assoc($result))
+ {
+
+?>
+ <tr>
+ <td class="tcl"><a href="moderate.php?get_host=<?php echo pun_htmlspecialchars($cur_ip['poster_ip']) ?>"><?php echo pun_htmlspecialchars($cur_ip['poster_ip']) ?></a></td>
+ <td class="tc2"><?php echo format_time($cur_ip['last_used']) ?></td>
+ <td class="tc3"><?php echo $cur_ip['used_times'] ?></td>
+ <td class="tcr"><a href="admin_users.php?show_users=<?php echo pun_htmlspecialchars($cur_ip['poster_ip']) ?>"><?php echo $lang_admin_users['Results find more link'] ?></a></td>
+ </tr>
+<?php
+
+ }
+ }
+ else
+ echo "\t\t\t\t".'<tr><td class="tcl" colspan="4">'.$lang_admin_users['Results no posts found'].'</td></tr>'."\n";
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+if (isset($_GET['show_users']))
+{
+ $ip = pun_trim($_GET['show_users']);
+
+ if (!@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $ip) && !@preg_match('%^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$%', $ip))
+ message($lang_admin_users['Bad IP message']);
+
+ // Fetch user count
+ $result = $db->query('SELECT DISTINCT poster_id, poster FROM '.$db->prefix.'posts WHERE poster_ip=\''.$db->escape($ip).'\'') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $num_users = $db->num_rows($result);
+
+ // Determine the user offset (based on $_GET['p'])
+ $num_pages = ceil($num_users / 50);
+
+ $p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+ $start_from = 50 * ($p - 1);
+
+ // Generate paging links
+ $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?show_users='.$ip);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Results head']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<div id="users2" class="blocktable">
+ <h2><span><?php echo $lang_admin_users['Results head'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_users['Results username head'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_admin_users['Results e-mail head'] ?></th>
+ <th class="tc3" scope="col"><?php echo $lang_admin_users['Results title head'] ?></th>
+ <th class="tc4" scope="col"><?php echo $lang_admin_users['Results posts head'] ?></th>
+ <th class="tc5" scope="col"><?php echo $lang_admin_users['Results admin note head'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_admin_users['Results actions head'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $result = $db->query('SELECT DISTINCT poster_id, poster FROM '.$db->prefix.'posts WHERE poster_ip=\''.$db->escape($ip).'\' ORDER BY poster ASC LIMIT '.$start_from.', 50') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $num_posts = $db->num_rows($result);
+
+ if ($num_posts)
+ {
+ $posters = $poster_ids = array();
+ while ($cur_poster = $db->fetch_assoc($result))
+ {
+ $posters[] = $cur_poster;
+ $poster_ids[] = $cur_poster['poster_id'];
+ }
+
+ $result = $db->query('SELECT u.id, u.username, u.email, u.title, u.num_posts, u.admin_note, g.g_id, g.g_user_title FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id>1 AND u.id IN('.implode(',', $poster_ids).')') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+ $user_data = array();
+ while ($cur_user = $db->fetch_assoc($result))
+ $user_data[$cur_user['id']] = $cur_user;
+
+ // Loop through users and print out some info
+ foreach ($posters as $cur_poster)
+ {
+ if (isset($user_data[$cur_poster['poster_id']]))
+ {
+ $user_title = get_title($user_data[$cur_poster['poster_id']]);
+
+ $actions = '<a href="admin_users.php?ip_stats='.$user_data[$cur_poster['poster_id']]['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user_posts&amp;user_id='.$user_data[$cur_poster['poster_id']]['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
+
+?>
+ <tr>
+ <td class="tcl"><?php echo '<a href="profile.php?id='.$user_data[$cur_poster['poster_id']]['id'].'">'.pun_htmlspecialchars($user_data[$cur_poster['poster_id']]['username']).'</a>' ?></td>
+ <td class="tc2"><a href="mailto:<?php echo pun_htmlspecialchars($user_data[$cur_poster['poster_id']]['email']) ?>"><?php echo pun_htmlspecialchars($user_data[$cur_poster['poster_id']]['email']) ?></a></td>
+ <td class="tc3"><?php echo $user_title ?></td>
+ <td class="tc4"><?php echo forum_number_format($user_data[$cur_poster['poster_id']]['num_posts']) ?></td>
+ <td class="tc5"><?php echo ($user_data[$cur_poster['poster_id']]['admin_note'] != '') ? pun_htmlspecialchars($user_data[$cur_poster['poster_id']]['admin_note']) : '&#160;' ?></td>
+ <td class="tcr"><?php echo $actions ?></td>
+ </tr>
+<?php
+
+ }
+ else
+ {
+
+?>
+ <tr>
+ <td class="tcl"><?php echo pun_htmlspecialchars($cur_poster['poster']) ?></td>
+ <td class="tc2">&#160;</td>
+ <td class="tc3"><?php echo $lang_admin_users['Results guest'] ?></td>
+ <td class="tc4">&#160;</td>
+ <td class="tc5">&#160;</td>
+ <td class="tcr">&#160;</td>
+ </tr>
+<?php
+
+ }
+ }
+ }
+ else
+ echo "\t\t\t\t".'<tr><td class="tcl" colspan="6">'.$lang_admin_users['Results no IP found'].'</td></tr>'."\n";
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+</div>
+<?php
+ require PUN_ROOT.'footer.php';
+}
+
+
+// Move multiple users to other user groups
+else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
+{
+ if ($pun_user['g_id'] > PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('admin_users.php');
+
+ if (isset($_POST['users']))
+ {
+ $user_ids = is_array($_POST['users']) ? array_keys($_POST['users']) : explode(',', $_POST['users']);
+ $user_ids = array_map('intval', $user_ids);
+
+ // Delete invalid IDs
+ $user_ids = array_diff($user_ids, array(0, 1));
+ }
+ else
+ $user_ids = array();
+
+ if (empty($user_ids))
+ message($lang_admin_users['No users selected']);
+
+ // Are we trying to batch move any admins?
+ $result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).') AND group_id='.PUN_ADMIN) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->result($result) > 0)
+ message($lang_admin_users['No move admins message']);
+
+ // Fetch all user groups
+ $all_groups = array();
+ $result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id NOT IN ('.PUN_GUEST.','.PUN_ADMIN.') ORDER BY g_title ASC') or error('Unable to fetch groups', __FILE__, __LINE__, $db->error());
+ while ($row = $db->fetch_row($result))
+ $all_groups[$row[0]] = $row[1];
+
+ if (isset($_POST['move_users_comply']))
+ {
+ $new_group = isset($_POST['new_group']) && isset($all_groups[$_POST['new_group']]) ? $_POST['new_group'] : message($lang_admin_users['Invalid group message']);
+
+ // Is the new group a moderator group?
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$new_group) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
+ $new_group_mod = $db->result($result);
+
+ // Fetch user groups
+ $user_groups = array();
+ $result = $db->query('SELECT id, group_id FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to fetch user groups', __FILE__, __LINE__, $db->error());
+ while ($cur_user = $db->fetch_assoc($result))
+ {
+ if (!isset($user_groups[$cur_user['group_id']]))
+ $user_groups[$cur_user['group_id']] = array();
+
+ $user_groups[$cur_user['group_id']][] = $cur_user['id'];
+ }
+
+ // Are any users moderators?
+ $group_ids = array_keys($user_groups);
+ $result = $db->query('SELECT g_id, g_moderator FROM '.$db->prefix.'groups WHERE g_id IN ('.implode(',', $group_ids).')') or error('Unable to fetch group moderators', __FILE__, __LINE__, $db->error());
+ while ($cur_group = $db->fetch_assoc($result))
+ {
+ if ($cur_group['g_moderator'] == '0')
+ unset($user_groups[$cur_group['g_id']]);
+ }
+
+ if (!empty($user_groups) && $new_group != PUN_ADMIN && $new_group_mod != '1')
+ {
+ // Fetch forum list and clean up their moderator list
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ foreach ($user_groups as $group_users)
+ $cur_moderators = array_diff($cur_moderators, $group_users);
+
+ $cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ // Change user group
+ $db->query('UPDATE '.$db->prefix.'users SET group_id='.$new_group.' WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to change user group', __FILE__, __LINE__, $db->error());
+
+ redirect('admin_users.php', $lang_admin_users['Users move redirect']);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Move users']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('users');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_users['Move users'] ?></span></h2>
+ <div class="box">
+ <form name="confirm_move_users" method="post" action="admin_users.php">
+ <input type="hidden" name="users" value="<?php echo implode(',', $user_ids) ?>" />
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_users['Move users subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['New group label'] ?></th>
+ <td>
+ <select name="new_group" tabindex="1">
+<?php foreach ($all_groups as $gid => $group) : ?> <option value="<?php echo $gid ?>"><?php echo pun_htmlspecialchars($group) ?></option>
+<?php endforeach; ?>
+ </select>
+ <span><?php echo $lang_admin_users['New group help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="move_users_comply" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="2" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+// Delete multiple users
+else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
+{
+ if ($pun_user['g_id'] > PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('admin_users.php');
+
+ if (isset($_POST['users']))
+ {
+ $user_ids = is_array($_POST['users']) ? array_keys($_POST['users']) : explode(',', $_POST['users']);
+ $user_ids = array_map('intval', $user_ids);
+
+ // Delete invalid IDs
+ $user_ids = array_diff($user_ids, array(0, 1));
+ }
+ else
+ $user_ids = array();
+
+ if (empty($user_ids))
+ message($lang_admin_users['No users selected']);
+
+ // Are we trying to delete any admins?
+ $result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).') AND group_id='.PUN_ADMIN) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->result($result) > 0)
+ message($lang_admin_users['No delete admins message']);
+
+ if (isset($_POST['delete_users_comply']))
+ {
+ // Fetch user groups
+ $user_groups = array();
+ $result = $db->query('SELECT id, group_id FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to fetch user groups', __FILE__, __LINE__, $db->error());
+ while ($cur_user = $db->fetch_assoc($result))
+ {
+ if (!isset($user_groups[$cur_user['group_id']]))
+ $user_groups[$cur_user['group_id']] = array();
+
+ $user_groups[$cur_user['group_id']][] = $cur_user['id'];
+ }
+
+ // Are any users moderators?
+ $group_ids = array_keys($user_groups);
+ $result = $db->query('SELECT g_id, g_moderator FROM '.$db->prefix.'groups WHERE g_id IN ('.implode(',', $group_ids).')') or error('Unable to fetch group moderators', __FILE__, __LINE__, $db->error());
+ while ($cur_group = $db->fetch_assoc($result))
+ {
+ if ($cur_group['g_moderator'] == '0')
+ unset($user_groups[$cur_group['g_id']]);
+ }
+
+ // Fetch forum list and clean up their moderator list
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ foreach ($user_groups as $group_users)
+ $cur_moderators = array_diff($cur_moderators, $group_users);
+
+ $cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+
+ // Delete any subscriptions
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id IN ('.implode(',', $user_ids).')') or error('Unable to delete topic subscriptions', __FILE__, __LINE__, $db->error());
+ $db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE user_id IN ('.implode(',', $user_ids).')') or error('Unable to delete forum subscriptions', __FILE__, __LINE__, $db->error());
+
+ // Remove them from the online list (if they happen to be logged in)
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE user_id IN ('.implode(',', $user_ids).')') or error('Unable to remove users from online list', __FILE__, __LINE__, $db->error());
+
+ // Should we delete all posts made by these users?
+ if (isset($_POST['delete_posts']))
+ {
+ require PUN_ROOT.'include/search_idx.php';
+ @set_time_limit(0);
+
+ // Find all posts made by this user
+ $result = $db->query('SELECT p.id, p.topic_id, t.forum_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.poster_id IN ('.implode(',', $user_ids).')') or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ while ($cur_post = $db->fetch_assoc($result))
+ {
+ // Determine whether this post is the "topic post" or not
+ $result2 = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$cur_post['topic_id'].' ORDER BY posted LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+
+ if ($db->result($result2) == $cur_post['id'])
+ delete_topic($cur_post['topic_id']);
+ else
+ delete_post($cur_post['id'], $cur_post['topic_id']);
+
+ update_forum($cur_post['forum_id']);
+ }
+ }
+ }
+ else
+ // Set all their posts to guest
+ $db->query('UPDATE '.$db->prefix.'posts SET poster_id=1 WHERE poster_id IN ('.implode(',', $user_ids).')') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+
+ // Delete the users
+ $db->query('DELETE FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to delete users', __FILE__, __LINE__, $db->error());
+
+ // Delete user avatars
+ foreach ($user_ids as $user_id)
+ delete_avatar($user_id);
+
+ // Regenerate the users info cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+
+ redirect('admin_users.php', $lang_admin_users['Users delete redirect']);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Delete users']);
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('users');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_users['Delete users'] ?></span></h2>
+ <div class="box">
+ <form name="confirm_del_users" method="post" action="admin_users.php">
+ <input type="hidden" name="users" value="<?php echo implode(',', $user_ids) ?>" />
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_users['Confirm delete legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_users['Confirm delete info'] ?></p>
+ <div class="rbox">
+ <label><input type="checkbox" name="delete_posts" value="1" checked="checked" /><?php echo $lang_admin_users['Delete posts'] ?><br /></label>
+ </div>
+ <p class="warntext"><strong><?php echo $lang_admin_users['Delete warning'] ?></strong></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="delete_users_comply" value="<?php echo $lang_admin_users['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+// Ban multiple users
+else if (isset($_POST['ban_users']) || isset($_POST['ban_users_comply']))
+{
+ if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('admin_users.php');
+
+ if (isset($_POST['users']))
+ {
+ $user_ids = is_array($_POST['users']) ? array_keys($_POST['users']) : explode(',', $_POST['users']);
+ $user_ids = array_map('intval', $user_ids);
+
+ // Delete invalid IDs
+ $user_ids = array_diff($user_ids, array(0, 1));
+ }
+ else
+ $user_ids = array();
+
+ if (empty($user_ids))
+ message($lang_admin_users['No users selected']);
+
+ // Are we trying to ban any admins?
+ $result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).') AND group_id='.PUN_ADMIN) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
+ if ($db->result($result) > 0)
+ message($lang_admin_users['No ban admins message']);
+
+ // Also, we cannot ban moderators
+ $result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id WHERE g.g_moderator=1 AND u.id IN ('.implode(',', $user_ids).')') or error('Unable to fetch moderator group info', __FILE__, __LINE__, $db->error());
+ if ($db->result($result) > 0)
+ message($lang_admin_users['No ban mods message']);
+
+ if (isset($_POST['ban_users_comply']))
+ {
+ $ban_message = pun_trim($_POST['ban_message']);
+ $ban_expire = pun_trim($_POST['ban_expire']);
+ $ban_the_ip = isset($_POST['ban_the_ip']) ? intval($_POST['ban_the_ip']) : 0;
+
+ if ($ban_expire != '' && $ban_expire != 'Never')
+ {
+ $ban_expire = strtotime($ban_expire.' GMT');
+
+ if ($ban_expire == -1 || !$ban_expire)
+ message($lang_admin_users['Invalid date message'].' '.$lang_admin_users['Invalid date reasons']);
+
+ $diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+ $ban_expire -= $diff;
+
+ if ($ban_expire <= time())
+ message($lang_admin_users['Invalid date message'].' '.$lang_admin_users['Invalid date reasons']);
+ }
+ else
+ $ban_expire = 'NULL';
+
+ $ban_message = ($ban_message != '') ? '\''.$db->escape($ban_message).'\'' : 'NULL';
+
+ // Fetch user information
+ $user_info = array();
+ $result = $db->query('SELECT id, username, email, registration_ip FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ while ($cur_user = $db->fetch_assoc($result))
+ $user_info[$cur_user['id']] = array('username' => $cur_user['username'], 'email' => $cur_user['email'], 'ip' => $cur_user['registration_ip']);
+
+ // Overwrite the registration IP with one from the last post (if it exists)
+ if ($ban_the_ip != 0)
+ {
+ $result = $db->query('SELECT p.poster_id, p.poster_ip FROM '.$db->prefix.'posts AS p INNER JOIN (SELECT MAX(id) AS id FROM '.$db->prefix.'posts WHERE poster_id IN ('.implode(',', $user_ids).') GROUP BY poster_id) AS i ON p.id=i.id') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ while ($cur_address = $db->fetch_assoc($result))
+ $user_info[$cur_address['poster_id']]['ip'] = $cur_address['poster_ip'];
+ }
+
+ // And insert the bans!
+ foreach ($user_ids as $user_id)
+ {
+ $ban_username = '\''.$db->escape($user_info[$user_id]['username']).'\'';
+ $ban_email = '\''.$db->escape($user_info[$user_id]['email']).'\'';
+ $ban_ip = ($ban_the_ip != 0) ? '\''.$db->escape($user_info[$user_id]['ip']).'\'' : 'NULL';
+
+ $db->query('INSERT INTO '.$db->prefix.'bans (username, ip, email, message, expire, ban_creator) VALUES('.$ban_username.', '.$ban_ip.', '.$ban_email.', '.$ban_message.', '.$ban_expire.', '.$pun_user['id'].')') or error('Unable to add ban', __FILE__, __LINE__, $db->error());
+ }
+
+ // Regenerate the bans cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_bans_cache();
+
+ redirect('admin_users.php', $lang_admin_users['Users banned redirect']);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans']);
+ $focus_element = array('bans2', 'ban_message');
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('users');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_users['Ban users'] ?></span></h2>
+ <div class="box">
+ <form id="bans2" name="confirm_ban_users" method="post" action="admin_users.php">
+ <input type="hidden" name="users" value="<?php echo implode(',', $user_ids) ?>" />
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_users['Message expiry subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Ban message label'] ?></th>
+ <td>
+ <input type="text" name="ban_message" size="50" maxlength="255" tabindex="1" />
+ <span><?php echo $lang_admin_users['Ban message help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Expire date label'] ?></th>
+ <td>
+ <input type="text" name="ban_expire" size="17" maxlength="10" tabindex="2" />
+ <span><?php echo $lang_admin_users['Expire date help'] ?></span>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Ban IP label'] ?></th>
+ <td>
+ <label class="conl"><input type="radio" name="ban_the_ip" tabindex="3" value="1" checked="checked" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong></label>
+ <label class="conl"><input type="radio" name="ban_the_ip" tabindex="4" value="0" checked="checked" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong></label>
+ <span class="clearb"><?php echo $lang_admin_users['Ban IP help'] ?></span>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="ban_users_comply" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="3" /></p>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_GET['find_user']))
+{
+ $form = isset($_GET['form']) ? $_GET['form'] : array();
+
+ // trim() all elements in $form
+ $form = array_map('pun_trim', $form);
+ $conditions = $query_str = array();
+
+ $posts_greater = isset($_GET['posts_greater']) ? pun_trim($_GET['posts_greater']) : '';
+ $posts_less = isset($_GET['posts_less']) ? pun_trim($_GET['posts_less']) : '';
+ $last_post_after = isset($_GET['last_post_after']) ? pun_trim($_GET['last_post_after']) : '';
+ $last_post_before = isset($_GET['last_post_before']) ? pun_trim($_GET['last_post_before']) : '';
+ $last_visit_after = isset($_GET['last_visit_after']) ? pun_trim($_GET['last_visit_after']) : '';
+ $last_visit_before = isset($_GET['last_visit_before']) ? pun_trim($_GET['last_visit_before']) : '';
+ $registered_after = isset($_GET['registered_after']) ? pun_trim($_GET['registered_after']) : '';
+ $registered_before = isset($_GET['registered_before']) ? pun_trim($_GET['registered_before']) : '';
+ $order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'email', 'num_posts', 'last_post', 'last_visit', 'registered')) ? $_GET['order_by'] : 'username';
+ $direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
+ $user_group = isset($_GET['user_group']) ? intval($_GET['user_group']) : -1;
+
+ $query_str[] = 'order_by='.$order_by;
+ $query_str[] = 'direction='.$direction;
+ $query_str[] = 'user_group='.$user_group;
+
+ if (preg_match('%[^0-9]%', $posts_greater.$posts_less))
+ message($lang_admin_users['Non numeric message']);
+
+ // Try to convert date/time to timestamps
+ if ($last_post_after != '')
+ {
+ $query_str[] = 'last_post_after='.$last_post_after;
+
+ $last_post_after = strtotime($last_post_after);
+ if ($last_post_after === false || $last_post_after == -1)
+ message($lang_admin_users['Invalid date time message']);
+
+ $conditions[] = 'u.last_post>'.$last_post_after;
+ }
+ if ($last_post_before != '')
+ {
+ $query_str[] = 'last_post_before='.$last_post_before;
+
+ $last_post_before = strtotime($last_post_before);
+ if ($last_post_before === false || $last_post_before == -1)
+ message($lang_admin_users['Invalid date time message']);
+
+ $conditions[] = 'u.last_post<'.$last_post_before;
+ }
+ if ($last_visit_after != '')
+ {
+ $query_str[] = 'last_visit_after='.$last_visit_after;
+
+ $last_visit_after = strtotime($last_visit_after);
+ if ($last_visit_after === false || $last_visit_after == -1)
+ message($lang_admin_users['Invalid date time message']);
+
+ $conditions[] = 'u.last_visit>'.$last_visit_after;
+ }
+ if ($last_visit_before != '')
+ {
+ $query_str[] = 'last_visit_before='.$last_visit_before;
+
+ $last_visit_before = strtotime($last_visit_before);
+ if ($last_visit_before === false || $last_visit_before == -1)
+ message($lang_admin_users['Invalid date time message']);
+
+ $conditions[] = 'u.last_visit<'.$last_visit_before;
+ }
+ if ($registered_after != '')
+ {
+ $query_str[] = 'registered_after='.$registered_after;
+
+ $registered_after = strtotime($registered_after);
+ if ($registered_after === false || $registered_after == -1)
+ message($lang_admin_users['Invalid date time message']);
+
+ $conditions[] = 'u.registered>'.$registered_after;
+ }
+ if ($registered_before != '')
+ {
+ $query_str[] = 'registered_before='.$registered_before;
+
+ $registered_before = strtotime($registered_before);
+ if ($registered_before === false || $registered_before == -1)
+ message($lang_admin_users['Invalid date time message']);
+
+ $conditions[] = 'u.registered<'.$registered_before;
+ }
+
+ $like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
+ foreach ($form as $key => $input)
+ {
+ if ($input != '' && in_array($key, array('username', 'email', 'title', 'realname', 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location', 'signature', 'admin_note')))
+ {
+ $conditions[] = 'u.'.$db->escape($key).' '.$like_command.' \''.$db->escape(str_replace(array('*', '_'), array('%', '\\_'), $input)).'\'';
+ $query_str[] = 'form%5B'.$key.'%5D='.urlencode($input);
+ }
+ }
+
+ if ($posts_greater != '')
+ {
+ $query_str[] = 'posts_greater='.$posts_greater;
+ $conditions[] = 'u.num_posts>'.$posts_greater;
+ }
+ if ($posts_less != '')
+ {
+ $query_str[] = 'posts_less='.$posts_less;
+ $conditions[] = 'u.num_posts<'.$posts_less;
+ }
+
+ if ($user_group > -1)
+ $conditions[] = 'u.group_id='.$user_group;
+
+ // Fetch user count
+ $result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id>1'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '')) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ $num_users = $db->result($result);
+
+ // Determine the user offset (based on $_GET['p'])
+ $num_pages = ceil($num_users / 50);
+
+ $p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+ $start_from = 50 * ($p - 1);
+
+ // Generate paging links
+ $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?find_user=&amp;'.implode('&amp;', $query_str));
+
+ // Some helper variables for permissions
+ $can_delete = $can_move = $pun_user['g_id'] == PUN_ADMIN;
+ $can_ban = $pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '1');
+ $can_action = ($can_delete || $can_ban || $can_move) && $num_users > 0;
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Results head']);
+ $page_head = array('js' => '<script type="text/javascript" src="common.js"></script>');
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+
+<form id="search-users-form" action="admin_users.php" method="post">
+<div id="users2" class="blocktable">
+ <h2><span><?php echo $lang_admin_users['Results head'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_admin_users['Results username head'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_admin_users['Results e-mail head'] ?></th>
+ <th class="tc3" scope="col"><?php echo $lang_admin_users['Results title head'] ?></th>
+ <th class="tc4" scope="col"><?php echo $lang_admin_users['Results posts head'] ?></th>
+ <th class="tc5" scope="col"><?php echo $lang_admin_users['Results admin note head'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_admin_users['Results actions head'] ?></th>
+<?php if ($can_action): ?> <th class="tcmod" scope="col"><?php echo $lang_admin_users['Select'] ?></th>
+<?php endif; ?>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $result = $db->query('SELECT u.id, u.username, u.email, u.title, u.num_posts, u.admin_note, g.g_id, g.g_user_title FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id>1'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '').' ORDER BY '.$db->escape($order_by).' '.$db->escape($direction).' LIMIT '.$start_from.', 50') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ while ($user_data = $db->fetch_assoc($result))
+ {
+ $user_title = get_title($user_data);
+
+ // This script is a special case in that we want to display "Not verified" for non-verified users
+ if (($user_data['g_id'] == '' || $user_data['g_id'] == PUN_UNVERIFIED) && $user_title != $lang_common['Banned'])
+ $user_title = '<span class="warntext">'.$lang_admin_users['Not verified'].'</span>';
+
+ $actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user_posts&amp;user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
+
+?>
+ <tr>
+ <td class="tcl"><?php echo '<a href="profile.php?id='.$user_data['id'].'">'.pun_htmlspecialchars($user_data['username']).'</a>' ?></td>
+ <td class="tc2"><a href="mailto:<?php echo pun_htmlspecialchars($user_data['email']) ?>"><?php echo pun_htmlspecialchars($user_data['email']) ?></a></td>
+ <td class="tc3"><?php echo $user_title ?></td>
+ <td class="tc4"><?php echo forum_number_format($user_data['num_posts']) ?></td>
+ <td class="tc5"><?php echo ($user_data['admin_note'] != '') ? pun_htmlspecialchars($user_data['admin_note']) : '&#160;' ?></td>
+ <td class="tcr"><?php echo $actions ?></td>
+<?php if ($can_action): ?> <td class="tcmod"><input type="checkbox" name="users[<?php echo $user_data['id'] ?>]" value="1" /></td>
+<?php endif; ?>
+ </tr>
+<?php
+
+ }
+ }
+ else
+ echo "\t\t\t\t".'<tr><td class="tcl" colspan="6">'.$lang_admin_users['No match'].'</td></tr>'."\n";
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+<?php if ($can_action): ?> <p class="conr modbuttons"><a href="#" onclick="return select_checkboxes('search-users-form', this, '<?php echo $lang_admin_users['Unselect all'] ?>')"><?php echo $lang_admin_users['Select all'] ?></a> <?php if ($can_ban) : ?><input type="submit" name="ban_users" value="<?php echo $lang_admin_users['Ban'] ?>" /><?php endif; if ($can_delete) : ?><input type="submit" name="delete_users" value="<?php echo $lang_admin_users['Delete'] ?>" /><?php endif; if ($can_move) : ?><input type="submit" name="move_users" value="<?php echo $lang_admin_users['Change group'] ?>" /><?php endif; ?></p>
+<?php endif; ?>
+ </div>
+ <ul class="crumbs">
+ <li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+</div>
+</form>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else
+{
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users']);
+ $focus_element = array('find_user', 'form[username]');
+ define('PUN_ACTIVE_PAGE', 'admin');
+ require PUN_ROOT.'header.php';
+
+ generate_admin_menu('users');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo $lang_admin_users['User search head'] ?></span></h2>
+ <div class="box">
+ <form id="find_user" method="get" action="admin_users.php">
+ <p class="submittop"><input type="submit" name="find_user" value="<?php echo $lang_admin_users['Submit search'] ?>" tabindex="1" /></p>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_users['User search subhead'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_admin_users['User search info'] ?></p>
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Username label'] ?></th>
+ <td><input type="text" name="form[username]" size="25" maxlength="25" tabindex="2" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['E-mail address label'] ?></th>
+ <td><input type="text" name="form[email]" size="30" maxlength="80" tabindex="3" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Title label'] ?></th>
+ <td><input type="text" name="form[title]" size="30" maxlength="50" tabindex="4" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Real name label'] ?></th>
+ <td><input type="text" name="form[realname]" size="30" maxlength="40" tabindex="5" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Website label'] ?></th>
+ <td><input type="text" name="form[url]" size="35" maxlength="100" tabindex="6" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Jabber label'] ?></th>
+ <td><input type="text" name="form[jabber]" size="30" maxlength="75" tabindex="7" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['ICQ label'] ?></th>
+ <td><input type="text" name="form[icq]" size="12" maxlength="12" tabindex="8" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['MSN label'] ?></th>
+ <td><input type="text" name="form[msn]" size="30" maxlength="50" tabindex="9" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['AOL label'] ?></th>
+ <td><input type="text" name="form[aim]" size="20" maxlength="20" tabindex="10" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Yahoo label'] ?></th>
+ <td><input type="text" name="form[yahoo]" size="20" maxlength="20" tabindex="11" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Location label'] ?></th>
+ <td><input type="text" name="form[location]" size="30" maxlength="30" tabindex="12" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Signature label'] ?></th>
+ <td><input type="text" name="form[signature]" size="35" maxlength="512" tabindex="13" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Admin note label'] ?></th>
+ <td><input type="text" name="form[admin_note]" size="30" maxlength="30" tabindex="14" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Posts more than label'] ?></th>
+ <td><input type="text" name="posts_greater" size="5" maxlength="8" tabindex="15" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Posts less than label'] ?></th>
+ <td><input type="text" name="posts_less" size="5" maxlength="8" tabindex="16" /></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Last post after label'] ?></th>
+ <td><input type="text" name="last_post_after" size="24" maxlength="19" tabindex="17" />
+ <span><?php echo $lang_admin_users['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Last post before label'] ?></th>
+ <td><input type="text" name="last_post_before" size="24" maxlength="19" tabindex="18" />
+ <span><?php echo $lang_admin_users['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Last visit after label'] ?></th>
+ <td><input type="text" name="last_visit_after" size="24" maxlength="19" tabindex="17" />
+ <span><?php echo $lang_admin_users['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Last visit before label'] ?></th>
+ <td><input type="text" name="last_visit_before" size="24" maxlength="19" tabindex="18" />
+ <span><?php echo $lang_admin_users['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Registered after label'] ?></th>
+ <td><input type="text" name="registered_after" size="24" maxlength="19" tabindex="19" />
+ <span><?php echo $lang_admin_users['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Registered before label'] ?></th>
+ <td><input type="text" name="registered_before" size="24" maxlength="19" tabindex="20" />
+ <span><?php echo $lang_admin_users['Date help'] ?></span></td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['Order by label'] ?></th>
+ <td>
+ <select name="order_by" tabindex="21">
+ <option value="username" selected="selected"><?php echo $lang_admin_users['Order by username'] ?></option>
+ <option value="email"><?php echo $lang_admin_users['Order by e-mail'] ?></option>
+ <option value="num_posts"><?php echo $lang_admin_users['Order by posts'] ?></option>
+ <option value="last_post"><?php echo $lang_admin_users['Order by last post'] ?></option>
+ <option value="last_visit"><?php echo $lang_admin_users['Order by last visit'] ?></option>
+ <option value="registered"><?php echo $lang_admin_users['Order by registered'] ?></option>
+ </select>&#160;&#160;&#160;<select name="direction" tabindex="22">
+ <option value="ASC" selected="selected"><?php echo $lang_admin_users['Ascending'] ?></option>
+ <option value="DESC"><?php echo $lang_admin_users['Descending'] ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['User group label'] ?></th>
+ <td>
+ <select name="user_group" tabindex="23">
+ <option value="-1" selected="selected"><?php echo $lang_admin_users['All groups'] ?></option>
+ <option value="0"><?php echo $lang_admin_users['Unverified users'] ?></option>
+<?php
+
+ $result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_group = $db->fetch_assoc($result))
+ echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+
+?>
+ </select>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="find_user" value="<?php echo $lang_admin_users['Submit search'] ?>" tabindex="25" /></p>
+ </form>
+ </div>
+
+ <h2 class="block2"><span><?php echo $lang_admin_users['IP search head'] ?></span></h2>
+ <div class="box">
+ <form method="get" action="admin_users.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_admin_users['IP search subhead'] ?></legend>
+ <div class="infldset">
+ <table class="aligntop">
+ <tr>
+ <th scope="row"><?php echo $lang_admin_users['IP address label'] ?><div><input type="submit" value="<?php echo $lang_admin_users['Find IP address'] ?>" tabindex="26" /></div></th>
+ <td><input type="text" name="show_users" size="18" maxlength="15" tabindex="24" />
+ <span><?php echo $lang_admin_users['IP address help'] ?></span></td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+ </div>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
diff --git a/cache/.htaccess b/cache/.htaccess
new file mode 100644
index 0000000..e67301e
--- /dev/null
+++ b/cache/.htaccess
@@ -0,0 +1,4 @@
+<Limit GET POST PUT>
+Order Allow,Deny
+Deny from All
+</Limit>
diff --git a/cache/index.html b/cache/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/cache/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/common.js b/common.js
new file mode 100644
index 0000000..47a7155
--- /dev/null
+++ b/common.js
@@ -0,0 +1,38 @@
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+function select_checkboxes(curFormId, link, new_string)
+{
+ var curForm = document.getElementById(curFormId);
+ var inputlist = curForm.getElementsByTagName("input");
+ for (i = 0; i < inputlist.length; i++)
+ {
+ if (inputlist[i].getAttribute("type") == 'checkbox' && inputlist[i].disabled == false)
+ inputlist[i].checked = true;
+ }
+
+ link.setAttribute('onclick', 'return unselect_checkboxes(\'' + curFormId + '\', this, \'' + link.innerHTML + '\')');
+ link.innerHTML = new_string;
+
+ return false;
+}
+
+function unselect_checkboxes(curFormId, link, new_string)
+{
+ var curForm = document.getElementById(curFormId);
+ var inputlist = curForm.getElementsByTagName("input");
+ for (i = 0; i < inputlist.length; i++)
+ {
+ if (inputlist[i].getAttribute("type") == 'checkbox' && inputlist[i].disabled == false)
+ inputlist[i].checked = false;
+ }
+
+ link.setAttribute('onclick', 'return select_checkboxes(\'' + curFormId + '\', this, \'' + link.innerHTML + '\')');
+ link.innerHTML = new_string;
+
+ return false;
+}
diff --git a/config.php b/config.php
new file mode 100644
index 0000000..c09b89d
--- /dev/null
+++ b/config.php
@@ -0,0 +1,17 @@
+<?php
+
+$db_type = 'mysqli';
+$db_host = 'localhost';
+$db_name = 'fluxbb';
+$db_username = 'fluxbb';
+$db_password = 'dzI3ODdkOHNnYSB4';
+$db_prefix = 'fluxbb_';
+$p_connect = false;
+
+$cookie_name = 'pun_cookie_143ac0';
+$cookie_domain = '';
+$cookie_path = '/';
+$cookie_secure = 0;
+$cookie_seed = '52856129188e6704';
+
+define('PUN', 1);
diff --git a/db_update.php b/db_update.php
new file mode 100644
index 0000000..2964bf3
--- /dev/null
+++ b/db_update.php
@@ -0,0 +1,1911 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The FluxBB version this script updates to
+define('UPDATE_TO', '1.5.11');
+
+define('UPDATE_TO_DB_REVISION', 21);
+define('UPDATE_TO_SI_REVISION', 2);
+define('UPDATE_TO_PARSER_REVISION', 2);
+
+define('MIN_PHP_VERSION', '4.4.0');
+define('MIN_MYSQL_VERSION', '4.1.2');
+define('MIN_PGSQL_VERSION', '7.0.0');
+define('PUN_SEARCH_MIN_WORD', 3);
+define('PUN_SEARCH_MAX_WORD', 20);
+
+// The MySQL connection character set that was used for FluxBB 1.2 - in 99% of cases this should be detected automatically,
+// but can be overridden using the below constant if required.
+//define('FORUM_DEFAULT_CHARSET', 'latin1');
+
+
+// The number of items to process per page view (lower this if the update script times out during UTF-8 conversion)
+define('PER_PAGE', 300);
+
+// Don't set to UTF-8 until after we've found out what the default character set is
+define('FORUM_NO_SET_NAMES', 1);
+
+// Make sure we are running at least MIN_PHP_VERSION
+if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
+ exit('You are running PHP version '.PHP_VERSION.'. FluxBB '.UPDATE_TO.' requires at least PHP '.MIN_PHP_VERSION.' to run properly. You must upgrade your PHP installation before you can continue.');
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+
+// Attempt to load the configuration file config.php
+if (file_exists(PUN_ROOT.'config.php'))
+ include PUN_ROOT.'config.php';
+
+// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
+if (defined('FORUM'))
+ define('PUN', FORUM);
+
+// If PUN isn't defined, config.php is missing or corrupt
+if (!defined('PUN'))
+{
+ header('Location: install.php');
+ exit;
+}
+
+// Enable debug mode
+if (!defined('PUN_DEBUG'))
+ define('PUN_DEBUG', 1);
+
+// Load the functions script
+require PUN_ROOT.'include/functions.php';
+
+// Load UTF-8 functions
+require PUN_ROOT.'include/utf8/utf8.php';
+
+// Strip out "bad" UTF-8 characters
+forum_remove_bad_characters();
+
+// Reverse the effect of register_globals
+forum_unregister_globals();
+
+// Turn on full PHP error reporting
+error_reporting(E_ALL);
+
+// Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
+setlocale(LC_CTYPE, 'C');
+
+// Turn off magic_quotes_runtime
+if (get_magic_quotes_runtime())
+ set_magic_quotes_runtime(0);
+
+// Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
+if (get_magic_quotes_gpc())
+{
+ function stripslashes_array($array)
+ {
+ return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
+ }
+
+ $_GET = stripslashes_array($_GET);
+ $_POST = stripslashes_array($_POST);
+ $_COOKIE = stripslashes_array($_COOKIE);
+ $_REQUEST = stripslashes_array($_REQUEST);
+}
+
+// If a cookie name is not specified in config.php, we use the default (forum_cookie)
+if (empty($cookie_name))
+ $cookie_name = 'pun_cookie';
+
+// If the cache directory is not specified, we use the default setting
+if (!defined('FORUM_CACHE_DIR'))
+ define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
+
+// Turn off PHP time limit
+@set_time_limit(0);
+
+// Define a few commonly used constants
+define('PUN_UNVERIFIED', 0);
+define('PUN_ADMIN', 1);
+define('PUN_MOD', 2);
+define('PUN_GUEST', 3);
+define('PUN_MEMBER', 4);
+
+// Load DB abstraction layer and try to connect
+require PUN_ROOT.'include/dblayer/common_db.php';
+
+$db->start_transaction();
+
+// Check what the default character set is - since 1.2 didn't specify any we will use whatever the default was (usually latin1)
+$old_connection_charset = defined('FORUM_DEFAULT_CHARSET') ? FORUM_DEFAULT_CHARSET : $db->get_names();
+
+// Set the connection to UTF-8 now
+$db->set_names('utf8');
+
+// Get the forum config
+$result = $db->query('SELECT * FROM '.$db->prefix.'config') or error('Unable to fetch config.', __FILE__, __LINE__, $db->error());
+while ($cur_config_item = $db->fetch_row($result))
+ $pun_config[$cur_config_item[0]] = $cur_config_item[1];
+
+// Load language file
+$default_lang = $pun_config['o_default_lang'];
+
+if (!file_exists(PUN_ROOT.'lang/'.$default_lang.'/update.php'))
+ $default_lang = 'English';
+
+require PUN_ROOT.'lang/'.$default_lang.'/common.php';
+require PUN_ROOT.'lang/'.$default_lang.'/update.php';
+
+// Check current version
+$cur_version = $pun_config['o_cur_version'];
+
+if (version_compare($cur_version, '1.2', '<'))
+ error(sprintf($lang_update['Version mismatch error'], $db_name));
+
+// Do some DB type specific checks
+$mysql = false;
+switch ($db_type)
+{
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ $mysql_info = $db->get_version();
+ if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
+ error(sprintf($lang_update['You are running error'], 'MySQL', $mysql_info['version'], UPDATE_TO, MIN_MYSQL_VERSION));
+
+ $mysql = true;
+ break;
+
+ case 'pgsql':
+ $pgsql_info = $db->get_version();
+ if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
+ error(sprintf($lang_update['You are running error'], 'PostgreSQL', $pgsql_info['version'], UPDATE_TO, MIN_PGSQL_VERSION));
+
+ break;
+}
+
+// Check the database, search index and parser revision and the current version
+if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION &&
+ isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION &&
+ isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION &&
+ version_compare($pun_config['o_cur_version'], UPDATE_TO, '>='))
+ error($lang_update['No update error']);
+
+$default_style = $pun_config['o_default_style'];
+if (!file_exists(PUN_ROOT.'style/'.$default_style.'.css'))
+ $default_style = 'Air';
+
+// Start a session, used to queue up errors if duplicate users occur when converting from FluxBB v1.2.
+session_start();
+
+//
+// Determines whether $str is UTF-8 encoded or not
+//
+function seems_utf8($str)
+{
+ $str_len = strlen($str);
+ for ($i = 0; $i < $str_len; ++$i)
+ {
+ if (ord($str[$i]) < 0x80) continue; # 0bbbbbbb
+ else if ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
+ else if ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
+ else if ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
+ else if ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
+ else if ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
+ else return false; # Does not match any model
+
+ for ($j = 0; $j < $n; ++$j) # n bytes matching 10bbbbbb follow ?
+ {
+ if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//
+// Translates the number from a HTML numeric entity into an UTF-8 character
+//
+function dcr2utf8($src)
+{
+ $dest = '';
+ if ($src < 0)
+ return false;
+ else if ($src <= 0x007f)
+ $dest .= chr($src);
+ else if ($src <= 0x07ff)
+ {
+ $dest .= chr(0xc0 | ($src >> 6));
+ $dest .= chr(0x80 | ($src & 0x003f));
+ }
+ else if ($src == 0xFEFF)
+ {
+ // nop -- zap the BOM
+ }
+ else if ($src >= 0xD800 && $src <= 0xDFFF)
+ {
+ // found a surrogate
+ return false;
+ }
+ else if ($src <= 0xffff)
+ {
+ $dest .= chr(0xe0 | ($src >> 12));
+ $dest .= chr(0x80 | (($src >> 6) & 0x003f));
+ $dest .= chr(0x80 | ($src & 0x003f));
+ }
+ else if ($src <= 0x10ffff)
+ {
+ $dest .= chr(0xf0 | ($src >> 18));
+ $dest .= chr(0x80 | (($src >> 12) & 0x3f));
+ $dest .= chr(0x80 | (($src >> 6) & 0x3f));
+ $dest .= chr(0x80 | ($src & 0x3f));
+ }
+ else
+ {
+ // out of range
+ return false;
+ }
+
+ return $dest;
+}
+
+
+//
+// Attempts to convert $str from $old_charset to UTF-8. Also converts HTML entities (including numeric entities) to UTF-8 characters
+//
+function convert_to_utf8(&$str, $old_charset)
+{
+ if (is_null($str) || $str == '')
+ return false;
+
+ $save = $str;
+
+ // Replace literal entities (for non-UTF-8 compliant html_entity_encode)
+ if (version_compare(PHP_VERSION, '5.0.0', '<') && $old_charset == 'ISO-8859-1' || $old_charset == 'ISO-8859-15')
+ $str = html_entity_decode($str, ENT_QUOTES, $old_charset);
+
+ if ($old_charset != 'UTF-8' && !seems_utf8($str))
+ {
+ if (function_exists('iconv'))
+ $str = iconv(!empty($old_charset) ? $old_charset : 'ISO-8859-1', 'UTF-8', $str);
+ else if (function_exists('mb_convert_encoding'))
+ $str = mb_convert_encoding($str, 'UTF-8', !empty($old_charset) ? $old_charset : 'ISO-8859-1');
+ else if ($old_charset == 'ISO-8859-1')
+ $str = utf8_encode($str);
+ }
+
+ // Replace literal entities (for UTF-8 compliant html_entity_encode)
+ if (version_compare(PHP_VERSION, '5.0.0', '>='))
+ $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
+
+ // Replace numeric entities
+ $str = preg_replace_callback('%&#([0-9]+);%', 'utf8_callback_1', $str);
+ $str = preg_replace_callback('%&#x([a-f0-9]+);%i', 'utf8_callback_2', $str);
+
+ // Remove "bad" characters
+ $str = remove_bad_characters($str);
+
+ return ($save != $str);
+}
+
+
+function utf8_callback_1($matches)
+{
+ return dcr2utf8($matches[1]);
+}
+
+
+function utf8_callback_2($matches)
+{
+ return dcr2utf8(hexdec($matches[1]));
+}
+
+
+//
+// Alter a table to be utf8. MySQL only
+// Function based on update_convert_table_utf8() from the Drupal project (http://drupal.org/)
+//
+function alter_table_utf8($table)
+{
+ global $mysql, $db;
+ static $types;
+
+ if (!$mysql)
+ return;
+
+ if (!isset($types))
+ {
+ $types = array(
+ 'char' => 'binary',
+ 'varchar' => 'varbinary',
+ 'tinytext' => 'tinyblob',
+ 'mediumtext' => 'mediumblob',
+ 'text' => 'blob',
+ 'longtext' => 'longblob'
+ );
+ }
+
+ // Set table default charset to utf8
+ $db->query('ALTER TABLE '.$table.' CHARACTER SET utf8') or error('Unable to set table character set', __FILE__, __LINE__, $db->error());
+
+ // Find out which columns need converting and build SQL statements
+ $result = $db->query('SHOW FULL COLUMNS FROM '.$table) or error('Unable to fetch column information', __FILE__, __LINE__, $db->error());
+ while ($cur_column = $db->fetch_assoc($result))
+ {
+ if (is_null($cur_column['Collation']))
+ continue;
+
+ list($type) = explode('(', $cur_column['Type']);
+ if (isset($types[$type]) && strpos($cur_column['Collation'], 'utf8') === false)
+ {
+ $allow_null = ($cur_column['Null'] == 'YES');
+ $collate = (substr($cur_column['Collation'], -3) == 'bin') ? 'utf8_bin' : 'utf8_general_ci';
+
+ $db->alter_field($table, $cur_column['Field'], preg_replace('%'.$type.'%i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to binary', __FILE__, __LINE__, $db->error());
+ $db->alter_field($table, $cur_column['Field'], $cur_column['Type'].' CHARACTER SET utf8 COLLATE '.$collate, $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to utf8', __FILE__, __LINE__, $db->error());
+ }
+ }
+}
+
+//
+// Safely converts text type columns into utf8
+// If finished returns true, otherwise returns $end_at
+//
+function convert_table_utf8($table, $callback, $old_charset, $key = null, $start_at = null, $error_callback = null)
+{
+ global $mysql, $db, $old_connection_charset;
+
+ $finished = true;
+ $end_at = 0;
+ if ($mysql)
+ {
+ // Only set up the tables if we are doing this in 1 go, or it's the first go
+ if (is_null($start_at) || $start_at == 0)
+ {
+ // Drop any temp table that exists, in-case it's left over from a failed update
+ $db->drop_table($table.'_utf8', true) or error('Unable to drop left over temp table', __FILE__, __LINE__, $db->error());
+
+ // Copy the table
+ $db->query('CREATE TABLE '.$table.'_utf8 LIKE '.$table) or error('Unable to create new table', __FILE__, __LINE__, $db->error());
+
+ // Set table default charset to utf8
+ alter_table_utf8($table.'_utf8');
+ }
+
+ // Change to the old character set so MySQL doesn't attempt to perform conversion on the data from the old table
+ $db->set_names($old_connection_charset);
+
+ // Move & Convert everything
+ $result = $db->query('SELECT * FROM '.$table.(is_null($start_at) ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.(is_null($start_at) ? '' : ' LIMIT '.PER_PAGE), false) or error('Unable to select from old table', __FILE__, __LINE__, $db->error());
+
+ // Change back to utf8 mode so we can insert it into the new table
+ $db->set_names('utf8');
+
+ while ($cur_item = $db->fetch_assoc($result))
+ {
+ $cur_item = call_user_func($callback, $cur_item, $old_charset);
+
+ $temp = array();
+ foreach ($cur_item as $idx => $value)
+ $temp[$idx] = is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'';
+
+ $db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or (is_null($error_callback) ? error('Unable to insert data to new table', __FILE__, __LINE__, $db->error()) : call_user_func($error_callback, $cur_item));
+
+ $end_at = $cur_item[$key];
+ }
+
+ // If we aren't doing this all in 1 go and $end_at has a value (i.e. we have processed at least 1 row), figure out if we have more to do or not
+ if (!is_null($start_at) && $end_at > 0)
+ {
+ $result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
+ $finished = $db->num_rows($result) == 0;
+ }
+
+ // Only swap the tables if we are doing this in 1 go, or it's the last go
+ if ($finished)
+ {
+ // Delete old table
+ $db->drop_table($table, true) or error('Unable to drop old table', __FILE__, __LINE__, $db->error());
+
+ // Rename table
+ $db->query('ALTER TABLE '.$table.'_utf8 RENAME '.$table) or error('Unable to rename new table', __FILE__, __LINE__, $db->error());
+
+ return true;
+ }
+
+ return $end_at;
+ }
+ else
+ {
+ // Convert everything
+ $result = $db->query('SELECT * FROM '.$table.(is_null($start_at) ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.(is_null($start_at ) ? '' : ' LIMIT '.PER_PAGE)) or error('Unable to select from table', __FILE__, __LINE__, $db->error());
+ while ($cur_item = $db->fetch_assoc($result))
+ {
+ $cur_item = call_user_func($callback, $cur_item, $old_charset);
+
+ $temp = array();
+ foreach ($cur_item as $idx => $value)
+ $temp[] = $idx.'='.(is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'');
+
+ if (!empty($temp))
+ $db->query('UPDATE '.$table.' SET '.implode(', ', $temp).' WHERE '.$key.'=\''.$db->escape($cur_item[$key]).'\'') or error('Unable to update data', __FILE__, __LINE__, $db->error());
+
+ $end_at = $cur_item[$key];
+ }
+
+ if (!is_null($start_at) && $end_at > 0)
+ {
+ $result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result) == 0)
+ return true;
+
+ return $end_at;
+ }
+
+ return true;
+ }
+}
+
+
+header('Content-type: text/html; charset=utf-8');
+
+// Empty all output buffers and stop buffering
+while (@ob_end_clean());
+
+
+$stage = isset($_REQUEST['stage']) ? $_REQUEST['stage'] : '';
+$old_charset = isset($_REQUEST['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_REQUEST['req_old_charset'])) : 'ISO-8859-1';
+$start_at = isset($_REQUEST['start_at']) ? intval($_REQUEST['start_at']) : 0;
+$query_str = '';
+
+// Show form
+if (empty($stage))
+{
+ if (file_exists(FORUM_CACHE_DIR.'db_update.lock'))
+ {
+ // Deal with newlines, tabs and multiple spaces
+ $pattern = array("\t", ' ', ' ');
+ $replace = array('&#160; &#160; ', '&#160; ', ' &#160;');
+ $message = str_replace($pattern, $replace, $pun_config['o_maintenance_message']);
+
+?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo $lang_update['Maintenance'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="punmaint" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<div class="block">
+ <h2><?php echo $lang_update['Maintenance'] ?></h2>
+ <div class="box">
+ <div class="inbox">
+ <p><?php echo $message ?></p>
+ </div>
+ </div>
+</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+ }
+ else
+ {
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo $lang_update['Update'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body onload="document.getElementById('install').req_db_pass.focus();document.getElementById('install').start.disabled=false;">
+
+<div id="pundb_update" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <div id="brdtitle" class="inbox">
+ <h1><span><?php echo $lang_update['Update'] ?></span></h1>
+ <div id="brddesc"><p><?php echo $lang_update['Update message'] ?></p><p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Members message']; ?></p></div>
+ </div>
+ </div>
+</div>
+
+<div id="brdmain">
+<div class="blockform">
+ <h2><span><?php echo $lang_update['Update'] ?></span></h2>
+ <div class="box">
+ <form id="install" method="post" action="db_update.php">
+ <input type="hidden" name="stage" value="start" />
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_update['Administrator only'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_update['Database password info'] ?></p>
+ <p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Database password note'] ?></p>
+ <label class="required"><strong><?php echo $lang_update['Database password'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="password" id="req_db_pass" name="req_db_pass" /><br /></label>
+ <p><?php echo $lang_update['Maintenance message info'] ?></p>
+ <div class="txtarea">
+ <label class="required"><strong><?php echo $lang_update['Maintenance message'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br />
+ <textarea name="req_maintenance_message" rows="4" cols="65"><?php echo pun_htmlspecialchars($pun_config['o_maintenance_message']) ?></textarea><br /></label>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <div class="forminfo">
+ <p><?php echo $lang_update['Intro 1'] ?></p>
+ <p><?php echo $lang_update['Intro 2'] ?></p>
+<?php
+
+ if (strpos($cur_version, '1.2') === 0)
+ {
+ if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
+ {
+
+?>
+ <p><?php echo $lang_update['No charset conversion'] ?></p>
+<?php
+
+ }
+
+?>
+ </div>
+ </div>
+ <div class="inform">
+ <div class="forminfo">
+ <p><?php echo $lang_update['Enable conversion'] ?></p>
+ <p><?php echo $lang_update['Current character set'] ?></p>
+ </div>
+ <fieldset>
+ <legend><?php echo $lang_update['Charset conversion'] ?></legend>
+ <div class="infldset">
+ <div class="rbox">
+ <label><input type="checkbox" name="convert_charset" value="1" checked="checked" /><?php echo $lang_update['Enable conversion label'] ?><br /></label>
+ </div>
+ <label>
+ <strong><?php echo $lang_update['Current character set label'] ?></strong><br /><?php echo $lang_update['Current character set info'] ?><br />
+ <input type="text" name="req_old_charset" size="12" maxlength="20" value="<?php echo $old_charset ?>" /><br />
+ </label>
+ </div>
+ </fieldset>
+<?php
+
+ }
+ else
+ echo "\t\t\t\t".'</div>'."\n";
+
+?>
+ </div>
+ <p class="buttons"><input type="submit" name="start" value="<?php echo $lang_update['Start update'] ?>" /></p>
+ </form>
+ </div>
+</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+ }
+ $db->end_transaction();
+ $db->close();
+ exit;
+
+}
+
+// Read the lock file
+$lock = file_exists(FORUM_CACHE_DIR.'db_update.lock') ? trim(file_get_contents(FORUM_CACHE_DIR.'db_update.lock')) : false;
+$lock_error = false;
+
+// Generate or fetch the UID - this confirms we have a valid admin
+if (isset($_POST['req_db_pass']))
+{
+ $req_db_pass = strtolower(pun_trim($_POST['req_db_pass']));
+
+ switch ($db_type)
+ {
+ // For SQLite we compare against the database file name, since the password is left blank
+ case 'sqlite':
+ if ($req_db_pass != strtolower($db_name))
+ error(sprintf($lang_update['Invalid file error'], 'config.php'));
+
+ break;
+ // For everything else, check the password matches
+ default:
+ if ($req_db_pass != strtolower($db_password))
+ error(sprintf($lang_update['Invalid password error'], 'config.php'));
+
+ break;
+ }
+
+ // Generate a unique id to identify this session, only if this is a valid session
+ $uid = pun_hash($req_db_pass.'|'.uniqid(rand(), true));
+ if ($lock) // We already have a lock file
+ $lock_error = true;
+ else // Create the lock file
+ {
+ $fh = @fopen(FORUM_CACHE_DIR.'db_update.lock', 'wb');
+ if (!$fh)
+ error(sprintf($lang_update['Unable to lock error'], 'cache'));
+
+ fwrite($fh, $uid);
+ fclose($fh);
+
+ // Update maintenance message
+ if ($_POST['req_maintenance_message'] != '')
+ $maintenance_message = pun_trim(pun_linebreaks($_POST['req_maintenance_message']));
+ else
+ {
+ // Load the admin_options.php language file
+ require PUN_ROOT.'lang/'.$default_lang.'/admin_options.php';
+
+ $maintenance_message = $lang_admin_options['Default maintenance message'];
+ }
+
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$db->escape($maintenance_message).'\' WHERE conf_name=\'o_maintenance_message\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the config cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_config_cache();
+ }
+}
+else if (isset($_GET['uid']))
+{
+ $uid = pun_trim($_GET['uid']);
+ if (!$lock || $lock !== $uid) // The lock doesn't exist or doesn't match the given UID
+ $lock_error = true;
+}
+else
+ error($lang_update['No password error']);
+
+// If there is an error with the lock file
+if ($lock_error)
+ error(sprintf($lang_update['Script runs error'], FORUM_CACHE_DIR.'db_update.lock'));
+
+switch ($stage)
+{
+ // Start by updating the database structure
+ case 'start':
+ $query_str = '?stage=preparse_posts';
+
+ // If we don't need to update the database, skip this stage
+ if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION)
+ break;
+
+ // Make all email fields VARCHAR(80)
+ $db->alter_field('bans', 'email', 'VARCHAR(80)', true) or error('Unable to alter email field', __FILE__, __LINE__, $db->error());
+ $db->alter_field('posts', 'poster_email', 'VARCHAR(80)', true) or error('Unable to alter poster_email field', __FILE__, __LINE__, $db->error());
+ $db->alter_field('users', 'email', 'VARCHAR(80)', false, '') or error('Unable to alter email field', __FILE__, __LINE__, $db->error());
+ $db->alter_field('users', 'jabber', 'VARCHAR(80)', true) or error('Unable to alter jabber field', __FILE__, __LINE__, $db->error());
+ $db->alter_field('users', 'msn', 'VARCHAR(80)', true) or error('Unable to alter msn field', __FILE__, __LINE__, $db->error());
+ $db->alter_field('users', 'activate_string', 'VARCHAR(80)', true) or error('Unable to alter activate_string field', __FILE__, __LINE__, $db->error());
+
+ // Make all IP fields VARCHAR(39) to support IPv6
+ $db->alter_field('posts', 'poster_ip', 'VARCHAR(39)', true) or error('Unable to alter poster_ip field', __FILE__, __LINE__, $db->error());
+ $db->alter_field('users', 'registration_ip', 'VARCHAR(39)', false, '0.0.0.0') or error('Unable to alter registration_ip field', __FILE__, __LINE__, $db->error());
+
+ // Make the message field MEDIUMTEXT to allow proper conversion of 65535 character posts to UTF-8
+ $db->alter_field('posts', 'message', 'MEDIUMTEXT', true) or error('Unable to alter message field', __FILE__, __LINE__, $db->error());
+
+ // Add the DST option to the users table
+ $db->add_field('users', 'dst', 'TINYINT(1)', false, 0, 'timezone') or error('Unable to add dst field', __FILE__, __LINE__, $db->error());
+
+ // Add the last_post column to the online table
+ $db->add_field('online', 'last_post', 'INT(10) UNSIGNED', true, null, null) or error('Unable to add last_post field', __FILE__, __LINE__, $db->error());
+
+ // Add the last_search column to the online table
+ $db->add_field('online', 'last_search', 'INT(10) UNSIGNED', true, null, null) or error('Unable to add last_search field', __FILE__, __LINE__, $db->error());
+
+ // Add the last_search column to the users table
+ $db->add_field('users', 'last_search', 'INT(10) UNSIGNED', true, null, 'last_post') or error('Unable to add last_search field', __FILE__, __LINE__, $db->error());
+
+ // Drop use_avatar column from users table
+ $db->drop_field('users', 'use_avatar') or error('Unable to drop use_avatar field', __FILE__, __LINE__, $db->error());
+
+ // Drop save_pass column from users table
+ $db->drop_field('users', 'save_pass') or error('Unable to drop save_pass field', __FILE__, __LINE__, $db->error());
+
+ // Drop g_edit_subjects_interval column from groups table
+ $db->drop_field('groups', 'g_edit_subjects_interval');
+
+ // Add database revision number
+ if (!array_key_exists('o_database_revision', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_database_revision\', \'0\')') or error('Unable to insert config value \'o_database_revision\'', __FILE__, __LINE__, $db->error());
+
+ // Add search index revision number
+ if (!array_key_exists('o_searchindex_revision', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_searchindex_revision\', \'0\')') or error('Unable to insert config value \'o_searchindex_revision\'', __FILE__, __LINE__, $db->error());
+
+ // Add parser revision number
+ if (!array_key_exists('o_parser_revision', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_parser_revision\', \'0\')') or error('Unable to insert config value \'o_parser_revision\'', __FILE__, __LINE__, $db->error());
+
+ // Add default email setting option
+ if (!array_key_exists('o_default_email_setting', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_email_setting\', \'1\')') or error('Unable to insert config value \'o_default_email_setting\'', __FILE__, __LINE__, $db->error());
+
+ // Make sure we have o_additional_navlinks (was added in 1.2.1)
+ if (!array_key_exists('o_additional_navlinks', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_additional_navlinks\', \'\')') or error('Unable to insert config value \'o_additional_navlinks\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_topic_views
+ if (!array_key_exists('o_topic_views', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_topic_views\', \'1\')') or error('Unable to insert config value \'o_topic_views\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_signatures
+ if (!array_key_exists('o_signatures', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_signatures\', \'1\')') or error('Unable to insert config value \'o_signatures\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_smtp_ssl
+ if (!array_key_exists('o_smtp_ssl', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_smtp_ssl\', \'0\')') or error('Unable to insert config value \'o_smtp_ssl\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_default_dst
+ if (!array_key_exists('o_default_dst', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_dst\', \'0\')') or error('Unable to insert config value \'o_default_dst\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_quote_depth
+ if (!array_key_exists('o_quote_depth', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_quote_depth\', \'3\')') or error('Unable to insert config value \'o_quote_depth\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_feed_type
+ if (!array_key_exists('o_feed_type', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_type\', \'2\')') or error('Unable to insert config value \'o_feed_type\'', __FILE__, __LINE__, $db->error());
+
+ // Insert new config option o_feed_ttl
+ if (!array_key_exists('o_feed_ttl', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_ttl\', \'0\')') or error('Unable to insert config value \'o_feed_ttl\'', __FILE__, __LINE__, $db->error());
+
+ // Insert config option o_base_url which was removed in 1.3
+ if (!array_key_exists('o_base_url', $pun_config))
+ {
+ // If it isn't in $pun_config['o_base_url'] it should be in $base_url, but just in-case it isn't we can make a guess at it
+ if (!isset($base_url))
+ {
+ // Make an educated guess regarding base_url
+ $base_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; // protocol
+ $base_url .= preg_replace('%:(80|443)$%', '', $_SERVER['HTTP_HOST']); // host[:port]
+ $base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); // path
+ }
+
+ if (substr($base_url, -1) == '/')
+ $base_url = substr($base_url, 0, -1);
+
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_base_url\', \''.$db->escape($base_url).'\')') or error('Unable to insert config value \'o_base_url\'', __FILE__, __LINE__, $db->error());
+ }
+
+ if (strpos($cur_version, '1.2') === 0)
+ {
+ // Groups are almost the same as 1.2:
+ // unverified: 32000 -> 0
+
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = 0 WHERE group_id = 32000') or error('Unable to update unverified users', __FILE__, __LINE__, $db->error());
+ }
+ else if (strpos($cur_version, '1.3') === 0)
+ {
+ // Groups have changed quite a lot from 1.3:
+ // unverified: 0 -> 0
+ // admin: 1 -> 1
+ // mod: ? -> 2
+ // guest: 2 -> 3
+ // member: ? -> 4
+
+ $result = $db->query('SELECT MAX(g_id) + 1 FROM '.$db->prefix.'groups') or error('Unable to select temp group ID', __FILE__, __LINE__, $db->error());
+ $temp_id = $db->result($result);
+
+ $result = $db->query('SELECT g_id FROM '.$db->prefix.'groups WHERE g_moderator = 1 AND g_id > 1 LIMIT 1') or error('Unable to select moderator group', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ $mod_gid = $db->result($result);
+ else
+ {
+ $db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('."'Moderators', 'Moderator', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)") or error('Unable to add group', __FILE__, __LINE__, $db->error());
+ $mod_gid = $db->insert_id();
+ }
+
+ $member_gid = $pun_config['o_default_user_group'];
+
+ // move the mod group to a temp place
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$mod_gid) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+ if ($member_gid == $mod_gid) $member_gid = $temp_id;
+
+ // move whoever is in 3 to a spare slot
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$mod_gid.' WHERE g_id = 3') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$mod_gid.' WHERE group_id = 3') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$mod_gid.' WHERE group_id = 3') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+ if ($member_gid == 3) $member_gid = $mod_gid;
+
+ // move guest to 3
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = 3 WHERE g_id = 2') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = 3 WHERE group_id = 2') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 3 WHERE group_id = 2') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+ if ($member_gid == 2) $member_gid = 3;
+
+ // move mod group in temp place to 2
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = 2 WHERE g_id = '.$temp_id) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = 2 WHERE group_id = '.$temp_id) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 2 WHERE group_id = '.$temp_id) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+ if ($member_gid == $temp_id) $member_gid = 2;
+
+ // Only move stuff around if it isn't already in the right place
+ if ($member_gid != $mod_gid || $member_gid != 4)
+ {
+ // move members to temp place
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$member_gid) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+
+ // move whoever is in 4 to members place
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$member_gid.' WHERE g_id = 4') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$member_gid.' WHERE group_id = 4') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$member_gid.' WHERE group_id = 4') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+
+ // move members in temp place to 4
+ $db->query('UPDATE '.$db->prefix.'groups SET g_id = 4 WHERE g_id = '.$temp_id) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'users SET group_id = 4 WHERE group_id = '.$temp_id) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 4 WHERE group_id = '.$temp_id) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+ }
+
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$member_gid.'\' WHERE conf_name=\'o_default_user_group\'') or error('Unable to update default user group ID', __FILE__, __LINE__, $db->error());
+ }
+
+ // Server time zone is now simply the default time zone
+ if (!array_key_exists('o_default_timezone', $pun_config))
+ $db->query('UPDATE '.$db->prefix.'config SET conf_name = \'o_default_timezone\' WHERE conf_name = \'o_server_timezone\'') or error('Unable to update time zone config', __FILE__, __LINE__, $db->error());
+
+ // Increase visit timeout to 30 minutes (only if it hasn't been changed from the default)
+ if (!array_key_exists('o_database_revision', $pun_config) && $pun_config['o_timeout_visit'] == '600')
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'1800\' WHERE conf_name = \'o_timeout_visit\'') or error('Unable to update visit timeout config', __FILE__, __LINE__, $db->error());
+
+ // Remove obsolete g_post_polls permission from groups table
+ $db->drop_field('groups', 'g_post_polls');
+
+ // Make room for multiple moderator groups
+ if (!$db->field_exists('groups', 'g_moderator'))
+ {
+ // Add g_moderator column to groups table
+ $db->add_field('groups', 'g_moderator', 'TINYINT(1)', false, 0, 'g_user_title') or error('Unable to add g_moderator field', __FILE__, __LINE__, $db->error());
+
+ // Give the moderator group moderator privileges
+ $db->query('UPDATE '.$db->prefix.'groups SET g_moderator = 1 WHERE g_id = 2') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+ }
+
+ // Replace obsolete p_mod_edit_users config setting with new per-group permission
+ if (array_key_exists('p_mod_edit_users', $pun_config))
+ {
+ $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_edit_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+ $db->add_field('groups', 'g_mod_edit_users', 'TINYINT(1)', false, 0, 'g_moderator') or error('Unable to add g_mod_edit_users field', __FILE__, __LINE__, $db->error());
+
+ $db->query('UPDATE '.$db->prefix.'groups SET g_mod_edit_users = '.$pun_config['p_mod_edit_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+ }
+
+ // Replace obsolete p_mod_rename_users config setting with new per-group permission
+ if (array_key_exists('p_mod_rename_users', $pun_config))
+ {
+ $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_rename_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+ $db->add_field('groups', 'g_mod_rename_users', 'TINYINT(1)', false, 0, 'g_mod_edit_users') or error('Unable to add g_mod_rename_users field', __FILE__, __LINE__, $db->error());
+
+ $db->query('UPDATE '.$db->prefix.'groups SET g_mod_rename_users = '.$pun_config['p_mod_rename_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+ }
+
+ // Replace obsolete p_mod_change_passwords config setting with new per-group permission
+ if (array_key_exists('p_mod_change_passwords', $pun_config))
+ {
+ $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_change_passwords\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+ $db->add_field('groups', 'g_mod_change_passwords', 'TINYINT(1)', false, 0, 'g_mod_rename_users') or error('Unable to add g_mod_change_passwords field', __FILE__, __LINE__, $db->error());
+
+ $db->query('UPDATE '.$db->prefix.'groups SET g_mod_change_passwords = '.$pun_config['p_mod_change_passwords'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+ }
+
+ // Replace obsolete p_mod_ban_users config setting with new per-group permission
+ if (array_key_exists('p_mod_ban_users', $pun_config))
+ {
+ $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_ban_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+ $db->add_field('groups', 'g_mod_ban_users', 'TINYINT(1)', false, 0, 'g_mod_change_passwords') or error('Unable to add g_mod_ban_users field', __FILE__, __LINE__, $db->error());
+
+ $db->query('UPDATE '.$db->prefix.'groups SET g_mod_ban_users = '.$pun_config['p_mod_ban_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+ }
+
+ // We need to add a unique index to avoid users having multiple rows in the online table
+ if (!$db->index_exists('online', 'user_id_ident_idx'))
+ {
+ $db->truncate_table('online') or error('Unable to clear online table', __FILE__, __LINE__, $db->error());
+
+ if ($mysql)
+ $db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident(25)'), true) or error('Unable to add user_id_ident_idx index', __FILE__, __LINE__, $db->error());
+ else
+ $db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident'), true) or error('Unable to add user_id_ident_idx index', __FILE__, __LINE__, $db->error());
+ }
+
+ // Remove the redundant user_id_idx on the online table
+ $db->drop_index('online', 'user_id_idx') or error('Unable to drop user_id_idx index', __FILE__, __LINE__, $db->error());
+
+ // Add an index to ident on the online table
+ if ($mysql)
+ $db->add_index('online', 'ident_idx', array('ident(25)')) or error('Unable to add ident_idx index', __FILE__, __LINE__, $db->error());
+ else
+ $db->add_index('online', 'ident_idx', array('ident')) or error('Unable to add ident_idx index', __FILE__, __LINE__, $db->error());
+
+ // Add an index to logged in the online table
+ $db->add_index('online', 'logged_idx', array('logged')) or error('Unable to add logged_idx index', __FILE__, __LINE__, $db->error());
+
+ // Add an index to last_post in the topics table
+ $db->add_index('topics', 'last_post_idx', array('last_post')) or error('Unable to add last_post_idx index', __FILE__, __LINE__, $db->error());
+
+ // Add an index to username on the bans table
+ if ($mysql)
+ $db->add_index('bans', 'username_idx', array('username(25)')) or error('Unable to add username_idx index', __FILE__, __LINE__, $db->error());
+ else
+ $db->add_index('bans', 'username_idx', array('username')) or error('Unable to add username_idx index', __FILE__, __LINE__, $db->error());
+
+ // Change the username_idx on users to a unique index of max size 25
+ $db->drop_index('users', 'username_idx') or error('Unable to drop old username_idx index', __FILE__, __LINE__, $db->error());
+ $field = $mysql ? 'username(25)' : 'username';
+
+ // Attempt to add a unique index. If the user doesn't use a transactional database this can fail due to multiple matching usernames in the
+ // users table. This is bad, but just giving up if it happens is even worse! If it fails just add a regular non-unique index.
+ if (!$db->add_index('users', 'username_idx', array($field), true))
+ $db->add_index('users', 'username_idx', array($field)) or error('Unable to add username_idx field', __FILE__, __LINE__, $db->error());
+
+ // Add g_view_users column to groups table
+ $db->add_field('groups', 'g_view_users', 'TINYINT(1)', false, 1, 'g_read_board') or error('Unable to add g_view_users field', __FILE__, __LINE__, $db->error());
+
+ // Add the last_email_sent column to the users table and the g_send_email and
+ // g_email_flood columns to the groups table
+ $db->add_field('users', 'last_email_sent', 'INT(10) UNSIGNED', true, null, 'last_search') or error('Unable to add last_email_sent field', __FILE__, __LINE__, $db->error());
+ $db->add_field('groups', 'g_send_email', 'TINYINT(1)', false, 1, 'g_search_users') or error('Unable to add g_send_email field', __FILE__, __LINE__, $db->error());
+ $db->add_field('groups', 'g_email_flood', 'SMALLINT(6)', false, 60, 'g_search_flood') or error('Unable to add g_email_flood field', __FILE__, __LINE__, $db->error());
+
+ // Add the last_report_sent column to the users table and the g_report_flood
+ // column to the groups table
+ $db->add_field('users', 'last_report_sent', 'INT(10) UNSIGNED', true, null, 'last_email_sent') or error('Unable to add last_report_sent field', __FILE__, __LINE__, $db->error());
+ $db->add_field('groups', 'g_report_flood', 'SMALLINT(6)', false, 60, 'g_email_flood') or error('Unable to add g_report_flood field', __FILE__, __LINE__, $db->error());
+
+ // Set non-default g_send_email, g_flood_email and g_flood_report values properly
+ $db->query('UPDATE '.$db->prefix.'groups SET g_send_email = 0 WHERE g_id = 3') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'groups SET g_email_flood = 0, g_report_flood = 0 WHERE g_id IN (1,2,3)') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
+
+ // Add the auto notify/subscription option to the users table
+ $db->add_field('users', 'auto_notify', 'TINYINT(1)', false, 0, 'notify_with_post') or error('Unable to add auto_notify field', __FILE__, __LINE__, $db->error());
+
+ // Add the first_post_id column to the topics table
+ if (!$db->field_exists('topics', 'first_post_id'))
+ {
+ $db->add_field('topics', 'first_post_id', 'INT(10) UNSIGNED', false, 0, 'posted') or error('Unable to add first_post_id field', __FILE__, __LINE__, $db->error());
+ $db->add_index('topics', 'first_post_id_idx', array('first_post_id')) or error('Unable to add first_post_id_idx index', __FILE__, __LINE__, $db->error());
+
+ // Now that we've added the column and indexed it, we need to give it correct data
+ $result = $db->query('SELECT MIN(id) AS first_post, topic_id FROM '.$db->prefix.'posts GROUP BY topic_id') or error('Unable to fetch first_post_id', __FILE__, __LINE__, $db->error());
+
+ while ($cur_post = $db->fetch_assoc($result))
+ $db->query('UPDATE '.$db->prefix.'topics SET first_post_id = '.$cur_post['first_post'].' WHERE id = '.$cur_post['topic_id']) or error('Unable to update first_post_id', __FILE__, __LINE__, $db->error());
+ }
+
+ // Move any users with the old unverified status to their new group
+ $db->query('UPDATE '.$db->prefix.'users SET group_id=0 WHERE group_id=32000') or error('Unable to move unverified users', __FILE__, __LINE__, $db->error());
+
+ // Add the ban_creator column to the bans table
+ $db->add_field('bans', 'ban_creator', 'INT(10) UNSIGNED', false, 0) or error('Unable to add ban_creator field', __FILE__, __LINE__, $db->error());
+
+ // Add the time/date format settings to the user table
+ $db->add_field('users', 'time_format', 'TINYINT(1)', false, 0, 'dst') or error('Unable to add time_format field', __FILE__, __LINE__, $db->error());
+ $db->add_field('users', 'date_format', 'TINYINT(1)', false, 0, 'dst') or error('Unable to add date_format field', __FILE__, __LINE__, $db->error());
+
+ // Change the search_data column to mediumtext
+ $db->alter_field('search_cache', 'search_data', 'MEDIUMTEXT', true) or error('Unable to alter search_data field', __FILE__, __LINE__, $db->error());
+
+ // Add the group promotion columns to the groups table
+ $db->add_field('groups', 'g_promote_min_posts', 'INT(10) UNSIGNED', false, 0, 'g_user_title') or error('Unable to add g_promote_min_posts field', __FILE__, __LINE__, $db->error());
+ $db->add_field('groups', 'g_promote_next_group', 'INT(10) UNSIGNED', false, 0, 'g_promote_min_posts') or error('Unable to add g_promote_next_group field', __FILE__, __LINE__, $db->error());
+
+ // Add a field for the per-group permission to post links
+ $db->add_field('groups', 'g_post_links', 'TINYINT(1)', false, 1, 'g_delete_topics') or error('Unable to add per-group permission to post links', __FILE__, __LINE__, $db->error());
+
+ // Add a field for the per-group permission to promote users to the next auto-promote group
+ $db->add_field('groups', 'g_mod_promote_users', 'TINYINT(1)', false, 0, 'g_mod_ban_users') or error('Unable to add per-group permission to promote users', __FILE__, __LINE__, $db->error());
+
+ // In case we had the fulltext search extension installed (1.3-legacy), remove it
+ $db->drop_index('topics', 'subject_idx') or error('Unable to drop subject_idx index', __FILE__, __LINE__, $db->error());
+ $db->drop_index('posts', 'message_idx') or error('Unable to drop message_idx index', __FILE__, __LINE__, $db->error());
+ // In case we had the fulltext search mod installed (1.2), remove it
+ $db->drop_index('topics', 'subject_fulltext_search') or error('Unable to drop subject_fulltext_search index', __FILE__, __LINE__, $db->error());
+ $db->drop_index('posts', 'message_fulltext_search') or error('Unable to drop message_fulltext_search index', __FILE__, __LINE__, $db->error());
+
+ // If the search_cache table has been dropped by the fulltext search extension, recreate it
+ if (!$db->table_exists('search_cache'))
+ {
+ $schema = array(
+ 'FIELDS' => array(
+ 'id' => array(
+ 'datatype' => 'INT(10) UNSIGNED',
+ 'allow_null' => false,
+ 'default' => '0'
+ ),
+ 'ident' => array(
+ 'datatype' => 'VARCHAR(200)',
+ 'allow_null' => false,
+ 'default' => '\'\''
+ ),
+ 'search_data' => array(
+ 'datatype' => 'MEDIUMTEXT',
+ 'allow_null' => true
+ )
+ ),
+ 'PRIMARY KEY' => array('id'),
+ 'INDEXES' => array(
+ 'ident_idx' => array('ident')
+ )
+ );
+
+ if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+ $schema['INDEXES']['ident_idx'] = array('ident(8)');
+
+ $db->create_table('search_cache', $schema);
+ }
+
+ // If the search_matches table has been dropped by the fulltext search extension, recreate it
+ if (!$db->table_exists('search_matches'))
+ {
+ $schema = array(
+ 'FIELDS' => array(
+ 'post_id' => array(
+ 'datatype' => 'INT(10) UNSIGNED',
+ 'allow_null' => false,
+ 'default' => '0'
+ ),
+ 'word_id' => array(
+ 'datatype' => 'INT(10) UNSIGNED',
+ 'allow_null' => false,
+ 'default' => '0'
+ ),
+ 'subject_match' => array(
+ 'datatype' => 'TINYINT(1)',
+ 'allow_null' => false,
+ 'default' => '0'
+ )
+ ),
+ 'INDEXES' => array(
+ 'word_id_idx' => array('word_id'),
+ 'post_id_idx' => array('post_id')
+ )
+ );
+
+ $db->create_table('search_matches', $schema);
+ }
+
+ // If the search_words table has been dropped by the fulltext search extension, recreate it
+ if (!$db->table_exists('search_words'))
+ {
+ $schema = array(
+ 'FIELDS' => array(
+ 'id' => array(
+ 'datatype' => 'SERIAL',
+ 'allow_null' => false
+ ),
+ 'word' => array(
+ 'datatype' => 'VARCHAR(20)',
+ 'allow_null' => false,
+ 'default' => '\'\'',
+ 'collation' => 'bin'
+ )
+ ),
+ 'PRIMARY KEY' => array('word'),
+ 'INDEXES' => array(
+ 'id_idx' => array('id')
+ )
+ );
+
+ if ($db_type == 'sqlite')
+ {
+ $schema['PRIMARY KEY'] = array('id');
+ $schema['UNIQUE KEYS'] = array('word_idx' => array('word'));
+ }
+
+ $db->create_table('search_words', $schema);
+ }
+
+ // Rename the subscription table
+ $db->rename_table('subscriptions', 'topic_subscriptions');
+
+ // if we don't have the forum_subscriptions table, create it
+ if (!$db->table_exists('forum_subscriptions'))
+ {
+ $schema = array(
+ 'FIELDS' => array(
+ 'user_id' => array(
+ 'datatype' => 'INT(10) UNSIGNED',
+ 'allow_null' => false,
+ 'default' => '0'
+ ),
+ 'forum_id' => array(
+ 'datatype' => 'INT(10) UNSIGNED',
+ 'allow_null' => false,
+ 'default' => '0'
+ )
+ ),
+ 'PRIMARY KEY' => array('user_id', 'forum_id')
+ );
+
+ $db->create_table('forum_subscriptions', $schema) or error('Unable to create forum subscriptions table', __FILE__, __LINE__, $db->error());
+ }
+
+ // Insert new config option o_forum_subscriptions
+ if (!array_key_exists('o_forum_subscriptions', $pun_config))
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_forum_subscriptions\', \'1\')') or error('Unable to insert config value \'o_forum_subscriptions\'', __FILE__, __LINE__, $db->error());
+
+ // Rename config option o_subscriptions to o_topic_subscriptions
+ if (!array_key_exists('o_topic_subscriptions', $pun_config))
+ $db->query('UPDATE '.$db->prefix.'config SET conf_name=\'o_topic_subscriptions\' WHERE conf_name=\'o_subscriptions\'') or error('Unable to rename config value \'o_subscriptions\'', __FILE__, __LINE__, $db->error());
+
+ // Change the default style if the old doesn't exist anymore
+ if ($pun_config['o_default_style'] != $default_style)
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($default_style).'\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style config', __FILE__, __LINE__, $db->error());
+
+ // For MySQL(i) without InnoDB, change the engine of the online table (for performance reasons)
+ if ($db_type == 'mysql' || $db_type == 'mysqli')
+ $db->query('ALTER TABLE '.$db->prefix.'online ENGINE = MyISAM') or error('Unable to change engine type of online table to MyISAM', __FILE__, __LINE__, $db->error());
+
+ // Remove config option o_ranks
+ if (array_key_exists('o_ranks', $pun_config))
+ $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name=\'o_ranks\'') or error('Unable to remove config value \'o_ranks\'', __FILE__, __LINE__, $db->error());
+
+ // Remove the ranks table
+ if ($db->table_exists('ranks'))
+ $db->drop_table('ranks') or error('Unable to drop ranks table', __FILE__, __LINE__, $db->error());
+
+ // Should we do charset conversion or not?
+ if (strpos($cur_version, '1.2') === 0 && isset($_POST['convert_charset']))
+ $query_str = '?stage=conv_bans&req_old_charset='.$old_charset;
+
+ break;
+
+
+ // Convert bans
+ case 'conv_bans':
+ $query_str = '?stage=conv_categories&req_old_charset='.$old_charset;
+
+ function _conv_bans($cur_item, $old_charset)
+ {
+ global $lang_update;
+
+ echo sprintf($lang_update['Converting item'], $lang_update['ban'], $cur_item['id']).'<br />'."\n";
+
+ convert_to_utf8($cur_item['username'], $old_charset);
+ convert_to_utf8($cur_item['message'], $old_charset);
+
+ return $cur_item;
+ }
+
+ $end_at = convert_table_utf8($db->prefix.'bans', '_conv_bans', $old_charset, 'id', $start_at);
+
+ if ($end_at !== true)
+ $query_str = '?stage=conv_bans&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+ break;
+
+
+ // Convert categories
+ case 'conv_categories':
+ $query_str = '?stage=conv_censors&req_old_charset='.$old_charset;
+
+ echo sprintf($lang_update['Converting'], $lang_update['categories']).'<br />'."\n";
+
+ function _conv_categories($cur_item, $old_charset)
+ {
+ convert_to_utf8($cur_item['cat_name'], $old_charset);
+
+ return $cur_item;
+ }
+
+ convert_table_utf8($db->prefix.'categories', '_conv_categories', $old_charset, 'id');
+
+ break;
+
+
+ // Convert censor words
+ case 'conv_censors':
+ $query_str = '?stage=conv_config&req_old_charset='.$old_charset;
+
+ echo sprintf($lang_update['Converting'], $lang_update['censor words']).'<br />'."\n";
+
+ function _conv_censoring($cur_item, $old_charset)
+ {
+ convert_to_utf8($cur_item['search_for'], $old_charset);
+ convert_to_utf8($cur_item['replace_with'], $old_charset);
+
+ return $cur_item;
+ }
+
+ convert_table_utf8($db->prefix.'censoring', '_conv_censoring', $old_charset, 'id');
+
+ break;
+
+
+ // Convert config
+ case 'conv_config':
+ $query_str = '?stage=conv_forums&req_old_charset='.$old_charset;
+
+ echo sprintf($lang_update['Converting'], $lang_update['configuration']).'<br />'."\n";
+
+ function _conv_config($cur_item, $old_charset)
+ {
+ convert_to_utf8($cur_item['conf_value'], $old_charset);
+
+ return $cur_item;
+ }
+
+ convert_table_utf8($db->prefix.'config', '_conv_config', $old_charset, 'conf_name');
+
+ break;
+
+
+ // Convert forums
+ case 'conv_forums':
+ $query_str = '?stage=conv_perms&req_old_charset='.$old_charset;
+
+ echo sprintf($lang_update['Converting'], $lang_update['forums']).'<br />'."\n";
+
+ function _conv_forums($cur_item, $old_charset)
+ {
+ $moderators = ($cur_item['moderators'] != '') ? unserialize($cur_item['moderators']) : array();
+ $moderators_utf8 = array();
+ foreach ($moderators as $mod_username => $mod_user_id)
+ {
+ convert_to_utf8($mod_username, $old_charset);
+ $moderators_utf8[$mod_username] = $mod_user_id;
+ }
+
+ convert_to_utf8($cur_item['forum_name'], $old_charset);
+ convert_to_utf8($cur_item['forum_desc'], $old_charset);
+ convert_to_utf8($cur_item['last_poster'], $old_charset);
+
+ if (!empty($moderators_utf8))
+ $cur_item['moderators'] = serialize($moderators_utf8);
+
+ return $cur_item;
+ }
+
+ convert_table_utf8($db->prefix.'forums', '_conv_forums', $old_charset, 'id');
+
+ break;
+
+
+ // Convert forum permissions
+ case 'conv_perms':
+ $query_str = '?stage=conv_groups&req_old_charset='.$old_charset;
+
+ alter_table_utf8($db->prefix.'forum_perms');
+
+ break;
+
+
+ // Convert groups
+ case 'conv_groups':
+ $query_str = '?stage=conv_online&req_old_charset='.$old_charset;
+
+ echo sprintf($lang_update['Converting'], $lang_update['groups']).'<br />'."\n";
+
+ function _conv_groups($cur_item, $old_charset)
+ {
+ convert_to_utf8($cur_item['g_title'], $old_charset);
+ convert_to_utf8($cur_item['g_user_title'], $old_charset);
+
+ return $cur_item;
+ }
+
+ convert_table_utf8($db->prefix.'groups', '_conv_groups', $old_charset, 'g_id');
+
+ break;
+
+
+ // Convert online
+ case 'conv_online':
+ $query_str = '?stage=conv_posts&req_old_charset='.$old_charset;
+
+ // Truncate the table
+ $db->truncate_table('online') or error('Unable to empty online table', __FILE__, __LINE__, $db->error());
+
+ alter_table_utf8($db->prefix.'online');
+
+ break;
+
+
+ // Convert posts
+ case 'conv_posts':
+ $query_str = '?stage=conv_reports&req_old_charset='.$old_charset;
+
+ function _conv_posts($cur_item, $old_charset)
+ {
+ global $lang_update;
+
+ echo sprintf($lang_update['Converting item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
+
+ convert_to_utf8($cur_item['poster'], $old_charset);
+ convert_to_utf8($cur_item['message'], $old_charset);
+ convert_to_utf8($cur_item['edited_by'], $old_charset);
+
+ return $cur_item;
+ }
+
+ $end_at = convert_table_utf8($db->prefix.'posts', '_conv_posts', $old_charset, 'id', $start_at);
+
+ if ($end_at !== true)
+ $query_str = '?stage=conv_posts&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+ break;
+
+
+ // Convert reports
+ case 'conv_reports':
+ $query_str = '?stage=conv_search_cache&req_old_charset='.$old_charset;
+
+ function _conv_reports($cur_item, $old_charset)
+ {
+ global $lang_update;
+
+ echo sprintf($lang_update['Converting item'], $lang_update['report'], $cur_item['id']).'<br />'."\n";
+
+ convert_to_utf8($cur_item['message'], $old_charset);
+
+ return $cur_item;
+ }
+
+ $end_at = convert_table_utf8($db->prefix.'reports', '_conv_reports', $old_charset, 'id', $start_at);
+
+ if ($end_at !== true)
+ $query_str = '?stage=conv_reports&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+ break;
+
+
+ // Convert search cache
+ case 'conv_search_cache':
+ $query_str = '?stage=conv_search_matches&req_old_charset='.$old_charset;
+
+ // Truncate the table
+ $db->truncate_table('search_cache') or error('Unable to empty search cache table', __FILE__, __LINE__, $db->error());
+
+ alter_table_utf8($db->prefix.'search_cache');
+
+ break;
+
+
+ // Convert search matches
+ case 'conv_search_matches':
+ $query_str = '?stage=conv_search_words&req_old_charset='.$old_charset;
+
+ // Truncate the table
+ $db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+
+ alter_table_utf8($db->prefix.'search_matches');
+
+ break;
+
+
+ // Convert search words
+ case 'conv_search_words':
+ $query_str = '?stage=conv_subscriptions&req_old_charset='.$old_charset;
+
+ // Truncate the table
+ $db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
+
+ // Reset the sequence for the search words (not needed for SQLite)
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
+ break;
+
+ case 'pgsql';
+ $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+ break;
+ }
+
+ alter_table_utf8($db->prefix.'search_words');
+
+ break;
+
+
+ // Convert subscriptions
+ case 'conv_subscriptions':
+ $query_str = '?stage=conv_topics&req_old_charset='.$old_charset;
+
+ // By this stage we should have already renamed the subscription table
+ alter_table_utf8($db->prefix.'topic_subscriptions');
+ alter_table_utf8($db->prefix.'forum_subscriptions'); // This should actually already be utf8, but for consistency...
+
+ break;
+
+
+ // Convert topics
+ case 'conv_topics':
+ $query_str = '?stage=conv_users&req_old_charset='.$old_charset;
+
+ function _conv_topics($cur_item, $old_charset)
+ {
+ global $lang_update;
+
+ echo sprintf($lang_update['Converting item'], $lang_update['topic'], $cur_item['id']).'<br />'."\n";
+
+ convert_to_utf8($cur_item['poster'], $old_charset);
+ convert_to_utf8($cur_item['subject'], $old_charset);
+ convert_to_utf8($cur_item['last_poster'], $old_charset);
+
+ return $cur_item;
+ }
+
+ $end_at = convert_table_utf8($db->prefix.'topics', '_conv_topics', $old_charset, 'id', $start_at);
+
+ if ($end_at !== true)
+ $query_str = '?stage=conv_topics&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+ break;
+
+
+ // Convert users
+ case 'conv_users':
+ $query_str = '?stage=preparse_posts';
+
+ if ($start_at == 0)
+ $_SESSION['dupe_users'] = array();
+
+ function _conv_users($cur_item, $old_charset)
+ {
+ global $lang_update;
+
+ echo sprintf($lang_update['Converting item'], $lang_update['user'], $cur_item['id']).'<br />'."\n";
+
+ convert_to_utf8($cur_item['username'], $old_charset);
+ convert_to_utf8($cur_item['title'], $old_charset);
+ convert_to_utf8($cur_item['realname'], $old_charset);
+ convert_to_utf8($cur_item['location'], $old_charset);
+ convert_to_utf8($cur_item['signature'], $old_charset);
+ convert_to_utf8($cur_item['admin_note'], $old_charset);
+
+ return $cur_item;
+ }
+
+ function _error_users($cur_user)
+ {
+ $_SESSION['dupe_users'][$cur_user['id']] = $cur_user;
+ }
+
+ $end_at = convert_table_utf8($db->prefix.'users', '_conv_users', $old_charset, 'id', $start_at, '_error_users');
+
+ if ($end_at !== true)
+ $query_str = '?stage=conv_users&req_old_charset='.$old_charset.'&start_at='.$end_at;
+ else if (!empty($_SESSION['dupe_users']))
+ $query_str = '?stage=conv_users_dupe';
+
+ break;
+
+
+ // Handle any duplicate users which occured due to conversion
+ case 'conv_users_dupe':
+ $query_str = '?stage=preparse_posts';
+
+ if (!$mysql || empty($_SESSION['dupe_users']))
+ break;
+
+ if (isset($_POST['form_sent']))
+ {
+ $errors = array();
+
+ require PUN_ROOT.'include/email.php';
+
+ foreach ($_SESSION['dupe_users'] as $id => $cur_user)
+ {
+ $errors[$id] = array();
+
+ $username = pun_trim($_POST['dupe_users'][$id]);
+
+ if (pun_strlen($username) < 2)
+ $errors[$id][] = $lang_update['Username too short error'];
+ else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
+ $errors[$id][] = $lang_update['Username too long error'];
+ else if (!strcasecmp($username, 'Guest'))
+ $errors[$id][] = $lang_update['Username Guest reserved error'];
+ else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
+ $errors[$id][] = $lang_update['Username IP format error'];
+ else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
+ $errors[$id][] = $lang_update['Username bad characters error'];
+ else if (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)%i', $username))
+ $errors[$id][] = $lang_update['Username BBCode error'];
+
+ $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ $busy = $db->result($result);
+ $errors[$id][] = sprintf($lang_update['Username duplicate error'], pun_htmlspecialchars($busy));
+ }
+
+ if (empty($errors[$id]))
+ {
+ $old_username = $cur_user['username'];
+ $_SESSION['dupe_users'][$id]['username'] = $cur_user['username'] = $username;
+
+ $temp = array();
+ foreach ($cur_user as $idx => $value)
+ $temp[$idx] = is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'';
+
+ // Insert the renamed user
+ $db->query('INSERT INTO '.$db->prefix.'users('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or error('Unable to insert data to new table', __FILE__, __LINE__, $db->error());
+
+ // Renaming a user also affects a bunch of other stuff, lets fix that too...
+ $db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($username).'\' WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+
+ // TODO: The following must compare using collation utf8_bin otherwise we will accidently update posts/topics/etc belonging to both of the duplicate users, not just the one we renamed!
+ $db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($username).'\' WHERE edited_by=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($username).'\' WHERE poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'topics SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forums SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update forums', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'online SET ident=\''.$db->escape($username).'\' WHERE ident=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+ // If the user is a moderator or an administrator we have to update the moderator lists
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$cur_user['group_id']) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+ $group_mod = $db->result($result);
+
+ if ($cur_user['group_id'] == PUN_ADMIN || $group_mod == '1')
+ {
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ if (in_array($id, $cur_moderators))
+ {
+ unset($cur_moderators[$old_username]);
+ $cur_moderators[$username] = $id;
+ uksort($cur_moderators, 'utf8_strcasecmp');
+
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ }
+ }
+
+ // Email the user alerting them of the change
+ if (file_exists(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'))
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'));
+ else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'))
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'));
+ else
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/English/mail_templates/rename.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
+ $mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
+ $mail_message = str_replace('<old_username>', $old_username, $mail_message);
+ $mail_message = str_replace('<new_username>', $username, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($cur_user['email'], $mail_subject, $mail_message);
+
+ unset($_SESSION['dupe_users'][$id]);
+ }
+ }
+ }
+
+ if (!empty($_SESSION['dupe_users']))
+ {
+ $query_str = '';
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo $lang_update['Update'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="pundb_update" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div class="blockform">
+ <h2><span><?php echo $lang_update['Error converting users'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="db_update.php?stage=conv_users_dupe&amp;uid=<?php echo $uid ?>">
+ <input type="hidden" name="form_sent" value="1" />
+ <div class="inform">
+ <div class="forminfo">
+ <p style="font-size: 1.1em"><?php echo $lang_update['Error info 1'] ?></p>
+ <p style="font-size: 1.1em"><?php echo $lang_update['Error info 2'] ?></p>
+ </div>
+ </div>
+<?php
+
+ foreach ($_SESSION['dupe_users'] as $id => $cur_user)
+ {
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo pun_htmlspecialchars($cur_user['username']); ?></legend>
+ <div class="infldset">
+ <label class="required"><strong><?php echo $lang_update['New username'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="text" name="<?php echo 'dupe_users['.$id.']'; ?>" value="<?php if (isset($_POST['dupe_users'][$id])) echo pun_htmlspecialchars($_POST['dupe_users'][$id]); ?>" size="25" maxlength="25" /><br /></label>
+ </div>
+ </fieldset>
+<?php if (!empty($errors[$id])): ?> <div class="forminfo error-info">
+ <h3><?php echo $lang_update['Correct errors'] ?></h3>
+ <ul class="error-list">
+<?php
+
+foreach ($errors[$id] as $cur_error)
+ echo "\t\t\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+ </ul>
+ </div>
+<?php endif; ?> </div>
+<?php
+
+ }
+
+?>
+ <p class="buttons"><input type="submit" name="rename" value="<?php echo $lang_update['Rename users'] ?>" /></p>
+ </form>
+ </div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+ }
+
+ break;
+
+
+ // Preparse posts
+ case 'preparse_posts':
+ $query_str = '?stage=preparse_sigs';
+
+ // If we don't need to parse the posts, skip this stage
+ if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
+ break;
+
+ require PUN_ROOT.'include/parser.php';
+
+ // Fetch posts to process this cycle
+ $result = $db->query('SELECT id, message FROM '.$db->prefix.'posts WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+ $temp = array();
+ $end_at = 0;
+ while ($cur_item = $db->fetch_assoc($result))
+ {
+ echo sprintf($lang_update['Preparsing item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
+ $db->query('UPDATE '.$db->prefix.'posts SET message = \''.$db->escape(preparse_bbcode($cur_item['message'], $temp)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update post', __FILE__, __LINE__, $db->error());
+
+ $end_at = $cur_item['id'];
+ }
+
+ // Check if there is more work to do
+ if ($end_at > 0)
+ {
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result) > 0)
+ $query_str = '?stage=preparse_posts&start_at='.$end_at;
+ }
+
+ break;
+
+
+ // Preparse signatures
+ case 'preparse_sigs':
+ $query_str = '?stage=rebuild_idx';
+
+ // If we don't need to parse the sigs, skip this stage
+ if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
+ break;
+
+ require PUN_ROOT.'include/parser.php';
+
+ // Fetch users to process this cycle
+ $result = $db->query('SELECT id, signature FROM '.$db->prefix.'users WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE) or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
+
+ $temp = array();
+ $end_at = 0;
+ while ($cur_item = $db->fetch_assoc($result))
+ {
+ echo sprintf($lang_update['Preparsing item'], $lang_update['signature'], $cur_item['id']).'<br />'."\n";
+ $db->query('UPDATE '.$db->prefix.'users SET signature = \''.$db->escape(preparse_bbcode($cur_item['signature'], $temp, true)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+ $end_at = $cur_item['id'];
+ }
+
+ // Check if there is more work to do
+ if ($end_at > 0)
+ {
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result) > 0)
+ $query_str = '?stage=preparse_sigs&start_at='.$end_at;
+ }
+
+ break;
+
+
+ // Rebuild the search index
+ case 'rebuild_idx':
+ $query_str = '?stage=finish';
+
+ // If we don't need to update the search index, skip this stage
+ if (isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION)
+ break;
+
+ if ($start_at == 0)
+ {
+ // Truncate the tables just in-case we didn't already (if we are coming directly here without converting the tables)
+ $db->truncate_table('search_cache') or error('Unable to empty search cache table', __FILE__, __LINE__, $db->error());
+ $db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+ $db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
+
+ // Reset the sequence for the search words (not needed for SQLite)
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
+ break;
+
+ case 'pgsql';
+ $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+ break;
+ }
+ }
+
+ require PUN_ROOT.'include/search_idx.php';
+
+ // Fetch posts to process this cycle
+ $result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id > '.$start_at.' ORDER BY p.id ASC LIMIT '.PER_PAGE) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+ $end_at = 0;
+ while ($cur_item = $db->fetch_assoc($result))
+ {
+ echo sprintf($lang_update['Rebuilding index item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
+
+ if ($cur_item['id'] == $cur_item['first_post_id'])
+ update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
+ else
+ update_search_index('post', $cur_item['id'], $cur_item['message']);
+
+ $end_at = $cur_item['id'];
+ }
+
+ // Check if there is more work to do
+ if ($end_at > 0)
+ {
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result) > 0)
+ $query_str = '?stage=rebuild_idx&start_at='.$end_at;
+ }
+
+ break;
+
+
+ // Show results page
+ case 'finish':
+ // We update the version number
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO.'\' WHERE conf_name = \'o_cur_version\'') or error('Unable to update version', __FILE__, __LINE__, $db->error());
+
+ // And the database revision number
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_DB_REVISION.'\' WHERE conf_name = \'o_database_revision\'') or error('Unable to update database revision number', __FILE__, __LINE__, $db->error());
+
+ // And the search index revision number
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_SI_REVISION.'\' WHERE conf_name = \'o_searchindex_revision\'') or error('Unable to update search index revision number', __FILE__, __LINE__, $db->error());
+
+ // And the parser revision number
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_PARSER_REVISION.'\' WHERE conf_name = \'o_parser_revision\'') or error('Unable to update parser revision number', __FILE__, __LINE__, $db->error());
+
+ // Check the default language still exists!
+ if (!file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/common.php'))
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'English\' WHERE conf_name = \'o_default_lang\'') or error('Unable to update default language', __FILE__, __LINE__, $db->error());
+
+ // Check the default style still exists!
+ if (!file_exists(PUN_ROOT.'style/'.$pun_config['o_default_style'].'.css'))
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'Air\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style', __FILE__, __LINE__, $db->error());
+
+ // This feels like a good time to synchronize the forums
+ $result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum IDs', __FILE__, __LINE__, $db->error());
+
+ while ($row = $db->fetch_row($result))
+ update_forum($row[0]);
+
+ // Empty the PHP cache
+ forum_clear_cache();
+
+ // Delete the update lock file
+ @unlink(FORUM_CACHE_DIR.'db_update.lock');
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo $lang_update['Update'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="pundb_update" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div class="blockform">
+ <h2><span><?php echo $lang_update['Update'] ?></span></h2>
+ <div class="box">
+ <div class="fakeform">
+ <div class="inform">
+ <div class="forminfo">
+ <p style="font-size: 1.1em"><?php printf($lang_update['Successfully updated'], sprintf('<a href="index.php">%s</a>', $lang_update['go to index'])) ?></p>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+ break;
+}
+
+$db->end_transaction();
+$db->close();
+
+if ($query_str != '')
+ exit('<meta http-equiv="refresh" content="0;url=db_update.php'.$query_str.'&uid='.$uid.'" /><hr /><p>'.sprintf($lang_update['Automatic redirect failed'], '<a href="db_update.php'.$query_str.'&uid='.$uid.'">'.$lang_update['Click here'].'</a>').'</p>');
diff --git a/delete.php b/delete.php
new file mode 100644
index 0000000..e253bb4
--- /dev/null
+++ b/delete.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// Fetch some info about the post, the topic and the forum
+$result = $db->query('SELECT f.id AS fid, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.id AS tid, t.subject, t.first_post_id, t.closed, p.posted, p.poster, p.poster_id, p.message, p.hide_smilies FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$cur_post = $db->fetch_assoc($result);
+
+if ($pun_config['o_censoring'] == '1')
+ $cur_post['subject'] = censor_words($cur_post['subject']);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_post['moderators'] != '') ? unserialize($cur_post['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+$is_topic_post = ($id == $cur_post['first_post_id']) ? true : false;
+
+// Do we have permission to edit this post?
+if (($pun_user['g_delete_posts'] == '0' ||
+ ($pun_user['g_delete_topics'] == '0' && $is_topic_post) ||
+ $cur_post['poster_id'] != $pun_user['id'] ||
+ $cur_post['closed'] == '1') &&
+ !$is_admmod)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+if ($is_admmod && $pun_user['g_id'] != PUN_ADMIN && in_array($cur_post['poster_id'], get_admin_ids()))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the delete.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/delete.php';
+
+
+if (isset($_POST['delete']))
+{
+ // Make sure they got here from the site
+ confirm_referrer('delete.php');
+
+ require PUN_ROOT.'include/search_idx.php';
+
+ if ($is_topic_post)
+ {
+ // Delete the topic and all of its posts
+ delete_topic($cur_post['tid']);
+ update_forum($cur_post['fid']);
+
+ redirect('viewforum.php?id='.$cur_post['fid'], $lang_delete['Topic del redirect']);
+ }
+ else
+ {
+ // Delete just this one post
+ delete_post($id, $cur_post['tid']);
+ update_forum($cur_post['fid']);
+
+ // Redirect towards the previous post
+ $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$cur_post['tid'].' AND id < '.$id.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $post_id = $db->result($result);
+
+ redirect('viewtopic.php?pid='.$post_id.'#p'.$post_id, $lang_delete['Post del redirect']);
+ }
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_delete['Delete post']);
+define ('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+require PUN_ROOT.'include/parser.php';
+$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+?>
+<div class="linkst">
+ <div class="inbox">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $cur_post['fid'] ?>"><?php echo pun_htmlspecialchars($cur_post['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><a href="viewtopic.php?pid=<?php echo $id ?>#p<?php echo $id ?>"><?php echo pun_htmlspecialchars($cur_post['subject']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_delete['Delete post'] ?></strong></li>
+ </ul>
+ </div>
+</div>
+
+<div class="blockform">
+ <h2><span><?php echo $lang_delete['Delete post'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="delete.php?id=<?php echo $id ?>">
+ <div class="inform">
+ <div class="forminfo">
+ <h3><span><?php printf($is_topic_post ? $lang_delete['Topic by'] : $lang_delete['Reply by'], '<strong>'.pun_htmlspecialchars($cur_post['poster']).'</strong>', format_time($cur_post['posted'])) ?></span></h3>
+ <p><?php echo ($is_topic_post) ? '<strong>'.$lang_delete['Topic warning'].'</strong>' : '<strong>'.$lang_delete['Warning'].'</strong>' ?><br /><?php echo $lang_delete['Delete info'] ?></p>
+ </div>
+ </div>
+ <p class="buttons"><input type="submit" name="delete" value="<?php echo $lang_delete['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+
+<div id="postreview">
+ <div class="blockpost">
+ <div class="box">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postleft">
+ <dl>
+ <dt><strong><?php echo pun_htmlspecialchars($cur_post['poster']) ?></strong></dt>
+ <dd><span><?php echo format_time($cur_post['posted']) ?></span></dd>
+ </dl>
+ </div>
+ <div class="postright">
+ <div class="postmsg">
+ <?php echo $cur_post['message']."\n" ?>
+ </div>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ </div>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..f799bd6
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,30 @@
+version: "3.3"
+
+volumes:
+ db-socket:
+ db-data:
+
+services:
+ app:
+ build: php
+ working_dir: /app
+ command: php -S 0.0.0.0:${PORT:-8080} -t /app
+ user: http
+ ports:
+ - "${PORT:-8080}:${PORT:-8080}"
+ volumes:
+ - ../:/app
+ - db-socket:/run/mysqld
+ tmpfs:
+ - /app/cache
+ depends_on:
+ - db
+
+ db:
+ build: mariadb
+ command: mysqld
+ user: mysql
+ network_mode: none
+ volumes:
+ - db-data:/var/lib/mysql
+ - db-socket:/run/mysqld
diff --git a/docker/mariadb/Dockerfile b/docker/mariadb/Dockerfile
new file mode 100644
index 0000000..b11b9a0
--- /dev/null
+++ b/docker/mariadb/Dockerfile
@@ -0,0 +1,4 @@
+FROM archlinux/base
+
+RUN pacman -Syu --noconfirm mariadb
+RUN mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql --auth-root-authentication-method=normal
diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile
new file mode 100644
index 0000000..90d82e3
--- /dev/null
+++ b/docker/php/Dockerfile
@@ -0,0 +1,4 @@
+FROM archlinux/base
+
+RUN pacman -Syu --noconfirm php php-apcu php-apcu-bc
+ADD etc/ /etc
diff --git a/docker/php/etc/php/conf.d/extensions.ini b/docker/php/etc/php/conf.d/extensions.ini
new file mode 100644
index 0000000..fa6402f
--- /dev/null
+++ b/docker/php/etc/php/conf.d/extensions.ini
@@ -0,0 +1,4 @@
+[PHP]
+extension=mysqli.so
+extension=apcu.so
+extension=apc.so
diff --git a/docker/restart b/docker/restart
new file mode 100755
index 0000000..06c6aa5
--- /dev/null
+++ b/docker/restart
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+pushd $(dirname $BASH_SOURCE)
+
+./stop
+./start
diff --git a/docker/start b/docker/start
new file mode 100755
index 0000000..921b80f
--- /dev/null
+++ b/docker/start
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+pushd $(dirname $BASH_SOURCE)
+
+if [[ -n "$(docker-compose ps -q)" ]]; then
+ echo "Service already running" >&2
+ exit 1
+fi
+
+docker-compose up --build -d
+docker-compose exec db mysqladmin -uroot --wait=10 ping
+docker-compose exec db mysqladmin -uroot create fluxbb
diff --git a/docker/stop b/docker/stop
new file mode 100755
index 0000000..732c654
--- /dev/null
+++ b/docker/stop
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+pushd $(dirname $BASH_SOURCE)
+
+docker-compose down -v
diff --git a/edit.php b/edit.php
new file mode 100644
index 0000000..5aba54d
--- /dev/null
+++ b/edit.php
@@ -0,0 +1,293 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// Fetch some info about the post, the topic and the forum
+$result = $db->query('SELECT f.id AS fid, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.id AS tid, t.subject, t.posted, t.first_post_id, t.sticky, t.closed, p.poster, p.poster_id, p.message, p.hide_smilies FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$cur_post = $db->fetch_assoc($result);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_post['moderators'] != '') ? unserialize($cur_post['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+$can_edit_subject = $id == $cur_post['first_post_id'];
+
+if ($pun_config['o_censoring'] == '1')
+{
+ $cur_post['subject'] = censor_words($cur_post['subject']);
+ $cur_post['message'] = censor_words($cur_post['message']);
+}
+
+// Do we have permission to edit this post?
+if (($pun_user['g_edit_posts'] == '0' ||
+ $cur_post['poster_id'] != $pun_user['id'] ||
+ $cur_post['closed'] == '1') &&
+ !$is_admmod)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+if ($is_admmod && $pun_user['g_id'] != PUN_ADMIN && in_array($cur_post['poster_id'], get_admin_ids()))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the post.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+// Start with a clean slate
+$errors = array();
+
+
+if (isset($_POST['form_sent']))
+{
+ // Make sure they got here from the site
+ confirm_referrer('edit.php');
+
+ // If it's a topic it must contain a subject
+ if ($can_edit_subject)
+ {
+ $subject = pun_trim($_POST['req_subject']);
+
+ if ($pun_config['o_censoring'] == '1')
+ $censored_subject = pun_trim(censor_words($subject));
+
+ if ($subject == '')
+ $errors[] = $lang_post['No subject'];
+ else if ($pun_config['o_censoring'] == '1' && $censored_subject == '')
+ $errors[] = $lang_post['No subject after censoring'];
+ else if (pun_strlen($subject) > 70)
+ $errors[] = $lang_post['Too long subject'];
+ else if ($pun_config['p_subject_all_caps'] == '0' && is_all_uppercase($subject) && !$pun_user['is_admmod'])
+ $errors[] = $lang_post['All caps subject'];
+ else if ($pun_user['g_post_links'] != '1' && preg_match('%(?:h\s*t|f)\s*t\s*p\s*(?:s\s*)?:\s*/\s*/%', $subject))
+ $errors[] = $lang_common['BBCode error tag url not allowed'];
+ }
+
+ // Clean up message from POST
+ $message = pun_linebreaks(pun_trim($_POST['req_message']));
+
+ // Here we use strlen() not pun_strlen() as we want to limit the post to PUN_MAX_POSTSIZE bytes, not characters
+ if (strlen($message) > PUN_MAX_POSTSIZE)
+ $errors[] = sprintf($lang_post['Too long message'], forum_number_format(PUN_MAX_POSTSIZE));
+ else if ($pun_config['p_message_all_caps'] == '0' && is_all_uppercase($message) && !$pun_user['is_admmod'])
+ $errors[] = $lang_post['All caps message'];
+
+ // Validate BBCode syntax
+ if ($pun_config['p_message_bbcode'] == '1')
+ {
+ require PUN_ROOT.'include/parser.php';
+ $message = preparse_bbcode($message, $errors);
+ }
+
+ if (empty($errors))
+ {
+ if ($message == '')
+ $errors[] = $lang_post['No message'];
+ else if ($pun_config['o_censoring'] == '1')
+ {
+ // Censor message to see if that causes problems
+ $censored_message = pun_trim(censor_words($message));
+
+ if ($censored_message == '')
+ $errors[] = $lang_post['No message after censoring'];
+ }
+ }
+
+ $hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
+ $stick_topic = isset($_POST['stick_topic']) ? '1' : '0';
+ if (!$is_admmod)
+ $stick_topic = $cur_post['sticky'];
+
+ // Replace four-byte characters (MySQL cannot handle them)
+ $message = strip_bad_multibyte_chars($message);
+
+ // Did everything go according to plan?
+ if (empty($errors) && !isset($_POST['preview']))
+ {
+ $edited_sql = (!isset($_POST['silent']) || !$is_admmod) ? ', edited='.time().', edited_by=\''.$db->escape($pun_user['username']).'\'' : '';
+
+ require PUN_ROOT.'include/search_idx.php';
+
+ if ($can_edit_subject)
+ {
+ // Update the topic and any redirect topics
+ $db->query('UPDATE '.$db->prefix.'topics SET subject=\''.$db->escape($subject).'\', sticky='.$stick_topic.' WHERE id='.$cur_post['tid'].' OR moved_to='.$cur_post['tid']) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ // We changed the subject, so we need to take that into account when we update the search words
+ update_search_index('edit', $id, $message, $subject);
+ }
+ else
+ update_search_index('edit', $id, $message);
+
+ // Update the post
+ $db->query('UPDATE '.$db->prefix.'posts SET message=\''.$db->escape($message).'\', hide_smilies='.$hide_smilies.$edited_sql.' WHERE id='.$id) or error('Unable to update post', __FILE__, __LINE__, $db->error());
+
+ redirect('viewtopic.php?pid='.$id.'#p'.$id, $lang_post['Edit redirect']);
+ }
+}
+
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_post['Edit post']);
+$required_fields = array('req_subject' => $lang_common['Subject'], 'req_message' => $lang_common['Message']);
+$focus_element = array('edit', 'req_message');
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+$cur_index = 1;
+
+?>
+<div class="linkst">
+ <div class="inbox">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $cur_post['fid'] ?>"><?php echo pun_htmlspecialchars($cur_post['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><a href="viewtopic.php?id=<?php echo $cur_post['tid'] ?>"><?php echo pun_htmlspecialchars($cur_post['subject']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_post['Edit post'] ?></strong></li>
+ </ul>
+ </div>
+</div>
+
+<?php
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+ <h2><span><?php echo $lang_post['Post errors'] ?></span></h2>
+ <div class="box">
+ <div class="inbox error-info">
+ <p><?php echo $lang_post['Post errors info'] ?></p>
+ <ul class="error-list">
+<?php
+
+ foreach ($errors as $cur_error)
+ echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+else if (isset($_POST['preview']))
+{
+ require_once PUN_ROOT.'include/parser.php';
+ $preview_message = parse_message($message, $hide_smilies);
+
+?>
+<div id="postpreview" class="blockpost">
+ <h2><span><?php echo $lang_post['Post preview'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postright">
+ <div class="postmsg">
+ <?php echo $preview_message."\n" ?>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+
+?>
+<div id="editform" class="blockform">
+ <h2><span><?php echo $lang_post['Edit post'] ?></span></h2>
+ <div class="box">
+ <form id="edit" method="post" action="edit.php?id=<?php echo $id ?>&amp;action=edit" onsubmit="return process_form(this)">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_post['Edit post legend'] ?></legend>
+ <input type="hidden" name="form_sent" value="1" />
+ <div class="infldset txtarea">
+<?php if ($can_edit_subject): ?> <label class="required"><strong><?php echo $lang_common['Subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input class="longinput" type="text" name="req_subject" size="80" maxlength="70" tabindex="<?php echo $cur_index++ ?>" value="<?php echo pun_htmlspecialchars(isset($_POST['req_subject']) ? $_POST['req_subject'] : $cur_post['subject']) ?>" /><br /></label>
+<?php endif; ?> <label class="required"><strong><?php echo $lang_common['Message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <textarea name="req_message" rows="20" cols="95" tabindex="<?php echo $cur_index++ ?>"><?php echo pun_htmlspecialchars(isset($_POST['req_message']) ? $message : $cur_post['message']) ?></textarea><br /></label>
+ <ul class="bblinks">
+ <li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#url" onclick="window.open(this.href); return false;"><?php echo $lang_common['url tag'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1' && $pun_user['g_post_links'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1' && $pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ </ul>
+ </div>
+ </fieldset>
+<?php
+
+$checkboxes = array();
+if ($can_edit_subject && $is_admmod)
+{
+ if (isset($_POST['stick_topic']) || !isset($_POST['form_sent']) && $cur_post['sticky'] == '1')
+ $checkboxes[] = '<label><input type="checkbox" name="stick_topic" value="1" checked="checked" tabindex="'.($cur_index++).'" />'.$lang_common['Stick topic'].'<br /></label>';
+ else
+ $checkboxes[] = '<label><input type="checkbox" name="stick_topic" value="1" tabindex="'.($cur_index++).'" />'.$lang_common['Stick topic'].'<br /></label>';
+}
+
+if ($pun_config['o_smilies'] == '1')
+{
+ if (isset($_POST['hide_smilies']) || !isset($_POST['form_sent']) && $cur_post['hide_smilies'] == '1')
+ $checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" checked="checked" tabindex="'.($cur_index++).'" />'.$lang_post['Hide smilies'].'<br /></label>';
+ else
+ $checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'" />'.$lang_post['Hide smilies'].'<br /></label>';
+}
+
+if ($is_admmod)
+{
+ if (isset($_POST['silent']) || !isset($_POST['form_sent']))
+ $checkboxes[] = '<label><input type="checkbox" name="silent" value="1" tabindex="'.($cur_index++).'" checked="checked" />'.$lang_post['Silent edit'].'<br /></label>';
+ else
+ $checkboxes[] = '<label><input type="checkbox" name="silent" value="1" tabindex="'.($cur_index++).'" />'.$lang_post['Silent edit'].'<br /></label>';
+}
+
+if (!empty($checkboxes))
+{
+
+?>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_common['Options'] ?></legend>
+ <div class="infldset">
+ <div class="rbox">
+ <?php echo implode("\n\t\t\t\t\t\t\t", $checkboxes)."\n" ?>
+ </div>
+ </div>
+ </fieldset>
+<?php
+
+ }
+
+?>
+ </div>
+ <p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="s" /> <input type="submit" name="preview" value="<?php echo $lang_post['Preview'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="p" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/extern.php b/extern.php
new file mode 100644
index 0000000..d43574b
--- /dev/null
+++ b/extern.php
@@ -0,0 +1,547 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+/*-----------------------------------------------------------------------------
+
+ INSTRUCTIONS
+
+ This script is used to include information about your board from
+ pages outside the forums and to syndicate news about recent
+ discussions via RSS/Atom/XML. The script can display a list of
+ recent discussions, a list of active users or a collection of
+ general board statistics. The script can be called directly via
+ an URL, from a PHP include command or through the use of Server
+ Side Includes (SSI).
+
+ The scripts behaviour is controlled via variables supplied in the
+ URL to the script. The different variables are: action (what to
+ do), show (how many items to display), fid (the ID or IDs of
+ the forum(s) to poll for topics), nfid (the ID or IDs of forums
+ that should be excluded), tid (the ID of the topic from which to
+ display posts) and type (output as HTML or RSS). The only
+ mandatory variable is action. Possible/default values are:
+
+ action: feed - show most recent topics/posts (HTML or RSS)
+ online - show users online (HTML)
+ online_full - as above, but includes a full list (HTML)
+ stats - show board statistics (HTML)
+
+ type: rss - output as RSS 2.0
+ atom - output as Atom 1.0
+ xml - output as XML
+ html - output as HTML (<li>'s)
+
+ fid: One or more forum IDs (comma-separated). If ignored,
+ topics from all readable forums will be pulled.
+
+ nfid: One or more forum IDs (comma-separated) that are to be
+ excluded. E.g. the ID of a a test forum.
+
+ tid: A topic ID from which to show posts. If a tid is supplied,
+ fid and nfid are ignored.
+
+ show: Any integer value between 1 and 50. The default is 15.
+
+ order: last_post - show topics ordered by when they were last
+ posted in, giving information about the reply.
+ posted - show topics ordered by when they were first
+ posted, giving information about the original post.
+
+-----------------------------------------------------------------------------*/
+
+define('PUN_QUIET_VISIT', 1);
+
+if (!defined('PUN_ROOT'))
+ define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+// The length at which topic subjects will be truncated (for HTML output)
+if (!defined('FORUM_EXTERN_MAX_SUBJECT_LENGTH'))
+ define('FORUM_EXTERN_MAX_SUBJECT_LENGTH', 30);
+
+// If we're a guest and we've sent a username/pass, we can try to authenticate using those details
+if ($pun_user['is_guest'] && isset($_SERVER['PHP_AUTH_USER']))
+ authenticate_user($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
+
+if ($pun_user['g_read_board'] == '0')
+{
+ http_authenticate_user();
+ exit($lang_common['No view']);
+}
+
+$action = isset($_GET['action']) ? strtolower($_GET['action']) : 'feed';
+
+// Handle a couple old formats, from FluxBB 1.2
+switch ($action)
+{
+ case 'active':
+ $action = 'feed';
+ $_GET['order'] = 'last_post';
+ break;
+
+ case 'new':
+ $action = 'feed';
+ $_GET['order'] = 'posted';
+ break;
+}
+
+//
+// Sends the proper headers for Basic HTTP Authentication
+//
+function http_authenticate_user()
+{
+ global $pun_config, $pun_user;
+
+ if (!$pun_user['is_guest'])
+ return;
+
+ header('WWW-Authenticate: Basic realm="'.$pun_config['o_board_title'].' External Syndication"');
+ header('HTTP/1.0 401 Unauthorized');
+}
+
+
+//
+// Output $feed as RSS 2.0
+//
+function output_rss($feed)
+{
+ global $lang_common, $pun_config;
+
+ // Send XML/no cache headers
+ header('Content-Type: application/xml; charset=utf-8');
+ header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+
+ echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
+ echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">'."\n";
+ echo "\t".'<channel>'."\n";
+ echo "\t\t".'<atom:link href="'.pun_htmlspecialchars(get_current_url()).'" rel="self" type="application/rss+xml" />'."\n";
+ echo "\t\t".'<title><![CDATA['.escape_cdata($feed['title']).']]></title>'."\n";
+ echo "\t\t".'<link>'.pun_htmlspecialchars($feed['link']).'</link>'."\n";
+ echo "\t\t".'<description><![CDATA['.escape_cdata($feed['description']).']]></description>'."\n";
+ echo "\t\t".'<lastBuildDate>'.gmdate('r', count($feed['items']) ? $feed['items'][0]['pubdate'] : time()).'</lastBuildDate>'."\n";
+
+ if ($pun_config['o_show_version'] == '1')
+ echo "\t\t".'<generator>FluxBB '.$pun_config['o_cur_version'].'</generator>'."\n";
+ else
+ echo "\t\t".'<generator>FluxBB</generator>'."\n";
+
+ foreach ($feed['items'] as $item)
+ {
+ echo "\t\t".'<item>'."\n";
+ echo "\t\t\t".'<title><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
+ echo "\t\t\t".'<link>'.pun_htmlspecialchars($item['link']).'</link>'."\n";
+ echo "\t\t\t".'<description><![CDATA['.escape_cdata($item['description']).']]></description>'."\n";
+ echo "\t\t\t".'<author><![CDATA['.(isset($item['author']['email']) ? escape_cdata($item['author']['email']) : 'dummy@example.com').' ('.escape_cdata($item['author']['name']).')]]></author>'."\n";
+ echo "\t\t\t".'<pubDate>'.gmdate('r', $item['pubdate']).'</pubDate>'."\n";
+ echo "\t\t\t".'<guid>'.pun_htmlspecialchars($item['link']).'</guid>'."\n";
+
+ echo "\t\t".'</item>'."\n";
+ }
+
+ echo "\t".'</channel>'."\n";
+ echo '</rss>'."\n";
+}
+
+
+//
+// Output $feed as Atom 1.0
+//
+function output_atom($feed)
+{
+ global $lang_common, $pun_config;
+
+ // Send XML/no cache headers
+ header('Content-Type: application/atom+xml; charset=utf-8');
+ header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+
+ echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
+ echo '<feed xmlns="http://www.w3.org/2005/Atom">'."\n";
+
+ echo "\t".'<title type="html"><![CDATA['.escape_cdata($feed['title']).']]></title>'."\n";
+ echo "\t".'<link rel="self" href="'.pun_htmlspecialchars(get_current_url()).'"/>'."\n";
+ echo "\t".'<link href="'.pun_htmlspecialchars($feed['link']).'"/>'."\n";
+ echo "\t".'<updated>'.gmdate('Y-m-d\TH:i:s\Z', count($feed['items']) ? $feed['items'][0]['pubdate'] : time()).'</updated>'."\n";
+
+ if ($pun_config['o_show_version'] == '1')
+ echo "\t".'<generator version="'.$pun_config['o_cur_version'].'">FluxBB</generator>'."\n";
+ else
+ echo "\t".'<generator>FluxBB</generator>'."\n";
+
+ echo "\t".'<id>'.pun_htmlspecialchars($feed['link']).'</id>'."\n";
+
+ $content_tag = ($feed['type'] == 'posts') ? 'content' : 'summary';
+
+ foreach ($feed['items'] as $item)
+ {
+ echo "\t".'<entry>'."\n";
+ echo "\t\t".'<title type="html"><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
+ echo "\t\t".'<link rel="alternate" href="'.pun_htmlspecialchars($item['link']).'"/>'."\n";
+ echo "\t\t".'<'.$content_tag.' type="html"><![CDATA['.escape_cdata($item['description']).']]></'.$content_tag.'>'."\n";
+ echo "\t\t".'<author>'."\n";
+ echo "\t\t\t".'<name><![CDATA['.escape_cdata($item['author']['name']).']]></name>'."\n";
+
+ if (isset($item['author']['email']))
+ echo "\t\t\t".'<email><![CDATA['.escape_cdata($item['author']['email']).']]></email>'."\n";
+
+ if (isset($item['author']['uri']))
+ echo "\t\t\t".'<uri>'.pun_htmlspecialchars($item['author']['uri']).'</uri>'."\n";
+
+ echo "\t\t".'</author>'."\n";
+ echo "\t\t".'<updated>'.gmdate('Y-m-d\TH:i:s\Z', $item['pubdate']).'</updated>'."\n";
+
+ echo "\t\t".'<id>'.pun_htmlspecialchars($item['link']).'</id>'."\n";
+ echo "\t".'</entry>'."\n";
+ }
+
+ echo '</feed>'."\n";
+}
+
+
+//
+// Output $feed as XML
+//
+function output_xml($feed)
+{
+ global $lang_common, $pun_config;
+
+ // Send XML/no cache headers
+ header('Content-Type: application/xml; charset=utf-8');
+ header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+
+ echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
+ echo '<source>'."\n";
+ echo "\t".'<url>'.pun_htmlspecialchars($feed['link']).'</url>'."\n";
+
+ $forum_tag = ($feed['type'] == 'posts') ? 'post' : 'topic';
+
+ foreach ($feed['items'] as $item)
+ {
+ echo "\t".'<'.$forum_tag.' id="'.$item['id'].'">'."\n";
+
+ echo "\t\t".'<title><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
+ echo "\t\t".'<link>'.pun_htmlspecialchars($item['link']).'</link>'."\n";
+ echo "\t\t".'<content><![CDATA['.escape_cdata($item['description']).']]></content>'."\n";
+ echo "\t\t".'<author>'."\n";
+ echo "\t\t\t".'<name><![CDATA['.escape_cdata($item['author']['name']).']]></name>'."\n";
+
+ if (isset($item['author']['email']))
+ echo "\t\t\t".'<email><![CDATA['.escape_cdata($item['author']['email']).']]></email>'."\n";
+
+ if (isset($item['author']['uri']))
+ echo "\t\t\t".'<uri>'.pun_htmlspecialchars($item['author']['uri']).'</uri>'."\n";
+
+ echo "\t\t".'</author>'."\n";
+ echo "\t\t".'<posted>'.gmdate('r', $item['pubdate']).'</posted>'."\n";
+
+ echo "\t".'</'.$forum_tag.'>'."\n";
+ }
+
+ echo '</source>'."\n";
+}
+
+
+//
+// Output $feed as HTML (using <li> tags)
+//
+function output_html($feed)
+{
+
+ // Send the Content-type header in case the web server is setup to send something else
+ header('Content-type: text/html; charset=utf-8');
+ header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+
+ foreach ($feed['items'] as $item)
+ {
+ if (utf8_strlen($item['title']) > FORUM_EXTERN_MAX_SUBJECT_LENGTH)
+ $subject_truncated = pun_htmlspecialchars(pun_trim(utf8_substr($item['title'], 0, (FORUM_EXTERN_MAX_SUBJECT_LENGTH - 5)))).' …';
+ else
+ $subject_truncated = pun_htmlspecialchars($item['title']);
+
+ echo '<li><a href="'.pun_htmlspecialchars($item['link']).'" title="'.pun_htmlspecialchars($item['title']).'">'.$subject_truncated.'</a></li>'."\n";
+ }
+}
+
+// Show recent discussions
+if ($action == 'feed')
+{
+ require PUN_ROOT.'include/parser.php';
+
+ // Determine what type of feed to output
+ $type = isset($_GET['type']) ? strtolower($_GET['type']) : 'html';
+ if (!in_array($type, array('html', 'rss', 'atom', 'xml')))
+ $type = 'html';
+
+ $show = isset($_GET['show']) ? intval($_GET['show']) : 15;
+ if ($show < 1 || $show > 50)
+ $show = 15;
+
+ // Was a topic ID supplied?
+ if (isset($_GET['tid']))
+ {
+ $tid = intval($_GET['tid']);
+
+ // Fetch topic subject
+ $result = $db->query('SELECT t.subject, t.first_post_id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.moved_to IS NULL AND t.id='.$tid) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ {
+ http_authenticate_user();
+ exit($lang_common['Bad request']);
+ }
+
+ $cur_topic = $db->fetch_assoc($result);
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+ // Setup the feed
+ $feed = array(
+ 'title' => $pun_config['o_board_title'].$lang_common['Title separator'].$cur_topic['subject'],
+ 'link' => get_base_url(true).'/viewtopic.php?id='.$tid,
+ 'description' => sprintf($lang_common['RSS description topic'], $cur_topic['subject']),
+ 'items' => array(),
+ 'type' => 'posts'
+ );
+
+ // Fetch $show posts
+ $result = $db->query('SELECT p.id, p.poster, p.message, p.hide_smilies, p.posted, p.poster_id, u.email_setting, u.email, p.poster_email FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id WHERE p.topic_id='.$tid.' ORDER BY p.posted DESC LIMIT '.$show) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ while ($cur_post = $db->fetch_assoc($result))
+ {
+ $cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+ $item = array(
+ 'id' => $cur_post['id'],
+ 'title' => $cur_topic['first_post_id'] == $cur_post['id'] ? $cur_topic['subject'] : $lang_common['RSS reply'].$cur_topic['subject'],
+ 'link' => get_base_url(true).'/viewtopic.php?pid='.$cur_post['id'].'#p'.$cur_post['id'],
+ 'description' => $cur_post['message'],
+ 'author' => array(
+ 'name' => $cur_post['poster'],
+ ),
+ 'pubdate' => $cur_post['posted']
+ );
+
+ if ($cur_post['poster_id'] > 1)
+ {
+ if ($cur_post['email_setting'] == '0' && !$pun_user['is_guest'])
+ $item['author']['email'] = $cur_post['email'];
+
+ $item['author']['uri'] = get_base_url(true).'/profile.php?id='.$cur_post['poster_id'];
+ }
+ else if ($cur_post['poster_email'] != '' && !$pun_user['is_guest'])
+ $item['author']['email'] = $cur_post['poster_email'];
+
+ $feed['items'][] = $item;
+ }
+
+ $output_func = 'output_'.$type;
+ $output_func($feed);
+ }
+ else
+ {
+ $order_posted = isset($_GET['order']) && strtolower($_GET['order']) == 'posted';
+ $forum_name = '';
+ $forum_sql = '';
+
+ // Were any forum IDs supplied?
+ if (isset($_GET['fid']) && is_scalar($_GET['fid']) && $_GET['fid'] != '')
+ {
+ $fids = explode(',', pun_trim($_GET['fid']));
+ $fids = array_map('intval', $fids);
+
+ if (!empty($fids))
+ $forum_sql .= ' AND t.forum_id IN('.implode(',', $fids).')';
+
+ if (count($fids) == 1)
+ {
+ // Fetch forum name
+ $result = $db->query('SELECT f.forum_name FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fids[0]) or error('Unable to fetch forum name', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ $forum_name = $lang_common['Title separator'].$db->result($result);
+ }
+ }
+
+ // Any forum IDs to exclude?
+ if (isset($_GET['nfid']) && is_scalar($_GET['nfid']) && $_GET['nfid'] != '')
+ {
+ $nfids = explode(',', pun_trim($_GET['nfid']));
+ $nfids = array_map('intval', $nfids);
+
+ if (!empty($nfids))
+ $forum_sql .= ' AND t.forum_id NOT IN('.implode(',', $nfids).')';
+ }
+
+ // Only attempt to cache if caching is enabled and we have all or a single forum
+ if ($pun_config['o_feed_ttl'] > 0 && ($forum_sql == '' || ($forum_name != '' && !isset($_GET['nfid']))))
+ $cache_id = 'feed'.sha1($pun_user['g_id'].'|'.$lang_common['lang_identifier'].'|'.($order_posted ? '1' : '0').($forum_name == '' ? '' : '|'.$fids[0]));
+
+ // Load cached feed
+ if (isset($cache_id) && file_exists(FORUM_CACHE_DIR.'cache_'.$cache_id.'.php'))
+ include FORUM_CACHE_DIR.'cache_'.$cache_id.'.php';
+
+ $now = time();
+ if (!isset($feed) || $cache_expire < $now)
+ {
+ // Setup the feed
+ $feed = array(
+ 'title' => $pun_config['o_board_title'].$forum_name,
+ 'link' => '/index.php',
+ 'description' => sprintf($lang_common['RSS description'], $pun_config['o_board_title']),
+ 'items' => array(),
+ 'type' => 'topics'
+ );
+
+ // Fetch $show topics
+ $result = $db->query('SELECT t.id, t.poster, t.subject, t.posted, t.last_post, t.last_poster, p.message, p.hide_smilies, u.email_setting, u.email, p.poster_id, p.poster_email FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON p.id='.($order_posted ? 't.first_post_id' : 't.last_post_id').' INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.moved_to IS NULL'.$forum_sql.' ORDER BY '.($order_posted ? 't.posted' : 't.last_post').' DESC LIMIT '.(isset($cache_id) ? 50 : $show)) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+ while ($cur_topic = $db->fetch_assoc($result))
+ {
+ if ($pun_config['o_censoring'] == '1')
+ $cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+ $cur_topic['message'] = parse_message($cur_topic['message'], $cur_topic['hide_smilies']);
+
+ $item = array(
+ 'id' => $cur_topic['id'],
+ 'title' => $cur_topic['subject'],
+ 'link' => '/viewtopic.php?id='.$cur_topic['id'].($order_posted ? '' : '&action=new'),
+ 'description' => $cur_topic['message'],
+ 'author' => array(
+ 'name' => $order_posted ? $cur_topic['poster'] : $cur_topic['last_poster']
+ ),
+ 'pubdate' => $order_posted ? $cur_topic['posted'] : $cur_topic['last_post']
+ );
+
+ if ($cur_topic['poster_id'] > 1)
+ {
+ if ($cur_topic['email_setting'] == '0' && !$pun_user['is_guest'])
+ $item['author']['email'] = $cur_topic['email'];
+
+ $item['author']['uri'] = '/profile.php?id='.$cur_topic['poster_id'];
+ }
+ else if ($cur_topic['poster_email'] != '' && !$pun_user['is_guest'])
+ $item['author']['email'] = $cur_topic['poster_email'];
+
+ $feed['items'][] = $item;
+ }
+
+ // Output feed as PHP code
+ if (isset($cache_id))
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ $content = '<?php'."\n\n".'$feed = '.var_export($feed, true).';'."\n\n".'$cache_expire = '.($now + ($pun_config['o_feed_ttl'] * 60)).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_'.$cache_id.'.php', $content);
+ }
+ }
+
+ // If we only want to show a few items but due to caching we have too many
+ if (count($feed['items']) > $show)
+ $feed['items'] = array_slice($feed['items'], 0, $show);
+
+ // Prepend the current base URL onto some links. Done after caching to handle http/https correctly
+ $feed['link'] = get_base_url(true).$feed['link'];
+
+ foreach ($feed['items'] as $key => $item)
+ {
+ $feed['items'][$key]['link'] = get_base_url(true).$item['link'];
+
+ if (isset($item['author']['uri']))
+ $feed['items'][$key]['author']['uri'] = get_base_url(true).$item['author']['uri'];
+ }
+
+ $output_func = 'output_'.$type;
+ $output_func($feed);
+ }
+
+ exit;
+}
+
+// Show users online
+else if ($action == 'online' || $action == 'online_full')
+{
+ // Load the index.php language file
+ require PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/index.php';
+
+ // Fetch users online info and generate strings for output
+ $num_guests = $num_users = 0;
+ $users = array();
+
+ $result = $db->query('SELECT user_id, ident FROM '.$db->prefix.'online WHERE idle=0 ORDER BY ident', true) or error('Unable to fetch online list', __FILE__, __LINE__, $db->error());
+
+ while ($pun_user_online = $db->fetch_assoc($result))
+ {
+ if ($pun_user_online['user_id'] > 1)
+ {
+ $users[] = ($pun_user['g_view_users'] == '1') ? '<a href="'.pun_htmlspecialchars(get_base_url(true)).'/profile.php?id='.$pun_user_online['user_id'].'">'.pun_htmlspecialchars($pun_user_online['ident']).'</a>' : pun_htmlspecialchars($pun_user_online['ident']);
+ ++$num_users;
+ }
+ else
+ ++$num_guests;
+ }
+
+ // Send the Content-type header in case the web server is setup to send something else
+ header('Content-type: text/html; charset=utf-8');
+ header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+
+ echo sprintf($lang_index['Guests online'], forum_number_format($num_guests)).'<br />'."\n";
+
+ if ($action == 'online_full' && !empty($users))
+ echo sprintf($lang_index['Users online'], implode(', ', $users)).'<br />'."\n";
+ else
+ echo sprintf($lang_index['Users online'], forum_number_format($num_users)).'<br />'."\n";
+
+ exit;
+}
+
+// Show board statistics
+else if ($action == 'stats')
+{
+ // Load the index.php language file
+ require PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/index.php';
+
+ // Collect some statistics from the database
+ if (file_exists(FORUM_CACHE_DIR.'cache_users_info.php'))
+ include FORUM_CACHE_DIR.'cache_users_info.php';
+
+ if (!defined('PUN_USERS_INFO_LOADED'))
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+ require FORUM_CACHE_DIR.'cache_users_info.php';
+ }
+
+ $result = $db->query('SELECT SUM(num_topics), SUM(num_posts) FROM '.$db->prefix.'forums') or error('Unable to fetch topic/post count', __FILE__, __LINE__, $db->error());
+ list($stats['total_topics'], $stats['total_posts']) = $db->fetch_row($result);
+
+ // Send the Content-type header in case the web server is setup to send something else
+ header('Content-type: text/html; charset=utf-8');
+ header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+
+ echo sprintf($lang_index['No of users'], forum_number_format($stats['total_users'])).'<br />'."\n";
+ echo sprintf($lang_index['Newest user'], (($pun_user['g_view_users'] == '1') ? '<a href="'.pun_htmlspecialchars(get_base_url(true)).'/profile.php?id='.$stats['last_user']['id'].'">'.pun_htmlspecialchars($stats['last_user']['username']).'</a>' : pun_htmlspecialchars($stats['last_user']['username']))).'<br />'."\n";
+ echo sprintf($lang_index['No of topics'], forum_number_format($stats['total_topics'])).'<br />'."\n";
+ echo sprintf($lang_index['No of posts'], forum_number_format($stats['total_posts'])).'<br />'."\n";
+
+ exit;
+}
+
+// If we end up here, the script was called with some wacky parameters
+exit($lang_common['Bad request']);
diff --git a/favicon.ico b/favicon.ico
new file mode 120000
index 0000000..179ce47
--- /dev/null
+++ b/favicon.ico
@@ -0,0 +1 @@
+style/ArchLinux/favicon.ico \ No newline at end of file
diff --git a/footer.php b/footer.php
new file mode 100644
index 0000000..ad666cf
--- /dev/null
+++ b/footer.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+$tpl_temp = trim(ob_get_contents());
+$tpl_main = str_replace('<pun_main>', $tpl_temp, $tpl_main);
+ob_end_clean();
+// END SUBST - <pun_main>
+
+
+// START SUBST - <pun_footer>
+ob_start();
+
+?>
+<div id="brdfooter" class="block">
+ <h2><span><?php echo $lang_common['Board footer'] ?></span></h2>
+ <div class="box">
+<?php
+
+if (isset($footer_style) && ($footer_style == 'viewforum' || $footer_style == 'viewtopic') && $is_admmod)
+{
+ echo "\t\t".'<div id="modcontrols" class="inbox">'."\n";
+
+ $token_url = '&amp;csrf_token='.pun_csrf_token();
+
+ if ($footer_style == 'viewforum')
+ {
+ echo "\t\t\t".'<dl>'."\n";
+ echo "\t\t\t\t".'<dt><strong>'.$lang_forum['Mod controls'].'</strong></dt>'."\n";
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;p='.$p.'">'.$lang_common['Moderate forum'].'</a></span></dd>'."\n";
+ echo "\t\t\t".'</dl>'."\n";
+ }
+ else if ($footer_style == 'viewtopic')
+ {
+ echo "\t\t\t".'<dl>'."\n";
+ echo "\t\t\t\t".'<dt><strong>'.$lang_topic['Mod controls'].'</strong></dt>'."\n";
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;tid='.$id.'&amp;p='.$p.'">'.$lang_common['Moderate topic'].'</a>'.($num_pages > 1 ? ' (<a href="moderate.php?fid='.$forum_id.'&amp;tid='.$id.'&amp;action=all">'.$lang_common['All'].'</a>)' : '').'</span></dd>'."\n";
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;move_topics='.$id.'">'.$lang_common['Move topic'].'</a></span></dd>'."\n";
+
+ if ($cur_topic['closed'] == '1')
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;open='.$id.$token_url.'">'.$lang_common['Open topic'].'</a></span></dd>'."\n";
+ else
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;close='.$id.$token_url.'">'.$lang_common['Close topic'].'</a></span></dd>'."\n";
+
+ if ($cur_topic['sticky'] == '1')
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;unstick='.$id.$token_url.'">'.$lang_common['Unstick topic'].'</a></span></dd>'."\n";
+ else
+ echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&amp;stick='.$id.$token_url.'">'.$lang_common['Stick topic'].'</a></span></dd>'."\n";
+
+ echo "\t\t\t".'</dl>'."\n";
+ }
+
+ echo "\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>'."\n";
+}
+
+?>
+ <div id="brdfooternav" class="inbox">
+<?php
+
+echo "\t\t\t".'<div class="conl">'."\n";
+
+// Display the "Jump to" drop list
+if ($pun_config['o_quickjump'] == '1')
+{
+ // Load cached quick jump
+ if (file_exists(FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php'))
+ include FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
+
+ if (!defined('PUN_QJ_LOADED'))
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_quickjump_cache($pun_user['g_id']);
+ require FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
+ }
+}
+
+echo "\t\t\t".'</div>'."\n";
+
+?>
+ <div class="conr">
+<?php
+
+// If no footer style has been specified, we use the default (only copyright/debug info)
+$footer_style = isset($footer_style) ? $footer_style : NULL;
+
+if ($footer_style == 'index')
+{
+ if ($pun_config['o_feed_type'] == '1')
+ echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&amp;type=rss">'.$lang_common['RSS active topics feed'].'</a></span></p>'."\n";
+ else if ($pun_config['o_feed_type'] == '2')
+ echo "\t\t\t\t".'<p id="feedlinks"><span class="atom"><a href="extern.php?action=feed&amp;type=atom">'.$lang_common['Atom active topics feed'].'</a></span></p>'."\n";
+}
+else if ($footer_style == 'viewforum')
+{
+ if ($pun_config['o_feed_type'] == '1')
+ echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&amp;fid='.$forum_id.'&amp;type=rss">'.$lang_common['RSS forum feed'].'</a></span></p>'."\n";
+ else if ($pun_config['o_feed_type'] == '2')
+ echo "\t\t\t\t".'<p id="feedlinks"><span class="atom"><a href="extern.php?action=feed&amp;fid='.$forum_id.'&amp;type=atom">'.$lang_common['Atom forum feed'].'</a></span></p>'."\n";
+}
+else if ($footer_style == 'viewtopic')
+{
+ if ($pun_config['o_feed_type'] == '1')
+ echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&amp;tid='.$id.'&amp;type=rss">'.$lang_common['RSS topic feed'].'</a></span></p>'."\n";
+ else if ($pun_config['o_feed_type'] == '2')
+ echo "\t\t\t\t".'<p id="feedlinks"><span class="atom"><a href="extern.php?action=feed&amp;tid='.$id.'&amp;type=atom">'.$lang_common['Atom topic feed'].'</a></span></p>'."\n";
+}
+
+?>
+ <p id="poweredby"><?php printf($lang_common['Powered by'], '<a href="http://fluxbb.org/">FluxBB</a>'.(($pun_config['o_show_version'] == '1') ? ' '.$pun_config['o_cur_version'] : '')) ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ </div>
+</div>
+<?php
+
+// Display debug info (if enabled/defined)
+if (defined('PUN_DEBUG'))
+{
+ echo '<p id="debugtime">[ ';
+
+ // Calculate script generation time
+ $time_diff = sprintf('%.3f', get_microtime() - $pun_start);
+ echo sprintf($lang_common['Querytime'], $time_diff, $db->get_num_queries());
+
+ if (function_exists('memory_get_usage'))
+ {
+ echo ' - '.sprintf($lang_common['Memory usage'], file_size(memory_get_usage()));
+
+ if (function_exists('memory_get_peak_usage'))
+ echo ' '.sprintf($lang_common['Peak usage'], file_size(memory_get_peak_usage()));
+ }
+
+ echo ' ]</p>'."\n";
+}
+
+
+// End the transaction
+$db->end_transaction();
+
+// Display executed queries (if enabled)
+if (defined('PUN_SHOW_QUERIES'))
+ display_saved_queries();
+
+$tpl_temp = trim(ob_get_contents());
+$tpl_main = str_replace('<pun_footer>', $tpl_temp, $tpl_main);
+ob_end_clean();
+// END SUBST - <pun_footer>
+
+
+// Close the db connection (and free up any result data)
+$db->close();
+
+// Spit out the page
+exit($tpl_main);
diff --git a/header.php b/header.php
new file mode 100644
index 0000000..b579ada
--- /dev/null
+++ b/header.php
@@ -0,0 +1,332 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+// Send no-cache headers
+header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+header('Cache-Control: post-check=0, pre-check=0', false);
+header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+// Send the Content-type header in case the web server is setup to send something else
+header('Content-type: text/html; charset=utf-8');
+
+// Prevent site from being embedded in a frame unless FORUM_FRAME_OPTIONS is set
+// to a valid X-Frame-Options header value or false
+if (defined('FORUM_FRAME_OPTIONS'))
+{
+ if (preg_match('/^(?:allow-from|deny|sameorigin)/i', FORUM_FRAME_OPTIONS))
+ header('X-Frame-Options: '.FORUM_FRAME_OPTIONS);
+}
+else
+ header('X-Frame-Options: deny');
+
+// Load the template
+if (defined('PUN_ADMIN_CONSOLE'))
+ $tpl_file = 'admin.tpl';
+else if (defined('PUN_HELP'))
+ $tpl_file = 'help.tpl';
+else
+ $tpl_file = 'main.tpl';
+
+if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/'.$tpl_file))
+{
+ $tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/'.$tpl_file;
+ $tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
+}
+else
+{
+ $tpl_file = PUN_ROOT.'include/template/'.$tpl_file;
+ $tpl_inc_dir = PUN_ROOT.'include/user/';
+}
+
+$tpl_main = file_get_contents($tpl_file);
+
+// START SUBST - <pun_include "*">
+preg_match_all('%<pun_include "([^"]+)">%i', $tpl_main, $pun_includes, PREG_SET_ORDER);
+
+foreach ($pun_includes as $cur_include)
+{
+ ob_start();
+
+ $file_info = pathinfo($cur_include[1]);
+
+ if (!in_array($file_info['extension'], array('php', 'php4', 'php5', 'inc', 'html', 'txt'))) // Allow some extensions
+ error(sprintf($lang_common['Pun include extension'], pun_htmlspecialchars($cur_include[0]), basename($tpl_file), pun_htmlspecialchars($file_info['extension'])));
+
+ if (strpos($file_info['dirname'], '..') !== false) // Don't allow directory traversal
+ error(sprintf($lang_common['Pun include directory'], pun_htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+ // Allow for overriding user includes, too.
+ if (file_exists($tpl_inc_dir.$cur_include[1]))
+ require $tpl_inc_dir.$cur_include[1];
+ else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1]))
+ require PUN_ROOT.'include/user/'.$cur_include[1];
+ else
+ error(sprintf($lang_common['Pun include error'], pun_htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+ $tpl_temp = ob_get_contents();
+ $tpl_main = str_replace($cur_include[0], $tpl_temp, $tpl_main);
+ ob_end_clean();
+}
+// END SUBST - <pun_include "*">
+
+
+// START SUBST - <pun_language>
+$tpl_main = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_main);
+// END SUBST - <pun_language>
+
+
+// START SUBST - <pun_content_direction>
+$tpl_main = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_main);
+// END SUBST - <pun_content_direction>
+
+
+// START SUBST - <pun_head>
+ob_start();
+
+// Define $p if it's not set to avoid a PHP notice
+$p = isset($p) ? $p : null;
+
+// Is this a page that we want search index spiders to index?
+if (!defined('PUN_ALLOW_INDEX'))
+ echo '<meta name="ROBOTS" content="NOINDEX, FOLLOW" />'."\n";
+
+?>
+<title><?php echo generate_page_title($page_title, $p) ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
+<?php
+
+if (defined('PUN_ADMIN_CONSOLE'))
+{
+ if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/base_admin.css'))
+ echo '<link rel="stylesheet" type="text/css" href="style/'.$pun_user['style'].'/base_admin.css" />'."\n";
+ else
+ echo '<link rel="stylesheet" type="text/css" href="style/imports/base_admin.css" />'."\n";
+}
+
+if (isset($required_fields))
+{
+ // Output JavaScript to validate form (make sure required fields are filled out)
+
+?>
+<script type="text/javascript">
+/* <![CDATA[ */
+function process_form(the_form)
+{
+ var required_fields = {
+<?php
+ // Output a JavaScript object with localised field names
+ $tpl_temp = count($required_fields);
+ foreach ($required_fields as $elem_orig => $elem_trans)
+ {
+ echo "\t\t\"".$elem_orig.'": "'.addslashes(str_replace('&#160;', ' ', $elem_trans));
+ if (--$tpl_temp) echo "\",\n";
+ else echo "\"\n\t};\n";
+ }
+?>
+ if (document.all || document.getElementById)
+ {
+ for (var i = 0; i < the_form.length; ++i)
+ {
+ var elem = the_form.elements[i];
+ if (elem.name && required_fields[elem.name] && !elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
+ {
+ alert('"' + required_fields[elem.name] + '" <?php echo $lang_common['required field'] ?>');
+ elem.focus();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+/* ]]> */
+</script>
+<?php
+
+}
+
+if (!empty($page_head))
+ echo implode("\n", $page_head)."\n";
+
+$tpl_temp = trim(ob_get_contents());
+$tpl_main = str_replace('<pun_head>', $tpl_temp, $tpl_main);
+ob_end_clean();
+// END SUBST - <pun_head>
+
+
+// START SUBST - <body>
+if (isset($focus_element))
+{
+ $tpl_main = str_replace('<body onload="', '<body onload="document.getElementById(\''.$focus_element[0].'\').elements[\''.$focus_element[1].'\'].focus();', $tpl_main);
+ $tpl_main = str_replace('<body>', '<body onload="document.getElementById(\''.$focus_element[0].'\').elements[\''.$focus_element[1].'\'].focus()">', $tpl_main);
+}
+// END SUBST - <body>
+
+
+// START SUBST - <pun_page>
+$tpl_main = str_replace('<pun_page>', htmlspecialchars(basename($_SERVER['SCRIPT_NAME'], '.php')), $tpl_main);
+// END SUBST - <pun_page>
+
+
+// START SUBST - <pun_title>
+$tpl_main = str_replace('<pun_title>', '<h1><a href="index.php">'.pun_htmlspecialchars($pun_config['o_board_title']).'</a></h1>', $tpl_main);
+// END SUBST - <pun_title>
+
+
+// START SUBST - <pun_desc>
+$tpl_main = str_replace('<pun_desc>', '<div id="brddesc">'.$pun_config['o_board_desc'].'</div>', $tpl_main);
+// END SUBST - <pun_desc>
+
+
+// START SUBST - <pun_navlinks>
+$links = array();
+
+// Index should always be displayed
+$links[] = '<li id="navindex"'.((PUN_ACTIVE_PAGE == 'index') ? ' class="isactive"' : '').'><a href="index.php">'.$lang_common['Index'].'</a></li>';
+
+if ($pun_user['g_read_board'] == '1' && $pun_user['g_view_users'] == '1')
+ $links[] = '<li id="navuserlist"'.((PUN_ACTIVE_PAGE == 'userlist') ? ' class="isactive"' : '').'><a href="userlist.php">'.$lang_common['User list'].'</a></li>';
+
+if ($pun_config['o_rules'] == '1' && (!$pun_user['is_guest'] || $pun_user['g_read_board'] == '1' || $pun_config['o_regs_allow'] == '1'))
+ $links[] = '<li id="navrules"'.((PUN_ACTIVE_PAGE == 'rules') ? ' class="isactive"' : '').'><a href="misc.php?action=rules">'.$lang_common['Rules'].'</a></li>';
+
+if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+ $links[] = '<li id="navsearch"'.((PUN_ACTIVE_PAGE == 'search') ? ' class="isactive"' : '').'><a href="search.php">'.$lang_common['Search'].'</a></li>';
+
+if ($pun_user['is_guest'])
+{
+ $links[] = '<li id="navregister"'.((PUN_ACTIVE_PAGE == 'register') ? ' class="isactive"' : '').'><a href="register.php">'.$lang_common['Register'].'</a></li>';
+ $links[] = '<li id="navlogin"'.((PUN_ACTIVE_PAGE == 'login') ? ' class="isactive"' : '').'><a href="login.php">'.$lang_common['Login'].'</a></li>';
+}
+else
+{
+ $links[] = '<li id="navprofile"'.((PUN_ACTIVE_PAGE == 'profile') ? ' class="isactive"' : '').'><a href="profile.php?id='.$pun_user['id'].'">'.$lang_common['Profile'].'</a></li>';
+
+ if ($pun_user['is_admmod'])
+ $links[] = '<li id="navadmin"'.((PUN_ACTIVE_PAGE == 'admin') ? ' class="isactive"' : '').'><a href="admin_index.php">'.$lang_common['Admin'].'</a></li>';
+
+ $links[] = '<li id="navlogout"><a href="login.php?action=out&amp;id='.$pun_user['id'].'&amp;csrf_token='.pun_csrf_token().'">'.$lang_common['Logout'].'</a></li>';
+}
+
+// Are there any additional navlinks we should insert into the array before imploding it?
+if ($pun_user['g_read_board'] == '1' && $pun_config['o_additional_navlinks'] != '')
+{
+ if (preg_match_all('%([0-9]+)\s*=\s*(.*?)\n%s', $pun_config['o_additional_navlinks']."\n", $extra_links))
+ {
+ // Insert any additional links into the $links array (at the correct index)
+ $num_links = count($extra_links[1]);
+ for ($i = 0; $i < $num_links; ++$i)
+ array_splice($links, $extra_links[1][$i], 0, array('<li id="navextra'.($i + 1).'">'.$extra_links[2][$i].'</li>'));
+ }
+}
+
+$tpl_temp = '<div id="brdmenu" class="inbox">'."\n\t\t\t".'<ul>'."\n\t\t\t\t".implode("\n\t\t\t\t", $links)."\n\t\t\t".'</ul>'."\n\t\t".'</div>';
+$tpl_main = str_replace('<pun_navlinks>', $tpl_temp, $tpl_main);
+// END SUBST - <pun_navlinks>
+
+
+// START SUBST - <pun_status>
+$page_statusinfo = $page_topicsearches = array();
+
+if ($pun_user['is_guest'])
+ $page_statusinfo = '<p class="conl">'.$lang_common['Not logged in'].'</p>';
+else
+{
+ $page_statusinfo[] = '<li><span>'.$lang_common['Logged in as'].' <strong>'.pun_htmlspecialchars($pun_user['username']).'</strong></span></li>';
+ $page_statusinfo[] = '<li><span>'.sprintf($lang_common['Last visit'], format_time($pun_user['last_visit'])).'</span></li>';
+
+ if ($pun_user['is_admmod'])
+ {
+ if ($pun_config['o_report_method'] == '0' || $pun_config['o_report_method'] == '2')
+ {
+ $result_header = $db->query('SELECT 1 FROM '.$db->prefix.'reports WHERE zapped IS NULL') or error('Unable to fetch reports info', __FILE__, __LINE__, $db->error());
+
+ if ($db->result($result_header))
+ $page_statusinfo[] = '<li class="reportlink"><span><strong><a href="admin_reports.php">'.$lang_common['New reports'].'</a></strong></span></li>';
+ }
+
+ if ($pun_config['o_maintenance'] == '1')
+ $page_statusinfo[] = '<li class="maintenancelink"><span><strong><a href="admin_options.php#maintenance">'.$lang_common['Maintenance mode enabled'].'</a></strong></span></li>';
+ }
+
+ if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+ {
+ $page_topicsearches[] = '<a href="search.php?action=show_replies" title="'.$lang_common['Show posted topics'].'">'.$lang_common['Posted topics'].'</a>';
+ $page_topicsearches[] = '<a href="search.php?action=show_new" title="'.$lang_common['Show new posts'].'">'.$lang_common['New posts header'].'</a>';
+ }
+}
+
+// Quick searches
+if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+{
+ $page_topicsearches[] = '<a href="search.php?action=show_recent" title="'.$lang_common['Show active topics'].'">'.$lang_common['Active topics'].'</a>';
+ $page_topicsearches[] = '<a href="search.php?action=show_unanswered" title="'.$lang_common['Show unanswered topics'].'">'.$lang_common['Unanswered topics'].'</a>';
+}
+
+
+// Generate all that jazz
+$tpl_temp = '<div id="brdwelcome" class="inbox">';
+
+// The status information
+if (is_array($page_statusinfo))
+{
+ $tpl_temp .= "\n\t\t\t".'<ul class="conl">';
+ $tpl_temp .= "\n\t\t\t\t".implode("\n\t\t\t\t", $page_statusinfo);
+ $tpl_temp .= "\n\t\t\t".'</ul>';
+}
+else
+ $tpl_temp .= "\n\t\t\t".$page_statusinfo;
+
+// Generate quicklinks
+if (!empty($page_topicsearches))
+{
+ $tpl_temp .= "\n\t\t\t".'<ul class="conr">';
+ $tpl_temp .= "\n\t\t\t\t".'<li><span>'.$lang_common['Topic searches'].' '.implode(' | ', $page_topicsearches).'</span></li>';
+ $tpl_temp .= "\n\t\t\t".'</ul>';
+}
+
+$tpl_temp .= "\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
+
+$tpl_main = str_replace('<pun_status>', $tpl_temp, $tpl_main);
+// END SUBST - <pun_status>
+
+
+// START SUBST - <pun_announcement>
+if ($pun_user['g_read_board'] == '1' && $pun_config['o_announcement'] == '1')
+{
+ ob_start();
+
+?>
+<div id="announce" class="block">
+ <div class="hd"><h2><span><?php echo $lang_common['Announcement'] ?></span></h2></div>
+ <div class="box">
+ <div id="announce-block" class="inbox">
+ <div class="usercontent"><?php echo $pun_config['o_announcement_message'] ?></div>
+ </div>
+ </div>
+</div>
+<?php
+
+ $tpl_temp = trim(ob_get_contents());
+ $tpl_main = str_replace('<pun_announcement>', $tpl_temp, $tpl_main);
+ ob_end_clean();
+}
+else
+ $tpl_main = str_replace('<pun_announcement>', '', $tpl_main);
+// END SUBST - <pun_announcement>
+
+
+// START SUBST - <pun_main>
+ob_start();
+
+
+define('PUN_HEADER', 1);
diff --git a/help.php b/help.php
new file mode 100644
index 0000000..7ab702c
--- /dev/null
+++ b/help.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the help template
+define('PUN_HELP', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+// Load the help.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/help.php';
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_help['Help']);
+define('PUN_ACTIVE_PAGE', 'help');
+require PUN_ROOT.'header.php';
+
+?>
+<h2><span><?php echo $lang_help['BBCode'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><a name="bbcode"></a><?php echo $lang_help['BBCode info 1'] ?></p>
+ <p><?php echo $lang_help['BBCode info 2'] ?></p>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Text style'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><?php echo $lang_help['Text style info'] ?></p>
+ <p><code>[b]<?php echo $lang_help['Bold text'] ?>[/b]</code> <?php echo $lang_help['produces'] ?> <samp><strong><?php echo $lang_help['Bold text'] ?></strong></samp></p>
+ <p><code>[u]<?php echo $lang_help['Underlined text'] ?>[/u]</code> <?php echo $lang_help['produces'] ?> <samp><span class="bbu"><?php echo $lang_help['Underlined text'] ?></span></samp></p>
+ <p><code>[i]<?php echo $lang_help['Italic text'] ?>[/i]</code> <?php echo $lang_help['produces'] ?> <samp><em><?php echo $lang_help['Italic text'] ?></em></samp></p>
+ <p><code>[s]<?php echo $lang_help['Strike-through text'] ?>[/s]</code> <?php echo $lang_help['produces'] ?> <samp><span class="bbs"><?php echo $lang_help['Strike-through text'] ?></span></samp></p>
+ <p><code>[del]<?php echo $lang_help['Deleted text'] ?>[/del]</code> <?php echo $lang_help['produces'] ?> <samp><del><?php echo $lang_help['Deleted text'] ?></del></samp></p>
+ <p><code>[ins]<?php echo $lang_help['Inserted text'] ?>[/ins]</code> <?php echo $lang_help['produces'] ?> <samp><ins><?php echo $lang_help['Inserted text'] ?></ins></samp></p>
+ <p><code>[em]<?php echo $lang_help['Emphasised text'] ?>[/em]</code> <?php echo $lang_help['produces'] ?> <samp><em><?php echo $lang_help['Emphasised text'] ?></em></samp></p>
+ <p><code>[color=#FF0000]<?php echo $lang_help['Red text'] ?>[/color]</code> <?php echo $lang_help['produces'] ?> <samp><span style="color: #ff0000"><?php echo $lang_help['Red text'] ?></span></samp></p>
+ <p><code>[color=blue]<?php echo $lang_help['Blue text'] ?>[/color]</code> <?php echo $lang_help['produces'] ?> <samp><span style="color: blue"><?php echo $lang_help['Blue text'] ?></span></samp></p>
+ <p><code>[h]<?php echo $lang_help['Heading text'] ?>[/h]</code> <?php echo $lang_help['produces'] ?></p> <div class="postmsg"><h5><?php echo $lang_help['Heading text'] ?></h5></div>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Links and images'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><?php echo $lang_help['Links info'] ?></p>
+ <p><a name="url"></a><code>[url=<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>]<?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>"><?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?></a></samp></p>
+ <p><code>[url]<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/') ?></a></samp></p>
+ <p><code>[url=/help.php]<?php echo $lang_help['This help page'] ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/help.php') ?>"><?php echo $lang_help['This help page'] ?></a></samp></p>
+ <p><code>[email]myname@example.com[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname@example.com">myname@example.com</a></samp></p>
+ <p><code>[email=myname@example.com]<?php echo $lang_help['My email address'] ?>[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname@example.com"><?php echo $lang_help['My email address'] ?></a></samp></p>
+ <p><code>[topic=1]<?php echo $lang_help['Test topic'] ?>[/topic]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?id=1') ?>"><?php echo $lang_help['Test topic'] ?></a></samp></p>
+ <p><code>[topic]1[/topic]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?id=1') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?id=1') ?></a></samp></p>
+ <p><code>[post=1]<?php echo $lang_help['Test post'] ?>[/post]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?pid=1#p1') ?>"><?php echo $lang_help['Test post'] ?></a></samp></p>
+ <p><code>[post]1[/post]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?pid=1#p1') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?pid=1#p1') ?></a></samp></p>
+ <p><code>[forum=1]<?php echo $lang_help['Test forum'] ?>[/forum]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewforum.php?id=1') ?>"><?php echo $lang_help['Test forum'] ?></a></samp></p>
+ <p><code>[forum]1[/forum]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewforum.php?id=1') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/viewforum.php?id=1') ?></a></samp></p>
+ <p><code>[user=2]<?php echo $lang_help['Test user'] ?>[/user]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/profile.php?id=2') ?>"><?php echo $lang_help['Test user'] ?></a></samp></p>
+ <p><code>[user]2[/user]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/profile.php?id=2') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/profile.php?id=2') ?></a></samp></p>
+ </div>
+ <div class="inbox">
+ <p><a name="img"></a><?php echo $lang_help['Images info'] ?></p>
+ <p><code>[img=<?php echo $lang_help['FluxBB bbcode test'] ?>]<?php echo pun_htmlspecialchars(get_base_url(true)) ?>/img/test.png[/img]</code> <?php echo $lang_help['produces'] ?> <samp><img style="height: 21px" src="<?php echo pun_htmlspecialchars(get_base_url(true)) ?>/img/test.png" alt="<?php echo $lang_help['FluxBB bbcode test'] ?>" /></samp></p>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Quotes'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><?php echo $lang_help['Quotes info'] ?></p>
+ <p><code>[quote=James]<?php echo $lang_help['Quote text'] ?>[/quote]</code></p>
+ <p><?php echo $lang_help['produces quote box'] ?></p>
+ <div class="postmsg">
+ <div class="quotebox"><cite>James <?php echo $lang_common['wrote'] ?></cite><blockquote><div><p><?php echo $lang_help['Quote text'] ?></p></div></blockquote></div>
+ </div>
+ <p><?php echo $lang_help['Quotes info 2'] ?></p>
+ <p><code>[quote]<?php echo $lang_help['Quote text'] ?>[/quote]</code></p>
+ <p><?php echo $lang_help['produces quote box'] ?></p>
+ <div class="postmsg">
+ <div class="quotebox"><blockquote><div><p><?php echo $lang_help['Quote text'] ?></p></div></blockquote></div>
+ </div>
+ <p><?php echo $lang_help['quote note'] ?></p>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Code'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><?php echo $lang_help['Code info'] ?></p>
+ <p><code>[code]<?php echo $lang_help['Code text'] ?>[/code]</code></p>
+ <p><?php echo $lang_help['produces code box'] ?></p>
+ <div class="postmsg">
+ <div class="codebox"><pre><code><?php echo $lang_help['Code text'] ?></code></pre></div>
+ </div>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Lists'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><a name="lists"></a><?php echo $lang_help['List info'] ?></p>
+ <p><code>[list][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
+ <br /><span><?php echo $lang_help['produces list'] ?></span></p>
+ <div class="postmsg">
+ <ul><li><p><?php echo $lang_help['List text 1'] ?></p></li><li><p><?php echo $lang_help['List text 2'] ?></p></li><li><p><?php echo $lang_help['List text 3'] ?></p></li></ul>
+ </div>
+ <p><code>[list=1][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
+ <br /><span><?php echo $lang_help['produces decimal list'] ?></span></p>
+ <div class="postmsg">
+ <ol class="decimal"><li><p><?php echo $lang_help['List text 1'] ?></p></li><li><p><?php echo $lang_help['List text 2'] ?></p></li><li><p><?php echo $lang_help['List text 3'] ?></p></li></ol>
+ </div>
+ <p><code>[list=a][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
+ <br /><span><?php echo $lang_help['produces alpha list'] ?></span></p>
+ <div class="postmsg">
+ <ol class="alpha"><li><p><?php echo $lang_help['List text 1'] ?></p></li><li><p><?php echo $lang_help['List text 2'] ?></p></li><li><p><?php echo $lang_help['List text 3'] ?></p></li></ol>
+ </div>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Nested tags'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><?php echo $lang_help['Nested tags info'] ?></p>
+ <p><code>[b][u]<?php echo $lang_help['Bold, underlined text'] ?>[/u][/b]</code> <?php echo $lang_help['produces'] ?> <samp><strong><span class="bbu"><?php echo $lang_help['Bold, underlined text'] ?></span></strong></samp></p>
+ </div>
+</div>
+<h2><span><?php echo $lang_help['Smilies'] ?></span></h2>
+<div class="box">
+ <div class="inbox">
+ <p><a name="smilies"></a><?php echo $lang_help['Smilies info'] ?></p>
+<?php
+
+// Display the smiley set
+require PUN_ROOT.'include/parser.php';
+
+$smiley_groups = array();
+
+foreach ($smilies as $smiley_text => $smiley_img)
+ $smiley_groups[$smiley_img][] = $smiley_text;
+
+foreach ($smiley_groups as $smiley_img => $smiley_texts)
+ echo "\t\t".'<p><code>'.implode('</code> '.$lang_common['and'].' <code>', $smiley_texts).'</code> <span>'.$lang_help['produces'].'</span> <samp><img src="'.pun_htmlspecialchars(get_base_url(true)).'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.$smiley_texts[0].'" /></samp></p>'."\n";
+
+?>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/img/avatars/index.html b/img/avatars/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/img/avatars/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/img/index.html b/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/img/smilies/big_smile.png b/img/smilies/big_smile.png
new file mode 100644
index 0000000..1c4ea81
--- /dev/null
+++ b/img/smilies/big_smile.png
Binary files differ
diff --git a/img/smilies/cool.png b/img/smilies/cool.png
new file mode 100644
index 0000000..54ec46f
--- /dev/null
+++ b/img/smilies/cool.png
Binary files differ
diff --git a/img/smilies/hmm.png b/img/smilies/hmm.png
new file mode 100644
index 0000000..47b87b9
--- /dev/null
+++ b/img/smilies/hmm.png
Binary files differ
diff --git a/img/smilies/index.html b/img/smilies/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/img/smilies/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/img/smilies/lol.png b/img/smilies/lol.png
new file mode 100644
index 0000000..bad0718
--- /dev/null
+++ b/img/smilies/lol.png
Binary files differ
diff --git a/img/smilies/mad.png b/img/smilies/mad.png
new file mode 100644
index 0000000..d8ffcc6
--- /dev/null
+++ b/img/smilies/mad.png
Binary files differ
diff --git a/img/smilies/neutral.png b/img/smilies/neutral.png
new file mode 100644
index 0000000..a2e4cb8
--- /dev/null
+++ b/img/smilies/neutral.png
Binary files differ
diff --git a/img/smilies/roll.png b/img/smilies/roll.png
new file mode 100644
index 0000000..1c3bc7e
--- /dev/null
+++ b/img/smilies/roll.png
Binary files differ
diff --git a/img/smilies/sad.png b/img/smilies/sad.png
new file mode 100644
index 0000000..b6a0899
--- /dev/null
+++ b/img/smilies/sad.png
Binary files differ
diff --git a/img/smilies/smile.png b/img/smilies/smile.png
new file mode 100644
index 0000000..77099a0
--- /dev/null
+++ b/img/smilies/smile.png
Binary files differ
diff --git a/img/smilies/tongue.png b/img/smilies/tongue.png
new file mode 100644
index 0000000..397b98a
--- /dev/null
+++ b/img/smilies/tongue.png
Binary files differ
diff --git a/img/smilies/wink.png b/img/smilies/wink.png
new file mode 100644
index 0000000..fbc40af
--- /dev/null
+++ b/img/smilies/wink.png
Binary files differ
diff --git a/img/smilies/yikes.png b/img/smilies/yikes.png
new file mode 100644
index 0000000..ddb055f
--- /dev/null
+++ b/img/smilies/yikes.png
Binary files differ
diff --git a/img/test.png b/img/test.png
new file mode 100644
index 0000000..4241f08
--- /dev/null
+++ b/img/test.png
Binary files differ
diff --git a/include/addons.php b/include/addons.php
new file mode 100644
index 0000000..8a0ff48
--- /dev/null
+++ b/include/addons.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+
+/**
+ * Class flux_addon_manager
+ *
+ * This class is responsible for loading the addons and storing their hook listeners.
+ */
+class flux_addon_manager
+{
+ var $hooks = array();
+
+ var $loaded = false;
+
+ function load()
+ {
+ $this->loaded = true;
+
+ $d = dir(PUN_ROOT.'addons');
+ if (!$d) return;
+
+ while (($addon_file = $d->read()) !== false)
+ {
+ if (!is_dir(PUN_ROOT.'addons/'.$addon_file) && preg_match('%(\w+)\.php$%', $addon_file))
+ {
+ $addon_name = 'addon_'.substr($addon_file, 0, -4);
+
+ include PUN_ROOT.'addons/'.$addon_file;
+ $addon = new $addon_name;
+
+ $addon->register($this);
+ }
+ }
+ $d->close();
+ }
+
+ function bind($hook, $callback)
+ {
+ if (!isset($this->hooks[$hook]))
+ $this->hooks[$hook] = array();
+
+ if (is_callable($callback))
+ $this->hooks[$hook][] = $callback;
+ }
+
+ function hook($name)
+ {
+ if (!$this->loaded)
+ $this->load();
+
+ $callbacks = isset($this->hooks[$name]) ? $this->hooks[$name] : array();
+
+ // Execute every registered callback for this hook
+ foreach ($callbacks as $callback)
+ {
+ list($addon, $method) = $callback;
+ $addon->$method();
+ }
+ }
+}
+
+
+/**
+ * Class flux_addon
+ *
+ * This class can be extended to provide addon functionality.
+ * Subclasses should implement the register method which will be called so that they have a chance to register possible
+ * listeners for all hooks.
+ */
+class flux_addon
+{
+ function register($manager)
+ { }
+}
diff --git a/include/cache.php b/include/cache.php
new file mode 100644
index 0000000..c1947ae
--- /dev/null
+++ b/include/cache.php
@@ -0,0 +1,263 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+
+//
+// Generate the config cache PHP script
+//
+function generate_config_cache()
+{
+ global $db;
+
+ // Get the forum config from the DB
+ $result = $db->query('SELECT * FROM '.$db->prefix.'config', true) or error('Unable to fetch forum config', __FILE__, __LINE__, $db->error());
+
+ $output = array();
+ while ($cur_config_item = $db->fetch_row($result))
+ $output[$cur_config_item[0]] = $cur_config_item[1];
+
+ // Output config as PHP code
+ $content = '<?php'."\n\n".'define(\'PUN_CONFIG_LOADED\', 1);'."\n\n".'$pun_config = '.var_export($output, true).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_config.php', $content);
+}
+
+
+//
+// Generate the bans cache PHP script
+//
+function generate_bans_cache()
+{
+ global $db;
+
+ // Get the ban list from the DB
+ $result = $db->query('SELECT * FROM '.$db->prefix.'bans', true) or error('Unable to fetch ban list', __FILE__, __LINE__, $db->error());
+
+ $output = array();
+ while ($cur_ban = $db->fetch_assoc($result))
+ $output[] = $cur_ban;
+
+ // Output ban list as PHP code
+ $content = '<?php'."\n\n".'define(\'PUN_BANS_LOADED\', 1);'."\n\n".'$pun_bans = '.var_export($output, true).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_bans.php', $content);
+}
+
+
+//
+// Generate quick jump cache PHP scripts
+//
+function generate_quickjump_cache($group_id = false)
+{
+ global $db, $lang_common;
+
+ $groups = array();
+
+ // If a group_id was supplied, we generate the quick jump cache for that group only
+ if ($group_id !== false)
+ {
+ // Is this group even allowed to read forums?
+ $result = $db->query('SELECT g_read_board FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch user group read permission', __FILE__, __LINE__, $db->error());
+ $read_board = $db->result($result);
+
+ $groups[$group_id] = $read_board;
+ }
+ else
+ {
+ // A group_id was not supplied, so we generate the quick jump cache for all groups
+ $result = $db->query('SELECT g_id, g_read_board FROM '.$db->prefix.'groups') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+ while ($row = $db->fetch_row($result))
+ $groups[$row[0]] = $row[1];
+ }
+
+ // Loop through the groups in $groups and output the cache for each of them
+ foreach ($groups as $group_id => $read_board)
+ {
+ // Output quick jump as PHP code
+ $output = '<?php'."\n\n".'if (!defined(\'PUN\')) exit;'."\n".'define(\'PUN_QJ_LOADED\', 1);'."\n".'$forum_id = isset($forum_id) ? $forum_id : 0;'."\n\n".'?>';
+
+ if ($read_board == '1')
+ {
+ $result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$group_id.') WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ $output .= "\t\t\t\t".'<form id="qjump" method="get" action="viewforum.php">'."\n\t\t\t\t\t".'<div><label><span><?php echo $lang_common[\'Jump to\'] ?>'.'<br /></span>'."\n\t\t\t\t\t".'<select name="id" onchange="window.location=(\'viewforum.php?id=\'+this.options[this.selectedIndex].value)">'."\n";
+
+ $cur_category = 0;
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category)
+ $output .= "\t\t\t\t\t\t".'</optgroup>'."\n";
+
+ $output .= "\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+ $cur_category = $cur_forum['cid'];
+ }
+
+ $redirect_tag = ($cur_forum['redirect_url'] != '') ? ' &gt;&gt;&gt;' : '';
+ $output .= "\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'"<?php echo ($forum_id == '.$cur_forum['fid'].') ? \' selected="selected"\' : \'\' ?>>'.pun_htmlspecialchars($cur_forum['forum_name']).$redirect_tag.'</option>'."\n";
+ }
+
+ $output .= "\t\t\t\t\t\t".'</optgroup>'."\n\t\t\t\t\t".'</select></label>'."\n\t\t\t\t\t".'<input type="submit" value="<?php echo $lang_common[\'Go\'] ?>" accesskey="g" />'."\n\t\t\t\t\t".'</div>'."\n\t\t\t\t".'</form>'."\n";
+ }
+ }
+
+ fluxbb_write_cache_file('cache_quickjump_'.$group_id.'.php', $output);
+ }
+}
+
+
+//
+// Generate the censoring cache PHP script
+//
+function generate_censoring_cache()
+{
+ global $db;
+
+ $result = $db->query('SELECT search_for, replace_with FROM '.$db->prefix.'censoring') or error('Unable to fetch censoring list', __FILE__, __LINE__, $db->error());
+ $num_words = $db->num_rows($result);
+
+ $search_for = $replace_with = array();
+ for ($i = 0; $i < $num_words; $i++)
+ {
+ list($search_for[$i], $replace_with[$i]) = $db->fetch_row($result);
+ $search_for[$i] = '%(?<=[^\p{L}\p{N}])('.str_replace('\*', '[\p{L}\p{N}]*?', preg_quote($search_for[$i], '%')).')(?=[^\p{L}\p{N}])%iu';
+ }
+
+ // Output censored words as PHP code
+ $content = '<?php'."\n\n".'define(\'PUN_CENSOR_LOADED\', 1);'."\n\n".'$search_for = '.var_export($search_for, true).';'."\n\n".'$replace_with = '.var_export($replace_with, true).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_censoring.php', $content);
+}
+
+
+//
+// Generate the stopwords cache PHP script
+//
+function generate_stopwords_cache()
+{
+ $stopwords = array();
+
+ $d = dir(PUN_ROOT.'lang');
+ while (($entry = $d->read()) !== false)
+ {
+ if ($entry{0} == '.')
+ continue;
+
+ if (is_dir(PUN_ROOT.'lang/'.$entry) && file_exists(PUN_ROOT.'lang/'.$entry.'/stopwords.txt'))
+ $stopwords = array_merge($stopwords, file(PUN_ROOT.'lang/'.$entry.'/stopwords.txt'));
+ }
+ $d->close();
+
+ // Tidy up and filter the stopwords
+ $stopwords = array_map('pun_trim', $stopwords);
+ $stopwords = array_filter($stopwords);
+
+ // Output stopwords as PHP code
+ $content = '<?php'."\n\n".'$cache_id = \''.generate_stopwords_cache_id().'\';'."\n".'if ($cache_id != generate_stopwords_cache_id()) return;'."\n\n".'define(\'PUN_STOPWORDS_LOADED\', 1);'."\n\n".'$stopwords = '.var_export($stopwords, true).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_stopwords.php', $content);
+}
+
+
+//
+// Load some information about the latest registered users
+//
+function generate_users_info_cache()
+{
+ global $db;
+
+ $stats = array();
+
+ $result = $db->query('SELECT COUNT(id)-1 FROM '.$db->prefix.'users WHERE group_id!='.PUN_UNVERIFIED) or error('Unable to fetch total user count', __FILE__, __LINE__, $db->error());
+ $stats['total_users'] = $db->result($result);
+
+ $result = $db->query('SELECT id, username FROM '.$db->prefix.'users WHERE group_id!='.PUN_UNVERIFIED.' ORDER BY registered DESC LIMIT 1') or error('Unable to fetch newest registered user', __FILE__, __LINE__, $db->error());
+ $stats['last_user'] = $db->fetch_assoc($result);
+
+ // Output users info as PHP code
+ $content = '<?php'."\n\n".'define(\'PUN_USERS_INFO_LOADED\', 1);'."\n\n".'$stats = '.var_export($stats, true).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_users_info.php', $content);
+}
+
+
+//
+// Generate the admins cache PHP script
+//
+function generate_admins_cache()
+{
+ global $db;
+
+ // Get admins from the DB
+ $result = $db->query('SELECT id FROM '.$db->prefix.'users WHERE group_id='.PUN_ADMIN) or error('Unable to fetch users info', __FILE__, __LINE__, $db->error());
+
+ $output = array();
+ while ($row = $db->fetch_row($result))
+ $output[] = $row[0];
+
+ // Output admin list as PHP code
+ $content = '<?php'."\n\n".'define(\'PUN_ADMINS_LOADED\', 1);'."\n\n".'$pun_admins = '.var_export($output, true).';'."\n\n".'?>';
+ fluxbb_write_cache_file('cache_admins.php', $content);
+}
+
+
+//
+// Safely write out a cache file.
+//
+function fluxbb_write_cache_file($file, $content)
+{
+ $fh = @fopen(FORUM_CACHE_DIR.$file, 'wb');
+ if (!$fh)
+ error('Unable to write cache file '.pun_htmlspecialchars($file).' to cache directory. Please make sure PHP has write access to the directory \''.pun_htmlspecialchars(FORUM_CACHE_DIR).'\'', __FILE__, __LINE__);
+
+ flock($fh, LOCK_EX);
+ ftruncate($fh, 0);
+
+ fwrite($fh, $content);
+
+ flock($fh, LOCK_UN);
+ fclose($fh);
+
+ fluxbb_invalidate_cached_file(FORUM_CACHE_DIR.$file);
+}
+
+
+//
+// Delete all feed caches
+//
+function clear_feed_cache()
+{
+ $d = dir(FORUM_CACHE_DIR);
+ while (($entry = $d->read()) !== false)
+ {
+ if (substr($entry, 0, 10) == 'cache_feed' && substr($entry, -4) == '.php')
+ {
+ @unlink(FORUM_CACHE_DIR.$entry);
+ fluxbb_invalidate_cached_file(FORUM_CACHE_DIR.$entry);
+ }
+ }
+ $d->close();
+}
+
+
+//
+// Invalidate updated php files that are cached by an opcache
+//
+function fluxbb_invalidate_cached_file($file)
+{
+ if (function_exists('opcache_invalidate'))
+ opcache_invalidate($file, true);
+ elseif (function_exists('apc_delete_file'))
+ @apc_delete_file($file);
+}
+
+
+define('FORUM_CACHE_FUNCTIONS_LOADED', true);
diff --git a/include/common.php b/include/common.php
new file mode 100644
index 0000000..ad34b5e
--- /dev/null
+++ b/include/common.php
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+if (!defined('PUN_ROOT'))
+ exit('The constant PUN_ROOT must be defined and point to a valid FluxBB installation root directory.');
+
+// Define the version and database revision that this code was written for
+define('FORUM_VERSION', '1.5.11');
+
+define('FORUM_DB_REVISION', 21);
+define('FORUM_SI_REVISION', 2);
+define('FORUM_PARSER_REVISION', 2);
+
+// Block prefetch requests
+if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch')
+{
+ header('HTTP/1.1 403 Prefetching Forbidden');
+
+ // Send no-cache headers
+ header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: post-check=0, pre-check=0', false);
+ header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+ exit;
+}
+
+// Attempt to load the configuration file config.php
+if (file_exists(PUN_ROOT.'config.php'))
+ require PUN_ROOT.'config.php';
+
+// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
+if (defined('FORUM'))
+ define('PUN', FORUM);
+
+// If PUN isn't defined, config.php is missing or corrupt
+if (!defined('PUN'))
+{
+ header('Location: install.php');
+ exit;
+}
+
+// Load the functions script
+require PUN_ROOT.'include/functions.php';
+
+// Load addon functionality
+require PUN_ROOT.'include/addons.php';
+
+// Load UTF-8 functions
+require PUN_ROOT.'include/utf8/utf8.php';
+
+// Strip out "bad" UTF-8 characters
+forum_remove_bad_characters();
+
+// Reverse the effect of register_globals
+forum_unregister_globals();
+
+// The addon manager is responsible for storing the hook listeners and communicating with the addons
+$flux_addons = new flux_addon_manager();
+
+// Record the start time (will be used to calculate the generation time for the page)
+$pun_start = get_microtime();
+
+// Seed the random number generator for systems where this does not happen automatically
+mt_srand();
+
+// Make sure PHP reports all errors except E_NOTICE. FluxBB supports E_ALL, but a lot of scripts it may interact with, do not
+// We set this in php.ini
+//error_reporting(E_ALL ^ E_NOTICE);
+
+// Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
+setlocale(LC_CTYPE, 'C');
+
+// Turn off magic_quotes_runtime
+if (get_magic_quotes_runtime())
+ set_magic_quotes_runtime(0);
+
+// Strip slashes from GET/POST/COOKIE/REQUEST/FILES (if magic_quotes_gpc is enabled)
+if (!defined('FORUM_DISABLE_STRIPSLASHES') && get_magic_quotes_gpc())
+{
+ function stripslashes_array($array)
+ {
+ return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
+ }
+
+ $_GET = stripslashes_array($_GET);
+ $_POST = stripslashes_array($_POST);
+ $_COOKIE = stripslashes_array($_COOKIE);
+ $_REQUEST = stripslashes_array($_REQUEST);
+ if (is_array($_FILES))
+ {
+ // Don't strip valid slashes from tmp_name path on Windows
+ foreach ($_FILES AS $key => $value)
+ $_FILES[$key]['tmp_name'] = str_replace('\\', '\\\\', $value['tmp_name']);
+ $_FILES = stripslashes_array($_FILES);
+ }
+}
+
+// If a cookie name is not specified in config.php, we use the default (pun_cookie)
+if (empty($cookie_name))
+ $cookie_name = 'pun_cookie';
+
+// If the cache directory is not specified, we use the default setting
+if (!defined('FORUM_CACHE_DIR'))
+ define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
+
+// Define a few commonly used constants
+define('PUN_UNVERIFIED', 0);
+define('PUN_ADMIN', 1);
+define('PUN_MOD', 2);
+define('PUN_GUEST', 3);
+define('PUN_MEMBER', 4);
+
+// Load DB abstraction layer and connect
+require PUN_ROOT.'include/dblayer/common_db.php';
+
+// Start a transaction
+$db->start_transaction();
+
+// Load cached config
+if (file_exists(FORUM_CACHE_DIR.'cache_config.php'))
+ include FORUM_CACHE_DIR.'cache_config.php';
+
+if (!defined('PUN_CONFIG_LOADED'))
+{
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_config_cache();
+ require FORUM_CACHE_DIR.'cache_config.php';
+}
+
+// Verify that we are running the proper database schema revision
+if (!isset($pun_config['o_database_revision']) || $pun_config['o_database_revision'] < FORUM_DB_REVISION ||
+ !isset($pun_config['o_searchindex_revision']) || $pun_config['o_searchindex_revision'] < FORUM_SI_REVISION ||
+ !isset($pun_config['o_parser_revision']) || $pun_config['o_parser_revision'] < FORUM_PARSER_REVISION ||
+ version_compare($pun_config['o_cur_version'], FORUM_VERSION, '<'))
+{
+ header('Location: db_update.php');
+ exit;
+}
+
+// Enable output buffering
+if (!defined('PUN_DISABLE_BUFFERING'))
+{
+ // Should we use gzip output compression?
+ if ($pun_config['o_gzip'] && extension_loaded('zlib'))
+ ob_start('ob_gzhandler');
+ else
+ ob_start();
+}
+
+// Define standard date/time formats
+$forum_time_formats = array($pun_config['o_time_format'], 'H:i:s', 'H:i', 'g:i:s a', 'g:i a');
+$forum_date_formats = array($pun_config['o_date_format'], 'Y-m-d', 'Y-d-m', 'd-m-Y', 'm-d-Y', 'M j Y', 'jS M Y');
+
+// Check/update/set cookie and fetch user info
+$pun_user = array();
+check_cookie($pun_user);
+
+// Attempt to load the common language file
+if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/common.php'))
+ include PUN_ROOT.'lang/'.$pun_user['language'].'/common.php';
+else
+ error('There is no valid language pack \''.pun_htmlspecialchars($pun_user['language']).'\' installed. Please reinstall a language of that name');
+
+// Check if we are to display a maintenance message
+if ($pun_config['o_maintenance'] && $pun_user['g_id'] > PUN_ADMIN && !defined('PUN_TURN_OFF_MAINT'))
+ maintenance_message();
+
+// Load cached bans
+if (file_exists(FORUM_CACHE_DIR.'cache_bans.php'))
+ include FORUM_CACHE_DIR.'cache_bans.php';
+
+if (!defined('PUN_BANS_LOADED'))
+{
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_bans_cache();
+ require FORUM_CACHE_DIR.'cache_bans.php';
+}
+
+// Check if current user is banned
+check_bans();
+
+// Update online list
+update_users_online();
+
+// Check to see if we logged in without a cookie being set
+if ($pun_user['is_guest'] && isset($_GET['login']))
+ message($lang_common['No cookie']);
+
+// The maximum size of a post, in bytes, since the field is now MEDIUMTEXT this allows ~16MB but lets cap at 1MB...
+if (!defined('PUN_MAX_POSTSIZE'))
+ define('PUN_MAX_POSTSIZE', 1048576);
+
+if (!defined('PUN_SEARCH_MIN_WORD'))
+ define('PUN_SEARCH_MIN_WORD', 3);
+if (!defined('PUN_SEARCH_MAX_WORD'))
+ define('PUN_SEARCH_MAX_WORD', 20);
+
+if (!defined('FORUM_MAX_COOKIE_SIZE'))
+ define('FORUM_MAX_COOKIE_SIZE', 4048);
diff --git a/include/common_admin.php b/include/common_admin.php
new file mode 100644
index 0000000..bb6ce50
--- /dev/null
+++ b/include/common_admin.php
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+// Make sure we have a usable language pack for admin.
+if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/admin_common.php'))
+ $admin_language = $pun_user['language'];
+else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/admin_common.php'))
+ $admin_language = $pun_config['o_default_lang'];
+else
+ $admin_language = 'English';
+
+// Attempt to load the admin_common language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_common.php';
+
+//
+// Fetch a list of available admin plugins
+//
+function forum_list_plugins($is_admin)
+{
+ $plugins = array();
+
+ $d = dir(PUN_ROOT.'plugins');
+ if (!$d) return $plugins;
+
+ while (($entry = $d->read()) !== false)
+ {
+ if (!is_dir(PUN_ROOT.'plugins/'.$entry) && preg_match('%^AM?P_(\w+)\.php$%i', $entry))
+ {
+ $prefix = substr($entry, 0, strpos($entry, '_'));
+
+ if ($prefix == 'AMP' || ($is_admin && $prefix == 'AP'))
+ $plugins[$entry] = substr($entry, strlen($prefix) + 1, -4);
+ }
+ }
+ $d->close();
+
+ natcasesort($plugins);
+
+ return $plugins;
+}
+
+
+//
+// Display the admin navigation menu
+//
+function generate_admin_menu($page = '')
+{
+ global $pun_config, $pun_user, $lang_admin_common;
+
+ $is_admin = $pun_user['g_id'] == PUN_ADMIN ? true : false;
+
+?>
+<div id="adminconsole" class="block2col">
+ <div id="adminmenu" class="blockmenu">
+ <h2><span><?php echo $lang_admin_common['Moderator menu'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <ul>
+ <li<?php if ($page == 'index') echo ' class="isactive"'; ?>><a href="admin_index.php"><?php echo $lang_admin_common['Index'] ?></a></li>
+ <li<?php if ($page == 'users') echo ' class="isactive"'; ?>><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+<?php if ($is_admin || $pun_user['g_mod_ban_users'] == '1'): ?> <li<?php if ($page == 'bans') echo ' class="isactive"'; ?>><a href="admin_bans.php"><?php echo $lang_admin_common['Bans'] ?></a></li>
+<?php endif; if ($is_admin || $pun_config['o_report_method'] == '0' || $pun_config['o_report_method'] == '2'): ?> <li<?php if ($page == 'reports') echo ' class="isactive"'; ?>><a href="admin_reports.php"><?php echo $lang_admin_common['Reports'] ?></a></li>
+<?php endif; ?> </ul>
+ </div>
+ </div>
+<?php
+
+ if ($is_admin)
+ {
+
+?>
+ <h2 class="block2"><span><?php echo $lang_admin_common['Admin menu'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <ul>
+ <li<?php if ($page == 'options') echo ' class="isactive"'; ?>><a href="admin_options.php"><?php echo $lang_admin_common['Options'] ?></a></li>
+ <li<?php if ($page == 'permissions') echo ' class="isactive"'; ?>><a href="admin_permissions.php"><?php echo $lang_admin_common['Permissions'] ?></a></li>
+ <li<?php if ($page == 'categories') echo ' class="isactive"'; ?>><a href="admin_categories.php"><?php echo $lang_admin_common['Categories'] ?></a></li>
+ <li<?php if ($page == 'forums') echo ' class="isactive"'; ?>><a href="admin_forums.php"><?php echo $lang_admin_common['Forums'] ?></a></li>
+ <li<?php if ($page == 'groups') echo ' class="isactive"'; ?>><a href="admin_groups.php"><?php echo $lang_admin_common['User groups'] ?></a></li>
+ <li<?php if ($page == 'censoring') echo ' class="isactive"'; ?>><a href="admin_censoring.php"><?php echo $lang_admin_common['Censoring'] ?></a></li>
+ <li<?php if ($page == 'maintenance') echo ' class="isactive"'; ?>><a href="admin_maintenance.php"><?php echo $lang_admin_common['Maintenance'] ?></a></li>
+ </ul>
+ </div>
+ </div>
+<?php
+
+ }
+
+ // See if there are any plugins
+ $plugins = forum_list_plugins($is_admin);
+
+ // Did we find any plugins?
+ if (!empty($plugins))
+ {
+
+?>
+ <h2 class="block2"><span><?php echo $lang_admin_common['Plugins menu'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <ul>
+<?php
+
+ foreach ($plugins as $plugin_name => $plugin)
+ echo "\t\t\t\t\t".'<li'.(($page == $plugin_name) ? ' class="isactive"' : '').'><a href="admin_loader.php?plugin='.$plugin_name.'">'.str_replace('_', ' ', $plugin).'</a></li>'."\n";
+
+?>
+ </ul>
+ </div>
+ </div>
+<?php
+
+ }
+
+?>
+ </div>
+
+<?php
+
+}
+
+
+//
+// Delete topics from $forum_id that are "older than" $prune_date (if $prune_sticky is 1, sticky topics will also be deleted)
+//
+function prune($forum_id, $prune_sticky, $prune_date)
+{
+ global $db;
+
+ $extra_sql = ($prune_date != -1) ? ' AND last_post<'.$prune_date : '';
+
+ if (!$prune_sticky)
+ $extra_sql .= ' AND sticky=\'0\'';
+
+ // Fetch topics to prune
+ $result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.$extra_sql, true) or error('Unable to fetch topics', __FILE__, __LINE__, $db->error());
+
+ $topic_ids = '';
+ while ($row = $db->fetch_row($result))
+ $topic_ids .= (($topic_ids != '') ? ',' : '').$row[0];
+
+ if ($topic_ids != '')
+ {
+ // Fetch posts to prune
+ $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topic_ids.')', true) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+ $post_ids = '';
+ while ($row = $db->fetch_row($result))
+ $post_ids .= (($post_ids != '') ? ',' : '').$row[0];
+
+ if ($post_ids != '')
+ {
+ // Delete topics
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topic_ids.')') or error('Unable to prune topics', __FILE__, __LINE__, $db->error());
+ // Delete subscriptions
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.$topic_ids.')') or error('Unable to prune subscriptions', __FILE__, __LINE__, $db->error());
+ // Delete posts
+ $db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$post_ids.')') or error('Unable to prune posts', __FILE__, __LINE__, $db->error());
+
+ // We removed a bunch of posts, so now we have to update the search index
+ require_once PUN_ROOT.'include/search_idx.php';
+ strip_search_index($post_ids);
+ }
+ }
+}
diff --git a/include/dblayer/common_db.php b/include/dblayer/common_db.php
new file mode 100644
index 0000000..5b9e67e
--- /dev/null
+++ b/include/dblayer/common_db.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+
+// Load the appropriate DB layer class
+switch ($db_type)
+{
+ case 'mysql':
+ require_once PUN_ROOT.'include/dblayer/mysql.php';
+ break;
+
+ case 'mysql_innodb':
+ require_once PUN_ROOT.'include/dblayer/mysql_innodb.php';
+ break;
+
+ case 'mysqli':
+ require_once PUN_ROOT.'include/dblayer/mysqli.php';
+ break;
+
+ case 'mysqli_innodb':
+ require_once PUN_ROOT.'include/dblayer/mysqli_innodb.php';
+ break;
+
+ case 'pgsql':
+ require_once PUN_ROOT.'include/dblayer/pgsql.php';
+ break;
+
+ case 'sqlite':
+ require_once PUN_ROOT.'include/dblayer/sqlite.php';
+ break;
+
+ default:
+ error('\''.$db_type.'\' is not a valid database type. Please check settings in config.php.', __FILE__, __LINE__);
+ break;
+}
+
+
+// Create the database adapter object (and open/connect to/select db)
+$db = new DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
diff --git a/include/dblayer/index.html b/include/dblayer/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/dblayer/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/dblayer/mysql.php b/include/dblayer/mysql.php
new file mode 100644
index 0000000..1b36648
--- /dev/null
+++ b/include/dblayer/mysql.php
@@ -0,0 +1,378 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysql_connect'))
+ exit('This PHP environment doesn\'t have MySQL support built in. MySQL support is required if you want to use a MySQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+ var $prefix;
+ var $link_id;
+ var $query_result;
+
+ var $saved_queries = array();
+ var $num_queries = 0;
+
+ var $error_no = false;
+ var $error_msg = 'Unknown';
+
+ var $datatype_transformations = array(
+ '%^SERIAL$%' => 'INT(10) UNSIGNED AUTO_INCREMENT'
+ );
+
+
+ function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->prefix = $db_prefix;
+
+ if ($p_connect)
+ $this->link_id = @mysql_pconnect($db_host, $db_username, $db_password);
+ else
+ $this->link_id = @mysql_connect($db_host, $db_username, $db_password);
+
+ if ($this->link_id)
+ {
+ if (!@mysql_select_db($db_name, $this->link_id))
+ error('Unable to select database. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+ }
+ else
+ error('Unable to connect to MySQL server. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+
+ // Setup the client-server character set (UTF-8)
+ if (!defined('FORUM_NO_SET_NAMES'))
+ $this->set_names('utf8');
+
+ return $this->link_id;
+ }
+
+
+ function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->__construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
+ }
+
+
+ function start_transaction()
+ {
+ return;
+ }
+
+
+ function end_transaction()
+ {
+ return;
+ }
+
+
+ function query($sql, $unbuffered = false)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $q_start = get_microtime();
+
+ if ($unbuffered)
+ $this->query_result = @mysql_unbuffered_query($sql, $this->link_id);
+ else
+ $this->query_result = @mysql_query($sql, $this->link_id);
+
+ if ($this->query_result)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, sprintf('%.5F', get_microtime() - $q_start));
+
+ ++$this->num_queries;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, 0);
+
+ $this->error_no = @mysql_errno($this->link_id);
+ $this->error_msg = @mysql_error($this->link_id);
+
+ return false;
+ }
+ }
+
+
+ function result($query_id = 0, $row = 0, $col = 0)
+ {
+ return ($query_id) ? @mysql_result($query_id, $row, $col) : false;
+ }
+
+
+ function fetch_assoc($query_id = 0)
+ {
+ return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+ }
+
+
+ function fetch_row($query_id = 0)
+ {
+ return ($query_id) ? @mysql_fetch_row($query_id) : false;
+ }
+
+
+ function num_rows($query_id = 0)
+ {
+ return ($query_id) ? @mysql_num_rows($query_id) : false;
+ }
+
+
+ function affected_rows()
+ {
+ return ($this->link_id) ? @mysql_affected_rows($this->link_id) : false;
+ }
+
+
+ function insert_id()
+ {
+ return ($this->link_id) ? @mysql_insert_id($this->link_id) : false;
+ }
+
+
+ function get_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+
+ function get_saved_queries()
+ {
+ return $this->saved_queries;
+ }
+
+
+ function free_result($query_id = false)
+ {
+ return ($query_id) ? @mysql_free_result($query_id) : false;
+ }
+
+
+ function escape($str)
+ {
+ if (is_array($str))
+ return '';
+ else if (function_exists('mysql_real_escape_string'))
+ return mysql_real_escape_string($str, $this->link_id);
+ else
+ return mysql_escape_string($str);
+ }
+
+
+ function error()
+ {
+ $result['error_sql'] = @current(@end($this->saved_queries));
+ $result['error_no'] = $this->error_no;
+ $result['error_msg'] = $this->error_msg;
+
+ return $result;
+ }
+
+
+ function close()
+ {
+ if ($this->link_id)
+ {
+ if (is_resource($this->query_result))
+ @mysql_free_result($this->query_result);
+
+ return @mysql_close($this->link_id);
+ }
+ else
+ return false;
+ }
+
+ function get_names()
+ {
+ $result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+ return $this->result($result, 0, 1);
+ }
+
+
+ function set_names($names)
+ {
+ return $this->query('SET NAMES \''.$this->escape($names).'\'');
+ }
+
+
+ function get_version()
+ {
+ $result = $this->query('SELECT VERSION()');
+
+ return array(
+ 'name' => 'MySQL Standard',
+ 'version' => preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+ );
+ }
+
+
+ function table_exists($table_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function field_exists($table_name, $field_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function index_exists($table_name, $index_name, $no_prefix = false)
+ {
+ $exists = false;
+
+ $result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+ while ($cur_index = $this->fetch_assoc($result))
+ {
+ if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+ {
+ $exists = true;
+ break;
+ }
+ }
+
+ return $exists;
+ }
+
+
+ function create_table($table_name, $schema, $no_prefix = false)
+ {
+ if ($this->table_exists($table_name, $no_prefix))
+ return true;
+
+ $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+ // Go through every schema element and add it to the query
+ foreach ($schema['FIELDS'] as $field_name => $field_data)
+ {
+ $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+ $query .= $field_name.' '.$field_data['datatype'];
+
+ if (isset($field_data['collation']))
+ $query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+ if (!$field_data['allow_null'])
+ $query .= ' NOT NULL';
+
+ if (isset($field_data['default']))
+ $query .= ' DEFAULT '.$field_data['default'];
+
+ $query .= ",\n";
+ }
+
+ // If we have a primary key, add it
+ if (isset($schema['PRIMARY KEY']))
+ $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+ // Add unique keys
+ if (isset($schema['UNIQUE KEYS']))
+ {
+ foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+ $query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+ }
+
+ // Add indexes
+ if (isset($schema['INDEXES']))
+ {
+ foreach ($schema['INDEXES'] as $index_name => $index_fields)
+ $query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+ }
+
+ // We remove the last two characters (a newline and a comma) and add on the ending
+ $query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'MyISAM').' CHARACTER SET utf8';
+
+ return $this->query($query) ? true : false;
+ }
+
+
+ function drop_table($table_name, $no_prefix = false)
+ {
+ if (!$this->table_exists($table_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+
+
+ function rename_table($old_table, $new_table, $no_prefix = false)
+ {
+ // If the new table exists and the old one doesn't, then we're happy
+ if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+ }
+
+
+ function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if ($this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function drop_field($table_name, $field_name, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+ }
+
+
+ function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+ {
+ if ($this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+ }
+
+
+ function drop_index($table_name, $index_name, $no_prefix = false)
+ {
+ if (!$this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+ }
+
+ function truncate_table($table_name, $no_prefix = false)
+ {
+ return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+}
diff --git a/include/dblayer/mysql_innodb.php b/include/dblayer/mysql_innodb.php
new file mode 100644
index 0000000..d284f67
--- /dev/null
+++ b/include/dblayer/mysql_innodb.php
@@ -0,0 +1,392 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysql_connect'))
+ exit('This PHP environment doesn\'t have MySQL support built in. MySQL support is required if you want to use a MySQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+ var $prefix;
+ var $link_id;
+ var $query_result;
+ var $in_transaction = 0;
+
+ var $saved_queries = array();
+ var $num_queries = 0;
+
+ var $error_no = false;
+ var $error_msg = 'Unknown';
+
+ var $datatype_transformations = array(
+ '%^SERIAL$%' => 'INT(10) UNSIGNED AUTO_INCREMENT'
+ );
+
+
+ function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->prefix = $db_prefix;
+
+ if ($p_connect)
+ $this->link_id = @mysql_pconnect($db_host, $db_username, $db_password);
+ else
+ $this->link_id = @mysql_connect($db_host, $db_username, $db_password);
+
+ if ($this->link_id)
+ {
+ if (!@mysql_select_db($db_name, $this->link_id))
+ error('Unable to select database. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+ }
+ else
+ error('Unable to connect to MySQL server. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+
+ // Setup the client-server character set (UTF-8)
+ if (!defined('FORUM_NO_SET_NAMES'))
+ $this->set_names('utf8');
+
+ return $this->link_id;
+ }
+
+
+ function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->__construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
+ }
+
+
+ function start_transaction()
+ {
+ ++$this->in_transaction;
+
+ mysql_query('START TRANSACTION', $this->link_id);
+ return;
+ }
+
+
+ function end_transaction()
+ {
+ --$this->in_transaction;
+
+ mysql_query('COMMIT', $this->link_id);
+ return;
+ }
+
+
+ function query($sql, $unbuffered = false)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $q_start = get_microtime();
+
+ if ($unbuffered)
+ $this->query_result = @mysql_unbuffered_query($sql, $this->link_id);
+ else
+ $this->query_result = @mysql_query($sql, $this->link_id);
+
+ if ($this->query_result)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, sprintf('%.5F', get_microtime() - $q_start));
+
+ ++$this->num_queries;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, 0);
+
+ $this->error_no = @mysql_errno($this->link_id);
+ $this->error_msg = @mysql_error($this->link_id);
+
+ // Rollback transaction
+ if ($this->in_transaction)
+ mysql_query('ROLLBACK', $this->link_id);
+
+ --$this->in_transaction;
+
+ return false;
+ }
+ }
+
+
+ function result($query_id = 0, $row = 0, $col = 0)
+ {
+ return ($query_id) ? @mysql_result($query_id, $row, $col) : false;
+ }
+
+
+ function fetch_assoc($query_id = 0)
+ {
+ return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+ }
+
+
+ function fetch_row($query_id = 0)
+ {
+ return ($query_id) ? @mysql_fetch_row($query_id) : false;
+ }
+
+
+ function num_rows($query_id = 0)
+ {
+ return ($query_id) ? @mysql_num_rows($query_id) : false;
+ }
+
+
+ function affected_rows()
+ {
+ return ($this->link_id) ? @mysql_affected_rows($this->link_id) : false;
+ }
+
+
+ function insert_id()
+ {
+ return ($this->link_id) ? @mysql_insert_id($this->link_id) : false;
+ }
+
+
+ function get_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+
+ function get_saved_queries()
+ {
+ return $this->saved_queries;
+ }
+
+
+ function free_result($query_id = false)
+ {
+ return ($query_id) ? @mysql_free_result($query_id) : false;
+ }
+
+
+ function escape($str)
+ {
+ if (is_array($str))
+ return '';
+ else if (function_exists('mysql_real_escape_string'))
+ return mysql_real_escape_string($str, $this->link_id);
+ else
+ return mysql_escape_string($str);
+ }
+
+
+ function error()
+ {
+ $result['error_sql'] = @current(@end($this->saved_queries));
+ $result['error_no'] = $this->error_no;
+ $result['error_msg'] = $this->error_msg;
+
+ return $result;
+ }
+
+
+ function close()
+ {
+ if ($this->link_id)
+ {
+ if (is_resource($this->query_result))
+ @mysql_free_result($this->query_result);
+
+ return @mysql_close($this->link_id);
+ }
+ else
+ return false;
+ }
+
+
+ function get_names()
+ {
+ $result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+ return $this->result($result, 0, 1);
+ }
+
+
+ function set_names($names)
+ {
+ return $this->query('SET NAMES \''.$this->escape($names).'\'');
+ }
+
+
+ function get_version()
+ {
+ $result = $this->query('SELECT VERSION()');
+
+ return array(
+ 'name' => 'MySQL Standard (InnoDB)',
+ 'version' => preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+ );
+ }
+
+
+ function table_exists($table_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function field_exists($table_name, $field_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function index_exists($table_name, $index_name, $no_prefix = false)
+ {
+ $exists = false;
+
+ $result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+ while ($cur_index = $this->fetch_assoc($result))
+ {
+ if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+ {
+ $exists = true;
+ break;
+ }
+ }
+
+ return $exists;
+ }
+
+
+ function create_table($table_name, $schema, $no_prefix = false)
+ {
+ if ($this->table_exists($table_name, $no_prefix))
+ return true;
+
+ $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+ // Go through every schema element and add it to the query
+ foreach ($schema['FIELDS'] as $field_name => $field_data)
+ {
+ $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+ $query .= $field_name.' '.$field_data['datatype'];
+
+ if (isset($field_data['collation']))
+ $query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+ if (!$field_data['allow_null'])
+ $query .= ' NOT NULL';
+
+ if (isset($field_data['default']))
+ $query .= ' DEFAULT '.$field_data['default'];
+
+ $query .= ",\n";
+ }
+
+ // If we have a primary key, add it
+ if (isset($schema['PRIMARY KEY']))
+ $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+ // Add unique keys
+ if (isset($schema['UNIQUE KEYS']))
+ {
+ foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+ $query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+ }
+
+ // Add indexes
+ if (isset($schema['INDEXES']))
+ {
+ foreach ($schema['INDEXES'] as $index_name => $index_fields)
+ $query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+ }
+
+ // We remove the last two characters (a newline and a comma) and add on the ending
+ $query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'InnoDB').' CHARACTER SET utf8';
+
+ return $this->query($query) ? true : false;
+ }
+
+
+ function drop_table($table_name, $no_prefix = false)
+ {
+ if (!$this->table_exists($table_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+
+
+ function rename_table($old_table, $new_table, $no_prefix = false)
+ {
+ // If the new table exists and the old one doesn't, then we're happy
+ if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+ }
+
+
+ function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if ($this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function drop_field($table_name, $field_name, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+ }
+
+
+ function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+ {
+ if ($this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+ }
+
+
+ function drop_index($table_name, $index_name, $no_prefix = false)
+ {
+ if (!$this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+ }
+
+ function truncate_table($table_name, $no_prefix = false)
+ {
+ return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+}
diff --git a/include/dblayer/mysqli.php b/include/dblayer/mysqli.php
new file mode 100644
index 0000000..05ae599
--- /dev/null
+++ b/include/dblayer/mysqli.php
@@ -0,0 +1,385 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysqli_connect'))
+ exit('This PHP environment doesn\'t have Improved MySQL (mysqli) support built in. Improved MySQL support is required if you want to use a MySQL 4.1 (or later) database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+ var $prefix;
+ var $link_id;
+ var $query_result;
+
+ var $saved_queries = array();
+ var $num_queries = 0;
+
+ var $error_no = false;
+ var $error_msg = 'Unknown';
+
+ var $datatype_transformations = array(
+ '%^SERIAL$%' => 'INT(10) UNSIGNED AUTO_INCREMENT'
+ );
+
+
+ function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->prefix = $db_prefix;
+
+ // Was a custom port supplied with $db_host?
+ if (strpos($db_host, ':') !== false)
+ list($db_host, $db_port) = explode(':', $db_host);
+
+ // Persistent connection in MySQLi are only available in PHP 5.3 and later releases
+ $p_connect = $p_connect && version_compare(PHP_VERSION, '5.3.0', '>=') ? 'p:' : '';
+
+ if (isset($db_port))
+ $this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name, $db_port);
+ else
+ $this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name);
+
+ if (!$this->link_id)
+ error('Unable to connect to MySQL and select database. MySQL reported: '.mysqli_connect_error(), __FILE__, __LINE__);
+
+ // Setup the client-server character set (UTF-8)
+ if (!defined('FORUM_NO_SET_NAMES'))
+ $this->set_names('utf8');
+
+ return $this->link_id;
+ }
+
+
+ function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->__construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
+ }
+
+
+ function start_transaction()
+ {
+ return;
+ }
+
+
+ function end_transaction()
+ {
+ return;
+ }
+
+
+ function query($sql, $unbuffered = false)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $q_start = get_microtime();
+
+ $this->query_result = @mysqli_query($this->link_id, $sql);
+
+ if ($this->query_result)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, sprintf('%.5F', get_microtime() - $q_start));
+
+ ++$this->num_queries;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, 0);
+
+ $this->error_no = @mysqli_errno($this->link_id);
+ $this->error_msg = @mysqli_error($this->link_id);
+
+ return false;
+ }
+ }
+
+
+ function result($query_id = 0, $row = 0, $col = 0)
+ {
+ if ($query_id)
+ {
+ if ($row !== 0 && @mysqli_data_seek($query_id, $row) === false)
+ return false;
+
+ $cur_row = @mysqli_fetch_row($query_id);
+ if ($cur_row === false)
+ return false;
+
+ return $cur_row[$col];
+ }
+ else
+ return false;
+ }
+
+
+ function fetch_assoc($query_id = 0)
+ {
+ return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+ }
+
+
+ function fetch_row($query_id = 0)
+ {
+ return ($query_id) ? @mysqli_fetch_row($query_id) : false;
+ }
+
+
+ function num_rows($query_id = 0)
+ {
+ return ($query_id) ? @mysqli_num_rows($query_id) : false;
+ }
+
+
+ function affected_rows()
+ {
+ return ($this->link_id) ? @mysqli_affected_rows($this->link_id) : false;
+ }
+
+
+ function insert_id()
+ {
+ return ($this->link_id) ? @mysqli_insert_id($this->link_id) : false;
+ }
+
+
+ function get_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+
+ function get_saved_queries()
+ {
+ return $this->saved_queries;
+ }
+
+
+ function free_result($query_id = false)
+ {
+ return ($query_id) ? @mysqli_free_result($query_id) : false;
+ }
+
+
+ function escape($str)
+ {
+ return is_array($str) ? '' : mysqli_real_escape_string($this->link_id, $str);
+ }
+
+
+ function error()
+ {
+ $result['error_sql'] = @current(@end($this->saved_queries));
+ $result['error_no'] = $this->error_no;
+ $result['error_msg'] = $this->error_msg;
+
+ return $result;
+ }
+
+
+ function close()
+ {
+ if ($this->link_id)
+ {
+ if ($this->query_result instanceof mysqli_result)
+ @mysqli_free_result($this->query_result);
+
+ return @mysqli_close($this->link_id);
+ }
+ else
+ return false;
+ }
+
+
+ function get_names()
+ {
+ $result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+ return $this->result($result, 0, 1);
+ }
+
+
+ function set_names($names)
+ {
+ return $this->query('SET NAMES \''.$this->escape($names).'\'');
+ }
+
+
+ function get_version()
+ {
+ $result = $this->query('SELECT VERSION()');
+
+ return array(
+ 'name' => 'MySQL Improved',
+ 'version' => preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+ );
+ }
+
+
+ function table_exists($table_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function field_exists($table_name, $field_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function index_exists($table_name, $index_name, $no_prefix = false)
+ {
+ $exists = false;
+
+ $result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+ while ($cur_index = $this->fetch_assoc($result))
+ {
+ if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+ {
+ $exists = true;
+ break;
+ }
+ }
+
+ return $exists;
+ }
+
+
+ function create_table($table_name, $schema, $no_prefix = false)
+ {
+ if ($this->table_exists($table_name, $no_prefix))
+ return true;
+
+ $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+ // Go through every schema element and add it to the query
+ foreach ($schema['FIELDS'] as $field_name => $field_data)
+ {
+ $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+ $query .= $field_name.' '.$field_data['datatype'];
+
+ if (isset($field_data['collation']))
+ $query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+ if (!$field_data['allow_null'])
+ $query .= ' NOT NULL';
+
+ if (isset($field_data['default']))
+ $query .= ' DEFAULT '.$field_data['default'];
+
+ $query .= ",\n";
+ }
+
+ // If we have a primary key, add it
+ if (isset($schema['PRIMARY KEY']))
+ $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+ // Add unique keys
+ if (isset($schema['UNIQUE KEYS']))
+ {
+ foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+ $query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+ }
+
+ // Add indexes
+ if (isset($schema['INDEXES']))
+ {
+ foreach ($schema['INDEXES'] as $index_name => $index_fields)
+ $query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+ }
+
+ // We remove the last two characters (a newline and a comma) and add on the ending
+ $query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'MyISAM').' CHARACTER SET utf8';
+
+ return $this->query($query) ? true : false;
+ }
+
+
+ function drop_table($table_name, $no_prefix = false)
+ {
+ if (!$this->table_exists($table_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+
+
+ function rename_table($old_table, $new_table, $no_prefix = false)
+ {
+ // If the new table exists and the old one doesn't, then we're happy
+ if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+ }
+
+
+ function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if ($this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function drop_field($table_name, $field_name, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+ }
+
+
+ function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+ {
+ if ($this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+ }
+
+
+ function drop_index($table_name, $index_name, $no_prefix = false)
+ {
+ if (!$this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+ }
+
+ function truncate_table($table_name, $no_prefix = false)
+ {
+ return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+}
diff --git a/include/dblayer/mysqli_innodb.php b/include/dblayer/mysqli_innodb.php
new file mode 100644
index 0000000..f132276
--- /dev/null
+++ b/include/dblayer/mysqli_innodb.php
@@ -0,0 +1,398 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysqli_connect'))
+ exit('This PHP environment doesn\'t have Improved MySQL (mysqli) support built in. Improved MySQL support is required if you want to use a MySQL 4.1 (or later) database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+ var $prefix;
+ var $link_id;
+ var $query_result;
+
+ var $saved_queries = array();
+ var $num_queries = 0;
+ var $in_transaction = 0;
+
+ var $error_no = false;
+ var $error_msg = 'Unknown';
+
+ var $datatype_transformations = array(
+ '%^SERIAL$%' => 'INT(10) UNSIGNED AUTO_INCREMENT'
+ );
+
+
+ function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->prefix = $db_prefix;
+
+ // Was a custom port supplied with $db_host?
+ if (strpos($db_host, ':') !== false)
+ list($db_host, $db_port) = explode(':', $db_host);
+
+ // Persistent connection in MySQLi are only available in PHP 5.3 and later releases
+ $p_connect = $p_connect && version_compare(PHP_VERSION, '5.3.0', '>=') ? 'p:' : '';
+
+ if (isset($db_port))
+ $this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name, $db_port);
+ else
+ $this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name);
+
+ if (!$this->link_id)
+ error('Unable to connect to MySQL and select database. MySQL reported: '.mysqli_connect_error(), __FILE__, __LINE__);
+
+ // Setup the client-server character set (UTF-8)
+ if (!defined('FORUM_NO_SET_NAMES'))
+ $this->set_names('utf8');
+
+ return $this->link_id;
+ }
+
+
+ function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->__construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
+ }
+
+
+ function start_transaction()
+ {
+ ++$this->in_transaction;
+
+ mysqli_query($this->link_id, 'START TRANSACTION');
+ return;
+ }
+
+
+ function end_transaction()
+ {
+ --$this->in_transaction;
+
+ mysqli_query($this->link_id, 'COMMIT');
+ return;
+ }
+
+
+ function query($sql, $unbuffered = false)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $q_start = get_microtime();
+
+ $this->query_result = @mysqli_query($this->link_id, $sql);
+
+ if ($this->query_result)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, sprintf('%.5F', get_microtime() - $q_start));
+
+ ++$this->num_queries;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, 0);
+
+ $this->error_no = @mysqli_errno($this->link_id);
+ $this->error_msg = @mysqli_error($this->link_id);
+
+ // Rollback transaction
+ if ($this->in_transaction)
+ mysqli_query($this->link_id, 'ROLLBACK');
+
+ --$this->in_transaction;
+
+ return false;
+ }
+ }
+
+
+ function result($query_id = 0, $row = 0, $col = 0)
+ {
+ if ($query_id)
+ {
+ if ($row !== 0 && @mysqli_data_seek($query_id, $row) === false)
+ return false;
+
+ $cur_row = @mysqli_fetch_row($query_id);
+ if ($cur_row === false)
+ return false;
+
+ return $cur_row[$col];
+ }
+ else
+ return false;
+ }
+
+
+ function fetch_assoc($query_id = 0)
+ {
+ return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+ }
+
+
+ function fetch_row($query_id = 0)
+ {
+ return ($query_id) ? @mysqli_fetch_row($query_id) : false;
+ }
+
+
+ function num_rows($query_id = 0)
+ {
+ return ($query_id) ? @mysqli_num_rows($query_id) : false;
+ }
+
+
+ function affected_rows()
+ {
+ return ($this->link_id) ? @mysqli_affected_rows($this->link_id) : false;
+ }
+
+
+ function insert_id()
+ {
+ return ($this->link_id) ? @mysqli_insert_id($this->link_id) : false;
+ }
+
+
+ function get_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+
+ function get_saved_queries()
+ {
+ return $this->saved_queries;
+ }
+
+
+ function free_result($query_id = false)
+ {
+ return ($query_id) ? @mysqli_free_result($query_id) : false;
+ }
+
+
+ function escape($str)
+ {
+ return is_array($str) ? '' : mysqli_real_escape_string($this->link_id, $str);
+ }
+
+
+ function error()
+ {
+ $result['error_sql'] = @current(@end($this->saved_queries));
+ $result['error_no'] = $this->error_no;
+ $result['error_msg'] = $this->error_msg;
+
+ return $result;
+ }
+
+
+ function close()
+ {
+ if ($this->link_id)
+ {
+ if ($this->query_result instanceof mysqli_result)
+ @mysqli_free_result($this->query_result);
+
+ return @mysqli_close($this->link_id);
+ }
+ else
+ return false;
+ }
+
+
+ function get_names()
+ {
+ $result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+ return $this->result($result, 0, 1);
+ }
+
+
+ function set_names($names)
+ {
+ return $this->query('SET NAMES \''.$this->escape($names).'\'');
+ }
+
+
+ function get_version()
+ {
+ $result = $this->query('SELECT VERSION()');
+
+ return array(
+ 'name' => 'MySQL Improved (InnoDB)',
+ 'version' => preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+ );
+ }
+
+
+ function table_exists($table_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function field_exists($table_name, $field_name, $no_prefix = false)
+ {
+ $result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function index_exists($table_name, $index_name, $no_prefix = false)
+ {
+ $exists = false;
+
+ $result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+ while ($cur_index = $this->fetch_assoc($result))
+ {
+ if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+ {
+ $exists = true;
+ break;
+ }
+ }
+
+ return $exists;
+ }
+
+
+ function create_table($table_name, $schema, $no_prefix = false)
+ {
+ if ($this->table_exists($table_name, $no_prefix))
+ return true;
+
+ $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+ // Go through every schema element and add it to the query
+ foreach ($schema['FIELDS'] as $field_name => $field_data)
+ {
+ $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+ $query .= $field_name.' '.$field_data['datatype'];
+
+ if (isset($field_data['collation']))
+ $query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+ if (!$field_data['allow_null'])
+ $query .= ' NOT NULL';
+
+ if (isset($field_data['default']))
+ $query .= ' DEFAULT '.$field_data['default'];
+
+ $query .= ",\n";
+ }
+
+ // If we have a primary key, add it
+ if (isset($schema['PRIMARY KEY']))
+ $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+ // Add unique keys
+ if (isset($schema['UNIQUE KEYS']))
+ {
+ foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+ $query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+ }
+
+ // Add indexes
+ if (isset($schema['INDEXES']))
+ {
+ foreach ($schema['INDEXES'] as $index_name => $index_fields)
+ $query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+ }
+
+ // We remove the last two characters (a newline and a comma) and add on the ending
+ $query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'InnoDB').' CHARACTER SET utf8';
+
+ return $this->query($query) ? true : false;
+ }
+
+
+ function drop_table($table_name, $no_prefix = false)
+ {
+ if (!$this->table_exists($table_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+
+
+ function rename_table($old_table, $new_table, $no_prefix = false)
+ {
+ // If the new table exists and the old one doesn't, then we're happy
+ if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+ }
+
+
+ function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if ($this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+ }
+
+
+ function drop_field($table_name, $field_name, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+ }
+
+
+ function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+ {
+ if ($this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+ }
+
+
+ function drop_index($table_name, $index_name, $no_prefix = false)
+ {
+ if (!$this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+ }
+
+ function truncate_table($table_name, $no_prefix = false)
+ {
+ return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+}
diff --git a/include/dblayer/pgsql.php b/include/dblayer/pgsql.php
new file mode 100644
index 0000000..8d13ad9
--- /dev/null
+++ b/include/dblayer/pgsql.php
@@ -0,0 +1,442 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for PostgreSQL
+if (!function_exists('pg_connect'))
+ exit('This PHP environment doesn\'t have PostgreSQL support built in. PostgreSQL support is required if you want to use a PostgreSQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+ var $prefix;
+ var $link_id;
+ var $query_result;
+ var $last_query_text = array();
+ var $in_transaction = 0;
+
+ var $saved_queries = array();
+ var $num_queries = 0;
+
+ var $error_no = false;
+ var $error_msg = 'Unknown';
+
+ var $datatype_transformations = array(
+ '%^(TINY|SMALL)INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i' => 'SMALLINT',
+ '%^(MEDIUM)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i' => 'INTEGER',
+ '%^BIGINT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i' => 'BIGINT',
+ '%^(TINY|MEDIUM|LONG)?TEXT$%i' => 'TEXT',
+ '%^DOUBLE( )?(\\([0-9,]+\\))?( )?(UNSIGNED)?$%i' => 'DOUBLE PRECISION',
+ '%^FLOAT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i' => 'REAL'
+ );
+
+
+ function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->prefix = $db_prefix;
+
+ if ($db_host)
+ {
+ if (strpos($db_host, ':') !== false)
+ {
+ list($db_host, $dbport) = explode(':', $db_host);
+ $connect_str[] = 'host='.$db_host.' port='.$dbport;
+ }
+ else
+ $connect_str[] = 'host='.$db_host;
+ }
+
+ if ($db_name)
+ $connect_str[] = 'dbname='.$db_name;
+
+ if ($db_username)
+ $connect_str[] = 'user='.$db_username;
+
+ if ($db_password)
+ $connect_str[] = 'password='.$db_password;
+
+ if ($p_connect)
+ $this->link_id = @pg_pconnect(implode(' ', $connect_str));
+ else
+ $this->link_id = @pg_connect(implode(' ', $connect_str));
+
+ if (!$this->link_id)
+ error('Unable to connect to PostgreSQL server', __FILE__, __LINE__);
+
+ // Setup the client-server character set (UTF-8)
+ if (!defined('FORUM_NO_SET_NAMES'))
+ $this->set_names('utf8');
+
+ return $this->link_id;
+ }
+
+
+ function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->__construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
+ }
+
+
+ function start_transaction()
+ {
+ ++$this->in_transaction;
+
+ return (@pg_query($this->link_id, 'BEGIN')) ? true : false;
+ }
+
+
+ function end_transaction()
+ {
+ --$this->in_transaction;
+
+ if (@pg_query($this->link_id, 'COMMIT'))
+ return true;
+ else
+ {
+ @pg_query($this->link_id, 'ROLLBACK');
+ return false;
+ }
+ }
+
+
+ function query($sql, $unbuffered = false) // $unbuffered is ignored since there is no pgsql_unbuffered_query()
+ {
+ if (strrpos($sql, 'LIMIT') !== false)
+ $sql = preg_replace('%LIMIT ([0-9]+),([ 0-9]+)%', 'LIMIT \\2 OFFSET \\1', $sql);
+
+ if (defined('PUN_SHOW_QUERIES'))
+ $q_start = get_microtime();
+
+ @pg_send_query($this->link_id, $sql);
+ $this->query_result = @pg_get_result($this->link_id);
+
+ if (pg_result_status($this->query_result) != PGSQL_FATAL_ERROR)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, sprintf('%.5F', get_microtime() - $q_start));
+
+ ++$this->num_queries;
+
+ $this->last_query_text[intval($this->query_result)] = $sql;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, 0);
+
+ $this->error_no = false;
+ $this->error_msg = @pg_result_error($this->query_result);
+
+ if ($this->in_transaction)
+ @pg_query($this->link_id, 'ROLLBACK');
+
+ --$this->in_transaction;
+
+ return false;
+ }
+ }
+
+
+ function result($query_id = 0, $row = 0, $col = 0)
+ {
+ return ($query_id) ? @pg_fetch_result($query_id, $row, $col) : false;
+ }
+
+
+ function fetch_assoc($query_id = 0)
+ {
+ return ($query_id) ? @pg_fetch_assoc($query_id) : false;
+ }
+
+
+ function fetch_row($query_id = 0)
+ {
+ return ($query_id) ? @pg_fetch_row($query_id) : false;
+ }
+
+
+ function num_rows($query_id = 0)
+ {
+ return ($query_id) ? @pg_num_rows($query_id) : false;
+ }
+
+
+ function affected_rows()
+ {
+ return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
+ }
+
+
+ function insert_id()
+ {
+ $query_id = $this->query_result;
+
+ if ($query_id && $this->last_query_text[intval($query_id)] != '')
+ {
+ if (preg_match('%^INSERT INTO ([a-z0-9\_\-]+)%is', $this->last_query_text[intval($query_id)], $table_name))
+ {
+ // Hack (don't ask)
+ if (substr($table_name[1], -6) == 'groups')
+ $table_name[1] .= '_g';
+
+ $temp_q_id = @pg_query($this->link_id, 'SELECT currval(\''.$table_name[1].'_id_seq\')');
+ return ($temp_q_id) ? intval(@pg_fetch_result($temp_q_id, 0)) : false;
+ }
+ }
+
+ return false;
+ }
+
+
+ function get_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+
+ function get_saved_queries()
+ {
+ return $this->saved_queries;
+ }
+
+
+ function free_result($query_id = false)
+ {
+ if (!$query_id)
+ $query_id = $this->query_result;
+
+ return ($query_id) ? @pg_free_result($query_id) : false;
+ }
+
+
+ function escape($str)
+ {
+ return is_array($str) ? '' : pg_escape_string($str);
+ }
+
+
+ function error()
+ {
+ $result['error_sql'] = @current(@end($this->saved_queries));
+ $result['error_no'] = $this->error_no;
+ $result['error_msg'] = $this->error_msg;
+
+ return $result;
+ }
+
+
+ function close()
+ {
+ if ($this->link_id)
+ {
+ if ($this->in_transaction)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array('COMMIT', 0);
+
+ @pg_query($this->link_id, 'COMMIT');
+ }
+
+ if ($this->query_result)
+ @pg_free_result($this->query_result);
+
+ return @pg_close($this->link_id);
+ }
+ else
+ return false;
+ }
+
+
+ function get_names()
+ {
+ $result = $this->query('SHOW client_encoding');
+ return strtolower($this->result($result)); // MySQL returns lowercase so lets be consistent
+ }
+
+
+ function set_names($names)
+ {
+ return $this->query('SET NAMES \''.$this->escape($names).'\'');
+ }
+
+
+ function get_version()
+ {
+ $result = $this->query('SELECT VERSION()');
+
+ return array(
+ 'name' => 'PostgreSQL',
+ 'version' => preg_replace('%^[^0-9]+([^\s,-]+).*$%', '\\1', $this->result($result))
+ );
+ }
+
+
+ function table_exists($table_name, $no_prefix = false)
+ {
+ $result = $this->query('SELECT 1 FROM pg_class WHERE relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function field_exists($table_name, $field_name, $no_prefix = false)
+ {
+ $result = $this->query('SELECT 1 FROM pg_class c INNER JOIN pg_attribute a ON a.attrelid = c.oid WHERE c.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND a.attname = \''.$this->escape($field_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function index_exists($table_name, $index_name, $no_prefix = false)
+ {
+ $result = $this->query('SELECT 1 FROM pg_index i INNER JOIN pg_class c1 ON c1.oid = i.indrelid INNER JOIN pg_class c2 ON c2.oid = i.indexrelid WHERE c1.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND c2.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function create_table($table_name, $schema, $no_prefix = false)
+ {
+ if ($this->table_exists($table_name, $no_prefix))
+ return true;
+
+ $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+ // Go through every schema element and add it to the query
+ foreach ($schema['FIELDS'] as $field_name => $field_data)
+ {
+ $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+ $query .= $field_name.' '.$field_data['datatype'];
+
+ // The SERIAL datatype is a special case where we don't need to say not null
+ if (!$field_data['allow_null'] && $field_data['datatype'] != 'SERIAL')
+ $query .= ' NOT NULL';
+
+ if (isset($field_data['default']))
+ $query .= ' DEFAULT '.$field_data['default'];
+
+ $query .= ",\n";
+ }
+
+ // If we have a primary key, add it
+ if (isset($schema['PRIMARY KEY']))
+ $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+ // Add unique keys
+ if (isset($schema['UNIQUE KEYS']))
+ {
+ foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+ $query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
+ }
+
+ // We remove the last two characters (a newline and a comma) and add on the ending
+ $query = substr($query, 0, strlen($query) - 2)."\n".')';
+
+ $result = $this->query($query) ? true : false;
+
+ // Add indexes
+ if (isset($schema['INDEXES']))
+ {
+ foreach ($schema['INDEXES'] as $index_name => $index_fields)
+ $result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
+ }
+
+ return $result;
+ }
+
+
+ function drop_table($table_name, $no_prefix = false)
+ {
+ if (!$this->table_exists($table_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+
+
+ function rename_table($old_table, $new_table, $no_prefix = false)
+ {
+ // If the new table exists and the old one doesn't, then we're happy
+ if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+ }
+
+
+ function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if ($this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ $result = $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type) ? true : false;
+
+ if (!is_null($default_value))
+ {
+ if (!is_int($default_value) && !is_float($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ $result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET DEFAULT '.$default_value) ? true : false;
+ $result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET '.$field_name.'='.$default_value) ? true : false;
+ }
+
+ if (!$allow_null)
+ $result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET NOT NULL') ? true : false;
+
+ return $result;
+ }
+
+
+ function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+ $result = $this->add_field($table_name, 'tmp_'.$field_name, $field_type, $allow_null, $default_value, $after_field, $no_prefix);
+ $result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET tmp_'.$field_name.' = '.$field_name) ? true : false;
+ $result &= $this->drop_field($table_name, $field_name, $no_prefix);
+ $result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' RENAME COLUMN tmp_'.$field_name.' TO '.$field_name) ? true : false;
+
+ return $result;
+ }
+
+
+ function drop_field($table_name, $field_name, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+ }
+
+
+ function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+ {
+ if ($this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
+ }
+
+
+ function drop_index($table_name, $index_name, $no_prefix = false)
+ {
+ if (!$this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+ }
+
+ function truncate_table($table_name, $no_prefix = false)
+ {
+ return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+}
diff --git a/include/dblayer/sqlite.php b/include/dblayer/sqlite.php
new file mode 100644
index 0000000..f9164fa
--- /dev/null
+++ b/include/dblayer/sqlite.php
@@ -0,0 +1,601 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for SQLite
+if (!function_exists('sqlite_open'))
+ exit('This PHP environment doesn\'t have SQLite support built in. SQLite support is required if you want to use a SQLite database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+ var $prefix;
+ var $link_id;
+ var $query_result;
+ var $in_transaction = 0;
+
+ var $saved_queries = array();
+ var $num_queries = 0;
+
+ var $error_no = false;
+ var $error_msg = 'Unknown';
+
+ var $datatype_transformations = array(
+ '%^SERIAL$%' => 'INTEGER',
+ '%^(TINY|SMALL|MEDIUM|BIG)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i' => 'INTEGER',
+ '%^(TINY|MEDIUM|LONG)?TEXT$%i' => 'TEXT'
+ );
+
+
+ function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ // Prepend $db_name with the path to the forum root directory
+ $db_name = PUN_ROOT.$db_name;
+
+ $this->prefix = $db_prefix;
+
+ if (!file_exists($db_name))
+ {
+ @touch($db_name);
+ @chmod($db_name, 0666);
+ if (!file_exists($db_name))
+ error('Unable to create new database \''.$db_name.'\'. Permission denied', __FILE__, __LINE__);
+ }
+
+ if (!is_readable($db_name))
+ error('Unable to open database \''.$db_name.'\' for reading. Permission denied', __FILE__, __LINE__);
+
+ if (!forum_is_writable($db_name))
+ error('Unable to open database \''.$db_name.'\' for writing. Permission denied', __FILE__, __LINE__);
+
+ if ($p_connect)
+ $this->link_id = @sqlite_popen($db_name, 0666, $sqlite_error);
+ else
+ $this->link_id = @sqlite_open($db_name, 0666, $sqlite_error);
+
+ if (!$this->link_id)
+ error('Unable to open database \''.$db_name.'\'. SQLite reported: '.$sqlite_error, __FILE__, __LINE__);
+ else
+ return $this->link_id;
+ }
+
+
+ function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+ {
+ $this->__construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
+ }
+
+
+ function start_transaction()
+ {
+ ++$this->in_transaction;
+
+ return (@sqlite_query($this->link_id, 'BEGIN')) ? true : false;
+ }
+
+
+ function end_transaction()
+ {
+ --$this->in_transaction;
+
+ if (@sqlite_query($this->link_id, 'COMMIT'))
+ return true;
+ else
+ {
+ @sqlite_query($this->link_id, 'ROLLBACK');
+ return false;
+ }
+ }
+
+
+ function query($sql, $unbuffered = false)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $q_start = get_microtime();
+
+ if ($unbuffered)
+ $this->query_result = @sqlite_unbuffered_query($this->link_id, $sql);
+ else
+ $this->query_result = @sqlite_query($this->link_id, $sql);
+
+ if ($this->query_result)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, sprintf('%.5F', get_microtime() - $q_start));
+
+ ++$this->num_queries;
+
+ return $this->query_result;
+ }
+ else
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array($sql, 0);
+
+ $this->error_no = @sqlite_last_error($this->link_id);
+ $this->error_msg = @sqlite_error_string($this->error_no);
+
+ if ($this->in_transaction)
+ @sqlite_query($this->link_id, 'ROLLBACK');
+
+ --$this->in_transaction;
+
+ return false;
+ }
+ }
+
+
+ function result($query_id = 0, $row = 0, $col = 0)
+ {
+ if ($query_id)
+ {
+ if ($row !== 0 && @sqlite_seek($query_id, $row) === false)
+ return false;
+
+ $cur_row = @sqlite_current($query_id);
+ if ($cur_row === false)
+ return false;
+
+ return $cur_row[$col];
+ }
+ else
+ return false;
+ }
+
+
+ function fetch_assoc($query_id = 0)
+ {
+ if ($query_id)
+ {
+ $cur_row = @sqlite_fetch_array($query_id, SQLITE_ASSOC);
+ if ($cur_row)
+ {
+ // Horrible hack to get rid of table names and table aliases from the array keys
+ foreach ($cur_row as $key => $value)
+ {
+ $dot_spot = strpos($key, '.');
+ if ($dot_spot !== false)
+ {
+ unset($cur_row[$key]);
+ $key = substr($key, $dot_spot+1);
+ $cur_row[$key] = $value;
+ }
+ }
+ }
+
+ return $cur_row;
+ }
+ else
+ return false;
+ }
+
+
+ function fetch_row($query_id = 0)
+ {
+ return ($query_id) ? @sqlite_fetch_array($query_id, SQLITE_NUM) : false;
+ }
+
+
+ function num_rows($query_id = 0)
+ {
+ return ($query_id) ? @sqlite_num_rows($query_id) : false;
+ }
+
+
+ function affected_rows()
+ {
+ return ($this->link_id) ? @sqlite_changes($this->link_id) : false;
+ }
+
+
+ function insert_id()
+ {
+ return ($this->link_id) ? @sqlite_last_insert_rowid($this->link_id) : false;
+ }
+
+
+ function get_num_queries()
+ {
+ return $this->num_queries;
+ }
+
+
+ function get_saved_queries()
+ {
+ return $this->saved_queries;
+ }
+
+
+ function free_result($query_id = false)
+ {
+ return true;
+ }
+
+
+ function escape($str)
+ {
+ return is_array($str) ? '' : sqlite_escape_string($str);
+ }
+
+
+ function error()
+ {
+ $result['error_sql'] = @current(@end($this->saved_queries));
+ $result['error_no'] = $this->error_no;
+ $result['error_msg'] = $this->error_msg;
+
+ return $result;
+ }
+
+
+ function close()
+ {
+ if ($this->link_id)
+ {
+ if ($this->in_transaction)
+ {
+ if (defined('PUN_SHOW_QUERIES'))
+ $this->saved_queries[] = array('COMMIT', 0);
+
+ @sqlite_query($this->link_id, 'COMMIT');
+ }
+
+ return @sqlite_close($this->link_id);
+ }
+ else
+ return false;
+ }
+
+
+ function get_names()
+ {
+ return '';
+ }
+
+
+ function set_names($names)
+ {
+ return true;
+ }
+
+
+ function get_version()
+ {
+ return array(
+ 'name' => 'SQLite',
+ 'version' => sqlite_libversion()
+ );
+ }
+
+
+ function table_exists($table_name, $no_prefix = false)
+ {
+ $result = $this->query('SELECT 1 FROM sqlite_master WHERE name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND type=\'table\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function field_exists($table_name, $field_name, $no_prefix = false)
+ {
+ $result = $this->query('SELECT sql FROM sqlite_master WHERE name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND type=\'table\'');
+ if (!$this->num_rows($result))
+ return false;
+
+ return preg_match('%[\r\n]'.preg_quote($field_name, '%').' %', $this->result($result));
+ }
+
+
+ function index_exists($table_name, $index_name, $no_prefix = false)
+ {
+ $result = $this->query('SELECT 1 FROM sqlite_master WHERE tbl_name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\' AND type=\'index\'');
+ return $this->num_rows($result) > 0;
+ }
+
+
+ function create_table($table_name, $schema, $no_prefix = false)
+ {
+ if ($this->table_exists($table_name, $no_prefix))
+ return true;
+
+ $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+ // Go through every schema element and add it to the query
+ foreach ($schema['FIELDS'] as $field_name => $field_data)
+ {
+ $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+ $query .= $field_name.' '.$field_data['datatype'];
+
+ if (!$field_data['allow_null'])
+ $query .= ' NOT NULL';
+
+ if (isset($field_data['default']))
+ $query .= ' DEFAULT '.$field_data['default'];
+
+ $query .= ",\n";
+ }
+
+ // If we have a primary key, add it
+ if (isset($schema['PRIMARY KEY']))
+ $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+ // Add unique keys
+ if (isset($schema['UNIQUE KEYS']))
+ {
+ foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+ $query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
+ }
+
+ // We remove the last two characters (a newline and a comma) and add on the ending
+ $query = substr($query, 0, strlen($query) - 2)."\n".')';
+
+ $result = $this->query($query) ? true : false;
+
+ // Add indexes
+ if (isset($schema['INDEXES']))
+ {
+ foreach ($schema['INDEXES'] as $index_name => $index_fields)
+ $result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
+ }
+
+ return $result;
+ }
+
+
+ function drop_table($table_name, $no_prefix = false)
+ {
+ if (!$this->table_exists($table_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+ }
+
+
+ function rename_table($old_table, $new_table, $no_prefix = false)
+ {
+ // If the old table does not exist
+ if (!$this->table_exists($old_table, $no_prefix))
+ return false;
+ // If the table names are the same
+ else if ($old_table == $new_table)
+ return true;
+ // If the new table already exists
+ else if ($this->table_exists($new_table, $no_prefix))
+ return false;
+
+ $table = $this->get_table_info($old_table, $no_prefix);
+
+ // Create new table
+ $query = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($old_table).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($new_table).' (', $table['sql']);
+ $result = $this->query($query) ? true : false;
+
+ // Recreate indexes
+ if (!empty($table['indices']))
+ {
+ foreach ($table['indices'] as $cur_index)
+ {
+ $query = str_replace('CREATE INDEX '.($no_prefix ? '' : $this->prefix).$this->escape($old_table), 'CREATE INDEX '.($no_prefix ? '' : $this->prefix).$this->escape($new_table), $cur_index);
+ $query = str_replace('ON '.($no_prefix ? '' : $this->prefix).$this->escape($old_table), 'ON '.($no_prefix ? '' : $this->prefix).$this->escape($new_table), $query);
+ $result &= $this->query($query) ? true : false;
+ }
+ }
+
+ // Copy content across
+ $result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($new_table).' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($old_table)) ? true : false;
+
+ // Drop the old table if the new one exists
+ if ($this->table_exists($new_table, $no_prefix))
+ $result &= $this->drop_table($old_table, $no_prefix);
+
+ return $result;
+ }
+
+
+ function get_table_info($table_name, $no_prefix = false)
+ {
+ // Grab table info
+ $result = $this->query('SELECT sql FROM sqlite_master WHERE tbl_name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' ORDER BY type DESC') or error('Unable to fetch table information', __FILE__, __LINE__, $this->error());
+ $num_rows = $this->num_rows($result);
+
+ if ($num_rows == 0)
+ return;
+
+ $table = array();
+ $table['indices'] = array();
+ while ($cur_index = $this->fetch_assoc($result))
+ {
+ if (empty($cur_index['sql']))
+ continue;
+
+ if (!isset($table['sql']))
+ $table['sql'] = $cur_index['sql'];
+ else
+ $table['indices'][] = $cur_index['sql'];
+ }
+
+ // Work out the columns in the table currently
+ $table_lines = explode("\n", $table['sql']);
+ $table['columns'] = array();
+ foreach ($table_lines as $table_line)
+ {
+ $table_line = trim($table_line, " \t\n\r,"); // trim spaces, tabs, newlines, and commas
+ if (substr($table_line, 0, 12) == 'CREATE TABLE')
+ continue;
+ else if (substr($table_line, 0, 11) == 'PRIMARY KEY')
+ $table['primary_key'] = $table_line;
+ else if (substr($table_line, 0, 6) == 'UNIQUE')
+ $table['unique'] = $table_line;
+ else if (substr($table_line, 0, strpos($table_line, ' ')) != '')
+ $table['columns'][substr($table_line, 0, strpos($table_line, ' '))] = trim(substr($table_line, strpos($table_line, ' ')));
+ }
+
+ return $table;
+ }
+
+
+ function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ if ($this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $table = $this->get_table_info($table_name, $no_prefix);
+
+ // Create temp table
+ $now = time();
+ $tmptable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' (', $table['sql']);
+ $result = $this->query($tmptable) ? true : false;
+ $result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+
+ // Create new table sql
+ $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+ $query = $field_type;
+
+ if (!$allow_null)
+ $query .= ' NOT NULL';
+
+ if (is_string($default_value))
+ $default_value = '\''.$this->escape($default_value).'\'';
+
+ if (!is_null($default_value))
+ $query .= ' DEFAULT '.$default_value;
+
+ $old_columns = array_keys($table['columns']);
+
+ // Determine the proper offset
+ if (!is_null($after_field))
+ $offset = array_search($after_field, array_keys($table['columns']), true) + 1;
+ else
+ $offset = count($table['columns']);
+
+ // Out of bounds checks
+ if ($offset > count($table['columns']))
+ $offset = count($table['columns']);
+ else if ($offset < 0)
+ $offset = 0;
+
+ if (!is_null($field_name) && $field_name !== '')
+ $table['columns'] = array_merge(array_slice($table['columns'], 0, $offset), array($field_name => $query), array_slice($table['columns'], $offset));
+
+ $new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
+
+ foreach ($table['columns'] as $cur_column => $column_details)
+ $new_table .= "\n".$cur_column.' '.$column_details.',';
+
+ if (isset($table['unique']))
+ $new_table .= "\n".$table['unique'].',';
+
+ if (isset($table['primary_key']))
+ $new_table .= "\n".$table['primary_key'].',';
+
+ $new_table = trim($new_table, ',')."\n".');';
+
+ // Drop old table
+ $result &= $this->drop_table($table_name, $no_prefix);
+
+ // Create new table
+ $result &= $this->query($new_table) ? true : false;
+
+ // Recreate indexes
+ if (!empty($table['indices']))
+ {
+ foreach ($table['indices'] as $cur_index)
+ $result &= $this->query($cur_index) ? true : false;
+ }
+
+ // Copy content back
+ $result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' ('.implode(', ', $old_columns).') SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
+
+ // Drop temp table
+ $result &= $this->drop_table($table_name.'_t'.$now, $no_prefix);
+
+ return $result;
+ }
+
+
+ function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+ {
+ // Unneeded for SQLite
+ return true;
+ }
+
+
+ function drop_field($table_name, $field_name, $no_prefix = false)
+ {
+ if (!$this->field_exists($table_name, $field_name, $no_prefix))
+ return true;
+
+ $table = $this->get_table_info($table_name, $no_prefix);
+
+ // Create temp table
+ $now = time();
+ $tmptable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' (', $table['sql']);
+ $result = $this->query($tmptable) ? true : false;
+ $result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+
+ // Work out the columns we need to keep and the sql for the new table
+ unset($table['columns'][$field_name]);
+ $new_columns = array_keys($table['columns']);
+
+ $new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
+
+ foreach ($table['columns'] as $cur_column => $column_details)
+ $new_table .= "\n".$cur_column.' '.$column_details.',';
+
+ if (isset($table['unique']))
+ $new_table .= "\n".$table['unique'].',';
+
+ if (isset($table['primary_key']))
+ $new_table .= "\n".$table['primary_key'].',';
+
+ $new_table = trim($new_table, ',')."\n".');';
+
+ // Drop old table
+ $result &= $this->drop_table($table_name, $no_prefix);
+
+ // Create new table
+ $result &= $this->query($new_table) ? true : false;
+
+ // Recreate indexes
+ if (!empty($table['indices']))
+ {
+ foreach ($table['indices'] as $cur_index)
+ if (!preg_match('%\('.preg_quote($field_name, '%').'\)%', $cur_index))
+ $result &= $this->query($cur_index) ? true : false;
+ }
+
+ // Copy content back
+ $result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' SELECT '.implode(', ', $new_columns).' FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
+
+ // Drop temp table
+ $result &= $this->drop_table($table_name.'_t'.$now, $no_prefix);
+
+ return $result;
+ }
+
+
+ function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+ {
+ if ($this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
+ }
+
+
+ function drop_index($table_name, $index_name, $no_prefix = false)
+ {
+ if (!$this->index_exists($table_name, $index_name, $no_prefix))
+ return true;
+
+ return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+ }
+
+ function truncate_table($table_name, $no_prefix = false)
+ {
+ return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+ }
+}
diff --git a/include/email.php b/include/email.php
new file mode 100644
index 0000000..b66b584
--- /dev/null
+++ b/include/email.php
@@ -0,0 +1,364 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+// Define line breaks in mail headers; possible values can be PHP_EOL, "\r\n", "\n" or "\r"
+if (!defined('FORUM_EOL'))
+ define('FORUM_EOL', PHP_EOL);
+
+require PUN_ROOT.'include/utf8/utils/ascii.php';
+
+//
+// Validate an email address
+//
+function is_valid_email($email)
+{
+ if (strlen($email) > 80)
+ return false;
+
+ return preg_match('%^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|("[^"]+"))@((\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\])|(([a-zA-Z\d\-]+\.)+[a-zA-Z]{2,}))$%', $email);
+}
+
+
+//
+// Check if $email is banned
+//
+function is_banned_email($email)
+{
+ global $pun_bans;
+
+ foreach ($pun_bans as $cur_ban)
+ {
+ if ($cur_ban['email'] != '' &&
+ ($email == $cur_ban['email'] ||
+ (strpos($cur_ban['email'], '@') === false && stristr($email, '@'.$cur_ban['email']))))
+ return true;
+ }
+
+ return false;
+}
+
+
+//
+// Only encode with base64, if there is at least one unicode character in the string
+//
+function encode_mail_text($str)
+{
+ if (utf8_is_ascii($str))
+ return $str;
+
+ return '=?UTF-8?B?'.base64_encode($str).'?=';
+}
+
+
+//
+// Make a post email safe
+//
+function bbcode2email($text, $wrap_length = 72)
+{
+ static $base_url;
+
+ if (!isset($base_url))
+ $base_url = get_base_url();
+
+ $text = pun_trim($text, "\t\n ");
+
+ $shortcut_urls = array(
+ 'topic' => '/viewtopic.php?id=$1',
+ 'post' => '/viewtopic.php?pid=$1#p$1',
+ 'forum' => '/viewforum.php?id=$1',
+ 'user' => '/profile.php?id=$1',
+ );
+
+ // Split code blocks and text so BBcode in codeblocks won't be touched
+ list($code, $text) = extract_blocks($text, '[code]', '[/code]');
+
+ // Strip all bbcodes, except the quote, url, img, email, code and list items bbcodes
+ $text = preg_replace(array(
+ '%\[/?(?!(?:quote|url|topic|post|user|forum|img|email|code|list|\*))[a-z]+(?:=[^\]]+)?\]%i',
+ '%\n\[/?list(?:=[^\]]+)?\]%i' // A separate regex for the list tags to get rid of some whitespace
+ ), '', $text);
+
+ // Match the deepest nested bbcode
+ // An adapted example from Mastering Regular Expressions
+ $match_quote_regex = '%
+ \[(quote|\*|url|img|email|topic|post|user|forum)(?:=([^\]]+))?\]
+ (
+ (?>[^\[]*)
+ (?>
+ (?!\[/?\1(?:=[^\]]+)?\])
+ \[
+ [^\[]*
+ )*
+ )
+ \[/\1\]
+ %ix';
+
+ $url_index = 1;
+ $url_stack = array();
+ while (preg_match($match_quote_regex, $text, $matches))
+ {
+ // Quotes
+ if ($matches[1] == 'quote')
+ {
+ // Put '>' or '> ' at the start of a line
+ $replacement = preg_replace(
+ array('%^(?=\>)%m', '%^(?!\>)%m'),
+ array('>', '> '),
+ $matches[2].":\n".$matches[3]);
+ }
+
+ // List items
+ elseif ($matches[1] == '*')
+ {
+ $replacement = ' * '.$matches[3];
+ }
+
+ // URLs and emails
+ elseif (in_array($matches[1], array('url', 'email')))
+ {
+ if (!empty($matches[2]))
+ {
+ $replacement = '['.$matches[3].']['.$url_index.']';
+ $url_stack[$url_index] = $matches[2];
+ $url_index++;
+ }
+ else
+ $replacement = '['.$matches[3].']';
+ }
+
+ // Images
+ elseif ($matches[1] == 'img')
+ {
+ if (!empty($matches[2]))
+ $replacement = '['.$matches[2].']['.$url_index.']';
+ else
+ $replacement = '['.basename($matches[3]).']['.$url_index.']';
+
+ $url_stack[$url_index] = $matches[3];
+ $url_index++;
+ }
+
+ // Topic, post, forum and user URLs
+ elseif (in_array($matches[1], array('topic', 'post', 'forum', 'user')))
+ {
+ $url = isset($shortcut_urls[$matches[1]]) ? $base_url.$shortcut_urls[$matches[1]] : '';
+
+ if (!empty($matches[2]))
+ {
+ $replacement = '['.$matches[3].']['.$url_index.']';
+ $url_stack[$url_index] = str_replace('$1', $matches[2], $url);
+ $url_index++;
+ }
+ else
+ $replacement = '['.str_replace('$1', $matches[3], $url).']';
+ }
+
+ // Update the main text if there is a replacement
+ if (!is_null($replacement))
+ {
+ $text = str_replace($matches[0], $replacement, $text);
+ $replacement = null;
+ }
+ }
+
+ // Put code blocks and text together
+ if (isset($code))
+ {
+ $parts = explode("\1", $text);
+ $text = '';
+ foreach ($parts as $i => $part)
+ {
+ $text .= $part;
+ if (isset($code[$i]))
+ $text .= trim($code[$i], "\n\r");
+ }
+ }
+
+ // Put URLs at the bottom
+ if ($url_stack)
+ {
+ $text .= "\n\n";
+ foreach ($url_stack as $i => $url)
+ $text .= "\n".' ['.$i.']: '.$url;
+ }
+
+ // Wrap lines if $wrap_length is higher than -1
+ if ($wrap_length > -1)
+ {
+ // Split all lines and wrap them individually
+ $parts = explode("\n", $text);
+ foreach ($parts as $k => $part)
+ {
+ preg_match('%^(>+ )?(.*)%', $part, $matches);
+ $parts[$k] = wordwrap($matches[1].$matches[2], $wrap_length -
+ strlen($matches[1]), "\n".$matches[1]);
+ }
+
+ return implode("\n", $parts);
+ }
+ else
+ return $text;
+}
+
+
+//
+// Wrapper for PHP's mail()
+//
+function pun_mail($to, $subject, $message, $reply_to_email = '', $reply_to_name = '')
+{
+ global $pun_config, $lang_common;
+
+ // Use \r\n for SMTP servers, the system's line ending for local mailers
+ $smtp = $pun_config['o_smtp_host'] != '';
+ $EOL = $smtp ? "\r\n" : FORUM_EOL;
+
+ // Default sender/return address
+ $from_name = sprintf($lang_common['Mailer'], $pun_config['o_board_title']);
+ $from_email = $pun_config['o_webmaster_email'];
+
+ // Do a little spring cleaning
+ $to = pun_trim(preg_replace('%[\n\r]+%s', '', $to));
+ $subject = pun_trim(preg_replace('%[\n\r]+%s', '', $subject));
+ $from_email = pun_trim(preg_replace('%[\n\r:]+%s', '', $from_email));
+ $from_name = pun_trim(preg_replace('%[\n\r:]+%s', '', str_replace('"', '', $from_name)));
+ $reply_to_email = pun_trim(preg_replace('%[\n\r:]+%s', '', $reply_to_email));
+ $reply_to_name = pun_trim(preg_replace('%[\n\r:]+%s', '', str_replace('"', '', $reply_to_name)));
+
+ // Set up some headers to take advantage of UTF-8
+ $from = '"'.encode_mail_text($from_name).'" <'.$from_email.'>';
+ $subject = encode_mail_text($subject);
+
+ $headers = 'From: '.$from.$EOL.'Date: '.gmdate('r').$EOL.'MIME-Version: 1.0'.$EOL.'Content-transfer-encoding: 8bit'.$EOL.'Content-type: text/plain; charset=utf-8'.$EOL.'X-Mailer: FluxBB Mailer';
+
+ // If we specified a reply-to email, we deal with it here
+ if (!empty($reply_to_email))
+ {
+ $reply_to = '"'.encode_mail_text($reply_to_name).'" <'.$reply_to_email.'>';
+
+ $headers .= $EOL.'Reply-To: '.$reply_to;
+ }
+
+ // Make sure all linebreaks are LF in message (and strip out any NULL bytes)
+ $message = str_replace("\0", '', pun_linebreaks($message));
+ $message = str_replace("\n", $EOL, $message);
+
+ $mailer = $smtp ? 'smtp_mail' : 'mail';
+ $mailer($to, $subject, $message, $headers);
+}
+
+
+//
+// This function was originally a part of the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+// They deserve all the credit for writing it. I made small modifications for it to suit PunBB and its coding standards
+//
+function server_parse($socket, $expected_response)
+{
+ $server_response = '';
+ while (substr($server_response, 3, 1) != ' ')
+ {
+ if (!($server_response = fgets($socket, 256)))
+ error('Couldn\'t get mail server response codes. Please contact the forum administrator.', __FILE__, __LINE__);
+ }
+
+ if (!(substr($server_response, 0, 3) == $expected_response))
+ error('Unable to send email. Please contact the forum administrator with the following error message reported by the SMTP server: "'.$server_response.'"', __FILE__, __LINE__);
+}
+
+
+//
+// This function was originally a part of the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+// They deserve all the credit for writing it. I made small modifications for it to suit PunBB and its coding standards.
+//
+function smtp_mail($to, $subject, $message, $headers = '')
+{
+ global $pun_config;
+ static $local_host;
+
+ $recipients = explode(',', $to);
+
+ // Sanitize the message
+ $message = str_replace("\r\n.", "\r\n..", $message);
+ $message = (substr($message, 0, 1) == '.' ? '.'.$message : $message);
+
+ // Are we using port 25 or a custom port?
+ if (strpos($pun_config['o_smtp_host'], ':') !== false)
+ list($smtp_host, $smtp_port) = explode(':', $pun_config['o_smtp_host']);
+ else
+ {
+ $smtp_host = $pun_config['o_smtp_host'];
+ $smtp_port = 25;
+ }
+
+ if ($pun_config['o_smtp_ssl'] == '1')
+ $smtp_host = 'ssl://'.$smtp_host;
+
+ if (!($socket = fsockopen($smtp_host, $smtp_port, $errno, $errstr, 15)))
+ error('Could not connect to smtp host "'.$pun_config['o_smtp_host'].'" ('.$errno.') ('.$errstr.')', __FILE__, __LINE__);
+
+ server_parse($socket, '220');
+
+ if (!isset($local_host))
+ {
+ // Here we try to determine the *real* hostname (reverse DNS entry preferably)
+ $local_host = php_uname('n');
+
+ // Able to resolve name to IP
+ if (($local_addr = @gethostbyname($local_host)) !== $local_host)
+ {
+ // Able to resolve IP back to name
+ if (($local_name = @gethostbyaddr($local_addr)) !== $local_addr)
+ $local_host = $local_name;
+ }
+ }
+
+ if ($pun_config['o_smtp_user'] != '' && $pun_config['o_smtp_pass'] != '')
+ {
+ fwrite($socket, 'EHLO '.$local_host."\r\n");
+ server_parse($socket, '250');
+
+ fwrite($socket, 'AUTH LOGIN'."\r\n");
+ server_parse($socket, '334');
+
+ fwrite($socket, base64_encode($pun_config['o_smtp_user'])."\r\n");
+ server_parse($socket, '334');
+
+ fwrite($socket, base64_encode($pun_config['o_smtp_pass'])."\r\n");
+ server_parse($socket, '235');
+ }
+ else
+ {
+ fwrite($socket, 'HELO '.$local_host."\r\n");
+ server_parse($socket, '250');
+ }
+
+ fwrite($socket, 'MAIL FROM: <'.$pun_config['o_webmaster_email'].'>'."\r\n");
+ server_parse($socket, '250');
+
+ foreach ($recipients as $email)
+ {
+ fwrite($socket, 'RCPT TO: <'.$email.'>'."\r\n");
+ server_parse($socket, '250');
+ }
+
+ fwrite($socket, 'DATA'."\r\n");
+ server_parse($socket, '354');
+
+ fwrite($socket, 'Subject: '.$subject."\r\n".'To: <'.implode('>, <', $recipients).'>'."\r\n".$headers."\r\n\r\n".$message."\r\n");
+
+ fwrite($socket, '.'."\r\n");
+ server_parse($socket, '250');
+
+ fwrite($socket, 'QUIT'."\r\n");
+ fclose($socket);
+
+ return true;
+}
diff --git a/include/functions.php b/include/functions.php
new file mode 100644
index 0000000..ace2934
--- /dev/null
+++ b/include/functions.php
@@ -0,0 +1,2227 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+
+
+//
+// Return current timestamp (with microseconds) as a float
+//
+function get_microtime()
+{
+ list($usec, $sec) = explode(' ', microtime());
+ return ((float)$usec + (float)$sec);
+}
+
+//
+// Cookie stuff!
+//
+function check_cookie(&$pun_user)
+{
+ global $db, $db_type, $pun_config, $cookie_name, $cookie_seed;
+
+ $now = time();
+
+ // If the cookie is set and it matches the correct pattern, then read the values from it
+ if (isset($_COOKIE[$cookie_name]) && preg_match('%^(\d+)\|([0-9a-fA-F]+)\|(\d+)\|([0-9a-fA-F]+)$%', $_COOKIE[$cookie_name], $matches))
+ {
+ $cookie = array(
+ 'user_id' => intval($matches[1]),
+ 'password_hash' => $matches[2],
+ 'expiration_time' => intval($matches[3]),
+ 'cookie_hash' => $matches[4],
+ );
+ }
+
+ // If it has a non-guest user, and hasn't expired
+ if (isset($cookie) && $cookie['user_id'] > 1 && $cookie['expiration_time'] > $now)
+ {
+ // If the cookie has been tampered with
+ $is_authorized = pun_hash_equals(forum_hmac($cookie['user_id'].'|'.$cookie['expiration_time'], $cookie_seed.'_cookie_hash'), $cookie['cookie_hash']);
+ if (!$is_authorized)
+ {
+ $expire = $now + 31536000; // The cookie expires after a year
+ pun_setcookie(1, pun_hash(uniqid(rand(), true)), $expire);
+ set_default_user();
+
+ return;
+ }
+
+ // Check if there's a user with the user ID and password hash from the cookie
+ $result = $db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.intval($cookie['user_id'])) or error('Unable to fetch user information', __FILE__, __LINE__, $db->error());
+ $pun_user = $db->fetch_assoc($result);
+
+ // If user authorisation failed
+ $is_authorized = pun_hash_equals(forum_hmac($pun_user['password'], $cookie_seed.'_password_hash'), $cookie['password_hash']);
+ if (!isset($pun_user['id']) || !$is_authorized)
+ {
+ $expire = $now + 31536000; // The cookie expires after a year
+ pun_setcookie(1, pun_hash(uniqid(rand(), true)), $expire);
+ set_default_user();
+
+ return;
+ }
+
+ // Send a new, updated cookie with a new expiration timestamp
+ $expire = ($cookie['expiration_time'] > $now + $pun_config['o_timeout_visit']) ? $now + 1209600 : $now + $pun_config['o_timeout_visit'];
+ pun_setcookie($pun_user['id'], $pun_user['password'], $expire);
+
+ // Set a default language if the user selected language no longer exists
+ if (!file_exists(PUN_ROOT.'lang/'.$pun_user['language']))
+ $pun_user['language'] = $pun_config['o_default_lang'];
+
+ // Set a default style if the user selected style no longer exists
+ if (!file_exists(PUN_ROOT.'style/'.$pun_user['style'].'.css'))
+ $pun_user['style'] = $pun_config['o_default_style'];
+
+ if (!$pun_user['disp_topics'])
+ $pun_user['disp_topics'] = $pun_config['o_disp_topics_default'];
+ if (!$pun_user['disp_posts'])
+ $pun_user['disp_posts'] = $pun_config['o_disp_posts_default'];
+
+ // Define this if you want this visit to affect the online list and the users last visit data
+ if (!defined('PUN_QUIET_VISIT'))
+ {
+ // Update the online list
+ if (!$pun_user['logged'])
+ {
+ $pun_user['logged'] = $now;
+
+ // With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ case 'sqlite':
+ $db->query('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged) VALUES('.$pun_user['id'].', \''.$db->escape($pun_user['username']).'\', '.$pun_user['logged'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+ break;
+
+ default:
+ $db->query('INSERT INTO '.$db->prefix.'online (user_id, ident, logged) SELECT '.$pun_user['id'].', \''.$db->escape($pun_user['username']).'\', '.$pun_user['logged'].' WHERE NOT EXISTS (SELECT 1 FROM '.$db->prefix.'online WHERE user_id='.$pun_user['id'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+ break;
+ }
+
+ // Reset tracked topics
+ set_tracked_topics(null);
+ }
+ else
+ {
+ // Special case: We've timed out, but no other user has browsed the forums since we timed out
+ if ($pun_user['logged'] < ($now-$pun_config['o_timeout_visit']))
+ {
+ $db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
+ $pun_user['last_visit'] = $pun_user['logged'];
+ }
+
+ $idle_sql = ($pun_user['idle'] == '1') ? ', idle=0' : '';
+ $db->query('UPDATE '.$db->prefix.'online SET logged='.$now.$idle_sql.' WHERE user_id='.$pun_user['id']) or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+ // Update tracked topics with the current expire time
+ if (isset($_COOKIE[$cookie_name.'_track']))
+ forum_setcookie($cookie_name.'_track', $_COOKIE[$cookie_name.'_track'], $now + $pun_config['o_timeout_visit']);
+ }
+ }
+ else
+ {
+ if (!$pun_user['logged'])
+ $pun_user['logged'] = $pun_user['last_visit'];
+ }
+
+ $pun_user['is_guest'] = false;
+ $pun_user['is_admmod'] = $pun_user['g_id'] == PUN_ADMIN || $pun_user['g_moderator'] == '1';
+ }
+ else
+ set_default_user();
+}
+
+
+//
+// Converts the CDATA end sequence ]]> into ]]&gt;
+//
+function escape_cdata($str)
+{
+ return str_replace(']]>', ']]&gt;', $str);
+}
+
+
+//
+// Authenticates the provided username and password against the user database
+// $user can be either a user ID (integer) or a username (string)
+// $password can be either a plaintext password or a password hash including salt ($password_is_hash must be set accordingly)
+//
+function authenticate_user($user, $password, $password_is_hash = false)
+{
+ global $db, $pun_user;
+
+ // Check if there's a user matching $user and $password
+ $result = $db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE '.(is_int($user) ? 'u.id='.intval($user) : 'u.username=\''.$db->escape($user).'\'')) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ $pun_user = $db->fetch_assoc($result);
+
+ $is_password_authorized = pun_hash_equals($password, $pun_user['password']);
+ $is_hash_authorized = pun_hash_equals(pun_hash($password), $pun_user['password']);
+
+ if (!isset($pun_user['id']) ||
+ ($password_is_hash && !$is_password_authorized ||
+ (!$password_is_hash && !$is_hash_authorized)))
+ set_default_user();
+ else
+ $pun_user['is_guest'] = false;
+}
+
+
+//
+// Try to determine the current URL
+//
+function get_current_url($max_length = 0)
+{
+ $protocol = get_current_protocol();
+ $port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'] != '80' && $protocol == 'http') || ($_SERVER['SERVER_PORT'] != '443' && $protocol == 'https')) && strpos($_SERVER['HTTP_HOST'], ':') === false) ? ':'.$_SERVER['SERVER_PORT'] : '';
+
+ $url = urldecode($protocol.'://'.$_SERVER['HTTP_HOST'].$port.$_SERVER['REQUEST_URI']);
+
+ if (strlen($url) <= $max_length || $max_length == 0)
+ return $url;
+
+ // We can't find a short enough url
+ return null;
+}
+
+
+//
+// Fetch the current protocol in use - http or https
+//
+function get_current_protocol()
+{
+ $protocol = 'http';
+
+ // Check if the server is claiming to using HTTPS
+ if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off')
+ $protocol = 'https';
+
+ // If we are behind a reverse proxy try to decide which protocol it is using
+ if (defined('FORUM_BEHIND_REVERSE_PROXY'))
+ {
+ // Check if we are behind a Microsoft based reverse proxy
+ if (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) != 'off')
+ $protocol = 'https';
+
+ // Check if we're behind a "proper" reverse proxy, and what protocol it's using
+ if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
+ $protocol = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']);
+ }
+
+ return $protocol;
+}
+
+
+//
+// Fetch the base_url, optionally support HTTPS and HTTP
+//
+function get_base_url($support_https = false)
+{
+ global $pun_config;
+ static $base_url;
+
+ if (!$support_https)
+ return $pun_config['o_base_url'];
+
+ if (!isset($base_url))
+ {
+ // Make sure we are using the correct protocol
+ $base_url = str_replace(array('http://', 'https://'), get_current_protocol().'://', $pun_config['o_base_url']);
+ }
+
+ return $base_url;
+}
+
+
+//
+// Fetch admin IDs
+//
+function get_admin_ids()
+{
+ if (file_exists(FORUM_CACHE_DIR.'cache_admins.php'))
+ include FORUM_CACHE_DIR.'cache_admins.php';
+
+ if (!defined('PUN_ADMINS_LOADED'))
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_admins_cache();
+ require FORUM_CACHE_DIR.'cache_admins.php';
+ }
+
+ return $pun_admins;
+}
+
+
+//
+// Fill $pun_user with default values (for guests)
+//
+function set_default_user()
+{
+ global $db, $db_type, $pun_user, $pun_config;
+
+ $remote_addr = get_remote_address();
+
+ // Fetch guest user
+ $result = $db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.ident=\''.$db->escape($remote_addr).'\' WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ exit('Unable to fetch guest information. Your database must contain both a guest user and a guest user group.');
+
+ $pun_user = $db->fetch_assoc($result);
+
+ // Update online list
+ if (!$pun_user['logged'])
+ {
+ $pun_user['logged'] = time();
+
+ // With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ case 'sqlite':
+ $db->query('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged) VALUES(1, \''.$db->escape($remote_addr).'\', '.$pun_user['logged'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+ break;
+
+ default:
+ $db->query('INSERT INTO '.$db->prefix.'online (user_id, ident, logged) SELECT 1, \''.$db->escape($remote_addr).'\', '.$pun_user['logged'].' WHERE NOT EXISTS (SELECT 1 FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($remote_addr).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+ break;
+ }
+ }
+ else
+ $db->query('UPDATE '.$db->prefix.'online SET logged='.time().' WHERE ident=\''.$db->escape($remote_addr).'\'') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+ $pun_user['disp_topics'] = $pun_config['o_disp_topics_default'];
+ $pun_user['disp_posts'] = $pun_config['o_disp_posts_default'];
+ $pun_user['timezone'] = $pun_config['o_default_timezone'];
+ $pun_user['dst'] = $pun_config['o_default_dst'];
+ $pun_user['language'] = $pun_config['o_default_lang'];
+ $pun_user['style'] = $pun_config['o_default_style'];
+ $pun_user['is_guest'] = true;
+ $pun_user['is_admmod'] = false;
+}
+
+
+//
+// SHA1 HMAC with PHP 4 fallback
+//
+function forum_hmac($data, $key, $raw_output = false)
+{
+ if (function_exists('hash_hmac'))
+ return hash_hmac('sha1', $data, $key, $raw_output);
+
+ // If key size more than blocksize then we hash it once
+ if (strlen($key) > 64)
+ $key = pack('H*', sha1($key)); // we have to use raw output here to match the standard
+
+ // Ensure we're padded to exactly one block boundary
+ $key = str_pad($key, 64, chr(0x00));
+
+ $hmac_opad = str_repeat(chr(0x5C), 64);
+ $hmac_ipad = str_repeat(chr(0x36), 64);
+
+ // Do inner and outer padding
+ for ($i = 0;$i < 64;$i++) {
+ $hmac_opad[$i] = $hmac_opad[$i] ^ $key[$i];
+ $hmac_ipad[$i] = $hmac_ipad[$i] ^ $key[$i];
+ }
+
+ // Finally, calculate the HMAC
+ $hash = sha1($hmac_opad.pack('H*', sha1($hmac_ipad.$data)));
+
+ // If we want raw output then we need to pack the final result
+ if ($raw_output)
+ $hash = pack('H*', $hash);
+
+ return $hash;
+}
+
+
+//
+// Set a cookie, FluxBB style!
+// Wrapper for forum_setcookie
+//
+function pun_setcookie($user_id, $password_hash, $expire)
+{
+ global $cookie_name, $cookie_seed;
+
+ forum_setcookie($cookie_name, $user_id.'|'.forum_hmac($password_hash, $cookie_seed.'_password_hash').'|'.$expire.'|'.forum_hmac($user_id.'|'.$expire, $cookie_seed.'_cookie_hash'), $expire);
+}
+
+
+//
+// Set a cookie, FluxBB style!
+//
+function forum_setcookie($name, $value, $expire)
+{
+ global $cookie_path, $cookie_domain, $cookie_secure, $pun_config;
+
+ if ($expire - time() - $pun_config['o_timeout_visit'] < 1)
+ $expire = 0;
+
+ // Enable sending of a P3P header
+ header('P3P: CP="CUR ADM"');
+
+ if (version_compare(PHP_VERSION, '5.2.0', '>='))
+ setcookie($name, $value, $expire, $cookie_path, $cookie_domain, $cookie_secure, true);
+ else
+ setcookie($name, $value, $expire, $cookie_path.'; HttpOnly', $cookie_domain, $cookie_secure);
+}
+
+
+//
+// Check whether the connecting user is banned (and delete any expired bans while we're at it)
+//
+function check_bans()
+{
+ global $db, $pun_config, $lang_common, $pun_user, $pun_bans;
+
+ // Admins and moderators aren't affected
+ if ($pun_user['is_admmod'] || !$pun_bans)
+ return;
+
+ // Add a dot or a colon (depending on IPv4/IPv6) at the end of the IP address to prevent banned address
+ // 192.168.0.5 from matching e.g. 192.168.0.50
+ $user_ip = get_remote_address();
+ $user_ip .= (strpos($user_ip, '.') !== false) ? '.' : ':';
+
+ $bans_altered = false;
+ $is_banned = false;
+
+ foreach ($pun_bans as $cur_ban)
+ {
+ // Has this ban expired?
+ if ($cur_ban['expire'] != '' && $cur_ban['expire'] <= time())
+ {
+ $db->query('DELETE FROM '.$db->prefix.'bans WHERE id='.$cur_ban['id']) or error('Unable to delete expired ban', __FILE__, __LINE__, $db->error());
+ $bans_altered = true;
+ continue;
+ }
+
+ if ($cur_ban['username'] != '' && utf8_strtolower($pun_user['username']) == utf8_strtolower($cur_ban['username']))
+ $is_banned = true;
+
+ if ($cur_ban['ip'] != '')
+ {
+ $cur_ban_ips = explode(' ', $cur_ban['ip']);
+
+ $num_ips = count($cur_ban_ips);
+ for ($i = 0; $i < $num_ips; ++$i)
+ {
+ // Add the proper ending to the ban
+ if (strpos($user_ip, '.') !== false)
+ $cur_ban_ips[$i] = $cur_ban_ips[$i].'.';
+ else
+ $cur_ban_ips[$i] = $cur_ban_ips[$i].':';
+
+ if (substr($user_ip, 0, strlen($cur_ban_ips[$i])) == $cur_ban_ips[$i])
+ {
+ $is_banned = true;
+ break;
+ }
+ }
+ }
+
+ if ($is_banned)
+ {
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($pun_user['username']).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+ message($lang_common['Ban message'].' '.(($cur_ban['expire'] != '') ? $lang_common['Ban message 2'].' '.strtolower(format_time($cur_ban['expire'], true)).'. ' : '').(($cur_ban['message'] != '') ? $lang_common['Ban message 3'].'<br /><br /><strong>'.pun_htmlspecialchars($cur_ban['message']).'</strong><br /><br />' : '<br /><br />').$lang_common['Ban message 4'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.', true);
+ }
+ }
+
+ // If we removed any expired bans during our run-through, we need to regenerate the bans cache
+ if ($bans_altered)
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_bans_cache();
+ }
+}
+
+
+//
+// Check username
+//
+function check_username($username, $exclude_id = null)
+{
+ global $db, $pun_config, $errors, $lang_prof_reg, $lang_register, $lang_common, $pun_bans;
+
+ // Include UTF-8 function
+ require_once PUN_ROOT.'include/utf8/strcasecmp.php';
+
+ // Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
+ $username = preg_replace('%\s+%s', ' ', $username);
+
+ // Validate username
+ if (pun_strlen($username) < 2)
+ $errors[] = $lang_prof_reg['Username too short'];
+ else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
+ $errors[] = $lang_prof_reg['Username too long'];
+ else if (!strcasecmp($username, 'Guest') || !utf8_strcasecmp($username, $lang_common['Guest']))
+ $errors[] = $lang_prof_reg['Username guest'];
+ else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
+ $errors[] = $lang_prof_reg['Username IP'];
+ else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
+ $errors[] = $lang_prof_reg['Username reserved chars'];
+ else if (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*|topic|post|forum|user)\]|\[(?:img|url|quote|list)=)%i', $username))
+ $errors[] = $lang_prof_reg['Username BBCode'];
+
+ // Check username for any censored words
+ if ($pun_config['o_censoring'] == '1' && censor_words($username) != $username)
+ $errors[] = $lang_register['Username censor'];
+
+ // Check that the username (or a too similar username) is not already registered
+ $query = (!is_null($exclude_id)) ? ' AND id!='.$exclude_id : '';
+
+ $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ $busy = $db->result($result);
+ $errors[] = $lang_register['Username dupe 1'].' '.pun_htmlspecialchars($busy).'. '.$lang_register['Username dupe 2'];
+ }
+
+ // Check username for any banned usernames
+ foreach ($pun_bans as $cur_ban)
+ {
+ if ($cur_ban['username'] != '' && utf8_strtolower($username) == utf8_strtolower($cur_ban['username']))
+ {
+ $errors[] = $lang_prof_reg['Banned username'];
+ break;
+ }
+ }
+}
+
+
+//
+// Update "Users online"
+//
+function update_users_online()
+{
+ global $db, $pun_config;
+
+ $now = time();
+
+ // Fetch all online list entries that are older than "o_timeout_online"
+ $result = $db->query('SELECT user_id, ident, logged, idle FROM '.$db->prefix.'online WHERE logged<'.($now-$pun_config['o_timeout_online'])) or error('Unable to fetch old entries from online list', __FILE__, __LINE__, $db->error());
+ while ($cur_user = $db->fetch_assoc($result))
+ {
+ // If the entry is a guest, delete it
+ if ($cur_user['user_id'] == '1')
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($cur_user['ident']).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+ else
+ {
+ // If the entry is older than "o_timeout_visit", update last_visit for the user in question, then delete him/her from the online list
+ if ($cur_user['logged'] < ($now-$pun_config['o_timeout_visit']))
+ {
+ $db->query('UPDATE '.$db->prefix.'users SET last_visit='.$cur_user['logged'].' WHERE id='.$cur_user['user_id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$cur_user['user_id']) or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+ }
+ else if ($cur_user['idle'] == '0')
+ $db->query('UPDATE '.$db->prefix.'online SET idle=1 WHERE user_id='.$cur_user['user_id']) or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+ }
+ }
+}
+
+
+//
+// Display the profile navigation menu
+//
+function generate_profile_menu($page = '')
+{
+ global $lang_profile, $pun_config, $pun_user, $id;
+
+?>
+<div id="profile" class="block2col">
+ <div class="blockmenu">
+ <h2><span><?php echo $lang_profile['Profile menu'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <ul>
+ <li<?php if ($page == 'essentials') echo ' class="isactive"'; ?>><a href="profile.php?section=essentials&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section essentials'] ?></a></li>
+ <li<?php if ($page == 'personal') echo ' class="isactive"'; ?>><a href="profile.php?section=personal&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section personal'] ?></a></li>
+ <li<?php if ($page == 'messaging') echo ' class="isactive"'; ?>><a href="profile.php?section=messaging&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section messaging'] ?></a></li>
+<?php if ($pun_config['o_avatars'] == '1' || $pun_config['o_signatures'] == '1'): ?> <li<?php if ($page == 'personality') echo ' class="isactive"'; ?>><a href="profile.php?section=personality&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section personality'] ?></a></li>
+<?php endif; ?> <li<?php if ($page == 'display') echo ' class="isactive"'; ?>><a href="profile.php?section=display&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section display'] ?></a></li>
+ <li<?php if ($page == 'privacy') echo ' class="isactive"'; ?>><a href="profile.php?section=privacy&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section privacy'] ?></a></li>
+<?php if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '1')): ?> <li<?php if ($page == 'admin') echo ' class="isactive"'; ?>><a href="profile.php?section=admin&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Section admin'] ?></a></li>
+<?php endif; ?> </ul>
+ </div>
+ </div>
+ </div>
+<?php
+
+}
+
+
+//
+// Outputs markup to display a user's avatar
+//
+function generate_avatar_markup($user_id)
+{
+ global $pun_config;
+
+ $filetypes = array('jpg', 'gif', 'png');
+ $avatar_markup = '';
+
+ foreach ($filetypes as $cur_type)
+ {
+ $path = $pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type;
+
+ if (file_exists(PUN_ROOT.$path) && $img_size = getimagesize(PUN_ROOT.$path))
+ {
+ $avatar_markup = '<img src="'.pun_htmlspecialchars(get_base_url(true).'/'.$path.'?m='.filemtime(PUN_ROOT.$path)).'" '.$img_size[3].' alt="" />';
+ break;
+ }
+ }
+
+ return $avatar_markup;
+}
+
+
+//
+// Generate browser's title
+//
+function generate_page_title($page_title, $p = null)
+{
+ global $lang_common;
+
+ if (!is_array($page_title))
+ $page_title = array($page_title);
+
+ $page_title = array_reverse($page_title);
+
+ if ($p > 1)
+ $page_title[0] .= ' ('.sprintf($lang_common['Page'], forum_number_format($p)).')';
+
+ $crumbs = implode($lang_common['Title separator'], $page_title);
+
+ return $crumbs;
+}
+
+
+//
+// Save array of tracked topics in cookie
+//
+function set_tracked_topics($tracked_topics)
+{
+ global $cookie_name, $cookie_path, $cookie_domain, $cookie_secure, $pun_config;
+
+ $cookie_data = '';
+ if (!empty($tracked_topics))
+ {
+ // Sort the arrays (latest read first)
+ arsort($tracked_topics['topics'], SORT_NUMERIC);
+ arsort($tracked_topics['forums'], SORT_NUMERIC);
+
+ // Homebrew serialization (to avoid having to run unserialize() on cookie data)
+ foreach ($tracked_topics['topics'] as $id => $timestamp)
+ $cookie_data .= 't'.$id.'='.$timestamp.';';
+ foreach ($tracked_topics['forums'] as $id => $timestamp)
+ $cookie_data .= 'f'.$id.'='.$timestamp.';';
+
+ // Enforce a byte size limit (4096 minus some space for the cookie name - defaults to 4048)
+ if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
+ {
+ $cookie_data = substr($cookie_data, 0, FORUM_MAX_COOKIE_SIZE);
+ $cookie_data = substr($cookie_data, 0, strrpos($cookie_data, ';')).';';
+ }
+ }
+
+ forum_setcookie($cookie_name.'_track', $cookie_data, time() + $pun_config['o_timeout_visit']);
+ $_COOKIE[$cookie_name.'_track'] = $cookie_data; // Set it directly in $_COOKIE as well
+}
+
+
+//
+// Extract array of tracked topics from cookie
+//
+function get_tracked_topics()
+{
+ global $cookie_name;
+
+ $cookie_data = isset($_COOKIE[$cookie_name.'_track']) ? $_COOKIE[$cookie_name.'_track'] : false;
+ if (!$cookie_data)
+ return array('topics' => array(), 'forums' => array());
+
+ if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
+ return array('topics' => array(), 'forums' => array());
+
+ // Unserialize data from cookie
+ $tracked_topics = array('topics' => array(), 'forums' => array());
+ $temp = explode(';', $cookie_data);
+ foreach ($temp as $t)
+ {
+ $type = substr($t, 0, 1) == 'f' ? 'forums' : 'topics';
+ $id = intval(substr($t, 1));
+ $timestamp = intval(substr($t, strpos($t, '=') + 1));
+ if ($id > 0 && $timestamp > 0)
+ $tracked_topics[$type][$id] = $timestamp;
+ }
+
+ return $tracked_topics;
+}
+
+
+//
+// Shortcut method for executing all callbacks registered with the addon manager for the given hook
+//
+function flux_hook($name)
+{
+ global $flux_addons;
+
+ $flux_addons->hook($name);
+}
+
+
+//
+// Update posts, topics, last_post, last_post_id and last_poster for a forum
+//
+function update_forum($forum_id)
+{
+ global $db;
+
+ $result = $db->query('SELECT COUNT(id), SUM(num_replies) FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id) or error('Unable to fetch forum topic count', __FILE__, __LINE__, $db->error());
+ list($num_topics, $num_posts) = $db->fetch_row($result);
+
+ $num_posts = $num_posts + $num_topics; // $num_posts is only the sum of all replies (we have to add the topic posts)
+
+ $result = $db->query('SELECT last_post, last_post_id, last_poster FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.' AND moved_to IS NULL ORDER BY last_post DESC LIMIT 1') or error('Unable to fetch last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result)) // There are topics in the forum
+ {
+ list($last_post, $last_post_id, $last_poster) = $db->fetch_row($result);
+
+ $db->query('UPDATE '.$db->prefix.'forums SET num_topics='.$num_topics.', num_posts='.$num_posts.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster=\''.$db->escape($last_poster).'\' WHERE id='.$forum_id) or error('Unable to update last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error());
+ }
+ else // There are no topics
+ $db->query('UPDATE '.$db->prefix.'forums SET num_topics='.$num_topics.', num_posts='.$num_posts.', last_post=NULL, last_post_id=NULL, last_poster=NULL WHERE id='.$forum_id) or error('Unable to update last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error());
+}
+
+
+//
+// Deletes any avatars owned by the specified user ID
+//
+function delete_avatar($user_id)
+{
+ global $pun_config;
+
+ $filetypes = array('jpg', 'gif', 'png');
+
+ // Delete user avatar
+ foreach ($filetypes as $cur_type)
+ {
+ if (file_exists(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type))
+ @unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type);
+ }
+}
+
+
+//
+// Delete a topic and all of its posts
+//
+function delete_topic($topic_id)
+{
+ global $db;
+
+ // Delete the topic and any redirect topics
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id='.$topic_id.' OR moved_to='.$topic_id) or error('Unable to delete topic', __FILE__, __LINE__, $db->error());
+
+ // Create a list of the post IDs in this topic
+ $post_ids = '';
+ $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+ while ($row = $db->fetch_row($result))
+ $post_ids .= ($post_ids != '') ? ','.$row[0] : $row[0];
+
+ // Make sure we have a list of post IDs
+ if ($post_ids != '')
+ {
+ strip_search_index($post_ids);
+
+ // Delete posts in topic
+ $db->query('DELETE FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
+ }
+
+ // Delete any subscriptions for this topic
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id='.$topic_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+}
+
+
+//
+// Delete a single post
+//
+function delete_post($post_id, $topic_id)
+{
+ global $db;
+
+ $result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id.' ORDER BY id DESC LIMIT 2') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ list($last_id, ,) = $db->fetch_row($result);
+ list($second_last_id, $second_poster, $second_posted) = $db->fetch_row($result);
+
+ // Delete the post
+ $db->query('DELETE FROM '.$db->prefix.'posts WHERE id='.$post_id) or error('Unable to delete post', __FILE__, __LINE__, $db->error());
+
+ strip_search_index($post_id);
+
+ // Count number of replies in the topic
+ $result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
+ $num_replies = $db->result($result, 0) - 1;
+
+ // If the message we deleted is the most recent in the topic (at the end of the topic)
+ if ($last_id == $post_id)
+ {
+ // If there is a $second_last_id there is more than 1 reply to the topic
+ if (!empty($second_last_id))
+ $db->query('UPDATE '.$db->prefix.'topics SET last_post='.$second_posted.', last_post_id='.$second_last_id.', last_poster=\''.$db->escape($second_poster).'\', num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+ else
+ // We deleted the only reply, so now last_post/last_post_id/last_poster is posted/id/poster from the topic itself
+ $db->query('UPDATE '.$db->prefix.'topics SET last_post=posted, last_post_id=id, last_poster=poster, num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+ }
+ else
+ // Otherwise we just decrement the reply counter
+ $db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+}
+
+
+//
+// Delete every .php file in the forum's cache directory
+//
+function forum_clear_cache()
+{
+ $d = dir(FORUM_CACHE_DIR);
+ while (($entry = $d->read()) !== false)
+ {
+ if (substr($entry, -4) == '.php')
+ @unlink(FORUM_CACHE_DIR.$entry);
+ }
+ $d->close();
+}
+
+
+//
+// Replace censored words in $text
+//
+function censor_words($text)
+{
+ global $db;
+ static $search_for, $replace_with;
+
+ // If not already built in a previous call, build an array of censor words and their replacement text
+ if (!isset($search_for))
+ {
+ if (file_exists(FORUM_CACHE_DIR.'cache_censoring.php'))
+ include FORUM_CACHE_DIR.'cache_censoring.php';
+
+ if (!defined('PUN_CENSOR_LOADED'))
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_censoring_cache();
+ require FORUM_CACHE_DIR.'cache_censoring.php';
+ }
+ }
+
+ if (!empty($search_for))
+ $text = substr(ucp_preg_replace($search_for, $replace_with, ' '.$text.' '), 1, -1);
+
+ return $text;
+}
+
+
+//
+// Determines the correct title for $user
+// $user must contain the elements 'username', 'title', 'posts', 'g_id' and 'g_user_title'
+//
+function get_title($user)
+{
+ global $pun_bans, $lang_common;
+ static $ban_list;
+
+ // If not already built in a previous call, build an array of lowercase banned usernames
+ if (empty($ban_list))
+ {
+ $ban_list = array();
+
+ foreach ($pun_bans as $cur_ban)
+ $ban_list[] = utf8_strtolower($cur_ban['username']);
+ }
+
+ // If the user is banned
+ if (in_array(utf8_strtolower($user['username']), $ban_list))
+ $user_title = $lang_common['Banned'];
+ // If the user has a custom title
+ else if ($user['title'] != '')
+ $user_title = pun_htmlspecialchars($user['title']);
+ // If the user group has a default user title
+ else if ($user['g_user_title'] != '')
+ $user_title = pun_htmlspecialchars($user['g_user_title']);
+ // If the user is a guest
+ else if ($user['g_id'] == PUN_GUEST)
+ $user_title = $lang_common['Guest'];
+ // If nothing else helps, we assign the default
+ else
+ $user_title = $lang_common['Member'];
+
+ return $user_title;
+}
+
+
+//
+// Generate a string with numbered links (for multipage scripts)
+//
+function paginate($num_pages, $cur_page, $link)
+{
+ global $lang_common;
+
+ $pages = array();
+ $link_to_all = false;
+
+ // If $cur_page == -1, we link to all pages (used in viewforum.php)
+ if ($cur_page == -1)
+ {
+ $cur_page = 1;
+ $link_to_all = true;
+ }
+
+ if ($num_pages <= 1)
+ $pages = array('<strong class="item1">1</strong>');
+ else
+ {
+ // Add a previous page link
+ if ($num_pages > 1 && $cur_page > 1)
+ $pages[] = '<a rel="prev"'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.($cur_page == 2 ? '' : '&amp;p='.($cur_page - 1)).'">'.$lang_common['Previous'].'</a>';
+
+ if ($cur_page > 3)
+ {
+ $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'">1</a>';
+
+ if ($cur_page > 5)
+ $pages[] = '<span class="spacer">'.$lang_common['Spacer'].'</span>';
+ }
+
+ // Don't ask me how the following works. It just does, OK? :-)
+ for ($current = ($cur_page == 5) ? $cur_page - 3 : $cur_page - 2, $stop = ($cur_page + 4 == $num_pages) ? $cur_page + 4 : $cur_page + 3; $current < $stop; ++$current)
+ {
+ if ($current < 1 || $current > $num_pages)
+ continue;
+ else if ($current != $cur_page || $link_to_all)
+ $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.($current == 1 ? '' : '&amp;p='.$current).'">'.forum_number_format($current).'</a>';
+ else
+ $pages[] = '<strong'.(empty($pages) ? ' class="item1"' : '').'>'.forum_number_format($current).'</strong>';
+ }
+
+ if ($cur_page <= ($num_pages-3))
+ {
+ if ($cur_page != ($num_pages-3) && $cur_page != ($num_pages-4))
+ $pages[] = '<span class="spacer">'.$lang_common['Spacer'].'</span>';
+
+ $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.$num_pages.'">'.forum_number_format($num_pages).'</a>';
+ }
+
+ // Add a next page link
+ if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages)
+ $pages[] = '<a rel="next"'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.($cur_page +1).'">'.$lang_common['Next'].'</a>';
+ }
+
+ return implode(' ', $pages);
+}
+
+
+//
+// Display a message
+//
+function message($message, $no_back_link = false, $http_status = null)
+{
+ global $db, $lang_common, $pun_config, $pun_start, $tpl_main, $pun_user;
+
+ // Did we receive a custom header?
+ if(!is_null($http_status)) {
+ header('HTTP/1.1 ' . $http_status);
+ }
+
+ if (!defined('PUN_HEADER'))
+ {
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Info']);
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+ }
+
+?>
+
+<div id="msg" class="block">
+ <h2><span><?php echo $lang_common['Info'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <p><?php echo $message ?></p>
+<?php if (!$no_back_link): ?> <p><a href="javascript: history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+<?php endif; ?> </div>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+//
+// Format a time string according to $time_format and time zones
+//
+function format_time($timestamp, $date_only = false, $date_format = null, $time_format = null, $time_only = false, $no_text = false, $user = null)
+{
+ global $lang_common, $pun_user, $forum_date_formats, $forum_time_formats;
+
+ if ($timestamp == '')
+ return $lang_common['Never'];
+
+ if (is_null($user))
+ $user = $pun_user;
+
+ $diff = ($user['timezone'] + $user['dst']) * 3600;
+ $timestamp += $diff;
+ $now = time();
+
+ if(is_null($date_format))
+ $date_format = $forum_date_formats[$user['date_format']];
+
+ if(is_null($time_format))
+ $time_format = $forum_time_formats[$user['time_format']];
+
+ $date = gmdate($date_format, $timestamp);
+ $today = gmdate($date_format, $now+$diff);
+ $yesterday = gmdate($date_format, $now+$diff-86400);
+
+ if(!$no_text)
+ {
+ if ($date == $today)
+ $date = $lang_common['Today'];
+ else if ($date == $yesterday)
+ $date = $lang_common['Yesterday'];
+ }
+
+ if ($date_only)
+ return $date;
+ else if ($time_only)
+ return gmdate($time_format, $timestamp);
+ else
+ return $date.' '.gmdate($time_format, $timestamp);
+}
+
+
+//
+// A wrapper for PHP's number_format function
+//
+function forum_number_format($number, $decimals = 0)
+{
+ global $lang_common;
+
+ return is_numeric($number) ? number_format($number, $decimals, $lang_common['lang_decimal_point'], $lang_common['lang_thousands_sep']) : $number;
+}
+
+
+//
+// Generate a random key of length $len
+//
+function random_key($len, $readable = false, $hash = false)
+{
+ if (!function_exists('secure_random_bytes'))
+ include PUN_ROOT.'include/srand.php';
+
+ $key = secure_random_bytes($len);
+
+ if ($hash)
+ return substr(bin2hex($key), 0, $len);
+ else if ($readable)
+ {
+ $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+ $result = '';
+ for ($i = 0; $i < $len; ++$i)
+ $result .= substr($chars, (ord($key[$i]) % strlen($chars)), 1);
+
+ return $result;
+ }
+
+ return $key;
+}
+
+
+//
+// Make sure that HTTP_REFERER matches base_url/script
+//
+function confirm_referrer($scripts, $error_msg = false)
+{
+ global $lang_common;
+
+ if (!is_array($scripts))
+ $scripts = array($scripts);
+
+ // There is no referrer
+ if (empty($_SERVER['HTTP_REFERER']))
+ message($error_msg ? $error_msg : $lang_common['Bad referrer']);
+
+ $referrer = parse_url(strtolower($_SERVER['HTTP_REFERER']));
+ // Remove www subdomain if it exists
+ if (strpos($referrer['host'], 'www.') === 0)
+ $referrer['host'] = substr($referrer['host'], 4);
+
+ $valid_paths = array();
+ foreach ($scripts as $script)
+ {
+ $valid = parse_url(strtolower(get_base_url().'/'.$script));
+ // Remove www subdomain if it exists
+ if (strpos($valid['host'], 'www.') === 0)
+ $valid['host'] = substr($valid['host'], 4);
+
+ $valid_host = $valid['host'];
+ $valid_paths[] = $valid['path'];
+ }
+
+ // Check the host and path match. Ignore the scheme, port, etc.
+ if ($referrer['host'] != $valid_host || !in_array($referrer['path'], $valid_paths, true))
+ message($error_msg ? $error_msg : $lang_common['Bad referrer']);
+}
+
+
+//
+// Validate the given redirect URL, use the fallback otherwise
+//
+function validate_redirect($redirect_url, $fallback_url)
+{
+ $referrer = parse_url(strtolower($redirect_url));
+
+ // Make sure the host component exists
+ if (!isset($referrer['host']))
+ $referrer['host'] = '';
+
+ // Remove www subdomain if it exists
+ if (strpos($referrer['host'], 'www.') === 0)
+ $referrer['host'] = substr($referrer['host'], 4);
+
+ // Make sure the path component exists
+ if (!isset($referrer['path']))
+ $referrer['path'] = '';
+
+ $valid = parse_url(strtolower(get_base_url()));
+
+ // Remove www subdomain if it exists
+ if (strpos($valid['host'], 'www.') === 0)
+ $valid['host'] = substr($valid['host'], 4);
+
+ // Make sure the path component exists
+ if (!isset($valid['path']))
+ $valid['path'] = '';
+
+ if ($referrer['host'] == $valid['host'] && preg_match('%^'.preg_quote($valid['path'], '%').'/(.*?)\.php%i', $referrer['path']))
+ return $redirect_url;
+ else
+ return $fallback_url;
+}
+
+
+//
+// Generate a random password of length $len
+// Compatibility wrapper for random_key
+//
+function random_pass($len)
+{
+ return random_key($len, true);
+}
+
+
+//
+// Compute a hash of $str
+//
+function pun_hash($str)
+{
+ return sha1($str);
+}
+
+
+//
+// Compare two strings in constant time
+// Inspired by WordPress
+//
+function pun_hash_equals($a, $b)
+{
+ if (function_exists('hash_equals'))
+ return hash_equals((string) $a, (string) $b);
+
+ $a_length = strlen($a);
+
+ if ($a_length !== strlen($b))
+ return false;
+
+ $result = 0;
+
+ // Do not attempt to "optimize" this.
+ for ($i = 0; $i < $a_length; $i++)
+ $result |= ord($a[$i]) ^ ord($b[$i]);
+
+ return $result === 0;
+}
+
+
+//
+// Compute a random hash used against CSRF attacks
+//
+function pun_csrf_token()
+{
+ global $pun_user;
+ static $token;
+
+ if (!isset($token))
+ $token = pun_hash($pun_user['id'].$pun_user['password'].pun_hash(get_remote_address()));
+
+ return $token;
+}
+
+//
+// Check if the CSRF hash is correct
+//
+function check_csrf($token)
+{
+ global $lang_common;
+
+ $is_hash_authorized = pun_hash_equals($token, pun_csrf_token());
+
+ if (!isset($token) || !$is_hash_authorized)
+ message($lang_common['Bad csrf hash'], false, '404 Not Found');
+}
+
+
+//
+// Try to determine the correct remote IP-address
+//
+function get_remote_address()
+{
+ $remote_addr = $_SERVER['REMOTE_ADDR'];
+
+ // If we are behind a reverse proxy try to find the real users IP
+ if (defined('FORUM_BEHIND_REVERSE_PROXY'))
+ {
+ if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
+ {
+ // The general format of the field is:
+ // X-Forwarded-For: client1, proxy1, proxy2
+ // where the value is a comma+space separated list of IP addresses, the left-most being the farthest downstream client,
+ // and each successive proxy that passed the request adding the IP address where it received the request from.
+ $forwarded_for = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+ $forwarded_for = trim($forwarded_for[0]);
+
+ if (@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $forwarded_for) || @preg_match('%^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$%', $forwarded_for))
+ $remote_addr = $forwarded_for;
+ }
+ }
+
+ return $remote_addr;
+}
+
+
+//
+// Calls htmlspecialchars with a few options already set
+//
+function pun_htmlspecialchars($str)
+{
+ return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
+}
+
+
+//
+// Calls htmlspecialchars_decode with a few options already set
+//
+function pun_htmlspecialchars_decode($str)
+{
+ if (function_exists('htmlspecialchars_decode'))
+ return htmlspecialchars_decode($str, ENT_QUOTES);
+
+ static $translations;
+ if (!isset($translations))
+ {
+ $translations = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);
+ $translations['&#039;'] = '\''; // get_html_translation_table doesn't include &#039; which is what htmlspecialchars translates ' to, but apparently that is okay?! http://bugs.php.net/bug.php?id=25927
+ $translations = array_flip($translations);
+ }
+
+ return strtr($str, $translations);
+}
+
+
+//
+// A wrapper for utf8_strlen for compatibility
+//
+function pun_strlen($str)
+{
+ return utf8_strlen($str);
+}
+
+
+//
+// Convert \r\n and \r to \n
+//
+function pun_linebreaks($str)
+{
+ return str_replace(array("\r\n", "\r"), "\n", $str);
+}
+
+
+//
+// A wrapper for utf8_trim for compatibility
+//
+function pun_trim($str, $charlist = false)
+{
+ return is_string($str) ? utf8_trim($str, $charlist) : '';
+}
+
+//
+// Checks if a string is in all uppercase
+//
+function is_all_uppercase($string)
+{
+ return utf8_strtoupper($string) == $string && utf8_strtolower($string) != $string;
+}
+
+
+//
+// Inserts $element into $input at $offset
+// $offset can be either a numerical offset to insert at (eg: 0 inserts at the beginning of the array)
+// or a string, which is the key that the new element should be inserted before
+// $key is optional: it's used when inserting a new key/value pair into an associative array
+//
+function array_insert(&$input, $offset, $element, $key = null)
+{
+ if (is_null($key))
+ $key = $offset;
+
+ // Determine the proper offset if we're using a string
+ if (!is_int($offset))
+ $offset = array_search($offset, array_keys($input), true);
+
+ // Out of bounds checks
+ if ($offset > count($input))
+ $offset = count($input);
+ else if ($offset < 0)
+ $offset = 0;
+
+ $input = array_merge(array_slice($input, 0, $offset), array($key => $element), array_slice($input, $offset));
+}
+
+
+//
+// Display a message when board is in maintenance mode
+//
+function maintenance_message()
+{
+ global $db, $pun_config, $lang_common, $pun_user;
+
+ header('HTTP/1.1 503 Service Unavailable');
+
+ // Send no-cache headers
+ header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: post-check=0, pre-check=0', false);
+ header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+ // Send the Content-type header in case the web server is setup to send something else
+ header('Content-type: text/html; charset=utf-8');
+
+ // Deal with newlines, tabs and multiple spaces
+ $pattern = array("\t", ' ', ' ');
+ $replace = array('&#160; &#160; ', '&#160; ', ' &#160;');
+ $message = str_replace($pattern, $replace, $pun_config['o_maintenance_message']);
+
+ if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/maintenance.tpl'))
+ {
+ $tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/maintenance.tpl';
+ $tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
+ }
+ else
+ {
+ $tpl_file = PUN_ROOT.'include/template/maintenance.tpl';
+ $tpl_inc_dir = PUN_ROOT.'include/user/';
+ }
+
+ $tpl_maint = file_get_contents($tpl_file);
+
+ // START SUBST - <pun_include "*">
+ preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_maint, $pun_includes, PREG_SET_ORDER);
+
+ foreach ($pun_includes as $cur_include)
+ {
+ ob_start();
+
+ // Allow for overriding user includes, too.
+ if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
+ require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
+ else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
+ require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
+ else
+ error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+ $tpl_temp = ob_get_contents();
+ $tpl_maint = str_replace($cur_include[0], $tpl_temp, $tpl_maint);
+ ob_end_clean();
+ }
+ // END SUBST - <pun_include "*">
+
+
+ // START SUBST - <pun_language>
+ $tpl_maint = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_maint);
+ // END SUBST - <pun_language>
+
+
+ // START SUBST - <pun_content_direction>
+ $tpl_maint = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_maint);
+ // END SUBST - <pun_content_direction>
+
+
+ // START SUBST - <pun_head>
+ ob_start();
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Maintenance']);
+
+?>
+<title><?php echo generate_page_title($page_title) ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
+<?php
+
+ $tpl_temp = trim(ob_get_contents());
+ $tpl_maint = str_replace('<pun_head>', $tpl_temp, $tpl_maint);
+ ob_end_clean();
+ // END SUBST - <pun_head>
+
+
+ // START SUBST - <pun_maint_main>
+ ob_start();
+
+?>
+<div class="block">
+ <h2><?php echo $lang_common['Maintenance'] ?></h2>
+ <div class="box">
+ <div class="inbox">
+ <p><?php echo $message ?></p>
+ </div>
+ </div>
+</div>
+<?php
+
+ $tpl_temp = trim(ob_get_contents());
+ $tpl_maint = str_replace('<pun_maint_main>', $tpl_temp, $tpl_maint);
+ ob_end_clean();
+ // END SUBST - <pun_maint_main>
+
+
+ // End the transaction
+ $db->end_transaction();
+
+
+ // Close the db connection (and free up any result data)
+ $db->close();
+
+ exit($tpl_maint);
+}
+
+
+//
+// Display $message and redirect user to $destination_url
+//
+function redirect($destination_url, $message)
+{
+ global $db, $pun_config, $lang_common, $pun_user;
+
+ // Prefix with base_url (unless there's already a valid URI)
+ if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
+ $destination_url = get_base_url(true).'/'.$destination_url;
+
+ // Do a little spring cleaning
+ $destination_url = preg_replace('%([\r\n])|(\%0[ad])|(;\s*data\s*:)%i', '', $destination_url);
+
+ // If the delay is 0 seconds, we might as well skip the redirect all together
+ if ($pun_config['o_redirect_delay'] == '0')
+ {
+ $db->end_transaction();
+ $db->close();
+
+ header('Location: '.str_replace('&amp;', '&', $destination_url));
+ exit;
+ }
+
+ // Send no-cache headers
+ header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: post-check=0, pre-check=0', false);
+ header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+ // Send the Content-type header in case the web server is setup to send something else
+ header('Content-type: text/html; charset=utf-8');
+
+ if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/redirect.tpl'))
+ {
+ $tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/redirect.tpl';
+ $tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
+ }
+ else
+ {
+ $tpl_file = PUN_ROOT.'include/template/redirect.tpl';
+ $tpl_inc_dir = PUN_ROOT.'include/user/';
+ }
+
+ $tpl_redir = file_get_contents($tpl_file);
+
+ // START SUBST - <pun_include "*">
+ preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_redir, $pun_includes, PREG_SET_ORDER);
+
+ foreach ($pun_includes as $cur_include)
+ {
+ ob_start();
+
+ // Allow for overriding user includes, too.
+ if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
+ require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
+ else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
+ require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
+ else
+ error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+ $tpl_temp = ob_get_contents();
+ $tpl_redir = str_replace($cur_include[0], $tpl_temp, $tpl_redir);
+ ob_end_clean();
+ }
+ // END SUBST - <pun_include "*">
+
+
+ // START SUBST - <pun_language>
+ $tpl_redir = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_redir);
+ // END SUBST - <pun_language>
+
+
+ // START SUBST - <pun_content_direction>
+ $tpl_redir = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_redir);
+ // END SUBST - <pun_content_direction>
+
+
+ // START SUBST - <pun_head>
+ ob_start();
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Redirecting']);
+
+?>
+<meta http-equiv="refresh" content="<?php echo $pun_config['o_redirect_delay'] ?>;URL=<?php echo $destination_url ?>" />
+<title><?php echo generate_page_title($page_title) ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
+<?php
+
+ $tpl_temp = trim(ob_get_contents());
+ $tpl_redir = str_replace('<pun_head>', $tpl_temp, $tpl_redir);
+ ob_end_clean();
+ // END SUBST - <pun_head>
+
+
+ // START SUBST - <pun_redir_main>
+ ob_start();
+
+?>
+<div class="block">
+ <h2><?php echo $lang_common['Redirecting'] ?></h2>
+ <div class="box">
+ <div class="inbox">
+ <p><?php echo $message.'<br /><br /><a href="'.$destination_url.'">'.$lang_common['Click redirect'].'</a>' ?></p>
+ </div>
+ </div>
+</div>
+<?php
+
+ $tpl_temp = trim(ob_get_contents());
+ $tpl_redir = str_replace('<pun_redir_main>', $tpl_temp, $tpl_redir);
+ ob_end_clean();
+ // END SUBST - <pun_redir_main>
+
+
+ // START SUBST - <pun_footer>
+ ob_start();
+
+ // End the transaction
+ $db->end_transaction();
+
+ // Display executed queries (if enabled)
+ if (defined('PUN_SHOW_QUERIES'))
+ display_saved_queries();
+
+ $tpl_temp = trim(ob_get_contents());
+ $tpl_redir = str_replace('<pun_footer>', $tpl_temp, $tpl_redir);
+ ob_end_clean();
+ // END SUBST - <pun_footer>
+
+
+ // Close the db connection (and free up any result data)
+ $db->close();
+
+ exit($tpl_redir);
+}
+
+
+//
+// Display a simple error message
+//
+function error($message, $file = null, $line = null, $db_error = false)
+{
+ global $pun_config, $lang_common;
+
+ // Set some default settings if the script failed before $pun_config could be populated
+ if (empty($pun_config))
+ {
+ $pun_config = array(
+ 'o_board_title' => 'FluxBB',
+ 'o_gzip' => '0'
+ );
+ }
+
+ // Set some default translations if the script failed before $lang_common could be populated
+ if (empty($lang_common))
+ {
+ $lang_common = array(
+ 'Title separator' => ' / ',
+ 'Page' => 'Page %s'
+ );
+ }
+
+ // Empty all output buffers and stop buffering
+ while (@ob_end_clean());
+
+ // "Restart" output buffering if we are using ob_gzhandler (since the gzip header is already sent)
+ if ($pun_config['o_gzip'] && extension_loaded('zlib'))
+ ob_start('ob_gzhandler');
+
+ header('HTTP/1.1 500 Internal Server Error');
+
+ // Send no-cache headers
+ header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+ header('Cache-Control: post-check=0, pre-check=0', false);
+ header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+ // Send the Content-type header in case the web server is setup to send something else
+ header('Content-type: text/html; charset=utf-8');
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<?php $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), 'Error') ?>
+<title><?php echo generate_page_title($page_title) ?></title>
+<style type="text/css">
+<!--
+BODY {MARGIN: 10% 20% auto 20%; font: 10px Verdana, Arial, Helvetica, sans-serif}
+#errorbox {BORDER: 1px solid #B84623}
+H2 {MARGIN: 0; COLOR: #FFFFFF; BACKGROUND-COLOR: #B84623; FONT-SIZE: 1.1em; PADDING: 5px 4px}
+#errorbox DIV {PADDING: 6px 5px; BACKGROUND-COLOR: #F1F1F1}
+-->
+</style>
+</head>
+<body>
+
+<div id="errorbox">
+ <h2>An error was encountered</h2>
+ <div>
+<?php
+
+ if (defined('PUN_DEBUG') && !is_null($file) && !is_null($line))
+ {
+ $file = str_replace(realpath(PUN_ROOT), '', $file);
+
+ echo "\t\t".'<strong>File:</strong> '.$file.'<br />'."\n\t\t".'<strong>Line:</strong> '.$line.'<br /><br />'."\n\t\t".'<strong>FluxBB reported</strong>: '.$message."\n";
+
+ if ($db_error)
+ {
+ echo "\t\t".'<br /><br /><strong>Database reported:</strong> '.pun_htmlspecialchars($db_error['error_msg']).(($db_error['error_no']) ? ' (Errno: '.$db_error['error_no'].')' : '')."\n";
+
+ if ($db_error['error_sql'] != '')
+ echo "\t\t".'<br /><br /><strong>Failed query:</strong> '.pun_htmlspecialchars($db_error['error_sql'])."\n";
+ }
+ }
+ else
+ echo "\t\t".'Error: <strong>'.pun_htmlspecialchars($message).'.</strong>'."\n";
+
+?>
+ </div>
+</div>
+
+</body>
+</html>
+<?php
+
+ // If a database connection was established (before this error) we close it
+ if ($db_error)
+ $GLOBALS['db']->close();
+
+ exit;
+}
+
+
+//
+// Unset any variables instantiated as a result of register_globals being enabled
+//
+function forum_unregister_globals()
+{
+ $register_globals = ini_get('register_globals');
+ if ($register_globals === '' || $register_globals === '0' || strtolower($register_globals) === 'off')
+ return;
+
+ // Prevent script.php?GLOBALS[foo]=bar
+ if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS']))
+ exit('I\'ll have a steak sandwich and... a steak sandwich.');
+
+ // Variables that shouldn't be unset
+ $no_unset = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES');
+
+ // Remove elements in $GLOBALS that are present in any of the superglobals
+ $input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
+ foreach ($input as $k => $v)
+ {
+ if (!in_array($k, $no_unset) && isset($GLOBALS[$k]))
+ {
+ unset($GLOBALS[$k]);
+ unset($GLOBALS[$k]); // Double unset to circumvent the zend_hash_del_key_or_index hole in PHP <4.4.3 and <5.1.4
+ }
+ }
+}
+
+
+//
+// Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from user input
+//
+function forum_remove_bad_characters()
+{
+ $_GET = remove_bad_characters($_GET);
+ $_POST = remove_bad_characters($_POST);
+ $_COOKIE = remove_bad_characters($_COOKIE);
+ $_REQUEST = remove_bad_characters($_REQUEST);
+}
+
+//
+// Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from the given string
+// See: http://kb.mozillazine.org/Network.IDN.blacklist_chars
+//
+function remove_bad_characters($array)
+{
+ static $bad_utf8_chars;
+
+ if (!isset($bad_utf8_chars))
+ {
+ $bad_utf8_chars = array(
+ "\xcc\xb7" => '', // COMBINING SHORT SOLIDUS OVERLAY 0337 *
+ "\xcc\xb8" => '', // COMBINING LONG SOLIDUS OVERLAY 0338 *
+ "\xe1\x85\x9F" => '', // HANGUL CHOSEONG FILLER 115F *
+ "\xe1\x85\xA0" => '', // HANGUL JUNGSEONG FILLER 1160 *
+ "\xe2\x80\x8b" => '', // ZERO WIDTH SPACE 200B *
+ "\xe2\x80\x8c" => '', // ZERO WIDTH NON-JOINER 200C
+ "\xe2\x80\x8d" => '', // ZERO WIDTH JOINER 200D
+ "\xe2\x80\x8e" => '', // LEFT-TO-RIGHT MARK 200E
+ "\xe2\x80\x8f" => '', // RIGHT-TO-LEFT MARK 200F
+ "\xe2\x80\xaa" => '', // LEFT-TO-RIGHT EMBEDDING 202A
+ "\xe2\x80\xab" => '', // RIGHT-TO-LEFT EMBEDDING 202B
+ "\xe2\x80\xac" => '', // POP DIRECTIONAL FORMATTING 202C
+ "\xe2\x80\xad" => '', // LEFT-TO-RIGHT OVERRIDE 202D
+ "\xe2\x80\xae" => '', // RIGHT-TO-LEFT OVERRIDE 202E
+ "\xe2\x80\xaf" => '', // NARROW NO-BREAK SPACE 202F *
+ "\xe2\x81\x9f" => '', // MEDIUM MATHEMATICAL SPACE 205F *
+ "\xe2\x81\xa0" => '', // WORD JOINER 2060
+ "\xe3\x85\xa4" => '', // HANGUL FILLER 3164 *
+ "\xef\xbb\xbf" => '', // ZERO WIDTH NO-BREAK SPACE FEFF
+ "\xef\xbe\xa0" => '', // HALFWIDTH HANGUL FILLER FFA0 *
+ "\xef\xbf\xb9" => '', // INTERLINEAR ANNOTATION ANCHOR FFF9 *
+ "\xef\xbf\xba" => '', // INTERLINEAR ANNOTATION SEPARATOR FFFA *
+ "\xef\xbf\xbb" => '', // INTERLINEAR ANNOTATION TERMINATOR FFFB *
+ "\xef\xbf\xbc" => '', // OBJECT REPLACEMENT CHARACTER FFFC *
+ "\xef\xbf\xbd" => '', // REPLACEMENT CHARACTER FFFD *
+ "\xe2\x80\x80" => ' ', // EN QUAD 2000 *
+ "\xe2\x80\x81" => ' ', // EM QUAD 2001 *
+ "\xe2\x80\x82" => ' ', // EN SPACE 2002 *
+ "\xe2\x80\x83" => ' ', // EM SPACE 2003 *
+ "\xe2\x80\x84" => ' ', // THREE-PER-EM SPACE 2004 *
+ "\xe2\x80\x85" => ' ', // FOUR-PER-EM SPACE 2005 *
+ "\xe2\x80\x86" => ' ', // SIX-PER-EM SPACE 2006 *
+ "\xe2\x80\x87" => ' ', // FIGURE SPACE 2007 *
+ "\xe2\x80\x88" => ' ', // PUNCTUATION SPACE 2008 *
+ "\xe2\x80\x89" => ' ', // THIN SPACE 2009 *
+ "\xe2\x80\x8a" => ' ', // HAIR SPACE 200A *
+ "\xE3\x80\x80" => ' ', // IDEOGRAPHIC SPACE 3000 *
+ );
+ }
+
+ if (is_array($array))
+ return array_map('remove_bad_characters', $array);
+
+ // Strip out any invalid characters
+ $array = utf8_bad_strip($array);
+
+ // Remove control characters
+ $array = preg_replace('%[\x00-\x08\x0b-\x0c\x0e-\x1f]%', '', $array);
+
+ // Replace some "bad" characters
+ $array = str_replace(array_keys($bad_utf8_chars), array_values($bad_utf8_chars), $array);
+
+ return $array;
+}
+
+
+//
+// Converts the file size in bytes to a human readable file size
+//
+function file_size($size)
+{
+ global $lang_common;
+
+ $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB');
+
+ for ($i = 0; $size > 1024; $i++)
+ $size /= 1024;
+
+ return sprintf($lang_common['Size unit '.$units[$i]], round($size, 2));
+}
+
+
+//
+// Fetch a list of available styles
+//
+function forum_list_styles()
+{
+ $styles = array();
+
+ $d = dir(PUN_ROOT.'style');
+ while (($entry = $d->read()) !== false)
+ {
+ if ($entry{0} == '.')
+ continue;
+
+ if (substr($entry, -4) == '.css')
+ $styles[] = substr($entry, 0, -4);
+ }
+ $d->close();
+
+ natcasesort($styles);
+
+ return $styles;
+}
+
+
+//
+// Fetch a list of available language packs
+//
+function forum_list_langs()
+{
+ $languages = array();
+
+ $d = dir(PUN_ROOT.'lang');
+ while (($entry = $d->read()) !== false)
+ {
+ if ($entry{0} == '.')
+ continue;
+
+ if (is_dir(PUN_ROOT.'lang/'.$entry) && file_exists(PUN_ROOT.'lang/'.$entry.'/common.php'))
+ $languages[] = $entry;
+ }
+ $d->close();
+
+ natcasesort($languages);
+
+ return $languages;
+}
+
+
+//
+// Generate a cache ID based on the last modification time for all stopwords files
+//
+function generate_stopwords_cache_id()
+{
+ $files = glob(PUN_ROOT.'lang/*/stopwords.txt');
+ if ($files === false)
+ return 'cache_id_error';
+
+ $hash = array();
+
+ foreach ($files as $file)
+ {
+ $hash[] = $file;
+ $hash[] = filemtime($file);
+ }
+
+ return sha1(implode('|', $hash));
+}
+
+
+//
+// Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
+//
+function split_text($text, $start, $end, $retab = true)
+{
+ global $pun_config;
+
+ $result = array(0 => array(), 1 => array()); // 0 = inside, 1 = outside
+
+ // split the text into parts
+ $parts = preg_split('%'.preg_quote($start, '%').'(.*)'.preg_quote($end, '%').'%Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $num_parts = count($parts);
+
+ // preg_split results in outside parts having even indices, inside parts having odd
+ for ($i = 0;$i < $num_parts;$i++)
+ $result[1 - ($i % 2)][] = $parts[$i];
+
+ if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
+ {
+ $spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
+ $result[1] = str_replace("\t", $spaces, $result[1]);
+ }
+
+ return $result;
+}
+
+
+//
+// Extract blocks from a text with a starting and ending string
+// This function always matches the most outer block so nesting is possible
+//
+function extract_blocks($text, $start, $end, $retab = true)
+{
+ global $pun_config;
+
+ $code = array();
+ $start_len = strlen($start);
+ $end_len = strlen($end);
+ $regex = '%(?:'.preg_quote($start, '%').'|'.preg_quote($end, '%').')%';
+ $matches = array();
+
+ if (preg_match_all($regex, $text, $matches))
+ {
+ $counter = $offset = 0;
+ $start_pos = $end_pos = false;
+
+ foreach ($matches[0] as $match)
+ {
+ if ($match == $start)
+ {
+ if ($counter == 0)
+ $start_pos = strpos($text, $start);
+ $counter++;
+ }
+ elseif ($match == $end)
+ {
+ $counter--;
+ if ($counter == 0)
+ $end_pos = strpos($text, $end, $offset + 1);
+ $offset = strpos($text, $end, $offset + 1);
+ }
+
+ if ($start_pos !== false && $end_pos !== false)
+ {
+ $code[] = substr($text, $start_pos + $start_len,
+ $end_pos - $start_pos - $start_len);
+ $text = substr_replace($text, "\1", $start_pos,
+ $end_pos - $start_pos + $end_len);
+ $start_pos = $end_pos = false;
+ $offset = 0;
+ }
+ }
+ }
+
+ if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
+ {
+ $spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
+ $text = str_replace("\t", $spaces, $text);
+ }
+
+ return array($code, $text);
+}
+
+
+//
+// function url_valid($url) {
+//
+// Return associative array of valid URI components, or FALSE if $url is not
+// RFC-3986 compliant. If the passed URL begins with: "www." or "ftp.", then
+// "http://" or "ftp://" is prepended and the corrected full-url is stored in
+// the return array with a key name "url". This value should be used by the caller.
+//
+// Return value: FALSE if $url is not valid, otherwise array of URI components:
+// e.g.
+// Given: "http://www.jmrware.com:80/articles?height=10&width=75#fragone"
+// Array(
+// [scheme] => http
+// [authority] => www.jmrware.com:80
+// [userinfo] =>
+// [host] => www.jmrware.com
+// [IP_literal] =>
+// [IPV6address] =>
+// [ls32] =>
+// [IPvFuture] =>
+// [IPv4address] =>
+// [regname] => www.jmrware.com
+// [port] => 80
+// [path_abempty] => /articles
+// [query] => height=10&width=75
+// [fragment] => fragone
+// [url] => http://www.jmrware.com:80/articles?height=10&width=75#fragone
+// )
+function url_valid($url)
+{
+ if (strpos($url, 'www.') === 0) $url = 'http://'. $url;
+ if (strpos($url, 'ftp.') === 0) $url = 'ftp://'. $url;
+ if (!preg_match('/# Valid absolute URI having a non-empty, valid DNS host.
+ ^
+ (?P<scheme>[A-Za-z][A-Za-z0-9+\-.]*):\/\/
+ (?P<authority>
+ (?:(?P<userinfo>(?:[A-Za-z0-9\-._~!$&\'()*+,;=:]|%[0-9A-Fa-f]{2})*)@)?
+ (?P<host>
+ (?P<IP_literal>
+ \[
+ (?:
+ (?P<IPV6address>
+ (?: (?:[0-9A-Fa-f]{1,4}:){6}
+ | ::(?:[0-9A-Fa-f]{1,4}:){5}
+ | (?: [0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}
+ | (?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}
+ | (?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}
+ | (?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?:: [0-9A-Fa-f]{1,4}:
+ | (?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::
+ )
+ (?P<ls32>[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}
+ | (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
+ (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
+ )
+ | (?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?:: [0-9A-Fa-f]{1,4}
+ | (?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::
+ )
+ | (?P<IPvFuture>[Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&\'()*+,;=:]+)
+ )
+ \]
+ )
+ | (?P<IPv4address>(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
+ (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))
+ | (?P<regname>(?:[A-Za-z0-9\-._~!$&\'()*+,;=]|%[0-9A-Fa-f]{2})+)
+ )
+ (?::(?P<port>[0-9]*))?
+ )
+ (?P<path_abempty>(?:\/(?:[A-Za-z0-9\-._~!$&\'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)
+ (?:\?(?P<query> (?:[A-Za-z0-9\-._~!$&\'()*+,;=:@\\/?]|%[0-9A-Fa-f]{2})*))?
+ (?:\#(?P<fragment> (?:[A-Za-z0-9\-._~!$&\'()*+,;=:@\\/?]|%[0-9A-Fa-f]{2})*))?
+ $
+ /mx', $url, $m)) return FALSE;
+ switch ($m['scheme'])
+ {
+ case 'https':
+ case 'http':
+ if ($m['userinfo']) return FALSE; // HTTP scheme does not allow userinfo.
+ break;
+ case 'ftps':
+ case 'ftp':
+ break;
+ default:
+ return FALSE; // Unrecognised URI scheme. Default to FALSE.
+ }
+ // Validate host name conforms to DNS "dot-separated-parts".
+ if ($m{'regname'}) // If host regname specified, check for DNS conformance.
+ {
+ if (!preg_match('/# HTTP DNS host name.
+ ^ # Anchor to beginning of string.
+ (?!.{256}) # Overall host length is less than 256 chars.
+ (?: # Group dot separated host part alternatives.
+ [0-9A-Za-z]\. # Either a single alphanum followed by dot
+ | # or... part has more than one char (63 chars max).
+ [0-9A-Za-z] # Part first char is alphanum (no dash).
+ [\-0-9A-Za-z]{0,61} # Internal chars are alphanum plus dash.
+ [0-9A-Za-z] # Part last char is alphanum (no dash).
+ \. # Each part followed by literal dot.
+ )* # One or more parts before top level domain.
+ (?: # Top level domains
+ [A-Za-z]{2,63}| # Country codes are exactly two alpha chars.
+ xn--[0-9A-Za-z]{4,59}) # Internationalized Domain Name (IDN)
+ $ # Anchor to end of string.
+ /ix', $m['host'])) return FALSE;
+ }
+ $m['url'] = $url;
+ for ($i = 0; isset($m[$i]); ++$i) unset($m[$i]);
+ return $m; // return TRUE == array of useful named $matches plus the valid $url.
+}
+
+//
+// Replace string matching regular expression
+//
+// This function takes care of possibly disabled unicode properties in PCRE builds
+//
+function ucp_preg_replace($pattern, $replace, $subject, $callback = false)
+{
+ if($callback)
+ $replaced = preg_replace_callback($pattern, create_function('$matches', 'return '.$replace.';'), $subject);
+ else
+ $replaced = preg_replace($pattern, $replace, $subject);
+
+ // If preg_replace() returns false, this probably means unicode support is not built-in, so we need to modify the pattern a little
+ if ($replaced === false)
+ {
+ if (is_array($pattern))
+ {
+ foreach ($pattern as $cur_key => $cur_pattern)
+ $pattern[$cur_key] = str_replace('\p{L}\p{N}', '\w', $cur_pattern);
+
+ $replaced = preg_replace($pattern, $replace, $subject);
+ }
+ else
+ $replaced = preg_replace(str_replace('\p{L}\p{N}', '\w', $pattern), $replace, $subject);
+ }
+
+ return $replaced;
+}
+
+//
+// A wrapper for ucp_preg_replace
+//
+function ucp_preg_replace_callback($pattern, $replace, $subject)
+{
+ return ucp_preg_replace($pattern, $replace, $subject, true);
+}
+
+//
+// Replace four-byte characters with a question mark
+//
+// As MySQL cannot properly handle four-byte characters with the default utf-8
+// charset up until version 5.5.3 (where a special charset has to be used), they
+// need to be replaced, by question marks in this case.
+//
+function strip_bad_multibyte_chars($str)
+{
+ $result = '';
+ $length = strlen($str);
+
+ for ($i = 0; $i < $length; $i++)
+ {
+ // Replace four-byte characters (11110www 10zzzzzz 10yyyyyy 10xxxxxx)
+ $ord = ord($str[$i]);
+ if ($ord >= 240 && $ord <= 244)
+ {
+ $result .= '?';
+ $i += 3;
+ }
+ else
+ {
+ $result .= $str[$i];
+ }
+ }
+
+ return $result;
+}
+
+//
+// Check whether a file/folder is writable.
+//
+// This function also works on Windows Server where ACLs seem to be ignored.
+//
+function forum_is_writable($path)
+{
+ if (is_dir($path))
+ {
+ $path = rtrim($path, '/').'/';
+ return forum_is_writable($path.uniqid(mt_rand()).'.tmp');
+ }
+
+ // Check temporary file for read/write capabilities
+ $rm = file_exists($path);
+ $f = @fopen($path, 'a');
+
+ if ($f === false)
+ return false;
+
+ fclose($f);
+
+ if (!$rm)
+ @unlink($path);
+
+ return true;
+}
+
+
+// DEBUG FUNCTIONS BELOW
+
+//
+// Display executed queries (if enabled)
+//
+function display_saved_queries()
+{
+ global $db, $lang_common;
+
+ // Get the queries so that we can print them out
+ $saved_queries = $db->get_saved_queries();
+
+?>
+
+<div id="debug" class="blocktable">
+ <h2><span><?php echo $lang_common['Debug table'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_common['Query times'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_common['Query'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $query_time_total = 0.0;
+ foreach ($saved_queries as $cur_query)
+ {
+ $query_time_total += $cur_query[1];
+
+?>
+ <tr>
+ <td class="tcl"><?php echo ($cur_query[1] != 0) ? $cur_query[1] : '&#160;' ?></td>
+ <td class="tcr"><?php echo pun_htmlspecialchars($cur_query[0]) ?></td>
+ </tr>
+<?php
+
+ }
+
+?>
+ <tr>
+ <td class="tcl" colspan="2"><?php printf($lang_common['Total query time'], $query_time_total.' s') ?></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+<?php
+
+}
+
+
+//
+// Dump contents of variable(s)
+//
+function dump()
+{
+ echo '<pre>';
+
+ $num_args = func_num_args();
+
+ for ($i = 0; $i < $num_args; ++$i)
+ {
+ print_r(func_get_arg($i));
+ echo "\n\n";
+ }
+
+ echo '</pre>';
+ exit;
+}
diff --git a/include/index.html b/include/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/parser.php b/include/parser.php
new file mode 100644
index 0000000..b7eb4bd
--- /dev/null
+++ b/include/parser.php
@@ -0,0 +1,987 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+// Global variables
+/* regular expression to match nested BBCode LIST tags
+'%
+\[list # match opening bracket and tag name of outermost LIST tag
+(?:=([1a*]))?+ # optional attribute capture in group 1
+\] # closing bracket of outermost opening LIST tag
+( # capture contents of LIST tag in group 2
+ (?: # non capture group for either contents or whole nested LIST
+ [^\[]*+ # unroll the loop! consume everything up to next [ (normal *)
+ (?: # (See "Mastering Regular Expressions" chapter 6 for details)
+ (?! # negative lookahead ensures we are NOT on [LIST*] or [/LIST]
+ \[list # opening LIST tag
+ (?:=[1a*])?+ # with optional attribute
+ \] # closing bracket of opening LIST tag
+ | # or...
+ \[/list\] # a closing LIST tag
+ ) # end negative lookahead assertion (we are not on a LIST tag)
+ \[ # match the [ which is NOT the start of LIST tag (special)
+ [^\[]*+ # consume everything up to next [ (normal *)
+ )*+ # finish up "unrolling the loop" technique (special (normal*))*
+ | # or...
+ (?R) # recursively match a whole nested LIST element
+ )* # as many times as necessary until deepest nested LIST tag grabbed
+) # end capturing contents of LIST tag into group 2
+\[/list\] # match outermost closing LIST tag
+%iex' */
+$re_list = '%\[list(?:=([1a*]))?+\]((?:[^\[]*+(?:(?!\[list(?:=[1a*])?+\]|\[/list\])\[[^\[]*+)*+|(?R))*)\[/list\]%i';
+
+// Here you can add additional smilies if you like (please note that you must escape single quote and backslash)
+$smilies = array(
+ ':)' => 'smile.png',
+ '=)' => 'smile.png',
+ ':|' => 'neutral.png',
+ '=|' => 'neutral.png',
+ ':(' => 'sad.png',
+ '=(' => 'sad.png',
+ ':D' => 'big_smile.png',
+ '=D' => 'big_smile.png',
+ ':o' => 'yikes.png',
+ ':O' => 'yikes.png',
+ ';)' => 'wink.png',
+ ':/' => 'hmm.png',
+ ':P' => 'tongue.png',
+ ':p' => 'tongue.png',
+ ':lol:' => 'lol.png',
+ ':mad:' => 'mad.png',
+ ':rolleyes:' => 'roll.png',
+ ':cool:' => 'cool.png');
+
+//
+// Make sure all BBCodes are lower case and do a little cleanup
+//
+function preparse_bbcode($text, &$errors, $is_signature = false)
+{
+ global $pun_config, $lang_common, $lang_post, $re_list;
+
+ // Remove empty tags
+ while (($new_text = strip_empty_bbcode($text)) !== false)
+ {
+ if ($new_text != $text)
+ {
+ $text = $new_text;
+ if ($new_text == '')
+ {
+ $errors[] = $lang_post['Empty after strip'];
+ return '';
+ }
+ }
+ else
+ break;
+ }
+
+ if ($is_signature)
+ {
+ global $lang_profile;
+
+ if (preg_match('%\[/?(?:quote|code|list|h)\b[^\]]*\]%i', $text))
+ $errors[] = $lang_profile['Signature quote/code/list/h'];
+ }
+
+ // If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
+ if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
+ list($inside, $text) = extract_blocks($text, '[code]', '[/code]');
+
+ // Tidy up lists
+ $temp = preg_replace_callback($re_list, create_function('$matches', 'return preparse_list_tag($matches[2], $matches[1]);'), $text);
+
+ // If the regex failed
+ if (is_null($temp))
+ $errors[] = $lang_common['BBCode list size error'];
+ else
+ $text = str_replace('*'."\0".']', '*]', $temp);
+
+ if ($pun_config['o_make_links'] == '1')
+ $text = do_clickable($text);
+
+ $temp_text = false;
+ if (empty($errors))
+ $temp_text = preparse_tags($text, $errors, $is_signature);
+
+ if ($temp_text !== false)
+ $text = $temp_text;
+
+ // If we split up the message before we have to concatenate it together again (code tags)
+ if (isset($inside))
+ {
+ $outside = explode("\1", $text);
+ $text = '';
+
+ $num_tokens = count($outside);
+ for ($i = 0; $i < $num_tokens; ++$i)
+ {
+ $text .= $outside[$i];
+ if (isset($inside[$i]))
+ $text .= '[code]'.$inside[$i].'[/code]';
+ }
+
+ unset($inside);
+ }
+
+ // Remove empty tags
+ while (($new_text = strip_empty_bbcode($text)) !== false)
+ {
+ if ($new_text != $text)
+ {
+ $text = $new_text;
+ if ($new_text == '')
+ {
+ $errors[] = $lang_post['Empty after strip'];
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ return pun_trim($text);
+}
+
+
+//
+// Strip empty bbcode tags from some text
+//
+function strip_empty_bbcode($text)
+{
+ // If the message contains a code tag we have to split it up (empty tags within [code][/code] are fine)
+ if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
+ list($inside, $text) = extract_blocks($text, '[code]', '[/code]');
+
+ // Remove empty tags
+ while (!is_null($new_text = preg_replace('%\[(b|u|s|ins|del|em|i|h|colou?r|quote|img|url|email|list|topic|post|forum|user)(?:\=[^\]]*)?\]\s*\[/\1\]%', '', $text)))
+ {
+ if ($new_text != $text)
+ $text = $new_text;
+ else
+ break;
+ }
+
+ // If we split up the message before we have to concatenate it together again (code tags)
+ if (isset($inside))
+ {
+ $parts = explode("\1", $text);
+ $text = '';
+ foreach ($parts as $i => $part)
+ {
+ $text .= $part;
+ if (isset($inside[$i]))
+ $text .= '[code]'.$inside[$i].'[/code]';
+ }
+ }
+
+ // Remove empty code tags
+ while (!is_null($new_text = preg_replace('%\[(code)\]\s*\[/\1\]%', '', $text)))
+ {
+ if ($new_text != $text)
+ $text = $new_text;
+ else
+ break;
+ }
+
+ return $text;
+}
+
+
+//
+// Check the structure of bbcode tags and fix simple mistakes where possible
+//
+function preparse_tags($text, &$errors, $is_signature = false)
+{
+ global $lang_common, $pun_config, $pun_user;
+
+ // Start off by making some arrays of bbcode tags and what we need to do with each one
+
+ // List of all the tags
+ $tags = array('quote', 'code', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'img', 'list', '*', 'h', 'topic', 'post', 'forum', 'user');
+ // List of tags that we need to check are open (You could not put b,i,u in here then illegal nesting like [b][i][/b][/i] would be allowed)
+ $tags_opened = $tags;
+ // and tags we need to check are closed (the same as above, added it just in case)
+ $tags_closed = $tags;
+ // Tags we can nest and the depth they can be nested to
+ $tags_nested = array('quote' => $pun_config['o_quote_depth'], 'list' => 5, '*' => 5);
+ // Tags to ignore the contents of completely (just code)
+ $tags_ignore = array('code');
+ // Tags not allowed
+ $tags_forbidden = array();
+ // Block tags, block tags can only go within another block tag, they cannot be in a normal tag
+ $tags_block = array('quote', 'code', 'list', 'h', '*');
+ // Inline tags, we do not allow new lines in these
+ $tags_inline = array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'h', 'topic', 'post', 'forum', 'user');
+ // Tags we trim interior space
+ $tags_trim = array('img');
+ // Tags we remove quotes from the argument
+ $tags_quotes = array('url', 'email', 'img', 'topic', 'post', 'forum', 'user');
+ // Tags we limit bbcode in
+ $tags_limit_bbcode = array(
+ '*' => array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'list', 'img', 'code', 'topic', 'post', 'forum', 'user'),
+ 'list' => array('*'),
+ 'url' => array('img'),
+ 'email' => array('img'),
+ 'topic' => array('img'),
+ 'post' => array('img'),
+ 'forum' => array('img'),
+ 'user' => array('img'),
+ 'img' => array(),
+ 'h' => array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'topic', 'post', 'forum', 'user'),
+ );
+ // Tags we can automatically fix bad nesting
+ $tags_fix = array('quote', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'h', 'topic', 'post', 'forum', 'user');
+
+ // Disallow URL tags
+ if ($pun_user['g_post_links'] != '1')
+ $tags_forbidden[] = 'url';
+
+ $split_text = preg_split('%(\[[\*a-zA-Z0-9-/]*?(?:=.*?)?\])%', $text, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+
+ $open_tags = array('fluxbb-bbcode');
+ $open_args = array('');
+ $opened_tag = 0;
+ $new_text = '';
+ $current_ignore = '';
+ $current_nest = '';
+ $current_depth = array();
+ $limit_bbcode = $tags;
+ $count_ignored = array();
+
+ foreach ($split_text as $current)
+ {
+ if ($current == '')
+ continue;
+
+ // Are we dealing with a tag?
+ if (substr($current, 0, 1) != '[' || substr($current, -1, 1) != ']')
+ {
+ // It's not a bbcode tag so we put it on the end and continue
+ // If we are nested too deeply don't add to the end
+ if ($current_nest)
+ continue;
+
+ $current = str_replace("\r\n", "\n", $current);
+ $current = str_replace("\r", "\n", $current);
+ if (in_array($open_tags[$opened_tag], $tags_inline) && strpos($current, "\n") !== false)
+ {
+ // Deal with new lines
+ $split_current = preg_split('%(\n\n+)%', $current, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+ $current = '';
+
+ if (!pun_trim($split_current[0], "\n")) // The first part is a linebreak so we need to handle any open tags first
+ array_unshift($split_current, '');
+
+ for ($i = 1; $i < count($split_current); $i += 2)
+ {
+ $temp_opened = array();
+ $temp_opened_arg = array();
+ $temp = $split_current[$i - 1];
+ while (!empty($open_tags))
+ {
+ $temp_tag = array_pop($open_tags);
+ $temp_arg = array_pop($open_args);
+
+ if (in_array($temp_tag , $tags_inline))
+ {
+ array_push($temp_opened, $temp_tag);
+ array_push($temp_opened_arg, $temp_arg);
+ $temp .= '[/'.$temp_tag.']';
+ }
+ else
+ {
+ array_push($open_tags, $temp_tag);
+ array_push($open_args, $temp_arg);
+ break;
+ }
+ }
+ $current .= $temp.$split_current[$i];
+ $temp = '';
+ while (!empty($temp_opened))
+ {
+ $temp_tag = array_pop($temp_opened);
+ $temp_arg = array_pop($temp_opened_arg);
+ if (empty($temp_arg))
+ $temp .= '['.$temp_tag.']';
+ else
+ $temp .= '['.$temp_tag.'='.$temp_arg.']';
+ array_push($open_tags, $temp_tag);
+ array_push($open_args, $temp_arg);
+ }
+ $current .= $temp;
+ }
+
+ if (array_key_exists($i - 1, $split_current))
+ $current .= $split_current[$i - 1];
+ }
+
+ if (in_array($open_tags[$opened_tag], $tags_trim))
+ $new_text .= pun_trim($current);
+ else
+ $new_text .= $current;
+
+ continue;
+ }
+
+ // Get the name of the tag
+ $current_arg = '';
+ if (strpos($current, '/') === 1)
+ {
+ $current_tag = substr($current, 2, -1);
+ }
+ else if (strpos($current, '=') === false)
+ {
+ $current_tag = substr($current, 1, -1);
+ }
+ else
+ {
+ $current_tag = substr($current, 1, strpos($current, '=')-1);
+ $current_arg = substr($current, strpos($current, '=')+1, -1);
+ }
+ $current_tag = strtolower($current_tag);
+
+ // Is the tag defined?
+ if (!in_array($current_tag, $tags))
+ {
+ // It's not a bbcode tag so we put it on the end and continue
+ if (!$current_nest)
+ $new_text .= $current;
+
+ continue;
+ }
+
+ // We definitely have a bbcode tag
+
+ // Make the tag string lower case
+ if ($equalpos = strpos($current,'='))
+ {
+ // We have an argument for the tag which we don't want to make lowercase
+ if (strlen(substr($current, $equalpos)) == 2)
+ {
+ // Empty tag argument
+ $errors[] = sprintf($lang_common['BBCode error empty attribute'], $current_tag);
+ return false;
+ }
+ $current = strtolower(substr($current, 0, $equalpos)).substr($current, $equalpos);
+ }
+ else
+ $current = strtolower($current);
+
+ // This is if we are currently in a tag which escapes other bbcode such as code
+ // We keep a count of ignored bbcodes (code tags) so we can nest them, but
+ // only balanced sets of tags can be nested
+ if ($current_ignore)
+ {
+ // Increase the current ignored tags counter
+ if ('['.$current_ignore.']' == $current)
+ $count_ignored[$current_tag]++;
+
+ // Decrease the current ignored tags counter
+ if ('[/'.$current_ignore.']' == $current)
+ $count_ignored[$current_tag]--;
+
+ if ('[/'.$current_ignore.']' == $current && $count_ignored[$current_tag] == 0)
+ {
+ // We've finished the ignored section
+ $current = '[/'.$current_tag.']';
+ $current_ignore = '';
+ $count_ignored = array();
+ }
+
+ $new_text .= $current;
+
+ continue;
+ }
+
+ // Is the tag forbidden?
+ if (in_array($current_tag, $tags_forbidden))
+ {
+ if (isset($lang_common['BBCode error tag '.$current_tag.' not allowed']))
+ $errors[] = sprintf($lang_common['BBCode error tag '.$current_tag.' not allowed']);
+ else
+ $errors[] = sprintf($lang_common['BBCode error tag not allowed'], $current_tag);
+
+ return false;
+ }
+
+ if ($current_nest)
+ {
+ // We are currently too deeply nested so lets see if we are closing the tag or not
+ if ($current_tag != $current_nest)
+ continue;
+
+ if (substr($current, 1, 1) == '/')
+ $current_depth[$current_nest]--;
+ else
+ $current_depth[$current_nest]++;
+
+ if ($current_depth[$current_nest] <= $tags_nested[$current_nest])
+ $current_nest = '';
+
+ continue;
+ }
+
+ // Check the current tag is allowed here
+ if (!in_array($current_tag, $limit_bbcode) && $current_tag != $open_tags[$opened_tag])
+ {
+ $errors[] = sprintf($lang_common['BBCode error invalid nesting'], $current_tag, $open_tags[$opened_tag]);
+ return false;
+ }
+
+ if (substr($current, 1, 1) == '/')
+ {
+ // This is if we are closing a tag
+ if ($opened_tag == 0 || !in_array($current_tag, $open_tags))
+ {
+ // We tried to close a tag which is not open
+ if (in_array($current_tag, $tags_opened))
+ {
+ $errors[] = sprintf($lang_common['BBCode error no opening tag'], $current_tag);
+ return false;
+ }
+ }
+ else
+ {
+ // Check nesting
+ while (true)
+ {
+ // Nesting is ok
+ if ($open_tags[$opened_tag] == $current_tag)
+ {
+ array_pop($open_tags);
+ array_pop($open_args);
+ $opened_tag--;
+ break;
+ }
+
+ // Nesting isn't ok, try to fix it
+ if (in_array($open_tags[$opened_tag], $tags_closed) && in_array($current_tag, $tags_closed))
+ {
+ if (in_array($current_tag, $open_tags))
+ {
+ $temp_opened = array();
+ $temp_opened_arg = array();
+ $temp = '';
+ while (!empty($open_tags))
+ {
+ $temp_tag = array_pop($open_tags);
+ $temp_arg = array_pop($open_args);
+
+ if (!in_array($temp_tag, $tags_fix))
+ {
+ // We couldn't fix nesting
+ $errors[] = sprintf($lang_common['BBCode error no closing tag'], $temp_tag);
+ return false;
+ }
+ array_push($temp_opened, $temp_tag);
+ array_push($temp_opened_arg, $temp_arg);
+
+ if ($temp_tag == $current_tag)
+ break;
+ else
+ $temp .= '[/'.$temp_tag.']';
+ }
+ $current = $temp.$current;
+ $temp = '';
+ array_pop($temp_opened);
+ array_pop($temp_opened_arg);
+
+ while (!empty($temp_opened))
+ {
+ $temp_tag = array_pop($temp_opened);
+ $temp_arg = array_pop($temp_opened_arg);
+ if (empty($temp_arg))
+ $temp .= '['.$temp_tag.']';
+ else
+ $temp .= '['.$temp_tag.'='.$temp_arg.']';
+ array_push($open_tags, $temp_tag);
+ array_push($open_args, $temp_arg);
+ }
+ $current .= $temp;
+ $opened_tag--;
+ break;
+ }
+ else
+ {
+ // We couldn't fix nesting
+ $errors[] = sprintf($lang_common['BBCode error no opening tag'], $current_tag);
+ return false;
+ }
+ }
+ else if (in_array($open_tags[$opened_tag], $tags_closed))
+ break;
+ else
+ {
+ array_pop($open_tags);
+ array_pop($open_args);
+ $opened_tag--;
+ }
+ }
+ }
+
+ if (in_array($current_tag, array_keys($tags_nested)))
+ {
+ if (isset($current_depth[$current_tag]))
+ $current_depth[$current_tag]--;
+ }
+
+ if (in_array($open_tags[$opened_tag], array_keys($tags_limit_bbcode)))
+ $limit_bbcode = $tags_limit_bbcode[$open_tags[$opened_tag]];
+ else
+ $limit_bbcode = $tags;
+
+ $new_text .= $current;
+
+ continue;
+ }
+ else
+ {
+ // We are opening a tag
+ if (in_array($current_tag, array_keys($tags_limit_bbcode)))
+ $limit_bbcode = $tags_limit_bbcode[$current_tag];
+ else
+ $limit_bbcode = $tags;
+
+ if (in_array($current_tag, $tags_block) && !in_array($open_tags[$opened_tag], $tags_block) && $opened_tag != 0)
+ {
+ // We tried to open a block tag within a non-block tag
+ $errors[] = sprintf($lang_common['BBCode error invalid nesting'], $current_tag, $open_tags[$opened_tag]);
+ return false;
+ }
+
+ if (in_array($current_tag, $tags_ignore))
+ {
+ // It's an ignore tag so we don't need to worry about what's inside it
+ $current_ignore = $current_tag;
+ $count_ignored[$current_tag] = 1;
+ $new_text .= $current;
+ continue;
+ }
+
+ // Deal with nested tags
+ if (in_array($current_tag, $open_tags) && !in_array($current_tag, array_keys($tags_nested)))
+ {
+ // We nested a tag we shouldn't
+ $errors[] = sprintf($lang_common['BBCode error invalid self-nesting'], $current_tag);
+ return false;
+ }
+ else if (in_array($current_tag, array_keys($tags_nested)))
+ {
+ // We are allowed to nest this tag
+
+ if (isset($current_depth[$current_tag]))
+ $current_depth[$current_tag]++;
+ else
+ $current_depth[$current_tag] = 1;
+
+ // See if we are nested too deep
+ if ($current_depth[$current_tag] > $tags_nested[$current_tag])
+ {
+ $current_nest = $current_tag;
+ continue;
+ }
+ }
+
+ // Remove quotes from arguments for certain tags
+ if (strpos($current, '=') !== false && in_array($current_tag, $tags_quotes))
+ {
+ $current = preg_replace('%\['.$current_tag.'=("|\'|)(.*?)\\1\]\s*%i', '['.$current_tag.'=$2]', $current);
+ }
+
+ if (in_array($current_tag, array_keys($tags_limit_bbcode)))
+ $limit_bbcode = $tags_limit_bbcode[$current_tag];
+
+ $open_tags[] = $current_tag;
+ $open_args[] = $current_arg;
+ $opened_tag++;
+ $new_text .= $current;
+ continue;
+ }
+ }
+
+ // Check we closed all the tags we needed to
+ foreach ($tags_closed as $check)
+ {
+ if (in_array($check, $open_tags))
+ {
+ // We left an important tag open
+ $errors[] = sprintf($lang_common['BBCode error no closing tag'], $check);
+ return false;
+ }
+ }
+
+ if ($current_ignore)
+ {
+ // We left an ignore tag open
+ $errors[] = sprintf($lang_common['BBCode error no closing tag'], $current_ignore);
+ return false;
+ }
+
+ return $new_text;
+}
+
+
+//
+// Preparse the contents of [list] bbcode
+//
+function preparse_list_tag($content, $type = '*')
+{
+ global $lang_common, $re_list;
+
+ if (strlen($type) != 1)
+ $type = '*';
+
+ if (strpos($content,'[list') !== false)
+ {
+ $content = preg_replace_callback($re_list, create_function('$matches', 'return preparse_list_tag($matches[2], $matches[1]);'), $content);
+ }
+
+ $items = explode('[*]', str_replace('\"', '"', $content));
+
+ $content = '';
+ foreach ($items as $item)
+ {
+ if (pun_trim($item) != '')
+ $content .= '[*'."\0".']'.str_replace('[/*]', '', pun_trim($item)).'[/*'."\0".']'."\n";
+ }
+
+ return '[list='.$type.']'."\n".$content.'[/list]';
+}
+
+
+//
+// Truncate URL if longer than 55 characters (add http:// or ftp:// if missing)
+//
+function handle_url_tag($url, $link = '', $bbcode = false)
+{
+ $url = pun_trim($url);
+
+ // Deal with [url][img]http://example.com/test.png[/img][/url]
+ if (preg_match('%<img src=\"(.*?)\"%', $url, $matches))
+ return handle_url_tag($matches[1], $url, $bbcode);
+
+ $full_url = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
+ if (strpos($url, 'www.') === 0) // If it starts with www, we add http://
+ $full_url = 'http://'.$full_url;
+ else if (strpos($url, 'ftp.') === 0) // Else if it starts with ftp, we add ftp://
+ $full_url = 'ftp://'.$full_url;
+ else if (strpos($url, '/') === 0) // Allow for relative URLs that start with a slash
+ $full_url = get_base_url(true).$full_url;
+ else if (!preg_match('#^([a-z0-9]{3,6})://#', $url)) // Else if it doesn't start with abcdef://, we add http://
+ $full_url = 'http://'.$full_url;
+
+ // Ok, not very pretty :-)
+ if ($bbcode)
+ {
+ if ($full_url == $link)
+ return '[url]'.$link.'[/url]';
+ else
+ return '[url='.$full_url.']'.$link.'[/url]';
+ }
+ else
+ {
+ if ($link == '' || $link == $url)
+ {
+ $url = pun_htmlspecialchars_decode($url);
+ $link = utf8_strlen($url) > 55 ? utf8_substr($url, 0 , 39).' … '.utf8_substr($url, -10) : $url;
+ $link = pun_htmlspecialchars($link);
+ }
+ else
+ $link = stripslashes($link);
+
+ return '<a href="'.$full_url.'" rel="nofollow">'.$link.'</a>';
+ }
+}
+
+
+//
+// Turns an URL from the [img] tag into an <img> tag or a <a href...> tag
+//
+function handle_img_tag($url, $is_signature = false, $alt = null)
+{
+ global $lang_common, $pun_user;
+
+ if (is_null($alt))
+ $alt = basename($url);
+
+ $img_tag = '<a href="'.$url.'" rel="nofollow">&lt;'.$lang_common['Image link'].' - '.$alt.'&gt;</a>';
+
+ if ($is_signature && $pun_user['show_img_sig'] != '0')
+ $img_tag = '<img class="sigimage" src="'.$url.'" alt="'.$alt.'" />';
+ else if (!$is_signature && $pun_user['show_img'] != '0')
+ $img_tag = '<span class="postimg"><img src="'.$url.'" alt="'.$alt.'" /></span>';
+
+ return $img_tag;
+}
+
+
+//
+// Parse the contents of [list] bbcode
+//
+function handle_list_tag($content, $type = '*')
+{
+ global $re_list;
+
+ if (strlen($type) != 1)
+ $type = '*';
+
+ if (strpos($content,'[list') !== false)
+ {
+ $content = preg_replace_callback($re_list, create_function('$matches', 'return handle_list_tag($matches[2], $matches[1]);'), $content);
+ }
+
+ $content = preg_replace('#\s*\[\*\](.*?)\[/\*\]\s*#s', '<li><p>$1</p></li>', pun_trim($content));
+
+ if ($type == '*')
+ $content = '<ul>'.$content.'</ul>';
+ else
+ if ($type == 'a')
+ $content = '<ol class="alpha">'.$content.'</ol>';
+ else
+ $content = '<ol class="decimal">'.$content.'</ol>';
+
+ return '</p>'.$content.'<p>';
+}
+
+
+//
+// Convert BBCodes to their HTML equivalent
+//
+function do_bbcode($text, $is_signature = false)
+{
+ global $lang_common, $pun_user, $pun_config, $re_list;
+
+ if (strpos($text, '[quote') !== false)
+ {
+ $text = preg_replace('%\[quote\]\s*%', '</p><div class="quotebox"><blockquote><div><p>', $text);
+ $text = preg_replace_callback('%\[quote=(&quot;|&\#039;|"|\'|)([^\r\n]*?)\\1\]%s', create_function('$matches', 'global $lang_common; return "</p><div class=\"quotebox\"><cite>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), $matches[2])." ".$lang_common[\'wrote\']."</cite><blockquote><div><p>";'), $text);
+ $text = preg_replace('%\s*\[\/quote\]%S', '</p></div></blockquote></div><p>', $text);
+ }
+ if (!$is_signature)
+ {
+ $pattern_callback[] = $re_list;
+ $replace_callback[] = 'handle_list_tag($matches[2], $matches[1])';
+ }
+
+ $pattern[] = '%\[b\](.*?)\[/b\]%ms';
+ $pattern[] = '%\[i\](.*?)\[/i\]%ms';
+ $pattern[] = '%\[u\](.*?)\[/u\]%ms';
+ $pattern[] = '%\[s\](.*?)\[/s\]%ms';
+ $pattern[] = '%\[del\](.*?)\[/del\]%ms';
+ $pattern[] = '%\[ins\](.*?)\[/ins\]%ms';
+ $pattern[] = '%\[em\](.*?)\[/em\]%ms';
+ $pattern[] = '%\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]%ms';
+ $pattern[] = '%\[h\](.*?)\[/h\]%ms';
+
+ $replace[] = '<strong>$1</strong>';
+ $replace[] = '<em>$1</em>';
+ $replace[] = '<span class="bbu">$1</span>';
+ $replace[] = '<span class="bbs">$1</span>';
+ $replace[] = '<del>$1</del>';
+ $replace[] = '<ins>$1</ins>';
+ $replace[] = '<em>$1</em>';
+ $replace[] = '<span style="color: $1">$2</span>';
+ $replace[] = '</p><h5>$1</h5><p>';
+
+ if (($is_signature && $pun_config['p_sig_img_tag'] == '1') || (!$is_signature && $pun_config['p_message_img_tag'] == '1'))
+ {
+ $pattern_callback[] = '%\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%';
+ $pattern_callback[] = '%\[img=([^\[]*?)\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%';
+ if ($is_signature)
+ {
+ $replace_callback[] = 'handle_img_tag($matches[1].$matches[3], true)';
+ $replace_callback[] = 'handle_img_tag($matches[2].$matches[4], true, $matches[1])';
+ }
+ else
+ {
+ $replace_callback[] = 'handle_img_tag($matches[1].$matches[3], false)';
+ $replace_callback[] = 'handle_img_tag($matches[2].$matches[4], false, $matches[1])';
+ }
+ }
+
+ $pattern_callback[] = '%\[url\]([^\[]*?)\[/url\]%';
+ $pattern_callback[] = '%\[url=([^\[]+?)\](.*?)\[/url\]%';
+ $pattern[] = '%\[email\]([^\[]*?)\[/email\]%';
+ $pattern[] = '%\[email=([^\[]+?)\](.*?)\[/email\]%';
+ $pattern_callback[] = '%\[topic\]([1-9]\d*)\[/topic\]%';
+ $pattern_callback[] = '%\[topic=([1-9]\d*)\](.*?)\[/topic\]%';
+ $pattern_callback[] = '%\[post\]([1-9]\d*)\[/post\]%';
+ $pattern_callback[] = '%\[post=([1-9]\d*)\](.*?)\[/post\]%';
+ $pattern_callback[] = '%\[forum\]([1-9]\d*)\[/forum\]%';
+ $pattern_callback[] = '%\[forum=([1-9]\d*)\](.*?)\[/forum\]%';
+ $pattern_callback[] = '%\[user\]([1-9]\d*)\[/user\]%';
+ $pattern_callback[] = '%\[user=([1-9]\d*)\](.*?)\[/user\]%';
+
+ $replace_callback[] = 'handle_url_tag($matches[1])';
+ $replace_callback[] = 'handle_url_tag($matches[1], $matches[2])';
+ $replace[] = '<a href="mailto:$1">$1</a>';
+ $replace[] = '<a href="mailto:$1">$2</a>';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?id=\'.$matches[1])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?id=\'.$matches[1], $matches[2])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?pid=\'.$matches[1].\'#p\'.$matches[1])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?pid=\'.$matches[1].\'#p\'.$matches[1], $matches[2])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/viewforum.php?id=\'.$matches[1])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/viewforum.php?id=\'.$matches[1], $matches[2])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/profile.php?id=\'.$matches[1])';
+ $replace_callback[] = 'handle_url_tag(\''.get_base_url(true).'/profile.php?id=\'.$matches[1], $matches[2])';
+
+ // This thing takes a while! :)
+ $text = preg_replace($pattern, $replace, $text);
+ $count = count($pattern_callback);
+ for($i = 0 ; $i < $count ; $i++)
+ {
+ $text = preg_replace_callback($pattern_callback[$i], create_function('$matches', 'return '.$replace_callback[$i].';'), $text);
+ }
+ return $text;
+}
+
+
+//
+// Make hyperlinks clickable
+//
+function do_clickable($text)
+{
+ $text = ' '.$text;
+ $text = ucp_preg_replace_callback('%(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\p{L}\p{N}\-]+\.([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/(?:[^\s\[]*[^\s.,?!\[;:-])?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])%ui', 'stripslashes($matches[1].$matches[2].$matches[3].$matches[4]).handle_url_tag($matches[5]."://".$matches[6], $matches[5]."://".$matches[6], true).stripslashes($matches[4].forum_array_key($matches, 10).forum_array_key($matches, 11).forum_array_key($matches, 12))', $text);
+ $text = ucp_preg_replace_callback('%(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\p{L}\p{N}\-]+\.)+[\p{L}\p{N}]+(:[0-9]+)?(/(?:[^\s\[]*[^\s.,?!\[;:-])?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])%ui','stripslashes($matches[1].$matches[2].$matches[3].$matches[4]).handle_url_tag($matches[5].".".$matches[6], $matches[5].".".$matches[6], true).stripslashes($matches[4].forum_array_key($matches, 10).forum_array_key($matches, 11).forum_array_key($matches, 12))', $text);
+
+ return substr($text, 1);
+}
+
+
+//
+// Return an array key, if it exists, otherwise return an empty string
+//
+function forum_array_key($arr, $key)
+{
+ return isset($arr[$key]) ? $arr[$key] : '';
+}
+
+
+//
+// Convert a series of smilies to images
+//
+function do_smilies($text)
+{
+ global $smilies;
+
+ $text = ' '.$text.' ';
+
+ foreach ($smilies as $smiley_text => $smiley_img)
+ {
+ if (strpos($text, $smiley_text) !== false)
+ $text = ucp_preg_replace('%(?<=[>\s])'.preg_quote($smiley_text, '%').'(?=[^\p{L}\p{N}])%um', '<img src="'.pun_htmlspecialchars(get_base_url(true).'/img/smilies/'.$smiley_img).'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
+ }
+
+ return substr($text, 1, -1);
+}
+
+
+//
+// Parse message text
+//
+function parse_message($text, $hide_smilies)
+{
+ global $pun_config, $lang_common, $pun_user;
+
+ if ($pun_config['o_censoring'] == '1')
+ $text = censor_words($text);
+
+ // Convert applicable characters to HTML entities
+ $text = pun_htmlspecialchars($text);
+
+ // If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
+ if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
+ list($inside, $text) = extract_blocks($text, '[code]', '[/code]');
+
+ if ($pun_config['p_message_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
+ $text = do_bbcode($text);
+
+ if ($pun_config['o_smilies'] == '1' && $pun_user['show_smilies'] == '1' && $hide_smilies == '0')
+ $text = do_smilies($text);
+
+ // Deal with newlines, tabs and multiple spaces
+ $pattern = array("\n", "\t", ' ', ' ');
+ $replace = array('<br />', '&#160; &#160; ', '&#160; ', ' &#160;');
+ $text = str_replace($pattern, $replace, $text);
+
+ // If we split up the message before we have to concatenate it together again (code tags)
+ if (isset($inside))
+ {
+ $parts = explode("\1", $text);
+ $text = '';
+ foreach ($parts as $i => $part)
+ {
+ $text .= $part;
+ if (isset($inside[$i]))
+ {
+ $num_lines = (substr_count($inside[$i], "\n"));
+ $text .= '</p><div class="codebox"><pre'.(($num_lines > 28) ? ' class="vscroll"' : '').'><code>'.pun_trim($inside[$i], "\n\r").'</code></pre></div><p>';
+ }
+ }
+ }
+
+ return clean_paragraphs($text);
+}
+
+
+//
+// Clean up paragraphs and line breaks
+//
+function clean_paragraphs($text)
+{
+ // Add paragraph tag around post, but make sure there are no empty paragraphs
+
+ $text = '<p>'.$text.'</p>';
+
+ // Replace any breaks next to paragraphs so our replace below catches them
+ $text = preg_replace('%(</?p>)(?:\s*?<br />){1,2}%i', '$1', $text);
+ $text = preg_replace('%(?:<br />\s*?){1,2}(</?p>)%i', '$1', $text);
+
+ // Remove any empty paragraph tags (inserted via quotes/lists/code/etc) which should be stripped
+ $text = str_replace('<p></p>', '', $text);
+
+ $text = preg_replace('%<br />\s*?<br />%i', '</p><p>', $text);
+
+ $text = str_replace('<p><br />', '<br /><p>', $text);
+ $text = str_replace('<br /></p>', '</p><br />', $text);
+ $text = str_replace('<p></p>', '<br /><br />', $text);
+
+ return $text;
+}
+
+
+//
+// Parse signature text
+//
+function parse_signature($text)
+{
+ global $pun_config, $lang_common, $pun_user;
+
+ if ($pun_config['o_censoring'] == '1')
+ $text = censor_words($text);
+
+ // Convert applicable characters to HTML entities
+ $text = pun_htmlspecialchars($text);
+
+ if ($pun_config['p_sig_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
+ $text = do_bbcode($text, true);
+
+ if ($pun_config['o_smilies_sig'] == '1' && $pun_user['show_smilies'] == '1')
+ $text = do_smilies($text);
+
+
+ // Deal with newlines, tabs and multiple spaces
+ $pattern = array("\n", "\t", ' ', ' ');
+ $replace = array('<br />', '&#160; &#160; ', '&#160; ', ' &#160;');
+ $text = str_replace($pattern, $replace, $text);
+
+ return clean_paragraphs($text);
+}
diff --git a/include/search_idx.php b/include/search_idx.php
new file mode 100644
index 0000000..49fe257
--- /dev/null
+++ b/include/search_idx.php
@@ -0,0 +1,316 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The contents of this file are very much inspired by the file functions_search.php
+// from the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+
+// Make a regex that will match CJK or Hangul characters
+define('PUN_CJK_HANGUL_REGEX', '['.
+ '\x{1100}-\x{11FF}'. // Hangul Jamo 1100-11FF (http://www.fileformat.info/info/unicode/block/hangul_jamo/index.htm)
+ '\x{3130}-\x{318F}'. // Hangul Compatibility Jamo 3130-318F (http://www.fileformat.info/info/unicode/block/hangul_compatibility_jamo/index.htm)
+ '\x{AC00}-\x{D7AF}'. // Hangul Syllables AC00-D7AF (http://www.fileformat.info/info/unicode/block/hangul_syllables/index.htm)
+
+ // Hiragana
+ '\x{3040}-\x{309F}'. // Hiragana 3040-309F (http://www.fileformat.info/info/unicode/block/hiragana/index.htm)
+
+ // Katakana
+ '\x{30A0}-\x{30FF}'. // Katakana 30A0-30FF (http://www.fileformat.info/info/unicode/block/katakana/index.htm)
+ '\x{31F0}-\x{31FF}'. // Katakana Phonetic Extensions 31F0-31FF (http://www.fileformat.info/info/unicode/block/katakana_phonetic_extensions/index.htm)
+
+ // CJK Unified Ideographs (http://en.wikipedia.org/wiki/CJK_Unified_Ideographs)
+ '\x{2E80}-\x{2EFF}'. // CJK Radicals Supplement 2E80-2EFF (http://www.fileformat.info/info/unicode/block/cjk_radicals_supplement/index.htm)
+ '\x{2F00}-\x{2FDF}'. // Kangxi Radicals 2F00-2FDF (http://www.fileformat.info/info/unicode/block/kangxi_radicals/index.htm)
+ '\x{2FF0}-\x{2FFF}'. // Ideographic Description Characters 2FF0-2FFF (http://www.fileformat.info/info/unicode/block/ideographic_description_characters/index.htm)
+ '\x{3000}-\x{303F}'. // CJK Symbols and Punctuation 3000-303F (http://www.fileformat.info/info/unicode/block/cjk_symbols_and_punctuation/index.htm)
+ '\x{31C0}-\x{31EF}'. // CJK Strokes 31C0-31EF (http://www.fileformat.info/info/unicode/block/cjk_strokes/index.htm)
+ '\x{3200}-\x{32FF}'. // Enclosed CJK Letters and Months 3200-32FF (http://www.fileformat.info/info/unicode/block/enclosed_cjk_letters_and_months/index.htm)
+ '\x{3400}-\x{4DBF}'. // CJK Unified Ideographs Extension A 3400-4DBF (http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs_extension_a/index.htm)
+ '\x{4E00}-\x{9FFF}'. // CJK Unified Ideographs 4E00-9FFF (http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs/index.htm)
+ '\x{20000}-\x{2A6DF}'. // CJK Unified Ideographs Extension B 20000-2A6DF (http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs_extension_b/index.htm)
+']');
+
+
+//
+// "Cleans up" a text string and returns an array of unique words
+// This function depends on the current locale setting
+//
+function split_words($text, $idx)
+{
+ // Remove BBCode
+ $text = preg_replace('%\[/?(b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|topic|post|forum|user)(?:\=[^\]]*)?\]%', ' ', $text);
+
+ // Remove any apostrophes or dashes which aren't part of words
+ $text = substr(ucp_preg_replace('%((?<=[^\p{L}\p{N}])[\'\-]|[\'\-](?=[^\p{L}\p{N}]))%u', '', ' '.$text.' '), 1, -1);
+
+ // Remove punctuation and symbols (actually anything that isn't a letter or number), allow apostrophes and dashes (and % * if we aren't indexing)
+ $text = ucp_preg_replace('%(?![\'\-'.($idx ? '' : '\%\*').'])[^\p{L}\p{N}]+%u', ' ', $text);
+
+ // Replace multiple whitespace or dashes
+ $text = preg_replace('%(\s){2,}%u', '\1', $text);
+
+ // Fill an array with all the words
+ $words = array_unique(explode(' ', $text));
+
+ // Remove any words that should not be indexed
+ foreach ($words as $key => $value)
+ {
+ // If the word shouldn't be indexed, remove it
+ if (!validate_search_word($value, $idx))
+ unset($words[$key]);
+ }
+
+ return $words;
+}
+
+
+//
+// Checks if a word is a valid searchable word
+//
+function validate_search_word($word, $idx)
+{
+ static $stopwords;
+
+ // If the word is a keyword we don't want to index it, but we do want to be allowed to search it
+ if (is_keyword($word))
+ return !$idx;
+
+ if (!isset($stopwords))
+ {
+ if (file_exists(FORUM_CACHE_DIR.'cache_stopwords.php'))
+ include FORUM_CACHE_DIR.'cache_stopwords.php';
+
+ if (!defined('PUN_STOPWORDS_LOADED'))
+ {
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_stopwords_cache();
+ require FORUM_CACHE_DIR.'cache_stopwords.php';
+ }
+ }
+
+ // If it is a stopword it isn't valid
+ if (in_array($word, $stopwords))
+ return false;
+
+ // If the word is CJK we don't want to index it, but we do want to be allowed to search it
+ if (is_cjk($word))
+ return !$idx;
+
+ // Exclude % and * when checking whether current word is valid
+ $word = str_replace(array('%', '*'), '', $word);
+
+ // Check the word is within the min/max length
+ $num_chars = pun_strlen($word);
+ return $num_chars >= PUN_SEARCH_MIN_WORD && $num_chars <= PUN_SEARCH_MAX_WORD;
+}
+
+
+//
+// Check a given word is a search keyword.
+//
+function is_keyword($word)
+{
+ return $word == 'and' || $word == 'or' || $word == 'not';
+}
+
+
+//
+// Check if a given word is CJK or Hangul.
+//
+function is_cjk($word)
+{
+ return preg_match('%^'.PUN_CJK_HANGUL_REGEX.'+$%u', $word) ? true : false;
+}
+
+
+//
+// Strip [img] [url] and [email] out of the message so we don't index their contents
+//
+function strip_bbcode($text)
+{
+ static $patterns;
+
+ if (!isset($patterns))
+ {
+ $patterns = array(
+ '%\[img=([^\]]*+)\]([^[]*+)\[/img\]%' => '$2 $1', // Keep the url and description
+ '%\[(url|email)=([^\]]*+)\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%' => '$2 $3', // Keep the url and text
+ '%\[(img|url|email)\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%' => '$2', // Keep the url
+ '%\[(topic|post|forum|user)\][1-9]\d*\[/\1\]%' => ' ', // Do not index topic/post/forum/user ID
+ );
+ }
+
+ return preg_replace(array_keys($patterns), array_values($patterns), $text);
+}
+
+
+//
+// Updates the search index with the contents of $post_id (and $subject)
+//
+function update_search_index($mode, $post_id, $message, $subject = null)
+{
+ global $db_type, $db;
+
+ $message = utf8_strtolower($message);
+ $subject = utf8_strtolower($subject);
+
+ // Remove any bbcode that we shouldn't index
+ $message = strip_bbcode($message);
+
+ // Split old and new post/subject to obtain array of 'words'
+ $words_message = split_words($message, true);
+ $words_subject = ($subject) ? split_words($subject, true) : array();
+
+ if ($mode == 'edit')
+ {
+ $result = $db->query('SELECT w.id, w.word, m.subject_match FROM '.$db->prefix.'search_words AS w INNER JOIN '.$db->prefix.'search_matches AS m ON w.id=m.word_id WHERE m.post_id='.$post_id, true) or error('Unable to fetch search index words', __FILE__, __LINE__, $db->error());
+
+ // Declare here to stop array_keys() and array_diff() from complaining if not set
+ $cur_words['post'] = array();
+ $cur_words['subject'] = array();
+
+ while ($row = $db->fetch_row($result))
+ {
+ $match_in = ($row[2]) ? 'subject' : 'post';
+ $cur_words[$match_in][$row[1]] = $row[0];
+ }
+
+ $db->free_result($result);
+
+ $words['add']['post'] = array_diff($words_message, array_keys($cur_words['post']));
+ $words['add']['subject'] = array_diff($words_subject, array_keys($cur_words['subject']));
+ $words['del']['post'] = array_diff(array_keys($cur_words['post']), $words_message);
+ $words['del']['subject'] = array_diff(array_keys($cur_words['subject']), $words_subject);
+ }
+ else
+ {
+ $words['add']['post'] = $words_message;
+ $words['add']['subject'] = $words_subject;
+ $words['del']['post'] = array();
+ $words['del']['subject'] = array();
+ }
+
+ unset($words_message);
+ unset($words_subject);
+
+ // Get unique words from the above arrays
+ $unique_words = array_unique(array_merge($words['add']['post'], $words['add']['subject']));
+
+ if (!empty($unique_words))
+ {
+ $result = $db->query('SELECT id, word FROM '.$db->prefix.'search_words WHERE word IN(\''.implode('\',\'', array_map(array($db, 'escape'), $unique_words)).'\')', true) or error('Unable to fetch search index words', __FILE__, __LINE__, $db->error());
+
+ $word_ids = array();
+ while ($row = $db->fetch_row($result))
+ $word_ids[$row[1]] = $row[0];
+
+ $db->free_result($result);
+
+ $new_words = array_diff($unique_words, array_keys($word_ids));
+ unset($unique_words);
+
+ if (!empty($new_words))
+ {
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ $db->query('INSERT INTO '.$db->prefix.'search_words (word) VALUES(\''.implode('\'),(\'', array_map(array($db, 'escape'), $new_words)).'\')');
+ break;
+
+ default:
+ foreach ($new_words as $word)
+ $db->query('INSERT INTO '.$db->prefix.'search_words (word) VALUES(\''.$db->escape($word).'\')');
+ break;
+ }
+ }
+
+ unset($new_words);
+ }
+
+ // Delete matches (only if editing a post)
+ foreach ($words['del'] as $match_in => $wordlist)
+ {
+ $subject_match = ($match_in == 'subject') ? 1 : 0;
+
+ if (!empty($wordlist))
+ {
+ $sql = '';
+ foreach ($wordlist as $word)
+ $sql .= (($sql != '') ? ',' : '').$cur_words[$match_in][$word];
+
+ $db->query('DELETE FROM '.$db->prefix.'search_matches WHERE word_id IN('.$sql.') AND post_id='.$post_id.' AND subject_match='.$subject_match) or error('Unable to delete search index word matches', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ // Add new matches
+ foreach ($words['add'] as $match_in => $wordlist)
+ {
+ $subject_match = ($match_in == 'subject') ? 1 : 0;
+
+ if (!empty($wordlist))
+ $db->query('INSERT INTO '.$db->prefix.'search_matches (post_id, word_id, subject_match) SELECT '.$post_id.', id, '.$subject_match.' FROM '.$db->prefix.'search_words WHERE word IN(\''.implode('\',\'', array_map(array($db, 'escape'), $wordlist)).'\')') or error('Unable to insert search index word matches', __FILE__, __LINE__, $db->error());
+ }
+
+ unset($words);
+}
+
+
+//
+// Strip search index of indexed words in $post_ids
+//
+function strip_search_index($post_ids)
+{
+ global $db_type, $db;
+
+ switch ($db_type)
+ {
+ case 'mysql':
+ case 'mysqli':
+ case 'mysql_innodb':
+ case 'mysqli_innodb':
+ {
+ $result = $db->query('SELECT word_id FROM '.$db->prefix.'search_matches WHERE post_id IN('.$post_ids.') GROUP BY word_id') or error('Unable to fetch search index word match', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ $word_ids = '';
+ while ($row = $db->fetch_row($result))
+ $word_ids .= ($word_ids != '') ? ','.$row[0] : $row[0];
+
+ $result = $db->query('SELECT word_id FROM '.$db->prefix.'search_matches WHERE word_id IN('.$word_ids.') GROUP BY word_id HAVING COUNT(word_id)=1') or error('Unable to fetch search index word match', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ $word_ids = '';
+ while ($row = $db->fetch_row($result))
+ $word_ids .= ($word_ids != '') ? ','.$row[0] : $row[0];
+
+ $db->query('DELETE FROM '.$db->prefix.'search_words WHERE id IN('.$word_ids.')') or error('Unable to delete search index word', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ break;
+ }
+
+ default:
+ $db->query('DELETE FROM '.$db->prefix.'search_words WHERE id IN(SELECT word_id FROM '.$db->prefix.'search_matches WHERE word_id IN(SELECT word_id FROM '.$db->prefix.'search_matches WHERE post_id IN('.$post_ids.') GROUP BY word_id) GROUP BY word_id HAVING COUNT(word_id)=1)') or error('Unable to delete from search index', __FILE__, __LINE__, $db->error());
+ break;
+ }
+
+ $db->query('DELETE FROM '.$db->prefix.'search_matches WHERE post_id IN('.$post_ids.')') or error('Unable to delete search index word match', __FILE__, __LINE__, $db->error());
+}
diff --git a/include/srand.php b/include/srand.php
new file mode 100644
index 0000000..cb7985f
--- /dev/null
+++ b/include/srand.php
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * Author:
+ * George Argyros <argyros.george@gmail.com>
+ *
+ * Copyright (c) 2012, George Argyros
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GEORGE ARGYROS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
+ * The function is providing, at least at the systems tested :),
+ * $len bytes of entropy under any PHP installation or operating system.
+ * The execution time should be at most 10-20 ms in any system.
+ */
+function secure_random_bytes($len = 10)
+{
+
+ /*
+ * Our primary choice for a cryptographic strong randomness function is
+ * openssl_random_pseudo_bytes.
+ */
+ $SSLstr = '4'; // http://xkcd.com/221/
+ if (function_exists('openssl_random_pseudo_bytes') &&
+ (substr(PHP_VERSION, 0, 3) == '5.4' && version_compare(PHP_VERSION, '5.4.44') >= 0) ||
+ (substr(PHP_VERSION, 0, 3) == '5.5' && version_compare(PHP_VERSION, '5.5.28') >= 0) ||
+ (version_compare(PHP_VERSION, '5.6.12') >= 0))
+ {
+ $SSLstr = openssl_random_pseudo_bytes($len, $strong);
+ if ($strong) {
+ return $SSLstr;
+ }
+ }
+
+ /*
+ * If mcrypt extension is available then we use it to gather entropy from
+ * the operating system's PRNG. This is better than reading /dev/urandom
+ * directly since it avoids reading larger blocks of data than needed.
+ * Older versions of mcrypt_create_iv may be broken or take too much time
+ * to finish so we only use this function with PHP 5.3.7 and above.
+ * @see https://bugs.php.net/bug.php?id=55169
+ */
+ if (function_exists('mcrypt_create_iv') &&
+ (version_compare(PHP_VERSION, '5.3.7') >= 0 ||
+ substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $str = mcrypt_create_iv($len, MCRYPT_DEV_URANDOM);
+ if ($str !== false) {
+ return $str;
+ }
+ }
+
+
+ /*
+ * No build-in crypto randomness function found. We collect any entropy
+ * available in the PHP core PRNGs along with some filesystem info and memory
+ * stats. To make this data cryptographically strong we add data either from
+ * /dev/urandom or if its unavailable, we gather entropy by measuring the
+ * time needed to compute a number of SHA-1 hashes.
+ */
+ $str = '';
+ $bits_per_round = 2; // bits of entropy collected in each clock drift round
+ $msec_per_round = 400; // expected running time of each round in microseconds
+ $hash_len = 20; // SHA-1 Hash length
+ $total = $len; // total bytes of entropy to collect
+
+ $handle = @fopen('/dev/urandom', 'rb');
+ if ($handle && function_exists('stream_set_read_buffer')) {
+ @stream_set_read_buffer($handle, 0);
+ }
+
+ do
+ {
+ $bytes = ($total > $hash_len)? $hash_len : $total;
+ $total -= $bytes;
+
+ //collect any entropy available from the PHP system and filesystem
+ $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
+ $entropy .= implode('', @fstat(@fopen( __FILE__, 'r')));
+ $entropy .= memory_get_usage() . getmypid();
+ $entropy .= serialize($_ENV) . serialize($_SERVER);
+ if (function_exists('posix_times')) {
+ $entropy .= serialize(posix_times());
+ }
+ if (function_exists('zend_thread_id')) {
+ $entropy .= zend_thread_id();
+ }
+ if ($handle) {
+ $entropy .= @fread($handle, $bytes);
+ } else {
+ // Measure the time that the operations will take on average
+ for ($i = 0; $i < 3; $i++)
+ {
+ $c1 = get_microtime();
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < 50; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = get_microtime();
+ $entropy .= $c1 . $c2;
+ }
+
+ // Based on the above measurement determine the total rounds
+ // in order to bound the total running time.
+ $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000));
+
+ // Take the additional measurements. On average we can expect
+ // at least $bits_per_round bits of entropy from each measurement.
+ $iter = $bytes * (int) (ceil(8 / $bits_per_round));
+ for ($i = 0; $i < $iter; $i++)
+ {
+ $c1 = get_microtime();
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < $rounds; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = get_microtime();
+ $entropy .= $c1 . $c2;
+ }
+
+ }
+ // We assume sha1 is a deterministic extractor for the $entropy variable.
+ $str .= sha1($entropy, true);
+ } while ($len > strlen($str));
+
+ if ($handle) {
+ @fclose($handle);
+ }
+ return substr($str, 0, $len);
+}
diff --git a/include/template/admin.tpl b/include/template/admin.tpl
new file mode 100644
index 0000000..b87e0af
--- /dev/null
+++ b/include/template/admin.tpl
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punadmin" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <div id="brdtitle" class="inbox">
+ <pun_title>
+ <pun_desc>
+ </div>
+ <pun_navlinks>
+ <pun_status>
+ </div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/help.tpl b/include/template/help.tpl
new file mode 100644
index 0000000..6d923bf
--- /dev/null
+++ b/include/template/help.tpl
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punhelp" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/index.html b/include/template/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/template/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/template/main.tpl b/include/template/main.tpl
new file mode 100644
index 0000000..733a063
--- /dev/null
+++ b/include/template/main.tpl
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="pun<pun_page>" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <div id="brdtitle" class="inbox">
+ <pun_title>
+ <pun_desc>
+ </div>
+ <pun_navlinks>
+ <pun_status>
+ </div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/maintenance.tpl b/include/template/maintenance.tpl
new file mode 100644
index 0000000..fe55db4
--- /dev/null
+++ b/include/template/maintenance.tpl
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punmaint" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_maint_main>
+</div>
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/redirect.tpl b/include/template/redirect.tpl
new file mode 100644
index 0000000..ce5fadd
--- /dev/null
+++ b/include/template/redirect.tpl
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punredirect" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_redir_main>
+</div>
+
+<pun_footer>
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/include/user/index.html b/include/user/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/user/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/index.html b/include/utf8/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/mbstring/core.php b/include/utf8/mbstring/core.php
new file mode 100644
index 0000000..bea1c32
--- /dev/null
+++ b/include/utf8/mbstring/core.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+* @version $Id: core.php,v 1.5 2006/02/28 22:12:25 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+// Define UTF8_CORE as required
+if (!defined('UTF8_CORE'))
+ define('UTF8_CORE', true);
+
+/**
+* Wrapper round mb_strlen
+* Assumes you have mb_internal_encoding to UTF-8 already
+* Note: this function does not count bad bytes in the string - these
+* are simply ignored
+* @param string UTF-8 string
+* @return int number of UTF-8 characters in string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strlen($str)
+{
+ return mb_strlen($str);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strpos
+* Find position of first occurrence of a string
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer offset in characters (from left)
+* @return mixed integer position or FALSE on failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strpos($str, $search, $offset = false)
+{
+ // Strip unvalid characters
+ $str = utf8_bad_strip($str);
+
+ if ($offset === false)
+ return mb_strpos($str, $search);
+ else
+ return mb_strpos($str, $search, $offset);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strrpos
+* Find position of last occurrence of a char in a string
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer (optional) offset (from left)
+* @return mixed integer position or FALSE on failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strrpos($str, $search, $offset = false)
+{
+ // Strip unvalid characters
+ $str = utf8_bad_strip($str);
+
+ if (!$offset)
+ {
+ // Emulate behaviour of strrpos rather than raising warning
+ if (empty($str))
+ return false;
+
+ return mb_strrpos($str, $search);
+ }
+ else
+ {
+ if (!is_int($offset))
+ {
+ trigger_error('utf8_strrpos expects parameter 3 to be long', E_USER_WARNING);
+ return false;
+ }
+
+ $str = mb_substr($str, $offset);
+
+ if (($pos = mb_strrpos($str, $search)) !== false)
+ return $pos + $offset;
+
+ return false;
+ }
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_substr
+* Return part of a string given character offset (and optionally length)
+* @param string
+* @param integer number of UTF-8 characters offset (from left)
+* @param integer (optional) length in UTF-8 characters from offset
+* @return mixed string or FALSE if failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_substr($str, $offset, $length = false)
+{
+ if ($length === false)
+ return mb_substr($str, $offset);
+ else
+ return mb_substr($str, $offset, $length);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strtolower
+* Make a string lowercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtolower($str)
+{
+ return mb_strtolower($str);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strtoupper
+* Make a string uppercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtoupper($str)
+{
+ return mb_strtoupper($str);
+}
diff --git a/include/utf8/mbstring/index.html b/include/utf8/mbstring/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/mbstring/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/native/core.php b/include/utf8/native/core.php
new file mode 100644
index 0000000..58636f5
--- /dev/null
+++ b/include/utf8/native/core.php
@@ -0,0 +1,422 @@
+<?php
+
+/**
+* @version $Id: core.php,v 1.9 2007/08/12 01:11:33 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+// Define UTF8_CORE as required
+if (!defined('UTF8_CORE'))
+ define('UTF8_CORE', true);
+
+/**
+* Unicode aware replacement for strlen(). Returns the number
+* of characters in the string (not the number of bytes), replacing
+* multibyte characters with a single byte equivalent
+* utf8_decode() converts characters that are not in ISO-8859-1
+* to '?', which, for the purpose of counting, is alright - It's
+* much faster than iconv_strlen
+* Note: this function does not count bad UTF-8 bytes in the string
+* - these are simply ignored
+* @author <chernyshevsky at hotmail dot com>
+* @link http://www.php.net/manual/en/function.strlen.php
+* @link http://www.php.net/manual/en/function.utf8-decode.php
+* @param string UTF-8 string
+* @return int number of UTF-8 characters in string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strlen($str)
+{
+ return strlen(utf8_decode($str));
+}
+
+/**
+* UTF-8 aware alternative to strpos
+* Find position of first occurrence of a string
+* Note: This will get alot slower if offset is used
+* Note: requires utf8_strlen amd utf8_substr to be loaded
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer offset in characters (from left)
+* @return mixed integer position or FALSE on failure
+* @see http://www.php.net/strpos
+* @see utf8_strlen
+* @see utf8_substr
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strpos($str, $needle, $offset = false)
+{
+ if ($offset === false)
+ {
+ $ar = explode($needle, $str, 2);
+
+ if (count($ar) > 1)
+ return utf8_strlen($ar[0]);
+
+ return false;
+ }
+ else
+ {
+ if (!is_int($offset))
+ {
+ trigger_error('utf8_strpos: Offset must be an integer', E_USER_ERROR);
+ return false;
+ }
+
+ $str = utf8_substr($str, $offset);
+
+ if (($pos = utf8_strpos($str, $needle)) !== false)
+ return $pos + $offset;
+
+ return false;
+ }
+}
+
+/**
+* UTF-8 aware alternative to strrpos
+* Find position of last occurrence of a char in a string
+* Note: This will get alot slower if offset is used
+* Note: requires utf8_substr and utf8_strlen to be loaded
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer (optional) offset (from left)
+* @return mixed integer position or FALSE on failure
+* @see http://www.php.net/strrpos
+* @see utf8_substr
+* @see utf8_strlen
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strrpos($str, $needle, $offset = false)
+{
+ if ($offset === false)
+ {
+ $ar = explode($needle, $str);
+
+ if (count($ar) > 1)
+ {
+ // Pop off the end of the string where the last match was made
+ array_pop($ar);
+ $str = join($needle, $ar);
+
+ return utf8_strlen($str);
+ }
+
+ return false;
+ }
+ else
+ {
+ if (!is_int($offset))
+ {
+ trigger_error('utf8_strrpos expects parameter 3 to be long', E_USER_WARNING);
+ return false;
+ }
+
+ $str = utf8_substr($str, $offset);
+
+ if (($pos = utf8_strrpos($str, $needle)) !== false)
+ return $pos + $offset;
+
+ return false;
+ }
+}
+
+/**
+* UTF-8 aware alternative to substr
+* Return part of a string given character offset (and optionally length)
+*
+* Note arguments: comparied to substr - if offset or length are
+* not integers, this version will not complain but rather massages them
+* into an integer.
+*
+* Note on returned values: substr documentation states false can be
+* returned in some cases (e.g. offset > string length)
+* mb_substr never returns false, it will return an empty string instead.
+* This adopts the mb_substr approach
+*
+* Note on implementation: PCRE only supports repetitions of less than
+* 65536, in order to accept up to MAXINT values for offset and length,
+* we'll repeat a group of 65535 characters when needed.
+*
+* Note on implementation: calculating the number of characters in the
+* string is a relatively expensive operation, so we only carry it out when
+* necessary. It isn't necessary for +ve offsets and no specified length
+*
+* @author Chris Smith<chris@jalakai.co.uk>
+* @param string
+* @param integer number of UTF-8 characters offset (from left)
+* @param integer (optional) length in UTF-8 characters from offset
+* @return mixed string or FALSE if failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_substr($str, $offset, $length = false)
+{
+ // Generates E_NOTICE for PHP4 objects, but not PHP5 objects
+ $str = (string) $str;
+ $offset = (int) $offset;
+
+ if ($length)
+ $length = (int) $length;
+
+ // Handle trivial cases
+ if ($length === 0)
+ return '';
+ if ($offset < 0 && $length < 0 && $length < $offset)
+ return '';
+
+ // Normalise negative offsets (we could use a tail
+ // anchored pattern, but they are horribly slow!)
+ if ($offset < 0)
+ {
+ // See notes
+ $strlen = utf8_strlen($str);
+ $offset = $strlen + $offset;
+
+ if ($offset < 0)
+ $offset = 0;
+ }
+
+ $Op = '';
+ $Lp = '';
+
+ // Establish a pattern for offset, a
+ // non-captured group equal in length to offset
+ if ($offset > 0)
+ {
+ $Ox = (int) ($offset / 65535);
+ $Oy = $offset % 65535;
+
+ if ($Ox)
+ $Op = '(?:.{65535}){'.$Ox.'}';
+
+ $Op = '^(?:'.$Op.'.{'.$Oy.'})';
+ }
+ else
+ $Op = '^';
+
+
+ // Establish a pattern for length
+ if (!$length)
+ {
+ // The rest of the string
+ $Lp = '(.*)$';
+ }
+ else
+ {
+ // See notes
+ if (!isset($strlen))
+ $strlen = strlen(utf8_decode($str));
+
+ // Another trivial case
+ if ($offset > $strlen)
+ return '';
+
+ if ($length > 0)
+ {
+ // Reduce any length that would go passed the end of the string
+ $length = min($strlen-$offset, $length);
+
+ $Lx = (int)( $length / 65535 );
+ $Ly = $length % 65535;
+
+ // Negative length requires a captured group of length characters
+ if ($Lx) $Lp = '(?:.{65535}){'.$Lx.'}';
+ $Lp = '('.$Lp.'.{'.$Ly.'})';
+ }
+ else if ($length < 0)
+ {
+
+ if ($length < ($offset - $strlen))
+ return '';
+
+ $Lx = (int)((-$length)/65535);
+ $Ly = (-$length)%65535;
+
+ // Negative length requires ... capture everything except a group of
+ // -length characters anchored at the tail-end of the string
+ if ($Lx)
+ $Lp = '(?:.{65535}){'.$Lx.'}';
+
+ $Lp = '(.*)(?:'.$Lp.'.{'.$Ly.'})$';
+ }
+ }
+
+ if (!preg_match('#'.$Op.$Lp.'#us', $str, $match))
+ return '';
+
+ return $match[1];
+}
+
+/**
+* UTF-8 aware alternative to strtolower
+* Make a string lowercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* Note: requires utf8_to_unicode and utf8_from_unicode
+* @author Andreas Gohr <andi@splitbrain.org>
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @see http://www.php.net/strtolower
+* @see utf8_to_unicode
+* @see utf8_from_unicode
+* @see http://www.unicode.org/reports/tr21/tr21-5.html
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtolower($string)
+{
+ static $UTF8_UPPER_TO_LOWER = false;
+
+ if (!$UTF8_UPPER_TO_LOWER)
+ {
+ $UTF8_UPPER_TO_LOWER = array(
+ 0x0041=>0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062,
+ 0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101,
+ 0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3,
+ 0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C,
+ 0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F,
+ 0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F,
+ 0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3,
+ 0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B,
+ 0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9,
+ 0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D,
+ 0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4,
+ 0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165,
+ 0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157,
+ 0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119,
+ 0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129,
+ 0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448,
+ 0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075,
+ 0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A,
+ 0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC,
+ 0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0,
+ 0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D,
+ 0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0,
+ 0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5,
+ 0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA,
+ 0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065,
+ 0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F,
+ 0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068,
+ 0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6,
+ 0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457,
+ 0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5,
+ 0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6,
+ 0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071,
+ 0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458,
+ 0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE,
+ 0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127,
+ 0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C,
+ 0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F,
+ 0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB,
+ 0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441,
+ 0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B,
+ 0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103,
+ 0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9,
+ 0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123);
+ }
+
+ $uni = utf8_to_unicode($string);
+
+ if (!$uni)
+ return false;
+
+ $cnt = count($uni);
+
+ for ($i=0; $i < $cnt; $i++)
+ if (isset($UTF8_UPPER_TO_LOWER[$uni[$i]]))
+ $uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]];
+
+ return utf8_from_unicode($uni);
+}
+
+/**
+* UTF-8 aware alternative to strtoupper
+* Make a string uppercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* Note: requires utf8_to_unicode and utf8_from_unicode
+* @author Andreas Gohr <andi@splitbrain.org>
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @see http://www.php.net/strtoupper
+* @see utf8_to_unicode
+* @see utf8_from_unicode
+* @see http://www.unicode.org/reports/tr21/tr21-5.html
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtoupper($string)
+{
+ static $UTF8_LOWER_TO_UPPER = false;
+
+ if (!$UTF8_LOWER_TO_UPPER)
+ {
+ $UTF8_LOWER_TO_UPPER = array(
+ 0x0061=>0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042,
+ 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100,
+ 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393,
+ 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C,
+ 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F,
+ 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E,
+ 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3,
+ 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A,
+ 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9,
+ 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C,
+ 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4,
+ 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164,
+ 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156,
+ 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118,
+ 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128,
+ 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428,
+ 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055,
+ 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A,
+ 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC,
+ 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0,
+ 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D,
+ 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0,
+ 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5,
+ 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA,
+ 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045,
+ 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F,
+ 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048,
+ 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6,
+ 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407,
+ 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395,
+ 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396,
+ 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051,
+ 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408,
+ 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F,
+ 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126,
+ 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C,
+ 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E,
+ 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB,
+ 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421,
+ 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A,
+ 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102,
+ 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9,
+ 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122);
+ }
+
+ $uni = utf8_to_unicode($string);
+
+ if (!$uni)
+ return false;
+
+ $cnt = count($uni);
+
+ for ($i=0; $i < $cnt; $i++)
+ if(isset($UTF8_LOWER_TO_UPPER[$uni[$i]]))
+ $uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]];
+
+ return utf8_from_unicode($uni);
+}
diff --git a/include/utf8/native/index.html b/include/utf8/native/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/native/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/ord.php b/include/utf8/ord.php
new file mode 100644
index 0000000..a333f96
--- /dev/null
+++ b/include/utf8/ord.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+* @version $Id: ord.php,v 1.4 2006/09/11 15:22:54 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to ord
+* Returns the unicode ordinal for a character
+* @param string UTF-8 encoded character
+* @return int unicode ordinal for the character
+* @see http://www.php.net/ord
+* @see http://www.php.net/manual/en/function.ord.php#46267
+*/
+function utf8_ord($chr)
+{
+ $ord0 = ord($chr);
+
+ if ($ord0 >= 0 && $ord0 <= 127)
+ return $ord0;
+
+ if (!isset($chr{1}))
+ {
+ trigger_error('Short sequence - at least 2 bytes expected, only 1 seen');
+ return false;
+ }
+
+ $ord1 = ord($chr{1});
+ if ($ord0 >= 192 && $ord0 <= 223)
+ return ($ord0 - 192) * 64 + ($ord1 - 128);
+
+ if (!isset($chr{2}))
+ {
+ trigger_error('Short sequence - at least 3 bytes expected, only 2 seen');
+ return false;
+ }
+
+ $ord2 = ord($chr{2});
+ if ($ord0 >= 224 && $ord0 <= 239)
+ return ($ord0-224)*4096 + ($ord1-128)*64 + ($ord2-128);
+
+ if (!isset($chr{3}))
+ {
+ trigger_error('Short sequence - at least 4 bytes expected, only 3 seen');
+ return false;
+ }
+
+ $ord3 = ord($chr{3});
+ if ($ord0>=240 && $ord0<=247)
+ return ($ord0-240)*262144 + ($ord1-128)*4096 + ($ord2-128)*64 + ($ord3-128);
+
+ if (!isset($chr{4}))
+ {
+ trigger_error('Short sequence - at least 5 bytes expected, only 4 seen');
+ return false;
+ }
+
+ $ord4 = ord($chr{4});
+ if ($ord0>=248 && $ord0<=251)
+ return ($ord0-248)*16777216 + ($ord1-128)*262144 + ($ord2-128)*4096 + ($ord3-128)*64 + ($ord4-128);
+
+ if (!isset($chr{5}))
+ {
+ trigger_error('Short sequence - at least 6 bytes expected, only 5 seen');
+ return false;
+ }
+
+ if ($ord0>=252 && $ord0<=253)
+ return ($ord0-252) * 1073741824 + ($ord1-128)*16777216 + ($ord2-128)*262144 + ($ord3-128)*4096 + ($ord4-128)*64 + (ord($c{5})-128);
+
+ if ($ord0 >= 254 && $ord0 <= 255)
+ {
+ trigger_error('Invalid UTF-8 with surrogate ordinal '.$ord0);
+ return false;
+ }
+}
diff --git a/include/utf8/str_ireplace.php b/include/utf8/str_ireplace.php
new file mode 100644
index 0000000..7257b0a
--- /dev/null
+++ b/include/utf8/str_ireplace.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+* @version $Id: str_ireplace.php,v 1.2 2007/08/12 01:20:46 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to str_ireplace
+* Case-insensitive version of str_replace
+* Note: requires utf8_strtolower
+* Note: it's not fast and gets slower if $search / $replace is array
+* Notes: it's based on the assumption that the lower and uppercase
+* versions of a UTF-8 character will have the same length in bytes
+* which is currently true given the hash table to strtolower
+* @param string
+* @return string
+* @see http://www.php.net/str_ireplace
+* @see utf8_strtolower
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ireplace($search, $replace, $str, $count=null)
+{
+ if (!is_array($search))
+ {
+ $slen = strlen($search);
+
+ if ($slen == 0)
+ return $str;
+
+ $lendif = strlen($replace) - strlen($search);
+ $search = utf8_strtolower($search);
+
+ $search = preg_quote($search);
+ $lstr = utf8_strtolower($str);
+ $i = 0;
+ $matched = 0;
+
+ while (preg_match('/(.*)'.$search.'/Us', $lstr, $matches))
+ {
+ if ($i === $count)
+ break;
+
+ $mlen = strlen($matches[0]);
+ $lstr = substr($lstr, $mlen);
+ $str = substr_replace($str, $replace, $matched+strlen($matches[1]), $slen);
+ $matched += $mlen + $lendif;
+ $i++;
+ }
+
+ return $str;
+ }
+ else
+ {
+ foreach (array_keys($search) as $k)
+ {
+ if (is_array($replace))
+ {
+ if (array_key_exists($k, $replace))
+ $str = utf8_ireplace($search[$k], $replace[$k], $str, $count);
+ else
+ $str = utf8_ireplace($search[$k], '', $str, $count);
+ }
+ else
+ $str = utf8_ireplace($search[$k], $replace, $str, $count);
+ }
+
+ return $str;
+ }
+}
diff --git a/include/utf8/str_pad.php b/include/utf8/str_pad.php
new file mode 100644
index 0000000..93a559a
--- /dev/null
+++ b/include/utf8/str_pad.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+* @version $Id: str_pad.php,v 1.1 2006/09/03 09:25:13 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* Replacement for str_pad. $padStr may contain multi-byte characters.
+*
+* @author Oliver Saunders <oliver (a) osinternetservices.com>
+* @param string $input
+* @param int $length
+* @param string $padStr
+* @param int $type ( same constants as str_pad )
+* @return string
+* @see http://www.php.net/str_pad
+* @see utf8_substr
+* @package utf8
+* @subpackage strings
+*/
+function utf8_str_pad($input, $length, $padStr=' ', $type=STR_PAD_RIGHT)
+{
+ $inputLen = utf8_strlen($input);
+ if ($length <= $inputLen)
+ return $input;
+
+ $padStrLen = utf8_strlen($padStr);
+ $padLen = $length - $inputLen;
+
+ if ($type == STR_PAD_RIGHT)
+ {
+ $repeatTimes = ceil($padLen / $padStrLen);
+ return utf8_substr($input.str_repeat($padStr, $repeatTimes), 0, $length);
+ }
+
+ if ($type == STR_PAD_LEFT)
+ {
+ $repeatTimes = ceil($padLen / $padStrLen);
+ return utf8_substr(str_repeat($padStr, $repeatTimes), 0, floor($padLen)).$input;
+ }
+
+ if ($type == STR_PAD_BOTH)
+ {
+ $padLen /= 2;
+ $padAmountLeft = floor($padLen);
+ $padAmountRight = ceil($padLen);
+ $repeatTimesLeft = ceil($padAmountLeft / $padStrLen);
+ $repeatTimesRight = ceil($padAmountRight / $padStrLen);
+
+ $paddingLeft = utf8_substr(str_repeat($padStr, $repeatTimesLeft), 0, $padAmountLeft);
+ $paddingRight = utf8_substr(str_repeat($padStr, $repeatTimesRight), 0, $padAmountLeft);
+
+ return $paddingLeft.$input.$paddingRight;
+ }
+
+ trigger_error('utf8_str_pad: Unknown padding type ('.$type.')', E_USER_ERROR);
+}
diff --git a/include/utf8/str_split.php b/include/utf8/str_split.php
new file mode 100644
index 0000000..15bc215
--- /dev/null
+++ b/include/utf8/str_split.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+* @version $Id: str_split.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to str_split
+* Convert a string to an array
+* Note: requires utf8_strlen to be loaded
+* @param string UTF-8 encoded
+* @param int number to characters to split string by
+* @return string characters in string reverses
+* @see http://www.php.net/str_split
+* @see utf8_strlen
+* @package utf8
+* @subpackage strings
+*/
+function utf8_str_split($str, $split_len=1)
+{
+ if (!preg_match('/^[0-9]+$/',$split_len) || $split_len < 1)
+ return false;
+
+ $len = utf8_strlen($str);
+ if ($len <= $split_len)
+ return array($str);
+
+ preg_match_all('/.{'.$split_len.'}|[^\x00]{1,'.$split_len.'}$/us', $str, $ar);
+
+ return $ar[0];
+}
diff --git a/include/utf8/strcasecmp.php b/include/utf8/strcasecmp.php
new file mode 100644
index 0000000..423f443
--- /dev/null
+++ b/include/utf8/strcasecmp.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+* @version $Id: strcasecmp.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strcasecmp
+* A case insensivite string comparison
+* Note: requires utf8_strtolower
+* @param string
+* @param string
+* @return int
+* @see http://www.php.net/strcasecmp
+* @see utf8_strtolower
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strcasecmp($strX, $strY)
+{
+ $strX = utf8_strtolower($strX);
+ $strY = utf8_strtolower($strY);
+
+ return strcmp($strX, $strY);
+}
diff --git a/include/utf8/strcspn.php b/include/utf8/strcspn.php
new file mode 100644
index 0000000..b05e327
--- /dev/null
+++ b/include/utf8/strcspn.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+* @version $Id: strcspn.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strcspn
+* Find length of initial segment not matching mask
+* Note: requires utf8_strlen and utf8_substr (if start, length are used)
+* @param string
+* @return int
+* @see http://www.php.net/strcspn
+* @see utf8_strlen
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strcspn($str, $mask, $start=null, $length=null)
+{
+ if (empty($mask) || strlen($mask) == 0)
+ return null;
+
+ $mask = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}', $mask);
+
+ if (!is_null($start) || !is_null($length))
+ $str = utf8_substr($str, $start, $length);
+
+ preg_match('/^[^'.$mask.']+/u', $str, $matches);
+
+ if (isset($matches[0]))
+ return utf8_strlen($matches[0]);
+
+ return 0;
+}
diff --git a/include/utf8/stristr.php b/include/utf8/stristr.php
new file mode 100644
index 0000000..fb9e6a5
--- /dev/null
+++ b/include/utf8/stristr.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+* @version $Id: stristr.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to stristr
+* Find first occurrence of a string using case insensitive comparison
+* Note: requires utf8_strtolower
+* @param string
+* @param string
+* @return int
+* @see http://www.php.net/strcasecmp
+* @see utf8_strtolower
+* @package utf8
+* @subpackage strings
+*/
+function utf8_stristr($str, $search)
+{
+ if (strlen($search) == 0)
+ return $str;
+
+ $lstr = utf8_strtolower($str);
+ $lsearch = utf8_strtolower($search);
+ preg_match('/^(.*)'.preg_quote($lsearch).'/Us', $lstr, $matches);
+
+ if (count($matches) == 2)
+ return substr($str, strlen($matches[1]));
+
+ return false;
+}
diff --git a/include/utf8/strrev.php b/include/utf8/strrev.php
new file mode 100644
index 0000000..ae9c32b
--- /dev/null
+++ b/include/utf8/strrev.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+* @version $Id: strrev.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strrev
+* Reverse a string
+* @param string UTF-8 encoded
+* @return string characters in string reverses
+* @see http://www.php.net/strrev
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strrev($str)
+{
+ preg_match_all('/./us', $str, $ar);
+ return implode(array_reverse($ar[0]));
+}
diff --git a/include/utf8/strspn.php b/include/utf8/strspn.php
new file mode 100644
index 0000000..49d300a
--- /dev/null
+++ b/include/utf8/strspn.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+* @version $Id: strspn.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strspn
+* Find length of initial segment matching mask
+* Note: requires utf8_strlen and utf8_substr (if start, length are used)
+* @param string
+* @return int
+* @see http://www.php.net/strspn
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strspn($str, $mask, $start=null, $length=null)
+{
+ $mask = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $mask);
+
+ if (!is_null($start)|| !is_null($length))
+ $str = utf8_substr($str, $start, $length);
+
+ preg_match('/^['.$mask.']+/u', $str, $matches);
+
+ if (isset($matches[0]))
+ return utf8_strlen($matches[0]);
+
+ return 0;
+}
diff --git a/include/utf8/substr_replace.php b/include/utf8/substr_replace.php
new file mode 100644
index 0000000..20a43b5
--- /dev/null
+++ b/include/utf8/substr_replace.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+* @version $Id: substr_replace.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware substr_replace.
+* Note: requires utf8_substr to be loaded
+* @see http://www.php.net/substr_replace
+* @see utf8_strlen
+* @see utf8_substr
+*/
+function utf8_substr_replace($str, $repl, $start , $length=null)
+{
+ preg_match_all('/./us', $str, $ar);
+ preg_match_all('/./us', $repl, $rar);
+
+ if(is_null($length))
+ $length = utf8_strlen($str);
+
+ array_splice($ar[0], $start, $length, $rar[0]);
+
+ return implode($ar[0]);
+}
diff --git a/include/utf8/trim.php b/include/utf8/trim.php
new file mode 100644
index 0000000..3d22840
--- /dev/null
+++ b/include/utf8/trim.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+* @version $Id: trim.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware replacement for ltrim()
+* Note: you only need to use this if you are supplying the charlist
+* optional arg and it contains UTF-8 characters. Otherwise ltrim will
+* work normally on a UTF-8 string
+* @author Andreas Gohr <andi@splitbrain.org>
+* @see http://www.php.net/ltrim
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @return string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ltrim( $str, $charlist=false)
+{
+ if($charlist === false)
+ return ltrim($str);
+
+ // Quote charlist for use in a characterclass
+ $charlist = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $charlist);
+
+ return preg_replace('/^['.$charlist.']+/u', '', $str);
+}
+
+/**
+* UTF-8 aware replacement for rtrim()
+* Note: you only need to use this if you are supplying the charlist
+* optional arg and it contains UTF-8 characters. Otherwise rtrim will
+* work normally on a UTF-8 string
+* @author Andreas Gohr <andi@splitbrain.org>
+* @see http://www.php.net/rtrim
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @return string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_rtrim($str, $charlist=false)
+{
+ if($charlist === false)
+ return rtrim($str);
+
+ // Quote charlist for use in a characterclass
+ $charlist = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $charlist);
+
+ return preg_replace('/['.$charlist.']+$/u', '', $str);
+}
+
+//---------------------------------------------------------------
+/**
+* UTF-8 aware replacement for trim()
+* Note: you only need to use this if you are supplying the charlist
+* optional arg and it contains UTF-8 characters. Otherwise trim will
+* work normally on a UTF-8 string
+* @author Andreas Gohr <andi@splitbrain.org>
+* @see http://www.php.net/trim
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @return string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_trim( $str, $charlist=false)
+{
+ if($charlist === false)
+ return trim($str);
+
+ return utf8_ltrim(utf8_rtrim($str, $charlist), $charlist);
+}
diff --git a/include/utf8/ucfirst.php b/include/utf8/ucfirst.php
new file mode 100644
index 0000000..efee55d
--- /dev/null
+++ b/include/utf8/ucfirst.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+* @version $Id: ucfirst.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to ucfirst
+* Make a string's first character uppercase
+* Note: requires utf8_strtoupper
+* @param string
+* @return string with first character as upper case (if applicable)
+* @see http://www.php.net/ucfirst
+* @see utf8_strtoupper
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ucfirst($str)
+{
+ switch (utf8_strlen($str))
+ {
+ case 0:
+ return '';
+ break;
+ case 1:
+ return utf8_strtoupper($str);
+ break;
+ default:
+ preg_match('/^(.{1})(.*)$/us', $str, $matches);
+ return utf8_strtoupper($matches[1]).$matches[2];
+ break;
+ }
+}
diff --git a/include/utf8/ucwords.php b/include/utf8/ucwords.php
new file mode 100644
index 0000000..e985cee
--- /dev/null
+++ b/include/utf8/ucwords.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+* @version $Id: ucwords.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to ucwords
+* Uppercase the first character of each word in a string
+* Note: requires utf8_substr_replace and utf8_strtoupper
+* @param string
+* @return string with first char of each word uppercase
+* @see http://www.php.net/ucwords
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ucwords($str)
+{
+ // Note: [\x0c\x09\x0b\x0a\x0d\x20] matches;
+ // Form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns
+ // This corresponds to the definition of a "word" defined at http://www.php.net/ucwords
+ $pattern = '/(^|([\x0c\x09\x0b\x0a\x0d\x20]+))([^\x0c\x09\x0b\x0a\x0d\x20]{1})[^\x0c\x09\x0b\x0a\x0d\x20]*/u';
+
+ return preg_replace_callback($pattern, 'utf8_ucwords_callback', $str);
+}
+
+/**
+* Callback function for preg_replace_callback call in utf8_ucwords
+* You don't need to call this yourself
+* @param array of matches corresponding to a single word
+* @return string with first char of the word in uppercase
+* @see utf8_ucwords
+* @see utf8_strtoupper
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ucwords_callback($matches)
+{
+ $leadingws = $matches[2];
+ $ucfirst = utf8_strtoupper($matches[3]);
+ $ucword = utf8_substr_replace(ltrim($matches[0]), $ucfirst, 0, 1);
+
+ return $leadingws.$ucword;
+}
diff --git a/include/utf8/utf8.php b/include/utf8/utf8.php
new file mode 100644
index 0000000..661b2d7
--- /dev/null
+++ b/include/utf8/utf8.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+* This is the dynamic loader for the library. It checks whether you have
+* the mbstring extension available and includes relevant files
+* on that basis, falling back to the native (as in written in PHP) version
+* if mbstring is unavailabe.
+*
+* It's probably easiest to use this, if you don't want to understand
+* the dependencies involved, in conjunction with PHP versions etc. At
+* the same time, you might get better performance by managing loading
+* yourself. The smartest way to do this, bearing in mind performance,
+* is probably to "load on demand" - i.e. just before you use these
+* functions in your code, load the version you need.
+*
+* It makes sure the the following functions are available;
+* utf8_strlen, utf8_strpos, utf8_strrpos, utf8_substr,
+* utf8_strtolower, utf8_strtoupper
+* Other functions in the ./native directory depend on these
+* six functions being available
+* @package utf8
+*/
+
+// Check whether PCRE has been compiled with UTF-8 support
+$UTF8_ar = array();
+if (preg_match('/^.{1}$/u', "ñ", $UTF8_ar) != 1)
+ trigger_error('PCRE is not compiled with UTF-8 support', E_USER_ERROR);
+
+unset($UTF8_ar);
+
+// Put the current directory in this constant
+if (!defined('UTF8'))
+ define('UTF8', dirname(__FILE__));
+
+if (extension_loaded('mbstring') && !defined('UTF8_USE_MBSTRING') && !defined('UTF8_USE_NATIVE'))
+ define('UTF8_USE_MBSTRING', true);
+else if (!defined('UTF8_USE_NATIVE'))
+ define('UTF8_USE_NATIVE', true);
+
+// utf8_strpos() and utf8_strrpos() need utf8_bad_strip() to strip invalid
+// characters. Mbstring doesn't do this while the Native implementation does.
+require UTF8.'/utils/bad.php';
+
+if (defined('UTF8_USE_MBSTRING'))
+{
+ /**
+ * If string overloading is active, it will break many of the
+ * native implementations. mbstring.func_overload must be set
+ * to 0, 1 or 4 in php.ini (string overloading disabled).
+ * Also need to check we have the correct internal mbstring
+ * encoding
+ */
+ if (ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING)
+ trigger_error('String functions are overloaded by mbstring', E_USER_ERROR);
+
+ mb_language('uni');
+ mb_internal_encoding('UTF-8');
+
+ if (!defined('UTF8_CORE'))
+ require UTF8.'/mbstring/core.php';
+}
+elseif (defined('UTF8_USE_NATIVE'))
+{
+ if (!defined('UTF8_CORE'))
+ {
+ require UTF8.'/utils/unicode.php';
+ require UTF8.'/native/core.php';
+ }
+}
+
+// Load the native implementation of utf8_trim
+require UTF8.'/trim.php';
diff --git a/include/utf8/utils/ascii.php b/include/utf8/utils/ascii.php
new file mode 100644
index 0000000..af75b92
--- /dev/null
+++ b/include/utf8/utils/ascii.php
@@ -0,0 +1,221 @@
+<?php
+
+/**
+* Tools to help with ASCII in UTF-8
+* @version $Id: ascii.php,v 1.5 2006/10/16 20:38:12 harryf Exp $
+* @package utf8
+* @subpackage ascii
+*/
+
+/**
+* Tests whether a string contains only 7bit ASCII bytes.
+* You might use this to conditionally check whether a string
+* needs handling as UTF-8 or not, potentially offering performance
+* benefits by using the native PHP equivalent if it's just ASCII e.g.;
+*
+* <code>
+* if ( utf8_is_ascii($someString) ) {
+* // It's just ASCII - use the native PHP version
+* $someString = strtolower($someString);
+* } else {
+* $someString = utf8_strtolower($someString);
+* }
+* </code>
+*
+* @param string
+* @return boolean TRUE if it's all ASCII
+* @package utf8
+* @subpackage ascii
+* @see utf8_is_ascii_ctrl
+*/
+function utf8_is_ascii($str)
+{
+ // Search for any bytes which are outside the ASCII range...
+ return (preg_match('/(?:[^\x00-\x7F])/', $str) !== 1);
+}
+
+/**
+* Tests whether a string contains only 7bit ASCII bytes with device
+* control codes omitted. The device control codes can be found on the
+* second table here: http://www.w3schools.com/tags/ref_ascii.asp
+*
+* @param string
+* @return boolean TRUE if it's all ASCII without device control codes
+* @package utf8
+* @subpackage ascii
+* @see utf8_is_ascii
+*/
+function utf8_is_ascii_ctrl($str)
+{
+ // Search for any bytes which are outside the ASCII range, or are device control codes
+ if (strlen($str) > 0)
+ return (preg_match('/[^\x09\x0A\x0D\x20-\x7E]/', $str) !== 1);
+
+ return false;
+}
+
+/**
+* Strip out all non-7bit ASCII bytes
+* If you need to transmit a string to system which you know can only
+* support 7bit ASCII, you could use this function.
+* @param string
+* @return string with non ASCII bytes removed
+* @package utf8
+* @subpackage ascii
+* @see utf8_strip_non_ascii_ctrl
+*/
+function utf8_strip_non_ascii($str)
+{
+ ob_start();
+
+ while (preg_match('/^([\x00-\x7F]+)|([^\x00-\x7F]+)/S', $str, $matches))
+ {
+ if (!isset($matches[2]))
+ echo $matches[0];
+
+ $str = substr($str, strlen($matches[0]));
+ }
+
+ $result = ob_get_contents();
+ ob_end_clean();
+
+ return $result;
+}
+
+/**
+* Strip out device control codes in the ASCII range
+* which are not permitted in XML. Note that this leaves
+* multi-byte characters untouched - it only removes device
+* control codes
+* @see http://hsivonen.iki.fi/producing-xml/#controlchar
+* @param string
+* @return string control codes removed
+*/
+function utf8_strip_ascii_ctrl($str)
+{
+ ob_start();
+
+ while (preg_match('/^([^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)|([\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)/S', $str, $matches))
+ {
+ if (!isset($matches[2]))
+ echo $matches[0];
+
+ $str = substr($str, strlen($matches[0]));
+ }
+
+ $result = ob_get_contents();
+ ob_end_clean();
+
+ return $result;
+}
+
+/**
+* Strip out all non 7bit ASCII bytes and ASCII device control codes.
+* For a list of ASCII device control codes see the 2nd table here:
+* http://www.w3schools.com/tags/ref_ascii.asp
+*
+* @param string
+* @return boolean TRUE if it's all ASCII
+* @package utf8
+* @subpackage ascii
+*/
+function utf8_strip_non_ascii_ctrl($str)
+{
+ ob_start();
+
+ while (preg_match( '/^([\x09\x0A\x0D\x20-\x7E]+)|([^\x09\x0A\x0D\x20-\x7E]+)/S', $str, $matches))
+ {
+ if (!isset($matches[2]))
+ echo $matches[0];
+
+ $str = substr($str, strlen($matches[0]));
+ }
+
+ $result = ob_get_contents();
+ ob_end_clean();
+
+ return $result;
+}
+
+/**
+* Replace accented UTF-8 characters by unaccented ASCII-7 "equivalents".
+* The purpose of this function is to replace characters commonly found in Latin
+* alphabets with something more or less equivalent from the ASCII range. This can
+* be useful for converting a UTF-8 to something ready for a filename, for example.
+* Following the use of this function, you would probably also pass the string
+* through utf8_strip_non_ascii to clean out any other non-ASCII chars
+* Use the optional parameter to just deaccent lower ($case = -1) or upper ($case = 1)
+* letters. Default is to deaccent both cases ($case = 0)
+*
+* For a more complete implementation of transliteration, see the utf8_to_ascii package
+* available from the phputf8 project downloads:
+* http://prdownloads.sourceforge.net/phputf8
+*
+* @param string UTF-8 string
+* @param int (optional) -1 lowercase only, +1 uppercase only, 1 both cases
+* @param string UTF-8 with accented characters replaced by ASCII chars
+* @return string accented chars replaced with ascii equivalents
+* @author Andreas Gohr <andi@splitbrain.org>
+* @package utf8
+* @subpackage ascii
+*/
+function utf8_accents_to_ascii($str, $case=0)
+{
+ static $UTF8_LOWER_ACCENTS = null;
+ static $UTF8_UPPER_ACCENTS = null;
+
+ if($case <= 0)
+ {
+
+ if (is_null($UTF8_LOWER_ACCENTS))
+ {
+ $UTF8_LOWER_ACCENTS = array(
+ 'à' => 'a', 'ô' => 'o', 'ď' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'š' => 's', 'ơ' => 'o',
+ 'ß' => 'ss', 'ă' => 'a', 'ř' => 'r', 'ț' => 't', 'ň' => 'n', 'ā' => 'a', 'ķ' => 'k',
+ 'ŝ' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'ṗ' => 'p', 'ó' => 'o',
+ 'ú' => 'u', 'ě' => 'e', 'é' => 'e', 'ç' => 'c', 'ẁ' => 'w', 'ċ' => 'c', 'õ' => 'o',
+ 'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c',
+ 'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't',
+ 'ū' => 'u', 'č' => 'c', 'ö' => 'oe', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l',
+ 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z',
+ 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't',
+ 'ŗ' => 'r', 'ä' => 'ae', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'ue', 'ò' => 'o',
+ 'ē' => 'e', 'ñ' => 'n', 'ń' => 'n', 'ĥ' => 'h', 'ĝ' => 'g', 'đ' => 'd', 'ĵ' => 'j',
+ 'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o',
+ 'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g',
+ 'ṁ' => 'm', 'ō' => 'o', 'ĩ' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a',
+ 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'ĕ' => 'e',
+ );
+ }
+
+ $str = str_replace(array_keys($UTF8_LOWER_ACCENTS), array_values($UTF8_LOWER_ACCENTS), $str);
+ }
+
+ if($case >= 0)
+ {
+ if (is_null($UTF8_UPPER_ACCENTS))
+ {
+ $UTF8_UPPER_ACCENTS = array(
+ 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O',
+ 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K',
+ 'Ŝ' => 'S', 'Ỳ' => 'Y', 'Ņ' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'Ṗ' => 'P', 'Ó' => 'O',
+ 'Ú' => 'U', 'Ě' => 'E', 'É' => 'E', 'Ç' => 'C', 'Ẁ' => 'W', 'Ċ' => 'C', 'Õ' => 'O',
+ 'Ṡ' => 'S', 'Ø' => 'O', 'Ģ' => 'G', 'Ŧ' => 'T', 'Ș' => 'S', 'Ė' => 'E', 'Ĉ' => 'C',
+ 'Ś' => 'S', 'Î' => 'I', 'Ű' => 'U', 'Ć' => 'C', 'Ę' => 'E', 'Ŵ' => 'W', 'Ṫ' => 'T',
+ 'Ū' => 'U', 'Č' => 'C', 'Ö' => 'Oe', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L',
+ 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z',
+ 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T',
+ 'Ŗ' => 'R', 'Ä' => 'Ae', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'Ue', 'Ò' => 'O',
+ 'Ē' => 'E', 'Ñ' => 'N', 'Ń' => 'N', 'Ĥ' => 'H', 'Ĝ' => 'G', 'Đ' => 'D', 'Ĵ' => 'J',
+ 'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Ţ' => 'T', 'Ý' => 'Y', 'Ő' => 'O',
+ 'Â' => 'A', 'Ľ' => 'L', 'Ẅ' => 'W', 'Ż' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ġ' => 'G',
+ 'Ṁ' => 'M', 'Ō' => 'O', 'Ĩ' => 'I', 'Ù' => 'U', 'Į' => 'I', 'Ź' => 'Z', 'Á' => 'A',
+ 'Û' => 'U', 'Þ' => 'Th', 'Ð' => 'Dh', 'Æ' => 'Ae', 'Ĕ' => 'E',
+ );
+ }
+
+ $str = str_replace(array_keys($UTF8_UPPER_ACCENTS), array_values($UTF8_UPPER_ACCENTS), $str);
+ }
+
+ return $str;
+}
diff --git a/include/utf8/utils/bad.php b/include/utf8/utils/bad.php
new file mode 100644
index 0000000..2704294
--- /dev/null
+++ b/include/utf8/utils/bad.php
@@ -0,0 +1,430 @@
+<?php
+
+/**
+* @version $Id: bad.php,v 1.2 2006/02/26 13:20:44 harryf Exp $
+* Tools for locating / replacing bad bytes in UTF-8 strings
+* The Original Code is Mozilla Communicator client code.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998
+* the Initial Developer. All Rights Reserved.
+* Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi)
+* Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com)
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage bad
+* @see utf8_is_valid
+*/
+
+/**
+* Locates the first bad byte in a UTF-8 string returning it's
+* byte index in the string
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string
+* @return mixed integer byte index or FALSE if no bad found
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_find($str)
+{
+ $UTF8_BAD =
+ '([\x00-\x7F]'. # ASCII (including control chars)
+ '|[\xC2-\xDF][\x80-\xBF]'. # Non-overlong 2-byte
+ '|\xE0[\xA0-\xBF][\x80-\xBF]'. # Excluding overlongs
+ '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # Straight 3-byte
+ '|\xED[\x80-\x9F][\x80-\xBF]'. # Excluding surrogates
+ '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # Planes 1-3
+ '|[\xF1-\xF3][\x80-\xBF]{3}'. # Planes 4-15
+ '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # Plane 16
+ '|(.{1}))'; # Invalid byte
+ $pos = 0;
+ $badList = array();
+
+ while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
+ {
+ $bytes = strlen($matches[0]);
+
+ if (isset($matches[2]))
+ return $pos;
+
+ $pos += $bytes;
+ $str = substr($str,$bytes);
+ }
+
+ return false;
+}
+
+/**
+* Locates all bad bytes in a UTF-8 string and returns a list of their
+* byte index in the string
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string
+* @return mixed array of integers or FALSE if no bad found
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_findall($str)
+{
+ $UTF8_BAD =
+ '([\x00-\x7F]'. # ASCII (including control chars)
+ '|[\xC2-\xDF][\x80-\xBF]'. # Non-overlong 2-byte
+ '|\xE0[\xA0-\xBF][\x80-\xBF]'. # Excluding overlongs
+ '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # Straight 3-byte
+ '|\xED[\x80-\x9F][\x80-\xBF]'. # Excluding surrogates
+ '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # Planes 1-3
+ '|[\xF1-\xF3][\x80-\xBF]{3}'. # Planes 4-15
+ '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # Plane 16
+ '|(.{1}))'; # Invalid byte
+ $pos = 0;
+ $badList = array();
+
+ while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
+ {
+ $bytes = strlen($matches[0]);
+
+ if (isset($matches[2]))
+ $badList[] = $pos;
+
+ $pos += $bytes;
+ $str = substr($str,$bytes);
+ }
+
+ if (count($badList) > 0)
+ return $badList;
+
+ return false;
+}
+
+/**
+* Strips out any bad bytes from a UTF-8 string and returns the rest
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string
+* @return string
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_strip($original)
+{
+ return utf8_bad_replace($original, '');
+}
+
+/**
+* Replace bad bytes with an alternative character - ASCII character
+* recommended is replacement char
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string to search
+* @param string to replace bad bytes with (defaults to '?') - use ASCII
+* @return string
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_replace($original, $replace = '?') {
+ $result = '';
+
+ $strlen = strlen($original);
+ for ($i = 0; $i < $strlen;) {
+ $char = $original[$i++];
+ $byte = ord($char);
+
+ if ($byte < 0x80) $bytes = 0; // 1-bytes (00000000 - 01111111)
+ else if ($byte < 0xC0) { // 1-bytes (10000000 - 10111111)
+ $result .= $replace;
+ continue;
+ }
+ else if ($byte < 0xE0) $bytes = 1; // 2-bytes (11000000 - 11011111)
+ else if ($byte < 0xF0) $bytes = 2; // 3-bytes (11100000 - 11101111)
+ else if ($byte < 0xF8) $bytes = 3; // 4-bytes (11110000 - 11110111)
+ else if ($byte < 0xFC) $bytes = 4; // 5-bytes (11111000 - 11111011)
+ else if ($byte < 0xFE) $bytes = 5; // 6-bytes (11111100 - 11111101)
+ else { // Otherwise it's something invalid
+ $result .= $replace;
+ continue;
+ }
+
+ // Check our input actually has enough data
+ if ($i + $bytes > $strlen) {
+ $result .= $replace;
+ continue;
+ }
+
+ // If we've got this far then we have a multiple-byte character
+ for ($j = 0; $j < $bytes; $j++) {
+ $byte = $original[$i + $j];
+
+ $char .= $byte;
+ $byte = ord($byte);
+
+ // Every following byte must be 10000000 - 10111111
+ if ($byte < 0x80 || $byte > 0xBF) {
+ $result .= $replace;
+ continue 2;
+ }
+ }
+
+ $i += $bytes;
+ $result .= $char;
+ }
+
+ return $result;
+}
+
+/**
+* Return code from utf8_bad_identify() when a five octet sequence is detected.
+* Note: 5 octets sequences are valid UTF-8 but are not supported by Unicode so
+* do not represent a useful character
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_5OCTET', 1);
+
+/**
+* Return code from utf8_bad_identify() when a six octet sequence is detected.
+* Note: 6 octets sequences are valid UTF-8 but are not supported by Unicode so
+* do not represent a useful character
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_6OCTET', 2);
+
+/**
+* Return code from utf8_bad_identify().
+* Invalid octet for use as start of multi-byte UTF-8 sequence
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_SEQID', 3);
+
+/**
+* Return code from utf8_bad_identify().
+* From Unicode 3.1, non-shortest form is illegal
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_NONSHORT', 4);
+
+/**
+* Return code from utf8_bad_identify().
+* From Unicode 3.2, surrogate characters are illegal
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_SURROGATE', 5);
+
+/**
+* Return code from utf8_bad_identify().
+* Codepoints outside the Unicode range are illegal
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_UNIOUTRANGE', 6);
+
+/**
+* Return code from utf8_bad_identify().
+* Incomplete multi-octet sequence
+* Note: this is kind of a "catch-all"
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_SEQINCOMPLETE', 7);
+
+/**
+* Reports on the type of bad byte found in a UTF-8 string. Returns a
+* status code on the first bad byte found
+* @author <hsivonen@iki.fi>
+* @param string UTF-8 encoded string
+* @return mixed integer constant describing problem or FALSE if valid UTF-8
+* @see utf8_bad_explain
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_identify($str, &$i)
+{
+ $mState = 0; // Cached expected number of octets after the current octet
+ // until the beginning of the next UTF8 character sequence
+ $mUcs4 = 0; // Cached Unicode character
+ $mBytes = 1; // Cached expected number of octets in the current sequence
+
+ $len = strlen($str);
+
+ for($i=0; $i < $len; $i++)
+ {
+ $in = ord($str{$i});
+
+ if ( $mState == 0)
+ {
+ // When mState is zero we expect either a US-ASCII character or a multi-octet sequence.
+ if (0 == (0x80 & ($in)))
+ {
+ // US-ASCII, pass straight through.
+ $mBytes = 1;
+ }
+ else if (0xC0 == (0xE0 & ($in)))
+ {
+ // First octet of 2 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x1F) << 6;
+ $mState = 1;
+ $mBytes = 2;
+ }
+ else if (0xE0 == (0xF0 & ($in)))
+ {
+ // First octet of 3 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x0F) << 12;
+ $mState = 2;
+ $mBytes = 3;
+ }
+ else if (0xF0 == (0xF8 & ($in)))
+ {
+ // First octet of 4 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x07) << 18;
+ $mState = 3;
+ $mBytes = 4;
+ }
+ else if (0xF8 == (0xFC & ($in)))
+ {
+ /* First octet of 5 octet sequence.
+ *
+ * This is illegal because the encoded codepoint must be either
+ * (a) not the shortest form or
+ * (b) outside the Unicode range of 0-0x10FFFF.
+ */
+ return UTF8_BAD_5OCTET;
+ }
+ else if (0xFC == (0xFE & ($in)))
+ {
+ // First octet of 6 octet sequence, see comments for 5 octet sequence.
+ return UTF8_BAD_6OCTET;
+ }
+ else
+ {
+ // Current octet is neither in the US-ASCII range nor a legal first
+ // octet of a multi-octet sequence.
+ return UTF8_BAD_SEQID;
+ }
+ }
+ else
+ {
+ // When mState is non-zero, we expect a continuation of the multi-octet sequence
+ if (0x80 == (0xC0 & ($in)))
+ {
+ // Legal continuation.
+ $shift = ($mState - 1) * 6;
+ $tmp = $in;
+ $tmp = ($tmp & 0x0000003F) << $shift;
+ $mUcs4 |= $tmp;
+
+ /**
+ * End of the multi-octet sequence. mUcs4 now contains the final
+ * Unicode codepoint to be output
+ */
+ if (0 == --$mState)
+ {
+ // From Unicode 3.1, non-shortest form is illegal
+ if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
+ ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+ ((4 == $mBytes) && ($mUcs4 < 0x10000)) )
+ return UTF8_BAD_NONSHORT;
+ else if (($mUcs4 & 0xFFFFF800) == 0xD800) // From Unicode 3.2, surrogate characters are illegal
+ return UTF8_BAD_SURROGATE;
+ else if ($mUcs4 > 0x10FFFF) // Codepoints outside the Unicode range are illegal
+ return UTF8_BAD_UNIOUTRANGE;
+
+ // Initialize UTF8 cache
+ $mState = 0;
+ $mUcs4 = 0;
+ $mBytes = 1;
+ }
+
+ }
+ else
+ {
+ // ((0xC0 & (*in) != 0x80) && (mState != 0))
+ // Incomplete multi-octet sequence.
+ $i--;
+ return UTF8_BAD_SEQINCOMPLETE;
+ }
+ }
+ }
+
+ // Incomplete multi-octet sequence
+ if ($mState != 0)
+ {
+ $i--;
+ return UTF8_BAD_SEQINCOMPLETE;
+ }
+
+ // No bad octets found
+ $i = null;
+ return false;
+}
+
+/**
+* Takes a return code from utf8_bad_identify() are returns a message
+* (in English) explaining what the problem is.
+* @param int return code from utf8_bad_identify
+* @return mixed string message or FALSE if return code unknown
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_explain($code)
+{
+ switch ($code)
+ {
+ case UTF8_BAD_5OCTET:
+ return 'Five octet sequences are valid UTF-8 but are not supported by Unicode';
+ break;
+
+ case UTF8_BAD_6OCTET:
+ return 'Six octet sequences are valid UTF-8 but are not supported by Unicode';
+ break;
+
+ case UTF8_BAD_SEQID:
+ return 'Invalid octet for use as start of multi-byte UTF-8 sequence';
+ break;
+
+ case UTF8_BAD_NONSHORT:
+ return 'From Unicode 3.1, non-shortest form is illegal';
+ break;
+
+ case UTF8_BAD_SURROGATE:
+ return 'From Unicode 3.2, surrogate characters are illegal';
+ break;
+
+ case UTF8_BAD_UNIOUTRANGE:
+ return 'Codepoints outside the Unicode range are illegal';
+ break;
+
+ case UTF8_BAD_SEQINCOMPLETE:
+ return 'Incomplete multi-octet sequence';
+ break;
+ }
+
+ trigger_error('Unknown error code: '.$code, E_USER_WARNING);
+
+ return false;
+}
diff --git a/include/utf8/utils/index.html b/include/utf8/utils/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/utils/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/utils/patterns.php b/include/utf8/utils/patterns.php
new file mode 100644
index 0000000..5a85a4f
--- /dev/null
+++ b/include/utf8/utils/patterns.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+* PCRE Regular expressions for UTF-8. Note this file is not actually used by
+* the rest of the library but these regular expressions can be useful to have
+* available.
+* @version $Id: patterns.php,v 1.1 2006/02/25 14:20:02 harryf Exp $
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+
+/**
+* PCRE Pattern to check a UTF-8 string is valid
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+$UTF8_VALID = '^('.
+ '[\x00-\x7F]'. # ASCII (including control chars)
+ '|[\xC2-\xDF][\x80-\xBF]'. # Non-overlong 2-byte
+ '|\xE0[\xA0-\xBF][\x80-\xBF]'. # Excluding overlongs
+ '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # Straight 3-byte
+ '|\xED[\x80-\x9F][\x80-\xBF]'. # Excluding surrogates
+ '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # Planes 1-3
+ '|[\xF1-\xF3][\x80-\xBF]{3}'. # Planes 4-15
+ '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # Plane 16
+ ')*$';
+
+/**
+* PCRE Pattern to match single UTF-8 characters
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+$UTF8_MATCH =
+ '([\x00-\x7F])'. # ASCII (including control chars)
+ '|([\xC2-\xDF][\x80-\xBF])'. # Non-overlong 2-byte
+ '|(\xE0[\xA0-\xBF][\x80-\xBF])'. # Excluding overlongs
+ '|([\xE1-\xEC\xEE\xEF][\x80-\xBF]{2})'. # Straight 3-byte
+ '|(\xED[\x80-\x9F][\x80-\xBF])'. # Excluding surrogates
+ '|(\xF0[\x90-\xBF][\x80-\xBF]{2})'. # Planes 1-3
+ '|([\xF1-\xF3][\x80-\xBF]{3})'. # Planes 4-15
+ '|(\xF4[\x80-\x8F][\x80-\xBF]{2})'; # Plane 16
+
+/**
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+$UTF8_BAD =
+ '([\x00-\x7F]'. # ASCII (including control chars)
+ '|[\xC2-\xDF][\x80-\xBF]'. # Non-overlong 2-byte
+ '|\xE0[\xA0-\xBF][\x80-\xBF]'. # Excluding overlongs
+ '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # Straight 3-byte
+ '|\xED[\x80-\x9F][\x80-\xBF]'. # Excluding surrogates
+ '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # Planes 1-3
+ '|[\xF1-\xF3][\x80-\xBF]{3}'. # Planes 4-15
+ '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # Plane 16
+ '|(.{1}))'; # Invalid byte
diff --git a/include/utf8/utils/position.php b/include/utf8/utils/position.php
new file mode 100644
index 0000000..7c62d10
--- /dev/null
+++ b/include/utf8/utils/position.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+* Locate a byte index given a UTF-8 character index
+* @version $Id: position.php,v 1.1 2006/10/01 00:01:31 harryf Exp $
+* @package utf8
+* @subpackage position
+*/
+
+/**
+* Given a string and a character index in the string, in
+* terms of the UTF-8 character position, returns the byte
+* index of that character. Can be useful when you want to
+* PHP's native string functions but we warned, locating
+* the byte can be expensive
+* Takes variable number of parameters - first must be
+* the search string then 1 to n UTF-8 character positions
+* to obtain byte indexes for - it is more efficient to search
+* the string for multiple characters at once, than make
+* repeated calls to this function
+*
+* @author Chris Smith<chris@jalakai.co.uk>
+* @param string string to locate index in
+* @param int (n times)
+* @return mixed - int if only one input int, array if more
+* @return boolean TRUE if it's all ASCII
+* @package utf8
+* @subpackage position
+*/
+function utf8_byte_position()
+{
+ $args = func_get_args();
+ $str =& array_shift($args);
+
+ if (!is_string($str))
+ return false;
+
+ $result = array();
+ $prev = array(0, 0); // Trivial byte index, character offset pair
+ $i = utf8_locate_next_chr($str, 300); // Use a short piece of str to estimate bytes per character. $i (& $j) -> byte indexes into $str
+ $c = strlen(utf8_decode(substr($str, 0, $i))); // $c -> character offset into $str
+
+ // Deal with arguments from lowest to highest
+ sort($args);
+
+ foreach ($args as $offset)
+ {
+ // Sanity checks FIXME
+
+ // 0 is an easy check
+ if ($offset == 0)
+ {
+ $result[] = 0; continue;
+ }
+
+ // Ensure no endless looping
+ $safety_valve = 50;
+
+ do
+ {
+ if (($c - $prev[1]) == 0)
+ {
+ // Hack: gone past end of string
+ $error = 0;
+ $i = strlen($str);
+ break;
+ }
+
+ $j = $i + (int)(($offset-$c) * ($i - $prev[0]) / ($c - $prev[1]));
+ $j = utf8_locate_next_chr($str, $j); // Correct to utf8 character boundary
+ $prev = array($i,$c); // Save the index, offset for use next iteration
+
+ if ($j > $i)
+ $c += strlen(utf8_decode(substr($str, $i, $j-$i))); // Determine new character offset
+ else
+ $c -= strlen(utf8_decode(substr($str, $j, $i-$j))); // Ditto
+
+ $error = abs($c-$offset);
+ $i = $j; // Ready for next time around
+ }
+ while (($error > 7) && --$safety_valve); // From 7 it is faster to iterate over the string
+
+ if ($error && $error <= 7)
+ {
+ if ($c < $offset)
+ {
+ // Move up
+ while ($error--)
+ $i = utf8_locate_next_chr($str, ++$i);
+ }
+ else
+ {
+ // Move down
+ while ($error--)
+ $i = utf8_locate_current_chr($str, --$i);
+ }
+
+ // Ready for next arg
+ $c = $offset;
+ }
+
+ $result[] = $i;
+ }
+
+ if (count($result) == 1)
+ return $result[0];
+
+ return $result;
+}
+
+/**
+* Given a string and any byte index, returns the byte index
+* of the start of the current UTF-8 character, relative to supplied
+* position. If the current character begins at the same place as the
+* supplied byte index, that byte index will be returned. Otherwise
+* this function will step backwards, looking for the index where
+* curent UTF-8 character begins
+* @author Chris Smith<chris@jalakai.co.uk>
+* @param string
+* @param int byte index in the string
+* @return int byte index of start of next UTF-8 character
+* @package utf8
+* @subpackage position
+*/
+function utf8_locate_current_chr( &$str, $idx )
+{
+ if ($idx <= 0)
+ return 0;
+
+ $limit = strlen($str);
+ if ($idx >= $limit)
+ return $limit;
+
+ // Binary value for any byte after the first in a multi-byte UTF-8 character
+ // will be like 10xxxxxx so & 0xC0 can be used to detect this kind
+ // of byte - assuming well formed UTF-8
+ while ($idx && ((ord($str[$idx]) & 0xC0) == 0x80))
+ $idx--;
+
+ return $idx;
+}
+
+/**
+* Given a string and any byte index, returns the byte index
+* of the start of the next UTF-8 character, relative to supplied
+* position. If the next character begins at the same place as the
+* supplied byte index, that byte index will be returned.
+* @author Chris Smith<chris@jalakai.co.uk>
+* @param string
+* @param int byte index in the string
+* @return int byte index of start of next UTF-8 character
+* @package utf8
+* @subpackage position
+*/
+function utf8_locate_next_chr(&$str, $idx)
+{
+ if ($idx <= 0)
+ return 0;
+
+ $limit = strlen($str);
+ if ($idx >= $limit)
+ return $limit;
+
+ // Binary value for any byte after the first in a multi-byte UTF-8 character
+ // will be like 10xxxxxx so & 0xC0 can be used to detect this kind
+ // of byte - assuming well formed UTF-8
+ while (($idx < $limit) && ((ord($str[$idx]) & 0xC0) == 0x80))
+ $idx++;
+
+ return $idx;
+}
diff --git a/include/utf8/utils/specials.php b/include/utf8/utils/specials.php
new file mode 100644
index 0000000..69219dc
--- /dev/null
+++ b/include/utf8/utils/specials.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+* Utilities for processing "special" characters in UTF-8. "Special" largely means anything which would
+* be regarded as a non-word character, like ASCII control characters and punctuation. This has a "Roman"
+* bias - it would be unaware of modern Chinese "punctuation" characters for example.
+* Note: requires utils/unicode.php to be loaded
+* @version $Id: specials.php,v 1.2 2006/10/16 21:13:59 harryf Exp $
+* @package utf8
+* @subpackage utils
+* @see utf8_is_valid
+*/
+
+/**
+* Used internally. Builds a PCRE pattern from the $UTF8_SPECIAL_CHARS
+* array defined in this file
+* The $UTF8_SPECIAL_CHARS should contain all special characters (non-letter/non-digit)
+* defined in the various local charsets - it's not a complete list of
+* non-alphanum characters in UTF-8. It's not perfect but should match most
+* cases of special chars.
+* This function adds the control chars 0x00 to 0x19 to the array of
+* special chars (they are not included in $UTF8_SPECIAL_CHARS)
+* @package utf8
+* @subpackage utils
+* @return string
+* @see utf8_from_unicode
+* @see utf8_is_word_chars
+* @see utf8_strip_specials
+*/
+function utf8_specials_pattern()
+{
+ static $pattern = null;
+
+ if (!$pattern)
+ {
+ $UTF8_SPECIAL_CHARS = array(
+ 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c,
+ 0x002f, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e,
+ 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088,
+ 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092,
+ 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c,
+ 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6,
+ 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0,
+ 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba,
+ 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00d7, 0x00f7, 0x02c7, 0x02d8, 0x02d9,
+ 0x02da, 0x02db, 0x02dc, 0x02dd, 0x0300, 0x0301, 0x0303, 0x0309, 0x0323, 0x0384,
+ 0x0385, 0x0387, 0x03b2, 0x03c6, 0x03d1, 0x03d2, 0x03d5, 0x03d6, 0x05b0, 0x05b1,
+ 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05bb, 0x05bc,
+ 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f3, 0x05f4, 0x060c,
+ 0x061b, 0x061f, 0x0640, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651,
+ 0x0652, 0x066a, 0x0e3f, 0x200c, 0x200d, 0x200e, 0x200f, 0x2013, 0x2014, 0x2015,
+ 0x2017, 0x2018, 0x2019, 0x201a, 0x201c, 0x201d, 0x201e, 0x2020, 0x2021, 0x2022,
+ 0x2026, 0x2030, 0x2032, 0x2033, 0x2039, 0x203a, 0x2044, 0x20a7, 0x20aa, 0x20ab,
+ 0x20ac, 0x2116, 0x2118, 0x2122, 0x2126, 0x2135, 0x2190, 0x2191, 0x2192, 0x2193,
+ 0x2194, 0x2195, 0x21b5, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x2200, 0x2202,
+ 0x2203, 0x2205, 0x2206, 0x2207, 0x2208, 0x2209, 0x220b, 0x220f, 0x2211, 0x2212,
+ 0x2215, 0x2217, 0x2219, 0x221a, 0x221d, 0x221e, 0x2220, 0x2227, 0x2228, 0x2229,
+ 0x222a, 0x222b, 0x2234, 0x223c, 0x2245, 0x2248, 0x2260, 0x2261, 0x2264, 0x2265,
+ 0x2282, 0x2283, 0x2284, 0x2286, 0x2287, 0x2295, 0x2297, 0x22a5, 0x22c5, 0x2310,
+ 0x2320, 0x2321, 0x2329, 0x232a, 0x2469, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514,
+ 0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2550, 0x2551, 0x2552, 0x2553,
+ 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d,
+ 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567,
+ 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590,
+ 0x2591, 0x2592, 0x2593, 0x25a0, 0x25b2, 0x25bc, 0x25c6, 0x25ca, 0x25cf, 0x25d7,
+ 0x2605, 0x260e, 0x261b, 0x261e, 0x2660, 0x2663, 0x2665, 0x2666, 0x2701, 0x2702,
+ 0x2703, 0x2704, 0x2706, 0x2707, 0x2708, 0x2709, 0x270c, 0x270d, 0x270e, 0x270f,
+ 0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719,
+ 0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723,
+ 0x2724, 0x2725, 0x2726, 0x2727, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e,
+ 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738,
+ 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f, 0x2740, 0x2741, 0x2742,
+ 0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274a, 0x274b, 0x274d,
+ 0x274f, 0x2750, 0x2751, 0x2752, 0x2756, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c,
+ 0x275d, 0x275e, 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x277f,
+ 0x2789, 0x2793, 0x2794, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e,
+ 0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7, 0x27a8,
+ 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af, 0x27b1, 0x27b2, 0x27b3,
+ 0x27b4, 0x27b5, 0x27b6, 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd,
+ 0x27be, 0xf6d9, 0xf6da, 0xf6db, 0xf8d7, 0xf8d8, 0xf8d9, 0xf8da, 0xf8db, 0xf8dc,
+ 0xf8dd, 0xf8de, 0xf8df, 0xf8e0, 0xf8e1, 0xf8e2, 0xf8e3, 0xf8e4, 0xf8e5, 0xf8e6,
+ 0xf8e7, 0xf8e8, 0xf8e9, 0xf8ea, 0xf8eb, 0xf8ec, 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0,
+ 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4, 0xf8f5, 0xf8f6, 0xf8f7, 0xf8f8, 0xf8f9, 0xf8fa,
+ 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0xfe7c, 0xfe7d);
+
+ $pattern = preg_quote(utf8_from_unicode($UTF8_SPECIAL_CHARS), '/');
+ $pattern = '/[\x00-\x19'.$pattern.']/u';
+ }
+
+ return $pattern;
+}
+
+/**
+* Checks a string for whether it contains only word characters. This
+* is logically equivalent to the \w PCRE meta character. Note that
+* this is not a 100% guarantee that the string only contains alpha /
+* numeric characters but just that common non-alphanumeric are not
+* in the string, including ASCII device control characters.
+* @package utf8
+* @subpackage utils
+* @param string to check
+* @return boolean TRUE if the string only contains word characters
+* @see utf8_specials_pattern
+*/
+function utf8_is_word_chars($str)
+{
+ return !(bool) preg_match(utf8_specials_pattern(), $str);
+}
+
+/**
+* Removes special characters (nonalphanumeric) from a UTF-8 string
+*
+* This can be useful as a helper for sanitizing a string for use as
+* something like a file name or a unique identifier. Be warned though
+* it does not handle all possible non-alphanumeric characters and is
+* not intended is some kind of security / injection filter.
+*
+* @package utf8
+* @subpackage utils
+* @author Andreas Gohr <andi@splitbrain.org>
+* @param string $string The UTF8 string to strip of special chars
+* @param string (optional) $repl Replace special with this string
+* @return string with common non-alphanumeric characters removed
+* @see utf8_specials_pattern
+*/
+function utf8_strip_specials($string, $repl='')
+{
+ return preg_replace(utf8_specials_pattern(), $repl, $string);
+}
diff --git a/include/utf8/utils/unicode.php b/include/utf8/utils/unicode.php
new file mode 100644
index 0000000..f0e86cb
--- /dev/null
+++ b/include/utf8/utils/unicode.php
@@ -0,0 +1,241 @@
+<?php
+
+/**
+* @version $Id: unicode.php,v 1.2 2006/02/26 13:20:44 harryf Exp $
+* Tools for conversion between UTF-8 and unicode
+* The Original Code is Mozilla Communicator client code.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998
+* the Initial Developer. All Rights Reserved.
+* Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi)
+* Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com)
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage unicode
+*/
+
+/**
+* Takes an UTF-8 string and returns an array of ints representing the
+* Unicode characters. Astral planes are supported ie. the ints in the
+* output can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates
+* are not allowed.
+* Returns false if the input string isn't a valid UTF-8 octet sequence
+* and raises a PHP error at level E_USER_WARNING
+* Note: this function has been modified slightly in this library to
+* trigger errors on encountering bad bytes
+* @author <hsivonen@iki.fi>
+* @param string UTF-8 encoded string
+* @return mixed array of unicode code points or FALSE if UTF-8 invalid
+* @see utf8_from_unicode
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage unicode
+*/
+function utf8_to_unicode($str)
+{
+ $mState = 0; // Cached expected number of octets after the current octet
+ // until the beginning of the next UTF8 character sequence
+ $mUcs4 = 0; // Cached Unicode character
+ $mBytes = 1; // Cached expected number of octets in the current sequence
+
+ $out = array();
+ $len = strlen($str);
+
+ for($i = 0; $i < $len; $i++)
+ {
+ $in = ord($str[$i]);
+
+ if ($mState == 0)
+ {
+ // When mState is zero we expect either a US-ASCII character or a multi-octet sequence.
+ if (0 == (0x80 & ($in)))
+ {
+ // US-ASCII, pass straight through.
+ $out[] = $in;
+ $mBytes = 1;
+ }
+ else if (0xC0 == (0xE0 & ($in)))
+ {
+ // First octet of 2 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x1F) << 6;
+ $mState = 1;
+ $mBytes = 2;
+ }
+ else if (0xE0 == (0xF0 & ($in)))
+ {
+ // First octet of 3 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x0F) << 12;
+ $mState = 2;
+ $mBytes = 3;
+ }
+ else if (0xF0 == (0xF8 & ($in)))
+ {
+ // First octet of 4 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x07) << 18;
+ $mState = 3;
+ $mBytes = 4;
+ }
+ else if (0xF8 == (0xFC & ($in)))
+ {
+ /* First octet of 5 octet sequence.
+ *
+ * This is illegal because the encoded codepoint must be either
+ * (a) not the shortest form or
+ * (b) outside the Unicode range of 0-0x10FFFF.
+ * Rather than trying to resynchronize, we will carry on until the end
+ * of the sequence and let the later error handling code catch it.
+ */
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x03) << 24;
+ $mState = 4;
+ $mBytes = 5;
+ }
+ else if (0xFC == (0xFE & ($in)))
+ {
+ // First octet of 6 octet sequence, see comments for 5 octet sequence.
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 1) << 30;
+ $mState = 5;
+ $mBytes = 6;
+ }
+ else
+ {
+ // Current octet is neither in the US-ASCII range nor a legal first octet of a multi-octet sequence
+ trigger_error('utf8_to_unicode: Illegal sequence identifier in UTF-8 at byte '.$i, E_USER_WARNING);
+ return false;
+ }
+ }
+ else
+ {
+ // When mState is non-zero, we expect a continuation of the multi-octet sequence
+ if (0x80 == (0xC0 & ($in)))
+ {
+ // Legal continuation.
+ $shift = ($mState - 1) * 6;
+ $tmp = $in;
+ $tmp = ($tmp & 0x0000003F) << $shift;
+ $mUcs4 |= $tmp;
+
+ /**
+ * End of the multi-octet sequence. mUcs4 now contains the final
+ * Unicode codepoint to be output
+ */
+ if (0 == --$mState)
+ {
+ /*
+ * Check for illegal sequences and codepoints.
+ */
+ // From Unicode 3.1, non-shortest form is illegal
+ if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+ ((4 == $mBytes) && ($mUcs4 < 0x10000)) || (4 < $mBytes) ||
+ // From Unicode 3.2, surrogate characters are illegal
+ (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+ // Codepoints outside the Unicode range are illegal
+ ($mUcs4 > 0x10FFFF))
+ {
+ trigger_error('utf8_to_unicode: Illegal sequence or codepoint in UTF-8 at byte '.$i, E_USER_WARNING);
+ return false;
+ }
+
+ // BOM is legal but we don't want to output it
+ if (0xFEFF != $mUcs4)
+ $out[] = $mUcs4;
+
+ // Initialize UTF8 cache
+ $mState = 0;
+ $mUcs4 = 0;
+ $mBytes = 1;
+ }
+
+ }
+ else
+ {
+ /* ((0xC0 & (*in) != 0x80) && (mState != 0))
+ Incomplete multi-octet sequence. */
+ trigger_error('utf8_to_unicode: Incomplete multi-octet sequence in UTF-8 at byte '.$i, E_USER_WARNING);
+ return false;
+ }
+ }
+ }
+
+ return $out;
+}
+
+/**
+* Takes an array of ints representing the Unicode characters and returns
+* a UTF-8 string. Astral planes are supported ie. the ints in the
+* input can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates
+* are not allowed.
+* Returns false if the input array contains ints that represent
+* surrogates or are outside the Unicode range
+* and raises a PHP error at level E_USER_WARNING
+* Note: this function has been modified slightly in this library to use
+* output buffering to concatenate the UTF-8 string (faster) as well as
+* reference the array by it's keys
+* @param array of unicode code points representing a string
+* @return mixed UTF-8 string or FALSE if array contains invalid code points
+* @author <hsivonen@iki.fi>
+* @see utf8_to_unicode
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage unicode
+*/
+function utf8_from_unicode($arr)
+{
+ ob_start();
+
+ foreach (array_keys($arr) as $k)
+ {
+ if ( ($arr[$k] >= 0) && ($arr[$k] <= 0x007f) ) // ASCII range (including control chars)
+ {
+ echo chr($arr[$k]);
+ }
+ else if ($arr[$k] <= 0x07ff) //2 byte sequence
+ {
+ echo chr(0xc0 | ($arr[$k] >> 6));
+ echo chr(0x80 | ($arr[$k] & 0x003f));
+ }
+ else if($arr[$k] == 0xFEFF) // Byte order mark (skip)
+ {
+ // Nop -- zap the BOM
+ }
+ else if ($arr[$k] >= 0xD800 && $arr[$k] <= 0xDFFF) // Test for illegal surrogates
+ {
+ // Found a surrogate
+ trigger_error('utf8_from_unicode: Illegal surrogate at index: '.$k.', value: '.$arr[$k], E_USER_WARNING);
+
+ return false;
+ }
+ else if ($arr[$k] <= 0xffff) // 3 byte sequence
+ {
+ echo chr(0xe0 | ($arr[$k] >> 12));
+ echo chr(0x80 | (($arr[$k] >> 6) & 0x003f));
+ echo chr(0x80 | ($arr[$k] & 0x003f));
+ }
+ else if ($arr[$k] <= 0x10ffff) // 4 byte sequence
+ {
+ echo chr(0xf0 | ($arr[$k] >> 18));
+ echo chr(0x80 | (($arr[$k] >> 12) & 0x3f));
+ echo chr(0x80 | (($arr[$k] >> 6) & 0x3f));
+ echo chr(0x80 | ($arr[$k] & 0x3f));
+ }
+ else
+ {
+ trigger_error('utf8_from_unicode: Codepoint out of Unicode range at index: '.$k.', value: '.$arr[$k], E_USER_WARNING);
+
+ // Out of range
+ return false;
+ }
+ }
+
+ $result = ob_get_contents();
+ ob_end_clean();
+
+ return $result;
+}
diff --git a/include/utf8/utils/validation.php b/include/utf8/utils/validation.php
new file mode 100644
index 0000000..90dce8e
--- /dev/null
+++ b/include/utf8/utils/validation.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+* @version $Id: validation.php,v 1.2 2006/02/26 13:20:44 harryf Exp $
+* Tools for validing a UTF-8 string is well formed.
+* The Original Code is Mozilla Communicator client code.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998
+* the Initial Developer. All Rights Reserved.
+* Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi)
+* Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com)
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage validation
+*/
+
+/**
+* Tests a string as to whether it's valid UTF-8 and supported by the
+* Unicode standard
+* Note: this function has been modified to simple return true or false
+* @author <hsivonen@iki.fi>
+* @param string UTF-8 encoded string
+* @return boolean true if valid
+* @see http://hsivonen.iki.fi/php-utf8/
+* @see utf8_compliant
+* @package utf8
+* @subpackage validation
+*/
+function utf8_is_valid($str)
+{
+ $mState = 0; // Cached expected number of octets after the current octet
+ // until the beginning of the next UTF8 character sequence
+ $mUcs4 = 0; // Cached Unicode character
+ $mBytes = 1; // Cached expected number of octets in the current sequence
+
+ $len = strlen($str);
+
+ for($i = 0; $i < $len; $i++)
+ {
+ $in = ord($str{$i});
+
+ if ( $mState == 0)
+ {
+ // When mState is zero we expect either a US-ASCII character or a multi-octet sequence.
+ if (0 == (0x80 & ($in)))
+ {
+ $mBytes = 1; // US-ASCII, pass straight through
+ }
+ else if (0xC0 == (0xE0 & ($in)))
+ {
+ // First octet of 2 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x1F) << 6;
+ $mState = 1;
+ $mBytes = 2;
+ }
+ else if (0xE0 == (0xF0 & ($in)))
+ {
+ // First octet of 3 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x0F) << 12;
+ $mState = 2;
+ $mBytes = 3;
+ }
+ else if (0xF0 == (0xF8 & ($in)))
+ {
+ // First octet of 4 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x07) << 18;
+ $mState = 3;
+ $mBytes = 4;
+ }
+ else if (0xF8 == (0xFC & ($in)))
+ {
+ /* First octet of 5 octet sequence.
+ *
+ * This is illegal because the encoded codepoint must be either
+ * (a) not the shortest form or
+ * (b) outside the Unicode range of 0-0x10FFFF.
+ * Rather than trying to resynchronize, we will carry on until the end
+ * of the sequence and let the later error handling code catch it.
+ */
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x03) << 24;
+ $mState = 4;
+ $mBytes = 5;
+ }
+ else if (0xFC == (0xFE & ($in)))
+ {
+ // First octet of 6 octet sequence, see comments for 5 octet sequence.
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 1) << 30;
+ $mState = 5;
+ $mBytes = 6;
+ }
+ else
+ {
+ // Current octet is neither in the US-ASCII range nor a legal first octet of a multi-octet sequence.
+ return false;
+ }
+ }
+ else
+ {
+ // When mState is non-zero, we expect a continuation of the multi-octet sequence
+ if (0x80 == (0xC0 & ($in)))
+ {
+ // Legal continuation.
+ $shift = ($mState - 1) * 6;
+ $tmp = $in;
+ $tmp = ($tmp & 0x0000003F) << $shift;
+ $mUcs4 |= $tmp;
+
+ /**
+ * End of the multi-octet sequence. mUcs4 now contains the final
+ * Unicode codepoint to be output
+ */
+ if (0 == --$mState)
+ {
+ /*
+ * Check for illegal sequences and codepoints.
+ */
+ // From Unicode 3.1, non-shortest form is illegal
+ if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+ ((4 == $mBytes) && ($mUcs4 < 0x10000)) || (4 < $mBytes) ||
+ // From Unicode 3.2, surrogate characters are illegal
+ (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+ // Codepoints outside the Unicode range are illegal
+ ($mUcs4 > 0x10FFFF))
+ {
+ return FALSE;
+ }
+
+ // Initialize UTF8 cache
+ $mState = 0;
+ $mUcs4 = 0;
+ $mBytes = 1;
+ }
+ }
+ else
+ {
+ /**
+ *((0xC0 & (*in) != 0x80) && (mState != 0))
+ * Incomplete multi-octet sequence.
+ */
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+* Tests whether a string complies as UTF-8. This will be much
+* faster than utf8_is_valid, but will pass five and six octet
+* UTF-8 sequences, which are not supported by Unicode and
+* so cannot be displayed correctly in a browser. In other words
+* it is not as strict as utf8_is_valid but it's faster. If you use
+* is to validate user input, you place yourself at the risk that
+* attackers will be able to inject 5 and 6 byte sequences (which
+* may or may not be a significant risk, depending on what you are
+* are doing)
+* Note: Does not pass five and six octet UTF-8 sequences anymore in
+* in the unit tests.
+* @see utf8_is_valid
+* @see http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805
+* @param string UTF-8 string to check
+* @return boolean TRUE if string is valid UTF-8
+* @package utf8
+* @subpackage validation
+*/
+function utf8_compliant($str)
+{
+ if (strlen($str) == 0)
+ return true;
+
+ // If even just the first character can be matched, when the /u
+ // modifier is used, then it's valid UTF-8. If the UTF-8 is somehow
+ // invalid, nothing at all will match, even if the string contains
+ // some valid sequences
+ return (preg_match('/^.{1}/us', $str, $ar) == 1);
+}
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..888cc91
--- /dev/null
+++ b/index.php
@@ -0,0 +1,278 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+// Load the index.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/index.php';
+
+// Get list of forums and topics with new posts since last visit
+if (!$pun_user['is_guest'])
+{
+ $result = $db->query('SELECT f.id, f.last_post FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.last_post>'.$pun_user['last_visit']) or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ $forums = $new_topics = array();
+ $tracked_topics = get_tracked_topics();
+
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if (!isset($tracked_topics['forums'][$cur_forum['id']]) || $tracked_topics['forums'][$cur_forum['id']] < $cur_forum['last_post'])
+ $forums[$cur_forum['id']] = $cur_forum['last_post'];
+ }
+
+ if (!empty($forums))
+ {
+ if (empty($tracked_topics['topics']))
+ $new_topics = $forums;
+ else
+ {
+ $result = $db->query('SELECT forum_id, id, last_post FROM '.$db->prefix.'topics WHERE forum_id IN('.implode(',', array_keys($forums)).') AND last_post>'.$pun_user['last_visit'].' AND moved_to IS NULL') or error('Unable to fetch new topics', __FILE__, __LINE__, $db->error());
+
+ while ($cur_topic = $db->fetch_assoc($result))
+ {
+ if (!isset($new_topics[$cur_topic['forum_id']]) && (!isset($tracked_topics['forums'][$cur_topic['forum_id']]) || $tracked_topics['forums'][$cur_topic['forum_id']] < $forums[$cur_topic['forum_id']]) && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']))
+ $new_topics[$cur_topic['forum_id']] = $forums[$cur_topic['forum_id']];
+ }
+ }
+ }
+ }
+}
+
+if ($pun_config['o_feed_type'] == '1')
+ $page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;type=rss" title="'.$lang_common['RSS active topics feed'].'" />');
+else if ($pun_config['o_feed_type'] == '2')
+ $page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;type=atom" title="'.$lang_common['Atom active topics feed'].'" />');
+
+$forum_actions = array();
+
+// Display a "mark all as read" link
+if (!$pun_user['is_guest'])
+ $forum_actions[] = '<a href="misc.php?action=markread&amp;csrf_token='.pun_csrf_token().'">'.$lang_common['Mark all as read'].'</a>';
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']));
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+// Print the categories and forums
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.forum_desc, f.redirect_url, f.moderators, f.num_topics, f.num_posts, f.last_post, f.last_post_id, f.last_poster FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+$cur_category = 0;
+$cat_count = 0;
+$forum_count = 0;
+while ($cur_forum = $db->fetch_assoc($result))
+{
+ $moderators = '';
+
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category != 0)
+ echo "\t\t\t".'</tbody>'."\n\t\t\t".'</table>'."\n\t\t".'</div>'."\n\t".'</div>'."\n".'</div>'."\n\n";
+
+ ++$cat_count;
+ $forum_count = 0;
+
+?>
+<div id="idx<?php echo $cat_count ?>" class="blocktable">
+ <h2><span><?php echo pun_htmlspecialchars($cur_forum['cat_name']) ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_common['Forum'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_index['Topics'] ?></th>
+ <th class="tc3" scope="col"><?php echo $lang_common['Posts'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ $cur_category = $cur_forum['cid'];
+ }
+
+ ++$forum_count;
+ $item_status = ($forum_count % 2 == 0) ? 'roweven' : 'rowodd';
+ $forum_field_new = '';
+ $icon_type = 'icon';
+
+ // Are there new posts since our last visit?
+ if (isset($new_topics[$cur_forum['fid']]))
+ {
+ $item_status .= ' inew';
+ $forum_field_new = '<span class="newtext">[ <a href="search.php?action=show_new&amp;fid='.$cur_forum['fid'].'">'.$lang_common['New posts'].'</a> ]</span>';
+ $icon_type = 'icon icon-new';
+ }
+
+ // Is this a redirect forum?
+ if ($cur_forum['redirect_url'] != '')
+ {
+ $forum_field = '<h3><span class="redirtext">'.$lang_index['Link to'].'</span> <a href="'.pun_htmlspecialchars($cur_forum['redirect_url']).'" title="'.$lang_index['Link to'].' '.pun_htmlspecialchars($cur_forum['redirect_url']).'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</a></h3>';
+ $num_topics = $num_posts = '-';
+ $item_status .= ' iredirect';
+ $icon_type = 'icon';
+ }
+ else
+ {
+ $forum_field = '<h3><a href="viewforum.php?id='.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</a>'.(!empty($forum_field_new) ? ' '.$forum_field_new : '').'</h3>';
+ $num_topics = $cur_forum['num_topics'];
+ $num_posts = $cur_forum['num_posts'];
+ }
+
+ if ($cur_forum['forum_desc'] != '')
+ $forum_field .= "\n\t\t\t\t\t\t\t\t".'<div class="forumdesc">'.$cur_forum['forum_desc'].'</div>';
+
+ // If there is a last_post/last_poster
+ if ($cur_forum['last_post'] != '')
+ $last_post = '<a href="viewtopic.php?pid='.$cur_forum['last_post_id'].'#p'.$cur_forum['last_post_id'].'">'.format_time($cur_forum['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_forum['last_poster']).'</span>';
+ else if ($cur_forum['redirect_url'] != '')
+ $last_post = '- - -';
+ else
+ $last_post = $lang_common['Never'];
+
+ if ($cur_forum['moderators'] != '')
+ {
+ $mods_array = unserialize($cur_forum['moderators']);
+ $moderators = array();
+
+ foreach ($mods_array as $mod_username => $mod_id)
+ {
+ if ($pun_user['g_view_users'] == '1')
+ $moderators[] = '<a href="profile.php?id='.$mod_id.'">'.pun_htmlspecialchars($mod_username).'</a>';
+ else
+ $moderators[] = pun_htmlspecialchars($mod_username);
+ }
+
+ $moderators = "\t\t\t\t\t\t\t\t".'<p class="modlist">(<em>'.$lang_common['Moderated by'].'</em> '.implode(', ', $moderators).')</p>'."\n";
+ }
+
+?>
+ <tr class="<?php echo $item_status ?>">
+ <td class="tcl">
+ <div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($forum_count) ?></div></div>
+ <div class="tclcon">
+ <div>
+ <?php echo $forum_field."\n".$moderators ?>
+ </div>
+ </div>
+ </td>
+ <td class="tc2"><?php echo forum_number_format($num_topics) ?></td>
+ <td class="tc3"><?php echo forum_number_format($num_posts) ?></td>
+ <td class="tcr"><?php echo $last_post ?></td>
+ </tr>
+<?php
+
+}
+
+// Did we output any categories and forums?
+if ($cur_category > 0)
+ echo "\t\t\t".'</tbody>'."\n\t\t\t".'</table>'."\n\t\t".'</div>'."\n\t".'</div>'."\n".'</div>'."\n\n";
+else
+ echo '<div id="idx0" class="block"><div class="box"><div class="inbox"><p>'.$lang_index['Empty board'].'</p></div></div></div>';
+
+// Collect some statistics from the database
+if (file_exists(FORUM_CACHE_DIR.'cache_users_info.php'))
+ include FORUM_CACHE_DIR.'cache_users_info.php';
+
+if (!defined('PUN_USERS_INFO_LOADED'))
+{
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+ require FORUM_CACHE_DIR.'cache_users_info.php';
+}
+
+$result = $db->query('SELECT SUM(num_topics), SUM(num_posts) FROM '.$db->prefix.'forums') or error('Unable to fetch topic/post count', __FILE__, __LINE__, $db->error());
+list($stats['total_topics'], $stats['total_posts']) = array_map('intval', $db->fetch_row($result));
+
+if ($pun_user['g_view_users'] == '1')
+ $stats['newest_user'] = '<a href="profile.php?id='.$stats['last_user']['id'].'">'.pun_htmlspecialchars($stats['last_user']['username']).'</a>';
+else
+ $stats['newest_user'] = pun_htmlspecialchars($stats['last_user']['username']);
+
+if (!empty($forum_actions))
+{
+
+?>
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <p class="subscribelink clearb"><?php echo implode(' - ', $forum_actions); ?></p>
+ </div>
+</div>
+<?php
+
+}
+
+?>
+<div id="brdstats" class="block">
+ <h2><span><?php echo $lang_index['Board info'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <dl class="conr">
+ <dt><strong><?php echo $lang_index['Board stats'] ?></strong></dt>
+ <dd><span><?php printf($lang_index['No of users'], '<strong>'.forum_number_format($stats['total_users']).'</strong>') ?></span></dd>
+ <dd><span><?php printf($lang_index['No of topics'], '<strong>'.forum_number_format($stats['total_topics']).'</strong>') ?></span></dd>
+ <dd><span><?php printf($lang_index['No of posts'], '<strong>'.forum_number_format($stats['total_posts']).'</strong>') ?></span></dd>
+ </dl>
+ <dl class="conl">
+ <dt><strong><?php echo $lang_index['User info'] ?></strong></dt>
+ <dd><span><?php printf($lang_index['Newest user'], $stats['newest_user']) ?></span></dd>
+<?php
+
+if ($pun_config['o_users_online'] == '1')
+{
+ // Fetch users online info and generate strings for output
+ $num_guests = 0;
+ $users = array();
+ $result = $db->query('SELECT user_id, ident FROM '.$db->prefix.'online WHERE idle=0 ORDER BY ident', true) or error('Unable to fetch online list', __FILE__, __LINE__, $db->error());
+
+ while ($pun_user_online = $db->fetch_assoc($result))
+ {
+ if ($pun_user_online['user_id'] > 1)
+ {
+ if ($pun_user['g_view_users'] == '1')
+ $users[] = "\n\t\t\t\t".'<dd><a href="profile.php?id='.$pun_user_online['user_id'].'">'.pun_htmlspecialchars($pun_user_online['ident']).'</a>';
+ else
+ $users[] = "\n\t\t\t\t".'<dd>'.pun_htmlspecialchars($pun_user_online['ident']);
+ }
+ else
+ ++$num_guests;
+ }
+
+ $num_users = count($users);
+ echo "\t\t\t\t".'<dd><span>'.sprintf($lang_index['Users online'], '<strong>'.forum_number_format($num_users).'</strong>').'</span></dd>'."\n\t\t\t\t".'<dd><span>'.sprintf($lang_index['Guests online'], '<strong>'.forum_number_format($num_guests).'</strong>').'</span></dd>'."\n\t\t\t".'</dl>'."\n";
+
+
+ if ($num_users > 0)
+ echo "\t\t\t".'<dl id="onlinelist" class="clearb">'."\n\t\t\t\t".'<dt><strong>'.$lang_index['Online'].' </strong></dt>'."\t\t\t\t".implode(',</dd> ', $users).'</dd>'."\n\t\t\t".'</dl>'."\n";
+ else
+ echo "\t\t\t".'<div class="clearer"></div>'."\n";
+
+}
+else
+ echo "\t\t\t".'</dl>'."\n\t\t\t".'<div class="clearer"></div>'."\n";
+
+
+?>
+ </div>
+ </div>
+</div>
+<?php
+
+$footer_style = 'index';
+require PUN_ROOT.'footer.php';
diff --git a/lang/English/admin_bans.php b/lang/English/admin_bans.php
new file mode 100644
index 0000000..2ea0248
--- /dev/null
+++ b/lang/English/admin_bans.php
@@ -0,0 +1,69 @@
+<?php
+
+// Language definitions used in admin_bans.php
+$lang_admin_bans = array(
+
+'No user message' => 'No user by that username registered. If you want to add a ban not tied to a specific username just leave the username blank.',
+'No user ID message' => 'No user by that ID registered.',
+'User is admin message' => 'The user %s is an administrator and can\'t be banned. If you want to ban an administrator, you must first demote him/her.',
+'User is mod message' => 'The user %s is a moderator and can\'t be banned. If you want to ban a moderator, you must first demote him/her.',
+'Must enter message' => 'You must enter either a username, an IP address or an email address (at least).',
+'Cannot ban guest message' => 'The guest user cannot be banned.',
+'Invalid IP message' => 'You entered an invalid IP/IP-range.',
+'Invalid e-mail message' => 'The email address (e.g. user@domain.com) or partial email address domain (e.g. domain.com) you entered is invalid.',
+'Duplicate domain message' => 'The domain %s has already been banned.',
+'Duplicate e-mail message' => 'The email address %s has already been banned.',
+'Invalid date message' => 'You entered an invalid expire date.',
+'Invalid date reasons' => 'The format should be YYYY-MM-DD and the date must be at least one day in the future.',
+'Ban added redirect' => 'Ban added. Redirecting …' ,
+'Ban edited redirect' => 'Ban edited. Redirecting …',
+'Ban removed redirect' => 'Ban removed. Redirecting …',
+
+'New ban head' => 'New ban',
+'Add ban subhead' => 'Add ban',
+'Username label' => 'Username',
+'Username help' => 'The username to ban (case-insensitive).',
+'Username advanced help' => 'The username to ban (case-insensitive). The next page will let you enter a custom IP and email. If you just want to ban a specific IP/IP-range or email just leave it blank.',
+
+'Ban search head' => 'Ban search',
+'Ban search subhead' => 'Enter search criteria',
+'Ban search info' => 'Search for bans in the database. You can enter one or more terms to search for. Wildcards in the form of asterisks (*) are accepted. To show all bans leave all fields empty.',
+'Date help' => '(yyyy-mm-dd)',
+'Message label' => 'Message',
+'Expire after label' => 'Expire after',
+'Expire before label' => 'Expire before',
+'Order by label' => 'Order by',
+'Order by username' => 'Username',
+'Order by ip' => 'IP',
+'Order by e-mail' => 'Email',
+'Order by expire' => 'Expire date',
+'Ascending' => 'Ascending',
+'Descending' => 'Descending',
+'Submit search' => 'Submit search',
+
+'E-mail label' => 'Email',
+'E-mail help' => 'The email or email domain you wish to ban (e.g. someone@somewhere.com or somewhere.com). See "Allow banned email addresses" in Permissions for more info.',
+'IP label' => 'IP address/IP-ranges',
+'IP help' => 'The IP address or IP-ranges you wish to ban (e.g. 150.11.110.1 or 150.11.110). Separate addresses with spaces. If an IP is entered already it is the last known IP of this user in the database.',
+'IP help link' => 'Click %s to see IP statistics for this user.',
+'Ban advanced head' => 'Ban advanced settings',
+'Ban advanced subhead' => 'Supplement ban with IP and email',
+'Ban message label' => 'Ban message',
+'Ban message help' => 'A message that will be displayed to the banned user when he/she visits the board.',
+'Message expiry subhead' => 'Ban message and expiry',
+'Ban IP range info' => 'You should be very careful when banning an IP-range because of the possibility of multiple users matching the same partial IP.',
+'Expire date label' => 'Expire date',
+'Expire date help' => 'The date when this ban should be automatically removed (format: yyyy-mm-dd). Leave blank to remove manually.',
+
+'Results head' => 'Search Results',
+'Results username head' => 'Username',
+'Results e-mail head' => 'Email',
+'Results IP address head' => 'IP/IP-ranges',
+'Results expire head' => 'Expires',
+'Results message head' => 'Message',
+'Results banned by head' => 'Banned by',
+'Results actions head' => 'Actions',
+'No match' => 'No match',
+'Unknown' => 'Unknown',
+
+);
diff --git a/lang/English/admin_categories.php b/lang/English/admin_categories.php
new file mode 100644
index 0000000..f19054d
--- /dev/null
+++ b/lang/English/admin_categories.php
@@ -0,0 +1,29 @@
+<?php
+
+// Language definitions used in admin_categories.php
+$lang_admin_categories = array(
+
+'Must enter name message' => 'You must enter a name for the category',
+'Category added redirect' => 'Category added. Redirecting …',
+'Category deleted redirect' => 'Category deleted. Redirecting …',
+'Delete category head' => 'Delete category (together with all forums and posts it contains)',
+'Confirm delete subhead' => 'Confirm delete category',
+'Confirm delete info' => 'Are you sure that you want to delete the category <strong>%s</strong>?',
+'Delete category warn' => '<strong>WARNING!</strong> Deleting a category will delete all forums and posts (if any) in this category!',
+'Must enter integer message' => 'Position must be a positive integer value.',
+'Categories updated redirect' => 'Categories updated. Redirecting …',
+'Add categories head' => 'Add categories',
+'Add categories subhead' => 'Add categories',
+'Add category label' => 'Add a new category',
+'Add new submit' => 'Add new',
+'Add category help' => 'The name of the new category you want to add. You can edit the name of the category later (see below). Go to %s to add forums to your new category.',
+'Delete categories head' => 'Delete categories',
+'Delete categories subhead' => 'Delete categories',
+'Delete category label' => 'Delete a category',
+'Delete category help' => 'Select the name of the category you want to delete. You will be asked to confirm your choice of category for deletion before it is deleted.',
+'Edit categories head' => 'Edit categories',
+'Edit categories subhead' => 'Edit categories',
+'Category position label' => 'Position',
+'Category name label' => 'Name',
+
+);
diff --git a/lang/English/admin_censoring.php b/lang/English/admin_censoring.php
new file mode 100644
index 0000000..85ad048
--- /dev/null
+++ b/lang/English/admin_censoring.php
@@ -0,0 +1,21 @@
+<?php
+
+// Language definitions used in admin_censoring.php
+$lang_admin_censoring = array(
+
+'Must enter word message' => 'You must enter a word to censor.',
+'Word updated redirect' => 'Censor word updated. Redirecting …',
+'Word added redirect' => 'Censor word added. Redirecting …',
+'Word removed redirect' => 'Censor word removed. Redirecting …',
+'Censoring head' => 'Censoring',
+'Add word subhead' => 'Add word',
+'Add word info' => 'Enter a word that you want to censor and the replacement text for this word. Wildcards are accepted (i.e. *some* would match somewhere and lonesome). Censor words also affect usernames. New users will not be able to register with usernames containing any censored words. The search is case insensitive.',
+'Censoring enabled' => '<strong>Censoring is enabled in %s.</strong>',
+'Censoring disabled' => '<strong>Censoring is disabled in %s.</strong>',
+'Censored word label' => 'Censored word',
+'Replacement label' => 'Replacement word(s)',
+'Action label' => 'Action',
+'Edit remove subhead' => 'Edit or remove words',
+'No words in list' => 'No censor words in list.',
+
+);
diff --git a/lang/English/admin_common.php b/lang/English/admin_common.php
new file mode 100644
index 0000000..8f2bd3a
--- /dev/null
+++ b/lang/English/admin_common.php
@@ -0,0 +1,44 @@
+<?php
+
+// Language definitions used in admin_common.php
+$lang_admin_common = array(
+
+// The menu
+'Admin menu' => 'Admin menu',
+'Plugins menu' => 'Plugins menu',
+'Moderator menu' => 'Moderator menu',
+'Index' => 'Index',
+'Categories' => 'Categories',
+'Forums' => 'Forums',
+'Users' => 'Users',
+'User groups' => 'User groups',
+'Options' => 'Options',
+'Permissions' => 'Permissions',
+'Censoring' => 'Censoring',
+'Bans' => 'Bans',
+'Prune' => 'Prune',
+'Maintenance' => 'Maintenance',
+'Reports' => 'Reports',
+'Server statistics' => 'Server statistics',
+
+'Admin' => 'Admin',
+'Go back' => 'Go back',
+'Delete' => 'Delete',
+'Update' => 'Update',
+'Add' => 'Add',
+'Edit' => 'Edit',
+'Remove' => 'Remove',
+'Yes' => 'Yes',
+'No' => 'No',
+'Save changes' => 'Save changes',
+'Save' => 'Save',
+'here' => 'here',
+'Action' => 'Action',
+'None' => 'None',
+'Maintenance mode' => 'maintenance mode', // Used for link text in more than one file
+
+// Admin loader
+'No plugin message' => 'There is no plugin called %s in the plugin directory.',
+'Plugin failed message' => 'Loading of the plugin - <strong>%s</strong> - failed.',
+
+);
diff --git a/lang/English/admin_forums.php b/lang/English/admin_forums.php
new file mode 100644
index 0000000..15accaa
--- /dev/null
+++ b/lang/English/admin_forums.php
@@ -0,0 +1,53 @@
+<?php
+
+// Language definitions used in admin_forums.php
+$lang_admin_forums = array(
+
+'Forum added redirect' => 'Forum added. Redirecting …',
+'Forum deleted redirect' => 'Forum deleted. Redirecting …',
+'Forums updated redirect' => 'Forums updated. Redirecting …',
+'Forum updated redirect' => 'Forum updated. Redirecting …',
+'Perms reverted redirect' => 'Permissions reverted to defaults. Redirecting …',
+'Must enter name message' => 'You must enter a forum name.',
+'Must be integer message' => 'Position must be a positive integer value.',
+'New forum' => 'New forum',
+
+// Entry page
+'Add forum head' => 'Add forum',
+'Create new subhead' => 'Create a new forum',
+'Add forum label' => 'Add forum to category',
+'Add forum help' => 'Select the category to which you wish to add a new forum.',
+'Add forum' => 'Add forum',
+'No categories exist' => 'No categories exist',
+'Edit forums head' => 'Edit forums',
+'Category subhead' => 'Category:',
+'Forum label' => 'Forum',
+'Edit link' => 'Edit',
+'Delete link' => 'Delete',
+'Position label' => 'Position',
+'Update positions' => 'Update positions',
+'Confirm delete head' => 'Confirm delete forum',
+'Confirm delete subhead' => 'Important! Read before deleting',
+'Confirm delete info' => 'Are you sure that you want to delete the forum <strong>%s</strong>?',
+'Confirm delete warn' => 'WARNING! Deleting a forum will delete all posts (if any) in that forum!',
+
+// Detailed edit page
+'Edit forum head' => 'Edit forum',
+'Edit details subhead' => 'Edit forum details',
+'Forum name label' => 'Forum name',
+'Forum description label' => 'Description (HTML)',
+'Category label' => 'Category',
+'Sort by label' => 'Sort topics by',
+'Last post' => 'Last post',
+'Topic start' => 'Topic start',
+'Subject' => 'Subject',
+'Redirect label' => 'Redirect URL',
+'Redirect help' => 'Only available in empty forums',
+'Group permissions subhead' => 'Edit group permissions for this forum',
+'Group permissions info' => 'In this form, you can set the forum specific permissions for the different user groups. If you haven\'t made any changes to this forum\'s group permissions, what you see below is the default based on settings in %s. Administrators always have full permissions and are thus excluded. Permission settings that differ from the default permissions for the user group are marked red. The "Read forum" permission checkbox will be disabled if the group in question lacks the "Read board" permission. For redirect forums, only the "Read forum" permission is editable.',
+'Read forum label' => 'Read forum',
+'Post replies label' => 'Post replies',
+'Post topics label' => 'Post topics',
+'Revert to default' => 'Revert to default',
+
+);
diff --git a/lang/English/admin_groups.php b/lang/English/admin_groups.php
new file mode 100644
index 0000000..f759d25
--- /dev/null
+++ b/lang/English/admin_groups.php
@@ -0,0 +1,91 @@
+<?php
+
+// Language definitions used in admin_groups.php
+$lang_admin_groups = array(
+
+'Must enter title message' => 'You must enter a group title.',
+'Title already exists message' => 'There is already a group with the title <strong>%s</strong>.',
+'Default group redirect' => 'Default group set. Redirecting …',
+'Cannot remove default message' => 'The default group cannot be removed. In order to delete this group, you must first setup a different group as the default.',
+'Group removed redirect' => 'Group removed. Redirecting …',
+'Group added redirect' => 'Group added. Redirecting …',
+'Group edited redirect' => 'Group edited. Redirecting …',
+
+'Add groups head' => 'Add/setup groups',
+'Add group subhead' => 'Add new group',
+'New group label' => 'Base new group on',
+'New group help' => 'Select a user group from which the new group will inherit its permission settings. The next page will let you fine-tune its settings.',
+'Default group subhead' => 'Set default group',
+'Default group label' => 'Default group',
+'Default group help' => 'This is the default user group, e.g. the group users are placed in when they register. For security reasons, users can\'t be placed in either the moderator or administrator user groups by default.',
+'Existing groups head' => 'Existing groups',
+'Edit groups subhead' => 'Edit/delete groups',
+'Edit groups info' => 'The pre-defined groups Guests, Administrators, Moderators and Members cannot be removed. However, they can be edited. Please note that in some groups, some options are unavailable (e.g. the <em>edit posts</em> permission for guests). Administrators always have full permissions.',
+'Edit link' => 'Edit',
+'Delete link' => 'Delete',
+'Group delete head' => 'Group delete',
+'Confirm delete subhead' => 'Confirm delete group',
+'Confirm delete info' => 'Are you sure that you want to delete the group <strong>%s</strong>?',
+'Confirm delete warn' => 'WARNING! After you deleted a group you cannot restore it.',
+'Delete group head' => 'Delete group',
+'Move users subhead' => 'Move users currently in group',
+'Move users info' => 'The group <strong>%s</strong> currently has <strong>%s</strong> members. Please select a group to which these members will be assigned upon deletion.',
+'Move users label' => 'Move users to',
+'Delete group' => 'Delete group',
+
+'Group settings head' => 'Group settings',
+'Group settings subhead' => 'Setup group options and permissions',
+'Group settings info' => 'Below options and permissions are the default permissions for the user group. These options apply if no forum specific permissions are in effect.',
+'Group title label' => 'Group title',
+'User title label' => 'User title',
+'User title help' => 'The rank users in this group have attained. Leave blank to use default title ("%s").',
+'Promote users label' => 'Promote users',
+'Promote users help' => 'You can promote users to a new group automatically if they reach a certain number of posts. Select "%s" to disable. For security reasons, you are not allowed to select an administrator group here. Also note that group changes for users affected by this setting will take effect <strong>immediately</strong>. Note that the amount of posts you enter is the total amount of posts of a user, not the amount of posts made as a member of this group.',
+'Disable promotion' => 'Disable promoting',
+'Mod privileges label' => 'Allow users moderator privileges',
+'Mod privileges help' => 'In order for a user in this group to have moderator abilities, he/she must be assigned to moderate one or more forums. This is done via the user administration page of the user\'s profile.',
+'Edit profile label' => 'Allow moderators to edit user profiles',
+'Edit profile help' => 'If moderator privileges are enabled, allow users in this group to edit user profiles.',
+'Rename users label' => 'Allow moderators to rename users',
+'Rename users help' => 'If moderator privileges are enabled, allow users in this group to rename users.',
+'Change passwords label' => 'Allow moderators to change passwords',
+'Change passwords help' => 'If moderator privileges are enabled, allow users in this group to change user passwords.',
+'Mod promote users label' => 'Allow moderators to promote users',
+'Mod promote users help' => 'If moderator privileges are enabled, allow users in this group to promote users.',
+'Ban users label' => 'Allow moderators to ban users',
+'Ban users help' => 'If moderator privileges are enabled, allow users in this group to ban users.',
+'Read board label' => 'Read board',
+'Read board help' => 'Allow users in this group to view the board. This setting applies to every aspect of the board and can therefore not be overridden by forum specific settings. If this is set to "No", users in this group will only be able to login/logout and register.',
+'View user info label' => 'View user information',
+'View user info help' => 'Allow users to view the user list and user profiles.',
+'Post replies label' => 'Post replies',
+'Post replies help' => 'Allow users in this group to post replies in topics.',
+'Post topics label' => 'Post topics',
+'Post topics help' => 'Allow users in this group to post new topics.',
+'Edit posts label' => 'Edit posts',
+'Edit posts help' => 'Allow users in this group to edit their own posts.',
+'Delete posts label' => 'Delete posts',
+'Delete posts help' => 'Allow users in this group to delete their own posts.',
+'Delete topics label' => 'Delete topics',
+'Delete topics help' => 'Allow users in this group to delete their own topics (including any replies).',
+'Post links label' => 'Post links',
+'Post links help' => 'Allow users in this group to include links in their posts. This setting also applies to signatures and the website field in users\' profiles.',
+'Set own title label' => 'Set own user title',
+'Set own title help' => 'Allow users in this group to set their own user title.',
+'User search label' => 'Use search',
+'User search help' => 'Allow users in this group to use the search feature.',
+'User list search label' => 'Search user list',
+'User list search help' => 'Allow users in this group to freetext search for users in the user list.',
+'Send e-mails label' => 'Send e-mails',
+'Send e-mails help' => 'Allow users in this group to send e-mails to other users.',
+'Post flood label' => 'Post flood interval',
+'Post flood help' => 'Number of seconds that users in this group have to wait between posts. Set to 0 to disable.',
+'Search flood label' => 'Search flood interval',
+'Search flood help' => 'Number of seconds that users in this group have to wait between searches. Set to 0 to disable.',
+'E-mail flood label' => 'Email flood interval',
+'E-mail flood help' => 'Number of seconds that users in this group have to wait between emails. Set to 0 to disable.',
+'Report flood label' => 'Report flood interval',
+'Report flood help' => 'Number of seconds that users in this group have to wait between reports. Set to 0 to disable.',
+'Moderator info' => 'Please note that in order for a user in this group to have moderator abilities, he/she must be assigned to moderate one or more forums. This is done via the user administration page of the user\'s profile.',
+
+);
diff --git a/lang/English/admin_index.php b/lang/English/admin_index.php
new file mode 100644
index 0000000..81208f9
--- /dev/null
+++ b/lang/English/admin_index.php
@@ -0,0 +1,63 @@
+<?php
+
+// Language definitions used in admin_index.php
+$lang_admin_index = array(
+
+'fopen disabled message' => 'Unable to check for upgrade since \'allow_url_fopen\' is disabled on this system.',
+'Upgrade check failed message' => 'Check for upgrade failed for unknown reasons.',
+'Running latest version message' => 'You are running the latest version of FluxBB.',
+'New version available message' => 'A new version of FluxBB has been released. You can download the latest version at %s.',
+'Deleted install.php redirect' => 'The file was successfully removed. Redirecting …',
+'Delete install.php failed' => 'Could not remove install.php. Please do so by hand.',
+'Not available' => 'Not available',
+'Forum admin head' => 'Forum administration',
+'NA' => 'N/A',
+'Welcome to admin' => 'Welcome to the FluxBB administration control panel. From here you can control vital aspects of the board. Depending on whether you are an administrator or a moderator you can:',
+'Welcome 1' => 'Organize categories and forums.',
+'Welcome 2' => 'Set forum-wide options and preferences.',
+'Welcome 3' => 'Control permissions for users and guests.',
+'Welcome 4' => 'View IP statistics for users.',
+'Welcome 5' => 'Ban users.',
+'Welcome 6' => 'Censor words.',
+'Welcome 7' => 'Set up user groups and promotions.',
+'Welcome 8' => 'Prune old posts.',
+'Welcome 9' => 'Handle post reports.',
+'Alerts head' => 'Alerts',
+'Install file exists' => 'The file install.php still exists, but should be removed. %s.',
+'Delete install file' => 'Delete it',
+'About head' => 'About FluxBB',
+'FluxBB version label' => 'FluxBB version',
+'Check for upgrade' => 'Check for upgrade',
+'FluxBB version data' => 'v%s - %s',
+'Server statistics label' => 'Server statistics',
+'View server statistics' => 'View server statistics',
+'Support label' => 'Support',
+'Forum label' => 'Forum',
+'IRC label' => 'IRC channel',
+
+// Language definitions used in admin_statistics.php
+'PHPinfo disabled message' => 'The PHP function phpinfo() has been disabled on this server.',
+'Server statistics head' => 'Server statistics',
+'Server load label' => 'Server load',
+'Server load data' => '%s - %s user(s) online',
+'Environment label' => 'Environment',
+'Environment data OS' => 'Operating system: %s',
+'Show info' => 'Show info',
+'Environment data version' => 'PHP: %s - %s',
+'Environment data acc' => 'Accelerator: %s',
+'Turck MMCache' => 'Turck MMCache',
+'Turck MMCache link' => 'turck-mmcache.sourceforge.net/',
+'ionCube PHP Accelerator' => 'ionCube PHP Accelerator',
+'ionCube PHP Accelerator link' => 'www.php-accelerator.co.uk/',
+'Alternative PHP Cache (APC)' => 'Alternative PHP Cache (APC)',
+'Alternative PHP Cache (APC) link' => 'www.php.net/apc/',
+'Zend Optimizer' => 'Zend Optimizer',
+'Zend Optimizer link' => 'www.zend.com/products/guard/zend-optimizer/',
+'eAccelerator' => 'eAccelerator',
+'eAccelerator link' => 'www.eaccelerator.net/',
+'XCache' => 'XCache',
+'XCache link' => 'xcache.lighttpd.net/',
+'Database label' => 'Database',
+'Database data rows' => 'Rows: %s',
+'Database data size' => 'Size: %s',
+);
diff --git a/lang/English/admin_maintenance.php b/lang/English/admin_maintenance.php
new file mode 100644
index 0000000..b7a9ce4
--- /dev/null
+++ b/lang/English/admin_maintenance.php
@@ -0,0 +1,40 @@
+<?php
+
+// Language definitions used in admin_maintenance.php
+$lang_admin_maintenance = array(
+
+'Maintenance head' => 'Forum maintenance',
+'Rebuild index subhead' => 'Rebuild search index',
+'Rebuild index info' => 'If you\'ve added, edited or removed posts manually in the database or if you\'re having problems searching, you should rebuild the search index. For best performance, you should put the forum in %s during rebuilding. <strong>Rebuilding the search index can take a long time and will increase server load during the rebuild process!</strong>',
+'Posts per cycle label' => 'Posts per cycle',
+'Posts per cycle help' => 'The number of posts to process per pageview. E.g. if you were to enter 300, three hundred posts would be processed and then the page would refresh. This is to prevent the script from timing out during the rebuild process.',
+'Starting post label' => 'Starting post ID',
+'Starting post help' => 'The post ID to start rebuilding at. The default value is the first available ID in the database. Normally you wouldn\'t want to change this.',
+'Empty index label' => 'Empty index',
+'Empty index help' => 'Select this if you want the search index to be emptied before rebuilding (see below).',
+'Rebuild completed info' => 'Once the process has completed, you will be redirected back to this page. If you are forced to abort the rebuild process, make a note of the last processed post ID and enter that ID+1 in "Starting post ID" when/if you want to continue ("Empty index" must not be selected).',
+'Rebuild index' => 'Rebuild index',
+'Rebuilding search index' => 'Rebuilding search index',
+'Rebuilding index info' => 'Rebuilding index. This might be a good time to put on some coffee :-)',
+'Processing post' => 'Processing post <strong>%s</strong> …',
+'Click here' => 'Click here',
+'Javascript redirect failed' => 'Automatic redirect unsuccessful. %s to continue …',
+'Posts must be integer message' => 'Posts per cycle must be a positive integer value.',
+'Days must be integer message' => 'Days to prune must be a positive integer value.',
+'No old topics message' => 'There are no topics that are %s days old. Please decrease the value of "Days old" and try again.',
+'Posts pruned redirect' => 'Posts pruned. Redirecting …',
+'Prune head' => 'Prune',
+'Prune subhead' => 'Prune old posts',
+'Days old label' => 'Days old',
+'Days old help' => 'The number of days "old" a topic must be to be pruned. E.g. if you were to enter 30, every topic that didn\'t contain a post dated less than 30 days old would be deleted.',
+'Prune sticky label' => 'Prune sticky topics',
+'Prune sticky help' => 'When enabled, sticky topics will also be pruned.',
+'Prune from label' => 'Prune from forum',
+'All forums' => 'All forums',
+'Prune from help' => 'The forum from which you want to prune posts.',
+'Prune info' => 'Use this feature with caution. <strong>Pruned posts can never be recovered.</strong> For best performance, you should put the forum in %s during pruning.',
+'Confirm prune subhead' => 'Confirm prune posts',
+'Confirm prune info' => 'Are you sure that you want to prune all topics older than %s days from %s (%s topics).',
+'Confirm prune warn' => 'WARNING! Pruning posts deletes them permanently.',
+
+);
diff --git a/lang/English/admin_options.php b/lang/English/admin_options.php
new file mode 100644
index 0000000..bed8177
--- /dev/null
+++ b/lang/English/admin_options.php
@@ -0,0 +1,227 @@
+<?php
+
+// Language definitions used in admin_options.php
+$lang_admin_options = array(
+
+'Bad HTTP Referer message' => 'Bad HTTP_REFERER. If you have moved these forums from one location to another or switched domains, you need to update the Base URL manually in the database (look for o_base_url in the config table) and then clear the cache by deleting all .php files in the /cache directory.',
+'Must enter title message' => 'You must enter a board title.',
+'Invalid e-mail message' => 'The admin email address you entered is invalid.',
+'Invalid webmaster e-mail message' => 'The webmaster email address you entered is invalid.',
+'SMTP passwords did not match' => 'You need to enter the SMTP password twice exactly the same to change it.',
+'Enter announcement here' => 'Enter your announcement here.',
+'Enter rules here' => 'Enter your rules here.',
+'Default maintenance message' => 'The forums are temporarily down for maintenance. Please try again in a few minutes.',
+'Timeout error message' => 'The value of "Timeout online" must be smaller than the value of "Timeout visit".',
+'Options updated redirect' => 'Options updated. Redirecting …',
+'Options head' => 'Options',
+
+// Essentials section
+'Essentials subhead' => 'Essentials',
+'Board title label' => 'Board title',
+'Board title help' => 'The title of this bulletin board (shown at the top of every page). This field may <strong>not</strong> contain HTML.',
+'Board desc label' => 'Board description',
+'Board desc help' => 'A short description of this bulletin board (shown at the top of every page). This field may contain HTML.',
+'Base URL label' => 'Base URL',
+'Base URL help' => 'The complete URL of the board without trailing slash (i.e. http://www.mydomain.com/forums). This <strong>must</strong> be correct in order for all admin and moderator features to work. If you get "Bad referer" errors, it\'s probably incorrect.',
+'Base URL problem' => 'Your installation does not support automatic conversion of internationalized domain names. As your base URL contains special characters, you <strong>must</strong> use an online converter in order to avoid "Bad referer" errors.',
+'Timezone label' => 'Default time zone',
+'Timezone help' => 'The default time zone for guests and users attempting to register for the board.',
+'DST label' => 'Adjust for DST',
+'DST help' => 'Check if daylight savings is in effect (advances times by 1 hour).',
+'Language label' => 'Default language',
+'Language help' => 'The default language for guests and users who haven\'t changed from the default in their profile. If you remove a language pack, this must be updated.',
+'Default style label' => 'Default style',
+'Default style help' => 'The default style for guests and users who haven\'t changed from the default in their profile.',
+
+// Essentials section timezone options
+'UTC-12:00' => '(UTC-12:00) International Date Line West',
+'UTC-11:00' => '(UTC-11:00) Niue, Samoa',
+'UTC-10:00' => '(UTC-10:00) Hawaii-Aleutian, Cook Island',
+'UTC-09:30' => '(UTC-09:30) Marquesas Islands',
+'UTC-09:00' => '(UTC-09:00) Alaska, Gambier Island',
+'UTC-08:30' => '(UTC-08:30) Pitcairn Islands',
+'UTC-08:00' => '(UTC-08:00) Pacific',
+'UTC-07:00' => '(UTC-07:00) Mountain',
+'UTC-06:00' => '(UTC-06:00) Central',
+'UTC-05:00' => '(UTC-05:00) Eastern',
+'UTC-04:00' => '(UTC-04:00) Atlantic',
+'UTC-03:30' => '(UTC-03:30) Newfoundland',
+'UTC-03:00' => '(UTC-03:00) Amazon, Central Greenland',
+'UTC-02:00' => '(UTC-02:00) Mid-Atlantic',
+'UTC-01:00' => '(UTC-01:00) Azores, Cape Verde, Eastern Greenland',
+'UTC' => '(UTC) Western European, Greenwich',
+'UTC+01:00' => '(UTC+01:00) Central European, West African',
+'UTC+02:00' => '(UTC+02:00) Eastern European, Central African',
+'UTC+03:00' => '(UTC+03:00) Eastern African',
+'UTC+03:30' => '(UTC+03:30) Iran',
+'UTC+04:00' => '(UTC+04:00) Moscow, Gulf, Samara',
+'UTC+04:30' => '(UTC+04:30) Afghanistan',
+'UTC+05:00' => '(UTC+05:00) Pakistan',
+'UTC+05:30' => '(UTC+05:30) India, Sri Lanka',
+'UTC+05:45' => '(UTC+05:45) Nepal',
+'UTC+06:00' => '(UTC+06:00) Bangladesh, Bhutan, Yekaterinburg',
+'UTC+06:30' => '(UTC+06:30) Cocos Islands, Myanmar',
+'UTC+07:00' => '(UTC+07:00) Indochina, Novosibirsk',
+'UTC+08:00' => '(UTC+08:00) Greater China, Australian Western, Krasnoyarsk',
+'UTC+08:45' => '(UTC+08:45) Southeastern Western Australia',
+'UTC+09:00' => '(UTC+09:00) Japan, Korea, Chita, Irkutsk',
+'UTC+09:30' => '(UTC+09:30) Australian Central',
+'UTC+10:00' => '(UTC+10:00) Australian Eastern',
+'UTC+10:30' => '(UTC+10:30) Lord Howe',
+'UTC+11:00' => '(UTC+11:00) Solomon Island, Vladivostok',
+'UTC+11:30' => '(UTC+11:30) Norfolk Island',
+'UTC+12:00' => '(UTC+12:00) New Zealand, Fiji, Magadan',
+'UTC+12:45' => '(UTC+12:45) Chatham Islands',
+'UTC+13:00' => '(UTC+13:00) Tonga, Phoenix Islands, Kamchatka',
+'UTC+14:00' => '(UTC+14:00) Line Islands',
+
+// Timeout Section
+'Timeouts subhead' => 'Time and timeouts',
+'Time format label' => 'Time format',
+'PHP manual' => 'PHP manual',
+'Time format help' => '[Current format: %s]. See %s for formatting options.',
+'Date format label' => 'Date format',
+'Date format help' => '[Current format: %s]. See %s for formatting options.',
+'Visit timeout label' => 'Visit timeout',
+'Visit timeout help' => 'Number of seconds a user must be idle before his/hers last visit data is updated (primarily affects new message indicators).',
+'Online timeout label' => 'Online timeout',
+'Online timeout help' => 'Number of seconds a user must be idle before being removed from the online users list.',
+'Redirect time label' => 'Redirect time',
+'Redirect time help' => 'Number of seconds to wait when redirecting. If set to 0, no redirect page will be displayed (not recommended).',
+
+// Display Section
+'Display subhead' => 'Display',
+'Version number label' => 'Version number',
+'Version number help' => 'Show FluxBB version number in footer.',
+'Info in posts label' => 'User info in posts',
+'Info in posts help' => 'Show information about the poster under the username in topic view. The information affected is location, register date, post count and the contact links (email and URL).',
+'Post count label' => 'User post count',
+'Post count help' => 'Show the number of posts a user has made (affects topic view, profile and user list).',
+'Smilies label' => 'Smilies in posts',
+'Smilies help' => 'Convert smilies to small graphic icons.',
+'Smilies sigs label' => 'Smilies in signatures',
+'Smilies sigs help' => 'Convert smilies to small graphic icons in user signatures.',
+'Clickable links label' => 'Make clickable links',
+'Clickable links help' => 'When enabled, FluxBB will automatically detect any URLs in posts and make them clickable hyperlinks.',
+'Topic review label' => 'Topic review',
+'Topic review help' => 'Maximum number of posts to display when posting (newest first). Set to 0 to disable.',
+'Topics per page label' => 'Topics per page',
+'Topics per page help' => 'The default number of topics to display per page in a forum. Users can personalize this setting.',
+'Posts per page label' => 'Posts per page',
+'Posts per page help' => 'The default number of posts to display per page in a topic. Users can personalize this setting.',
+'Indent label' => 'Indent size',
+'Indent help' => 'If set to 8, a regular tab will be used when displaying text within the [code][/code] tag. Otherwise this many spaces will be used to indent the text.',
+'Quote depth label' => 'Maximum [quote] depth',
+'Quote depth help' => 'The maximum times a [quote] tag can go inside other [quote] tags, any tags deeper than this will be discarded.',
+
+// Features section
+'Features subhead' => 'Features',
+'Quick post label' => 'Quick reply',
+'Quick post help' => 'When enabled, FluxBB will add a quick reply form at the bottom of topics. This way users can post directly from the topic view.',
+'Users online label' => 'Users online',
+'Users online help' => 'Display info on the index page about guests and registered users currently browsing the board.',
+'Censor words label' => 'Censor words',
+'Censor words help' => 'Enable this to censor specific words in the board. See %s for more info.',
+'Signatures label' => 'Signatures',
+'Signatures help' => 'Allow users to attach a signature to their posts.',
+'User has posted label' => 'User has posted earlier',
+'User has posted help' => 'This feature displays a dot in front of topics in viewforum.php in case the currently logged in user has posted in that topic earlier. Disable if you are experiencing high server load.',
+'Topic views label' => 'Topic views',
+'Topic views help' => 'Keep track of the number of views a topic has. Disable if you are experiencing high server load in a busy forum.',
+'Quick jump label' => 'Quick jump',
+'Quick jump help' => 'Enable the quick jump (jump to forum) drop list.',
+'GZip label' => 'GZip output',
+'GZip help' => 'If enabled, FluxBB will gzip the output sent to browsers. This will reduce bandwidth usage, but use a little more CPU. This feature requires that PHP is configured with zlib (--with-zlib). Note: If you already have one of the Apache modules mod_gzip or mod_deflate set up to compress PHP scripts, you should disable this feature.',
+'Search all label' => 'Search all forums',
+'Search all help' => 'When disabled, searches will only be allowed in one forum at a time. Disable if server load is high due to excessive searching.',
+'Menu items label' => 'Additional menu items',
+'Menu items help' => 'By entering HTML hyperlinks into this textbox, any number of items can be added to the navigation menu at the top of all pages. The format for adding new links is X = &lt;a href="URL"&gt;LINK&lt;/a&gt; where X is the position at which the link should be inserted (e.g. 0 to insert at the beginning and 2 to insert after "User list"). Separate entries with a linebreak.',
+
+// Feeds section
+'Feed subhead' => 'Syndication',
+'Default feed label' => 'Default feed type',
+'Default feed help' => 'Select the type of syndication feed to display. Note: Choosing none will not disable feeds, only hide them by default.',
+'None' => 'None',
+'RSS' => 'RSS',
+'Atom' => 'Atom',
+'Feed TTL label' => 'Duration to cache feeds',
+'Feed TTL help' => 'Feeds can be cached to lower the resource usage of feeds.',
+'No cache' => 'Don\'t cache',
+'Minutes' => '%d minutes',
+
+// Reports section
+'Reports subhead' => 'Reports',
+'Reporting method label' => 'Reporting method',
+'Internal' => 'Internal',
+'By e-mail' => 'Email',
+'Both' => 'Both',
+'Reporting method help' => 'Select the method for handling topic/post reports. You can choose whether topic/post reports should be handled by the internal report system, emailed to the addresses on the mailing list (see below) or both.',
+'Mailing list label' => 'Mailing list',
+'Mailing list help' => 'A comma separated list of subscribers. The people on this list are the recipients of reports.',
+
+// Avatars section
+'Avatars subhead' => 'Avatars',
+'Use avatars label' => 'Use avatars',
+'Use avatars help' => 'When enabled, users will be able to upload an avatar which will be displayed under their title.',
+'Upload directory label' => 'Upload directory',
+'Upload directory help' => 'The upload directory for avatars (relative to the FluxBB root directory). PHP must have write permissions to this directory.',
+'Max width label' => 'Max width',
+'Max width help' => 'The maximum allowed width of avatars in pixels (60 is recommended).',
+'Max height label' => 'Max height',
+'Max height help' => 'The maximum allowed height of avatars in pixels (60 is recommended).',
+'Max size label' => 'Max size',
+'Max size help' => 'The maximum allowed size of avatars in bytes (10240 is recommended).',
+
+// E-mail section
+'E-mail subhead' => 'Email',
+'Admin e-mail label' => 'Admin email',
+'Admin e-mail help' => 'The email address of the board administrator.',
+'Webmaster e-mail label' => 'Webmaster email',
+'Webmaster e-mail help' => 'This is the address that all emails sent by the board will be addressed from.',
+'Forum subscriptions label' => 'Forum subscriptions',
+'Forum subscriptions help' => 'Enable users to subscribe to forums (receive email when someone creates a new topic).',
+'Topic subscriptions label' => 'Topic subscriptions',
+'Topic subscriptions help' => 'Enable users to subscribe to topics (receive email when someone replies).',
+'SMTP address label' => 'SMTP server address',
+'SMTP address help' => 'The address of an external SMTP server to send emails with. You can specify a custom port number if the SMTP server doesn\'t run on the default port 25 (example: mail.myhost.com:3580). Leave blank to use the local mail program.',
+'SMTP username label' => 'SMTP username',
+'SMTP username help' => 'Username for SMTP server. Only enter a username if it is required by the SMTP server (most servers <strong>do not</strong> require authentication).',
+'SMTP password label' => 'SMTP password',
+'SMTP change password help' => 'Check this if you want to change or delete the currently stored password.',
+'SMTP password help' => 'Password for SMTP server. Only enter a password if it is required by the SMTP server (most servers <strong>do not</strong> require authentication). Please enter your password twice to confirm.',
+'SMTP SSL label' => 'Encrypt SMTP using SSL',
+'SMTP SSL help' => 'Encrypts the connection to the SMTP server using SSL. Should only be used if your SMTP server requires it and your version of PHP supports SSL.',
+
+// Registration Section
+'Registration subhead' => 'Registration',
+'Allow new label' => 'Allow new registrations',
+'Allow new help' => 'Controls whether this board accepts new registrations. Disable only under special circumstances.',
+'Verify label' => 'Verify registrations',
+'Verify help' => 'When enabled, users are emailed a random password when they register. They can then log in and change the password in their profile if they see fit. This feature also requires users to verify new email addresses if they choose to change from the one they registered with. This is an effective way of avoiding registration abuse and making sure that all users have "correct" email addresses in their profiles.',
+'Report new label' => 'Report new registrations',
+'Report new help' => 'If enabled, FluxBB will notify users on the mailing list (see above) when a new user registers in the forums.',
+'Use rules label' => 'User forum rules',
+'Use rules help' => 'When enabled, users must agree to a set of rules when registering (enter text below). The rules will always be available through a link in the navigation table at the top of every page.',
+'Rules label' => 'Enter your rules here',
+'Rules help' => 'Here you can enter any rules or other information that the user must review and accept when registering. If you enabled rules above you have to enter something here, otherwise it will be disabled. This text will not be parsed like regular posts and thus may contain HTML.',
+'E-mail default label' => 'Default email setting',
+'E-mail default help' => 'Choose the default privacy setting for new user registrations.',
+'Display e-mail label' => 'Display email address to other users.',
+'Hide allow form label' => 'Hide email address but allow form e-mail.',
+'Hide both label' => 'Hide email address and disallow form email.',
+
+// Announcement Section
+'Announcement subhead' => 'Announcements',
+'Display announcement label' => 'Display announcement',
+'Display announcement help' => 'Enable this to display the below message in the board.',
+'Announcement message label' => 'Announcement message',
+'Announcement message help' => 'This text will not be parsed like regular posts and thus may contain HTML.',
+
+// Maintenance Section
+'Maintenance subhead' => 'Maintenance',
+'Maintenance mode label' => 'Maintenance mode',
+'Maintenance mode help' => 'When enabled, the board will only be available to administrators. This should be used if the board needs to be taken down temporarily for maintenance. <strong>WARNING! Do not log out when the board is in maintenance mode.</strong> You will not be able to login again.',
+'Maintenance message label' => 'Maintenance message',
+'Maintenance message help' => 'The message that will be displayed to users when the board is in maintenance mode. If left blank, a default message will be used. This text will not be parsed like regular posts and thus may contain HTML.',
+
+);
diff --git a/lang/English/admin_permissions.php b/lang/English/admin_permissions.php
new file mode 100644
index 0000000..8324eda
--- /dev/null
+++ b/lang/English/admin_permissions.php
@@ -0,0 +1,36 @@
+<?php
+
+// Language definitions used in admin_permissions.php
+$lang_admin_permissions = array(
+
+'Perms updated redirect' => 'Permissions updated. Redirecting …',
+'Permissions head' => 'Permissions',
+'Posting subhead' => 'Posting',
+'BBCode label' => 'BBCode',
+'BBCode help' => 'Allow BBCode in posts (recommended).',
+'Image tag label' => 'Image tag',
+'Image tag help' => 'Allow the BBCode [img][/img] tag in posts.',
+'All caps message label' => 'All caps message',
+'All caps message help' => 'Allow a message to contain only capital letters.',
+'All caps subject label' => 'All caps subject',
+'All caps subject help' => 'Allow a subject to contain only capital letters.',
+'Require e-mail label' => 'Require guest email',
+'Require e-mail help' => 'Require guests to supply an email address when posting.',
+'Signatures subhead' => 'Signatures',
+'BBCode sigs label' => 'BBCodes in signatures',
+'BBCode sigs help' => 'Allow BBCodes in user signatures.',
+'Image tag sigs label' => 'Image tag in signatures',
+'Image tag sigs help' => 'Allow the BBCode [img][/img] tag in user signatures (not recommended).',
+'All caps sigs label' => 'All caps signature',
+'All caps sigs help' => 'Allow a signature to contain only capital letters.',
+'Max sig length label' => 'Maximum signature length',
+'Max sig length help' => 'The maximum number of characters a user signature may contain.',
+'Max sig lines label' => 'Maximum signature lines',
+'Max sig lines help' => 'The maximum number of lines a user signature may contain.',
+'Registration subhead' => 'Registration',
+'Banned e-mail label' => 'Allow banned email addresses',
+'Banned e-mail help' => 'Allow users to register with or change to a banned email address/domain. If left at its default setting (yes), this action will be allowed, but an alert email will be sent to the mailing list (an effective way of detecting multiple registrations).',
+'Duplicate e-mail label' => 'Allow duplicate email addresses',
+'Duplicate e-mail help' => 'Controls whether users should be allowed to register with an email address that another user already has. If allowed, an alert email will be sent to the mailing list if a duplicate is detected.',
+
+);
diff --git a/lang/English/admin_reports.php b/lang/English/admin_reports.php
new file mode 100644
index 0000000..0daa8da
--- /dev/null
+++ b/lang/English/admin_reports.php
@@ -0,0 +1,21 @@
+<?php
+
+// Language definitions used in admin_reports.php
+$lang_admin_reports = array(
+
+'Report zapped redirect' => 'Report marked as read. Redirecting …',
+'New reports head' => 'New reports',
+'Deleted user' => 'Deleted user',
+'Deleted' => 'Deleted',
+'Post ID' => 'Post #%s',
+'Report subhead' => 'Reported %s',
+'Reported by' => 'Reported by %s',
+'Reason' => 'Reason',
+'Zap' => 'Mark as read',
+'No new reports' => 'There are no new reports.',
+'Last 10 head' => '10 last read reports',
+'NA' => 'N/A',
+'Zapped subhead' => 'Marked as read %s by %s',
+'No zapped reports' => 'There are no read reports.',
+
+);
diff --git a/lang/English/admin_users.php b/lang/English/admin_users.php
new file mode 100644
index 0000000..66280b7
--- /dev/null
+++ b/lang/English/admin_users.php
@@ -0,0 +1,110 @@
+<?php
+
+// Language definitions used in admin_users.php
+$lang_admin_users = array(
+
+'Non numeric message' => 'You entered a non-numeric value into a numeric only column.',
+'Invalid date time message' => 'You entered an invalid date/time.',
+'Not verified' => 'Not verified',
+
+// Actions: mass delete/ban etc.
+'No users selected' => 'No users selected.',
+'No move admins message' => 'For security reasons, you are not allowed to move multiple administrators to another group. If you want to move these administrators, you can do so on their respective user profiles.',
+'No delete admins message' => 'Administrators cannot be deleted. In order to delete administrators, you must first move them to a different user group.',
+'No ban admins message' => 'Administrators cannot be banned. In order to ban administrators, you must first move them to a different user group.',
+'No ban mods message' => 'Moderators cannot be banned. In order to ban moderators, you must first move them to a different user group.',
+'Move users' => 'Change user group',
+'Move users subhead' => 'Select new user group',
+'New group label' => 'New group',
+'New group help' => 'Select the group to which the selected users will be moved. For security reasons, it is not possible to move multiple users to the administrator group.',
+'Invalid group message' => 'Invalid group ID.',
+'Users move redirect' => 'User group changed. Redirecting …',
+'Delete users' => 'Delete users',
+'Confirm delete legend' => 'Important: read before deleting users',
+'Confirm delete info' => 'Please confirm that you want to delete these users.',
+'Delete posts' => 'Delete any posts and topics these users have made.',
+'Delete warning' => 'Warning! Deleted users and/or posts cannot be restored. If you choose not to delete the posts made by these users, the posts can only be deleted manually at a later time.',
+'Users delete redirect' => 'Users deleted. Redirecting …',
+'Ban users' => 'Ban users',
+'Message expiry subhead' => 'Ban message and expiry',
+'Ban message label' => 'Ban message',
+'Ban message help' => 'A message that will be displayed to the banned users when they visit the board.',
+'Expire date label' => 'Expire date',
+'Expire date help' => 'The date when these bans should be automatically removed (format: yyyy-mm-dd). Leave blank to remove manually.',
+'Ban IP label' => 'Ban IP addresses',
+'Ban IP help' => 'Also ban the IP addresses of the banned users to make registering a new account more difficult for them.',
+'Invalid date message' => 'You entered an invalid expire date.',
+'Invalid date reasons' => 'The format should be YYYY-MM-DD and the date must be at least one day in the future.',
+'Users banned redirect' => 'Users banned. Redirecting …',
+
+'User search head' => 'User search',
+'User search subhead' => 'Enter search criteria',
+'User search info' => 'Search for users in the database. You can enter one or more terms to search for. Wildcards in the form of asterisks (*) are accepted.',
+'Username label' => 'Username',
+'E-mail address label' => 'Email address',
+'Title label' => 'Title',
+'Real name label' => 'Real name',
+'Website label' => 'Website',
+'Jabber label' => 'Jabber',
+'ICQ label' => 'ICQ',
+'MSN label' => 'Microsoft Account',
+'AOL label' => 'AOL IM',
+'Yahoo label' => 'Yahoo Messenger',
+'Location label' => 'Location',
+'Signature label' => 'Signature',
+'Admin note label' => 'Admin note',
+'Posts more than label' => 'Number of posts greater than',
+'Posts less than label' => 'Number of posts less than',
+'Last post after label' => 'Last post is after',
+'Date help' => '(yyyy-mm-dd hh:mm:ss)',
+'Last post before label' => 'Last post is before',
+'Last visit after label' => 'Last visit is after',
+'Last visit before label' => 'Last visit is before',
+'Registered after label' => 'Registered after',
+'Registered before label' => 'Registered before',
+'Order by label' => 'Order by',
+'Order by username' => 'Username',
+'Order by e-mail' => 'Email',
+'Order by posts' => 'Number of posts',
+'Order by last post' => 'Last post',
+'Order by last visit' => 'Last visit',
+'Order by registered' => 'Registered',
+'Ascending' => 'Ascending',
+'Descending' => 'Descending',
+'User group label' => 'User group',
+'All groups' => 'All groups',
+'Unverified users' => 'Unverified users',
+'Submit search' => 'Submit search',
+'IP search head' => 'IP search',
+'IP search subhead' => 'Enter IP to search for',
+'IP address label' => 'IP address',
+'IP address help' => 'The IP address to search for in the post database.',
+'Find IP address' => 'Find IP address',
+
+'Results head' => 'Search Results',
+'Results username head' => 'Username',
+'Results e-mail head' => 'Email',
+'Results title head' => 'Title/Status',
+'Results posts head' => 'Posts',
+'Results admin note head' => 'Admin note',
+'Results actions head' => 'Actions',
+'Results IP address head' => 'IP address',
+'Results last used head' => 'Last used',
+'Results times found head' => 'Times found',
+'Results action head' => 'Action',
+'Results find more link' => 'Find more users for this ip',
+'Results no posts found' => 'There are currently no posts by that user in the forum.',
+'Select' => 'Select',
+'Select all' => 'Select all',
+'Unselect all' => 'Unselect all',
+'Ban' => 'Ban',
+'Delete' => 'Delete',
+'Change group' => 'Change group',
+'Bad IP message' => 'The supplied IP address is not correctly formatted.',
+'Results view IP link' => 'View IP stats',
+'Results show posts link' => 'Show posts',
+'Results guest' => 'Guest',
+'Results no IP found' => 'The supplied IP address could not be found in the database.',
+'No match' => 'No match'
+
+);
diff --git a/lang/English/common.php b/lang/English/common.php
new file mode 100644
index 0000000..6f5ece2
--- /dev/null
+++ b/lang/English/common.php
@@ -0,0 +1,169 @@
+<?php
+
+// Language definitions for frequently used strings
+$lang_common = array(
+
+// Text orientation and encoding
+'lang_direction' => 'ltr', // ltr (Left-To-Right) or rtl (Right-To-Left)
+'lang_identifier' => 'en',
+
+// Number formatting
+'lang_decimal_point' => '.',
+'lang_thousands_sep' => ',',
+
+// Notices
+'Bad request' => 'Bad request. The link you followed is incorrect or outdated.',
+'No view' => 'You do not have permission to view these forums.',
+'No permission' => 'You do not have permission to access this page.',
+'Bad referrer' => 'Bad HTTP_REFERER. You were referred to this page from an unauthorized source. If the problem persists please make sure that \'Base URL\' is correctly set in Admin/Options and that you are visiting the forum by navigating to that URL. More information regarding the referrer check can be found in the FluxBB documentation.',
+'Bad csrf hash' => 'Bad CSRF hash. You were referred to this page from an unauthorized source.',
+'No cookie' => 'You appear to have logged in successfully, however a cookie has not been set. Please check your settings and if applicable, enable cookies for this website.',
+'Pun include extension' => 'Unable to process user include %s from template %s. "%s" files are not allowed',
+'Pun include directory' => 'Unable to process user include %s from template %s. Directory traversal is not allowed',
+'Pun include error' => 'Unable to process user include %s from template %s. There is no such file in neither the template directory nor in the user include directory',
+
+// Miscellaneous
+'Announcement' => 'Announcement',
+'Options' => 'Options',
+'Submit' => 'Submit', // "Name" of submit buttons
+'Ban message' => 'You are banned from this forum.',
+'Ban message 2' => 'The ban expires at the end of',
+'Ban message 3' => 'The administrator or moderator that banned you left the following message:',
+'Ban message 4' => 'Please direct any inquiries to the forum administrator at',
+'Never' => 'Never',
+'Today' => 'Today',
+'Yesterday' => 'Yesterday',
+'Info' => 'Info', // A common table header
+'Go back' => 'Go back',
+'Maintenance' => 'Maintenance',
+'Redirecting' => 'Redirecting',
+'Click redirect' => 'Click here if you do not want to wait any longer (or if your browser does not automatically forward you)',
+'on' => 'on', // As in "BBCode is on"
+'off' => 'off',
+'Invalid email' => 'The email address you entered is invalid.',
+'Required' => '(Required)',
+'required field' => 'is a required field in this form.', // For javascript form validation
+'Last post' => 'Last post',
+'by' => 'by', // As in last post by some user
+'New posts' => 'New posts', // The link that leads to the first new post
+'New posts info' => 'Go to the first new post in this topic.', // The popup text for new posts links
+'Username' => 'Username',
+'Password' => 'Password',
+'Email' => 'Email',
+'Send email' => 'Send email',
+'Moderated by' => 'Moderated by',
+'Registered' => 'Registered',
+'Subject' => 'Subject',
+'Message' => 'Message',
+'Topic' => 'Topic',
+'Forum' => 'Forum',
+'Posts' => 'Posts',
+'Replies' => 'Replies',
+'Pages' => 'Pages:',
+'Page' => 'Page %s',
+'BBCode' => 'BBCode:', // You probably shouldn't change this
+'url tag' => '[url] tag:',
+'img tag' => '[img] tag:',
+'Smilies' => 'Smilies:',
+'and' => 'and',
+'Image link' => 'image', // This is displayed (i.e. <image>) instead of images when "Show images" is disabled in the profile
+'wrote' => 'wrote:', // For [quote]'s
+'Mailer' => '%s Mailer', // As in "MyForums Mailer" in the signature of outgoing emails
+'Important information' => 'Important information',
+'Write message legend' => 'Write your message and submit',
+'Previous' => 'Previous',
+'Next' => 'Next',
+'Spacer' => '…', // Ellipsis for paginate
+
+// Title
+'Title' => 'Title',
+'Member' => 'Member', // Default title
+'Moderator' => 'Moderator',
+'Administrator' => 'Administrator',
+'Banned' => 'Banned',
+'Guest' => 'Guest',
+
+// Stuff for include/parser.php
+'BBCode error no opening tag' => '[/%1$s] was found without a matching [%1$s]',
+'BBCode error invalid nesting' => '[%1$s] was opened within [%2$s], this is not allowed',
+'BBCode error invalid self-nesting' => '[%s] was opened within itself, this is not allowed',
+'BBCode error no closing tag' => '[%1$s] was found without a matching [/%1$s]',
+'BBCode error empty attribute' => '[%s] tag had an empty attribute section',
+'BBCode error tag not allowed' => 'You are not allowed to use [%s] tags',
+'BBCode error tag url not allowed' => 'You are not allowed to post links',
+'BBCode list size error' => 'Your list was too long to parse, please make it smaller!',
+
+// Stuff for the navigator (top of every page)
+'Index' => 'Index',
+'User list' => 'User list',
+'Rules' => 'Rules',
+'Search' => 'Search',
+'Register' => 'Register',
+'Login' => 'Login',
+'Not logged in' => 'You are not logged in.',
+'Profile' => 'Profile',
+'Logout' => 'Logout',
+'Logged in as' => 'Logged in as',
+'Admin' => 'Administration',
+'Last visit' => 'Last visit: %s',
+'Topic searches' => 'Topics:',
+'New posts header' => 'New',
+'Active topics' => 'Active',
+'Unanswered topics' => 'Unanswered',
+'Posted topics' => 'Posted',
+'Show new posts' => 'Find topics with new posts since your last visit.',
+'Show active topics' => 'Find topics with recent posts.',
+'Show unanswered topics' => 'Find topics with no replies.',
+'Show posted topics' => 'Find topics you have posted to.',
+'Mark all as read' => 'Mark all topics as read',
+'Mark forum read' => 'Mark this forum as read',
+'Title separator' => ' / ',
+
+// Stuff for the page footer
+'Board footer' => 'Board footer',
+'Jump to' => 'Jump to',
+'Go' => ' Go ', // Submit button in forum jump
+'Moderate topic' => 'Moderate topic',
+'All' => 'All',
+'Move topic' => 'Move topic',
+'Open topic' => 'Open topic',
+'Close topic' => 'Close topic',
+'Unstick topic' => 'Unstick topic',
+'Stick topic' => 'Stick topic',
+'Moderate forum' => 'Moderate forum',
+'Powered by' => 'Powered by %s',
+
+// Debug information
+'Debug table' => 'Debug information',
+'Querytime' => 'Generated in %1$s seconds, %2$s queries executed',
+'Memory usage' => 'Memory usage: %1$s',
+'Peak usage' => '(Peak: %1$s)',
+'Query times' => 'Time (s)',
+'Query' => 'Query',
+'Total query time' => 'Total query time: %s',
+
+// For extern.php RSS feed
+'RSS description' => 'The most recent topics at %s.',
+'RSS description topic' => 'The most recent posts in %s.',
+'RSS reply' => 'Re: ', // The topic subject will be appended to this string (to signify a reply)
+'RSS active topics feed' => 'RSS active topics feed',
+'Atom active topics feed' => 'Atom active topics feed',
+'RSS forum feed' => 'RSS forum feed',
+'Atom forum feed' => 'Atom forum feed',
+'RSS topic feed' => 'RSS topic feed',
+'Atom topic feed' => 'Atom topic feed',
+
+// Admin related stuff in the header
+'New reports' => 'There are new reports',
+'Maintenance mode enabled' => 'Maintenance mode is enabled!',
+
+// Units for file sizes
+'Size unit B' => '%s B',
+'Size unit KiB' => '%s KiB',
+'Size unit MiB' => '%s MiB',
+'Size unit GiB' => '%s GiB',
+'Size unit TiB' => '%s TiB',
+'Size unit PiB' => '%s PiB',
+'Size unit EiB' => '%s EiB',
+
+);
diff --git a/lang/English/copyable_captcha.php b/lang/English/copyable_captcha.php
new file mode 100644
index 0000000..c9bc90e
--- /dev/null
+++ b/lang/English/copyable_captcha.php
@@ -0,0 +1,10 @@
+<?php
+
+// Language definitions used in Copyable Captcha plugin
+$lang_copyable_captcha = array (
+'Captcha' => 'Captcha',
+'Captcha legend' => 'Confirm that you\'re not robot',
+'Captcha info' => 'Captcha is valid for 2 min only. Refresh page if needed.<br />Please enter this characters: <strong class="masq">%1$s</strong>',
+'Captcha error' => 'Wrong or expired captcha',
+
+);
diff --git a/lang/English/delete.php b/lang/English/delete.php
new file mode 100644
index 0000000..b97478e
--- /dev/null
+++ b/lang/English/delete.php
@@ -0,0 +1,16 @@
+<?php
+
+// Language definitions used in delete.php
+$lang_delete = array(
+
+'Delete post' => 'Delete post',
+'Warning' => 'You are about to permanently delete this post.',
+'Topic warning' => 'Warning! This is the first post in the topic, the whole topic will be permanently deleted.',
+'Delete info' => 'The post you have chosen to delete is set out below for you to review before proceeding.',
+'Reply by' => 'Reply by %s - %s',
+'Topic by' => 'Topic started by %s - %s',
+'Delete' => 'Delete', // The submit button
+'Post del redirect' => 'Post deleted. Redirecting …',
+'Topic del redirect' => 'Topic deleted. Redirecting …'
+
+);
diff --git a/lang/English/forum.php b/lang/English/forum.php
new file mode 100644
index 0000000..a5973b8
--- /dev/null
+++ b/lang/English/forum.php
@@ -0,0 +1,17 @@
+<?php
+
+// Language definitions used in viewforum.php
+$lang_forum = array(
+
+'Post topic' => 'Post new topic',
+'Views' => 'Views',
+'Moved' => 'Moved:',
+'Sticky' => 'Sticky:',
+'Closed' => 'Closed:',
+'Empty forum' => 'Forum is empty.',
+'Mod controls' => 'Moderator controls',
+'Is subscribed' => 'You are currently subscribed to this forum',
+'Unsubscribe' => 'Unsubscribe',
+'Subscribe' => 'Subscribe to this forum'
+
+);
diff --git a/lang/English/funnyquestion.php b/lang/English/funnyquestion.php
new file mode 100644
index 0000000..740013b
--- /dev/null
+++ b/lang/English/funnyquestion.php
@@ -0,0 +1,8 @@
+<?php
+
+$lang_funnyquestion = array(
+
+'question-label' => 'Your answer',
+'wrong-answer' => 'Sorry, your answer was wrong. Try again!'
+
+);
diff --git a/lang/English/help.php b/lang/English/help.php
new file mode 100644
index 0000000..d7d1611
--- /dev/null
+++ b/lang/English/help.php
@@ -0,0 +1,66 @@
+<?php
+
+// Language definitions used in help.php
+$lang_help = array(
+
+'Help' => 'Help',
+'produces' => 'produces',
+
+'BBCode' => 'BBCode',
+'BBCode info 1' => 'BBCode is a collection of formatting tags that are used to change the look of text in this forum. BBCode is based on the same principal as, and is very similar to, HTML. Below is a list of all the available BBCodes and instructions on how to use them.',
+'BBCode info 2' => 'Administrators have the ability to enable or disable BBCode. You can tell if BBCode is enabled or disabled whenever you post a message or edit your signature.',
+
+'Text style' => 'Text style',
+'Text style info' => 'The following tags change the appearance of text:',
+'Bold text' => 'Bold text',
+'Underlined text' => 'Underlined text',
+'Italic text' => 'Italic text',
+'Strike-through text' => 'Strike-through text',
+'Red text' => 'Red text',
+'Blue text' => 'Blue text',
+'Heading text' => 'Heading text',
+'Deleted text' => 'Deleted text',
+'Inserted text' => 'Inserted text',
+'Emphasised text' => 'Emphasised text',
+
+'Links and images' => 'Links and images',
+'Links info' => 'You can create links to other documents or to email addresses using the following tags:',
+'This help page' => 'This help page',
+'My email address' => 'My email address',
+'Images info' => 'If you want to display an image you can use the img tag. The text appearing after the "=" sign in the opening tag is used for the alt attribute and should be included whenever possible.',
+'FluxBB bbcode test' => 'FluxBB bbcode test',
+
+'Test topic' => 'Test topic',
+'Test post' => 'Test post',
+'Test forum' => 'Test forum',
+'Test user' => 'Test user',
+
+'Quotes' => 'Quotes',
+'Quotes info' => 'If you want to quote someone, you should use the quote tag.',
+'Quotes info 2' => 'If you don\'t want to quote anyone in particular, you can use the quote tag without specifying a name.',
+'Quote text' => 'This is the text I want to quote.',
+'produces quote box' => 'produces a quote box like this:',
+'quote note' => 'Note: If a username contains the characters [ or ] you can enclose it in quote marks.',
+
+'Code' => 'Code',
+'Code info' => 'When displaying source code you should make sure that you use the code tag. Text displayed with the code tag will use a monospaced font and will not be affected by other tags.',
+'Code text' => 'This is some code.',
+'produces code box' => 'produces a code box like this:',
+
+'Nested tags' => 'Nested tags',
+'Nested tags info' => 'BBCode can be nested to create more advanced formatting. For example:',
+'Bold, underlined text' => 'Bold, underlined text',
+
+'Lists' => 'Lists',
+'List info' => 'To create a list you can use the list tag. You can create 3 types of lists using the list tag.',
+'List text 1' => 'Example list item 1.',
+'List text 2' => 'Example list item 2.',
+'List text 3' => 'Example list item 3.',
+'produces list' => 'produces a bulleted list.',
+'produces decimal list' => 'produces a numbered list.',
+'produces alpha list' => 'produces an alphabetically labelled list.',
+
+'Smilies' => 'Smilies',
+'Smilies info' => 'If you like (and if it is enabled), the forum can convert a series of smilies to images representations of that smiley. This forum recognizes the following smilies and replaces them with images:'
+
+);
diff --git a/lang/English/index.html b/lang/English/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/lang/English/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/lang/English/index.php b/lang/English/index.php
new file mode 100644
index 0000000..0ed222a
--- /dev/null
+++ b/lang/English/index.php
@@ -0,0 +1,20 @@
+<?php
+
+// Language definitions used in index.php
+$lang_index = array(
+
+'Topics' => 'Topics',
+'Link to' => 'Link to:', // As in "Link to: http://fluxbb.org/"
+'Empty board' => 'Board is empty.',
+'Newest user' => 'Newest registered user: %s',
+'Users online' => 'Registered users online: %s',
+'Guests online' => 'Guests online: %s',
+'No of users' => 'Total number of registered users: %s',
+'No of topics' => 'Total number of topics: %s',
+'No of posts' => 'Total number of posts: %s',
+'Online' => 'Online:', // As in "Online: User A, User B etc."
+'Board info' => 'Board information',
+'Board stats' => 'Board statistics',
+'User info' => 'User information'
+
+);
diff --git a/lang/English/install.php b/lang/English/install.php
new file mode 100644
index 0000000..7094111
--- /dev/null
+++ b/lang/English/install.php
@@ -0,0 +1,100 @@
+<?php
+
+// Language definitions used in install.php, localized by adaur
+
+$lang_install = array(
+
+'Choose install language' => 'Choose the install script language',
+'Choose install language info' => 'The language used for this install script. The default language used for the board itself can be set below.',
+'Install language' => 'Install language',
+'Change language' => 'Change language',
+'Already installed' => 'It seems like FluxBB is already installed. You should go <a href="index.php">here</a> instead.',
+'You are running error' => 'You are running %1$s version %2$s. FluxBB %3$s requires at least %1$s %4$s to run properly. You must upgrade your %1$s installation before you can continue.',
+'My FluxBB Forum' => 'My FluxBB Forum',
+'Description' => 'Unfortunately no one can be told what FluxBB is - you have to see it for yourself.',
+'Username 1' => 'Usernames must be at least 2 characters long.',
+'Username 2' => 'Usernames must not be more than 25 characters long.',
+'Username 3' => 'The username guest is reserved.',
+'Username 4' => 'Usernames may not be in the form of an IP address.',
+'Username 5' => 'Usernames may not contain all the characters \', " and [ or ] at once.',
+'Username 6' => 'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses.',
+'Short password' => 'Passwords must be at least 6 characters long.',
+'Passwords not match' => 'Passwords do not match.',
+'Wrong email' => 'The administrator email address you entered is invalid.',
+'No board title' => 'You must enter a board title.',
+'Error default language' => 'The default language chosen doesn\'t seem to exist.',
+'Error default style' => 'The default style chosen doesn\'t seem to exist.',
+'No DB extensions' => 'This PHP environment does not have support for any of the databases that FluxBB supports. PHP needs to have support for either MySQL, PostgreSQL or SQLite in order for FluxBB to be installed.',
+'Administrator username' => 'Administrator\'s username',
+'Administrator email' => 'Administrator\'s email',
+'Board title' => 'Board title',
+'Base URL' => 'The URL (without trailing slash) of your FluxBB forum. This must be correct.',
+'Required field' => 'is a required field in this form.',
+'FluxBB Installation' => 'FluxBB Installation',
+'Welcome' => 'You are about to install FluxBB. In order to install FluxBB, you must complete the form set out below. If you encounter any difficulties with the installation, please refer to the documentation.',
+'Install' => 'Install FluxBB %s',
+'Errors' => 'The following errors need to be corrected:',
+'Database setup' => 'Database setup',
+'Info 1' => 'All information we need to create a connection with your database.',
+'Select database' => 'Select your database type',
+'Info 2' => 'Select a database. We support SQLite, MySQL and PostgreSQL.',
+'Database type' => 'Database type',
+'Required' => '(Required)',
+'Database hostname' => 'Enter your database server hostname',
+'Info 3' => 'You should be able to get this info from your web host, if <code>localhost</code> does not work.',
+'Database server hostname' => 'Database server hostname',
+'Database enter name' => 'Enter the name of your database',
+'Info 4' => 'The name of the database you want to install FluxBB on.',
+'Database name' => 'Database name',
+'Database enter informations' => 'Enter your database username and password',
+'Database username' => 'Database username',
+'Info 5' => 'Your MySQL username and password (ignore of SQLite).',
+'Database password' => 'Database password',
+'Database enter prefix' => 'Enter database table prefix',
+'Info 6' => 'If you want to run multiple FluxBB installations in a single database, change this.',
+'Table prefix' => 'Table prefix',
+'Administration setup' => 'Administration setup',
+'Info 7' => 'Create the very first account on your board.',
+'Info 8' => 'Your username should be between 2 and 25 characters long. Your password must be at least 6 characters long. Remember that passwords are case-sensitive.',
+'Password' => 'Password',
+'Confirm password' => 'Confirm password',
+'Board setup' => 'Board setup',
+'Info 11' => 'Settings for your board. You can change this later.',
+'General information' => 'Enter your board\'s title and description.',
+'Board description' => 'Board description (supports HTML)',
+'Appearance' => 'Appearance',
+'Info 15' => 'Make your forum yours. Choose a language and a style for your board.',
+'Default language' => 'Default language',
+'Default style' => 'Default style',
+'Start install' => 'Start install',
+'DB type not valid' => '\'%s\' is not a valid database type',
+'Table prefix error' => 'The table prefix \'%s\' contains illegal characters or is too long. The prefix may contain the letters a to z, any numbers and the underscore character. They must however not start with a number. The maximum length is 40 characters. Please choose a different prefix',
+'Prefix reserved' => 'The table prefix \'sqlite_\' is reserved for use by the SQLite engine. Please choose a different prefix',
+'Existing table error' => 'A table called \'%susers\' is already present in the database \'%s\'. This could mean that FluxBB is already installed or that another piece of software is installed and is occupying one or more of the table names FluxBB requires. If you want to install multiple copies of FluxBB in the same database, you must choose a different table prefix',
+'InnoDB off' => 'InnoDB does not seem to be enabled. Please choose a database layer that does not have InnoDB support, or enable InnoDB on your MySQL server',
+'Administrators' => 'Administrators',
+'Administrator' => 'Administrator',
+'Moderators' => 'Moderators',
+'Moderator' => 'Moderator',
+'Guests' => 'Guests',
+'Guest' => 'Guest',
+'Members' => 'Members',
+'Announcement' => 'Enter your announcement here.',
+'Rules' => 'Enter your rules here',
+'Maintenance message' => 'The forums are temporarily down for maintenance. Please try again in a few minutes.',
+'Test post' => 'Test topic',
+'Message' => 'If you are looking at this (which I guess you are), the install of FluxBB appears to have worked! Now log in and head over to the administration control panel to configure your forum.',
+'Test category' => 'Test category',
+'Test forum' => 'Test forum',
+'This is just a test forum' => 'This is just a test forum',
+'Alert cache' => '<strong>The cache directory is currently not writable!</strong> In order for FluxBB to function properly, the directory <em>%s</em> must be writable by PHP. Use chmod to set the appropriate directory permissions. If in doubt, chmod to 0777.',
+'Alert avatar' => '<strong>The avatar directory is currently not writable!</strong> If you want users to be able to upload their own avatar images you must see to it that the directory <em>%s</em> is writable by PHP. You can later choose to save avatar images in a different directory (see Admin/Options). Use chmod to set the appropriate directory permissions. If in doubt, chmod to 0777.',
+'Alert upload' => '<strong>File uploads appear to be disallowed on this server!</strong> If you want users to be able to upload their own avatar images you must enable the file_uploads configuration setting in PHP. Once file uploads have been enabled, avatar uploads can be enabled in Administration/Options/Features.',
+'FluxBB has been installed' => 'FluxBB has been installed. To finalize the installation please follow the instructions below.',
+'Final instructions' => 'Final instructions',
+'Info 17' => 'To finalize the installation, you need to click on the button below to download a file called config.php. You then need to upload this file to the root directory of your FluxBB installation.',
+'Info 18' => 'Once you have uploaded config.php, FluxBB will be fully installed! At that point, you may <a href="index.php">go to the forum index</a>.',
+'Download config.php file' => 'Download config.php file',
+'FluxBB fully installed' => 'FluxBB has been fully installed! You may now <a href="index.php">go to the forum index</a>.',
+
+);
diff --git a/lang/English/login.php b/lang/English/login.php
new file mode 100644
index 0000000..fab1fae
--- /dev/null
+++ b/lang/English/login.php
@@ -0,0 +1,28 @@
+<?php
+
+// Language definitions used in login.php
+$lang_login = array(
+
+// Miscellaneous
+'Login errors' => 'Login error',
+'Login errors info' => 'The following error needs to be corrected before you can login:',
+'Wrong user/pass' => 'Wrong username and/or password.',
+'Forgotten pass' => 'Forgotten your password?',
+'Login redirect' => 'Logged in successfully. Redirecting …',
+'Logout redirect' => 'Logged out. Redirecting …',
+'No email match' => 'There is no user registered with the email address',
+'Request pass' => 'Request password',
+'Request pass legend' => 'Enter the email address with which you registered',
+'Request pass info' => 'A new password together with a link to activate the new password will be sent to that address.',
+'Not registered' => 'Not registered yet?',
+'Login legend' => 'Enter your username and password below',
+'Remember me' => 'Log me in automatically each time I visit.',
+'Login info' => 'If you have not registered or have forgotten your password click on the appropriate link below.',
+'New password errors' => 'Password request error',
+'New passworderrors info' => 'The following error needs to be corrected before a new password can be sent:',
+
+// Forget password mail stuff
+'Forget mail' => 'An email has been sent to the specified address with instructions on how to change your password. If it does not arrive you can contact the forum administrator at',
+'Email flood' => 'This account has already requested a password reset in the past hour. Please wait %s minutes before requesting a new password again.'
+
+);
diff --git a/lang/English/mail_templates/activate_email.tpl b/lang/English/mail_templates/activate_email.tpl
new file mode 100644
index 0000000..49095c0
--- /dev/null
+++ b/lang/English/mail_templates/activate_email.tpl
@@ -0,0 +1,12 @@
+Subject: Change email address requested
+
+Hello <username>,
+
+You have requested to have a new email address assigned to your account in the discussion forum at <base_url>. If you didn't request this or if you don't want to change your email address you should just ignore this message. Only if you visit the activation page below will your email address be changed. In order for the activation page to work, you must be logged in to the forum.
+
+To change your email address, please visit the following page:
+<activation_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/activate_password.tpl b/lang/English/mail_templates/activate_password.tpl
new file mode 100644
index 0000000..b408927
--- /dev/null
+++ b/lang/English/mail_templates/activate_password.tpl
@@ -0,0 +1,14 @@
+Subject: New password requested
+
+Hello <username>,
+
+You have requested to have a new password assigned to your account in the discussion forum at <base_url>. If you didn't request this or if you don't want to change your password you should just ignore this message. Only if you visit the activation page below will your password be changed.
+
+Your new password is: <new_password>
+
+To change your password, please visit the following page:
+<activation_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/banned_email_change.tpl b/lang/English/mail_templates/banned_email_change.tpl
new file mode 100644
index 0000000..276662f
--- /dev/null
+++ b/lang/English/mail_templates/banned_email_change.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Banned email detected
+
+User '<username>' changed to banned email address: <email>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/banned_email_post.tpl b/lang/English/mail_templates/banned_email_post.tpl
new file mode 100644
index 0000000..f7e0243
--- /dev/null
+++ b/lang/English/mail_templates/banned_email_post.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Banned email detected
+
+User '<username>' posted with banned email address: <email>
+
+Post URL: <post_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/banned_email_register.tpl b/lang/English/mail_templates/banned_email_register.tpl
new file mode 100644
index 0000000..f0085ec
--- /dev/null
+++ b/lang/English/mail_templates/banned_email_register.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Banned email detected
+
+User '<username>' registered with banned email address: <email>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/dupe_email_change.tpl b/lang/English/mail_templates/dupe_email_change.tpl
new file mode 100644
index 0000000..583fb24
--- /dev/null
+++ b/lang/English/mail_templates/dupe_email_change.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Duplicate email detected
+
+User '<username>' changed to an email address that also belongs to: <dupe_list>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/dupe_email_register.tpl b/lang/English/mail_templates/dupe_email_register.tpl
new file mode 100644
index 0000000..b1cb363
--- /dev/null
+++ b/lang/English/mail_templates/dupe_email_register.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Duplicate email detected
+
+User '<username>' registered with an email address that also belongs to: <dupe_list>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/form_email.tpl b/lang/English/mail_templates/form_email.tpl
new file mode 100644
index 0000000..e3e0d5f
--- /dev/null
+++ b/lang/English/mail_templates/form_email.tpl
@@ -0,0 +1,13 @@
+Subject: <mail_subject>
+
+<sender> from <board_title> has sent you a message. You can reply to <sender> by replying to this email.
+
+The message reads as follows:
+-----------------------------------------------------------------------
+
+<mail_message>
+
+-----------------------------------------------------------------------
+
+--
+<board_mailer> Mailer
diff --git a/lang/English/mail_templates/index.html b/lang/English/mail_templates/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/lang/English/mail_templates/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/lang/English/mail_templates/new_reply.tpl b/lang/English/mail_templates/new_reply.tpl
new file mode 100644
index 0000000..3719089
--- /dev/null
+++ b/lang/English/mail_templates/new_reply.tpl
@@ -0,0 +1,11 @@
+Subject: Reply to topic: '<topic_subject>'
+
+<replier> has replied to the topic '<topic_subject>' to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+
+The post is located at <post_url>
+
+You can unsubscribe by going to <unsubscribe_url> and clicking the Unsubscribe link at the bottom of the page.
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_reply_full.tpl b/lang/English/mail_templates/new_reply_full.tpl
new file mode 100644
index 0000000..ce2020e
--- /dev/null
+++ b/lang/English/mail_templates/new_reply_full.tpl
@@ -0,0 +1,18 @@
+Subject: Reply to topic: '<topic_subject>'
+
+<replier> has replied to the topic '<topic_subject>' to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+
+The post is located at <post_url>
+
+The message reads as follows:
+-----------------------------------------------------------------------
+
+<message>
+
+-----------------------------------------------------------------------
+
+You can unsubscribe by going to <unsubscribe_url> and clicking the Unsubscribe link at the bottom of the page.
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_report.tpl b/lang/English/mail_templates/new_report.tpl
new file mode 100644
index 0000000..dedb113
--- /dev/null
+++ b/lang/English/mail_templates/new_report.tpl
@@ -0,0 +1,9 @@
+Subject: Report(<forum_id>) - '<topic_subject>'
+
+User '<username>' has reported the following message: <post_url>
+
+Reason: <reason>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_topic.tpl b/lang/English/mail_templates/new_topic.tpl
new file mode 100644
index 0000000..97a8936
--- /dev/null
+++ b/lang/English/mail_templates/new_topic.tpl
@@ -0,0 +1,11 @@
+Subject: New topic in forum: '<forum_name>'
+
+<poster> has posted a new topic '<topic_subject>' in the forum '<forum_name>', to which you are subscribed.
+
+The topic is located at <topic_url>
+
+You can unsubscribe by going to <unsubscribe_url> and clicking the Unsubscribe link at the bottom of the page.
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_topic_full.tpl b/lang/English/mail_templates/new_topic_full.tpl
new file mode 100644
index 0000000..f197714
--- /dev/null
+++ b/lang/English/mail_templates/new_topic_full.tpl
@@ -0,0 +1,18 @@
+Subject: New topic in forum: '<forum_name>'
+
+<poster> has posted a new topic '<topic_subject>' in the forum '<forum_name>', to which you are subscribed.
+
+The topic is located at <topic_url>
+
+The message reads as follows:
+-----------------------------------------------------------------------
+
+<message>
+
+-----------------------------------------------------------------------
+
+You can unsubscribe by going to <unsubscribe_url> and clicking the Unsubscribe link at the bottom of the page.
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_user.tpl b/lang/English/mail_templates/new_user.tpl
new file mode 100644
index 0000000..30164e8
--- /dev/null
+++ b/lang/English/mail_templates/new_user.tpl
@@ -0,0 +1,12 @@
+Subject: Alert - New registration
+
+User '<username>' registered in the forums at <base_url>
+
+User profile: <profile_url>
+
+To administer this account, please visit the following page:
+<admin_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/rename.tpl b/lang/English/mail_templates/rename.tpl
new file mode 100644
index 0000000..3cba50d
--- /dev/null
+++ b/lang/English/mail_templates/rename.tpl
@@ -0,0 +1,12 @@
+Subject: User account renamed
+
+During an upgrade to the forums at <base_url> it was determined your username is too similar to an existing user. Your username has been changed accordingly.
+
+Old username: <old_username>
+New username: <new_username>
+
+We apologise for any inconvenience caused.
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/welcome.tpl b/lang/English/mail_templates/welcome.tpl
new file mode 100644
index 0000000..779a574
--- /dev/null
+++ b/lang/English/mail_templates/welcome.tpl
@@ -0,0 +1,12 @@
+Subject: Welcome to <board_title>!
+
+Thank you for registering in the forums at <base_url>. Your account details are:
+
+Username: <username>
+Password: <password>
+
+Login at <login_url> to activate the account.
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/misc.php b/lang/English/misc.php
new file mode 100644
index 0000000..e11c027
--- /dev/null
+++ b/lang/English/misc.php
@@ -0,0 +1,93 @@
+<?php
+
+// Language definitions used in various scripts
+$lang_misc = array(
+
+'Mark read redirect' => 'All topics and forums have been marked as read. Redirecting …',
+'Mark forum read redirect' => 'All topics in the specified forum have been marked as read. Redirecting …',
+
+// Send email
+'Form email disabled' => 'The user you are trying to send an email to has disabled form email.',
+'No email subject' => 'You must enter a subject.',
+'No email message' => 'You must enter a message.',
+'Too long email message' => 'Messages cannot be longer than 65535 characters (64 KB).',
+'Email flood' => 'At least %s seconds have to pass between sent emails. Please wait %s seconds and try sending again.',
+'Email sent redirect' => 'Email sent. Redirecting …',
+'Send email to' => 'Send email to',
+'Email subject' => 'Subject',
+'Email message' => 'Message',
+'Email disclosure note' => 'Please note that by using this form, your email address will be disclosed to the recipient.',
+'Write email' => 'Write and submit your email message',
+
+// Report
+'No reason' => 'You must enter a reason.',
+'Reason too long' => 'Your message must be under 65535 bytes (~64kb).',
+'Report flood' => 'At least %s seconds have to pass between reports. Please wait %s seconds and try sending again.',
+'Report redirect' => 'Post reported. Redirecting …',
+'Report post' => 'Report post',
+'Reason' => 'Reason',
+'Reason desc' => 'Please enter a short reason why you are reporting this post',
+
+// Subscriptions
+'Already subscribed topic' => 'You are already subscribed to this topic.',
+'Already subscribed forum' => 'You are already subscribed to this forum.',
+'Subscribe redirect' => 'Your subscription has been added. Redirecting …',
+'Not subscribed topic' => 'You are not subscribed to this topic.',
+'Not subscribed forum' => 'You are not subscribed to this forum.',
+'Unsubscribe redirect' => 'Your subscription has been removed. Redirecting …',
+
+// General forum and topic moderation
+'Moderate' => 'Moderate',
+'Select' => 'Select', // the header of a column of checkboxes
+'Move' => 'Move',
+'Split' => 'Split',
+'Delete' => 'Delete',
+'Merge' => 'Merge',
+
+// Moderate forum
+'Open' => 'Open',
+'Close' => 'Close',
+'Move topic' => 'Move topic',
+'Move topics' => 'Move topics',
+'Move legend' => 'Select destination of move',
+'Move to' => 'Move to',
+'Nowhere to move' => 'There are no forums into which you can move topics.',
+'Leave redirect' => 'Leave redirect topic(s)',
+'Move topic redirect' => 'Topic moved. Redirecting …',
+'Move topics redirect' => 'Topics moved. Redirecting …',
+'Confirm delete legend' => 'Please confirm deletion',
+'Delete topics' => 'Delete topics',
+'Delete topics comply' => 'Are you sure you want to delete the selected topics?',
+'Delete topics redirect' => 'Topics deleted. Redirecting …',
+'Open topic redirect' => 'Topic opened. Redirecting …',
+'Open topics redirect' => 'Topics opened. Redirecting …',
+'Close topic redirect' => 'Topic closed. Redirecting …',
+'Close topics redirect' => 'Topics closed. Redirecting …',
+'No topics selected' => 'You must select at least one topic for move/delete/open/close.',
+'Not enough topics selected' => 'You must select at least two topics for merge.',
+'Stick topic redirect' => 'Topic sticked. Redirecting …',
+'Unstick topic redirect' => 'Topic unsticked. Redirecting …',
+'Merge topics' => 'Merge topics',
+'Merge topics redirect' => 'Topics merged. Redirecting …',
+'Confirm merge legend' => 'Please confirm merge',
+'New subject' => 'New subject',
+
+// Split multiple posts in topic
+'Confirm split legend' => 'Please confirm split of selected posts and select destination of move.',
+'Split posts' => 'Split posts',
+'Split posts comply' => 'Are you sure you want to split the selected posts?',
+'Split posts redirect' => 'Posts have been split. Redirecting …',
+
+// Delete multiple posts in topic
+'Delete posts' => 'Delete posts',
+'Cannot select first' => 'First post cannot be selected for split/delete.',
+'Delete posts comply' => 'Are you sure you want to delete the selected posts?',
+'Delete posts redirect' => 'Posts deleted. Redirecting …',
+'No posts selected' => 'You must select at least one post for split/delete.',
+
+// Get host
+'Host info 1' => 'The IP address is: %s',
+'Host info 2' => 'The host name is: %s',
+'Show more users' => 'Show more users for this IP',
+
+);
diff --git a/lang/English/post.php b/lang/English/post.php
new file mode 100644
index 0000000..4784a06
--- /dev/null
+++ b/lang/English/post.php
@@ -0,0 +1,38 @@
+<?php
+
+// Language definitions used in post.php and edit.php
+$lang_post = array(
+
+// Post validation stuff (many are similiar to those in edit.php)
+'No subject' => 'Topics must contain a subject.',
+'No subject after censoring' => 'Topics must contain a subject. After applying censoring filters, your subject was empty.',
+'Too long subject' => 'Subjects cannot be longer than 70 characters.',
+'No message' => 'You must enter a message.',
+'No message after censoring' => 'You must enter a message. After applying censoring filters, your message was empty.',
+'Too long message' => 'Posts cannot be longer than %s bytes.',
+'All caps subject' => 'Subjects cannot contain only capital letters.',
+'All caps message' => 'Posts cannot contain only capital letters.',
+'Empty after strip' => 'It seems your post consisted of empty BBCodes only. It is possible that this happened because e.g. the innermost quote was discarded because of the maximum quote depth level.',
+
+// Posting
+'Post errors' => 'Post errors',
+'Post errors info' => 'The following errors need to be corrected before the message can be posted:',
+'Post preview' => 'Post preview',
+'Guest name' => 'Name', // For guests (instead of Username)
+'Post redirect' => 'Post entered. Redirecting …',
+'Post a reply' => 'Post a reply',
+'Post new topic' => 'Post new topic',
+'Hide smilies' => 'Never show smilies as icons for this post',
+'Subscribe' => 'Subscribe to this topic',
+'Stay subscribed' => 'Stay subscribed to this topic',
+'Topic review' => 'Topic review (newest first)',
+'Flood start' => 'At least %s seconds have to pass between posts. Please wait %s seconds and try posting again.',
+'Preview' => 'Preview', // submit button to preview message
+
+// Edit post
+'Edit post legend' => 'Edit the post and submit changes',
+'Silent edit' => 'Silent edit (don\'t display "Edited by ..." in topic view)',
+'Edit post' => 'Edit post',
+'Edit redirect' => 'Post updated. Redirecting …'
+
+);
diff --git a/lang/English/prof_reg.php b/lang/English/prof_reg.php
new file mode 100644
index 0000000..b3714ef
--- /dev/null
+++ b/lang/English/prof_reg.php
@@ -0,0 +1,79 @@
+<?php
+
+// Language definitions used in both profile.php and register.php
+$lang_prof_reg = array(
+
+'Email legend' => 'Enter a valid email address',
+'Email legend 2' => 'Enter and confirm a valid email address',
+'Localisation legend' => 'Set your localisation options',
+'Time zone' => 'Time zone',
+'Time zone info' => 'For the forum to display times correctly you must select your local time zone. If Daylight Savings Time is in effect you should also check the option provided which will advance times by 1 hour.',
+'DST' => 'Daylight Savings Time is in effect (advance time by 1 hour).',
+'Time format' => 'Time format',
+'Date format' => 'Date format',
+'Default' => 'Default',
+'Language' => 'Language',
+'Email setting info' => 'Select whether you want your email address to be viewable to other users or not and if you want other users to be able to send you email via the forum (form email) or not.',
+'Email setting 1' => 'Display your email address to other users.',
+'Email setting 2' => 'Hide your email address but allow form email.',
+'Email setting 3' => 'Hide your email address and disallow form email.',
+'Privacy options legend' => 'Set your privacy options',
+'Confirm pass' => 'Confirm password',
+
+'Username too short' => 'Usernames must be at least 2 characters long. Please choose another (longer) username.',
+'Username too long' => 'Usernames must not be more than 25 characters long. Please choose another (shorter) username.',
+'Username guest' => 'The username guest is reserved. Please choose another username.',
+'Username IP' => 'Usernames may not be in the form of an IP address. Please choose another username.',
+'Username reserved chars' => 'Usernames may not contain all the characters \', " and [ or ] at once. Please choose another username.',
+'Username BBCode' => 'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses. Please choose another username.',
+'Banned username' => 'The username you entered is banned in this forum. Please choose another username.',
+'Pass too short' => 'Passwords must be at least 9 characters long. Please choose another (longer) password.',
+'Pass not match' => 'Passwords do not match.',
+'Banned email' => 'The email address you entered is banned in this forum. Please choose another email address.',
+'Dupe email' => 'Someone else is already registered with that email address. Please choose another email address.',
+'Sig too long' => 'Signatures cannot be longer than %1$s characters. Please reduce your signature by %2$s characters.',
+'Sig too many lines' => 'Signatures cannot have more than %s lines.',
+'Bad ICQ' => 'You entered an invalid ICQ UIN. Please go back and correct.',
+
+'UTC-12:00' => '(UTC-12:00) International Date Line West',
+'UTC-11:00' => '(UTC-11:00) Niue, Samoa',
+'UTC-10:00' => '(UTC-10:00) Hawaii-Aleutian, Cook Island',
+'UTC-09:30' => '(UTC-09:30) Marquesas Islands',
+'UTC-09:00' => '(UTC-09:00) Alaska, Gambier Island',
+'UTC-08:30' => '(UTC-08:30) Pitcairn Islands',
+'UTC-08:00' => '(UTC-08:00) Pacific',
+'UTC-07:00' => '(UTC-07:00) Mountain',
+'UTC-06:00' => '(UTC-06:00) Central',
+'UTC-05:00' => '(UTC-05:00) Eastern',
+'UTC-04:00' => '(UTC-04:00) Atlantic',
+'UTC-03:30' => '(UTC-03:30) Newfoundland',
+'UTC-03:00' => '(UTC-03:00) Amazon, Central Greenland',
+'UTC-02:00' => '(UTC-02:00) Mid-Atlantic',
+'UTC-01:00' => '(UTC-01:00) Azores, Cape Verde, Eastern Greenland',
+'UTC' => '(UTC) Western European, Greenwich',
+'UTC+01:00' => '(UTC+01:00) Central European, West African',
+'UTC+02:00' => '(UTC+02:00) Eastern European, Central African',
+'UTC+03:00' => '(UTC+03:00) Eastern African',
+'UTC+03:30' => '(UTC+03:30) Iran',
+'UTC+04:00' => '(UTC+04:00) Moscow, Gulf, Samara',
+'UTC+04:30' => '(UTC+04:30) Afghanistan',
+'UTC+05:00' => '(UTC+05:00) Pakistan',
+'UTC+05:30' => '(UTC+05:30) India, Sri Lanka',
+'UTC+05:45' => '(UTC+05:45) Nepal',
+'UTC+06:00' => '(UTC+06:00) Bangladesh, Bhutan, Yekaterinburg',
+'UTC+06:30' => '(UTC+06:30) Cocos Islands, Myanmar',
+'UTC+07:00' => '(UTC+07:00) Indochina, Novosibirsk',
+'UTC+08:00' => '(UTC+08:00) Greater China, Australian Western, Krasnoyarsk',
+'UTC+08:45' => '(UTC+08:45) Southeastern Western Australia',
+'UTC+09:00' => '(UTC+09:00) Japan, Korea, Chita, Irkutsk',
+'UTC+09:30' => '(UTC+09:30) Australian Central',
+'UTC+10:00' => '(UTC+10:00) Australian Eastern',
+'UTC+10:30' => '(UTC+10:30) Lord Howe',
+'UTC+11:00' => '(UTC+11:00) Solomon Island, Vladivostok',
+'UTC+11:30' => '(UTC+11:30) Norfolk Island',
+'UTC+12:00' => '(UTC+12:00) New Zealand, Fiji, Magadan',
+'UTC+12:45' => '(UTC+12:45) Chatham Islands',
+'UTC+13:00' => '(UTC+13:00) Tonga, Phoenix Islands, Kamchatka',
+'UTC+14:00' => '(UTC+14:00) Line Islands'
+
+);
diff --git a/lang/English/profile.php b/lang/English/profile.php
new file mode 100644
index 0000000..08a2e66
--- /dev/null
+++ b/lang/English/profile.php
@@ -0,0 +1,143 @@
+<?php
+
+// Language definitions used in profile.php
+$lang_profile = array(
+
+// Navigation and sections
+'Profile menu' => 'Profile menu',
+'Section essentials' => 'Essentials',
+'Section personal' => 'Personal',
+'Section messaging' => 'Messaging',
+'Section personality' => 'Personality',
+'Section display' => 'Display',
+'Section privacy' => 'Privacy',
+'Section admin' => 'Administration',
+
+// Miscellaneous
+'Username and pass legend' => 'Enter your username and password',
+'Personal details legend' => 'Enter your personal details',
+'Contact details legend' => 'Enter your messaging details',
+'User activity' => 'User activity',
+'Paginate info' => 'Enter the number of topics and posts you wish to view on each page.',
+
+// Password stuff
+'Pass key bad' => 'The specified password activation key was incorrect or has expired. Please re-request a new password. If that fails, contact the forum administrator at',
+'Pass updated' => 'Your password has been updated. You can now login with your new password.',
+'Pass updated redirect' => 'Password updated. Redirecting …',
+'Wrong pass' => 'Wrong old password.',
+'Change pass' => 'Change password',
+'Change pass legend' => 'Enter and confirm your new password',
+'Old pass' => 'Old password',
+'New pass' => 'New password',
+'Confirm new pass' => 'Confirm new password',
+'Pass info' => 'Passwords must be at least 6 characters long. Passwords are case sensitive.',
+
+// Email stuff
+'Email key bad' => 'The specified email activation key was incorrect or has expired. Please re-request change of email address. If that fails, contact the forum administrator at',
+'Email updated' => 'Your email address has been updated.',
+'Activate email sent' => 'An email has been sent to the specified address with instructions on how to activate the new email address. If it doesn\'t arrive you can contact the forum administrator at',
+'Email legend' => 'Enter your new email address',
+'Email instructions' => 'An email will be sent to your new address with an activation link. You must click the link in the email you receive to activate the new address.',
+'Change email' => 'Change email address',
+'New email' => 'New email',
+
+// Avatar upload stuff
+'Avatars disabled' => 'The administrator has disabled avatar support.',
+'Too large ini' => 'The selected file was too large to upload. The server didn\'t allow the upload.',
+'Partial upload' => 'The selected file was only partially uploaded. Please try again.',
+'No tmp directory' => 'PHP was unable to save the uploaded file to a temporary location.',
+'No file' => 'You did not select a file for upload.',
+'Bad type' => 'The file you tried to upload is not of an allowed type. Allowed types are gif, jpeg and png.',
+'Too wide or high' => 'The file you tried to upload is wider and/or higher than the maximum allowed',
+'Too large' => 'The file you tried to upload is larger than the maximum allowed',
+'pixels' => 'pixels',
+'bytes' => 'bytes',
+'Move failed' => 'The server was unable to save the uploaded file. Please contact the forum administrator at',
+'Unknown failure' => 'An unknown error occurred. Please try again.',
+'Avatar upload redirect' => 'Avatar uploaded. Redirecting …',
+'Avatar deleted redirect' => 'Avatar deleted. Redirecting …',
+'Avatar desc' => 'An avatar is a small image that will be displayed under your username in your posts. It must not be any bigger than',
+'Upload avatar' => 'Upload avatar',
+'Upload avatar legend' => 'Enter an avatar file to upload',
+'Delete avatar' => 'Delete avatar', // only for admins
+'File' => 'File',
+'Upload' => 'Upload', // submit button
+
+// Form validation stuff
+'Forbidden title' => 'The title you entered contains a forbidden word. You must choose a different title.',
+'Profile redirect' => 'Profile updated. Redirecting …',
+
+// Profile display stuff
+'Users profile' => '%s\'s profile',
+'Username info' => 'Username: %s',
+'Email info' => 'Email: %s',
+'Posts info' => 'Posts: %s',
+'Registered info' => 'Registered: %s',
+'Last post info' => 'Last post: %s',
+'Last visit info' => 'Last visit: %s',
+'Show posts' => 'Show all posts',
+'Show topics' => 'Show all topics',
+'Show subscriptions' => 'Show all subscriptions',
+'Realname' => 'Real name',
+'Location' => 'Location',
+'Website' => 'Website',
+'Invalid website URL' => 'The website URL you entered is invalid.',
+'Website not allowed' => 'You are not allowed to add a website to your profile yet.',
+'Jabber' => 'Jabber',
+'ICQ' => 'ICQ',
+'MSN' => 'Microsoft Account',
+'AOL IM' => 'AOL IM',
+'Yahoo' => 'Yahoo! Messenger',
+'Avatar' => 'Avatar',
+'Signature' => 'Signature',
+'Sig max size' => 'Max length: %s characters / Max lines: %s',
+'Avatar legend' => 'Set your avatar display options',
+'Avatar info' => 'An avatar is a small image that will be displayed with all your posts. You can upload an avatar by clicking the link below.',
+'Change avatar' => 'Change avatar',
+'Signature legend' => 'Compose your signature',
+'Signature info' => 'A signature is a small piece of text that is attached to your posts. In it, you can enter just about anything you like. Perhaps you would like to enter your favourite quote or your star sign. It\'s up to you! In your signature you can use BBCode if it is allowed in this particular forum. You can see the features that are allowed/enabled listed below whenever you edit your signature.',
+'Sig preview' => 'Current signature preview:',
+'No sig' => 'No signature currently stored in profile.',
+'Signature quote/code/list/h' => 'The quote, code, list, and heading BBCodes are not allowed in signatures.',
+'Topics per page' => 'Topics',
+'Posts per page' => 'Posts',
+'Leave blank' => 'Leave blank to use forum default.',
+'Subscription legend' => 'Set your subscription options',
+'Notify full' => 'Include a plain text version of new posts in subscription notification emails.',
+'Auto notify full' => 'Automatically subscribe to every topic you post in.',
+'Show smilies' => 'Show smilies as graphic icons.',
+'Show images' => 'Show images in posts.',
+'Show images sigs' => 'Show images in user signatures.',
+'Show avatars' => 'Show user avatars in posts.',
+'Show sigs' => 'Show user signatures.',
+'Style legend' => 'Select your preferred style',
+'Styles' => 'Styles',
+'Admin note' => 'Admin note',
+'Pagination legend' => 'Enter your pagination options',
+'Post display legend' => 'Set your options for viewing posts',
+'Post display info' => 'If you are on a slow connection, disabling these options, particularly showing images in posts and signatures, will make pages load faster.',
+'Instructions' => 'When you update your profile, you will be redirected back to this page.',
+
+// Administration stuff
+'Group membership legend' => 'Choose user group',
+'Save' => 'Save',
+'Set mods legend' => 'Set moderator access',
+'Moderator in info' => 'Choose which forums this user should be allowed to moderate. Note: This only applies to moderators. Administrators always have full permissions in all forums.',
+'Update forums' => 'Update forums',
+'Delete ban legend' => 'Delete (administrators only) or ban user',
+'Delete user' => 'Delete user',
+'Ban user' => 'Ban user',
+'Confirm delete legend' => 'Important: read before deleting user',
+'Confirm delete user' => 'Confirm delete user',
+'Confirmation info' => 'Please confirm that you want to delete the user', // the username will be appended to this string
+'Delete warning' => 'Warning! Deleted users and/or posts cannot be restored. If you choose not to delete the posts made by this user, the posts can only be deleted manually at a later time.',
+'Delete posts' => 'Delete any posts and topics this user has made.',
+'Delete' => 'Delete', // submit button (confirm user delete)
+'User delete redirect' => 'User deleted. Redirecting …',
+'User promote redirect' => 'User promoted. Redirecting …',
+'Group membership redirect' => 'Group membership saved. Redirecting …',
+'Update forums redirect' => 'Forum moderator rights updated. Redirecting …',
+'Ban redirect' => 'Redirecting …',
+'No delete admin message' => 'Administrators cannot be deleted. In order to delete this user, you must first move him/her to a different user group.',
+
+);
diff --git a/lang/English/recaptcha_addon.php b/lang/English/recaptcha_addon.php
new file mode 100644
index 0000000..7264276
--- /dev/null
+++ b/lang/English/recaptcha_addon.php
@@ -0,0 +1,20 @@
+<?php
+
+$GLOBALS['lang_recaptcha'] = array(
+ 'Human' => 'Are you a human?',
+ 'Prove' => 'Please prove that you\'re a human being.',
+ 'Error' => 'Please prove that you are human.',
+ 'API error' => 'Cannot validate reCAPTCHA submission.',
+ 'General' => 'Set up reCAPTCHA integration',
+ 'General description' => 'Configure reCAPTCHA with your account credentials as provided by Google.',
+ 'Enable' => 'Enable reCAPTCHA',
+ 'Site key' => 'Site key',
+ 'Secret key' => 'Secret key',
+ 'Locations' => 'Locations',
+ 'Locations description' => 'Where do you want reCAPTCHA to show up?',
+ 'Register' => 'On the registration page',
+ 'Login' => 'On the login page',
+ 'Guest post' => 'When guests make a post',
+ 'Save' => 'Save',
+ 'Settings saved' => 'Settings saved successfully. Redirecting...',
+);
diff --git a/lang/English/register.php b/lang/English/register.php
new file mode 100644
index 0000000..3c41a40
--- /dev/null
+++ b/lang/English/register.php
@@ -0,0 +1,37 @@
+<?php
+
+// Language definitions used in register.php
+$lang_register = array(
+
+// Miscellaneous
+'No new regs' => 'This forum is not accepting new registrations.',
+'Reg cancel redirect' => 'Registration cancelled. Redirecting …',
+'Forum rules' => 'Forum rules',
+'Rules legend' => 'You must agree to the following in order to register',
+'Registration flood' => 'A new user was registered with the same IP address as you within the last hour. To prevent registration flooding, at least an hour has to pass between registrations from the same IP. Sorry for the inconvenience.',
+'Agree' => 'Agree',
+'Cancel' => 'Cancel',
+'Register' => 'Register',
+
+// Form validation stuff (some of these are also used in post.php)
+'Registration errors' => 'Registration errors',
+'Registration errors info' => 'The following errors need to be corrected before you can register:',
+'Username censor' => 'The username you entered contains one or more censored words. Please choose a different username.',
+'Username dupe 1' => 'Someone is already registered with the username',
+'Username dupe 2' => 'The username you entered is too similar. The username must differ from that by at least one alphanumerical character (a-z or 0-9). Please choose a different username.',
+'Email not match' => 'Email addresses do not match.',
+
+// Registration email stuff
+'Reg email' => 'Thank you for registering. Your password has been sent to the specified address. If it doesn\'t arrive you can contact the forum administrator at',
+'Reg complete' => 'Registration complete. Logging in and redirecting …',
+
+// Register info
+'Desc 1' => 'Registration will grant you access to a number of features and capabilities otherwise unavailable. These functions include the ability to edit and delete posts, design your own signature that accompanies your posts and much more. If you have any questions regarding this forum you should ask an administrator.',
+'Desc 2' => 'Below is a form you must fill out in order to register. Once you are registered you should visit your profile and review the different settings you can change. The fields below only make up a small part of all the settings you can alter in your profile.',
+'Username legend' => 'Please enter a username between 2 and 25 characters long',
+'Pass legend' => 'Please enter and confirm your chosen password',
+'Pass info' => 'Passwords must be at least 6 characters long. Passwords are case sensitive.',
+'Email info' => 'You must enter a valid email address as your randomly generated password will be sent to that address.',
+'Confirm email' => 'Confirm email address',
+
+);
diff --git a/lang/English/search.php b/lang/English/search.php
new file mode 100644
index 0000000..5c995a5
--- /dev/null
+++ b/lang/English/search.php
@@ -0,0 +1,64 @@
+<?php
+
+// Language definitions used in search.php
+$lang_search = array(
+
+// The search form
+'User search' => 'User search',
+'No search permission' => 'You do not have permission to use the search feature.',
+'Search flood' => 'At least %s seconds have to pass between searches. Please wait %s seconds and try searching again.',
+'Search' => 'Search',
+'Search criteria legend' => 'Enter your search criteria',
+'Search info' => 'To search by keyword, enter a term or terms to search for. Separate terms with spaces. Use AND, OR and NOT to refine your search. To search by author enter the username of the author whose posts you wish to search for. Use wildcard character * for partial matches.',
+'Keyword search' => 'Keyword search',
+'Author search' => 'Author search',
+'Search in legend' => 'Select where to search',
+'Search in info' => 'Choose in which forum you would like to search and if you want to search in topic subjects, message text or both.',
+'Search multiple forums info' => 'If no forums are selected, all forums will be searched.',
+'Forum search' => 'Forum',
+'Search in' => 'Search in',
+'Message and subject' => 'Message text and topic subject',
+'Message only' => 'Message text only',
+'Topic only' => 'Topic subject only',
+'Sort by' => 'Sort by',
+'Sort order' => 'Sort order',
+'Search results legend' => 'Select how to view search results',
+'Search results info' => 'You can choose how you wish to sort and show your results.',
+'Sort by post time' => 'Post time',
+'Sort by author' => 'Author',
+'Sort by subject' => 'Subject',
+'Sort by forum' => 'Forum',
+'Ascending' => 'Ascending',
+'Descending' => 'Descending',
+'Show as' => 'Show results as',
+'Show as topics' => 'Topics',
+'Show as posts' => 'Posts',
+
+// Results
+'Search' => 'Search',
+'Search results' => 'Search results',
+'Quick search show_new' => 'New',
+'Quick search show_recent' => 'Active',
+'Quick search show_unanswered' => 'Unanswered',
+'Quick search show_replies' => 'Posted',
+'Quick search show_user_topics' => 'Topics by %s',
+'Quick search show_user_posts' => 'Posts by %s',
+'Quick search show_subscriptions' => 'Subscribed by %s',
+'By keywords show as topics' => 'Topics with posts containing \'%s\'',
+'By keywords show as posts' => 'Posts containing \'%s\'',
+'By user show as topics' => 'Topics with posts by %s',
+'By user show as posts' => 'Posts by %s',
+'By both show as topics' => 'Topics with posts containing \'%s\', by %s',
+'By both show as posts' => 'Posts containing \'%s\', by %s',
+'No terms' => 'You have to enter at least one keyword and/or an author to search for. Search terms need to be at least three characters long.',
+'No hits' => 'Your search returned no hits.',
+'No user posts' => 'There are no posts by this user in this forum.',
+'No user topics' => 'There are no topics by this user in this forum.',
+'No subscriptions' => 'This user is currently not subscribed to any topics.',
+'No new posts' => 'There are no topics with new posts since your last visit.',
+'No recent posts' => 'No new posts have been made within the last 24 hours.',
+'No unanswered' => 'There are no unanswered posts in this forum.',
+'Go to post' => 'Go to post',
+'Go to topic' => 'Go to topic'
+
+);
diff --git a/lang/English/stopwords.txt b/lang/English/stopwords.txt
new file mode 100644
index 0000000..907a260
--- /dev/null
+++ b/lang/English/stopwords.txt
@@ -0,0 +1,175 @@
+about
+after
+ago
+all
+almost
+along
+also
+any
+anybody
+anywhere
+are
+arent
+aren't
+around
+ask
+been
+before
+being
+between
+but
+came
+can
+cant
+can't
+come
+could
+couldnt
+couldn't
+did
+didnt
+didn't
+does
+doesnt
+doesn't
+dont
+don't
+each
+either
+else
+even
+every
+everybody
+everyone
+find
+for
+from
+get
+going
+gone
+got
+had
+has
+have
+havent
+haven't
+having
+her
+here
+hers
+him
+his
+how
+ill
+i'll
+i'm
+into
+isnt
+isn't
+itll
+it'll
+its
+it's
+ive
+i've
+just
+know
+less
+like
+make
+many
+may
+more
+most
+much
+must
+near
+never
+none
+nothing
+now
+off
+often
+once
+one
+only
+other
+our
+ours
+our's
+out
+over
+please
+rather
+really
+said
+see
+she
+should
+small
+some
+something
+sometime
+somewhere
+take
+than
+thank
+thanks
+that
+thats
+that's
+the
+their
+theirs
+them
+then
+there
+these
+they
+thing
+think
+this
+those
+though
+through
+thus
+too
+true
+two
+under
+until
+upon
+use
+very
+want
+was
+way
+well
+were
+what
+when
+where
+which
+who
+whom
+whose
+why
+will
+with
+within
+without
+would
+yes
+yet
+you
+your
+youre
+you're
+yours
+http
+https
+ftp
+www
+com
+net
+org
diff --git a/lang/English/topic.php b/lang/English/topic.php
new file mode 100644
index 0000000..135a020
--- /dev/null
+++ b/lang/English/topic.php
@@ -0,0 +1,33 @@
+<?php
+
+// Language definitions used in viewtopic.php
+$lang_topic = array(
+
+'Post reply' => 'Post reply',
+'Topic closed' => 'Topic closed',
+'From' => 'From:', // User location
+'Promote user' => 'Promote user',
+'IP address logged' => 'IP address logged',
+'Note' => 'Note:', // Admin note
+'Posts' => 'Posts:',
+'Registered' => 'Registered:',
+'Replies' => 'Replies:',
+'Website' => 'Website',
+'Guest' => 'Guest',
+'Online' => 'Online',
+'Offline' => 'Offline',
+'Last edit' => 'Last edited by',
+'Report' => 'Report',
+'Delete' => 'Delete',
+'Edit' => 'Edit',
+'Quote' => 'Quote',
+'Is subscribed' => 'You are currently subscribed to this topic',
+'Unsubscribe' => 'Unsubscribe',
+'Subscribe' => 'Subscribe to this topic',
+'Quick post' => 'Quick reply',
+'Mod controls' => 'Moderator controls',
+'New icon' => 'New post',
+'Re' => 'Re:',
+'Preview' => 'Preview'
+
+);
diff --git a/lang/English/update.php b/lang/English/update.php
new file mode 100644
index 0000000..1a98f77
--- /dev/null
+++ b/lang/English/update.php
@@ -0,0 +1,76 @@
+<?php
+
+// Language definitions used in db_update.php
+
+$lang_update = array(
+
+'Update' => 'Update FluxBB',
+'Update message' => 'Your FluxBB database is out-of-date and must be upgraded in order to continue. If you are the board administrator, please follow the instructions below to complete the upgrade.',
+'Note' => 'Note:',
+'Members message' => 'This process is for board administrators only. If you are a member there is nothing to worry about - the forums will be back shortly!',
+'Administrator only' => 'This step is for the board administrator only!',
+'Database password info' => 'To perform the database update please enter the database password with which FluxBB was installed. If you cannot remember, this is stored in your \'config.php\' file.',
+'Database password note' => 'If you are running SQLite (and hence have no database password) please use the database file name instead. This must exactly match the database file name given in your configuration file.',
+'Database password' => 'Database password',
+'Maintenance' => 'Maintenance',
+'Maintenance message info' => 'The message that will be displayed to users during the updating process. This text will not be parsed like regular posts and thus may contain HTML.',
+'Maintenance message' => 'Maintenance message',
+
+'You are running error' => 'You are running %1$s version %2$s. FluxBB %3$s requires at least %1$s %4$s to run properly. You must upgrade your %1$s installation before you can continue.',
+'Version mismatch error' => 'Version mismatch. The database \'%s\' doesn\'t seem to be running a FluxBB database schema supported by this update script.',
+'Invalid file error' => 'Invalid database file name. When using SQLite the database file name must be entered exactly as it appears in your \'%s\'',
+'Invalid password error' => 'Invalid database password. To upgrade FluxBB you must enter your database password exactly as it appears in your \'%s\'',
+'No password error' => 'No database password provided',
+'Script runs error' => 'It appears the update script is already being ran by someone else. If this is not the case, please manually delete the file \'%s\' and try again',
+'No update error' => 'Your forum is already as up-to-date as this script can make it',
+
+'Intro 1' => 'This script will update your forum database. The update procedure might take anything from a second to hours depending on the speed of the server and the size of the forum database. Don\'t forget to make a backup of the database before continuing.',
+'Intro 2' => 'Did you read the update instructions in the documentation? If not, start there.',
+'No charset conversion' => '<strong>IMPORTANT!</strong> FluxBB has detected that this PHP environment does not have support for the encoding mechanisms required to do UTF-8 conversion from character sets other than ISO-8859-1. What this means is that if the current character set is not ISO-8859-1, FluxBB won\'t be able to convert your forum database to UTF-8 and you will have to do it manually. Instructions for doing manual charset conversion can be found in the update instructions.',
+'Enable conversion' => '<strong>Enable conversion:</strong> When enabled this update script will, after it has made the required structural changes to the database, convert all text in the database from the current character set to UTF-8. This conversion is required if you\'re upgrading from version 1.2.',
+'Current character set' => '<strong>Current character set:</strong> If the primary language in your forum is English, you can leave this at the default value. However, if your forum is non-English, you should enter the character set of the primary language pack used in the forum. <em>Getting this wrong can corrupt your database so don\'t just guess!</em> Note: This is required even if the old database is UTF-8.',
+'Charset conversion' => 'Charset conversion',
+'Enable conversion label' => '<strong>Enable conversion</strong> (perform database charset conversion).',
+'Current character set label' => 'Current character set',
+'Current character set info' => 'Accept default for English forums otherwise the character set of the primary language pack.',
+'Start update' => 'Start update',
+'Error converting users' => 'Error converting users',
+'Error info 1' => 'There was an error converting some users. This can occur when converting from FluxBB v1.2 if multiple users have registered with very similar usernames, for example "bob" and "böb".',
+'Error info 2' => 'Below is a list of users who failed to convert. Please choose a new username for each user. Users who are renamed will automatically be sent an email alerting them of the change.',
+'New username' => 'New username',
+'Required' => '(Required)',
+'Correct errors' => 'The following errors need to be corrected:',
+'Rename users' => 'Rename users',
+'Successfully updated' => 'Your forum database was successfully updated. You may now %s.',
+'go to index' => 'go to the forum index',
+
+'Unable to lock error' => 'Unable to write update lock. Please make sure PHP has write access to the directory \'%s\' and no-one else is currently running the update script.',
+
+'Converting' => 'Converting %s …',
+'Converting item' => 'Converting %1$s %2$s …',
+'Preparsing item' => 'Preparsing %1$s %2$s …',
+'Rebuilding index item' => 'Rebuilding index for %1$s %2$s',
+'Click here' => 'Click here',
+'Automatic redirect failed' => 'Automatic redirect unsuccessful. %s to continue …',
+
+'ban' => 'ban',
+'categories' => 'categories',
+'censor words' => 'censor words',
+'configuration' => 'configuration',
+'forums' => 'forums',
+'groups' => 'groups',
+'post' => 'post',
+'report' => 'report',
+'topic' => 'topic',
+'user' => 'user',
+'signature' => 'signature',
+
+'Username too short error' => 'Usernames must be at least 2 characters long. Please choose another (longer) username.',
+'Username too long error' => 'Usernames must not be more than 25 characters long. Please choose another (shorter) username.',
+'Username Guest reserved error' => 'The username guest is reserved. Please choose another username.',
+'Username IP format error' => 'Usernames may not be in the form of an IP address. Please choose another username.',
+'Username bad characters error' => 'Usernames may not contain all the characters \', " and [ or ] at once. Please choose another username.',
+'Username BBCode error' => 'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses. Please choose another username.',
+'Username duplicate error' => 'Someone is already registered with the username %s. The username you entered is too similar. The username must differ from that by at least one alphanumerical character (a-z or 0-9). Please choose a different username.',
+
+);
diff --git a/lang/English/userlist.php b/lang/English/userlist.php
new file mode 100644
index 0000000..0853d25
--- /dev/null
+++ b/lang/English/userlist.php
@@ -0,0 +1,13 @@
+<?php
+
+// Language definitions used in userlist.php
+$lang_ul = array(
+
+'User find legend' => 'Find and sort users',
+'User search info' => 'Enter a username to search for and/or a user group to filter by. The username field can be left blank. Use the wildcard character * for partial matches.',
+'User sort info' => 'Sort users by name, date registered or number of posts and in ascending/descending order.',
+'User group' => 'User group',
+'No of posts' => 'Number of posts',
+'All users' => 'All'
+
+);
diff --git a/lang/German/admin_bans.php b/lang/German/admin_bans.php
new file mode 100644
index 0000000..c4ba3d5
--- /dev/null
+++ b/lang/German/admin_bans.php
@@ -0,0 +1,69 @@
+<?php
+
+// Sprachdefinitionen, die in admin_bans.php verwendet werden
+$lang_admin_bans = array(
+
+'No user message' => 'Es ist kein Mitglied mit diesem Namen registriert. Wenn Du eine Mitgliedssperre hinzufügen möchtest, der nicht mit einem bestimmten Mitgliedsnamen verbunden ist, lass das Namensfeld einfach leer.',
+'No user ID message' => 'Es ist kein Mitglied mit dieser ID registriert.',
+'User is admin message' => 'Das Mitglied %s ist ein Administrator und kann nicht gesperrt werden. Wenn Du einen Administrator sperren möchtest, musst Du diesen zunächst auf die Berechtigungen eines einfachen Mitglieds zurückstufen.',
+'User is mod message' => 'Das Mitglied %s ist ein Moderator und kann nicht gesperrt werden. Wenn Du einen Moderator sperren möchtest, musst Du diesen zunächst auf die Berechtigungen eines einfachen Mitglieds zurückstufen.',
+'Must enter message' => 'Du musst mindestens einen Mitgliedsnamen, eine IP-Adresse oder eine Email-Adresse eingeben.',
+'Cannot ban guest message' => 'Gäste können nicht gesperrt werden.',
+'Invalid IP message' => 'Du hast eine ungültige IP-Adresse bzw. ungültigen IP-Bereich eingegeben.',
+'Invalid e-mail message' => 'Die eingegebene Email-Adresse (z.Bsp. mitglied@domain.de) oder ein Teil der Email-Domain (z.Bsp. domain.de) ist ungültig.',
+'Duplicate domain message' => 'Die Domain %s wurde bereits gesperrt.',
+'Duplicate e-mail message' => 'Die Email-Adresse %s wurde bereits gesperrt.',
+'Invalid date message' => 'Du hast ein ungültiges Ablaufdatum eingegeben.',
+'Invalid date reasons' => 'Das Datum muss im Format JJJJ-MM-TT angegeben werden und mindestens einen Tag in der Zukunft liegen.',
+'Ban added redirect' => 'Mitgliedssperre hinzugefügt. Leite weiter …' ,
+'Ban edited redirect' => 'Mitgliedssperre aktualisiert. Leite weiter …',
+'Ban removed redirect' => 'Mitgliedssperre entfernt. Leite weiter …',
+
+'New ban head' => 'Neue Mitgliedssperre',
+'Add ban subhead' => 'Eine Mitgliedssperre hinzufügen',
+'Username label' => 'Mitgliedsname',
+'Username help' => 'Der Name des zu sperrenden Mitglieds (nicht zeichensensitiv).',
+'Username advanced help' => 'Der zu sperrende Mitgliedsname (zeichensensitiv). Auf der nächsten Seite kannst Du eine IP sowie eine Email-Adresse festlegen. Wenn Du nur eine bestimmte IP bzw. IP-Bereich oder eine bestimmte Email-Adresse sperren willst, lass die anderen Felder leer.',
+
+'Ban search head' => 'Mitgliedssperren-Suche',
+'Ban search subhead' => 'Bitte gib die Suchkriterien ein',
+'Ban search info' => 'Mitgliedssperren-Suche in der Datenbank. Für die Suche kannst Du einen oder mehrere Begriffe eingeben. Platzhalter in Form von Asterisks (*) sind auch möglich.',
+'Date help' => '(jjjj-mm-tt)',
+'Message label' => 'Nachricht',
+'Expire after label' => 'Ablauf nach',
+'Expire before label' => 'Ablauf vor',
+'Order by label' => 'Sortieren nach',
+'Order by username' => 'Mitgliedsname',
+'Order by ip' => 'IP',
+'Order by e-mail' => 'Email',
+'Order by expire' => 'Ablaufdatum',
+'Ascending' => 'Aufsteigend',
+'Descending' => 'Absteigend',
+'Submit search' => 'Suche starten',
+
+'E-mail label' => 'Email-Adresse',
+'E-mail help' => 'Die Email-Adresse oder die Email-Domain, die gesperrt werden soll (z.Bsp. irgendjemand@domain.de oder domain.de). Weitere Informationen dazu finden Sie in den Berechtigungen unter "Email-Adresssperre erlauben".',
+'IP label' => 'IP-Adresse/IP-Bereich',
+'IP help' => 'Die IP-Adresse oder der IP-Bereich, der gesperrt werden soll (z.Bsp. 150.11.110.1 oder 150.11.110). Mehrere IP-Adressen können mit einem Leerzeichen getrennt eingegeben werden. Wenn in diesem Feld bereits eine IP-Adresse vorhanden ist, dann ist dies die letzte, in der Datenbank gespeicherte IP-Adresse des Mitglieds.',
+'IP help link' => 'Klicke %s, um die IP-Statistik für dieses Mitglied anzuzeigen.',
+'Ban advanced head' => 'Erweiterte Mitgliedssperren-Einstellungen',
+'Ban advanced subhead' => 'Kombinierte Mitgliedssperre mit IP und Email-Adresse',
+'Ban message label' => 'Sperr-Nachricht an das Mitglied',
+'Ban message help' => 'Die Nachricht, die dem Mitglied angezeigt wird, wenn es die Foren besucht.',
+'Message expiry subhead' => 'Sperr-Nachricht und Ablaufdatum',
+'Ban IP range info' => 'Du solltest vorsichtig sein, wenn Du einen IP-Bereich sperrst, da andere Mitglieder möglicherweise den gleichen IP-Bereich verwenden.',
+'Expire date label' => 'Ablaufdatum',
+'Expire date help' => 'Das Datum, nachdem die Mitgliedssperre automatisch entfernt werden soll (Format: JJJJ-MM-TT). Lass dieses Feld leer, wenn die Mitgliedssperre manuell entfernt werden soll.',
+
+'Results head' => 'Suchergebnisse',
+'Results username head' => 'Mitgliedsname',
+'Results e-mail head' => 'Email',
+'Results IP address head' => 'IP/IP-Bereich',
+'Results expire head' => 'Ablaufdatum',
+'Results message head' => 'Nachricht',
+'Results banned by head' => 'Gesperrt durch',
+'Results actions head' => 'Aktionen',
+'No match' => 'Keinen Eintrag gefunden',
+'Unknown' => 'Unbekannt',
+
+);
diff --git a/lang/German/admin_categories.php b/lang/German/admin_categories.php
new file mode 100644
index 0000000..4675d3d
--- /dev/null
+++ b/lang/German/admin_categories.php
@@ -0,0 +1,29 @@
+<?php
+
+// Sprachdefinitionen, die in admin_categories.php verwendet werden
+$lang_admin_categories = array(
+
+'Must enter name message' => 'Du musst einen Namen für diese Kategorie eingeben',
+'Category added redirect' => 'Kategorie hinzugefügt. Leite weiter …',
+'Category deleted redirect' => 'Kategorie gelöscht. Leite weiter …',
+'Delete category head' => 'Kategorie löschen (einschließlich aller darin enthaltenen Foren und Themen)',
+'Confirm delete subhead' => 'Löschen der Kategorie bestätigen',
+'Confirm delete info' => 'Willst Du wirklich die Kategorie %s löschen?',
+'Delete category warn' => '<strong>WARNUNG!</strong> Durch das Löschen einer Kategorie werden alle darin enthaltenen Foren und Beiträge ebenfalls gelöscht!',
+'Must enter integer message' => 'Position muss einen positiven Ganzzahlwert enthalten',
+'Categories updated redirect' => 'Kategorien aktualisiert. Leite weiter …',
+'Add categories head' => 'Kategorien erstellen',
+'Add categories subhead' => 'Kategorien erstellen',
+'Add category label' => 'Eine neue Kategorie erstellen',
+'Add new submit' => 'Neu erstellen',
+'Add category help' => 'Der Name der neuen Kategorie, die Du erstellen möchtest. Du kannst den Namen der Kategorie später noch ändern (siehe unten). Geh auf %s, um der neuen Kategorie Foren hinzuzufügen.',
+'Delete categories head' => 'Kategorien löschen',
+'Delete categories subhead' => 'Kategorien löschen',
+'Delete category label' => 'Eine Kategorie löschen',
+'Delete category help' => 'Wähle die Kategorie, die Du löschen möchtest. Bevor die Kategorie tatsächlich gelöscht wird, musst Du Deine Auswahl nochmals bestätigen.',
+'Edit categories head' => 'Kategorien bearbeiten',
+'Edit categories subhead' => 'Kategorien bearbeiten',
+'Category position label' => 'Position',
+'Category name label' => 'Name',
+
+);
diff --git a/lang/German/admin_censoring.php b/lang/German/admin_censoring.php
new file mode 100644
index 0000000..62d607a
--- /dev/null
+++ b/lang/German/admin_censoring.php
@@ -0,0 +1,21 @@
+<?php
+
+// Sprachdefinitionen, die in admin_censoring.php verwendet werden
+$lang_admin_censoring = array(
+
+'Must enter word message' => 'Du musst ein Wort eingeben, welches zensiert werden soll.',
+'Word updated redirect' => 'Zensierte Wörter aktualisiert. Leite weiter …',
+'Word added redirect' => 'Zensierte Wörter hinzugefügt. Leite weiter …',
+'Word removed redirect' => 'Zensierte Wörter entfernt. Leite weiter …',
+'Censoring head' => 'Wortzensur',
+'Add word subhead' => 'Ein Wort hinzufügen',
+'Add word info' => 'Gib hier ein Wort ein, das Du zensieren möchtest, sowie den Text, durch den dieses Wort ersetzt werden soll. Wildcards sind erlaubt (z.Bsp. stimmt *irgend* mit irgendwo und nirgendwann überein). Die Wortzensur hat auch Auswirkungen auf die Mitgliedsnamen. Neue Mitglieder können sich nicht mit Namen registrieren, die zensierte Wörter enthalten. Die Suche ist zeichensensitiv.',
+'Censoring enabled' => '<strong>Die Wortzensur kann in den %s aktiviert werden.</strong>',
+'Censoring disabled' => '<strong>Die Wortzensur kann in den %s deaktiviert werden.</strong>',
+'Censored word label' => 'Zu zensierendes Wort',
+'Replacement label' => 'Ersatzwort',
+'Action label' => 'Aktion',
+'Edit remove subhead' => 'Wörter bearbeiten oder entfernen',
+'No words in list' => 'Derzeit werden keine Wörter zensiert.',
+
+);
diff --git a/lang/German/admin_common.php b/lang/German/admin_common.php
new file mode 100644
index 0000000..7724586
--- /dev/null
+++ b/lang/German/admin_common.php
@@ -0,0 +1,44 @@
+<?php
+
+// Sprachdefinitionen, die in admin_common.php verwendet werden
+$lang_admin_common = array(
+
+// Das Menü
+'Admin menu' => 'Administrator-Menü',
+'Plugins menu' => 'Plugin-Menü',
+'Moderator menu' => 'Moderator-Menü',
+'Index' => 'Übersicht',
+'Categories' => 'Kategorien',
+'Forums' => 'Foren',
+'Users' => 'Mitglieder',
+'User groups' => 'Mitgliedergruppen',
+'Options' => 'Optionen',
+'Permissions' => 'Berechtigungen',
+'Censoring' => 'Wortzensur',
+'Bans' => 'Mitgliedersperren',
+'Prune' => 'Bereinigung',
+'Maintenance' => 'Wartung',
+'Reports' => 'Meldungen',
+'Server statistics' => 'Server-Statistiken',
+
+'Admin' => 'Administrator',
+'Go back' => 'Zurück',
+'Delete' => 'Löschen',
+'Update' => 'Aktualisieren',
+'Add' => 'Hinzufügen',
+'Edit' => 'Bearbeiten',
+'Remove' => 'Entfernen',
+'Yes' => 'Ja',
+'No' => 'Nein',
+'Save changes' => 'Änderungen speichern',
+'Save' => 'Speichern',
+'here' => 'hier',
+'Action' => 'Aktion',
+'None' => 'Nichts',
+'Maintenance mode' => 'Wartungsmodus', // wird für den Linktext in mehr als einer Datei verwendet
+
+// Der Admin-Loader
+'No plugin message' => 'Im Plugin-Verzeichnis ist kein Plugin mit dem Namen %s zu finden.',
+'Plugin failed message' => 'Das Laden des Plugins - <strong>%s</strong> - ist fehlgeschlagen.',
+
+);
diff --git a/lang/German/admin_forums.php b/lang/German/admin_forums.php
new file mode 100644
index 0000000..c68a4e0
--- /dev/null
+++ b/lang/German/admin_forums.php
@@ -0,0 +1,53 @@
+<?php
+
+// Sprachdefinitionen, die in admin_forums.php verwendet werden
+$lang_admin_forums = array(
+
+'Forum added redirect' => 'Forum hinzugefügt. Leite weiter …',
+'Forum deleted redirect' => 'Forum gelöscht. Leite weiter …',
+'Forums updated redirect' => 'Foren aktualisiert. Leite weiter …',
+'Forum updated redirect' => 'Forum aktualisiert. Leite weiter …',
+'Perms reverted redirect' => 'Die Berechtigungen wurden auf die programmseitigen Voreinstellungen zurückgesetzt. Leite weiter …',
+'Must enter name message' => 'Du musst einen Namen für das Forum eingeben.',
+'Must be integer message' => 'Das Feld Position muss einen positiven Ganzzahlwert enthalten.',
+'New forum' => 'Neues Forum',
+
+// Eingangsseite
+'Add forum head' => 'Ein Forum hinzufügen',
+'Create new subhead' => 'Ein neues Forum erstellen',
+'Add forum label' => 'Der Kategorie ein Forum hinzufügen',
+'Add forum help' => 'Wähle hier eine Kategorie, der das neue Forum hinzugefügt werden soll.',
+'Add forum' => 'Forum hinzufügen',
+'No categories exist' => 'Es existieren noch keine Kategorien',
+'Edit forums head' => 'Foren bearbeiten',
+'Category subhead' => 'Kategorie',
+'Forum label' => 'Forum',
+'Edit link' => 'Bearbeiten',
+'Delete link' => 'Löschen',
+'Position label' => 'Position',
+'Update positions' => 'Positionen aktualisieren',
+'Confirm delete head' => 'Das Löschen des Forums bestätigen',
+'Confirm delete subhead' => 'WICHTIG! Vor dem Löschen lesen',
+'Confirm delete info' => 'Wollen Sie wirklich das Forum <strong>%s</strong> löschen?',
+'Confirm delete warn' => 'WARNUNG! Mit dem Löschen eines Forums werden alle darin enthaltenen Beiträge ebenfalls gelöscht!',
+
+// Detailierte Bearbeitungsseite
+'Edit forum head' => 'Forum bearbeiten',
+'Edit details subhead' => 'Foren-Details bearbeiten',
+'Forum name label' => 'Foren-Name',
+'Forum description label' => 'Beschreibung (HTML)',
+'Category label' => 'Kategorie',
+'Sort by label' => 'Themen sortieren nach',
+'Last post' => 'Letztem Beitrag',
+'Topic start' => 'Themenstart',
+'Subject' => 'Betreff',
+'Redirect label' => 'Weiterleitungs-URL',
+'Redirect help' => 'Nur in leeren Foren verfügbar',
+'Group permissions subhead' => 'Die Gruppenberechtigungen für dieses Forum bearbeiten',
+'Group permissions info' => 'In diesem Formular kannst Du für jede Mitgliedergruppe spezifische Forenberechtigungen setzen. Wenn Du an den Foren-Gruppenberechtigungen keine Änderungen vorgenommen hast, siehst Du im folgenden die programmseitigen Voreinstellungen von %s. Administratoren haben immer alle Berechtigungen und sind davon ausgeschlossen. Berechtigungen, die abweichend von den programmseitigen Voreinstellungen gesetzt wurden, sind rot markiert. Das Kontrollfeld für die Berechtigung "Forum lesen" wird deaktiviert, wenn die betreffende Gruppe die Berechtigung "Board lesen" nicht hat. Für die Weiterleitung von Foren kann nur die Berechtigung "Forum lesen" bearbeitet werden.',
+'Read forum label' => 'Forum lesen',
+'Post replies label' => 'Antworten schreiben',
+'Post topics label' => 'Themen erstellen',
+'Revert to default' => 'Auf Standardberechtigungen zurücksetzen',
+
+);
diff --git a/lang/German/admin_groups.php b/lang/German/admin_groups.php
new file mode 100644
index 0000000..302769a
--- /dev/null
+++ b/lang/German/admin_groups.php
@@ -0,0 +1,92 @@
+<?php
+
+// Sprachdefinitionen, die in admin_groups.php verwendet werden
+$lang_admin_groups = array(
+
+'Must enter title message' => 'Du musst einen Gruppentitel eingeben.',
+'Title already exists message' => 'Eine Gruppe mit dem Titel <strong>%s</strong> existiert bereits.',
+'Default group redirect' => 'Voreingestellte Gruppe festgelegt. Leite weiter …',
+'Cannot remove default message' => 'Die voreingestellte Gruppe kann nicht entfernt werden. Um diese Gruppe löschen zu können, musst Du zunächst eine andere Gruppe als voreingestellte Gruppe festlegen.',
+'Group removed redirect' => 'Gruppe entfernt. Leite weiter …',
+'Group added redirect' => 'Gruppe hinzugefügt. Leite weiter …',
+'Group edited redirect' => 'Gruppe bearbeitet. Leite weiter …',
+
+'Add groups head' => 'Gruppen hinzufügen/festlegen',
+'Add group subhead' => 'Eine neue Gruppe hinzufügen',
+'New group label' => 'Neue Gruppe basiert auf',
+'New group help' => 'Wähle hier eine Mitgliedergruppe, auf deren Berechtigungseinstellungen die neue Gruppe basieren soll. Auf der nächsten Seite kannst Du dann die Einstellungen noch verfeinern.',
+'Default group subhead' => 'Voreingestellte Gruppe festlegen',
+'Default group label' => 'Voreingestellte Gruppe',
+'Default group help' => 'Neu registrierte Mitglieder werden dieser Mitgliedergruppe zugeordnet. Aus Sicherheitsgründen können die Moderator- und die Administrator-Gruppe nicht als voreingestellte Gruppe ausgewählt werden.',
+'Existing groups head' => 'Existierende Gruppen',
+'Edit groups subhead' => 'Gruppen bearbeiten/entfernen',
+'Edit groups info' => 'Die vordefinierten Gruppen Gäste, Administratoren, Moderatoren und Mitglieder können NICHT entfernt werden. Sie können jedoch bearbeitet werden. Bitte beachte außerdem, dass einige Optionen in einigen Gruppen nicht verfügbar sind (z.Bsp. die Berechtigung <em>Beiträge bearbeiten</em> für Gäste). Administratoren haben immer alle Berechtigungen.',
+'Edit link' => 'Bearbeiten',
+'Delete link' => 'Entfernen',
+'Group delete head' => 'Gruppe löschen',
+'Confirm delete subhead' => 'Löschen der Gruppe bestätigen',
+'Confirm delete info' => 'Willst Du wirklich die Gruppe <strong>%s</strong> löschen?',
+'Confirm delete warn' => 'WARNUNG! Nachdem eine Gruppe gelöscht wurde, kann sie NICHT wiederhergestellt werden.',
+'Delete group head' => 'Gruppe entfernen',
+'Move users subhead' => 'Aktuelle Mitglieder in eine andere Gruppen verschieben',
+'Move users info' => 'Die Gruppe <strong>%s</strong> hat aktuell <strong>%s</strong> Mitglieder. Bitte wähle eine Gruppe aus, welcher diese Mitglieder nach Entfernung der Gruppe zugeordnet werden sollen.',
+'Move users label' => 'Mitglieder verschieben nach',
+'Delete group' => 'Gruppe löschen',
+
+'Group settings head' => 'Gruppen-Einstellungen',
+'Group settings subhead' => 'Einstellung der Gruppen-Optionen und Berechtigungen',
+'Group settings info' => 'Unterhalb der Optionen und Berechtigungen siehst Du die voreingestellten Berechtigungen für diese Mitgliedergruppe. Diese Optionen werden nur dann wirksam, wenn keine Foren-spezifischen Berechtigungen gesetzt wurden.',
+'Group title label' => 'Gruppentitel',
+'User title label' => 'Mitgliedertitel',
+'User title help' => 'Der Rang den Mitglieder in dieser Grupper erlangt haben. Ohne Vorgabe wird der voreingestellte Rang verwendet ("%s").',
+'Promote users label' => 'Mitglieder befördern',
+'Promote users help' => 'Du kannst Mitglieder automatisch zu einer Gruppe befördern wenn diese eine bestimmte Anzahl von Beiträgen erreicht haben. Wähle "%s" zum deaktivieren. Aus Sicherheitsgründen kannst Du hier keine Administrator-Gruppe auswählen. Beachte zusätzlich, dass Änderungen der Gruppe aufgrund dieser Einstellung <strong>sofort</strong> wirksam werden. Die Anzahl der Beiträge, die Du hier einträgst entpsricht der Gesamtzahl des Nutzers und nicht der Anzahl der Beiträge, die als Mitglied dieser Gruppe erstellt wurden.',
+'Disable promotion' => 'Deaktiviere Beförderungen',
+
+'Mod privileges label' => 'Gibt Mitgliedern Moderator-Privilegien',
+'Mod privileges help' => 'Damit ein Mitglied dieser Gruppe diese moderieren kann, muss ihm/ihr die Berechtigung zum Moderieren einer oder mehrerer Foren zugeordnet werden. Dies erfolgt in der Mitglieder-Administration in dessen Profil.',
+'Edit profile label' => 'Erlaubt Moderatoren, Mitgliederprofile zu bearbeiten',
+'Edit profile help' => 'Wenn die Moderator-Privilegien aktiviert sind, können Mitglieder dieser Gruppe Mitgliederprofile bearbeiten.',
+'Rename users label' => 'Erlaubt Moderatoren, Mitglieder umzubenennen',
+'Rename users help' => 'Wenn die Moderator-Privilegien aktiviert sind, können Mitglieder dieser Gruppe Mitglieder umbenennen.',
+'Change passwords label' => 'Erlaubt Moderatoren, Passwörter zu ändern',
+'Change passwords help' => 'Wenn die Moderator-Privilegien aktiviert sind, können Mitglieder dieser Gruppe Mitgliederpasswörter ändern.',
+'Mod promote users label' => 'Erlaube Moderatoren Mitglieder zu befördern',
+'Mod promote users help' => 'Wenn Moderatoren-Rechte aktiv sind, erlaube Mitgliedern dieser Gruppe andere Mitglieder zu befördern.',
+'Ban users label' => 'Erlaubt Moderatoren, Mitglieder zu sperren',
+'Ban users help' => 'Wenn die Moderator-Privilegien aktiviert sind, können Mitglieder andere Mitglieder dieser Gruppe sperren.',
+'Read board label' => 'Board lesen',
+'Read board help' => 'Erlaubt den Mitgliedern dieser Gruppe, das Board anzusehen. Diese Einstellung wirkt sich auf alle Aspekte des Boards aus und kann nicht durch Foren-spezifische Einstellungen überschrieben werden. Ist die Einstellung "Nein", können sich die Mitglieder dieser Gruppe nur an-/abmelden und registrieren.',
+'View user info label' => 'Mitglieder-Information ansehen',
+'View user info help' => 'Erlaubt Mitgliedern, die Mitgliederliste und die Mitgliederprofile anzusehen.',
+'Post replies label' => 'Antworten posten',
+'Post replies help' => 'Erlaubt den Mitgliedern dieser Gruppe, auf Themen zu antworten.',
+'Post topics label' => 'Themen posten',
+'Post topics help' => 'Erlaubt den Mitgliedern dieser Gruppe, neue Themen zu posten.',
+'Edit posts label' => 'Antworten bearbeiten',
+'Edit posts help' => 'Erlaubt den Mitgliedern dieser Gruppe, ihre eigenen Antworten zu bearbeiten.',
+'Delete posts label' => 'Antworten löschen',
+'Delete posts help' => 'Erlaubt den Mitgliedern dieser Gruppe, ihre eigenen Antworten zu löschen.',
+'Delete topics label' => 'Themen löschen',
+'Delete topics help' => 'Erlaubt den Mitgliedern dieser Gruppe, eigene Themen zu löschen (einschließlich aller Antworten).',
+'Post links label' => 'Links in Beiträgen',
+'Post links help' => 'Erlaubt den Mitgliedern dieser Gruppe, Links in ihren Beiträgen zu nutzen. Diese Einstellung betrifft auch Signaturen und die im Profil des Mitgliedes angegebene Webseite.',
+'Set own title label' => 'Eigenen Mitgliedstitel festlegen',
+'Set own title help' => 'Erlaubt den Mitgliedern dieser Gruppe, einen eigenen Mitgliedstitel festzulegen.',
+'User search label' => 'Suche verwenden',
+'User search help' => 'Erlaubt den Mitgliedern dieser Gruppe, die Suche zu verwenden.',
+'User list search label' => 'Mitglieder suchen',
+'User list search help' => 'Erlaubt den Mitgliedern dieser Gruppe, in den Mitgliederlisten nach anderen Mitgliedern zu suchen.',
+'Send e-mails label' => 'Emails versenden',
+'Send e-mails help' => 'Erlaubt den Mitgliedern dieser Gruppe, anderen Mitgliedern Emails zu senden.',
+'Post flood label' => 'Antwort-Intervall',
+'Post flood help' => 'Anzahl der Sekunden, die Mitglieder dieser Gruppe zwischen 2 Antworten warten müssen. Mit 0 wird dies deaktiviert.',
+'Search flood label' => 'Such-Intervall',
+'Search flood help' => 'Anzahl der Sekunden, die Mitglieder dieser Gruppe zwischen 2 Suchvorgängen warten müssen. Mit 0 wird dies deaktiviert.',
+'E-mail flood label' => 'Email-Intervall',
+'E-mail flood help' => 'Anzahl der Sekunden, die Mitglieder dieser Gruppe zwischen dem Versand von Emails warten müssen. Mit 0 wird dies deaktiviert.',
+'Report flood label' => 'Meldungs-Intervall',
+'Report flood help' => 'Anzahl der Sekunden, die Mitglieder dieser Gruppe zwischen dem Versand von Meldungen warten müssen. Mit 0 wird dies deaktiviert.',
+'Moderator info' => 'Bitte beachten: Wenn ein Mitglied dieser Gruppe diese moderieren soll, muss ihm/ihr die Berechtigung zum Moderieren einer oder mehrerer Foren zugeordnet werden. Dies erfolgt in der Mitglieder-Administration in dessen Profil.',
+
+);
diff --git a/lang/German/admin_index.php b/lang/German/admin_index.php
new file mode 100644
index 0000000..3b2f257
--- /dev/null
+++ b/lang/German/admin_index.php
@@ -0,0 +1,64 @@
+<?php
+
+// Sprachdefinitionen, die in admin_index.php verwendet werden
+$lang_admin_index = array(
+
+'fopen disabled message' => 'Konnte nicht auf Aktualisierungen prüfen, da die Funktion \'allow_url_fopen\' auf diesem Server deaktiviert ist.',
+'Upgrade check failed message' => 'Die Prüfung auf Aktualisierungen ist aus unbekannten Gründen fehlgeschlagen.',
+'Running latest version message' => 'Du verwendest die aktuellste Version von FluxBB.',
+'New version available message' => 'Es wurde eine neue Version von FluxBB veröffentlicht. Du kannst diese unter %s herunterladen.',
+'Deleted install.php redirect' => 'Die Datei wurde erfolgreich entfernt. Leite weiter …',
+'Delete install.php failed' => 'Konnte install.php nicht entfernen. Bitte manuell löschen.',
+'Not available' => 'Nicht verfügbar',
+'Forum admin head' => 'Foren-Administration',
+'NA' => 'Keine Daten verfügbar',
+'Welcome to admin' => 'Willkommen in der FluxBB-Administration. Von hier aus kannst Du alle wichtigen Aspekte des Forums kontrollieren. In Abhängigkeit, ob Sie Administrator oder Moderator sind, kannst Du',
+'Welcome 1' => 'Kategorien und Foren verwalten.',
+'Welcome 2' => 'Forenweite Optionen und Einstellungen festlegen.',
+'Welcome 3' => 'Berechtigungen für Mitglieder und Gäste kontrollieren.',
+'Welcome 4' => 'IP-Statistiken der Mitglieder anzeigen.',
+'Welcome 5' => 'Mitglieder sperren',
+'Welcome 6' => 'Wörter zensieren.',
+'Welcome 7' => 'Mitgliedergruppen und Beförderungen festlegen.',
+'Welcome 8' => 'Alte Beiträge bereinigen.',
+'Welcome 9' => 'Eingesandte Meldungen bearbeiten.',
+'Alerts head' => 'Warnungen',
+'Install file exists' => 'Die Datei install.php existiert noch, sollte aber entfernt werden. %s.',
+'Delete install file' => 'Datei löschen',
+'About head' => 'Über FluxBB',
+'FluxBB version label' => 'FluxBB-Version',
+'Check for upgrade' => 'Auf Aktualisierung prüfen',
+'FluxBB version data' => 'v%s - %s',
+'Server statistics label' => 'Server-Statistiken',
+'View server statistics' => 'Server-Statistiken anzeigen',
+'Support label' => 'Support',
+'Forum label' => 'Forum',
+'IRC label' => 'IRC channel',
+
+// Language definitions used in admin_statistics.php
+'PHPinfo disabled message' => 'Die PHP-Funktion phpinfo() ist auf diesem Server deaktiviert.',
+'Server statistics head' => 'Server-Statistiken',
+'Server load label' => 'Server-Auslastung',
+'Server load data' => '%s - %s Besucher online',
+'Environment label' => 'Umgebung',
+'Environment data OS' => 'Betriebssystem: %s',
+'Show info' => 'Informationen anzeigen',
+'Environment data version' => 'PHP: %s - %s',
+'Environment data acc' => 'Beschleuniger: %s',
+'Turck MMCache' => 'Turk MMCache',
+'Turck MMCache link' => 'turck-mmcache.sourceforge.net',
+'ionCube PHP Accelerator' => 'ionCube PHP-Beschleuniger',
+'ionCube PHP Accelerator link' => 'www.php-accelerator.co.uk/',
+'Alternative PHP Cache (APC)' => 'Alternativer PHP-Cache (APC)',
+'Alternative PHP Cache (APC) link' => 'www.php.net/apc/',
+'Zend Optimizer' => 'Zend Optimizer',
+'Zend Optimizer link' => 'www.zend.com/products/guard/zend-optimizer',
+'eAccelerator' => 'eAccelerator',
+'eAccelerator link' => 'www.eaccelerator.net/',
+'XCache' => 'XCache',
+'XCache link' => 'xcache.lighttpd.net/',
+'Database label' => 'Datenbank',
+'Database data rows' => 'Zeilen: %s',
+'Database data size' => 'Größe: %s',
+
+);
diff --git a/lang/German/admin_maintenance.php b/lang/German/admin_maintenance.php
new file mode 100644
index 0000000..38d22fd
--- /dev/null
+++ b/lang/German/admin_maintenance.php
@@ -0,0 +1,40 @@
+<?php
+
+// Sprachdefinitionen, die in admin_maintenance.php verwendet werden
+$lang_admin_maintenance = array(
+
+'Maintenance head' => 'Foren-Wartung',
+'Rebuild index subhead' => 'Suchindex neu erstellen',
+'Rebuild index info' => 'Wenn Du in der Datenbank manuell Beiträge gelöscht, bearbeitet oder entfernt oder Probleme mit der Suche hast, solltest Du den Suchindex neu erstellen. Für eine optimale Performance solltest Du das Forum während der Neuerstellung des Suchindex in den Wartungsmodus versetzen. <strong>Die Neuerstellung des Suchindex kann längere Zeit in Anspruch nehmen und wird dabei die Auslastung des Servers erhöhen!</strong>',
+'Posts per cycle label' => 'Beiträge pro Durchlauf',
+'Posts per cycle help' => 'Die Anzahl der Beiträge, die pro Seitenansicht verarbeitet werden sollen. Wenn Du zum Beispiel 100 eingibst, werden die ersten einhundert Beiträge verarbeitet und dann die Seite neu geladen. Dies kann das Skript vor einem Zeitüberschreitungsfehler während der Neuindizierung bewahren.',
+'Starting post label' => 'Mit Beitrags-ID beginnen',
+'Starting post help' => 'Die Beitrags-ID, bei der mit der Neuerstellung des Suchindex begonnen werden soll. Der voreingestellte Wert ist die erste, in der Datenbank vorhandene ID. Normalerweise musst Du diesen Wert nicht ändern.',
+'Empty index label' => 'Index leeren',
+'Empty index help' => 'Aktiviere dieses Kontrollfeld, wenn der Suchindex vor dessen Neuerstellung geleert werden soll.',
+'Rebuild completed info' => 'Wenn der Prozess abgeschlossen ist, wirst Du auf diese Seite weitergeleitet. Wenn Du die Neuindizierung abbrechen möchtest, notiere dir die zuletzt verarbeitete Themen-ID und gib diese ID+1 dann im Feld "Mit dieser Themen-ID beginnen" ein, wenn die Neuindizierung fortgesetzt werden soll (Das Kontrollfeld "Index leeren" muss nicht aktiviert werden).',
+'Rebuild index' => 'Index neu erstellen',
+'Rebuilding search index' => 'Der Suchindex wird neu erstellt',
+'Rebuilding index info' => 'Der Suchindex wird jetzt neu erstellt. Daher hast Du nun etwas Zeit für eine kleine Kaffeepause :-)',
+'Processing post' => 'Der Beitrag <strong>%s</strong> wird verarbeitet …',
+'Click here' => 'Hier klicken',
+'Javascript redirect failed' => 'Die automatische Weiterleitung ist fehlgeschlagen. %s um weiterzumachen …',
+'Posts must be integer message' => 'Der Wert für "Beiträge pro Durchlauf" muss positiv und ganzzahlig sein.',
+'Days must be integer message' => 'Der Wert für "Themenalter" muss positiv und ganzzahlig sein.',
+'No old topics message' => 'Es gibt keine Beiträge, die älter sind als %s Tage. Bitte setze den Wert von "Themenalter" herab und versuche es noch einmal.',
+'Posts pruned redirect' => 'Beiträge bereinigt. Leite weiter …',
+'Prune head' => 'Bereinigung',
+'Prune subhead' => 'Alte Beiträge bereinigen',
+'Days old label' => 'Themenalter',
+'Days old help' => 'Das Alter eines Themas in Tagen, nach denen das Thema gelöscht wird. Wenn Du hier zum Beispiel 30 eingibst, wird jedes Thema gelöscht, dessen letzter Beitrag älter als 30 Tage ist.',
+'Prune sticky label' => 'Fixierte Themen bereinigen',
+'Prune sticky help' => 'Ist dieses Feld aktiviert, werden auch fixierte Beiträge bereinigt.',
+'Prune from label' => 'Zu bereinigendes Forum',
+'All forums' => 'Alle Foren',
+'Prune from help' => 'Das Forum, dessen Beiträge bereinigt werden sollen.',
+'Prune info' => 'Setze diese Funktion nur vorsichtig ein. <strong>Bereinigte Beiträge können NICHT wiederhergestellt werden.</strong> Aus Performance-Gründen solltest Du das Forum vor einer Beitragsbereinigung in den Wartungsmodus versetzen.',
+'Confirm prune subhead' => 'Bestätigung der Beitragsbereinigung',
+'Confirm prune info' => 'Willst Du wirklich alle Themen, die älter als %s Tage sind, aus dem Forum %s entfernen (%s Themen)?',
+'Confirm prune warn' => 'WARNUNG! Die Bereinigung von Beiträgen lässt sich NICHT wieder rückgängig machen.',
+
+);
diff --git a/lang/German/admin_options.php b/lang/German/admin_options.php
new file mode 100644
index 0000000..ea8b956
--- /dev/null
+++ b/lang/German/admin_options.php
@@ -0,0 +1,227 @@
+<?php
+
+// Sprachdefinitionen, die in admin_options.php verwendet werden
+$lang_admin_options = array(
+
+'Bad HTTP Referer message' => 'Falscher HTTP_REFERER. Wenn Du diese Foren innerhalb Ihrer Verzeichnisstruktur oder auf eine andere Domain verschoben hast, musst Du die Basis-URL in der Datenbank manuell aktualisieren (schau in der Tabelle config nach o_base_url) und leere dann den Zwischenspeicher, indem Du alle im Verzeichnis /cache enthaltenen .php-Dateien löschst.',
+'Must enter title message' => 'Du musst ein Titel für dieses Board eingeben.',
+'Invalid e-mail message' => 'Die für den Administrator eingegebene Email-Adresse ist ungültig.',
+'Invalid webmaster e-mail message' => 'Die für den Webmaster eingegebene Email-Adresse ist ungültig.',
+'SMTP passwords did not match' => 'Du musst zweimal das exakt gleiche SMTP-Passwort eingeben um es zu ändern.',
+'Enter announcement here' => 'Gib hier Deine Ankündigungen ein.',
+'Enter rules here' => 'Gib hier Deine Nutzungsbedingungen ein.',
+'Default maintenance message' => 'Die Foren sind aufgrund von Wartungsarbeiten vorübergehend geschlossen. Bitte versuch es in ein paar Minuten noch einmal.',
+'Timeout error message' => 'Der Wert für "Online-Zeitüberschreitung" muss kleiner sein als der Wert für "Besuchszeitüberschreitung".',
+'Options updated redirect' => 'Optionen aktualisiert. Leite weiter …',
+'Options head' => 'Optionen',
+
+// Sektion Grundlegendes
+'Essentials subhead' => 'Allgemeines',
+'Board title label' => 'Board-Titel',
+'Board title help' => 'Der Titel dieses Boards (wird auf jeder Seite ganz oben angezeigt). Dieses Feld darf <strong>KEIN</strong> HTML enthalten.',
+'Board desc label' => 'Board-Beschreibung',
+'Board desc help' => 'Eine kurze Beschreibung dieses Boards (wird auf jeder Seite ganz oben angezeigt). Dieses Feld kann HTML enthalten.',
+'Base URL label' => 'Basis-URL',
+'Base URL help' => 'Die vollständige URL des Forums ohne Folge-Slash (z.Bsp. http://www.meine-domain.de/foren). Damit alle Funktionen für Administratoren und Moderatoren ausgeführt werden können, <strong>MUSS</strong> dieser Eintrag richtig sein. Wenn Du einen Fehler "Falscher Referer" bekommst, ist die Basis-URL höchstwahrscheinlich falsch.',
+'Base URL problem' => 'Deine Installtion unterstützt nicht die automatische Konvertierung von internationalen Domain-Namen. Da Deine URL Sonderzeichen enthält, <strong>musst</strong> Du einen Online-Konverter nutzen um "Bad referer"-Fehler zu vermeiden.',
+'Timezone label' => 'Voreingestellte Zeitzone',
+'Timezone help' => 'Die voreingestellte Zeitzone für Gäste und neu registrierte Mitglieder dieses Boards.',
+'DST label' => 'Sommerzeit verwenden',
+'DST help' => 'Wähle hier, ob die Sommerzeit verwendet werden soll (der aktuellen Zeit wird 1 Stunde hinzu addiert).',
+'Language label' => 'Voreingestellte Sprache',
+'Language help' => 'Das ist die voreingestellte Sprache für Gäste und Mitglieder, die die Voreinstellungen in Ihrem Profil nicht geändert haben. Wenn Du ein Sprachpaket entfernt hast, muss dieses aktualisiert werden.',
+'Default style label' => 'Voreingestellter Style',
+'Default style help' => 'Das ist der voreingestellte Style, der für Gäste und Mitglieder, die die Voreinstellungen nicht geändert haben, verwendet wird.',
+
+// Grundlegende Sektion für die Zeitzonen-Einstellung
+'UTC-12:00' => '(WEZ-12:00) Internationale Datumsgrenze West',
+'UTC-11:00' => '(WEZ-11:00) Niue, Samoa',
+'UTC-10:00' => '(WEZ-10:00) Hawaii-Aleuten, Cook-Inseln',
+'UTC-09:30' => '(WEZ-09:30) Marquesas-Inseln',
+'UTC-09:00' => '(WEZ-09:00) Alaska, Gambier-Inseln',
+'UTC-08:30' => '(WEZ-08:30) Pitcairn-Inseln',
+'UTC-08:00' => '(WEZ-08:00) Pazifik',
+'UTC-07:00' => '(WEZ-07:00) Mountain',
+'UTC-06:00' => '(WEZ-06:00) Zentral',
+'UTC-05:00' => '(WEZ-05:00) Osten',
+'UTC-04:00' => '(WEZ-04:00) Atlantik',
+'UTC-03:30' => '(WEZ-03:30) Neufundland',
+'UTC-03:00' => '(WEZ-03:00) Amazonas, Zentral-Grönland',
+'UTC-02:00' => '(WEZ-02:00) Mittlerer Atlantik',
+'UTC-01:00' => '(WEZ-01:00) Azoren, Cape Verde, Östliches Grönland',
+'UTC' => '(WEZ) Westeuropäische Zeit, Greenwich',
+'UTC+01:00' => '(WEZ+01:00) Mitteleuropäische Zeit (MEZ), Westafrika',
+'UTC+02:00' => '(WEZ+02:00) Osteuropäische Zeit, Zentralafrika',
+'UTC+03:00' => '(WEZ+03:00) Ostafrika',
+'UTC+03:30' => '(WEZ+03:30) Iran',
+'UTC+04:00' => '(WEZ+04:00) Moskau, Golf, Samara',
+'UTC+04:30' => '(WEZ+04:30) Afghanistan',
+'UTC+05:00' => '(WEZ+05:00) Pakistan',
+'UTC+05:30' => '(WEZ+05:30) Indien, Sri Lanka',
+'UTC+05:45' => '(WEZ+05:45) Nepal',
+'UTC+06:00' => '(WEZ+06:00) Bangladesh, Bhutan, Jekaterinburg',
+'UTC+06:30' => '(WEZ+06:30) Cocos-Inseln, Myanmar',
+'UTC+07:00' => '(WEZ+07:00) Indochina, Nowosibirsk',
+'UTC+08:00' => '(WEZ+08:00) Groß-China, Westliches Australien, Krasnoyarsk',
+'UTC+08:45' => '(WEZ+08:45) Südöstliches West-Australien',
+'UTC+09:00' => '(WEZ+09:00) Japan, Korea, Chita, Irkutsk',
+'UTC+09:30' => '(WEZ+09:30) Zentral-Australian',
+'UTC+10:00' => '(WEZ+10:00) Ost-Australien',
+'UTC+10:30' => '(WEZ+10:30) Lord Howe',
+'UTC+11:00' => '(WEZ+11:00) Solomon-Inseln, Wladiwostok',
+'UTC+11:30' => '(WEZ+11:30) Norfolk-Inseln',
+'UTC+12:00' => '(WEZ+12:00) Neuseeland, Fiji, Magadan',
+'UTC+12:45' => '(WEZ+12:45) Chatham-Inseln',
+'UTC+13:00' => '(WEZ+13:00) Tonga, Phoenix-Inseln, Kamtschatka',
+'UTC+14:00' => '(WEZ+14:00) Grenz-Inseln',
+
+// Sektion Zeitüberschreitung
+'Timeouts subhead' => 'Zeit und Zeitüberschreitungen',
+'Time format label' => 'Zeitformat',
+'PHP manual' => 'PHP-Dokumentation',
+'Time format help' => '[Aktuelles Format: %s]. Weitere Informationen zu den Formatierungs-Optionen findest Du in der %s.',
+'Date format label' => 'Datumsformat',
+'Date format help' => '[Aktuelles Format: %s]. Weitere Informationen zu den Formatierungs-Optionen findest Du in der %s.',
+'Visit timeout label' => 'Besuchszeitüberschreitung',
+'Visit timeout help' => 'Anzahl der Sekunden, die ein Mitglied im Forum nichts getan haben muss, bis die Daten seines letzten Besuchs aktualisiert werden (dies hat primär Auswirkungen auf die Anzeige neuer Beiträge).',
+'Online timeout label' => 'Online-Zeitüberschreitung',
+'Online timeout help' => 'Anzahl der Sekunden, die ein Mitglied im Forum nichts getan haben muss, bis es aus der Liste der Mitglieder, die online sind, entfernt wird.',
+'Redirect time label' => 'Weiterleitungsdauer',
+'Redirect time help' => 'Anzahl der Sekunden, die vor einer Weiterleitung gewartet werden soll. Ist der Wert 0, wird keine Weiterleitungsseite angezeigt (nicht empfohlen).',
+
+// Sektion Anzeige
+'Display subhead' => 'Anzeige',
+'Version number label' => 'Versionsnummer',
+'Version number help' => 'Zeigt die FluxBB-Versionsnummer in der Fußzeile an.',
+'Info in posts label' => 'Mitglieder-Informationen in Beiträgen',
+'Info in posts help' => 'Zeigt in der Themenansicht Informationen über den Autor des Beitrags unter dem Mitgliedsnamen an. Die Einstellung hat Auswirkungen auf den Ort, das Registrierungsdatum, den Beitragszähler und die Kontakt-Links (Email und Webseite).',
+'Post count label' => 'Mitglieder-Beitragszähler',
+'Post count help' => 'Zeigt die bisherige Anzahl der Beiträge eines Mitgliedes an (hat Auswirkungen auf die Themenansicht, das Profil und die Mitgliederliste).',
+'Smilies label' => 'Smilies in Beiträgen',
+'Smilies help' => 'Konvertiert Smilies in Beiträgen in kleine grafische Darstellungen.',
+'Smilies sigs label' => 'Smilies in Signaturen',
+'Smilies sigs help' => 'Konvertiert Smilies in den Mitgliedersignaturen in kleine grafische Darstellungen.',
+'Clickable links label' => 'Klickbare Links erzeugen',
+'Clickable links help' => 'Ist diese Option aktiviert, versucht FluxBB, eingegebene URLs in Beiträgen automatisch zu erkennen und klickbare Hyperlinks daraus zu machen.',
+'Topic review label' => 'Themen-Ansicht',
+'Topic review help' => 'Die maximale Anzahl der Beiträge, die angezeigt werden, wenn ein neuer Beitrag verfasst wird (jeweils der neueste zuerst). Mit 0 wird dies deaktiviert.',
+'Topics per page label' => 'Themen pro Seite',
+'Topics per page help' => 'Die voreingestellte Anzahl an Themen, die in einem Forum pro Seite angezeigt werden. Die Mitglieder können diese Einstellung individuell anpassen.',
+'Posts per page label' => 'Beiträge pro Seite',
+'Posts per page help' => 'Die voreingestellte Anzahl an Beiträgen, die pro Seite zu einem Thema angezeigt werden. Die Mitglieder können diese Einstellung individuell anpassen.',
+'Indent label' => 'Größe der Einzugs',
+'Indent help' => 'Wird dieser Wert auf 8 gesetzt, wird ein regulärer Tabulator verwendet, um den Text innerhalb der [code][/code]-Tags anzuzeigen. Anderenfalls wird diese Anzahl Leerzeichen verwendet, um den Text einzurücken.',
+'Quote depth label' => 'Maximale [quote] Tiefe',
+'Quote depth help' => 'Die maximale Anzahl an [quote]-Tags, die in anderen [quote]-Tags enthalten sein können. Tiefer verschachtelte Tags werden nicht mehr angezeigt.',
+
+// Sektion Features
+'Features subhead' => 'Funktionen',
+'Quick post label' => 'Schnellantwort',
+'Quick post help' => 'Wird dies aktiviert, zeigt FluxBB unterhalb des Themas einen Textbereich an, über den eine schnelle Antwort zu diesem Thema erstellt werden kann. Auf diesem Weg können die Mitglieder Ihre Antworten direkt in der Themen-Ansicht eingeben.',
+'Users online label' => 'Mitglieder online',
+'Users online help' => 'Zeigt auf der Übersichtsseite Informationen an, wie viele Gäste und Mitglieder sich aktuell im Forum aufhalten.',
+'Censor words label' => 'Wortzensur',
+'Censor words help' => 'Aktiviere dies, um bestimmte Wörter im Forum zu zensieren. Weitere Informationen dazu finden Sie unter %s.',
+'Signatures label' => 'Signaturen',
+'Signatures help' => 'Erlaubt den Mitgliedern, ihren Beiträgen eine Signatur anzuhängen.',
+'User has posted label' => 'Mitglied hat einen Beitrag verfasst',
+'User has posted help' => 'Diese Funktion zeigt einen Punkt vor jedem Thema in der viewforum.php an, wenn das aktuell angemeldete Mitglied in diesem Thema irgendwann einen Beitrag verfasst hat. Deaktivieren Sie dies, falls Sie eine hohe Serverauslastung festgestellt haben.',
+'Topic views label' => 'Thema gelesen',
+'Topic views help' => 'Aktiviert die Zählung, wie oft ein Thema gelesen wurde. Deaktivieren Sie dies, falls Sie eine hohe Serverauslastung festgestellt haben.',
+'Quick jump label' => 'Schnellsprung',
+'Quick jump help' => 'Aktiviert den Schnellsprung-(Sprung zum Forum)-Auswahlliste.',
+'GZip label' => 'GZip verwenden',
+'GZip help' => 'Wird dies aktiviert, wird FluxBB die Inhalte via gzip komprimiert an die Browser senden. Dies reduziert die Bandbreite etwas, erhöht aber die Serverauslastung. Für diese Funktion muss PHP für die Verwendung der zlib konfiguriert sein (--with-zlib). Hinweis: Wenn Du bereits ein Apache-Modul wie etwa mod_gzip oder mod_deflate verwendest, um PHP-Skripte zu komprimieren, sollten Sie diese Funktion deaktivieren.',
+'Search all label' => 'Alle Foren durchsuchen',
+'Search all help' => 'Wird dies deaktiviert, wird nur ein Forum pro Durchlauf durchsucht. Wenn die Serverauslastung aufgrund der Suchvorgänge zu hoch ist, solltest Du dies deaktivieren.',
+'Menu items label' => 'Zusätzliche Menü-Einträge',
+'Menu items help' => 'Wenn Du in dieses Textfeld HTML-Hyperlinks eingibst, werden diese Einträge dem Navigations-Menü in der Kopfzeile jeder Seite hinzugefügt. Das Format für das Hinzufügen von neuen Links ist folgendes X = <a href="URL">LINK</a>. Dabei ist X die Position, an welcher der neue Link eingefügt werden soll (z.B. 0 zum Einfügen am Anfang des Menüs und 2 zum Einfügen nach der "Mitgliederliste"). Die einzelnen Einträge müssen durch einen Zeilenumbruch voneinander getrennt werden.',
+
+// Sektion Feeds
+'Feed subhead' => 'Syndication',
+'Default feed label' => 'Voreingestellter Feed-Typ',
+'Default feed help' => 'Wähle den anzuzeigenden Feed-Typ. Hinweis: Die Auswahl "Ohne Vorgabe" daktiviert nicht die Feeds, sondern versteckt sie nur.',
+'None' => 'Ohne Vorgabe',
+'RSS' => 'RSS',
+'Atom' => 'Atom',
+'Feed TTL label' => 'Zeitdauer für die Zwischenspeicherung von Feeds',
+'Feed TTL help' => 'Damit die Nutzung von Feeds die Ressourcen deines Servers weniger belastet, können Feeds zwischengespeichert werden.',
+'No cache' => 'Nicht zwischenspeichern',
+'Minutes' => '%d Minuten',
+
+// Sektion Meldungen
+'Reports subhead' => 'Meldungen',
+'Reporting method label' => 'Melde-Methode',
+'Internal' => 'Intern',
+'By e-mail' => 'Per Email',
+'Both' => 'Beides',
+'Reporting method help' => 'Wähle hier eine Methode aus, wie Meldungen über Themen/Beiträge behandelt werden sollen. Du hast mehrere Optionen zur Auswahl - entweder werden die Meldungen über ein internes Berichtssystem verarbeitet oder via Email an eine Mailingliste (siehe unten) versandt werden. Es können auch beide Optionen parallel verwendet werden.',
+'Mailing list label' => 'Mailingliste',
+'Mailing list help' => 'Eine durch Kommata getrennte Liste von Empfängern. An diese Personen werden die eingehenden Foren-Meldungen versandt.',
+
+// Sektion Avatare
+'Avatars subhead' => 'Avatare',
+'Use avatars label' => 'Avatare verwenden',
+'Use avatars help' => 'Wird dies aktiviert, können Mitglieder einen eigenen Avatar hochladen, der dann unter dem Titel angezeigt wird.',
+'Upload directory label' => 'Avatar-Verzeichnis',
+'Upload directory help' => 'Das Verzeichnis, in welches die Avatare hochgeladen werden (relativ zum Wurzelverzeichnis von FluxBB). PHP muss für dieses Verzeichnis eine Schreibberechtigung haben.',
+'Max width label' => 'Maximale Breite',
+'Max width help' => 'Die maximal erlaubte Breite des Avatars in Pixel (empfohlen 60).',
+'Max height label' => 'Maximale Höhe',
+'Max height help' => 'Die maximal erlaubte Höhe des Avatars in Pixel (empfohlen 60).',
+'Max size label' => 'Maximale Größe',
+'Max size help' => 'Die maximal erlaubte Dateigröße des Avatars in Bytes (empfohlen 10240).',
+
+// Sektion Email
+'E-mail subhead' => 'Email',
+'Admin e-mail label' => 'Administratoren-Email',
+'Admin e-mail help' => 'Die Email-Adresse des Foren-Administrators.',
+'Webmaster e-mail label' => 'Webmaster-Email',
+'Webmaster e-mail help' => 'Das ist die Email-Adresse, die als Absender für alle vom Forum versandten Emails verwendet wird.',
+'Forum subscriptions label' => 'Foren-Abonnements',
+'Forum subscriptions help' => 'Ermöglicht den Mitgliedern, Foren zu abonnieren (werden bei neuen Themen in diesem Forum via Email informiert).',
+'Topic subscriptions label' => 'Themen-Abonnements',
+'Topic subscriptions help' => 'Ermöglicht den Mitgliedern, Themen zu abonnieren (werden bei neuen Beiträgen zu diesem Thema via Email informiert).',
+'SMTP address label' => 'SMTP-Server-Adresse',
+'SMTP address help' => 'Die Adresse des externen SMTP-Server, mit dem die Emails versandt werden sollen. Du kannst hier einen eigenen Port festlegen, falls der SMTP-Server nicht mit dem Standard-Port 25 arbeitet (Beispiel: mail.meinhost.de:3580). Ohne Eintrag wird das lokale Mail-Programm verwendet.',
+'SMTP username label' => 'SMTP-Benutzername',
+'SMTP username help' => 'Benutzername für den SMTP-Server. Gib nur dann einen Benutzernamen ein, wenn dieser vom SMTP-Server angefordert wird (bei den meisten Servern ist <strong>keine</strong> Authentifizierung erforderlich).',
+'SMTP password label' => 'SMTP-Passwort',
+'SMTP change password help' => 'Aktiviere dies um das aktuell gespeicherte Passwort zu ändern oder zu löschen.',
+'SMTP password help' => 'Passwort für den SMTP-Server. Gib nur dann ein Passwort ein, wenn dieses vom SMTP-Server angefordert wird (bei den meisten Servern ist <strong>keine</strong> Authentifizierung erforderlich).',
+'SMTP SSL label' => 'SMTP mit SSL verschlüsseln',
+'SMTP SSL help' => 'Verschlüsselt die Verbindung mit dem SMTP-Server mit SSL. Sollte nur dann verwendet werden, wenn Dein SMTP-Server es erfordert und Deine PHP-Version SSL unterstützt.',
+
+// Sektion Registrierungen
+'Registration subhead' => 'Registrierung',
+'Allow new label' => 'Neue Registrierungen erlauben',
+'Allow new help' => 'Legt fest, ob dieses Forum neue Registrierungen akzeptiert. Sollte nur unter besonderen Umständen deaktiviert werden.',
+'Verify label' => 'Registrierung bestätigen',
+'Verify help' => 'Wird dies aktiviert, wird den Neu-Mitgliedern nach der Registrierung ein zufällig erstelltes Password via Email zugesandt. Erst dann können sich die neuen Mitglieder anmelden und das Passwort in ihrem Profil ändern. Auch, wenn die ursprünglichen Registrierungs-Email-Adresse in eine andere geändert wird, ist eine Bestätigung durch das Mitglied erforderlich. Dies ist ein effektiver Weg, um mißbräuchliche Registrierungen zu vermeiden und sicherzustellen, dass in den Profilen der Mitglieder die "richtigen" Email-Adressen eingetragen sind.',
+'Report new label' => 'Neue Registrierungen melden',
+'Report new help' => 'Wird dies aktiviert, benachrichtigt FluxBB die Mitglieder auf der Mailingliste (siehe unten), wenn sich neue Mitglieder in den Foren registrieren.',
+'Use rules label' => 'Nutzungsbedingungen',
+'Use rules help' => 'Wird dies aktiviert, müssen neue Mitglieder bei der Registrierung den Nutzungsbedingungen zustimmen (Text unten eingeben). Die Nutzungsbedingungen sind immer über einen Link in der Navigationsleiste im Kopf jeder Seite zu erreichen.',
+'Rules label' => 'Gib hier die Nutzungsbedingungen ein',
+'Rules help' => 'Hier kannst Du die Nutzungsbedingungen und sonstige Informationen eingeben, die sich jedes neue Mitglied durchlesen und akzeptieren muss. Um diese Option zu aktivieren, gib in dieses Feld Deine Nutzungsbedingungen ein - ohne Eintrag bleibt diese Option deaktiviert. Dieser Text wird nicht wie reguläre Beiträge verarbeitet und kann daher HTML enthalten.',
+'E-mail default label' => 'Standard-Email-Einstellungen',
+'E-mail default help' => 'Wähle die die Privatsphären-Einstellungen für neu registrierte Mitglieder.',
+'Display e-mail label' => 'Anderen Mitgliedern die Email-Adresse anzeigen.',
+'Hide allow form label' => 'Die Email-Adresse verbergen, aber die Kontaktaufnahme via Formular erlauben.',
+'Hide both label' => 'Die Email-Adresse verbergen und die Kontaktaufnahme via Formular verbieten.',
+
+// Sektion Ankündigungen
+'Announcement subhead' => 'Ankündigungen',
+'Display announcement label' => 'Ankündigungen anzeigen',
+'Display announcement help' => 'Aktiviere diese Option, um die unten angezeigte Nachricht oberhalb der Foren anzuzeigen.',
+'Announcement message label' => 'Text der Ankündigung',
+'Announcement message help' => 'Dieser Text wird nicht wie reguläre Beiträge verarbeitet und kann daher HTML enthalten.',
+
+// Sektion Wartung
+'Maintenance subhead' => 'Wartung',
+'Maintenance mode label' => 'Wartungs-Modus',
+'Maintenance mode help' => 'Wird diese Option aktiviert, können nur die Administratoren auf das Board zugreifen. Dieser Modus sollte verwendet werden, wenn zeitlich beschränkte Wartungsarbeiten am Board erforderlich sind. WARNUNG! Melde Dich NIEMALS ab, wenn sich das Board im Wartungs-Modus befindet. Du kannst Dich anschließend niemals wieder anmelden.',
+'Maintenance message label' => 'Wartungs-Nachricht',
+'Maintenance message help' => 'Die Nachricht, die den Besuchern der Webseite angezeigt wird, wenn sich das Board im Wartungs-Modus befindet. Bleibt dieses Feld leer, wird eine voreingestellte Nachricht verwendet. Dieser Text wird nicht wie reguläre Beiträge verarbeitet und kann daher HTML enthalten.',
+
+);
diff --git a/lang/German/admin_permissions.php b/lang/German/admin_permissions.php
new file mode 100644
index 0000000..58c63ed
--- /dev/null
+++ b/lang/German/admin_permissions.php
@@ -0,0 +1,36 @@
+<?php
+
+// Sprachdefinitionen, die in admin_permissions.php verwendet werden
+$lang_admin_permissions = array(
+
+'Perms updated redirect' => 'Berechtigungen aktualisiert. Leite weiter …',
+'Permissions head' => 'Berechtigungen',
+'Posting subhead' => 'Beitrag',
+'BBCode label' => 'BBCode',
+'BBCode help' => 'Erlaubt die Verwendung von BBCode in Beiträgen (empfohlen).',
+'Image tag label' => 'Image-Tag',
+'Image tag help' => 'Erlaubt die Verwendung des BBCode [img][/img]-Tags in Beiträgen.',
+'All caps message label' => 'Großbuchstaben-Nachrichten erlauben',
+'All caps message help' => 'Erlaubt Nachrichten, die nur Großbuchstaben enthalten.',
+'All caps subject label' => 'Großbuchstaben-Betreff erlauben',
+'All caps subject help' => 'Erlaubt Themen-Betreffs, die nur Großbuchstaben enthalten.',
+'Require e-mail label' => 'Gast-Email erforderlich',
+'Require e-mail help' => 'Erfordert von Gästen die Angabe einer Email-Adresse, wenn diese einen Beitrag erstellen wollen.',
+'Signatures subhead' => 'Signaturen',
+'BBCode sigs label' => 'BBCodes in Signaturen',
+'BBCode sigs help' => 'Erlaubt die Verwendung von BBCodes in Mitglieder-Signaturen.',
+'Image tag sigs label' => 'Image-Tag in Signaturen',
+'Image tag sigs help' => 'Erlaubt die Verwendung des BBCode [img][/img]-Tags in Mitglieder-Signaturen (nicht empfohlen).',
+'All caps sigs label' => 'Großbuchstaben-Signaturen',
+'All caps sigs help' => 'Erlaubt Signaturen, die nur aus Großbuchstaben bestehen.',
+'Max sig length label' => 'Maximale Signaturnlänge',
+'Max sig length help' => 'Legt die maximale Anzahl an Zeichen fest, die eine Mitglieder-Signatur enthalten darf.',
+'Max sig lines label' => 'Maximale Signaturzeilen-Anzahl',
+'Max sig lines help' => 'Legt die maximale Anzahl von Zeilen fest, die eine Mitglieder-Signatur enthalten darf.',
+'Registration subhead' => 'Registrierung',
+'Banned e-mail label' => 'Gesperrte Email-Adressen erlauben',
+'Banned e-mail help' => 'Erlaubt Mitgliedern, sich mit einer gesperrten Email-Adresse/Domain zu registrieren oder die Email-Adresse auf einen solchen Wert zu ändern. Bleiben die Voreinstellungen unverändert (ja), wird diese Aktion erlaubt, aber es wird eine Warn-Nachricht an die Mailingliste versandt (ein effektiver Weg, um Mehrfachregistrierungen zu erkennen).',
+'Duplicate e-mail label' => 'Doppelte Email-Adressen erlauben',
+'Duplicate e-mail help' => 'Legt fest, ob sich ein neues Mitglied mit einer Email-Adresse registrieren darf, die bereits von einem anderen Mitglied verwendet wird. Ist dies erlaubt, wird eine Warn-Nachricht an die Mailingliste versandt, sobald die Dopplung festgestellt wird.',
+
+);
diff --git a/lang/German/admin_reports.php b/lang/German/admin_reports.php
new file mode 100644
index 0000000..7c0e7dd
--- /dev/null
+++ b/lang/German/admin_reports.php
@@ -0,0 +1,21 @@
+<?php
+
+// Sprachdefinitionen, die in admin_reports.php verwendet werden
+$lang_admin_reports = array(
+
+'Report zapped redirect' => 'Meldung als gelesen markiert. Leite weiter …',
+'New reports head' => 'Neue Meldungen',
+'Deleted user' => 'Gelöschtes Mitglied',
+'Deleted' => 'Gelöscht',
+'Post ID' => 'Beitrag #%s',
+'Report subhead' => 'Gemeldet %s',
+'Reported by' => 'Gemeldet von %s',
+'Reason' => 'Grund',
+'Zap' => 'Als gelesen markieren',
+'No new reports' => 'Aktuell gibt es keine neuen Meldungen.',
+'Last 10 head' => 'Die 10 letzten gelesenen Meldungen',
+'NA' => 'Keine Daten verfügbar',
+'Zapped subhead' => 'Gelöscht %s von %s',
+'No zapped reports' => 'Aktuell gibt es keine ungelesenen Meldungen.',
+
+);
diff --git a/lang/German/admin_users.php b/lang/German/admin_users.php
new file mode 100644
index 0000000..69cb53f
--- /dev/null
+++ b/lang/German/admin_users.php
@@ -0,0 +1,110 @@
+<?php
+
+// Sprachdefinitionen, die in admin_users.php verwendet werden
+$lang_admin_users = array(
+
+'Non numeric message' => 'Du hast einen nichtnumerischen Wert in eine Spalte für numerische Werte eingetragen.',
+'Invalid date time message' => 'Du hast einen ungültigen Wert für Datum und/oder Zeit eingegeben.',
+'Not verified' => 'Nicht geprüft',
+
+// Aktionen: Massenlöschungen/-sperrungen etc.
+'No users selected' => 'Keine Mitglieder ausgewählt.',
+'No move admins message' => 'Aus Sicherheitsgründen ist es nicht erlaubt, mehrere Administratoren auf einmal einer anderen Gruppe zuzuordnen. Administratoren können nur über deren Mitgliedsprofil einer anderen Gruppe zugeordnet werden.',
+'No delete admins message' => 'Administratoren können nicht gelöscht werden. Um Administratoren zu löschen, musst Du diese zunächst einer anderen Mitgliedergruppe zuordnen.',
+'No ban admins message' => 'Administratoren können nicht gesperrt werden. Um Administratoren zu sperren, musst du diese zunächst einer anderen Mitgliedergruppe zuordnen.',
+'No ban mods message' => 'Moderatoren können nicht gesperrt werden. Um Moderatoren zu sperren, musst Du diese zunächst einer anderen Mitgliedergruppe zuordnen.',
+'Move users' => 'Mitgliedergruppe ändern',
+'Move users subhead' => 'Wähle eine neue Mitgliedergruppe',
+'New group label' => 'Neue Gruppe',
+'New group help' => 'Wähle die Gruppe, der die ausgewählten Mitglieder zugeordnet werden sollen. Aus Sicherheitsgründen ist es nicht möglich, der Administratorgruppe mehrere Mitglieder auf einmal zuzuordnen.',
+'Invalid group message' => 'Ungültige Gruppen-ID.',
+'Users move redirect' => 'Mitgliedergruppe geändert. Leite weiter …',
+'Delete users' => 'Mitglieder löschen',
+'Confirm delete legend' => 'WICHTIG: Bitte lesen, bevor du Mitglieder löschst',
+'Confirm delete info' => 'Bitte bestätige, dass du diese Mitglieder wirklich löschen möchtest.',
+'Delete posts' => 'Alle durch diese Mitglieder erstellten Beiträge und Themen löschen.',
+'Delete warning' => 'Warnung! Die Löschung von Mitgliedern und/oder Beiträgen kann nicht rückgängig gemacht werden. Wenn Du dich dafür entscheidest, die Beiträge dieser Mitglieder nicht zu löschen, können die Beiträge später nur noch manuell gelöscht werden.',
+'Users delete redirect' => 'Mitglieder gelöscht. Leite weiter …',
+'Ban users' => 'Mitglieder sperren',
+'Message expiry subhead' => 'Sperrnachricht und Ablaufdatum',
+'Ban message label' => 'Sperrnachricht',
+'Ban message help' => 'Die Nachricht, die den gesperrten Mitgliedern angezeigt wird, wenn sie das Forum besuchen.',
+'Expire date label' => 'Ablaufdatum',
+'Expire date help' => 'Das Datum, an dem die Sperren automatisch entfernt werden (Format: yyyy-mm-dd). Lass das Feld leer, wenn du die Sperre manuell entfernen möchtest.',
+'Ban IP label' => 'IP-Adressen sperren',
+'Ban IP help' => 'Auch die IP-Adresse der gesperrten Mitglieder sperren, um ihnen eine Neuanmeldung zu erschweren.',
+'Invalid date message' => 'Das eingegebene Ablaufdatum ist ungültig.',
+'Invalid date reasons' => 'Das Format für das Datum ist YYYY-MM-DD und muss mindesten einen Tag in der Zukunft liegen.',
+'Users banned redirect' => 'Mitglieder gesperrt. Leite weiter …',
+
+'User search head' => 'Mitgliedersuche',
+'User search subhead' => 'Gib Deine Suchkriterien ein',
+'User search info' => 'Mitgliedersuche in der Datenbank. Du kannst einen oder mehrere Begriffe eingeben, nach denen Du suchen willst. Sternchen (*) werden als Wildcards akzeptiert.',
+'Username label' => 'Mitgliedsname',
+'E-mail address label' => 'Email-Adresse',
+'Title label' => 'Titel',
+'Real name label' => 'Realname',
+'Website label' => 'Webseite',
+'Jabber label' => 'Jabber',
+'ICQ label' => 'ICQ',
+'MSN label' => 'Microsoft Account',
+'AOL label' => 'AOL IM',
+'Yahoo label' => 'Yahoo Messenger',
+'Location label' => 'Ort',
+'Signature label' => 'Signatur',
+'Admin note label' => 'Administratoren-Notiz',
+'Posts more than label' => 'Anzahl der Beiträge größer als',
+'Posts less than label' => 'Anzahl der Beiträge kleiner als',
+'Last post after label' => 'Letzter Beitrag nach',
+'Date help' => '(jjjj-mm-tt hh:mm:ss)',
+'Last post before label' => 'Letzter Beitrag vor',
+'Last visit after label' => 'Letzter Besuch nach',
+'Last visit before label' => 'Letzter Besuch vor',
+'Registered after label' => 'Registriert nach',
+'Registered before label' => 'Registriert vor',
+'Order by label' => 'Sortieren nach',
+'Order by username' => 'Mitgliedsname',
+'Order by e-mail' => 'Email',
+'Order by posts' => 'Anzahl der Beiträge',
+'Order by last post' => 'Letztem Beitrag',
+'Order by last visit' => 'Letztem Besuch',
+'Order by registered' => 'Registrierung',
+'Ascending' => 'Aufsteigend',
+'Descending' => 'Absteigend',
+'User group label' => 'Mitgliedergruppe',
+'All groups' => 'Alle Gruppen',
+'Unverified users' => 'Ungeprüfte Mitglieder',
+'Submit search' => 'Suche starten',
+'IP search head' => 'IP-Suche',
+'IP search subhead' => 'Gib die gesuchte IP ein',
+'IP address label' => 'IP-Adresse',
+'IP address help' => 'Die IP-Adresse, nach der in der Beitragsdatenbank gesucht werden soll.',
+'Find IP address' => 'IP-Adresse finden',
+
+'Results head' => 'Mitglieder',
+'Results username head' => 'Mitgliedsname',
+'Results e-mail head' => 'Email',
+'Results title head' => 'Titel/Status',
+'Results posts head' => 'Beiträge',
+'Results admin note head' => 'Administratoren-Notiz',
+'Results actions head' => 'Aktionen',
+'Results IP address head' => 'IP-Adresse',
+'Results last used head' => 'Zuletzt verwendet',
+'Results times found head' => 'mal gefunden',
+'Results action head' => 'Aktion',
+'Results find more link' => 'Weitere Mitglieder mit dieser IP finden',
+'Results no posts found' => 'Dieses Mitglied hat bislang noch keinen Beitrag im Forum geschrieben.',
+'Select' => 'Auswahl',
+'Select all' => 'Alle',
+'Unselect all' => 'Keiner',
+'Ban' => 'Sperren',
+'Delete' => 'Löschen',
+'Change group' => 'Gruppe ändern',
+'Bad IP message' => 'Die eingegebene IP-Adresse ist nicht richtig formatiert.',
+'Results view IP link' => 'IP-Statistiken anzeigen',
+'Results show posts link' => 'Beiträge anzeigen',
+'Results guest' => 'Gast',
+'Results no IP found' => 'Die angegebene IP-Adresse wurde in der Datenbank nicht gefunden.',
+'No match' => 'Keine Einträge gefunden'
+
+);
diff --git a/lang/German/common.php b/lang/German/common.php
new file mode 100644
index 0000000..3eaa9e7
--- /dev/null
+++ b/lang/German/common.php
@@ -0,0 +1,169 @@
+<?php
+
+// Sprachdefinitionen für häufig benötigte Zeichenketten
+$lang_common = array(
+
+// Textorientierung und Kodierung
+'lang_direction' => 'ltr', // ltr (von links nach rechts) oder rtl (von rechts nach links)
+'lang_identifier' => 'de',
+
+// Zahlenformatierung
+'lang_decimal_point' => ',',
+'lang_thousands_sep' => '.',
+
+// Hinweise
+'Bad request' => 'Ungültige Anfrage. Der Link, dem du gefolgt bist, ist ungültig oder veraltet.',
+'No view' => 'Du hast keine Berechtigung, diese Foren zu betrachten.',
+'No permission' => 'Du hast keine Berechtigung für den Zugriff auf diese Seite.',
+'Bad referrer' => 'Ungültiger HTTP_REFERER. Du wurdest von einer ungültigen Quelle auf diese Seite weitergeleitet. Bitte gehe zurück und versuche es noch einmal. Wenn dieses Problem weiterhin besteht, kontrolliere bitte die \'Base URL\'-Variable unter Administration / Optionen und stelle sicher, dass du dieses Forum tatsächlich über die eingestellte URL ansteuerst. Weitere Informationen über den Referrer-Check kannst du der FluxBB-Dokumentation entnehmen.',
+'Bad csrf hash' => 'Ungültiger CSRF-Hash. Du wurdest von einer nicht authorisierten Quelle auf diese Seite geleitet.',
+'No cookie' => 'Du scheinst dich erfolgreich angemeldet zu haben, es konnte jedoch kein Cookie gesetzt werden. Bitte überprüfe die Einstellungen deines Browsers und aktiviere die Verwendung von Cookies für diese Webseite (wenn möglich).',
+'Pun include extension' => 'Konnte User-Include %s von Template %s nicht verarbeiten. "%s" Dateien sind nicht erlaubt',
+'Pun include directory' => 'Konnte User-Include %s von Template %s nicht verarbeiten. Verzeichnis-Wechsel sind nicht erlaubt',
+'Pun include error' => 'Das benutzerdefinierte Include %s im Template %s konnte nicht verarbeitet werden. Die zu inkludierende Datei konnte weder im Template-Verzeichnis noch im User-Include-Verzeichnis gefunden werden.',
+
+// Verschiedenes
+'Announcement' => 'Ankündigung',
+'Options' => 'Beitragsoptionen',
+'Submit' => 'Absenden', // Beschriftung des Submit-Buttons
+'Ban message' => 'Du wurdest in diesem Forum gesperrt.',
+'Ban message 2' => 'Die Sperre endet am',
+'Ban message 3' => 'Der Administrator oder Moderator, der dich gesperrt hat, hat folgende Nachricht hinterlassen:',
+'Ban message 4' => 'Bei Fragen kontaktiere bitte den Foren-Administrator unter',
+'Never' => 'Niemals',
+'Today' => 'Heute',
+'Yesterday' => 'Gestern',
+'Info' => 'Info', // Die allgemeine Tabellenkopfzeile
+'Go back' => 'Zurück',
+'Maintenance' => 'Wartung',
+'Redirecting' => 'Weiterleitung',
+'Click redirect' => 'Klicke hier, wenn du nicht länger warten willst (oder dein Browser dich nicht automatisch weiterleitet)',
+'on' => 'an', // erscheint z.Bsp. in "BBCode ist an"
+'off' => 'aus',
+'Invalid email' => 'Die angegebene E-Mail-Adresse ist ungültig.',
+'Required' => '(Pflichtfeld)',
+'required field' => 'ist in diesem Formular ein Pflichtfeld.', // für die Formularprüfung via Javascript
+'Last post' => 'Letzter Beitrag',
+'by' => 'von', // erscheint z.Bsp. in "Letzter Beitrag von" (irgend einem Mitglied)
+'New posts' => 'Neue Beiträge', // der Link, der zum neuen Thema/Beitrag führt
+'New posts info' => 'Zum ersten neuen Beitrag dieses Themas gehen.', // der Popup-Text für die Links neuer Beiträge
+'Username' => 'Mitgliedsname',
+'Password' => 'Passwort',
+'Email' => 'E-Mail',
+'Send email' => 'E-Mail senden',
+'Moderated by' => 'Moderiert von',
+'Registered' => 'Registriert',
+'Subject' => 'Betreff',
+'Message' => 'Beitrag',
+'Topic' => 'Thema',
+'Forum' => 'Forum',
+'Posts' => 'Beiträge',
+'Replies' => 'Antworten',
+'Pages' => 'Seiten:',
+'Page' => 'Seite %s',
+'BBCode' => 'BBCode:', // Das sollten Sie möglichst nicht ändern
+'url tag' => '[url] Tag:',
+'img tag' => '[img] Tag:',
+'Smilies' => 'Smilies:',
+'and' => 'und',
+'Image link' => 'Bild', // Dieses Bild wird angezeigt, wenn im Profil die Option "Bilder anzeigen" deaktiviert ist
+'wrote' => 'schrieb:', // für Zitate (quote)
+'Mailer' => '%s Mailer', // Die Signatur der ausgehenden Emails des "MyForum-Mailers"
+'Important information' => 'Wichtige Information',
+'Write message legend' => 'Schreibe deinen Beitrag und versende ihn',
+'Previous' => 'Vorherige',
+'Next' => 'Nächste',
+'Spacer' => '…', // Ellipse für den Seitentrenner
+
+// Titel
+'Title' => 'Titel',
+'Member' => 'Mitglied', // Voreingestellter Titel
+'Moderator' => 'Moderator',
+'Administrator' => 'Administrator',
+'Banned' => 'Gesperrt',
+'Guest' => 'Gast',
+
+// Sprachstrings für include/parser.php
+'BBCode error no opening tag' => '[/%1$s] wurde ohne den öffnenden Tag [%1$s] gefunden',
+'BBCode error invalid nesting' => '[%1$s] wurde innerhalb [%2$s] geöffnet, dies ist nicht erlaubt',
+'BBCode error invalid self-nesting' => '[%s] wurde innerhalb sich selbst geöffnet, dies ist nicht erlaubt',
+'BBCode error no closing tag' => '[%1$s] wurde ohne den schließenden Tag [/%1$s] gefunden',
+'BBCode error empty attribute' => 'Der [%s]-Tag wurde ohne die erforderlichen Parameter verwendet',
+'BBCode error tag not allowed' => 'Du darfst keine [%s] Tags verwenden',
+'BBCode error tag url not allowed' => 'Du darfst keine Links absenden',
+'BBCode list size error' => 'Deine Liste ist für eine Verarbeitung zu lang, bitte kürze die Liste!',
+
+// Links, die sich in der Navigation befinden (oben auf jeder Seite)
+'Index' => 'Übersicht',
+'User list' => 'Mitglieder',
+'Rules' => 'Nutzungsbedingungen',
+'Search' => 'Suche',
+'Register' => 'Registrieren',
+'Login' => 'Anmelden',
+'Not logged in' => 'Du bist nicht angemeldet.',
+'Profile' => 'Profil',
+'Logout' => 'Abmelden',
+'Logged in as' => 'Angemeldet als:',
+'Admin' => 'Administration',
+'Last visit' => 'Dein letzter Besuch: %s',
+'Topic searches' => 'Themen:',
+'New posts header' => 'Neu',
+'Active topics' => 'Aktiv',
+'Unanswered topics' => 'Unbeantwortet',
+'Posted topics' => 'Beantwortet',
+'Show new posts' => 'Alle Themen mit neuen Beiträge seit deinem letzten Besuch anzeigen',
+'Show active topics' => 'Themen mit aktuellen Beiträgen anzeigen',
+'Show unanswered topics' => 'Unbeantwortete Themen anzeigen',
+'Show posted topics' => 'Themen anzeigen, auf die du geantwortet hast',
+'Mark all as read' => 'Alle Themen als gelesen markieren',
+'Mark forum read' => 'Dieses Forum als gelesen markieren',
+'Title separator' => ' / ',
+
+// Links, die sich in der Fusszeile der Seite befinden
+'Board footer' => 'Fußzeile des Forums',
+'Jump to' => 'Wechseln zu',
+'Go' => ' Los ', // Der Absende-Button zum Sprung ins Forum
+'Moderate topic' => 'Thema moderieren',
+'All' => 'Alle',
+'Move topic' => 'Thema verschieben',
+'Open topic' => 'Thema öffnen',
+'Close topic' => 'Thema schließen',
+'Unstick topic' => 'Themenfixierung lösen',
+'Stick topic' => 'Thema fixieren',
+'Moderate forum' => 'Forum moderieren',
+'Powered by' => 'Powered by %s', // Denglisch
+
+// Informationen zur Fehlersuche
+'Debug table' => 'Debug-Informationen',
+'Querytime' => 'Erstellt in %1$s Sekunden, %2$s Datenbank-Abfragen ausgeführt',
+'Memory usage' => 'Speichernutzung: %1$s',
+'Peak usage' => '(Maximum: %1$s)',
+'Query times' => 'Zeit in Sekunden',
+'Query' => 'Datenbank-Abfrage',
+'Total query time' => 'Gesamtzeit der Datenbankabfragen: %s',
+
+// Für den RSS-Feed der extern.php
+'RSS description' => 'Die aktuellsten Themen aus %s.',
+'RSS description topic' => 'Die aktuellsten Beiträge in %s.',
+'RSS reply' => 'Re: ', // Dem Betreff des Themas wird diese Zeichenkette vorangestellt (um eine Antwort zu kennzeichnen)
+'RSS active topics feed' => 'RSS - aktiver Themen-Feed',
+'Atom active topics feed' => 'Atom - aktiver Themen-Feed',
+'RSS forum feed' => 'RSS-Foren-Feed',
+'Atom forum feed' => 'Atom-Foren-Feed',
+'RSS topic feed' => 'RSS-Themen-Feed',
+'Atom topic feed' => 'Atom-Themen-Feed',
+
+// Teile aus dem Kopfbereich der Administration
+'New reports' => 'Es sind neue Berichte eingegangen',
+'Maintenance mode enabled' => 'Der Wartungs-Modus wurde aktiviert!',
+
+// Units for file sizes
+'Size unit B' => '%s B',
+'Size unit KiB' => '%s KiB',
+'Size unit MiB' => '%s MiB',
+'Size unit GiB' => '%s GiB',
+'Size unit TiB' => '%s TiB',
+'Size unit PiB' => '%s PiB',
+'Size unit EiB' => '%s EiB',
+
+);
diff --git a/lang/German/delete.php b/lang/German/delete.php
new file mode 100644
index 0000000..482df49
--- /dev/null
+++ b/lang/German/delete.php
@@ -0,0 +1,16 @@
+<?php
+
+// Sprachdefinitionen, die in delete.php verwendet werden
+$lang_delete = array(
+
+'Delete post' => 'Beitrag löschen',
+'Warning' => 'Du bist dabei, diesen Beitrag unwiderruflich zu löschen.',
+'Topic warning' => 'Achtung! Dies ist der erste Beitrag in diesem Thema, das ganze Thema wird unwiderruflich gelöscht.',
+'Delete info' => 'Unterhalb findest du den zu löschenden Beitrag.',
+'Reply by' => 'Antwort von %s - %s',
+'Topic by' => 'Thema erstellt von %s - %s',
+'Delete' => 'Löschen', // Beschriftung des Absende-Buttons
+'Post del redirect' => 'Beitrag gelöscht. Leite weiter …',
+'Topic del redirect' => 'Thema gelöscht. Leite weiter …'
+
+);
diff --git a/lang/German/forum.php b/lang/German/forum.php
new file mode 100644
index 0000000..2b70f0a
--- /dev/null
+++ b/lang/German/forum.php
@@ -0,0 +1,17 @@
+<?php
+
+// Sprachdefinitionen, die in viewforum.php verwendet werden
+$lang_forum = array(
+
+'Post topic' => 'Neues Thema erstellen',
+'Views' => 'Aufrufe',
+'Moved' => 'Verschoben:',
+'Sticky' => 'Fixiert:',
+'Closed' => 'Geschlossen:',
+'Empty forum' => 'Das Forum enthält noch keine Themen.',
+'Mod controls' => 'Moderationsfunktionen',
+'Is subscribed' => 'Du hast dieses Forum abonniert',
+'Unsubscribe' => 'Abonnement abbestellen',
+'Subscribe' => 'Dieses Forum abonnieren'
+
+);
diff --git a/lang/German/funnyquestion.php b/lang/German/funnyquestion.php
new file mode 100644
index 0000000..f061f69
--- /dev/null
+++ b/lang/German/funnyquestion.php
@@ -0,0 +1,8 @@
+<?php
+
+$lang_funnyquestion = array(
+
+ 'question-label' => 'Deine Antwort',
+ 'wrong-answer' => 'Deine Antwort war leider falsch. Versuche es nocheinmal!'
+
+);
diff --git a/lang/German/help.php b/lang/German/help.php
new file mode 100644
index 0000000..ab7e8dc
--- /dev/null
+++ b/lang/German/help.php
@@ -0,0 +1,66 @@
+<?php
+
+// Sprachdefinitionen, die in help.php verwendet werden
+$lang_help = array(
+
+'Help' => 'Hilfe',
+'produces' => 'ergibt:',
+
+'BBCode' => 'BBCode',
+'BBCode info 1' => 'BBCode ist den HTML Tags, die du sicherlich kennst, ähnlich. BBCode erlaubt es dir, ohne dass du HTML Tags benutzen musst, verschiedene Dinge in Elemente in deine Beiträge einzubauen bzw. Text zu formatieren. Du kannst BBCode nur verwenden, wenn der Administrator dies in dem jeweiligen Forum aktiviert hat. Selbst wenn HTML in einem Forum aktiviert ist, wirst du den BBCode benutzen wollen, da er einfacher als HTML Tags zu verwenden ist.',
+'BBCode info 2' => 'Der Administrator kann BBCode aktivieren und deaktivieren. Ob BBCode erlaubt ist, kannst du immer sehen, wenn du einen Beitrag schreibst oder deine Signatur bearbeitest.',
+
+'Text style' => 'Formatierter Text',
+'Text style info' => 'Mit folgenden BBCodes kannst du Text formatieren:',
+'Bold text' => 'Fetter Text',
+'Underlined text' => 'Unterstrichener Text',
+'Italic text' => 'Kursiver Text',
+'Strike-through text' => 'Durchgestrichener Text',
+'Red text' => 'Roter Text',
+'Blue text' => 'Blauer Text',
+'Heading text' => 'Überschrift',
+'Deleted text' => 'Gelöschter Text',
+'Inserted text' => 'Eingefügter Text',
+'Emphasised text' => 'Hervorgehobener Text',
+
+'Links and images' => 'Links und Bilder',
+'Links info' => 'Du kannst mit den folgenden Tags Links auf andere Dokumente oder zu einer E-Mail-Adresse erzeugen:',
+'This help page' => 'Diese Hilfe-Seite',
+'My email address' => 'Meine E-Mail-Adresse',
+'Images info' => 'Du kannst auch Bilder einbinden.',
+'FluxBB bbcode test' => 'FluxBB BBCode-Test',
+
+'Test topic' => 'Test-Thema',
+'Test post' => 'Test-Beitrag',
+'Test forum' => 'Test-Forum',
+'Test user' => 'Test-Mitglied',
+
+'Quotes' => 'Zitate',
+'Quotes info' => 'Um Textstellen zu zitieren, kopierst du den Text, den du zitieren möchtest, in den Beitrag und umschließt ihn mit dem [quote]-Tag.',
+'Quotes info 2' => 'Wenn du nur allgemein zitieren möchtest, kannst du diesen Tag auch ohne Namen verwenden.',
+'Quote text' => 'Dies ist der Text, den du zitieren möchtest.',
+'produces quote box' => 'ergibt folgende Zitat-Box:',
+'quote note' => 'Hinweis: Wenn ein Mitgliedsname die Zeichen [ oder ] enthält, musst du diesen mit doppelten Anführungsstrichen umschließen.',
+
+'Code' => 'Code',
+'Code info' => 'Wenn du den Quellcode einer Programmiersprache anzeigen möchtest, kannst du den [code]-Tag verwenden. Der mit dem [code]-Tag umschlossene Text wird in einem Zeichensatz mit konstanter Zeichenbreite angezeigt, um die Lesbarkeit zu erhöhen. Weitere Tags werden nicht berücksichtigt.',
+'Code text' => 'Hier steht dein Quellcode.',
+'produces code box' => 'erzeugt folgende Code-Box:',
+
+'Nested tags' => 'Verschachtelte Tags',
+'Nested tags info' => 'BBCode-Tags können für komplexere Formatierung kombiniert werden. Zum Beispiel:',
+'Bold, underlined text' => 'Fetter, unterstrichener Text',
+
+'Lists' => 'Listen',
+'List info' => 'Um eine Liste zu erstellen, kannst Du den [list]-Tag verwenden. Mit dem [list]-Tag können 3 Listentypen erstellt werden.',
+'List text 1' => 'Beispielliste 1.',
+'List text 2' => 'Beispielliste 2.',
+'List text 3' => 'Beispielliste 3.',
+'produces list' => 'erzeugt eine Liste mit Anstrichen.',
+'produces decimal list' => 'erzeugt eine durchnummerierte Liste.',
+'produces alpha list' => 'erzeugt eine alphabetisch gekennzeichnete Liste.',
+
+'Smilies' => 'Smilies',
+'Smilies info' => 'Smilies sind kleine Bilder, die den Sinn haben, Gefühle zum Ausdruck zu bringen und so die geschriebene Meinung zu unterstreichen. Wenn du hin und wieder mal chattest, dürften dir Smilies vermutlich ein Begriff sein. Einige "Standardzeichenfolgen" werden automatisch in Smilies umgewandelt (wenn der Administrator dies eingeschaltet hat). Einfach mal den Kopf zur Seite legen und mit ein bisschen Fantasie sollte man ein Gesicht erkennen können'
+
+);
diff --git a/lang/German/index.html b/lang/German/index.html
new file mode 100644
index 0000000..0a9b3ef
--- /dev/null
+++ b/lang/German/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html> \ No newline at end of file
diff --git a/lang/German/index.php b/lang/German/index.php
new file mode 100644
index 0000000..f6b8f84
--- /dev/null
+++ b/lang/German/index.php
@@ -0,0 +1,20 @@
+<?php
+
+// Sprachdefinitionen, die in index.php verwendet werden
+$lang_index = array(
+
+'Topics' => 'Themen',
+'Link to' => 'Link zu:', // Wie zum Beispiel "Link zu: http://fluxbb.org/"
+'Empty board' => 'Das Board ist leer.',
+'Newest user' => 'Das neueste Mitglied ist: %s',
+'Users online' => 'Registrierte Mitglieder online: %s',
+'Guests online' => 'Gäste online: %s',
+'No of users' => 'Anzahl der registrierten Mitglieder: %s',
+'No of topics' => 'Anzahl der Themen: %s',
+'No of posts' => 'Anzahl der Beiträge: %s',
+'Online' => 'Gerade online:', // Wie zum Beispiel "Online: Mitglied A, Mitglied B etc."
+'Board info' => 'Board-Informationen',
+'Board stats' => 'Board-Statistik',
+'User info' => 'Mitglieder-Information'
+
+);
diff --git a/lang/German/install.php b/lang/German/install.php
new file mode 100644
index 0000000..059ef4c
--- /dev/null
+++ b/lang/German/install.php
@@ -0,0 +1,105 @@
+<?php
+
+// Sprachdefinitionen, die in der install.php verwendet werden
+
+$lang_install = array(
+
+'Choose install language' => 'Wähle hier die Sprache für den Installationsassistenten',
+'Choose install language info' => 'Die Sprache, die für den Installationsassistenten verwendet werden soll. Die voreingestellte Sprache für das Forum kann weiter unten festgelegt werden.',
+'Install language' => 'Sprache installieren',
+'Change language' => 'Sprache ändern',
+'Already installed' => 'Es scheint so, als ob FluxBB bereits installiert ist. Du sollstest daher anstatt dessen <a href="index.php">hier</a> weitermachen.',
+'You are running error' => 'Du verwendest %1$s Version %2$s. FluxBB %3$s benötigt für seine volle Funktionsfähigkeit mindestens %1$s %4$s. Bevor du weitermachen kannst, musst du zunächst deine %1$s Installation aktualisieren.',
+'My FluxBB Forum' => 'Mein FluxBB-Forum',
+'Description' => 'FluxBB kann man nicht mit Worten beschreiben - man muss es selbst erlebt haben.',
+'Username 1' => 'Mitgliedsnamen müssen mindestens 2 Zeichen lang sein.',
+'Username 2' => 'Mitgliedsnamen dürfen nicht länger als 25 Zeichen sein.',
+'Username 3' => 'Der Mitgliedsname Gast ist reserviert.',
+'Username 4' => 'Mitgliedsnamen dürfen nicht die Form einer IP-Adresse haben.',
+'Username 5' => 'Mitgliedsnamen dürfen nicht die Zeichen \', " und [ oder ] enthalten.',
+'Username 6' => 'Mitgliedsnamen dürfen keine vom Forum verwendeten Formatierungs-Tags (BBCode) enthalten.',
+'Short password' => 'Passwörter müssen mindestens 6 Zeichen lang sein.',
+'Passwords not match' => 'Die Passwörter stimmen nicht überein.',
+'Wrong email' => 'Die für den Administrator eingegebene E-Mail-Adresse ist ungültig.',
+'No board title' => 'Du musst einen Board-Titel eingeben.',
+'Error default language' => 'Die voreingestellte Sprache scheint nicht zu existieren.',
+'Error default style' => 'Der voreingestellte Style scheint nicht zu existieren.',
+'No DB extensions' => 'Die PHP-Umgebung unterstützt keine der Datenbanken, die mit FluxBB verwendet werden können. Um FluxBB installieren zu können, muss PHP die Datenbanken MySQL, PostgreSQL oder SQLite unterstützen.',
+'Administrator username' => 'Mitgliedsname des Administrators',
+'Administrator email' => 'E-Mail des Administrators',
+'Board title' => 'Board-Titel',
+'Base URL' => 'Die URL (ohne abschließenden /) Deines FluxBB-Forums. Diese musst korrekt sein.',
+'Required field' => 'ist in diesem Formular ein Pflichtfeld.',
+'FluxBB Installation' => 'FluxBB-Installation',
+'Welcome' => 'Du möchtest FluxBB installieren. Dafür musst du das nachfolgende Formular vollständig ausfüllen. Wenn bei der Installation irgendwelche Schwierigkeiten auftreten, lies bitte in der Dokumentation nach.',
+'Install' => 'FluxBB %s installieren',
+'Errors' => 'Die folgenden Fehler müssen korrigiert werden:',
+'Database setup' => 'Datenbank-Einstellungen',
+'Info 1' => 'Alle Informationen die für eine Datenbank-Verbindung benötigt werden.',
+'Select database' => 'Wähle hier deinen Datenbank-Typ',
+'Info 2' => 'Wähle eine Datenbank. Wir unterstützen SQLite, MySQL und PostgreSQL.',
+'Dual MySQL' => 'FluxBB hat erkannt, dass deine PHP-Umgebung zwei verschiedene Wege unterstützt, mit MySQL zu kommunizieren. Diese beiden Optionen werden Standard und improved genannt. Wenn du unsicher bist, welche du verwenden sollst, versuche zunächst improved und falls es fehlschlägt, versuche Standard.',
+'InnoDB' => 'FluxBB hat erkannt, dass dein MySQL-Server <a href="http://dev.mysql.com/doc/refman/5.0/en/innodb.html">InnoDB</a> unterstützt. Dies ist eine gute Wahl, wenn du ein großes Forum planst. Falls du dir noch unsicher bist, solltest du InnoDB besser nicht verwenden.',
+'Database type' => 'Datenbank-Typ',
+'Required' => '(Pflichtfeld)',
+'Database hostname' => 'Gib hier den Hostnamen deines Datenbank-Servers ein',
+'Info 3' => 'Du solltest diese Information von Deinem Web Host bekommen, falls <code>localhost</code> nicht funktioniert.',
+'Database server hostname' => 'Hostname des Datenbank-Servers',
+'Database enter name' => 'Gib hier den Namen deiner Datenbank ein',
+'Info 4' => 'Der Name der Datenbank, in die FluxBB installiert werden soll.',
+'Database name' => 'Datenbank-Name',
+'Database enter informations' => 'Gib hier den Benutzernamen und das Passwort deiner Datenbank ein',
+'Database username' => 'Datenbank-Benutzername',
+'Info 5' => 'Dein MySQL-Benutzernamen und Passwort (kann für SQLite ignoriert werden).',
+'Database password' => 'Datenbank-Passwort',
+'Database enter prefix' => 'Gib hier den Präfix für die Datenbank-Tabellen ein',
+'Info 6' => 'Falls Du mehrere FluxBB-Instanzen mit nur einer Datenbank verwenden möchtest, ändere diese Einstellung.',
+'Table prefix' => 'Tabellen-Präfix',
+'Administration setup' => 'Administrations-Einstellung',
+'Info 7' => 'Erstelle das allererste Konto Deines Forums.',
+'Admin enter username' => 'Gib hier den Mitgliedsnamen des Administrators ein',
+'Info 8' => 'Mitgliedsnamen können zwischen 2 und 25 Zeichen lang sein. Passwörter müssen mindestens 6 Zeichen lang sein. Außerdem sind die Passwörter zeichensensitiv (Groß-/Kleinschreibung).',
+'Admin enter password' => 'Gib hier das Passwort des Administrators ein und bestätige es anschließend noch einmal',
+'Password' => 'Passwort',
+'Confirm password' => 'Passwort bestätigen',
+'Board setup' => 'Board-Einstellung',
+'Info 11' => 'Einstellungen für Dein Forum. Du kannst diese später ändern.',
+'General information' => 'Gib hier den Board-Titel und die -Beschreibung ein',
+'Board description' => 'Board-Beschreibung (unterstützt HTML)',
+'Appearance' => 'Erscheinungsbild',
+'Choose the default language' => 'Wähle hier die voreingestellte Sprache',
+'Info 15' => 'Wähle Sprache und Erscheinungsbild für Dein Forum.',
+'Default language' => 'Voreingestellt Sprache',
+'Default style' => 'Voreingestellter Style',
+'Start install' => 'Installation beginnen',
+'DB type not valid' => '\'%s\' ist kein gültiger Datenbank-Typ',
+'Table prefix error' => 'Der Tabellen-Präfix \'%s\' enthält ungültige Zeichen oder ist zu lang. Der Präfix darf nur die Buchstaben a bis z, Ziffern und das Unterstrich-Zeichen enthalten. Er muss nicht mit einer Ziffer beginnen. Die maximale Länge ist 40 Zeichen. Bitte wähle einen anderen Präfix',
+'Prefix reserved' => 'Der Tabellen-Präfix \'sqlite_\' ist für die Verwendung von SQLite-Datenbanken reserviert. Bitte wähle einen anderen Präfix',
+'Existing table error' => 'Eine tabelle mit dem Namen \'%susers\' ist bereits in der Datenbank \'%s\' vorhanden. Entweder ist FluxBB bereits installiert oder eine andere Software ist installiert und verwendet die gleichen Tabellennamen wie FluxBB. Wenn mehrere Kopien von FluxBB in der gleichen Datenbank installiert werden sollen, musst du jeweils einen anderen Tabellen-Präfix wählen',
+'InnoDB off' => 'InnoDB scheint nicht aktiviert zu sein. Bitte wähle einen Datenbank-Layer, der keine InnoDB-Unterstützung hat oder aktiviere InnoDB auf deinem MySQL-Server',
+'Administrators' => 'Administratoren',
+'Administrator' => 'Administrator',
+'Moderators' => 'Moderatoren',
+'Moderator' => 'Moderator',
+'Guests' => 'Gäste',
+'Guest' => 'Gast',
+'Members' => 'Mitglieder',
+'Announcement' => 'Gib hier den Text für Ankündigungen ein.',
+'Rules' => 'Gib hier die Regeln für dein Forum ein',
+'Maintenance message' => 'Das Forum ist aufgrund von Wartungsarbeiten vorübergehend geschlossen. Bitte probier es in ein paar Minuten noch einmal.<br />\\n<br />\\n/ Der Administrator',
+'Test post' => 'Test-Thema',
+'Message' => 'Wenn du dies lesen kannst (wovon ich mal ausgehe), scheint die Installation von FluxBB zu funktionieren! Du kannst dich jetzt einloggen und über die Administration dein Forum konfigurieren.',
+'Test category' => 'Test-Kategorie',
+'Test forum' => 'Test-Forum',
+'This is just a test forum' => 'Dies ist nur ein Test-Forum',
+'Alert cache' => '<strong>Das cache-Verzeichnis ist derzeit nicht beschreibbar!</strong> Damit FluxBB richtig funktionieren kann, muss PHP Schreibzugriff auf das Verzeichnis <em>%s</em> haben. Du kannst chmod verwenden, um dem Verzeichnis die entsprechenden Berechtigungen zu geben. Falls du zweifelst, setze chmod auf 0777.',
+'Alert avatar' => '<strong>Das Avatar-verzeichnis ist derzeit nicht beschreibbar!</strong> Wenn du möchtest, dass deine Mitglieder ihr eigenes Avatar-Bild hochladen können, muss PHP Schreibberechtigung auf das Verzeichnis <em>%s</em> haben. Du kannst dann später noch in der Administration unter Optionen ein anderes Verzeichnis zum Speichern der Avatar-Bilder auswählen. Du kannst chmod verwenden, um dem Verzeichnis die entsprechenden Berechtigungen zu geben. Falls du zweifelst, setze chmod auf 0777.',
+'Alert upload' => '<strong>Das Hochladen von Dateien scheint auf diesem Server nicht erlaubt zu sein!</strong> Wenn du möchtest, dass deine Mitglieder ihre eigenen Avatar-Bilder hochladen können, musst du in der PHP-Konfiguration file_uploads aktivieren. Sobald das Hochladen von Dateien aktiviert wurde, kann das Hochladen von Avatar-Bildern in der Administration unter Optionen freigegeben werden.',
+'FluxBB has been installed' => 'FluxBB wurde installiert. Um die Installation abzuschließen, folge den unten stehenden Instruktionen.',
+'Final instructions' => 'Abschließende Hinweise',
+'Info 17' => 'Um die Installation abzuschließen, musst du auf den nachfolgenden Button klicken, um die Datei config.php herunter zu laden. Diese Datei musst du anschließend ins Wurzelverzeichnis deiner FluxBB-Installation hochladen.',
+'Info 18' => 'Nachdem du die Datei config.php hochgeladen hast, ist FluxBB vollständig einsatzbereit! Du kannst dann <a href="index.php">hier zur Forenübersicht gehen</a>.',
+'Download config.php file' => 'Die Datei config.php herunter laden',
+'FluxBB fully installed' => 'Die FluxBB-Installation ist vollständig abgeschlossen! Du kannst jetzt <a href="index.php">hier zur Forenübersicht gehen</a>.',
+
+);
diff --git a/lang/German/login.php b/lang/German/login.php
new file mode 100644
index 0000000..7c63325
--- /dev/null
+++ b/lang/German/login.php
@@ -0,0 +1,28 @@
+<?php
+
+// Sprachdefinitionen, die in login.php verwendet werden
+$lang_login = array(
+
+// Verschiedenes
+'Login errors' => 'Anmelde-Fehler',
+'Login errors info' => 'Der folgende Fehler muss behoben werden, bevor Du Dich anmelden kannst:',
+'Wrong user/pass' => 'Der Mitgliedsname und/oder das Passwort sind falsch.',
+'Forgotten pass' => 'Hast du dein Passwort vergessen?',
+'Login redirect' => 'Anmeldung erfolgreich. Leite weiter …',
+'Logout redirect' => 'Abmeldung erfolgreich. Leite weiter …',
+'No email match' => 'Es gibt kein Mitglied mit dieser E-Mail-Adresse',
+'Request pass' => 'Neues Passwort anfordern',
+'Request pass legend' => 'Gib die E-Mail-Adresse ein, mit der du dich registriert hast',
+'Request pass info' => 'Das neue Passwort wird zusammen mit einem Link zur Aktivierung an diese E-Mail-Adresse geschickt.',
+'Not registered' => 'Noch nicht registriert?',
+'Login legend' => 'Gib deinen Mitgliedsnamen und das Passwort ein',
+'Remember me' => 'Automatisch bei jedem Besuch anmelden.',
+'Login info' => 'Wenn du noch nicht registriert bist oder dein Passwort vergessen hast, klicke auf den entsprechenden Link unten.',
+'New password errors' => 'Fehler bei der Passwort-Abfrage',
+'New passworderrors info' => 'Die folgenden Fehler müssen behoben werden, bevor ein neues Passwort versandt werden kann:',
+
+// Texte für vergessene Passwörter
+'Forget mail' => 'Es wurde eine E-Mail mit Anweisungen, wie das Passwort zu ändern ist, an die angegebene Adresse gesandt. Wenn diese E-Mail bei dir nicht ankommt, kontaktiere den Administrator unter',
+'Email flood' => 'Für dieses Konto wurde bereits in der vergangenen Stunde eine Anfrage zum Zurücksetzen des Passwortes gestellt. Bitte warte %s Minuten, bevor du eine erneute Passwortanfrage stellst.'
+
+);
diff --git a/lang/German/mail_templates/activate_email.tpl b/lang/German/mail_templates/activate_email.tpl
new file mode 100644
index 0000000..7ac9bb7
--- /dev/null
+++ b/lang/German/mail_templates/activate_email.tpl
@@ -0,0 +1,12 @@
+Subject: Änderung der E-Mail-Adresse bestätigen
+
+Hallo <username>,
+
+Du hast im Diskussionsforum <base_url> eine neue E-Mail-Adresse in deinem Profil eingetragen. Wenn du diese Änderung nicht selber vorgenommen hast oder du deine E-Mail-Adresse doch nicht ändern möchtest, ignoriere einfach diese Nachricht. Deine E-Mail-Adresse wird erst geändert, wenn du den Bestätigungs-Link am Ende dieser Nachricht klickst. Um die neue E-Mail-Adresse zu aktivieren, musst du im Forum angemeldet sein.
+
+Klicke diesen Link, um die neue E-Mail-Adresse zu aktivieren:
+<activation_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/activate_password.tpl b/lang/German/mail_templates/activate_password.tpl
new file mode 100644
index 0000000..d8995a1
--- /dev/null
+++ b/lang/German/mail_templates/activate_password.tpl
@@ -0,0 +1,14 @@
+Subject: Neues Passwort bestätigen
+
+Hallo <username>,
+
+Du hast im Diskussionsforum <base_url> ein neues Passwort angefordert. Wenn du diese Änderung nicht selber vorgenommen hast oder du kein neues Passwort möchtest, ignoriere einfach diese Nachricht. Dein Passwort wird erst geändert, wenn du den Bestätigungs-Link am Ende dieser Nachricht klickst. Um das neue Passwort zu aktivieren, musst du im Forum angemeldet sein.
+
+Dein neues Passwort ist: <new_password>
+
+Klicke diesen Link, um das neue Passwort zu aktivieren:
+<activation_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht) \ No newline at end of file
diff --git a/lang/German/mail_templates/banned_email_change.tpl b/lang/German/mail_templates/banned_email_change.tpl
new file mode 100644
index 0000000..fe0e0a5
--- /dev/null
+++ b/lang/German/mail_templates/banned_email_change.tpl
@@ -0,0 +1,9 @@
+Subject: Achtung - Gesperrte E-Mail-Adresse erkannt
+
+Das Mitglied '<username>' hat seine E-Mail-Adresse auf eine gesperrte E-Mail-Adresse geändert: <email>
+
+Mitgliedsprofil: <profile_url>
+
+--
+<board_mailer> Mailer
+(DAntworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/banned_email_post.tpl b/lang/German/mail_templates/banned_email_post.tpl
new file mode 100644
index 0000000..ee7678d
--- /dev/null
+++ b/lang/German/mail_templates/banned_email_post.tpl
@@ -0,0 +1,9 @@
+Subject: Achtung - Gesperrte E-Mail-Adresse erkannt
+
+Das Mitglied '<username>' hat mit einer gesperrten E-Mail-Adresse geantwortet: <email>
+
+Antwort-URL: <post_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/banned_email_register.tpl b/lang/German/mail_templates/banned_email_register.tpl
new file mode 100644
index 0000000..a801aff
--- /dev/null
+++ b/lang/German/mail_templates/banned_email_register.tpl
@@ -0,0 +1,9 @@
+Subject: Achtung - Gesperrte E-Mail-Adresse erkannt
+
+Das Mitglied '<username>' hat sich mit einer gesperrten E-Mail-Adresse registriert: <email>
+
+Mitgliedsprofil: <profile_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/dupe_email_change.tpl b/lang/German/mail_templates/dupe_email_change.tpl
new file mode 100644
index 0000000..2678aef
--- /dev/null
+++ b/lang/German/mail_templates/dupe_email_change.tpl
@@ -0,0 +1,9 @@
+Subject: Achtung - E-Mail-Duplikat erkannt
+
+Das Mitglied '<username>' hat seine E-Mail-Adresse auf eine E-Mail-Adresse geändert, die auch folgendem Mitglied zugeordnet ist: <dupe_list>
+
+Mitgliedsprofil: <profile_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/dupe_email_register.tpl b/lang/German/mail_templates/dupe_email_register.tpl
new file mode 100644
index 0000000..acf6075
--- /dev/null
+++ b/lang/German/mail_templates/dupe_email_register.tpl
@@ -0,0 +1,9 @@
+Subject: Achtung - E-Mail-Duplikat erkannt
+
+Das Mitglied '<username>' hat sich mit einer E-Mail-Adresse registriert, die auch folgendem Mitglied zugeordnet ist: <dupe_list>
+
+Mitgliedsprofil: <profile_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/form_email.tpl b/lang/German/mail_templates/form_email.tpl
new file mode 100644
index 0000000..84efa3c
--- /dev/null
+++ b/lang/German/mail_templates/form_email.tpl
@@ -0,0 +1,13 @@
+Subject: <mail_subject>
+
+<sender> aus dem Forum <board_title> hat dir eine Nachricht geschickt. Du kannst <sender> direkt auf diese E-Mail antworten.
+
+Dies ist die Nachricht:
+-----------------------------------------------------------------------
+
+<mail_message>
+
+-----------------------------------------------------------------------
+
+--
+<board_mailer> Mailer
diff --git a/lang/German/mail_templates/index.html b/lang/German/mail_templates/index.html
new file mode 100644
index 0000000..0a9b3ef
--- /dev/null
+++ b/lang/German/mail_templates/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html> \ No newline at end of file
diff --git a/lang/German/mail_templates/new_reply.tpl b/lang/German/mail_templates/new_reply.tpl
new file mode 100644
index 0000000..21a28bc
--- /dev/null
+++ b/lang/German/mail_templates/new_reply.tpl
@@ -0,0 +1,11 @@
+Subject: Antwort auf das Thema: '<topic_subject>'
+
+<replier> hat auf das Thema '<topic_subject>' geantwortet, welches du abonniert hast. Es können zwischenzeitlich mehrere Personen auf dieses Thema geantwortet haben, aber dies ist die einzige Benachrichtigung, die du bekommst, bis du dich wieder im Forum angemeldet hast.
+
+Den Beitrag findest du unter <post_url>
+
+Du kannst dieses Abonnement über den Link <unsubscribe_url> abbestellen, indem Du auf am Ende der Seite auf "Abonnement abbestellen" klickst.
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/new_reply_full.tpl b/lang/German/mail_templates/new_reply_full.tpl
new file mode 100644
index 0000000..77ac7cf
--- /dev/null
+++ b/lang/German/mail_templates/new_reply_full.tpl
@@ -0,0 +1,18 @@
+Subject: Antwort auf das Thema: '<topic_subject>'
+
+<replier> hat auf das Thema '<topic_subject>' geantwortet, welches du abonniert hast. Es können zwischenzeitlich mehrere Personen auf dieses Thema geantwortet haben, aber dies ist die einzige Benachrichtigung, die du bekommst, bis du dich wieder im Forum angemeldet hast.
+
+Den Beitrag findest du unter <post_url>
+
+Dies ist die Nachricht:
+-----------------------------------------------------------------------
+
+<message>
+
+-----------------------------------------------------------------------
+
+Du kannst dieses Abonnement über den Link <unsubscribe_url> abbestellen, indem Du auf am Ende der Seite auf "Abonnement abbestellen" klickst.
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/new_report.tpl b/lang/German/mail_templates/new_report.tpl
new file mode 100644
index 0000000..97a616a
--- /dev/null
+++ b/lang/German/mail_templates/new_report.tpl
@@ -0,0 +1,9 @@
+Subject: Meldung(<forum_id>) - '<topic_subject>'
+
+Das Mitglied '<username>' hat die folgende Nachricht gemeldet: <post_url>
+
+Grund: <reason>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/new_topic.tpl b/lang/German/mail_templates/new_topic.tpl
new file mode 100644
index 0000000..4542cdb
--- /dev/null
+++ b/lang/German/mail_templates/new_topic.tpl
@@ -0,0 +1,11 @@
+Subject: Neues Thema im Forum: '<forum_name>'
+
+<poster> hat im Forum '<forum_name>', welches du abonniert hast, ein neues Thema '<topic_subject>' erstellt.
+
+Das Thema ist unter <topic_url> zu finden
+
+Du kannst dieses Abonnement über den Link <unsubscribe_url> abbestellen, indem Du auf am Ende der Seite auf "Abonnement abbestellen" klickst.
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/new_topic_full.tpl b/lang/German/mail_templates/new_topic_full.tpl
new file mode 100644
index 0000000..6819296
--- /dev/null
+++ b/lang/German/mail_templates/new_topic_full.tpl
@@ -0,0 +1,18 @@
+Subject: Neues Thema im Forum: '<forum_name>'
+
+<poster> hat im Forum '<forum_name>', welches du abonniert hast, ein neues Thema '<topic_subject>' erstellt.
+
+Das Thema ist unter <topic_url> zu finden
+
+Dies ist die Nachricht:
+-----------------------------------------------------------------------
+
+<message>
+
+-----------------------------------------------------------------------
+
+Du kannst dieses Abonnement über den Link <unsubscribe_url> abbestellen, indem Du auf am Ende der Seite auf "Abonnement abbestellen" klickst.
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/new_user.tpl b/lang/German/mail_templates/new_user.tpl
new file mode 100644
index 0000000..e3fe16a
--- /dev/null
+++ b/lang/German/mail_templates/new_user.tpl
@@ -0,0 +1,12 @@
+Subject: Achtung - neue Registrierung
+
+Das Mitglied '<username>' wurde in den Foren auf <base_url> registriert
+
+Mitgliedsprofil: <profile_url>
+
+Besuche die folgende Seite um dieses Mitglied zu administrieren:
+<admin_url>
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/mail_templates/rename.tpl b/lang/German/mail_templates/rename.tpl
new file mode 100644
index 0000000..13b0021
--- /dev/null
+++ b/lang/German/mail_templates/rename.tpl
@@ -0,0 +1,12 @@
+Subject: Mitgliedsname wurde geändert
+
+Während eines Software-Aktualisierung des Forums auf <base_url> wurde festgestellt, dass dein Mitgliedsname eine zu große Ähnlichkeit zu einem anderen Mitglied aufweist. Dein Mitgliedsname wurde daher geändert.
+
+Alter Mitgliedsname: <old_username>
+Neuer Mitgliedsname: <new_username>
+
+Wir bitten, diese Unannehmlichkeit zu entschuldigen.
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht) \ No newline at end of file
diff --git a/lang/German/mail_templates/welcome.tpl b/lang/German/mail_templates/welcome.tpl
new file mode 100644
index 0000000..923ee1b
--- /dev/null
+++ b/lang/German/mail_templates/welcome.tpl
@@ -0,0 +1,12 @@
+Subject: Willkommen im Forum <board_title>!
+
+Vielen Dank für deine Registrierung im Forum <base_url>. Deine Zugangsdaten lauten:
+
+Mitgliedsname: <username>
+Passwort: <password>
+
+Melde dich unter <login_url> an, um die Registrierung abzuschließen.
+
+--
+<board_mailer> Mailer
+(Antworte nicht auf diese Nachricht)
diff --git a/lang/German/misc.php b/lang/German/misc.php
new file mode 100644
index 0000000..2744b87
--- /dev/null
+++ b/lang/German/misc.php
@@ -0,0 +1,93 @@
+<?php
+
+// Sprachdefinitionen, die in verschiedenen Bereichen verwendet werden
+$lang_misc = array(
+
+'Mark read redirect' => 'Alle Foren und Themen wurden als gelesen markiert. Leite weiter …',
+'Mark forum read redirect' => 'Alle Themen des angegebenen Forums wurden als gelesen markiert. Leite weiter …',
+
+// E-Mail-Versand
+'Form email disabled' => 'Das Mitglied, dem du eine E-Mail senden möchtest, hat das E-Mail-Formular deaktiviert.',
+'No email subject' => 'Du musst einen Betreff eingeben.',
+'No email message' => 'Du musst eine Nachricht eingeben.',
+'Too long email message' => 'Nachrichten dürfen nicht länger als 65535 Zeichen (64 KB) sein.',
+'Email flood' => 'Zwischen dem Versand der E-Mails müssen mindestens %s Sekunden vergehen. Bitte warte %s Sekunden und versuche es später noch einmal.',
+'Email sent redirect' => 'E-Mail versandt. Leite weiter …',
+'Send email to' => 'E-Mail senden an',
+'Email subject' => 'Betreff',
+'Email message' => 'Nachricht',
+'Email disclosure note' => 'Bitte beachte, dass durch das Absenden dieses Formulars deine E-Mail-Adresse dem Empfänger bekanntgegeben wird.',
+'Write email' => 'E-Mail schreiben und versenden',
+
+// Bericht
+'No reason' => 'Du musst einen Grund angeben.',
+'Reason too long' => 'Deine Nachricht ist zu lang.',
+'Report flood' => 'Zwischen zwei Meldungen müssen mindestens %s Sekunden vergangen sein. Bitte warte %s Sekunden und versuche es später noch einmal.',
+'Report redirect' => 'Beitrag gemeldet. Leite weiter …',
+'Report post' => 'Beitrag melden',
+'Reason' => 'Begründung',
+'Reason desc' => 'Bitte gib eine kurze Begründung an, warum du diesen Beitrag meldest',
+
+// Abonnements
+'Already subscribed topic' => 'Du hast dieses Thema bereits abonniert',
+'Already subscribed forum' => 'Du hast dieses Forum bereits abonniert',
+'Subscribe redirect' => 'Das Thema wurde erfolgreich abonniert. Leite weiter …',
+'Not subscribed topic' => 'Du hast dieses Thema nicht abonniert.',
+'Not subscribed forum' => 'Du hast dieses Forum nicht abonniert.',
+'Unsubscribe redirect' => 'Dein Abonnement wurde abbestellt. Leite weiter …',
+
+// Allgemeine Foren- und Themenmoderation
+'Moderate' => 'Moderieren',
+'Select' => 'Auswahl', // die Überschrift der Spalte der Kontrollfelder
+'Move' => 'Verschieben',
+'Split' => 'Teilen',
+'Delete' => 'Löschen',
+'Merge' => 'Zusammenfügen',
+
+// Forenmoderation
+'Open' => 'Öffnen',
+'Close' => 'Schließen',
+'Move topic' => 'Thema verschieben',
+'Move topics' => 'Themen verschieben',
+'Move legend' => 'Wähle ein Ziel aus, wohin dieses Thema verschoben werden soll',
+'Move to' => 'Verschieben nach',
+'Nowhere to move' => 'Es gibt keine Foren, in die du Themen verschieben könntest.',
+'Leave redirect' => 'Aktiviere diese Option, wenn ein Hinweis auf das verschobene Thema hinterlassen werden soll.',
+'Move topic redirect' => 'Thema verschoben. Leite weiter …',
+'Move topics redirect' => 'Themen verschoben. Leite weiter …',
+'Confirm delete legend' => 'Bitte bestätige das Löschen',
+'Delete topics' => 'Themen löschen',
+'Delete topics comply' => 'Willst du wirklich die ausgewählten Themen löschen?',
+'Delete topics redirect' => 'Themen gelöscht. Leite weiter …',
+'Open topic redirect' => 'Thema geöffnet. Leite weiter …',
+'Open topics redirect' => 'Themen geöffnet. Leite weiter …',
+'Close topic redirect' => 'Thema geschlossen. Leite weiter …',
+'Close topics redirect' => 'Themen geschlossen. Leite weiter …',
+'No topics selected' => 'Du musst mindestens ein Thema auswählen, um es zu ändern/löschen/öffnen/schließen.',
+'Not enough topics selected' => 'Du musst mindestens 2 Themen auswählen, die du zusammenführen möchtest.',
+'Stick topic redirect' => 'Thema fixiert. Leite weiter …',
+'Unstick topic redirect' => 'Themenfixierung gelöst. Leite weiter …',
+'Merge topics' => 'Themen zusammenführen',
+'Merge topics redirect' => 'Die Themen wurden zusammengeführt. Leite weiter …',
+'Confirm merge legend' => 'Bitte bestätige die Zusammenführung',
+'New subject' => 'Neuer Betreff',
+
+// Mehrere Beiträge in Themen teilen
+'Confirm split legend' => 'Bitte bestätige das Teilen der ausgewählten Beiträge und wähle das Zielforum für das neue Thema.',
+'Split posts' => 'Beiträge abtrennen',
+'Split posts comply' => 'Willst du wirklich die ausgewählten Beiträge abtrennen?',
+'Split posts redirect' => 'Die Beiträge wurden abgetrennt. Leite weiter …',
+
+// Beiträge in Themen löschen
+'Delete posts' => 'Beiträge löschen',
+'Cannot select first' => 'Der erste Beitrag kann nicht zum Teilen/Löschen ausgewählt werden.',
+'Delete posts comply' => 'Willst du wirklich die ausgewählten Beiträge löschen?',
+'Delete posts redirect' => 'Beiträge gelöscht. Leite weiter …',
+'No posts selected' => 'Du musst mindestens einen Beitrag auswählen, um ihn zu teilen/löschen.',
+
+// Hostinformationen
+'Host info 1' => 'Die IP-Adresse ist: %s',
+'Host info 2' => 'Der Hostname ist: %s',
+'Show more users' => 'Weitere Mitglieder mit dieser IP anzeigen',
+
+);
diff --git a/lang/German/post.php b/lang/German/post.php
new file mode 100644
index 0000000..9c8bb90
--- /dev/null
+++ b/lang/German/post.php
@@ -0,0 +1,38 @@
+<?php
+
+// Sprachdefinitionen, die in post.php und edit.php verwendet werden
+$lang_post = array(
+
+// Überprüfung des Versands (einige ähnlich wie in edit.php)
+'No subject' => 'Themen müssen einen Titel haben.',
+'No subject after censoring' => 'Themen müssen einen Titel haben. Nach Anwendung des Zensurfilters ist der Titel leer.',
+'Too long subject' => 'Der Betreff darf nicht länger als 70 Zeichen sein.',
+'No message' => 'Du musst einen Beitrag eingeben.',
+'No message after censoring' => 'Du musst einen Beitrag eingeben. Nach Anwendung des Zensurfilters ist deine Nachricht leer.',
+'Too long message' => 'Beiträge dürfen nicht größer als %s Byte sein.',
+'All caps subject' => 'Der Betreff darf nicht nur Großbuchstaben enthalten.',
+'All caps message' => 'Der Beitrag darf nicht nur Großbuchstaben enthalten.',
+'Empty after strip' => 'Scheinbar besteht Dein Beitrag nur aus leerem BBCode. Möglicherweise wurden zu tief geschachtelte Zitate verworfen.',
+
+// Beiträge
+'Post errors' => 'Versandfehler',
+'Post errors info' => 'Die folgenden Fehler müssen korrigiert werden, bevor der Beitrag gespeichert werden kann:',
+'Post preview' => 'Vorschau des Beitrags',
+'Guest name' => 'Name', // Für Gäste (anstatt Mitgliedsname)
+'Post redirect' => 'Beitrag gespeichert. Leite weiter …',
+'Post a reply' => 'Eine Antwort schreiben',
+'Post new topic' => 'Neues Thema erstellen',
+'Hide smilies' => 'In diesem Beitrag niemals Smilies anzeigen.',
+'Subscribe' => 'Dieses Thema abonnieren.',
+'Stay subscribed' => 'Dieses Thema auf abonniert lassen',
+'Topic review' => 'Themen-Übersicht (Neuester Beitrag zuerst)',
+'Flood start' => 'Es müssen mindestens %s Sekunden zwischen zwei Beiträgen vergangen sein. Bitte warte %s Sekunden und versuche es dann erneut.',
+'Preview' => 'Vorschau', // Beschriftung des Absende-Button für die Nachrichtenvorschau
+
+// Beitrag bearbeiten
+'Edit post legend' => 'Ändere den Beitrag und speichere die Änderungen',
+'Silent edit' => 'Stille Änderung ("geändert durch …" in der Beitragsansicht NICHT anzeigen)',
+'Edit post' => 'Beitrag ändern',
+'Edit redirect' => 'Beitrag geändert. Leite weiter …'
+
+);
diff --git a/lang/German/prof_reg.php b/lang/German/prof_reg.php
new file mode 100644
index 0000000..93db1fb
--- /dev/null
+++ b/lang/German/prof_reg.php
@@ -0,0 +1,79 @@
+<?php
+
+// Sprachdefinitionen, die in profile.php und register.php verwendet werden
+$lang_prof_reg = array(
+
+'Email legend' => 'Gib eine gültige E-Mail-Adresse ein',
+'Email legend 2' => 'Gib eine gültige E-Mail-Adresse ein und bestätige diese',
+'Localisation legend' => 'Stelle hier deine Zeitzone und Sprache ein',
+'Time zone' => 'Zeitzone',
+'Time zone info' => 'Damit im Forum die Zeit korrekt anzeigt wird, musst du die Zeitzone deines Standortes wählen. Mit aktivierter Sommerzeit-Option wird die Uhrzeit um eine Stunde vorgestellt.',
+'DST' => 'Sommerzeit (aktuelle Zeit + 1 Stunde).',
+'Time format' => 'Zeitformat',
+'Date format' => 'Datumsformat',
+'Default' => 'Standard',
+'Language' => 'Sprache',
+'Email setting info' => 'Stelle hier ein, ob deine E-Mail-Adresse in deinem Mitgliedsprofil und deinen Beiträgen angezeigt werden soll. Stelle auch ein, ob du E-Mails über den Forum-Mailer (E-Mail-Formular) bekommen möchtest oder nicht.',
+'Email setting 1' => 'E-Mail-Adresse öffentlich anzeigen.',
+'Email setting 2' => 'E-Mail-Adresse verstecken und E-Mail-Formular aktivieren.',
+'Email setting 3' => 'E-Mail-Adresse verstecken und E-Mail-Formular deaktivieren.',
+'Privacy options legend' => 'Setze hier deine Optionen für die Privatsphäre',
+'Confirm pass' => 'Passwort zur Bestätigung wiederholen',
+
+'Username too short' => 'Der Mitgliedsname muss mindestens 2 Zeichen lang sein. Bitte wähle einen anderen (längeren) Mitgliedsnamen.',
+'Username too long' => 'Der Mitgliedsname darf nicht länger als 25 Zeichen lang sein. Bitte wähle einen anderen (kürzeren) Mitgliedsnamen.',
+'Username guest' => 'Der Mitgliedsname "Gast" ist reserviert. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username IP' => 'Der Mitgliedsname darf nicht das Format von IP-Adressen haben. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username reserved chars' => 'Der Mitgliedsname darf die Zeichen \', " und [ oder ] nicht enthalten. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username BBCode' => 'Der Mitgliedsname darf keinen BBCode enthalten. Bitte wähle einen anderen Mitgliedsnamen.',
+'Banned username' => 'Der eingegebene Mitgliedsname ist in diesem Forum gesperrt. Bitte wähle einen anderen Mitgliedsnamen.',
+'Pass too short' => 'Das Passwort muss mindestens 9 Zeichen lang sein. Bitte wähle ein anderes (längeres) Passwort.',
+'Pass not match' => 'Die Passwörter stimmen nicht überein. Bitte geh zurück und korrigiere diese.',
+'Banned email' => 'Die eingegebene E-Mail-Adresse ist in diesem Forum gesperrt, bitte gib eine andere E-Mail-Adresse an.',
+'Dupe email' => 'Diese E-Mail-Adresse ist bereits vergeben, bitte gib eine andere E-Mail-Adresse an.',
+'Sig too long' => 'Signaturen dürfen nicht länger als %1$s Zeichen sein. Bitte kürze die Signatur um %2$s Zeichen.',
+'Sig too many lines' => 'Signaturen dürfen nicht mehr als %s Zeilen haben.',
+'Bad ICQ' => 'Die angegebene ICQ Nummer ist ungültig, bitte gehe zurück und korrigiere es.',
+
+'UTC-12:00' => '(WEZ-12:00) Internationale Datumsgrenze West',
+'UTC-11:00' => '(WEZ-11:00) Niue, Samoa',
+'UTC-10:00' => '(WEZ-10:00) Hawaii-Aleuten, Cook-Inseln',
+'UTC-09:30' => '(WEZ-09:30) Marquesas-Inseln',
+'UTC-09:00' => '(WEZ-09:00) Alaska, Gambier-Inseln',
+'UTC-08:30' => '(WEZ-08:30) Pitcairn-Inseln',
+'UTC-08:00' => '(WEZ-08:00) Pazifik',
+'UTC-07:00' => '(WEZ-07:00) Mountain',
+'UTC-06:00' => '(WEZ-06:00) Zentral',
+'UTC-05:00' => '(WEZ-05:00) Osten',
+'UTC-04:00' => '(WEZ-04:00) Atlantik',
+'UTC-03:30' => '(WEZ-03:30) Neufundland',
+'UTC-03:00' => '(WEZ-03:00) Amazonas, Zentral-Grönland',
+'UTC-02:00' => '(WEZ-02:00) Mittlerer Atlantik',
+'UTC-01:00' => '(WEZ-01:00) Azoren, Cape Verde, Östliches Grönland',
+'UTC' => '(WEZ) Westeuropäische Zeit, Greenwich',
+'UTC+01:00' => '(WEZ+01:00) Mitteleuropäische Zeit (MEZ), Westafrika',
+'UTC+02:00' => '(WEZ+02:00) Osteuropäische Zeit, Zentralafrika',
+'UTC+03:00' => '(WEZ+03:00) Ostafrika',
+'UTC+03:30' => '(WEZ+03:30) Iran',
+'UTC+04:00' => '(WEZ+04:00) Moskau, Golf, Samara',
+'UTC+04:30' => '(WEZ+04:30) Afghanistan',
+'UTC+05:00' => '(WEZ+05:00) Pakistan',
+'UTC+05:30' => '(WEZ+05:30) Indien, Sri Lanka',
+'UTC+05:45' => '(WEZ+05:45) Nepal',
+'UTC+06:00' => '(WEZ+06:00) Bangladesh, Bhutan, Jekaterinburg',
+'UTC+06:30' => '(WEZ+06:30) Cocos-Inseln, Myanmar',
+'UTC+07:00' => '(WEZ+07:00) Indochina, Nowosibirsk',
+'UTC+08:00' => '(WEZ+08:00) Groß-China, Westliches Australien, Krasnoyarsk',
+'UTC+08:45' => '(WEZ+08:45) Südöstliches West-Australien',
+'UTC+09:00' => '(WEZ+09:00) Japan, Korea, Chita, Irkutsk',
+'UTC+09:30' => '(WEZ+09:30) Zentral-Australian',
+'UTC+10:00' => '(WEZ+10:00) Ost-Australien',
+'UTC+10:30' => '(WEZ+10:30) Lord Howe',
+'UTC+11:00' => '(WEZ+11:00) Solomon-Inseln, Wladiwostok',
+'UTC+11:30' => '(WEZ+11:30) Norfolk-Inseln',
+'UTC+12:00' => '(WEZ+12:00) Neuseeland, Fiji, Magadan',
+'UTC+12:45' => '(WEZ+12:45) Chatham-Inseln',
+'UTC+13:00' => '(WEZ+13:00) Tonga, Phoenix-Inseln, Kamtschatka',
+'UTC+14:00' => '(WEZ+14:00) Grenz-Inseln',
+
+);
diff --git a/lang/German/profile.php b/lang/German/profile.php
new file mode 100644
index 0000000..973e4a4
--- /dev/null
+++ b/lang/German/profile.php
@@ -0,0 +1,143 @@
+<?php
+
+// Sprachdefinitionen, die in profile.php verwendet werden
+$lang_profile = array(
+
+// Navigation und Sektionen
+'Profile menu' => 'Profilmenü',
+'Section essentials' => 'Grundlegendes',
+'Section personal' => 'Persönliches',
+'Section messaging' => 'Messenger',
+'Section personality' => 'Personalisierung',
+'Section display' => 'Anzeige',
+'Section privacy' => 'Privatsphäre',
+'Section admin' => 'Administration',
+
+// Verschiedenes
+'Username and pass legend' => 'Gib hier deinen Mitgliedsnamen und dein Passwort ein',
+'Personal details legend' => 'Gib hier deine persönlichen Details ein',
+'Contact details legend' => 'Ändere oder ergänze hier deine Daten für Instant Messaging',
+'User activity' => 'Mitgliedsaktivität',
+'Paginate info' => 'Gib hier die Anzahl von Themen und Beiträgen an, die pro Seite angezeigt werden sollen.',
+
+// Passwort
+'Pass key bad' => 'Der eingegebene Aktivierungscode war falsch oder ist abgelaufen. Bitte fordere ein neues Passwort an. Falls es weiterhin zu Problemen kommt, kontaktiere den Administrator unter',
+'Pass updated' => 'Dein Passwort wurde aktualisiert. Du kannst dich jetzt mit deinem neuen Passwort anmelden.',
+'Pass updated redirect' => 'Passwort wurde aktualisiert. Leite weiter …',
+'Wrong pass' => 'Das alte Passwort ist falsch.',
+'Change pass' => 'Passwort ändern',
+'Change pass legend' => 'Gib dein neues Passwort ein und bestätige es',
+'Old pass' => 'Altes Passwort',
+'New pass' => 'Neues Passwort',
+'Confirm new pass' => 'Neues Passwort bestätigen',
+'Pass info' => 'Passwörter müssen mindestens 6 Zeichen lang sein. Groß-/Kleinschreibung wird unterschieden.',
+
+// E-Mail
+'Email key bad' => 'Der Aktivierungscode war falsch oder ist abgelaufen. Bitte fordere ein neues Passwort an. Falls es weiterhin zu Problemen kommt, kontaktiere den Administrator unter',
+'Email updated' => 'Deine E-Mail-Adresse wurde aktualisiert.',
+'Activate email sent' => 'Es wurde eine E-Mail mit Anweisungen, wie die neue E-Mail-Adresse zu aktivieren ist, an die angegebene E-Mail-Adresse gesandt. Falls keine E-Mail bei dir ankommt, kontaktiere bitte den Administrator unter',
+'Email legend' => 'Gib deine neue E-Mail-Adresse ein',
+'Email instructions' => 'Eine E-Mail mit einem Aktivierungs-Link wird an deine neue Adresse gesendet. Du musst den Link in dieser E-Mail anklicken, um die neue E-Mail-Adresse zu aktivieren.',
+'Change email' => 'E-Mail-Adresse ändern',
+'New email' => 'Neue E-Mail-Adresse',
+
+// Avatar
+'Avatars disabled' => 'Die Administratoren haben die Verwendung von Avataren deaktiviert.',
+'Too large ini' => 'Die ausgewählte Datei war zum Hochladen zu groß.',
+'Partial upload' => 'Die ausgewählte Datei wurde nur teilweise hochgeladen. Bitte versuche es noch einmal.',
+'No tmp directory' => 'PHP konnte die hochgeladene Datei nicht speichern.',
+'No file' => 'Du hast keine Datei zum Hochladen ausgewählt.',
+'Bad type' => 'Die ausgewählte Datei hat ein falsches Format. Erlaubte Formate sind gif, jpeg und png.',
+'Too wide or high' => 'Die ausgewählte Datei ist breiter und/oder höher als maximal erlaubt',
+'Too large' => 'Die ausgewählte Datei ist größer als maximal erlaubt',
+'pixels' => 'Pixel',
+'bytes' => 'Bytes',
+'Move failed' => 'Der Server konnte die hochgeladene Datei nicht speichern. Bitte kontaktiere den Administrator unter',
+'Unknown failure' => 'Ein unbekannter Fehler ist aufgetreten. Bitte versuche es noch einmal.',
+'Avatar upload redirect' => 'Der Avatar wurde hochgeladen. Leite weiter …',
+'Avatar deleted redirect' => 'Der Avatar wurde gelöscht. Leite weiter …',
+'Avatar desc' => 'Ein Avatar ist ein kleines Bild, das unter deinem Mitgliedsnamen in deinen Beiträgen angezeigt wird. Avatare dürfen nicht größer sein als',
+'Upload avatar' => 'Avatar hochladen',
+'Upload avatar legend' => 'Gib eine Avatar-Datei zum Hochladen an',
+'Delete avatar' => 'Avatar löschen', // nur für Administratoren
+'File' => 'Datei',
+'Upload' => 'Hochladen', // Beschriftung des Absende-Buttons
+
+// Formular-Prüfung
+'Forbidden title' => 'Der eingegebene Titel enthält ein verbotenes Wort. Bitte korrigiere dies.',
+'Profile redirect' => 'Profil aktualisiert. Leite weiter …',
+
+// Profilanzeige
+'Users profile' => '%s\'s Profil',
+'Username info' => 'Mitgliedsname: %s',
+'Email info' => 'E-Mail: %s',
+'Posts info' => 'Beiträge: %s',
+'Registered info' => 'Registriert: %s',
+'Last post info' => 'Letzter Beitrag: %s',
+'Last visit info' => 'Letzter Besuch: %s',
+'Show posts' => 'Alle Beiträge anzeigen',
+'Show topics' => 'Alle Themen anzeigen',
+'Show subscriptions' => 'Alle Abonnements anzeigen',
+'Realname' => 'Dein Name',
+'Location' => 'Wohnort',
+'Website' => 'Webseite',
+'Invalid website URL' => 'Die eingetragene Webseiten-Adresse ist ungültig.',
+'Website not allowed' => 'Du darfst noch keine Webseite zu Deinem Profil hinzufügen.',
+'Jabber' => 'Jabber',
+'ICQ' => 'ICQ',
+'MSN' => 'Microsoft Account',
+'AOL IM' => 'AOL IM',
+'Yahoo' => 'Yahoo! Messenger',
+'Avatar' => 'Avatar',
+'Signature' => 'Signatur',
+'Sig max size' => 'Maximale Länge: %s Zeichen / Maximale Zeilen: %s',
+'Avatar legend' => 'Lege hier die Optionen für die Anzeige deines Avatars fest',
+'Avatar info' => 'Ein Avatar ist ein kleines Bild, das unter deinem Mitgliedsnamen in deinen Beiträgen angezeigt wird. Du kannst über den unten stehenden Link deinen eigenen Avatar hochladen.',
+'Change avatar' => 'Avatar ändern',
+'Signature legend' => 'Stelle hier deine Signatur zusammen',
+'Signature info' => 'Eine Signatur ist ein kurzer Text, der an deine Beiträge angehängt wird. Du darfst fast alles eingeben, was du möchtest, z.B. dein Lieblingszitat oder dein Sternzeichen. Du kannst BBCode in der Signatur benutzen, wenn der Administrator es in diesem Forum erlaubt hat. Die erlaubten Möglichkeiten werden unterhalb der Eingabebox angezeigt.',
+'Sig preview' => 'Aktuelle Signatur-Vorschau:',
+'No sig' => 'Aktuell keine Signatur im Profil gespeichert.',
+'Signature quote/code/list/h' => 'Die BBCodes für Zitate, Code, Listen und überschriften sind in den Signaturen nicht erlaubt.',
+'Topics per page' => 'Themen pro Seite',
+'Posts per page' => 'Beiträge pro Seite',
+'Leave blank' => 'Um die Standardeinstellung zu verwenden, lasse die Eingabefelder leer.',
+'Subscription legend' => 'Setze hier die Optionen für deine Abonnements',
+'Notify full' => 'Den neuen Beitrag als einfachen Text mit der Benachrichtigungs-E-Mail versenden.',
+'Auto notify full' => 'Automatisch per E-Mail benachrichtigen, wenn jemand zu einem Thema oder einem Beitrag von dir geantwortet hat.',
+'Show smilies' => 'Smilies als Bilder anzeigen',
+'Show images' => 'Bilder in Beiträgen anzeigen.',
+'Show images sigs' => 'Bilder in den Mitglieder-Signaturen anzeigen.',
+'Show avatars' => 'Avatare in Beiträgen anzeigen.',
+'Show sigs' => 'Mitglieder-Signaturen anzeigen.',
+'Style legend' => 'Stelle hier das gewünschte Forum-Design ein',
+'Styles' => 'Styles',
+'Admin note' => 'Administratoren-Notiz',
+'Pagination legend' => 'Gib hier deine Optionen für die Seiten-Paginierung ein',
+'Post display legend' => 'Lege deine Optionen für die Anzeige von Beiträgen fest',
+'Post display info' => 'Wenn du eine langsame Internetverbindung hast, deaktiviere diese Optionen (insbesondere die Anzeige von Bildern in Beiträgen und Signaturen). Das macht das Laden der Seiten schneller.',
+'Instructions' => 'Wenn du dein Profil änderst, wirst du auf diese Seite weitergeleitet.',
+
+// Administration stuff
+'Group membership legend' => 'Wähle eine Mitgliedergruppe',
+'Save' => 'Speichern',
+'Set mods legend' => 'Lege die Berechtigungen des Moderators fest',
+'Moderator in info' => 'Wähle die Foren aus, die dieses Mitglied moderieren darf. Achtung: Dies hat nur Auswirkungen auf Moderatoren. Administratoren haben immer alle Berechtigungen in allen Foren.',
+'Update forums' => 'Moderatoren-Berechtigungen aktualisieren',
+'Delete ban legend' => 'Mitglieder löschen (nur Administratoren) oder sperren',
+'Delete user' => 'Mitglied löschen',
+'Ban user' => 'Mitglied sperren',
+'Confirm delete legend' => 'Wichtig: Lies dies, bevor du ein Mitglied löschst',
+'Confirm delete user' => 'Löschen des Mitglied bestätigen',
+'Confirmation info' => 'Bitte bestätige, dass du das folgende Mitglied löschen willst:', // Der Name des Mitglieds wird dieser Zeichenkette angehangen
+'Delete warning' => 'Warnung! Gelöschte Mitglieder und/oder Beiträge können NICHT wiederhergestellt werden. Wenn du die Beiträge dieses Mitglieds nicht löschst, können diese später nur noch manuell gelöscht werden.',
+'Delete posts' => 'Alle Beiträge und Themen dieses Mitglieds löschen.',
+'Delete' => 'Löschen', // Beschriftung des Absende-Buttons (Bestätigung der Mitgliederlöschung)
+'User delete redirect' => 'Mitglied gelöscht. Leite weiter …',
+'User promote redirect' => 'Mitglied befördert. Leite weiter …',
+'Group membership redirect' => 'Gruppenmitgliedschaft gespeichert. Leite weiter …',
+'Update forums redirect' => 'Moderatorenrechte aktualisiert. Leite weiter …',
+'Ban redirect' => 'Leite weiter …',
+'No delete admin message' => 'Administratoren können nicht gelöscht werden. Um dieses Mitlgied löschen zu können, muss es zunächst einer anderen Mitgliedergruppe zugeordnet werden.',
+
+);
diff --git a/lang/German/register.php b/lang/German/register.php
new file mode 100644
index 0000000..946dc8b
--- /dev/null
+++ b/lang/German/register.php
@@ -0,0 +1,37 @@
+<?php
+
+// Sprachdefinitionen, die in register.php verwendet werden
+$lang_register = array(
+
+// Verschiedenes
+'No new regs' => 'Dieses Forum akzeptiert momentan keine neuen Mitglieder.',
+'Reg cancel redirect' => 'Registrierung abgebrochen. Leite weiter …',
+'Forum rules' => 'Nutzungsbedingungen',
+'Rules legend' => 'Du musst den Nutzungsbedingungen zustimmen, um dich zu registrieren',
+'Registration flood' => 'In der letzten Stunde hat sich ein neues Mitglied mit der gleichen IP-Adresse registriert wie die von dir verwendete. Um Mehrfachregistrierungen zu verhindern, muss zwischen Registrierungen mit der gleichen IP mindestens 1 Stunde liegen. Entschuldige bitte die Unannehmlichkeiten.',
+'Agree' => 'Akzeptieren',
+'Cancel' => 'Ablehnen',
+'Register' => 'Registrieren',
+
+// Formular-Prüfung (einige werden auch in der post.php verwendet)
+'Registration errors' => 'Registrierungsfehler',
+'Registration errors info' => 'Die folgenden Fehler müssen korrigiert werden, bevor du dich registrieren kannst:',
+'Username censor' => 'Der eingegebene Mitgliedsname enthielt ein oder mehrere zensierte Worte. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username dupe 1' => 'Der gewünschte Mitgliedsname ist bereits vergeben',
+'Username dupe 2' => 'Der gewünschte Mitgliedsname ist einem anderen zu ähnlich. Er muss sich mindestens in einem Zeichen (a-z oder 0-9) unterscheiden. Bitte wähle einen anderen Mitgliedsnamen.',
+'Email not match' => 'Die E-Mail-Adressen stimmen nicht überein. Bitte versuche es noch einmal.',
+
+// Registrierungs-E-Mail
+'Reg email' => 'Danke für deine Registrierung. Dein Passwort wurde an die angegebene Adresse gesandt. Falls die Email nicht ankommen sollte, kontaktiere den Administrator unter',
+'Reg complete' => 'Registrierung vollständig. Anmelden und weiterleiten …',
+
+// Registrierungsinformationen
+'Desc 1' => 'Die Registrierung ist nicht erforderlich. Allerdings gibt sie dir Zugang zu einer ganzen Reihe Funktionen, die dir sonst nicht zugänglich wären. Dazu zählt z.B. die Möglichkeit, Beiträge zu bearbeiten und zu löschen oder auch die Möglichkeit, eine eigene Signatur zu erstellen, die automatisch deinen Beiträgen angefügt wird und noch vieles mehr. Wenn du Fragen bezüglich dieses Forums hast, solltest du den Administrator fragen.',
+'Desc 2' => 'Unten findest du ein Formular, mit dem du dich registrieren kannst. Nachdem du dich registriert hast, solltest du dein Mitgliedsprofil besuchen und deine Einstellungen kontrollieren bzw. fehlende Informationen ergänzen. Die folgenden Felder stellen nur einen kleinen Teil der Einstellungen dar, die im Profil vorgenommen werden können.',
+'Username legend' => 'Bitte gib einen Mitgliedsnamen mit einer Länge zwischen 2 und 25 Zeichen ein',
+'Pass legend' => 'Bitte gib ein Passwort ein und bestätige dies',
+'Pass info' => 'Passwörter müssen mindestens 6 Zeichen lang sein. Achte auf Groß-/Kleinschreibung.',
+'Email info' => 'Bitte gib eine gültige E-Mail-Adresse ein, an die dein zufällig erzeugtes Passwort gesendet werden kann.',
+'Confirm email' => 'E-Mail-Adresse zur Bestätigung wiederholen',
+
+);
diff --git a/lang/German/search.php b/lang/German/search.php
new file mode 100644
index 0000000..55638fb
--- /dev/null
+++ b/lang/German/search.php
@@ -0,0 +1,63 @@
+<?php
+
+// Sprachdefinitionen, die in search.php verwendet werden
+$lang_search = array(
+
+// Das Suchformular
+'User search' => 'Mitgliedersuche',
+'No search permission' => 'Du hast keine Berechtigung, die Suchfunktion zu nutzen.',
+'Search flood' => 'Zwischen 2 Suchvorgängen müssen mindestens %s Sekunden vergehen. Bitte warte %s Sekunden und versuche es dann noch einmal.',
+'Search' => 'Suche',
+'Search criteria legend' => 'Gib deine Suchkriterien ein',
+'Search info' => '<strong>Einfache Suche:</strong> Trenne deine Suchbegriffe mit Leerzeichen voneinander.<br /><br /><strong>Erweiterte Suche:</strong> Benutze AND, OR und NOT in Verbindung mit deinen Suchbegriffen, um detaillierter zu suchen.<br />Du kannst Sternchen (*) als Platzhalter für die Suche von Teilbegriffen verwenden. (eine Suche nach *ux* findet z.B. auch FluxBB etc.)',
+'Keyword search' => 'Schlüsselwortsuche',
+'Author search' => 'Autor suchen',
+'Search in legend' => 'Wähle, wo gesucht werden soll',
+'Search in info' => 'Wähle, in welchem Forum gesucht werden soll und ob sich die Suche nur auf Thementitel, Beitragstexte oder beides erstrecken soll.',
+'Search multiple forums info' => 'Wenn keine Foren ausgewählt sind, werden alle Foren durchsucht.',
+'Forum search' => 'Forum',
+'Search in' => 'Suche in',
+'Message and subject' => 'Beitragstext und Thementitel',
+'Message only' => 'Nur im Beitragstext',
+'Topic only' => 'Nur im Thementitel',
+'Sort by' => 'Treffer sortieren nach',
+'Sort order' => 'Sortierreihenfolge',
+'Search results legend' => 'Wähle, wie die Suchergebnisse angezeigt werden sollen',
+'Search results info' => 'Du kannst wählen, wie die Suchergebnisse sortiert und angezeigt werden sollen.',
+'Sort by post time' => 'Beitragszeit',
+'Sort by author' => 'Autor',
+'Sort by subject' => 'Thema',
+'Sort by forum' => 'Forum',
+'Ascending' => 'Aufsteigend',
+'Descending' => 'Absteigend',
+'Show as' => 'Ergebnisse zeigen als',
+'Show as topics' => 'Themen',
+'Show as posts' => 'Beiträge',
+
+// Ergebnisse
+'Search results' => 'Suchergebnisse',
+'Quick search show_new' => 'Neu',
+'Quick search show_recent' => 'Aktiv',
+'Quick search show_unanswered' => 'Unbeantwortet',
+'Quick search show_replies' => 'Beantwortet',
+'Quick search show_user_topics' => 'Von %s',
+'Quick search show_user_posts' => 'Von %s',
+'Quick search show_subscriptions' => 'Abonniert von %s',
+'By keywords show as topics' => 'Mit Beiträgen mit den Schlüsselbegriffen \'%s\'',
+'By keywords show as posts' => 'Mit den Schlüsselbegriffen \'%s\'',
+'By user show as topics' => 'Mit Beiträgen von %s',
+'By user show as posts' => 'Von %s',
+'By both show as topics' => 'Mit den Schlüsselbegriffen \'%s\' in Beiträgen von %s',
+'By both show as posts' => 'Mit den Schlüsselbegriffen \'%s\' von %s',
+'No terms' => 'Du musst mindestens ein Schlüsselwort und/oder Autor für die Suche eingeben. Suchbegriffe müssen mindestens drei Zeichen lang sein.',
+'No hits' => 'Deine Suche ergab leider keine Treffer.',
+'No user posts' => 'Es gibt keine Beiträge von diesem Mitglied in diesem Forum.',
+'No user topics' => 'Es gibt keine Themen von diesem Mitglied in diesem Forum.',
+'No subscriptions' => 'Du hast aktuell kein Thema abonniert.',
+'No new posts' => 'Es gibt keine neuen Beiträge seit deinem letzten Besuch.',
+'No recent posts' => 'Es gibt keine neuen Beiträge in den letzten 24 Stunden.',
+'No unanswered' => 'Es gibt keine unbeantworteten Beiträge in diesem Forum.',
+'Go to post' => 'Beitrag anzeigen',
+'Go to topic' => 'Thema anzeigen'
+
+);
diff --git a/lang/German/stopwords.txt b/lang/German/stopwords.txt
new file mode 100644
index 0000000..98a5cdd
--- /dev/null
+++ b/lang/German/stopwords.txt
@@ -0,0 +1,238 @@
+aber
+alle
+allem
+allen
+aller
+alles
+als
+also
+am
+an
+ander
+andere
+anderem
+anderen
+anderer
+anderes
+anderm
+andern
+anderr
+anders
+auch
+auf
+aus
+bei
+bin
+bis
+bist
+da
+damit
+dann
+das
+dass
+daß
+dasselbe
+dazu
+dein
+deine
+deinem
+deinen
+deiner
+deines
+dem
+demselben
+den
+denn
+denselben
+der
+derer
+derselbe
+derselben
+des
+desselben
+dessen
+dich
+die
+dies
+diese
+dieselbe
+dieselben
+diesem
+diesen
+dieser
+dieses
+dir
+doch
+dort
+du
+durch
+ein
+eine
+einem
+einen
+einer
+eines
+einig
+einige
+einigem
+einigen
+einiger
+einiges
+einmal
+er
+es
+etwas
+euch
+euer
+eure
+eurem
+euren
+eurer
+eures
+für
+gegen
+gewesen
+hab
+habe
+haben
+hat
+hatte
+hatten
+hier
+hin
+hinter
+ich
+ihm
+ihn
+ihnen
+ihr
+ihre
+ihrem
+ihren
+ihrer
+ihres
+im
+in
+indem
+ins
+ist
+jede
+jedem
+jeden
+jeder
+jedes
+jene
+jenem
+jenen
+jener
+jenes
+jetzt
+kann
+kein
+keine
+keinem
+keinen
+keiner
+keines
+können
+könnte
+machen
+mal
+man
+manche
+manchem
+manchen
+mancher
+manches
+mein
+meine
+meinem
+meinen
+meiner
+meines
+mich
+mir
+mit
+muß
+muss
+musste
+nach
+nicht
+nichts
+noch
+nun
+nur
+ob
+obwohl
+oder
+ohne
+schon
+sehr
+sein
+seine
+seinem
+seinen
+seiner
+seines
+selbst
+sich
+sie
+sind
+so
+solche
+solchem
+solchen
+solcher
+solches
+soll
+sollte
+sondern
+sonst
+über
+um
+und
+uns
+unse
+unsem
+unsen
+unser
+unsere
+unses
+unter
+viel
+viele
+vom
+von
+vor
+während
+war
+waren
+warst
+was
+weg
+weil
+weiter
+welche
+welchem
+welchen
+welcher
+welches
+wenn
+werde
+werden
+wie
+wieder
+will
+wir
+wird
+wirst
+wo
+wollen
+wollte
+würde
+würden
+zu
+zum
+zur
+zwar
+zwischen
diff --git a/lang/German/topic.php b/lang/German/topic.php
new file mode 100644
index 0000000..6566ecb
--- /dev/null
+++ b/lang/German/topic.php
@@ -0,0 +1,33 @@
+<?php
+
+// Sprachdefinitionen, die viewtopic.php verwendet werden
+$lang_topic = array(
+
+'Post reply' => 'Beitrag schreiben',
+'Topic closed' => 'Thema geschlossen',
+'From' => 'Ort:', // Mitglieds-Wohnort
+'Promote user' => 'Mitglied befördern',
+'IP address logged' => 'IP-Adresse',
+'Note' => 'Notiz:', // Administratoren-Notiz
+'Posts' => 'Beiträge:',
+'Registered' => 'Registriert:',
+'Replies' => 'Antworten:',
+'Website' => 'Webseite',
+'Guest' => 'Gast',
+'Online' => 'Online',
+'Offline' => 'Offline',
+'Last edit' => 'Beitrag geändert von',
+'Report' => 'Melden',
+'Delete' => 'Löschen',
+'Edit' => 'Bearbeiten',
+'Quote' => 'Zitieren',
+'Is subscribed' => 'Du hast dieses Thema abonniert',
+'Unsubscribe' => 'Abonnement abbestellen',
+'Subscribe' => 'Dieses Thema abonnieren',
+'Quick post' => 'Schnellantwort auf dieses Thema',
+'Mod controls' => 'Moderatorkontrollen',
+'New icon' => 'Neuer Beitrag',
+'Re' => 'Re:',
+'Preview' => 'Vorschau'
+
+);
diff --git a/lang/German/update.php b/lang/German/update.php
new file mode 100644
index 0000000..1c83d2d
--- /dev/null
+++ b/lang/German/update.php
@@ -0,0 +1,76 @@
+<?php
+
+// Sprachdefinitionen, die in der db_update.php verwendet werden
+
+$lang_update = array(
+
+'Update' => 'FluxBB aktualisieren',
+'Update message' => 'Deine FluxBB-Datenbank ist nicht mehr aktuell und muss aktualisiert werden, um damit weiterarbeiten zu können. Wenn du der Administrator bist, folge bitte der Anleitung, um die Aktualisierung abzuschließen.',
+'Note' => 'Hinweis:',
+'Members message' => 'Der aktuell laufende Prozess ist nur für Administratoren. Wenn du nur ein Mitglied bist, musst du nichts befürchten - das Forum wird in Kürze zurück sein!',
+'Administrator only' => 'Dieser Schritt kann nur durch Administratoren ausgeführt werden!',
+'Database password info' => 'Um die Datenbank-Aktualisierung durchführen zu können, musst du das Datenbank-Passwort eingeben, mit dem FluxBB installiert wurde. Falls du dich nicht mehr erinnern kannst, es wurde auch in der Datei \'config.php\' gespeichert.',
+'Database password note' => 'Wenn du SQLite verwendest (und demzufolge kein Datenbank-Passwort hast), verwende anstatt dessen den Dateinamen der Datenbank. Dieser muss GENAU mit dem Dateinamen der Datenbank übereinstimmen, wie er in deiner Konfigurationsdatei angegeben wurde.',
+'Database password' => 'Datenbank-Passwort',
+'Maintenance' => 'Wartung',
+'Maintenance message info' => 'Diese Nachricht wird dem Nutzer während der Aktualisierung angezeigt. Der Text wird nicht wie reguläre Beiträge interpretiert und darf daher HTML enhalten.',
+'Maintenance message' => 'Wartungs-Nachricht',
+
+'You are running error' => 'Du verwendest %1$s Version %2$s. FluxBB %3$s benötigt für seine volle Funktionsfähigkeit mindestens %1$s %4$s. Bevor du weitermachen kannst, musst du zunächst deine %1$s Installation aktualisieren.',
+'Version mismatch error' => 'Versionsfehler. Die Datenbank \'%s\' scheint kein FluxBB-Datenbank-Schema zu verwenden, welches von diesem Aktualisierungs-Script unterstützt wird.',
+'Invalid file error' => 'Ungültiger Datenbank-Dateiname. Wenn du SQLite verwendest, dann musst du die Datenbank-Datei haargenau so angeben wie in deiner \'%s\'',
+'Invalid password error' => 'Ungültiges Datenbank-Passwort. Um FluxBB aktualisieren zu können, musst du das Datenbank-Passwort haargenau so angeben wie in deiner \'%s\'',
+'No password error' => 'Es wurde kein Datenbank-Passwort angegeben',
+'Script runs error' => 'Es scheint, als ob die Aktualisierung bereits von jemand anderem ausgeführt wird. Falls das nicht zutrifft, lösche einfach die Datei \'%s\' manuell und versuch es dann noch einmal',
+'No update error' => 'Dein Forum ist bereits aktueller als das Script es machen kann',
+
+'Intro 1' => 'Dieses Script wird die Foren-Datenbank aktualisieren. In Abhängigkeit von der Geschwindigkeit des Servers und der Größe der Foren-Datenbank kann dies längere Zeit in Anspruch nehmen. Bitte vergiss nicht, zuvor eine Sicherheitskopie der Datenbank zu erstellen, bevor du weitermachst.',
+'Intro 2' => 'Hast du die Informationen zur Systemaktualisierung in der Dokumentation gelesen? Falls nicht, beginne hier.',
+'No charset conversion' => '<strong>ACHTUNG!</strong> FluxBB hat erkannt, dass deine PHP-Umgebung keine andere Zeichensatz-Konvertierung als von ISO-8859-1 nach UTF-8 unterstützt. Das bedeutet, dass FluxBB die Forendatenbank nicht nach UTF-8 konvertieren kann, falls der aktuelle Zeichensatz nicht ISO-8859-1 ist. Du musst dies daher manuell vornehmen. Eine Anleitung zur manuellen Konvertierung der Zeichensätze findest du in den Aktualisierungs-Informationen.',
+'Enable conversion' => '<strong>Konvertierung aktivieren:</strong> Wird dies aktiviert, wird das Aktualisierungs-Script nach den erforderlichen strukturellen Änderungen an der Datenbank alle in der Datenbank vorhandenen Texte vom aktuellen Zeichensatz nach UTF-8 konvertieren. Diese Konvertierung ist erforderlich, wenn von FluxBB Version 1.2 aktualisiert wird.',
+'Current character set' => '<strong>Aktueller Zeichensatz:</strong> Falls die in deinem Forum hauptsächlich verwendete Sprache Englisch ist, musst du keine Änderungen an den voreingestellten Werten vornehmen. Falls dein Forum jedoch kein Englisch verwendet, solltest du den Zeichensatz des primären, im Forum verwendeten Sprachpakets eingeben. <em>Mit einer falschen Eingabe kann die Datenbank beschädigt werden!</em> Hinweis: Dies ist nur dann erforderlich, wenn die alte Datenbank UTF-8 ist.',
+'Charset conversion' => 'Zeichensatz-Konvertierung',
+'Enable conversion label' => '<strong>Konvertierung aktivieren</strong> (führt eine Umwandlung des Datenbank-Zeichensatzes aus).',
+'Current character set label' => 'Aktueller Zeichensatz',
+'Current character set info' => 'Die Voreinstellung für englische Foren akzeptieren, anderenfalls den Zeichensatz des primären Sprachpakets eingeben.',
+'Start update' => 'Aktualisierung starten',
+'Error converting users' => 'Fehler bei der Konvertierung von Mitgliedern',
+'Error info 1' => 'Beim Konvertieren einiger Mitglieder ist ein Fehler aufgetreten. Dieser Fehler kann bei der Aktualisierung von FluxBB 1.2 auftreten, wenn sich mehrere Mitglieder mit ähnlichen Namen registriert haben, wie zum Beispiel "Bob" and "Böb".',
+'Error info 2' => 'Nachfolgend findest du eine Liste der Mitglieder, die nicht umgewandelt werden konnten. Bitte wähle einen neuen Mitgliedsnamen für jedes Mitglied. Die umbenamsten Mitglieder werden automatisch über diese Änderung via Email informiert.',
+'New username' => 'Neuer Mitgliedsname',
+'Required' => '(Pflichtfeld)',
+'Correct errors' => 'Die folgenden Fehler müssen behoben werden:',
+'Rename users' => 'Mitglieder umbenennen',
+'Successfully updated' => 'Deine Forendatenbank wurde aktualisiert. Du kannst jetzt %s.',
+'go to index' => 'Gehe zum Foren-Index',
+
+'Unable to lock error' => 'Konnte die Schreibrechte nicht setzen. Bitte stelle sicher, dass PHP Schreibzugriff auf das Verzeichnis \'%s\' und niemand sonst gerade das Aktualisierungs-Script ausführt.',
+
+'Converting' => 'Konvertiere %s …',
+'Converting item' => 'Konvertiere %1$s %2$s …',
+'Preparsing item' => 'Verarbeite %1$s %2$s …',
+'Rebuilding index item' => 'Erstelle den Index für %1$s %2$s neu',
+'Click here' => 'Hier klicken',
+'Automatic redirect failed' => 'Automatische Weiterleitung war nicht erfolgreich. %s um fortzufahren…',
+
+'ban' => 'Mitgliedssperre',
+'categories' => 'Kategorien',
+'censor words' => 'Zensierte Wörter',
+'configuration' => 'Konfiguration',
+'forums' => 'Foren',
+'groups' => 'Gruppen',
+'post' => 'Beitrag',
+'report' => 'Berichte',
+'topic' => 'Themen',
+'user' => 'Mitglied',
+'signature' => 'Signatur',
+
+'Username too short error' => 'Der Mitgliedsname muss mindestens 2 Zeichen lang sein. Bitte wähle einen anderen (längeren) Mitgliedsnamen.',
+'Username too long error' => 'Mitgliedsnamen dürfen nicht länger als 25 Zeichen lang sein. Bitte wähle einen anderen (kürzeren) Mitgliedsnamen.',
+'Username Guest reserved error' => 'Der Mitgliedsname Gast ist reserviert. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username IP format error' => 'Der Mitgliedsname darf nicht die Form einer IP-Adresse haben. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username bad characters error' => 'Mitgliedsnamen dürfen nicht die Zeichen \', " und [ oder ] enthalten. Bitte wähle einen anderen Mitgliedsnamen.',
+'Username BBCode error' => 'Mitgliedsnamen dürfen keine (vom Forum verwendeten) Formatierungs-Tags enthalten (BBCode). Bitte wähle einen anderen Mitgliedsnamen.',
+'Username duplicate error' => 'Es hat sich bereits ein Mitglied mit dem Namen %s registriert. Der eingegebene Mitgliedsname ist zu ähnlich. Der Mitgliedsname muss mindestens um ein alphanumerisches Zeichen davon abweichen (a-z oder 0-9). Bitte wähle einen anderen Mitgliedsnamen.',
+
+);
diff --git a/lang/German/userlist.php b/lang/German/userlist.php
new file mode 100644
index 0000000..aa3a293
--- /dev/null
+++ b/lang/German/userlist.php
@@ -0,0 +1,13 @@
+<?php
+
+// Sprachdefinitionen, die in userlist.php verwendet werden
+$lang_ul = array(
+
+'User find legend' => 'Mitglieder suchen und sortieren',
+'User search info' => 'Gib einen Mitgliedsnamen und/oder eine Mitgliedergruppe ein, nach der gefiltert werden soll. Der Mitgliedsname kann leer bleiben. Benutze Wildcards (*) für eine Suche nach Teilbegriffen. Sortiere die Mitglieder nach Namen, Registrierungsdatum oder Anzahl von Beiträgen und in auf-/absteigender Reihenfolge.',
+'User sort info' => 'Sortiere Mitglieder nach Name, Registrierungsdatum oder Anzahl der Beiträge in aufsteigender oder absteigender Reihenfolge.',
+'User group' => 'Mitgliedergruppe',
+'No of posts' => 'Anzahl der Beiträge',
+'All users' => 'Alle'
+
+);
diff --git a/lang/index.html b/lang/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/lang/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/login.php b/login.php
new file mode 100644
index 0000000..323629a
--- /dev/null
+++ b/login.php
@@ -0,0 +1,335 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+if (isset($_GET['action']))
+ define('PUN_QUIET_VISIT', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+// Load the login.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/login.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$errors = array();
+
+if (isset($_POST['form_sent']) && $action == 'in')
+{
+ flux_hook('login_before_validation');
+
+ check_csrf($_POST['csrf_token']);
+
+ $form_username = pun_trim($_POST['req_username']);
+ $form_password = pun_trim($_POST['req_password']);
+ $save_pass = isset($_POST['save_pass']);
+
+ $username_sql = ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb') ? 'username=\''.$db->escape($form_username).'\'' : 'LOWER(username)=LOWER(\''.$db->escape($form_username).'\')';
+
+ $result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE '.$username_sql) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ $cur_user = $db->fetch_assoc($result);
+
+ $authorized = false;
+
+ if (!empty($cur_user['password']))
+ {
+ $form_password_hash = pun_hash($form_password); // Will result in a SHA-1 hash
+
+ // If there is a salt in the database we have upgraded from 1.3-legacy though haven't yet logged in
+ if (!empty($cur_user['salt']))
+ {
+ $is_salt_authorized = pun_hash_equals(sha1($cur_user['salt'].sha1($form_password)), $cur_user['password']);
+ if ($is_salt_authorized) // 1.3 used sha1(salt.sha1(pass))
+ {
+ $authorized = true;
+
+ $db->query('UPDATE '.$db->prefix.'users SET password=\''.$form_password_hash.'\', salt=NULL WHERE id='.$cur_user['id']) or error('Unable to update user password', __FILE__, __LINE__, $db->error());
+ }
+ }
+ // If the length isn't 40 then the password isn't using sha1, so it must be md5 from 1.2
+ else if (strlen($cur_user['password']) != 40)
+ {
+ $is_md5_authorized = pun_hash_equals(md5($form_password), $cur_user['password']);
+ if ($is_md5_authorized)
+ {
+ $authorized = true;
+
+ $db->query('UPDATE '.$db->prefix.'users SET password=\''.$form_password_hash.'\' WHERE id='.$cur_user['id']) or error('Unable to update user password', __FILE__, __LINE__, $db->error());
+ }
+ }
+ // Otherwise we should have a normal sha1 password
+ else
+ $authorized = pun_hash_equals($cur_user['password'], $form_password_hash);
+ }
+
+ if (!$authorized)
+ $errors[] = $lang_login['Wrong user/pass'];
+
+ flux_hook('login_after_validation');
+
+ // Did everything go according to plan?
+ if (empty($errors))
+ {
+ // Update the status if this is the first time the user logged in
+ if ($cur_user['group_id'] == PUN_UNVERIFIED)
+ {
+ $db->query('UPDATE '.$db->prefix.'users SET group_id='.$pun_config['o_default_user_group'].' WHERE id='.$cur_user['id']) or error('Unable to update user status', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the users info cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+ }
+
+ // Remove this user's guest entry from the online list
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape(get_remote_address()).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+
+ $expire = ($save_pass == '1') ? time() + 1209600 : time() + $pun_config['o_timeout_visit'];
+ pun_setcookie($cur_user['id'], $form_password_hash, $expire);
+
+ // Reset tracked topics
+ set_tracked_topics(null);
+
+ // Try to determine if the data in redirect_url is valid (if not, we redirect to index.php after login)
+ $redirect_url = validate_redirect($_POST['redirect_url'], 'index.php');
+
+ redirect(pun_htmlspecialchars($redirect_url), $lang_login['Login redirect']);
+ }
+}
+
+
+else if ($action == 'out')
+{
+ if ($pun_user['is_guest'] || !isset($_GET['id']) || $_GET['id'] != $pun_user['id'])
+ {
+ header('Location: index.php');
+ exit;
+ }
+
+ check_csrf($_GET['csrf_token']);
+
+ // Remove user from "users online" list
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$pun_user['id']) or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+
+ // Update last_visit (make sure there's something to update it with)
+ if (isset($pun_user['logged']))
+ $db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
+
+ pun_setcookie(1, pun_hash(uniqid(rand(), true)), time() + 31536000);
+
+ redirect('index.php', $lang_login['Logout redirect']);
+}
+
+
+else if ($action == 'forget' || $action == 'forget_2')
+{
+ if (!$pun_user['is_guest'])
+ {
+ header('Location: index.php');
+ exit;
+ }
+
+ if (isset($_POST['form_sent']))
+ {
+ flux_hook('forget_password_before_validation');
+
+ require PUN_ROOT.'include/email.php';
+
+ // Validate the email address
+ $email = strtolower(pun_trim($_POST['req_email']));
+ if (!is_valid_email($email))
+ $errors[] = $lang_common['Invalid email'];
+
+ flux_hook('forget_password_after_validation');
+
+ // Did everything go according to plan?
+ if (empty($errors))
+ {
+ $result = $db->query('SELECT id, username, last_email_sent FROM '.$db->prefix.'users WHERE email=\''.$db->escape($email).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ // Load the "activate password" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/activate_password.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ // Do the generic replacements first (they apply to all emails sent out here)
+ $mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ // Loop through users we found
+ while ($cur_hit = $db->fetch_assoc($result))
+ {
+ if ($cur_hit['last_email_sent'] != '' && (time() - $cur_hit['last_email_sent']) < 3600 && (time() - $cur_hit['last_email_sent']) >= 0)
+ message(sprintf($lang_login['Email flood'], intval((3600 - (time() - $cur_hit['last_email_sent'])) / 60)), true);
+
+ // Generate a new password and a new password activation code
+ $new_password = random_pass(12);
+ $new_password_key = random_pass(8);
+
+ $db->query('UPDATE '.$db->prefix.'users SET activate_string=\''.pun_hash($new_password).'\', activate_key=\''.$new_password_key.'\', last_email_sent = '.time().' WHERE id='.$cur_hit['id']) or error('Unable to update activation data', __FILE__, __LINE__, $db->error());
+
+ // Do the user specific replacements to the template
+ $cur_mail_message = str_replace('<username>', $cur_hit['username'], $mail_message);
+ $cur_mail_message = str_replace('<activation_url>', get_base_url().'/profile.php?id='.$cur_hit['id'].'&action=change_pass&key='.$new_password_key, $cur_mail_message);
+ $cur_mail_message = str_replace('<new_password>', $new_password, $cur_mail_message);
+
+ pun_mail($email, $mail_subject, $cur_mail_message);
+ }
+
+ message($lang_login['Forget mail'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.', true);
+ }
+ else
+ $errors[] = $lang_login['No email match'].' '.pun_htmlspecialchars($email).'.';
+ }
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_login['Request pass']);
+ $required_fields = array('req_email' => $lang_common['Email']);
+ $focus_element = array('request_pass', 'req_email');
+
+ flux_hook('forget_password_before_header');
+
+ define ('PUN_ACTIVE_PAGE', 'login');
+ require PUN_ROOT.'header.php';
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+ <h2><span><?php echo $lang_login['New password errors'] ?></span></h2>
+ <div class="box">
+ <div class="inbox error-info">
+ <p><?php echo $lang_login['New passworderrors info'] ?></p>
+ <ul class="error-list">
+<?php
+
+ foreach ($errors as $cur_error)
+ echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_login['Request pass'] ?></span></h2>
+ <div class="box">
+ <form id="request_pass" method="post" action="login.php?action=forget_2" onsubmit="this.request_pass.disabled=true;if(process_form(this)){return true;}else{this.request_pass.disabled=false;return false;}">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_login['Request pass legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <label class="required"><strong><?php echo $lang_common['Email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input id="req_email" type="text" name="req_email" value="<?php if (isset($_POST['req_email'])) echo pun_htmlspecialchars($_POST['req_email']); ?>" size="50" maxlength="80" /><br /></label>
+ <p><?php echo $lang_login['Request pass info'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+<?php flux_hook('forget_password_before_submit') ?>
+ <p class="buttons"><input type="submit" name="request_pass" value="<?php echo $lang_common['Submit'] ?>" /><?php if (empty($errors)): ?> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a><?php endif; ?></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+if (!$pun_user['is_guest'])
+{
+ header('Location: index.php');
+ exit;
+}
+
+// Try to determine if the data in HTTP_REFERER is valid (if not, we redirect to index.php after login)
+if (!empty($_SERVER['HTTP_REFERER']))
+ $redirect_url = validate_redirect($_SERVER['HTTP_REFERER'], null);
+
+if (!isset($redirect_url))
+ $redirect_url = get_base_url(true).'/index.php';
+else if (preg_match('%viewtopic\.php\?pid=(\d+)$%', $redirect_url, $matches))
+ $redirect_url .= '#p'.$matches[1];
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Login']);
+$required_fields = array('req_username' => $lang_common['Username'], 'req_password' => $lang_common['Password']);
+$focus_element = array('login', 'req_username');
+
+flux_hook('login_before_header');
+
+define('PUN_ACTIVE_PAGE', 'login');
+require PUN_ROOT.'header.php';
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+ <h2><span><?php echo $lang_login['Login errors'] ?></span></h2>
+ <div class="box">
+ <div class="inbox error-info">
+ <p><?php echo $lang_login['Login errors info'] ?></p>
+ <ul class="error-list">
+<?php
+
+ foreach ($errors as $cur_error)
+ echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_common['Login'] ?></span></h2>
+ <div class="box">
+ <form id="login" method="post" action="login.php?action=in" onsubmit="return process_form(this)">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_login['Login legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <input type="hidden" name="redirect_url" value="<?php echo pun_htmlspecialchars($redirect_url) ?>" />
+ <input type="hidden" name="csrf_token" value="<?php echo pun_csrf_token() ?>" />
+ <label class="conl required"><strong><?php echo $lang_common['Username'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_username" value="<?php if (isset($_POST['req_username'])) echo pun_htmlspecialchars($_POST['req_username']); ?>" size="25" maxlength="25" tabindex="1" /><br /></label>
+ <label class="conl required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password" size="25" tabindex="2" /><br /></label>
+
+ <div class="rbox clearb">
+ <label><input type="checkbox" name="save_pass" value="1"<?php if (isset($_POST['save_pass'])) echo ' checked="checked"'; ?> tabindex="3" /><?php echo $lang_login['Remember me'] ?><br /></label>
+ </div>
+
+ <p class="clearb"><?php echo $lang_login['Login info'] ?></p>
+ <p class="actions"><span><a href="register.php" tabindex="5"><?php echo $lang_login['Not registered'] ?></a></span> <span><a href="login.php?action=forget" tabindex="6"><?php echo $lang_login['Forgotten pass'] ?></a></span></p>
+ </div>
+ </fieldset>
+ </div>
+<?php flux_hook('login_before_submit') ?>
+ <p class="buttons"><input type="submit" name="login" value="<?php echo $lang_common['Login'] ?>" tabindex="4" /></p>
+ </form>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/misc.php b/misc.php
new file mode 100644
index 0000000..de3184c
--- /dev/null
+++ b/misc.php
@@ -0,0 +1,414 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+if (isset($_GET['action']))
+ define('PUN_QUIET_VISIT', 1);
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+// Load the misc.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/misc.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+
+
+if ($action == 'rules')
+{
+ if ($pun_config['o_rules'] == '0' || ($pun_user['is_guest'] && $pun_user['g_read_board'] == '0' && $pun_config['o_regs_allow'] == '0'))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Load the register.php language file
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_register['Forum rules']);
+ define('PUN_ACTIVE_PAGE', 'rules');
+ require PUN_ROOT.'header.php';
+
+?>
+<div id="rules" class="block">
+ <div class="hd"><h2><span><?php echo $lang_register['Forum rules'] ?></span></h2></div>
+ <div class="box">
+ <div id="rules-block" class="inbox">
+ <div class="usercontent"><?php echo $pun_config['o_rules_message'] ?></div>
+ </div>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'markread')
+{
+ if ($pun_user['is_guest'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ check_csrf($_GET['csrf_token']);
+
+ $db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user last visit data', __FILE__, __LINE__, $db->error());
+
+ // Reset tracked topics
+ set_tracked_topics(null);
+
+ redirect('index.php', $lang_misc['Mark read redirect']);
+}
+
+
+// Mark the topics/posts in a forum as read?
+else if ($action == 'markforumread')
+{
+ if ($pun_user['is_guest'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ check_csrf($_GET['csrf_token']);
+
+ $fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+ if ($fid < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $tracked_topics = get_tracked_topics();
+ $tracked_topics['forums'][$fid] = time();
+ set_tracked_topics($tracked_topics);
+
+ redirect('viewforum.php?id='.$fid, $lang_misc['Mark forum read redirect']);
+}
+
+
+else if (isset($_GET['email']))
+{
+ if ($pun_user['is_guest'] || $pun_user['g_send_email'] == '0')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ $recipient_id = intval($_GET['email']);
+ if ($recipient_id < 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT username, email, email_setting FROM '.$db->prefix.'users WHERE id='.$recipient_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($recipient, $recipient_email, $email_setting) = $db->fetch_row($result);
+
+ if ($email_setting == 2 && !$pun_user['is_admmod'])
+ message($lang_misc['Form email disabled']);
+
+
+ if (isset($_POST['form_sent']))
+ {
+ confirm_referrer('misc.php');
+
+ // Clean up message and subject from POST
+ $subject = pun_trim($_POST['req_subject']);
+ $message = pun_trim($_POST['req_message']);
+
+ if ($subject == '')
+ message($lang_misc['No email subject']);
+ else if ($message == '')
+ message($lang_misc['No email message']);
+ // Here we use strlen() not pun_strlen() as we want to limit the post to PUN_MAX_POSTSIZE bytes, not characters
+ else if (strlen($message) > PUN_MAX_POSTSIZE)
+ message($lang_misc['Too long email message']);
+
+ if ($pun_user['last_email_sent'] != '' && (time() - $pun_user['last_email_sent']) < $pun_user['g_email_flood'] && (time() - $pun_user['last_email_sent']) >= 0)
+ message(sprintf($lang_misc['Email flood'], $pun_user['g_email_flood'], $pun_user['g_email_flood'] - (time() - $pun_user['last_email_sent'])));
+
+ // Load the "form email" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/form_email.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = pun_trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = pun_trim(substr($mail_tpl, $first_crlf));
+
+ $mail_subject = str_replace('<mail_subject>', $subject, $mail_subject);
+ $mail_message = str_replace('<sender>', $pun_user['username'], $mail_message);
+ $mail_message = str_replace('<board_title>', $pun_config['o_board_title'], $mail_message);
+ $mail_message = str_replace('<mail_message>', $message, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ require_once PUN_ROOT.'include/email.php';
+
+ pun_mail($recipient_email, $mail_subject, $mail_message, $pun_user['email'], $pun_user['username']);
+
+ $db->query('UPDATE '.$db->prefix.'users SET last_email_sent='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+ // Try to determine if the data in redirect_url is valid (if not, we redirect to index.php after the email is sent)
+ $redirect_url = validate_redirect($_POST['redirect_url'], 'index.php');
+
+ redirect(pun_htmlspecialchars($redirect_url), $lang_misc['Email sent redirect']);
+ }
+
+
+ // Try to determine if the data in HTTP_REFERER is valid (if not, we redirect to the user's profile after the email is sent)
+ if (!empty($_SERVER['HTTP_REFERER']))
+ $redirect_url = validate_redirect($_SERVER['HTTP_REFERER'], null);
+
+ if (!isset($redirect_url))
+ $redirect_url = get_base_url(true).'/profile.php?id='.$recipient_id;
+ else if (preg_match('%viewtopic\.php\?pid=(\d+)$%', $redirect_url, $matches))
+ $redirect_url .= '#p'.$matches[1];
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Send email to'].' '.pun_htmlspecialchars($recipient));
+ $required_fields = array('req_subject' => $lang_misc['Email subject'], 'req_message' => $lang_misc['Email message']);
+ $focus_element = array('email', 'req_subject');
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div id="emailform" class="blockform">
+ <h2><span><?php echo $lang_misc['Send email to'] ?> <?php echo pun_htmlspecialchars($recipient) ?></span></h2>
+ <div class="box">
+ <form id="email" method="post" action="misc.php?email=<?php echo $recipient_id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_misc['Write email'] ?></legend>
+ <div class="infldset txtarea">
+ <input type="hidden" name="form_sent" value="1" />
+ <input type="hidden" name="redirect_url" value="<?php echo pun_htmlspecialchars($redirect_url) ?>" />
+ <label class="required"><strong><?php echo $lang_misc['Email subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input class="longinput" type="text" name="req_subject" size="75" maxlength="70" tabindex="1" /><br /></label>
+ <label class="required"><strong><?php echo $lang_misc['Email message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <textarea name="req_message" rows="10" cols="75" tabindex="2"></textarea><br /></label>
+ <p><?php echo $lang_misc['Email disclosure note'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" tabindex="3" accesskey="s" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_GET['report']))
+{
+ if ($pun_user['is_guest'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ $post_id = intval($_GET['report']);
+ if ($post_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if (isset($_POST['form_sent']))
+ {
+ // Make sure they got here from the site
+ confirm_referrer('misc.php');
+
+ // Clean up reason from POST
+ $reason = pun_linebreaks(pun_trim($_POST['req_reason']));
+ if ($reason == '')
+ message($lang_misc['No reason']);
+ else if (strlen($reason) > 65535) // TEXT field can only hold 65535 bytes
+ message($lang_misc['Reason too long']);
+
+ if ($pun_user['last_report_sent'] != '' && (time() - $pun_user['last_report_sent']) < $pun_user['g_report_flood'] && (time() - $pun_user['last_report_sent']) >= 0)
+ message(sprintf($lang_misc['Report flood'], $pun_user['g_report_flood'], $pun_user['g_report_flood'] - (time() - $pun_user['last_report_sent'])));
+
+ // Get the topic ID
+ $result = $db->query('SELECT topic_id FROM '.$db->prefix.'posts WHERE id='.$post_id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $topic_id = $db->result($result);
+
+ // Get the subject and forum ID
+ $result = $db->query('SELECT subject, forum_id FROM '.$db->prefix.'topics WHERE id='.$topic_id) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($subject, $forum_id) = $db->fetch_row($result);
+
+ // Should we use the internal report handling?
+ if ($pun_config['o_report_method'] == '0' || $pun_config['o_report_method'] == '2')
+ $db->query('INSERT INTO '.$db->prefix.'reports (post_id, topic_id, forum_id, reported_by, created, message) VALUES('.$post_id.', '.$topic_id.', '.$forum_id.', '.$pun_user['id'].', '.time().', \''.$db->escape($reason).'\')' ) or error('Unable to create report', __FILE__, __LINE__, $db->error());
+
+ // Should we email the report?
+ if ($pun_config['o_report_method'] == '1' || $pun_config['o_report_method'] == '2')
+ {
+ // We send it to the complete mailing-list in one swoop
+ if ($pun_config['o_mailing_list'] != '')
+ {
+ // Load the "new report" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/new_report.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_subject = str_replace('<forum_id>', $forum_id, $mail_subject);
+ $mail_subject = str_replace('<topic_subject>', $subject, $mail_subject);
+ $mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+ $mail_message = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$post_id.'#p'.$post_id, $mail_message);
+ $mail_message = str_replace('<reason>', $reason, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ require PUN_ROOT.'include/email.php';
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+ }
+
+ $db->query('UPDATE '.$db->prefix.'users SET last_report_sent='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+ redirect('viewforum.php?id='.$forum_id, $lang_misc['Report redirect']);
+ }
+
+ // Fetch some info about the post, the topic and the forum
+ $result = $db->query('SELECT f.id AS fid, f.forum_name, t.id AS tid, t.subject FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.id='.$post_id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $cur_post = $db->fetch_assoc($result);
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_post['subject'] = censor_words($cur_post['subject']);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Report post']);
+ $required_fields = array('req_reason' => $lang_misc['Reason']);
+ $focus_element = array('report', 'req_reason');
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $cur_post['fid'] ?>"><?php echo pun_htmlspecialchars($cur_post['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><a href="viewtopic.php?pid=<?php echo $post_id ?>#p<?php echo $post_id ?>"><?php echo pun_htmlspecialchars($cur_post['subject']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_misc['Report post'] ?></strong></li>
+ </ul>
+ </div>
+</div>
+
+<div id="reportform" class="blockform">
+ <h2><span><?php echo $lang_misc['Report post'] ?></span></h2>
+ <div class="box">
+ <form id="report" method="post" action="misc.php?report=<?php echo $post_id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_misc['Reason desc'] ?></legend>
+ <div class="infldset txtarea">
+ <input type="hidden" name="form_sent" value="1" />
+ <label class="required"><strong><?php echo $lang_misc['Reason'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><textarea name="req_reason" rows="5" cols="60"></textarea><br /></label>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'subscribe')
+{
+ if ($pun_user['is_guest'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ check_csrf($_GET['csrf_token']);
+
+ $topic_id = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
+ $forum_id = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+ if ($topic_id < 1 && $forum_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($topic_id)
+ {
+ if ($pun_config['o_topic_subscriptions'] != '1')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ // Make sure the user can view the topic
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$topic_id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ message($lang_misc['Already subscribed topic']);
+
+ $db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$topic_id.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+ redirect('viewtopic.php?id='.$topic_id, $lang_misc['Subscribe redirect']);
+ }
+
+ if ($forum_id)
+ {
+ if ($pun_config['o_forum_subscriptions'] != '1')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ // Make sure the user can view the forum
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$forum_id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$pun_user['id'].' AND forum_id='.$forum_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ message($lang_misc['Already subscribed forum']);
+
+ $db->query('INSERT INTO '.$db->prefix.'forum_subscriptions (user_id, forum_id) VALUES('.$pun_user['id'].' ,'.$forum_id.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+ redirect('viewforum.php?id='.$forum_id, $lang_misc['Subscribe redirect']);
+ }
+}
+
+
+else if ($action == 'unsubscribe')
+{
+ if ($pun_user['is_guest'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ check_csrf($_GET['csrf_token']);
+
+ $topic_id = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
+ $forum_id = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+ if ($topic_id < 1 && $forum_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ if ($topic_id)
+ {
+ if ($pun_config['o_topic_subscriptions'] != '1')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_misc['Not subscribed topic']);
+
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+
+ redirect('viewtopic.php?id='.$topic_id, $lang_misc['Unsubscribe redirect']);
+ }
+
+ if ($forum_id)
+ {
+ if ($pun_config['o_forum_subscriptions'] != '1')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$pun_user['id'].' AND forum_id='.$forum_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_misc['Not subscribed forum']);
+
+ $db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$pun_user['id'].' AND forum_id='.$forum_id) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+
+ redirect('viewforum.php?id='.$forum_id, $lang_misc['Unsubscribe redirect']);
+ }
+}
+
+
+else
+ message($lang_common['Bad request'], false, '404 Not Found');
diff --git a/moderate.php b/moderate.php
new file mode 100644
index 0000000..02d62b0
--- /dev/null
+++ b/moderate.php
@@ -0,0 +1,1020 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+// This particular function doesn't require forum-based moderator access. It can be used
+// by all moderators and admins
+if (isset($_GET['get_host']))
+{
+ if (!$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ // Is get_host an IP address or a post ID?
+ if (@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $_GET['get_host']) || @preg_match('%^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$%', $_GET['get_host']))
+ $ip = $_GET['get_host'];
+ else
+ {
+ $get_host = intval($_GET['get_host']);
+ if ($get_host < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT poster_ip FROM '.$db->prefix.'posts WHERE id='.$get_host) or error('Unable to fetch post IP address', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $ip = $db->result($result);
+ }
+
+ // Load the misc.php language file
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/misc.php';
+
+ message(sprintf($lang_misc['Host info 1'], $ip).'<br />'.sprintf($lang_misc['Host info 2'], pun_htmlspecialchars(@gethostbyaddr($ip))).'<br /><br /><a href="admin_users.php?show_users='.$ip.'">'.$lang_misc['Show more users'].'</a>');
+}
+
+
+// All other functions require moderator/admin access
+$fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+if ($fid < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$result = $db->query('SELECT moderators FROM '.$db->prefix.'forums WHERE id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+
+$moderators = $db->result($result);
+$mods_array = ($moderators != '') ? unserialize($moderators) : array();
+
+if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] == '0' || !array_key_exists($pun_user['username'], $mods_array)))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Get topic/forum tracking data
+if (!$pun_user['is_guest'])
+ $tracked_topics = get_tracked_topics();
+
+// Load the misc.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/misc.php';
+
+
+// All other topic moderation features require a topic ID in GET
+if (isset($_GET['tid']))
+{
+ $tid = intval($_GET['tid']);
+ if ($tid < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Fetch some info about the topic
+ $result = $db->query('SELECT t.subject, t.num_replies, t.first_post_id, f.id AS forum_id, forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid.' AND t.id='.$tid.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $cur_topic = $db->fetch_assoc($result);
+
+ // Delete one or more posts
+ if (isset($_POST['delete_posts']) || isset($_POST['delete_posts_comply']))
+ {
+ $posts = isset($_POST['posts']) ? $_POST['posts'] : array();
+ if (empty($posts))
+ message($lang_misc['No posts selected']);
+
+ if (isset($_POST['delete_posts_comply']))
+ {
+ confirm_referrer('moderate.php');
+
+ if (@preg_match('%[^0-9,]%', $posts))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Verify that the post IDs are valid
+ $admins_sql = ($pun_user['g_id'] != PUN_ADMIN) ? ' AND poster_id NOT IN('.implode(',', get_admin_ids()).')' : '';
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id IN('.$posts.') AND topic_id='.$tid.$admins_sql) or error('Unable to check posts', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result) != substr_count($posts, ',') + 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Delete the posts
+ $db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$posts.')') or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
+
+ require PUN_ROOT.'include/search_idx.php';
+ strip_search_index($posts);
+
+ // Get last_post, last_post_id, and last_poster for the topic after deletion
+ $result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $last_post = $db->fetch_assoc($result);
+
+ // How many posts did we just delete?
+ $num_posts_deleted = substr_count($posts, ',') + 1;
+
+ // Update the topic
+ $db->query('UPDATE '.$db->prefix.'topics SET last_post='.$last_post['posted'].', last_post_id='.$last_post['id'].', last_poster=\''.$db->escape($last_post['poster']).'\', num_replies=num_replies-'.$num_posts_deleted.' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ update_forum($fid);
+
+ redirect('viewtopic.php?id='.$tid, $lang_misc['Delete posts redirect']);
+ }
+
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_misc['Delete posts'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="moderate.php?fid=<?php echo $fid ?>&amp;tid=<?php echo $tid ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_misc['Confirm delete legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="posts" value="<?php echo implode(',', array_map('intval', array_keys($posts))) ?>" />
+ <p><?php echo $lang_misc['Delete posts comply'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="delete_posts_comply" value="<?php echo $lang_misc['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ }
+ else if (isset($_POST['split_posts']) || isset($_POST['split_posts_comply']))
+ {
+ $posts = isset($_POST['posts']) ? $_POST['posts'] : array();
+ if (empty($posts))
+ message($lang_misc['No posts selected']);
+
+ if (isset($_POST['split_posts_comply']))
+ {
+ confirm_referrer('moderate.php');
+
+ if (@preg_match('%[^0-9,]%', $posts))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $move_to_forum = isset($_POST['move_to_forum']) ? intval($_POST['move_to_forum']) : 0;
+ if ($move_to_forum < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // How many posts did we just split off?
+ $num_posts_splitted = substr_count($posts, ',') + 1;
+
+ // Verify that the post IDs are valid
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id IN('.$posts.') AND topic_id='.$tid) or error('Unable to check posts', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result) != $num_posts_splitted)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Verify that the move to forum ID is valid
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.group_id='.$pun_user['g_id'].' AND fp.forum_id='.$move_to_forum.') WHERE f.redirect_url IS NULL AND (fp.post_topics IS NULL OR fp.post_topics=1)') or error('Unable to fetch forum permissions', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Load the post.php language file
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+ // Check subject
+ $new_subject = isset($_POST['new_subject']) ? pun_trim($_POST['new_subject']) : '';
+
+ if ($new_subject == '')
+ message($lang_post['No subject']);
+ else if (pun_strlen($new_subject) > 70)
+ message($lang_post['Too long subject']);
+
+ // Get data from the new first post
+ $result = $db->query('SELECT p.id, p.poster, p.posted FROM '.$db->prefix.'posts AS p WHERE id IN('.$posts.') ORDER BY p.id ASC LIMIT 1') or error('Unable to get first post', __FILE__, __LINE__, $db->error());
+ $first_post_data = $db->fetch_assoc($result);
+
+ // Create the new topic
+ $db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, first_post_id, forum_id) VALUES (\''.$db->escape($first_post_data['poster']).'\', \''.$db->escape($new_subject).'\', '.$first_post_data['posted'].', '.$first_post_data['id'].', '.$move_to_forum.')') or error('Unable to create new topic', __FILE__, __LINE__, $db->error());
+ $new_tid = $db->insert_id();
+
+ // Move the posts to the new topic
+ $db->query('UPDATE '.$db->prefix.'posts SET topic_id='.$new_tid.' WHERE id IN('.$posts.')') or error('Unable to move posts into new topic', __FILE__, __LINE__, $db->error());
+
+ // Apply every subscription to both topics
+ $db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) SELECT user_id, '.$new_tid.' FROM '.$db->prefix.'topic_subscriptions WHERE topic_id='.$tid) or error('Unable to copy existing subscriptions', __FILE__, __LINE__, $db->error());
+
+ // Get last_post, last_post_id, and last_poster from the topic and update it
+ $result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $last_post_data = $db->fetch_assoc($result);
+ $db->query('UPDATE '.$db->prefix.'topics SET last_post='.$last_post_data['posted'].', last_post_id='.$last_post_data['id'].', last_poster=\''.$db->escape($last_post_data['poster']).'\', num_replies=num_replies-'.$num_posts_splitted.' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ // Get last_post, last_post_id, and last_poster from the new topic and update it
+ $result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$new_tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $last_post_data = $db->fetch_assoc($result);
+ $db->query('UPDATE '.$db->prefix.'topics SET last_post='.$last_post_data['posted'].', last_post_id='.$last_post_data['id'].', last_poster=\''.$db->escape($last_post_data['poster']).'\', num_replies='.($num_posts_splitted-1).' WHERE id='.$new_tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ update_forum($fid);
+ update_forum($move_to_forum);
+
+ redirect('viewtopic.php?id='.$new_tid, $lang_misc['Split posts redirect']);
+ }
+
+ $result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.post_topics IS NULL OR fp.post_topics=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+ $focus_element = array('subject','new_subject');
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_misc['Split posts'] ?></span></h2>
+ <div class="box">
+ <form id="subject" method="post" action="moderate.php?fid=<?php echo $fid ?>&amp;tid=<?php echo $tid ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_misc['Confirm split legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="posts" value="<?php echo implode(',', array_map('intval', array_keys($posts))) ?>" />
+ <label class="required"><strong><?php echo $lang_misc['New subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="new_subject" size="80" maxlength="70" /><br /></label>
+ <label><?php echo $lang_misc['Move to'] ?>
+ <br /><select name="move_to_forum">
+<?php
+
+ $cur_category = 0;
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category)
+ echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+ echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+ $cur_category = $cur_forum['cid'];
+ }
+
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'"'.($fid == $cur_forum['fid'] ? ' selected="selected"' : '').'>'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
+ }
+
+?>
+ </optgroup>
+ </select>
+ <br /></label>
+ <p><?php echo $lang_misc['Split posts comply'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="split_posts_comply" value="<?php echo $lang_misc['Split'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ }
+
+
+ // Show the moderate posts view
+
+ // Load the viewtopic.php language file
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
+
+ // Used to disable the Move and Delete buttons if there are no replies to this topic
+ $button_status = ($cur_topic['num_replies'] == 0) ? ' disabled="disabled"' : '';
+
+ if (isset($_GET['action']) && $_GET['action'] == 'all')
+ $pun_user['disp_posts'] = $cur_topic['num_replies'] + 1;
+
+ // Determine the post offset (based on $_GET['p'])
+ $num_pages = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+ $p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+ $start_from = $pun_user['disp_posts'] * ($p - 1);
+
+ // Generate paging links
+ $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'moderate.php?fid='.$fid.'&amp;tid='.$tid);
+
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_topic['forum_name']), pun_htmlspecialchars($cur_topic['subject']));
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $fid ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><a href="viewtopic.php?id=<?php echo $tid ?>"><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_misc['Moderate'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<form method="post" action="moderate.php?fid=<?php echo $fid ?>&amp;tid=<?php echo $tid ?>">
+<?php
+
+ require PUN_ROOT.'include/parser.php';
+
+ $post_count = 0; // Keep track of post numbers
+
+ // Retrieve a list of post IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+ $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id LIMIT '.$start_from.','.$pun_user['disp_posts']) or error('Unable to fetch post IDs', __FILE__, __LINE__, $db->error());
+
+ $post_ids = array();
+ for ($i = 0;$cur_post_id = $db->result($result, $i);$i++)
+ $post_ids[] = $cur_post_id;
+
+ // Retrieve the posts (and their respective poster)
+ $result = $db->query('SELECT u.title, u.num_posts, g.g_id, g.g_user_title, p.id, p.poster, p.poster_id, p.message, p.hide_smilies, p.posted, p.edited, p.edited_by FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE p.id IN ('.implode(',', $post_ids).') ORDER BY p.id', true) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+
+ while ($cur_post = $db->fetch_assoc($result))
+ {
+ $post_count++;
+
+ // If the poster is a registered user
+ if ($cur_post['poster_id'] > 1)
+ {
+ if ($pun_user['g_view_users'] == '1')
+ $poster = '<a href="profile.php?id='.$cur_post['poster_id'].'">'.pun_htmlspecialchars($cur_post['poster']).'</a>';
+ else
+ $poster = pun_htmlspecialchars($cur_post['poster']);
+
+ // get_title() requires that an element 'username' be present in the array
+ $cur_post['username'] = $cur_post['poster'];
+ $user_title = get_title($cur_post);
+
+ if ($pun_config['o_censoring'] == '1')
+ $user_title = censor_words($user_title);
+ }
+ // If the poster is a guest (or a user that has been deleted)
+ else
+ {
+ $poster = pun_htmlspecialchars($cur_post['poster']);
+ $user_title = $lang_topic['Guest'];
+ }
+
+ // Perform the main parsing of the message (BBCode, smilies, censor words etc)
+ $cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+?>
+
+<div id="p<?php echo $cur_post['id'] ?>" class="blockpost<?php if($cur_post['id'] == $cur_topic['first_post_id']) echo ' firstpost' ?><?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?><?php if ($post_count == 1) echo ' blockpost1' ?>">
+ <h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?></span> <a href="viewtopic.php?pid=<?php echo $cur_post['id'].'#p'.$cur_post['id'] ?>"><?php echo format_time($cur_post['posted']) ?></a></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postleft">
+ <dl>
+ <dt><strong><?php echo $poster ?></strong></dt>
+ <dd class="usertitle"><strong><?php echo $user_title ?></strong></dd>
+ </dl>
+ </div>
+ <div class="postright">
+ <h3 class="nosize"><?php echo $lang_common['Message'] ?></h3>
+ <div class="postmsg">
+ <?php echo $cur_post['message']."\n" ?>
+<?php if ($cur_post['edited'] != '') echo "\t\t\t\t\t\t".'<p class="postedit"><em>'.$lang_topic['Last edit'].' '.pun_htmlspecialchars($cur_post['edited_by']).' ('.format_time($cur_post['edited']).')</em></p>'."\n"; ?>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="inbox">
+ <div class="postfoot clearb">
+ <div class="postfootright"><?php echo ($cur_post['id'] != $cur_topic['first_post_id']) ? '<p class="multidelete"><label><strong>'.$lang_misc['Select'].'</strong>&#160;<input type="checkbox" name="posts['.$cur_post['id'].']" value="1" /></label></p>' : '<p>'.$lang_misc['Cannot select first'].'</p>' ?></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<?php
+
+ }
+
+?>
+<div class="postlinksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+ <p class="conr modbuttons"><input type="submit" name="split_posts" value="<?php echo $lang_misc['Split'] ?>"<?php echo $button_status ?> /> <input type="submit" name="delete_posts" value="<?php echo $lang_misc['Delete'] ?>"<?php echo $button_status ?> /></p>
+ <div class="clearer"></div>
+ </div>
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $fid ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><a href="viewtopic.php?id=<?php echo $tid ?>"><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_misc['Moderate'] ?></strong></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+</div>
+</form>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+// Move one or more topics
+if (isset($_REQUEST['move_topics']) || isset($_POST['move_topics_to']))
+{
+ if (isset($_POST['move_topics_to']))
+ {
+ confirm_referrer('moderate.php');
+
+ if (@preg_match('%[^0-9,]%', $_POST['topics']))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $topics = explode(',', $_POST['topics']);
+ $move_to_forum = isset($_POST['move_to_forum']) ? intval($_POST['move_to_forum']) : 0;
+ if (empty($topics) || $move_to_forum < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Verify that the topic IDs are valid
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.implode(',',$topics).') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result) != count($topics))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Verify that the move to forum ID is valid
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.group_id='.$pun_user['g_id'].' AND fp.forum_id='.$move_to_forum.') WHERE f.redirect_url IS NULL AND (fp.post_topics IS NULL OR fp.post_topics=1)') or error('Unable to fetch forum permissions', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Delete any redirect topics if there are any (only if we moved/copied the topic back to where it was once moved from)
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE forum_id='.$move_to_forum.' AND moved_to IN('.implode(',',$topics).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+
+ // Move the topic(s)
+ $db->query('UPDATE '.$db->prefix.'topics SET forum_id='.$move_to_forum.' WHERE id IN('.implode(',',$topics).')') or error('Unable to move topics', __FILE__, __LINE__, $db->error());
+
+ // Should we create redirect topics?
+ if (isset($_POST['with_redirect']))
+ {
+ foreach ($topics as $cur_topic)
+ {
+ // Fetch info for the redirect topic
+ $result = $db->query('SELECT poster, subject, posted, last_post FROM '.$db->prefix.'topics WHERE id='.$cur_topic) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+ $moved_to = $db->fetch_assoc($result);
+
+ // Create the redirect topic
+ $db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, last_post, moved_to, forum_id) VALUES(\''.$db->escape($moved_to['poster']).'\', \''.$db->escape($moved_to['subject']).'\', '.$moved_to['posted'].', '.$moved_to['last_post'].', '.$cur_topic.', '.$fid.')') or error('Unable to create redirect topic', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ update_forum($fid); // Update the forum FROM which the topic was moved
+ update_forum($move_to_forum); // Update the forum TO which the topic was moved
+
+ $redirect_msg = (count($topics) > 1) ? $lang_misc['Move topics redirect'] : $lang_misc['Move topic redirect'];
+ redirect('viewforum.php?id='.$move_to_forum, $redirect_msg);
+ }
+
+ if (isset($_POST['move_topics']))
+ {
+ $topics = isset($_POST['topics']) ? $_POST['topics'] : array();
+ if (empty($topics))
+ message($lang_misc['No topics selected']);
+
+ $topics = implode(',', array_map('intval', array_keys($topics)));
+ $action = 'multi';
+ }
+ else
+ {
+ $topics = intval($_GET['move_topics']);
+ if ($topics < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $action = 'single';
+ }
+
+ $result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.post_topics IS NULL OR fp.post_topics=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result) < 2)
+ message($lang_misc['Nowhere to move']);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo ($action == 'single') ? $lang_misc['Move topic'] : $lang_misc['Move topics'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+ <div class="inform">
+ <input type="hidden" name="topics" value="<?php echo $topics ?>" />
+ <fieldset>
+ <legend><?php echo $lang_misc['Move legend'] ?></legend>
+ <div class="infldset">
+ <label><?php echo $lang_misc['Move to'] ?>
+ <br /><select name="move_to_forum">
+<?php
+
+ $cur_category = 0;
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category)
+ echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+ echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+ $cur_category = $cur_forum['cid'];
+ }
+
+ if ($cur_forum['fid'] != $fid)
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
+ }
+
+?>
+ </optgroup>
+ </select>
+ <br /></label>
+ <div class="rbox">
+ <label><input type="checkbox" name="with_redirect" value="1"<?php if ($action == 'single') echo ' checked="checked"' ?> /><?php echo $lang_misc['Leave redirect'] ?><br /></label>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="move_topics_to" value="<?php echo $lang_misc['Move'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+// Merge two or more topics
+else if (isset($_POST['merge_topics']) || isset($_POST['merge_topics_comply']))
+{
+ if (isset($_POST['merge_topics_comply']))
+ {
+ confirm_referrer('moderate.php');
+
+ if (@preg_match('%[^0-9,]%', $_POST['topics']))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $topics = explode(',', $_POST['topics']);
+ if (count($topics) < 2)
+ message($lang_misc['Not enough topics selected']);
+
+ // Verify that the topic IDs are valid (redirect links will point to the merged topic after the merge)
+ $result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND forum_id='.$fid.' ORDER BY id ASC') or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result) != count($topics))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // The topic that we are merging into is the one with the smallest ID
+ $merge_to_tid = $db->result($result);
+
+ // Make any redirect topics point to our new, merged topic
+ $query = 'UPDATE '.$db->prefix.'topics SET moved_to='.$merge_to_tid.' WHERE moved_to IN('.implode(',', $topics).')';
+
+ // Should we create redirect topics?
+ if (isset($_POST['with_redirect']))
+ $query .= ' OR (id IN('.implode(',', $topics).') AND id != '.$merge_to_tid.')';
+
+ $db->query($query) or error('Unable to make redirection topics', __FILE__, __LINE__, $db->error());
+
+ // Merge the posts into the topic
+ $db->query('UPDATE '.$db->prefix.'posts SET topic_id='.$merge_to_tid.' WHERE topic_id IN('.implode(',', $topics).')') or error('Unable to merge the posts into the topic', __FILE__, __LINE__, $db->error());
+
+ // Update any subscriptions
+ $result = $db->query('SELECT DISTINCT user_id FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.implode(',', $topics).')') or error('Unable to fetch subscriptions of merged topics', __FILE__, __LINE__, $db->error());
+
+ $subscribed_users = array();
+ while ($row = $db->fetch_row($result))
+ $subscribed_users[] = $row[0];
+
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.implode(',', $topics).')') or error('Unable to delete subscriptions of merged topics', __FILE__, __LINE__, $db->error());
+
+ foreach ($subscribed_users as $cur_user_id)
+ $db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (topic_id, user_id) VALUES ('.$merge_to_tid.', '.$cur_user_id.')') or error('Unable to re-enter subscriptions for merge topic', __FILE__, __LINE__, $db->error());
+
+ // Without redirection the old topics are removed
+ if (!isset($_POST['with_redirect']))
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND id != '.$merge_to_tid) or error('Unable to delete old topics', __FILE__, __LINE__, $db->error());
+
+ // Count number of replies in the topic
+ $result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$merge_to_tid) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
+ $num_replies = $db->result($result, 0) - 1;
+
+ // Get last_post, last_post_id and last_poster
+ $result = $db->query('SELECT posted, id, poster FROM '.$db->prefix.'posts WHERE topic_id='.$merge_to_tid.' ORDER BY id DESC LIMIT 1') or error('Unable to get last post info', __FILE__, __LINE__, $db->error());
+ list($last_post, $last_post_id, $last_poster) = $db->fetch_row($result);
+
+ // Update topic
+ $db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster=\''.$db->escape($last_poster).'\' WHERE id='.$merge_to_tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ // Update the forum FROM which the topic was moved and redirect
+ update_forum($fid);
+ redirect('viewforum.php?id='.$fid, $lang_misc['Merge topics redirect']);
+ }
+
+ $topics = isset($_POST['topics']) ? $_POST['topics'] : array();
+ if (count($topics) < 2)
+ message($lang_misc['Not enough topics selected']);
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_misc['Merge topics'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+ <input type="hidden" name="topics" value="<?php echo implode(',', array_map('intval', array_keys($topics))) ?>" />
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_misc['Confirm merge legend'] ?></legend>
+ <div class="infldset">
+ <div class="rbox">
+ <label><input type="checkbox" name="with_redirect" value="1" /><?php echo $lang_misc['Leave redirect'] ?><br /></label>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="merge_topics_comply" value="<?php echo $lang_misc['Merge'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+// Delete one or more topics
+else if (isset($_POST['delete_topics']) || isset($_POST['delete_topics_comply']))
+{
+ $topics = isset($_POST['topics']) ? $_POST['topics'] : array();
+ if (empty($topics))
+ message($lang_misc['No topics selected']);
+
+ if (isset($_POST['delete_topics_comply']))
+ {
+ confirm_referrer('moderate.php');
+
+ if (@preg_match('%[^0-9,]%', $topics))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ require PUN_ROOT.'include/search_idx.php';
+
+ // Verify that the topic IDs are valid
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.$topics.') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result) != substr_count($topics, ',') + 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Verify that the posts are not by admins
+ if ($pun_user['g_id'] != PUN_ADMIN)
+ {
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE topic_id IN('.$topics.') AND poster_id IN('.implode(',', get_admin_ids()).')') or error('Unable to check posts', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ message($lang_common['No permission'], false, '403 Forbidden');
+ }
+
+ // Delete the topics and any redirect topics
+ $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topics.') OR moved_to IN('.$topics.')') or error('Unable to delete topic', __FILE__, __LINE__, $db->error());
+
+ // Delete any subscriptions
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.$topics.')') or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+
+ // Create a list of the post IDs in this topic and then strip the search index
+ $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topics.')') or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+ $post_ids = '';
+ while ($row = $db->fetch_row($result))
+ $post_ids .= ($post_ids != '') ? ','.$row[0] : $row[0];
+
+ // We have to check that we actually have a list of post IDs since we could be deleting just a redirect topic
+ if ($post_ids != '')
+ strip_search_index($post_ids);
+
+ // Delete posts
+ $db->query('DELETE FROM '.$db->prefix.'posts WHERE topic_id IN('.$topics.')') or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
+
+ update_forum($fid);
+
+ redirect('viewforum.php?id='.$fid, $lang_misc['Delete topics redirect']);
+ }
+
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_misc['Delete topics'] ?></span></h2>
+ <div class="box">
+ <form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+ <input type="hidden" name="topics" value="<?php echo implode(',', array_map('intval', array_keys($topics))) ?>" />
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_misc['Confirm delete legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_misc['Delete topics comply'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="delete_topics_comply" value="<?php echo $lang_misc['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+// Open or close one or more topics
+else if (isset($_REQUEST['open']) || isset($_REQUEST['close']))
+{
+ $action = (isset($_REQUEST['open'])) ? 0 : 1;
+
+ // There could be an array of topic IDs in $_POST
+ if (isset($_POST['open']) || isset($_POST['close']))
+ {
+ confirm_referrer('moderate.php');
+
+ $topics = isset($_POST['topics']) ? @array_map('intval', @array_keys($_POST['topics'])) : array();
+ if (empty($topics))
+ message($lang_misc['No topics selected']);
+
+ $db->query('UPDATE '.$db->prefix.'topics SET closed='.$action.' WHERE id IN('.implode(',', $topics).') AND forum_id='.$fid) or error('Unable to close topics', __FILE__, __LINE__, $db->error());
+
+ $redirect_msg = ($action) ? $lang_misc['Close topics redirect'] : $lang_misc['Open topics redirect'];
+ redirect('moderate.php?fid='.$fid, $redirect_msg);
+ }
+ // Or just one in $_GET
+ else
+ {
+ confirm_referrer('viewtopic.php');
+
+ check_csrf($_GET['csrf_token']);
+
+ $topic_id = ($action) ? intval($_GET['close']) : intval($_GET['open']);
+ if ($topic_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $db->query('UPDATE '.$db->prefix.'topics SET closed='.$action.' WHERE id='.$topic_id.' AND forum_id='.$fid) or error('Unable to close topic', __FILE__, __LINE__, $db->error());
+
+ $redirect_msg = ($action) ? $lang_misc['Close topic redirect'] : $lang_misc['Open topic redirect'];
+ redirect('viewtopic.php?id='.$topic_id, $redirect_msg);
+ }
+}
+
+
+// Stick a topic
+else if (isset($_GET['stick']))
+{
+ confirm_referrer('viewtopic.php');
+
+ check_csrf($_GET['csrf_token']);
+
+ $stick = intval($_GET['stick']);
+ if ($stick < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $db->query('UPDATE '.$db->prefix.'topics SET sticky=\'1\' WHERE id='.$stick.' AND forum_id='.$fid) or error('Unable to stick topic', __FILE__, __LINE__, $db->error());
+
+ redirect('viewtopic.php?id='.$stick, $lang_misc['Stick topic redirect']);
+}
+
+
+// Unstick a topic
+else if (isset($_GET['unstick']))
+{
+ confirm_referrer('viewtopic.php');
+
+ check_csrf($_GET['csrf_token']);
+
+ $unstick = intval($_GET['unstick']);
+ if ($unstick < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $db->query('UPDATE '.$db->prefix.'topics SET sticky=\'0\' WHERE id='.$unstick.' AND forum_id='.$fid) or error('Unable to unstick topic', __FILE__, __LINE__, $db->error());
+
+ redirect('viewtopic.php?id='.$unstick, $lang_misc['Unstick topic redirect']);
+}
+
+
+// No specific forum moderation action was specified in the query string, so we'll display the moderator forum
+
+// Load the viewforum.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
+
+// Fetch some info about the forum
+$result = $db->query('SELECT f.forum_name, f.redirect_url, f.num_topics, f.sort_by FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$cur_forum = $db->fetch_assoc($result);
+
+// Is this a redirect forum? In that case, abort!
+if ($cur_forum['redirect_url'] != '')
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+switch ($cur_forum['sort_by'])
+{
+ case 0:
+ $sort_by = 'last_post DESC';
+ break;
+ case 1:
+ $sort_by = 'posted DESC';
+ break;
+ case 2:
+ $sort_by = 'subject ASC';
+ break;
+ default:
+ $sort_by = 'last_post DESC';
+ break;
+}
+
+// Determine the topic offset (based on $_GET['p'])
+$num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = $pun_user['disp_topics'] * ($p - 1);
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'moderate.php?fid='.$fid);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_forum['forum_name']));
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $fid ?>"><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_misc['Moderate'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+<div id="vf" class="blocktable">
+ <h2><span><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_common['Replies'] ?></th>
+<?php if ($pun_config['o_topic_views'] == '1'): ?> <th class="tc3" scope="col"><?php echo $lang_forum['Views'] ?></th>
+<?php endif; ?> <th class="tcr"><?php echo $lang_common['Last post'] ?></th>
+ <th class="tcmod" scope="col"><?php echo $lang_misc['Select'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+
+// Retrieve a list of topic IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$fid.' ORDER BY sticky DESC, '.$sort_by.', id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
+
+// If there are topics in this forum
+if ($db->num_rows($result))
+{
+ $topic_ids = array();
+ for ($i = 0;$cur_topic_id = $db->result($result, $i);$i++)
+ $topic_ids[] = $cur_topic_id;
+
+ // Select topics
+ $result = $db->query('SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.$sort_by.', id DESC') or error('Unable to fetch topic list for forum', __FILE__, __LINE__, $db->error());
+
+ $button_status = '';
+ $topic_count = 0;
+ while ($cur_topic = $db->fetch_assoc($result))
+ {
+
+ ++$topic_count;
+ $status_text = array();
+ $item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
+ $icon_type = 'icon';
+
+ if (is_null($cur_topic['moved_to']))
+ {
+ $last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
+ $ghost_topic = false;
+ }
+ else
+ {
+ $last_post = '- - -';
+ $ghost_topic = true;
+ }
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+ if ($cur_topic['sticky'] == '1')
+ {
+ $item_status .= ' isticky';
+ $status_text[] = '<span class="stickytext">'.$lang_forum['Sticky'].'</span>';
+ }
+
+ if ($cur_topic['moved_to'] != 0)
+ {
+ $subject = '<a href="viewtopic.php?id='.$cur_topic['moved_to'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+ $status_text[] = '<span class="movedtext">'.$lang_forum['Moved'].'</span>';
+ $item_status .= ' imoved';
+ }
+ else if ($cur_topic['closed'] == '0')
+ $subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+ else
+ {
+ $subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+ $status_text[] = '<span class="closedtext">'.$lang_forum['Closed'].'</span>';
+ $item_status .= ' iclosed';
+ }
+
+ if (!$ghost_topic && $cur_topic['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']) && (!isset($tracked_topics['forums'][$fid]) || $tracked_topics['forums'][$fid] < $cur_topic['last_post']))
+ {
+ $item_status .= ' inew';
+ $icon_type = 'icon icon-new';
+ $subject = '<strong>'.$subject.'</strong>';
+ $subject_new_posts = '<span class="newtext">[ <a href="viewtopic.php?id='.$cur_topic['id'].'&amp;action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a> ]</span>';
+ }
+ else
+ $subject_new_posts = null;
+
+ // Insert the status text before the subject
+ $subject = implode(' ', $status_text).' '.$subject;
+
+ $num_pages_topic = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+ if ($num_pages_topic > 1)
+ $subject_multipage = '<span class="pagestext">[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_topic['id']).' ]</span>';
+ else
+ $subject_multipage = null;
+
+ // Should we show the "New posts" and/or the multipage links?
+ if (!empty($subject_new_posts) || !empty($subject_multipage))
+ {
+ $subject .= !empty($subject_new_posts) ? ' '.$subject_new_posts : '';
+ $subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
+ }
+
+?>
+ <tr class="<?php echo $item_status ?>">
+ <td class="tcl">
+ <div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($topic_count + $start_from) ?></div></div>
+ <div class="tclcon">
+ <div>
+ <?php echo $subject."\n" ?>
+ </div>
+ </div>
+ </td>
+ <td class="tc2"><?php echo (!$ghost_topic) ? forum_number_format($cur_topic['num_replies']) : '-' ?></td>
+<?php if ($pun_config['o_topic_views'] == '1'): ?> <td class="tc3"><?php echo (!$ghost_topic) ? forum_number_format($cur_topic['num_views']) : '-' ?></td>
+<?php endif; ?> <td class="tcr"><?php echo $last_post ?></td>
+ <td class="tcmod"><input type="checkbox" name="topics[<?php echo $cur_topic['id'] ?>]" value="1" /></td>
+ </tr>
+<?php
+
+ }
+}
+else
+{
+ $colspan = ($pun_config['o_topic_views'] == '1') ? 5 : 4;
+ $button_status = ' disabled="disabled"';
+ echo "\t\t\t\t\t".'<tr><td class="tcl" colspan="'.$colspan.'">'.$lang_forum['Empty forum'].'</td></tr>'."\n";
+}
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+ <p class="conr modbuttons"><input type="submit" name="move_topics" value="<?php echo $lang_misc['Move'] ?>"<?php echo $button_status ?> /> <input type="submit" name="delete_topics" value="<?php echo $lang_misc['Delete'] ?>"<?php echo $button_status ?> /> <input type="submit" name="merge_topics" value="<?php echo $lang_misc['Merge'] ?>"<?php echo $button_status ?> /> <input type="submit" name="open" value="<?php echo $lang_misc['Open'] ?>"<?php echo $button_status ?> /> <input type="submit" name="close" value="<?php echo $lang_misc['Close'] ?>"<?php echo $button_status ?> /></p>
+ <div class="clearer"></div>
+ </div>
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $fid ?>"><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $lang_misc['Moderate'] ?></strong></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+</div>
+</form>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/plugins/AP_reCAPTCHA.php b/plugins/AP_reCAPTCHA.php
new file mode 100644
index 0000000..2a56e04
--- /dev/null
+++ b/plugins/AP_reCAPTCHA.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * New reCAPTCHA plugin for FluxBB
+ *
+ * Created by Franz Liedke
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+ exit;
+
+// Tell admin_loader.php that this is indeed a plugin and that it is loaded
+define('PUN_PLUGIN_LOADED', 1);
+
+// Load language file
+if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/recaptcha_addon.php'))
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/recaptcha_addon.php';
+else
+ require PUN_ROOT.'lang/English/recaptcha_addon.php';
+
+// Store the config
+if (isset($_POST['process_form']))
+{
+ $enabled = isset($_POST['recaptcha_enabled']) ? 1 : 0;
+ $site_key = isset($_POST['recaptcha_site_key']) ? pun_trim($_POST['recaptcha_site_key']) : '';
+ $secret_key = isset($_POST['recaptcha_secret_key']) ? pun_trim($_POST['recaptcha_secret_key']) : '';
+ $location_register = isset($_POST['recaptcha_location_register']) ? 1 : 0;
+ $location_login = isset($_POST['recaptcha_location_login']) ? 1 : 0;
+ $location_guestpost = isset($_POST['recaptcha_location_guestpost']) ? 1 : 0;
+
+ foreach (compact('enabled', 'site_key', 'secret_key', 'location_register', 'location_login', 'location_guestpost') as $key => $value)
+ {
+ $key = 'recaptcha_'.$key;
+
+ if (isset($pun_config[$key]))
+ $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($value).'\' WHERE conf_name = \''.$db->escape($key).'\'') or error('Unable to update config value for '.$key, __FILE__, __LINE__, $db->error());
+ else
+ $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\''.$db->escape($key).'\', \''.$db->escape($value).'\')') or error('Unable to store config value for '.$key, __FILE__, __LINE__, $db->error());
+ }
+
+ // Regenerate the config cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_config_cache();
+
+ redirect('admin_loader.php?plugin=AP_reCAPTCHA.php', $lang_recaptcha['Settings saved']);
+}
+
+
+// Display the admin navigation menu
+generate_admin_menu($plugin);
+
+?>
+
+<div class="blockform">
+ <h2><span><?= $lang_recaptcha['reCAPTCHA'] ?></span></h2>
+ <div class="box">
+ <form id="recaptcha" method="post" action="<?php echo $_SERVER['REQUEST_URI'] ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?= $lang_recaptcha['General']; ?></legend>
+ <div class="infldset">
+ <p>
+ <?= $lang_recaptcha['General description']; ?>
+ </p>
+ <table class="aligntop" cellspacing="0">
+ <tr>
+ <th scope="row"><?= $lang_recaptcha['Enable']; ?></th>
+ <td>
+ <input type="checkbox" name="recaptcha_enabled" <?= empty($pun_config['recaptcha_enabled']) ? '' : 'checked' ?> />
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?= $lang_recaptcha['Site key']; ?></th>
+ <td>
+ <input type="text" name="recaptcha_site_key" size="40" value="<?php if (!empty($pun_config['recaptcha_site_key'])) echo pun_htmlspecialchars($pun_config['recaptcha_site_key']); ?>" />
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?= $lang_recaptcha['Secret key']; ?></th>
+ <td>
+ <input type="text" name="recaptcha_secret_key" size="40" value="<?php if (!empty($pun_config['recaptcha_secret_key'])) echo pun_htmlspecialchars($pun_config['recaptcha_secret_key']); ?>" />
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= $lang_recaptcha['Locations']; ?></legend>
+ <div class="infldset">
+ <p>
+ <?= $lang_recaptcha['Locations description']; ?>
+ </p>
+ <table class="aligntop" cellspacing="0">
+ <tr>
+ <th scope="row"><?= $lang_recaptcha['Register']; ?></th>
+ <td>
+ <input type="checkbox" name="recaptcha_location_register" <?= empty($pun_config['recaptcha_location_register']) ? '' : 'checked' ?> />
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?= $lang_recaptcha['Login']; ?></th>
+ <td>
+ <input type="checkbox" name="recaptcha_location_login" <?= empty($pun_config['recaptcha_location_login']) ? '' : 'checked' ?> />
+ </td>
+ </tr>
+ <tr>
+ <th scope="row"><?= $lang_recaptcha['Guest post']; ?></th>
+ <td>
+ <input type="checkbox" name="recaptcha_location_guestpost" <?= empty($pun_config['recaptcha_location_guestpost']) ? '' : 'checked' ?> />
+ </td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ <p class="submitend"><input type="submit" name="process_form" value="<?= $lang_recaptcha['Save'] ?>" /></p>
+ </form>
+ </div>
+</div>
diff --git a/plugins/index.html b/plugins/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/plugins/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/post.php b/post.php
new file mode 100644
index 0000000..067d87b
--- /dev/null
+++ b/post.php
@@ -0,0 +1,781 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+$tid = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
+$fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+if ($tid < 1 && $fid < 1 || $tid > 0 && $fid > 0)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// Fetch some info about the topic and/or the forum
+if ($tid)
+ $result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.subject, t.closed, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') LEFT JOIN '.$db->prefix.'topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$tid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+else
+ $result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$cur_posting = $db->fetch_assoc($result);
+$is_subscribed = $tid && $cur_posting['is_subscribed'];
+
+// Is someone trying to post into a redirect forum?
+if ($cur_posting['redirect_url'] != '')
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_posting['moderators'] != '') ? unserialize($cur_posting['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+if ($tid && $pun_config['o_censoring'] == '1')
+ $cur_posting['subject'] = censor_words($cur_posting['subject']);
+
+// Do we have permission to post?
+if ((($tid && (($cur_posting['post_replies'] == '' && $pun_user['g_post_replies'] == '0') || $cur_posting['post_replies'] == '0')) ||
+ ($fid && (($cur_posting['post_topics'] == '' && $pun_user['g_post_topics'] == '0') || $cur_posting['post_topics'] == '0')) ||
+ (isset($cur_posting['closed']) && $cur_posting['closed'] == '1')) &&
+ !$is_admmod)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the post.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+// Start with a clean slate
+$errors = array();
+
+
+// Did someone just hit "Submit" or "Preview"?
+if (isset($_POST['form_sent']))
+{
+ flux_hook('post_before_validation');
+
+ // Flood protection
+ if (!isset($_POST['preview']) && $pun_user['last_post'] != '' && (time() - $pun_user['last_post']) < $pun_user['g_post_flood'])
+ $errors[] = sprintf($lang_post['Flood start'], $pun_user['g_post_flood'], $pun_user['g_post_flood'] - (time() - $pun_user['last_post']));
+
+ // Make sure they got here from the site
+ confirm_referrer(array('post.php', 'viewtopic.php'));
+
+ // If it's a new topic
+ if ($fid)
+ {
+ $subject = pun_trim($_POST['req_subject']);
+
+ if ($subject == '')
+ $errors[] = $lang_post['No subject'];
+ else if ($pun_config['o_censoring'] == '1')
+ {
+ // Censor subject to see if that causes problems
+ $subject = pun_trim(censor_words($subject));
+
+ if ($subject == '')
+ $errors[] = $lang_post['No subject after censoring'];
+ }
+
+ if (empty($errors))
+ {
+ if (pun_strlen($subject) > 70)
+ $errors[] = $lang_post['Too long subject'];
+ else if ($pun_config['p_subject_all_caps'] == '0' && is_all_uppercase($subject) && !$pun_user['is_admmod'])
+ $errors[] = $lang_post['All caps subject'];
+ else if ($pun_user['g_post_links'] != '1' && preg_match('%(?:h\s*t|f)\s*t\s*p\s*(?:s\s*)?:\s*/\s*/%', $subject))
+ $errors[] = $lang_common['BBCode error tag url not allowed'];
+ }
+ }
+
+ // If the user is logged in we get the username and email from $pun_user
+ if (!$pun_user['is_guest'])
+ {
+ $username = $pun_user['username'];
+ $email = $pun_user['email'];
+ }
+ // Otherwise it should be in $_POST
+ else
+ {
+ $username = pun_trim($_POST['req_username']);
+ $email = strtolower(pun_trim(($pun_config['p_force_guest_email'] == '1') ? $_POST['req_email'] : $_POST['email']));
+ $banned_email = false;
+
+ // Load the register.php/prof_reg.php language files
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+ // It's a guest, so we have to validate the username
+ check_username($username);
+
+ if ($pun_config['p_force_guest_email'] == '1' || $email != '')
+ {
+ require PUN_ROOT.'include/email.php';
+ if (!is_valid_email($email))
+ $errors[] = $lang_common['Invalid email'];
+
+ // Check if it's a banned email address
+ // we should only check guests because members' addresses are already verified
+ if ($pun_user['is_guest'] && is_banned_email($email))
+ {
+ if ($pun_config['p_allow_banned_email'] == '0')
+ $errors[] = $lang_prof_reg['Banned email'];
+
+ $banned_email = true; // Used later when we send an alert email
+ }
+ }
+ }
+
+ // Clean up message from POST
+ $orig_message = $message = pun_linebreaks(pun_trim($_POST['req_message']));
+
+ // Here we use strlen() not pun_strlen() as we want to limit the post to PUN_MAX_POSTSIZE bytes, not characters
+ if (strlen($message) > PUN_MAX_POSTSIZE)
+ $errors[] = sprintf($lang_post['Too long message'], forum_number_format(PUN_MAX_POSTSIZE));
+ else if ($pun_config['p_message_all_caps'] == '0' && is_all_uppercase($message) && !$pun_user['is_admmod'])
+ $errors[] = $lang_post['All caps message'];
+
+ // Validate BBCode syntax
+ if ($pun_config['p_message_bbcode'] == '1')
+ {
+ require PUN_ROOT.'include/parser.php';
+ $message = preparse_bbcode($message, $errors);
+ }
+
+ if (empty($errors))
+ {
+ if ($message == '')
+ $errors[] = $lang_post['No message'];
+ else if ($pun_config['o_censoring'] == '1')
+ {
+ // Censor message to see if that causes problems
+ $message = pun_trim(censor_words($message));
+
+ if ($message == '')
+ $errors[] = $lang_post['No message after censoring'];
+ }
+ }
+
+ $hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
+ $subscribe = isset($_POST['subscribe']) ? '1' : '0';
+ $stick_topic = isset($_POST['stick_topic']) && $is_admmod ? '1' : '0';
+
+ // Replace four-byte characters (MySQL cannot handle them)
+ $message = strip_bad_multibyte_chars($message);
+
+ $now = time();
+
+ flux_hook('post_after_validation');
+
+ // Did everything go according to plan?
+ if (empty($errors) && !isset($_POST['preview']))
+ {
+ require PUN_ROOT.'include/search_idx.php';
+
+ // If it's a reply
+ if ($tid)
+ {
+ if (!$pun_user['is_guest'])
+ {
+ $new_tid = $tid;
+
+ // Insert the new post
+ $db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', '.$pun_user['id'].', \''.$db->escape(get_remote_address()).'\', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+ $new_pid = $db->insert_id();
+
+ // To subscribe or not to subscribe, that ...
+ if ($pun_config['o_topic_subscriptions'] == '1')
+ {
+ if ($subscribe && !$is_subscribed)
+ $db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+ else if (!$subscribe && $is_subscribed)
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$tid) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+ }
+ }
+ else
+ {
+ // It's a guest. Insert the new post
+ $email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$db->escape($email).'\'' : 'NULL';
+ $db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_ip, poster_email, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape(get_remote_address()).'\', '.$email_sql.', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+ $new_pid = $db->insert_id();
+ }
+
+ // Update topic
+ $db->query('UPDATE '.$db->prefix.'topics SET num_replies=num_replies+1, last_post='.$now.', last_post_id='.$new_pid.', last_poster=\''.$db->escape($username).'\' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ update_search_index('post', $new_pid, $message);
+
+ update_forum($cur_posting['id']);
+
+ // Should we send out notifications?
+ if ($pun_config['o_topic_subscriptions'] == '1')
+ {
+ // Get the post time for the previous post in this topic
+ $result = $db->query('SELECT posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1, 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+ $previous_post_time = $db->result($result);
+
+ // Get any subscribed users that should be notified (banned users are excluded)
+ $result = $db->query('SELECT u.id, u.email, u.notify_with_post, u.language FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'topic_subscriptions AS s ON u.id=s.user_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id='.$cur_posting['id'].' AND fp.group_id=u.group_id) LEFT JOIN '.$db->prefix.'online AS o ON u.id=o.user_id LEFT JOIN '.$db->prefix.'bans AS b ON u.username=b.username WHERE b.username IS NULL AND COALESCE(o.logged, u.last_visit)>'.$previous_post_time.' AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.topic_id='.$tid.' AND u.id!='.$pun_user['id']) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ require_once PUN_ROOT.'include/email.php';
+
+ $notification_emails = array();
+ $languages = forum_list_langs();
+
+ $cleaned_message = bbcode2email($message, -1);
+
+ // Loop through subscribed users and send emails
+ while ($cur_subscriber = $db->fetch_assoc($result))
+ {
+ if (!in_array($cur_subscriber['language'], $languages))
+ $cur_subscriber['language'] = $pun_config['o_default_lang'];
+
+ // Is the subscription email for $cur_subscriber['language'] cached or not?
+ if (!isset($notification_emails[$cur_subscriber['language']]))
+ {
+ if (file_exists(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl'))
+ {
+ // Load the "new reply" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl'));
+
+ // Load the "new reply full" template (with post included)
+ $mail_tpl_full = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply_full.tpl'));
+
+ // The first row contains the subject (it also starts with "Subject:")
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $first_crlf = strpos($mail_tpl_full, "\n");
+ $mail_subject_full = trim(substr($mail_tpl_full, 8, $first_crlf-8));
+ $mail_message_full = trim(substr($mail_tpl_full, $first_crlf));
+
+ $mail_subject = str_replace('<topic_subject>', $cur_posting['subject'], $mail_subject);
+ $mail_message = str_replace('<topic_subject>', $cur_posting['subject'], $mail_message);
+ $mail_message = str_replace('<replier>', $username, $mail_message);
+ $mail_message = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message);
+ $mail_message = str_replace('<unsubscribe_url>', get_base_url().'/viewtopic.php?id='.$tid.'#unsubscribe', $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ $mail_subject_full = str_replace('<topic_subject>', $cur_posting['subject'], $mail_subject_full);
+ $mail_message_full = str_replace('<topic_subject>', $cur_posting['subject'], $mail_message_full);
+ $mail_message_full = str_replace('<replier>', $username, $mail_message_full);
+ $mail_message_full = str_replace('<message>', $cleaned_message, $mail_message_full);
+ $mail_message_full = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message_full);
+ $mail_message_full = str_replace('<unsubscribe_url>', get_base_url().'/viewtopic.php?id='.$tid.'#unsubscribe', $mail_message_full);
+ $mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message_full);
+
+ $notification_emails[$cur_subscriber['language']][0] = $mail_subject;
+ $notification_emails[$cur_subscriber['language']][1] = $mail_message;
+ $notification_emails[$cur_subscriber['language']][2] = $mail_subject_full;
+ $notification_emails[$cur_subscriber['language']][3] = $mail_message_full;
+
+ $mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null;
+ }
+ }
+
+ // We have to double check here because the templates could be missing
+ if (isset($notification_emails[$cur_subscriber['language']]))
+ {
+ if ($cur_subscriber['notify_with_post'] == '0')
+ pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]);
+ else
+ pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
+ }
+ }
+
+ unset($cleaned_message);
+ }
+ }
+ }
+ // If it's a new topic
+ else if ($fid)
+ {
+ // Create the topic
+ $db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, last_post, last_poster, sticky, forum_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape($subject).'\', '.$now.', '.$now.', \''.$db->escape($username).'\', '.$stick_topic.', '.$fid.')') or error('Unable to create topic', __FILE__, __LINE__, $db->error());
+ $new_tid = $db->insert_id();
+
+ if (!$pun_user['is_guest'])
+ {
+ // To subscribe or not to subscribe, that ...
+ if ($pun_config['o_topic_subscriptions'] == '1' && $subscribe)
+ $db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$new_tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+ // Create the post ("topic post")
+ $db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', '.$pun_user['id'].', \''.$db->escape(get_remote_address()).'\', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$new_tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+ }
+ else
+ {
+ // Create the post ("topic post")
+ $email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$db->escape($email).'\'' : 'NULL';
+ $db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_ip, poster_email, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape(get_remote_address()).'\', '.$email_sql.', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$new_tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+ }
+ $new_pid = $db->insert_id();
+
+ // Update the topic with last_post_id
+ $db->query('UPDATE '.$db->prefix.'topics SET last_post_id='.$new_pid.', first_post_id='.$new_pid.' WHERE id='.$new_tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+ update_search_index('post', $new_pid, $message, $subject);
+
+ update_forum($fid);
+
+ // Should we send out notifications?
+ if ($pun_config['o_forum_subscriptions'] == '1')
+ {
+ // Get any subscribed users that should be notified (banned users are excluded)
+ $result = $db->query('SELECT u.id, u.email, u.notify_with_post, u.language FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'forum_subscriptions AS s ON u.id=s.user_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id='.$cur_posting['id'].' AND fp.group_id=u.group_id) LEFT JOIN '.$db->prefix.'bans AS b ON u.username=b.username WHERE b.username IS NULL AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.forum_id='.$cur_posting['id'].' AND u.id!='.$pun_user['id']) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ require_once PUN_ROOT.'include/email.php';
+
+ $notification_emails = array();
+ $languages = forum_list_langs();
+
+ $cleaned_message = bbcode2email($message, -1);
+
+ // Loop through subscribed users and send emails
+ while ($cur_subscriber = $db->fetch_assoc($result))
+ {
+ if (!in_array($cur_subscriber['language'], $languages))
+ $cur_subscriber['language'] = $pun_config['o_default_lang'];
+
+ // Is the subscription email for $cur_subscriber['language'] cached or not?
+ if (!isset($notification_emails[$cur_subscriber['language']]))
+ {
+ if (file_exists(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl'))
+ {
+ // Load the "new topic" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl'));
+
+ // Load the "new topic full" template (with post included)
+ $mail_tpl_full = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic_full.tpl'));
+
+ // The first row contains the subject (it also starts with "Subject:")
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $first_crlf = strpos($mail_tpl_full, "\n");
+ $mail_subject_full = trim(substr($mail_tpl_full, 8, $first_crlf-8));
+ $mail_message_full = trim(substr($mail_tpl_full, $first_crlf));
+
+ $mail_subject = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_subject);
+ $mail_message = str_replace('<topic_subject>', $subject, $mail_message);
+ $mail_message = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_message);
+ $mail_message = str_replace('<poster>', $username, $mail_message);
+ $mail_message = str_replace('<topic_url>', get_base_url().'/viewtopic.php?id='.$new_tid, $mail_message);
+ $mail_message = str_replace('<unsubscribe_url>', get_base_url().'/viewforum.php?id='.$cur_posting['id'].'#unsubscribe', $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ $mail_subject_full = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_subject_full);
+ $mail_message_full = str_replace('<topic_subject>', $subject, $mail_message_full);
+ $mail_message_full = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_message_full);
+ $mail_message_full = str_replace('<poster>', $username, $mail_message_full);
+ $mail_message_full = str_replace('<message>', $cleaned_message, $mail_message_full);
+ $mail_message_full = str_replace('<topic_url>', get_base_url().'/viewtopic.php?id='.$new_tid, $mail_message_full);
+ $mail_message_full = str_replace('<unsubscribe_url>', get_base_url().'/viewforum.php?id='.$cur_posting['id'].'#unsubscribe', $mail_message_full);
+ $mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message_full);
+
+ $notification_emails[$cur_subscriber['language']][0] = $mail_subject;
+ $notification_emails[$cur_subscriber['language']][1] = $mail_message;
+ $notification_emails[$cur_subscriber['language']][2] = $mail_subject_full;
+ $notification_emails[$cur_subscriber['language']][3] = $mail_message_full;
+
+ $mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null;
+ }
+ }
+
+ // We have to double check here because the templates could be missing
+ if (isset($notification_emails[$cur_subscriber['language']]))
+ {
+ if ($cur_subscriber['notify_with_post'] == '0')
+ pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]);
+ else
+ pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
+ }
+ }
+
+ unset($cleaned_message);
+ }
+ }
+ }
+
+ // If we previously found out that the email was banned
+ if ($pun_user['is_guest'] && $banned_email && $pun_config['o_mailing_list'] != '')
+ {
+ // Load the "banned email post" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/banned_email_post.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $username, $mail_message);
+ $mail_message = str_replace('<email>', $email, $mail_message);
+ $mail_message = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+
+ // If the posting user is logged in, increment his/her post count
+ if (!$pun_user['is_guest'])
+ {
+ $db->query('UPDATE '.$db->prefix.'users SET num_posts=num_posts+1, last_post='.$now.' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+ // Promote this user to a new group if enabled
+ if ($pun_user['g_promote_next_group'] != 0 && $pun_user['num_posts'] + 1 >= $pun_user['g_promote_min_posts'])
+ {
+ $new_group_id = $pun_user['g_promote_next_group'];
+ $db->query('UPDATE '.$db->prefix.'users SET group_id='.$new_group_id.' WHERE id='.$pun_user['id']) or error('Unable to promote user to new group', __FILE__, __LINE__, $db->error());
+ }
+
+ // Topic tracking stuff...
+ $tracked_topics = get_tracked_topics();
+ $tracked_topics['topics'][$new_tid] = time();
+ set_tracked_topics($tracked_topics);
+ }
+ else
+ {
+ $db->query('UPDATE '.$db->prefix.'online SET last_post='.$now.' WHERE ident=\''.$db->escape(get_remote_address()).'\'' ) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+ }
+
+ redirect('viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $lang_post['Post redirect']);
+ }
+}
+
+
+// If a topic ID was specified in the url (it's a reply)
+if ($tid)
+{
+ $action = $lang_post['Post a reply'];
+ $form = '<form id="post" method="post" action="post.php?action=post&amp;tid='.$tid.'" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">';
+
+ // If a quote ID was specified in the url
+ if (isset($_GET['qid']))
+ {
+ $qid = intval($_GET['qid']);
+ if ($qid < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT poster, message FROM '.$db->prefix.'posts WHERE id='.$qid.' AND topic_id='.$tid) or error('Unable to fetch quote info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($q_poster, $q_message) = $db->fetch_row($result);
+
+ // If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
+ if (strpos($q_message, '[code]') !== false && strpos($q_message, '[/code]') !== false)
+ {
+ list($inside, $outside) = split_text($q_message, '[code]', '[/code]');
+
+ $q_message = implode("\1", $outside);
+ }
+
+ // Remove [img] tags from quoted message
+ $q_message = preg_replace('%\[img(?:=(?:[^\[]*?))?\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%U', '\1\3', $q_message);
+
+ // If we split up the message before we have to concatenate it together again (code tags)
+ if (isset($inside))
+ {
+ $outside = explode("\1", $q_message);
+ $q_message = '';
+
+ $num_tokens = count($outside);
+ for ($i = 0; $i < $num_tokens; ++$i)
+ {
+ $q_message .= $outside[$i];
+ if (isset($inside[$i]))
+ $q_message .= '[code]'.$inside[$i].'[/code]';
+ }
+
+ unset($inside);
+ }
+
+ if ($pun_config['o_censoring'] == '1')
+ $q_message = censor_words($q_message);
+
+ $q_message = pun_htmlspecialchars($q_message);
+
+ if ($pun_config['p_message_bbcode'] == '1')
+ {
+ // If username contains a square bracket, we add "" or '' around it (so we know when it starts and ends)
+ if (strpos($q_poster, '[') !== false || strpos($q_poster, ']') !== false)
+ {
+ if (strpos($q_poster, '\'') !== false)
+ $q_poster = '"'.$q_poster.'"';
+ else
+ $q_poster = '\''.$q_poster.'\'';
+ }
+ else
+ {
+ // Get the characters at the start and end of $q_poster
+ $ends = substr($q_poster, 0, 1).substr($q_poster, -1, 1);
+
+ // Deal with quoting "Username" or 'Username' (becomes '"Username"' or "'Username'")
+ if ($ends == '\'\'')
+ $q_poster = '"'.$q_poster.'"';
+ else if ($ends == '""')
+ $q_poster = '\''.$q_poster.'\'';
+ }
+
+ $quote = '[quote='.$q_poster.']'.$q_message.'[/quote]'."\n";
+ }
+ else
+ $quote = '> '.$q_poster.' '.$lang_common['wrote']."\n\n".'> '.$q_message."\n";
+ }
+}
+// If a forum ID was specified in the url (new topic)
+else if ($fid)
+{
+ $action = $lang_post['Post new topic'];
+ $form = '<form id="post" method="post" action="post.php?action=post&amp;fid='.$fid.'" onsubmit="return process_form(this)">';
+}
+else
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $action);
+$required_fields = array('req_email' => $lang_common['Email'], 'req_subject' => $lang_common['Subject'], 'req_message' => $lang_common['Message']);
+$focus_element = array('post');
+
+if (!$pun_user['is_guest'])
+ $focus_element[] = ($fid) ? 'req_subject' : 'req_message';
+else
+{
+ $required_fields['req_username'] = $lang_post['Guest name'];
+ $focus_element[] = 'req_username';
+}
+
+flux_hook('post_before_header');
+
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $cur_posting['id'] ?>"><?php echo pun_htmlspecialchars($cur_posting['forum_name']) ?></a></li>
+<?php if (isset($_POST['req_subject'])): ?> <li><span>»&#160;</span><?php echo pun_htmlspecialchars($_POST['req_subject']) ?></li>
+<?php endif; ?>
+<?php if (isset($cur_posting['subject'])): ?> <li><span>»&#160;</span><a href="viewtopic.php?id=<?php echo $tid ?>"><?php echo pun_htmlspecialchars($cur_posting['subject']) ?></a></li>
+<?php endif; ?> <li><span>»&#160;</span><strong><?php echo $action ?></strong></li>
+ </ul>
+ </div>
+</div>
+
+<?php
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+ <h2><span><?php echo $lang_post['Post errors'] ?></span></h2>
+ <div class="box">
+ <div class="inbox error-info">
+ <p><?php echo $lang_post['Post errors info'] ?></p>
+ <ul class="error-list">
+<?php
+
+ foreach ($errors as $cur_error)
+ echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+else if (isset($_POST['preview']))
+{
+ require_once PUN_ROOT.'include/parser.php';
+ $preview_message = parse_message($message, $hide_smilies);
+
+?>
+<div id="postpreview" class="blockpost">
+ <h2><span><?php echo $lang_post['Post preview'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postright">
+ <div class="postmsg">
+ <?php echo $preview_message."\n" ?>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+
+
+$cur_index = 1;
+
+?>
+<div id="postform" class="blockform">
+ <h2><span><?php echo $action ?></span></h2>
+ <div class="box">
+ <?php echo $form."\n" ?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_common['Write message legend'] ?></legend>
+ <div class="infldset txtarea">
+ <input type="hidden" name="form_sent" value="1" />
+<?php
+
+if ($pun_user['is_guest'])
+{
+ $email_label = ($pun_config['p_force_guest_email'] == '1') ? '<strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong>' : $lang_common['Email'];
+ $email_form_name = ($pun_config['p_force_guest_email'] == '1') ? 'req_email' : 'email';
+
+?>
+ <label class="conl required"><strong><?php echo $lang_post['Guest name'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_username" value="<?php if (isset($_POST['req_username'])) echo pun_htmlspecialchars($username); ?>" size="25" maxlength="25" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+ <label class="conl<?php echo ($pun_config['p_force_guest_email'] == '1') ? ' required' : '' ?>"><?php echo $email_label ?><br /><input type="text" name="<?php echo $email_form_name ?>" value="<?php if (isset($_POST[$email_form_name])) echo pun_htmlspecialchars($email); ?>" size="50" maxlength="80" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+ <div class="clearer"></div>
+<?php
+
+}
+
+if ($fid): ?>
+ <label class="required"><strong><?php echo $lang_common['Subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input class="longinput" type="text" name="req_subject" value="<?php if (isset($_POST['req_subject'])) echo pun_htmlspecialchars($_POST['req_subject']); ?>" size="80" maxlength="70" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+<?php endif; ?> <label class="required"><strong><?php echo $lang_common['Message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <textarea name="req_message" rows="20" cols="95" tabindex="<?php echo $cur_index++ ?>"><?php echo isset($_POST['req_message']) ? pun_htmlspecialchars($orig_message) : (isset($quote) ? $quote : ''); ?></textarea><br /></label>
+ <ul class="bblinks">
+ <li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#url" onclick="window.open(this.href); return false;"><?php echo $lang_common['url tag'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1' && $pun_user['g_post_links'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1' && $pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ </ul>
+ </div>
+ </fieldset>
+<?php
+
+$checkboxes = array();
+if ($fid && $is_admmod)
+ $checkboxes[] = '<label><input type="checkbox" name="stick_topic" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['stick_topic']) ? ' checked="checked"' : '').' />'.$lang_common['Stick topic'].'<br /></label>';
+
+if (!$pun_user['is_guest'])
+{
+ if ($pun_config['o_smilies'] == '1')
+ $checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['hide_smilies']) ? ' checked="checked"' : '').' />'.$lang_post['Hide smilies'].'<br /></label>';
+
+ if ($pun_config['o_topic_subscriptions'] == '1')
+ {
+ $subscr_checked = false;
+
+ // If it's a preview
+ if (isset($_POST['preview']))
+ $subscr_checked = isset($_POST['subscribe']) ? true : false;
+ // If auto subscribed
+ else if ($pun_user['auto_notify'])
+ $subscr_checked = true;
+ // If already subscribed to the topic
+ else if ($is_subscribed)
+ $subscr_checked = true;
+
+ $checkboxes[] = '<label><input type="checkbox" name="subscribe" value="1" tabindex="'.($cur_index++).'"'.($subscr_checked ? ' checked="checked"' : '').' />'.($is_subscribed ? $lang_post['Stay subscribed'] : $lang_post['Subscribe']).'<br /></label>';
+ }
+}
+else if ($pun_config['o_smilies'] == '1')
+ $checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['hide_smilies']) ? ' checked="checked"' : '').' />'.$lang_post['Hide smilies'].'<br /></label>';
+
+if (!empty($checkboxes))
+{
+
+?>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_common['Options'] ?></legend>
+ <div class="infldset">
+ <div class="rbox">
+ <?php echo implode("\n\t\t\t\t\t\t\t", $checkboxes)."\n" ?>
+ </div>
+ </div>
+ </fieldset>
+<?php
+
+}
+
+?>
+ </div>
+<?php flux_hook('post_before_submit') ?>
+ <p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="s" /> <input type="submit" name="preview" value="<?php echo $lang_post['Preview'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="p" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+
+<?php
+
+// Check to see if the topic review is to be displayed
+if ($tid && $pun_config['o_topic_review'] != '0')
+{
+ require_once PUN_ROOT.'include/parser.php';
+
+ $result = $db->query('SELECT poster, message, hide_smilies, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT '.$pun_config['o_topic_review']) or error('Unable to fetch topic review', __FILE__, __LINE__, $db->error());
+
+?>
+
+<div id="postreview">
+ <h2><span><?php echo $lang_post['Topic review'] ?></span></h2>
+<?php
+
+ // Set background switching on
+ $post_count = 0;
+
+ while ($cur_post = $db->fetch_assoc($result))
+ {
+ $post_count++;
+
+ $cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+?>
+ <div class="blockpost">
+ <div class="box<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?>">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postleft">
+ <dl>
+ <dt><strong><?php echo pun_htmlspecialchars($cur_post['poster']) ?></strong></dt>
+ <dd><span><?php echo format_time($cur_post['posted']) ?></span></dd>
+ </dl>
+ </div>
+ <div class="postright">
+ <div class="postmsg">
+ <?php echo $cur_post['message']."\n" ?>
+ </div>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ </div>
+ </div>
+<?php
+
+ }
+
+?>
+</div>
+<?php
+
+}
+
+require PUN_ROOT.'footer.php';
diff --git a/profile.php b/profile.php
new file mode 100644
index 0000000..8b0eb86
--- /dev/null
+++ b/profile.php
@@ -0,0 +1,1882 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+// Include UTF-8 function
+require PUN_ROOT.'include/utf8/substr_replace.php';
+require PUN_ROOT.'include/utf8/ucwords.php'; // utf8_ucwords needs utf8_substr_replace
+require PUN_ROOT.'include/utf8/strcasecmp.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$section = isset($_GET['section']) ? $_GET['section'] : null;
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+if ($action != 'change_pass' || !isset($_GET['key']))
+{
+ if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+ else if ($pun_user['g_view_users'] == '0' && ($pun_user['is_guest'] || $pun_user['id'] != $id))
+ message($lang_common['No permission'], false, '403 Forbidden');
+}
+
+// Load the prof_reg.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
+
+// Load the profile.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/profile.php';
+
+
+if ($action == 'change_pass')
+{
+ if (isset($_GET['key']))
+ {
+ // If the user is already logged in we shouldn't be here :)
+ if (!$pun_user['is_guest'])
+ {
+ header('Location: index.php');
+ exit;
+ }
+
+ $key = $_GET['key'];
+
+ $result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch new password', __FILE__, __LINE__, $db->error());
+ $cur_user = $db->fetch_assoc($result);
+
+ if ($key == '' || $key != $cur_user['activate_key'])
+ message($lang_profile['Pass key bad'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.');
+ else
+ {
+ $db->query('UPDATE '.$db->prefix.'users SET password=\''.$db->escape($cur_user['activate_string']).'\', activate_string=NULL, activate_key=NULL'.(!empty($cur_user['salt']) ? ', salt=NULL' : '').' WHERE id='.$id) or error('Unable to update password', __FILE__, __LINE__, $db->error());
+
+ message($lang_profile['Pass updated'], true);
+ }
+ }
+
+ // Make sure we are allowed to change this user's password
+ if ($pun_user['id'] != $id)
+ {
+ if (!$pun_user['is_admmod']) // A regular user trying to change another user's password?
+ message($lang_common['No permission'], false, '403 Forbidden');
+ else if ($pun_user['g_moderator'] == '1') // A moderator trying to change a user's password?
+ {
+ $result = $db->query('SELECT u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($group_id, $is_moderator) = $db->fetch_row($result);
+
+ if ($pun_user['g_mod_edit_users'] == '0' || $pun_user['g_mod_change_passwords'] == '0' || $group_id == PUN_ADMIN || $is_moderator == '1')
+ message($lang_common['No permission'], false, '403 Forbidden');
+ }
+ }
+
+ if (isset($_POST['form_sent']))
+ {
+ // Make sure they got here from the site
+ confirm_referrer('profile.php');
+
+ $old_password = isset($_POST['req_old_password']) ? pun_trim($_POST['req_old_password']) : '';
+ $new_password1 = pun_trim($_POST['req_new_password1']);
+ $new_password2 = pun_trim($_POST['req_new_password2']);
+
+ if ($new_password1 != $new_password2)
+ message($lang_prof_reg['Pass not match']);
+ if (pun_strlen($new_password1) < 9)
+ message($lang_prof_reg['Pass too short']);
+
+ $result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch password', __FILE__, __LINE__, $db->error());
+ $cur_user = $db->fetch_assoc($result);
+
+ $authorized = false;
+
+ if (!empty($cur_user['password']))
+ {
+ $old_password_hash = pun_hash($old_password);
+
+ if ($cur_user['password'] == $old_password_hash || $pun_user['is_admmod'])
+ $authorized = true;
+ }
+
+ if (!$authorized)
+ message($lang_profile['Wrong pass']);
+
+ $new_password_hash = pun_hash($new_password1);
+
+ $db->query('UPDATE '.$db->prefix.'users SET password=\''.$new_password_hash.'\''.(!empty($cur_user['salt']) ? ', salt=NULL' : '').' WHERE id='.$id) or error('Unable to update password', __FILE__, __LINE__, $db->error());
+
+ if ($pun_user['id'] == $id)
+ pun_setcookie($pun_user['id'], $new_password_hash, time() + $pun_config['o_timeout_visit']);
+
+ redirect('profile.php?section=essentials&amp;id='.$id, $lang_profile['Pass updated redirect']);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Change pass']);
+ $required_fields = array('req_old_password' => $lang_profile['Old pass'], 'req_new_password1' => $lang_profile['New pass'], 'req_new_password2' => $lang_profile['Confirm new pass']);
+ $focus_element = array('change_pass', ((!$pun_user['is_admmod']) ? 'req_old_password' : 'req_new_password1'));
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_profile['Change pass'] ?></span></h2>
+ <div class="box">
+ <form id="change_pass" method="post" action="profile.php?action=change_pass&amp;id=<?php echo $id ?>" onsubmit="return process_form(this)">
+ <div class="inform">
+ <input type="hidden" name="form_sent" value="1" />
+ <fieldset>
+ <legend><?php echo $lang_profile['Change pass legend'] ?></legend>
+ <div class="infldset">
+<?php if (!$pun_user['is_admmod']): ?> <label class="required"><strong><?php echo $lang_profile['Old pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input type="password" name="req_old_password" size="16" /><br /></label>
+<?php endif; ?> <label class="conl required"><strong><?php echo $lang_profile['New pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input type="password" name="req_new_password1" size="16" /><br /></label>
+ <label class="conl required"><strong><?php echo $lang_profile['Confirm new pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input type="password" name="req_new_password2" size="16" /><br /></label>
+ <p class="clearb"><?php echo $lang_profile['Pass info'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'change_email')
+{
+ // Make sure we are allowed to change this user's email
+ if ($pun_user['id'] != $id)
+ {
+ if (!$pun_user['is_admmod']) // A regular user trying to change another user's email?
+ message($lang_common['No permission'], false, '403 Forbidden');
+ else if ($pun_user['g_moderator'] == '1') // A moderator trying to change a user's email?
+ {
+ $result = $db->query('SELECT u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($group_id, $is_moderator) = $db->fetch_row($result);
+
+ if ($pun_user['g_mod_edit_users'] == '0' || $group_id == PUN_ADMIN || $is_moderator == '1')
+ message($lang_common['No permission'], false, '403 Forbidden');
+ }
+ }
+
+ if (isset($_GET['key']))
+ {
+ $key = $_GET['key'];
+
+ $result = $db->query('SELECT activate_string, activate_key FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch activation data', __FILE__, __LINE__, $db->error());
+ list($new_email, $new_email_key) = $db->fetch_row($result);
+
+ if ($key == '' || $key != $new_email_key)
+ message($lang_profile['Email key bad'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.');
+ else
+ {
+ $db->query('UPDATE '.$db->prefix.'users SET email=activate_string, activate_string=NULL, activate_key=NULL WHERE id='.$id) or error('Unable to update email address', __FILE__, __LINE__, $db->error());
+
+ message($lang_profile['Email updated'], true);
+ }
+ }
+ else if (isset($_POST['form_sent']))
+ {
+ if (pun_hash($_POST['req_password']) !== $pun_user['password'])
+ message($lang_profile['Wrong pass']);
+
+ // Make sure they got here from the site
+ confirm_referrer('profile.php');
+
+ require PUN_ROOT.'include/email.php';
+
+ // Validate the email address
+ $new_email = strtolower(pun_trim($_POST['req_new_email']));
+ if (!is_valid_email($new_email))
+ message($lang_common['Invalid email']);
+
+ // Check if it's a banned email address
+ if (is_banned_email($new_email))
+ {
+ if ($pun_config['p_allow_banned_email'] == '0')
+ message($lang_prof_reg['Banned email']);
+ else if ($pun_config['o_mailing_list'] != '')
+ {
+ // Load the "banned email change" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/banned_email_change.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+ $mail_message = str_replace('<email>', $new_email, $mail_message);
+ $mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$id, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+ }
+
+ // Check if someone else already has registered with that email address
+ $result = $db->query('SELECT id, username FROM '.$db->prefix.'users WHERE email=\''.$db->escape($new_email).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ if ($pun_config['p_allow_dupe_email'] == '0')
+ message($lang_prof_reg['Dupe email']);
+ else if ($pun_config['o_mailing_list'] != '')
+ {
+ while ($cur_dupe = $db->fetch_assoc($result))
+ $dupe_list[] = $cur_dupe['username'];
+
+ // Load the "dupe email change" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/dupe_email_change.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+ $mail_message = str_replace('<dupe_list>', implode(', ', $dupe_list), $mail_message);
+ $mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$id, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+ }
+
+
+ $new_email_key = random_pass(8);
+
+ $db->query('UPDATE '.$db->prefix.'users SET activate_string=\''.$db->escape($new_email).'\', activate_key=\''.$new_email_key.'\' WHERE id='.$id) or error('Unable to update activation data', __FILE__, __LINE__, $db->error());
+
+ // Load the "activate email" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/activate_email.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+ $mail_message = str_replace('<base_url>', get_base_url(), $mail_message);
+ $mail_message = str_replace('<activation_url>', get_base_url().'/profile.php?action=change_email&id='.$id.'&key='.$new_email_key, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($new_email, $mail_subject, $mail_message);
+
+ message($lang_profile['Activate email sent'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.', true);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Change email']);
+ $required_fields = array('req_new_email' => $lang_profile['New email'], 'req_password' => $lang_common['Password']);
+ $focus_element = array('change_email', 'req_new_email');
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_profile['Change email'] ?></span></h2>
+ <div class="box">
+ <form id="change_email" method="post" action="profile.php?action=change_email&amp;id=<?php echo $id ?>" onsubmit="return process_form(this)">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Email legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <label class="required"><strong><?php echo $lang_profile['New email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_new_email" size="50" maxlength="80" /><br /></label>
+ <label class="required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password" size="16" /><br /></label>
+ <p><?php echo $lang_profile['Email instructions'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="new_email" value="<?php echo $lang_common['Submit'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
+{
+ if ($pun_config['o_avatars'] == '0')
+ message($lang_profile['Avatars disabled']);
+
+ if ($pun_user['id'] != $id && !$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ if (isset($_POST['form_sent']))
+ {
+ if (!isset($_FILES['req_file']))
+ message($lang_profile['No file']);
+
+ // Make sure they got here from the site
+ confirm_referrer('profile.php');
+
+ $uploaded_file = $_FILES['req_file'];
+
+ // Make sure the upload went smooth
+ if (isset($uploaded_file['error']))
+ {
+ switch ($uploaded_file['error'])
+ {
+ case 1: // UPLOAD_ERR_INI_SIZE
+ case 2: // UPLOAD_ERR_FORM_SIZE
+ message($lang_profile['Too large ini']);
+ break;
+
+ case 3: // UPLOAD_ERR_PARTIAL
+ message($lang_profile['Partial upload']);
+ break;
+
+ case 4: // UPLOAD_ERR_NO_FILE
+ message($lang_profile['No file']);
+ break;
+
+ case 6: // UPLOAD_ERR_NO_TMP_DIR
+ message($lang_profile['No tmp directory']);
+ break;
+
+ default:
+ // No error occured, but was something actually uploaded?
+ if ($uploaded_file['size'] == 0)
+ message($lang_profile['No file']);
+ break;
+ }
+ }
+
+ if (is_uploaded_file($uploaded_file['tmp_name']))
+ {
+ // Preliminary file check, adequate in most cases
+ $allowed_types = array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png');
+ if (!in_array($uploaded_file['type'], $allowed_types))
+ message($lang_profile['Bad type']);
+
+ // Make sure the file isn't too big
+ if ($uploaded_file['size'] > $pun_config['o_avatars_size'])
+ message($lang_profile['Too large'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].'.');
+
+ // Move the file to the avatar directory. We do this before checking the width/height to circumvent open_basedir restrictions
+ if (!@move_uploaded_file($uploaded_file['tmp_name'], PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp'))
+ message($lang_profile['Move failed'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.');
+
+ list($width, $height, $type,) = @getimagesize(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+
+ // Determine type
+ if ($type == IMAGETYPE_GIF)
+ $extension = '.gif';
+ else if ($type == IMAGETYPE_JPEG)
+ $extension = '.jpg';
+ else if ($type == IMAGETYPE_PNG)
+ $extension = '.png';
+ else
+ {
+ // Invalid type
+ @unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+ message($lang_profile['Bad type']);
+ }
+
+ // Now check the width/height
+ if (empty($width) || empty($height) || $width > $pun_config['o_avatars_width'] || $height > $pun_config['o_avatars_height'])
+ {
+ @unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+ message($lang_profile['Too wide or high'].' '.$pun_config['o_avatars_width'].'x'.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].'.');
+ }
+
+ // Delete any old avatars and put the new one in place
+ delete_avatar($id);
+ @rename(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp', PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.$extension);
+ @chmod(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.$extension, 0644);
+ }
+ else
+ message($lang_profile['Unknown failure']);
+
+ redirect('profile.php?section=personality&amp;id='.$id, $lang_profile['Avatar upload redirect']);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Upload avatar']);
+ $required_fields = array('req_file' => $lang_profile['File']);
+ $focus_element = array('upload_avatar', 'req_file');
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_profile['Upload avatar'] ?></span></h2>
+ <div class="box">
+ <form id="upload_avatar" method="post" enctype="multipart/form-data" action="profile.php?action=upload_avatar2&amp;id=<?php echo $id ?>" onsubmit="return process_form(this)">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Upload avatar legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $pun_config['o_avatars_size'] ?>" />
+ <label class="required"><strong><?php echo $lang_profile['File'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input name="req_file" type="file" size="40" /><br /></label>
+ <p><?php echo $lang_profile['Avatar desc'].' '.$pun_config['o_avatars_width'].' x '.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].' '.$lang_common['and'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].' ('.file_size($pun_config['o_avatars_size']).').' ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="upload" value="<?php echo $lang_profile['Upload'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'delete_avatar')
+{
+ if ($pun_user['id'] != $id && !$pun_user['is_admmod'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('profile.php');
+
+ check_csrf($_GET['csrf_token']);
+
+ delete_avatar($id);
+
+ redirect('profile.php?section=personality&amp;id='.$id, $lang_profile['Avatar deleted redirect']);
+}
+
+
+else if (isset($_POST['update_group_membership']))
+{
+ if ($pun_user['g_id'] > PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('profile.php');
+
+ $new_group_id = intval($_POST['group_id']);
+
+ $result = $db->query('SELECT group_id FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user group', __FILE__, __LINE__, $db->error());
+ $old_group_id = $db->result($result);
+
+ $db->query('UPDATE '.$db->prefix.'users SET group_id='.$new_group_id.' WHERE id='.$id) or error('Unable to change user group', __FILE__, __LINE__, $db->error());
+
+ // Regenerate the users info cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+
+ if ($old_group_id == PUN_ADMIN || $new_group_id == PUN_ADMIN)
+ generate_admins_cache();
+
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$new_group_id) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+ $new_group_mod = $db->result($result);
+
+ // If the user was a moderator or an administrator, we remove him/her from the moderator list in all forums as well
+ if ($new_group_id != PUN_ADMIN && $new_group_mod != '1')
+ {
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ if (in_array($id, $cur_moderators))
+ {
+ $username = array_search($id, $cur_moderators);
+ unset($cur_moderators[$username]);
+ $cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ }
+ }
+
+ redirect('profile.php?section=admin&amp;id='.$id, $lang_profile['Group membership redirect']);
+}
+
+
+else if (isset($_POST['update_forums']))
+{
+ if ($pun_user['g_id'] > PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('profile.php');
+
+ // Get the username of the user we are processing
+ $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ $username = $db->result($result);
+
+ $moderator_in = (isset($_POST['moderator_in'])) ? array_keys($_POST['moderator_in']) : array();
+
+ // Loop through all forums
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+ // If the user should have moderator access (and he/she doesn't already have it)
+ if (in_array($cur_forum['id'], $moderator_in) && !in_array($id, $cur_moderators))
+ {
+ $cur_moderators[$username] = $id;
+ uksort($cur_moderators, 'utf8_strcasecmp');
+
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ // If the user shouldn't have moderator access (and he/she already has it)
+ else if (!in_array($cur_forum['id'], $moderator_in) && in_array($id, $cur_moderators))
+ {
+ unset($cur_moderators[$username]);
+ $cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ }
+
+ redirect('profile.php?section=admin&amp;id='.$id, $lang_profile['Update forums redirect']);
+}
+
+
+else if (isset($_POST['ban']))
+{
+ if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ // Get the username of the user we are banning
+ $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch username', __FILE__, __LINE__, $db->error());
+ $username = $db->result($result);
+
+ // Check whether user is already banned
+ $result = $db->query('SELECT id FROM '.$db->prefix.'bans WHERE username = \''.$db->escape($username).'\' ORDER BY expire IS NULL DESC, expire DESC LIMIT 1') or error('Unable to fetch ban ID', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ $ban_id = $db->result($result);
+ redirect('admin_bans.php?edit_ban='.$ban_id.'&amp;exists', $lang_profile['Ban redirect']);
+ }
+ else
+ redirect('admin_bans.php?add_ban='.$id, $lang_profile['Ban redirect']);
+}
+
+
+else if ($action == 'promote')
+{
+ if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_promote_users'] == '0'))
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('viewtopic.php');
+
+ check_csrf($_GET['csrf_token']);
+
+ $pid = isset($_GET['pid']) ? intval($_GET['pid']) : 0;
+
+ $sql = 'SELECT g.g_promote_next_group FROM '.$db->prefix.'groups AS g INNER JOIN '.$db->prefix.'users AS u ON u.group_id=g.g_id WHERE u.id='.$id.' AND g.g_promote_next_group>0';
+ $result = $db->query($sql) or error('Unable to fetch promotion information', __FILE__, __LINE__, $db->error());
+
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $next_group_id = $db->result($result);
+ $db->query('UPDATE '.$db->prefix.'users SET group_id='.$next_group_id.' WHERE id='.$id) or error('Unable to promote user', __FILE__, __LINE__, $db->error());
+
+ redirect('viewtopic.php?pid='.$pid.'#p'.$pid, $lang_profile['User promote redirect']);
+}
+
+
+else if (isset($_POST['delete_user']) || isset($_POST['delete_user_comply']))
+{
+ if ($pun_user['g_id'] > PUN_ADMIN)
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ confirm_referrer('profile.php');
+
+ // Get the username and group of the user we are deleting
+ $result = $db->query('SELECT group_id, username FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ list($group_id, $username) = $db->fetch_row($result);
+
+ if ($group_id == PUN_ADMIN)
+ message($lang_profile['No delete admin message']);
+
+ if (isset($_POST['delete_user_comply']))
+ {
+ // If the user is a moderator or an administrator, we remove him/her from the moderator list in all forums as well
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+ $group_mod = $db->result($result);
+
+ if ($group_id == PUN_ADMIN || $group_mod == '1')
+ {
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ if (in_array($id, $cur_moderators))
+ {
+ unset($cur_moderators[$username]);
+ $cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ }
+ }
+
+ // Delete any subscriptions
+ $db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$id) or error('Unable to delete topic subscriptions', __FILE__, __LINE__, $db->error());
+ $db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$id) or error('Unable to delete forum subscriptions', __FILE__, __LINE__, $db->error());
+
+ // Remove him/her from the online list (if they happen to be logged in)
+ $db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$id) or error('Unable to remove user from online list', __FILE__, __LINE__, $db->error());
+
+ // Should we delete all posts made by this user?
+ if (isset($_POST['delete_posts']))
+ {
+ require PUN_ROOT.'include/search_idx.php';
+ @set_time_limit(0);
+
+ // Find all posts made by this user
+ $result = $db->query('SELECT p.id, p.topic_id, t.forum_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.poster_id='.$id) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ while ($cur_post = $db->fetch_assoc($result))
+ {
+ // Determine whether this post is the "topic post" or not
+ $result2 = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$cur_post['topic_id'].' ORDER BY posted LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+
+ if ($db->result($result2) == $cur_post['id'])
+ delete_topic($cur_post['topic_id']);
+ else
+ delete_post($cur_post['id'], $cur_post['topic_id']);
+
+ update_forum($cur_post['forum_id']);
+ }
+ }
+ }
+ else
+ // Set all his/her posts to guest
+ $db->query('UPDATE '.$db->prefix.'posts SET poster_id=1 WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+
+ // Delete the user
+ $db->query('DELETE FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to delete user', __FILE__, __LINE__, $db->error());
+
+ // Delete user avatar
+ delete_avatar($id);
+
+ // Regenerate the users info cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+
+ if ($group_id == PUN_ADMIN)
+ generate_admins_cache();
+
+ redirect('index.php', $lang_profile['User delete redirect']);
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Confirm delete user']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_profile['Confirm delete user'] ?></span></h2>
+ <div class="box">
+ <form id="confirm_del_user" method="post" action="profile.php?id=<?php echo $id ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Confirm delete legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_profile['Confirmation info'].' <strong>'.pun_htmlspecialchars($username).'</strong>.' ?></p>
+ <div class="rbox">
+ <label><input type="checkbox" name="delete_posts" value="1" checked="checked" /><?php echo $lang_profile['Delete posts'] ?><br /></label>
+ </div>
+ <p class="warntext"><strong><?php echo $lang_profile['Delete warning'] ?></strong></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="delete_user_comply" value="<?php echo $lang_profile['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_POST['form_sent']))
+{
+ // Fetch the user group of the user we are editing
+ $result = $db->query('SELECT u.username, u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($old_username, $group_id, $is_moderator) = $db->fetch_row($result);
+
+ if ($pun_user['id'] != $id && // If we aren't the user (i.e. editing your own profile)
+ (!$pun_user['is_admmod'] || // and we are not an admin or mod
+ ($pun_user['g_id'] != PUN_ADMIN && // or we aren't an admin and ...
+ ($pun_user['g_mod_edit_users'] == '0' || // mods aren't allowed to edit users
+ $group_id == PUN_ADMIN || // or the user is an admin
+ $is_moderator)))) // or the user is another mod
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ // Make sure they got here from the site
+ confirm_referrer('profile.php');
+
+ $username_updated = false;
+
+ // Validate input depending on section
+ switch ($section)
+ {
+ case 'essentials':
+ {
+ $form = array(
+ 'timezone' => floatval($_POST['form']['timezone']),
+ 'dst' => isset($_POST['form']['dst']) ? '1' : '0',
+ 'time_format' => intval($_POST['form']['time_format']),
+ 'date_format' => intval($_POST['form']['date_format']),
+ );
+
+ // Make sure we got a valid language string
+ if (isset($_POST['form']['language']))
+ {
+ $languages = forum_list_langs();
+ $form['language'] = pun_trim($_POST['form']['language']);
+ if (!in_array($form['language'], $languages))
+ message($lang_common['Bad request'], false, '404 Not Found');
+ }
+ else
+ $form['language'] = $pun_config['o_default_lang'];
+
+ if ($pun_user['is_admmod'])
+ {
+ $form['admin_note'] = pun_trim($_POST['admin_note']);
+
+ // Are we allowed to change usernames?
+ if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_rename_users'] == '1'))
+ {
+ $form['username'] = pun_trim($_POST['req_username']);
+
+ if ($form['username'] != $old_username)
+ {
+ // Check username
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+ $errors = array();
+ check_username($form['username'], $id);
+ if (!empty($errors))
+ message($errors[0]);
+
+ $username_updated = true;
+ }
+ }
+
+ // We only allow administrators to update the post count
+ if ($pun_user['g_id'] == PUN_ADMIN)
+ $form['num_posts'] = intval($_POST['num_posts']);
+ }
+
+ if ($pun_config['o_regs_verify'] == '0' || $pun_user['is_admmod'])
+ {
+ require PUN_ROOT.'include/email.php';
+
+ // Validate the email address
+ $form['email'] = strtolower(pun_trim($_POST['req_email']));
+ if (!is_valid_email($form['email']))
+ message($lang_common['Invalid email']);
+ }
+
+ break;
+ }
+
+ case 'personal':
+ {
+ $form = array(
+ 'realname' => isset($_POST['form']['realname']) ? pun_trim($_POST['form']['realname']) : '',
+ 'url' => isset($_POST['form']['url']) ? pun_trim($_POST['form']['url']) : '',
+ 'location' => isset($_POST['form']['location']) ? pun_trim($_POST['form']['location']) : '',
+ );
+
+ // Add http:// if the URL doesn't contain it already (while allowing https://, too)
+ if ($pun_user['g_post_links'] == '1')
+ {
+ if ($form['url'] != '')
+ {
+ $url = url_valid($form['url']);
+
+ if ($url === false)
+ message($lang_profile['Invalid website URL']);
+
+ $form['url'] = $url['url'];
+ }
+ }
+ else
+ {
+ if (!empty($form['url']))
+ message($lang_profile['Website not allowed']);
+
+ $form['url'] = '';
+ }
+
+ if ($pun_user['g_id'] == PUN_ADMIN)
+ $form['title'] = pun_trim($_POST['title']);
+ else if ($pun_user['g_set_title'] == '1')
+ {
+ $form['title'] = pun_trim($_POST['title']);
+
+ if ($form['title'] != '')
+ {
+ // A list of words that the title may not contain
+ // If the language is English, there will be some duplicates, but it's not the end of the world
+ $forbidden = array('member', 'moderator', 'administrator', 'banned', 'guest', utf8_strtolower($lang_common['Member']), utf8_strtolower($lang_common['Moderator']), utf8_strtolower($lang_common['Administrator']), utf8_strtolower($lang_common['Banned']), utf8_strtolower($lang_common['Guest']));
+
+ if (in_array(utf8_strtolower($form['title']), $forbidden))
+ message($lang_profile['Forbidden title']);
+ }
+ }
+
+ break;
+ }
+
+ case 'messaging':
+ {
+ $form = array(
+ 'jabber' => pun_trim($_POST['form']['jabber']),
+ 'icq' => pun_trim($_POST['form']['icq']),
+ 'msn' => pun_trim($_POST['form']['msn']),
+ 'aim' => pun_trim($_POST['form']['aim']),
+ 'yahoo' => pun_trim($_POST['form']['yahoo']),
+ );
+
+ // If the ICQ UIN contains anything other than digits it's invalid
+ if (preg_match('%[^0-9]%', $form['icq']))
+ message($lang_prof_reg['Bad ICQ']);
+
+ break;
+ }
+
+ case 'personality':
+ {
+ $form = array();
+
+ // Clean up signature from POST
+ if ($pun_config['o_signatures'] == '1')
+ {
+ $form['signature'] = pun_linebreaks(pun_trim($_POST['signature']));
+
+ // Validate signature
+ if (pun_strlen($form['signature']) > $pun_config['p_sig_length'])
+ message(sprintf($lang_prof_reg['Sig too long'], $pun_config['p_sig_length'], pun_strlen($form['signature']) - $pun_config['p_sig_length']));
+ else if (substr_count($form['signature'], "\n") > ($pun_config['p_sig_lines']-1))
+ message(sprintf($lang_prof_reg['Sig too many lines'], $pun_config['p_sig_lines']));
+ else if ($form['signature'] && $pun_config['p_sig_all_caps'] == '0' && is_all_uppercase($form['signature']) && !$pun_user['is_admmod'])
+ $form['signature'] = utf8_ucwords(utf8_strtolower($form['signature']));
+
+ // Validate BBCode syntax
+ if ($pun_config['p_sig_bbcode'] == '1')
+ {
+ require PUN_ROOT.'include/parser.php';
+
+ $errors = array();
+
+ $form['signature'] = preparse_bbcode($form['signature'], $errors, true);
+
+ if(count($errors) > 0)
+ message('<ul><li>'.implode('</li><li>', $errors).'</li></ul>');
+ }
+ }
+
+ break;
+ }
+
+ case 'display':
+ {
+ $form = array(
+ 'disp_topics' => pun_trim($_POST['form']['disp_topics']),
+ 'disp_posts' => pun_trim($_POST['form']['disp_posts']),
+ );
+
+ if ($form['disp_topics'] != '')
+ {
+ $form['disp_topics'] = intval($form['disp_topics']);
+ if ($form['disp_topics'] < 3)
+ $form['disp_topics'] = 3;
+ else if ($form['disp_topics'] > 75)
+ $form['disp_topics'] = 75;
+ }
+
+ if ($form['disp_posts'] != '')
+ {
+ $form['disp_posts'] = intval($form['disp_posts']);
+ if ($form['disp_posts'] < 3)
+ $form['disp_posts'] = 3;
+ else if ($form['disp_posts'] > 75)
+ $form['disp_posts'] = 75;
+ }
+
+ if ($pun_config['o_smilies'] == '1' || $pun_config['o_smilies_sig'] == '1')
+ $form['show_smilies'] = isset($_POST['form']['show_smilies']) ? '1' : '0';
+
+ if ($pun_config['p_message_bbcode'] == '1' && $pun_config['p_message_img_tag'] == '1')
+ $form['show_img'] = isset($_POST['form']['show_img']) ? '1' : '0';
+
+ if ($pun_config['o_signatures'] == '1' && $pun_config['p_sig_bbcode'] == '1' && $pun_config['p_sig_img_tag'] == '1')
+ $form['show_img_sig'] = isset($_POST['form']['show_img_sig']) ? '1' : '0';
+
+ if ($pun_config['o_avatars'] == '1')
+ $form['show_avatars'] = isset($_POST['form']['show_avatars']) ? '1' : '0';
+
+ if ($pun_config['o_signatures'] == '1')
+ $form['show_sig'] = isset($_POST['form']['show_sig']) ? '1' : '0';
+
+ // Make sure we got a valid style string
+ if (isset($_POST['form']['style']))
+ {
+ $styles = forum_list_styles();
+ $form['style'] = pun_trim($_POST['form']['style']);
+ if (!in_array($form['style'], $styles))
+ message($lang_common['Bad request'], false, '404 Not Found');
+ }
+
+ break;
+ }
+
+ case 'privacy':
+ {
+ $form = array(
+ 'email_setting' => intval($_POST['form']['email_setting']),
+ 'notify_with_post' => isset($_POST['form']['notify_with_post']) ? '1' : '0',
+ 'auto_notify' => isset($_POST['form']['auto_notify']) ? '1' : '0',
+ );
+
+ if ($form['email_setting'] < 0 || $form['email_setting'] > 2)
+ $form['email_setting'] = $pun_config['o_default_email_setting'];
+
+ break;
+ }
+
+ default:
+ message($lang_common['Bad request'], false, '404 Not Found');
+ }
+
+
+ // Single quotes around non-empty values and NULL for empty values
+ $temp = array();
+ foreach ($form as $key => $input)
+ {
+ $value = ($input !== '') ? '\''.$db->escape($input).'\'' : 'NULL';
+
+ $temp[] = $key.'='.$value;
+ }
+
+ if (empty($temp))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+
+ $db->query('UPDATE '.$db->prefix.'users SET '.implode(',', $temp).' WHERE id='.$id) or error('Unable to update profile', __FILE__, __LINE__, $db->error());
+
+ // If we changed the username we have to update some stuff
+ if ($username_updated)
+ {
+ $db->query('UPDATE '.$db->prefix.'bans SET username=\''.$db->escape($form['username']).'\' WHERE username=\''.$db->escape($old_username).'\'') or error('Unable to update bans', __FILE__, __LINE__, $db->error());
+ // If any bans were updated, we will need to know because the cache will need to be regenerated.
+ if ($db->affected_rows() > 0)
+ $bans_updated = true;
+ $db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($form['username']).'\' WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($form['username']).'\' WHERE edited_by=\''.$db->escape($old_username).'\'') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($form['username']).'\' WHERE poster=\''.$db->escape($old_username).'\'') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'topics SET last_poster=\''.$db->escape($form['username']).'\' WHERE last_poster=\''.$db->escape($old_username).'\'') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'forums SET last_poster=\''.$db->escape($form['username']).'\' WHERE last_poster=\''.$db->escape($old_username).'\'') or error('Unable to update forums', __FILE__, __LINE__, $db->error());
+ $db->query('UPDATE '.$db->prefix.'online SET ident=\''.$db->escape($form['username']).'\' WHERE ident=\''.$db->escape($old_username).'\'') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+ // If the user is a moderator or an administrator we have to update the moderator lists
+ $result = $db->query('SELECT group_id FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ $group_id = $db->result($result);
+
+ $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+ $group_mod = $db->result($result);
+
+ if ($group_id == PUN_ADMIN || $group_mod == '1')
+ {
+ $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ if (in_array($id, $cur_moderators))
+ {
+ unset($cur_moderators[$old_username]);
+ $cur_moderators[$form['username']] = $id;
+ uksort($cur_moderators, 'utf8_strcasecmp');
+
+ $db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+ }
+ }
+ }
+
+ // Regenerate the users info cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+
+ // Check if the bans table was updated and regenerate the bans cache when needed
+ if (isset($bans_updated))
+ generate_bans_cache();
+ }
+
+ redirect('profile.php?section='.$section.'&amp;id='.$id, $lang_profile['Profile redirect']);
+}
+
+flux_hook('profile_after_form_handling');
+
+
+$result = $db->query('SELECT u.username, u.email, u.title, u.realname, u.url, u.jabber, u.icq, u.msn, u.aim, u.yahoo, u.location, u.signature, u.disp_topics, u.disp_posts, u.email_setting, u.notify_with_post, u.auto_notify, u.show_smilies, u.show_img, u.show_img_sig, u.show_avatars, u.show_sig, u.timezone, u.dst, u.language, u.style, u.num_posts, u.last_post, u.registered, u.registration_ip, u.admin_note, u.date_format, u.time_format, u.last_visit, g.g_id, g.g_user_title, g.g_moderator FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$user = $db->fetch_assoc($result);
+
+$last_post = format_time($user['last_post']);
+
+if ($user['signature'] != '')
+{
+ require PUN_ROOT.'include/parser.php';
+ $parsed_signature = parse_signature($user['signature']);
+}
+
+
+// View or edit?
+if ($pun_user['id'] != $id && // If we aren't the user (i.e. editing your own profile)
+ (!$pun_user['is_admmod'] || // and we are not an admin or mod
+ ($pun_user['g_id'] != PUN_ADMIN && // or we aren't an admin and ...
+ ($pun_user['g_mod_edit_users'] == '0' || // mods aren't allowed to edit users
+ $user['g_id'] == PUN_ADMIN || // or the user is an admin
+ $user['g_moderator'] == '1')))) // or the user is another mod
+{
+ $user_personal = array();
+
+ $user_personal[] = '<dt>'.$lang_common['Username'].'</dt>';
+ $user_personal[] = '<dd>'.pun_htmlspecialchars($user['username']).'</dd>';
+
+ $user_title_field = get_title($user);
+ $user_personal[] = '<dt>'.$lang_common['Title'].'</dt>';
+ $user_personal[] = '<dd>'.(($pun_config['o_censoring'] == '1') ? censor_words($user_title_field) : $user_title_field).'</dd>';
+
+ if ($user['realname'] != '')
+ {
+ $user_personal[] = '<dt>'.$lang_profile['Realname'].'</dt>';
+ $user_personal[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['realname']) : $user['realname']).'</dd>';
+ }
+
+ if ($user['location'] != '')
+ {
+ $user_personal[] = '<dt>'.$lang_profile['Location'].'</dt>';
+ $user_personal[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['location']) : $user['location']).'</dd>';
+ }
+
+ if ($user['url'] != '')
+ {
+ $user['url'] = pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['url']) : $user['url']);
+ $user_personal[] = '<dt>'.$lang_profile['Website'].'</dt>';
+ $user_personal[] = '<dd><span class="website"><a href="'.$user['url'].'" rel="nofollow">'.$user['url'].'</a></span></dd>';
+ }
+
+ if ($user['email_setting'] == '0' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+ $email_field = '<a href="mailto:'.pun_htmlspecialchars($user['email']).'">'.pun_htmlspecialchars($user['email']).'</a>';
+ else if ($user['email_setting'] == '1' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+ $email_field = '<a href="misc.php?email='.$id.'">'.$lang_common['Send email'].'</a>';
+ else
+ $email_field = '';
+ if ($email_field != '')
+ {
+ $user_personal[] = '<dt>'.$lang_common['Email'].'</dt>';
+ $user_personal[] = '<dd><span class="email">'.$email_field.'</span></dd>';
+ }
+
+ $user_messaging = array();
+
+ if ($user['jabber'] != '')
+ {
+ $user_messaging[] = '<dt>'.$lang_profile['Jabber'].'</dt>';
+ $user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['jabber']) : $user['jabber']).'</dd>';
+ }
+
+ if ($user['icq'] != '')
+ {
+ $user_messaging[] = '<dt>'.$lang_profile['ICQ'].'</dt>';
+ $user_messaging[] = '<dd>'.$user['icq'].'</dd>';
+ }
+
+ if ($user['msn'] != '')
+ {
+ $user_messaging[] = '<dt>'.$lang_profile['MSN'].'</dt>';
+ $user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['msn']) : $user['msn']).'</dd>';
+ }
+
+ if ($user['aim'] != '')
+ {
+ $user_messaging[] = '<dt>'.$lang_profile['AOL IM'].'</dt>';
+ $user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['aim']) : $user['aim']).'</dd>';
+ }
+
+ if ($user['yahoo'] != '')
+ {
+ $user_messaging[] = '<dt>'.$lang_profile['Yahoo'].'</dt>';
+ $user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['yahoo']) : $user['yahoo']).'</dd>';
+ }
+
+ $user_personality = array();
+
+ if ($pun_config['o_avatars'] == '1')
+ {
+ $avatar_field = generate_avatar_markup($id);
+ if ($avatar_field != '')
+ {
+ $user_personality[] = '<dt>'.$lang_profile['Avatar'].'</dt>';
+ $user_personality[] = '<dd>'.$avatar_field.'</dd>';
+ }
+ }
+
+ if ($pun_config['o_signatures'] == '1')
+ {
+ if (isset($parsed_signature))
+ {
+ $user_personality[] = '<dt>'.$lang_profile['Signature'].'</dt>';
+ $user_personality[] = '<dd><div class="postsignature postmsg">'.$parsed_signature.'</div></dd>';
+ }
+ }
+
+ $user_activity = array();
+
+ $posts_field = '';
+ if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
+ $posts_field = forum_number_format($user['num_posts']);
+ if ($pun_user['g_search'] == '1')
+ {
+ $quick_searches = array();
+ if ($user['num_posts'] > 0)
+ {
+ $quick_searches[] = '<a href="search.php?action=show_user_topics&amp;user_id='.$id.'">'.$lang_profile['Show topics'].'</a>';
+ $quick_searches[] = '<a href="search.php?action=show_user_posts&amp;user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+ }
+ if ($pun_user['is_admmod'] && $pun_config['o_topic_subscriptions'] == '1')
+ $quick_searches[] = '<a href="search.php?action=show_subscriptions&amp;user_id='.$id.'">'.$lang_profile['Show subscriptions'].'</a>';
+
+ if (!empty($quick_searches))
+ $posts_field .= (($posts_field != '') ? ' - ' : '').implode(' - ', $quick_searches);
+ }
+ if ($posts_field != '')
+ {
+ $user_activity[] = '<dt>'.$lang_common['Posts'].'</dt>';
+ $user_activity[] = '<dd>'.$posts_field.'</dd>';
+ }
+
+ if ($user['num_posts'] > 0)
+ {
+ $user_activity[] = '<dt>'.$lang_common['Last post'].'</dt>';
+ $user_activity[] = '<dd>'.$last_post.'</dd>';
+ }
+
+ $user_activity[] = '<dt>'.$lang_common['Registered'].'</dt>';
+ $user_activity[] = '<dd>'.format_time($user['registered'], true).'</dd>';
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), sprintf($lang_profile['Users profile'], pun_htmlspecialchars($user['username'])));
+ define('PUN_ALLOW_INDEX', 1);
+ define('PUN_ACTIVE_PAGE', 'index');
+ require PUN_ROOT.'header.php';
+
+?>
+<div id="viewprofile" class="block">
+ <h2><span><?php echo $lang_common['Profile'] ?></span></h2>
+ <div class="box">
+ <div class="fakeform">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Section personal'] ?></legend>
+ <div class="infldset">
+ <dl>
+ <?php echo implode("\n\t\t\t\t\t\t\t", $user_personal)."\n" ?>
+ </dl>
+ <div class="clearer"></div>
+ </div>
+ </fieldset>
+ </div>
+<?php if (!empty($user_messaging)): ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Section messaging'] ?></legend>
+ <div class="infldset">
+ <dl>
+ <?php echo implode("\n\t\t\t\t\t\t\t", $user_messaging)."\n" ?>
+ </dl>
+ <div class="clearer"></div>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; if (!empty($user_personality)): ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Section personality'] ?></legend>
+ <div class="infldset">
+ <dl>
+ <?php echo implode("\n\t\t\t\t\t\t\t", $user_personality)."\n" ?>
+ </dl>
+ <div class="clearer"></div>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['User activity'] ?></legend>
+ <div class="infldset">
+ <dl>
+ <?php echo implode("\n\t\t\t\t\t\t\t", $user_activity)."\n" ?>
+ </dl>
+ <div class="clearer"></div>
+ </div>
+ </fieldset>
+ </div>
+ </div>
+ </div>
+</div>
+
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+else
+{
+ if (!$section || $section == 'essentials')
+ {
+ if ($pun_user['is_admmod'])
+ {
+ if ($pun_user['g_id'] == PUN_ADMIN || $pun_user['g_mod_rename_users'] == '1')
+ $username_field = '<label class="required"><strong>'.$lang_common['Username'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_username" value="'.pun_htmlspecialchars($user['username']).'" size="25" maxlength="25" /><br /></label>'."\n";
+ else
+ $username_field = '<p>'.sprintf($lang_profile['Username info'], pun_htmlspecialchars($user['username'])).'</p>'."\n";
+
+ $email_field = '<label class="required"><strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_email" value="'.pun_htmlspecialchars($user['email']).'" size="40" maxlength="80" /><br /></label><p><span class="email"><a href="misc.php?email='.$id.'">'.$lang_common['Send email'].'</a></span></p>'."\n";
+ }
+ else
+ {
+ $username_field = '<p>'.$lang_common['Username'].': '.pun_htmlspecialchars($user['username']).'</p>'."\n";
+
+ if ($pun_config['o_regs_verify'] == '1')
+ $email_field = '<p>'.sprintf($lang_profile['Email info'], pun_htmlspecialchars($user['email']).' - <a href="profile.php?action=change_email&amp;id='.$id.'">'.$lang_profile['Change email'].'</a>').'</p>'."\n";
+ else
+ $email_field = '<label class="required"><strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_email" value="'.$user['email'].'" size="40" maxlength="80" /><br /></label>'."\n";
+ }
+
+ $posts_field = '';
+ $posts_actions = array();
+
+ if ($pun_user['g_id'] == PUN_ADMIN)
+ $posts_field .= '<label>'.$lang_common['Posts'].'<br /><input type="text" name="num_posts" value="'.$user['num_posts'].'" size="8" maxlength="8" /><br /></label>';
+ else if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
+ $posts_actions[] = sprintf($lang_profile['Posts info'], forum_number_format($user['num_posts']));
+
+ if ($pun_user['g_search'] == '1' || $pun_user['g_id'] == PUN_ADMIN)
+ {
+ $posts_actions[] = '<a href="search.php?action=show_user_topics&amp;user_id='.$id.'">'.$lang_profile['Show topics'].'</a>';
+ $posts_actions[] = '<a href="search.php?action=show_user_posts&amp;user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+
+ if ($pun_config['o_topic_subscriptions'] == '1')
+ $posts_actions[] = '<a href="search.php?action=show_subscriptions&amp;user_id='.$id.'">'.$lang_profile['Show subscriptions'].'</a>';
+ }
+
+ $posts_field .= (!empty($posts_actions) ? '<p class="actions">'.implode(' - ', $posts_actions).'</p>' : '')."\n";
+
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section essentials']);
+ $required_fields = array('req_username' => $lang_common['Username'], 'req_email' => $lang_common['Email']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('essentials');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section essentials'] ?></span></h2>
+ <div class="box">
+ <form id="profile1" method="post" action="profile.php?section=essentials&amp;id=<?php echo $id ?>" onsubmit="return process_form(this)">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Username and pass legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <?php echo $username_field ?>
+<?php if ($pun_user['id'] == $id || $pun_user['g_id'] == PUN_ADMIN || ($user['g_moderator'] == '0' && $pun_user['g_mod_change_passwords'] == '1')): ?> <p class="actions"><span><a href="profile.php?action=change_pass&amp;id=<?php echo $id ?>"><?php echo $lang_profile['Change pass'] ?></a></span></p>
+<?php endif; ?> </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_prof_reg['Email legend'] ?></legend>
+ <div class="infldset">
+ <?php echo $email_field ?>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_prof_reg['Localisation legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_prof_reg['Time zone info'] ?></p>
+ <label><?php echo $lang_prof_reg['Time zone']."\n" ?>
+ <br /><select name="form[timezone]">
+ <option value="-12"<?php if ($user['timezone'] == -12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-12:00'] ?></option>
+ <option value="-11"<?php if ($user['timezone'] == -11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-11:00'] ?></option>
+ <option value="-10"<?php if ($user['timezone'] == -10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-10:00'] ?></option>
+ <option value="-9.5"<?php if ($user['timezone'] == -9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:30'] ?></option>
+ <option value="-9"<?php if ($user['timezone'] == -9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:00'] ?></option>
+ <option value="-8.5"<?php if ($user['timezone'] == -8.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:30'] ?></option>
+ <option value="-8"<?php if ($user['timezone'] == -8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:00'] ?></option>
+ <option value="-7"<?php if ($user['timezone'] == -7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-07:00'] ?></option>
+ <option value="-6"<?php if ($user['timezone'] == -6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-06:00'] ?></option>
+ <option value="-5"<?php if ($user['timezone'] == -5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-05:00'] ?></option>
+ <option value="-4"<?php if ($user['timezone'] == -4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-04:00'] ?></option>
+ <option value="-3.5"<?php if ($user['timezone'] == -3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:30'] ?></option>
+ <option value="-3"<?php if ($user['timezone'] == -3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:00'] ?></option>
+ <option value="-2"<?php if ($user['timezone'] == -2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-02:00'] ?></option>
+ <option value="-1"<?php if ($user['timezone'] == -1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-01:00'] ?></option>
+ <option value="0"<?php if ($user['timezone'] == 0) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC'] ?></option>
+ <option value="1"<?php if ($user['timezone'] == 1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+01:00'] ?></option>
+ <option value="2"<?php if ($user['timezone'] == 2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+02:00'] ?></option>
+ <option value="3"<?php if ($user['timezone'] == 3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:00'] ?></option>
+ <option value="3.5"<?php if ($user['timezone'] == 3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:30'] ?></option>
+ <option value="4"<?php if ($user['timezone'] == 4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:00'] ?></option>
+ <option value="4.5"<?php if ($user['timezone'] == 4.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:30'] ?></option>
+ <option value="5"<?php if ($user['timezone'] == 5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:00'] ?></option>
+ <option value="5.5"<?php if ($user['timezone'] == 5.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:30'] ?></option>
+ <option value="5.75"<?php if ($user['timezone'] == 5.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:45'] ?></option>
+ <option value="6"<?php if ($user['timezone'] == 6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:00'] ?></option>
+ <option value="6.5"<?php if ($user['timezone'] == 6.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:30'] ?></option>
+ <option value="7"<?php if ($user['timezone'] == 7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+07:00'] ?></option>
+ <option value="8"<?php if ($user['timezone'] == 8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:00'] ?></option>
+ <option value="8.75"<?php if ($user['timezone'] == 8.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:45'] ?></option>
+ <option value="9"<?php if ($user['timezone'] == 9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:00'] ?></option>
+ <option value="9.5"<?php if ($user['timezone'] == 9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:30'] ?></option>
+ <option value="10"<?php if ($user['timezone'] == 10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:00'] ?></option>
+ <option value="10.5"<?php if ($user['timezone'] == 10.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:30'] ?></option>
+ <option value="11"<?php if ($user['timezone'] == 11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:00'] ?></option>
+ <option value="11.5"<?php if ($user['timezone'] == 11.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:30'] ?></option>
+ <option value="12"<?php if ($user['timezone'] == 12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:00'] ?></option>
+ <option value="12.75"<?php if ($user['timezone'] == 12.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:45'] ?></option>
+ <option value="13"<?php if ($user['timezone'] == 13) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+13:00'] ?></option>
+ <option value="14"<?php if ($user['timezone'] == 14) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+14:00'] ?></option>
+ </select>
+ <br /></label>
+ <div class="rbox">
+ <label><input type="checkbox" name="form[dst]" value="1"<?php if ($user['dst'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['DST'] ?><br /></label>
+ </div>
+ <label><?php echo $lang_prof_reg['Time format'] ?>
+
+ <br /><select name="form[time_format]">
+<?php
+ foreach (array_unique($forum_time_formats) as $key => $time_format)
+ {
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$key.'"';
+ if ($user['time_format'] == $key)
+ echo ' selected="selected"';
+ echo '>'. format_time(time(), false, null, $time_format, true, true, $user);
+ if ($key == 0)
+ echo ' ('.$lang_prof_reg['Default'].')';
+ echo "</option>\n";
+ }
+ ?>
+ </select>
+ <br /></label>
+ <label><?php echo $lang_prof_reg['Date format'] ?>
+
+ <br /><select name="form[date_format]">
+<?php
+ foreach (array_unique($forum_date_formats) as $key => $date_format)
+ {
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$key.'"';
+ if ($user['date_format'] == $key)
+ echo ' selected="selected"';
+ echo '>'. format_time(time(), true, $date_format, null, false, true, $user);
+ if ($key == 0)
+ echo ' ('.$lang_prof_reg['Default'].')';
+ echo "</option>\n";
+ }
+ ?>
+ </select>
+ <br /></label>
+
+<?php
+
+ $languages = forum_list_langs();
+
+ // Only display the language selection box if there's more than one language available
+ if (count($languages) > 1)
+ {
+
+?>
+ <label><?php echo $lang_prof_reg['Language'] ?>
+ <br /><select name="form[language]">
+<?php
+
+ foreach ($languages as $temp)
+ {
+ if ($user['language'] == $temp)
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+ }
+
+?>
+ </select>
+ <br /></label>
+<?php
+
+ }
+
+?>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['User activity'] ?></legend>
+ <div class="infldset">
+ <p><?php printf($lang_profile['Registered info'], format_time($user['registered'], true).(($pun_user['is_admmod']) ? ' (<a href="moderate.php?get_host='.pun_htmlspecialchars($user['registration_ip']).'">'.pun_htmlspecialchars($user['registration_ip']).'</a>)' : '')) ?></p>
+ <p><?php printf($lang_profile['Last post info'], $last_post) ?></p>
+ <p><?php printf($lang_profile['Last visit info'], format_time($user['last_visit'])) ?></p>
+ <?php echo $posts_field ?>
+<?php if ($pun_user['is_admmod']): ?> <label><?php echo $lang_profile['Admin note'] ?><br />
+ <input id="admin_note" type="text" name="admin_note" value="<?php echo pun_htmlspecialchars($user['admin_note']) ?>" size="30" maxlength="30" /><br /></label>
+<?php endif; ?> </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+ </form>
+ </div>
+ </div>
+<?php
+
+ }
+ else if ($section == 'personal')
+ {
+ if ($pun_user['g_set_title'] == '1')
+ $title_field = '<label>'.$lang_common['Title'].' <em>('.$lang_profile['Leave blank'].')</em><br /><input type="text" name="title" value="'.pun_htmlspecialchars($user['title']).'" size="30" maxlength="50" /><br /></label>'."\n";
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section personal']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('personal');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section personal'] ?></span></h2>
+ <div class="box">
+ <form id="profile2" method="post" action="profile.php?section=personal&amp;id=<?php echo $id ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Personal details legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <label><?php echo $lang_profile['Realname'] ?><br /><input type="text" name="form[realname]" value="<?php echo pun_htmlspecialchars($user['realname']) ?>" size="40" maxlength="40" /><br /></label>
+<?php if (isset($title_field)): ?> <?php echo $title_field ?>
+<?php endif; ?> <label><?php echo $lang_profile['Location'] ?><br /><input type="text" name="form[location]" value="<?php echo pun_htmlspecialchars($user['location']) ?>" size="30" maxlength="30" /><br /></label>
+<?php if ($pun_user['g_post_links'] == '1' || $pun_user['g_id'] == PUN_ADMIN) : ?> <label><?php echo $lang_profile['Website'] ?><br /><input type="text" name="form[url]" value="<?php echo pun_htmlspecialchars($user['url']) ?>" size="50" maxlength="80" /><br /></label>
+<?php endif; ?>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+ </form>
+ </div>
+ </div>
+<?php
+
+ }
+ else if ($section == 'messaging')
+ {
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section messaging']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('messaging');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section messaging'] ?></span></h2>
+ <div class="box">
+ <form id="profile3" method="post" action="profile.php?section=messaging&amp;id=<?php echo $id ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Contact details legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <label><?php echo $lang_profile['Jabber'] ?><br /><input id="jabber" type="text" name="form[jabber]" value="<?php echo pun_htmlspecialchars($user['jabber']) ?>" size="40" maxlength="75" /><br /></label>
+ <label><?php echo $lang_profile['ICQ'] ?><br /><input id="icq" type="text" name="form[icq]" value="<?php echo $user['icq'] ?>" size="12" maxlength="12" /><br /></label>
+ <label><?php echo $lang_profile['MSN'] ?><br /><input id="msn" type="text" name="form[msn]" value="<?php echo pun_htmlspecialchars($user['msn']) ?>" size="40" maxlength="50" /><br /></label>
+ <label><?php echo $lang_profile['AOL IM'] ?><br /><input id="aim" type="text" name="form[aim]" value="<?php echo pun_htmlspecialchars($user['aim']) ?>" size="20" maxlength="30" /><br /></label>
+ <label><?php echo $lang_profile['Yahoo'] ?><br /><input id="yahoo" type="text" name="form[yahoo]" value="<?php echo pun_htmlspecialchars($user['yahoo']) ?>" size="20" maxlength="30" /><br /></label>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+ </form>
+ </div>
+ </div>
+<?php
+
+ }
+ else if ($section == 'personality')
+ {
+ if ($pun_config['o_avatars'] == '0' && $pun_config['o_signatures'] == '0')
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $avatar_field = '<span><a href="profile.php?action=upload_avatar&amp;id='.$id.'">'.$lang_profile['Change avatar'].'</a></span>';
+
+ $user_avatar = generate_avatar_markup($id);
+ if ($user_avatar)
+ $avatar_field .= ' <span><a href="profile.php?action=delete_avatar&amp;id='.$id.'&amp;csrf_token='.pun_csrf_token().'">'.$lang_profile['Delete avatar'].'</a></span>';
+ else
+ $avatar_field = '<span><a href="profile.php?action=upload_avatar&amp;id='.$id.'">'.$lang_profile['Upload avatar'].'</a></span>';
+
+ if ($user['signature'] != '')
+ $signature_preview = '<p>'.$lang_profile['Sig preview'].'</p>'."\n\t\t\t\t\t\t\t".'<div class="postsignature postmsg">'."\n\t\t\t\t\t\t\t\t".'<hr />'."\n\t\t\t\t\t\t\t\t".$parsed_signature."\n\t\t\t\t\t\t\t".'</div>'."\n";
+ else
+ $signature_preview = '<p>'.$lang_profile['No sig'].'</p>'."\n";
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section personality']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('personality');
+
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section personality'] ?></span></h2>
+ <div class="box">
+ <form id="profile4" method="post" action="profile.php?section=personality&amp;id=<?php echo $id ?>">
+ <div><input type="hidden" name="form_sent" value="1" /></div>
+<?php if ($pun_config['o_avatars'] == '1'): ?> <div class="inform">
+ <fieldset id="profileavatar">
+ <legend><?php echo $lang_profile['Avatar legend'] ?></legend>
+ <div class="infldset">
+<?php if ($user_avatar): ?> <div class="useravatar"><?php echo $user_avatar ?></div>
+<?php endif; ?> <p><?php echo $lang_profile['Avatar info'] ?></p>
+ <p class="clearb actions"><?php echo $avatar_field ?></p>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; if ($pun_config['o_signatures'] == '1'): ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Signature legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_profile['Signature info'] ?></p>
+ <div class="txtarea">
+ <label><?php printf($lang_profile['Sig max size'], forum_number_format($pun_config['p_sig_length']), $pun_config['p_sig_lines']) ?><br />
+ <textarea name="signature" rows="4" cols="65"><?php echo pun_htmlspecialchars($user['signature']) ?></textarea><br /></label>
+ </div>
+ <ul class="bblinks">
+ <li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_sig_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#url" onclick="window.open(this.href); return false;"><?php echo $lang_common['url tag'] ?></a> <?php echo ($pun_config['p_sig_bbcode'] == '1' && $pun_user['g_post_links'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_sig_bbcode'] == '1' && $pun_config['p_sig_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies_sig'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ </ul>
+ <?php echo $signature_preview ?>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; ?> <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+ </form>
+ </div>
+ </div>
+<?php
+
+ }
+ else if ($section == 'display')
+ {
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section display']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('display');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section display'] ?></span></h2>
+ <div class="box">
+ <form id="profile5" method="post" action="profile.php?section=display&amp;id=<?php echo $id ?>">
+ <div><input type="hidden" name="form_sent" value="1" /></div>
+<?php
+
+ $styles = forum_list_styles();
+
+ // Only display the style selection box if there's more than one style available
+ if (count($styles) == 1)
+ echo "\t\t\t".'<div><input type="hidden" name="form[style]" value="'.$styles[0].'" /></div>'."\n";
+ else if (count($styles) > 1)
+ {
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Style legend'] ?></legend>
+ <div class="infldset">
+ <label><?php echo $lang_profile['Styles'] ?><br />
+ <select name="form[style]">
+<?php
+
+ foreach ($styles as $temp)
+ {
+ if ($user['style'] == $temp)
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.str_replace('_', ' ', $temp).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.str_replace('_', ' ', $temp).'</option>'."\n";
+ }
+
+?>
+ </select>
+ <br /></label>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ }
+
+?>
+<?php if ($pun_config['o_smilies'] == '1' || $pun_config['o_smilies_sig'] == '1' || $pun_config['o_signatures'] == '1' || $pun_config['o_avatars'] == '1' || ($pun_config['p_message_bbcode'] == '1' && $pun_config['p_message_img_tag'] == '1')): ?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Post display legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_profile['Post display info'] ?></p>
+ <div class="rbox">
+<?php if ($pun_config['o_smilies'] == '1' || $pun_config['o_smilies_sig'] == '1'): ?> <label><input type="checkbox" name="form[show_smilies]" value="1"<?php if ($user['show_smilies'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show smilies'] ?><br /></label>
+<?php endif; if ($pun_config['o_signatures'] == '1'): ?> <label><input type="checkbox" name="form[show_sig]" value="1"<?php if ($user['show_sig'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show sigs'] ?><br /></label>
+<?php endif; if ($pun_config['o_avatars'] == '1'): ?> <label><input type="checkbox" name="form[show_avatars]" value="1"<?php if ($user['show_avatars'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show avatars'] ?><br /></label>
+<?php endif; if ($pun_config['p_message_bbcode'] == '1' && $pun_config['p_message_img_tag'] == '1'): ?> <label><input type="checkbox" name="form[show_img]" value="1"<?php if ($user['show_img'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show images'] ?><br /></label>
+<?php endif; if ($pun_config['o_signatures'] == '1' && $pun_config['p_sig_bbcode'] == '1' && $pun_config['p_sig_img_tag'] == '1'): ?> <label><input type="checkbox" name="form[show_img_sig]" value="1"<?php if ($user['show_img_sig'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show images sigs'] ?><br /></label>
+<?php endif; ?>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; ?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Pagination legend'] ?></legend>
+ <div class="infldset">
+ <label class="conl"><?php echo $lang_profile['Topics per page'] ?><br /><input type="text" name="form[disp_topics]" value="<?php echo $user['disp_topics'] ?>" size="6" maxlength="2" /><br /></label>
+ <label class="conl"><?php echo $lang_profile['Posts per page'] ?><br /><input type="text" name="form[disp_posts]" value="<?php echo $user['disp_posts'] ?>" size="6" maxlength="2" /><br /></label>
+ <p class="clearb"><?php echo $lang_profile['Paginate info'] ?> <?php echo $lang_profile['Leave blank'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+ </form>
+ </div>
+ </div>
+<?php
+
+ }
+ else if ($section == 'privacy')
+ {
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section privacy']);
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('privacy');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section privacy'] ?></span></h2>
+ <div class="box">
+ <form id="profile6" method="post" action="profile.php?section=privacy&amp;id=<?php echo $id ?>">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_prof_reg['Privacy options legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <p><?php echo $lang_prof_reg['Email setting info'] ?></p>
+ <div class="rbox">
+ <label><input type="radio" name="form[email_setting]" value="0"<?php if ($user['email_setting'] == '0') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 1'] ?><br /></label>
+ <label><input type="radio" name="form[email_setting]" value="1"<?php if ($user['email_setting'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 2'] ?><br /></label>
+ <label><input type="radio" name="form[email_setting]" value="2"<?php if ($user['email_setting'] == '2') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 3'] ?><br /></label>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+<?php if ($pun_config['o_forum_subscriptions'] == '1' || $pun_config['o_topic_subscriptions'] == '1'): ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Subscription legend'] ?></legend>
+ <div class="infldset">
+ <div class="rbox">
+ <label><input type="checkbox" name="form[notify_with_post]" value="1"<?php if ($user['notify_with_post'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Notify full'] ?><br /></label>
+<?php if ($pun_config['o_topic_subscriptions'] == '1'): ?> <label><input type="checkbox" name="form[auto_notify]" value="1"<?php if ($user['auto_notify'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Auto notify full'] ?><br /></label>
+<?php endif; ?>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; ?> <p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+ </form>
+ </div>
+ </div>
+<?php
+
+ }
+ else if ($section == 'admin')
+ {
+ if (!$pun_user['is_admmod'] || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '0'))
+ message($lang_common['Bad request'], false, '403 Forbidden');
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section admin']);
+
+ flux_hook('profile_admin_before_header');
+
+ define('PUN_ACTIVE_PAGE', 'profile');
+ require PUN_ROOT.'header.php';
+
+ generate_profile_menu('admin');
+
+?>
+ <div class="blockform">
+ <h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section admin'] ?></span></h2>
+ <div class="box">
+ <form id="profile7" method="post" action="profile.php?section=admin&amp;id=<?php echo $id ?>">
+ <div class="inform">
+ <input type="hidden" name="form_sent" value="1" />
+ <fieldset>
+<?php
+
+ if ($pun_user['g_moderator'] == '1')
+ {
+
+?>
+ <legend><?php echo $lang_profile['Delete ban legend'] ?></legend>
+ <div class="infldset">
+ <p><input type="submit" name="ban" value="<?php echo $lang_profile['Ban user'] ?>" /></p>
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ }
+ else
+ {
+ if ($pun_user['id'] != $id)
+ {
+
+?>
+ <legend><?php echo $lang_profile['Group membership legend'] ?></legend>
+ <div class="infldset">
+ <select id="group_id" name="group_id">
+<?php
+
+ $result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+ while ($cur_group = $db->fetch_assoc($result))
+ {
+ if ($cur_group['g_id'] == $user['g_id'] || ($cur_group['g_id'] == $pun_config['o_default_user_group'] && $user['g_id'] == ''))
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ }
+
+?>
+ </select>
+ <input type="submit" name="update_group_membership" value="<?php echo $lang_profile['Save'] ?>" />
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+<?php
+
+ }
+
+?>
+ <legend><?php echo $lang_profile['Delete ban legend'] ?></legend>
+ <div class="infldset">
+ <input type="submit" name="delete_user" value="<?php echo $lang_profile['Delete user'] ?>" /> <input type="submit" name="ban" value="<?php echo $lang_profile['Ban user'] ?>" />
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ if ($user['g_moderator'] == '1' || $user['g_id'] == PUN_ADMIN)
+ {
+
+?>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_profile['Set mods legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_profile['Moderator in info'] ?></p>
+<?php
+
+ $result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.moderators FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id WHERE f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+ $cur_category = 0;
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category)
+ echo "\n\t\t\t\t\t\t\t\t".'</div>';
+
+ if ($cur_category != 0)
+ echo "\n\t\t\t\t\t\t\t".'</div>'."\n";
+
+ echo "\t\t\t\t\t\t\t".'<div class="conl">'."\n\t\t\t\t\t\t\t\t".'<p><strong>'.pun_htmlspecialchars($cur_forum['cat_name']).'</strong></p>'."\n\t\t\t\t\t\t\t\t".'<div class="rbox">';
+ $cur_category = $cur_forum['cid'];
+ }
+
+ $moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+ echo "\n\t\t\t\t\t\t\t\t\t".'<label><input type="checkbox" name="moderator_in['.$cur_forum['fid'].']" value="1"'.((in_array($id, $moderators)) ? ' checked="checked"' : '').' />'.pun_htmlspecialchars($cur_forum['forum_name']).'<br /></label>'."\n";
+ }
+
+?>
+ </div>
+ </div>
+ <br class="clearb" /><input type="submit" name="update_forums" value="<?php echo $lang_profile['Update forums'] ?>" />
+ </div>
+ </fieldset>
+ </div>
+<?php
+
+ }
+ }
+
+?>
+ </form>
+<?php flux_hook('profile_admin_after_form') ?>
+ </div>
+ </div>
+<?php
+
+ }
+ else
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+?>
+ <div class="clearer"></div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..bf844de
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,28 @@
+# FluxBB 1.5 Readme
+
+## About
+
+FluxBB is an open source forum application released under the GNU General Public
+Licence. It is free to download and use and will remain so. FluxBB was conceived and
+designed to be fast and light with less of the "not so essential" features that some
+of the other forums have whilst not sacrificing essential functionality or usability.
+
+## Requirements
+
+* A webserver
+* PHP 4.4.0 or later
+* A database such as MySQL 4.1.2 or later, PostgreSQL 7.0 or later, or SQLite 2
+
+## Recommendations
+
+* Make use of a PHP accelerator such as APC or XCache
+* Make sure PHP has the zlib module installed to allow FluxBB to gzip output
+
+## Links
+
+* Homepage: http://fluxbb.org
+* Documentation: http://fluxbb.org/docs/v1.5
+* Community: http://fluxbb.org/forums/
+* Resources: http://fluxbb.org/resources/
+* IRC: irc://irc.freenode.net/fluxbb
+* Development: http://github.com/fluxbb/fluxbb \ No newline at end of file
diff --git a/register.php b/register.php
new file mode 100644
index 0000000..04a6417
--- /dev/null
+++ b/register.php
@@ -0,0 +1,448 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+// If we are logged in, we shouldn't be here
+if (!$pun_user['is_guest'])
+{
+ header('Location: index.php');
+ exit;
+}
+
+// Load the register.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+// Load the register.php/profile.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
+
+if ($pun_config['o_regs_allow'] == '0')
+ message($lang_register['No new regs']);
+
+
+// User pressed the cancel button
+if (isset($_GET['cancel']))
+ redirect('index.php', $lang_register['Reg cancel redirect']);
+
+
+else if ($pun_config['o_rules'] == '1' && !isset($_GET['agree']) && !isset($_POST['form_sent']))
+{
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_register['Register'], $lang_register['Forum rules']);
+ define('PUN_ACTIVE_PAGE', 'register');
+ require PUN_ROOT.'header.php';
+
+?>
+<div id="rules" class="blockform">
+ <div class="hd"><h2><span><?php echo $lang_register['Forum rules'] ?></span></h2></div>
+ <div class="box">
+ <form method="get" action="register.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_register['Rules legend'] ?></legend>
+ <div class="infldset">
+ <div class="usercontent"><?php echo $pun_config['o_rules_message'] ?></div>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="agree" value="<?php echo $lang_register['Agree'] ?>" /> <input type="submit" name="cancel" value="<?php echo $lang_register['Cancel'] ?>" /></p>
+ </form>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+}
+
+// Start with a clean slate
+$errors = array();
+
+if (isset($_POST['form_sent']))
+{
+ flux_hook('register_before_validation');
+
+ // Check that someone from this IP didn't register a user within the last hour (DoS prevention)
+ $result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE registration_ip=\''.$db->escape(get_remote_address()).'\' AND registered>'.(time() - 3600)) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ message($lang_register['Registration flood']);
+
+
+ $username = pun_trim($_POST['req_user']);
+ $email1 = strtolower(pun_trim($_POST['req_email1']));
+
+ if ($pun_config['o_regs_verify'] == '1')
+ {
+ $email2 = strtolower(pun_trim($_POST['req_email2']));
+
+ $password1 = random_pass(12);
+ $password2 = $password1;
+ }
+ else
+ {
+ $password1 = pun_trim($_POST['req_password1']);
+ $password2 = pun_trim($_POST['req_password2']);
+ }
+
+ // Validate username and passwords
+ check_username($username);
+
+ if (pun_strlen($password1) < 9)
+ $errors[] = $lang_prof_reg['Pass too short'];
+ else if ($password1 != $password2)
+ $errors[] = $lang_prof_reg['Pass not match'];
+
+ // Validate email
+ require PUN_ROOT.'include/email.php';
+
+ if (!is_valid_email($email1))
+ $errors[] = $lang_common['Invalid email'];
+ else if ($pun_config['o_regs_verify'] == '1' && $email1 != $email2)
+ $errors[] = $lang_register['Email not match'];
+
+ // Check if it's a banned email address
+ if (is_banned_email($email1))
+ {
+ if ($pun_config['p_allow_banned_email'] == '0')
+ $errors[] = $lang_prof_reg['Banned email'];
+
+ $banned_email = true; // Used later when we send an alert email
+ }
+ else
+ $banned_email = false;
+
+ // Check if someone else already has registered with that email address
+ $dupe_list = array();
+
+ $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE email=\''.$db->escape($email1).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+ if ($db->num_rows($result))
+ {
+ if ($pun_config['p_allow_dupe_email'] == '0')
+ $errors[] = $lang_prof_reg['Dupe email'];
+
+ while ($cur_dupe = $db->fetch_assoc($result))
+ $dupe_list[] = $cur_dupe['username'];
+ }
+
+ // Make sure we got a valid language string
+ if (isset($_POST['language']))
+ {
+ $language = preg_replace('%[\.\\\/]%', '', $_POST['language']);
+ if (!file_exists(PUN_ROOT.'lang/'.$language.'/common.php'))
+ message($lang_common['Bad request'], false, '404 Not Found');
+ }
+ else
+ $language = $pun_config['o_default_lang'];
+
+ $timezone = round($_POST['timezone'], 1);
+
+ $dst = isset($_POST['dst']) ? '1' : '0';
+
+ $email_setting = intval($_POST['email_setting']);
+ if ($email_setting < 0 || $email_setting > 2)
+ $email_setting = $pun_config['o_default_email_setting'];
+
+ flux_hook('register_after_validation');
+
+ // Did everything go according to plan?
+ if (empty($errors))
+ {
+ // Insert the new user into the database. We do this now to get the last inserted ID for later use
+ $now = time();
+
+ $intial_group_id = ($pun_config['o_regs_verify'] == '0') ? $pun_config['o_default_user_group'] : PUN_UNVERIFIED;
+ $password_hash = pun_hash($password1);
+
+ // Add the user
+ $db->query('INSERT INTO '.$db->prefix.'users (username, group_id, password, email, email_setting, timezone, dst, language, style, registered, registration_ip, last_visit) VALUES(\''.$db->escape($username).'\', '.$intial_group_id.', \''.$password_hash.'\', \''.$db->escape($email1).'\', '.$email_setting.', '.$timezone.' , '.$dst.', \''.$db->escape($language).'\', \''.$pun_config['o_default_style'].'\', '.$now.', \''.$db->escape(get_remote_address()).'\', '.$now.')') or error('Unable to create user', __FILE__, __LINE__, $db->error());
+ $new_uid = $db->insert_id();
+
+ if ($pun_config['o_regs_verify'] == '0')
+ {
+ // Regenerate the users info cache
+ if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+ require PUN_ROOT.'include/cache.php';
+
+ generate_users_info_cache();
+ }
+
+ // If the mailing list isn't empty, we may need to send out some alerts
+ if ($pun_config['o_mailing_list'] != '')
+ {
+ // If we previously found out that the email was banned
+ if ($banned_email)
+ {
+ // Load the "banned email register" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/banned_email_register.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $username, $mail_message);
+ $mail_message = str_replace('<email>', $email1, $mail_message);
+ $mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$new_uid, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+
+ // If we previously found out that the email was a dupe
+ if (!empty($dupe_list))
+ {
+ // Load the "dupe email register" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/dupe_email_register.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $username, $mail_message);
+ $mail_message = str_replace('<dupe_list>', implode(', ', $dupe_list), $mail_message);
+ $mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$new_uid, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+
+ // Should we alert people on the admin mailing list that a new user has registered?
+ if ($pun_config['o_regs_report'] == '1')
+ {
+ // Load the "new user" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/new_user.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_message = str_replace('<username>', $username, $mail_message);
+ $mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
+ $mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$new_uid, $mail_message);
+ $mail_message = str_replace('<admin_url>', get_base_url().'/profile.php?section=admin&id='.$new_uid, $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+ }
+ }
+
+ // Must the user verify the registration or do we log him/her in right now?
+ if ($pun_config['o_regs_verify'] == '1')
+ {
+ // Load the "welcome" template
+ $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/welcome.tpl'));
+
+ // The first row contains the subject
+ $first_crlf = strpos($mail_tpl, "\n");
+ $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+ $mail_message = trim(substr($mail_tpl, $first_crlf));
+
+ $mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
+ $mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
+ $mail_message = str_replace('<username>', $username, $mail_message);
+ $mail_message = str_replace('<password>', $password1, $mail_message);
+ $mail_message = str_replace('<login_url>', get_base_url().'/login.php', $mail_message);
+ $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
+
+ pun_mail($email1, $mail_subject, $mail_message);
+
+ message($lang_register['Reg email'].' <a href="mailto:'.pun_htmlspecialchars($pun_config['o_admin_email']).'">'.pun_htmlspecialchars($pun_config['o_admin_email']).'</a>.', true);
+ }
+
+ pun_setcookie($new_uid, $password_hash, time() + $pun_config['o_timeout_visit']);
+
+ redirect('index.php', $lang_register['Reg complete']);
+ }
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_register['Register']);
+$required_fields = array('req_user' => $lang_common['Username'], 'req_password1' => $lang_common['Password'], 'req_password2' => $lang_prof_reg['Confirm pass'], 'req_email1' => $lang_common['Email'], 'req_email2' => $lang_common['Email'].' 2');
+$focus_element = array('register', 'req_user');
+
+flux_hook('register_before_header');
+
+define('PUN_ACTIVE_PAGE', 'register');
+require PUN_ROOT.'header.php';
+
+$timezone = isset($timezone) ? $timezone : $pun_config['o_default_timezone'];
+$dst = isset($dst) ? $dst : $pun_config['o_default_dst'];
+$email_setting = isset($email_setting) ? $email_setting : $pun_config['o_default_email_setting'];
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+ <h2><span><?php echo $lang_register['Registration errors'] ?></span></h2>
+ <div class="box">
+ <div class="inbox error-info">
+ <p><?php echo $lang_register['Registration errors info'] ?></p>
+ <ul class="error-list">
+<?php
+
+ foreach ($errors as $cur_error)
+ echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+ </ul>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+?>
+<div id="regform" class="blockform">
+ <h2><span><?php echo $lang_register['Register'] ?></span></h2>
+ <div class="box">
+ <form id="register" method="post" action="register.php?action=register" onsubmit="this.register.disabled=true;if(process_form(this)){return true;}else{this.register.disabled=false;return false;}">
+ <div class="inform">
+ <div class="forminfo">
+ <h3><?php echo $lang_common['Important information'] ?></h3>
+ <p><?php echo $lang_register['Desc 1'] ?></p>
+ <p><?php echo $lang_register['Desc 2'] ?></p>
+ </div>
+ <fieldset>
+ <legend><?php echo $lang_register['Username legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="form_sent" value="1" />
+ <label class="required"><strong><?php echo $lang_common['Username'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_user" value="<?php if (isset($_POST['req_user'])) echo pun_htmlspecialchars($_POST['req_user']); ?>" size="25" maxlength="25" /><br /></label>
+ </div>
+ </fieldset>
+ </div>
+<?php if ($pun_config['o_regs_verify'] == '0'): ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_register['Pass legend'] ?></legend>
+ <div class="infldset">
+ <label class="conl required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password1" value="<?php if (isset($_POST['req_password1'])) echo pun_htmlspecialchars($_POST['req_password1']); ?>" size="16" /><br /></label>
+ <label class="conl required"><strong><?php echo $lang_prof_reg['Confirm pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password2" value="<?php if (isset($_POST['req_password2'])) echo pun_htmlspecialchars($_POST['req_password2']); ?>" size="16" /><br /></label>
+ <p class="clearb"><?php echo $lang_register['Pass info'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+<?php endif; ?> <div class="inform">
+ <fieldset>
+ <legend><?php echo ($pun_config['o_regs_verify'] == '1') ? $lang_prof_reg['Email legend 2'] : $lang_prof_reg['Email legend'] ?></legend>
+ <div class="infldset">
+<?php if ($pun_config['o_regs_verify'] == '1'): ?> <p><?php echo $lang_register['Email info'] ?></p>
+<?php endif; ?> <label class="required"><strong><?php echo $lang_common['Email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input type="text" name="req_email1" value="<?php if (isset($_POST['req_email1'])) echo pun_htmlspecialchars($_POST['req_email1']); ?>" size="50" maxlength="80" /><br /></label>
+<?php if ($pun_config['o_regs_verify'] == '1'): ?> <label class="required"><strong><?php echo $lang_register['Confirm email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+ <input type="text" name="req_email2" value="<?php if (isset($_POST['req_email2'])) echo pun_htmlspecialchars($_POST['req_email2']); ?>" size="50" maxlength="80" /><br /></label>
+<?php endif; ?> </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_prof_reg['Localisation legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_prof_reg['Time zone info'] ?></p>
+ <label><?php echo $lang_prof_reg['Time zone']."\n" ?>
+ <br /><select id="time_zone" name="timezone">
+ <option value="-12"<?php if ($timezone == -12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-12:00'] ?></option>
+ <option value="-11"<?php if ($timezone == -11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-11:00'] ?></option>
+ <option value="-10"<?php if ($timezone == -10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-10:00'] ?></option>
+ <option value="-9.5"<?php if ($timezone == -9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:30'] ?></option>
+ <option value="-9"<?php if ($timezone == -9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:00'] ?></option>
+ <option value="-8.5"<?php if ($timezone == -8.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:30'] ?></option>
+ <option value="-8"<?php if ($timezone == -8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:00'] ?></option>
+ <option value="-7"<?php if ($timezone == -7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-07:00'] ?></option>
+ <option value="-6"<?php if ($timezone == -6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-06:00'] ?></option>
+ <option value="-5"<?php if ($timezone == -5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-05:00'] ?></option>
+ <option value="-4"<?php if ($timezone == -4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-04:00'] ?></option>
+ <option value="-3.5"<?php if ($timezone == -3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:30'] ?></option>
+ <option value="-3"<?php if ($timezone == -3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:00'] ?></option>
+ <option value="-2"<?php if ($timezone == -2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-02:00'] ?></option>
+ <option value="-1"<?php if ($timezone == -1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-01:00'] ?></option>
+ <option value="0"<?php if ($timezone == 0) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC'] ?></option>
+ <option value="1"<?php if ($timezone == 1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+01:00'] ?></option>
+ <option value="2"<?php if ($timezone == 2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+02:00'] ?></option>
+ <option value="3"<?php if ($timezone == 3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:00'] ?></option>
+ <option value="3.5"<?php if ($timezone == 3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:30'] ?></option>
+ <option value="4"<?php if ($timezone == 4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:00'] ?></option>
+ <option value="4.5"<?php if ($timezone == 4.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:30'] ?></option>
+ <option value="5"<?php if ($timezone == 5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:00'] ?></option>
+ <option value="5.5"<?php if ($timezone == 5.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:30'] ?></option>
+ <option value="5.75"<?php if ($timezone == 5.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:45'] ?></option>
+ <option value="6"<?php if ($timezone == 6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:00'] ?></option>
+ <option value="6.5"<?php if ($timezone == 6.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:30'] ?></option>
+ <option value="7"<?php if ($timezone == 7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+07:00'] ?></option>
+ <option value="8"<?php if ($timezone == 8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:00'] ?></option>
+ <option value="8.75"<?php if ($timezone == 8.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:45'] ?></option>
+ <option value="9"<?php if ($timezone == 9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:00'] ?></option>
+ <option value="9.5"<?php if ($timezone == 9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:30'] ?></option>
+ <option value="10"<?php if ($timezone == 10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:00'] ?></option>
+ <option value="10.5"<?php if ($timezone == 10.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:30'] ?></option>
+ <option value="11"<?php if ($timezone == 11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:00'] ?></option>
+ <option value="11.5"<?php if ($timezone == 11.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:30'] ?></option>
+ <option value="12"<?php if ($timezone == 12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:00'] ?></option>
+ <option value="12.75"<?php if ($timezone == 12.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:45'] ?></option>
+ <option value="13"<?php if ($timezone == 13) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+13:00'] ?></option>
+ <option value="14"<?php if ($timezone == 14) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+14:00'] ?></option>
+ </select>
+ <br /></label>
+ <div class="rbox">
+ <label><input type="checkbox" name="dst" value="1"<?php if ($dst == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['DST'] ?><br /></label>
+ </div>
+<?php
+
+ $languages = forum_list_langs();
+
+ // Only display the language selection box if there's more than one language available
+ if (count($languages) > 1)
+ {
+
+?>
+ <label><?php echo $lang_prof_reg['Language'] ?>
+ <br /><select name="language">
+<?php
+
+ foreach ($languages as $temp)
+ {
+ if ($pun_config['o_default_lang'] == $temp)
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+ }
+
+?>
+ </select>
+ <br /></label>
+<?php
+
+ }
+?>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_prof_reg['Privacy options legend'] ?></legend>
+ <div class="infldset">
+ <p><?php echo $lang_prof_reg['Email setting info'] ?></p>
+ <div class="rbox">
+ <label><input type="radio" name="email_setting" value="0"<?php if ($email_setting == '0') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 1'] ?><br /></label>
+ <label><input type="radio" name="email_setting" value="1"<?php if ($email_setting == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 2'] ?><br /></label>
+ <label><input type="radio" name="email_setting" value="2"<?php if ($email_setting == '2') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 3'] ?><br /></label>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+<?php flux_hook('register_before_submit'); ?>
+ <p class="buttons"><input type="submit" name="register" value="<?php echo $lang_register['Register'] ?>" /></p>
+ </form>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 0000000..eac8f0d
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,3 @@
+User-agent: *
+Allow: /
+Disallow: /search.php
diff --git a/search.php b/search.php
new file mode 100644
index 0000000..c6ff39a
--- /dev/null
+++ b/search.php
@@ -0,0 +1,914 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The contents of this file are very much inspired by the file search.php
+// from the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+// Load the search.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/search.php';
+require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+else if ($pun_user['g_search'] == '0')
+ message($lang_search['No search permission'], false, '403 Forbidden');
+
+require PUN_ROOT.'include/search_idx.php';
+
+// Figure out what to do :-)
+if (isset($_GET['action']) || isset($_GET['search_id']))
+{
+ $action = (isset($_GET['action'])) ? $_GET['action'] : null;
+ $forums = isset($_GET['forums']) ? (is_array($_GET['forums']) ? $_GET['forums'] : array_filter(explode(',', $_GET['forums']))) : (isset($_GET['forum']) ? array($_GET['forum']) : array());
+ $sort_dir = (isset($_GET['sort_dir']) && $_GET['sort_dir'] == 'DESC') ? 'DESC' : 'ASC';
+
+ $forums = array_map('intval', $forums);
+
+ // Allow the old action names for backwards compatibility reasons
+ if ($action == 'show_user')
+ $action = 'show_user_posts';
+ else if ($action == 'show_24h')
+ $action = 'show_recent';
+
+ // If a search_id was supplied
+ if (isset($_GET['search_id']))
+ {
+ $search_id = intval($_GET['search_id']);
+ if ($search_id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+ }
+ // If it's a regular search (keywords and/or author)
+ else if ($action == 'search')
+ {
+ $keywords = (isset($_GET['keywords'])) ? utf8_strtolower(pun_trim($_GET['keywords'])) : null;
+ $author = (isset($_GET['author'])) ? utf8_strtolower(pun_trim($_GET['author'])) : null;
+
+ if (preg_match('%^[\*\%]+$%', $keywords) || (pun_strlen(str_replace(array('*', '%'), '', $keywords)) < PUN_SEARCH_MIN_WORD && !is_cjk($keywords)))
+ $keywords = '';
+
+ if (preg_match('%^[\*\%]+$%', $author) || pun_strlen(str_replace(array('*', '%'), '', $author)) < 2)
+ $author = '';
+
+ if (!$keywords && !$author)
+ message($lang_search['No terms']);
+
+ if ($author)
+ $author = str_replace(array('*', '_'), array('%', '\\_'), $author);
+
+ $show_as = (isset($_GET['show_as']) && $_GET['show_as'] == 'topics') ? 'topics' : 'posts';
+ $sort_by = (isset($_GET['sort_by'])) ? intval($_GET['sort_by']) : 0;
+ $search_in = (!isset($_GET['search_in']) || $_GET['search_in'] == '0') ? 0 : (($_GET['search_in'] == '1') ? 1 : -1);
+ }
+ // If it's a user search (by ID)
+ else if ($action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions')
+ {
+ $user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : $pun_user['id'];
+ if ($user_id < 2)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ // Subscribed topics can only be viewed by admins, moderators and the users themselves
+ if ($action == 'show_subscriptions' && !$pun_user['is_admmod'] && $user_id != $pun_user['id'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+ }
+ else if ($action == 'show_recent')
+ $interval = isset($_GET['value']) ? intval($_GET['value']) : 86400;
+ else if ($action == 'show_replies')
+ {
+ if ($pun_user['is_guest'])
+ message($lang_common['Bad request'], false, '404 Not Found');
+ }
+ else if ($action != 'show_new' && $action != 'show_unanswered')
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+
+ // If a valid search_id was supplied we attempt to fetch the search results from the db
+ if (isset($search_id))
+ {
+ $ident = ($pun_user['is_guest']) ? get_remote_address() : $pun_user['username'];
+
+ $result = $db->query('SELECT search_data FROM '.$db->prefix.'search_cache WHERE id='.$search_id.' AND ident=\''.$db->escape($ident).'\'') or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+ if ($row = $db->fetch_assoc($result))
+ {
+ $temp = unserialize($row['search_data']);
+
+ $search_ids = unserialize($temp['search_ids']);
+ $num_hits = $temp['num_hits'];
+ $sort_by = $temp['sort_by'];
+ $sort_dir = $temp['sort_dir'];
+ $show_as = $temp['show_as'];
+ $search_type = $temp['search_type'];
+
+ unset($temp);
+ }
+ else
+ message($lang_search['No hits']);
+ }
+ else
+ {
+ $keyword_results = $author_results = array();
+
+ // Search a specific forum?
+ $forum_sql = (!empty($forums) || (empty($forums) && $pun_config['o_search_all_forums'] == '0' && !$pun_user['is_admmod'])) ? ' AND t.forum_id IN ('.implode(',', $forums).')' : '';
+
+ if (!empty($author) || !empty($keywords))
+ {
+ // Flood protection
+ if ($pun_user['last_search'] && (time() - $pun_user['last_search']) < $pun_user['g_search_flood'] && (time() - $pun_user['last_search']) >= 0)
+ message(sprintf($lang_search['Search flood'], $pun_user['g_search_flood'], $pun_user['g_search_flood'] - (time() - $pun_user['last_search'])));
+
+ if (!$pun_user['is_guest'])
+ $db->query('UPDATE '.$db->prefix.'users SET last_search='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+ else
+ $db->query('UPDATE '.$db->prefix.'online SET last_search='.time().' WHERE ident=\''.$db->escape(get_remote_address()).'\'' ) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+ switch ($sort_by)
+ {
+ case 1:
+ $sort_by_sql = ($show_as == 'topics') ? 't.poster' : 'p.poster';
+ $sort_type = SORT_STRING;
+ break;
+
+ case 2:
+ $sort_by_sql = 't.subject';
+ $sort_type = SORT_STRING;
+ break;
+
+ case 3:
+ $sort_by_sql = 't.forum_id';
+ $sort_type = SORT_NUMERIC;
+ break;
+
+ case 4:
+ $sort_by_sql = 't.last_post';
+ $sort_type = SORT_NUMERIC;
+ break;
+
+ default:
+ $sort_by_sql = ($show_as == 'topics') ? 't.last_post' : 'p.posted';
+ $sort_type = SORT_NUMERIC;
+ break;
+ }
+
+ // If it's a search for keywords
+ if ($keywords)
+ {
+ // split the keywords into words
+ $keywords_array = split_words($keywords, true);
+
+ if (empty($keywords_array))
+ message($lang_search['No hits']);
+
+ // Should we search in message body or topic subject specifically?
+ $search_in_cond = ($search_in) ? (($search_in > 0) ? ' AND m.subject_match = 0' : ' AND m.subject_match = 1') : '';
+
+ $word_count = 0;
+ $match_type = 'and';
+
+ $sort_data = array();
+ foreach ($keywords_array as $cur_word)
+ {
+ switch ($cur_word)
+ {
+ case 'and':
+ case 'or':
+ case 'not':
+ $match_type = $cur_word;
+ break;
+
+ default:
+ {
+ if (is_cjk($cur_word))
+ {
+ $where_cond = str_replace('*', '%', $cur_word);
+ $where_cond = ($search_in ? (($search_in > 0) ? 'p.message LIKE \'%'.$db->escape($where_cond).'%\'' : 't.subject LIKE \'%'.$db->escape($where_cond).'%\'') : 'p.message LIKE \'%'.$db->escape($where_cond).'%\' OR t.subject LIKE \'%'.$db->escape($where_cond).'%\'');
+
+ $result = $db->query('SELECT p.id AS post_id, p.topic_id, '.$sort_by_sql.' AS sort_by FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE ('.$where_cond.') AND (fp.read_forum IS NULL OR fp.read_forum=1)'.$forum_sql, true) or error('Unable to search for posts', __FILE__, __LINE__, $db->error());
+ }
+ else
+ $result = $db->query('SELECT m.post_id, p.topic_id, '.$sort_by_sql.' AS sort_by FROM '.$db->prefix.'search_words AS w INNER JOIN '.$db->prefix.'search_matches AS m ON m.word_id = w.id INNER JOIN '.$db->prefix.'posts AS p ON p.id=m.post_id INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE w.word LIKE \''.$db->escape(str_replace('*', '%', $cur_word)).'\''.$search_in_cond.' AND (fp.read_forum IS NULL OR fp.read_forum=1)'.$forum_sql, true) or error('Unable to search for posts', __FILE__, __LINE__, $db->error());
+
+ $row = array();
+ while ($temp = $db->fetch_assoc($result))
+ {
+ $row[$temp['post_id']] = $temp['topic_id'];
+
+ if (!$word_count)
+ {
+ $keyword_results[$temp['post_id']] = $temp['topic_id'];
+ $sort_data[$temp['post_id']] = $temp['sort_by'];
+ }
+ else if ($match_type == 'or')
+ {
+ $keyword_results[$temp['post_id']] = $temp['topic_id'];
+ $sort_data[$temp['post_id']] = $temp['sort_by'];
+ }
+ else if ($match_type == 'not')
+ {
+ unset($keyword_results[$temp['post_id']]);
+ unset($sort_data[$temp['post_id']]);
+ }
+ }
+
+ if ($match_type == 'and' && $word_count)
+ {
+ foreach ($keyword_results as $post_id => $topic_id)
+ {
+ if (!isset($row[$post_id]))
+ {
+ unset($keyword_results[$post_id]);
+ unset($sort_data[$post_id]);
+ }
+ }
+ }
+
+ ++$word_count;
+ $db->free_result($result);
+
+ break;
+ }
+ }
+ }
+
+ // Sort the results - annoyingly array_multisort re-indexes arrays with numeric keys, so we need to split the keys out into a separate array then combine them again after
+ $post_ids = array_keys($keyword_results);
+ $topic_ids = array_values($keyword_results);
+
+ array_multisort(array_values($sort_data), $sort_dir == 'DESC' ? SORT_DESC : SORT_ASC, $sort_type, $post_ids, $topic_ids);
+
+ // combine the arrays back into a key=>value array (array_combine is PHP5 only unfortunately)
+ $num_results = count($keyword_results);
+ $keyword_results = array();
+ for ($i = 0;$i < $num_results;$i++)
+ $keyword_results[$post_ids[$i]] = $topic_ids[$i];
+
+ unset($sort_data, $post_ids, $topic_ids);
+ }
+
+ // If it's a search for author name (and that author name isn't Guest)
+ if ($author && $author != 'guest' && $author != utf8_strtolower($lang_common['Guest']))
+ {
+ switch ($db_type)
+ {
+ case 'pgsql':
+ $result = $db->query('SELECT id FROM '.$db->prefix.'users WHERE username ILIKE \''.$db->escape($author).'\'') or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
+ break;
+
+ default:
+ $result = $db->query('SELECT id FROM '.$db->prefix.'users WHERE username LIKE \''.$db->escape($author).'\'') or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
+ break;
+ }
+
+ if ($db->num_rows($result))
+ {
+ $user_ids = array();
+ while ($row = $db->fetch_row($result))
+ $user_ids[] = $row[0];
+
+ $result = $db->query('SELECT p.id AS post_id, p.topic_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id IN('.implode(',', $user_ids).')'.$forum_sql.' ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch matched posts list', __FILE__, __LINE__, $db->error());
+ while ($temp = $db->fetch_assoc($result))
+ $author_results[$temp['post_id']] = $temp['topic_id'];
+
+ $db->free_result($result);
+ }
+ }
+
+ // If we searched for both keywords and author name we want the intersection between the results
+ if ($author && $keywords)
+ {
+ $search_ids = array_intersect_assoc($keyword_results, $author_results);
+ $search_type = array('both', array($keywords, pun_trim($_GET['author'])), implode(',', $forums), $search_in);
+ }
+ else if ($keywords)
+ {
+ $search_ids = $keyword_results;
+ $search_type = array('keywords', $keywords, implode(',', $forums), $search_in);
+ }
+ else
+ {
+ $search_ids = $author_results;
+ $search_type = array('author', pun_trim($_GET['author']), implode(',', $forums), $search_in);
+ }
+
+ unset($keyword_results, $author_results);
+
+ if ($show_as == 'topics')
+ $search_ids = array_values($search_ids);
+ else
+ $search_ids = array_keys($search_ids);
+
+ $search_ids = array_unique($search_ids);
+
+ $num_hits = count($search_ids);
+ if (!$num_hits)
+ message($lang_search['No hits']);
+ }
+ else if ($action == 'show_new' || $action == 'show_recent' || $action == 'show_replies' || $action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions' || $action == 'show_unanswered')
+ {
+ $search_type = array('action', $action);
+ $show_as = 'topics';
+ // We want to sort things after last post
+ $sort_by = 0;
+ $sort_dir = 'DESC';
+
+ // If it's a search for new posts since last visit
+ if ($action == 'show_new')
+ {
+ if ($pun_user['is_guest'])
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+ $result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.$pun_user['last_visit'].' AND t.moved_to IS NULL'.(isset($_GET['fid']) ? ' AND t.forum_id='.intval($_GET['fid']) : '').' ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No new posts']);
+ }
+ // If it's a search for recent posts (in a certain time interval)
+ else if ($action == 'show_recent')
+ {
+ $result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.(time() - $interval).' AND t.moved_to IS NULL'.(isset($_GET['fid']) ? ' AND t.forum_id='.intval($_GET['fid']) : '').' ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No recent posts']);
+ }
+ // If it's a search for topics in which the user has posted
+ else if ($action == 'show_replies')
+ {
+ $result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$pun_user['id'].' GROUP BY t.id'.($db_type == 'pgsql' ? ', t.last_post' : '').' ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No user posts']);
+ }
+ // If it's a search for posts by a specific user ID
+ else if ($action == 'show_user_posts')
+ {
+ $show_as = 'posts';
+
+ $result = $db->query('SELECT p.id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON p.topic_id=t.id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$user_id.' ORDER BY p.posted DESC') or error('Unable to fetch user posts', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No user posts']);
+
+ // Pass on the user ID so that we can later know whose posts we're searching for
+ $search_type[2] = $user_id;
+ }
+ // If it's a search for topics by a specific user ID
+ else if ($action == 'show_user_topics')
+ {
+ $result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.first_post_id=p.id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$user_id.' ORDER BY t.last_post DESC') or error('Unable to fetch user topics', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No user topics']);
+
+ // Pass on the user ID so that we can later know whose topics we're searching for
+ $search_type[2] = $user_id;
+ }
+ // If it's a search for subscribed topics
+ else if ($action == 'show_subscriptions')
+ {
+ if ($pun_user['is_guest'])
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$user_id.') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No subscriptions']);
+
+ // Pass on user ID so that we can later know whose subscriptions we're searching for
+ $search_type[2] = $user_id;
+ }
+ // If it's a search for unanswered posts
+ else
+ {
+ $result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.num_replies=0 AND t.moved_to IS NULL ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+ $num_hits = $db->num_rows($result);
+
+ if (!$num_hits)
+ message($lang_search['No unanswered']);
+ }
+
+ $search_ids = array();
+ while ($row = $db->fetch_row($result))
+ $search_ids[] = $row[0];
+
+ $db->free_result($result);
+ }
+ else
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+
+ // Prune "old" search results
+ $old_searches = array();
+ $result = $db->query('SELECT ident FROM '.$db->prefix.'online') or error('Unable to fetch online list', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ {
+ while ($row = $db->fetch_row($result))
+ $old_searches[] = '\''.$db->escape($row[0]).'\'';
+
+ $db->query('DELETE FROM '.$db->prefix.'search_cache WHERE ident NOT IN('.implode(',', $old_searches).')') or error('Unable to delete search results', __FILE__, __LINE__, $db->error());
+ }
+
+ // Fill an array with our results and search properties
+ $temp = serialize(array(
+ 'search_ids' => serialize($search_ids),
+ 'num_hits' => $num_hits,
+ 'sort_by' => $sort_by,
+ 'sort_dir' => $sort_dir,
+ 'show_as' => $show_as,
+ 'search_type' => $search_type
+ ));
+ $search_id = mt_rand(1, 2147483647);
+
+ $ident = ($pun_user['is_guest']) ? get_remote_address() : $pun_user['username'];
+
+ $db->query('INSERT INTO '.$db->prefix.'search_cache (id, ident, search_data) VALUES('.$search_id.', \''.$db->escape($ident).'\', \''.$db->escape($temp).'\')') or error('Unable to insert search results', __FILE__, __LINE__, $db->error());
+
+ if ($search_type[0] != 'action')
+ {
+ $db->end_transaction();
+ $db->close();
+
+ // Redirect the user to the cached result page
+ header('Location: search.php?search_id='.$search_id);
+ exit;
+ }
+ }
+
+ $forum_actions = array();
+
+ // If we're on the new posts search, display a "mark all as read" link
+ if (!$pun_user['is_guest'] && $search_type[0] == 'action' && $search_type[1] == 'show_new')
+ $forum_actions[] = '<a href="misc.php?action=markread&amp;csrf_token='.pun_csrf_token().'">'.$lang_common['Mark all as read'].'</a>';
+
+ // Fetch results to display
+ if (!empty($search_ids))
+ {
+ switch ($sort_by)
+ {
+ case 1:
+ $sort_by_sql = ($show_as == 'topics') ? 't.poster' : 'p.poster';
+ break;
+
+ case 2:
+ $sort_by_sql = 't.subject';
+ break;
+
+ case 3:
+ $sort_by_sql = 't.forum_id';
+ break;
+
+ default:
+ $sort_by_sql = ($show_as == 'topics') ? 't.last_post' : 'p.posted';
+ break;
+ }
+
+ // Determine the topic or post offset (based on $_GET['p'])
+ $per_page = ($show_as == 'posts') ? $pun_user['disp_posts'] : $pun_user['disp_topics'];
+ $num_pages = ceil($num_hits / $per_page);
+
+ $p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+ $start_from = $per_page * ($p - 1);
+
+ // Generate paging links
+ $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'search.php?search_id='.$search_id);
+
+ // throw away the first $start_from of $search_ids, only keep the top $per_page of $search_ids
+ $search_ids = array_slice($search_ids, $start_from, $per_page);
+
+ // Run the query and fetch the results
+ if ($show_as == 'posts')
+ $result = $db->query('SELECT p.id AS pid, p.poster AS pposter, p.posted AS pposted, p.poster_id, p.message, p.hide_smilies, t.id AS tid, t.poster, t.subject, t.first_post_id, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.forum_id, f.forum_name FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+ else
+ $result = $db->query('SELECT t.id AS tid, t.poster, t.subject, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.closed, t.sticky, t.forum_id, f.forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE t.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+
+ $search_set = array();
+ while ($row = $db->fetch_assoc($result))
+ $search_set[] = $row;
+
+ $crumbs_text = array();
+ $crumbs_text['show_as'] = $lang_search['Search'];
+
+ if ($search_type[0] == 'action')
+ {
+ if ($search_type[1] == 'show_user_topics')
+ $crumbs_text['search_type'] = '<a href="search.php?action=show_user_topics&amp;user_id='.$search_type[2].'">'.sprintf($lang_search['Quick search show_user_topics'], pun_htmlspecialchars($search_set[0]['poster'])).'</a>';
+ else if ($search_type[1] == 'show_user_posts')
+ $crumbs_text['search_type'] = '<a href="search.php?action=show_user_posts&amp;user_id='.$search_type[2].'">'.sprintf($lang_search['Quick search show_user_posts'], pun_htmlspecialchars($search_set[0]['pposter'])).'</a>';
+ else if ($search_type[1] == 'show_subscriptions')
+ {
+ // Fetch username of subscriber
+ $subscriber_id = $search_type[2];
+ $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE id='.$subscriber_id) or error('Unable to fetch username of subscriber', __FILE__, __LINE__, $db->error());
+
+ if ($db->num_rows($result))
+ $subscriber_name = $db->result($result);
+ else
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ $crumbs_text['search_type'] = '<a href="search.php?action=show_subscriptions&amp;user_id='.$subscriber_id.'">'.sprintf($lang_search['Quick search show_subscriptions'], pun_htmlspecialchars($subscriber_name)).'</a>';
+ }
+ else
+ $crumbs_text['search_type'] = '<a href="search.php?action='.$search_type[1].'">'.$lang_search['Quick search '.$search_type[1]].'</a>';
+ }
+ else
+ {
+ $keywords = $author = '';
+
+ if ($search_type[0] == 'both')
+ {
+ list ($keywords, $author) = $search_type[1];
+ $crumbs_text['search_type'] = sprintf($lang_search['By both show as '.$show_as], pun_htmlspecialchars($keywords), pun_htmlspecialchars($author));
+ }
+ else if ($search_type[0] == 'keywords')
+ {
+ $keywords = $search_type[1];
+ $crumbs_text['search_type'] = sprintf($lang_search['By keywords show as '.$show_as], pun_htmlspecialchars($keywords));
+ }
+ else if ($search_type[0] == 'author')
+ {
+ $author = $search_type[1];
+ $crumbs_text['search_type'] = sprintf($lang_search['By user show as '.$show_as], pun_htmlspecialchars($author));
+ }
+
+ $crumbs_text['search_type'] = '<a href="search.php?action=search&amp;keywords='.urlencode($keywords).'&amp;author='.urlencode($author).'&amp;forums='.$search_type[2].'&amp;search_in='.$search_type[3].'&amp;sort_by='.$sort_by.'&amp;sort_dir='.$sort_dir.'&amp;show_as='.$show_as.'">'.$crumbs_text['search_type'].'</a>';
+ }
+
+ $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_search['Search results']);
+ define('PUN_ACTIVE_PAGE', 'search');
+ require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="search.php"><?php echo $crumbs_text['show_as'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $crumbs_text['search_type'] ?></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<?php
+
+ if ($show_as == 'topics')
+ {
+ $topic_count = 0;
+
+?>
+<div id="vf" class="blocktable">
+ <h2><span><?php echo $lang_search['Search results'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_common['Forum'] ?></th>
+ <th class="tc3" scope="col"><?php echo $lang_common['Replies'] ?></th>
+ <th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+ }
+ else if ($show_as == 'posts')
+ {
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
+
+ require PUN_ROOT.'include/parser.php';
+
+ $post_count = 0;
+ }
+
+ // Get topic/forum tracking data
+ if (!$pun_user['is_guest'])
+ $tracked_topics = get_tracked_topics();
+
+ foreach ($search_set as $cur_search)
+ {
+ $forum = '<a href="viewforum.php?id='.$cur_search['forum_id'].'">'.pun_htmlspecialchars($cur_search['forum_name']).'</a>';
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_search['subject'] = censor_words($cur_search['subject']);
+
+ if ($show_as == 'posts')
+ {
+ ++$post_count;
+ $icon_type = 'icon';
+
+ if (!$pun_user['is_guest'] && $cur_search['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_search['tid']]) || $tracked_topics['topics'][$cur_search['tid']] < $cur_search['last_post']) && (!isset($tracked_topics['forums'][$cur_search['forum_id']]) || $tracked_topics['forums'][$cur_search['forum_id']] < $cur_search['last_post']))
+ {
+ $item_status = 'inew';
+ $icon_type = 'icon icon-new';
+ $icon_text = $lang_topic['New icon'];
+ }
+ else
+ {
+ $item_status = '';
+ $icon_text = '<!-- -->';
+ }
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_search['message'] = censor_words($cur_search['message']);
+
+ $message = parse_message($cur_search['message'], $cur_search['hide_smilies']);
+ $pposter = pun_htmlspecialchars($cur_search['pposter']);
+
+ if ($cur_search['poster_id'] > 1)
+ {
+ if ($pun_user['g_view_users'] == '1')
+ $pposter = '<strong><a href="profile.php?id='.$cur_search['poster_id'].'">'.$pposter.'</a></strong>';
+ else
+ $pposter = '<strong>'.$pposter.'</strong>';
+ }
+
+
+?>
+<div class="blockpost<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?><?php if ($cur_search['pid'] == $cur_search['first_post_id']) echo ' firstpost' ?><?php if ($post_count == 1) echo ' blockpost1' ?><?php if ($item_status != '') echo ' '.$item_status ?>">
+ <h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?></span> <span><?php if ($cur_search['pid'] != $cur_search['first_post_id']) echo $lang_topic['Re'].' ' ?><?php echo $forum ?></span> <span>»&#160;<a href="viewtopic.php?id=<?php echo $cur_search['tid'] ?>"><?php echo pun_htmlspecialchars($cur_search['subject']) ?></a></span> <span>»&#160;<a href="viewtopic.php?pid=<?php echo $cur_search['pid'].'#p'.$cur_search['pid'] ?>"><?php echo format_time($cur_search['pposted']) ?></a></span></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postleft">
+ <dl>
+ <dt><?php echo $pposter ?></dt>
+<?php if ($cur_search['pid'] == $cur_search['first_post_id']) : ?> <dd><span><?php echo $lang_topic['Replies'].' '.forum_number_format($cur_search['num_replies']) ?></span></dd>
+<?php endif; ?>
+ <dd><div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo $icon_text ?></div></div></dd>
+ </dl>
+ </div>
+ <div class="postright">
+ <div class="postmsg">
+ <?php echo $message."\n" ?>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ </div>
+ <div class="inbox">
+ <div class="postfoot clearb">
+ <div class="postfootright">
+ <ul>
+ <li><span><a href="viewtopic.php?id=<?php echo $cur_search['tid'] ?>"><?php echo $lang_search['Go to topic'] ?></a></span></li>
+ <li><span><a href="viewtopic.php?pid=<?php echo $cur_search['pid'].'#p'.$cur_search['pid'] ?>"><?php echo $lang_search['Go to post'] ?></a></span></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<?php
+
+ }
+ else
+ {
+ ++$topic_count;
+ $status_text = array();
+ $item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
+ $icon_type = 'icon';
+
+ $subject = '<a href="viewtopic.php?id='.$cur_search['tid'].'">'.pun_htmlspecialchars($cur_search['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_search['poster']).'</span>';
+
+ if ($cur_search['sticky'] == '1')
+ {
+ $item_status .= ' isticky';
+ $status_text[] = '<span class="stickytext">'.$lang_forum['Sticky'].'</span>';
+ }
+
+ if ($cur_search['closed'] != '0')
+ {
+ $status_text[] = '<span class="closedtext">'.$lang_forum['Closed'].'</span>';
+ $item_status .= ' iclosed';
+ }
+
+ if (!$pun_user['is_guest'] && $cur_search['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_search['tid']]) || $tracked_topics['topics'][$cur_search['tid']] < $cur_search['last_post']) && (!isset($tracked_topics['forums'][$cur_search['forum_id']]) || $tracked_topics['forums'][$cur_search['forum_id']] < $cur_search['last_post']))
+ {
+ $item_status .= ' inew';
+ $icon_type = 'icon icon-new';
+ $subject = '<strong>'.$subject.'</strong>';
+ $subject_new_posts = '<span class="newtext">[ <a href="viewtopic.php?id='.$cur_search['tid'].'&amp;action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a> ]</span>';
+ }
+ else
+ $subject_new_posts = null;
+
+ // Insert the status text before the subject
+ $subject = implode(' ', $status_text).' '.$subject;
+
+ $num_pages_topic = ceil(($cur_search['num_replies'] + 1) / $pun_user['disp_posts']);
+
+ if ($num_pages_topic > 1)
+ $subject_multipage = '<span class="pagestext">[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_search['tid']).' ]</span>';
+ else
+ $subject_multipage = null;
+
+ // Should we show the "New posts" and/or the multipage links?
+ if (!empty($subject_new_posts) || !empty($subject_multipage))
+ {
+ $subject .= !empty($subject_new_posts) ? ' '.$subject_new_posts : '';
+ $subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
+ }
+
+?>
+ <tr class="<?php echo $item_status ?>">
+ <td class="tcl">
+ <div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($topic_count + $start_from) ?></div></div>
+ <div class="tclcon">
+ <div>
+ <?php echo $subject."\n" ?>
+ </div>
+ </div>
+ </td>
+ <td class="tc2"><?php echo $forum ?></td>
+ <td class="tc3"><?php echo forum_number_format($cur_search['num_replies']) ?></td>
+ <td class="tcr"><?php echo '<a href="viewtopic.php?pid='.$cur_search['last_post_id'].'#p'.$cur_search['last_post_id'].'">'.format_time($cur_search['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_search['last_poster']) ?></span></td>
+ </tr>
+<?php
+
+ }
+ }
+
+ if ($show_as == 'topics')
+ echo "\t\t\t".'</tbody>'."\n\t\t\t".'</table>'."\n\t\t".'</div>'."\n\t".'</div>'."\n".'</div>'."\n\n";
+
+?>
+<div class="<?php echo ($show_as == 'topics') ? 'linksb' : 'postlinksb'; ?>">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ </div>
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="search.php"><?php echo $crumbs_text['show_as'] ?></a></li>
+ <li><span>»&#160;</span><strong><?php echo $crumbs_text['search_type'] ?></strong></li>
+ </ul>
+<?php echo (!empty($forum_actions) ? "\t\t".'<p class="subscribelink clearb">'.implode(' - ', $forum_actions).'</p>'."\n" : '') ?>
+ <div class="clearer"></div>
+ </div>
+</div>
+<?php
+
+ require PUN_ROOT.'footer.php';
+ }
+ else
+ message($lang_search['No hits']);
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_search['Search']);
+$focus_element = array('search', 'keywords');
+define('PUN_ACTIVE_PAGE', 'search');
+require PUN_ROOT.'header.php';
+
+?>
+<div id="searchform" class="blockform">
+ <h2><span><?php echo $lang_search['Search'] ?></span></h2>
+ <div class="box">
+ <form id="search" method="get" action="search.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_search['Search criteria legend'] ?></legend>
+ <div class="infldset">
+ <input type="hidden" name="action" value="search" />
+ <label class="conl"><?php echo $lang_search['Keyword search'] ?><br /><input type="text" name="keywords" size="40" maxlength="100" /><br /></label>
+ <label class="conl"><?php echo $lang_search['Author search'] ?><br /><input id="author" type="text" name="author" size="25" maxlength="25" /><br /></label>
+ <p class="clearb"><?php echo $lang_search['Search info'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_search['Search in legend'] ?></legend>
+ <div class="infldset">
+<?php
+
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+// We either show a list of forums of which multiple can be selected
+if ($pun_config['o_search_all_forums'] == '1' || $pun_user['is_admmod'])
+{
+ echo "\t\t\t\t\t\t".'<div class="conl multiselect">'.$lang_search['Forum search']."\n";
+ echo "\t\t\t\t\t\t".'<br />'."\n";
+ echo "\t\t\t\t\t\t".'<div class="checklist">'."\n";
+
+ $cur_category = 0;
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category)
+ {
+ echo "\t\t\t\t\t\t\t\t".'</div>'."\n";
+ echo "\t\t\t\t\t\t\t".'</fieldset>'."\n";
+ }
+
+ echo "\t\t\t\t\t\t\t".'<fieldset><legend><span>'.pun_htmlspecialchars($cur_forum['cat_name']).'</span></legend>'."\n";
+ echo "\t\t\t\t\t\t\t\t".'<div class="rbox">';
+ $cur_category = $cur_forum['cid'];
+ }
+
+ echo "\t\t\t\t\t\t\t\t".'<label><input type="checkbox" name="forums[]" id="forum-'.$cur_forum['fid'].'" value="'.$cur_forum['fid'].'" />'.pun_htmlspecialchars($cur_forum['forum_name']).'</label>'."\n";
+ }
+
+ if ($cur_category)
+ {
+ echo "\t\t\t\t\t\t\t\t".'</div>'."\n";
+ echo "\t\t\t\t\t\t\t".'</fieldset>'."\n";
+ }
+
+ echo "\t\t\t\t\t\t".'</div>'."\n";
+ echo "\t\t\t\t\t\t".'</div>'."\n";
+}
+// ... or a simple select list for one forum only
+else
+{
+ echo "\t\t\t\t\t\t".'<label class="conl">'.$lang_search['Forum search']."\n";
+ echo "\t\t\t\t\t\t".'<br />'."\n";
+ echo "\t\t\t\t\t\t".'<select id="forum" name="forum">'."\n";
+
+ $cur_category = 0;
+ while ($cur_forum = $db->fetch_assoc($result))
+ {
+ if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+ {
+ if ($cur_category)
+ echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+ echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+ $cur_category = $cur_forum['cid'];
+ }
+
+ echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
+ }
+
+ echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+ echo "\t\t\t\t\t\t".'</select>'."\n";
+ echo "\t\t\t\t\t\t".'<br /></label>'."\n";
+}
+
+?>
+ <label class="conl"><?php echo $lang_search['Search in']."\n" ?>
+ <br /><select id="search_in" name="search_in">
+ <option value="0"><?php echo $lang_search['Message and subject'] ?></option>
+ <option value="1"><?php echo $lang_search['Message only'] ?></option>
+ <option value="-1"><?php echo $lang_search['Topic only'] ?></option>
+ </select>
+ <br /></label>
+ <p class="clearl"><?php echo $lang_search['Search in info'] ?></p>
+<?php echo ($pun_config['o_search_all_forums'] == '1' || $pun_user['is_admmod'] ? '<p>'.$lang_search['Search multiple forums info'].'</p>' : '') ?>
+ </div>
+ </fieldset>
+ </div>
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_search['Search results legend'] ?></legend>
+ <div class="infldset">
+ <label class="conl"><?php echo $lang_search['Sort by']."\n" ?>
+ <br /><select name="sort_by">
+ <option value="0"><?php echo $lang_search['Sort by post time'] ?></option>
+ <option value="1"><?php echo $lang_search['Sort by author'] ?></option>
+ <option value="2"><?php echo $lang_search['Sort by subject'] ?></option>
+ <option value="3"><?php echo $lang_search['Sort by forum'] ?></option>
+ </select>
+ <br /></label>
+ <label class="conl"><?php echo $lang_search['Sort order']."\n" ?>
+ <br /><select name="sort_dir">
+ <option value="DESC"><?php echo $lang_search['Descending'] ?></option>
+ <option value="ASC"><?php echo $lang_search['Ascending'] ?></option>
+ </select>
+ <br /></label>
+ <label class="conl"><?php echo $lang_search['Show as']."\n" ?>
+ <br /><select name="show_as">
+ <option value="topics"><?php echo $lang_search['Show as topics'] ?></option>
+ <option value="posts"><?php echo $lang_search['Show as posts'] ?></option>
+ </select>
+ <br /></label>
+ <p class="clearb"><?php echo $lang_search['Search results info'] ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="search" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
+ </form>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/style/Air.css b/style/Air.css
new file mode 100644
index 0000000..d872d44
--- /dev/null
+++ b/style/Air.css
@@ -0,0 +1,1651 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3, .pun h4, .pun h5, .pun pre, .pun blockquote,
+.pun ul, .pun ol, .pun li, .pun dl, .pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun legend .pun img,
+.pun abbr, .pun cite {
+ border: 0;
+ font-style: normal;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+.pun ul, .pun ol {
+ list-style: none;
+}
+
+.pun select {
+ padding-bottom: 1px;
+ padding-top: 1px;
+ padding-right: 1px;
+}
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 81.25%/1.462em Arial, Helvetica, sans-serif;
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun legend {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 1em;
+}
+
+.pun pre, .pun code {
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace;
+ font-size: 1em;
+}
+
+.pun pre code {
+ font-size: 1em;
+}
+
+.pun table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 0;
+ empty-cells: show;
+ width: 100%;
+}
+
+.pun h1 {
+ font:2.154em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+ padding: 7px 0;
+}
+
+.pun h2, .pun .hd h2 {
+ font: 1.462em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+ padding: 7px 0;
+}
+
+.pun h3 {
+ font-size: 1.154em;
+ line-height: 1.267em;
+ padding: 7px 0;
+}
+
+.pun h4 {
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun h5, .pun h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun p, .pun ul, .pun ol, .pun dl, .pun th, .pun td, .pun legend {
+ padding: 7px 0;
+}
+
+.pun strong, .pun th, .pun span.warntext, .pun p.warntext {
+ font-weight: bold;
+}
+
+.pun em {
+ font-style: italic;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+ text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ text-decoration: underline;
+}
+
+.pun .actions span {
+ padding-left: 16px;
+ padding-right: 8px;
+ background: url(Air/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #debug h2, #brdstats .conl dt, #brdstats .conr dt, #modcontrols dt,
+#searchlinks dt, div.postright h3, .pun .subscribelink span, #announce .hd, #reportform h2, #punmoderate #vf h2,
+#punviewforum #vf h2, .pun .required strong span, .pun .icon div {
+ display: block;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+/* Generic Float Clear
+----------------------------------------------------------------*/
+
+.pun .inbox, .pun #brdmain, .pun .crumbs, .pun .pagepost, .pun .block2col {
+ min-height: 1px;
+}
+
+* html .pun .inbox, * html .pun #brdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+ display: inline-block;
+}
+
+* html .pun .inbox, * html .pun #bdrdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+ display: block;
+}
+
+.pun .inbox:after, .pun #brdmain:after, .pun .crumbs:after, .pun .pagepost:after, .pun .block2col:after {
+ content: " ";
+ display: block;
+ height: 0;
+ font-size: 0;
+ clear: both;
+ visibility: hidden;
+}
+
+.pun .block2col .inbox:after {
+ content: none;
+ clear: none;
+}
+
+.clearl {
+ clear: left;
+}
+
+
+/*****************************************************************
+2. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+ max-width: 1070px;
+ margin: 0 auto;
+ padding: 30px 40px;
+}
+
+#punredirect, #punmaint {
+ padding: 60px 20% 12px 20%;
+}
+
+#puninstall, #pundb_update {
+ padding: 20px 10%;
+}
+
+.pun .punwrap {
+ border: 1px solid;
+ border-radius: 10px;
+ padding: 18px;
+}
+
+#punredirect h2, #punmaint h2 {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin-bottom: 3px;
+}
+
+/* Section Spacing and Borders
+----------------------------------------------------------------*/
+
+#brdmain {
+ border-style: solid none;
+ border-width: 2px 0;
+ margin-bottom: 12px;
+ padding: 12px 0;
+}
+
+#punindex #brdmain {
+ padding-top: 24px;
+}
+
+#punredirect #brdmain, #punmaint #brdmain {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+#brdstats {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ margin-top: 24px;
+ padding-top: 12px;
+}
+
+#quickpost {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ margin-top: 12px;
+ padding-top: 12px;
+}
+
+#announce {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ padding-top: 3px;
+}
+
+/*****************************************************************
+3. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Logo, Description and Main Menu
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ padding: 0 0 10px 0;
+}
+
+#brddesc {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ padding: 10px 0;
+}
+
+#brddesc p {
+ padding: 0;
+}
+
+#brdmenu ul {
+ padding: 0;
+}
+
+#brdmenu li {
+ float: left;
+}
+
+#brdmenu a:link, #brdmenu a:visited {
+ border-right-style: solid;
+ border-width: 1px;
+ display: block;
+ min-width: 60px;
+ padding: 12px 16px 6px 8px;
+ white-space: nowrap;
+}
+
+#brdmenu a:hover, #brmenu a:active, #brdmenu a:focus {
+ text-decoration: none;
+}
+
+/* Welcome Box
+----------------------------------------------------------------*/
+
+#brdwelcome {
+ padding: 10px 0;
+}
+
+#brdwelcome .conl, #brdwelcome .conr, #brdwelcome p, #brdwelcome li {
+ display: inline;
+ padding: 0;
+}
+
+#brdwelcome .conl {
+ float: left;
+}
+
+#brdwelcome .conr {
+ float: right;
+}
+
+#brdwelcome li span {
+ background: url(Air/img/bull.png) center left no-repeat;
+ padding-left: 18px;
+ margin-right: 3px;
+ display: inline-block;
+ line-height: normal;
+ white-space: nowrap;
+}
+
+#brdwelcome .conl li:first-child span {
+ padding-left: 0;
+ background: none;
+}
+
+/* Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+}
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+}
+
+#brdstats #onlinelist {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ clear: both;
+}
+
+#brdstats #onlinelist dt, #brdstats #onlinelist dd {
+ display: inline;
+}
+
+/* Footer
+----------------------------------------------------------------*/
+
+.pun #modcontrols {
+ border-style: none none dotted none;
+ border-width: 0 0 1px 0;
+ margin-bottom: 4px;
+ text-align: center;
+ width: 100%;
+}
+
+.pun #modcontrols dd {
+ display: inline;
+}
+
+.pun #brdfooter #modcontrols dd span {
+ background: url(Air/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #brdfooter .conl {
+ float: left;
+}
+
+.pun #brdfooter .conr {
+ text-align: right;
+ float: right;
+}
+
+.pun #brdfooter #poweredby a {
+ font-size: 1.077em;
+ font-weight: bold;
+}
+
+.pun #brdfooter #qjump {
+ padding-top: 5px;
+}
+
+.pun #brdfooter #qjump * {
+ white-space: nowrap;
+}
+
+.pun #brdfooter #searchlinks dd span {
+ background: url(Air/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #brdfooter #feedlinks {
+ padding-bottom: 0;
+}
+
+.pun #brdfooter #feedlinks span {
+ background: url(Air/img/feed.png) center left no-repeat;
+ display: inline-block;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #debugtime {
+ border-style: dotted none none none;
+ border-width: 1px 0 0 0;
+ margin-top: 7px;
+ text-align: center;
+}
+
+/* Breadcrumbs, Postlink, Pagination
+----------------------------------------------------------------*/
+
+.pun .linkst .inbox, .pun .linksb .inbox, .pun .postlinksb .inbox {
+ overflow: hidden;
+}
+
+.pun .linksb, .pun .postlinksb, .pun .linkst, .pun .crumbs {
+ clear: both;
+ position: relative;
+}
+
+.pun .linkst .crumbs {
+ font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+ font-size: 1.462em;
+ line-height: 1.211em;
+ padding: 7px 0;
+}
+
+.pun .linksb .crumbs, .pun .postlinksb .crumbs {
+ font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+ font-size: 1.154em;
+}
+
+.pun .linkst .crumbsplus .pagepost {
+ border-top-style: dotted;
+ border-top-width: 1px;
+}
+
+.pun .linksb .crumbsplus .pagepost, .pun .postlinksb .crumbsplus .pagepost {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+}
+
+.pun .postlinksb .crumbs {
+ margin-right: 11em;
+}
+
+.pun .crumbs li {
+ float: left;
+ padding-right: 0.4em;
+ white-space: nowrap;
+}
+
+.pun .crumbs li strong {
+ font-weight: normal;
+}
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+}
+
+.pun .pagelink strong, .pun .pagelink a, .pun .pagelink span.spacer {
+ border-style: none none none solid;
+ border-width: 0 0 0 1px;
+ display: inline-block;
+ padding: 0 12px 0 10px;
+ margin-right: -6px;
+}
+
+.pun .pagelink .item1 {
+ border: 0;
+}
+
+.pun .pagelink .pages-label {
+ display: inline-block;
+}
+
+.pun .postlink {
+ float: right;
+ font-weight: bold;
+ text-align: right;
+}
+
+.pun .modbuttons {
+ float: right;
+ padding: 5px 0 3px 0;
+}
+
+.pun .modbuttons input {
+ margin-left: 8px;
+}
+
+.pun .subscribelink {
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 33px;
+}
+
+#punindex .subscribelink {
+ top: 0px;
+}
+
+#punindex .linksb {
+ height: 12px;
+}
+
+/*****************************************************************
+4. MAIN TABLES
+*****************************************************************/
+
+.pun #brdmain .blocktable {
+ position: relative;
+}
+
+#punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ margin: 1px 1px 0 1px;
+ padding-left: 8px;
+ position: absolute;
+ left: 0;
+ white-space: nowrap;
+ z-index: 100;
+}
+
+#punindex .blocktable th.tcl, #punsearch #vf th.tcl {
+ font-size: 0;
+ text-indent: -9999em;
+}
+
+.pun .blocktable .box {
+ border-style: solid;
+ border-width: 1px;
+ margin-bottom: -1px;
+ overflow: hidden;
+ position: relative;
+}
+
+* html .pun .blocktable .box {
+ display: inline-block;
+}
+
+.pun .blocktable table {
+ table-layout: fixed;
+ margin-bottom: -1px;
+}
+
+.pun .blocktable th {
+ padding: 7px 8px;
+ border-style: none none solid none;
+ border-width: 1px;
+ text-align: left;
+}
+
+.pun .blocktable td {
+ padding: 7px 8px;
+ line-height: 1.3077em;
+ border-style: none none solid none;
+ border-width: 1px;
+ text-align: left;
+}
+
+.pun .blocktable h3 {
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 0;
+}
+
+.pun .blocktable p {
+ padding: 0;
+}
+
+.pun .blocktable .tcl p {
+ padding: 5px 0 0 0;
+}
+
+.pun .blocktable .tcl {
+ width: auto;
+}
+
+.pun .blocktable .tc2, .pun .blocktable .tc3, .pun .blocktable .tcmod {
+ padding-left: 0;
+ padding-right: 0;
+ text-align: center;
+ width: 11%;
+}
+
+.pun .blocktable .tcr {
+ width: 30%;
+}
+
+.pun .blocktable td .newtext, .pun .blocktable td .pagestext, .pun .blocktable td .byuser {
+ white-space: nowrap;
+}
+
+.pun .blocktable .tcl h3 span.newtext {
+ font-size: 0.929em;
+ font-weight: normal;
+}
+
+.pun #vf td.tcl span.stickytext, .pun #vf td.tcl span.closedtext {
+ font-size: 1em;
+ font-weight: bold;
+}
+
+#punsearch #vf .tc2 {
+ padding-left: 8px;
+ padding-right: 8px;
+ text-align: left;
+ width: 18%;
+}
+
+#users1 .tcr {
+ width: 25%;
+}
+
+#users1 .tc2 {
+ padding-left: 8px;
+ padding-right: 8px;
+ text-align: left;
+ width: 25%;
+}
+
+#debug {
+ margin-top: 12px;
+}
+
+#debug .tcl {
+ width: 10%;
+}
+
+#punredirect #debug .tcl, #punmaint #debug .tcl {
+ width: 20%;
+}
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal;
+}
+
+#punindex .tcr .byuser {
+ display: block;
+}
+
+#punindex td.tc2, #punindex td.tc3, #punindex td.tcr, .pun #vf td.tc2, .pun #vf td.tc3,
+.pun #vf td.tcr, #punindex td.tcl div.forumdesc, .pun #vf td.tcl span {
+ font-size: 0.923em;
+}
+
+.pun #vf td.tcl a {
+ font-weight: bold;
+}
+
+.pun #vf td.tcl span a {
+ font-weight: normal;
+}
+
+.pun .blocktable .tclcon {
+ min-height: 1px;
+ overflow: hidden;
+ padding: 0 11px 0 12px;
+ position: relative;
+}
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+}
+
+.pun .icon {
+ border-style: solid;
+ border-width: 8px;
+ float: left;
+ height: 0;
+ overflow: hidden;
+ width: 0;
+}
+
+.pun .iposted .ipost {
+ font-weight: bold;
+ left: 0;
+ padding-left: 4px;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ width: 8px;
+}
+
+/*****************************************************************
+MAIN POSTS
+*****************************************************************/
+
+/* Structure
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+ border-style: solid;
+ border-width: 1px;
+ margin-bottom: -1px;
+ overflow: hidden;
+ position: relative;
+}
+
+* html .pun .blockpost {
+ display: inline-block;
+}
+
+.pun .blockpost h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ white-space: nowrap;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ height: 1.462em;
+ padding: 0.538em 8px 0.538em 236px;
+ font-weight: normal;
+}
+
+#punsearch .blockpost h2 {
+ height: auto;
+ padding-left: 36px;
+ white-space: normal;
+}
+
+#punsearch .blockpost h2 span span {
+ white-space: nowrap;
+ display: inline-block;
+ font: 1.077em "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+#punsearch .blockpost .icon {
+ position: absolute;
+ top: 0;
+ margin-top: -2.154em;
+}
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+}
+
+.pun .blockpost .inbox {
+ float: right;
+ position: relative;
+ width: 100%;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+ border-left-style: solid;
+ border-left-width: 1px;
+ float: right;
+ margin-right: -218px;
+ position: relative;
+ text-align: left;
+ width: 100%;
+}
+
+.pun .blockpost .postleft, .pun .blockpost .postfootleft {
+ width: 194px;
+ padding: 7px 12px 7px 12px;
+ float: left;
+ margin-left: -218px;
+ position: relative;
+}
+
+.pun .blockpost .postleft dl {
+ padding: 0;
+}
+
+#punviewtopic .blockpost dt, #punmoderate .blockpost dt {
+ display: block;
+ position: absolute;
+ padding: 0.538em 0 0.538em 12px;
+ height: 1.462em;
+ top: -2.615em;
+ left: 0;
+ overflow: hidden;
+ width: 206px;
+}
+
+.pun .blockpost dt {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.pun .blockpost dt strong {
+ font-size: 1.231em;
+ font-weight: bold;
+}
+
+.pun .blockpost .postleft dd {
+ font-size: 0.923em;
+}
+
+.pun .blockpost .postleft .usertitle {
+ padding: 4px 0 6px 0;
+ font-size: 1em;
+}
+
+.pun .blockpost .postleft .postavatar {
+ display: block;
+ margin: 0 0 4px 0;
+}
+
+.pun .blockpost .postright {
+ position: relative;
+ padding: 4px 230px 7px 18px;
+}
+
+.pun .postmsg {
+ width:100%;
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
+.pun .blockpost .postfootright {
+ position: relative;
+ padding: 7px 230px 7px 18px;
+ text-align: right;
+}
+
+.pun .postfoot p, .pun .postfoot ul {
+ padding: 0;
+}
+
+.pun .blockpost .postfootright li {
+ display: inline;
+}
+
+.pun .blockpost .postfootright li span {
+ display: inline-block;
+ padding-left: 16px;
+ margin-left: 8px;
+ line-height: normal;
+ background: url(Air/img/bull.png) center left no-repeat;
+}
+
+.pun .blockpost .usercontacts {
+ padding: 7px 0;
+}
+
+.pun .blockpost .usercontacts .email {
+ background: url(Air/img/email.png) left 65% no-repeat;
+ margin-right: 5px;
+ padding-left: 21px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun .blockpost .usercontacts .website {
+ background: url(Air/img/ext.png) left 65% no-repeat;
+ padding-left: 18px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun .postsignature hr {
+ border:none;
+ height: 1px;
+ margin-left: 0px;
+ text-align: left;
+}
+
+/* Content (includes other user content)
+----------------------------------------------------------------*/
+
+.pun .usercontent {
+ padding: 7px 0;
+}
+
+.pun .postmsg p, .pun .postmsg li, #punhelp p samp {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .usercontent h1, .pun .usercontent h2, .pun .usercontent h3,
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ padding: 7px 0 0 0;
+}
+
+.pun .postmsg h5, #punhelp h5 {
+ font-size: 1.231em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+ list-style: disc;
+ padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+ list-style: decimal;
+ padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha;
+}
+
+.pun .usercontent li, .pun .postmsg li {
+ padding: 0 3px;
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0;
+}
+
+.pun span.bbu {
+ text-decoration: underline;
+}
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+}
+
+.pun .blockpost .postmsg .postedit {
+ font-size: 0.857em;
+}
+
+.pun .blockform .postsignature, .pun .blockpost .postsignature {
+ font-size: 0.923em;
+}
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+}
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px 1px 1px 3px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ font-weight: bold;
+ line-height: 1.462em;
+}
+
+.pun .quotebox blockquote {
+ overflow: hidden;
+ width: 100%;
+}
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ direction: ltr;
+ text-align: left;
+}
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+*:first-child+html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+.pun .codebox pre code {
+ padding: 0.5em;
+ white-space: pre;
+}
+
+.pun div[class*=codebox] pre code {
+ display: inline-block;
+}
+
+* html .pun .codebox pre code {
+ display: block;
+}
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto;
+}
+
+.pun .postmsg img, #punhelp samp img {
+ vertical-align: text-top;
+}
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+}
+
+/*****************************************************************
+MAIN FORMS
+*****************************************************************/
+
+#punedit .blockform h2, #punpost .blockform h2, #postpreview h2, #posterror h2,
+.pun #quickpost h2, .pun #reportform h2, #pundelete .blockform h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ white-space: nowrap;
+ padding: 10px 19px 4px 37px;
+ border: 0;
+}
+
+#punpost .blockform h2, #punedit .blockform h2,.pun #quickpost h2,
+#pundelete .blockform h2 {
+ margin: 1px 1px 0 1px;
+ width: 25em;
+ position: absolute;
+ z-index: 100;
+}
+
+.pun #quickpost legend, #punpost legend, #punedit legend {
+ width: 25em;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.pun .blockform .box {
+ border-style: solid;
+ border-width: 1px;
+ padding-bottom: 12px;
+}
+
+.pun #posterror {
+ border-style: solid;
+ border-width: 1px;
+}
+
+.pun #posterror .box {
+ padding: 0 18px 12px 18px;
+}
+
+* html .pun .blockform .box, * html .pun #posterror {
+ display: inline-block;
+}
+
+.pun .blockform .forminfo, .pun .error-info {
+ padding: 12px 18px;
+ border-style: solid;
+ border-width: 1px;
+ position: relative;
+}
+
+.pun .blockform .forminfo {
+ margin-top: 12px;
+}
+
+#pundelete .blockform .forminfo {
+ margin-top: 33px;
+}
+
+.pun .forminfo h3 {
+ padding-bottom: 0;
+}
+
+.pun .error-list li {
+ padding-left: 24px;
+ background: url(Air/img/exclaim.png) center left no-repeat;
+}
+
+.pun .inform {
+ padding: 0 18px;
+}
+
+.pun legend {
+ font-weight: bold;
+ padding: 10px 19px 4px 19px;
+}
+
+* html .pun legend {
+ margin-left: -7px;
+}
+
+*:first-child+html .pun legend {
+ margin-left: -7px;
+}
+
+.pun .infldset {
+ border-style: solid;
+ border-width: 1px;
+ padding: 12px 18px;
+}
+
+#punregister #rules .infldset {
+ padding: 5px 18px;
+}
+
+.pun fieldset p {
+ padding: 0 0 7px 0;
+ width: 100%;
+}
+
+.pun fieldset .usercontent p {
+ padding: 7px 0;
+}
+
+.pun fieldset label {
+ display: block;
+ padding: 0 0 7px 0;
+}
+
+.pun label em {
+ font-weight: normal;
+ font-style: normal;
+}
+
+.pun .required strong {
+ background: url(Air/img/asterisk.png) center right no-repeat;
+ font-weight: normal;
+ padding-right: 14px;
+ white-space: pre;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun label input, .pun label select, .pun label textarea {
+ margin-top: 2px;
+}
+
+.pun label.conl {
+ display: inline-block;
+ padding-right: 12px;
+}
+
+.pun form .buttons {
+ padding: 8px 19px 8px 34px;
+ margin-bottom: -12px;
+}
+
+.pun .blockform .buttons input {
+ margin-right: 12px;
+}
+
+.pun .rbox {
+ padding: 3px 0;
+}
+
+.pun .rbox label {
+ padding: 3px 0 3px 1.75em;
+ position: relative;
+ min-height: 1px;
+}
+
+* html .pun .rbox label {
+ text-indent: -3px;
+ height: 1%;
+}
+
+.pun .rbox input {
+ margin: 3px 0.75em 3px -1.75em;
+ float: left;
+ position: relative;
+ vertical-align: middle;
+ padding: 0;
+ height: 1em;
+ width: 1em;
+}
+
+.pun input[type=text], .pun input[type=password], .pun select, .pun textarea {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 98%;
+}
+
+.pun textarea {
+ resize: vertical;
+}
+
+.pun #quickpost .txtarea {
+ padding-right: 12px;
+ position: relative;
+}
+
+.pun .blockform .bblinks {
+ padding-top: 0;
+}
+
+.pun .blockform .bblinks li {
+ display: inline;
+}
+
+.pun .blockform .bblinks li span {
+ background: url(Air/img/help.png) center left no-repeat;
+ margin-right: 8px;
+ padding-left: 20px;
+ display: inline-block;
+}
+
+.pun #quickpost .bblinks {
+ padding-top: 0;
+}
+
+.pun #quickpost .bblinks li {
+ display: inline;
+}
+
+.pun #login p.clearb {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ font-size: 0;
+ height: 0;
+ line-height: 0;
+ margin-top: 7px;
+ overflow: hidden;
+ padding-bottom: 3px;
+ padding-top: 7px;
+ text-indent: -9999em;
+ width: 100%;
+}
+
+.pun #postreview {
+ padding-top: 12px;
+}
+
+.pun #postpreview, .pun #posterror {
+ margin-bottom: 12px;
+}
+
+.pun #postpreview .postright {
+ padding: 0;
+}
+
+.pun #postpreview .postbody {
+ border-style: solid;
+ border-width: 1px;
+ float: none;
+ margin: 0 18px 12px 18px;
+ padding: 0;
+ padding: 4px 18px 4px 18px;
+ width: auto;
+}
+
+.pun span.email {
+ background: url(Air/img/email.png) left 65% no-repeat;
+ margin-right: 5px;
+ padding-left: 21px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun span.website {
+ background: url(Air/img/ext.png) left 65% no-repeat;
+ padding-left: 18px;
+ display: inline-block;
+ line-height: normal;
+}
+
+#punmisc #rules .box {
+ border-style: solid;
+ border-width: 1px;
+ padding: 5px 18px;
+}
+
+
+#punhelp .box {
+ border-style: solid;
+ border-width: 1px;
+ padding: 7px 12px;
+}
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+}
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.25em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+ padding: 0;
+}
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+}
+
+/*****************************************************************
+PROFILES (+ ADMIN MENU)
+*****************************************************************/
+
+/* Profile / Admin
+----------------------------------------------------------------*/
+
+.pun .blockmenu {
+ width: 13em;
+ float: left;
+ padding-bottom: 12px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 15em;
+}
+
+.pun .blockmenu .block2 {
+ padding-top: 19px;
+}
+
+.pun .blockmenu ul {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ padding: 0;
+}
+
+.pun .blockmenu li {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ font-weight: bold;
+ padding: 0;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ display: block;
+ padding: 9px 6px 3px 6px;
+ min-height: 1px;
+ text-decoration: none;
+}
+
+* html .pun .blockmenu a:link, * html .pun .blockmenu a:visited {
+ height: 1%;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+ text-decoration: none;
+}
+
+#viewprofile .box {
+ border-style: solid;
+ border-width: 1px;
+ padding-bottom: 18px;
+}
+
+#viewprofile dt, #adstats dt {
+ padding: 7px 0;
+ position: absolute;
+ width: 13em;
+ left: 0;
+}
+
+#viewprofile dl {
+ border-style: solid none none none;
+ border-width: 1px;
+ margin: 7px 0;
+ padding: 0;
+ width: 100%;
+ position: relative;
+}
+
+#adintro, #adstats {
+ border-style: solid;
+ border-width: 1px;
+ padding: 18px;
+}
+
+#adintro li span {
+ display: inline-block;
+ padding-left: 16px;
+ margin-left: 8px;
+ line-height: normal;
+ background: url(Air/img/bull.png) center left no-repeat;
+}
+
+#adstats .inbox, #adintro .inbox {
+ border-style: solid;
+ border-width: 1px;
+ padding: 18px;
+}
+
+#adstats dl {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ position: relative;
+}
+
+#viewprofile dd, #adstats dd {
+ border-style: none none solid none;
+ border-width: 1px;
+ padding: 7px 0 7px 13em;
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Basic defaults and Common Items
+----------------------------------------------------------------*/
+
+html, body, .pun {
+ background: #f6f9fc;
+ color: #333;
+}
+
+.pun .punwrap {
+ background: #fff;
+ border-color: #cad7e1;
+ color: #566579;
+}
+
+#brdtitle #brddesc, .pun .pagepost, #brdstats #onlinelist, #brdfooter #searchlinks, #brdfooter #modcontrols,
+#punmaint h2, #punredirect h2, #adminconsole .submittop, .pun #debugtime, .pun .pagelink a, .pun .pagelink * {
+ border-color: #b9c5ce;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+ color: #2365B0;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #b50000;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #22538a;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #b50000;
+}
+
+/* Primary Navigation
+----------------------------------------------------------------*/
+
+#brdmenu {
+ background: #44699c;
+}
+
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited {
+ background: #44699c;
+ border-color: #fff;
+ color: #d4dae2;
+}
+
+#brdmenu a:hover, #brdmenu a:active, #brdmenu a:focus {
+ background: #b50000;
+ border-color: #fff;
+ color: #fff;
+}
+
+/* Main Tables
+----------------------------------------------------------------*/
+
+.pun .blocktable .box {
+ background: #fcfdfe;
+ border-color: #b9c5ce #d9e1e7;
+}
+
+#punindex .blocktable h2, .pun #vf h2 {
+ color: #357082;
+}
+
+#adminconsole fieldset th, #adminconsole fieldset td {
+ background: #f6f9fc;
+ border-color: #dfe6ee;
+}
+
+.pun #users1 h2 {
+ background: #fff;
+}
+
+.pun .blocktable td {
+ border-color: #dfe6ee;
+}
+
+.pun .blocktable th {
+ background: #ebf1f5;
+ border-color: #cad7e1;
+ color: #357082;
+}
+
+.pun .blocktable td.tcl span.stickytext {
+ color: #3399CC;
+}
+
+/* Main Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+ background: #f6f9fc;
+ border-color: #b9c5ce #d9e1e7;
+}
+
+.pun .blockpost h2 {
+ background: #ebf1f5;
+ border-color: #cad7e1;
+ color: #357082;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+ background: #fcfdfe;
+ border-color: #dfe6ee;
+}
+
+.pun .blockpost .postfootright li {
+ color: #fcfdfe;
+}
+
+.pun .postmsg, #punhelp code, #punhelp samp {
+ color: #333;
+}
+
+.pun .postsignature, .pun .postmsg .postedit {
+ color: #566579;
+}
+
+.pun .quotebox {
+ background: #f8f9f0;
+ border-color: #7aadbd;
+ color: #566579;
+}
+
+.pun .quotebox cite {
+ color: #357082;
+}
+
+.pun .codebox, #punhelp .codebox code {
+ background: #333;
+ color: #fff;
+}
+
+.pun .postmsg hr {
+ background: #b9c5ce;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+}
+
+/* Main Forms + Profile
+----------------------------------------------------------------*/
+
+.pun .blockform .box, #adstats, #adintro, #postpreview, #posterror {
+ border-color: #b9c5ce #d9e1e7;
+ background: #ebf1f5;
+}
+
+#punmisc #rules .box, #punhelp .box {
+ border-color: #b9c5ce #d9e1e7;
+ background: #f6f9fc;
+}
+
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2,
+#pundelete .blockform h2 {
+ background: #ebf1f5;
+ color: #357082;
+}
+
+.pun .forminfo {
+ background: #fff;
+ border-color: #dfe6ee;
+}
+
+#puninstall form#install .forminfo {
+ background: #44699c;
+ color: #fff;
+}
+
+.pun #posterror .error-info {
+ background: #ffffe1;
+ border-color: #dfe6ee;
+}
+
+#puninstall form#install .error-info {
+ background: #ffffe1;
+ border-color: #dfe6ee;
+ color: #333;
+}
+
+.pun .infldset, #adintro .inbox, #adstats .inbox {
+ background: #f6f9fc;
+ border-color: #dfe6ee;
+}
+
+.pun label, .pun legend, #adminconsole fieldset th {
+ color: #357082;
+}
+
+.pun fieldset p {
+ border-color: #b9c5ce;
+}
+
+.pun .blockmenu ul, .pun .blockmenu li {
+ border-color: #b9c5ce;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+ background: #ffffe6;
+}
+
+.pun .blockmenu .isactive a:link, .pun .blockmenu .isactive a:visited {
+ color: #333;
+ background: #f6f9fc;
+}
+
+.pun #viewprofile .box {
+ border-color: #b9c5ce #d9e1e7;
+ background: #ebf1f5;
+}
+
+.pun #viewprofile dt, #adstats dt {
+ color: #357082;
+}
+
+.pun #viewprofile dl, .pun #viewprofile dd, #adstats dl, #adstats dd {
+ border-color: #dfe6ee;
+}
+
+#adminconsole fieldset td.nodefault {
+ background: #d59b9b;
+}
+
+.pun .multiselect {
+ color: #357082;
+}
+
+.pun .checklist {
+ background: white;
+ border-color: #ccc;
+}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #e8ecf1 #d4d9dd #dfe3e8 #e8ecf1;
+}
+
+.pun .iredirect .icon {
+ border-color: #b9c5ce;
+ border-width: 1px;
+ padding: 7px;
+}
+
+.pun .inew .icon {
+ border-color: #91b3d9 #87a8d1 #6c85bb #7292c3;
+}
diff --git a/style/Air/base_admin.css b/style/Air/base_admin.css
new file mode 100644
index 0000000..8136885
--- /dev/null
+++ b/style/Air/base_admin.css
@@ -0,0 +1,177 @@
+#adminconsole .blockform .box {
+ padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+ position: relative;
+ overflow: hidden;
+}
+
+#adminconsole fieldset table {
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+ padding: 10px 8px 10px 0;
+ text-align: left;
+ white-space: normal;
+ border-style: solid none;
+ border-width: 1px 0;
+}
+
+#punadmin thead th {
+ border-top: 0;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+ display: block; font-size: 1em;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+ display: inline-block;
+}
+
+#adminconsole fieldset th {
+ width: 15em;
+ font-weight: normal;
+ padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+ vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+ padding-top: 3px;
+}
+
+#adminconsole .inform {
+ padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+ padding-bottom: 0;
+ padding-top: 0;
+}
+
+#adminconsole p.submittop {
+ text-align: center;
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin: 0 18px;
+ padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+ text-align: center;
+ padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+ padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+ padding: 10px 0 12px 0;
+}
+
+#adalerts {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #b9c5ce #d9e1e7;
+ background: #ebf1f5;
+ padding: 18px;
+}
+
+#adalerts p {
+ border: 1px solid #dfe6ee;
+ background: #ffffe1;
+ padding: 18px;
+}
+
+#categoryedit .tcl {
+ width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2 {
+ width: 20%;
+}
+
+#edforum .tcl {
+ width: 18%;
+}
+
+#edforum .tc2 {
+ width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+ text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+ padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+ display: block;
+ left: -9999em;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+ font-weight: bold;
+}
+
+#users2 th, #bans1 th {
+ text-align: left;
+}
+
+#users2 th.tcmod {
+ text-align: center;
+}
+
+#users2 .tcl, #bans1 .tcl {
+ width: auto;
+ text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+ width: 18%;
+ text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+ width: 12%;
+ text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+ width: 10%;
+ text-align: center;
+}
+
+#users2 .tcr {
+ width: 20%;
+ white-space: nowrap;
+}
+
+#bans1 .tcr {
+ width: 15%;
+ white-space: nowrap;
+}
+
+#users2 .tcmod {
+ width: 10%;
+ text-align: center;
+}
+
+.plugin p {
+ padding: 12px 18px 0;
+}
diff --git a/style/Air/img/asterisk.png b/style/Air/img/asterisk.png
new file mode 100644
index 0000000..f7438de
--- /dev/null
+++ b/style/Air/img/asterisk.png
Binary files differ
diff --git a/style/Air/img/bull.png b/style/Air/img/bull.png
new file mode 100644
index 0000000..c0706f8
--- /dev/null
+++ b/style/Air/img/bull.png
Binary files differ
diff --git a/style/Air/img/email.png b/style/Air/img/email.png
new file mode 100644
index 0000000..39ee55f
--- /dev/null
+++ b/style/Air/img/email.png
Binary files differ
diff --git a/style/Air/img/exclaim.png b/style/Air/img/exclaim.png
new file mode 100644
index 0000000..b98bb3c
--- /dev/null
+++ b/style/Air/img/exclaim.png
Binary files differ
diff --git a/style/Air/img/ext.png b/style/Air/img/ext.png
new file mode 100644
index 0000000..b63b4b7
--- /dev/null
+++ b/style/Air/img/ext.png
Binary files differ
diff --git a/style/Air/img/feed.png b/style/Air/img/feed.png
new file mode 100644
index 0000000..3704226
--- /dev/null
+++ b/style/Air/img/feed.png
Binary files differ
diff --git a/style/Air/img/help.png b/style/Air/img/help.png
new file mode 100644
index 0000000..b20fb2d
--- /dev/null
+++ b/style/Air/img/help.png
Binary files differ
diff --git a/style/Air/img/index.html b/style/Air/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Air/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Air/index.html b/style/Air/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Air/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/ArchLinux.css b/style/ArchLinux.css
new file mode 120000
index 0000000..33e9080
--- /dev/null
+++ b/style/ArchLinux.css
@@ -0,0 +1 @@
+Air.css \ No newline at end of file
diff --git a/style/ArchLinux/admin.tpl b/style/ArchLinux/admin.tpl
new file mode 100644
index 0000000..718123a
--- /dev/null
+++ b/style/ArchLinux/admin.tpl
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punadmin" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <pun_navlinks>
+ <pun_status>
+ </div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux/arch.css b/style/ArchLinux/arch.css
new file mode 100644
index 0000000..adbe853
--- /dev/null
+++ b/style/ArchLinux/arch.css
@@ -0,0 +1,162 @@
+* { font-family: sans-serif !important; }
+.pun { max-width: none; margin: 0; }
+#archnavbar.anb-forum ul li.anb-selected a { color: #fff !important; }
+.pun pre, .pun code { font-family: monospace !important; }
+html, body, .pun { background: #f6f9fc; color: #222; }
+.pun .punwrap { border-color: #bcd; border-radius: 0; }
+
+.pun a, .pun a:link, .pun a:visited { text-decoration:none; color:#07b; outline: none; }
+.pun a:focus, #brdmain a:focus { color: #e90; }
+.pun a:hover, #brdmain a:hover { color:#999 !important; text-decoration:underline; }
+.pun a:active, #brdmain a:active { color: #e90 !important; }
+
+#brdmenu { background: #fff; }
+#brdmenu ul { margin-left: -9px; }
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited { display: inline; padding-right: 10px; background: #fff; color: #07b; }
+#brdmain a:visited { color: #666; }
+#brdmenu a:hover { color:#999; text-decoration:underline; }
+#brdmenu a:active, #brdmenu a:focus { color: #e90; }
+
+.pun .postmsg a { font-weight: bold; }
+
+/* hide round edges */
+.pun .top-box { background: none; }
+.pun .top-box div { background: none; }
+.pun .end-box { background: none; }
+.pun .end-box div { background: none; }
+
+/* hide moderator list */
+#brdmain .tcl .modlist { display: none; }
+
+/* replace green color */
+#punindex .blocktable h2, .pun #vf h2 { color: #444; }
+.pun .blocktable th { color: #444; }
+.pun .blockpost h2 { color: #444; }
+.pun .quotebox cite { color: #444; }
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2, #pundelete .blockform h2 { color: #444; }
+.pun label, .pun legend, #adminconsole fieldset th { color: #444; }
+.pun #viewprofile dt, #adstats dt { color: #444; }
+
+.pun .icon { border-color: #e8ecf1; border-width: 6px; margin: 2px 0 0 2px; }
+.pun .inew .icon { border-color: #07b; }
+
+.pun .quotebox { border-color: #bcd; background: #f6f9fc; color: #444; }
+.pun .codebox { border-color: #bcd; background: #ebf1f5; color: #222; }
+
+/* hide border of images */
+.pun .postimg img { border: none; }
+
+@media (max-width: 575px) {
+ #brdstats .conr,
+ #punindex .tcr .byuser,
+ #punpost .blockform h2, #punedit .blockform h2, .pun #quickpost h2,
+ #pundelete .blockform h2,
+ .pun .blocktable .tc2,
+ .pun .blocktable .tc3,
+ .pun .blockpost .postfootleft,
+ .pun .blockpost .postleft dd,
+ .pun .blocktable td .byuser,
+ .pun .blockpost h2 .conr,
+ .pun .blockpost .postleft .postavatar,
+ .pun #quickpost legend, #punpost legend, #punedit legend,
+ .pun #quickpost .bblinks {
+ display: none;
+ }
+
+ .pun .blockpost .postleft {
+ margin-left: 0;
+ padding: 0;
+ }
+
+ .pun .blockpost .postbody {
+ float: none;
+ }
+
+ .pun .blockpost .postright {
+ padding: 5px;
+ }
+
+ .pun .inform {
+ padding: 0 5px;
+ }
+
+ .pun input[type=text], .pun input[type=password], .pun select, .pun textarea {
+ max-width: 75vw;
+ }
+
+ .pun .blockpost {
+ background-color: #fcfdfe;
+ }
+
+ .pun .quotebox {
+ margin: 0.75em 0.5em;
+ }
+
+ .pun .blockmenu {
+ width: 100%;
+ float: none;
+ }
+
+ .pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 0;
+ }
+
+ #brdmenu a:link, #brdmenu a:visited {
+ padding-left: 0;
+ }
+}
+
+@media (max-width: 768px) {
+ .pun {
+ padding: 30px 5px;
+ }
+
+ .pun .punwrap {
+ padding: 18px 5px;
+ }
+
+ #brdwelcome {
+ padding: 20px 0;
+ }
+
+ #brdmenu ul {
+ margin-left: 0;
+ }
+
+ #punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+ padding-top: 4px;
+ }
+
+ .pun .required strong {
+ white-space: normal;
+ }
+
+ .pun .crumbs li {
+ white-space: normal;
+ }
+
+ .pun .postlinksb .crumbs {
+ margin-right: 0;
+ }
+
+ .pun .blockpost .postfoot {
+ border-left-style: none;
+ }
+
+ #brdwelcome .conl {
+ display: none;
+ }
+
+ .pun .subscribelink {
+ position: inherit;
+ }
+}
+
+#archfooter {
+ display: flex;
+ justify-content: flex-end;
+}
+
+#archfooter li {
+ margin-left: 10px;
+}
diff --git a/style/ArchLinux/archfooter.php b/style/ArchLinux/archfooter.php
new file mode 100644
index 0000000..b720bb6
--- /dev/null
+++ b/style/ArchLinux/archfooter.php
@@ -0,0 +1,11 @@
+<?php global $arch_footer;
+if ($arch_footer) { ?>
+ <ul id="archfooter">
+ <?php
+ foreach ($arch_footer as $arch_name => $arch_url) {
+ echo '<li id="aft-' . strtolower($arch_name) . '"'
+ . '><a href="' . $arch_url . '">' . $arch_name . '</a></li>';
+ }
+ ?>
+ </ul>
+<?php }
diff --git a/style/ArchLinux/archicon.svg b/style/ArchLinux/archicon.svg
new file mode 100644
index 0000000..62a35f2
--- /dev/null
+++ b/style/ArchLinux/archicon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" version="1"><path d="M600 0c-53.416 130.962-85.671 216.74-145.143 343.81 36.464 38.651 81.22 83.449 153.905 134.285-78.145-32.156-131.403-64.213-171.238-97.714C361.41 539.2 241.989 765.194 0 1200c190.161-109.784 337.665-177.397 475.048-203.238-5.895-25.367-9.18-52.896-8.953-81.524l.19-5.905C469.305 787.48 532.72 693.638 607.81 700c75.089 6.362 133.494 110.528 130.476 232.381-.568 22.916-3.105 44.885-7.62 65.333C866.528 1024.325 1012.462 1091.73 1200 1200c-36.995-68.111-70.18-129.335-101.714-187.81-49.672-38.499-101.365-88.613-207.048-142.857 72.64 18.875 124.793 40.612 165.333 64.953C735.952 337.348 709.948 258.016 600 0z" fill="#1793d1" fill-rule="evenodd"/></svg> \ No newline at end of file
diff --git a/style/ArchLinux/archlogo.svg b/style/ArchLinux/archlogo.svg
new file mode 100644
index 0000000..957a6b8
--- /dev/null
+++ b/style/ArchLinux/archlogo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1" width="600" height="126"><path d="M159.568 34.427c-8.89-.014-16.267 1.809-19.12 2.803l-2.937 15.857c-.007.058 14.617-3.9 21.059-3.667 10.665.383 11.646 4.076 11.46 9.06.182.292-2.752-4.503-11.979-4.664-11.64-.2-28.069 4.122-28.046 21.692-.314 19.765 14.764 25.579 25.032 25.686 9.232-.168 13.563-3.496 15.934-5.28 3.115-3.257 6.679-6.532 10.078-10.462-3.216 5.844-6.005 9.884-8.907 12.977v2.611l14.033-2.361.096-38.144c-.143-5.399 3.096-26.057-26.703-26.108zm-2.016 33.21c5.817.08 12.488 2.948 12.497 9.849.03 6.277-7.863 9.651-12.996 9.598-5.135-.053-11.949-4.036-11.979-10.155.099-5.47 6.426-9.432 12.478-9.291zm37.972-29.685l-.095 63.166 16.348-3.15.027-35.814c.004-5.333 7.62-11.564 17.178-11.464 2.028-3.67 5.84-13.05 6.77-15.183-21.351-.051-21.623 6.137-25.336 9.18-.04-5.806-.013-9.292-.013-9.292l-14.879 2.557zm92.002 8.292c-.158-.074-8.526-9.788-25.35-9.864-15.758-.262-33.433 5.847-33.716 32.27.138 23.232 16.979 32.311 33.805 32.488 18.007.187 25.172-11.26 25.602-11.543-2.149-1.863-10.196-9.837-10.196-9.837s-5.027 7.157-14.779 7.248c-9.755.093-18.234-7.54-18.354-18.189-.125-10.65 7.795-16.419 18.427-16.885 9.205-.002 14.516 5.943 14.516 5.943zm20.606-30.399l-15.434 3.628.115 82.277 15.204-2.745.172-38.72c.033-4.06 5.874-10.295 15.626-10.097 9.325.097 11.41 6.215 11.384 6.988l.269 44.824 14.993-2.65.057-47.53c.099-4.574-10.018-14.233-26.28-14.302-7.729.012-12.009 1.762-14.187 3.052-3.726 2.879-7.985 5.637-12.17 9.157 3.869-4.97 7.117-8.407 10.29-10.961l-.04-22.921z" fill="#fff" fill-rule="evenodd"/><path d="M360.136 17.218l6.962-1.742.33 82.95-7.074 1.204zm18.928 24.757l6.101-2.716.052 59.478-5.892 1.217zm-1.45-21.448l4.92-4.015 4.086 4.547-4.921 4.121zm19.024 20.365l6.962-1.421.033 12.434c.001.534 3.823-13.89 22.258-13.57 17.9.1 20.827 13.957 20.73 17.064l.221 43.725-6.102 1.324-.035-43.189c.07-1.261-2.79-11.927-15.439-11.966-12.646-.037-21.409 9.186-21.393 15.078l.1 38.047-7.07 1.847zm110.954 58.546l-6.962 1.42-.033-12.433c-.001-.534-3.825 13.89-22.258 13.57-17.9-.1-20.827-13.957-20.73-17.064l-.221-43.725 7.397-1.494.114 43.19c.003 1.18 1.416 12.096 14.065 12.135 12.646.037 21.506-7.616 21.569-19.139l-.09-34.076 6.885-1.757zm13.645-59.037l-4.882 3.82 18.717 24.494-19.963 28.3 5.179 3.843 18.766-26.28 19.368 26.902 4.791-3.82-20.757-28.765 16.56-23.262-5.092-4.305-15.085 21.525zM61.88 1.778c-5.385 13.203-8.633 21.839-14.629 34.649 3.676 3.896 8.188 8.434 15.516 13.559-7.878-3.242-13.252-6.497-17.267-9.874-7.673 16.011-19.695 38.818-44.09 82.65 19.174-11.068 34.037-17.893 47.889-20.497a35.103 35.103 0 0 1-.91-8.213l.023-.614c.304-12.284 6.694-21.73 14.264-21.09 7.57.642 13.454 11.126 13.15 23.41-.058 2.312-.319 4.536-.774 6.598 13.701 2.68 28.405 9.487 47.32 20.407-3.73-6.866-7.059-13.056-10.238-18.95-5.007-3.882-10.23-8.933-20.884-14.402 7.323 1.903 12.566 4.099 16.653 6.552C75.58 35.786 72.963 27.79 61.88 1.778z" fill="#1793d1" fill-rule="evenodd"/><path d="M576.771 93.265V80.603h-4.73v-1.695h11.38v1.695h-4.75v12.662h-1.9m8.629 0V78.908h2.859l3.398 10.166c.314.947.542 1.655.686 2.125.163-.522.418-1.29.764-2.301l3.437-9.99h2.556v14.357h-1.831V81.25l-4.172 12.016h-1.714l-4.152-12.222v12.222h-1.832" font-weight="400" font-size="8.441" font-family="DejaVu Sans Mono" fill="gray"/></svg> \ No newline at end of file
diff --git a/style/ArchLinux/archnavbar.css b/style/ArchLinux/archnavbar.css
new file mode 100644
index 0000000..df460b6
--- /dev/null
+++ b/style/ArchLinux/archnavbar.css
@@ -0,0 +1,76 @@
+/*
+ * ARCH GLOBAL NAVBAR
+ *
+ * We're forcing all generic selectors with !important
+ * to help prevent other stylesheets from interfering.
+ *
+ */
+
+/* container for the entire bar */
+#archnavbar { height: 40px !important; padding: 10px 15px !important; background: #333 !important; border-bottom: 5px #08c solid !important; }
+
+#archnavbarlogo { float: left !important; margin: 0 !important; padding: 0 !important; height: 40px !important; width: 190px !important; }
+html > body #archnavbarlogo { background: url('archlogo.svg') no-repeat !important; background-size: 190px 40px !important;}
+
+/* move the heading/paragraph text offscreen */
+#archnavbarlogo p { margin: 0 !important; padding: 0 !important; text-indent: -9999px !important; }
+#archnavbarlogo h1 { margin: 0 !important; padding: 0 !important; text-indent: -9999px !important; }
+
+/* make the link the same size as the logo */
+#archnavbarlogo a { display: block !important; height: 40px !important; width: 190px !important; }
+
+/* display the list inline, float it to the right and style it */
+#archnavbar ul { display: inline !important; float: right !important; list-style: none !important; margin: 0 !important; padding: 0 !important; }
+#archnavbar ul li { float: left !important; font-size: 14px !important; font-family: sans-serif !important; line-height: 45px !important; padding-right: 15px !important; padding-left: 15px !important; }
+
+/* style the links */
+#archnavbar ul#archnavbarlist li a { color: #999; font-weight: bold !important; text-decoration: none !important; }
+#archnavbar ul li a:hover { color: white !important; text-decoration: underline !important; }
+
+@media (max-width: 680px) {
+ #anb-forum,
+ #anb-forums {
+ display: none;
+ }
+
+ html > body #archnavbarlogo {
+ width: 40px !important;
+ margin-right: 5px !important;
+ background: url('archicon.svg') no-repeat !important;
+ background-size: 40px 40px !important;
+ }
+
+ #archnavbar ul {
+ display: flex !important;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+ overflow: hidden;
+ float: none !important;
+ }
+
+ #archnavbar ul li {
+ padding: 0 !important;
+ float: none !important;
+ }
+}
+
+@media (max-width: 830px) {
+ #anb-home,
+ #anb-start {
+ display: none;
+ }
+
+ html > body #archnavbarlogo {
+ padding-right: 15px !important;
+ }
+
+ #archnavbar {
+ padding-left: 5px !important;
+ padding-right: 5px !important;
+ }
+
+ #archnavbar ul li {
+ padding-left: 5px !important;
+ padding-right: 5px !important;
+ }
+}
diff --git a/style/ArchLinux/archnavbar.php b/style/ArchLinux/archnavbar.php
new file mode 100644
index 0000000..6478edd
--- /dev/null
+++ b/style/ArchLinux/archnavbar.php
@@ -0,0 +1,17 @@
+<?php global $arch_home, $arch_navbar, $arch_navbar_selected; ?>
+<div id="archnavbar" class="anb-forum">
+ <div id="archnavbarlogo"><h1><a href="<?php if (isset($arch_home)) { echo $arch_home; } ?>">Arch Linux</a></h1></div>
+ <div id="archnavbarmenu">
+ <ul id="archnavbarlist">
+ <?php
+ if (isset($arch_navbar)) {
+ foreach ($arch_navbar as $arch_name => $arch_url) {
+ echo '<li id="anb-'.strtolower($arch_name).'"'
+ .(isset($arch_navbar_selected) && $arch_navbar_selected == $arch_name ? ' class="anb-selected"' : '')
+ .'><a href="'.$arch_url.'">'.$arch_name.'</a></li>';
+ }
+ }
+ ?>
+ </ul>
+ </div>
+</div>
diff --git a/style/ArchLinux/base_admin.css b/style/ArchLinux/base_admin.css
new file mode 120000
index 0000000..c07ee04
--- /dev/null
+++ b/style/ArchLinux/base_admin.css
@@ -0,0 +1 @@
+../Air/base_admin.css \ No newline at end of file
diff --git a/style/ArchLinux/css.php b/style/ArchLinux/css.php
new file mode 100644
index 0000000..a963629
--- /dev/null
+++ b/style/ArchLinux/css.php
@@ -0,0 +1,7 @@
+<?php
+
+foreach (array('arch', 'archnavbar') as $cssFile) {
+ ?>
+ <link rel="stylesheet" media="screen" href="style/ArchLinux/<?= $cssFile ?>.css?v=5"/>
+ <?php
+}
diff --git a/style/ArchLinux/favicon.ico b/style/ArchLinux/favicon.ico
new file mode 100644
index 0000000..8ef6f13
--- /dev/null
+++ b/style/ArchLinux/favicon.ico
Binary files differ
diff --git a/style/ArchLinux/help.tpl b/style/ArchLinux/help.tpl
new file mode 100644
index 0000000..d8482b9
--- /dev/null
+++ b/style/ArchLinux/help.tpl
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punhelp" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux/index.html b/style/ArchLinux/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/ArchLinux/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/ArchLinux/main.tpl b/style/ArchLinux/main.tpl
new file mode 100644
index 0000000..eecb5e3
--- /dev/null
+++ b/style/ArchLinux/main.tpl
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="pun<pun_page>" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <pun_navlinks>
+ <pun_status>
+ </div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux/maintenance.tpl b/style/ArchLinux/maintenance.tpl
new file mode 100644
index 0000000..44a81a6
--- /dev/null
+++ b/style/ArchLinux/maintenance.tpl
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punmaint" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_maint_main>
+</div>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux/redirect.tpl b/style/ArchLinux/redirect.tpl
new file mode 100644
index 0000000..115b7d9
--- /dev/null
+++ b/style/ArchLinux/redirect.tpl
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punredirect" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_redir_main>
+</div>
+
+<pun_footer>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux32.css b/style/ArchLinux32.css
new file mode 120000
index 0000000..33e9080
--- /dev/null
+++ b/style/ArchLinux32.css
@@ -0,0 +1 @@
+Air.css \ No newline at end of file
diff --git a/style/ArchLinux32/admin.tpl b/style/ArchLinux32/admin.tpl
new file mode 100644
index 0000000..8d33aa4
--- /dev/null
+++ b/style/ArchLinux32/admin.tpl
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux32/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punadmin" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <pun_navlinks>
+ <pun_status>
+ </div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux32/arch.css b/style/ArchLinux32/arch.css
new file mode 100644
index 0000000..adbe853
--- /dev/null
+++ b/style/ArchLinux32/arch.css
@@ -0,0 +1,162 @@
+* { font-family: sans-serif !important; }
+.pun { max-width: none; margin: 0; }
+#archnavbar.anb-forum ul li.anb-selected a { color: #fff !important; }
+.pun pre, .pun code { font-family: monospace !important; }
+html, body, .pun { background: #f6f9fc; color: #222; }
+.pun .punwrap { border-color: #bcd; border-radius: 0; }
+
+.pun a, .pun a:link, .pun a:visited { text-decoration:none; color:#07b; outline: none; }
+.pun a:focus, #brdmain a:focus { color: #e90; }
+.pun a:hover, #brdmain a:hover { color:#999 !important; text-decoration:underline; }
+.pun a:active, #brdmain a:active { color: #e90 !important; }
+
+#brdmenu { background: #fff; }
+#brdmenu ul { margin-left: -9px; }
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited { display: inline; padding-right: 10px; background: #fff; color: #07b; }
+#brdmain a:visited { color: #666; }
+#brdmenu a:hover { color:#999; text-decoration:underline; }
+#brdmenu a:active, #brdmenu a:focus { color: #e90; }
+
+.pun .postmsg a { font-weight: bold; }
+
+/* hide round edges */
+.pun .top-box { background: none; }
+.pun .top-box div { background: none; }
+.pun .end-box { background: none; }
+.pun .end-box div { background: none; }
+
+/* hide moderator list */
+#brdmain .tcl .modlist { display: none; }
+
+/* replace green color */
+#punindex .blocktable h2, .pun #vf h2 { color: #444; }
+.pun .blocktable th { color: #444; }
+.pun .blockpost h2 { color: #444; }
+.pun .quotebox cite { color: #444; }
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2, #pundelete .blockform h2 { color: #444; }
+.pun label, .pun legend, #adminconsole fieldset th { color: #444; }
+.pun #viewprofile dt, #adstats dt { color: #444; }
+
+.pun .icon { border-color: #e8ecf1; border-width: 6px; margin: 2px 0 0 2px; }
+.pun .inew .icon { border-color: #07b; }
+
+.pun .quotebox { border-color: #bcd; background: #f6f9fc; color: #444; }
+.pun .codebox { border-color: #bcd; background: #ebf1f5; color: #222; }
+
+/* hide border of images */
+.pun .postimg img { border: none; }
+
+@media (max-width: 575px) {
+ #brdstats .conr,
+ #punindex .tcr .byuser,
+ #punpost .blockform h2, #punedit .blockform h2, .pun #quickpost h2,
+ #pundelete .blockform h2,
+ .pun .blocktable .tc2,
+ .pun .blocktable .tc3,
+ .pun .blockpost .postfootleft,
+ .pun .blockpost .postleft dd,
+ .pun .blocktable td .byuser,
+ .pun .blockpost h2 .conr,
+ .pun .blockpost .postleft .postavatar,
+ .pun #quickpost legend, #punpost legend, #punedit legend,
+ .pun #quickpost .bblinks {
+ display: none;
+ }
+
+ .pun .blockpost .postleft {
+ margin-left: 0;
+ padding: 0;
+ }
+
+ .pun .blockpost .postbody {
+ float: none;
+ }
+
+ .pun .blockpost .postright {
+ padding: 5px;
+ }
+
+ .pun .inform {
+ padding: 0 5px;
+ }
+
+ .pun input[type=text], .pun input[type=password], .pun select, .pun textarea {
+ max-width: 75vw;
+ }
+
+ .pun .blockpost {
+ background-color: #fcfdfe;
+ }
+
+ .pun .quotebox {
+ margin: 0.75em 0.5em;
+ }
+
+ .pun .blockmenu {
+ width: 100%;
+ float: none;
+ }
+
+ .pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 0;
+ }
+
+ #brdmenu a:link, #brdmenu a:visited {
+ padding-left: 0;
+ }
+}
+
+@media (max-width: 768px) {
+ .pun {
+ padding: 30px 5px;
+ }
+
+ .pun .punwrap {
+ padding: 18px 5px;
+ }
+
+ #brdwelcome {
+ padding: 20px 0;
+ }
+
+ #brdmenu ul {
+ margin-left: 0;
+ }
+
+ #punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+ padding-top: 4px;
+ }
+
+ .pun .required strong {
+ white-space: normal;
+ }
+
+ .pun .crumbs li {
+ white-space: normal;
+ }
+
+ .pun .postlinksb .crumbs {
+ margin-right: 0;
+ }
+
+ .pun .blockpost .postfoot {
+ border-left-style: none;
+ }
+
+ #brdwelcome .conl {
+ display: none;
+ }
+
+ .pun .subscribelink {
+ position: inherit;
+ }
+}
+
+#archfooter {
+ display: flex;
+ justify-content: flex-end;
+}
+
+#archfooter li {
+ margin-left: 10px;
+}
diff --git a/style/ArchLinux32/arch32logo.png b/style/ArchLinux32/arch32logo.png
new file mode 100644
index 0000000..a7fd0d8
--- /dev/null
+++ b/style/ArchLinux32/arch32logo.png
Binary files differ
diff --git a/style/ArchLinux32/archfooter.php b/style/ArchLinux32/archfooter.php
new file mode 100644
index 0000000..b720bb6
--- /dev/null
+++ b/style/ArchLinux32/archfooter.php
@@ -0,0 +1,11 @@
+<?php global $arch_footer;
+if ($arch_footer) { ?>
+ <ul id="archfooter">
+ <?php
+ foreach ($arch_footer as $arch_name => $arch_url) {
+ echo '<li id="aft-' . strtolower($arch_name) . '"'
+ . '><a href="' . $arch_url . '">' . $arch_name . '</a></li>';
+ }
+ ?>
+ </ul>
+<?php }
diff --git a/style/ArchLinux32/archicon.svg b/style/ArchLinux32/archicon.svg
new file mode 100644
index 0000000..62a35f2
--- /dev/null
+++ b/style/ArchLinux32/archicon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" version="1"><path d="M600 0c-53.416 130.962-85.671 216.74-145.143 343.81 36.464 38.651 81.22 83.449 153.905 134.285-78.145-32.156-131.403-64.213-171.238-97.714C361.41 539.2 241.989 765.194 0 1200c190.161-109.784 337.665-177.397 475.048-203.238-5.895-25.367-9.18-52.896-8.953-81.524l.19-5.905C469.305 787.48 532.72 693.638 607.81 700c75.089 6.362 133.494 110.528 130.476 232.381-.568 22.916-3.105 44.885-7.62 65.333C866.528 1024.325 1012.462 1091.73 1200 1200c-36.995-68.111-70.18-129.335-101.714-187.81-49.672-38.499-101.365-88.613-207.048-142.857 72.64 18.875 124.793 40.612 165.333 64.953C735.952 337.348 709.948 258.016 600 0z" fill="#1793d1" fill-rule="evenodd"/></svg> \ No newline at end of file
diff --git a/style/ArchLinux32/archlogo.svg b/style/ArchLinux32/archlogo.svg
new file mode 100644
index 0000000..957a6b8
--- /dev/null
+++ b/style/ArchLinux32/archlogo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1" width="600" height="126"><path d="M159.568 34.427c-8.89-.014-16.267 1.809-19.12 2.803l-2.937 15.857c-.007.058 14.617-3.9 21.059-3.667 10.665.383 11.646 4.076 11.46 9.06.182.292-2.752-4.503-11.979-4.664-11.64-.2-28.069 4.122-28.046 21.692-.314 19.765 14.764 25.579 25.032 25.686 9.232-.168 13.563-3.496 15.934-5.28 3.115-3.257 6.679-6.532 10.078-10.462-3.216 5.844-6.005 9.884-8.907 12.977v2.611l14.033-2.361.096-38.144c-.143-5.399 3.096-26.057-26.703-26.108zm-2.016 33.21c5.817.08 12.488 2.948 12.497 9.849.03 6.277-7.863 9.651-12.996 9.598-5.135-.053-11.949-4.036-11.979-10.155.099-5.47 6.426-9.432 12.478-9.291zm37.972-29.685l-.095 63.166 16.348-3.15.027-35.814c.004-5.333 7.62-11.564 17.178-11.464 2.028-3.67 5.84-13.05 6.77-15.183-21.351-.051-21.623 6.137-25.336 9.18-.04-5.806-.013-9.292-.013-9.292l-14.879 2.557zm92.002 8.292c-.158-.074-8.526-9.788-25.35-9.864-15.758-.262-33.433 5.847-33.716 32.27.138 23.232 16.979 32.311 33.805 32.488 18.007.187 25.172-11.26 25.602-11.543-2.149-1.863-10.196-9.837-10.196-9.837s-5.027 7.157-14.779 7.248c-9.755.093-18.234-7.54-18.354-18.189-.125-10.65 7.795-16.419 18.427-16.885 9.205-.002 14.516 5.943 14.516 5.943zm20.606-30.399l-15.434 3.628.115 82.277 15.204-2.745.172-38.72c.033-4.06 5.874-10.295 15.626-10.097 9.325.097 11.41 6.215 11.384 6.988l.269 44.824 14.993-2.65.057-47.53c.099-4.574-10.018-14.233-26.28-14.302-7.729.012-12.009 1.762-14.187 3.052-3.726 2.879-7.985 5.637-12.17 9.157 3.869-4.97 7.117-8.407 10.29-10.961l-.04-22.921z" fill="#fff" fill-rule="evenodd"/><path d="M360.136 17.218l6.962-1.742.33 82.95-7.074 1.204zm18.928 24.757l6.101-2.716.052 59.478-5.892 1.217zm-1.45-21.448l4.92-4.015 4.086 4.547-4.921 4.121zm19.024 20.365l6.962-1.421.033 12.434c.001.534 3.823-13.89 22.258-13.57 17.9.1 20.827 13.957 20.73 17.064l.221 43.725-6.102 1.324-.035-43.189c.07-1.261-2.79-11.927-15.439-11.966-12.646-.037-21.409 9.186-21.393 15.078l.1 38.047-7.07 1.847zm110.954 58.546l-6.962 1.42-.033-12.433c-.001-.534-3.825 13.89-22.258 13.57-17.9-.1-20.827-13.957-20.73-17.064l-.221-43.725 7.397-1.494.114 43.19c.003 1.18 1.416 12.096 14.065 12.135 12.646.037 21.506-7.616 21.569-19.139l-.09-34.076 6.885-1.757zm13.645-59.037l-4.882 3.82 18.717 24.494-19.963 28.3 5.179 3.843 18.766-26.28 19.368 26.902 4.791-3.82-20.757-28.765 16.56-23.262-5.092-4.305-15.085 21.525zM61.88 1.778c-5.385 13.203-8.633 21.839-14.629 34.649 3.676 3.896 8.188 8.434 15.516 13.559-7.878-3.242-13.252-6.497-17.267-9.874-7.673 16.011-19.695 38.818-44.09 82.65 19.174-11.068 34.037-17.893 47.889-20.497a35.103 35.103 0 0 1-.91-8.213l.023-.614c.304-12.284 6.694-21.73 14.264-21.09 7.57.642 13.454 11.126 13.15 23.41-.058 2.312-.319 4.536-.774 6.598 13.701 2.68 28.405 9.487 47.32 20.407-3.73-6.866-7.059-13.056-10.238-18.95-5.007-3.882-10.23-8.933-20.884-14.402 7.323 1.903 12.566 4.099 16.653 6.552C75.58 35.786 72.963 27.79 61.88 1.778z" fill="#1793d1" fill-rule="evenodd"/><path d="M576.771 93.265V80.603h-4.73v-1.695h11.38v1.695h-4.75v12.662h-1.9m8.629 0V78.908h2.859l3.398 10.166c.314.947.542 1.655.686 2.125.163-.522.418-1.29.764-2.301l3.437-9.99h2.556v14.357h-1.831V81.25l-4.172 12.016h-1.714l-4.152-12.222v12.222h-1.832" font-weight="400" font-size="8.441" font-family="DejaVu Sans Mono" fill="gray"/></svg> \ No newline at end of file
diff --git a/style/ArchLinux32/archnavbar.css b/style/ArchLinux32/archnavbar.css
new file mode 100644
index 0000000..dbfb09e
--- /dev/null
+++ b/style/ArchLinux32/archnavbar.css
@@ -0,0 +1,76 @@
+/*
+ * ARCH GLOBAL NAVBAR
+ *
+ * We're forcing all generic selectors with !important
+ * to help prevent other stylesheets from interfering.
+ *
+ */
+
+/* container for the entire bar */
+#archnavbar { height: 40px !important; padding: 10px 15px !important; background: #333 !important; border-bottom: 5px #08c solid !important; }
+
+#archnavbarlogo { float: left !important; margin: 0 !important; padding: 0 !important; height: 40px !important; width: 190px !important; }
+html > body #archnavbarlogo { background: url('arch32logo.png') no-repeat !important; background-size: 190px 40px !important;}
+
+/* move the heading/paragraph text offscreen */
+#archnavbarlogo p { margin: 0 !important; padding: 0 !important; text-indent: -9999px !important; }
+#archnavbarlogo h1 { margin: 0 !important; padding: 0 !important; text-indent: -9999px !important; }
+
+/* make the link the same size as the logo */
+#archnavbarlogo a { display: block !important; height: 40px !important; width: 190px !important; }
+
+/* display the list inline, float it to the right and style it */
+#archnavbar ul { display: inline !important; float: right !important; list-style: none !important; margin: 0 !important; padding: 0 !important; }
+#archnavbar ul li { float: left !important; font-size: 14px !important; font-family: sans-serif !important; line-height: 45px !important; padding-right: 15px !important; padding-left: 15px !important; }
+
+/* style the links */
+#archnavbar ul#archnavbarlist li a { color: #999; font-weight: bold !important; text-decoration: none !important; }
+#archnavbar ul li a:hover { color: white !important; text-decoration: underline !important; }
+
+@media (max-width: 680px) {
+ #anb-forum,
+ #anb-forums {
+ display: none;
+ }
+
+ html > body #archnavbarlogo {
+ width: 40px !important;
+ margin-right: 5px !important;
+ background: url('archicon.svg') no-repeat !important;
+ background-size: 40px 40px !important;
+ }
+
+ #archnavbar ul {
+ display: flex !important;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+ overflow: hidden;
+ float: none !important;
+ }
+
+ #archnavbar ul li {
+ padding: 0 !important;
+ float: none !important;
+ }
+}
+
+@media (max-width: 830px) {
+ #anb-home,
+ #anb-start {
+ display: none;
+ }
+
+ html > body #archnavbarlogo {
+ padding-right: 15px !important;
+ }
+
+ #archnavbar {
+ padding-left: 5px !important;
+ padding-right: 5px !important;
+ }
+
+ #archnavbar ul li {
+ padding-left: 5px !important;
+ padding-right: 5px !important;
+ }
+}
diff --git a/style/ArchLinux32/archnavbar.php b/style/ArchLinux32/archnavbar.php
new file mode 100644
index 0000000..17a4b0b
--- /dev/null
+++ b/style/ArchLinux32/archnavbar.php
@@ -0,0 +1,18 @@
+<?php global $arch_home, $arch_navbar, $arch_navbar_selected; ?>
+
+<div id="archnavbar" class="anb-forum">
+ <div id="archnavbarlogo">
+ <div id="archnavbarlogo"><h1><a href="https://www.archlinux32.org">Arch Linux</a></h1></div>
+ </div>
+ <div id="archnavbarmenu">
+ <ul id="archnavbarlist">
+ <li id="anb-home"><a href="https://www.archlinux32.org/">Home</a></li>
+ <li id="anb-packages"><a href="https://packages.archlinux32.org/">Packages</a></li>
+ <li id="anb-forums" class="anb-selected"><a href="https://bbs.archlinux32.org/">Forums</a></li>
+ <li id="anb-bugs"><a href="https://bugs.archlinux32.org/">Bugs</a></li>
+ <li id="anb-mailing list"><a href="https://lists.archlinux.org/listinfo/arch-ports">Mailing List</a></li>
+ <li id="anb-download"><a href="https://archlinux32.org/download/">Download</a></li>
+ <li id="anb-arch linux official"><a href="https://www.archlinux.org/">Arch Linux Official</a></li>
+ </ul>
+ </div>
+</div>
diff --git a/style/ArchLinux32/base_admin.css b/style/ArchLinux32/base_admin.css
new file mode 100644
index 0000000..8136885
--- /dev/null
+++ b/style/ArchLinux32/base_admin.css
@@ -0,0 +1,177 @@
+#adminconsole .blockform .box {
+ padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+ position: relative;
+ overflow: hidden;
+}
+
+#adminconsole fieldset table {
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+ padding: 10px 8px 10px 0;
+ text-align: left;
+ white-space: normal;
+ border-style: solid none;
+ border-width: 1px 0;
+}
+
+#punadmin thead th {
+ border-top: 0;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+ display: block; font-size: 1em;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+ display: inline-block;
+}
+
+#adminconsole fieldset th {
+ width: 15em;
+ font-weight: normal;
+ padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+ vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+ padding-top: 3px;
+}
+
+#adminconsole .inform {
+ padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+ padding-bottom: 0;
+ padding-top: 0;
+}
+
+#adminconsole p.submittop {
+ text-align: center;
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin: 0 18px;
+ padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+ text-align: center;
+ padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+ padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+ padding: 10px 0 12px 0;
+}
+
+#adalerts {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #b9c5ce #d9e1e7;
+ background: #ebf1f5;
+ padding: 18px;
+}
+
+#adalerts p {
+ border: 1px solid #dfe6ee;
+ background: #ffffe1;
+ padding: 18px;
+}
+
+#categoryedit .tcl {
+ width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2 {
+ width: 20%;
+}
+
+#edforum .tcl {
+ width: 18%;
+}
+
+#edforum .tc2 {
+ width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+ text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+ padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+ display: block;
+ left: -9999em;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+ font-weight: bold;
+}
+
+#users2 th, #bans1 th {
+ text-align: left;
+}
+
+#users2 th.tcmod {
+ text-align: center;
+}
+
+#users2 .tcl, #bans1 .tcl {
+ width: auto;
+ text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+ width: 18%;
+ text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+ width: 12%;
+ text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+ width: 10%;
+ text-align: center;
+}
+
+#users2 .tcr {
+ width: 20%;
+ white-space: nowrap;
+}
+
+#bans1 .tcr {
+ width: 15%;
+ white-space: nowrap;
+}
+
+#users2 .tcmod {
+ width: 10%;
+ text-align: center;
+}
+
+.plugin p {
+ padding: 12px 18px 0;
+}
diff --git a/style/ArchLinux32/css.php b/style/ArchLinux32/css.php
new file mode 100644
index 0000000..a963629
--- /dev/null
+++ b/style/ArchLinux32/css.php
@@ -0,0 +1,7 @@
+<?php
+
+foreach (array('arch', 'archnavbar') as $cssFile) {
+ ?>
+ <link rel="stylesheet" media="screen" href="style/ArchLinux/<?= $cssFile ?>.css?v=5"/>
+ <?php
+}
diff --git a/style/ArchLinux32/favicon.ico b/style/ArchLinux32/favicon.ico
new file mode 100644
index 0000000..8ef6f13
--- /dev/null
+++ b/style/ArchLinux32/favicon.ico
Binary files differ
diff --git a/style/ArchLinux32/help.tpl b/style/ArchLinux32/help.tpl
new file mode 100644
index 0000000..50bd2b4
--- /dev/null
+++ b/style/ArchLinux32/help.tpl
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux32/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punhelp" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux32/index.html b/style/ArchLinux32/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/ArchLinux32/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/ArchLinux32/main.tpl b/style/ArchLinux32/main.tpl
new file mode 100644
index 0000000..d1f3ae2
--- /dev/null
+++ b/style/ArchLinux32/main.tpl
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux32/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="pun<pun_page>" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+ <div class="box">
+ <pun_navlinks>
+ <pun_status>
+ </div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux32/maintenance.tpl b/style/ArchLinux32/maintenance.tpl
new file mode 100644
index 0000000..74f7a0d
--- /dev/null
+++ b/style/ArchLinux32/maintenance.tpl
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux32/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punmaint" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_maint_main>
+</div>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/ArchLinux32/redirect.tpl b/style/ArchLinux32/redirect.tpl
new file mode 100644
index 0000000..a3f6e12
--- /dev/null
+++ b/style/ArchLinux32/redirect.tpl
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<pun_head>
+<pun_include "css.php">
+<link rel="shortcut icon" href="style/ArchLinux32/favicon.ico" />
+</head>
+
+<body>
+<pun_include "archnavbar.php">
+
+<div id="punredirect" class="pun">
+<div class="top-box"></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_redir_main>
+</div>
+
+<pun_footer>
+
+<pun_include "archfooter.php">
+
+</div>
+<div class="end-box"></div>
+</div>
+
+</body>
+</html>
diff --git a/style/Cobalt.css b/style/Cobalt.css
new file mode 100644
index 0000000..aff1091
--- /dev/null
+++ b/style/Cobalt.css
@@ -0,0 +1,1150 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+.pun ul, .pun ol {
+ list-style: none
+ }
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+ }
+
+.pun .clearer, .pun .clearb {
+ clear: both
+ }
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+ }
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+ }
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+ }
+
+.clearl {
+ clear: left;
+ }
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+ }
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+ line-height: normal;
+ }
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: verdana, helvetica, arial, sans-serif;
+ }
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+ }
+
+.pun pre code {
+ font-size: 1em;
+ }
+
+.pun strong {
+ font-weight: bold;
+ }
+
+.pun em {
+ font-style: italic;
+ }
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+ }
+
+.pun h2 {
+ font-size: 1em;
+ font-weight: normal;
+ padding: 4px 6px;
+ }
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+ }
+
+.pun table p, .pun table h3 {
+ padding: 0;
+ }
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+ }
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.75em 0
+ }
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+ }
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+ }
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+ }
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+ }
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+ }
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+ }
+
+.pun span.bbu {
+ text-decoration: underline
+ }
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+ }
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+ }
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+html, body {
+ margin: 0;
+ padding: 0
+ }
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+ }
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%
+ }
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+ }
+
+#brdtitle p {
+ padding-top: 0px
+ }
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+ }
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+ }
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+ }
+
+#postreview .blockpost {
+ margin-bottom: -1px;
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+ }
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+ }
+
+.pun .postlinksb {
+ margin-top: -6px
+ }
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+ }
+
+#brdheader .box {
+ border-top-width: 4px;
+ }
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+ }
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 3px 0 0 0;
+ }
+
+#brdmenu li {
+ display: inline;
+ margin-right: 12px;
+ }
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none
+ }
+
+#brdmenu a:hover, #brdmenu a:active {
+ text-decoration: underline
+ }
+
+#brdwelcome .conl {
+ float: left;
+ }
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+ }
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+ }
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+ }
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+ }
+
+.pun .postlink {
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+ }
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+ }
+
+.pun .modbuttons input {
+ margin-left: 6px;
+ }
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ text-decoration: none
+ }
+
+.pun .postlink a:hover, .pun .postlink a:active {
+ text-decoration: underline;
+ }
+
+#punindex .subscribelink {
+ margin-top: 6px;
+ }
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+ float: left;
+ }
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+ }
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+ }
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin:0 6px;
+ }
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+ }
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+ }
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+ }
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ }
+
+.pun .blocktable table {
+ table-layout: fixed;
+ }
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+.pun td, .pun th {
+ border-style: solid none none solid;
+ border-width: 1px;
+ }
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+ }
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+ }
+
+.pun .tcr {
+ width: 30%;
+ }
+
+.pun .tcl h3 {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .tcl h3 span.newtext {
+ font-size: 0.917em;
+ }
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap;
+ font-weight: normal;
+ }
+
+.pun td span.byuser {
+ white-space: nowrap;
+ }
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+ }
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#users1 .tcr {
+ width: 25%
+ }
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#debug .tcl {
+ width: 10%
+ }
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+ }
+
+#punindex .tcr .byuser {
+ display: block
+ }
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+ }
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .icon {
+ margin: 0.1em 0 0 0.2em;
+ border-width: 0.6em;
+ border-style: solid;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ float: left;
+ }
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+ }
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+ }
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+ PADDING: 20px 20px 15px 20px
+ }
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+ }
+
+.pun .forminfo h3 {
+ font-weight: bold;
+ }
+
+.pun .inform {
+ padding-bottom: 12px
+ }
+
+.pun fieldset {
+ padding: 0px 12px 0px 12px;
+ border-style: solid;
+ border-width: 1px
+ }
+
+.pun legend {
+ padding: 0px 6px
+ }
+
+.pun .infldset {
+ padding: 9px 0px 12px 0
+ }
+
+.pun label {
+ display: block;
+ padding: 3px 0
+ }
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+ }
+
+.pun fieldset .rbox br {
+ display: none;
+ }
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun .txtarea {
+ width: 75%
+ }
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+ }
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+ }
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+ }
+
+.pun .blockform .buttons {
+ padding-left: 12px;
+ }
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+ }
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+ }
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+ }
+
+.pun p.actions span {
+ margin-right: 12px;
+ }
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+ }
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.3em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+ }
+
+.pun .checklist fieldset {
+ border: 0;
+ padding: 0;
+ }
+
+.pun .checklist legend {
+ padding: 0;
+ }
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+ }
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+ }
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+ }
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+ }
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+ }
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+ }
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+ }
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+ }
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+ }
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+ }
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+ }
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+ }
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+ }
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+ }
+
+.pun .blockpost .box {
+ overflow: hidden;
+ }
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ position: relative;
+ overflow: hidden;
+ }
+
+.pun .postleft dl {
+ padding: 6px;
+ }
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+ }
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+ }
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+ }
+
+#postpreview .postright {
+ border-left: 0
+ }
+
+.pun .postright {
+ padding: 0 6px;
+ }
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+ }
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+ }
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 10px 6px 5px 6px;
+ }
+
+.pun .postfootright li {
+ display: inline;
+ }
+
+.pun .postfootright li:before {
+ content: " | ";
+ }
+
+.pun .postfootright li:first-child:before {
+ content: "";
+ }
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+ }
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+ }
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+ }
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+ }
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ }
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+ }
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+ }
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+ }
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+ }
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+ }
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+ }
+
+.pun .postmsg img {
+ vertical-align: bottom;
+ }
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+ }
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+ }
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ }
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+ }
+
+#punhelp div.box {
+ padding: 10px
+ }
+
+#debugtime {
+ margin-top: -12px;
+ text-align: center;
+ }
+
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+ line-height: 1.4em
+ }
+
+#announce div.inbox div {
+ padding: 3px 0
+ }
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #2a2a2a;
+ color: #d4d4d4
+ }
+
+.pun {
+ color: #d4d4d4
+ }
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #383838
+ }
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+ background-color: #424242
+ }
+
+.pun h2, #brdmenu {
+ background-color: #565656;
+ color: #d4d4d4
+ }
+
+.pun th {
+ background-color: #484848
+ }
+
+.pun legend {
+ color: #60a0dc
+ }
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #d4d4d4
+ }
+
+.pun .usercontent * {
+ background: transparent;
+ color: #d4d4d4
+ }
+
+.pun textarea, .pun input, .pun select {
+ background-color: #2a2a2a;
+ color: #d4d4d4
+ }
+
+.pun .multiselect, .pun .checklist {
+ color: #D4D4D4;
+ }
+
+.pun .checklist {
+ border-color: #666;
+ }
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #383838
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #424242
+ }
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #424242
+ }
+
+.pun .blockpost h2 {
+ background-color: #565656
+ }
+
+.pun .blockpost h2 span.conr {
+ color: #a19e96
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+ }
+
+.pun hr {
+ background-color: #606060;
+ color: #606060
+ }
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-color:#565656
+ }
+
+.pun td, #brdfooter #modcontrols {
+ border-color: #565656
+ }
+
+.pun th {
+ border-color: #484848
+ }
+
+.pun fieldset {
+ border-color: #606060
+ }
+
+#adminconsole td, #adminconsole th {
+ border-color: #383838
+ }
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+ border-color: #606060
+ }
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #60a0dc
+ }
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #80d6ff
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #60a0dc;
+ }
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #80d6ff;
+ }
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+ color: #d4d4d4
+ }
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+ color: #d4d4d4
+ }
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+ }
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+ }
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #ff4000
+ }
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #ff5010
+ }
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #484848 #404040 #3c3c3c #444444
+ }
+
+.pun .iredirect .icon {
+ border-color: #383838 #383838 #383838 #383838
+ }
+
+.pun .inew .icon {
+ border-color: #5496d8 #4b85c0 #4377ac #4f8dcb
+ }
diff --git a/style/Earth.css b/style/Earth.css
new file mode 100644
index 0000000..2590c99
--- /dev/null
+++ b/style/Earth.css
@@ -0,0 +1,1650 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3, .pun h4, .pun h5, .pun pre, .pun blockquote,
+.pun ul, .pun ol, .pun li, .pun dl, .pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun legend .pun img,
+.pun abbr, .pun cite {
+ border: 0;
+ font-style: normal;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+.pun ul, .pun ol {
+ list-style: none;
+}
+
+.pun select {
+ padding-bottom: 1px;
+ padding-top: 1px;
+ padding-right: 1px;
+}
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 81.25%/1.462em Arial, Helvetica, sans-serif;
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun legend {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 1em;
+}
+
+.pun pre, .pun code {
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace;
+ font-size: 1em;
+}
+
+.pun pre code {
+ font-size: 1em;
+}
+
+.pun table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 0;
+ empty-cells: show;
+ width: 100%;
+}
+
+.pun h1 {
+ font:2.154em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+ padding: 7px 0;
+}
+
+.pun h2, .pun .hd h2 {
+ font: 1.462em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+ padding: 7px 0;
+}
+
+.pun h3 {
+ font-size: 1.154em;
+ line-height: 1.267em;
+ padding: 7px 0;
+}
+
+.pun h4 {
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun h5, .pun h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun p, .pun ul, .pun ol, .pun dl, .pun th, .pun td, .pun legend {
+ padding: 7px 0;
+}
+
+.pun strong, .pun th, .pun span.warntext, .pun p.warntext {
+ font-weight: bold;
+}
+
+.pun em {
+ font-style: italic;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+ text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ text-decoration: underline;
+}
+
+.pun .actions span {
+ padding-left: 16px;
+ padding-right: 8px;
+ background: url(Earth/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #debug h2, #brdstats .conl dt, #brdstats .conr dt, #modcontrols dt,
+#searchlinks dt, div.postright h3, .pun .subscribelink span, #announce .hd, #reportform h2, #punmoderate #vf h2,
+#punviewforum #vf h2, .pun .required strong span, .pun .icon div {
+ display: block;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+/* Generic Float Clear
+----------------------------------------------------------------*/
+
+.pun .inbox, .pun #brdmain, .pun .crumbs, .pun .pagepost, .pun .block2col {
+ min-height: 1px;
+}
+
+* html .pun .inbox, * html .pun #brdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+ display: inline-block;
+}
+
+* html .pun .inbox, * html .pun #bdrdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+ display: block;
+}
+
+.pun .inbox:after, .pun #brdmain:after, .pun .crumbs:after, .pun .pagepost:after, .pun .block2col:after {
+ content: " ";
+ display: block;
+ height: 0;
+ font-size: 0;
+ clear: both;
+ visibility: hidden;
+}
+
+.pun .block2col .inbox:after {
+ content: none;
+ clear: none;
+}
+
+.clearl {
+ clear: left;
+}
+
+/*****************************************************************
+2. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+ max-width: 1070px;
+ margin: 0 auto;
+ padding: 30px 40px;
+}
+
+#punredirect, #punmaint {
+ padding: 60px 20% 12px 20%;
+}
+
+#puninstall, #pundb_update {
+ padding: 20px 10%;
+}
+
+.pun .punwrap {
+ border: 1px solid;
+ border-radius: 10px;
+ padding: 18px;
+}
+
+#punredirect h2, #punmaint h2 {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin-bottom: 3px;
+}
+
+/* Section Spacing and Borders
+----------------------------------------------------------------*/
+
+#brdmain {
+ border-style: solid none;
+ border-width: 2px 0;
+ margin-bottom: 12px;
+ padding: 12px 0;
+}
+
+#punindex #brdmain {
+ padding-top: 24px;
+}
+
+#punredirect #brdmain, #punmaint #brdmain {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+#brdstats {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ margin-top: 24px;
+ padding-top: 12px;
+}
+
+#quickpost {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ margin-top: 12px;
+ padding-top: 12px;
+}
+
+#announce {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ padding-top: 3px;
+}
+
+/*****************************************************************
+3. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Logo, Description and Main Menu
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ padding: 0 0 10px 0;
+}
+
+#brddesc {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ padding: 10px 0;
+}
+
+#brddesc p {
+ padding: 0;
+}
+
+#brdmenu ul {
+ padding: 0;
+}
+
+#brdmenu li {
+ float: left;
+}
+
+#brdmenu a:link, #brdmenu a:visited {
+ border-right-style: solid;
+ border-width: 1px;
+ display: block;
+ min-width: 60px;
+ padding: 12px 16px 6px 8px;
+ white-space: nowrap;
+}
+
+#brdmenu a:hover, #brmenu a:active, #brdmenu a:focus {
+ text-decoration: none;
+}
+
+/* Welcome Box
+----------------------------------------------------------------*/
+
+#brdwelcome {
+ padding: 10px 0;
+}
+
+#brdwelcome .conl, #brdwelcome .conr, #brdwelcome p, #brdwelcome li {
+ display: inline;
+ padding: 0;
+}
+
+#brdwelcome .conl {
+ float: left;
+}
+
+#brdwelcome .conr {
+ float: right;
+}
+
+#brdwelcome li span {
+ background: url(Earth/img/bull.png) center left no-repeat;
+ padding-left: 18px;
+ margin-right: 3px;
+ display: inline-block;
+ line-height: normal;
+ white-space: nowrap;
+}
+
+#brdwelcome .conl li:first-child span {
+ padding-left: 0;
+ background: none;
+}
+
+/* Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+}
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+}
+
+#brdstats #onlinelist {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ clear: both;
+}
+
+#brdstats #onlinelist dt, #brdstats #onlinelist dd {
+ display: inline;
+}
+
+/* Footer
+----------------------------------------------------------------*/
+
+.pun #modcontrols {
+ border-style: none none dotted none;
+ border-width: 0 0 1px 0;
+ margin-bottom: 4px;
+ text-align: center;
+ width: 100%;
+}
+
+.pun #modcontrols dd {
+ display: inline;
+}
+
+.pun #brdfooter #modcontrols dd span {
+ background: url(Earth/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #brdfooter .conl {
+ float: left;
+}
+
+.pun #brdfooter .conr {
+ text-align: right;
+ float: right;
+}
+
+.pun #brdfooter #poweredby a {
+ font-size: 1.077em;
+ font-weight: bold;
+}
+
+.pun #brdfooter #qjump {
+ padding-top: 5px;
+}
+
+.pun #brdfooter #qjump * {
+ white-space: nowrap;
+}
+
+.pun #brdfooter #searchlinks dd span {
+ background: url(Earth/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #brdfooter #feedlinks {
+ padding-bottom: 0;
+}
+
+.pun #brdfooter #feedlinks span {
+ background: url(Earth/img/feed.png) center left no-repeat;
+ display: inline-block;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #debugtime {
+ border-style: dotted none none none;
+ border-width: 1px 0 0 0;
+ margin-top: 7px;
+ text-align: center;
+}
+
+/* Breadcrumbs, Postlink, Pagination
+----------------------------------------------------------------*/
+
+.pun .linkst .inbox, .pun .linksb .inbox, .pun .postlinksb .inbox {
+ overflow: hidden;
+}
+
+.pun .linksb, .pun .postlinksb, .pun .linkst, .pun .crumbs {
+ clear: both;
+ position: relative;
+}
+
+.pun .linkst .crumbs {
+ font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+ font-size: 1.462em;
+ line-height: 1.211em;
+ padding: 7px 0;
+}
+
+.pun .linksb .crumbs, .pun .postlinksb .crumbs {
+ font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+ font-size: 1.154em;
+}
+
+.pun .linkst .crumbsplus .pagepost {
+ border-top-style: dotted;
+ border-top-width: 1px;
+}
+
+.pun .linksb .crumbsplus .pagepost, .pun .postlinksb .crumbsplus .pagepost {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+}
+
+.pun .postlinksb .crumbs {
+ margin-right: 11em;
+}
+
+.pun .crumbs li {
+ float: left;
+ padding-right: 0.4em;
+ white-space: nowrap;
+}
+
+.pun .crumbs li strong {
+ font-weight: normal;
+}
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+}
+
+.pun .pagelink strong, .pun .pagelink a, .pun .pagelink span.spacer {
+ border-style: none none none solid;
+ border-width: 0 0 0 1px;
+ display: inline-block;
+ padding: 0 12px 0 10px;
+ margin-right: -6px;
+}
+
+.pun .pagelink .item1 {
+ border: 0;
+}
+
+.pun .pagelink .pages-label {
+ display: inline-block;
+}
+
+.pun .postlink {
+ float: right;
+ font-weight: bold;
+ text-align: right;
+}
+
+.pun .modbuttons {
+ float: right;
+ padding: 5px 0 3px 0;
+}
+
+.pun .modbuttons input {
+ margin-left: 8px;
+}
+
+.pun .subscribelink {
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 33px;
+}
+
+#punindex .subscribelink {
+ top: 0px;
+}
+
+#punindex .linksb {
+ height: 12px;
+}
+
+/*****************************************************************
+4. MAIN TABLES
+*****************************************************************/
+
+.pun #brdmain .blocktable {
+ position: relative;
+}
+
+#punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ margin: 1px 1px 0 1px;
+ padding-left: 8px;
+ position: absolute;
+ left: 0;
+ white-space: nowrap;
+ z-index: 100;
+}
+
+#punindex .blocktable th.tcl, #punsearch #vf th.tcl {
+ font-size: 0;
+ text-indent: -9999em;
+}
+
+.pun .blocktable .box {
+ border-style: solid;
+ border-width: 1px;
+ margin-bottom: -1px;
+ overflow: hidden;
+ position: relative;
+}
+
+* html .pun .blocktable .box {
+ display: inline-block;
+}
+
+.pun .blocktable table {
+ table-layout: fixed;
+ margin-bottom: -1px;
+}
+
+.pun .blocktable th {
+ padding: 7px 8px;
+ border-style: none none solid none;
+ border-width: 1px;
+ text-align: left;
+}
+
+.pun .blocktable td {
+ padding: 7px 8px;
+ line-height: 1.3077em;
+ border-style: none none solid none;
+ border-width: 1px;
+ text-align: left;
+}
+
+.pun .blocktable h3 {
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 0;
+}
+
+.pun .blocktable p {
+ padding: 0;
+}
+
+.pun .blocktable .tcl p {
+ padding: 5px 0 0 0;
+}
+
+.pun .blocktable .tcl {
+ width: auto;
+}
+
+.pun .blocktable .tc2, .pun .blocktable .tc3, .pun .blocktable .tcmod {
+ padding-left: 0;
+ padding-right: 0;
+ text-align: center;
+ width: 11%;
+}
+
+.pun .blocktable .tcr {
+ width: 30%;
+}
+
+.pun .blocktable td .newtext, .pun .blocktable td .pagestext, .pun .blocktable td .byuser {
+ white-space: nowrap;
+}
+
+.pun .blocktable .tcl h3 span.newtext {
+ font-size: 0.929em;
+ font-weight: normal;
+}
+
+.pun #vf td.tcl span.stickytext, .pun #vf td.tcl span.closedtext {
+ font-size: 1em;
+ font-weight: bold;
+}
+
+#punsearch #vf .tc2 {
+ padding-left: 8px;
+ padding-right: 8px;
+ text-align: left;
+ width: 18%;
+}
+
+#users1 .tcr {
+ width: 25%;
+}
+
+#users1 .tc2 {
+ padding-left: 8px;
+ padding-right: 8px;
+ text-align: left;
+ width: 25%;
+}
+
+#debug {
+ margin-top: 12px;
+}
+
+#debug .tcl {
+ width: 10%;
+}
+
+#punredirect #debug .tcl, #punmaint #debug .tcl {
+ width: 20%;
+}
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal;
+}
+
+#punindex .tcr .byuser {
+ display: block;
+}
+
+#punindex td.tc2, #punindex td.tc3, #punindex td.tcr, .pun #vf td.tc2, .pun #vf td.tc3,
+.pun #vf td.tcr, #punindex td.tcl div.forumdesc, .pun #vf td.tcl span {
+ font-size: 0.923em;
+}
+
+.pun #vf td.tcl a {
+ font-weight: bold;
+}
+
+.pun #vf td.tcl span a {
+ font-weight: normal;
+}
+
+.pun .blocktable .tclcon {
+ min-height: 1px;
+ overflow: hidden;
+ padding: 0 11px 0 12px;
+ position: relative;
+}
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+}
+
+.pun .icon {
+ border-style: solid;
+ border-width: 8px;
+ float: left;
+ height: 0;
+ overflow: hidden;
+ width: 0;
+}
+
+.pun .iposted .ipost {
+ font-weight: bold;
+ left: 0;
+ padding-left: 4px;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ width: 8px;
+}
+
+/*****************************************************************
+MAIN POSTS
+*****************************************************************/
+
+/* Structure
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+ border-style: solid;
+ border-width: 1px;
+ margin-bottom: -1px;
+ overflow: hidden;
+ position: relative;
+}
+
+* html .pun .blockpost {
+ display: inline-block;
+}
+
+.pun .blockpost h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ white-space: nowrap;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ height: 1.462em;
+ padding: 0.538em 8px 0.538em 236px;
+ font-weight: normal;
+}
+
+#punsearch .blockpost h2 {
+ height: auto;
+ padding-left: 36px;
+ white-space: normal;
+}
+
+#punsearch .blockpost h2 span span {
+ white-space: nowrap;
+ display: inline-block;
+ font: 1.077em "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+#punsearch .blockpost .icon {
+ position: absolute;
+ top: 0;
+ margin-top: -2.154em;
+}
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+}
+
+.pun .blockpost .inbox {
+ float: right;
+ position: relative;
+ width: 100%;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+ border-left-style: solid;
+ border-left-width: 1px;
+ float: right;
+ margin-right: -218px;
+ position: relative;
+ text-align: left;
+ width: 100%;
+}
+
+.pun .blockpost .postleft, .pun .blockpost .postfootleft {
+ width: 194px;
+ padding: 7px 12px 7px 12px;
+ float: left;
+ margin-left: -218px;
+ position: relative;
+}
+
+.pun .blockpost .postleft dl {
+ padding: 0;
+}
+
+#punviewtopic .blockpost dt, #punmoderate .blockpost dt {
+ display: block;
+ position: absolute;
+ padding: 0.538em 0 0.538em 12px;
+ height: 1.462em;
+ top: -2.615em;
+ left: 0;
+ overflow: hidden;
+ width: 206px;
+}
+
+.pun .blockpost dt {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.pun .blockpost dt strong {
+ font-size: 1.231em;
+ font-weight: bold;
+}
+
+.pun .blockpost .postleft dd {
+ font-size: 0.923em;
+}
+
+.pun .blockpost .postleft .usertitle {
+ padding: 4px 0 6px 0;
+ font-size: 1em;
+}
+
+.pun .blockpost .postleft .postavatar {
+ display: block;
+ margin: 0 0 4px 0;
+}
+
+.pun .blockpost .postright {
+ position: relative;
+ padding: 4px 230px 7px 18px;
+}
+
+.pun .postmsg {
+ width:100%;
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
+.pun .blockpost .postfootright {
+ position: relative;
+ padding: 7px 230px 7px 18px;
+ text-align: right;
+}
+
+.pun .postfoot p, .pun .postfoot ul {
+ padding: 0;
+}
+
+.pun .blockpost .postfootright li {
+ display: inline;
+}
+
+.pun .blockpost .postfootright li span {
+ display: inline-block;
+ padding-left: 16px;
+ margin-left: 8px;
+ line-height: normal;
+ background: url(Earth/img/bull.png) center left no-repeat;
+}
+
+.pun .blockpost .usercontacts {
+ padding: 7px 0;
+}
+
+.pun .blockpost .usercontacts .email {
+ background: url(Earth/img/email.png) left 65% no-repeat;
+ margin-right: 5px;
+ padding-left: 21px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun .blockpost .usercontacts .website {
+ background: url(Earth/img/ext.png) left 65% no-repeat;
+ padding-left: 18px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun .postsignature hr {
+ border:none;
+ height: 1px;
+ margin-left: 0px;
+ text-align: left;
+}
+
+/* Content (includes other user content)
+----------------------------------------------------------------*/
+
+.pun .usercontent {
+ padding: 7px 0;
+}
+
+.pun .postmsg p, .pun .postmsg li, #punhelp p samp {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .usercontent h1, .pun .usercontent h2, .pun .usercontent h3,
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ padding: 7px 0 0 0;
+}
+
+.pun .postmsg h5, #punhelp h5 {
+ font-size: 1.231em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+ list-style: disc;
+ padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+ list-style: decimal;
+ padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha;
+}
+
+.pun .usercontent li, .pun .postmsg li {
+ padding: 0 3px;
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0;
+}
+
+.pun span.bbu {
+ text-decoration: underline;
+}
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+}
+
+.pun .blockpost .postmsg .postedit {
+ font-size: 0.857em;
+}
+
+.pun .blockform .postsignature, .pun .blockpost .postsignature {
+ font-size: 0.923em;
+}
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+}
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px 1px 1px 3px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ font-weight: bold;
+ line-height: 1.462em;
+}
+
+.pun .quotebox blockquote {
+ overflow: hidden;
+ width: 100%;
+}
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ direction: ltr;
+ text-align: left;
+}
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+*:first-child+html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+.pun .codebox pre code {
+ padding: 0.5em;
+ white-space: pre;
+}
+
+.pun div[class*=codebox] pre code {
+ display: inline-block;
+}
+
+* html .pun .codebox pre code {
+ display: block;
+}
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto;
+}
+
+.pun .postmsg img, #punhelp samp img {
+ vertical-align: text-top;
+}
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+}
+
+/*****************************************************************
+MAIN FORMS
+*****************************************************************/
+
+#punedit .blockform h2, #punpost .blockform h2, #postpreview h2, #posterror h2,
+.pun #quickpost h2, .pun #reportform h2, #pundelete .blockform h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ white-space: nowrap;
+ padding: 10px 19px 4px 37px;
+ border: 0;
+}
+
+#punpost .blockform h2, #punedit .blockform h2,.pun #quickpost h2,
+#pundelete .blockform h2 {
+ margin: 1px 1px 0 1px;
+ width: 25em;
+ position: absolute;
+ z-index: 100;
+}
+
+.pun #quickpost legend, #punpost legend, #punedit legend {
+ width: 25em;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.pun .blockform .box {
+ border-style: solid;
+ border-width: 1px;
+ padding-bottom: 12px;
+}
+
+.pun #posterror {
+ border-style: solid;
+ border-width: 1px;
+}
+
+.pun #posterror .box {
+ padding: 0 18px 12px 18px;
+}
+
+* html .pun .blockform .box, * html .pun #posterror {
+ display: inline-block;
+}
+
+.pun .blockform .forminfo, .pun .error-info {
+ padding: 12px 18px;
+ border-style: solid;
+ border-width: 1px;
+ position: relative;
+}
+
+.pun .blockform .forminfo {
+ margin-top: 12px;
+}
+
+#pundelete .blockform .forminfo {
+ margin-top: 33px;
+}
+
+.pun .forminfo h3 {
+ padding-bottom: 0;
+}
+
+.pun .error-list li {
+ padding-left: 24px;
+ background: url(Earth/img/exclaim.png) center left no-repeat;
+}
+
+.pun .inform {
+ padding: 0 18px;
+}
+
+.pun legend {
+ font-weight: bold;
+ padding: 10px 19px 4px 19px;
+}
+
+* html .pun legend {
+ margin-left: -7px;
+}
+
+*:first-child+html .pun legend {
+ margin-left: -7px;
+}
+
+.pun .infldset {
+ border-style: solid;
+ border-width: 1px;
+ padding: 12px 18px;
+}
+
+#punregister #rules .infldset {
+ padding: 5px 18px;
+}
+
+.pun fieldset p {
+ padding: 0 0 7px 0;
+ width: 100%;
+}
+
+.pun fieldset .usercontent p {
+ padding: 7px 0;
+}
+
+.pun fieldset label {
+ display: block;
+ padding: 0 0 7px 0;
+}
+
+.pun label em {
+ font-weight: normal;
+ font-style: normal;
+}
+
+.pun .required strong {
+ background: url(Earth/img/asterisk.png) center right no-repeat;
+ font-weight: normal;
+ padding-right: 14px;
+ white-space: pre;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun label input, .pun label select, .pun label textarea {
+ margin-top: 2px;
+}
+
+.pun label.conl {
+ display: inline-block;
+ padding-right: 12px;
+}
+
+.pun form .buttons {
+ padding: 8px 19px 8px 34px;
+ margin-bottom: -12px;
+}
+
+.pun .blockform .buttons input {
+ margin-right: 12px;
+}
+
+.pun .rbox {
+ padding: 3px 0;
+}
+
+.pun .rbox label {
+ padding: 3px 0 3px 1.75em;
+ position: relative;
+ min-height: 1px;
+}
+
+* html .pun .rbox label {
+ text-indent: -3px;
+ height: 1%;
+}
+
+.pun .rbox input {
+ margin: 3px 0.75em 3px -1.75em;
+ float: left;
+ position: relative;
+ vertical-align: middle;
+ padding: 0;
+ height: 1em;
+ width: 1em;
+}
+
+.pun input[type=text], .pun input[type=password], .pun select, .pun textarea {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 98%;
+}
+
+.pun textarea {
+ resize: vertical;
+}
+
+.pun #quickpost .txtarea {
+ padding-right: 12px;
+ position: relative;
+}
+
+.pun .blockform .bblinks {
+ padding-top: 0;
+}
+
+.pun .blockform .bblinks li {
+ display: inline;
+}
+
+.pun .blockform .bblinks li span {
+ background: url(Earth/img/help.png) center left no-repeat;
+ margin-right: 8px;
+ padding-left: 20px;
+ display: inline-block;
+}
+
+.pun #quickpost .bblinks {
+ padding-top: 0;
+}
+
+.pun #quickpost .bblinks li {
+ display: inline;
+}
+
+.pun #login p.clearb {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ font-size: 0;
+ height: 0;
+ line-height: 0;
+ margin-top: 7px;
+ overflow: hidden;
+ padding-bottom: 3px;
+ padding-top: 7px;
+ text-indent: -9999em;
+ width: 100%;
+}
+
+.pun #postreview {
+ padding-top: 12px;
+}
+
+.pun #postpreview, .pun #posterror {
+ margin-bottom: 12px;
+}
+
+.pun #postpreview .postright {
+ padding: 0;
+}
+
+.pun #postpreview .postbody {
+ border-style: solid;
+ border-width: 1px;
+ float: none;
+ margin: 0 18px 12px 18px;
+ padding: 0;
+ padding: 4px 18px 4px 18px;
+ width: auto;
+}
+
+.pun span.email {
+ background: url(Earth/img/email.png) left 65% no-repeat;
+ margin-right: 5px;
+ padding-left: 21px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun span.website {
+ background: url(Earth/img/ext.png) left 65% no-repeat;
+ padding-left: 18px;
+ display: inline-block;
+ line-height: normal;
+}
+
+#punmisc #rules .box {
+ border-style: solid;
+ border-width: 1px;
+ padding: 5px 18px;
+}
+
+
+#punhelp .box {
+ border-style: solid;
+ border-width: 1px;
+ padding: 7px 12px;
+}
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+}
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.25em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+ padding: 0;
+}
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+}
+
+/*****************************************************************
+PROFILES (+ ADMIN MENU)
+*****************************************************************/
+
+/* Profile / Admin
+----------------------------------------------------------------*/
+
+.pun .blockmenu {
+ width: 13em;
+ float: left;
+ padding-bottom: 12px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 15em;
+}
+
+.pun .blockmenu .block2 {
+ padding-top: 19px;
+}
+
+.pun .blockmenu ul {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ padding: 0;
+}
+
+.pun .blockmenu li {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ font-weight: bold;
+ padding: 0;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ display: block;
+ padding: 9px 6px 3px 6px;
+ min-height: 1px;
+ text-decoration: none;
+}
+
+* html .pun .blockmenu a:link, * html .pun .blockmenu a:visited {
+ height: 1%;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+ text-decoration: none;
+}
+
+#viewprofile .box {
+ border-style: solid;
+ border-width: 1px;
+ padding-bottom: 18px;
+}
+
+#viewprofile dt, #adstats dt {
+ padding: 7px 0;
+ position: absolute;
+ width: 13em;
+ left: 0;
+}
+
+#viewprofile dl {
+ border-style: solid none none none;
+ border-width: 1px;
+ margin: 7px 0;
+ padding: 0;
+ width: 100%;
+ position: relative;
+}
+
+#adintro, #adstats {
+ border-style: solid;
+ border-width: 1px;
+ padding: 18px;
+}
+
+#adintro li span {
+ display: inline-block;
+ padding-left: 16px;
+ margin-left: 8px;
+ line-height: normal;
+ background: url(Earth/img/bull.png) center left no-repeat;
+}
+
+#adstats .inbox, #adintro .inbox {
+ border-style: solid;
+ border-width: 1px;
+ padding: 18px;
+}
+
+#adstats dl {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ position: relative;
+}
+
+#viewprofile dd, #adstats dd {
+ border-style: none none solid none;
+ border-width: 1px;
+ padding: 7px 0 7px 13em;
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Basic defaults and Common Items
+----------------------------------------------------------------*/
+
+html, body, .pun {
+ background: #eaede2;
+ color: #333;
+}
+
+.pun .punwrap {
+ background: #fff;
+ border-color: #ccd7c1;
+ color: #526550;
+}
+
+#brdtitle #brddesc, .pun .pagepost, #brdstats #onlinelist, #brdfooter #searchlinks, #brdfooter #modcontrols,
+#punmaint h2, #punredirect h2, #adminconsole .submittop, .pun #debugtime, .pun .pagelink a, .pun .pagelink * {
+ border-color: #bbc6b2;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+ color: #047E00;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #73A900;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #047E00;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #73A900;
+}
+
+/* Primary Navigation
+----------------------------------------------------------------*/
+
+#brdmenu {
+ background: #32671d;
+}
+
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited {
+ background: #32671d;
+ border-color: #fff;
+ color: #e3e3c8;
+}
+
+#brdmenu a:hover, #brdmenu a:active, #brdmenu a:focus {
+ background: #2ca100;
+ border-color: #fff;
+ color: #fff;
+}
+
+/* Main Tables
+----------------------------------------------------------------*/
+
+.pun .blocktable .box {
+ background: #fcfcf4;
+ border-color: #bbc6b2 #d8dccf;
+}
+
+#punindex .blocktable h2, .pun #vf h2 {
+ color: #83866a;
+}
+
+#adminconsole fieldset th, #adminconsole fieldset td {
+ background: #f6f6ea;
+ border-color: #dce6d8;
+}
+
+.pun #users1 h2 {
+ background: #fff;
+}
+
+.pun .blocktable td {
+ border-color: #dce6d8;
+}
+
+.pun .blocktable th {
+ background: #eaecda;
+ border-color: #ccd7c1;
+ color: #83866a;
+}
+
+.pun .blocktable td.tcl span.stickytext {
+ color: #c08b20;
+}
+
+/* Main Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+ background: #f6f6ea;
+ border-color: #bbc6b2 #d8dccf;
+}
+
+.pun .blockpost h2 {
+ background: #eaecda;
+ border-color: #ccd7c1;
+ color: #83866a;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+ background: #fcfcf4;
+ border-color: #dce6d8;
+}
+
+.pun .blockpost .postfootright li {
+ color: #fcfcf4;
+}
+
+.pun .postmsg, #punhelp code, #punhelp samp {
+ color: #333;
+}
+
+.pun .postsignature, .pun .postmsg .postedit {
+ color: #526550;
+}
+
+.pun .quotebox {
+ background: #f9fae5;
+ border-color: #bdbc7a;
+ color: #566579;
+}
+
+.pun .quotebox cite {
+ color: #83866a;
+}
+
+.pun .codebox, #punhelp .codebox code {
+ background: #333;
+ color: #fff;
+}
+
+.pun .postmsg hr {
+ background: #bbc6b2;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+}
+
+/* Main Forms + Profile
+----------------------------------------------------------------*/
+
+.pun .blockform .box, #adstats, #adintro, #postpreview, #posterror {
+ border-color: #bbc6b2 #d8dccf;
+ background: #eaecda;
+}
+
+#punmisc #rules .box, #punhelp .box {
+ border-color: #bbc6b2 #d8dccf;
+ background: #f6f6ea;
+}
+
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2,
+#pundelete .blockform h2 {
+ background: #eaecda;
+ color: #83866a;
+}
+
+.pun .forminfo {
+ background: #fff;
+ border-color: #dce6d8;
+}
+
+#puninstall form#install .forminfo {
+ background: #32671d;
+ color: #fff;
+}
+
+.pun #posterror .error-info {
+ background: #ffffe1;
+ border-color: #dfe6ee;
+}
+
+#puninstall form#install .error-info {
+ background: #ffffe1;
+ border-color: #dfe6ee;
+ color: #333;
+}
+
+.pun .infldset, #adintro .inbox, #adstats .inbox {
+ background: #f6f6ea;
+ border-color: #dce6d8;
+}
+
+.pun label, .pun legend, #adminconsole fieldset th {
+ color: #83866a;
+}
+
+.pun fieldset p {
+ border-color: #bbc6b2;
+}
+
+.pun .blockmenu ul, .pun .blockmenu li {
+ border-color: #bbc6b2;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+ background: #ffffe6;
+}
+
+.pun .blockmenu .isactive a:link, .pun .blockmenu .isactive a:visited {
+ color: #333;
+ background: #f6f6ea;
+}
+
+.pun #viewprofile .box {
+ border-color: #bbc6b2 #d8dccf;
+ background: #eaecda;
+}
+
+.pun #viewprofile dt, #adstats dt {
+ color: #83866a;
+}
+
+.pun #viewprofile dl, .pun #viewprofile dd, #adstats dl, #adstats dd {
+ border-color: #dce6d8;
+}
+
+#adminconsole fieldset td.nodefault {
+ background: #d59b9b;
+}
+
+.pun .multiselect {
+ color: #83866A;
+}
+
+.pun .checklist {
+ background: white;
+ border-color: #ccc;
+}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #eef1e8 #dbddd4 #e6e8df #eef1e8;
+}
+
+.pun .iredirect .icon {
+ border-color: #bbc6b2;
+ border-width: 1px;
+ padding: 7px;
+}
+
+.pun .inew .icon {
+ border-color: #50a42f #408426 #32671d #4a982c;
+}
diff --git a/style/Earth/base_admin.css b/style/Earth/base_admin.css
new file mode 100644
index 0000000..8136885
--- /dev/null
+++ b/style/Earth/base_admin.css
@@ -0,0 +1,177 @@
+#adminconsole .blockform .box {
+ padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+ position: relative;
+ overflow: hidden;
+}
+
+#adminconsole fieldset table {
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+ padding: 10px 8px 10px 0;
+ text-align: left;
+ white-space: normal;
+ border-style: solid none;
+ border-width: 1px 0;
+}
+
+#punadmin thead th {
+ border-top: 0;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+ display: block; font-size: 1em;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+ display: inline-block;
+}
+
+#adminconsole fieldset th {
+ width: 15em;
+ font-weight: normal;
+ padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+ vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+ padding-top: 3px;
+}
+
+#adminconsole .inform {
+ padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+ padding-bottom: 0;
+ padding-top: 0;
+}
+
+#adminconsole p.submittop {
+ text-align: center;
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin: 0 18px;
+ padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+ text-align: center;
+ padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+ padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+ padding: 10px 0 12px 0;
+}
+
+#adalerts {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #b9c5ce #d9e1e7;
+ background: #ebf1f5;
+ padding: 18px;
+}
+
+#adalerts p {
+ border: 1px solid #dfe6ee;
+ background: #ffffe1;
+ padding: 18px;
+}
+
+#categoryedit .tcl {
+ width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2 {
+ width: 20%;
+}
+
+#edforum .tcl {
+ width: 18%;
+}
+
+#edforum .tc2 {
+ width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+ text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+ padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+ display: block;
+ left: -9999em;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+ font-weight: bold;
+}
+
+#users2 th, #bans1 th {
+ text-align: left;
+}
+
+#users2 th.tcmod {
+ text-align: center;
+}
+
+#users2 .tcl, #bans1 .tcl {
+ width: auto;
+ text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+ width: 18%;
+ text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+ width: 12%;
+ text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+ width: 10%;
+ text-align: center;
+}
+
+#users2 .tcr {
+ width: 20%;
+ white-space: nowrap;
+}
+
+#bans1 .tcr {
+ width: 15%;
+ white-space: nowrap;
+}
+
+#users2 .tcmod {
+ width: 10%;
+ text-align: center;
+}
+
+.plugin p {
+ padding: 12px 18px 0;
+}
diff --git a/style/Earth/img/asterisk.png b/style/Earth/img/asterisk.png
new file mode 100644
index 0000000..f7438de
--- /dev/null
+++ b/style/Earth/img/asterisk.png
Binary files differ
diff --git a/style/Earth/img/bull.png b/style/Earth/img/bull.png
new file mode 100644
index 0000000..5fd1a9a
--- /dev/null
+++ b/style/Earth/img/bull.png
Binary files differ
diff --git a/style/Earth/img/email.png b/style/Earth/img/email.png
new file mode 100644
index 0000000..80ff5d9
--- /dev/null
+++ b/style/Earth/img/email.png
Binary files differ
diff --git a/style/Earth/img/exclaim.png b/style/Earth/img/exclaim.png
new file mode 100644
index 0000000..b98bb3c
--- /dev/null
+++ b/style/Earth/img/exclaim.png
Binary files differ
diff --git a/style/Earth/img/ext.png b/style/Earth/img/ext.png
new file mode 100644
index 0000000..daba1b1
--- /dev/null
+++ b/style/Earth/img/ext.png
Binary files differ
diff --git a/style/Earth/img/feed.png b/style/Earth/img/feed.png
new file mode 100644
index 0000000..3704226
--- /dev/null
+++ b/style/Earth/img/feed.png
Binary files differ
diff --git a/style/Earth/img/help.png b/style/Earth/img/help.png
new file mode 100644
index 0000000..2375e4a
--- /dev/null
+++ b/style/Earth/img/help.png
Binary files differ
diff --git a/style/Earth/img/index.html b/style/Earth/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Earth/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Earth/index.html b/style/Earth/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Earth/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Fire.css b/style/Fire.css
new file mode 100644
index 0000000..217c048
--- /dev/null
+++ b/style/Fire.css
@@ -0,0 +1,1650 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3, .pun h4, .pun h5, .pun pre, .pun blockquote,
+.pun ul, .pun ol, .pun li, .pun dl, .pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun legend .pun img,
+.pun abbr, .pun cite {
+ border: 0;
+ font-style: normal;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+.pun ul, .pun ol {
+ list-style: none;
+}
+
+.pun select {
+ padding-bottom: 1px;
+ padding-top: 1px;
+ padding-right: 1px;
+}
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 81.25%/1.462em Arial, Helvetica, sans-serif;
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun legend {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 1em;
+}
+
+.pun pre, .pun code {
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace;
+ font-size: 1em;
+}
+
+.pun pre code {
+ font-size: 1em;
+}
+
+.pun table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 0;
+ empty-cells: show;
+ width: 100%;
+}
+
+.pun h1 {
+ font:2.154em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+ padding: 7px 0;
+}
+
+.pun h2, .pun .hd h2 {
+ font: 1.462em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+ padding: 7px 0;
+}
+
+.pun h3 {
+ font-size: 1.154em;
+ line-height: 1.267em;
+ padding: 7px 0;
+}
+
+.pun h4 {
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun h5, .pun h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun p, .pun ul, .pun ol, .pun dl, .pun th, .pun td, .pun legend {
+ padding: 7px 0;
+}
+
+.pun strong, .pun th, .pun span.warntext, .pun p.warntext {
+ font-weight: bold;
+}
+
+.pun em {
+ font-style: italic;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+ text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ text-decoration: underline;
+}
+
+.pun .actions span {
+ padding-left: 16px;
+ padding-right: 8px;
+ background: url(Fire/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #debug h2, #brdstats .conl dt, #brdstats .conr dt, #modcontrols dt,
+#searchlinks dt, div.postright h3, .pun .subscribelink span, #announce .hd, #reportform h2, #punmoderate #vf h2,
+#punviewforum #vf h2, .pun .required strong span, .pun .icon div {
+ display: block;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+/* Generic Float Clear
+----------------------------------------------------------------*/
+
+.pun .inbox, .pun #brdmain, .pun .crumbs, .pun .pagepost, .pun .block2col {
+ min-height: 1px;
+}
+
+* html .pun .inbox, * html .pun #brdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+ display: inline-block;
+}
+
+* html .pun .inbox, * html .pun #bdrdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+ display: block;
+}
+
+.pun .inbox:after, .pun #brdmain:after, .pun .crumbs:after, .pun .pagepost:after, .pun .block2col:after {
+ content: " ";
+ display: block;
+ height: 0;
+ font-size: 0;
+ clear: both;
+ visibility: hidden;
+}
+
+.pun .block2col .inbox:after {
+ content: none;
+ clear: none;
+}
+
+.clearl {
+ clear: left;
+}
+
+/*****************************************************************
+2. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+ max-width: 1070px;
+ margin: 0 auto;
+ padding: 30px 40px;
+}
+
+#punredirect, #punmaint {
+ padding: 60px 20% 12px 20%;
+}
+
+#puninstall, #pundb_update {
+ padding: 20px 10%;
+}
+
+.pun .punwrap {
+ border: 1px solid;
+ border-radius: 10px;
+ padding: 18px;
+}
+
+#punredirect h2, #punmaint h2 {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin-bottom: 3px;
+}
+
+/* Section Spacing and Borders
+----------------------------------------------------------------*/
+
+#brdmain {
+ border-style: solid none;
+ border-width: 2px 0;
+ margin-bottom: 12px;
+ padding: 12px 0;
+}
+
+#punindex #brdmain {
+ padding-top: 24px;
+}
+
+#punredirect #brdmain, #punmaint #brdmain {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+#brdstats {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ margin-top: 24px;
+ padding-top: 12px;
+}
+
+#quickpost {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ margin-top: 12px;
+ padding-top: 12px;
+}
+
+#announce {
+ border-style: solid none none none;
+ border-width: 2px 0 0 0;
+ padding-top: 3px;
+}
+
+/*****************************************************************
+3. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Logo, Description and Main Menu
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ padding: 0 0 10px 0;
+}
+
+#brddesc {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ padding: 10px 0;
+}
+
+#brddesc p {
+ padding: 0;
+}
+
+#brdmenu ul {
+ padding: 0;
+}
+
+#brdmenu li {
+ float: left;
+}
+
+#brdmenu a:link, #brdmenu a:visited {
+ border-right-style: solid;
+ border-width: 1px;
+ display: block;
+ min-width: 60px;
+ padding: 12px 16px 6px 8px;
+ white-space: nowrap;
+}
+
+#brdmenu a:hover, #brmenu a:active, #brdmenu a:focus {
+ text-decoration: none;
+}
+
+/* Welcome Box
+----------------------------------------------------------------*/
+
+#brdwelcome {
+ padding: 10px 0;
+}
+
+#brdwelcome .conl, #brdwelcome .conr, #brdwelcome p, #brdwelcome li {
+ display: inline;
+ padding: 0;
+}
+
+#brdwelcome .conl {
+ float: left;
+}
+
+#brdwelcome .conr {
+ float: right;
+}
+
+#brdwelcome li span {
+ background: url(Fire/img/bull.png) center left no-repeat;
+ padding-left: 18px;
+ margin-right: 3px;
+ display: inline-block;
+ line-height: normal;
+ white-space: nowrap;
+}
+
+#brdwelcome .conl li:first-child span {
+ padding-left: 0;
+ background: none;
+}
+
+/* Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+}
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+}
+
+#brdstats #onlinelist {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ clear: both;
+}
+
+#brdstats #onlinelist dt, #brdstats #onlinelist dd {
+ display: inline;
+}
+
+/* Footer
+----------------------------------------------------------------*/
+
+.pun #modcontrols {
+ border-style: none none dotted none;
+ border-width: 0 0 1px 0;
+ margin-bottom: 4px;
+ text-align: center;
+ width: 100%;
+}
+
+.pun #modcontrols dd {
+ display: inline;
+}
+
+.pun #brdfooter #modcontrols dd span {
+ background: url(Fire/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #brdfooter .conl {
+ float: left;
+}
+
+.pun #brdfooter .conr {
+ text-align: right;
+ float: right;
+}
+
+.pun #brdfooter #poweredby a {
+ font-size: 1.077em;
+ font-weight: bold;
+}
+
+.pun #brdfooter #qjump {
+ padding-top: 5px;
+}
+
+.pun #brdfooter #qjump * {
+ white-space: nowrap;
+}
+
+.pun #brdfooter #searchlinks dd span {
+ background: url(Fire/img/bull.png) center left no-repeat;
+ display: inline-block;
+ line-height: normal;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #brdfooter #feedlinks {
+ padding-bottom: 0;
+}
+
+.pun #brdfooter #feedlinks span {
+ background: url(Fire/img/feed.png) center left no-repeat;
+ display: inline-block;
+ padding-left: 18px;
+ white-space: nowrap;
+}
+
+.pun #debugtime {
+ border-style: dotted none none none;
+ border-width: 1px 0 0 0;
+ margin-top: 7px;
+ text-align: center;
+}
+
+/* Breadcrumbs, Postlink, Pagination
+----------------------------------------------------------------*/
+
+.pun .linkst .inbox, .pun .linksb .inbox, .pun .postlinksb .inbox {
+ overflow: hidden;
+}
+
+.pun .linksb, .pun .postlinksb, .pun .linkst, .pun .crumbs {
+ clear: both;
+ position: relative;
+}
+
+.pun .linkst .crumbs {
+ font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+ font-size: 1.462em;
+ line-height: 1.211em;
+ padding: 7px 0;
+}
+
+.pun .linksb .crumbs, .pun .postlinksb .crumbs {
+ font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+ font-size: 1.154em;
+}
+
+.pun .linkst .crumbsplus .pagepost {
+ border-top-style: dotted;
+ border-top-width: 1px;
+}
+
+.pun .linksb .crumbsplus .pagepost, .pun .postlinksb .crumbsplus .pagepost {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+}
+
+.pun .postlinksb .crumbs {
+ margin-right: 11em;
+}
+
+.pun .crumbs li {
+ float: left;
+ padding-right: 0.4em;
+ white-space: nowrap;
+}
+
+.pun .crumbs li strong {
+ font-weight: normal;
+}
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+}
+
+.pun .pagelink strong, .pun .pagelink a, .pun .pagelink span.spacer {
+ border-style: none none none solid;
+ border-width: 0 0 0 1px;
+ display: inline-block;
+ padding: 0 12px 0 10px;
+ margin-right: -6px;
+}
+
+.pun .pagelink .item1 {
+ border: 0;
+}
+
+.pun .pagelink .pages-label {
+ display: inline-block;
+}
+
+.pun .postlink {
+ float: right;
+ font-weight: bold;
+ text-align: right;
+}
+
+.pun .modbuttons {
+ float: right;
+ padding: 5px 0 3px 0;
+}
+
+.pun .modbuttons input {
+ margin-left: 8px;
+}
+
+.pun .subscribelink {
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 33px;
+}
+
+#punindex .subscribelink {
+ top: 0px;
+}
+
+#punindex .linksb {
+ height: 12px;
+}
+
+/*****************************************************************
+4. MAIN TABLES
+*****************************************************************/
+
+.pun #brdmain .blocktable {
+ position: relative;
+}
+
+#punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ margin: 1px 1px 0 1px;
+ padding-left: 8px;
+ position: absolute;
+ left: 0;
+ white-space: nowrap;
+ z-index: 100;
+}
+
+#punindex .blocktable th.tcl, #punsearch #vf th.tcl {
+ font-size: 0;
+ text-indent: -9999em;
+}
+
+.pun .blocktable .box {
+ border-style: solid;
+ border-width: 1px;
+ margin-bottom: -1px;
+ overflow: hidden;
+ position: relative;
+}
+
+* html .pun .blocktable .box {
+ display: inline-block;
+}
+
+.pun .blocktable table {
+ table-layout: fixed;
+ margin-bottom: -1px;
+}
+
+.pun .blocktable th {
+ padding: 7px 8px;
+ border-style: none none solid none;
+ border-width: 1px;
+ text-align: left;
+}
+
+.pun .blocktable td {
+ padding: 7px 8px;
+ line-height: 1.3077em;
+ border-style: none none solid none;
+ border-width: 1px;
+ text-align: left;
+}
+
+.pun .blocktable h3 {
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 0;
+}
+
+.pun .blocktable p {
+ padding: 0;
+}
+
+.pun .blocktable .tcl p {
+ padding: 5px 0 0 0;
+}
+
+.pun .blocktable .tcl {
+ width: auto;
+}
+
+.pun .blocktable .tc2, .pun .blocktable .tc3, .pun .blocktable .tcmod {
+ padding-left: 0;
+ padding-right: 0;
+ text-align: center;
+ width: 11%;
+}
+
+.pun .blocktable .tcr {
+ width: 30%;
+}
+
+.pun .blocktable td .newtext, .pun .blocktable td .pagestext, .pun .blocktable td .byuser {
+ white-space: nowrap;
+}
+
+.pun .blocktable .tcl h3 span.newtext {
+ font-size: 0.929em;
+ font-weight: normal;
+}
+
+.pun #vf td.tcl span.stickytext, .pun #vf td.tcl span.closedtext {
+ font-size: 1em;
+ font-weight: bold;
+}
+
+#punsearch #vf .tc2 {
+ padding-left: 8px;
+ padding-right: 8px;
+ text-align: left;
+ width: 18%;
+}
+
+#users1 .tcr {
+ width: 25%;
+}
+
+#users1 .tc2 {
+ padding-left: 8px;
+ padding-right: 8px;
+ text-align: left;
+ width: 25%;
+}
+
+#debug {
+ margin-top: 12px;
+}
+
+#debug .tcl {
+ width: 10%;
+}
+
+#punredirect #debug .tcl, #punmaint #debug .tcl {
+ width: 20%;
+}
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal;
+}
+
+#punindex .tcr .byuser {
+ display: block;
+}
+
+#punindex td.tc2, #punindex td.tc3, #punindex td.tcr, .pun #vf td.tc2, .pun #vf td.tc3,
+.pun #vf td.tcr, #punindex td.tcl div.forumdesc, .pun #vf td.tcl span {
+ font-size: 0.923em;
+}
+
+.pun #vf td.tcl a {
+ font-weight: bold;
+}
+
+.pun #vf td.tcl span a {
+ font-weight: normal;
+}
+
+.pun .blocktable .tclcon {
+ min-height: 1px;
+ overflow: hidden;
+ padding: 0 11px 0 12px;
+ position: relative;
+}
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+}
+
+.pun .icon {
+ border-style: solid;
+ border-width: 8px;
+ float: left;
+ height: 0;
+ overflow: hidden;
+ width: 0;
+}
+
+.pun .iposted .ipost {
+ font-weight: bold;
+ left: 0;
+ padding-left: 4px;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ width: 8px;
+}
+
+/*****************************************************************
+MAIN POSTS
+*****************************************************************/
+
+/* Structure
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+ border-style: solid;
+ border-width: 1px;
+ margin-bottom: -1px;
+ overflow: hidden;
+ position: relative;
+}
+
+* html .pun .blockpost {
+ display: inline-block;
+}
+
+.pun .blockpost h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ white-space: nowrap;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ height: 1.462em;
+ padding: 0.538em 8px 0.538em 236px;
+ font-weight: normal;
+}
+
+#punsearch .blockpost h2 {
+ height: auto;
+ padding-left: 36px;
+ white-space: normal;
+}
+
+#punsearch .blockpost h2 span span {
+ white-space: nowrap;
+ display: inline-block;
+ font: 1.077em "Trebuchet MS", Arial, Helvetica, sans-serif;
+}
+
+#punsearch .blockpost .icon {
+ position: absolute;
+ top: 0;
+ margin-top: -2.154em;
+}
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+}
+
+.pun .blockpost .inbox {
+ float: right;
+ position: relative;
+ width: 100%;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+ border-left-style: solid;
+ border-left-width: 1px;
+ float: right;
+ margin-right: -218px;
+ position: relative;
+ text-align: left;
+ width: 100%;
+}
+
+.pun .blockpost .postleft, .pun .blockpost .postfootleft {
+ width: 194px;
+ padding: 7px 12px 7px 12px;
+ float: left;
+ margin-left: -218px;
+ position: relative;
+}
+
+.pun .blockpost .postleft dl {
+ padding: 0;
+}
+
+#punviewtopic .blockpost dt, #punmoderate .blockpost dt {
+ display: block;
+ position: absolute;
+ padding: 0.538em 0 0.538em 12px;
+ height: 1.462em;
+ top: -2.615em;
+ left: 0;
+ overflow: hidden;
+ width: 206px;
+}
+
+.pun .blockpost dt {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.pun .blockpost dt strong {
+ font-size: 1.231em;
+ font-weight: bold;
+}
+
+.pun .blockpost .postleft dd {
+ font-size: 0.923em;
+}
+
+.pun .blockpost .postleft .usertitle {
+ padding: 4px 0 6px 0;
+ font-size: 1em;
+}
+
+.pun .blockpost .postleft .postavatar {
+ display: block;
+ margin: 0 0 4px 0;
+}
+
+.pun .blockpost .postright {
+ position: relative;
+ padding: 4px 230px 7px 18px;
+}
+
+.pun .postmsg {
+ width:100%;
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
+.pun .blockpost .postfootright {
+ position: relative;
+ padding: 7px 230px 7px 18px;
+ text-align: right;
+}
+
+.pun .postfoot p, .pun .postfoot ul {
+ padding: 0;
+}
+
+.pun .blockpost .postfootright li {
+ display: inline;
+}
+
+.pun .blockpost .postfootright li span {
+ display: inline-block;
+ padding-left: 16px;
+ margin-left: 8px;
+ line-height: normal;
+ background: url(Fire/img/bull.png) center left no-repeat;
+}
+
+.pun .blockpost .usercontacts {
+ padding: 7px 0;
+}
+
+.pun .blockpost .usercontacts .email {
+ background: url(Fire/img/email.png) left 65% no-repeat;
+ margin-right: 5px;
+ padding-left: 21px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun .blockpost .usercontacts .website {
+ background: url(Fire/img/ext.png) left 65% no-repeat;
+ padding-left: 18px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun .postsignature hr {
+ border:none;
+ height: 1px;
+ margin-left: 0px;
+ text-align: left;
+}
+
+/* Content (includes other user content)
+----------------------------------------------------------------*/
+
+.pun .usercontent {
+ padding: 7px 0;
+}
+
+.pun .postmsg p, .pun .postmsg li, #punhelp p samp {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .usercontent h1, .pun .usercontent h2, .pun .usercontent h3,
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ padding: 7px 0 0 0;
+}
+
+.pun .postmsg h5, #punhelp h5 {
+ font-size: 1.231em;
+ font-weight: bold;
+ padding: 7px 0;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+ list-style: disc;
+ padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+ list-style: decimal;
+ padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha;
+}
+
+.pun .usercontent li, .pun .postmsg li {
+ padding: 0 3px;
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0;
+}
+
+.pun span.bbu {
+ text-decoration: underline;
+}
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+}
+
+.pun .blockpost .postmsg .postedit {
+ font-size: 0.857em;
+}
+
+.pun .blockform .postsignature, .pun .blockpost .postsignature {
+ font-size: 0.923em;
+}
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+}
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px 1px 1px 3px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ font-weight: bold;
+ line-height: 1.462em;
+}
+
+.pun .quotebox blockquote {
+ overflow: hidden;
+ width: 100%;
+}
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ direction: ltr;
+ text-align: left;
+}
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+*:first-child+html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+.pun .codebox pre code {
+ padding: 0.5em;
+ white-space: pre;
+}
+
+.pun div[class*=codebox] pre code {
+ display: inline-block;
+}
+
+* html .pun .codebox pre code {
+ display: block;
+}
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto;
+}
+
+.pun .postmsg img, #punhelp samp img {
+ vertical-align: text-top;
+}
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+}
+
+/*****************************************************************
+MAIN FORMS
+*****************************************************************/
+
+#punedit .blockform h2, #punpost .blockform h2, #postpreview h2, #posterror h2,
+.pun #quickpost h2, .pun #reportform h2, #pundelete .blockform h2 {
+ font: 1em/1.462em Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ white-space: nowrap;
+ padding: 10px 19px 4px 37px;
+ border: 0;
+}
+
+#punpost .blockform h2, #punedit .blockform h2,.pun #quickpost h2,
+#pundelete .blockform h2 {
+ margin: 1px 1px 0 1px;
+ width: 25em;
+ position: absolute;
+ z-index: 100;
+}
+
+.pun #quickpost legend, #punpost legend, #punedit legend {
+ width: 25em;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.pun .blockform .box {
+ border-style: solid;
+ border-width: 1px;
+ padding-bottom: 12px;
+}
+
+.pun #posterror {
+ border-style: solid;
+ border-width: 1px;
+}
+
+.pun #posterror .box {
+ padding: 0 18px 12px 18px;
+}
+
+* html .pun .blockform .box, * html .pun #posterror {
+ display: inline-block;
+}
+
+.pun .blockform .forminfo, .pun .error-info {
+ padding: 12px 18px;
+ border-style: solid;
+ border-width: 1px;
+ position: relative;
+}
+
+.pun .blockform .forminfo {
+ margin-top: 12px;
+}
+
+#pundelete .blockform .forminfo {
+ margin-top: 33px;
+}
+
+.pun .forminfo h3 {
+ padding-bottom: 0;
+}
+
+.pun .error-list li {
+ padding-left: 24px;
+ background: url(Fire/img/exclaim.png) center left no-repeat;
+}
+
+.pun .inform {
+ padding: 0 18px;
+}
+
+.pun legend {
+ font-weight: bold;
+ padding: 10px 19px 4px 19px;
+}
+
+* html .pun legend {
+ margin-left: -7px;
+}
+
+*:first-child+html .pun legend {
+ margin-left: -7px;
+}
+
+.pun .infldset {
+ border-style: solid;
+ border-width: 1px;
+ padding: 12px 18px;
+}
+
+#punregister #rules .infldset {
+ padding: 5px 18px;
+}
+
+.pun fieldset p {
+ padding: 0 0 7px 0;
+ width: 100%;
+}
+
+.pun fieldset .usercontent p {
+ padding: 7px 0;
+}
+
+.pun fieldset label {
+ display: block;
+ padding: 0 0 7px 0;
+}
+
+.pun label em {
+ font-weight: normal;
+ font-style: normal;
+}
+
+.pun .required strong {
+ background: url(Fire/img/asterisk.png) center right no-repeat;
+ font-weight: normal;
+ padding-right: 14px;
+ white-space: pre;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun label input, .pun label select, .pun label textarea {
+ margin-top: 2px;
+}
+
+.pun label.conl {
+ display: inline-block;
+ padding-right: 12px;
+}
+
+.pun form .buttons {
+ padding: 8px 19px 8px 34px;
+ margin-bottom: -12px;
+}
+
+.pun .blockform .buttons input {
+ margin-right: 12px;
+}
+
+.pun .rbox {
+ padding: 3px 0;
+}
+
+.pun .rbox label {
+ padding: 3px 0 3px 1.75em;
+ position: relative;
+ min-height: 1px;
+}
+
+* html .pun .rbox label {
+ text-indent: -3px;
+ height: 1%;
+}
+
+.pun .rbox input {
+ margin: 3px 0.75em 3px -1.75em;
+ float: left;
+ position: relative;
+ vertical-align: middle;
+ padding: 0;
+ height: 1em;
+ width: 1em;
+}
+
+.pun input[type=text], .pun input[type=password], .pun select, .pun textarea {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 98%;
+}
+
+.pun textarea {
+ resize: vertical;
+}
+
+.pun #quickpost .txtarea {
+ padding-right: 12px;
+ position: relative;
+}
+
+.pun .blockform .bblinks {
+ padding-top: 0;
+}
+
+.pun .blockform .bblinks li {
+ display: inline;
+}
+
+.pun .blockform .bblinks li span {
+ background: url(Fire/img/help.png) center left no-repeat;
+ margin-right: 8px;
+ padding-left: 20px;
+ display: inline-block;
+}
+
+.pun #quickpost .bblinks {
+ padding-top: 0;
+}
+
+.pun #quickpost .bblinks li {
+ display: inline;
+}
+
+.pun #login p.clearb {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ font-size: 0;
+ height: 0;
+ line-height: 0;
+ margin-top: 7px;
+ overflow: hidden;
+ padding-bottom: 3px;
+ padding-top: 7px;
+ text-indent: -9999em;
+ width: 100%;
+}
+
+.pun #postreview {
+ padding-top: 12px;
+}
+
+.pun #postpreview, .pun #posterror {
+ margin-bottom: 12px;
+}
+
+.pun #postpreview .postright {
+ padding: 0;
+}
+
+.pun #postpreview .postbody {
+ border-style: solid;
+ border-width: 1px;
+ float: none;
+ margin: 0 18px 12px 18px;
+ padding: 0;
+ padding: 4px 18px 4px 18px;
+ width: auto;
+}
+
+.pun span.email {
+ background: url(Fire/img/email.png) left 65% no-repeat;
+ margin-right: 5px;
+ padding-left: 21px;
+ display: inline-block;
+ line-height: normal;
+}
+
+.pun span.website {
+ background: url(Fire/img/ext.png) left 65% no-repeat;
+ padding-left: 18px;
+ display: inline-block;
+ line-height: normal;
+}
+
+#punmisc #rules .box {
+ border-style: solid;
+ border-width: 1px;
+ padding: 5px 18px;
+}
+
+
+#punhelp .box {
+ border-style: solid;
+ border-width: 1px;
+ padding: 7px 12px;
+}
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+}
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.25em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+ padding: 0;
+}
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+}
+
+/*****************************************************************
+PROFILES (+ ADMIN MENU)
+*****************************************************************/
+
+/* Profile / Admin
+----------------------------------------------------------------*/
+
+.pun .blockmenu {
+ width: 13em;
+ float: left;
+ padding-bottom: 12px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 15em;
+}
+
+.pun .blockmenu .block2 {
+ padding-top: 19px;
+}
+
+.pun .blockmenu ul {
+ border-top-style: dotted;
+ border-top-width: 1px;
+ padding: 0;
+}
+
+.pun .blockmenu li {
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ font-weight: bold;
+ padding: 0;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ display: block;
+ padding: 9px 6px 3px 6px;
+ min-height: 1px;
+ text-decoration: none;
+}
+
+* html .pun .blockmenu a:link, * html .pun .blockmenu a:visited {
+ height: 1%;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+ text-decoration: none;
+}
+
+#viewprofile .box {
+ border-style: solid;
+ border-width: 1px;
+ padding-bottom: 18px;
+}
+
+#viewprofile dt, #adstats dt {
+ padding: 7px 0;
+ position: absolute;
+ width: 13em;
+ left: 0;
+}
+
+#viewprofile dl {
+ border-style: solid none none none;
+ border-width: 1px;
+ margin: 7px 0;
+ padding: 0;
+ width: 100%;
+ position: relative;
+}
+
+#adintro, #adstats {
+ border-style: solid;
+ border-width: 1px;
+ padding: 18px;
+}
+
+#adintro li span {
+ display: inline-block;
+ padding-left: 16px;
+ margin-left: 8px;
+ line-height: normal;
+ background: url(Fire/img/bull.png) center left no-repeat;
+}
+
+#adstats .inbox, #adintro .inbox {
+ border-style: solid;
+ border-width: 1px;
+ padding: 18px;
+}
+
+#adstats dl {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ position: relative;
+}
+
+#viewprofile dd, #adstats dd {
+ border-style: none none solid none;
+ border-width: 1px;
+ padding: 7px 0 7px 13em;
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Basic defaults and Common Items
+----------------------------------------------------------------*/
+
+html, body, .pun {
+ background: #eeedea;
+ color: #333;
+}
+
+.pun .punwrap {
+ background: #fff;
+ border-color: #e1d9ca;
+ color: #665858;
+}
+
+#brdtitle #brddesc, .pun .pagepost, #brdstats #onlinelist, #brdfooter #searchlinks, #brdfooter #modcontrols,
+#punmaint h2, #punredirect h2, #adminconsole .submittop, .pun #debugtime, .pun .pagelink * {
+ border-color: #cec7b9;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+ color: #990000;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #f60;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #990000;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #f60;
+}
+
+/* Primary Navigation
+----------------------------------------------------------------*/
+
+#brdmenu {
+ background: #990000;
+}
+
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited {
+ background: #990000;
+ border-color: #fff;
+ color: #ddd;
+}
+
+#brdmenu a:hover, #brdmenu a:active, #brdmenu a:focus {
+ background: #f60;
+ border-color: #fff;
+ color: #fff;
+}
+
+/* Main Tables
+----------------------------------------------------------------*/
+
+.pun .blocktable .box {
+ background: #fbfaf5;
+ border-color: #cec7b9 #e7e2d9;
+}
+
+#punindex .blocktable h2, .pun #vf h2 {
+ color: #836359;
+}
+
+#adminconsole fieldset th, #adminconsole fieldset td {
+ background: #f3f2ed;
+ border-color: #ebe0dc;
+}
+
+.pun #users1 h2 {
+ background: #fff;
+}
+
+.pun .blocktable td {
+ border-color: #ebe0dc;
+}
+
+.pun .blocktable th {
+ background: #ecebe7;
+ border-color: #e1d9ca;
+ color: #836359;
+}
+
+.pun .blocktable td.tcl span.stickytext {
+ color: #000;
+}
+
+/* Main Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+ background: #f3f2ed;
+ border-color: #cec7b9 #e7e2d9;
+}
+
+.pun .blockpost h2 {
+ background: #ecebe7;
+ border-color: #e1d9ca;
+ color: #836359;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+ background: #fbfaf5;
+ border-color: #ebe0dc;
+}
+
+.pun .blockpost .postfootright li {
+ color: #fbfaf5;
+}
+
+.pun .postmsg, #punhelp code, #punhelp samp {
+ color: #333;
+}
+
+.pun .postsignature, .pun .postmsg .postedit {
+ color: #665858;
+}
+
+.pun .quotebox {
+ background: #f4f4e8;
+ border-color: #ccc;
+ color: #665858;
+}
+
+.pun .quotebox cite {
+ color: #836359;
+}
+
+.pun .codebox, #punhelp .codebox code {
+ background: #333;
+ color: #fff;
+}
+
+.pun .postmsg hr {
+ background: #cec7b9;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+}
+
+/* Main Forms + Profile
+----------------------------------------------------------------*/
+
+.pun .blockform .box, #adstats, #adintro, #postpreview, #posterror {
+ border-color: #cec7b9 #e7e2d9;
+ background: #ecebe7;
+}
+
+#punmisc #rules .box, #punhelp .box {
+ border-color: #cec7b9 #e7e2d9;
+ background: #f3f2ed;
+}
+
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2,
+#pundelete .blockform h2 {
+ background: #ecebe7;
+ color: #836359;
+}
+
+.pun .forminfo {
+ background: #fff;
+ border-color: #ebe0dc;
+}
+
+#puninstall form#install .forminfo {
+ background: #990000;
+ color: #fff;
+}
+
+.pun #posterror .error-info {
+ background: #ffffe1;
+ border-color: #dfe6ee;
+}
+
+#puninstall form#install .error-info {
+ background: #ffffe1;
+ border-color: #dfe6ee;
+ color: #333;
+}
+
+.pun .infldset, #adintro .inbox, #adstats .inbox {
+ background: #f3f2ed;
+ border-color: #ebe0dc;
+}
+
+.pun label, .pun legend, #adminconsole fieldset th {
+ color: #836359;
+}
+
+.pun fieldset p {
+ border-color: #cec7b9;
+}
+
+.pun .blockmenu ul, .pun .blockmenu li {
+ border-color: #cec7b9;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+ background: #ffffe6;
+}
+
+.pun .blockmenu .isactive a:link, .pun .blockmenu .isactive a:visited {
+ color: #333;
+ background: #f3f2ed;
+}
+
+.pun #viewprofile .box {
+ border-color: #cec7b9 #e7e2d9;
+ background: #ecebe7;
+}
+
+.pun #viewprofile dt, #adstats dt {
+ color: #836359;
+}
+
+.pun #viewprofile dl, .pun #viewprofile dd, #adstats dl, #adstats dd {
+ border-color: #ebe0dc;
+}
+
+#adminconsole fieldset td.nodefault {
+ background: #d59b9b;
+}
+
+.pun .multiselect {
+ color: #836359;
+}
+
+.pun .checklist {
+ background: white;
+ border-color: #ccc;
+}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #f1eee8 #dddad4 #e8e5df #f1efe8;
+}
+
+.pun .iredirect .icon {
+ border-color: #cec7b9;
+ border-width: 1px;
+ padding: 7px;
+}
+
+.pun .inew .icon {
+ border-color: #ca0000 #ab0000 #bb0000 #d30000;
+}
diff --git a/style/Fire/base_admin.css b/style/Fire/base_admin.css
new file mode 100644
index 0000000..8136885
--- /dev/null
+++ b/style/Fire/base_admin.css
@@ -0,0 +1,177 @@
+#adminconsole .blockform .box {
+ padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+ position: relative;
+ overflow: hidden;
+}
+
+#adminconsole fieldset table {
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+ padding: 10px 8px 10px 0;
+ text-align: left;
+ white-space: normal;
+ border-style: solid none;
+ border-width: 1px 0;
+}
+
+#punadmin thead th {
+ border-top: 0;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+ display: block; font-size: 1em;
+ font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+ display: inline-block;
+}
+
+#adminconsole fieldset th {
+ width: 15em;
+ font-weight: normal;
+ padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+ vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+ padding-top: 3px;
+}
+
+#adminconsole .inform {
+ padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+ padding-bottom: 0;
+ padding-top: 0;
+}
+
+#adminconsole p.submittop {
+ text-align: center;
+ border-bottom-style: dotted;
+ border-bottom-width: 1px;
+ margin: 0 18px;
+ padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+ text-align: center;
+ padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+ padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+ padding: 10px 0 12px 0;
+}
+
+#adalerts {
+ border-style: solid;
+ border-width: 1px;
+ border-color: #b9c5ce #d9e1e7;
+ background: #ebf1f5;
+ padding: 18px;
+}
+
+#adalerts p {
+ border: 1px solid #dfe6ee;
+ background: #ffffe1;
+ padding: 18px;
+}
+
+#categoryedit .tcl {
+ width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2 {
+ width: 20%;
+}
+
+#edforum .tcl {
+ width: 18%;
+}
+
+#edforum .tc2 {
+ width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+ text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+ padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+ display: block;
+ left: -9999em;
+ overflow: hidden;
+ position: absolute;
+ text-indent: -9999em;
+ width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+ font-weight: bold;
+}
+
+#users2 th, #bans1 th {
+ text-align: left;
+}
+
+#users2 th.tcmod {
+ text-align: center;
+}
+
+#users2 .tcl, #bans1 .tcl {
+ width: auto;
+ text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+ width: 18%;
+ text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+ width: 12%;
+ text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+ width: 10%;
+ text-align: center;
+}
+
+#users2 .tcr {
+ width: 20%;
+ white-space: nowrap;
+}
+
+#bans1 .tcr {
+ width: 15%;
+ white-space: nowrap;
+}
+
+#users2 .tcmod {
+ width: 10%;
+ text-align: center;
+}
+
+.plugin p {
+ padding: 12px 18px 0;
+}
diff --git a/style/Fire/img/asterisk.png b/style/Fire/img/asterisk.png
new file mode 100644
index 0000000..f7438de
--- /dev/null
+++ b/style/Fire/img/asterisk.png
Binary files differ
diff --git a/style/Fire/img/bull.png b/style/Fire/img/bull.png
new file mode 100644
index 0000000..3fd1f61
--- /dev/null
+++ b/style/Fire/img/bull.png
Binary files differ
diff --git a/style/Fire/img/email.png b/style/Fire/img/email.png
new file mode 100644
index 0000000..dd3a39d
--- /dev/null
+++ b/style/Fire/img/email.png
Binary files differ
diff --git a/style/Fire/img/exclaim.png b/style/Fire/img/exclaim.png
new file mode 100644
index 0000000..b98bb3c
--- /dev/null
+++ b/style/Fire/img/exclaim.png
Binary files differ
diff --git a/style/Fire/img/ext.png b/style/Fire/img/ext.png
new file mode 100644
index 0000000..637236b
--- /dev/null
+++ b/style/Fire/img/ext.png
Binary files differ
diff --git a/style/Fire/img/feed.png b/style/Fire/img/feed.png
new file mode 100644
index 0000000..3704226
--- /dev/null
+++ b/style/Fire/img/feed.png
Binary files differ
diff --git a/style/Fire/img/help.png b/style/Fire/img/help.png
new file mode 100644
index 0000000..080de7d
--- /dev/null
+++ b/style/Fire/img/help.png
Binary files differ
diff --git a/style/Fire/img/index.html b/style/Fire/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Fire/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Fire/index.html b/style/Fire/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Fire/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Lithium.css b/style/Lithium.css
new file mode 100644
index 0000000..10955a6
--- /dev/null
+++ b/style/Lithium.css
@@ -0,0 +1,1149 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+.pun ul, .pun ol {
+ list-style: none
+ }
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+ }
+
+.pun .clearer, .pun .clearb {
+ clear: both
+ }
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+ }
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+ }
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+ }
+
+.clearl {
+ clear: left;
+ }
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+ }
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+ line-height: normal;
+ }
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: verdana, helvetica, arial, sans-serif;
+ }
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+ }
+
+.pun pre code {
+ font-size: 1em;
+ }
+
+.pun strong {
+ font-weight: bold;
+ }
+
+.pun em {
+ font-style: italic;
+ }
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+ }
+
+.pun h2 {
+ font-size: 1em;
+ font-weight: normal;
+ padding: 4px 6px;
+ }
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+ }
+
+.pun table p, .pun table h3 {
+ padding: 0;
+ }
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+ }
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.75em 0
+ }
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+ }
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+ }
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+ }
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+ }
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+ }
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+ }
+
+.pun span.bbu {
+ text-decoration: underline
+ }
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+ }
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+ }
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+html, body {
+ margin: 0;
+ padding: 0
+ }
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+ }
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%
+ }
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+ }
+
+#brdtitle p {
+ padding-top: 0px
+ }
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+ }
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+ }
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+ }
+
+#postreview .blockpost {
+ margin-bottom: -1px;
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+ }
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+ }
+
+.pun .postlinksb {
+ margin-top: -6px
+ }
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+ }
+
+#brdheader .box {
+ border-top-width: 4px;
+ }
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+ }
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 3px 0 0 0;
+ }
+
+#brdmenu li {
+ display: inline;
+ margin-right: 12px;
+ }
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none
+ }
+
+#brdmenu a:hover, #brdmenu a:active {
+ text-decoration: underline
+ }
+
+#brdwelcome .conl {
+ float: left;
+ }
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+ }
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+ }
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+ }
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+ }
+
+.pun .postlink {
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+ }
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+ }
+
+.pun .modbuttons input {
+ margin-left: 6px;
+ }
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ text-decoration: none
+ }
+
+.pun .postlink a:hover, .pun .postlink a:active {
+ text-decoration: underline;
+ }
+
+#punindex .subscribelink {
+ margin-top: 6px;
+ }
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+ float: left;
+ }
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+ }
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+ }
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin:0 6px;
+ }
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+ }
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+ }
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+ }
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ }
+
+.pun .blocktable table {
+ table-layout: fixed;
+ }
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+.pun td, .pun th {
+ border-style: solid none none solid;
+ border-width: 1px;
+ }
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+ }
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+ }
+
+.pun .tcr {
+ width: 30%;
+ }
+
+.pun .tcl h3 {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .tcl h3 span.newtext {
+ font-size: 0.917em;
+ }
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap;
+ font-weight: normal;
+ }
+
+.pun td span.byuser {
+ white-space: nowrap;
+ }
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+ }
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#users1 .tcr {
+ width: 25%
+ }
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#debug .tcl {
+ width: 10%
+ }
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+ }
+
+#punindex .tcr .byuser {
+ display: block
+ }
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+ }
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .icon {
+ margin: 0.1em 0 0 0.2em;
+ border-width: 0.6em;
+ border-style: solid;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ float: left;
+ }
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+ }
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+ }
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+ PADDING: 20px 20px 15px 20px
+ }
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+ }
+
+.pun .forminfo h3 {
+ font-weight: bold;
+ }
+
+.pun .inform {
+ padding-bottom: 12px
+ }
+
+.pun fieldset {
+ padding: 0px 12px 0px 12px;
+ border-style: solid;
+ border-width: 1px
+ }
+
+.pun legend {
+ padding: 0px 6px
+ }
+
+.pun .infldset {
+ padding: 9px 0px 12px 0
+ }
+
+.pun label {
+ display: block;
+ padding: 3px 0
+ }
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+ }
+
+.pun fieldset .rbox br {
+ display: none;
+ }
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun .txtarea {
+ width: 75%
+ }
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+ }
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+ }
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+ }
+
+.pun .blockform .buttons {
+ padding-left: 12px;
+ }
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+ }
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+ }
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+ }
+
+.pun p.actions span {
+ margin-right: 12px;
+ }
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+ }
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.3em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+ }
+
+.pun .checklist fieldset {
+ border: 0;
+ padding: 0;
+ }
+
+.pun .checklist legend {
+ padding: 0;
+ }
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+ }
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+ }
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+ }
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+ }
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+ }
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+ }
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+ }
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+ }
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+ }
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+ }
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+ }
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+ }
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+ }
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+ }
+
+.pun .blockpost .box {
+ overflow: hidden;
+ }
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ position: relative;
+ overflow: hidden;
+ }
+
+.pun .postleft dl {
+ padding: 6px;
+ }
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+ }
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+ }
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+ }
+
+#postpreview .postright {
+ border-left: 0
+ }
+
+.pun .postright {
+ padding: 0 6px;
+ }
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+ }
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+ }
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 10px 6px 5px 6px;
+ }
+
+.pun .postfootright li {
+ display: inline;
+ }
+
+.pun .postfootright li:before {
+ content: " | ";
+ }
+
+.pun .postfootright li:first-child:before {
+ content: "";
+ }
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+ }
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+ }
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+ }
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+ }
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ }
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+ }
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+ }
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+ }
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+ }
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+ }
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+ }
+
+.pun .postmsg img {
+ vertical-align: bottom;
+ }
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+ }
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+ }
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ }
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+ }
+
+#punhelp div.box {
+ padding: 10px
+ }
+
+#debugtime {
+ margin-top: -12px;
+ text-align: center;
+ }
+
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+ line-height: 1.4em
+ }
+
+#announce div.inbox div {
+ padding: 3px 0
+ }
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #fff;
+ color: #333
+ }
+
+.pun {
+ color: #333
+ }
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #f1f1f1
+ }
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+ background-color: #dedfdf
+ }
+
+.pun h2, #brdmenu {
+ background-color: #6c8a3f;
+ color: #fff
+ }
+
+.pun th {
+ background-color: #d1d1d1
+ }
+
+.pun legend {
+ color: #6c8a3f
+ }
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #333
+ }
+
+.pun .usercontent * {
+ background: transparent;
+ color: #333
+ }
+
+.pun .multiselect, .pun .checklist {
+ color: #333;
+ }
+
+.pun .checklist {
+ border-color: #ACA899;
+ }
+
+/* posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #dedfdf
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #f1f1f1
+ }
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #f1f1f1
+ }
+
+#punhelp .codebox, #punhelp .quotebox {
+ background-color: #f9f9f9;
+ }
+
+.pun .blockpost h2 {
+ background-color: #7ea34b
+ }
+
+.pun .blockpost h2 span.conr {
+ color: #b7d094
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+ }
+
+.pun hr {
+ background-color: #333;
+ color: #333
+ }
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-color: #6c8a3f
+ }
+
+.pun td, #brdfooter #modcontrols {
+ border-color: #cedeb9
+ }
+
+.pun th {
+ border-color: #d1d1d1
+ }
+
+.pun fieldset {
+ border-color: #aca899
+ }
+
+#adminconsole td, #adminconsole th {
+ border-color: #f1f1f1
+ }
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+ border-color: #aca899 #fff #fff #aca899
+ }
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #638137
+ }
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #8eb653
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #638137;
+ }
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #8eb653;
+ }
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+ color: #fff
+ }
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+ color: #fff
+ }
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+ }
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+ }
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #b42000
+ }
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #b42000
+ }
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #e6e6e6 #dedede #dadada #e2e2e2
+ }
+
+.pun .iredirect .icon {
+ border-color: #f1f1f1 #f1f1f1 #f1f1f1 #f1f1f1
+ }
+
+.pun .inew .icon {
+ border-color: #8bb453 #7a9e48 #709142 #799c47
+ }
diff --git a/style/Mercury.css b/style/Mercury.css
new file mode 100644
index 0000000..51cf48d
--- /dev/null
+++ b/style/Mercury.css
@@ -0,0 +1,1150 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+.pun ul, .pun ol {
+ list-style: none
+ }
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+ }
+
+.pun .clearer, .pun .clearb {
+ clear: both
+ }
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+ }
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+ }
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+ }
+
+.clearl {
+ clear: left;
+ }
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+ }
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+ line-height: normal;
+ }
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: verdana, helvetica, arial, sans-serif;
+ }
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+ }
+
+.pun pre code {
+ font-size: 1em;
+ }
+
+.pun strong {
+ font-weight: bold;
+ }
+
+.pun em {
+ font-style: italic;
+ }
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+ }
+
+.pun h2 {
+ font-size: 1em;
+ font-weight: normal;
+ padding: 4px 6px;
+ }
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+ }
+
+.pun table p, .pun table h3 {
+ padding: 0;
+ }
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+ }
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.75em 0
+ }
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+ }
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+ }
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+ }
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+ }
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+ }
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+ }
+
+.pun span.bbu {
+ text-decoration: underline
+ }
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+ }
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+ }
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+html, body {
+ margin: 0;
+ padding: 0
+ }
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+ }
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%
+ }
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+ }
+
+#brdtitle p {
+ padding-top: 0px
+ }
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+ }
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+ }
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+ }
+
+#postreview .blockpost {
+ margin-bottom: -1px;
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+ }
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+ }
+
+.pun .postlinksb {
+ margin-top: -6px
+ }
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+ }
+
+#brdheader .box {
+ border-top-width: 4px;
+ }
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+ }
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 3px 0 0 0;
+ }
+
+#brdmenu li {
+ display: inline;
+ margin-right: 12px;
+ }
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none
+ }
+
+#brdmenu a:hover, #brdmenu a:active {
+ text-decoration: underline
+ }
+
+#brdwelcome .conl {
+ float: left;
+ }
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+ }
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+ }
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+ }
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+ }
+
+.pun .postlink {
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+ }
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+ }
+
+.pun .modbuttons input {
+ margin-left: 6px;
+ }
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ text-decoration: none
+ }
+
+.pun .postlink a:hover, .pun .postlink a:active {
+ text-decoration: underline;
+ }
+
+#punindex .subscribelink {
+ margin-top: 6px;
+ }
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+ float: left;
+ }
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+ }
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+ }
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin:0 6px;
+ }
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+ }
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+ }
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+ }
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ }
+
+.pun .blocktable table {
+ table-layout: fixed;
+ }
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+.pun td, .pun th {
+ border-style: solid none none solid;
+ border-width: 1px;
+ }
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+ }
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+ }
+
+.pun .tcr {
+ width: 30%;
+ }
+
+.pun .tcl h3 {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .tcl h3 span.newtext {
+ font-size: 0.917em;
+ }
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap;
+ font-weight: normal;
+ }
+
+.pun td span.byuser {
+ white-space: nowrap;
+ }
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+ }
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#users1 .tcr {
+ width: 25%
+ }
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#debug .tcl {
+ width: 10%
+ }
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+ }
+
+#punindex .tcr .byuser {
+ display: block
+ }
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+ }
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .icon {
+ margin: 0.1em 0 0 0.2em;
+ border-width: 0.6em;
+ border-style: solid;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ float: left;
+ }
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+ }
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+ }
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+ PADDING: 20px 20px 15px 20px
+ }
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+ }
+
+.pun .forminfo h3 {
+ font-weight: bold;
+ }
+
+.pun .inform {
+ padding-bottom: 12px
+ }
+
+.pun fieldset {
+ padding: 0px 12px 0px 12px;
+ border-style: solid;
+ border-width: 1px
+ }
+
+.pun legend {
+ padding: 0px 6px
+ }
+
+.pun .infldset {
+ padding: 9px 0px 12px 0
+ }
+
+.pun label {
+ display: block;
+ padding: 3px 0
+ }
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+ }
+
+.pun fieldset .rbox br {
+ display: none;
+ }
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun .txtarea {
+ width: 75%
+ }
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+ }
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+ }
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+ }
+
+.pun .blockform .buttons {
+ padding-left: 12px;
+ }
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+ }
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+ }
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+ }
+
+.pun p.actions span {
+ margin-right: 12px;
+ }
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+ }
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.3em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+ }
+
+.pun .checklist fieldset {
+ border: 0;
+ padding: 0;
+ }
+
+.pun .checklist legend {
+ padding: 0;
+ }
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+ }
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+ }
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+ }
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+ }
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+ }
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+ }
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+ }
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+ }
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+ }
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+ }
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+ }
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+ }
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+ }
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+ }
+
+.pun .blockpost .box {
+ overflow: hidden;
+ }
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ position: relative;
+ overflow: hidden;
+ }
+
+.pun .postleft dl {
+ padding: 6px;
+ }
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+ }
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+ }
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+ }
+
+#postpreview .postright {
+ border-left: 0
+ }
+
+.pun .postright {
+ padding: 0 6px;
+ }
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+ }
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+ }
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 10px 6px 5px 6px;
+ }
+
+.pun .postfootright li {
+ display: inline;
+ }
+
+.pun .postfootright li:before {
+ content: " | ";
+ }
+
+.pun .postfootright li:first-child:before {
+ content: "";
+ }
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+ }
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+ }
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+ }
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+ }
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ }
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+ }
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+ }
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+ }
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+ }
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+ }
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+ }
+
+.pun .postmsg img {
+ vertical-align: bottom;
+ }
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+ }
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+ }
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ }
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+ }
+
+#punhelp div.box {
+ padding: 10px
+ }
+
+#debugtime {
+ margin-top: -12px;
+ text-align: center;
+ }
+
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+ line-height: 1.4em
+ }
+
+#announce div.inbox div {
+ padding: 3px 0
+ }
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #2a2a2a;
+ color: #d4d4d4
+ }
+
+.pun {
+ color: #d4d4d4
+ }
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #383838
+ }
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+ background-color: #424242
+ }
+
+.pun h2, #brdmenu {
+ background-color: #565656;
+ color: #d4d4d4
+ }
+
+.pun th {
+ background-color: #484848
+ }
+
+.pun legend {
+ color: #f6b620
+ }
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #d4d4d4
+ }
+
+.pun .usercontent * {
+ background: transparent;
+ color: #d4d4d4
+ }
+
+.pun textarea, .pun input, .pun select {
+ background-color: #2a2a2a;
+ color: #d4d4d4
+ }
+
+.pun .multiselect, .pun .checklist {
+ color: #D4D4D4;
+ }
+
+.pun .checklist {
+ border-color: #666;
+ }
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #383838
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #424242
+ }
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #424242
+ }
+
+.pun .blockpost h2 {
+ background-color: #565656
+ }
+
+.pun .blockpost h2 span.conr {
+ color: #a19e96
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+ }
+
+.pun hr {
+ background-color: #606060;
+ color: #606060
+ }
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-color:#565656
+ }
+
+.pun td, #brdfooter #modcontrols {
+ border-color: #565656
+ }
+
+.pun th {
+ border-color: #484848
+ }
+
+.pun fieldset {
+ border-color: #565656
+ }
+
+#adminconsole td, #adminconsole th {
+ border-color: #383838
+ }
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+ border-color: #565656
+ }
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #f6b620
+ }
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #ffee40
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #f6b620;
+ }
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #ffee40;
+ }
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+ color: #d4d4d4
+ }
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+ color: #d4d4d4
+ }
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+ }
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+ }
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #ff4000
+ }
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #ff5010
+ }
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #484848 #404040 #3c3c3c #444444
+ }
+
+.pun .iredirect .icon {
+ border-color: #383838 #383838 #383838 #383838
+ }
+
+.pun .inew .icon {
+ border-color: #f6b620 #ecae1f #d09a1b #e1a61d
+ }
diff --git a/style/Oxygen.css b/style/Oxygen.css
new file mode 100644
index 0000000..ac3d321
--- /dev/null
+++ b/style/Oxygen.css
@@ -0,0 +1,1150 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+.pun ul, .pun ol {
+ list-style: none
+ }
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+ }
+
+.pun .clearer, .pun .clearb {
+ clear: both
+ }
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+ }
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+ }
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+ }
+
+.clearl {
+ clear: left;
+ }
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+ }
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+ line-height: normal;
+ }
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun small, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: verdana, helvetica, arial, sans-serif;
+ }
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+ }
+
+.pun pre code {
+ font-size: 1em;
+ }
+
+.pun strong {
+ font-weight: bold;
+ }
+
+.pun em {
+ font-style: italic;
+ }
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+ }
+
+.pun h2 {
+ font-size: 1em;
+ font-weight: normal;
+ padding: 4px 6px;
+ }
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+ }
+
+.pun table p, .pun table h3 {
+ padding: 0;
+ }
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+ }
+
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.75em 0
+ }
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+ }
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+ }
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+ }
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+ }
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+ }
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+ }
+
+.pun span.bbu {
+ text-decoration: underline
+ }
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+ }
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+ }
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+html, body {
+ margin: 0;
+ padding: 0
+ }
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+ }
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%
+ }
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+ }
+
+#brdtitle p {
+ padding-top: 0px
+ }
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+ }
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+ }
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+ }
+
+#postreview .blockpost {
+ margin-bottom: -1px;
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+ }
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+ }
+
+.pun .postlinksb {
+ margin-top: -6px
+ }
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+ }
+
+#brdheader .box {
+ border-top-width: 4px;
+ }
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+ }
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 3px 0 0 0;
+ }
+
+#brdmenu li {
+ display: inline;
+ margin-right: 12px;
+ }
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none
+ }
+
+#brdmenu a:hover, #brdmenu a:active {
+ text-decoration: underline
+ }
+
+#brdwelcome .conl {
+ float: left;
+ }
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+ }
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+ }
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+ }
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+ }
+
+.pun .postlink {
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+ }
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+ }
+
+.pun .modbuttons input {
+ margin-left: 6px;
+ }
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ text-decoration: none
+ }
+
+.pun .postlink a:hover, .pun .postlink a:active {
+ text-decoration: underline;
+ }
+
+#punindex .subscribelink {
+ margin-top: 6px;
+ }
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+ float: left;
+ }
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+ }
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+ }
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin:0 6px;
+ }
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+ }
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+ }
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+ }
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ }
+
+.pun .blocktable table {
+ table-layout: fixed;
+ }
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+.pun td, .pun th {
+ border-style: solid none none solid;
+ border-width: 1px;
+ }
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+ }
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+ }
+
+.pun .tcr {
+ width: 30%;
+ }
+
+.pun .tcl h3 {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .tcl h3 span.newtext {
+ font-size: 0.917em;
+ }
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap;
+ font-weight: normal;
+ }
+
+.pun td span.byuser {
+ white-space: nowrap;
+ }
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+ }
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#users1 .tcr {
+ width: 25%
+ }
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#debug .tcl {
+ width: 10%
+ }
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+ }
+
+#punindex .tcr .byuser {
+ display: block
+ }
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+ }
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .icon {
+ margin: 0.1em 0 0 0.2em;
+ border-width: 0.6em;
+ border-style: solid;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ float: left;
+ }
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+ }
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+ }
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+ PADDING: 20px 20px 15px 20px
+ }
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+ }
+
+.pun .forminfo h3 {
+ font-weight: bold;
+ }
+
+.pun .inform {
+ padding-bottom: 12px
+ }
+
+.pun fieldset {
+ padding: 0px 12px 0px 12px;
+ border-style: solid;
+ border-width: 1px
+ }
+
+.pun legend {
+ padding: 0px 6px
+ }
+
+.pun .infldset {
+ padding: 9px 0px 12px 0
+ }
+
+.pun label {
+ display: block;
+ padding: 3px 0
+ }
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+ }
+
+.pun fieldset .rbox br {
+ display: none;
+ }
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun .txtarea {
+ width: 75%
+ }
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+ }
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+ }
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+ }
+
+.pun .blockform .buttons {
+ padding-left: 12px;
+ }
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+ }
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+ }
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+ }
+
+.pun p.actions span {
+ margin-right: 12px;
+ }
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+ }
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.3em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+ }
+
+.pun .checklist fieldset {
+ border: 0;
+ padding: 0;
+ }
+
+.pun .checklist legend {
+ padding: 0;
+ }
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+ }
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+ }
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+ }
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+ }
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+ }
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+ }
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+ }
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+ }
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+ }
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+ }
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+ }
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+ }
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+ }
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+ }
+
+.pun .blockpost .box {
+ overflow: hidden;
+ }
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ position: relative;
+ overflow: hidden;
+ }
+
+.pun .postleft dl {
+ padding: 6px;
+ }
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+ }
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+ }
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+ }
+
+#postpreview .postright {
+ border-left: 0
+ }
+
+.pun .postright {
+ padding: 0 6px;
+ }
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+ }
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+ }
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 10px 6px 5px 6px;
+ }
+
+.pun .postfootright li {
+ display: inline;
+ }
+
+.pun .postfootright li:before {
+ content: " | ";
+ }
+
+.pun .postfootright li:first-child:before {
+ content: "";
+ }
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+ }
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+ }
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+ }
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+ }
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ }
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+ }
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+ }
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+ }
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+ }
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+ }
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+ }
+
+.pun .postmsg img {
+ vertical-align: bottom;
+ }
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+ }
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+ }
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ }
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+ }
+
+#punhelp div.box {
+ padding: 10px
+ }
+
+#debugtime {
+ margin-top: -12px;
+ text-align: center;
+ }
+
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+ line-height: 1.4em
+ }
+
+#announce div.inbox div {
+ padding: 3px 0
+ }
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #fff;
+ color: #333
+ }
+
+.pun {
+ color: #333
+ }
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #f1f1f1
+ }
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#adminconsole fieldset td, .pun .blockmenu .box, #adstats dd, #brdfooter #modcontrols {
+ background-color: #dedfdf
+ }
+
+.pun h2, #brdmenu {
+ background-color: #0066b9;
+ color: #fff
+ }
+
+.pun th {
+ background-color: #d1d1d1
+ }
+
+.pun legend {
+ color: #005cb1
+ }
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #333
+ }
+
+.pun .usercontent * {
+ background: transparent;
+ color: #333
+ }
+
+.pun .multiselect, .pun .checklist {
+ color: #333;
+ }
+
+.pun .checklist {
+ border-color: #ACA899;
+ }
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #dedfdf
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #f1f1f1
+ }
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #f1f1f1
+ }
+
+#punhelp .codebox, #punhelp .quotebox {
+ background-color: #f9f9f9;
+ }
+
+.pun .blockpost h2 {
+ background-color: #006fc9
+ }
+
+.pun .blockpost h2 span.conr {
+ color: #aabdcd
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+ }
+
+.pun hr {
+ background-color: #333;
+ color: #333
+ }
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-color: #0066b9
+ }
+
+.pun td, #brdfooter #modcontrols {
+ border-color: #bbcede
+ }
+
+.pun th {
+ border-color: #d1d1d1
+ }
+
+.pun fieldset {
+ border-color: #aca899
+ }
+
+#adminconsole td, #adminconsole th {
+ border-color: #f1f1f1
+ }
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+ border-color: #aca899 #fff #fff #aca899
+ }
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #005cb1
+ }
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #b42000
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #005cb1;
+ }
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #b42000;
+ }
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+ color: #fff
+ }
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+ color: #fff
+ }
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+ }
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+ }
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #b42000
+ }
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #b42000
+ }
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #e6e6e6 #dedede #dadada #e2e2e2
+ }
+
+.pun .iredirect .icon {
+ border-color: #f1f1f1 #f1f1f1 #f1f1f1 #f1f1f1
+ }
+
+.pun .inew .icon {
+ border-color: #0080d7 #0065c0 #0058b3 #0072ca
+ }
diff --git a/style/Radium.css b/style/Radium.css
new file mode 100644
index 0000000..c572dbb
--- /dev/null
+++ b/style/Radium.css
@@ -0,0 +1,1150 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+.pun ul, .pun ol {
+ list-style: none
+ }
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+ }
+
+.pun .clearer, .pun .clearb {
+ clear: both
+ }
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+ }
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+ }
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+ }
+
+.clearl {
+ clear: left;
+ }
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+ }
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+ line-height: normal;
+ }
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: verdana, helvetica, arial, sans-serif;
+ }
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+ }
+
+.pun pre code {
+ font-size: 1em;
+ }
+
+.pun strong {
+ font-weight: bold;
+ }
+
+.pun em {
+ font-style: italic;
+ }
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+ }
+
+.pun h2 {
+ font-size: 1em;
+ font-weight: normal;
+ padding: 4px 6px;
+ }
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+ }
+
+.pun table p, .pun table h3 {
+ padding: 0;
+ }
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+ }
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.75em 0
+ }
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+ }
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+ }
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+ }
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+ }
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+ }
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+ }
+
+.pun span.bbu {
+ text-decoration: underline
+ }
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+ }
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+ }
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+html, body {
+ margin: 0;
+ padding: 0
+ }
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+ }
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%
+ }
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+ }
+
+#brdtitle p {
+ padding-top: 0px
+ }
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+ }
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+ }
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+ }
+
+#postreview .blockpost {
+ margin-bottom: -1px;
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+ }
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+ }
+
+.pun .postlinksb {
+ margin-top: -6px
+ }
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+ }
+
+#brdheader .box {
+ border-top-width: 4px;
+ }
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+ }
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 3px 0 0 0;
+ }
+
+#brdmenu li {
+ display: inline;
+ margin-right: 12px;
+ }
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none
+ }
+
+#brdmenu a:hover, #brdmenu a:active {
+ text-decoration: underline
+ }
+
+#brdwelcome .conl {
+ float: left;
+ }
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+ }
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+ }
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+ }
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+ }
+
+.pun .postlink {
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+ }
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+ }
+
+.pun .modbuttons input {
+ margin-left: 6px;
+ }
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ text-decoration: none
+ }
+
+.pun .postlink a:hover, .pun .postlink a:active {
+ text-decoration: underline;
+ }
+
+#punindex .subscribelink {
+ margin-top: 6px;
+ }
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+ float: left;
+ }
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+ }
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+ }
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin:0 6px;
+ }
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+ }
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+ }
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+ }
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ }
+
+.pun .blocktable table {
+ table-layout: fixed;
+ }
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+.pun td, .pun th {
+ border-style: solid none none solid;
+ border-width: 1px;
+ }
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+ }
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+ }
+
+.pun .tcr {
+ width: 30%;
+ }
+
+.pun .tcl h3 {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .tcl h3 span.newtext {
+ font-size: 0.917em;
+ }
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap;
+ font-weight: normal;
+ }
+
+.pun td span.byuser {
+ white-space: nowrap;
+ }
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+ }
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#users1 .tcr {
+ width: 25%
+ }
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#debug .tcl {
+ width: 10%
+ }
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+ }
+
+#punindex .tcr .byuser {
+ display: block
+ }
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+ }
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .icon {
+ margin: 0.1em 0 0 0.2em;
+ border-width: 0.6em;
+ border-style: solid;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ float: left;
+ }
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+ }
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+ }
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+ PADDING: 20px 20px 15px 20px
+ }
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+ }
+
+.pun .forminfo h3 {
+ font-weight: bold;
+ }
+
+.pun .inform {
+ padding-bottom: 12px
+ }
+
+.pun fieldset {
+ padding: 0px 12px 0px 12px;
+ border-style: solid;
+ border-width: 1px
+ }
+
+.pun legend {
+ padding: 0px 6px
+ }
+
+.pun .infldset {
+ padding: 9px 0px 12px 0
+ }
+
+.pun label {
+ display: block;
+ padding: 3px 0
+ }
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+ }
+
+.pun fieldset .rbox br {
+ display: none;
+ }
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun .txtarea {
+ width: 75%
+ }
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+ }
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+ }
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+ }
+
+.pun .blockform .buttons {
+ padding-left: 12px;
+ }
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+ }
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+ }
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+ }
+
+.pun p.actions span {
+ margin-right: 12px;
+ }
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+ }
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.3em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+ }
+
+.pun .checklist fieldset {
+ border: 0;
+ padding: 0;
+ }
+
+.pun .checklist legend {
+ padding: 0;
+ }
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+ }
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+ }
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+ }
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+ }
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+ }
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+ }
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+ }
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+ }
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+ }
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+ }
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+ }
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+ }
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+ }
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+ }
+
+.pun .blockpost .box {
+ overflow: hidden;
+ }
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ position: relative;
+ overflow: hidden;
+ }
+
+.pun .postleft dl {
+ padding: 6px;
+ }
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+ }
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+ }
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+ }
+
+#postpreview .postright {
+ border-left: 0
+ }
+
+.pun .postright {
+ padding: 0 6px;
+ }
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+ }
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+ }
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 10px 6px 5px 6px;
+ }
+
+.pun .postfootright li {
+ display: inline;
+ }
+
+.pun .postfootright li:before {
+ content: " | ";
+ }
+
+.pun .postfootright li:first-child:before {
+ content: "";
+ }
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+ }
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+ }
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+ }
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+ }
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ }
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+ }
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+ }
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+ }
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+ }
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+ }
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+ }
+
+.pun .postmsg img {
+ vertical-align: bottom;
+ }
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+ }
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+ }
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ }
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+ }
+
+#punhelp div.box {
+ padding: 10px
+ }
+
+#debugtime {
+ margin-top: -12px;
+ text-align: center;
+ }
+
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+ line-height: 1.4em
+ }
+
+#announce div.inbox div {
+ padding: 3px 0
+ }
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #2a2a2a;
+ color: #d4d4d4
+ }
+
+.pun {
+ color: #d4d4d4
+ }
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #383838
+ }
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+ background-color: #424242
+ }
+
+.pun h2, #brdmenu {
+ background-color: #565656;
+ color: #d4d4d4
+ }
+
+.pun th {
+ background-color: #484848
+ }
+
+.pun legend {
+ color: #60c860
+ }
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #d4d4d4
+ }
+
+.pun .usercontent * {
+ background: transparent;
+ color: #d4d4d4
+ }
+
+.pun textarea, .pun input, .pun select {
+ background-color: #2a2a2a;
+ color: #d4d4d4
+ }
+
+.pun .multiselect, .pun .checklist {
+ color: #D4D4D4;
+ }
+
+.pun .checklist {
+ border-color: #666;
+ }
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #383838
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #424242
+ }
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #424242
+ }
+
+.pun .blockpost h2 {
+ background-color: #565656
+ }
+
+.pun .blockpost h2 span.conr {
+ color: #a19e96
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+ }
+
+.pun hr {
+ background-color: #606060;
+ color: #606060
+ }
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-color:#565656
+ }
+
+.pun td, #brdfooter #modcontrols {
+ border-color: #565656
+ }
+
+.pun th {
+ border-color: #484848
+ }
+
+.pun fieldset {
+ border-color: #606060
+ }
+
+#adminconsole td, #adminconsole th {
+ border-color: #383838
+ }
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+ border-color: #606060
+ }
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #60c860
+ }
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #80ee80
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #60c860;
+ }
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #80ee80;
+ }
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+ color: #d4d4d4
+ }
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+ color: #d4d4d4
+ }
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+ }
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+ }
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #ff4000
+ }
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #ff5010
+ }
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #484848 #404040 #3c3c3c #444444
+ }
+
+.pun .iredirect .icon {
+ border-color: #383838 #383838 #383838 #383838
+ }
+
+.pun .inew .icon {
+ border-color: #60c860 #54af54 #499849 #59b657
+ }
diff --git a/style/Sulfur.css b/style/Sulfur.css
new file mode 100644
index 0000000..0bf449f
--- /dev/null
+++ b/style/Sulfur.css
@@ -0,0 +1,1149 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+.pun ul, .pun ol {
+ list-style: none
+ }
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+ }
+
+.pun .clearer, .pun .clearb {
+ clear: both
+ }
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+ }
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+ }
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+ }
+
+.clearl {
+ clear: left;
+ }
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+ }
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+ line-height: normal;
+ }
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: verdana, helvetica, arial, sans-serif;
+ }
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+ }
+
+.pun pre code {
+ font-size: 1em;
+ }
+
+.pun strong {
+ font-weight: bold;
+ }
+
+.pun em {
+ font-style: italic;
+ }
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+ }
+
+.pun h2 {
+ font-size: 1em;
+ font-weight: normal;
+ padding: 4px 6px;
+ }
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+ }
+
+.pun table p, .pun table h3 {
+ padding: 0;
+ }
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+ }
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.75em 0
+ }
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+ }
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+ }
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+ }
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+ }
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+ }
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+ }
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+ }
+
+.pun span.bbu {
+ text-decoration: underline
+ }
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+ }
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+ }
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+html, body {
+ margin: 0;
+ padding: 0
+ }
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+ }
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%
+ }
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+ }
+
+#brdtitle p {
+ padding-top: 0px
+ }
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+ }
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+ }
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+ }
+
+#postreview .blockpost {
+ margin-bottom: -1px;
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+ }
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+ }
+
+.pun .postlinksb {
+ margin-top: -6px
+ }
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+ }
+
+#brdheader .box {
+ border-top-width: 4px;
+ }
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+ }
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 3px 0 0 0;
+ }
+
+#brdmenu li {
+ display: inline;
+ margin-right: 12px;
+ }
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none
+ }
+
+#brdmenu a:hover, #brdmenu a:active {
+ text-decoration: underline
+ }
+
+#brdwelcome .conl {
+ float: left;
+ }
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+ }
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+ }
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+ }
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+.pun .pagelink {
+ float: left;
+ white-space: nowrap;
+ }
+
+.pun .postlink {
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+ }
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+ }
+
+.pun .modbuttons input {
+ margin-left: 6px;
+ }
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ text-decoration: none
+ }
+
+.pun .postlink a:hover, .pun .postlink a:active {
+ text-decoration: underline;
+ }
+
+#punindex .subscribelink {
+ margin-top: 6px;
+ }
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+ float: left;
+ }
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+ }
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+ }
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin:0 6px;
+ }
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+ }
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+ }
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+ }
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+ }
+
+.pun .blocktable table {
+ table-layout: fixed;
+ }
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+.pun td, .pun th {
+ border-style: solid none none solid;
+ border-width: 1px;
+ }
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+ }
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+ }
+
+.pun .tcr {
+ width: 30%;
+ }
+
+.pun .tcl h3 {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .tcl h3 span.newtext {
+ font-size: 0.917em;
+ }
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap;
+ font-weight: normal;
+ }
+
+.pun td span.byuser {
+ white-space: nowrap;
+ }
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+ }
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#users1 .tcr {
+ width: 25%
+ }
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+ }
+
+#debug .tcl {
+ width: 10%
+ }
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+ }
+
+#punindex .tcr .byuser {
+ display: block
+ }
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+ }
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+ }
+
+.pun .icon {
+ margin: 0.1em 0 0 0.2em;
+ border-width: 0.6em;
+ border-style: solid;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ float: left;
+ }
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+ }
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+ }
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+ PADDING: 20px 20px 15px 20px
+ }
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+ }
+
+.pun .forminfo h3 {
+ font-weight: bold;
+ }
+
+.pun .inform {
+ padding-bottom: 12px
+ }
+
+.pun fieldset {
+ padding: 0px 12px 0px 12px;
+ border-style: solid;
+ border-width: 1px
+ }
+
+.pun legend {
+ padding: 0px 6px
+ }
+
+.pun .infldset {
+ padding: 9px 0px 12px 0
+ }
+
+.pun label {
+ display: block;
+ padding: 3px 0
+ }
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+ }
+
+.pun fieldset .rbox br {
+ display: none;
+ }
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+ }
+
+.pun .txtarea {
+ width: 75%
+ }
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+ }
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+ }
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+ }
+
+.pun .blockform .buttons {
+ padding-left: 12px;
+ }
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+ }
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+ }
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+ }
+
+.pun p.actions span {
+ margin-right: 12px;
+ }
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+ }
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.3em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+ }
+
+.pun .checklist fieldset {
+ border: 0;
+ padding: 0;
+ }
+
+.pun .checklist legend {
+ padding: 0;
+ }
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+ }
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+ }
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+ }
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+ }
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+ }
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+ }
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+ }
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+ }
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+ }
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+ }
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+ }
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+ }
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+ }
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+ }
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+ }
+
+.pun .blockpost .box {
+ overflow: hidden;
+ }
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ position: relative;
+ overflow: hidden;
+ }
+
+.pun .postleft dl {
+ padding: 6px;
+ }
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+ }
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+ }
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+ }
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+ }
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+ }
+
+#postpreview .postright {
+ border-left: 0
+ }
+
+.pun .postright {
+ padding: 0 6px;
+ }
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+ }
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+ }
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 10px 6px 5px 6px;
+ }
+
+.pun .postfootright li {
+ display: inline;
+ }
+
+.pun .postfootright li:before {
+ content: " | ";
+ }
+
+.pun .postfootright li:first-child:before {
+ content: "";
+ }
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+ }
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+ }
+
+.pun .codebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0;
+ }
+
+.pun .quotebox {
+ border-style: solid;
+ border-width: 1px;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+ }
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+ }
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+ }
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+ }
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+ }
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+ }
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+ }
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+ }
+
+.pun .postmsg img {
+ vertical-align: bottom;
+ }
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+ }
+
+.pun .postmsg .postimg img {
+ max-width: 98%;
+ vertical-align: middle;
+ margin: 7px 0.5em 7px 0;
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-style: solid;
+ border-width: 2px;
+ }
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+ }
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ }
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+ }
+
+#punhelp div.box {
+ padding: 10px
+ }
+
+#debugtime {
+ margin-top: -12px;
+ text-align: center;
+ }
+
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+ line-height: 1.4em
+ }
+
+#announce div.inbox div {
+ padding: 3px 0
+ }
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #fff;
+ color: #333
+ }
+
+.pun {
+ color: #333
+ }
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #f1f1f1
+ }
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+ background-color: #dedfdf
+ }
+
+.pun h2, #brdmenu {
+ background-color: #b84623;
+ color: #fff
+ }
+
+.pun th {
+ background-color: #d1d1d1
+ }
+
+.pun legend {
+ color: #822100
+ }
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #333
+ }
+
+.pun .usercontent * {
+ background: transparent;
+ color: #333
+ }
+
+.pun .multiselect, .pun .checklist {
+ color: #333;
+ }
+
+.pun .checklist {
+ border-color: #ACA899;
+ }
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #dedfdf
+ }
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #f1f1f1
+ }
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #f1f1f1
+ }
+
+#punhelp .codebox, #punhelp .quotebox {
+ background-color: #f9f9f9;
+ }
+
+.pun .blockpost h2 {
+ background-color: #d25028
+ }
+
+.pun .blockpost h2 span.conr {
+ color: #fccfc1
+ }
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+ }
+
+.pun hr {
+ background-color: #333;
+ color: #333
+ }
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-color: #b84623
+ }
+
+.pun td, #brdfooter #modcontrols {
+ border-color: #e1c3c3
+ }
+
+.pun th {
+ border-color: #d1d1d1
+ }
+
+.pun fieldset {
+ border-color: #aca899
+ }
+
+#adminconsole td, #adminconsole th {
+ border-color: #f1f1f1
+ }
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+ border-color: #aca899 #fff #fff #aca899
+ }
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #822100
+ }
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #ca3300
+ }
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+ border-color: #822100;
+ }
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+ border-color: #ca3300;
+ }
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+ color: #fff
+ }
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+ color: #fff
+ }
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+ }
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+ }
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #b42000
+ }
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #b42000
+ }
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ border-color: #e6e6e6 #dedede #dadada #e2e2e2
+ }
+
+.pun .iredirect .icon {
+ border-color: #f1f1f1 #f1f1f1 #f1f1f1 #f1f1f1
+ }
+
+.pun .inew .icon {
+ border-color: #c23000 #af2c00 #992600 #ac2b00
+ }
diff --git a/style/Technetium.css b/style/Technetium.css
new file mode 100644
index 0000000..aa87399
--- /dev/null
+++ b/style/Technetium.css
@@ -0,0 +1,1373 @@
+/*****************************************************************
+Technitium theme CSS
+*****************************************************************/
+
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+.pun ul, .pun ol {
+ list-style: none
+}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+body {
+ line-height: 1.9em;
+}
+
+.pun {
+ width: 95%;
+ margin: 0 auto;
+}
+
+.pun .clearer, .pun .nosize {
+ height: 0;
+ width: 0;
+ line-height: 0;
+ font-size: 0;
+ overflow: hidden
+}
+
+.pun .clearer, .pun .clearb {
+ clear: both
+}
+
+.pun .nosize {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ width: 0;
+}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+ height: 1px
+}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+ min-height: 1px
+}
+
+.clearl {
+ clear: left;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3 {
+ position: absolute;
+ display: block;
+ overflow: hidden;
+ width: 0;
+ left: -9999em;
+ text-indent: -9999em;
+}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+ font: 10pt Georgia, Times, "Times New Roman", serif
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
+ font-size: 1em;
+ font-family: Georgia, Times, "Times New Roman", serif;
+}
+
+.pun pre, .pun code {
+ font-size: 1.182em;
+ font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+}
+
+.pun pre code {
+ font-size: 1em;
+}
+
+.pun strong {
+ font-weight: bold;
+}
+
+.pun em {
+ font-style: italic;
+}
+
+#vf td {
+ font-size: 1.1em;
+}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+ font-size: 1em;
+ padding: 3px 0;
+}
+
+.pun h2 {
+ font-size: 1.5em;
+ font-weight: normal;
+ padding: 4px 6px;
+ padding: 6px;
+ border-radius: 0.3em 0.3em 0 0;
+}
+
+.pun h2 #brdmenu {
+ font-size: 1.1em;
+}
+
+.pun .blockpost h2 {
+ font-size: 1.0em;
+ padding: 8px;
+}
+
+.pun h3 {
+ font-size: 1.091em;
+ padding: 3px 0;
+}
+
+.pun table p, .pun table h3 {
+ padding: 0;
+}
+
+.pun span.warntext, .pun p.warntext {
+ font-weight: bold
+}
+
+.pun .postleft dl dt {
+ font-size: 1.4em;
+}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+ padding: 0.5em 0;
+ line-height: 1.6em;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: disc
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+ padding: 0.75em 1em 0.75em 2.5em;
+ list-style: decimal
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+ list-style: lower-alpha
+}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+ padding: 0.25em 1em 0.75em 2.5em
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+ padding: 0
+}
+
+.pun .usercontent h1 {
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+}
+
+.pun .usercontent h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+}
+
+.pun .usercontent h3 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+ font-size: 1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0
+}
+
+.pun .quotebox cite {
+ font-weight: bold;
+ font-style: normal;
+ padding: 0.75em 0.75em 0 0.75em
+}
+
+.pun span.bbu {
+ text-decoration: underline
+}
+
+.pun span.bbs, .pun del {
+ text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ text-decoration: none;
+}
+
+.pun div.postmsg h5, #punhelp h5 {
+ font-size: 1.1em;
+ font-weight: bold;
+ padding: 0.75em 0 0 0;
+}
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+ max-width: 1070px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 12px 20px;
+}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+ margin: 50px 20% 12px 20%;
+ width: auto;
+}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+ margin: 0 0 12px 0;
+}
+
+#announce, #brdstats {
+ margin: 12px 0 12px 0;
+}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+ margin-bottom: 12px
+}
+
+#punindex .blocktable, .pun .blockpost {
+ margin-bottom: 6px
+}
+
+#postreview .box {
+ margin-bottom: 3px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-bottom: 0px
+}
+
+.pun .linkst, .pun .linksb {
+ margin-top: -12px
+}
+
+.pun .postlinksb {
+ margin-top: -6px
+}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+ border-style: solid;
+ border-width: 1px;
+}
+
+
+.pun h2 {
+ border-width: 1px;
+ border-style: solid;
+ border-bottom: none;
+}
+
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+ padding: 3px 6px
+}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+ font-size: 1.5em;
+ line-height: 1.1em;
+ padding: 3px 0 0 0;
+}
+
+#brdmenu {
+ font-size:1.2em;
+}
+
+#brddesc {
+ padding: 3px 0;
+}
+
+#brddesc * {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+
+#brdmenu li {
+ display: inline;
+ margin-right: 10px;
+ margin-top: 12px;
+ margin-bottom: 12px;
+}
+
+
+#brdmenu a:link, #brdmenu a:visited {
+ text-decoration: none;
+ text-shadow: #FFF 1px -1px 0.4em;
+ padding: 0.2em;
+}
+
+#brdwelcome .conl {
+ float: left;
+ white-space: nowrap;
+}
+
+#brdwelcome li {
+ float:left;
+ margin-right:8px;
+ white-space:nowrap;
+}
+
+#brdwelcome .conl li strong:after {
+ content: '.'
+}
+
+#brdwelcome .conr {
+ float: right;
+ text-align: right;
+}
+
+#brdwelcome .conr li {
+ float: right;
+ text-align: right;
+}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+ padding: 8px 6px 3px 6px
+}
+
+.pun .linksb, .pun .postlinksb {
+ padding: 3px 6px 8px 6px
+}
+
+.pun .crumbs {
+ clear: both;
+ width: 100%;
+ overflow: hidden;
+}
+
+.pun .crumbs li {
+ display: inline;
+ white-space: nowrap;
+ font-weight: bold;
+}
+
+.pun .linkst .crumbs {
+ font-size: 1.3em;
+}
+
+.pun .pagelink {
+ float: left;
+ display: block;
+ white-space: normal;
+}
+
+.pun .postlink {
+ font-size: 1.1em;
+ font-weight: bold;
+ white-space: wrap;
+ text-decoration: none;
+}
+
+.pun .linkst .postlink {
+ position: relative;
+ margin-top: -0.2em;
+ margin-bottom: 0.5em;
+}
+
+.pun .linksb .postlink, .pun .postlinksb .postlink {
+ position: relative;
+ margin-top: 0.5em;
+ margin-bottom: -0.5em;
+}
+
+.pun .postlink, .pun .modbuttons {
+ float: right;
+ text-align: right;
+}
+
+.pun .modbuttons {
+ padding: 1px 0;
+ white-space: nowrap;
+}
+
+.pun .modbuttons input {
+ margin-left: 6px;
+}
+
+#punindex .subscribelink {
+ margin-top: 6px;
+}
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter #modcontrols {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ text-align: center;
+}
+
+#brdfooter #modcontrols dd {
+ display: inline;
+ margin-right: 10px;
+}
+
+#brdfooter .conl {
+ float: left;
+}
+
+#brdfooter .conr {
+ float: right;
+ text-align: right;
+}
+
+#brdfooter #feedlinks span {
+ background:url("Technetium/feed.png") no-repeat scroll left center transparent;
+ padding-left:18px;
+ display: inline;
+}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+ float: left;
+}
+
+#brdstats .conr {
+ float: right;
+ text-align: right;
+}
+
+#onlinelist dd, #onlinelist dt {
+ display: inline;
+}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: show;
+}
+
+.pun .blocktable table {
+ table-layout: fixed;
+}
+
+.pun td, .pun th {
+ padding: 4px 6px;
+ line-height: 1.4em;
+ text-align: left;
+ font-weight: normal;
+}
+
+.pun td {
+ border-style: solid none none solid;
+ border-width: 1px;
+}
+
+.pun .tcl {
+ border-left: 0;
+ width: auto;
+}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+ width: 10%;
+ text-align: center;
+ padding: 4px 0;
+}
+
+.pun .tcr {
+ width: 30%;
+}
+
+.pun .tcl h3 {
+ font-size: 1.3em;
+ font-weight: bold;
+}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+ white-space: nowrap
+}
+
+.pun .tcl p {
+ padding: 5px 0 0 0
+}
+
+#punsearch #vf .tc2 {
+ width: 18%;
+ text-align: left;
+ padding: 4px 6px;
+}
+
+#users1 .tcr {
+ width: 25%
+}
+
+#users1 .tc2 {
+ width: 25%;
+ text-align: left;
+ padding: 4px 6px;
+}
+
+#debug .tcl {
+ width: 10%
+}
+
+#debug .tcr {
+ width: 90%;
+ white-space: normal
+}
+
+#punindex .tcr .byuser {
+ display: block
+}
+
+.pun .blocktable .tclcon {
+ padding: 0 11px 0 12px;
+ overflow: hidden;
+ min-height: 1px;
+ position: relative;
+}
+
+.pun .blocktable .tclcon div {
+ width: 100%;
+ overflow: hidden;
+}
+
+.pun .icon {
+ height: 24px;
+ width: 24px;
+ overflow: hidden;
+ float: left;
+}
+
+.pun .icon div {
+ position: absolute;
+ left: -9999em;
+ text-indent: -9999em;
+ height: 0;
+}
+
+.pun .iposted .ipost {
+ position: absolute;
+ left: 0;
+ font-weight: bold;
+ width: 8px;
+ padding-left: 4px;
+ text-align: center;
+ top: 0;
+}
+
+.pun .stickytext {
+ display: none;
+}
+
+.pun .movedtext {
+ display: none;
+}
+
+.pun .closedtext {
+ font-weight: bold;
+}
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+
+.pun .forminfo {
+ margin-bottom: 12px;
+ padding: 9px 10px;
+ border-style: solid;
+ border-width: 1px;
+}
+
+.pun .forminfo h3 {
+ font-weight: bold;
+}
+
+
+
+.pun .inform {
+ padding: 0 18px;
+}
+
+.pun fieldset {
+ overflow: hidden;
+ width: 100%;
+ padding-bottom: 0.8em;
+}
+
+.pun legend {
+ font-size: 1.2em;
+ font-weight: bold;
+ margin-left: -7px;
+ padding: 10px 19px 7px 19px;
+}
+
+.pun div[class*="inform"] legend {
+ margin-left: 0;
+}
+
+.pun .infldset {
+ border-style: solid;
+ border-width: 1px;
+ display: inline-block;
+ overflow: hidden;
+ padding: 12px 18px;
+}
+
+.pun div[class*="infldset"] {
+ display: block;
+}
+
+#punregister #rules .infldset {
+ padding: 5px 18px;
+}
+
+.pun fieldset p {
+ clear: both;
+ padding: 0 0 7px 0;
+ width: 100%;
+}
+
+.pun fieldset .usercontent p {
+ padding: 7px 0;
+}
+
+.pun fieldset label {
+ clear: both;
+ display: block;
+ padding: 0 0 7px 0;
+}
+
+
+.pun label {
+ display: block;
+ padding: 3px 0
+}
+
+.pun label.conl {
+ float: left;
+ overflow: visible;
+ margin-right: 10px
+}
+
+.pun select {
+ padding-top: 1px;
+ padding-bottom: 1px;
+}
+
+.pun fieldset .rbox {
+}
+
+.pun fieldset .rbox br {
+ display: none;
+}
+
+.pun fieldset .rbox label {
+ padding: 3px 0 3px 25px;
+ position: relative;
+ vertical-align: middle;
+}
+
+.pun fieldset .rbox input {
+ margin: 0 9px 0 -25px;
+ padding: 0;
+ width: 16px;
+ position: relative;
+ vertical-align: middle;
+}
+
+.pun .txtarea {
+ width: 90%
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+ width: 100%
+}
+
+.pun .bblinks {
+ padding-bottom: 10px;
+ padding-left: 4px
+}
+
+.pun .bblinks li {
+ display: inline;
+ padding-right: 20px
+}
+
+.pun .blockform .buttons {
+ padding-left: 18px;
+ padding-bottom: 5px;
+}
+
+.pun .blockform .buttons input {
+ margin-right: 8px;
+}
+
+#posterror ul {
+ list-style: square;
+ padding: 3px 0 3px 24px;
+}
+
+.pun .deletemsg {
+ border-style: solid;
+ border-width: 1px;
+ padding: 6px 15px;
+}
+
+.pun .multiselect {
+ float: left;
+ padding-bottom: 7px;
+}
+
+.pun .checklist {
+ border-width: 1px;
+ border-style: solid;
+ max-height: 9em;
+ width: 20em;
+ overflow: auto;
+ padding: 0.25em 0.5em;
+ margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+ padding: 0;
+}
+
+.pun .checklist legend span {
+ width: auto;
+ max-width: 25em;
+}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+ padding-bottom: 1px
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+ margin-left: 14em
+}
+
+.pun .blockmenu {
+ float:left;
+ width: 13em
+}
+
+.pun .blockmenu li {
+ padding: 3px 0;
+ font-weight: bold;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+ text-decoration: none
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+ text-decoration: underline
+}
+
+#viewprofile dl {
+ float: left;
+ width: 100%;
+ overflow: hidden
+}
+
+#viewprofile dd {
+ margin-left: 14em;
+ padding: 3px;
+}
+
+#viewprofile dt {
+ float: left;
+ width: 13em;
+ margin: 3px 0;
+}
+
+#profileavatar img {
+ float: right;
+ margin-left: 1em
+}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+ text-decoration: none;
+}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+ text-decoration: underline;
+}
+
+.pun .blockpost h2 .conr {
+ float: right;
+ text-align: right;
+}
+
+#punsearch .blockpost h2 span {
+ white-space: nowrap;
+}
+
+.pun .blockpost .box {
+ overflow: hidden;
+}
+
+.pun .postleft, .pun .postfootleft {
+ float:left;
+ width: 18em;
+ overflow: hidden;
+ position: relative;
+ overflow: hidden;
+}
+
+.pun .postleft dl {
+ padding: 0.5em 6px;
+}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+ margin-top: 6px
+}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+ margin-bottom: 6px;
+ display: block;
+}
+
+.pun .blockpost dt {
+ font-size: 1.091em;
+ font-weight: bold;
+}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+ text-decoration: none;
+}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+ text-decoration: underline;
+}
+
+.pun .postright, .pun .postfootright {
+ border-left-width: 18em;
+ border-left-style: solid
+}
+
+#postpreview .postright {
+ border-left: 0
+}
+
+.pun .postright {
+ padding: 0 6px;
+}
+
+.pun .postfootright, .pun .multidelete {
+ text-align: right
+}
+
+.pun .postmsg {
+ width:98%;
+ overflow: hidden;
+ padding-bottom: 6px;
+ word-wrap: break-word;
+}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+ padding: 6px 6px 6px 6px;
+}
+
+.pun .postfootright li {
+ display: inline;
+}
+
+.pun .postfootright li:before {
+ content: " | ";
+}
+
+.pun .postfootright li:first-child:before {
+ content: "";
+}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+ text-decoration: none
+}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+ text-decoration: underline
+}
+
+
+.pun .quotebox, .pun .codebox {
+ border-style: solid;
+ border-width: 2px;
+ border-left: 5px solid;
+ margin: 0.75em 1em;
+ padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+ display: block;
+ padding: 0.75em 0 0 0;
+}
+
+.pun .quotebox blockquote {
+ width: 100%;
+ overflow: hidden
+}
+
+.pun .codebox pre {
+ overflow: auto;
+ width: 100%;
+ overflow-y:hidden
+}
+
+* html .pun .codebox pre {
+ padding-bottom: 10px;
+}
+
+*+html .pun .codebox pre {
+ padding-bottom: 10px
+}
+
+.pun .codebox pre code {
+ display: block;
+ padding: 0.75em;
+}
+
+.pun .codebox pre.vscroll {
+ height: 32em;
+ overflow: auto;
+ overflow-y: auto
+}
+
+.pun .postmsg .postimg img, .pun .postmsg a .postimg img {
+ max-width: 100%;
+ vertical-align: middle;
+}
+
+.pun .postmsg img {
+ vertical-align: bottom;
+}
+
+.pun .postsignature hr {
+ margin-left: 0px;
+ width: 200px;
+ text-align: left;
+ height: 1px;
+ border:none
+}
+
+.pun .blockpost label {
+ padding: 3px 6px;
+ border-style: solid;
+ border-width: 1px;
+ vertical-align: middle;
+ display: inline-block;
+}
+
+.pun .blockpost label * {
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+}
+
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+ margin-top: 12px
+}
+
+#punhelp div.box {
+ padding: 10px
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+ background: #fff url("Technetium/bg.png") repeat-x top;
+ color: #122434
+}
+
+.pun {
+ color: #122434
+}
+
+.pun .box, #adminconsole fieldset th {
+ background-color: #FBFCFD;
+ background-image: url("Technetium/light-shade.png");
+ background-position: bottom;
+ background-repeat: repeat-x;
+}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+ background-color: #F4F9FD;
+ background-image: url("Technetium/light-shade.png");
+ background-repeat: repeat-x;
+ background-position: bottom;
+}
+
+.pun h2, #brdmenu {
+ background-color: #C5D8EB;
+ color: #122434;
+ background-image: url("Technetium/dark-shade.png");
+ background-repeat: repeat-x;
+ background-position: top;
+}
+
+.pun h2 {
+ background-color: #C5D8EB;
+ background-image: url("Technetium/inv-shade.png");
+ background-repeat: repeat-x;
+ color: #122434;
+ text-shadow: #FFF 1px -1px 0.7em;
+}
+
+.pun #announce h2 {
+ display: none;
+}
+
+.box #announce-block {
+ background-color: #FDFCE3;
+}
+
+.pun th {
+ background-color: #EFF3F8;
+ background-image: url("Technetium/light-shade.png");
+ background-repeat: repeat-x;
+}
+
+.pun td {
+ background-image: url("Technetium/light-shade.png");
+ background-color: #FBFCFD;
+ background-position: bottom;
+ background-repeat: repeat-x;
+}
+
+.pun .isticky td {
+ background-color: #E4EBF1;
+}
+
+.pun .iclosed td {
+ background-color: #ECEEF0
+}
+
+.pun .iclosed.isticky td {
+ background-color: #CDD6E0;
+}
+
+.pun legend {
+ color: #122434
+}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+ color: #333
+}
+
+.pun .usercontent * {
+ background: transparent;
+ color: #333
+}
+
+#adminmenu .box {
+ background: #FBFCFD;
+}
+
+.pun .postlink a:link, .pun .postlink a:visited, .pun .postlink a:active {
+ background-color:#F0F5FA;
+ background-image:url(Technetium/inv-shade.png);
+ background-repeat:repeat-x;
+ border: 1px solid;
+ color:#2B3037;
+ text-shadow: #FFF 1px -1px 0.4em;
+ padding: 0.3em;
+ border-radius: 0.4em;
+ position: relative;
+}
+
+.pun .postlink a:hover {
+ background-color: #C0D6E9;
+ color: #000;
+}
+
+#brdheader .box {
+ background-color:#F0F5FA;
+ background-image:url(Technetium/light-shade.png);
+ background-repeat:repeat-x;
+ background-position: top;
+ color:#122434;
+ border-radius: 0.4em;
+}
+
+.pun #brdtitle {
+ background-color: #D1E1EF;
+ background-image: url("Technetium/inv-shade.png");
+ background-repeat: repeat-x;
+ text-shadow: #FFF 1px -1px 0.4em;
+}
+
+
+.pun .infldset, #pundelete .deletemsg, #adintro .inbox, #adstats .inbox {
+ background: #FBFCFD url(Technetium/light-shade.png) bottom repeat-x;
+ border-color: #C0CCD8;
+}
+
+#adintro ul {
+ list-style-type: disc;
+ margin-left: 8px;
+ padding-left: 16px;
+}
+
+.pun .multiselect {
+ color: #122434;
+}
+
+.pun .checklist {
+ background: white;
+ border-color: #A2B5CC;
+}
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+ background-color: #F8FAFD;
+}
+
+.pun .postright, .pun .postfootright {
+ border-left-color: #F0F5FA
+}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+ background-color: #F0F5FA
+}
+
+#punhelp .codebox, #punhelp .quotebox {
+ background-color: #f9f9f9;
+}
+
+.pun .blockpost h2 {
+ background-color: #F0F5FA;
+}
+
+.pun .blockpost h2 span.conr {
+ color: #aabdcd
+}
+
+.pun hr {
+ background-color: #333;
+ color: #333
+}
+
+.pun .quotebox {
+ background-color: #FAFCFE;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+ background-color: #ff0;
+}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box, .pun h2, #brdfooter #modcontrols {
+ border-color: #A2B5CC;
+}
+
+.blocktable #announce h2 {
+ border-color: #F6B620;
+}
+
+.pun td {
+ border-color: #A2B5CC;
+}
+
+.pun th, .pun fieldset {
+ border-color: #A2B5CC;
+}
+
+#adminconsole td, #adminconsole th {
+ border-style:solid;
+ border-width:1px 0px !important;
+}
+
+#adminconsole th {
+ border-width: 1px 0px 1px 1px !important;
+}
+
+#adminconsole td {
+ border-width: 1px 1px 1px 1px !important;
+}
+
+.pun .forminfo, .pun .blockpost label, .pun .deletemsg {
+ border-color: #aca899 #fff #fff #aca899;
+}
+
+.pun .quotebox, .pun .codebox {
+ border-color: #BCD2E9;
+}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+ border-color: #C0D6E9
+}
+
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+ color: #1F537B;
+ text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+ color: #9E1D00
+}
+
+#brdmenu li a:active, #brdmenu li a:link, #brdmenu li a:visited {
+ color: #1F2E3D;
+}
+
+#brdmenu li a:hover {
+ color: #000;
+ text-shadow: #8895A2 1px -1px 0.4em;
+}
+
+#vf a {
+ font-weight: bold;
+}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+ color: #888
+}
+
+.pun .isticky.iclosed td.tcl a:link, .pun .isticky.iclosed td.tcl a:visited {
+ color: #475F6B !important;
+}
+
+.pun .isticky.iclosed td.tcl a:hover {
+ color: #577382 !important;
+}
+
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+ color: #aaa
+}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+ color: #b42000
+}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+ color: #b42000
+}
+
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+ background-image:url(Technetium/icon-nonew.png);
+}
+
+.pun .iredirect .icon {
+ background-image:url(Technetium/icon-moved.png);
+}
+
+.pun .inew .icon{
+ background-image:url(Technetium/icon-new.png);
+}
+
+.pun .iclosed .icon {
+ background-image:url(Technetium/icon-closed.png);
+}
+
+.pun .isticky .icon {
+ background-image:url(Technetium/icon-nonew-sticky.png);
+}
+
+.pun .isticky.inew .icon {
+ background-image:url(Technetium/icon-new-sticky.png);
+}
+
+.pun .iclosed.isticky .icon {
+ background-image:url(Technetium/icon-closed-sticky.png);
+}
+
+.pun .imoved .icon {
+ background-image:url(Technetium/icon-moved.png);
+} \ No newline at end of file
diff --git a/style/Technetium/bg.png b/style/Technetium/bg.png
new file mode 100644
index 0000000..9261117
--- /dev/null
+++ b/style/Technetium/bg.png
Binary files differ
diff --git a/style/Technetium/dark-shade.png b/style/Technetium/dark-shade.png
new file mode 100644
index 0000000..0bd7ae6
--- /dev/null
+++ b/style/Technetium/dark-shade.png
Binary files differ
diff --git a/style/Technetium/darker-shade.png b/style/Technetium/darker-shade.png
new file mode 100644
index 0000000..b3ca98a
--- /dev/null
+++ b/style/Technetium/darker-shade.png
Binary files differ
diff --git a/style/Technetium/feed.png b/style/Technetium/feed.png
new file mode 100644
index 0000000..3704226
--- /dev/null
+++ b/style/Technetium/feed.png
Binary files differ
diff --git a/style/Technetium/icon-closed-sticky.png b/style/Technetium/icon-closed-sticky.png
new file mode 100644
index 0000000..92f76a9
--- /dev/null
+++ b/style/Technetium/icon-closed-sticky.png
Binary files differ
diff --git a/style/Technetium/icon-closed.png b/style/Technetium/icon-closed.png
new file mode 100644
index 0000000..4d9c93e
--- /dev/null
+++ b/style/Technetium/icon-closed.png
Binary files differ
diff --git a/style/Technetium/icon-moved.png b/style/Technetium/icon-moved.png
new file mode 100644
index 0000000..2484c2f
--- /dev/null
+++ b/style/Technetium/icon-moved.png
Binary files differ
diff --git a/style/Technetium/icon-new-sticky.png b/style/Technetium/icon-new-sticky.png
new file mode 100644
index 0000000..e9fb5a6
--- /dev/null
+++ b/style/Technetium/icon-new-sticky.png
Binary files differ
diff --git a/style/Technetium/icon-new.png b/style/Technetium/icon-new.png
new file mode 100644
index 0000000..a832adb
--- /dev/null
+++ b/style/Technetium/icon-new.png
Binary files differ
diff --git a/style/Technetium/icon-nonew-sticky.png b/style/Technetium/icon-nonew-sticky.png
new file mode 100644
index 0000000..f148013
--- /dev/null
+++ b/style/Technetium/icon-nonew-sticky.png
Binary files differ
diff --git a/style/Technetium/icon-nonew.png b/style/Technetium/icon-nonew.png
new file mode 100644
index 0000000..547c760
--- /dev/null
+++ b/style/Technetium/icon-nonew.png
Binary files differ
diff --git a/style/Technetium/index.html b/style/Technetium/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Technetium/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Technetium/inv-shade.png b/style/Technetium/inv-shade.png
new file mode 100644
index 0000000..eb9650b
--- /dev/null
+++ b/style/Technetium/inv-shade.png
Binary files differ
diff --git a/style/Technetium/light-shade.png b/style/Technetium/light-shade.png
new file mode 100644
index 0000000..cd95460
--- /dev/null
+++ b/style/Technetium/light-shade.png
Binary files differ
diff --git a/style/imports/base_admin.css b/style/imports/base_admin.css
new file mode 100644
index 0000000..d046690
--- /dev/null
+++ b/style/imports/base_admin.css
@@ -0,0 +1,54 @@
+#adminconsole .block2 {margin-top: 12px}
+
+/*** Admin Main Content ***/
+* html #adstats dd {height: 1%}
+#adstats dd {margin-left: 14em; padding: 3px; margin-bottom: 5px; line-height: 1.5em}
+#adstats dt {float: left; width: 13em; padding: 3px; line-height: 1.5em}
+#adstats {padding: 15px 15px 5px 10px}
+#adintro {padding: 5px}
+#adintro p {padding: 10px}
+#adstats dl {padding: 5px 0 10px 5px}
+#adalerts {padding: 10px}
+#adalerts p {padding: 10px; background: #ffffe1; color: #000000; border: 1px solid #dfe6ee}
+
+#adminconsole fieldset td {text-align: left; padding: 4px; white-space: normal}
+#adminconsole fieldset th {text-align: left; padding: 4px; white-space: normal}
+#adminconsole fieldset td span, #adminconsole fieldset th span {display: block; font-size: 1em; font-weight: normal}
+#adminconsole th {width: 15em; font-weight: bold}
+#adminconsole input, #adminconsole select, #adminconsole textarea {margin-bottom: 0; margin-top: 0; font-weight: normal}
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {vertical-align: top}
+#adminconsole table.aligntop th {padding-top: 0.7em}
+#adminconsole td, #adminconsole th {border-style: solid; border-width: 3px 0 3px 0}
+#adminconsole p {padding-bottom: 6px}
+#adminconsole .topspace {padding-top: 6px}
+#adminconsole p.submittop, #adminconsole p.submitend {text-align: center}
+#adminconsole th.hidehead {color: #f1f1f1}
+#adminconsole thead th {padding-bottom: 0}
+#adminconsole p.linkactions {font-weight: bold; padding-left: 5px}
+#adminconsole th input, #adminconsole div.fsetsubmit {margin-top: 6px}
+
+/*** Particular table settings ***/
+#categoryedit .tcl {width: 25%}
+#censoring .tcl, #censoring .tc2 {width: 20%}
+#edforum .tcl, #edforum .tc2 {width: 8%}
+#edforum .tc2 {width: 6%}
+
+table#forumperms th, table#forumperms td {white-space: normal; width: auto; text-align: center}
+table#forumperms .atcl {text-align: left; width: 15em; white-space: nowrap}
+#adminconsole td.nodefault {background-color: #d59b9b}
+
+/*** User/Ban Search Result Tables ***/
+#users2 th, #bans1 th {text-align: left}
+#users2 th.tcmod {text-align: center}
+#users2 .tcl, #bans1 .tcl {width: auto; text-align: left; padding: 4px 6px}
+#users2 .tc2, #bans1 .tc2 {width: 18%; text-align: left; padding: 4px 6px}
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {width: 12%; text-align: left; padding: 4px 6px}
+#users2 .tc4, #bans1 .tc4 {width: 10%; text-align: center}
+#users2 .tcr {width: 20%; white-space: nowrap}
+#bans1 .tcr {width: 15%; white-space: nowrap}
+#users2 .tcmod {width: 10%; text-align: center}
+#adminconsole #linkst, #adminconsole #linksb a {font-weight: bold}
+
+/*** Plugins ***/
+.plugin .inbox {padding-bottom: 10px}
+.plugin p {padding: 10px 10px 0}
diff --git a/style/imports/index.html b/style/imports/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/imports/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/index.html b/style/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/userlist.php b/userlist.php
new file mode 100644
index 0000000..bb02b63
--- /dev/null
+++ b/userlist.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+else if ($pun_user['g_view_users'] == '0')
+ message($lang_common['No permission'], false, '403 Forbidden');
+
+// Load the userlist.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/userlist.php';
+
+// Load the search.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/search.php';
+
+
+// Determine if we are allowed to view post counts
+$show_post_count = ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod']) ? true : false;
+
+$username = isset($_GET['username']) && $pun_user['g_search_users'] == '1' ? pun_trim($_GET['username']) : '';
+$show_group = isset($_GET['show_group']) ? intval($_GET['show_group']) : -1;
+$sort_by = isset($_GET['sort_by']) && (in_array($_GET['sort_by'], array('username', 'registered')) || ($_GET['sort_by'] == 'num_posts' && $show_post_count)) ? $_GET['sort_by'] : 'username';
+$sort_dir = isset($_GET['sort_dir']) && $_GET['sort_dir'] == 'DESC' ? 'DESC' : 'ASC';
+
+// Create any SQL for the WHERE clause
+$where_sql = array();
+$like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
+
+if ($username != '')
+ $where_sql[] = 'u.username '.$like_command.' \''.$db->escape(str_replace(array('*', '_',), array('%', '\\_'), $username)).'\'';
+if ($show_group > -1)
+ $where_sql[] = 'u.group_id='.$show_group;
+
+// Fetch user count
+$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'users AS u WHERE u.id>1 AND u.group_id!='.PUN_UNVERIFIED.(!empty($where_sql) ? ' AND '.implode(' AND ', $where_sql) : '')) or error('Unable to fetch user list count', __FILE__, __LINE__, $db->error());
+$num_users = $db->result($result);
+
+// Determine the user offset (based on $_GET['p'])
+$num_pages = ceil($num_users / 50);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = 50 * ($p - 1);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['User list']);
+if ($pun_user['g_search_users'] == '1')
+ $focus_element = array('userlist', 'username');
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'userlist.php?username='.urlencode($username).'&amp;show_group='.$show_group.'&amp;sort_by='.$sort_by.'&amp;sort_dir='.$sort_dir);
+
+
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'userlist');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+ <h2><span><?php echo $lang_search['User search'] ?></span></h2>
+ <div class="box">
+ <form id="userlist" method="get" action="userlist.php">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_ul['User find legend'] ?></legend>
+ <div class="infldset">
+<?php if ($pun_user['g_search_users'] == '1'): ?> <label class="conl"><?php echo $lang_common['Username'] ?><br /><input type="text" name="username" value="<?php echo pun_htmlspecialchars($username) ?>" size="25" maxlength="25" /><br /></label>
+<?php endif; ?> <label class="conl"><?php echo $lang_ul['User group']."\n" ?>
+ <br /><select name="show_group">
+ <option value="-1"<?php if ($show_group == -1) echo ' selected="selected"' ?>><?php echo $lang_ul['All users'] ?></option>
+<?php
+
+$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' ORDER BY g_id') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+while ($cur_group = $db->fetch_assoc($result))
+{
+ if ($cur_group['g_id'] == $show_group)
+ echo "\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+ else
+ echo "\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+}
+
+?>
+ </select>
+ <br /></label>
+ <label class="conl"><?php echo $lang_search['Sort by']."\n" ?>
+ <br /><select name="sort_by">
+ <option value="username"<?php if ($sort_by == 'username') echo ' selected="selected"' ?>><?php echo $lang_common['Username'] ?></option>
+ <option value="registered"<?php if ($sort_by == 'registered') echo ' selected="selected"' ?>><?php echo $lang_common['Registered'] ?></option>
+<?php if ($show_post_count): ?> <option value="num_posts"<?php if ($sort_by == 'num_posts') echo ' selected="selected"' ?>><?php echo $lang_ul['No of posts'] ?></option>
+<?php endif; ?> </select>
+ <br /></label>
+ <label class="conl"><?php echo $lang_search['Sort order']."\n" ?>
+ <br /><select name="sort_dir">
+ <option value="ASC"<?php if ($sort_dir == 'ASC') echo ' selected="selected"' ?>><?php echo $lang_search['Ascending'] ?></option>
+ <option value="DESC"<?php if ($sort_dir == 'DESC') echo ' selected="selected"' ?>><?php echo $lang_search['Descending'] ?></option>
+ </select>
+ <br /></label>
+ <p class="clearb"><?php echo ($pun_user['g_search_users'] == '1' ? $lang_ul['User search info'].' ' : '').$lang_ul['User sort info']; ?></p>
+ </div>
+ </fieldset>
+ </div>
+ <p class="buttons"><input type="submit" name="search" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
+ </form>
+ </div>
+</div>
+
+<div class="linkst">
+ <div class="inbox">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<div id="users1" class="blocktable">
+ <h2><span><?php echo $lang_common['User list'] ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_common['Username'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_common['Title'] ?></th>
+<?php if ($show_post_count): ?> <th class="tc3" scope="col"><?php echo $lang_common['Posts'] ?></th>
+<?php endif; ?> <th class="tcr" scope="col"><?php echo $lang_common['Registered'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+// Retrieve a list of user IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT u.id FROM '.$db->prefix.'users AS u WHERE u.id>1 AND u.group_id!='.PUN_UNVERIFIED.(!empty($where_sql) ? ' AND '.implode(' AND ', $where_sql) : '').' ORDER BY '.$sort_by.' '.$sort_dir.', u.id ASC LIMIT '.$start_from.', 50') or error('Unable to fetch user IDs', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result))
+{
+ $user_ids = array();
+ for ($i = 0;$cur_user_id = $db->result($result, $i);$i++)
+ $user_ids[] = $cur_user_id;
+
+ // Grab the users
+ $result = $db->query('SELECT u.id, u.username, u.title, u.num_posts, u.registered, g.g_id, g.g_user_title FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id IN('.implode(',', $user_ids).') ORDER BY '.$sort_by.' '.$sort_dir.', u.id ASC') or error('Unable to fetch user list', __FILE__, __LINE__, $db->error());
+
+ while ($user_data = $db->fetch_assoc($result))
+ {
+ $user_title_field = get_title($user_data);
+
+?>
+ <tr>
+ <td class="tcl"><?php echo '<a href="profile.php?id='.$user_data['id'].'">'.pun_htmlspecialchars($user_data['username']).'</a>' ?></td>
+ <td class="tc2"><?php echo $user_title_field ?></td>
+<?php if ($show_post_count): ?> <td class="tc3"><?php echo forum_number_format($user_data['num_posts']) ?></td>
+<?php endif; ?>
+ <td class="tcr"><?php echo format_time($user_data['registered'], true) ?></td>
+ </tr>
+<?php
+
+ }
+}
+else
+ echo "\t\t\t".'<tr>'."\n\t\t\t\t\t".'<td class="tcl" colspan="'.(($show_post_count) ? 4 : 3).'">'.$lang_search['No hits'].'</td></tr>'."\n";
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox">
+ <p class="pagelink"><?php echo $paging_links ?></p>
+ <div class="clearer"></div>
+ </div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/viewforum.php b/viewforum.php
new file mode 100644
index 0000000..2607a21
--- /dev/null
+++ b/viewforum.php
@@ -0,0 +1,311 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// Load the viewforum.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
+
+// Fetch some info about the forum
+if (!$pun_user['is_guest'])
+ $result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics, s.user_id AS is_subscribed FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_subscriptions AS s ON (f.id=s.forum_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+else
+ $result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics, 0 AS is_subscribed FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$cur_forum = $db->fetch_assoc($result);
+
+// Is this a redirect forum? In that case, redirect!
+if ($cur_forum['redirect_url'] != '')
+{
+ header('Location: '.$cur_forum['redirect_url']);
+ exit;
+}
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+switch ($cur_forum['sort_by'])
+{
+ case 0:
+ $sort_by = 'last_post DESC';
+ break;
+ case 1:
+ $sort_by = 'posted DESC';
+ break;
+ case 2:
+ $sort_by = 'subject ASC';
+ break;
+ default:
+ $sort_by = 'last_post DESC';
+ break;
+}
+
+// Can we or can we not post new topics?
+if (($cur_forum['post_topics'] == '' && $pun_user['g_post_topics'] == '1') || $cur_forum['post_topics'] == '1' || $is_admmod)
+ $post_link = "\t\t\t".'<p class="postlink conr"><a href="post.php?fid='.$id.'">'.$lang_forum['Post topic'].'</a></p>'."\n";
+else
+ $post_link = '';
+
+// Get topic/forum tracking data
+if (!$pun_user['is_guest'])
+ $tracked_topics = get_tracked_topics();
+
+// Determine the topic offset (based on $_GET['p'])
+$num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = $pun_user['disp_topics'] * ($p - 1);
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewforum.php?id='.$id);
+
+// Add relationship meta tags
+$page_head = array();
+$page_head['canonical'] = '<link rel="canonical" href="viewforum.php?id='.$id.($p == 1 ? '' : '&amp;p='.$p).'" title="'.sprintf($lang_common['Page'], $p).'" />';
+
+if ($num_pages > 1)
+{
+ if ($p > 1)
+ $page_head['prev'] = '<link rel="prev" href="viewforum.php?id='.$id.($p == 2 ? '' : '&amp;p='.($p - 1)).'" title="'.sprintf($lang_common['Page'], $p - 1).'" />';
+ if ($p < $num_pages)
+ $page_head['next'] = '<link rel="next" href="viewforum.php?id='.$id.'&amp;p='.($p + 1).'" title="'.sprintf($lang_common['Page'], $p + 1).'" />';
+}
+
+if ($pun_config['o_feed_type'] == '1')
+ $page_head['feed'] = '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;fid='.$id.'&amp;type=rss" title="'.$lang_common['RSS forum feed'].'" />';
+else if ($pun_config['o_feed_type'] == '2')
+ $page_head['feed'] = '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;fid='.$id.'&amp;type=atom" title="'.$lang_common['Atom forum feed'].'" />';
+
+$forum_actions = array();
+
+if (!$pun_user['is_guest'])
+{
+ $token_url = '&amp;csrf_token='.pun_csrf_token();
+
+ if ($pun_config['o_forum_subscriptions'] == '1')
+ {
+ if ($cur_forum['is_subscribed'])
+ $forum_actions[] = '<span>'.$lang_forum['Is subscribed'].' - </span><a id="unsubscribe" href="misc.php?action=unsubscribe&amp;fid='.$id.$token_url.'">'.$lang_forum['Unsubscribe'].'</a>';
+ else
+ $forum_actions[] = '<a href="misc.php?action=subscribe&amp;fid='.$id.$token_url.'">'.$lang_forum['Subscribe'].'</a>';
+ }
+
+ $forum_actions[] = '<a href="misc.php?action=markforumread&amp;fid='.$id.$token_url.'">'.$lang_common['Mark forum read'].'</a>';
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_forum['forum_name']));
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><strong><a href="viewforum.php?id=<?php echo $id ?>"><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></a></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<div id="vf" class="blocktable">
+ <h2><span><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <table>
+ <thead>
+ <tr>
+ <th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
+ <th class="tc2" scope="col"><?php echo $lang_common['Replies'] ?></th>
+<?php if ($pun_config['o_topic_views'] == '1'): ?> <th class="tc3" scope="col"><?php echo $lang_forum['Views'] ?></th>
+<?php endif; ?> <th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php
+
+// Retrieve a list of topic IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$id.' ORDER BY sticky DESC, '.$sort_by.', id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
+
+// If there are topics in this forum
+if ($db->num_rows($result))
+{
+ $topic_ids = array();
+ for ($i = 0; $cur_topic_id = $db->result($result, $i); $i++)
+ $topic_ids[] = $cur_topic_id;
+
+ // Fetch list of topics to display on this page
+ if ($pun_user['is_guest'] || $pun_config['o_show_dot'] == '0')
+ {
+ // Without "the dot"
+ $sql = 'SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.$sort_by.', id DESC';
+ }
+ else
+ {
+ // With "the dot"
+ $sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.id IN('.implode(',', $topic_ids).') GROUP BY t.id'.($db_type == 'pgsql' ? ', t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to, p.poster_id' : '').' ORDER BY t.sticky DESC, t.'.$sort_by.', t.id DESC';
+ }
+
+ $result = $db->query($sql) or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+
+ $topic_count = 0;
+ while ($cur_topic = $db->fetch_assoc($result))
+ {
+ ++$topic_count;
+ $status_text = array();
+ $item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
+ $icon_type = 'icon';
+
+ if (is_null($cur_topic['moved_to']))
+ $last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
+ else
+ $last_post = '- - -';
+
+ if ($pun_config['o_censoring'] == '1')
+ $cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+ if ($cur_topic['sticky'] == '1')
+ {
+ $item_status .= ' isticky';
+ $status_text[] = '<span class="stickytext">'.$lang_forum['Sticky'].'</span>';
+ }
+
+ if ($cur_topic['moved_to'] != 0)
+ {
+ $subject = '<a href="viewtopic.php?id='.$cur_topic['moved_to'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+ $status_text[] = '<span class="movedtext">'.$lang_forum['Moved'].'</span>';
+ $item_status .= ' imoved';
+ }
+ else if ($cur_topic['closed'] == '0')
+ $subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+ else
+ {
+ $subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+ $status_text[] = '<span class="closedtext">'.$lang_forum['Closed'].'</span>';
+ $item_status .= ' iclosed';
+ }
+
+ if (!$pun_user['is_guest'] && $cur_topic['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']) && (!isset($tracked_topics['forums'][$id]) || $tracked_topics['forums'][$id] < $cur_topic['last_post']) && is_null($cur_topic['moved_to']))
+ {
+ $item_status .= ' inew';
+ $icon_type = 'icon icon-new';
+ $subject = '<strong>'.$subject.'</strong>';
+ $subject_new_posts = '<span class="newtext">[ <a href="viewtopic.php?id='.$cur_topic['id'].'&amp;action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a> ]</span>';
+ }
+ else
+ $subject_new_posts = null;
+
+ // Insert the status text before the subject
+ $subject = implode(' ', $status_text).' '.$subject;
+
+ // Should we display the dot or not? :)
+ if (!$pun_user['is_guest'] && $pun_config['o_show_dot'] == '1')
+ {
+ if ($cur_topic['has_posted'] == $pun_user['id'])
+ {
+ $subject = '<strong class="ipost">·&#160;</strong>'.$subject;
+ $item_status .= ' iposted';
+ }
+ }
+
+ $num_pages_topic = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+ if ($num_pages_topic > 1)
+ $subject_multipage = '<span class="pagestext">[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_topic['id']).' ]</span>';
+ else
+ $subject_multipage = null;
+
+ // Should we show the "New posts" and/or the multipage links?
+ if (!empty($subject_new_posts) || !empty($subject_multipage))
+ {
+ $subject .= !empty($subject_new_posts) ? ' '.$subject_new_posts : '';
+ $subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
+ }
+
+?>
+ <tr class="<?php echo $item_status ?>">
+ <td class="tcl">
+ <div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($topic_count + $start_from) ?></div></div>
+ <div class="tclcon">
+ <div>
+ <?php echo $subject."\n" ?>
+ </div>
+ </div>
+ </td>
+ <td class="tc2"><?php echo (is_null($cur_topic['moved_to'])) ? forum_number_format($cur_topic['num_replies']) : '-' ?></td>
+<?php if ($pun_config['o_topic_views'] == '1'): ?> <td class="tc3"><?php echo (is_null($cur_topic['moved_to'])) ? forum_number_format($cur_topic['num_views']) : '-' ?></td>
+<?php endif; ?> <td class="tcr"><?php echo $last_post ?></td>
+ </tr>
+<?php
+
+ }
+}
+else
+{
+ $colspan = ($pun_config['o_topic_views'] == '1') ? 4 : 3;
+
+?>
+ <tr class="rowodd inone">
+ <td class="tcl" colspan="<?php echo $colspan ?>">
+ <div class="icon inone"><div class="nosize"><!-- --></div></div>
+ <div class="tclcon">
+ <div>
+ <strong><?php echo $lang_forum['Empty forum'] ?></strong>
+ </div>
+ </div>
+ </td>
+ </tr>
+<?php
+
+}
+
+?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="linksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+ </div>
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><strong><a href="viewforum.php?id=<?php echo $id ?>"><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></a></strong></li>
+ </ul>
+<?php echo (!empty($forum_actions) ? "\t\t".'<p class="subscribelink clearb">'.implode(' - ', $forum_actions).'</p>'."\n" : '') ?>
+ <div class="clearer"></div>
+ </div>
+</div>
+<?php
+
+$forum_id = $id;
+$footer_style = 'viewforum';
+require PUN_ROOT.'footer.php';
diff --git a/viewtopic.php b/viewtopic.php
new file mode 100644
index 0000000..4a21a55
--- /dev/null
+++ b/viewtopic.php
@@ -0,0 +1,486 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', dirname(__FILE__).'/');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+ message($lang_common['No view'], false, '403 Forbidden');
+
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+$pid = isset($_GET['pid']) ? intval($_GET['pid']) : 0;
+if ($id < 1 && $pid < 1)
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+// Load the viewtopic.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
+
+
+// If a post ID is specified we determine topic ID and page number so we can redirect to the correct message
+if ($pid)
+{
+ $result = $db->query('SELECT topic_id, posted FROM '.$db->prefix.'posts WHERE id='.$pid) or error('Unable to fetch topic ID', __FILE__, __LINE__, $db->error());
+ if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+ list($id, $posted) = $db->fetch_row($result);
+
+ // Determine on which page the post is located (depending on $forum_user['disp_posts'])
+ $result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted<'.$posted) or error('Unable to count previous posts', __FILE__, __LINE__, $db->error());
+ $num_posts = $db->result($result) + 1;
+
+ $_GET['p'] = ceil($num_posts / $pun_user['disp_posts']);
+}
+else
+{
+ // If action=new, we redirect to the first new post (if any)
+ if ($action == 'new')
+ {
+ if (!$pun_user['is_guest'])
+ {
+ // We need to check if this topic has been viewed recently by the user
+ $tracked_topics = get_tracked_topics();
+ $last_viewed = isset($tracked_topics['topics'][$id]) ? $tracked_topics['topics'][$id] : $pun_user['last_visit'];
+
+ $result = $db->query('SELECT MIN(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted>'.$last_viewed) or error('Unable to fetch first new post info', __FILE__, __LINE__, $db->error());
+ $first_new_post_id = $db->result($result);
+
+ if ($first_new_post_id)
+ {
+ header('Location: viewtopic.php?pid='.$first_new_post_id.'#p'.$first_new_post_id);
+ exit;
+ }
+ }
+
+ // If there is no new post, we go to the last post
+ $action = 'last';
+ }
+
+ // If action=last, we redirect to the last post
+ if ($action == 'last')
+ {
+ $result = $db->query('SELECT MAX(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id) or error('Unable to fetch last post info', __FILE__, __LINE__, $db->error());
+ $last_post_id = $db->result($result);
+
+ if ($last_post_id)
+ {
+ header('Location: viewtopic.php?pid='.$last_post_id.'#p'.$last_post_id);
+ exit;
+ }
+ }
+}
+
+
+// Fetch some info about the topic
+if (!$pun_user['is_guest'])
+ $result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+else
+ $result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, 0 AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+
+if (!$db->num_rows($result))
+ message($lang_common['Bad request'], false, '404 Not Found');
+
+$cur_topic = $db->fetch_assoc($result);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_topic['moderators'] != '') ? unserialize($cur_topic['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+if ($is_admmod)
+ $admin_ids = get_admin_ids();
+
+// Can we or can we not post replies?
+if ($cur_topic['closed'] == '0')
+{
+ if (($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1') || $cur_topic['post_replies'] == '1' || $is_admmod)
+ $post_link = "\t\t\t".'<p class="postlink conr"><a href="post.php?tid='.$id.'">'.$lang_topic['Post reply'].'</a></p>'."\n";
+ else
+ $post_link = '';
+}
+else
+{
+ $post_link = $lang_topic['Topic closed'];
+
+ if ($is_admmod)
+ $post_link .= ' / <a href="post.php?tid='.$id.'">'.$lang_topic['Post reply'].'</a>';
+
+ $post_link = "\t\t\t".'<p class="postlink conr">'.$post_link.'</p>'."\n";
+}
+
+
+// Add/update this topic in our list of tracked topics
+if (!$pun_user['is_guest'])
+{
+ $tracked_topics = get_tracked_topics();
+ $tracked_topics['topics'][$id] = time();
+ set_tracked_topics($tracked_topics);
+}
+
+
+// Determine the post offset (based on $_GET['p'])
+$num_pages = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = $pun_user['disp_posts'] * ($p - 1);
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewtopic.php?id='.$id);
+
+
+if ($pun_config['o_censoring'] == '1')
+ $cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+
+$quickpost = false;
+if ($pun_config['o_quickpost'] == '1' &&
+ ($cur_topic['post_replies'] == '1' || ($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1')) &&
+ ($cur_topic['closed'] == '0' || $is_admmod))
+{
+ // Load the post.php language file
+ require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+ $required_fields = array('req_message' => $lang_common['Message']);
+ if ($pun_user['is_guest'])
+ {
+ $required_fields['req_username'] = $lang_post['Guest name'];
+ if ($pun_config['p_force_guest_email'] == '1')
+ $required_fields['req_email'] = $lang_common['Email'];
+ }
+ $quickpost = true;
+}
+
+if (!$pun_user['is_guest'] && $pun_config['o_topic_subscriptions'] == '1')
+{
+ $token_url = '&amp;csrf_token='.pun_csrf_token();
+
+ if ($cur_topic['is_subscribed'])
+ // I apologize for the variable naming here. It's a mix of subscription and action I guess :-)
+ $subscraction = "\t\t".'<p class="subscribelink clearb"><span>'.$lang_topic['Is subscribed'].' - </span><a id="unsubscribe" href="misc.php?action=unsubscribe&amp;tid='.$id.$token_url.'">'.$lang_topic['Unsubscribe'].'</a></p>'."\n";
+ else
+ $subscraction = "\t\t".'<p class="subscribelink clearb"><a href="misc.php?action=subscribe&amp;tid='.$id.$token_url.'">'.$lang_topic['Subscribe'].'</a></p>'."\n";
+}
+else
+ $subscraction = '';
+
+// Add relationship meta tags
+$page_head = array();
+$page_head['canonical'] = '<link rel="canonical" href="viewtopic.php?id='.$id.($p == 1 ? '' : '&amp;p='.$p).'" title="'.sprintf($lang_common['Page'], $p).'" />';
+
+if ($num_pages > 1)
+{
+ if ($p > 1)
+ $page_head['prev'] = '<link rel="prev" href="viewtopic.php?id='.$id.($p == 2 ? '' : '&amp;p='.($p - 1)).'" title="'.sprintf($lang_common['Page'], $p - 1).'" />';
+ if ($p < $num_pages)
+ $page_head['next'] = '<link rel="next" href="viewtopic.php?id='.$id.'&amp;p='.($p + 1).'" title="'.sprintf($lang_common['Page'], $p + 1).'" />';
+}
+
+if ($pun_config['o_feed_type'] == '1')
+ $page_head['feed'] = '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;tid='.$id.'&amp;type=rss" title="'.$lang_common['RSS topic feed'].'" />';
+else if ($pun_config['o_feed_type'] == '2')
+ $page_head['feed'] = '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;tid='.$id.'&amp;type=atom" title="'.$lang_common['Atom topic feed'].'" />';
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_topic['forum_name']), pun_htmlspecialchars($cur_topic['subject']));
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+ <div class="inbox crumbsplus">
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><strong><a href="viewtopic.php?id=<?php echo $id ?>"><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></a></strong></li>
+ </ul>
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+ </div>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<?php
+
+
+require PUN_ROOT.'include/parser.php';
+
+$post_count = 0; // Keep track of post numbers
+
+// Retrieve a list of post IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$id.' ORDER BY id LIMIT '.$start_from.','.$pun_user['disp_posts']) or error('Unable to fetch post IDs', __FILE__, __LINE__, $db->error());
+
+$post_ids = array();
+for ($i = 0;$cur_post_id = $db->result($result, $i);$i++)
+ $post_ids[] = $cur_post_id;
+
+if (empty($post_ids))
+ error('The post table and topic table seem to be out of sync!', __FILE__, __LINE__);
+
+// Retrieve the posts (and their respective poster/online status)
+$result = $db->query('SELECT u.email, u.title, u.url, u.location, u.signature, u.email_setting, u.num_posts, u.registered, u.admin_note, p.id, p.poster AS username, p.poster_id, p.poster_ip, p.poster_email, p.message, p.hide_smilies, p.posted, p.edited, p.edited_by, g.g_id, g.g_user_title, g.g_promote_next_group, o.user_id AS is_online FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online AS o ON (o.user_id=u.id AND o.user_id!=1 AND o.idle=0) WHERE p.id IN ('.implode(',', $post_ids).') ORDER BY p.id', true) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+while ($cur_post = $db->fetch_assoc($result))
+{
+ $post_count++;
+ $user_avatar = '';
+ $user_info = array();
+ $user_contacts = array();
+ $post_actions = array();
+ $is_online = '';
+ $signature = '';
+
+ // If the poster is a registered user
+ if ($cur_post['poster_id'] > 1)
+ {
+ if ($pun_user['g_view_users'] == '1')
+ $username = '<a href="profile.php?id='.$cur_post['poster_id'].'">'.pun_htmlspecialchars($cur_post['username']).'</a>';
+ else
+ $username = pun_htmlspecialchars($cur_post['username']);
+
+ $user_title = get_title($cur_post);
+
+ if ($pun_config['o_censoring'] == '1')
+ $user_title = censor_words($user_title);
+
+ // Format the online indicator
+ $is_online = ($cur_post['is_online'] == $cur_post['poster_id']) ? '<strong>'.$lang_topic['Online'].'</strong>' : '<span>'.$lang_topic['Offline'].'</span>';
+
+ if ($pun_config['o_avatars'] == '1' && $pun_user['show_avatars'] != '0')
+ {
+ if (isset($user_avatar_cache[$cur_post['poster_id']]))
+ $user_avatar = $user_avatar_cache[$cur_post['poster_id']];
+ else
+ $user_avatar = $user_avatar_cache[$cur_post['poster_id']] = generate_avatar_markup($cur_post['poster_id']);
+ }
+
+ // We only show location, register date, post count and the contact links if "Show user info" is enabled
+ if ($pun_config['o_show_user_info'] == '1')
+ {
+ if ($cur_post['location'] != '')
+ {
+ if ($pun_config['o_censoring'] == '1')
+ $cur_post['location'] = censor_words($cur_post['location']);
+
+ $user_info[] = '<dd><span>'.$lang_topic['From'].' '.pun_htmlspecialchars($cur_post['location']).'</span></dd>';
+ }
+
+ $user_info[] = '<dd><span>'.$lang_topic['Registered'].' '.format_time($cur_post['registered'], true).'</span></dd>';
+
+ if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
+ $user_info[] = '<dd><span>'.$lang_topic['Posts'].' '.forum_number_format($cur_post['num_posts']).'</span></dd>';
+
+ // Now let's deal with the contact links (Email and URL)
+ if ((($cur_post['email_setting'] == '0' && !$pun_user['is_guest']) || $pun_user['is_admmod']) && $pun_user['g_send_email'] == '1')
+ $user_contacts[] = '<span class="email"><a href="mailto:'.pun_htmlspecialchars($cur_post['email']).'">'.$lang_common['Email'].'</a></span>';
+ else if ($cur_post['email_setting'] == '1' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+ $user_contacts[] = '<span class="email"><a href="misc.php?email='.$cur_post['poster_id'].'">'.$lang_common['Email'].'</a></span>';
+
+ if ($cur_post['url'] != '')
+ {
+ if ($pun_config['o_censoring'] == '1')
+ $cur_post['url'] = censor_words($cur_post['url']);
+
+ $user_contacts[] = '<span class="website"><a href="'.pun_htmlspecialchars($cur_post['url']).'" rel="nofollow">'.$lang_topic['Website'].'</a></span>';
+ }
+ }
+
+ if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_promote_users'] == '1'))
+ {
+ if ($cur_post['g_promote_next_group'])
+ $user_info[] = '<dd><span><a href="profile.php?action=promote&amp;id='.$cur_post['poster_id'].'&amp;pid='.$cur_post['id'].'&amp;csrf_token='.pun_csrf_token().'">'.$lang_topic['Promote user'].'</a></span></dd>';
+ }
+
+ if ($pun_user['is_admmod'])
+ {
+ $user_info[] = '<dd><span><a href="moderate.php?get_host='.$cur_post['id'].'" title="'.pun_htmlspecialchars($cur_post['poster_ip']).'">'.$lang_topic['IP address logged'].'</a></span></dd>';
+
+ if ($cur_post['admin_note'] != '')
+ $user_info[] = '<dd><span>'.$lang_topic['Note'].' <strong>'.pun_htmlspecialchars($cur_post['admin_note']).'</strong></span></dd>';
+ }
+ }
+ // If the poster is a guest (or a user that has been deleted)
+ else
+ {
+ $username = pun_htmlspecialchars($cur_post['username']);
+ $user_title = get_title($cur_post);
+
+ if ($pun_user['is_admmod'])
+ $user_info[] = '<dd><span><a href="moderate.php?get_host='.$cur_post['id'].'" title="'.pun_htmlspecialchars($cur_post['poster_ip']).'">'.$lang_topic['IP address logged'].'</a></span></dd>';
+
+ if ($pun_config['o_show_user_info'] == '1' && $cur_post['poster_email'] != '' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+ $user_contacts[] = '<span class="email"><a href="mailto:'.pun_htmlspecialchars($cur_post['poster_email']).'">'.$lang_common['Email'].'</a></span>';
+ }
+
+ // Generation post action array (quote, edit, delete etc.)
+ if (!$is_admmod)
+ {
+ if (!$pun_user['is_guest'])
+ $post_actions[] = '<li class="postreport"><span><a href="misc.php?report='.$cur_post['id'].'">'.$lang_topic['Report'].'</a></span></li>';
+
+ if ($cur_topic['closed'] == '0')
+ {
+ if ($cur_post['poster_id'] == $pun_user['id'])
+ {
+ if ((($start_from + $post_count) == 1 && $pun_user['g_delete_topics'] == '1') || (($start_from + $post_count) > 1 && $pun_user['g_delete_posts'] == '1'))
+ $post_actions[] = '<li class="postdelete"><span><a href="delete.php?id='.$cur_post['id'].'">'.$lang_topic['Delete'].'</a></span></li>';
+ if ($pun_user['g_edit_posts'] == '1')
+ $post_actions[] = '<li class="postedit"><span><a href="edit.php?id='.$cur_post['id'].'">'.$lang_topic['Edit'].'</a></span></li>';
+ }
+
+ if (($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1') || $cur_topic['post_replies'] == '1')
+ $post_actions[] = '<li class="postquote"><span><a href="post.php?tid='.$id.'&amp;qid='.$cur_post['id'].'">'.$lang_topic['Quote'].'</a></span></li>';
+ }
+ }
+ else
+ {
+ $post_actions[] = '<li class="postreport"><span><a href="misc.php?report='.$cur_post['id'].'">'.$lang_topic['Report'].'</a></span></li>';
+ if ($pun_user['g_id'] == PUN_ADMIN || !in_array($cur_post['poster_id'], $admin_ids))
+ {
+ $post_actions[] = '<li class="postdelete"><span><a href="delete.php?id='.$cur_post['id'].'">'.$lang_topic['Delete'].'</a></span></li>';
+ $post_actions[] = '<li class="postedit"><span><a href="edit.php?id='.$cur_post['id'].'">'.$lang_topic['Edit'].'</a></span></li>';
+ }
+ $post_actions[] = '<li class="postquote"><span><a href="post.php?tid='.$id.'&amp;qid='.$cur_post['id'].'">'.$lang_topic['Quote'].'</a></span></li>';
+ }
+
+ // Perform the main parsing of the message (BBCode, smilies, censor words etc)
+ $cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+ // Do signature parsing/caching
+ if ($pun_config['o_signatures'] == '1' && $cur_post['signature'] != '' && $pun_user['show_sig'] != '0')
+ {
+ if (isset($signature_cache[$cur_post['poster_id']]))
+ $signature = $signature_cache[$cur_post['poster_id']];
+ else
+ {
+ $signature = parse_signature($cur_post['signature']);
+ $signature_cache[$cur_post['poster_id']] = $signature;
+ }
+ }
+
+?>
+<div id="p<?php echo $cur_post['id'] ?>" class="blockpost<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?><?php if ($cur_post['id'] == $cur_topic['first_post_id']) echo ' firstpost'; ?><?php if ($post_count == 1) echo ' blockpost1'; ?>">
+ <h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?></span> <a href="viewtopic.php?pid=<?php echo $cur_post['id'].'#p'.$cur_post['id'] ?>"><?php echo format_time($cur_post['posted']) ?></a></span></h2>
+ <div class="box">
+ <div class="inbox">
+ <div class="postbody">
+ <div class="postleft">
+ <dl>
+ <dt><strong><?php echo $username ?></strong></dt>
+ <dd class="usertitle"><strong><?php echo $user_title ?></strong></dd>
+<?php if ($user_avatar != '') echo "\t\t\t\t\t\t".'<dd class="postavatar">'.$user_avatar.'</dd>'."\n"; ?>
+<?php if (count($user_info)) echo "\t\t\t\t\t\t".implode("\n\t\t\t\t\t\t", $user_info)."\n"; ?>
+<?php if (count($user_contacts)) echo "\t\t\t\t\t\t".'<dd class="usercontacts">'.implode(' ', $user_contacts).'</dd>'."\n"; ?>
+ </dl>
+ </div>
+ <div class="postright">
+ <h3><?php if ($cur_post['id'] != $cur_topic['first_post_id']) echo $lang_topic['Re'].' '; ?><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></h3>
+ <div class="postmsg">
+ <?php echo $cur_post['message']."\n" ?>
+<?php if ($cur_post['edited'] != '') echo "\t\t\t\t\t\t".'<p class="postedit"><em>'.$lang_topic['Last edit'].' '.pun_htmlspecialchars($cur_post['edited_by']).' ('.format_time($cur_post['edited']).')</em></p>'."\n"; ?>
+ </div>
+<?php if ($signature != '') echo "\t\t\t\t\t".'<div class="postsignature postmsg"><hr />'.$signature.'</div>'."\n"; ?>
+ </div>
+ </div>
+ </div>
+ <div class="inbox">
+ <div class="postfoot clearb">
+ <div class="postfootleft"><?php if ($cur_post['poster_id'] > 1) echo '<p>'.$is_online.'</p>'; ?></div>
+<?php if (count($post_actions)) echo "\t\t\t\t".'<div class="postfootright">'."\n\t\t\t\t\t".'<ul>'."\n\t\t\t\t\t\t".implode("\n\t\t\t\t\t\t", $post_actions)."\n\t\t\t\t\t".'</ul>'."\n\t\t\t\t".'</div>'."\n" ?>
+ </div>
+ </div>
+ </div>
+</div>
+
+<?php
+
+}
+
+?>
+<div class="postlinksb">
+ <div class="inbox crumbsplus">
+ <div class="pagepost">
+ <p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+ </div>
+ <ul class="crumbs">
+ <li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+ <li><span>»&#160;</span><a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+ <li><span>»&#160;</span><strong><a href="viewtopic.php?id=<?php echo $id ?>"><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></a></strong></li>
+ </ul>
+<?php echo $subscraction ?>
+ <div class="clearer"></div>
+ </div>
+</div>
+
+<?php
+
+// Display quick post if enabled
+if ($quickpost)
+{
+
+$cur_index = 1;
+
+?>
+<div id="quickpost" class="blockform">
+ <h2><span><?php echo $lang_topic['Quick post'] ?></span></h2>
+ <div class="box">
+ <form id="quickpostform" method="post" action="post.php?tid=<?php echo $id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
+ <div class="inform">
+ <fieldset>
+ <legend><?php echo $lang_common['Write message legend'] ?></legend>
+ <div class="infldset txtarea">
+ <input type="hidden" name="form_sent" value="1" />
+<?php if ($pun_config['o_topic_subscriptions'] == '1' && ($pun_user['auto_notify'] == '1' || $cur_topic['is_subscribed'])): ?> <input type="hidden" name="subscribe" value="1" />
+<?php endif; ?>
+<?php
+
+if ($pun_user['is_guest'])
+{
+ $email_label = ($pun_config['p_force_guest_email'] == '1') ? '<strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong>' : $lang_common['Email'];
+ $email_form_name = ($pun_config['p_force_guest_email'] == '1') ? 'req_email' : 'email';
+
+?>
+ <label class="conl required"><strong><?php echo $lang_post['Guest name'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_username" size="25" maxlength="25" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+ <label class="conl<?php echo ($pun_config['p_force_guest_email'] == '1') ? ' required' : '' ?>"><?php echo $email_label ?><br /><input type="text" name="<?php echo $email_form_name ?>" size="50" maxlength="80" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+ <div class="clearer"></div>
+<?php
+
+ echo "\t\t\t\t\t\t".'<label class="required"><strong>'.$lang_common['Message'].' <span>'.$lang_common['Required'].'</span></strong><br />';
+}
+else
+ echo "\t\t\t\t\t\t".'<label>';
+
+?>
+<textarea name="req_message" rows="7" cols="75" tabindex="<?php echo $cur_index++ ?>"></textarea></label>
+ <ul class="bblinks">
+ <li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#url" onclick="window.open(this.href); return false;"><?php echo $lang_common['url tag'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1' && $pun_user['g_post_links'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1' && $pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ <li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+ </ul>
+ </div>
+ </fieldset>
+ </div>
+<?php flux_hook('quickpost_before_submit') ?>
+ <p class="buttons"><input type="submit" name="submit" tabindex="<?php echo $cur_index++ ?>" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /> <input type="submit" name="preview" value="<?php echo $lang_topic['Preview'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="p" /></p>
+ </form>
+ </div>
+</div>
+<?php
+
+}
+
+// Increment "num_views" for topic
+if ($pun_config['o_topic_views'] == '1')
+ $db->query('UPDATE '.$db->prefix.'topics SET num_views=num_views+1 WHERE id='.$id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+$forum_id = $cur_topic['forum_id'];
+$footer_style = 'viewtopic';
+require PUN_ROOT.'footer.php';