diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6b97151
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+/.gitignore export-ignore
+/.gitattributes export-ignore
+/.travis.yml export-ignore
diff --git a/.github/workflows/dokuwiki.yml b/.github/workflows/dokuwiki.yml
new file mode 100644
index 0000000..608ef34
--- /dev/null
+++ b/.github/workflows/dokuwiki.yml
@@ -0,0 +1,11 @@
+name: DokuWiki Default Tasks
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '1 18 5 * *'
+
+
+jobs:
+ all:
+ uses: dokuwiki/github-action/.github/workflows/all.yml@main
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..54f680b
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,24 @@
+# Create release on change to plugin.info.txt version line
+# https://github.com/dokuwiki/dokuwiki/issues/3951
+#
+# Requires DOKUWIKI_USER and DOKUWIKI_PASS secrets be set in GitHub Actions
+
+name: Release
+
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - "*.info.txt"
+
+jobs:
+ release:
+ name: Release
+ # https://github.com/dokuwiki/dokuwiki/pull/3966
+ uses: glensc/dokuwiki/.github/workflows/plugin-release.yml@39431875f734bddc35cc6b4a899bbfdec97e8aba
+ secrets:
+ DOKUWIKI_USER: ${{ secrets.DOKUWIKI_USER }}
+ DOKUWIKI_PASS: ${{ secrets.DOKUWIKI_PASS }}
+
+# vim:ts=2:sw=2:et
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/MenuItem.php b/MenuItem.php
new file mode 100644
index 0000000..9bad087
--- /dev/null
+++ b/MenuItem.php
@@ -0,0 +1,35 @@
+getLang('renamepage');
+ }
+}
diff --git a/README b/README
index b24492a..112c7e9 100644
--- a/README
+++ b/README
@@ -1 +1,53 @@
-A DokuWiki pagemove plugin
+move Plugin for DokuWiki
+
+Move and rename pages and media files while maintaining the links.
+
+All documentation for this plugin can be found at
+http://www.dokuwiki.org/plugin:move
+
+If you install this plugin manually, make sure it is installed in
+lib/plugins/move/ - if the folder is called different it
+will not work!
+
+Please refer to http://www.dokuwiki.org/plugins for additional info
+on how to install plugins in DokuWiki.
+
+----
+Copyright (C) Michael Hamann ,
+ Andreas Gohr ,
+ and others
+
+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; version 2 of the License
+
+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.
+
+See the COPYING file in your DokuWiki folder for details
+
+Sources of the icons contained in images/:
+
+disk.png
+folder_add.png
+page.png
+page_link.png
+
+Silk icon set 1.3
+
+_________________________________________
+Mark James
+http://www.famfamfam.com/lab/icons/silk/
+_________________________________________
+
+Licensed under a
+Creative Commons Attribution 2.5 License.
+[ http://creativecommons.org/licenses/by/2.5/ ]
+
+
+rename.png
+
+SEMLabs Free Web Design Icon Set,
+http://semlabs.co.uk/blog/free-web-design-icon-set/
\ No newline at end of file
diff --git a/_test/GeneralTest.php b/_test/GeneralTest.php
new file mode 100644
index 0000000..918656b
--- /dev/null
+++ b/_test/GeneralTest.php
@@ -0,0 +1,84 @@
+assertFileExists($file);
+
+ $info = confToHash($file);
+
+ $this->assertArrayHasKey('base', $info);
+ $this->assertArrayHasKey('author', $info);
+ $this->assertArrayHasKey('email', $info);
+ $this->assertArrayHasKey('date', $info);
+ $this->assertArrayHasKey('name', $info);
+ $this->assertArrayHasKey('desc', $info);
+ $this->assertArrayHasKey('url', $info);
+
+ $this->assertEquals('move', $info['base']);
+ $this->assertRegExp('/^https?:\/\//', $info['url']);
+ $this->assertTrue(mail_isvalid($info['email']));
+ $this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
+ $this->assertTrue(false !== strtotime($info['date']));
+ }
+
+ /**
+ * Test to ensure that every conf['...'] entry in conf/default.php has a corresponding meta['...'] entry in
+ * conf/metadata.php.
+ */
+ public function testPluginConf(): void
+ {
+ $conf_file = __DIR__ . '/../conf/default.php';
+ $meta_file = __DIR__ . '/../conf/metadata.php';
+
+ if (!file_exists($conf_file) && !file_exists($meta_file)) {
+ self::markTestSkipped('No config files exist -> skipping test');
+ }
+
+ if (file_exists($conf_file)) {
+ include($conf_file);
+ }
+ if (file_exists($meta_file)) {
+ include($meta_file);
+ }
+
+ $this->assertEquals(
+ gettype($conf),
+ gettype($meta),
+ 'Both ' . DOKU_PLUGIN . 'move/conf/default.php and ' . DOKU_PLUGIN . 'move/conf/metadata.php have to exist and contain the same keys.'
+ );
+
+ if ($conf !== null && $meta !== null) {
+ foreach ($conf as $key => $value) {
+ $this->assertArrayHasKey(
+ $key,
+ $meta,
+ 'Key $meta[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'move/conf/metadata.php'
+ );
+ }
+
+ foreach ($meta as $key => $value) {
+ $this->assertArrayHasKey(
+ $key,
+ $conf,
+ 'Key $conf[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'move/conf/default.php'
+ );
+ }
+ }
+ }
+}
diff --git a/_test/affectedPagesNs.test.php b/_test/affectedPagesNs.test.php
new file mode 100644
index 0000000..3c55dc5
--- /dev/null
+++ b/_test/affectedPagesNs.test.php
@@ -0,0 +1,58 @@
+abort();
+ parent::tearDown();
+ }
+
+ /**
+ * @covers helper_plugin_move_plan::findAffectedPages
+ * @uses Doku_Indexer
+ */
+ public function test_affectedPagesNS_Media() {
+
+ saveWikiText('oldns:start', '{{oldnsimage_missing.png}}', 'setup');
+ idx_addPage('oldns:start');
+
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper','move_plan');
+
+ $this->assertFalse($plan->inProgress());
+
+ $plan->addMediaNamespaceMove('oldns', 'newns');
+
+ $plan->commit();
+
+ $affected_file = file(TMP_DIR . '/data/meta/__move_affected');
+
+ $this->assertSame('oldns:start',trim($affected_file[0]));
+
+ }
+
+}
diff --git a/_test/cache_handling.test.php b/_test/cache_handling.test.php
new file mode 100644
index 0000000..0dd521c
--- /dev/null
+++ b/_test/cache_handling.test.php
@@ -0,0 +1,47 @@
+pluginsEnabled[] = 'move';
+ parent::setUp();
+ }
+
+ /**
+ * @group slow
+ */
+ function test_cache_handling() {
+ $testid = 'wiki:bar:test';
+ saveWikiText($testid,
+ '[[wiki:foo:]]', 'Test setup');
+ idx_addPage($testid);
+ saveWikiText('wiki:foo:start',
+ 'bar', 'Test setup');
+ idx_addPage('wiki:foo:start');
+
+ sleep(1); // wait in order to make sure that conditions with < give the right result.
+ p_wiki_xhtml($testid); // populate cache
+
+ $cache = new cache_renderer($testid, wikiFN($testid), 'xhtml');
+ $this->assertTrue($cache->useCache());
+
+ /** @var helper_plugin_move_op $move */
+ $move = plugin_load('helper', 'move_op');
+ $this->assertTrue($move->movePage('wiki:foo:start', 'wiki:foo2:start'));
+
+ $cache = new cache_renderer($testid, wikiFN($testid), 'xhtml');
+ $this->assertFalse($cache->useCache());
+
+ }
+
+}
diff --git a/_test/findMissingDocuments.test.php b/_test/findMissingDocuments.test.php
new file mode 100644
index 0000000..b231cd6
--- /dev/null
+++ b/_test/findMissingDocuments.test.php
@@ -0,0 +1,113 @@
+tmpstore;
+ }
+
+}
+
+
+/**
+ * Test cases for helper_plugin_move_plan::stepThroughDocuments function of the move plugin
+ *
+ * @group plugin_move
+ * @group plugin_move_unittests
+ * @group plugins
+ * @group unittests
+ * @covers helper_plugin_move_plan::findMissingDocuments
+ */
+class plugin_move_findMissingPages_test extends DokuWikiTest {
+
+ protected $pluginsEnabled = array('move',);
+ /** @var helper_plugin_move_plan_findMissingDocuments_mock $plan */
+ protected $plan;
+
+ /**
+ * @coversNothing
+ */
+ public function setUp(): void {
+ parent::setUp();
+ $this->plan = new helper_plugin_move_plan_findMissingDocuments_mock();
+ }
+
+
+ /**
+ * @coversNothing
+ */
+ public function tearDown(): void {
+ global $conf;
+
+ $dirs = array('indexdir','datadir','metadir', 'mediadir');
+ foreach ($dirs as $dir) {
+ io_rmdir($conf[$dir],true);
+ mkdir($conf[$dir]);
+ }
+ $this->plan->abort();
+ parent::tearDown();
+ }
+
+
+ function test_findMissingPages_empty () {
+ $this->plan->findMissingDocuments('oldns','newns:');
+ $tmpstore = $this->plan->getTmpstore();
+ $this->assertSame(array(),$tmpstore['miss']);
+ }
+
+ function test_findMissingPages_missingPage_default () {
+ saveWikiText('start','[[oldns:missing]]','test edit');
+ idx_addPage('start');
+ $this->plan->findMissingDocuments('oldns:','newns:');
+ $tmpstore = $this->plan->getTmpstore();
+ $this->assertSame(array('oldns:missing' => 'newns:missing',),$tmpstore['miss']);
+ }
+
+ function test_findMissingPages_missingPage_explicit () {
+ saveWikiText('start','[[oldns:missing]]','test edit');
+ idx_addPage('start');
+ $this->plan->findMissingDocuments('oldns:','newns:',helper_plugin_move_plan::TYPE_PAGES);
+ $tmpstore = $this->plan->getTmpstore();
+ $this->assertSame(array('oldns:missing' => 'newns:missing',),$tmpstore['miss']);
+ }
+
+ function test_findMissingPages_missingPage_integrated () {
+ saveWikiText('oldns:start','[[oldns:missing]] {{oldns:missing.png}}','test edit');
+ idx_addPage('oldns:start');
+ $this->plan->addPageNamespaceMove('oldns', 'newns');
+ $this->plan->addMediaNamespaceMove('oldns', 'newns');
+
+ $this->plan->commit();
+
+ $missing_file = file(TMP_DIR . '/data/meta/__move_missing');
+ $this->assertSame(array("oldns:missing\tnewns:missing\n",),$missing_file,'new configuration fails');
+
+ $missing_media_file = file(TMP_DIR . '/data/meta/__move_missing_media');
+ $this->assertSame(array("oldns:missing.png\tnewns:missing.png\n",),$missing_media_file,'new configuration fails');
+
+ }
+
+ function test_findMissingPages_missingMedia () {
+ saveWikiText('start','{{oldns:missing.png}}','test edit');
+ idx_addPage('start');
+ $this->plan->findMissingDocuments('oldns:','newns:',helper_plugin_move_plan::TYPE_MEDIA);
+ $tmpstore = $this->plan->getTmpstore();
+ $this->assertSame(array('oldns:missing.png' => 'newns:missing.png',),$tmpstore['miss_media']);
+ }
+
+ function test_findMissingDocuments_nonMissingMedia () {
+ $filepath = DOKU_TMP_DATA.'media/oldns/oldnsimage.png';
+ io_makeFileDir($filepath);
+ io_saveFile($filepath,'');
+ saveWikiText('start','{{oldns:oldnsimage.png}}','test edit');
+ idx_addPage('start');
+ $this->plan->findMissingDocuments('oldns:','newns:',helper_plugin_move_plan::TYPE_MEDIA);
+ $tmpstore = $this->plan->getTmpstore();
+ $this->assertSame(array(),$tmpstore['miss_media']);
+ }
+}
diff --git a/_test/handler.test.php b/_test/handler.test.php
new file mode 100644
index 0000000..cc05be4
--- /dev/null
+++ b/_test/handler.test.php
@@ -0,0 +1,75 @@
+pluginsEnabled[] = 'move';
+ parent::setUp();
+ }
+
+ public function test_relativeLink() {
+ /** @var $handler helper_plugin_move_handler */
+ $handler = plugin_load('helper', 'move_handler');
+ $handler->init('deep:namespace:page', 'used:to:be:here', array(), array(), array());
+
+ $tests = array(
+ 'deep:namespace:new1' => 'new1',
+ 'deep:new2' => '..:new2',
+ 'new3' => ':new3', // absolute is shorter than relative
+ 'deep:namespace:deeper:new4' => '.deeper:new4',
+ 'deep:namespace:deeper:deepest:new5' => '.deeper:deepest:new5',
+ 'deep:foobar:new6' => '..:foobar:new6',
+ );
+
+ foreach($tests as $new => $rel) {
+ $this->assertEquals($rel, $handler->relativeLink('foo', $new, 'page'));
+ }
+
+ $this->assertEquals('.deeper:', $handler->relativeLink('.deeper:', 'deep:namespace:deeper:start', 'page'));
+ $this->assertEquals('.:', $handler->relativeLink('.:', 'deep:namespace:start', 'page'));
+ }
+
+ public function test_resolveMoves() {
+ /** @var $handler helper_plugin_move_handler */
+ $handler = plugin_load('helper', 'move_handler');
+ $handler->init(
+ 'deep:namespace:page',
+ 'used:to:be:here',
+ array(
+ array('used:to:be:here', 'deep:namespace:page'),
+ array('foo', 'bar'),
+ array('used:to:be:this1', 'used:to:be:that1'),
+ array('used:to:be:this2', 'deep:namespace:that1'),
+ array('used:to:be:this3', 'deep:that3'),
+ array('deep:that3', 'but:got:moved3'),
+ ),
+ array(),
+ array()
+ );
+
+ $tests = array(
+ 'used:to:be:here' => 'deep:namespace:page', // full link to self
+ ':foo' => 'bar', // absolute link that moved
+ ':bang' => 'bang', // absolute link that did not move
+ 'foo' => 'used:to:be:foo', // relative link that did not move
+ 'this1' => 'used:to:be:that1', // relative link that did not move but is in move list
+ 'this2' => 'deep:namespace:that1', // relative link that moved
+ 'this3' => 'but:got:moved3', // relative link that moved twice
+ );
+
+ foreach($tests as $match => $id) {
+ $this->assertEquals($id, $handler->resolveMoves($match, 'page'));
+ }
+ }
+
+}
diff --git a/_test/log.test.php b/_test/log.test.php
new file mode 100644
index 0000000..07582fa
--- /dev/null
+++ b/_test/log.test.php
@@ -0,0 +1,46 @@
+build_log_line('P','oldpage','newpage',true);
+
+ $expected_log = "$now\t$date\tP\toldpage\tnewpage\tsuccess\t\n";
+
+ $this->assertSame($expected_log, $actual_log);
+ }
+
+ public function test_log_build_line_failure() {
+ global $MSG;
+ $MSG = array();
+ $msg = array('msg'=>"TestMessage01",);
+ array_push($MSG,$msg);
+
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper', 'move_plan');
+ $now = time();
+ $date = date('Y-m-d H:i:s', $now);
+
+ $actual_log = $plan->build_log_line('P','oldpage','newpage',false);
+
+ $expected_log = "$now\t$date\tP\toldpage\tnewpage\tfailed\tTestMessage01\n";
+
+ $this->assertSame($expected_log, $actual_log);
+ }
+
+}
diff --git a/_test/mediamove.test.php b/_test/mediamove.test.php
new file mode 100644
index 0000000..da532ac
--- /dev/null
+++ b/_test/mediamove.test.php
@@ -0,0 +1,124 @@
+pluginsEnabled[] = 'move';
+ parent::setUp();
+ }
+
+ /**
+ * @group slow
+ */
+ public function test_movePageWithRelativeMedia() {
+ $src = 'mediareltest:foo';
+ saveWikiText($src,
+ '{{ myimage.png}} [[:start|{{ testimage.png?200x800 }}]] [[bar|{{testimage.gif?400x200}}]]
+[[doku>wiki:dokuwiki|{{wiki:logo.png}}]] [[http://www.example.com|{{testimage.jpg}}]]
+[[doku>wiki:foo|{{foo.gif?200x3000}}]]', 'Test setup');
+ idx_addPage($src);
+
+ $dst = 'foo';
+
+ /** @var helper_plugin_move_op $move */
+ $move = plugin_load('helper', 'move_op');
+ $this->assertTrue($move->movePage($src, $dst));
+
+ $this->assertEquals('{{ mediareltest:myimage.png}} [[:start|{{ mediareltest:testimage.png?200x800 }}]] [[mediareltest:bar|{{mediareltest:testimage.gif?400x200}}]]
+[[doku>wiki:dokuwiki|{{wiki:logo.png}}]] [[http://www.example.com|{{mediareltest:testimage.jpg}}]]
+[[doku>wiki:foo|{{mediareltest:foo.gif?200x3000}}]]', rawWiki('foo'));
+ }
+
+ /**
+ * @group slow
+ */
+ public function test_moveSingleMedia() {
+ global $AUTH_ACL;
+ $AUTH_ACL[] = "wiki:*\t@ALL\t16";
+ $AUTH_ACL[] = "foobar:*\t@ALL\t8";
+
+ saveWikiText('wiki:movetest', '{{wiki:dokuwiki-128.png?200}}', 'Test initialized');
+ idx_addPage('wiki:movetest');
+
+ $src = 'wiki:dokuwiki-128.png';
+ $dst = 'foobar:logo.png';
+
+ /** @var helper_plugin_move_op $move */
+ $move = plugin_load('helper', 'move_op');
+ $this->assertTrue($move->moveMedia($src, $dst));
+
+ $this->assertTrue(@file_exists(mediaFn('foobar:logo.png')));
+
+ $this->assertEquals('{{foobar:logo.png?200}}', rawWiki('wiki:movetest'));
+ }
+
+ /**
+ * @group slow
+ */
+ public function test_moveSingleMedia_colonstart() {
+ global $AUTH_ACL;
+ $AUTH_ACL[] = "wiki:*\t@ALL\t16";
+ $AUTH_ACL[] = "foobar:*\t@ALL\t16";
+ $AUTH_ACL[] = "*\t@ALL\t8";
+
+ $filepath = DOKU_TMP_DATA.'media/wiki/testimage.png';
+ io_makeFileDir($filepath);
+ io_saveFile($filepath,'');
+
+ saveWikiText('wiki:movetest', '{{:wiki:testimage.png?200}}', 'Test initialized');
+ idx_addPage('wiki:movetest');
+
+ $src = 'wiki:testimage.png';
+ $dst = 'foobar:logo_2.png';
+
+ /** @var helper_plugin_move_op $move */
+ $move = plugin_load('helper', 'move_op');
+ $this->assertTrue($move->moveMedia($src, $dst));
+
+ $this->assertTrue(@file_exists(mediaFn('foobar:logo_2.png')));
+
+ $this->assertEquals('{{foobar:logo_2.png?200}}', rawWiki('wiki:movetest'));
+
+ $this->assertTrue($move->moveMedia($dst, 'logo_2.png'));
+
+ $this->assertTrue(@file_exists(mediaFn('logo_2.png')));
+
+ $this->assertEquals('{{:logo_2.png?200}}', rawWiki('wiki:movetest'));
+ }
+
+ /**
+ * @group slow
+ */
+ public function test_moveSingleMedia_space() {
+ global $AUTH_ACL;
+ $AUTH_ACL[] = "wiki:*\t@ALL\t16";
+ $AUTH_ACL[] = "foobar:*\t@ALL\t8";
+
+ $filepath = DOKU_TMP_DATA.'media/wiki/foo/test_image.png';
+ io_makeFileDir($filepath);
+ io_saveFile($filepath,'');
+
+ saveWikiText('wiki:movetest', '{{:wiki:foo:test image.png?200|test image}}', 'Test initialized');
+ idx_addPage('wiki:movetest');
+
+ $src = 'wiki:foo:test_image.png';
+ $dst = 'wiki:foobar:test_image.png';
+
+ /** @var helper_plugin_move_op $move */
+ $move = plugin_load('helper', 'move_op');
+ $this->assertTrue($move->moveMedia($src, $dst));
+
+ $this->assertTrue(@file_exists(mediaFn('wiki:foobar:test_image.png')));
+
+ $this->assertEquals('{{wiki:foobar:test_image.png?200|test image}}', rawWiki('wiki:movetest'));
+ }
+}
diff --git a/_test/namespace_move.test.php b/_test/namespace_move.test.php
new file mode 100644
index 0000000..500a775
--- /dev/null
+++ b/_test/namespace_move.test.php
@@ -0,0 +1,478 @@
+pluginsEnabled[] = 'move';
+ parent::setUp();
+ }
+
+ /**
+ * @coversNothing
+ */
+ public function tearDown(): void {
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper', 'move_plan');
+ $plan->abort();
+
+ io_rmdir(DOKU_TMP_DATA."pages/newns",true);
+ io_rmdir(DOKU_TMP_DATA."media/newns",true);
+ io_rmdir(DOKU_TMP_DATA."meta/newns",true);
+
+ parent::tearDown();
+ }
+
+ /**
+ * This is an integration test, which checks the correct working of an entire namespace move.
+ * Hence it is not an unittest, hence it @coversNothing
+ *
+ * @group slow
+ */
+ public function test_move_wiki_namespace() {
+ global $AUTH_ACL;
+
+ $AUTH_ACL[] = "wiki:*\t@ALL\t16";
+
+ idx_addPage('wiki:dokuwiki');
+ idx_addPage('wiki:syntax');
+
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper', 'move_plan');
+
+ $this->assertFalse($plan->inProgress());
+
+ $plan->addPageNamespaceMove('wiki', 'foo');
+ $plan->addMediaNamespaceMove('wiki', 'foo');
+
+ $plan->commit();
+
+ $this->assertSame(1, $plan->nextStep(),'pages');
+ $this->assertSame(1, $plan->nextStep(),'media');
+ $this->assertSame(1, $plan->nextStep(),'missing');
+ $this->assertSame(1, $plan->nextStep(),'namespace');
+ $this->assertSame(1, $plan->nextStep(),'autorewrite');
+ $this->assertSame(0, $plan->nextStep(),'done');
+
+ $this->assertFileExists(wikiFN('foo:dokuwiki'));
+ $this->assertFileNotExists(wikiFN('wiki:syntax'));
+ $this->assertFileExists(mediaFN('foo:dokuwiki-128.png'));
+ }
+
+ /**
+ * This is an integration test, which checks the correct working of an entire namespace move.
+ * Hence it is not an unittest, hence it @coversNothing
+ *
+ * @group slow
+ */
+ public function test_move_missing() {
+ saveWikiText('oldspace:page', '[[missing]]', 'setup');
+ idx_addPage('oldspace:page');
+
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper', 'move_plan');
+
+ $this->assertFalse($plan->inProgress());
+
+ $plan->addPageNamespaceMove('oldspace', 'newspace');
+
+ $plan->commit();
+
+ $this->assertSame(1, $plan->nextStep(),'page');
+ $this->assertSame(1, $plan->nextStep(),'missing');
+ $this->assertSame(1, $plan->nextStep(),'namespace');
+ $this->assertSame(1, $plan->nextStep(),'autorewrite');
+ $this->assertSame(0, $plan->nextStep(),'done');
+
+ $this->assertFileExists(wikiFN('newspace:page'));
+ $this->assertFileNotExists(wikiFN('oldspace:page'));
+
+ $this->assertEquals('[[missing]]', rawWiki('newspace:page'));
+ }
+
+ /**
+ * @covers helper_plugin_move_plan::findAffectedPages
+ * @uses Doku_Indexer
+ */
+ public function test_move_affected() {
+ saveWikiText('oldaffectedspace:page', '[[missing]]', 'setup');
+ idx_addPage('oldaffectedspace:page');
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper', 'move_plan');
+
+ $this->assertFalse($plan->inProgress());
+
+ $plan->addPageNamespaceMove('oldaffectedspace', 'newaffectedspace');
+
+ $plan->commit();
+
+ $affected_file = file(TMP_DIR . '/data/meta/__move_affected');
+ $this->assertSame('newaffectedspace:page',trim($affected_file[0]));
+ }
+
+ /**
+ * This is an integration test, which checks the correct working of an entire namespace move.
+ * Hence it is not an unittest, hence it @coversNothing
+ *
+ * @group slow
+ */
+ function test_move_large_ns(){
+
+ $this->markTestSkipped(
+ 'This test randomly fails with the page "testns:start" being moved, but "start" not being rewritten in the request.'
+ );
+
+ global $conf;
+
+ $test = '[[testns:start]] [[testns:test_page17]]';
+ $summary = 'testsetup';
+
+
+ saveWikiText(':start', $test, $summary);
+ idx_addPage(':start');
+ saveWikiText('testns:start', $test, $summary);
+ idx_addPage('testns:start');
+ saveWikiText('testns:test_page1', $test, $summary);
+ idx_addPage('testns:test_page1');
+ saveWikiText('testns:test_page2', $test, $summary);
+ idx_addPage('testns:test_page2');
+ saveWikiText('testns:test_page3', $test, $summary);
+ idx_addPage('testns:test_page3');
+ saveWikiText('testns:test_page4', $test, $summary);
+ idx_addPage('testns:test_page4');
+ saveWikiText('testns:test_page5', $test, $summary);
+ idx_addPage('testns:test_page5');
+ saveWikiText('testns:test_page6', $test, $summary);
+ idx_addPage('testns:test_page6');
+ saveWikiText('testns:test_page7', $test, $summary);
+ idx_addPage('testns:test_page7');
+ saveWikiText('testns:test_page8', $test, $summary);
+ idx_addPage('testns:test_page8');
+ saveWikiText('testns:test_page9', $test, $summary);
+ idx_addPage('testns:test_page9');
+ saveWikiText('testns:test_page10', $test, $summary);
+ idx_addPage('testns:test_page10');
+ saveWikiText('testns:test_page11', $test, $summary);
+ idx_addPage('testns:test_page11');
+ saveWikiText('testns:test_page12', $test, $summary);
+ idx_addPage('testns:test_page12');
+ saveWikiText('testns:test_page13', $test, $summary);
+ idx_addPage('testns:test_page13');
+ saveWikiText('testns:test_page14', $test, $summary);
+ idx_addPage('testns:test_page14');
+ saveWikiText('testns:test_page15', $test, $summary);
+ idx_addPage('testns:test_page15');
+ saveWikiText('testns:test_page16', $test, $summary);
+ idx_addPage('testns:test_page16');
+ saveWikiText('testns:test_page17', $test, $summary);
+ idx_addPage('testns:test_page17');
+ saveWikiText('testns:test_page18', $test, $summary);
+ idx_addPage('testns:test_page18');
+ saveWikiText('testns:test_page19', $test, $summary);
+ idx_addPage('testns:test_page19');
+
+ $conf['plugin']['move']['autorewrite'] = 0;
+
+ /** @var helper_plugin_move_plan $plan */
+ $plan = plugin_load('helper', 'move_plan');
+
+ $this->assertFalse($plan->inProgress());
+
+ $plan->addPageNamespaceMove('testns', 'foo:testns');
+
+ $plan->commit();
+ global $conf;
+ $lockfile = $conf['lockdir'] . 'move.lock';
+
+ $this->assertSame(10, $plan->nextStep(),"After processing first chunk of pages, 10 steps should be left");
+
+ $request = new TestRequest();
+ $response = $request->get();
+ $actual_response = $response->getContent();
+ //clean away clutter
+ $actual_response = substr($actual_response,strpos($actual_response,"") + 23);
+ $actual_response = substr($actual_response,strpos($actual_response, 'doku.php'));
+ $actual_response = substr($actual_response,0,strpos($actual_response,""));
+ $actual_response = trim($actual_response);
+ $actual_response = rtrim($actual_response,"
';
+ }
+
+ /**
+ * print the HTML tree structure
+ *
+ * @param int $type
+ */
+ protected function htmlTree($type = self::TYPE_PAGES) {
+ $data = $this->tree($type);
+
+ // wrap a list with the root level around the other namespaces
+ array_unshift(
+ $data, array(
+ 'level' => 0, 'id' => '*', 'type' => 'd',
+ 'open' => 'true', 'label' => $this->getLang('root')
+ )
+ );
+ echo html_buildlist(
+ $data, 'tree_list idx',
+ array($this, 'html_list'),
+ array($this, 'html_li')
+ );
+ }
+
+ /**
+ * Build a tree info structure from media or page directories
+ *
+ * @param int $type
+ * @param string $open The hierarchy to open FIXME not supported yet
+ * @param string $base The namespace to start from
+ * @return array
+ */
+ public function tree($type = self::TYPE_PAGES, $open = '', $base = '') {
+ global $conf;
+
+ $opendir = utf8_encodeFN(str_replace(':', '/', $open));
+ $basedir = utf8_encodeFN(str_replace(':', '/', $base));
+
+ $opts = array(
+ 'pagesonly' => ($type == self::TYPE_PAGES),
+ 'listdirs' => true,
+ 'listfiles' => true,
+ 'sneakyacl' => $conf['sneaky_index'],
+ 'showmsg' => false,
+ 'depth' => 1,
+ 'showhidden' => true
+ );
+
+ $data = array();
+ if($type == self::TYPE_PAGES) {
+ search($data, $conf['datadir'], 'search_universal', $opts, $basedir);
+ } elseif($type == self::TYPE_MEDIA) {
+ search($data, $conf['mediadir'], 'search_universal', $opts, $basedir);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Item formatter for the tree view
+ *
+ * User function for html_buildlist()
+ *
+ * @author Andreas Gohr
+ */
+ function html_list($item) {
+ $ret = '';
+ // what to display
+ if(!empty($item['label'])) {
+ $base = $item['label'];
+ } else {
+ $base = ':' . $item['id'];
+ $base = substr($base, strrpos($base, ':') + 1);
+ }
+
+ if($item['id'] == '*') $item['id'] = '';
+
+ if ($item['id']) {
+ $ret .= ' ';
+ }
+
+ // namespace or page?
+ if($item['type'] == 'd') {
+ $ret .= '';
+ $ret .= $base;
+ $ret .= '';
+ } else {
+ $ret .= '';
+ $ret .= noNS($item['id']);
+ $ret .= '';
+ }
+
+ if($item['id']) $ret .= '';
+ else $ret .= '';
+
+ return $ret;
+ }
+
+ /**
+ * print the opening LI for a list item
+ *
+ * @param array $item
+ * @return string
+ */
+ function html_li($item) {
+ if($item['id'] == '*') $item['id'] = '';
+
+ $params = array();
+ $params['class'] = ' type-' . $item['type'];
+ if($item['type'] == 'd') $params['class'] .= ' ' . ($item['open'] ? 'open' : 'closed');
+ $params['data-name'] = noNS($item['id']);
+ $params['data-id'] = $item['id'];
+ $attr = buildAttributes($params);
+
+ return "
";
+ }
+
+}
diff --git a/conf/default.php b/conf/default.php
new file mode 100644
index 0000000..cc1cbe7
--- /dev/null
+++ b/conf/default.php
@@ -0,0 +1,7 @@
+
+ * @author Andreas Gohr
+ */
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+/**
+ * Class helper_plugin_move_file
+ *
+ * This helps with moving files from one folder to another. It simply matches files and moves them
+ * arround. No fancy rewriting happens here.
+ */
+class helper_plugin_move_file extends DokuWiki_Plugin {
+
+ /**
+ * Move the meta files of a page
+ *
+ * @param string $src_ns The original namespace
+ * @param string $src_name The original basename of the moved doc (empty for namespace moves)
+ * @param string $dst_ns The namespace after the move
+ * @param string $dst_name The basename after the move (empty for namespace moves)
+ * @return bool If the meta files were moved successfully
+ */
+ public function movePageMeta($src_ns, $src_name, $dst_ns, $dst_name) {
+ global $conf;
+
+ $regex = '\.[^.]+';
+ return $this->execute($conf['metadir'], $src_ns, $src_name, $dst_ns, $dst_name, $regex);
+ }
+
+ /**
+ * Move the old revisions of a page
+ *
+ * @param string $src_ns The original namespace
+ * @param string $src_name The original basename of the moved doc (empty for namespace moves)
+ * @param string $dst_ns The namespace after the move
+ * @param string $dst_name The basename after the move (empty for namespace moves)
+ * @return bool If the attic files were moved successfully
+ */
+ public function movePageAttic($src_ns, $src_name, $dst_ns, $dst_name) {
+ global $conf;
+
+ $regex = '\.\d+\.txt(?:\.gz|\.bz2)?';
+ return $this->execute($conf['olddir'], $src_ns, $src_name, $dst_ns, $dst_name, $regex);
+ }
+
+ /**
+ * Move the meta files of the page that is specified in the options.
+ *
+ * @param string $src_ns The original namespace
+ * @param string $src_name The original basename of the moved doc (empty for namespace moves)
+ * @param string $dst_ns The namespace after the move
+ * @param string $dst_name The basename after the move (empty for namespace moves)
+ * @return bool If the meta files were moved successfully
+ */
+ public function moveMediaMeta($src_ns, $src_name, $dst_ns, $dst_name) {
+ global $conf;
+
+ $regex = '\.[^.]+';
+ return $this->execute($conf['mediametadir'], $src_ns, $src_name, $dst_ns, $dst_name, $regex);
+ }
+
+ /**
+ * Move the old revisions of the media file that is specified in the options
+ *
+ * @param string $src_ns The original namespace
+ * @param string $src_name The original basename of the moved doc (empty for namespace moves)
+ * @param string $dst_ns The namespace after the move
+ * @param string $dst_name The basename after the move (empty for namespace moves)
+ * @return bool If the attic files were moved successfully
+ */
+ public function moveMediaAttic($src_ns, $src_name, $dst_ns, $dst_name) {
+ global $conf;
+
+ $ext = mimetype($src_name);
+ if($ext[0] !== false) {
+ $name = substr($src_name, 0, -1 * strlen($ext[0]) - 1);
+ } else {
+ $name = $src_name;
+ }
+ $newext = mimetype($dst_name);
+ if($newext[0] !== false) {
+ $newname = substr($dst_name, 0, -1 * strlen($newext[0]) - 1);
+ } else {
+ $newname = $dst_name;
+ }
+ $regex = '\.\d+\.' . preg_quote((string) $ext[0], '/');
+
+ return $this->execute($conf['mediaolddir'], $src_ns, $name, $dst_ns, $newname, $regex);
+ }
+
+ /**
+ * Moves the subscription file for a namespace
+ *
+ * @param string $src_ns
+ * @param string $dst_ns
+ * @return bool
+ */
+ public function moveNamespaceSubscription($src_ns, $dst_ns){
+ global $conf;
+
+ $regex = '\.mlist';
+ return $this->execute($conf['metadir'], $src_ns, '', $dst_ns, '', $regex);
+ }
+
+ /**
+ * Executes the move op
+ *
+ * @param string $dir The root path of the files (e.g. $conf['metadir'] or $conf['olddir']
+ * @param string $src_ns The original namespace
+ * @param string $src_name The original basename of the moved doc (empty for namespace moves)
+ * @param string $dst_ns The namespace after the move
+ * @param string $dst_name The basename after the move (empty for namespace moves)
+ * @param string $extregex Regular expression for matching the extension of the file that shall be moved
+ * @return bool If the files were moved successfully
+ */
+ protected function execute($dir, $src_ns, $src_name, $dst_ns, $dst_name, $extregex) {
+ $old_path = $dir;
+ if($src_ns != '') $old_path .= '/' . utf8_encodeFN(str_replace(':', '/', $src_ns));
+ $new_path = $dir;
+ if($dst_ns != '') $new_path .= '/' . utf8_encodeFN(str_replace(':', '/', $dst_ns));
+ $regex = '/^' . preg_quote(utf8_encodeFN($src_name)) . '(' . $extregex . ')$/u';
+
+ if(!is_dir($old_path)) return true; // no media files found
+
+ $dh = @opendir($old_path);
+ if($dh) {
+ while(($file = readdir($dh)) !== false) {
+ if($file == '.' || $file == '..') continue;
+ $match = array();
+ if(is_file($old_path . '/' . $file) && preg_match($regex, $file, $match)) {
+ if(!is_dir($new_path)) {
+ if(!io_mkdir_p($new_path)) {
+ msg('Creating directory ' . hsc($new_path) . ' failed.', -1);
+ return false;
+ }
+ }
+ if(!io_rename($old_path . '/' . $file, $new_path . '/' . utf8_encodeFN($dst_name . $match[1]))) {
+ msg('Moving ' . hsc($old_path . '/' . $file) . ' to ' . hsc($new_path . '/' . utf8_encodeFN($dst_name . $match[1])) . ' failed.', -1);
+ return false;
+ }
+ }
+ }
+ closedir($dh);
+ } else {
+ msg('Directory ' . hsc($old_path) . ' couldn\'t be opened.', -1);
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/helper/handler.php b/helper/handler.php
new file mode 100644
index 0000000..fff2254
--- /dev/null
+++ b/helper/handler.php
@@ -0,0 +1,387 @@
+
+ */
+
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+/**
+ * Handler class for move. It does the actual rewriting of the content.
+ *
+ * Note: This is not actually a valid DokuWiki Helper plugin and can not be loaded via plugin_load()
+ */
+class helper_plugin_move_handler extends DokuWiki_Plugin {
+ public $calls = '';
+
+ protected $id;
+ protected $ns;
+ protected $origID;
+ protected $origNS;
+ protected $page_moves;
+ protected $media_moves;
+ protected $handlers;
+
+ /**
+ * Do not allow re-using instances.
+ *
+ * @return bool false - the handler must not be re-used.
+ */
+ public function isSingleton() {
+ return false;
+ }
+
+ /**
+ * Initialize the move handler.
+ *
+ * @param string $id The id of the text that is passed to the handler
+ * @param string $original The name of the original ID of this page. Same as $id if this page wasn't moved
+ * @param array $page_moves Moves that shall be considered in the form [[$old,$new],...] ($old can be $original)
+ * @param array $media_moves Moves of media files that shall be considered in the form $old => $new
+ * @param array $handlers Handlers for plugin content in the form $plugin_name => $callback
+ */
+ public function init($id, $original, $page_moves, $media_moves, $handlers) {
+ $this->id = $id;
+ $this->ns = getNS($id);
+ $this->origID = $original;
+ $this->origNS = getNS($original);
+ $this->page_moves = $page_moves;
+ $this->media_moves = $media_moves;
+ $this->handlers = $handlers;
+ }
+
+ /**
+ * Go through the list of moves and find the new value for the given old ID
+ *
+ * @param string $old the old, full qualified ID
+ * @param string $type 'media' or 'page'
+ * @throws Exception on bad argument
+ * @return string the new full qualified ID
+ */
+ public function resolveMoves($old, $type) {
+ global $conf;
+
+ if($type != 'media' && $type != 'page') throw new Exception('Not a valid type');
+
+ $old = resolve_id($this->origNS, $old, false);
+
+ if($type == 'page') {
+ // FIXME this simply assumes that the link pointed to :$conf['start'], but it could also point to another page
+ // resolve_pageid does a lot more here, but we can't really assume this as the original pages might have been
+ // deleted already
+ if(substr($old, -1) === ':' || $old === '') $old .= $conf['start'];
+
+ $moves = $this->page_moves;
+ } else {
+ $moves = $this->media_moves;
+ }
+
+ $old = cleanID($old);
+
+ foreach($moves as $move) {
+ if($move[0] == $old) {
+ $old = $move[1];
+ }
+ }
+
+ return $old; // this is now new
+ }
+
+ /**
+ * if the old link ended with a colon and the new one is a start page, adjust
+ *
+ * @param $relold string the old, possibly relative ID
+ * @param $new string the new, full qualified ID
+ * @param $type 'media' or 'page'
+ * @return string
+ */
+ protected function _nsStartCheck($relold, $new, $type) {
+ global $conf;
+ if($type == 'page' && substr($relold, -1) == ':') {
+ $len = strlen($conf['start']);
+ if($new == $conf['start']) {
+ $new = '.:';
+ } else if(substr($new, -1 * ($len + 1)) == ':' . $conf['start']) {
+ $new = substr($new, 0, -1 * $len);
+ }
+ }
+ return $new;
+ }
+
+ /**
+ * Construct a new ID relative to the current page's location
+ *
+ * Uses a relative link only if the original was relative, too. This function is for
+ * pages and media files.
+ *
+ * @param string $relold the old, possibly relative ID
+ * @param string $new the new, full qualified ID
+ * @param string $type 'media' or 'page'
+ * @throws Exception on bad argument
+ * @return string
+ */
+ public function relativeLink($relold, $new, $type) {
+ global $conf;
+ if($type != 'media' && $type != 'page') throw new Exception('Not a valid type');
+
+ // first check if the old link still resolves
+ $exists = false;
+ $old = $relold;
+ if($type == 'page') {
+ resolve_pageid($this->ns, $old, $exists);
+ // Work around bug in DokuWiki 2020-07-29 where resolve_pageid doesn't append the start page to a link to
+ // the root.
+ if ($old === '') {
+ $old = $conf['start'];
+ }
+ } else {
+ resolve_mediaid($this->ns, $old, $exists);
+ }
+ if($old == $new) {
+ return $relold; // old link still resolves, keep as is
+ }
+
+ if($conf['useslash']) $relold = str_replace('/', ':', $relold);
+
+ // check if the link was relative
+ if(strpos($relold, ':') === false ||$relold[0] == '.') {
+ $wasrel = true;
+ } else {
+ $wasrel = false;
+ }
+
+ // if it wasn't relative then, leave it absolute now, too
+ if(!$wasrel) {
+ if($this->ns && !getNS($new)) $new = ':' . $new;
+ $new = $this->_nsStartCheck($relold, $new, $type);
+ return $new;
+ }
+
+ // split the paths and see how much common parts there are
+ $selfpath = explode(':', $this->ns);
+ $goalpath = explode(':', getNS($new));
+ $min = min(count($selfpath), count($goalpath));
+ for($common = 0; $common < $min; $common++) {
+ if($selfpath[$common] != $goalpath[$common]) break;
+ }
+
+ // we now have the non-common part and a number of uppers
+ $ups = max(count($selfpath) - $common, 0);
+ $remainder = array_slice($goalpath, $common);
+ $upper = $ups ? array_fill(0, $ups, '..:') : array();
+
+ // build the new relative path
+ $newrel = join(':', $upper);
+ if($remainder) $newrel .= join(':', $remainder) . ':';
+ $newrel .= noNS($new);
+ $newrel = str_replace('::', ':', trim($newrel, ':'));
+ if($newrel[0] != '.' && $this->ns && getNS($newrel)) $newrel = '.' . $newrel;
+
+ // if the old link ended with a colon and the new one is a start page, adjust
+ $newrel = $this->_nsStartCheck($relold,$newrel,$type);
+
+ // don't use relative paths if it is ridicoulus:
+ if(strlen($newrel) > strlen($new)) {
+ $newrel = $new;
+ if($this->ns && !getNS($new)) $newrel = ':' . $newrel;
+ $newrel = $this->_nsStartCheck($relold,$newrel,$type);
+ }
+
+ return $newrel;
+ }
+
+ /**
+ * Handle camelcase links
+ *
+ * @param string $match The text match
+ * @param string $state The starte of the parser
+ * @param int $pos The position in the input
+ * @return bool If parsing should be continued
+ */
+ public function camelcaselink($match, $state, $pos) {
+ $oldID = cleanID($this->origNS . ':' . $match);
+ $newID = $this->resolveMoves($oldID, 'page');
+ $newNS = getNS($newID);
+
+ if($oldID == $newID || $this->origNS == $newNS) {
+ // link is still valid as is
+ $this->calls .= $match;
+ } else {
+ if(noNS($oldID) == noNS($newID)) {
+ // only namespace changed, keep CamelCase in link
+ $this->calls .= "[[$newNS:$match]]";
+ } else {
+ // all new, keep CamelCase in title
+ $this->calls .= "[[$newID|$match]]";
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Handle rewriting of internal links
+ *
+ * @param string $match The text match
+ * @param string $state The starte of the parser
+ * @param int $pos The position in the input
+ * @return bool If parsing should be continued
+ */
+ public function internallink($match, $state, $pos) {
+ // Strip the opening and closing markup
+ $link = preg_replace(array('/^\[\[/', '/\]\]$/u'), '', $match);
+
+ // Split title from URL
+ $link = explode('|', $link, 2);
+ if(!isset($link[1])) {
+ $link[1] = null;
+ } else if(preg_match('/^\{\{[^\}]+\}\}$/', $link[1])) {
+ // If the title is an image, rewrite it
+ $old_title = $link[1];
+ $link[1] = $this->rewrite_media($link[1]);
+ // do a simple replace of the first match so really only the id is changed and not e.g. the alignment
+ $oldpos = strpos($match, $old_title);
+ $oldlen = strlen($old_title);
+ $match = substr_replace($match, $link[1], $oldpos, $oldlen);
+ }
+ $link[0] = trim($link[0]);
+
+ //decide which kind of link it is
+
+ if(preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u', $link[0])) {
+ // Interwiki
+ $this->calls .= $match;
+ } elseif(preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u', $link[0])) {
+ // Windows Share
+ $this->calls .= $match;
+ } elseif(preg_match('#^([a-z0-9\-\.+]+?)://#i', $link[0])) {
+ // external link (accepts all protocols)
+ $this->calls .= $match;
+ } elseif(preg_match('<' . PREG_PATTERN_VALID_EMAIL . '>', $link[0])) {
+ // E-Mail (pattern above is defined in inc/mail.php)
+ $this->calls .= $match;
+ } elseif(preg_match('!^#.+!', $link[0])) {
+ // local hash link
+ $this->calls .= $match;
+ } else {
+ $id = $link[0];
+
+ $hash = '';
+ $parts = explode('#', $id, 2);
+ if(count($parts) === 2) {
+ $id = $parts[0];
+ $hash = $parts[1];
+ }
+
+ $params = '';
+ $parts = explode('?', $id, 2);
+ if(count($parts) === 2) {
+ $id = $parts[0];
+ $params = $parts[1];
+ }
+
+ $new_id = $this->resolveMoves($id, 'page');
+ $new_id = $this->relativeLink($id, $new_id, 'page');
+
+ if($id == $new_id) {
+ $this->calls .= $match;
+ } else {
+ if($params !== '') {
+ $new_id .= '?' . $params;
+ }
+
+ if($hash !== '') {
+ $new_id .= '#' . $hash;
+ }
+
+ if($link[1] != null) {
+ $new_id .= '|' . $link[1];
+ }
+
+ $this->calls .= '[[' . $new_id . ']]';
+ }
+
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle rewriting of media links
+ *
+ * @param string $match The text match
+ * @param string $state The starte of the parser
+ * @param int $pos The position in the input
+ * @return bool If parsing should be continued
+ */
+ public function media($match, $state, $pos) {
+ $this->calls .= $this->rewrite_media($match);
+ return true;
+ }
+
+ /**
+ * Rewrite a media syntax
+ *
+ * @param string $match The text match of the media syntax
+ * @return string The rewritten syntax
+ */
+ protected function rewrite_media($match) {
+ $p = Doku_Handler_Parse_Media($match);
+ if($p['type'] == 'internalmedia') { // else: external media
+
+ $new_src = $this->resolveMoves($p['src'], 'media');
+ $new_src = $this->relativeLink($p['src'], $new_src, 'media');
+
+ if($new_src !== $p['src']) {
+ // do a simple replace of the first match so really only the id is changed and not e.g. the alignment
+ $srcpos = strpos($match, $p['src']);
+ $srclen = strlen($p['src']);
+ return substr_replace($match, $new_src, $srcpos, $srclen);
+ }
+ }
+ return $match;
+ }
+
+ /**
+ * Handle rewriting of plugin syntax, calls the registered handlers
+ *
+ * @param string $match The text match
+ * @param string $state The starte of the parser
+ * @param int $pos The position in the input
+ * @param string $pluginname The name of the plugin
+ * @return bool If parsing should be continued
+ */
+ public function plugin($match, $state, $pos, $pluginname) {
+ if(isset($this->handlers[$pluginname])) {
+ $this->calls .= call_user_func($this->handlers[$pluginname], $match, $state, $pos, $pluginname, $this);
+ } else {
+ $this->calls .= $match;
+ }
+ return true;
+ }
+
+ /**
+ * Catchall handler for the remaining syntax
+ *
+ * @param string $name Function name that was called
+ * @param array $params Original parameters
+ * @return bool If parsing should be continue
+ */
+ public function __call($name, $params) {
+ if(count($params) == 3) {
+ $this->calls .= $params[0];
+ return true;
+ } else {
+ trigger_error('Error, handler function ' . hsc($name) . ' with ' . count($params) . ' parameters called which isn\'t implemented', E_USER_ERROR);
+ return false;
+ }
+ }
+
+ public function _finalize() {
+ // remove padding that is added by the parser in parse()
+ $this->calls = substr($this->calls, 1, -1);
+ }
+
+}
diff --git a/helper/op.php b/helper/op.php
new file mode 100644
index 0000000..48c4175
--- /dev/null
+++ b/helper/op.php
@@ -0,0 +1,323 @@
+
+ * @author Gary Owen
+ * @author Andreas Gohr
+ */
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+class helper_plugin_move_op extends DokuWiki_Plugin {
+
+ /**
+ * @var string symbol to make move operations easily recognizable in change log
+ */
+ public $symbol = '↷';
+
+ /**
+ * @var array stores the affected pages of the last operation
+ */
+ protected $affectedPages = array();
+
+ /**
+ * Check if the given page can be moved to the given destination
+ *
+ * @param $src
+ * @param $dst
+ * @return bool
+ */
+ public function checkPage($src, $dst) {
+ // Check we have rights to move this document
+ if(!page_exists($src)) {
+ msg(sprintf($this->getLang('notexist'), $src), -1);
+ return false;
+ }
+ if(auth_quickaclcheck($src) < AUTH_EDIT) {
+ msg(sprintf($this->getLang('norights'), $src), -1);
+ return false;
+ }
+
+ // Check file is not locked
+ // checklock checks if the page lock hasn't expired and the page hasn't been locked by another user
+ // the file exists check checks if the page is reported unlocked if a lock exists which means that
+ // the page is locked by the current user
+ if(checklock($src) !== false || @file_exists(wikiLockFN($src))) {
+ msg(sprintf($this->getLang('filelocked'), $src), -1);
+ return false;
+ }
+
+ // Has the document name and/or namespace changed?
+ if($src == $dst) {
+ msg(sprintf($this->getLang('notchanged'), $src), -1);
+ return false;
+ }
+
+ // Check the page does not already exist
+ if(page_exists($dst)) {
+ msg(sprintf($this->getLang('exists'), $src, $dst), -1);
+ return false;
+ }
+
+ // Check if the current user can create the new page
+ if(auth_quickaclcheck($dst) < AUTH_CREATE) {
+ msg(sprintf($this->getLang('notargetperms'), $dst), -1);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the given media file can be moved to the given destination
+ *
+ * @param $src
+ * @param $dst
+ * @return bool
+ */
+ public function checkMedia($src, $dst) {
+ // Check we have rights to move this document
+ if(!file_exists(mediaFN($src))) {
+ msg(sprintf($this->getLang('medianotexist'), $src), -1);
+ return false;
+ }
+ if(auth_quickaclcheck($src) < AUTH_DELETE) {
+ msg(sprintf($this->getLang('nomediarights'), $src), -1);
+ return false;
+ }
+
+ // Has the document name and/or namespace changed?
+ if($src == $dst) {
+ msg(sprintf($this->getLang('medianotchanged'), $src), -1);
+ return false;
+ }
+
+ // Check the page does not already exist
+ if(@file_exists(mediaFN($dst))) {
+ msg(sprintf($this->getLang('mediaexists'), $src, $dst), -1);
+ return false;
+ }
+
+ // Check if the current user can create the new page
+ if(auth_quickaclcheck($dst) < AUTH_UPLOAD) {
+ msg(sprintf($this->getLang('nomediatargetperms'), $dst), -1);
+ return false;
+ }
+
+ // check if the file extension is unchanged
+ if (pathinfo(mediaFN($src), PATHINFO_EXTENSION) !== pathinfo(mediaFN($dst), PATHINFO_EXTENSION)) {
+ msg($this->getLang('extensionchange'), -1);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Execute a page move/rename
+ *
+ * @param string $src original ID
+ * @param string $dst new ID
+ * @return bool
+ */
+ public function movePage($src, $dst) {
+ if(!$this->checkPage($src, $dst)) return false;
+
+ // lock rewrites
+ helper_plugin_move_rewrite::addLock();
+
+ /** @var helper_plugin_move_rewrite $Rewriter */
+ $Rewriter = plugin_load('helper', 'move_rewrite');
+
+ // remember what this page was called before the move in meta data
+ $Rewriter->setSelfMoveMeta($src);
+
+ // ft_backlinks() is not used here, as it does a hidden page and acl check but we really need all pages
+ $affected_pages = idx_get_indexer()->lookupKey('relation_references', $src);
+ $affected_pages[] = $dst; // the current page is always affected, because all relative links may have changed
+ $affected_pages = array_unique($affected_pages);
+
+ $src_ns = getNS($src);
+ $src_name = noNS($src);
+ $dst_ns = getNS($dst);
+ $dst_name = noNS($dst);
+
+ // pass this info on to other plugins
+ $eventdata = array(
+ // this is for compatibility to old plugin
+ 'opts' => array(
+ 'ns' => $src_ns,
+ 'name' => $src_name,
+ 'newns' => $dst_ns,
+ 'newname' => $dst_name,
+ ),
+ 'affected_pages' => &$affected_pages,
+ 'src_id' => $src,
+ 'dst_id' => $dst,
+ );
+
+ // give plugins the option to add their own meta files to the list of files that need to be moved
+ // to the oldfiles/newfiles array or to adjust their own metadata, database, ...
+ // and to add other pages to the affected pages
+ $event = new Doku_Event('PLUGIN_MOVE_PAGE_RENAME', $eventdata);
+ if($event->advise_before()) {
+ lock($src);
+
+ /** @var helper_plugin_move_file $FileMover */
+ $FileMover = plugin_load('helper', 'move_file');
+
+ // Move the Subscriptions & Indexes (new feature since Spring 2013 release)
+ $Indexer = idx_get_indexer();
+ if(($idx_msg = $Indexer->renamePage($src, $dst)) !== true
+ || ($idx_msg = $Indexer->renameMetaValue('relation_references', $src, $dst)) !== true
+ ) {
+ msg(sprintf($this->getLang('indexerror'), $idx_msg), -1);
+ return false;
+ }
+ if(!$FileMover->movePageMeta($src_ns, $src_name, $dst_ns, $dst_name)) {
+ msg(sprintf($this->getLang('metamoveerror'), $src), -1);
+ return false;
+ }
+
+ // prepare the summary for the changelog entry
+ if($src_ns == $dst_ns) {
+ $lang_key = 'renamed';
+ } elseif($src_name == $dst_name) {
+ $lang_key = 'moved';
+ } else {
+ $lang_key = 'move_rename';
+ }
+ $summary = $this->symbol . ' ' . sprintf($this->getLang($lang_key), $src, $dst);
+
+ // Wait a second when the page has just been rewritten
+ $oldRev = filemtime(wikiFN($src));
+ if($oldRev == time()) sleep(1);
+
+ // Save the updated document in its new location
+ $text = rawWiki($src);
+ saveWikiText($dst, $text, $summary);
+
+ // Delete the orginal file
+ if(@file_exists(wikiFN($dst))) {
+ saveWikiText($src, '', $summary);
+ }
+
+ // Move the old revisions
+ if(!$FileMover->movePageAttic($src_ns, $src_name, $dst_ns, $dst_name)) {
+ // it's too late to stop the move, so just display a message.
+ msg(sprintf($this->getLang('atticmoveerror'), $src ), -1);
+ }
+
+ // Add meta data to all affected pages, so links get updated later
+ foreach($affected_pages as $id) {
+ $Rewriter->setMoveMeta($id, $src, $dst, 'pages');
+ }
+
+ unlock($src);
+ }
+ $event->advise_after();
+
+ // store this for later use
+ $this->affectedPages = $affected_pages;
+
+ // unlock rewrites
+ helper_plugin_move_rewrite::removeLock();
+
+ return true;
+ }
+
+ /**
+ * Execute a media file move/rename
+ *
+ * @param string $src original ID
+ * @param string $dst new ID
+ * @return bool true if the move was successfully executed
+ */
+ public function moveMedia($src, $dst) {
+ if(!$this->checkMedia($src, $dst)) return false;
+
+ // get all pages using this media
+ $affected_pages = idx_get_indexer()->lookupKey('relation_media', $src);
+
+ $src_ns = getNS($src);
+ $src_name = noNS($src);
+ $dst_ns = getNS($dst);
+ $dst_name = noNS($dst);
+
+ // pass this info on to other plugins
+ $eventdata = array(
+ // this is for compatibility to old plugin
+ 'opts' => array(
+ 'ns' => $src_ns,
+ 'name' => $src_name,
+ 'newns' => $dst_ns,
+ 'newname' => $dst_name,
+ ),
+ 'affected_pages' => &$affected_pages,
+ 'src_id' => $src,
+ 'dst_id' => $dst,
+ );
+
+ // give plugins the option to add their own meta files to the list of files that need to be moved
+ // to the oldfiles/newfiles array or to adjust their own metadata, database, ...
+ // and to add other pages to the affected pages
+ $event = new Doku_Event('PLUGIN_MOVE_MEDIA_RENAME', $eventdata);
+ if($event->advise_before()) {
+ /** @var helper_plugin_move_file $FileMover */
+ $FileMover = plugin_load('helper', 'move_file');
+ /** @var helper_plugin_move_rewrite $Rewriter */
+ $Rewriter = plugin_load('helper', 'move_rewrite');
+
+ // Move the Subscriptions & Indexes (new feature since Spring 2013 release)
+ $Indexer = idx_get_indexer();
+ if(($idx_msg = $Indexer->renameMetaValue('relation_media', $src, $dst)) !== true) {
+ msg(sprintf($this->getLang('indexerror'), $idx_msg), -1);
+ return false;
+ }
+ if(!$FileMover->moveMediaMeta($src_ns, $src_name, $dst_ns, $dst_name)) {
+ msg(sprintf($this->getLang('mediametamoveerror'), $src), -1);
+ return false;
+ }
+
+ // prepare directory
+ io_createNamespace($dst, 'media');
+
+ // move it FIXME this does not create a changelog entry!
+ if(!io_rename(mediaFN($src), mediaFN($dst))) {
+ msg(sprintf($this->getLang('mediamoveerror'), $src), -1);
+ return false;
+ }
+
+ // clean up old ns
+ io_sweepNS($src, 'mediadir');
+
+ // Move the old revisions
+ if(!$FileMover->moveMediaAttic($src_ns, $src_name, $dst_ns, $dst_name)) {
+ // it's too late to stop the move, so just display a message.
+ msg(sprintf($this->getLang('mediaatticmoveerror'), $src), -1);
+ }
+
+ // Add meta data to all affected pages, so links get updated later
+ foreach($affected_pages as $id) {
+ $Rewriter->setMoveMeta($id, $src, $dst, 'media');
+ }
+ }
+ $event->advise_after();
+
+ // store this for later use
+ $this->affectedPages = $affected_pages;
+
+ return true;
+ }
+
+ /**
+ * Get a list of pages that where affected by the last successful move operation
+ *
+ * @return array
+ */
+ public function getAffectedPages() {
+ return $this->affectedPages;
+ }
+}
diff --git a/helper/plan.php b/helper/plan.php
new file mode 100644
index 0000000..6d632d3
--- /dev/null
+++ b/helper/plan.php
@@ -0,0 +1,953 @@
+
+ * @author Andreas Gohr
+ */
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+/**
+ * Class helper_plugin_move_plan
+ *
+ * This thing prepares and keeps progress info on complex move operations (eg. where more than a single
+ * object is affected).
+ *
+ * Please note: this has not a complex move resolver. Move operations may not depend on each other (eg. you
+ * can not use a namespace as source that will only be created by a different move operation) instead all given
+ * operations should be operations on the current state to come to a wanted future state. The tree manager takes
+ * care of that by abstracting all moves on a DOM representation first, then submitting the needed changes (eg.
+ * differences between now and wanted).
+ *
+ * Glossary:
+ *
+ * document - refers to either a page or a media file here
+ */
+class helper_plugin_move_plan extends DokuWiki_Plugin {
+ /** Number of operations per step */
+ const OPS_PER_RUN = 10;
+
+ const TYPE_PAGES = 1;
+ const TYPE_MEDIA = 2;
+ const CLASS_NS = 4;
+ const CLASS_DOC = 8;
+
+ /**
+ * @var array the options for this move plan
+ */
+ protected $options = array(); // defaults are set in loadOptions()
+
+ /**
+ * @var array holds the location of the different list and state files
+ */
+ protected $files = array();
+
+ /**
+ * @var array the planned moves
+ */
+ protected $plan = array();
+
+ /**
+ * @var array temporary holder of document lists
+ */
+ protected $tmpstore = array(
+ 'pages' => array(),
+ 'media' => array(),
+ 'ns' => array(),
+ 'affpg' => array(),
+ 'miss' => array(),
+ 'miss_media' => array(),
+ );
+
+ /** @var helper_plugin_move_op $MoveOperator */
+ protected $MoveOperator = null;
+
+ /**
+ * Constructor
+ *
+ * initializes state (if any) for continuiation of a running move op
+ */
+ public function __construct() {
+ global $conf;
+
+ // set the file locations
+ $this->files = array(
+ 'opts' => $conf['metadir'] . '/__move_opts',
+ 'pagelist' => $conf['metadir'] . '/__move_pagelist',
+ 'medialist' => $conf['metadir'] . '/__move_medialist',
+ 'affected' => $conf['metadir'] . '/__move_affected',
+ 'namespaces' => $conf['metadir'] . '/__move_namespaces',
+ 'missing' => $conf['metadir'] . '/__move_missing',
+ 'missing_media' => $conf['metadir'] . '/__move_missing_media',
+ );
+
+ $this->MoveOperator = plugin_load('helper', 'move_op');
+
+ $this->loadOptions();
+ }
+
+ /**
+ * Load the current options if any
+ *
+ * If no options are found, the default options will be extended by any available
+ * config options
+ */
+ protected function loadOptions() {
+ // (re)set defaults
+ $this->options = array(
+ // status
+ 'committed' => false,
+ 'started' => 0,
+
+ // counters
+ 'pages_all' => 0,
+ 'pages_run' => 0,
+ 'media_all' => 0,
+ 'media_run' => 0,
+ 'affpg_all' => 0,
+ 'affpg_run' => 0,
+
+ // options
+ 'autoskip' => $this->getConf('autoskip'),
+ 'autorewrite' => $this->getConf('autorewrite'),
+
+ // errors
+ 'lasterror' => false
+ );
+
+ // merge whatever options are saved currently
+ $file = $this->files['opts'];
+ if(file_exists($file)) {
+ $options = unserialize(io_readFile($file, false));
+ $this->options = array_merge($this->options, $options);
+ }
+ }
+
+ /**
+ * Save the current options
+ *
+ * @return bool
+ */
+ protected function saveOptions() {
+ return io_saveFile($this->files['opts'], serialize($this->options));
+ }
+
+ /**
+ * Return the current state of an option, null for unknown options
+ *
+ * @param $name
+ * @return mixed|null
+ */
+ public function getOption($name) {
+ if(isset($this->options[$name])) {
+ return $this->options[$name];
+ }
+ return null;
+ }
+
+ /**
+ * Set an option
+ *
+ * Note, this otpion will only be set to the current instance of this helper object. It will only
+ * be written to the option file once the plan gets committed
+ *
+ * @param $name
+ * @param $value
+ */
+ public function setOption($name, $value) {
+ $this->options[$name] = $value;
+ }
+
+ /**
+ * Returns the progress of this plan in percent
+ *
+ * @return float
+ */
+ public function getProgress() {
+ $max =
+ $this->options['pages_all'] +
+ $this->options['media_all'];
+
+ $remain =
+ $this->options['pages_run'] +
+ $this->options['media_run'];
+
+ if($this->options['autorewrite']) {
+ $max += $this->options['affpg_all'];
+ $remain += $this->options['affpg_run'];
+ }
+
+ if($max == 0) return 0;
+ return round((($max - $remain) * 100) / $max, 2);
+ }
+
+ /**
+ * Check if there is a move in progress currently
+ *
+ * @return bool
+ */
+ public function inProgress() {
+ return (bool) $this->options['started'];
+ }
+
+ /**
+ * Check if this plan has been committed, yet
+ *
+ * @return bool
+ */
+ public function isCommited() {
+ return $this->options['committed'];
+ }
+
+ /**
+ * Add a single page to be moved to the plan
+ *
+ * @param string $src
+ * @param string $dst
+ */
+ public function addPageMove($src, $dst) {
+ $this->addMove($src, $dst, self::CLASS_DOC, self::TYPE_PAGES);
+ }
+
+ /**
+ * Add a single media file to be moved to the plan
+ *
+ * @param string $src
+ * @param string $dst
+ */
+ public function addMediaMove($src, $dst) {
+ $this->addMove($src, $dst, self::CLASS_DOC, self::TYPE_MEDIA);
+ }
+
+ /**
+ * Add a page namespace to be moved to the plan
+ *
+ * @param string $src
+ * @param string $dst
+ */
+ public function addPageNamespaceMove($src, $dst) {
+ $this->addMove($src, $dst, self::CLASS_NS, self::TYPE_PAGES);
+ }
+
+ /**
+ * Add a media namespace to be moved to the plan
+ *
+ * @param string $src
+ * @param string $dst
+ */
+ public function addMediaNamespaceMove($src, $dst) {
+ $this->addMove($src, $dst, self::CLASS_NS, self::TYPE_MEDIA);
+ }
+
+ /**
+ * Plans the move of a namespace or document
+ *
+ * @param string $src ID of the item to move
+ * @param string $dst new ID of item namespace
+ * @param int $class (self::CLASS_NS|self::CLASS_DOC)
+ * @param int $type (PLUGIN_MOVE_TYPE_PAGE|self::TYPE_MEDIA)
+ * @throws Exception
+ */
+ protected function addMove($src, $dst, $class = self::CLASS_NS, $type = self::TYPE_PAGES) {
+ if($this->options['committed']) throw new Exception('plan is committed already, can not be added to');
+
+ $src = cleanID($src);
+ $dst = cleanID($dst);
+
+ $this->plan[] = array(
+ 'src' => $src,
+ 'dst' => $dst,
+ 'class' => $class,
+ 'type' => $type
+ );
+ }
+
+ /**
+ * Abort any move or plan in progress and reset the helper
+ */
+ public function abort() {
+ foreach($this->files as $file) {
+ @unlink($file);
+ }
+ $this->plan = array();
+ $this->loadOptions();
+ helper_plugin_move_rewrite::removeAllLocks();
+ }
+
+ /**
+ * This locks up the plan and prepares execution
+ *
+ * the plan is reordered an the needed move operations are gathered and stored in the appropriate
+ * list files
+ *
+ * @throws Exception if you try to commit a plan twice
+ * @return bool true if the plan was committed
+ */
+ public function commit() {
+ global $conf;
+
+ if($this->options['committed']) throw new Exception('plan is committed already, can not be committed again');
+
+ helper_plugin_move_rewrite::addLock();
+
+
+ usort($this->plan, array($this, 'planSorter'));
+
+ // get all the documents to be moved and store them in their lists
+ foreach($this->plan as $move) {
+ if($move['class'] == self::CLASS_DOC) {
+ // these can just be added
+ $this->addToDocumentList($move['src'], $move['dst'], $move['type']);
+ } else {
+ // here we need a list of content first, search for it
+ $docs = array();
+ $path = utf8_encodeFN(str_replace(':', '/', $move['src']));
+ $opts = array('depth' => 0, 'skipacl' => true);
+ if($move['type'] == self::TYPE_PAGES) {
+ search($docs, $conf['datadir'], 'search_allpages', $opts, $path);
+ } else {
+ search($docs, $conf['mediadir'], 'search_media', $opts, $path);
+ }
+
+ // how much namespace to strip?
+ if($move['src'] !== '') {
+ $strip = strlen($move['src']) + 1;
+ } else {
+ $strip = 0;
+ }
+ if($move['dst']) $move['dst'] .= ':';
+
+ // now add all the found documents to our lists
+ foreach($docs as $doc) {
+ $from = $doc['id'];
+ $to = $move['dst'] . substr($doc['id'], $strip);
+ $this->addToDocumentList($from, $to, $move['type']);
+ }
+
+ // remember the namespace move itself
+ if($move['type'] == self::TYPE_PAGES) {
+ // FIXME we use this to move namespace subscriptions later on and for now only do it on
+ // page namespace moves, but subscriptions work for both, but what when only one of
+ // them is moved? Should it be copied then? Complicated. This is good enough for now
+ $this->addToDocumentList($move['src'], $move['dst'], self::CLASS_NS);
+ }
+ $this->findMissingDocuments($move['src'] . ':', $move['dst'],$move['type']);
+ }
+ // store what pages are affected by this move
+ $this->findAffectedPages($move['src'], $move['dst'], $move['class'], $move['type']);
+ }
+
+ $this->storeDocumentLists();
+
+ if(!$this->options['pages_all'] && !$this->options['media_all']) {
+ msg($this->getLang('noaction'), -1);
+ return false;
+ }
+
+ $this->options['committed'] = true;
+ $this->saveOptions();
+
+ return true;
+ }
+
+ /**
+ * Execute the next steps
+ *
+ * @param bool $skip set to true to skip the next first step (skip error)
+ * @return bool|int false on errors, otherwise the number of remaining steps
+ * @throws Exception
+ */
+ public function nextStep($skip = false) {
+ if(!$this->options['committed']) throw new Exception('plan is not committed yet!');
+
+ // execution has started
+ if(!$this->options['started']) $this->options['started'] = time();
+
+ helper_plugin_move_rewrite::addLock();
+
+ if(@filesize($this->files['pagelist']) > 1) {
+ $todo = $this->stepThroughDocuments(self::TYPE_PAGES, $skip);
+ if($todo === false) return $this->storeError();
+ return max($todo, 1); // force one more call
+ }
+
+ if(@filesize($this->files['medialist']) > 1) {
+ $todo = $this->stepThroughDocuments(self::TYPE_MEDIA, $skip);
+ if($todo === false) return $this->storeError();
+ return max($todo, 1); // force one more call
+ }
+
+ if(@filesize($this->files['missing']) > 1 && @filesize($this->files['affected']) > 1) {
+ $todo = $this->stepThroughMissingDocuments(self::TYPE_PAGES);
+ if($todo === false) return $this->storeError();
+ return max($todo, 1); // force one more call
+ }
+
+ if(@filesize($this->files['missing_media']) > 1 && @filesize($this->files['affected']) > 1) {
+ $todo = $this->stepThroughMissingDocuments(self::TYPE_MEDIA);
+ if($todo === false)return $this->storeError();
+ return max($todo, 1); // force one more call
+ }
+
+ if(@filesize($this->files['namespaces']) > 1) {
+ $todo = $this->stepThroughNamespaces();
+ if($todo === false) return $this->storeError();
+ return max($todo, 1); // force one more call
+ }
+
+ helper_plugin_move_rewrite::removeAllLocks();
+
+ if($this->options['autorewrite'] && @filesize($this->files['affected']) > 1) {
+ $todo = $this->stepThroughAffectedPages();
+ if($todo === false) return $this->storeError();
+ return max($todo, 1); // force one more call
+ }
+
+ // we're done here, clean up
+ $this->abort();
+ return 0;
+ }
+
+ /**
+ * Returns the list of page and media moves and the affected pages as a HTML list
+ *
+ * @return string
+ */
+ public function previewHTML() {
+ $html = '';
+
+ $html .= '
';
+
+ return $html;
+ }
+
+ /**
+ * Step through the next bunch of pages or media files
+ *
+ * @param int $type (self::TYPE_PAGES|self::TYPE_MEDIA)
+ * @param bool $skip should the first item be skipped?
+ * @return bool|int false on error, otherwise the number of remaining documents
+ */
+ protected function stepThroughDocuments($type = self::TYPE_PAGES, $skip = false) {
+
+ if($type == self::TYPE_PAGES) {
+ $file = $this->files['pagelist'];
+ $mark = 'P';
+ $call = 'movePage';
+ $items_run_counter = 'pages_run';
+ } else {
+ $file = $this->files['medialist'];
+ $mark = 'M';
+ $call = 'moveMedia';
+ $items_run_counter = 'media_run';
+ }
+
+ $doclist = fopen($file, 'a+');
+
+ for($i = 0; $i < helper_plugin_move_plan::OPS_PER_RUN; $i++) {
+ $log = "";
+ $line = $this->getLastLine($doclist);
+ if($line === false) {
+ break;
+ }
+ list($src, $dst) = explode("\t", trim($line));
+
+ // should this item be skipped?
+ if($skip === true) {
+ $skip = false;
+ } else {
+ // move the page
+ if(!$this->MoveOperator->$call($src, $dst)) {
+ $log .= $this->build_log_line($mark, $src, $dst, false); // FAILURE!
+
+ // automatically skip this item only if wanted...
+ if(!$this->options['autoskip']) {
+ // ...otherwise abort the operation
+ fclose($doclist);
+ $return_items_run = false;
+ break;
+ }
+ } else {
+ $log .= $this->build_log_line($mark, $src, $dst, true); // SUCCESS!
+ }
+ }
+
+ /*
+ * This adjusts counters and truncates the document list correctly
+ * It is used to finalize a successful or skipped move
+ */
+
+ ftruncate($doclist, ftell($doclist));
+ $this->options[$items_run_counter]--;
+ $return_items_run = $this->options[$items_run_counter];
+ $this->write_log($log);
+ $this->saveOptions();
+ }
+
+ if ($return_items_run !== false) {
+ fclose($doclist);
+ }
+ return $return_items_run;
+ }
+
+ /**
+ * Step through the next bunch of pages that need link corrections
+ *
+ * @return bool|int false on error, otherwise the number of remaining documents
+ */
+ protected function stepThroughAffectedPages() {
+ /** @var helper_plugin_move_rewrite $Rewriter */
+ $Rewriter = plugin_load('helper', 'move_rewrite');
+
+ // handle affected pages
+ $doclist = fopen($this->files['affected'], 'a+');
+ for($i = 0; $i < helper_plugin_move_plan::OPS_PER_RUN; $i++) {
+ $page = $this->getLastLine($doclist);
+ if($page === false) break;
+
+ // rewrite it
+ $Rewriter->rewritePage($page);
+
+ // update the list file
+ ftruncate($doclist, ftell($doclist));
+ $this->options['affpg_run']--;
+ $this->saveOptions();
+ }
+
+ fclose($doclist);
+ return $this->options['affpg_run'];
+ }
+
+ /**
+ * Step through all the links to missing pages that should be moved
+ *
+ * This simply adds the moved missing pages to all affected pages meta data. This will add
+ * the meta data to pages not linking to the affected pages but this should still be faster
+ * than figuring out which pages need this info.
+ *
+ * This does not step currently, but handles all pages in one step.
+ *
+ * @param int $type
+ *
+ * @return int always 0
+ * @throws Exception
+ */
+ protected function stepThroughMissingDocuments($type = self::TYPE_PAGES) {
+ if($type != self::TYPE_PAGES && $type != self::TYPE_MEDIA) {
+ throw new Exception('wrong type specified');
+ }
+ /** @var helper_plugin_move_rewrite $Rewriter */
+ $Rewriter = plugin_load('helper', 'move_rewrite');
+
+ $miss = array();
+ if ($type == self::TYPE_PAGES) {
+ $missing_fn = $this->files['missing'];
+ } else {
+ $missing_fn = $this->files['missing_media'];
+ }
+ $missing = file($missing_fn);
+ foreach($missing as $line) {
+ $line = trim($line);
+ if($line == '') continue;
+ list($src, $dst) = explode("\t", $line);
+ $miss[$src] = $dst;
+ }
+
+ $affected = file($this->files['affected']);
+ foreach($affected as $page){
+ $page = trim($page);
+
+ if ($type == self::TYPE_PAGES) {
+ $Rewriter->setMoveMetas($page, $miss, 'pages');
+ } else {
+ $Rewriter->setMoveMetas($page, $miss, 'media');
+ }
+ }
+
+ unlink($missing_fn);
+ return 0;
+ }
+
+ /**
+ * Step through all the namespace moves
+ *
+ * This does not step currently, but handles all namespaces in one step.
+ *
+ * Currently moves namespace subscriptions only.
+ *
+ * @return int always 0
+ * @todo maybe add an event so plugins can move more stuff?
+ * @todo fixed that $src and $dst are seperated by tab, not newline. This method has no tests?
+ */
+ protected function stepThroughNamespaces() {
+ /** @var helper_plugin_move_file $FileMover */
+ $FileMover = plugin_load('helper', 'move_file');
+
+ $lines = io_readFile($this->files['namespaces']);
+ $lines = explode("\n", $lines);
+
+ foreach($lines as $line) {
+ // There is an empty line at the end of the list.
+ if ($line === '') continue;
+
+ list($src, $dst) = explode("\t", trim($line));
+ $FileMover->moveNamespaceSubscription($src, $dst);
+ }
+
+ @unlink($this->files['namespaces']);
+ return 0;
+ }
+
+ /**
+ * Retrieve the last error from the MSG array and store it in the options
+ *
+ * @todo rebuild error handling based on exceptions
+ *
+ * @return bool always false
+ */
+ protected function storeError() {
+ global $MSG;
+
+ if(is_array($MSG) && count($MSG)) {
+ $last = array_shift($MSG);
+ $this->options['lasterror'] = $last['msg'];
+ unset($GLOBALS['MSG']);
+ } else {
+ $this->options['lasterror'] = 'Unknown error';
+ }
+ $this->saveOptions();
+
+ return false;
+ }
+
+ /**
+ * Reset the error state
+ */
+ protected function clearError() {
+ $this->options['lasterror'] = false;
+ $this->saveOptions();
+ }
+
+ /**
+ * Get the last error message or false if no error occured
+ *
+ * @return bool|string
+ */
+ public function getLastError() {
+ return $this->options['lasterror'];
+ }
+
+ /**
+ * Appends a page move operation in the list file
+ *
+ * If the src has been added before, this is ignored. This makes sure you can move a single page
+ * out of a namespace first, then move the namespace somewhere else.
+ *
+ * @param string $src
+ * @param string $dst
+ * @param int $type
+ * @throws Exception
+ */
+ protected function addToDocumentList($src, $dst, $type = self::TYPE_PAGES) {
+ if($type == self::TYPE_PAGES) {
+ $store = 'pages';
+ } else if($type == self::TYPE_MEDIA) {
+ $store = 'media';
+ } else if($type == self::CLASS_NS) {
+ $store = 'ns';
+ } else {
+ throw new Exception('Unknown type ' . $type);
+ }
+
+ if(!isset($this->tmpstore[$store][$src])) {
+ $this->tmpstore[$store][$src] = $dst;
+ }
+ }
+
+ /**
+ * Add the list of pages to the list of affected pages whose links need adjustment
+ *
+ * @param string|array $pages
+ */
+ protected function addToAffectedPagesList($pages) {
+ if(!is_array($pages)) $pages = array($pages);
+
+ foreach($pages as $page) {
+ if(!isset($this->tmpstore['affpg'][$page])) {
+ $this->tmpstore['affpg'][$page] = true;
+ }
+ }
+ }
+
+ /**
+ * Looks up pages that will be affected by a move of $src
+ *
+ * Calls addToAffectedPagesList() directly to store the result
+ *
+ * @param string $src source namespace
+ * @param string $dst destination namespace
+ * @param int $class
+ * @param int $type
+ */
+ protected function findAffectedPages($src, $dst, $class, $type) {
+ $idx = idx_get_indexer();
+
+ if($class == self::CLASS_NS) {
+ $src_ = "$src:*"; // use wildcard lookup for namespaces
+ } else {
+ $src_ = $src;
+ }
+
+ $pages = array();
+ if($type == self::TYPE_PAGES) {
+ $pages = $idx->lookupKey('relation_references', $src_);
+ $len = strlen($src);
+ foreach($pages as &$page) {
+ if (substr($page, 0, $len + 1) === "$src:") {
+ $page = $dst . substr($page, $len + 1);
+ }
+ }
+ unset($page);
+ } else if($type == self::TYPE_MEDIA) {
+ $pages = $idx->lookupKey('relation_media', $src_);
+ }
+
+ $this->addToAffectedPagesList($pages);
+ }
+
+ /**
+ * Find missing pages in the $src namespace
+ *
+ * @param string $src source namespace
+ * @param string $dst destination namespace
+ * @param int $type either self::TYPE_PAGES or self::TYPE_MEDIA
+ */
+ protected function findMissingDocuments($src, $dst, $type = self::TYPE_PAGES) {
+ global $conf;
+
+ // FIXME this duplicates Doku_Indexer::getIndex()
+ if ($type == self::TYPE_PAGES) {
+ $fn = $conf['indexdir'] . '/relation_references_w.idx';
+ } else {
+ $fn = $conf['indexdir'] . '/relation_media_w.idx';
+ }
+ if (!@file_exists($fn)){
+ $referenceidx = array();
+ } else {
+ $referenceidx = file($fn, FILE_IGNORE_NEW_LINES);
+ }
+
+ $len = strlen($src);
+ foreach($referenceidx as $idx => $page) {
+ if(substr($page, 0, $len) != "$src") continue;
+
+ // remember missing pages
+ if ($type == self::TYPE_PAGES) {
+ if(!page_exists($page)) {
+ $newpage = $dst . substr($page, $len);
+ $this->tmpstore['miss'][$page] = $newpage;
+ }
+ } else {
+ if(!file_exists(mediaFN($page))){
+ $newpage = $dst . substr($page, $len);
+ $this->tmpstore['miss_media'][$page] = $newpage;
+ }
+ }
+ }
+ }
+
+ /**
+ * Store the aggregated document lists in the file system and reset the internal storage
+ *
+ * @throws Exception
+ */
+ protected function storeDocumentLists() {
+ $lists = array(
+ 'pages' => $this->files['pagelist'],
+ 'media' => $this->files['medialist'],
+ 'ns' => $this->files['namespaces'],
+ 'affpg' => $this->files['affected'],
+ 'miss' => $this->files['missing'],
+ 'miss_media' => $this->files['missing_media'],
+ );
+
+ foreach($lists as $store => $file) {
+ // anything to do?
+ $count = count($this->tmpstore[$store]);
+ if(!$count) continue;
+
+ // prepare and save content
+ $data = '';
+ $this->tmpstore[$store] = array_reverse($this->tmpstore[$store]); // store in reverse order
+ foreach($this->tmpstore[$store] as $src => $dst) {
+ if($dst === true) {
+ $data .= "$src\n"; // for affected pages only one ID is saved
+ } else {
+ $data .= "$src\t$dst\n";
+ }
+
+ }
+ io_saveFile($file, $data);
+
+ // set counters
+ if($store != 'ns') {
+ $this->options[$store . '_all'] = $count;
+ $this->options[$store . '_run'] = $count;
+ }
+
+ // reset the list
+ $this->tmpstore[$store] = array();
+ }
+ }
+
+ /**
+ * Get the last line from the list that is stored in the file that is referenced by the handle
+ * The handle is set to the newline before the file id
+ *
+ * @param resource $handle The file handle to read from
+ * @return string|bool the last id from the list or false if there is none
+ */
+ protected function getLastLine($handle) {
+ // begin the seek at the end of the file
+ fseek($handle, 0, SEEK_END);
+ $line = '';
+
+ // seek one backwards as long as it's possible
+ while(fseek($handle, -1, SEEK_CUR) >= 0) {
+ $c = fgetc($handle);
+ if($c === false) return false; // EOF, i.e. the file is empty
+ fseek($handle, -1, SEEK_CUR); // reset the position to the character that was read
+
+ if($c == "\n") {
+ if($line === '') {
+ continue; // this line was empty, continue
+ } else {
+ break; // we have a line, finish
+ }
+ }
+
+ $line = $c . $line; // prepend char to line
+ }
+
+ if($line === '') return false; // beginning of file reached and no content
+
+ return $line;
+ }
+
+ /**
+ * Callback for usort to sort the move plan
+ *
+ * @param $a
+ * @param $b
+ * @return int
+ */
+ public function planSorter($a, $b) {
+ // do page moves before namespace moves
+ if($a['class'] == self::CLASS_DOC && $b['class'] == self::CLASS_NS) {
+ return -1;
+ }
+ if($a['class'] == self::CLASS_NS && $b['class'] == self::CLASS_DOC) {
+ return 1;
+ }
+
+ // do pages before media
+ if($a['type'] == self::TYPE_PAGES && $b['type'] == self::TYPE_MEDIA) {
+ return -1;
+ }
+ if($a['type'] == self::TYPE_MEDIA && $b['type'] == self::TYPE_PAGES) {
+ return 1;
+ }
+
+ // from here on we compare only apples to apples
+ // we sort by depth of namespace, deepest namespaces first
+
+ $alen = substr_count($a['src'], ':');
+ $blen = substr_count($b['src'], ':');
+
+ if($alen > $blen) {
+ return -1;
+ } elseif($alen < $blen) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Create line to log result of an operation
+ *
+ * @param string $type
+ * @param string $from
+ * @param string $to
+ * @param bool $success
+ *
+ * @return string
+ *
+ * @author Andreas Gohr
+ * @author Michael Große
+ */
+ public function build_log_line ($type, $from, $to, $success) {
+ global $MSG;
+
+ $now = time();
+ $date = date('Y-m-d H:i:s', $now); // for human readability
+ if($success) {
+ $ok = 'success';
+ $msg = '';
+ } else {
+ $ok = 'failed';
+ $msg = $MSG[count($MSG) - 1]['msg']; // get detail from message array
+ }
+
+ $log = "$now\t$date\t$type\t$from\t$to\t$ok\t$msg\n";
+ return $log;
+ }
+
+ /**
+ * write log to file
+ *
+ * @param $log
+ */
+ protected function write_log ($log) {
+ global $conf;
+ $optime = $this->options['started'];
+ $file = $conf['cachedir'] . '/move/' . strftime('%Y%m%d-%H%M%S', $optime) . '.log';
+ io_saveFile($file, $log, true);
+ }
+
+}
diff --git a/helper/rewrite.php b/helper/rewrite.php
new file mode 100644
index 0000000..73ae471
--- /dev/null
+++ b/helper/rewrite.php
@@ -0,0 +1,297 @@
+
+ * @author Gary Owen
+ * @author Andreas Gohr
+ */
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+// load required handler class
+require_once(dirname(__FILE__) . '/handler.php');
+
+/**
+ * Class helper_plugin_move_rewrite
+ *
+ * This class handles the rewriting of wiki text to update the links
+ */
+class helper_plugin_move_rewrite extends DokuWiki_Plugin {
+
+ /**
+ * Under what key is move data to be saved in metadata
+ */
+ const METAKEY = 'plugin_move';
+
+ /**
+ * What is they filename of the lockfile
+ */
+ const LOCKFILENAME = '_plugin_move.lock';
+
+ /**
+ * @var string symbol to make move operations easily recognizable in change log
+ */
+ public $symbol = '↷';
+
+ /**
+ * This function loads and returns the persistent metadata for the move plugin. If there is metadata for the
+ * pagemove plugin (not the old one but the version that immediately preceeded the move plugin) it will be migrated.
+ *
+ * @param string $id The id of the page the metadata shall be loaded for
+ * @return array|null The metadata of the page
+ */
+ public function getMoveMeta($id) {
+ $all_meta = p_get_metadata($id, '', METADATA_DONT_RENDER);
+
+ /* todo migrate old move data
+ if(isset($all_meta['plugin_pagemove']) && !is_null($all_meta['plugin_pagemove'])) {
+ if(isset($all_meta[self::METAKEY])) {
+ $all_meta[self::METAKEY] = array_merge_recursive($all_meta['plugin_pagemove'], $all_meta[self::METAKEY]);
+ } else {
+ $all_meta[self::METAKEY] = $all_meta['plugin_pagemove'];
+ }
+ p_set_metadata($id, array(self::METAKEY => $all_meta[self::METAKEY], 'plugin_pagemove' => null), false, true);
+ }
+ */
+
+ // discard missing or empty array or string
+ $meta = !empty($all_meta[self::METAKEY]) ? $all_meta[self::METAKEY] : array();
+ if(!isset($meta['origin'])) {
+ $meta['origin'] = '';
+ }
+ if(!isset($meta['pages'])) {
+ $meta['pages'] = array();
+ }
+ if(!isset($meta['media'])) {
+ $meta['media'] = array();
+ }
+
+ return $meta;
+ }
+
+ /**
+ * Remove any existing move meta data for the given page
+ *
+ * @param $id
+ */
+ public function unsetMoveMeta($id) {
+ p_set_metadata($id, array(self::METAKEY => array()), false, true);
+ }
+
+ /**
+ * Add info about a moved document to the metadata of an affected page
+ *
+ * @param string $id affected page
+ * @param string $src moved document's original id
+ * @param string $dst moved document's new id
+ * @param string $type 'media' or 'page'
+ * @throws Exception on wrong argument
+ */
+ public function setMoveMeta($id, $src, $dst, $type) {
+ $this->setMoveMetas($id, array($src => $dst), $type);
+ }
+
+ /**
+ * Add info about several moved documents to the metadata of an affected page
+ *
+ * @param string $id affected page
+ * @param array $moves list of moves (src is key, dst is value)
+ * @param string $type 'media' or 'page'
+ * @throws Exception
+ */
+ public function setMoveMetas($id, $moves, $type) {
+ if($type != 'pages' && $type != 'media') {
+ throw new Exception('wrong type specified');
+ }
+ if(!page_exists($id, '', false)) {
+ return;
+ }
+
+ $meta = $this->getMoveMeta($id);
+ foreach($moves as $src => $dst) {
+ $meta[$type][] = array($src, $dst);
+ }
+
+ p_set_metadata($id, array(self::METAKEY => $meta), false, true);
+ }
+
+ /**
+ * Store info about the move of a page in its own meta data
+ *
+ * This has to be called before the move is executed
+ *
+ * @param string $id moved page's original (and still current) id
+ */
+ public function setSelfMoveMeta($id) {
+ $meta = $this->getMoveMeta($id);
+ // was this page moved multiple times? keep the orignal name til rewriting occured
+ if(isset($meta['origin']) && $meta['origin'] !== '') {
+ return;
+ }
+ $meta['origin'] = $id;
+
+ p_set_metadata($id, array(self::METAKEY => $meta), false, true);
+ }
+
+ /**
+ * Check if rewrites may be executed within this process right now
+ *
+ * @return bool
+ */
+ public static function isLocked() {
+ global $PLUGIN_MOVE_WORKING;
+ global $conf;
+ $lockfile = $conf['lockdir'] . self::LOCKFILENAME;
+ return ((isset($PLUGIN_MOVE_WORKING) && $PLUGIN_MOVE_WORKING > 0) || file_exists($lockfile));
+ }
+
+ /**
+ * Do not allow any rewrites in this process right now
+ */
+ public static function addLock() {
+ global $PLUGIN_MOVE_WORKING;
+ global $conf;
+ $PLUGIN_MOVE_WORKING = $PLUGIN_MOVE_WORKING ? $PLUGIN_MOVE_WORKING + 1 : 1;
+ $lockfile = $conf['lockdir'] . self::LOCKFILENAME;
+ if (!file_exists($lockfile)) {
+ io_savefile($lockfile, "1\n");
+ } else {
+ $stack = intval(file_get_contents($lockfile));
+ ++$stack;
+ io_savefile($lockfile, strval($stack));
+ }
+ }
+
+ /**
+ * Allow rerites in this process again, unless some other lock exists
+ */
+ public static function removeLock() {
+ global $PLUGIN_MOVE_WORKING;
+ global $conf;
+ $PLUGIN_MOVE_WORKING = $PLUGIN_MOVE_WORKING ? $PLUGIN_MOVE_WORKING - 1 : 0;
+ $lockfile = $conf['lockdir'] . self::LOCKFILENAME;
+ if (!file_exists($lockfile)) {
+ throw new Exception("removeLock failed: lockfile missing");
+ } else {
+ $stack = intval(file_get_contents($lockfile));
+ if($stack === 1) {
+ unlink($lockfile);
+ } else {
+ --$stack;
+ io_savefile($lockfile, strval($stack));
+ }
+ }
+ }
+
+ /**
+ * Allow rewrites in this process again.
+ *
+ * @author Michael Große
+ */
+ public static function removeAllLocks() {
+ global $conf;
+ $lockfile = $conf['lockdir'] . self::LOCKFILENAME;
+ if (file_exists($lockfile)) {
+ unlink($lockfile);
+ }
+ unset($GLOBALS['PLUGIN_MOVE_WORKING']);
+ }
+
+
+ /**
+ * Rewrite a text in order to fix the content after the given moves.
+ *
+ * @param string $id The id of the wiki page, if the page itself was moved the old id
+ * @param string $text The text to be rewritten
+ * @return string The rewritten wiki text
+ */
+ public function rewrite($id, $text) {
+ $meta = $this->getMoveMeta($id);
+
+ $handlers = array();
+ $pages = $meta['pages'];
+ $media = $meta['media'];
+ $origin = $meta['origin'];
+ if($origin == '') $origin = $id;
+
+ $data = array(
+ 'id' => $id,
+ 'origin' => &$origin,
+ 'pages' => &$pages,
+ 'media_moves' => &$media,
+ 'handlers' => &$handlers
+ );
+
+ /*
+ * PLUGIN_MOVE_HANDLERS REGISTER event:
+ *
+ * Plugin handlers can be registered in the $handlers array, the key is the plugin name as it is given to the handler
+ * The handler needs to be a valid callback, it will get the following parameters:
+ * $match, $state, $pos, $pluginname, $handler. The first three parameters are equivalent to the parameters
+ * of the handle()-function of syntax plugins, the $pluginname is just the plugin name again so handler functions
+ * that handle multiple plugins can distinguish for which the match is. The last parameter is the handler object
+ * which is an instance of helper_plugin_move_handle
+ */
+ trigger_event('PLUGIN_MOVE_HANDLERS_REGISTER', $data);
+
+ $modes = p_get_parsermodes();
+
+ // Create the parser
+ $Parser = new Doku_Parser();
+
+ // Add the Handler
+ /** @var $Parser->Handler helper_plugin_move_handler */
+ $Parser->Handler = $this->loadHelper('move_handler');
+ $Parser->Handler->init($id, $origin, $pages, $media, $handlers);
+
+ //add modes to parser
+ foreach($modes as $mode) {
+ $Parser->addMode($mode['mode'], $mode['obj']);
+ }
+
+ return $Parser->parse($text);
+ }
+
+ /**
+ * Rewrite the text of a page according to the recorded moves, the rewritten text is saved
+ *
+ * @param string $id The id of the page that shall be rewritten
+ * @param string|null $text Old content of the page. When null is given the content is loaded from disk
+ * @return string|bool The rewritten content, false on error
+ */
+ public function rewritePage($id, $text = null, $save = true) {
+ $meta = $this->getMoveMeta($id);
+ if(is_null($text)) {
+ $text = rawWiki($id);
+ }
+
+ if($meta['pages'] || $meta['media']) {
+ $old_text = $text;
+ $text = $this->rewrite($id, $text);
+
+ $changed = ($old_text != $text);
+ $file = wikiFN($id, '', false);
+ if ($save === true) {
+ if(is_writable($file) || !$changed) {
+ if($changed) {
+ // Wait a second when the page has just been rewritten
+ $oldRev = filemtime(wikiFN($id));
+ if($oldRev == time()) sleep(1);
+
+ saveWikiText($id, $text, $this->symbol . ' ' . $this->getLang('linkchange'), $this->getConf('minor'));
+ }
+ $this->unsetMoveMeta($id);
+ } else {
+ // FIXME: print error here or fail silently?
+ msg('Error: Page ' . hsc($id) . ' needs to be rewritten because of page renames but is not writable.', -1);
+ return false;
+ }
+ }
+ }
+
+ return $text;
+ }
+
+}
diff --git a/images/disk.png b/images/disk.png
new file mode 100644
index 0000000..99d532e
Binary files /dev/null and b/images/disk.png differ
diff --git a/images/folder_add.png b/images/folder_add.png
new file mode 100644
index 0000000..529fe8f
Binary files /dev/null and b/images/folder_add.png differ
diff --git a/images/page.png b/images/page.png
new file mode 100644
index 0000000..03ddd79
Binary files /dev/null and b/images/page.png differ
diff --git a/images/page_link.png b/images/page_link.png
new file mode 100644
index 0000000..312eab0
Binary files /dev/null and b/images/page_link.png differ
diff --git a/images/rename.png b/images/rename.png
new file mode 100644
index 0000000..8943caf
Binary files /dev/null and b/images/rename.png differ
diff --git a/images/rename.svg b/images/rename.svg
new file mode 100644
index 0000000..1f03921
--- /dev/null
+++ b/images/rename.svg
@@ -0,0 +1 @@
+
diff --git a/images/sprite.png b/images/sprite.png
new file mode 100644
index 0000000..ed8cc3c
Binary files /dev/null and b/images/sprite.png differ
diff --git a/lang/ar/lang.php b/lang/ar/lang.php
new file mode 100644
index 0000000..c7567a6
--- /dev/null
+++ b/lang/ar/lang.php
@@ -0,0 +1,10 @@
+
+ */
+$lang['menu'] = 'نقل الصفحات ومساحات الأسماء';
+$lang['inprogress'] = '(التحرك معلق)';
+$lang['treelink'] = 'بدلاً من هذا النموذج البسيط ، يمكنك إدارة إعادة الهيكلة المعقدة لموقع wiki الخاص بك باستخدام مدير النقل المستند إلى الشجرة . ';
diff --git a/lang/cs/lang.php b/lang/cs/lang.php
new file mode 100644
index 0000000..265820a
--- /dev/null
+++ b/lang/cs/lang.php
@@ -0,0 +1,67 @@
+
+ * @author Tomáš Borland Valenta
+ * @author Pavel Kochman
+ */
+$lang['menu'] = 'Přesun/přejmenování stránky';
+$lang['inprogress'] = '(probíhá přesun)';
+$lang['treelink'] = 'Alternativou k tomuto jednoduchému formuláři můžete použít komplexní přeskupení vaší wiki pomocí stromového manažeru přesunu.';
+$lang['notexist'] = 'Stránka \'%s\' neexistuje.';
+$lang['norights'] = 'Nedostatečné oprávnění editovat \'%s\'.';
+$lang['filelocked'] = 'Stránka \'%s\' je uzamčna - zkuste prosím znovu později.';
+$lang['notchanged'] = 'Žádný cíl pro stránku \'%s\' (umístění nezměněno).';
+$lang['exists'] = 'Stránku \'%s\' nelze přesunout do \'%s\', cíl již existuje.';
+$lang['notargetperms'] = 'Nemáte oprávnění vytvořit stránku \'%s\'.';
+$lang['medianotexist'] = 'Médium \'%s\' neexistuje.';
+$lang['nomediarights'] = 'Nedostatečné oprávnění ke smazání \'%s\'.';
+$lang['medianotchanged'] = 'Žádný cíl pro médium \'%s\' (umístění nezměněno).';
+$lang['mediaexists'] = 'Médium \'%s\' nelze přesunout do \'%s\', cíl již existuje.';
+$lang['nomediatargetperms'] = 'Nemáte oprávnění vytvořit médium \'%s\'.';
+$lang['indexerror'] = 'Chyba při aktualizaci search index \'%s\'.';
+$lang['metamoveerror'] = 'Meta soubory stránky \'%s\' nemohou být přesunut.';
+$lang['atticmoveerror'] = 'Staré verze stránky \'%s\' nemohou být přesunuty. Prosím, přsuňte je ručně.';
+$lang['mediametamoveerror'] = 'Meta soubory média \'%s\' nemohou být přesunuty.';
+$lang['mediamoveerror'] = 'Přesunutí média \'%s\' selhalo.';
+$lang['mediaatticmoveerror'] = 'Staré verze média \'%s\' nemohou být přesunuty. Prosím, přsuňte je ručně.';
+$lang['renamed'] = 'Stránka přejmenována z \'%s\' na \'%s\'';
+$lang['moved'] = 'Stránka přesunuta z \'%s\' do \'%s\'';
+$lang['move_rename'] = 'Stránka přesunuta a přejmenována z \'%s\' do \'%s\'';
+$lang['delete'] = 'Smazáno pluginem move';
+$lang['linkchange'] = 'Odkaz upraven z důvodu přesunutí';
+$lang['intro'] = 'Přesun dosud nebyl zahájen!';
+$lang['preview'] = 'Náhled změn, které budou provedeny.';
+$lang['inexecution'] = 'Předchozí přesun nebyl dokončen - použijte tlačítko dole k pokračování či přerušení přesunu.';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Pokračovat';
+$lang['btn_retry'] = 'Znovu';
+$lang['btn_skip'] = 'Přeskočit položku';
+$lang['btn_abort'] = 'Přerušit';
+$lang['legend'] = 'Přesunout aktuální stránku nebo jmenný prostor';
+$lang['movepage'] = 'Přesunout stránku';
+$lang['movens'] = 'Přesunout aktuální jmenný prostor';
+$lang['dst'] = 'Nové jméno:';
+$lang['content_to_move'] = 'Obsah k přesunutí:';
+$lang['autoskip'] = 'Ignorovat chyby a přeskočit stránky a média, které nemohou být přesunuty';
+$lang['autorewrite'] = 'Přepsat odkazy po úspěšmén přesunu';
+$lang['move_pages'] = 'Stránky';
+$lang['move_media'] = 'Média';
+$lang['move_media_and_pages'] = 'Stránky a média';
+$lang['nodst'] = 'Žádné nové jméno';
+$lang['noaction'] = 'Nebyl definován přesun';
+$lang['renamepage'] = 'Přejmenovat stránku';
+$lang['cantrename'] = 'Stránka nemůže být právě přejmenována. Zkuste to později.';
+$lang['js']['rename'] = 'Přejmenovat';
+$lang['js']['cancel'] = 'Zrušit';
+$lang['js']['newname'] = 'Nový název stránky:';
+$lang['js']['inprogress'] = 'Přesouvám stránky a upravuji odkazy...';
+$lang['js']['complete'] = 'Přesun byl dokončen.';
+$lang['js']['renameitem'] = 'Přejmenovat tuto položku';
+$lang['js']['add'] = 'Vytvořit nový jmenný prostor';
+$lang['js']['duplicate'] = 'Lituji, ale \'%s\' již existuje ve jmenném prosoru.';
+$lang['root'] = '[Kořen]';
+$lang['noscript'] = 'Tato vlastost vyžaduje JavaScript';
+$lang['moveinprogress'] = 'Právě probíhá jiný přsun, proto nyní nemůžete použít tento nástroj.';
diff --git a/lang/cs/lang.php.txt b/lang/cs/lang.php.txt
deleted file mode 100644
index 26d3668..0000000
--- a/lang/cs/lang.php.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/lang/cs/pagemove.txt.txt b/lang/cs/move.txt
similarity index 61%
rename from lang/cs/pagemove.txt.txt
rename to lang/cs/move.txt
index 8d84cf9..c2a832b 100644
--- a/lang/cs/pagemove.txt.txt
+++ b/lang/cs/move.txt
@@ -1,11 +1,10 @@
-====== Pagemove ======
+====== Move ======
Pomocí tohoto pluginu lze přesunout nebo přejmenovat aktuální stránku. Platí však jistá omezení:
- * Nelze přesouvat titulní stránku.
- * Musíte mít práva na danou stránku a na všechny, které se na ní odkazují.
- * Tato stránka a na ni odkazující stránky nesmí být zamčené.
+ * Musíte mít práva na danou stránku.
+ * Stránky které se přesouvají nesmí být zamčeny pro úpravu.
* Pro přesun stránky potřebujete práva pro zápis v cílovém místě.
* Nelze přesunout stránku někam, kde již existuje stránka stejného jména.
-
+
Všechny odkazy na a ze stránky budou upraveny podle nového umístění stránky.
diff --git a/lang/cs/progress.txt b/lang/cs/progress.txt
new file mode 100644
index 0000000..3ac1e43
--- /dev/null
+++ b/lang/cs/progress.txt
@@ -0,0 +1,3 @@
+====== Probíhá přesun... ======
+
+Prosím nezavírejte tuto stránku, dokud přesun neskončí.
\ No newline at end of file
diff --git a/lang/cs/settings.php b/lang/cs/settings.php
new file mode 100644
index 0000000..282b473
--- /dev/null
+++ b/lang/cs/settings.php
@@ -0,0 +1,13 @@
+
+ * @author Pavel Kochman
+ */
+$lang['allowrename'] = 'Povolit přeijmenovat stránky témto skupinám a uživatelům (oddělených čárkou).';
+$lang['minor'] = 'Označit úpravu odkaů jako drobná změna? Drobné změny nebudou viditelné v RSS feeds a v odebíraných mailech změn.';
+$lang['autoskip'] = 'Zapnout automatické přeskakování chyb při přesouvání namespace.';
+$lang['autorewrite'] = 'Zapnout automatické přepisování odkazů po přesunutí namespace.';
+$lang['pagetools_integration'] = 'Přidat tlačítko pro přejmenování do nástrojů stránky';
diff --git a/lang/cs/tree.txt b/lang/cs/tree.txt
new file mode 100644
index 0000000..8c4db4a
--- /dev/null
+++ b/lang/cs/tree.txt
@@ -0,0 +1,3 @@
+====== Move Manager =====
+
+Tento interface umožňuje přeorganizovat vaše wiki namespace, stránky a média pomocí Drag'n'Drop.
\ No newline at end of file
diff --git a/lang/da/lang.php b/lang/da/lang.php
new file mode 100644
index 0000000..73cf293
--- /dev/null
+++ b/lang/da/lang.php
@@ -0,0 +1,65 @@
+
+ */
+$lang['menu'] = 'Flyt sider og navnerum';
+$lang['inprogress'] = '(flytning afventer)';
+$lang['treelink'] = 'Som alternativ til denne simple formular, kan du omstrukturere din wiki ved hjælp af trævisningen i flytningshåndtering.';
+$lang['notexist'] = 'Siden %s findes ikke';
+$lang['norights'] = 'Du har ikke rettigheder til at redigere %s.';
+$lang['filelocked'] = 'Siden %s er låst. Prøv igen senere.';
+$lang['notchanged'] = 'Ingen ny lokation til siden %s angivet (lokation uændret).';
+$lang['exists'] = 'Siden %s kan ikke flyttes til %s, da destinationssiden allerede findes.';
+$lang['notargetperms'] = 'Du har ikke rettigheder til at oprette siden %s.';
+$lang['medianotexist'] = 'Mediefilen %s findes ikke';
+$lang['nomediarights'] = 'Du har ikke rettigheder til at slette %s.';
+$lang['medianotchanged'] = 'Ingen ny lokation til siden %s angivet (lokation uændret).';
+$lang['mediaexists'] = 'Mediet %s kan ikke flyttes til %s, der findes allerede et medie der.';
+$lang['nomediatargetperms'] = 'Du har ikke rettigheder til at oprette mediefilen %s.';
+$lang['indexerror'] = 'Fejl under opdatering af søgeindeks %s';
+$lang['metamoveerror'] = 'Meta filerne til siden %s kunne ikke flyttes';
+$lang['atticmoveerror'] = 'Attic-filerne til siden %s kunne ikke flyttes. Disse skal flyttes manuelt.';
+$lang['mediametamoveerror'] = 'Meta filerne til mediefilen %s kunne ikke flyttes';
+$lang['mediamoveerror'] = 'Flytning af mediefilen %s fejlede';
+$lang['mediaatticmoveerror'] = 'Attic-filerne til mediefilen %s kunne ikke flyttes. Disse skal flyttes manuelt.';
+$lang['renamed'] = 'Sidenavn ændret fra %s til %s';
+$lang['moved'] = 'Side flyttet fra %s til %s';
+$lang['move_rename'] = 'Side flyttet og omdøbt fra %s til %s';
+$lang['delete'] = 'Slettet af flytningsudvidelse';
+$lang['linkchange'] = 'Links tilrettede pga. en flytningsoperation';
+$lang['intro'] = 'Flytningsoperation er ikke startet endnu!';
+$lang['preview'] = 'Forhåndsvisning af ændringer der bliver udført.';
+$lang['inexecution'] = 'En tidligere flytningsoperation blev ikke fuldført - brug knapperne herunder til at fortsætte eller afbryde flytningen.';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Fortsæt';
+$lang['btn_retry'] = 'Forsøg igen';
+$lang['btn_skip'] = 'Spring over';
+$lang['btn_abort'] = 'Afbryd';
+$lang['legend'] = 'Flyt aktuelle side eller navnerum';
+$lang['movepage'] = 'Flyt side';
+$lang['movens'] = 'Flyt navnerum';
+$lang['dst'] = 'Nyt navn:';
+$lang['content_to_move'] = 'Indhold der skal flyttes:';
+$lang['autoskip'] = 'Ignorer fejl, og spring over sider/filer der ikke kan flyttes';
+$lang['autorewrite'] = 'Tilret links når flytning er fuldført';
+$lang['move_pages'] = 'Sider';
+$lang['move_media'] = 'Mediefiler';
+$lang['move_media_and_pages'] = 'Sider og mediefiler';
+$lang['nodst'] = 'Intet nyt navn angivet';
+$lang['noaction'] = 'Der blev ikke defineret nogen flytninger';
+$lang['renamepage'] = 'Omdøb side';
+$lang['cantrename'] = 'Siden kan ikke omdøbes i øjeblikket. Prøv igen senere.';
+$lang['js']['rename'] = 'Omdøb';
+$lang['js']['cancel'] = 'Annuller';
+$lang['js']['newname'] = 'Nyt navn:';
+$lang['js']['inprogress'] = 'omdøber side og tilretter links...';
+$lang['js']['complete'] = 'Flytningsoperation færdig.';
+$lang['js']['renameitem'] = 'Omdøb denne';
+$lang['js']['add'] = 'Opret et nyt navnerum';
+$lang['js']['duplicate'] = 'Beklager, "%s" findes allerede i dette navnerum.';
+$lang['root'] = '[Rod navnerum]';
+$lang['noscript'] = 'Denne handling kræver JavaScript';
+$lang['moveinprogress'] = 'Der er i øjeblikket en anden flytningsoperation i gang, du kan ikke anvende dette værktøj på dette tidspunkt.';
diff --git a/lang/da/move.txt b/lang/da/move.txt
new file mode 100644
index 0000000..21a7540
--- /dev/null
+++ b/lang/da/move.txt
@@ -0,0 +1,10 @@
+====== Flyt ======
+
+Denne udvidelse gør dig i stand til at flytte og/eller omdøbe den aktuelle side, eller det aktuelle navnerum, med følgende restriktioner:
+
+ * Du skal have rettigheder til at redigere den aktuelle side/alle sider i det aktuelle navnerum
+ * Siderne der flyttes må ikke være låst pga. redigering
+ * For at flytte en side, skal du også have rettigheder til at oprette sider i det navnerum der flyttes til
+ * Du kan ikke flytte en side til et navnerum, hvor der allerede findes en side med samme navn.
+
+Alle links til og fra sider der flyttes, vil blive tilrettet så de passer til den nye lokation og/eller det nye navn.
\ No newline at end of file
diff --git a/lang/da/progress.txt b/lang/da/progress.txt
new file mode 100644
index 0000000..9e83c1b
--- /dev/null
+++ b/lang/da/progress.txt
@@ -0,0 +1,3 @@
+===== Flytning i gang... =====
+
+Hold venligst denne side åben mens flytningen behandles.
\ No newline at end of file
diff --git a/lang/da/settings.php b/lang/da/settings.php
new file mode 100644
index 0000000..dfdb432
--- /dev/null
+++ b/lang/da/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Tillad disse grupper og brugere at omdøbe sider (adskil med komma).';
+$lang['minor'] = 'Markér tilretning af links som mindre rettelser? Mindre rettelser optræder ikke i RSS-feeds elller mails.';
+$lang['autoskip'] = 'Slå overspring af fejl under flytning af navnerum til som standard';
+$lang['autorewrite'] = 'Slå automatisk tilrettelse af links efter flytning af navnerum til som standard';
+$lang['pagetools_integration'] = 'Tilføj omdøbningsknap til sideværktøjer';
diff --git a/lang/da/tree.txt b/lang/da/tree.txt
new file mode 100644
index 0000000..dad74fc
--- /dev/null
+++ b/lang/da/tree.txt
@@ -0,0 +1,7 @@
+===== Flytningshåndtering =====
+
+Denne side gør dig i stand til at omarrangere din wikis navnerum, sider og mediefiler via træk og slip.
+
+For at flytte mange navnerum, sider eller mediefiler til det samme navnerum, kan du benytte afkrydsningsfelter som følger:
+ * Marker de navnerum, sider og/eller mediefiler som du ønsker at flytte
+ * Træk et af de markerede elementer til den ønskede lokation, og alle øvrige markerede elementer flyttes automatisk med.
\ No newline at end of file
diff --git a/lang/de-informal/lang.php b/lang/de-informal/lang.php
new file mode 100644
index 0000000..2fb0ef3
--- /dev/null
+++ b/lang/de-informal/lang.php
@@ -0,0 +1,71 @@
+
+ */
+$lang['menu'] = 'Seite/Namensraum verschieben/umbenennen...';
+$lang['inprogress'] = '(Verschiebung läuft gerade)';
+$lang['treelink'] = 'Alternativ zu dieser einfachen Form können komplexere Umstrukturierungen des Wikis mit dem baumbasierten Move-Manager durchgeführt werden.';
+$lang['notexist'] = 'Die Seite %s existiert nicht.';
+$lang['norights'] = 'Du hast unzureichende Rechte um %s zu bearbeiten.';
+$lang['filelocked'] = 'Die Seite %s ist gesperrt - bitte versuche es später noch einmal.';
+$lang['notchanged'] = 'Kein Ziel für Seite %s angegeben (Ort bleibt unverändert).';
+$lang['exists'] = 'Seite %s kann nicht nach %s verschoben werden, da das Ziel bereits existiert.';
+$lang['notargetperms'] = 'Du hast nicht die Berechtigung, die Seite %s anzulegen.';
+$lang['medianotexist'] = 'Die Mediendatei %s existiert nicht';
+$lang['nomediarights'] = 'Du hast unzureichende Rechte, um die Mediendatei %s zu löschen.';
+$lang['medianotchanged'] = 'Kein Ziel für Seite %s angegeben (Ort bleibt unverändert).';
+$lang['mediaexists'] = 'Mediendatei %s kann nicht nach %s verschoben werden, da das Ziel bereits existiert.';
+$lang['nomediatargetperms'] = 'Du hast keine Berechtigung, die Mediendatei %s anzulegen.';
+$lang['extensionchange'] = 'Die Dateierweiterungen der alten und der neuen Datei sind unterschiedlich.';
+$lang['indexerror'] = 'Fehler während der Aktualisierung des Suchindexes %s';
+$lang['metamoveerror'] = 'Die Metadateien der Seite %s konnten nicht verschoben werden';
+$lang['atticmoveerror'] = 'Die Attic-Dateien der Seite %s konnten nicht verschoben werden. Bitte verschiebe Sie sie manuell.';
+$lang['mediametamoveerror'] = 'Die Metadateien der Mediendatei %s konnten nicht verschoben werden';
+$lang['mediamoveerror'] = 'Verschieben der Mediendatei %s fehlgeschlagen';
+$lang['mediaatticmoveerror'] = 'Die Attic-Dateien der Mediendatei %s konnten nicht verschoben werden. Bitte verschiebe sie manuell.';
+$lang['renamed'] = 'Seitename wurde von %s auf %s geändert';
+$lang['moved'] = 'Seite von %s nach %s verschoben';
+$lang['move_rename'] = 'Seite von %s nach %s verschoben und umbenannt';
+$lang['delete'] = 'Gelöscht durch das move Plugin';
+$lang['linkchange'] = 'Links angepasst weil Seiten im Wiki verschoben wurden';
+$lang['intro'] = 'Die Verschiebung wurde noch nicht gestartet!';
+$lang['preview'] = 'Vorschau der Änderungen, die ausgeführt werden sollen ';
+$lang['inexecution'] = 'Eine vorherige Verschiebung wurde nicht beendet - benutze die Knöpfe unten um die Verschiebung fortzusetzen oder abzubrechen. ';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Fortsetzen';
+$lang['btn_retry'] = 'Element wiederholen';
+$lang['btn_skip'] = 'Element überspringen';
+$lang['btn_abort'] = 'Abbrechen';
+$lang['legend'] = 'Aktuelle Seite oder Namensraum verschieben';
+$lang['movepage'] = 'Seite verschieben';
+$lang['movens'] = 'Namensraum verschieben';
+$lang['dst'] = 'Neuer Name:';
+$lang['content_to_move'] = 'Zu verschiebender Inhalt:';
+$lang['autoskip'] = 'Zu verschiebender Inhalt:';
+$lang['autorewrite'] = 'Links anpassen, nachdem die Verschiebung abgeschlossen wurde';
+$lang['move_pages'] = 'Seiten';
+$lang['move_media'] = 'Mediandateien';
+$lang['move_media_and_pages'] = 'Seiten und Mediendateien';
+$lang['nodst'] = 'Kein neuer Name angegeben';
+$lang['noaction'] = 'Es wurden keine Verschiebungen angegeben';
+$lang['renamepage'] = 'Seite umbenennen';
+$lang['notallowed'] = 'Du hast unzureichende Rechte, um Seiten oder Medien umzubenennen.';
+$lang['cantrename'] = 'Auf die Seite kann zur Zeit nicht zugegriffen werden - bitte versuche es später noch einmal.';
+$lang['js']['rename'] = 'Umbenennen';
+$lang['js']['cancel'] = 'Abbrechen';
+$lang['js']['newname'] = 'Neuer Name:';
+$lang['js']['inprogress'] = 'Benenne Seite um und passe Links an...';
+$lang['js']['complete'] = 'Verschieben abgeschlossen.';
+$lang['js']['renameitem'] = 'Dieses Element umbenennen';
+$lang['js']['add'] = 'Neuen Namensraum erstellen';
+$lang['js']['duplicate'] = 'Entschuldigung, "%s" existiert in diesem Namensraum bereits. ';
+$lang['root'] = '[Oberster Namensraum]';
+$lang['noscript'] = 'Dieses Feature benötigt JavaScript.';
+$lang['moveinprogress'] = 'Eine andere Verschiebeoperation läuft momentan, du kannst dieses Tool gerade nicht benutzen.';
+
+// Media Manager
+$lang['js']['moveButton'] = 'Datei verschieben / umbenennen';
+$lang['js']['dialogIntro'] = 'Neuer Dateiname. Du kannst den Namensraum ändern, aber nicht die Dateierweiterung.';
diff --git a/lang/de-informal/move.txt b/lang/de-informal/move.txt
new file mode 100644
index 0000000..daf9f1e
--- /dev/null
+++ b/lang/de-informal/move.txt
@@ -0,0 +1,11 @@
+====== Seite/Namensraum verschieben/umbenennen ======
+
+Mit diesem Plugin kann die aktuelle Seite oder ein gesamter Namensraum verschoben oder umbenannt werden.
+Folgende Einschränkungen/Bedingungen gelten:
+
+ * Sie müssen die Bearbeiten-Rechte für die Seite/alle Seiten im aktuellen Namensraum haben.
+ * Die Seiten, die verschoben werden, dürfen nicht gesperrt sein, d.h. sich im Bearbeitungsmodus befinden.
+ * Um eine Seite zu verschieben, benötigt man das Erstellen-Recht im Ziel-Namensraum.
+ * Eine Seite kann nicht in einen Namensraum verschoben werden, in der bereits eine Seite gleichen Namens existiert.
+
+Alle Links von und auf die Seite werden aktualisiert, um dem neuen Namen bzw. dem neuen Namensraum zu entsprechen.
diff --git a/lang/de-informal/progress.txt b/lang/de-informal/progress.txt
new file mode 100644
index 0000000..f9b39e1
--- /dev/null
+++ b/lang/de-informal/progress.txt
@@ -0,0 +1,3 @@
+====== Verschiebung läuft... ======
+
+Bitte lassen Sie diese Seite geöffnet, während die Verschiebung läuft.
\ No newline at end of file
diff --git a/lang/de-informal/settings.php b/lang/de-informal/settings.php
new file mode 100644
index 0000000..71ba293
--- /dev/null
+++ b/lang/de-informal/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Umbenennen von Seiten und Mediendateien diesen Gruppen und Benutzern erlauben (durch Kommas getrennt).';
+$lang['minor'] = 'Linkanpassung als kleine Änderung markieren? Kleine Änderungen werden in RSS Feeds und Benachrichtigungsmails nicht aufgeführt.';
+$lang['autoskip'] = 'Automatisches Überspringen von Fehlern beim Verschieben von Namensräumen standardmäßig aktivieren. ';
+$lang['autorewrite'] = 'Automatische Linkanpassung standardmäßig aktivieren.';
+$lang['pagetools_integration'] = 'Umbenennen-Button zu Pagetools hinzufügen';
diff --git a/lang/de-informal/tree.txt b/lang/de-informal/tree.txt
new file mode 100644
index 0000000..a5c31d1
--- /dev/null
+++ b/lang/de-informal/tree.txt
@@ -0,0 +1,3 @@
+====== Move-Manager =====
+
+Diese Ansicht ermöglicht die Restrukturierung von Namensräumen, Seiten und Mediendateien des Wikis per Drag'n'Drop.
diff --git a/lang/de/lang.php b/lang/de/lang.php
index 5144573..e6f6479 100644
--- a/lang/de/lang.php
+++ b/lang/de/lang.php
@@ -1,48 +1,73 @@
+ *
+ * @author picsar
+ * @author e-dschungel
+ * @author F. Mueller-Donath
*/
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Seite/Namespace verschieben/umbenennen...';
-$lang['desc'] = 'Seite/Namespace verschieben/umbenennen... Plugin';
+$lang['menu'] = 'Seite/Namensraum verschieben/umbenennen...';
+$lang['inprogress'] = '(Verschiebung läuft gerade)';
+$lang['treelink'] = 'Alternativ zu dieser einfachen Form können komplexere Umstrukturierungen des Wikis mit dem baumbasierten Move-Manager durchgeführt werden.';
+$lang['notexist'] = 'Die Seite %s existiert nicht.';
+$lang['norights'] = 'Sie haben unzureichende Rechte um %s zu bearbeiten.';
+$lang['filelocked'] = 'Die Seite %s ist gesperrt - versuchen Sie es später noch einmal.';
+$lang['notchanged'] = 'Kein Ziel für Seite %s angegeben (Ort bleibt unverändert).';
+$lang['exists'] = 'Seite %s kann nicht nach %s verschoben werden, da das Ziel bereits existiert.';
+$lang['notargetperms'] = 'Sie haben nicht die Berechtigung, die Seite %s anzulegen.';
+$lang['medianotexist'] = 'Die Mediendatei %s existiert nicht';
+$lang['nomediarights'] = 'Sie haben unzureichende Rechte, um die Mediendatei %s zu löschen.';
+$lang['medianotchanged'] = 'Kein Ziel für Seite %s angegeben (Ort bleibt unverändert).';
+$lang['mediaexists'] = 'Mediendatei %s kann nicht nach %s verschoben werden, da das Ziel bereits existiert.';
+$lang['nomediatargetperms'] = 'Sie haben nicht die Berechtigung, die Mediendatei %s anzulegen.';
+$lang['extensionchange'] = 'Die Dateierweiterungen der alten und der neuen Datei sind unterschiedlich.';
+$lang['indexerror'] = 'Fehler während der Aktualisierung des Suchindexes %s';
+$lang['metamoveerror'] = 'Die Metadateien der Seite %s konnten nicht verschoben werden';
+$lang['atticmoveerror'] = 'Die Attic-Dateien der Seite %s konnten nicht verschoben werden. Bitte verschieben Sie sie manuell.';
+$lang['mediametamoveerror'] = 'Die Metadateien der Mediendatei %s konnten nicht verschoben werden';
+$lang['mediamoveerror'] = 'Verschieben der Mediendatei %s fehlgeschlagen';
+$lang['mediaatticmoveerror'] = 'Die Attic-Dateien der Mediendatei %s konnten nicht verschoben werden. Bitte verschieben Sie sie manuell.';
+$lang['renamed'] = 'Seitename wurde von %s auf %s geändert';
+$lang['moved'] = 'Seite von %s nach %s verschoben';
+$lang['move_rename'] = 'Seite von %s nach %s verschoben und umbenannt';
+$lang['delete'] = 'Gelöscht durch das move Plugin';
+$lang['linkchange'] = 'Links angepasst, weil Seiten im Wiki verschoben wurden';
+$lang['intro'] = 'Die Verschiebung wurde noch nicht gestartet!';
+$lang['preview'] = 'Vorschau der Änderungen, die ausgeführt werden sollen ';
+$lang['inexecution'] = 'Eine vorherige Verschiebung wurde nicht beendet - benutzen Sie die Knöpfe unten um die Verschiebung fortzusetzen oder abzubrechen. ';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Fortsetzen';
+$lang['btn_retry'] = 'Element wiederholen';
+$lang['btn_skip'] = 'Element überspringen';
+$lang['btn_abort'] = 'Abbrechen';
+$lang['legend'] = 'Aktuelle Seite oder aktueller Namensraum';
+$lang['movepage'] = 'Seite verschieben';
+$lang['movens'] = 'Namensraum verschieben';
+$lang['dst'] = 'Neuer Name:';
+$lang['content_to_move'] = 'Zu verschiebender Inhalt:';
+$lang['autoskip'] = 'Fehler ignorieren und Seiten oder Dateien, die nicht verschoben werden können, überspringen';
+$lang['autorewrite'] = 'Links anpassen, nachdem die Verschiebung abgeschlossen wurde';
+$lang['move_pages'] = 'Seiten';
+$lang['move_media'] = 'Mediendateien';
+$lang['move_media_and_pages'] = 'Seiten und Mediendateien';
+$lang['nodst'] = 'Kein neuer Name angegeben';
+$lang['noaction'] = 'Es wurden keine Verschiebungen angegeben';
+$lang['renamepage'] = 'Seite umbenennen';
+$lang['notallowed'] = 'Sie haben unzureichende Rechte, um Seiten oder Medien umzubenennen.';
+$lang['cantrename'] = 'Auf die Seite kann zur Zeit nicht zugegriffen werden - versuchen Sie es später noch einmal.';
+$lang['js']['rename'] = 'Umbenennen';
+$lang['js']['cancel'] = 'Abbrechen';
+$lang['js']['newname'] = 'Neuer Name:';
+$lang['js']['inprogress'] = 'Benenne Seite um und passe Links an...';
+$lang['js']['complete'] = 'Verschieben abgeschlossen.';
+$lang['js']['renameitem'] = 'Dieses Element umbenennen';
+$lang['js']['add'] = 'Neuen Namensraum erstellen';
+$lang['js']['duplicate'] = 'Entschuldigung, "%s" existiert in diesem Namensraum bereits. ';
+$lang['root'] = '[Oberster Namensraum]';
+$lang['noscript'] = 'Dieses Feature benötigt JavaScript.';
+$lang['moveinprogress'] = 'Eine andere Verschiebeoperation läuft momentan, Sie können dieses Tool gerade nicht benutzen.';
-$lang['pm_notexist'] = 'Dieses Thema existiert noch nicht';
-$lang['pm_notstart'] = 'Die Startseite kann nicht verschoben oder umbenannt werden';
-$lang['pm_notwrite'] = 'Sie haben unzureichende Rechte um diese Seite zu ändern';
-$lang['pm_badns'] = 'Ungültige Zeichen in der Namensraum-Bezeichnung.';
-$lang['pm_badname'] = 'Ungültiges Zeichen im Seitennamen.';
-$lang['pm_nochange'] = 'Name und Namensraum der Seite sind unverändert.';
-$lang['pm_existing'] = 'Eine Seite mit der Bezeichnung %s existiert bereits in %s';
-$lang['pm_root'] = '[Wurzel des Namensraumes / Root namespace]';
-$lang['pm_current'] = '(Aktueller)';
-$lang['pm_movedfrom'] = 'Seite verschoben von ';
-$lang['pm_movedto'] = 'Seite verschoben nach ';
-$lang['pm_renamed'] = 'Seitename wurde geändert von %s auf %s';
-$lang['pm_moved'] = 'Seite verschoben von %s nach %s';
-$lang['pm_move_rename'] = 'Seite verschoben und umbenannt von %s nach %s';
-$lang['pm_norights'] = 'Sie haben unzureichende Rechte, einen oder mehrere Rückverweise mit diesem Dokument zu verändern.';
-$lang['pm_tryagain'] = 'Versuchen Sie es später nochmal.';
-$lang['pm_filelocked'] = 'Diese Datei ist gesperrt - ';
-$lang['pm_fileslocked'] = 'Diese Dateien sind gesperrt - ';
-$lang['pm_linkchange'] = 'Link mit %s geändert zu %s';
-$lang['pm_newname'] = 'Neuer Seitenname:';
-$lang['pm_newnsname'] = 'Neuen Namen für Namensraum verwenden:';
-$lang['pm_targetns'] = 'Wählen Sie einen neuen Namensraum: ';
-$lang['pm_newtargetns'] = 'Erstellen Sie einen neuen Namensraum';
-$lang['pm_movepage'] = 'Seite verschieben';
-$lang['pm_movens'] = 'Namensraum verschieben';
-$lang['pm_previewpage'] = ' wird verschoben nach ';
-$lang['pm_previewns'] = 'Alle Seiten und Namensräume im Namensraum %s: werden verschoben in den Namensraum';
-$lang['pm_preview'] = 'Vorschau';
-$lang['pm_delete'] = 'Gelöscht durch pagemove Plugin';
-$lang['pm_submit'] = 'Übernehmen';
-?>
+// Media Manager
+$lang['js']['moveButton'] = 'Datei verschieben / umbenennen';
+$lang['js']['dialogIntro'] = 'Neuer Dateiname. Sie können den Namensraum ändern, aber nicht die Dateierweiterung.';
diff --git a/lang/de/move.txt b/lang/de/move.txt
new file mode 100644
index 0000000..daf9f1e
--- /dev/null
+++ b/lang/de/move.txt
@@ -0,0 +1,11 @@
+====== Seite/Namensraum verschieben/umbenennen ======
+
+Mit diesem Plugin kann die aktuelle Seite oder ein gesamter Namensraum verschoben oder umbenannt werden.
+Folgende Einschränkungen/Bedingungen gelten:
+
+ * Sie müssen die Bearbeiten-Rechte für die Seite/alle Seiten im aktuellen Namensraum haben.
+ * Die Seiten, die verschoben werden, dürfen nicht gesperrt sein, d.h. sich im Bearbeitungsmodus befinden.
+ * Um eine Seite zu verschieben, benötigt man das Erstellen-Recht im Ziel-Namensraum.
+ * Eine Seite kann nicht in einen Namensraum verschoben werden, in der bereits eine Seite gleichen Namens existiert.
+
+Alle Links von und auf die Seite werden aktualisiert, um dem neuen Namen bzw. dem neuen Namensraum zu entsprechen.
diff --git a/lang/de/pagemove.txt b/lang/de/pagemove.txt
deleted file mode 100644
index 9a33ea7..0000000
--- a/lang/de/pagemove.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-====== Seite/Namespace verschieben/umbenennen ======
-
-Mit diesem Plugin kann die aktuelle Seite verschoben/umbenannt oder ein gesamter Namensraum verschoben/umbenannt werden.
-
-Folgende Einschränkungen/Bedingungen gelten:
-
- * Die Startseite kann nicht verschoben werden.
- * Man benötigt die Bearbeiten-Rechte für die Seite und für alle Seiten die darauf verlinkt sind (Rückverweise).
- * Die Seite und deren Rückverweise dürfen nicht gesperrt sein, d.h. sich im Bearbeitungsmodus befinden.
- * Um eine Seite zu verschieben, benötigt man das Erstellen-Recht im Ziel-Namensraum.
- * Eine Seite kann nicht in einen Namensraum verschoben werden, in der bereits eine Seite gleichen Namens existiert.
-
-Alle Links von und auf die Seite werden aktualisiert, um dem neuen Namen bzw. dem neuen Namensraum zu entsprechen.
-
diff --git a/lang/de/progress.txt b/lang/de/progress.txt
new file mode 100644
index 0000000..f9b39e1
--- /dev/null
+++ b/lang/de/progress.txt
@@ -0,0 +1,3 @@
+====== Verschiebung läuft... ======
+
+Bitte lassen Sie diese Seite geöffnet, während die Verschiebung läuft.
\ No newline at end of file
diff --git a/lang/de/settings.php b/lang/de/settings.php
new file mode 100644
index 0000000..9e9a4a5
--- /dev/null
+++ b/lang/de/settings.php
@@ -0,0 +1,13 @@
+
+ * @author e-dschungel
+ */
+$lang['allowrename'] = 'Umbenennen von Seiten und Mediendateien diesen Gruppen und Benutzern erlauben (durch Kommas getrennt).';
+$lang['minor'] = 'Linkanpassung als kleine Änderung markieren? Kleine Änderung werden in RSS Feeds und Benachrichtigungsmails nicht aufgeführt.';
+$lang['autoskip'] = 'Automatisches Überspringen von Fehlern beim Verschieben von Namensräumen standardmäßig aktivieren. ';
+$lang['autorewrite'] = 'Automatische Linkanpassung standardmäßig aktivieren.';
+$lang['pagetools_integration'] = 'Umbenennen-Button zu Pagetools hinzufügen';
diff --git a/lang/de/tree.txt b/lang/de/tree.txt
new file mode 100644
index 0000000..a5c31d1
--- /dev/null
+++ b/lang/de/tree.txt
@@ -0,0 +1,3 @@
+====== Move-Manager =====
+
+Diese Ansicht ermöglicht die Restrukturierung von Namensräumen, Seiten und Mediendateien des Wikis per Drag'n'Drop.
diff --git a/lang/el/lang.php b/lang/el/lang.php
new file mode 100644
index 0000000..739afc1
--- /dev/null
+++ b/lang/el/lang.php
@@ -0,0 +1,65 @@
+
+ */
+$lang['menu'] = 'Μετακινείστε σελίδες και κενά ονομάτων';
+$lang['inprogress'] = '(μετακίνηση εκκρεμοτήτων)';
+$lang['treelink'] = 'Κατ\'εναλλαγή προς αυτή την απλή φόρμα μπορείτε να ανοικοδομήστε το σύμπλεγμα wiki σας χρησιμοποιώντας tree-based move manager ';
+$lang['notexist'] = 'Η σελίδα %s δεν υπάρχει';
+$lang['norights'] = 'Δεν έχετε επαρκείς άδειες να εκδώσετε %s. ';
+$lang['filelocked'] = 'Η σελίδα %s είναι κλειδωμένη. Παρακαλώ προσπαθήστε ξανά. ';
+$lang['notchanged'] = 'Ο νέος προορισμός που δόθηκε για την σελίδα %s (η τοποθεσία δεν άλλαξε).';
+$lang['exists'] = 'Η σελίδα δεν μπορεί να μετακινηθεί στο %s, ο προορισμός υπάρχει ήδη.';
+$lang['notargetperms'] = 'Δεν έχετε την άδεια να δημιουργήσετε την σελίδα %s.';
+$lang['medianotexist'] = 'Το αρχείο μέσων ενημέρωσης %s δεν υπάρχει';
+$lang['nomediarights'] = 'Δεν έχετε άδεια να καταργήσετε το %s.';
+$lang['medianotchanged'] = 'Ο νέος προορισμός που δόθηκε για την σελίδα %s (η θέση δεν άλλαξε)';
+$lang['mediaexists'] = 'Τα μέσα %s δεν μπορούν να μετακινηθούν στο %s, ο προορισμός υπάρχει ήδη.';
+$lang['nomediatargetperms'] = 'Δεν έχετε άδεια να δημιουργήσετε τον φάκελλο μέσων ενημέρωσης %s';
+$lang['indexerror'] = 'Υπήρξε σφάλμα κατά την ενημέρωση του δείκτη %s.';
+$lang['metamoveerror'] = '5Οι φάκελλοι meta της σελίδας %s δεν μπόρεσαν να μετακινηθούν';
+$lang['atticmoveerror'] = 'Οι φάκελλοι attic της σελίδας %s δεν μπόρεσαν να μετακινηθούν. Παρακαλώ μετακινήστε τους με το χέρι.';
+$lang['mediametamoveerror'] = 'Οι φάκελλοι meta του φακέλλου μέσων ενημέρωσης %s δεν μπόρεσαν να μετακινηθούν';
+$lang['mediamoveerror'] = 'Η μετακίνηση του φακέλλου μέσων ενημέρωσης %s απέτυχε';
+$lang['mediaatticmoveerror'] = 'Οι αττικοί φάκελλοι μέσων ενημέρωσης %s δεν μπόρεσαν να μετακινηθούν. Παρακαλώ μετακινήστε τους χειροκίνητα. ';
+$lang['renamed'] = 'Το όνομα της σελίδας άλλαξε από %s σε %s ';
+$lang['moved'] = 'Η σελίδα μετακινήθηκε από %s σε %s';
+$lang['move_rename'] = 'H σελίδα μετακινήθηκε και μετονομάστηκε από %s σε %s';
+$lang['delete'] = 'Διαγράφηκε μετακινώντας το plugin ';
+$lang['linkchange'] = 'Οι σύνδεσμοι άλλαξαν λόγω μια διαδικασίας μετακίνησης';
+$lang['intro'] = 'Η διαδικασία μετακίνησης δεν άρχισε ακόμη!';
+$lang['preview'] = 'Δέστε τις αλλαγές που πρέπει να γίνουν.';
+$lang['inexecution'] = 'Μια προηγούμενη κίνηση δεν ολοκληρώθηκε-χρησιμοποιείστε τα κουμπιά κάτω για να συνεχίσετε ή αφαιρέστε την διαδικασία.';
+$lang['btn_start'] = 'Έναρξη ';
+$lang['btn_continue'] = 'Συνεχίστε';
+$lang['btn_retry'] = 'Ξαναπροσπαθήσετε ';
+$lang['btn_skip'] = 'Παρακάμψετε θέμα';
+$lang['btn_abort'] = 'Αφαίρεση';
+$lang['legend'] = 'Μετακινείστε την παρούσα σελίδα ή τον χώρο ονόματος ';
+$lang['movepage'] = 'Μετακινείστε την σελίδα';
+$lang['movens'] = 'Μετακινείστε τον χώρο ονόματος ';
+$lang['dst'] = 'Νέο όνομα:';
+$lang['content_to_move'] = 'Περιεχόμενο προς μετακίνηση:';
+$lang['autoskip'] = 'Αγνοήστε τα λάθη και παραλείψετε σελίδες ή φακέλλους που δεν μπορούν να μετακινηθούν';
+$lang['autorewrite'] = 'Ξαναγράψετε συνδέσμους αφού ολοκληρωθεί η μετακίνηση';
+$lang['move_pages'] = 'Σελίδες ';
+$lang['move_media'] = 'Αρχεία μέσων ενημέρωσης';
+$lang['move_media_and_pages'] = 'Σελίδες και αρχεία μέσων επικοινωνίας';
+$lang['nodst'] = 'Δεν δόθηκε νέο όνομα';
+$lang['noaction'] = 'Δεν βρέθηκαν αλλαγές';
+$lang['renamepage'] = 'Μετονομάστε την Σελίδα ';
+$lang['cantrename'] = 'Αυτή η σελίδα δεν μπορεί να μετονομαστεί τώρα. Παρακαλώ προσπαθήστε αργότερα.';
+$lang['js']['rename'] = 'Μετονομάστε';
+$lang['js']['cancel'] = 'Ακυρώστε';
+$lang['js']['newname'] = 'Νέο όνομα :';
+$lang['js']['inprogress'] = 'μετονομασία σελίδας και προσαρμογή συνδέσμων';
+$lang['js']['complete'] = 'Η διαδικασία της μετακίνησης ολοκληρώθηκε';
+$lang['js']['renameitem'] = 'Μετονομάστε αυτό το τεμάχιο';
+$lang['js']['add'] = 'Δημιουργήστε ένα νέο χώρο ονόματος';
+$lang['js']['duplicate'] = 'Λυπάμαι, το "%s" υπάρχει ήδη σε αυτό το χώρο ονόματος';
+$lang['root'] = '{Βασικός χώρος ονόματος}';
+$lang['noscript'] = 'Αυτό το χαρακτηριστικό χρειάζεται JavaScript';
+$lang['moveinprogress'] = 'Υπάρχει μια άλλη διαδικασία μετακίνησης σε εξέλιξη τώρα, δεν μπορείτε να χρησιμοποιήστε αυτό το εργαλείο.';
diff --git a/lang/el/move.txt b/lang/el/move.txt
new file mode 100644
index 0000000..ee4f6ae
--- /dev/null
+++ b/lang/el/move.txt
@@ -0,0 +1,9 @@
+====== Μετακίνηση ======
+
+Αυτό το επιπρόσθετο σας επιτρέπει να μετακινήσετε και/ή μετονομάστε την παρούσα σελίδα ή μετακινήστε και/ή μετονομάστε τον τρέχοντα χώρο ονόματος στα πλαίσια των ακόλουθων περιορισμών:
+
+ * Πρέπει να έχετε άδεια έκδοσης για αυτήν την σελίδα/όλες τις σελίδες στο παρόντα κενό χώρο.
+ * Οι σελίδες που μετακινούνται δεν πρέπει να είναι κλειδωμένες για έκδοση.
+ * Για να μετακινήσετε μια σελίδα πρέπει επίσης να δημιουργήσετε 'άδεια' στον κενό χώρο ονομασίας.
+ * Δεν μπορείτε να μετακινήσετε μια σελίδα σε χώρο ονόματος όπου υπάρχει μια σελίδα με το ίδιο όνομα.
+Όλες οι ιστοσελίδες από και προς τις σελίδες που μετακινούνται θα ενημερωθούν για να απεικονίσουν το νέο μέρος και/ή όνομα.
diff --git a/lang/el/progress.txt b/lang/el/progress.txt
new file mode 100644
index 0000000..4fbed7d
--- /dev/null
+++ b/lang/el/progress.txt
@@ -0,0 +1,2 @@
+====== Μετακίνηση σε Εξέλιξη... ======
+Παρακαλώ κρατήστε αυτήν την σελίδα ανοικτή κατά την διάρκεια της διαδικασίας εξέλιξης.
diff --git a/lang/el/settings.php b/lang/el/settings.php
new file mode 100644
index 0000000..28d38b8
--- /dev/null
+++ b/lang/el/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Συμβάλλετε στην μετονομασία σελίδων σε αυτές τις ομάδες και χρήστες (χωρισμός με κόμμα).';
+$lang['minor'] = 'Θέλετε να σημειώσετε τις ρυθμίσεις των ιστοσελίδων ως ελάσσονες? Οι ελάσσονες αλλαγές δεν θα συμπεριληφθούν στην λίστα των τροφοδοτήσεων RSS και στις εγγραφές με email.';
+$lang['autoskip'] = 'Ενεργοποιείστε την αυτόματη παράλειψη λαθών σε αρχεία που μετακινείτε κατ\'επιλογήν.';
+$lang['autorewrite'] = 'Ενεργοποιείστε το σύνδεσμο αυτόματης επανεγγραφής μετά τις μετακινήσεις αρχείων κατ\'επιλογήν.';
+$lang['pagetools_integration'] = 'Προσθέσετε το πλήκτρο μετονομασίας στα εργαλεία σελίδων.';
diff --git a/lang/el/tree.txt b/lang/el/tree.txt
new file mode 100644
index 0000000..ce0deca
--- /dev/null
+++ b/lang/el/tree.txt
@@ -0,0 +1,2 @@
+====== Βασική Μετακίνηση =====
+Αυτή η προβολή σας επιτρέπει να ρυθμίσετε εκ νέου τις ονομασίες αρχείων Wiki, φακέλλους σελίδων και μέσων ενημέρωσης μέσω του Drag'n Drop.
diff --git a/lang/en/lang.php b/lang/en/lang.php
index 1432f5b..48a81d6 100644
--- a/lang/en/lang.php
+++ b/lang/en/lang.php
@@ -5,44 +5,84 @@
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Gary Owen <>
*/
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Page/Namespace Move/Rename...';
-$lang['desc'] = 'Page/Namespace Move/Rename Plugin';
-
-$lang['pm_notexist'] = 'This topic does not exist yet';
-$lang['pm_notstart'] = 'The Start page cannot be moved or renamed';
-$lang['pm_notwrite'] = 'You do not have sufficient rights to modify this page';
-$lang['pm_badns'] = 'Invalid characters in namespace.';
-$lang['pm_badname'] = 'Invalid characters in pagename.';
-$lang['pm_nochange'] = 'Document name and namespace are unchanged.';
-$lang['pm_existing'] = 'A document called %s already exists in %s';
-$lang['pm_root'] = '[Root namespace]';
-$lang['pm_current'] = '(Current)';
-$lang['pm_movedfrom'] = 'Document moved from ';
-$lang['pm_movedto'] = 'Document moved to ';
-$lang['pm_renamed'] = 'Page name changed from %s to %s';
-$lang['pm_moved'] = 'Page moved from %s to %s';
-$lang['pm_move_rename'] = 'Page moved and renamed from %s to %s';
-$lang['pm_norights'] = 'You have insufficient permissions to edit one or more backlinks for this document.';
-$lang['pm_tryagain'] = 'Try again latter.';
-$lang['pm_filelocked'] = 'This file is locked - ';
-$lang['pm_fileslocked'] = 'These files are locked - ';
-$lang['pm_linkchange'] = 'Links to %s changed to %s';
-$lang['pm_newname'] = 'New document name :';
-$lang['pm_newnsname'] = 'Use new Name for Namespace:';
-$lang['pm_targetns'] = 'Select new Namespace :';
-$lang['pm_newtargetns'] = 'Create a new Namespace';
-$lang['pm_movepage'] = 'Move page';
-$lang['pm_movens'] = 'Move Namespace';
-$lang['pm_previewpage'] = ' get moved to ';
-$lang['pm_previewns'] = 'All pages and namespaces in the namespace %s: get moved in the namespace';
-$lang['pm_delete'] = 'Deleted by PageMove plugin';
-$lang['pm_preview'] = 'Preview';
-$lang['pm_submit'] = 'Submit';
-?>
\ No newline at end of file
+
+$lang['menu'] = 'Move pages and namespaces';
+$lang['inprogress'] = '(move pending)';
+$lang['treelink'] = 'Alternatively to this simple form you can manage complex restructuring of your wiki using the tree-based move manager.';
+
+// page errors
+$lang['notexist'] = 'The page %s does not exist';
+$lang['norights'] = 'You have insufficient permissions to edit %s.';
+$lang['filelocked'] = 'The page %s is locked. Try again later.';
+$lang['notchanged'] = 'No new destination given for page %s (location unchanged).';
+$lang['exists'] = 'Page %s can\'t be moved to %s, the target already exists.';
+$lang['notargetperms'] = 'You don\'t have the permission to create the page %s.';
+
+// media errors
+$lang['medianotexist'] = 'The media file %s does not exist';
+$lang['nomediarights'] = 'You have insufficient permissions to delete %s.';
+$lang['medianotchanged'] = 'No new destination given for page %s (location unchanged).';
+$lang['mediaexists'] = 'Media %s can\'t be moved to %s, the target already exists.';
+$lang['nomediatargetperms'] = 'You don\'t have the permission to create the media file %s.';
+$lang['extensionchange'] = 'Extension of the new file is not the same as the original.';
+
+// system errors
+$lang['indexerror'] = 'Error while updating the search index %s';
+$lang['metamoveerror'] = 'The meta files of page %s couldn\'t be moved';
+$lang['atticmoveerror'] = 'The attic files of page %s couldn\'t be moved. Please move them manually.';
+$lang['mediametamoveerror'] = 'The meta files of the media file %s couldn\'t be moved';
+$lang['mediamoveerror'] = 'Moving the media file %s failed';
+$lang['mediaatticmoveerror'] = 'The attic files of media file %s couldn\'t be moved. Please move them manually.';
+
+// changelog summaries
+$lang['renamed'] = 'Page name changed from %s to %s';
+$lang['moved'] = 'Page moved from %s to %s';
+$lang['move_rename'] = 'Page moved and renamed from %s to %s';
+$lang['delete'] = 'Deleted by move plugin';
+$lang['linkchange'] = 'Links adapted because of a move operation';
+
+// progress view
+$lang['intro'] = 'The move operation has not been started, yet!';
+$lang['preview'] = 'Preview changes to be executed.';
+$lang['inexecution'] = 'A previous move was not completed - use the buttons below to continue or abort the execution.';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Continue';
+$lang['btn_retry'] = 'Retry item';
+$lang['btn_skip'] = 'Skip item';
+$lang['btn_abort'] = 'Abort';
+
+// Form labels
+$lang['legend'] = 'Move current page or namespace';
+$lang['movepage'] = 'Move page';
+$lang['movens'] = 'Move namespace';
+$lang['dst'] = 'New name:';
+$lang['content_to_move'] = 'Content to move:';
+$lang['autoskip'] = 'Ignore errors and skip pages or files that can\'t be moved';
+$lang['autorewrite'] = 'Rewrite links right after the move completed';
+$lang['move_pages'] = 'Pages';
+$lang['move_media'] = 'Media files';
+$lang['move_media_and_pages'] = 'Pages and media files';
+$lang['nodst'] = 'No new name given';
+$lang['noaction'] = 'There were no moves defined';
+
+// Rename feature
+$lang['renamepage'] = 'Rename Page';
+$lang['cantrename'] = 'The page can\'t be renamed right now. Please try later.';
+$lang['notallowed'] = 'You are not allowed to rename pages or media.';
+$lang['js']['rename'] = 'Rename';
+$lang['js']['cancel'] = 'Cancel';
+$lang['js']['newname'] = 'New name:';
+$lang['js']['inprogress'] = 'renaming page and adjusting links...';
+$lang['js']['complete'] = 'Move operation finished.';
+
+// Tree Manager
+$lang['root'] = '[Root namespace]';
+$lang['noscript'] = 'This feature requires JavaScript';
+$lang['moveinprogress'] = 'There is another move operation in progress currently, you can\'t use this tool right now.';
+$lang['js']['renameitem'] = 'Rename this item';
+$lang['js']['add'] = 'Create a new namespace';
+$lang['js']['duplicate'] = 'Sorry, "%s" already exists in this namespace.';
+
+// Media Manager
+$lang['js']['moveButton'] = 'Move file';
+$lang['js']['dialogIntro'] = 'Enter new file destination. You may change the namespace but not the file extension.';
diff --git a/lang/en/move.txt b/lang/en/move.txt
new file mode 100644
index 0000000..fe04cb7
--- /dev/null
+++ b/lang/en/move.txt
@@ -0,0 +1,10 @@
+====== Move ======
+
+This plugin allows you to move and/or rename the current page or move and/or rename the current namespace within the following restrictions:
+
+ * You must have edit permission for the current page/all pages in the current namespace.
+ * The pages that are moved must not be locked for editing.
+ * For moving a page you also need create permission in the target namespace.
+ * You cannot move a page to a namespace where a page with the same name already exists.
+
+All links to and from the pages that are moved will be updated to reflect the new location and/or name.
diff --git a/lang/en/pagemove.txt b/lang/en/pagemove.txt
deleted file mode 100644
index f801b3f..0000000
--- a/lang/en/pagemove.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-====== Pagemove ======
-
-This plugin allows you to move and/or rename the current document or move and/or rename the current namespace within the following restrictions.
-
- * You cannot move the start page.
- * You must have edit permission for the document and any documents that point to it (backlinks).
- * The document and its' backlinks cannot be locked for editing.
- * For moving a file you also need create permission in the target directory.
- * You cannot move the document to a namespace where a document with the same name already exists.
-
-All links to and from the page will be updated to reflect the new location and/or name.
-\\
-\\
\ No newline at end of file
diff --git a/lang/en/progress.txt b/lang/en/progress.txt
new file mode 100644
index 0000000..cd3432d
--- /dev/null
+++ b/lang/en/progress.txt
@@ -0,0 +1,3 @@
+====== Move in Progress... ======
+
+Please keep this page open while the move progresses.
\ No newline at end of file
diff --git a/lang/en/settings.php b/lang/en/settings.php
new file mode 100644
index 0000000..659f2e1
--- /dev/null
+++ b/lang/en/settings.php
@@ -0,0 +1,7 @@
+
+ */
+$lang['menu'] = 'Mover/Renombrar página...';
+$lang['inprogress'] = '(pendientes de mover)';
+$lang['treelink'] = 'Como alternativa a este sencillo formulario, puede gestionar una reestructuración compleja de su wiki utilizando el administrador para mover páginas basado en listas desplegables.';
+$lang['notexist'] = 'La página %s aún no existe';
+$lang['norights'] = 'No tiene permisos suficientes para editar %s.';
+$lang['filelocked'] = 'La página %s está bloqueada. Inténtalo de nuevo más tarde.';
+$lang['notchanged'] = 'No se ha indicado un destino nuevo para la página %s (ubicación sin cambios).';
+$lang['exists'] = 'La página %s no se puede mover a %s, ya existe el destino.';
+$lang['notargetperms'] = 'No tienes permisos para crear la página %s.';
+$lang['medianotexist'] = 'El fichero multimedia %s no existe';
+$lang['nomediarights'] = 'No tiene permisos suficientes para borrar %s.';
+$lang['medianotchanged'] = 'No se ha indicado un destino nuevo para la página %s (ubicación sin cambios).';
+$lang['mediaexists'] = 'El fichero multimedia %s no se puede mover a %s, ya existe el destino.';
+$lang['nomediatargetperms'] = 'No tienes permisos para crear el fichero multimedia %s.';
+$lang['indexerror'] = 'Error al actualizar el índice de búsqueda %s';
+$lang['metamoveerror'] = 'Los ficheros meta de la página %s no se pudieron mover';
+$lang['atticmoveerror'] = 'Los ficheros attic de la página %s no se pudieron mover. Por favor, muévalos manualmente.';
+$lang['mediametamoveerror'] = 'Los ficheros meta del fichero multimedia %s no se pudieron mover';
+$lang['mediamoveerror'] = 'Se ha producido un error al mover el fichero multimedia %s';
+$lang['mediaatticmoveerror'] = 'Los ficheros attic del fichero multimedia %s no se pudieron mover. Por favor, muévalos manualmente.';
+$lang['renamed'] = 'El nombre de la página cambió de %s a %s';
+$lang['moved'] = 'Página movida de %s a %s';
+$lang['move_rename'] = 'Página movida y renombrada de %s a %s';
+$lang['delete'] = 'Borrado por la extensión move';
+$lang['linkchange'] = 'Enlaces adaptados debido a una operación de mover';
+$lang['intro'] = '¡La operación de mover aún no se ha iniciado!';
+$lang['preview'] = 'Previsualizar los cambios a ejecutar.';
+$lang['inexecution'] = 'Una operación de mover anterior no se completó - utilice los botones de abajo para continuar o anular la ejecución.';
+$lang['btn_start'] = 'Empezar';
+$lang['btn_continue'] = 'Continuar';
+$lang['btn_retry'] = 'Reintentar elemento';
+$lang['btn_skip'] = 'Saltar elemento';
+$lang['btn_abort'] = 'Anular';
+$lang['legend'] = 'Mover la página actual o el espacio de nombres';
+$lang['movepage'] = 'Mover la página';
+$lang['movens'] = 'Mover el espacio de nombres';
+$lang['dst'] = 'Nuevo nombre:';
+$lang['content_to_move'] = 'Contenido para mover:';
+$lang['autoskip'] = 'Ignorar errores y omitir páginas o ficheros que no se pueden mover';
+$lang['autorewrite'] = 'Reescribir los enlaces justo después de completar la operación de mover';
+$lang['move_pages'] = 'Páginas';
+$lang['move_media'] = 'Ficheros multimedia';
+$lang['move_media_and_pages'] = 'Páginas y ficheros multimedia';
+$lang['nodst'] = 'No se ha dado ningún nombre nuevo';
+$lang['noaction'] = 'No hubo operaciones de mover definidas';
+$lang['renamepage'] = 'Cambiar nombre de página';
+$lang['cantrename'] = 'No se puede cambiar el nombre de la página en este momento. Inténtalo de nuevo más tarde.';
+$lang['js']['rename'] = 'Renombrar';
+$lang['js']['cancel'] = 'Cancelar';
+$lang['js']['newname'] = 'Nuevo nombre:';
+$lang['js']['inprogress'] = 'cambiar el nombre de la página y ajustar los enlaces...';
+$lang['js']['complete'] = 'La operación de mover ha finalizado.';
+$lang['js']['renameitem'] = 'Renombrar este elemento';
+$lang['js']['add'] = 'Crear un nuevo espacio de nombres';
+$lang['js']['duplicate'] = 'Lo sentimos, "%s" ya existe en este espacio de nombres.';
+$lang['root'] = '[Espacio de nombres raíz]';
+$lang['noscript'] = 'Esta función requiere JavaScript';
+$lang['moveinprogress'] = 'Hay otra operación de mover actualmente en curso, no se puede usar esta herramienta ahora mismo.';
diff --git a/lang/es/lang.php.txt b/lang/es/lang.php.txt
deleted file mode 100644
index 7a0f6e2..0000000
--- a/lang/es/lang.php.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
- * @translation Ruben Squartini <>
-*/
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Mover/Renombrar página...';
-$lang['desc'] = 'Extensión para Mover/Renombrar páginas';
-
-$lang['pm_notexist'] = 'Este tema no existe aún';
-$lang['pm_notstart'] = 'La página de Inicio no puede ser movida o renombrada';
-$lang['pm_notwrite'] = 'No tiene suficientes derechos para modificar esta página';
-$lang['pm_badns'] = 'Caractéres inválidos en el Espacio de Nombres.';
-$lang['pm_badname'] = 'Caractéres inválidos en el nombre de la página.';
-$lang['pm_nochange'] = 'Los cambios en el nombre de la página y del espacio no fueron cambiados.';
-$lang['pm_existing1'] = 'Un documento llamado ';
-$lang['pm_existing2'] = ' ya existe en ';
-$lang['pm_root'] = '[Espacio de Nombres Raiz]';
-$lang['pm_current'] = '(Actual)';
-$lang['pm_movedfrom'] = 'El documento fue movido de ';
-$lang['pm_movedto'] = 'El documento fue movido a ';
-$lang['pm_norights'] = 'No tiene suficientes permisos para editar uno o mas de los punteros a este documento.';
-$lang['pm_tryagain'] = 'Trate de nuevo en unos minutos.';
-$lang['pm_filelocked'] = 'Este documento está bloqueado - ';
-$lang['pm_fileslocked'] = 'Estos documentos están bloqueados - ';
-$lang['pm_linkchange1'] = 'Enlaces a ';
-$lang['pm_linkchange2'] = ' cambiados a ';
-$lang['pm_newname'] = 'Nombre del nuevo documento :';
-$lang['pm_targetns'] = 'Seleccione el Espacio de Nombres de Destino :';
-$lang['pm_submit'] = 'Enviar';
-?>
-
\ No newline at end of file
diff --git a/lang/es/move.txt b/lang/es/move.txt
new file mode 100644
index 0000000..68cdcec
--- /dev/null
+++ b/lang/es/move.txt
@@ -0,0 +1,10 @@
+====== Mover ======
+
+Esta extensión le permite mover y/o renombrar el documento actual respetando las siguientes restricciones.
+
+ * Debe tener permisos de edición sobre la página actual.
+ * La página actual no puede ser bloqueada para la edición.
+ * Para mover una página también necesita permiso de creación sobre el directorio de destino.
+ * No puede mover la página a un espacio de nombres donde ya existe una página con el mismo nombre.
+
+Todos los enlaces desde y hacia esta página serán actualizados para reflejar la nueva ubicación y/o nombre.
diff --git a/lang/es/pagemove.txt.txt b/lang/es/pagemove.txt.txt
deleted file mode 100644
index 421d26d..0000000
--- a/lang/es/pagemove.txt.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-====== Pagemove ======
-
-Esta extensión le permite mover y/o renombrar el documento actual respetando las siguientes restricciones.
-
- * No puede mover la página de Inicio.
- * Debe tener permisos de edición sobre el documento actual y sobre todos los documentos que enlazan con éste.
- * El documento actual y los que hacen referencia a éste no pueden ser bloqueados para la edición.
- * Para mover un documento también necesita permiso de creación sobre el directorio de destino.
- * No puede mover el documento a un Espacio de Nombres donde ya existe un documento con el mismo nombre.
-
-Todos los enlaces desde y hacia esta página serán actualizados para reflejar la nueva ubicación y/o nombre.
-\\
-\\
\ No newline at end of file
diff --git a/lang/es/progress.txt b/lang/es/progress.txt
new file mode 100644
index 0000000..ae6caef
--- /dev/null
+++ b/lang/es/progress.txt
@@ -0,0 +1,3 @@
+====== Mover en progreso... ======
+
+Por favor, mantenga esta página abierta mientras progresa la operación de mover.
\ No newline at end of file
diff --git a/lang/es/settings.php b/lang/es/settings.php
new file mode 100644
index 0000000..332caa6
--- /dev/null
+++ b/lang/es/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Permitir el cambio de nombre de páginas a estos grupos y usuarios (separados por comas).';
+$lang['minor'] = '¿Marcar los ajustes de enlace como cambios menores? Los cambios menores no se incluirán en los feeds RSS ni en los correos de suscripción.';
+$lang['autoskip'] = 'Habilitar de forma predeterminada el salto automático de errores en las operaciones de mover el espacio de nombres.';
+$lang['autorewrite'] = 'Habilitar de forma predeterminada la reescritura automática de enlaces después de mover el espacio de nombres.';
+$lang['pagetools_integration'] = 'Añadir botón de cambio de nombre a las herramientas de página';
diff --git a/lang/es/tree.txt b/lang/es/tree.txt
new file mode 100644
index 0000000..6c30939
--- /dev/null
+++ b/lang/es/tree.txt
@@ -0,0 +1,3 @@
+====== Administrador de mover =====
+
+Esta interfaz le permite reorganizar los espacios de nombres, páginas y ficheros multimedia de su wiki a través de arrastrar y soltar (Drag'n'Drop).
\ No newline at end of file
diff --git a/lang/fr/lang.php b/lang/fr/lang.php
index d239c32..8e9242a 100644
--- a/lang/fr/lang.php
+++ b/lang/fr/lang.php
@@ -1,40 +1,71 @@
- * @translator Clement Hermann (nodens)
+ *
+ * @author Schplurtz le Déboulonné
+ * @author Gary Owen
+ * @author Nicolas Friedli
*/
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Déplacer/Renommer la page...';
-$lang['desc'] = 'Plugin de Déplacement/Renommage';
-
-$lang['pm_notexist'] = 'Ce sujet n\'existe pas encore';
-$lang['pm_notstart'] = 'La page de démarrage (Start) ne peut être déplacée ou renommée';
-$lang['pm_notwrite'] = 'Vous ne disposez pas des droits suffisants pour modifier cette page';
-$lang['pm_badns'] = "Caractères invalides dans l'espace de nommage.";
-$lang['pm_badname'] = 'Caractères invalides dans le nom de la page.';
-$lang['pm_nochange'] = "Le nom du document et l'espace de nomage sont inchangés.";
-$lang['pm_existing1'] = 'Un document appelé ';
-$lang['pm_existing2'] = ' existe déjà dans ';
-$lang['pm_root'] = '[Espace de nom racine]';
-$lang['pm_current'] = '(Courant)';
-$lang['pm_movedfrom'] = 'Document déplacé depuis ';
-$lang['pm_movedto'] = 'Document déplacé vers ';
-$lang['pm_norights'] = 'Vos permissions sont insufisante pour éditer un ou plusieurs liens pointant vers cette page.';
-$lang['pm_tryagain'] = 'Réessayez plus tard.';
-$lang['pm_filelocked'] = 'Ce fichier est verrouillé - ';
-$lang['pm_fileslocked'] = 'Ces fichiers sont verrouillés - ';
-$lang['pm_linkchange1'] = 'Les liens vers ';
-$lang['pm_linkchange2'] = ' ont été changés vers ';
-$lang['pm_newname'] = 'Nouveau nom du document :';
-$lang['pm_targetns'] = 'Sélectionnez le nouvel espace de nommage :';
-
-?>
+$lang['menu'] = 'Déplacer/Renommer pages et catégories...';
+$lang['inprogress'] = '(déplacement en attente)';
+$lang['treelink'] = 'Au lieu de ce simple formulaire, vous pouvez effectuer des restructurations complexes de votre wiki en utilisant le gestionnaire de déplacements par arborescence';
+$lang['notexist'] = 'La page %s n\'existe pas encore';
+$lang['norights'] = 'Vos permissions sont insufisante pour éditer %s.';
+$lang['filelocked'] = 'La page %s est verrouillée - réessayez plus tard.';
+$lang['notchanged'] = 'Pas de nouvelle destination pour la page %s (emplacement inchangé).';
+$lang['exists'] = 'La page %s ne peut être déplacée vers %s, la cible existe déjà.';
+$lang['notargetperms'] = 'Pous n\'avez pas la permission de créer la page %s.';
+$lang['medianotexist'] = 'Le fichier média %s n\'existe pas.';
+$lang['nomediarights'] = 'Vous ne disposez pas des permissions suffisantes pour effacer %s.';
+$lang['medianotchanged'] = 'Pas de nouvelle destination pour la page %s (emplacement inchangé).';
+$lang['mediaexists'] = 'Impossible de déplacer le média %s. la cible %s existe déjà.';
+$lang['nomediatargetperms'] = 'Vous n\'avez pas les permissions de créer le média %s';
+$lang['extensionchange'] = 'L\'extension du nouveau fichier diffère de celle de l\'original.';
+$lang['indexerror'] = 'Erreur lors de la mise à jour de l\'index de recherche %s.';
+$lang['metamoveerror'] = 'Impossible de déplacer les métafichiers de la page %s.';
+$lang['atticmoveerror'] = 'L\'historique (attic) de la page %s ne peut être déplacé. Veuillez déplacer les fichiers manuellement.';
+$lang['mediametamoveerror'] = 'Impossible de déplacer les métafichiers du media %s.';
+$lang['mediamoveerror'] = 'Échec du déplacement du média %s.';
+$lang['mediaatticmoveerror'] = 'L\'historique (attic) du média %s ne peut être déplacé. Veuillez déplacer les fichiers manuellement.';
+$lang['renamed'] = 'Nom de la page changé de %s à %s';
+$lang['moved'] = 'Page déplacée de %s à %s';
+$lang['move_rename'] = 'Page déplacée et renommée de %s à %s';
+$lang['delete'] = 'Effacé par le plugin de déplacement';
+$lang['linkchange'] = 'Liens modifiés en raison d\'un déplacement.';
+$lang['intro'] = 'L\'opération de déplacement n\'a pas encore commencé.';
+$lang['preview'] = 'Visualiser les changements à effectuer.';
+$lang['inexecution'] = 'Un déplacement n\'est pas arrivé à son terme - utilisez les boutons ci-dessous pour continuer ou abandonner son exécution.';
+$lang['btn_start'] = 'Commencer';
+$lang['btn_continue'] = 'Continuer';
+$lang['btn_retry'] = 'Réessayer cette étape';
+$lang['btn_skip'] = 'Passer cette étape';
+$lang['btn_abort'] = 'Abandonner';
+$lang['legend'] = 'Déplacer la page ou l\'espace de nom courant';
+$lang['movepage'] = 'Déplacer la page';
+$lang['movens'] = 'Déplacer l\'espace de nom';
+$lang['dst'] = 'Nouveau nom:';
+$lang['content_to_move'] = 'Contenu à déplacer:';
+$lang['autoskip'] = 'Ignorer les erreurs et passer les pages ou fichiers qui ne peuvent être déplacés.';
+$lang['autorewrite'] = 'Récrire les liens à l\'issue du déplacement.';
+$lang['move_pages'] = 'Pages';
+$lang['move_media'] = 'Fichiers média';
+$lang['move_media_and_pages'] = 'Pages et fichiers média';
+$lang['nodst'] = 'Pas de nouveau nom donné';
+$lang['noaction'] = 'Pas de déplacement demandé';
+$lang['renamepage'] = 'Renommer la page';
+$lang['cantrename'] = 'Impossible de renommer la page maintenant. Veuillez essayer plus tard.';
+$lang['notallowed'] = 'Vous n\'êtes pas autorisé à renommer les pages ou les médias.';
+$lang['js']['rename'] = 'Renommer';
+$lang['js']['cancel'] = 'Annuler';
+$lang['js']['newname'] = 'Nouveau nom:';
+$lang['js']['inprogress'] = 'Renommage des pages et ajustement des pages en cours...';
+$lang['js']['complete'] = 'Déplacement effectué.';
+$lang['js']['renameitem'] = 'Renommer cet élément';
+$lang['js']['add'] = 'Créer une nouvelle catégorie';
+$lang['js']['duplicate'] = 'Désolé, "%s" existe dans cette catégorie.';
+$lang['js']['moveButton'] = 'Déplacer le fichier.';
+$lang['js']['dialogIntro'] = 'Entrez le nouvel emplacement du fichier. Vous pouvez changer la catégorie, mais pas l\'extension.';
+$lang['root'] = '[Catégorie racine]';
+$lang['noscript'] = 'Cette fonction requiert JavaScript';
+$lang['moveinprogress'] = 'Une opération de déplacement est en cours en ce moment. Vous ne pouvez pas utiliser cet outil maintenant.';
diff --git a/lang/fr/move.txt b/lang/fr/move.txt
new file mode 100644
index 0000000..b4c4a85
--- /dev/null
+++ b/lang/fr/move.txt
@@ -0,0 +1,10 @@
+====== Move ======
+
+Cette extension permet de déplacer et/ou renommer le document courant ou la catégorie courante, avec les limitations suivantes :
+
+ * Il faut avoir les droits d'édition sur le document/tous les documents de la catégorie
+ * Les documents ne doivent pas être verrouillés pour l'édition
+ * Pour déplacer une page il faut également disposer du droit de création dans la catégorie de destination
+ * Il est impossible de déplacer le document vers une catégorie où un document du même nom existe déjà.
+
+Tous les liens depuis et vers la page seront mis à jour pour refléter le nouvel emplacement et/ou le nouveau nom.
diff --git a/lang/fr/pagemove.txt b/lang/fr/pagemove.txt
deleted file mode 100644
index e9306df..0000000
--- a/lang/fr/pagemove.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-====== Pagemove ======
-
-Ce plugin permet de déplacer et/ou renommer le document courant, avec les limitations suivantes :
- * Il est impossible de déplacer la page d'accueil (start)
- * Il faut avoir les droits d'édition sur le document et sur chaque document qui pointe dessus (backlinks)
- * Le document et les liens pointant dessus ne peuvent être verrouillés pour l'édition
- * Pour déplacer une page il faut également disposer du droit de création dans le répertoire de destination
- * Il est impossible de déplacer le document vers un espace de nommage où un document du même nom existe déjà.
-
-Tous les liens depuis et vers la page seront mis à jour pour refléter le nouvel emplacement et/ou le nouveau nom.
-
-\\
-\\
\ No newline at end of file
diff --git a/lang/fr/progress.txt b/lang/fr/progress.txt
new file mode 100644
index 0000000..51b56a8
--- /dev/null
+++ b/lang/fr/progress.txt
@@ -0,0 +1,3 @@
+====== Déplacements en cours... ======
+
+Veuillez garder cette page ouverte pendant toute la durée du déplacement.
\ No newline at end of file
diff --git a/lang/fr/settings.php b/lang/fr/settings.php
new file mode 100644
index 0000000..19918ff
--- /dev/null
+++ b/lang/fr/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Autoriser le renommage de page à ces utilisateurs et groupes (utiliser des virgules)';
+$lang['minor'] = 'Noter les ajustements de liens comme des modifications mineures. Les modifications mineures ne sont indiquées ni dans les flux RSS, ni dans les courriels des abonnements.';
+$lang['autoskip'] = 'Par défaut, ignorer les erreurs de déplacement de catégories.';
+$lang['autorewrite'] = 'Activer par défaut la ré-écriture automatique des liens après déplacement de catégories.';
+$lang['pagetools_integration'] = 'Ajouter un bouton "renommage" aux outils de page';
diff --git a/lang/fr/tree.txt b/lang/fr/tree.txt
new file mode 100644
index 0000000..0f537ee
--- /dev/null
+++ b/lang/fr/tree.txt
@@ -0,0 +1,3 @@
+====== Gestionnaire déplacement =====
+
+Cette interface vous permet de réorganiser catégories, pages et médias de votre wiki par glisser-déposer.
\ No newline at end of file
diff --git a/lang/hr/lang.php b/lang/hr/lang.php
new file mode 100644
index 0000000..ad9f222
--- /dev/null
+++ b/lang/hr/lang.php
@@ -0,0 +1,65 @@
+
+ */
+$lang['menu'] = 'Premještanje stranica i imenskih prostora';
+$lang['inprogress'] = '(čekam premještanje)';
+$lang['treelink'] = 'Umjesto ovog jednostavnog oblika možete raditi kompleksnu reorganizaciju vašeg wiki-a korištenjem vizualnog upravitelja pomicanja';
+$lang['notexist'] = 'Stranica %s ne postoji';
+$lang['norights'] = 'Nemate dovoljne ovlasti za uređivanje %s.';
+$lang['filelocked'] = 'Stranica %s je zaključana. Pokušajte kasnije.';
+$lang['notchanged'] = 'Za stranicu %s nije dana nova lokacija (lokacija nije promijenjena)';
+$lang['exists'] = 'Stranica %s ne može biti pomaknuta na %s, odredište već postoji.';
+$lang['notargetperms'] = 'Nemate ovlaštenja da kreirate stranicu %s.';
+$lang['medianotexist'] = 'Medijska datoteka %s ne postoji';
+$lang['nomediarights'] = 'Nemate dovoljna prava da obrišete %s.';
+$lang['medianotchanged'] = 'Za stranicu %s nije dana nova lokacija (lokacija nije promijenjena)';
+$lang['mediaexists'] = 'Medijska datoteka %s ne može biti pomaknuta na %s, odredište već postoji.';
+$lang['nomediatargetperms'] = 'Nemate ovlaštenja da kreirate medijsku datoteku %s.';
+$lang['indexerror'] = 'Greška pri izmjeni indeksa pretrage %s';
+$lang['metamoveerror'] = 'Meta datoteke stranice %s ne mogu biti premještene';
+$lang['atticmoveerror'] = 'Arhiva stranice (attic datoteke) %s ne može biti premještena. Molimo premjestite ju ručno.';
+$lang['mediametamoveerror'] = 'Meta podaci medijske datoteke %s ne mogu biti premještene';
+$lang['mediamoveerror'] = 'Neuspješno premještanje medijske datoteke %s';
+$lang['mediaatticmoveerror'] = 'Arhiva (attic) medijske datoteke %s ne može biti premještena. Molimo premjestite ju ručno.';
+$lang['renamed'] = 'Ime stranice promijenjeno iz %s u %s';
+$lang['moved'] = 'Stranica premještena iz %s u %s';
+$lang['move_rename'] = 'Stranica premještena i preimenovana iz %s u %s';
+$lang['delete'] = 'Obrisano od strane dodatka za premještanje';
+$lang['linkchange'] = 'Poveznice prilagođene zbog operacije premještanja';
+$lang['intro'] = 'Operacija premještanja još nije počela!';
+$lang['preview'] = 'Pregled izmjena koje će biti izvršene.';
+$lang['inexecution'] = 'Prethodno premještanje nije završeno - upotrijebite tipke ispod za nastavak ili prekid izvršavanja.';
+$lang['btn_start'] = 'Kreni';
+$lang['btn_continue'] = 'Nastavi';
+$lang['btn_retry'] = 'Ponovi stavku';
+$lang['btn_skip'] = 'Preskoči stavku';
+$lang['btn_abort'] = 'Prekini';
+$lang['legend'] = 'Premjesti trenutnu stranicu ili imenski prostor';
+$lang['movepage'] = 'Premjesti stranicu';
+$lang['movens'] = 'Premjesti imenski prostor';
+$lang['dst'] = 'Novo ime:';
+$lang['content_to_move'] = 'Sadržaj za premjestiti:';
+$lang['autoskip'] = 'Zanemari greške i preskoči stranice ili datoteke koje ne mogu biti premještene';
+$lang['autorewrite'] = 'Izmijeniti poveznice odmah nakon završetka premještanja';
+$lang['move_pages'] = 'Stranice';
+$lang['move_media'] = 'Medijske datoteke';
+$lang['move_media_and_pages'] = 'Stranice i medijske datoteke';
+$lang['nodst'] = 'Nije uneseno novo ime';
+$lang['noaction'] = 'Nema definiranih premještanja';
+$lang['renamepage'] = 'Preimenuj stranicu';
+$lang['cantrename'] = 'Stranica trenutno ne može biti preimenovana. Molimo pokušajte kasnije.';
+$lang['js']['rename'] = 'Preimenuj';
+$lang['js']['cancel'] = 'Prekini';
+$lang['js']['newname'] = 'Novo ime:';
+$lang['js']['inprogress'] = 'preimenovanje stranice i prilagodba poveznica...';
+$lang['js']['complete'] = 'Operacija premještanja završila.';
+$lang['js']['renameitem'] = 'Preimenuj ovu stavku';
+$lang['js']['add'] = 'Kreiraj novi imenski prostor';
+$lang['js']['duplicate'] = 'Isprika ali "%s" već postoji u ovom imenskom prostoru';
+$lang['root'] = '[Korijen imenskog prostora]';
+$lang['noscript'] = 'Ova osobina zahtijeva JavaScript';
+$lang['moveinprogress'] = 'Trenutno je druga operacija premještanja u tijeku, zasada ne možete koristiti ovaj alat.';
diff --git a/lang/hr/move.txt b/lang/hr/move.txt
new file mode 100644
index 0000000..a939a69
--- /dev/null
+++ b/lang/hr/move.txt
@@ -0,0 +1,10 @@
+====== Premjesti ======
+
+Ovaj dodatak omogućava premještanje i/ili preimenovanje trenutne stranice ili imenskog prostora unutar slijedećih ograničenja:
+
+ * Morate imate dopuštenje za izmjenu trenutne ili svih stranicau trenutnom imenskom prostoru.
+ * Stranice koje se premještaju ne smiju biti zaključane.
+ * Za premještanje stranice također morate imati ovlaštenje za kreiranje u odredišnom imenskom prostoru.
+ * Na možete premjestiti stranicu u imenski prostor gdje stranica s istim imenom već postoji.
+
+Sve poveznice na ili sa stranice koja se premješta biti će preusmjerene da pokazuju na novu lokaciju i/ili ime.
diff --git a/lang/hr/progress.txt b/lang/hr/progress.txt
new file mode 100644
index 0000000..e585c72
--- /dev/null
+++ b/lang/hr/progress.txt
@@ -0,0 +1,3 @@
+====== Premještanje u tijeku... ======
+
+Molimo držite ovu stranicu otvorenom dok traje premještanje.
\ No newline at end of file
diff --git a/lang/hr/settings.php b/lang/hr/settings.php
new file mode 100644
index 0000000..fac20b6
--- /dev/null
+++ b/lang/hr/settings.php
@@ -0,0 +1,13 @@
+
+ */
+$lang['allowrename'] = 'Omogući preimenovanje stranica ovim grupama i korisnicima (zarezom odvojena lista).';
+$lang['minor'] = 'Označiti prilagodbe poveznica kao manje značajne? Takve promjene neće biti prikazane u RSS obavijestima i obavijesnim porukama.';
+$lang['autoskip'] = 'Omogući kao podrazumijevano automatsko preskakanje grešaka pri premještanju imenskog prostora.';
+$lang['autorewrite'] = 'Omogući kao podrazumijevano automatsku prilagodbu poveznica.
+';
+$lang['pagetools_integration'] = 'Dodaj tipku za preimenovanje na alate stranice';
diff --git a/lang/hr/tree.txt b/lang/hr/tree.txt
new file mode 100644
index 0000000..36b6db5
--- /dev/null
+++ b/lang/hr/tree.txt
@@ -0,0 +1,4 @@
+====== Upravitelj premještanja =====
+
+Ovo sučelje omogućava reorganizaciju wiki imenskih prostora, stranica i medijskih datoteka korištenjem Povuci-i-Spusti radnje.
+
diff --git a/lang/id/lang.php b/lang/id/lang.php
new file mode 100644
index 0000000..55a9df3
--- /dev/null
+++ b/lang/id/lang.php
@@ -0,0 +1,62 @@
+
+ */
+$lang['menu'] = 'pindahkan halaman dan ruangnama';
+$lang['notexist'] = 'Halaman %s tidak ada';
+$lang['norights'] = 'Anda tidak memiliki izin untuk menyunting %s.';
+$lang['filelocked'] = 'Halaman %s telah dikunci. Coba lagi nanti.';
+$lang['notchanged'] = 'Tujuan baru tidak diberikan untuk halaman %s (lokasi halaman tidak berubah)';
+$lang['exists'] = 'Halaman %s tidak dapat dipindahkan ke %s, target telah ada.';
+$lang['notargetperms'] = 'Anda tidak memiliki izin untuk membuat halaman %s.';
+$lang['medianotexist'] = 'Berkas %s tidak ada';
+$lang['nomediarights'] = 'Anda tidak memiliki izin untuk menghapus %s.';
+$lang['medianotchanged'] = 'Tidak ada tujuan yang dipilih untuk halaman %s (lokasi halaman tidak berubah).';
+$lang['mediaexists'] = 'Media %s tidak dapat dipindahkan ke %s, target telah ada.';
+$lang['nomediatargetperms'] = 'Anda tidak memiliki izin untuk mengunggah berkas %s.';
+$lang['indexerror'] = 'Galat ketika memperbaharui indeks pencarian %s';
+$lang['metamoveerror'] = 'Berkas meta halaman %s tidak dapat dipindahkan';
+$lang['atticmoveerror'] = 'File attic halaman %s tidak dapat dipindahkan. Mohon pindahkan secara manual.';
+$lang['mediametamoveerror'] = 'Berkas meta dari berkas media tidak dapat dipindahkan';
+$lang['mediamoveerror'] = 'Gagal memindahkan berkas media %s';
+$lang['mediaatticmoveerror'] = 'File attic dari berkas media %s tidak dapat dipindahkan. Mohon pindahkan secara manual.';
+$lang['renamed'] = 'Nama halaman diubah dari %s menjadi %s';
+$lang['moved'] = 'Halaman dipindahkan dari %s ke %s';
+$lang['move_rename'] = 'Halaman dipindahkan dan diubah dari %s menjadi %s';
+$lang['delete'] = 'Dihapus oleh move plugin';
+$lang['linkchange'] = 'Pranala diubah karena operasi pemindahan';
+$lang['intro'] = 'Operasi pemindahan belum dimulai!';
+$lang['preview'] = 'Sedang mengeksekusi pratinjau perubahan.';
+$lang['inexecution'] = 'Pemindahan sebelumnya belum selesai - gunakan tombol dibawah untuk melanjutkan atau membatalkan pemindahan.';
+$lang['btn_start'] = 'Mulai';
+$lang['btn_continue'] = 'Lanjutkan';
+$lang['btn_retry'] = 'Coba butir';
+$lang['btn_skip'] = 'Lewati butir';
+$lang['btn_abort'] = 'Batalkan';
+$lang['legend'] = 'Pindahkan halaman atau ruangnama terkini';
+$lang['movepage'] = 'Pindahkan halaman';
+$lang['movens'] = 'Pindahkan ruangnama';
+$lang['dst'] = 'Nama baru:';
+$lang['content_to_move'] = 'Konten untuk dipindahkan:';
+$lang['autoskip'] = 'Abaikan galat dan lewati halaman atau berkas yang tidak dapat dipindahkan';
+$lang['autorewrite'] = 'Ubah pranala setelah pemindahan selesai';
+$lang['move_pages'] = 'Halaman';
+$lang['move_media'] = 'Berkas media';
+$lang['move_media_and_pages'] = 'Halaman dan berkas';
+$lang['nodst'] = 'Tidak ada nama baru diberikan';
+$lang['renamepage'] = 'Ubah nama halaman';
+$lang['cantrename'] = 'Nama halaman tidak dapat diubah saat ini. Coba lagi nanti.';
+$lang['js']['rename'] = 'Ubah nama';
+$lang['js']['cancel'] = 'Batal';
+$lang['js']['newname'] = 'Nama baru:';
+$lang['js']['inprogress'] = 'mengubah nama halaman dan menyesuaikan pranala...';
+$lang['js']['complete'] = 'Pemindahan selesai.';
+$lang['js']['renameitem'] = 'Ubah nama item ini';
+$lang['js']['add'] = 'Buat ruangnama baru';
+$lang['js']['duplicate'] = 'Maaf, %s telah ada di ruangnama ini.';
+$lang['root'] = '[Ruang nama root]';
+$lang['noscript'] = 'Fitur ini membutuhkan JavaScript';
+$lang['moveinprogress'] = 'Ada operasi pemindahan lain yang belum selesai, Anda tidak dapat menggunakan alat ini sekarang.';
diff --git a/lang/id/progress.txt b/lang/id/progress.txt
new file mode 100644
index 0000000..fdf7613
--- /dev/null
+++ b/lang/id/progress.txt
@@ -0,0 +1,3 @@
+====== Pemindahan dalam proses... ======
+
+Mohon tetap di halaman ini saat pemindahan sedang diproses.
\ No newline at end of file
diff --git a/lang/id/settings.php b/lang/id/settings.php
new file mode 100644
index 0000000..d277e4b
--- /dev/null
+++ b/lang/id/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Izinkan pengguna dan grup ini untuk mengubah nama halaman (pisahkan dengan koma).';
+$lang['minor'] = 'Tandai penyesuaian pranala sebagai perubahan kecil? Perubahan kecil tidak akan terlihat di feed RSS dan milis.';
+$lang['autoskip'] = 'Aktifkan lewati otomatis galat saat memindahkan ruangnama secara default.';
+$lang['autorewrite'] = 'Aktifkan pengubahan pranala secara default saat memindahkan ruangnama.';
+$lang['pagetools_integration'] = 'Tambahkan tombol ubah nama ke pagetools';
diff --git a/lang/id/tree.txt b/lang/id/tree.txt
new file mode 100644
index 0000000..3a9cc4c
--- /dev/null
+++ b/lang/id/tree.txt
@@ -0,0 +1,7 @@
+====== Move Manager =====
+
+Antarmuka ini mengizinkan Anda untuk mengurutkan ulang ruangnama, halaman, dan berkas media dengan geser dan lepas.
+
+Untuk memindahkan ruangnama, halaman, atau berkas sekaligus ke tujuan yang sama, Anda dapat menggunakan kotak centang sebagaimana dibawah ini:
+ * klik kotak centang pada ruangnama, halaman, atau berkas yang ingin Anda pindahkan;
+ * pindahkan satu dari butir yang telah dipilih ke tujuan yang diinginkan, semua butir yang dipilih secara otomatis akan dipindahkan ke tujuan tersebut.
\ No newline at end of file
diff --git a/lang/it/lang.php b/lang/it/lang.php
new file mode 100644
index 0000000..fa96a3a
--- /dev/null
+++ b/lang/it/lang.php
@@ -0,0 +1,38 @@
+
+ * @author Dario Sguassero
+ */
+$lang['menu'] = 'Pagina/Namespace Sposta/Rinomina...';
+$lang['inprogress'] = '(in sospeso)';
+$lang['treelink'] = 'In alternativa a questo semplice modulo puoi gestire la ristrutturazione complessa della tua wiki usando il tree-based gestore di movimento basato su albero .';
+$lang['notexist'] = 'La pagina %s non esiste';
+$lang['norights'] = 'Non hai permessi sufficienti per modificare %s.';
+$lang['filelocked'] = 'La pagina %s è bloccata. Riprova più tardi.';
+$lang['notchanged'] = 'Nessuna nuova destinazione indicata per la pagina %s (posizione invariata).';
+$lang['exists'] = 'La pagina% s non può essere spostata su %s, la destinazione esiste già.';
+$lang['notargetperms'] = 'Non hai permessi sufficienti per creare la pagina %s.';
+$lang['medianotexist'] = 'Il file media %s non esiste';
+$lang['nomediarights'] = 'Non hai permessi sufficienti per cancellare %s.';
+$lang['medianotchanged'] = 'Nessuna nuova destinazione indicata per la pagina %s (posizione invariata).';
+$lang['mediaexists'] = 'Il supporto%s non può essere spostato su %s, la destinazione esiste già.';
+$lang['nomediatargetperms'] = 'Non hai i permessi per creare il file media %s.';
+$lang['indexerror'] = 'Errore durante l\'aggiornamento dell\'indice di ricerca %s';
+$lang['metamoveerror'] = 'I meta file della pagina %s non possono essere spostati';
+$lang['renamed'] = 'Il nome della pagina è stato cambiato da %s a %s';
+$lang['moved'] = 'Pagina spostata da %s a %s';
+$lang['move_rename'] = 'Pagina spostata e rinominata da %s a %s';
+$lang['delete'] = 'Cancellato da move (sposta) plugin';
+$lang['linkchange'] = 'Collegamento modificati a causa di un\'operazione di spostamento';
+$lang['movepage'] = 'Sposta la pagina';
+$lang['movens'] = 'Sposta il namespace';
+$lang['content_to_move'] = 'Contenuto da spostare';
+$lang['move_pages'] = 'Pagine';
+$lang['move_media'] = 'File media';
+$lang['move_media_and_pages'] = 'Pagine e file media';
+$lang['root'] = '[Namespace radice]';
diff --git a/lang/it/move.txt b/lang/it/move.txt
new file mode 100644
index 0000000..fa67e5f
--- /dev/null
+++ b/lang/it/move.txt
@@ -0,0 +1,10 @@
+====== Move ======
+
+Questo plugin ti permette di spostare e/o rinominare la pagina corrente o muovere e/o rinominare il namespace corrente, con le seguenti restrizioni:
+
+ * Devi avere i permessi di modificare la pagina corrente/tutte le pagine nel namespace corrente.
+ * Le pagine che verranno mosse non devono essere bloccate per un richiesta di modifica.
+ * Per muovere un pagina hai anche bisogno di creare i permessi nel namespace di destinazione.
+ * Non puoi muovere una pagina verso un namespace dove una pagina con lo stesso nome già esiste.
+
+Tutti i collegamenti verso e dalle pagine che verranno mosse saranno aggiornate in coerenza con la nuova posizione e/o nome.
diff --git a/lang/ja/lang.php b/lang/ja/lang.php
new file mode 100644
index 0000000..8b3adf8
--- /dev/null
+++ b/lang/ja/lang.php
@@ -0,0 +1,65 @@
+
+ */
+$lang['menu'] = 'ページ・名前空間の移動';
+$lang['inprogress'] = '(移動保留中)';
+$lang['treelink'] = 'この簡単なフォームの代わりに、ツリー型の移動管理を利用して Wiki の複雑な再構築を管理できます。';
+$lang['notexist'] = '%s ページは存在しません。';
+$lang['norights'] = '%s の編集権限がありません。';
+$lang['filelocked'] = '%s ページはロックされています。後で再実行して下さい。';
+$lang['notchanged'] = '%s ページの移動先が指定されていません(変更なし)。';
+$lang['exists'] = '%s ページは %s に移動できません。既に存在します。';
+$lang['notargetperms'] = '%s ページの作成権限がありません。';
+$lang['medianotexist'] = '%s メディアファイルは存在しません。';
+$lang['nomediarights'] = '%s の削除権限がありません。';
+$lang['medianotchanged'] = '%s メディアファイルの移動先が指定されていません(変更なし)。';
+$lang['mediaexists'] = '%s メディアファイルは %s に移動できません。既に存在します。';
+$lang['nomediatargetperms'] = '%s メディアファイルの作成権限がありません。';
+$lang['indexerror'] = '%s 検索インデックス更新中にエラーが発生しました。';
+$lang['metamoveerror'] = '%s ページの meta ファイルが移動できませんでした。';
+$lang['atticmoveerror'] = '%s ページの attic ファイルが移動できませんでした。手動で移動して下さい';
+$lang['mediametamoveerror'] = '%s メディアファイルの meta ファイルが移動できませんでした。';
+$lang['mediamoveerror'] = '%s メディアファイルが移動できませんでした。';
+$lang['mediaatticmoveerror'] = '%s メディアファイルの attic ファイルが移動できませんでした。手動で移動して下さい。';
+$lang['renamed'] = '%s から %s へページを名称変更しました。';
+$lang['moved'] = '%s から %s へページを移動しました。';
+$lang['move_rename'] = '%s から %s へページの移動と名称変更しました。';
+$lang['delete'] = '移動プラグインが削除しました。';
+$lang['linkchange'] = '移動操作に合わせてリンクを書き換えました。';
+$lang['intro'] = '移動操作はまだ開始していません!';
+$lang['preview'] = '実行する変更を事前確認して下さい。';
+$lang['inexecution'] = '前の移動が完了していません。処理の続行・中止を下のボタンで指定して下さい。';
+$lang['btn_start'] = '開始';
+$lang['btn_continue'] = '続行';
+$lang['btn_retry'] = '再実行';
+$lang['btn_skip'] = 'スキップ';
+$lang['btn_abort'] = '中止';
+$lang['legend'] = '現在のページ・名前空間の移動';
+$lang['movepage'] = 'ページの移動';
+$lang['movens'] = '名前空間の移動';
+$lang['dst'] = '移動先: ';
+$lang['content_to_move'] = '移動する内容: ';
+$lang['autoskip'] = 'エラーを無視し、移動できないページ・ファイルをスキップします。';
+$lang['autorewrite'] = '移動完了後にリンクを書き換えます。';
+$lang['move_pages'] = 'ページ';
+$lang['move_media'] = 'メディアファイル';
+$lang['move_media_and_pages'] = 'ページとメディアファイル';
+$lang['nodst'] = '移動先が指定されていません。';
+$lang['noaction'] = '移動操作が指定されていません。';
+$lang['renamepage'] = 'ページの名称変更';
+$lang['cantrename'] = 'ページを名称変更できません。後で再実行して下さい。';
+$lang['js']['rename'] = '名称変更';
+$lang['js']['cancel'] = '中止';
+$lang['js']['newname'] = '新ページ名: ';
+$lang['js']['inprogress'] = 'ページの名称変更・リンクの書き換え中…';
+$lang['js']['complete'] = '名称変更操作が完了しました。';
+$lang['js']['renameitem'] = 'この項目を名称変更します。';
+$lang['js']['add'] = '新しい名前空間の作成';
+$lang['js']['duplicate'] = '"%s" はこの名前空間内に既に存在します。';
+$lang['root'] = '[ルート名前空間]';
+$lang['noscript'] = 'この機能には JavaScriptが必要です。';
+$lang['moveinprogress'] = '別の移動操作を処理中なので、今はこのツールを使用できません。';
diff --git a/lang/ja/move.txt b/lang/ja/move.txt
new file mode 100644
index 0000000..8004a86
--- /dev/null
+++ b/lang/ja/move.txt
@@ -0,0 +1,10 @@
+====== 移動 ======
+
+このプラグインは、以下の制約の範囲内で、今いるページの移動・名称変更、今いる名前空間の移動・名称変更が可能です:
+
+ * 今いるページ/今いる名前空間内の全ページに対する編集権限があること
+ * 移動するページが編集ロックされていないこと
+ * ページ移動のために、移動先の名前空間に対する作成権限があること
+ * 移動先の名前空間内に同じ名前のページが存在しないこと
+
+移動したページとの間の全てのリンクは、新しい場所・新しい名前を反映して更新されます。
\ No newline at end of file
diff --git a/lang/ja/progress.txt b/lang/ja/progress.txt
new file mode 100644
index 0000000..aeca181
--- /dev/null
+++ b/lang/ja/progress.txt
@@ -0,0 +1,3 @@
+====== 移動処理中・・・ ======
+
+移動処理中は、このページを閉じないで下さい。
diff --git a/lang/ja/settings.php b/lang/ja/settings.php
new file mode 100644
index 0000000..7a31427
--- /dev/null
+++ b/lang/ja/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'ページ名変更を許可するグループ・ユーザー(カンマ区切り)';
+$lang['minor'] = 'リンクの書き換えを小変更扱いにする。小変更にすると RSS 配信・メール更新通知には表示されない。';
+$lang['autoskip'] = '名前空間移動時のエラーの自動スキップ。デフォルトは有効。';
+$lang['autorewrite'] = '名前空間移動後のリンクの自動書き換え。デフォルトは有効。';
+$lang['pagetools_integration'] = '名称変更ボタンのページツールへの追加';
diff --git a/lang/ja/tree.txt b/lang/ja/tree.txt
new file mode 100644
index 0000000..7bf9059
--- /dev/null
+++ b/lang/ja/tree.txt
@@ -0,0 +1,3 @@
+====== 移動管理 =====
+
+この画面では、Wiki 内の名前空間・ページ・メディアファイルをドラッグ&ドロップで再配置することができます。
\ No newline at end of file
diff --git a/lang/ko/lang.php b/lang/ko/lang.php
new file mode 100644
index 0000000..3396dcd
--- /dev/null
+++ b/lang/ko/lang.php
@@ -0,0 +1,67 @@
+
+ * @author Jaemin Kim
+ */
+$lang['menu'] = '문서와 이름공간 이동';
+$lang['inprogress'] = '(이동 보류 중)';
+$lang['treelink'] = '다른 방법으로 이 간단한 양식은 트리 기반의 이동 관리자를 사용하여 위키의 복잡한 구조 조정을 관리할 수 있습니다.';
+$lang['notexist'] = '%s 문서가 존재하지 않습니다';
+$lang['norights'] = '%s 문서를 편집할 충분하지 않은 권한이 있습니다.';
+$lang['filelocked'] = '%s 문서가 잠겨 있습니다. 나중에 다시 시도해주세요.';
+$lang['notchanged'] = '%s 문서에 주어진 새 대상이 없습니다. (위치가 바뀌지 않음)';
+$lang['exists'] = '%s 문서는 %s 문서로 이동할 수 없으며, 대상이 이미 존재합니다.';
+$lang['notargetperms'] = '%s 문서를 만들 권한이 없습니다.';
+$lang['medianotexist'] = '%s 미디어 파일이 존재하지 않습니다';
+$lang['nomediarights'] = '%s 문서를 삭제할 충분하지 않은 권한이 있습니다.';
+$lang['medianotchanged'] = '%s 문서에 주어진 새 대상이 없습니다. (위치가 바뀌지 않음)';
+$lang['mediaexists'] = '%s 미디어는 %s 미디어로 이동할 수 없으며, 대상이 이미 존재합니다.';
+$lang['nomediatargetperms'] = '%s 미디어 파일을 만들 권한이 없습니다.';
+$lang['indexerror'] = '%s 검색 색인을 업데이트하는 동안 오류';
+$lang['metamoveerror'] = '%s 문서의 메타 파일을 이동할 수 없습니다';
+$lang['atticmoveerror'] = '%s 문서의 첨부 파일을 이동할 수 없습니다. 수동으로 이동해주시기 바랍니다.';
+$lang['mediametamoveerror'] = '%s 미디어 파일의 메타 파일을 이동할 수 없습니다';
+$lang['mediamoveerror'] = '%s 미디어 파일을 이동하는 데 실패했습니다';
+$lang['mediaatticmoveerror'] = '%s 문서의 첨부 파일을 이동할 수 없습니다. 수동으로 이동해주시기 바랍니다.';
+$lang['renamed'] = '문서 이름이 %s에서 %s(으)로 바뀌었습니다';
+$lang['moved'] = '문서가 %s에서 %s(으)로 이동되었습니다';
+$lang['move_rename'] = '문서가 %s에서 %s(으)로 이동되고 이름이 바뀌었습니다';
+$lang['delete'] = 'move 플러그인에 의해 삭제됨';
+$lang['linkchange'] = '링크가 이동 작업으로 인해 적응했습니다';
+$lang['intro'] = '이동 작업은 아직 시작되지 않았습니다!';
+$lang['preview'] = '실행할 바뀜을 미리 봅니다.';
+$lang['inexecution'] = '이전 이동이 완료되지 않았습니다 - 실행을 계속하거나 중단하려면 아래 버튼을 사용하세요.';
+$lang['btn_start'] = '시작';
+$lang['btn_continue'] = '계속';
+$lang['btn_retry'] = '항목 다시 시도';
+$lang['btn_skip'] = '항목 건너뛰기';
+$lang['btn_abort'] = '중단';
+$lang['legend'] = '현재 문서 또는 이름공간 이동';
+$lang['movepage'] = '문서 이동';
+$lang['movens'] = '이름공간 이동';
+$lang['dst'] = '새 이름:';
+$lang['content_to_move'] = '이동할 내용:';
+$lang['autoskip'] = '오류를 무시하고 이동할 수 없는 문서나 파일을 건너뛰기';
+$lang['autorewrite'] = '이동을 완료하고 나서 링크를 올바르게 다시 쓰기';
+$lang['move_pages'] = '문서';
+$lang['move_media'] = '미디어 파일';
+$lang['move_media_and_pages'] = '문서와 미디어 파일';
+$lang['nodst'] = '주어진 새 이름이 없습니다';
+$lang['noaction'] = '정의한 이동이 없습니다';
+$lang['renamepage'] = '문서 이름 바꾸기';
+$lang['cantrename'] = '문서는 지금 바로 이름을 바꿀 수 없습니다. 나중에 시도해주시기 바랍니다.';
+$lang['js']['rename'] = '이름 바꾸기';
+$lang['js']['cancel'] = '취소';
+$lang['js']['newname'] = '새 이름:';
+$lang['js']['inprogress'] = '문서의 이름을 바꾸고 링크를 조절하는 중...';
+$lang['js']['complete'] = '이동 작업이 완료되었습니다.';
+$lang['js']['renameitem'] = '이 항목 이름 바꾸기';
+$lang['js']['add'] = '새 이름공간 만들기';
+$lang['js']['duplicate'] = '죄송하지만, "%s" 문서는 이미 이 이름공간에 존재합니다.';
+$lang['root'] = '[루트 이름공간]';
+$lang['noscript'] = '이 기능은 자바스크립트가 필요합니다';
+$lang['moveinprogress'] = '현재 진행 중인 다른 이동 작업이 있으므로, 지금 바로 이 도구를 사용할 수 없습니다.';
diff --git a/lang/ko/move.txt b/lang/ko/move.txt
new file mode 100644
index 0000000..17b1885
--- /dev/null
+++ b/lang/ko/move.txt
@@ -0,0 +1,10 @@
+====== 이동 ======
+
+이 플러그인은 다음 제한 안에서 현재 문서를 이동하고/거나 이름을 바꾸거나 현재 이름공간을 이동하고/거나 이름을 바꿀 수 있습니다:
+
+ * 현재 이름공간에서 현재 문서/모든 문서에 대한 편집 권한이 있어야 합니다.
+ * 이동되는 문서는 편집을 위해 잠겨 있지 않아야 합니다.
+ * 문서 이동을 위해서는 대상 이름공간에서 만들기 권한도 필요합니다.
+ * 같은 이름으로 된 문서가 이미 존재하는 이름공간으로 문서를 이동할 수 없습니다.
+
+기존에서 새로 이동하는 문서의 모든 링크는 새 위치와/나 이름을 반영하도록 바뀝니다.
\ No newline at end of file
diff --git a/lang/ko/progress.txt b/lang/ko/progress.txt
new file mode 100644
index 0000000..e2f3705
--- /dev/null
+++ b/lang/ko/progress.txt
@@ -0,0 +1,3 @@
+====== 진행 중인 이동... ======
+
+이동을 진행하는 동안 이 문서를 열어두시기 바랍니다.
\ No newline at end of file
diff --git a/lang/ko/settings.php b/lang/ko/settings.php
new file mode 100644
index 0000000..6c88357
--- /dev/null
+++ b/lang/ko/settings.php
@@ -0,0 +1,13 @@
+
+ * @author Jaemin Kim
+ */
+$lang['allowrename'] = '문서의 이름을 바꿀 수 있는 그룹 및 사용자. (쉼표로 구분)';
+$lang['minor'] = '링크 조절을 사소한 바뀜으로 표시하겠습니까? 사소한 바뀜은 RSS 피드와 구독 메일에 나열되지 않을 것입니다.';
+$lang['autoskip'] = '기본적으로 이름공간 이동에서 오류를 자동으로 건너뛰도록 활성화합니다.';
+$lang['autorewrite'] = '기본적으로 이름공간을 이동하고 나서 자동으로 링크 다시 쓰기를 활성화합니다.';
+$lang['pagetools_integration'] = '페이지도구에 이름 새로짓기 버튼 추가하기';
diff --git a/lang/ko/tree.txt b/lang/ko/tree.txt
new file mode 100644
index 0000000..51b8780
--- /dev/null
+++ b/lang/ko/tree.txt
@@ -0,0 +1,3 @@
+====== 이동 관리자 =====
+
+이 인터페이스는 드래그 앤 드롭을 통해 위키의 이름공간, 문서와 미디어 파일을 재배열할 수 있습니다.
\ No newline at end of file
diff --git a/lang/lv/lang.php b/lang/lv/lang.php
index 5d4f6c5..b870863 100644
--- a/lang/lv/lang.php
+++ b/lang/lv/lang.php
@@ -5,35 +5,27 @@
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Aivars Miška <>
*/
-
+
// settings must be present and set appropriately for the language
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
-
+
// for admin plugins, the menu prompt to be displayed in the admin menu
// if set here, the plugin doesn't need to override the getMenuText() method
$lang['menu'] = 'Lapas pārdēvēšana/pārvietošana...';
$lang['desc'] = 'Page Move/Rename Plugin';
-$lang['pm_notexist'] = 'This topic does not exist yet';
-$lang['pm_notstart'] = 'Ievadlapu nevar nedz pārdēvēt, nedz pārvietot';
-$lang['pm_notwrite'] = 'Tev nav tiesību labot šo lapu';
-$lang['pm_badns'] = 'Neatļautas rakstzīmes nodaļas vārdā.';
-$lang['pm_badname'] = 'Neatļautas rakstzīmes lapas vārdā.';
-$lang['pm_nochange'] = 'Vārds un nodaļa nav mainīti.';
-$lang['pm_existing1'] = 'Dokuments ';
-$lang['pm_existing2'] = ' jau ir nodaļā ';
-$lang['pm_root'] = '[Saknes direktorija]';
-$lang['pm_current'] = '(Tekošais)';
-$lang['pm_movedfrom'] = 'Dokuments pārvietots no ';
-$lang['pm_movedto'] = 'Dokuments pārvietos uz ';
-$lang['pm_norights'] = 'Tev nav tiesību labot kādu lapu, kas norāda uz šo dokumentu.';
-$lang['pm_tryagain'] = 'Mēģini vēlāk vēlreiz.';
-$lang['pm_filelocked'] = 'Bloķēts fails ';
-$lang['pm_fileslocked'] = 'Bloķēti faili ';
-$lang['pm_linkchange1'] = 'Norādes uz ';
-$lang['pm_linkchange2'] = ' nomainītas ar ';
-$lang['pm_newname'] = 'Jaunais dokumenta vārds :';
-$lang['pm_targetns'] = 'Nodaļa, kurp pārvietot :';
-$lang['pm_submit'] = 'Darīt!';
-?>
+//$lang['notexist'] = 'This topic does not exist yet';
+$lang['notwrite'] = 'Tev nav tiesību labot šo lapu';
+$lang['badns'] = 'Neatļautas rakstzīmes nodaļas vārdā.';
+$lang['badname'] = 'Neatļautas rakstzīmes lapas vārdā.';
+$lang['nochange'] = 'Vārds un nodaļa nav mainīti.';
+$lang['existing1'] = 'Dokuments %s jau ir nodaļā %s';
+$lang['root'] = '[Saknes direktorija]';
+$lang['current'] = '(Tekošais)';
+//$lang['norights'] = 'Tev nav tiesību labot kādu lapu, kas norāda uz šo dokumentu.';
+$lang['filelocked'] = 'Bloķēts fails - mēģini vēlāk vēlreiz.';
+$lang['linkchange1'] = 'Norādes uz %s nomainītas ar %s';
+$lang['newname'] = 'Jaunais dokumenta vārds :';
+$lang['targetns'] = 'Nodaļa, kurp pārvietot :';
+$lang['submit'] = 'Darīt!';
diff --git a/lang/lv/pagemove.txt b/lang/lv/move.txt
similarity index 56%
rename from lang/lv/pagemove.txt
rename to lang/lv/move.txt
index 2507792..23ea936 100644
--- a/lang/lv/pagemove.txt
+++ b/lang/lv/move.txt
@@ -1,14 +1,11 @@
====== Lapas pārvietošana ======
-Šis modulis ļauj pārvietot un/vai pārdēvēt tekošo lapu. \\
+Šis modulis ļauj pārvietot un/vai pārdēvēt tekošo lapu.
Ar nosacījumiem:
- * Nevar pārvietot ievadlapu.
- * Tev vajag tiesības labot šo lapu un visas lapas, kas uz to norāda.
- * Šī lapa un lapas, kas uz to norāda, nav bloķētas labošanai.
+ * You must have edit permission for the current page/all pages in the current namespace.
+ * The pages that are moved must not be locked for editing.
* Lai pārvietotu, tev vajag attiecīgās tiesības mērķa direktorijā.
* Nevar pārvietot lapu uz nodaļu, kurā jau ir šāda nosaukuma lapa.
Visas saites uz šo lapu un no tās tiks automātiski nomainītas atbilstoši jaunajam lapas nosaukumam un/vai vietai.
-\\
-\\
\ No newline at end of file
diff --git a/lang/nl/lang.php b/lang/nl/lang.php
index f50471e..8a11125 100644
--- a/lang/nl/lang.php
+++ b/lang/nl/lang.php
@@ -1,39 +1,68 @@
+ *
+ * @author Gerrit Uitslag
+ * @author Gary Owen
+ * @author Andre Dierick
+ * @author Peter van Diest
*/
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Pagina Verplaatsen/Hernoemen...';
-$lang['desc'] = 'Pagina Verplaatsen/Hernoemen Plugin';
-
-$lang['pm_notexist'] = 'Dit onderwerp bestaat nog niet';
-$lang['pm_notstart'] = 'De startpagina kan niet worden verplaatst of hernoemd';
-$lang['pm_notwrite'] = 'U heeft niet voldoende rechten om deze pagina te wijzigen';
-$lang['pm_badns'] = 'De namespace bevat een niet-toegestaan teken.';
-$lang['pm_badname'] = 'De paginanaam bevat een niet-toegestaan teken.';
-$lang['pm_nochange'] = 'De paginanaam en -namespace zijn ongewijzigd.';
-$lang['pm_existing1'] = 'Het document met naam ';
-$lang['pm_existing2'] = ' bestaat al in ';
-$lang['pm_root'] = '[Hoofdnamespace]';
-$lang['pm_current'] = '(Huidig)';
-$lang['pm_movedfrom'] = 'Document verplaatst van ';
-$lang['pm_movedto'] = 'Document verplaatst naar ';
-$lang['pm_norights'] = 'U heeft niet voldoende rechten om een of meerdere referenties aan te passen.';
-$lang['pm_tryagain'] = 'Probeer later opnieuw.';
-$lang['pm_filelocked'] = 'Deze file is geblokkeerd - ';
-$lang['pm_fileslocked'] = 'Deze files zijn geblokeerd - ';
-$lang['pm_linkchange1'] = 'Gelinkt aan ';
-$lang['pm_linkchange2'] = ' gewijzigd naar ';
-$lang['pm_newname'] = 'Nieuwe naam document:';
-$lang['pm_targetns'] = 'Selecteer namespace van het doel:';
-
-?>
+$lang['menu'] = 'Pagina Verplaatsen/Hernoemen...';
+$lang['inprogress'] = '(verplaatsen verwacht)';
+$lang['treelink'] = 'Als alternatief voor dit eenvoudig formulier kun je complexere herstructurering aan met de tree-based move manager.';
+$lang['notexist'] = 'De pagina %s bestaat niet';
+$lang['norights'] = 'U hebt onvoldoende permissies om %s aan te passen';
+$lang['filelocked'] = 'Pagina %s is geblokkeerd. Probeer het later nog eens.';
+$lang['notchanged'] = 'Geen nieuwe bestemming gegeven voor pagina %s (locatie onveranderd).';
+$lang['exists'] = 'Pagina %s kan niet worden verplaatst naar %s, het doel bestaat al';
+$lang['notargetperms'] = 'U heeft niet voldoende permissies om pagina %s aan te maken';
+$lang['medianotexist'] = 'Het media bestand %s bestaat niet';
+$lang['nomediarights'] = 'U heeft niet voldoende permissies om pagina %s te verwijderen';
+$lang['medianotchanged'] = 'Geen nieuwe bestemming gegeven voor pagina %s (locatie onveranderd).';
+$lang['mediaexists'] = 'Media %s kan niet worden verplaatst naar %s, het doel bestaat al.';
+$lang['nomediatargetperms'] = 'U heeft niet voldoende permissies om het media bestand %s aan te maken ';
+$lang['indexerror'] = 'Fout bij updaten van zoekindex %s';
+$lang['metamoveerror'] = 'De metabestanden van pagina %s konden niet worden verplaatst';
+$lang['atticmoveerror'] = 'De atticbestanden van pagina %s konden niet worden verplaatst. Verplaats ze handmatig.';
+$lang['mediametamoveerror'] = 'De metabestanden van mediabestand %s konden niet worden verplaatst';
+$lang['mediamoveerror'] = 'Verplaatsen van het mediabestand %s is mislukt';
+$lang['mediaatticmoveerror'] = 'De atticbestanden van mediabestand %s konden niet worden verplaatst. Verplaats ze handmatig.';
+$lang['renamed'] = 'Pagina naam van %s gewijzigd in %s';
+$lang['moved'] = 'Pagina verplaatst van %s naar %s';
+$lang['move_rename'] = 'Pagina verplaatst en hernoemd van %s naar %s';
+$lang['delete'] = 'Verwijderd door move plugin';
+$lang['linkchange'] = 'Links aangepast vanwege een verplaatsing';
+$lang['intro'] = 'De verplaatsing is nog niet begonnen!';
+$lang['preview'] = 'Bekijk de wijzigingen die worden uitgevoerd.';
+$lang['inexecution'] = 'Een vorige verplaatsing was niet gereed - gebruik onderstaande knoppen om door te gaan of te stoppen.';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Ga door';
+$lang['btn_retry'] = 'Probeer opnieuw voor dit item';
+$lang['btn_skip'] = 'Sla item over';
+$lang['btn_abort'] = 'Breek af';
+$lang['legend'] = 'Verplaats huidige pagina of namespace';
+$lang['movepage'] = 'Verplaats pagina';
+$lang['movens'] = 'Verplaats namespace';
+$lang['dst'] = 'Nieuwe naam:';
+$lang['content_to_move'] = 'Te verplaatsen inhoud:';
+$lang['autoskip'] = 'Negeer fouten en sla pagina\'s of bestanden die niet kunnen worden verplaatst over.';
+$lang['autorewrite'] = 'Herschrijf links meteen nadat de verplaatsing gereed is';
+$lang['move_pages'] = 'Pagina\'s';
+$lang['move_media'] = 'Mediabestanden';
+$lang['move_media_and_pages'] = 'Pagina\'s en mediabestanden';
+$lang['nodst'] = 'Geen nieuwe naam opgegeven';
+$lang['noaction'] = 'Er zijn geen verplaatsingen gedefinieerd';
+$lang['renamepage'] = 'Hernoem Pagina';
+$lang['cantrename'] = 'De pagina kan momenteel niet worden hernoemd. Probeer het later nog eens.';
+$lang['js']['rename'] = 'Hernoem';
+$lang['js']['cancel'] = 'Annuleren';
+$lang['js']['newname'] = 'Nieuwe naam:';
+$lang['js']['inprogress'] = 'pagina hernoemen en links aanpassen...';
+$lang['js']['complete'] = 'Verplaatsing compleet.';
+$lang['js']['renameitem'] = 'Hernoem dit item';
+$lang['js']['add'] = 'Maak een nieuwe namespace';
+$lang['js']['duplicate'] = 'Sorry, "%s" bestaat al in deze namespace.';
+$lang['root'] = '[Hoofdnamespace]';
+$lang['noscript'] = 'Deze mogelijkheid vereist Javascript';
+$lang['moveinprogress'] = 'Er is een andere verplaatsingsactie gaande, gebruik van deze tool is momenteel niet mogelijk.';
diff --git a/lang/nl/move.txt b/lang/nl/move.txt
new file mode 100644
index 0000000..0528d81
--- /dev/null
+++ b/lang/nl/move.txt
@@ -0,0 +1,10 @@
+====== Pagina's verplaatsen ======
+
+Deze plugin stelt je in staat pagina's te verplaatsen en/of te hernoemen binnen volgende beperkingen:
+
+ * Je moet edit-permissie hebben voor de huidige pagina/alle pagina's in de huidige namespace.
+ * De te verplaatsen pagina's mogen niet geblokkeerd zijn vanwege aanpassing.
+ * Om pagina's te verplaatsen heb je ook schrijfrechten nodig in de doelfolder.
+ * Je kunt een pagina niet plaatsen naar een namespace waar al een pagina bestaat met dezelfde naam
+
+Alle links van en naar de pagina zullen geupdated worden naar de nieuwe naam/locatie.
diff --git a/lang/nl/pagemove.txt b/lang/nl/pagemove.txt
deleted file mode 100644
index 5184031..0000000
--- a/lang/nl/pagemove.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-====== Pagina's verplaatsen ======
-
-Deze plugin stelt je in staat pagina's te verplaatsen en/of te hernoemen binnen volgende beperkingen:
- * Het is onmogelijk om de startpagina te verplaatsen
- * Je moest toestemming hebben om wijzigingen aan te brengen aan al de documenten die verwijzen naar de te verplaatsen pagina (backlinks)
- * De pagina en zijn referenties kunnen niet geblokkeerd worden tijdens het verplaatsen
- * Om pagina's te verplaatsen heb je ook schrijfrechten nodig in de folder van bestemming
- * Je kan een pagina niet plaatsen in een namespace waar al een pagina bestaat met dezelfde naam
-Al de links van en naar de pagina zullen geupdated worden naar de nieuwe naam/locatie.
\ No newline at end of file
diff --git a/lang/nl/progress.txt b/lang/nl/progress.txt
new file mode 100644
index 0000000..73f404d
--- /dev/null
+++ b/lang/nl/progress.txt
@@ -0,0 +1,3 @@
+====== Bezig met verplaatsing... ======
+
+Houd deze pagina open zolang de verplaatsing bezig is.
\ No newline at end of file
diff --git a/lang/nl/settings.php b/lang/nl/settings.php
new file mode 100644
index 0000000..e5b49be
--- /dev/null
+++ b/lang/nl/settings.php
@@ -0,0 +1,13 @@
+
+ * @author Peter van Diest
+ */
+$lang['allowrename'] = 'Hernoemen van pagina\'s toestaan voor deze groepen en gebruikers (komma-gescheiden).';
+$lang['minor'] = 'Link-aanpassingen als kleine aanpassingen markeren? Kleine aanpassingen worden niet genoemd in RRS feeds en nieuwsbrieven.';
+$lang['autoskip'] = 'Automatisch overslaan van fouten bij verplaatsingen van namespaces standaard inschakelen.';
+$lang['autorewrite'] = 'Automatisch herschrijven van links na verplaatsingen van namespaces standaard inschakelen.';
+$lang['pagetools_integration'] = 'Voeg een \'Hernoem Pagina\' knop toe aan het paginamenu';
diff --git a/lang/nl/tree.txt b/lang/nl/tree.txt
new file mode 100644
index 0000000..a8bc657
--- /dev/null
+++ b/lang/nl/tree.txt
@@ -0,0 +1,3 @@
+====== Move Manager =====
+
+Met dit interface kun je de namespaces, pagina's en mediabestanden verplaatsen door te slepen.
\ No newline at end of file
diff --git a/lang/no/lang.php b/lang/no/lang.php
new file mode 100644
index 0000000..4fd7235
--- /dev/null
+++ b/lang/no/lang.php
@@ -0,0 +1,69 @@
+
+ * @author Daniel Raknes
+ * @author Christopher Schive
+ * @author Daniel Raknes
+ * @author Arne Hanssen
+ */
+$lang['menu'] = 'Flytt sider og navnerom';
+$lang['inprogress'] = '(flytting avventer)';
+$lang['treelink'] = 'I tillegg til dette enkle skjemaet kan du også utføre kompleks omstrukturering av din wiki ved å bruke tree-based move manager.';
+$lang['notexist'] = 'Siden %s eksisterer ikke';
+$lang['norights'] = 'Du har ikke nok rettigheter til å endre %s.';
+$lang['filelocked'] = 'Side %s er låst. Prøv igjen senere.';
+$lang['notchanged'] = 'Det er ikke lagt inn nytt lagringssted for %s (lagringsstedet er uendret).';
+$lang['exists'] = 'Siden %s kan ikke flyttes til %s, mål finnes allerede.';
+$lang['notargetperms'] = 'Du har ikke rettigheter til å lage siden %s.';
+$lang['medianotexist'] = 'Media-filen %s eksisterer ikke';
+$lang['nomediarights'] = 'Du har ikke rettigheter til å slette %s.';
+$lang['medianotchanged'] = 'Ingen ny plassering er gitt for siden %s (uendret plassering).';
+$lang['mediaexists'] = 'Media %s kan ikke flyttes til %s da den allerede eksisterer.';
+$lang['nomediatargetperms'] = 'Du har ikke tillatelse til å opprette mediafilen %s.';
+$lang['indexerror'] = 'Feil ved oppdatering av søkeindeks %s';
+$lang['metamoveerror'] = 'Metafilene for siden %s kunne ikke flyttes.';
+$lang['atticmoveerror'] = 'Revisjonsfiler til siden %s kunne ikke flyttes. Vennligst flytt dem manuelt.';
+$lang['mediametamoveerror'] = 'Metafiler for mediafilen %s kunne ikke flyttes';
+$lang['mediamoveerror'] = 'Flytting av mediafilAktive automatisk link en %s feilet';
+$lang['mediaatticmoveerror'] = 'Revisjonsfiler til mediafilen %s kunne ikke flyttes. Vennligst flytt dem manuelt.';
+$lang['renamed'] = 'Sidenavn endret fra %s til %s';
+$lang['moved'] = 'Siden ble flyttet fra %s til %s';
+$lang['move_rename'] = 'Side flyttet og endret navn fra %s til %s';
+$lang['delete'] = 'Slettet av move plugin';
+$lang['linkchange'] = 'Lenke endret på grunn av flyttingen';
+$lang['intro'] = 'Flyttingen har ikke startet ennå!';
+$lang['preview'] = 'Forhåndsvis endringer som skal utføres.';
+$lang['inexecution'] = 'En tidligere flytting var ikke avsluttet - bruk knappene under for å fortsette eller avbryt flyttingen.';
+$lang['btn_start'] = 'Start';
+$lang['btn_continue'] = 'Fortsett';
+$lang['btn_retry'] = 'Prøv på nytt';
+$lang['btn_skip'] = 'Hopp over';
+$lang['btn_abort'] = 'Avbryt';
+$lang['legend'] = 'Flytt gjeldende side eller navnerom';
+$lang['movepage'] = 'Flytt side';
+$lang['movens'] = 'Flytt navnerom';
+$lang['dst'] = 'Nytt navn:';
+$lang['content_to_move'] = 'Innhold som skal flyttes:';
+$lang['autoskip'] = 'Ignorer feil og utelat sider eller filer som ikke kan flyttes';
+$lang['autorewrite'] = 'Oppdater lenker etter at flyttingen er avsluttet';
+$lang['move_pages'] = 'Sider';
+$lang['move_media'] = 'Mediafiler';
+$lang['move_media_and_pages'] = 'Sider og mediafiler';
+$lang['nodst'] = 'Intet nytt navn er gitt';
+$lang['noaction'] = 'Det var ikke angitt noen flyttinger';
+$lang['renamepage'] = 'Gi side nytt navn';
+$lang['cantrename'] = 'Siden kan ikke endre navn akkurat nå. Prøv igjen senere.';
+$lang['js']['rename'] = 'Gi nytt navn';
+$lang['js']['cancel'] = 'Avbryt';
+$lang['js']['newname'] = 'Nytt navn:';
+$lang['js']['inprogress'] = 'endrer navn på side og justerer lenker...';
+$lang['js']['complete'] = 'Flytting avsluttet';
+$lang['js']['renameitem'] = 'Endre navn ';
+$lang['js']['add'] = 'Lag et nytt navnerom';
+$lang['js']['duplicate'] = 'Beklager, "%s" finnes allerede i dette navnerommet.';
+$lang['root'] = '[Rot navnerom]';
+$lang['noscript'] = 'Denne funksjonen krever Javascript';
+$lang['moveinprogress'] = 'En annen flyttingsjobb pågår for øyeblikket så denne funksjonen kan ikke brukes akkurat nå. ';
diff --git a/lang/no/move.txt b/lang/no/move.txt
new file mode 100644
index 0000000..b056d99
--- /dev/null
+++ b/lang/no/move.txt
@@ -0,0 +1,10 @@
+====== Flytt ======
+
+Denne programutvidelse lar deg flytte og/eller endre navn på siden og/eller endre navn må navnerom med følgende restriksjoner:
+
+ * Du må ha redigeringsrettigheter til siden/sidene i navnerommet.
+ * Sidene som flyttes må ikke være låst for redigering.
+ * Du må også ha redigeringsrettigheter til navnerommet siden/sidene flyttes til.
+ * Du kan ikke flytte en side til et navnerom som inneholder en side med samme navn.
+
+Alle lenker til og fra siden vil bli oppdatert ved flytting.
diff --git a/lang/no/progress.txt b/lang/no/progress.txt
new file mode 100644
index 0000000..62cf791
--- /dev/null
+++ b/lang/no/progress.txt
@@ -0,0 +1,3 @@
+====== Flytting pågår... ======
+
+Vennligst hold denne siden åpen mens flyttingen pågår.
\ No newline at end of file
diff --git a/lang/no/settings.php b/lang/no/settings.php
new file mode 100644
index 0000000..2a36cee
--- /dev/null
+++ b/lang/no/settings.php
@@ -0,0 +1,13 @@
+
+ * @author Arne Hanssen
+ */
+$lang['allowrename'] = 'Tillatt navenendring på disse sidene for disse gruppene og brukerne (separert med komma).';
+$lang['minor'] = 'Merk lenkejusteringer som liten endring? Små endringer vil ikke bli listet i RSS eller varslet til de som abonnerer på endringseposter. ';
+$lang['autoskip'] = 'Tillatt automatisk ignorering av feil i navnerom som standard.';
+$lang['autorewrite'] = 'Tillatt automatisk lenkeoppdatering etter flytting av navnerom som standard.';
+$lang['pagetools_integration'] = 'Legg til knapp for å endre navn i sideverktøyet';
diff --git a/lang/no/tree.txt b/lang/no/tree.txt
new file mode 100644
index 0000000..c2e9f21
--- /dev/null
+++ b/lang/no/tree.txt
@@ -0,0 +1,3 @@
+====== Move Manager =====
+
+Denne dialogen tillater deg å flytte din wikis navnerom, sider og mediafiler ved hjelp av «dra og slipp».
\ No newline at end of file
diff --git a/lang/pl/lang.php b/lang/pl/lang.php
new file mode 100644
index 0000000..ede7877
--- /dev/null
+++ b/lang/pl/lang.php
@@ -0,0 +1,39 @@
+
+ * @auther Wojciech Kazimierczak
+ */
+
+// settings must be present and set appropriately for the language
+$lang['encoding'] = 'utf-8';
+$lang['direction'] = 'ltr';
+
+// for admin plugins, the menu prompt to be displayed in the admin menu
+// if set here, the plugin doesn't need to override the getMenuText() method
+$lang['menu'] = 'Przenieś / Zmień nazwę strony';
+$lang['desc'] = 'Wtyczka Przenieś/Zmień nazwę';
+
+//$lang['notexist'] = 'Ten temat jeszcze nie istnieje';
+$lang['notwrite'] = 'Niewystarczające uprawnienia by wykonać operację';
+$lang['badns'] = 'Nieprawidłowy znak w nazwie katalogu';
+$lang['badname'] = 'Nieprawidłowy znak w nazwie dokumentu';
+$lang['nochange'] = 'Nazwa dokumentu i katalogu niezmieniona';
+$lang['existing'] = 'Dokument o nazwie %s już istnieje %s';
+$lang['root'] = '[katalog bazowy]';
+$lang['current'] = '(Bieżący)';
+//$lang['norights'] = 'Nie masz wystarczających uprawnień do zmiany co najmniej jednego z linków do tej strony';
+$lang['filelocked'] = 'Ten plik jest zablokowany - spróbuj ponownie później';
+$lang['linkchange1'] = 'Linki do %s zmienione na %s';
+$lang['newname'] = 'Nowa nazwa dokumentu:';
+$lang['targetns'] = 'Nazwa docelowego katalogu:';
+$lang['submit'] = 'Zmień';
+
+$lang['notallowed'] = 'Brak uprawnień do wykonania zmiany.';
+$lang['extensionchange'] = 'Rozszerzenia plików nie są identyczne.';
+
+// Media Manager
+$lang['js']['moveButton'] = 'Przenieś/Zmień nazwę';
+$lang['js']['dialogIntro'] = 'Nowa nazwa pliku. Możesz zmienić katalog, ale nie rozszerzenie pliku.';
diff --git a/lang/pl/lang.php.txt b/lang/pl/lang.php.txt
deleted file mode 100644
index 436efce..0000000
--- a/lang/pl/lang.php.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
- * @translator Wojciech Kazimierczak
-
- */
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Przenieś / Zmień nazwę strony';
-$lang['desc'] = 'Wtyczka Przenieś/Zmień nazwę';
-
-$lang['pm_notexist'] = 'Ten temat jeszcze nie istnieje';
-$lang['pm_notstart'] = 'Strona start nie może być przeniesiona ani przemianowana';
-$lang['pm_notwrite'] = 'Niewystarczające uprawnienia by wykonać operację';
-$lang['pm_badns'] = 'Nieprawidłowy znak w nazwie katalogu';
-$lang['pm_badname'] = 'Nieprawidłowy znak w nazwie dokumentu';
-$lang['pm_nochange'] = 'Nazwa dokumentu i katalogu niezmieniona';
-$lang['pm_existing1'] = 'Dokument o nazwie ';
-$lang['pm_existing2'] = ' już istnieje ';
-$lang['pm_root'] = '[katalog bazowy]';
-$lang['pm_current'] = '(Bieżący)';
-$lang['pm_movedfrom'] = 'Dokument przeniesiony z ';
-$lang['pm_movedto'] = 'Dokument przeniesiony do ';
-$lang['pm_norights'] = 'Nie masz wystarczających uprawnień do zmiany co najmniej jednego z linków do tej strony';
-$lang['pm_tryagain'] = 'Spróbuj ponownie później';
-$lang['pm_filelocked'] = 'Ten plik jest zablokowany - ';
-$lang['pm_fileslocked'] = 'Te pliki są zablokowane - ';
-$lang['pm_linkchange1'] = 'Linki do ';
-$lang['pm_linkchange2'] = ' zmienione na ';
-$lang['pm_newname'] = 'Nowa nazwa dokumentu:';
-$lang['pm_targetns'] = 'Nazwa docelowego katalogu:';
-$lang['pm_submit'] = 'Zmień';
-?>
-
\ No newline at end of file
diff --git a/lang/pl/pagemove.txt.txt b/lang/pl/move.txt
similarity index 57%
rename from lang/pl/pagemove.txt.txt
rename to lang/pl/move.txt
index 1c0c46e..f20ba9e 100644
--- a/lang/pl/pagemove.txt.txt
+++ b/lang/pl/move.txt
@@ -1,13 +1,10 @@
-====== Pagemove ======
+====== Move ======
Ta wtyczka umożliwia przeniesienie lub zmianę nazwy dokumentu z poniższymi zastrzeżeniami:
- * Nie można przenieść strony start.
- * Musisz mieć uprawnienia do tego dokumentu i tych które na niego wskazują (linki wsteczne).
- * Dokument i jego wsteczne linki nie mogą być w trakcie edycji (założona blokada).
+ * You must have edit permission for the current page/all pages in the current namespace
+ * The pages that are moved must not be locked for editing.
* Do utworzenia pliku niezbędne jest uprawnienie w docelowym katalogu.
* Nie można przesunąć dokumentu do katalogu, w którym już jest dokument o tej samej nazwie.
We wszystkich linkach z i do zmienianej strony będzie użyta nowa lokalizacja.
-\\
-\\
diff --git a/lang/pt-br/lang.php b/lang/pt-br/lang.php
new file mode 100644
index 0000000..f68c2dd
--- /dev/null
+++ b/lang/pt-br/lang.php
@@ -0,0 +1,66 @@
+
+ * @author Fábio Nogueira
+ */
+$lang['menu'] = 'Mover páginas e domínios';
+$lang['inprogress'] = '(movimentação pendente)';
+$lang['treelink'] = 'Como alternativa a este formulário simples, você pode gerenciar reestruturações complexas da sua wiki usando o gerenciador de movimentação baseado em árvore.';
+$lang['notexist'] = 'A página %s não existe';
+$lang['norights'] = 'Você tem permissões insuficientes para editar %s';
+$lang['filelocked'] = 'A página %s está bloqueada. Tente mais tarde.';
+$lang['notchanged'] = 'Não foi fornecido um novo destino para a página %s (localização inalterada).';
+$lang['exists'] = 'A página %s não pode ser movida para %s, o destino já existe.';
+$lang['notargetperms'] = 'Você não tem permissão para criar a página %s.';
+$lang['medianotexist'] = 'O arquivo de mídia %s não existe';
+$lang['nomediarights'] = 'Você não tem permissões suficientes para excluir %s.';
+$lang['medianotchanged'] = 'Não foi fornecido um novo destino para a página %s (localização inalterada).';
+$lang['mediaexists'] = 'A mídia %s não pode ser movida para %s, o destino já existe.';
+$lang['nomediatargetperms'] = 'Você não tem permissão para criar o arquivo de mídia %s.';
+$lang['indexerror'] = 'Erro ao atualizar o índice de pesquisa %s';
+$lang['metamoveerror'] = 'Os meta-arquivos da página %s não puderam ser movidos';
+$lang['atticmoveerror'] = 'Os arquivos do diretório superior ao da página %s não puderam ser movidos. Por favor mova-os manualmente.';
+$lang['mediametamoveerror'] = 'Os meta-arquivos do arquivo de mídia %s não puderam ser movidos';
+$lang['mediamoveerror'] = 'A movimentação do arquivo de mídia %s falhou';
+$lang['mediaatticmoveerror'] = 'Os arquivos do diretório superior ao do arquivo de mídia %s não puderam ser movidos. Por favor mova-os manualmente.';
+$lang['renamed'] = 'Nome da página alterado de %s para %s';
+$lang['moved'] = 'Página movida de %s para %s';
+$lang['move_rename'] = 'Página movida e renomeada de %s para %s';
+$lang['delete'] = 'Excluído pelo plugin de movimentação';
+$lang['linkchange'] = 'Links adaptados devido a uma operação de movimentação';
+$lang['intro'] = 'A operação de movimentação ainda não foi iniciada!';
+$lang['preview'] = 'Visualize as alterações a serem executadas.';
+$lang['inexecution'] = 'Um movimento anterior não foi concluído - use os botões abaixo para continuar ou abortar a execução.';
+$lang['btn_start'] = 'Iniciar';
+$lang['btn_continue'] = 'Continuar';
+$lang['btn_retry'] = 'Repetir item';
+$lang['btn_skip'] = 'Ignorar item';
+$lang['btn_abort'] = 'Abortar';
+$lang['legend'] = 'Mover página atual ou domínio';
+$lang['movepage'] = 'Mover página';
+$lang['movens'] = 'Mover domínio';
+$lang['dst'] = 'Novo nome:';
+$lang['content_to_move'] = 'Conteúdo a mover:';
+$lang['autoskip'] = 'Ignore erros e pule páginas ou arquivos que não podem ser movidos';
+$lang['autorewrite'] = 'Reescrever links logo após a conclusão da mudança';
+$lang['move_pages'] = 'Páginas';
+$lang['move_media'] = 'Arquivos de mídia';
+$lang['move_media_and_pages'] = 'Páginas e arquivos de mídia';
+$lang['nodst'] = 'Não foi fornecido um nome novo';
+$lang['noaction'] = 'Não houve movimentos definidos';
+$lang['renamepage'] = 'Renomear Página';
+$lang['cantrename'] = 'A página não pode ser renomeada agora. Por favor tente mais tarde.';
+$lang['js']['rename'] = 'Renomear';
+$lang['js']['cancel'] = 'Cancelar';
+$lang['js']['newname'] = 'Novo nome:';
+$lang['js']['inprogress'] = 'renomeando página e ajustando links...';
+$lang['js']['complete'] = 'Operação de movimentação concluída.';
+$lang['js']['renameitem'] = 'Renomear este item';
+$lang['js']['add'] = 'Criar um novo domínio';
+$lang['js']['duplicate'] = 'Desculpe, "%s" já existe neste domínio.';
+$lang['root'] = '[Domínio raiz]';
+$lang['noscript'] = 'Este recurso requer JavaScript';
+$lang['moveinprogress'] = 'Há outra operação de movimentação em andamento no momento, você não pode usar esta ferramenta agora.';
diff --git a/lang/pt-br/move.txt b/lang/pt-br/move.txt
new file mode 100644
index 0000000..19a6c43
--- /dev/null
+++ b/lang/pt-br/move.txt
@@ -0,0 +1,9 @@
+====== Movimentação ======
+Este plugin permite mover e/ou renomear a página atual ou mover e/ou renomear o domínio atual dentro das seguintes restrições:
+
+ * Você deve ter permissão de edição para a página atual ou para todas as páginas no domínio atual.
+ * As páginas que a serem movidas não devem estar bloqueadas para edição.
+ * Para mover uma página, você também precisa criar permissão no domínio de destino.
+ * Você não pode mover uma página para um domínio onde já existe uma página com o mesmo nome.
+
+Todos os links de e para as páginas movidas serão atualizados para refletir o novo local e/ou nome.
\ No newline at end of file
diff --git a/lang/pt-br/progress.txt b/lang/pt-br/progress.txt
new file mode 100644
index 0000000..a65c3ad
--- /dev/null
+++ b/lang/pt-br/progress.txt
@@ -0,0 +1,2 @@
+====== Movimentação em andamento... ======
+Por favor, mantenha esta página aberta enquanto a mudança está sendo realizada.
\ No newline at end of file
diff --git a/lang/pt-br/settings.php b/lang/pt-br/settings.php
new file mode 100644
index 0000000..2926b9a
--- /dev/null
+++ b/lang/pt-br/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Permitir renomeação de páginas para esses grupos e usuários (separados por vírgula).';
+$lang['minor'] = 'Marcar ajustes de link como menores? Alterações menores não serão listadas em feeds RSS e listas de e-mails.';
+$lang['autoskip'] = 'Ativa o salto automático de erros em movimentações de domínio por padrão.';
+$lang['autorewrite'] = 'Habilita a regravação automática do link após a movimentação do domínio por padrão.';
+$lang['pagetools_integration'] = 'Adicionar botão de renomeação às ferramentas de página';
diff --git a/lang/pt-br/tree.txt b/lang/pt-br/tree.txt
new file mode 100644
index 0000000..031ed58
--- /dev/null
+++ b/lang/pt-br/tree.txt
@@ -0,0 +1,6 @@
+====== Gerenciador de Movimentação ======
+Esta interface permite que você reorganize os domínios, páginas e arquivos de mídia da sua wiki via Arraste e Solte.
+
+Para mover vários domínios, páginas ou arquivos de mídia para o mesmo destino, você pode usar as caixas de seleção da seguinte forma:
+ * verifique os domínios, páginas ou arquivos de mídia que deseja mover;
+ * mova um dos itens marcados para o destino desejado, todos os itens selecionados serão movidos para este destino.
\ No newline at end of file
diff --git a/lang/ru/lang.php b/lang/ru/lang.php
index 3b76460..508ca58 100644
--- a/lang/ru/lang.php
+++ b/lang/ru/lang.php
@@ -1,39 +1,69 @@
+ *
+ * @author Aleksandr Selivanov
+ * @author S'Adm*n
+ * @author Chang Zhao
*/
-
-// settings must be present and set appropriately for the language
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// for admin plugins, the menu prompt to be displayed in the admin menu
-// if set here, the plugin doesn't need to override the getMenuText() method
-$lang['menu'] = 'Перемещение/переименование страницы...';
-$lang['desc'] = 'Page Move/Rename Plugin';
-
-$lang['pm_notexist'] = 'Эта страница еще не существует';
-$lang['pm_notstart'] = 'Недоступно для стартовой страницы';
-$lang['pm_notwrite'] = 'Ваши права доступа не позволяют Вам изменять эту страницу';
-$lang['pm_badns'] = 'В названии пространства имён присутствуют недопустимые символы.';
-$lang['pm_badname'] = 'Недопустимые символы в названии страниц.';
-$lang['pm_nochange'] = 'Параметры страницы не изменены.';
-$lang['pm_existing1'] = 'Страница с именем ';
-$lang['pm_existing2'] = ' уже существует в ';
-$lang['pm_root'] = '[Корневой каталог]';
-$lang['pm_current'] = '(Текущий)';
-$lang['pm_movedfrom'] = 'Документ перемещён из ';
-$lang['pm_movedto'] = 'Документ перемещён в ';
-$lang['pm_norights'] = 'У Вас нет прав на редактирование одной из страниц, ссылающихся на данный документ.';
-$lang['pm_tryagain'] = 'Попробуйте позже.';
-$lang['pm_filelocked'] = 'Изменение данного файла запрещено - ';
-$lang['pm_fileslocked'] = 'Изменение данных файлов запрещено - ';
-$lang['pm_linkchange1'] = 'Ссылки на страницу ';
-$lang['pm_linkchange2'] = ' изменены на ';
-$lang['pm_newname'] = 'Новое название документа :';
-$lang['pm_targetns'] = 'Переместить в пространство :';
-$lang['pm_submit'] = 'Применить';
-?>
\ No newline at end of file
+$lang['menu'] = 'Перемещение страниц и пространств имён';
+$lang['inprogress'] = '(идёт перемещение)';
+$lang['treelink'] = 'Для сложной реструктуризации страниц вы можете использовать древовидное управление перемещением.';
+$lang['notexist'] = 'Страница %s не существует';
+$lang['norights'] = 'У вас нет прав редактировать %s.';
+$lang['filelocked'] = 'Изменение страницы %s сейчас заблокировано. Попробуйте позже.';
+$lang['notchanged'] = 'Не задано нового названия %s (адрес не изменён).';
+$lang['exists'] = 'Невозможно переместить %s, уже существует страница %s.';
+$lang['notargetperms'] = 'У вас недостаточно прав для создания страницы %s.';
+$lang['medianotexist'] = 'Медиафайла %s не существует';
+$lang['nomediarights'] = 'У вас недостаточно прав для удаления %s.';
+$lang['medianotchanged'] = 'Не задано нового названия %s (адрес не изменён).';
+$lang['mediaexists'] = 'Невозможно переместить %s - уже существует страница %s.';
+$lang['nomediatargetperms'] = 'У вас недостаточно прав для создания медиафайла %s.';
+$lang['indexerror'] = 'Ошибка при обновлении индексирования поиска %s';
+$lang['metamoveerror'] = 'Не удалось переместить медиафайлы страницы %s';
+$lang['atticmoveerror'] = 'Не удалось переместить архивы (attic) страницы %s. Переместите их вручную.';
+$lang['mediametamoveerror'] = 'Не удалось переместить мета-файлы страницы %s.';
+$lang['mediamoveerror'] = 'Не удалось переместить медиафайл %s.';
+$lang['mediaatticmoveerror'] = 'Не удалось переместить архивы (attic) медиафайла %s. Переместите их вручную.';
+$lang['renamed'] = 'Имя страницы %s изменено на %s';
+$lang['moved'] = 'Страница перемещена из %s в %s';
+$lang['move_rename'] = 'Страница перемещена и переименована из %s в %s';
+$lang['delete'] = 'Удалено плагином Move (перемещения страниц)';
+$lang['linkchange'] = 'Операцией перемещения обновлены ссылки';
+$lang['intro'] = 'Операция перемещения ещё не начата!';
+$lang['preview'] = 'Просмотреть ожидаемые изменения.';
+$lang['inexecution'] = 'Предыдущее перемещение не завершилось - кнопками внизу выберите завершение или отмену.';
+$lang['btn_start'] = 'Начать';
+$lang['btn_continue'] = 'Продолжить';
+$lang['btn_retry'] = 'Попытаться ещё';
+$lang['btn_skip'] = 'Пропустить';
+$lang['btn_abort'] = 'Отменить';
+$lang['legend'] = 'Переместить текущую страницу или папку';
+$lang['movepage'] = 'Переместить страницу';
+$lang['movens'] = 'Переместить папку';
+$lang['dst'] = 'Новое название:';
+$lang['content_to_move'] = 'Переместить содержимое:';
+$lang['autoskip'] = 'Игнорировать ошибки, пропускать страницы и медиафайлы, которые не удаётся переместить';
+$lang['autorewrite'] = 'По окончании перемещения обновить ссылки.';
+$lang['move_pages'] = 'Страницы';
+$lang['move_media'] = 'Медиафайлы';
+$lang['move_media_and_pages'] = 'Страницы и медиафайлы';
+$lang['nodst'] = 'Не задано новое имя';
+$lang['noaction'] = 'Не было задано перемещений';
+$lang['renamepage'] = 'Переименовать страницу';
+$lang['cantrename'] = 'Сейчас не удаётся переименовать страницу. Попробуйте позже.';
+$lang['js']['rename'] = 'Переименовать';
+$lang['js']['cancel'] = 'Отменить';
+$lang['js']['newname'] = 'Новое название:';
+$lang['js']['inprogress'] = 'переименование страницы и обновление ссылок...';
+$lang['js']['complete'] = 'Перемещение завершено.';
+$lang['js']['renameitem'] = 'Переименовать';
+$lang['js']['add'] = 'Создать новое пространство имён';
+$lang['js']['duplicate'] = 'Не получается: „%s“ уже существует в данной папке.';
+$lang['root'] = '[Корневой каталог]';
+$lang['noscript'] = 'Для этого требуется включить JavaScript';
+$lang['moveinprogress'] = 'Сейчас происходит другая операция перемещения; пока она не закончена, данный инструмент не работает.';
diff --git a/lang/ru/move.txt b/lang/ru/move.txt
new file mode 100644
index 0000000..bf3f576
--- /dev/null
+++ b/lang/ru/move.txt
@@ -0,0 +1,10 @@
+====== Перемещение ======
+
+Плагин позволяет перемещать и переименовывать текущую страницу или текущее пространство имён со следующими ограничениями:
+
+ * у вас должны быть права для редактирования текущей страницы или страниц в текущем пространстве имён;
+ * перемещаемые страницы не должны быть заблокированы (например, когда их редактируют другие пользователи);
+ * для перемещения страницы в другое пространство имён вам нужны права создания в этом пространство имён;
+ * вы не можете переместить страницу в пространство имён, уже содержащее страницу с таким же названием.
+
+Все ссылки, ведущие с перемещаемых и переименовываемых страниц или на них, будут обновлены в соответствии с внесёнными изменениями.
diff --git a/lang/ru/pagemove.txt b/lang/ru/pagemove.txt
deleted file mode 100644
index 9b9e50e..0000000
--- a/lang/ru/pagemove.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-====== Перемещение страницы ======
-
-Этот плагин позволяет перемещать и/или переименовывать текущую страницу со следующими ограничениями:
-
- * Стартовая страница не может быть перемещена.
- * Вы должны обладать правом редактировать эту страницу и все другие, ссылающиеся на неё.
- * Текущая страница и ссылающиеся на неё не должны быть заблокированы для редактирования.
- * Вы должны обладать соответствующими правами доступа в директории, куда Вы планируете переместить файл.
- * Вы не можете переместить страницу в пространство имён, уже содержащее страницу с таким же названием.
-
-Все ссылки, ведущие с этой страницы и на неё, будут обновлены в соответствии с внесёнными изменениями.
-\\
-\\
diff --git a/lang/ru/progress.txt b/lang/ru/progress.txt
new file mode 100644
index 0000000..3855281
--- /dev/null
+++ b/lang/ru/progress.txt
@@ -0,0 +1,3 @@
+====== Идёт перемещение... ======
+
+Пожалуйста, не закрывайте эту страницу.
diff --git a/lang/ru/settings.php b/lang/ru/settings.php
new file mode 100644
index 0000000..f85ba2d
--- /dev/null
+++ b/lang/ru/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Разрешить переименование страниц следующим группам и пользователям (разделяйте запятыми)';
+$lang['minor'] = 'Помечать обновление ссылок как небольшие изменения? Они не будут появляться в письмах подписчикам и RSS.';
+$lang['autoskip'] = 'Включить автоматический пропуск ошибок при перемещении пространств имён по умолчанию';
+$lang['autorewrite'] = 'Включить автоматическое обновление ссылок после перемещения пространств имён по умолчанию';
+$lang['pagetools_integration'] = 'Добавить кнопку переименования на панель инструментов страницы';
diff --git a/lang/ru/tree.txt b/lang/ru/tree.txt
new file mode 100644
index 0000000..4d71570
--- /dev/null
+++ b/lang/ru/tree.txt
@@ -0,0 +1,3 @@
+====== Управление перемещением =====
+
+Здесь вы можете перемещать пространства имён, страницы и медиафайлы вашей вики перетаскиванием (drag-and-drop).
\ No newline at end of file
diff --git a/lang/sk/lang.php b/lang/sk/lang.php
new file mode 100644
index 0000000..309f654
--- /dev/null
+++ b/lang/sk/lang.php
@@ -0,0 +1,72 @@
+
+ */
+
+$lang['menu'] = 'Presun/premenovanie stránky';
+$lang['inprogress'] = '(prebieha presun)';
+$lang['treelink'] = 'Ako alternatívu k tomuto jednoduchému formuláru môžte použiť stromový manažér presuov na komplexné preskupovanie vašej wiki.';
+
+$lang['notexist'] = 'Stránka \'%s\' neexistuje.';
+$lang['norights'] = 'Nedostatočné oprávnenie editovať \'%s\'.';
+$lang['filelocked'] = 'Stránka \'%s\' je uzamknutá - skúste prosím neskôr.';
+$lang['notchanged'] = 'Žiaden nový cieľ pre stránku \'%s\' nezadaný (umiestnenie nezmenené).';
+$lang['exists'] = 'Stránku \'%s\' nie je možné do \'%s\', cieľ už existuje.';
+$lang['notargetperms'] = 'Nemáte dostatočné oprávnenie vytvoriť stránku \'%s\'.';
+
+$lang['medianotexist'] = 'Médium \'%s\' neexistuje.';
+$lang['nomediarights'] = 'Nemáte dostatočné oprávnenie na zmazanie \'%s\'.';
+$lang['medianotchanged'] = 'Žiaden nový cieľ pre stránku \'%s\' nezadaný (umiestnenie nezmenené).';
+$lang['mediaexists'] = 'Médium \'%s\' nie je možné do \'%s\', cieľ už existuje.';
+$lang['nomediatargetperms'] = 'Nemáte dostatočné oprávnnenie vytvoriť médium \'%s\'.';
+
+$lang['indexerror'] = 'Chyba pri aktualizácii indexu vyhľadávania \'%s\'.';
+$lang['metamoveerror'] = 'Meta súbory stránky \'%s\' nemôžu byť presunuté.';
+$lang['atticmoveerror'] = 'Staré verzie stránky \'%s\' nemôžu byť presunuté. Prosím, presuňte ich ručne.';
+$lang['mediametamoveerror'] = 'Meta súbory média \'%s\' nemôžu byť presunuté.';
+$lang['mediamoveerror'] = 'Presunutie média \'%s\' zlyhalo.';
+$lang['mediaatticmoveerror'] = 'Staré verzie média \'%s\' nemôžu byť presunuté. Prosím, presuňte ich ručne.';
+
+$lang['renamed'] = 'Stránka premenovaná z \'%s\' na \'%s\'';
+$lang['moved'] = 'Stránka presunutá z \'%s\' do \'%s\'';
+$lang['move_rename'] = 'Stránka presunutá a premenovaná z \'%s\' na \'%s\'';
+$lang['delete'] = 'Zmazané pluginem move';
+$lang['linkchange'] = 'Odkazy upravené z dôvodu presunu';
+
+$lang['intro'] = 'Operácia presunu zatiaľ nezačala!';
+$lang['preview'] = 'Náhled zmien, ktoré budou vykonané.';
+$lang['inexecution'] = 'Predchádzajúci presun nebol dokončený - použite tlačidlá dole k pokračovaniu alebo zrušeniu presunu.';
+$lang['btn_start'] = 'Štart';
+$lang['btn_continue'] = 'Pokračovať';
+$lang['btn_retry'] = 'Znova';
+$lang['btn_skip'] = 'Preskočiť položku';
+$lang['btn_abort'] = 'Prerušiť';
+
+$lang['legend'] = 'Presunúť aktuálnu stránku nebo menný priestor';
+$lang['movepage'] = 'Presunúť stránku';
+$lang['movens'] = 'Presunúť aktuálny menný prostor';
+$lang['dst'] = 'Nové meno:';
+$lang['content_to_move'] = 'Obsah k presunu:';
+$lang['autoskip'] = 'Ignorovať chyby a preskočiť stránky a médiá, ktoré nemôžu byť presunuté';
+$lang['autorewrite'] = 'Prepísať odkazy hneď po ukončení presunu';
+$lang['move_pages'] = 'Stránky';
+$lang['move_media'] = 'Médiá';
+$lang['move_media_and_pages'] = 'Stránky a médiá';
+$lang['nodst'] = 'Nebolo zadané nové meno';
+$lang['noaction'] = 'Neboli definované žiadne presuny';
+
+$lang['renamepage'] = 'Premenovať stránku';
+$lang['cantrename'] = 'Stránka nemôže byť momentálne premenovaná. Prosím skúste neskôr.';
+$lang['js']['rename'] = 'Premenovať';
+$lang['js']['cancel'] = 'Zrušit';
+$lang['js']['newname'] = 'Nový názov:';
+$lang['js']['inprogress'] = 'Presúvam stránky a upravujem odkazy...';
+$lang['js']['complete'] = 'Presun bol dokončený.';
+
+$lang['root'] = '[Koreňový menný priestor]';
+$lang['noscript'] = 'Táto vlastosť vyžaduje JavaScript';
+$lang['moveinprogress'] = 'Práve prebieha iná operácia presunu, tento nástroj momentálne nemôžete použiť.';
+$lang['js']['renameitem'] = 'Premenovať túto položku';
+$lang['js']['add'] = 'Vytvoriť nový menný priestor';
+$lang['js']['duplicate'] = 'Ľutujeme, \'%s\' už v tomto mennom priestore existuje.';
diff --git a/lang/sk/move.txt b/lang/sk/move.txt
new file mode 100644
index 0000000..42f9911
--- /dev/null
+++ b/lang/sk/move.txt
@@ -0,0 +1,11 @@
+====== Move ======
+
+Tento plugin vám umožňuje presunúť alebo premenovať aktuálnu stránku alebo namespace, v rámci istých obmedzení:
+
+ * Musíte mať právo editovať aktuálnu stránku / všetky stránky v aktuálnom mennom priestore.
+ * Stránky ktoré majú byť presunuté nesmú byť zamknuté pre úpravu.
+ * Na presun stránky potrebujete právo zápisu do cieľového menného priestoru.
+ * Nemôžte presunúť stránku do menného priestoru, v ktorom už stránka s rovnakým menom existuje.
+
+Všetky odkazy z a na presúvané stránky budú upravené podľa ich nového umiestnenia.
+
diff --git a/lang/sk/progress.txt b/lang/sk/progress.txt
new file mode 100644
index 0000000..3a61d2a
--- /dev/null
+++ b/lang/sk/progress.txt
@@ -0,0 +1,3 @@
+====== Prebieha presun... ======
+
+Prosím, nezatvárajte túto stránku kým presun neskončí.
diff --git a/lang/sk/settings.php b/lang/sk/settings.php
new file mode 100644
index 0000000..c0e2324
--- /dev/null
+++ b/lang/sk/settings.php
@@ -0,0 +1,11 @@
+
+ */
+
+$lang['allowrename'] = 'Povoliť premenovávanie stránok týmto používateľom a skupinám (oddelených čiarkou).';
+$lang['minor'] = 'Označiť úpravu odkazov ako drobnú zmenu? Drobné zmeny nebudú viditelné v RSS feedoch a v e-mailoch informujcich o zmenách.';
+$lang['autoskip'] = 'Zapnúť východzie automatické preskakovanie chýb pri presune menného priestoru.';
+$lang['autorewrite'] = 'Zapnúť východzie automatické prepisovanie odkazov po presune menného priestoru.';
+$lang['pagetools_integration'] = 'Pridať tlačidlo na premenovanie do nástrojov stránky';
diff --git a/lang/sk/tree.txt b/lang/sk/tree.txt
new file mode 100644
index 0000000..b37e71a
--- /dev/null
+++ b/lang/sk/tree.txt
@@ -0,0 +1,8 @@
+====== Manažér presunov =====
+
+Tento interface vám umožňuje preskupiť menné priestory, stránky a médiá vo vašej wiki použitím metódy Drag'n'Drop.
+
+Na presun viacerých menných priestorov, stránok a médií do rovnakého cieľa môžete použiť zaškrtávacie políčka nasledujúcim spôsobom:
+ * zaškrtnite menné priestory, stránky alebo médiá, ktoré chcete presunúť;
+ * presuňte jeden zo zaškrtnutých objektov do žiadanej cieľovej lokality, presunuté budú všetky zaškrtnuté objekty.
+
diff --git a/lang/sl/lang.php b/lang/sl/lang.php
index a55a701..9dbf223 100644
--- a/lang/sl/lang.php
+++ b/lang/sl/lang.php
@@ -1,39 +1,31 @@
*/
-
+
// settings must be present and set appropriately for the language
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
-
+
// for admin plugins, the menu prompt to be displayed in the admin menu
// if set here, the plugin doesn't need to override the getMenuText() method
$lang['menu'] = 'Premakni/preimenuj stran...';
$lang['desc'] = 'Plugin za premikanje/preimenovanje strani';
-$lang['pm_notexist'] = 'Ta stran še ne obstaja';
-$lang['pm_notstart'] = 'Začetne strani ne morete premakniti/preimenovati';
-$lang['pm_notwrite'] = 'Nimate zadostnih pravic za urejanje te strani';
-$lang['pm_badns'] = 'Neveljavni znaki v imenskem prostoru.';
-$lang['pm_badname'] = 'Neveljavni znaki v imenu strani.';
-$lang['pm_nochange'] = 'Ime dokumenta in imenski prostor sta nespremenjena.';
-$lang['pm_existing1'] = 'Dokument imenovan ';
-$lang['pm_existing2'] = ' že obstaja v ';
-$lang['pm_root'] = '[Korenski imenski prostor/Root namespace]';
-$lang['pm_current'] = '(Trenutno)';
-$lang['pm_movedfrom'] = 'Dokument premaknjen iz ';
-$lang['pm_movedto'] = 'Dokument premaknjen na ';
-$lang['pm_norights'] = 'Nimate zadostnih pravic za urejanje enega ali več backlinkov za ta dokument.';
-$lang['pm_tryagain'] = 'Poskusite ponovno kasneje.';
-$lang['pm_filelocked'] = 'Ta datoteka je zaklenjena - ';
-$lang['pm_fileslocked'] = 'Te datoteke so zaklenjene - ';
-$lang['pm_linkchange1'] = 'Kaže na ';
-$lang['pm_linkchange2'] = ' spremenjeno na ';
-$lang['pm_newname'] = 'Novo ime dokumenta :';
-$lang['pm_targetns'] = 'Izberite ciljni imenski prostor :';
-$lang['pm_submit'] = 'Potrdi';
-?>
+//$lang['notexist'] = 'Ta stran še ne obstaja';
+$lang['notwrite'] = 'Nimate zadostnih pravic za urejanje te strani';
+$lang['badns'] = 'Neveljavni znaki v imenskem prostoru.';
+$lang['badname'] = 'Neveljavni znaki v imenu strani.';
+$lang['nochange'] = 'Ime dokumenta in imenski prostor sta nespremenjena.';
+$lang['existing1'] = 'Dokument imenovan %s že obstaja v %s';
+$lang['root'] = '[Korenski imenski prostor/Root namespace]';
+$lang['current'] = '(Trenutno)';
+//$lang['norights'] = 'Nimate zadostnih pravic za urejanje enega ali več backlinkov za ta dokument.';
+$lang['filelocked'] = 'Ta datoteka je zaklenjena - poskusite ponovno kasneje.';
+$lang['linkchange1'] = 'Kaže na %s spremenjeno na %s';
+$lang['newname'] = 'Novo ime dokumenta :';
+$lang['targetns'] = 'Izberite ciljni imenski prostor :';
+$lang['submit'] = 'Potrdi';
diff --git a/lang/sl/pagemove.txt b/lang/sl/move.txt
similarity index 59%
rename from lang/sl/pagemove.txt
rename to lang/sl/move.txt
index 3aea1d6..e4000e0 100644
--- a/lang/sl/pagemove.txt
+++ b/lang/sl/move.txt
@@ -1,13 +1,10 @@
-====== Pagemove ======
+====== Move ======
Ta plugin omogoča premikanje in/ali preimenovanje trenutnega dokumenta v okviru naslednjih omejitev:
- * Začetne strani ne morete premakniti.
- * Morate imeti pravico do urejanja dokumenta in vseh dokumentov, ki kažejo na ta dokument (backlinki).
- * Dokument in njegovi backlinki ne smejo biti zaklenjeni za urejanje.
+ * You must have edit permission for the current page/all pages in the current namespace.
+ * The pages that are moved must not be locked for editing.
* Za premikanje datoteke potrebujete pravico za ustvarjanje v ciljni mapi.
* Ne morete premakniti dokumenta v imenski prostor, kjer dokument z istim imenom že obstaja.
Vsi linki na in z strani bodo posodobljeni, da bodo kazali na novo lokacijo in/ali ime.
-\\
-\\
\ No newline at end of file
diff --git a/lang/sv/lang.php b/lang/sv/lang.php
new file mode 100644
index 0000000..281223f
--- /dev/null
+++ b/lang/sv/lang.php
@@ -0,0 +1,51 @@
+
+ */
+$lang['menu'] = 'Flytt/Namnbyte på Sida/Namnrymd...';
+$lang['notexist'] = 'Sidan %s finns inte';
+$lang['norights'] = 'Du har inte tillräcklig behörighet för att ändra %s.';
+$lang['filelocked'] = 'Sidan %s är låst. Försök senare.';
+$lang['notargetperms'] = 'Du har inte tillräcklig behörighet för att skapa sidan %s.';
+$lang['medianotexist'] = 'Media filen %s finns inte';
+$lang['nomediarights'] = 'Du har inte tillräcklig behörighet att ta bort %s.';
+$lang['nomediatargetperms'] = 'Du har inte tillräcklig behörighet för att skapa mediafilen %s.';
+$lang['indexerror'] = 'Fel när sökindex %s uppdaterades';
+$lang['metamoveerror'] = 'Metafilerna för sidorna %s kunde inte flyttas';
+$lang['renamed'] = 'Sidnamn ändrat från %s till %s';
+$lang['moved'] = 'Sida flyttad från %s till %s';
+$lang['move_rename'] = 'Sida flyttad och namnändrad från %s till %s';
+$lang['delete'] = 'Borttagen av pluginen';
+$lang['linkchange'] = 'Länkar som ändrats på grund av flytt/namnändring';
+$lang['preview'] = 'Förhandsvisa ändringar att utföra ';
+$lang['btn_start'] = 'Starta';
+$lang['btn_continue'] = 'Fortsätt';
+$lang['btn_skip'] = 'Hoppa över post';
+$lang['btn_abort'] = 'Avbryt';
+$lang['legend'] = 'Flytta nuvarande sida eller namnrymd';
+$lang['movepage'] = 'Flytta sida';
+$lang['movens'] = 'Flytta namnrymd';
+$lang['dst'] = 'Nytt namn:';
+$lang['content_to_move'] = 'Innehåll att flytta';
+$lang['autoskip'] = 'Ignorera fel och hoppa över sidor eller filer som inte kunde flyttas';
+$lang['autorewrite'] = 'Skriv om länkar efter att flytt/namnbyte genomförts';
+$lang['move_pages'] = 'Sidor';
+$lang['move_media'] = 'Mediafiler';
+$lang['move_media_and_pages'] = 'Sidor och mediafiler';
+$lang['nodst'] = 'Inget nytt namn är angett';
+$lang['renamepage'] = 'Byt namn på sida';
+$lang['cantrename'] = 'Kan inte byta namn på sidan just nu. Var god försök senare.';
+$lang['js']['rename'] = 'Byt namn';
+$lang['js']['cancel'] = 'Avbryt';
+$lang['js']['newname'] = 'Nytt namn:';
+$lang['js']['inprogress'] = 'ändra namn och justera länkar...';
+$lang['js']['complete'] = 'Flytt/Namnbyte avklarat.';
+$lang['js']['renameitem'] = 'Ändra namn på denna post';
+$lang['js']['add'] = 'Skapa en ny namnrymd';
+$lang['js']['duplicate'] = 'Tyvärr, "%s" existerar redan i denna namnrymd.';
+$lang['root'] = '[Rotnamnrymd]';
+$lang['noscript'] = 'Denna funktion kräver JavaScript.';
diff --git a/lang/sv/move.txt b/lang/sv/move.txt
new file mode 100644
index 0000000..01c6edf
--- /dev/null
+++ b/lang/sv/move.txt
@@ -0,0 +1,10 @@
+====== Flytta och byt namn ======
+
+Den här pluginen gör det möjligt att flytta och/eller byta namn på den aktuella sidan och/eller namnrymden. Följande begränsningar gäller:
+
+ * Du måste ha behörighet att redigera nuvarande/alla sidor i aktuell namnrymd.
+ * Sidan/sidorna som flyttas får inte vara låsta.
+ * Om du vill flytta en sida måste du också ha behörighet att skapa dokument in den nya namnrymden.
+ * Det går inte att flytta en sida till en namnrymd där det redan finns en sida med det namn du vill ge sidan.
+
+Alla länkar från alla sidor till sidor som flyttas uppdateras så att de pekar på det nya namnet eller den nya platsen.
diff --git a/lang/sv/progress.txt b/lang/sv/progress.txt
new file mode 100644
index 0000000..fd00db7
--- /dev/null
+++ b/lang/sv/progress.txt
@@ -0,0 +1,3 @@
+====== Flytt/namnbyte pågår... ======
+
+Var god håll den här sidan öppen medan flytten/namnbytet pågår.
\ No newline at end of file
diff --git a/lang/sv/settings.php b/lang/sv/settings.php
new file mode 100644
index 0000000..edb281d
--- /dev/null
+++ b/lang/sv/settings.php
@@ -0,0 +1,9 @@
+
+ */
+$lang['allowrename'] = 'Tillåt namnbyte på sidor i dessa grupper och från användare (kommaseparerat).';
+$lang['pagetools_integration'] = 'Lägg till namnändringsknapp till sidverktygen';
diff --git a/lang/tr/lang.php b/lang/tr/lang.php
new file mode 100644
index 0000000..26ea42f
--- /dev/null
+++ b/lang/tr/lang.php
@@ -0,0 +1,36 @@
+
+ */
+$lang['menu'] = 'Sayfaları ve ad alanlarını taşıma';
+$lang['inprogress'] = '(devam et)';
+$lang['intro'] = 'Hareket işlemi henüz başlatılmamış!';
+$lang['preview'] = 'Yürütülecek değişiklikleri önizleyin.';
+$lang['inexecution'] = 'Önceki bir hareket tamamlanmadı. Yürütmeye devam etmek veya iptal etmek için aşağıdaki düğmeleri kullanın.';
+$lang['btn_start'] = 'Başla';
+$lang['btn_continue'] = 'Devam et';
+$lang['btn_skip'] = 'Öğeyi atla';
+$lang['btn_abort'] = 'İptal et';
+$lang['legend'] = 'Geçerli sayfayı veya ad alanını taşı';
+$lang['movepage'] = 'Sayfayı taşı';
+$lang['movens'] = 'Ad alanını taşı';
+$lang['dst'] = 'Yeni isim:';
+$lang['content_to_move'] = 'Taşınacak içerik:';
+$lang['autoskip'] = 'Hataları yoksay ve taşınamayan sayfaları veya dosyaları atla';
+$lang['autorewrite'] = 'Hareket tamamlandıktan hemen sonra bağlantıları yeniden yaz';
+$lang['move_pages'] = 'Sayfalar';
+$lang['move_media'] = 'Medya dosyaları';
+$lang['move_media_and_pages'] = 'Sayfalar ve medya dosyaları';
+$lang['nodst'] = 'Yeni ad yok';
+$lang['noaction'] = 'Tanımlanan hareket yok';
+$lang['renamepage'] = 'Sayfayı Yeniden Adlandır';
+$lang['cantrename'] = 'Sayfa şu anda yeniden adlandırılamaz. Lütfen daha sonra tekrar deneyin.';
+$lang['js']['cancel'] = 'İptal et';
+$lang['js']['newname'] = 'Yeni isim:';
+$lang['js']['complete'] = 'Taşıma işlemi bitti.';
+$lang['root'] = '[Kök ad alanı]';
+$lang['noscript'] = 'Bu özellik JavaScript gerektirir';
+$lang['moveinprogress'] = 'Şu anda devam eden başka bir hareket işlemi var, şu anda bu aracı kullanamazsınız.';
diff --git a/lang/tr/progress.txt b/lang/tr/progress.txt
new file mode 100644
index 0000000..127d1af
--- /dev/null
+++ b/lang/tr/progress.txt
@@ -0,0 +1,3 @@
+====== Devam Ediyor... ======
+
+Hareket ilerledikçe lütfen bu sayfayı açık tutunuz.
\ No newline at end of file
diff --git a/lang/uk/lang.php b/lang/uk/lang.php
new file mode 100644
index 0000000..a2d0c27
--- /dev/null
+++ b/lang/uk/lang.php
@@ -0,0 +1,46 @@
+
+ */
+$lang['menu'] = 'Переміщення сторінок і просторів назв';
+$lang['inprogress'] = '(очікування переміщення)';
+$lang['treelink'] = 'Також можливо виконувати реструктурізацію за допомогою дерево подібного менеджера';
+$lang['notexist'] = 'Сторінка %s не існує.';
+$lang['norights'] = 'Ви не маєте прав на редагування сторінки %s.';
+$lang['filelocked'] = 'Сторінка %s заблоковано. Спробуйте пізніше.';
+$lang['notchanged'] = 'Не вказано куди переміщати сторінку %s (положення не змінено).';
+$lang['exists'] = 'Сторінка %s не може бути перенесена в %s тому, що така сторінка вже існує.';
+$lang['notargetperms'] = 'Ви не маєте прав на створення сторінки %s.';
+$lang['medianotexist'] = 'Файл %s не існує';
+$lang['nomediarights'] = 'Ви не маєте прав на знищення %s';
+$lang['mediametamoveerror'] = 'Метафайли мультимедійного файла %s не можуть бути переміщені';
+$lang['mediamoveerror'] = 'Файл %s не вдалось перемістити';
+$lang['renamed'] = 'Назва сторінки змінено з %s на %s';
+$lang['moved'] = 'Сторінка посунуто з %s на %s';
+$lang['move_rename'] = 'Сторінка змінена з %s на %s';
+$lang['delete'] = 'Плагін move видалено';
+$lang['linkchange'] = 'Посилання оновлені через операцію переміщення';
+$lang['intro'] = 'Переміщення ще не почато!';
+$lang['preview'] = 'Попередній перегляд майбутніх змін.';
+$lang['inexecution'] = 'Попереднє перейменування не завершено -- нажміть кнопки нижче щоб продовжити або зупинити процес.';
+$lang['btn_start'] = 'Почати';
+$lang['btn_continue'] = 'Продовжити';
+$lang['btn_retry'] = 'Повторити';
+$lang['btn_skip'] = 'Пропустити';
+$lang['btn_abort'] = 'Зупинити';
+$lang['movepage'] = 'Перенести сторінку';
+$lang['movens'] = 'Перенести простір назв';
+$lang['dst'] = 'Нова назва:';
+$lang['content_to_move'] = 'Перенести зміст:';
+$lang['move_pages'] = 'Сторінок';
+$lang['move_media'] = 'Медіа файли';
+$lang['move_media_and_pages'] = 'Сторінки та медіа файли';
+$lang['renamepage'] = 'Перейменована сторінка';
+$lang['js']['rename'] = 'Перейменувати';
+$lang['js']['cancel'] = 'Відмінити';
+$lang['js']['newname'] = 'Нова назва:';
+$lang['js']['complete'] = 'Операцію перенесення закінчено.';
+$lang['js']['renameitem'] = 'Перейменувати це ';
diff --git a/lang/vi/lang.php b/lang/vi/lang.php
new file mode 100644
index 0000000..ad97f75
--- /dev/null
+++ b/lang/vi/lang.php
@@ -0,0 +1,65 @@
+
+ */
+$lang['menu'] = 'Di chuyển trang và không gian tên';
+$lang['inprogress'] = '(di chuyển đang chờ xử lý)';
+$lang['treelink'] = 'Ngoài ra, với hình thức đơn giản này, bạn có thể quản lý việc tái cấu trúc phức tạp wiki của mình bằng cách sử dụng quản lý di chuyển dựa trên cây.';
+$lang['notexist'] = 'Trang %s không tồn tại';
+$lang['norights'] = 'Bạn không có đủ quyền để sửa đổi %s.';
+$lang['filelocked'] = 'Trang %s đã bị khóa. Thử lại sau.';
+$lang['notchanged'] = 'Không có đích đến mới cho trang %s (vị trí không thay đổi).';
+$lang['exists'] = 'Không thể di chuyển trang %s đến %s, đích đến đã tồn tại.';
+$lang['notargetperms'] = 'Bạn không có quyền tạo trang %s.';
+$lang['medianotexist'] = 'Không tồn tại tập tin phương tiện %s';
+$lang['nomediarights'] = 'Bạn không có đủ quyền để xóa %s.';
+$lang['medianotchanged'] = 'Không có đích đến mới cho trang %s (vị trí không thay đổi).';
+$lang['mediaexists'] = 'Không thể di chuyển phương tiện %s đến %s, đích đến đã tồn tại.';
+$lang['nomediatargetperms'] = 'Bạn không có quyền tạo tập tin phương tiện %s.';
+$lang['indexerror'] = 'Lỗi trong khi cập nhật chỉ mục tìm kiếm %s';
+$lang['metamoveerror'] = 'Không thể di chuyển các tập tin meta của trang %s';
+$lang['atticmoveerror'] = 'Không thể di chuyển các tập tin tum của trang %s. Vui lòng di chuyển chúng thủ công.';
+$lang['mediametamoveerror'] = 'Không thể di chuyển các tập tin meta của tập tin phương tiện %s';
+$lang['mediamoveerror'] = 'Di chuyển tập tin phương tiện %s không thành công.';
+$lang['mediaatticmoveerror'] = 'Không thể di chuyển các tập tin tum của tập tin phương tiện %s. Vui lòng di chuyển chúng thủ công.';
+$lang['renamed'] = 'Đã thay đổi tên trang từ %s thành %s';
+$lang['moved'] = 'Đã di chuyển trang từ %s thành %s';
+$lang['move_rename'] = 'Đã di chuyển và đổi tên trang từ %s thành %s';
+$lang['delete'] = 'Đã xóa bằng cách di chuyển plugin';
+$lang['linkchange'] = 'Đã thích nghi liên kết vì một hoạt động di chuyển';
+$lang['intro'] = 'Hoạt động di chuyển chưa được bắt đầu!';
+$lang['preview'] = 'Xem trước các thay đổi sẽ được thực hiện.';
+$lang['inexecution'] = 'Một di chuyển trước đó đã không được hoàn thành - sử dụng các nút bên dưới để tiếp tục hoặc hủy bỏ việc thực hiện.';
+$lang['btn_start'] = 'Bắt đầu';
+$lang['btn_continue'] = 'Tiếp tục';
+$lang['btn_retry'] = 'Thử lại mục';
+$lang['btn_skip'] = 'Bỏ qua mục';
+$lang['btn_abort'] = 'Hủy bỏ';
+$lang['legend'] = 'Di chuyển trang hiện tại hoặc không gian tên';
+$lang['movepage'] = 'Di chuyển trang';
+$lang['movens'] = 'Di chuyển không gian tên';
+$lang['dst'] = 'Tên mới:';
+$lang['content_to_move'] = 'Nội dung cần di chuyển:';
+$lang['autoskip'] = 'Bỏ qua lỗi và bỏ qua các trang hoặc tập tin không thể di chuyển';
+$lang['autorewrite'] = 'Viết lại liên kết ngay sau khi hoàn thành di chuyển';
+$lang['move_pages'] = 'Trang';
+$lang['move_media'] = 'Tập tin phương tiện';
+$lang['move_media_and_pages'] = 'Trang và tập tin phương tiện';
+$lang['nodst'] = 'Không có tên mới nào được chỉ định';
+$lang['noaction'] = 'Không có động thái nào được xác định';
+$lang['renamepage'] = 'Đổi tên trang';
+$lang['cantrename'] = 'Không thể đổi tên trang này ngay bây giờ. Vui lòng thử lại sau.';
+$lang['js']['rename'] = 'Đổi tên';
+$lang['js']['cancel'] = 'Hủy bỏ';
+$lang['js']['newname'] = 'Tên mới';
+$lang['js']['inprogress'] = 'đang đổi tên trang và điều chỉnh liên kết ...';
+$lang['js']['complete'] = 'Đã hoàn thành hoạt động di chuyển.';
+$lang['js']['renameitem'] = 'Đổi tên mục này';
+$lang['js']['add'] = 'Tạo không gian tên mới';
+$lang['js']['duplicate'] = 'Xin lỗi, đã tồn tại "%s" trong không gian tên này.';
+$lang['root'] = '[Không gian tên Gốc]';
+$lang['noscript'] = 'Tính năng này yêu cầu JavaScript';
+$lang['moveinprogress'] = 'Hiện tại đang diễn ra một hoạt động di chuyển khác, bạn không thể sử dụng công cụ này ngay bây giờ.';
diff --git a/lang/vi/move.txt b/lang/vi/move.txt
new file mode 100644
index 0000000..dd33d2f
--- /dev/null
+++ b/lang/vi/move.txt
@@ -0,0 +1,10 @@
+====== Di chuyển ======
+
+Plugin này cho phép bạn di chuyển và/hoặc đổi tên trang hiện tại hoặc di chuyển và/hoặc đổi tên không gian tên hiện tại với các hạn chế sau:
+
+ * Bạn phải có quyền sửa đổi trang hiện tại/tất cả các trang trong không gian tên hiện tại.
+ * Không được khóa sửa đổi trang được di chuyển.
+ * Để di chuyển một trang, bạn cũng cần tạo quyền trong không gian tên đích.
+ * Bạn không thể di chuyển trang đến không gian tên mà nơi đó đã tồn tại một trang có cùng tên với trang bạn di chuyển.
+
+Tất cả các liên kết đến và từ các trang được di chuyển sẽ được cập nhật để phản ánh vị trí và/hoặc tên mới.
\ No newline at end of file
diff --git a/lang/vi/progress.txt b/lang/vi/progress.txt
new file mode 100644
index 0000000..7e17c5e
--- /dev/null
+++ b/lang/vi/progress.txt
@@ -0,0 +1,3 @@
+====== Đang thực hiện di chuyển... ======
+
+Vui lòng giữ trang này mở trong khi đang thực hiện di chuyển.
\ No newline at end of file
diff --git a/lang/vi/settings.php b/lang/vi/settings.php
new file mode 100644
index 0000000..2788fae
--- /dev/null
+++ b/lang/vi/settings.php
@@ -0,0 +1,12 @@
+
+ */
+$lang['allowrename'] = 'Cho phép đổi tên các trang thành các nhóm và thành viên này (được phân tách bằng dấu phẩy).';
+$lang['minor'] = 'Đánh dấu việc điều chỉnh liên kết là nhỏ? Những thay đổi nhỏ sẽ không được liệt kê trong nguồn cấp dữ liệu RSS và thư đăng ký.';
+$lang['autoskip'] = 'Cho phép tự động bỏ qua các lỗi trong di chuyển không gian tên theo mặc định.';
+$lang['autorewrite'] = 'Cho phép tự động viết lại liên kết sau khi di chuyển không gian tên theo mặc định.';
+$lang['pagetools_integration'] = 'Thêm nút đổi tên vào Công cụ trang';
diff --git a/lang/vi/tree.txt b/lang/vi/tree.txt
new file mode 100644
index 0000000..c69bc04
--- /dev/null
+++ b/lang/vi/tree.txt
@@ -0,0 +1,7 @@
+====== Quản lý di chuyển =====
+
+Giao diện này cho phép bạn sắp xếp lại các không gian tên, trang và tập tin phương tiện wiki của bạn thông qua Kéo và Thả.
+
+Để di chuyển nhiều không gian tên, trang hoặc tập tin phương tiện đến cùng một đích đến, bạn có thể sử dụng các hộp kiểm như sau:
+ * kiểm tra các không gian tên, trang hoặc tập tin phương tiện bạn muốn di chuyển;
+ * di chuyển một trong các mục đã chọn đến đích đến mong muốn, tất cả các mục đã chọn sẽ được di chuyển đến đích đến này.
\ No newline at end of file
diff --git a/lang/zh-tw/lang.php b/lang/zh-tw/lang.php
new file mode 100644
index 0000000..ce4a660
--- /dev/null
+++ b/lang/zh-tw/lang.php
@@ -0,0 +1,66 @@
+
+ * @author Aaron Wang
+ */
+$lang['menu'] = '頁面移動/重新命名……';
+$lang['inprogress'] = '(待移動)';
+$lang['treelink'] = '您還可以通過樹形移動.來為您的wiki條目結構做複雜調整';
+$lang['notexist'] = '頁面 %s 不存在';
+$lang['norights'] = '您沒有權限編輯%s.';
+$lang['filelocked'] = '文件 %s 被鎖定 - 請稍後重試';
+$lang['notchanged'] = '未為頁面%s指定新地址(位置未變)';
+$lang['exists'] = '頁面%s無法被移動至%s ,該目標已存在 ';
+$lang['notargetperms'] = '您沒有權限建立頁面%s.';
+$lang['medianotexist'] = '媒體文件%s不存在';
+$lang['nomediarights'] = '您沒有權限刪除%s.';
+$lang['medianotchanged'] = '未為頁面%s指定新地址(位置未變)';
+$lang['mediaexists'] = '媒體文件%s無法被移動至%s,目標已存在。';
+$lang['nomediatargetperms'] = '您沒有權限建立媒體文件%s。';
+$lang['indexerror'] = '更新索引 %s 時發生錯誤';
+$lang['metamoveerror'] = '頁面 %s 的 meta 無法移動';
+$lang['atticmoveerror'] = '頁面 %s 的 attic 無法移動,請手動搬移';
+$lang['mediametamoveerror'] = '媒體 %s 的 meta 無法移動';
+$lang['mediamoveerror'] = '移動媒體 %s 的 attic 失敗';
+$lang['mediaatticmoveerror'] = '媒體 %s 的 attic 無法移動,請手動搬移';
+$lang['renamed'] = '頁面名由%s改為%s';
+$lang['moved'] = '頁面%s被移動至%s';
+$lang['move_rename'] = '頁面%s被移動並更名為%s';
+$lang['delete'] = '被Move插件刪除';
+$lang['linkchange'] = '鏈結因頁面移動而自動修正';
+$lang['intro'] = '移動操作尚未開始!';
+$lang['preview'] = '預覽即將執行的變更';
+$lang['inexecution'] = '上一個移動尚未完成;請點擊下方按鈕繼續或放棄執行';
+$lang['btn_start'] = '開始';
+$lang['btn_continue'] = '繼續';
+$lang['btn_retry'] = '重試';
+$lang['btn_skip'] = '跳過';
+$lang['btn_abort'] = '放棄';
+$lang['legend'] = '移動當前頁面或目錄';
+$lang['movepage'] = '移動頁面';
+$lang['movens'] = '移動目錄';
+$lang['dst'] = '新名字:';
+$lang['content_to_move'] = '移動內容:';
+$lang['autoskip'] = '忽略錯誤並跳過無法被移動的頁面或文件';
+$lang['autorewrite'] = '待移動完成後立即重寫鏈接';
+$lang['move_pages'] = '頁面';
+$lang['move_media'] = '媒體文件';
+$lang['move_media_and_pages'] = '頁面與媒體文件';
+$lang['nodst'] = '未指定新名字';
+$lang['noaction'] = '未定義任何移動';
+$lang['renamepage'] = '重新命名頁面';
+$lang['cantrename'] = '頁面現在無法重新命名,請稍後再試。';
+$lang['js']['rename'] = '重新命名';
+$lang['js']['cancel'] = '取消';
+$lang['js']['newname'] = '新名字:';
+$lang['js']['inprogress'] = '重新命名頁面並修正有關鏈結:';
+$lang['js']['complete'] = '移動操作完畢。';
+$lang['js']['renameitem'] = '重新命名該項';
+$lang['js']['add'] = '產生新的目錄';
+$lang['js']['duplicate'] = '抱歉,"%s"在該目錄已存在';
+$lang['root'] = '[根目錄]';
+$lang['noscript'] = '此功能需要JavaScript';
+$lang['moveinprogress'] = '另一個移動操作正在進行,您現在無法使用該工具';
diff --git a/lang/zh-tw/move.txt b/lang/zh-tw/move.txt
new file mode 100644
index 0000000..b27a498
--- /dev/null
+++ b/lang/zh-tw/move.txt
@@ -0,0 +1,11 @@
+====== Move ======
+此插件允許您移動並/或重新命名目前頁面,或移動並/或重新命名目前目錄。但是必須符合以下條件:
+
+ *您必須擁有編輯當前頁面/該目錄中所有頁面的權限;
+ *頁面未因編輯而處於鎖定狀態;
+ *為了移動頁面,您必須擁有目的地目錄的寫入權限;
+ *您無法將頁面移動至另一個已有同名頁面的目錄中。
+
+所有來自和指向被移動頁面的鏈接,都將被自動更新以反映新地址和/或新名字。
+
+正體中文化: Aaron Wang
diff --git a/lang/zh-tw/progress.txt b/lang/zh-tw/progress.txt
new file mode 100644
index 0000000..5ae1ebc
--- /dev/null
+++ b/lang/zh-tw/progress.txt
@@ -0,0 +1,3 @@
+====== 正在移動... ======
+
+移動正在進行中,請勿關閉本頁面。
diff --git a/lang/zh-tw/settings.php b/lang/zh-tw/settings.php
new file mode 100644
index 0000000..45b5f1d
--- /dev/null
+++ b/lang/zh-tw/settings.php
@@ -0,0 +1,13 @@
+
+ * @author Aaron Wang
+ */
+$lang['allowrename'] = '允許這些用戶/用戶群組重命名頁面(用逗號分隔)';
+$lang['minor'] = '將鏈接修正標記為『細微變動』,這樣就不會被列入RSS訂閱或訂閱郵件中。';
+$lang['autoskip'] = '目錄移動時,啟用預設自動跳過錯誤';
+$lang['autorewrite'] = '目錄移動時,啟用預設自動修正鏈接';
+$lang['pagetools_integration'] = '在頁面工具欄中加入重新命名';
diff --git a/lang/zh-tw/tree.txt b/lang/zh-tw/tree.txt
new file mode 100644
index 0000000..c2f634e
--- /dev/null
+++ b/lang/zh-tw/tree.txt
@@ -0,0 +1,3 @@
+====== 移動主控台 =====
+
+本頁面允許您透過鼠標拖放,重新安排您wiki的目錄、頁面與媒體文件
diff --git a/lang/zh/lang.php b/lang/zh/lang.php
index cf4b887..2752eb1 100644
--- a/lang/zh/lang.php
+++ b/lang/zh/lang.php
@@ -1,39 +1,73 @@
- * @翻译 TombCrow
+ * @author 小命Leaflet <2532846822@qq.com>
+ * @author Gary Owen
+ * @author TombCrow
+ * @author Hanjia Zhuang
+ * @author kuma
*/
-
-// 此处设置用户语言
-$lang['encoding'] = 'utf-8';
-$lang['direction'] = 'ltr';
-
-// 若在此处设置,插件不必要求 getMenuText() 模式
-$lang['menu'] = '页面移动/重命名……';
-$lang['desc'] = '页面移动/重命名插件';
-
-$lang['pm_notexist'] = '这个主题还不存在';
-$lang['pm_notstart'] = '不能移动或重命名起始页面';
-$lang['pm_notwrite'] = '您无权修改这个页面';
-$lang['pm_badns'] = '分类名中存在无效字符';
-$lang['pm_badname'] = '页面名中存在无效字符';
-$lang['pm_nochange'] = '不能更改文件名和分类名';
-$lang['pm_existing1'] = '文件 ';
-$lang['pm_existing2'] = ' 已经存在于 ';
-$lang['pm_root'] = '[跟目录]';
-$lang['pm_current'] = '(当前)';
-$lang['pm_movedfrom'] = '文件移动自 ';
-$lang['pm_movedto'] = '文件移动至 ';
-$lang['pm_norights'] = '您无权编辑此文件的 backlinks';
-$lang['pm_tryagain'] = '请稍后重试';
-$lang['pm_filelocked'] = '此文件被锁定 - ';
-$lang['pm_fileslocked'] = '这些文件被锁定 - ';
-$lang['pm_linkchange1'] = '链接到 ';
-$lang['pm_linkchange2'] = ' 更改到 ';
-$lang['pm_newname'] = '新的文件名 :';
-$lang['pm_targetns'] = '选择目标分类 :';
-$lang['pm_submit'] = '提交';
-?>
\ No newline at end of file
+$lang['menu'] = '页面移动/重命名……';
+$lang['inprogress'] = '(待移动)';
+$lang['treelink'] = '您还可以通过树形移动.来为您的wiki条目结构做复杂调整';
+$lang['notexist'] = '页面 %s不存在';
+$lang['norights'] = '您没有权限编辑%s.';
+$lang['filelocked'] = '此页面%s被锁定 - 请稍后重试';
+$lang['notchanged'] = '未为页面%s指定新地址(位置未变)';
+$lang['exists'] = '页面%s无法被移动至%s ,该目标已存在 ';
+$lang['notargetperms'] = '您没有权限创建页面%s.';
+$lang['medianotexist'] = '媒体文件%s不存在';
+$lang['nomediarights'] = '您没有权限删除%s.';
+$lang['medianotchanged'] = '未为页面%s指定新地址(位置未变)';
+$lang['mediaexists'] = '媒体文件%s无法被移动至%s,目标已存在。';
+$lang['nomediatargetperms'] = '您没有权限创建媒体文件%s。';
+$lang['extensionchange'] = '新文件的扩展名与原始文件不相同';
+$lang['indexerror'] = '当更新搜索索引 %s 时发生错误';
+$lang['metamoveerror'] = 'meta文件的页面 %s 无法移动';
+$lang['atticmoveerror'] = '页面文件 %s 的attic文件无法移动。请手动移动它们。';
+$lang['mediametamoveerror'] = '媒体文件 %s 的meta文件无法移动';
+$lang['mediamoveerror'] = '移动媒体文件 %s 失败';
+$lang['mediaatticmoveerror'] = '媒体文件 %s 的attic文件无法移动。请手动移动它们。';
+$lang['renamed'] = '页面名由%s改为%s';
+$lang['moved'] = '页面%s被移动至%s';
+$lang['move_rename'] = '页面%s被移动并更名为%s';
+$lang['delete'] = '被Move插件删除';
+$lang['linkchange'] = '链接因页面移动而自动修正';
+$lang['intro'] = '移动操作尚未开始!';
+$lang['preview'] = '预览即将执行的变动';
+$lang['inexecution'] = '上一个移动尚未完成-点击下方按钮继续或放弃执行';
+$lang['btn_start'] = '开始';
+$lang['btn_continue'] = '继续';
+$lang['btn_retry'] = '重试';
+$lang['btn_skip'] = '跳过';
+$lang['btn_abort'] = '放弃';
+$lang['legend'] = '移动当前页面或目录';
+$lang['movepage'] = '移动页面';
+$lang['movens'] = '移动目录';
+$lang['dst'] = '新名字:';
+$lang['content_to_move'] = '移动内容:';
+$lang['autoskip'] = '忽略错误并跳过无法被移动的页面或文件';
+$lang['autorewrite'] = '待移动完成后立即重写链接';
+$lang['move_pages'] = '页面';
+$lang['move_media'] = '媒体文件';
+$lang['move_media_and_pages'] = '页面和媒体文件';
+$lang['nodst'] = '未指定新名字';
+$lang['noaction'] = '未定义任何移动';
+$lang['renamepage'] = '页面重命名';
+$lang['cantrename'] = '页面现在无法重命名,请稍后再试。';
+$lang['notallowed'] = '您没有权限重命名页面或媒体文件';
+$lang['js']['rename'] = '重命名';
+$lang['js']['cancel'] = '取消';
+$lang['js']['newname'] = '新名字:';
+$lang['js']['inprogress'] = '重命名页面并修正有关链接:';
+$lang['js']['complete'] = '移动操作完毕。';
+$lang['js']['renameitem'] = '重命名该项';
+$lang['js']['add'] = '创建一个新的名称空间';
+$lang['js']['duplicate'] = '抱歉,"%s"在该目录已存在';
+$lang['js']['moveButton'] = '文件移动';
+$lang['js']['dialogIntro'] = '输入新文件的目标位置。您可以更改命名空间,但无法更改文件扩展名';
+$lang['root'] = '[跟目录]';
+$lang['noscript'] = '该功能需要JavaScript';
+$lang['moveinprogress'] = '另一个移动操作正在进行,您现在无法使用该工具';
diff --git a/lang/zh/move.txt b/lang/zh/move.txt
new file mode 100644
index 0000000..12c7dc3
--- /dev/null
+++ b/lang/zh/move.txt
@@ -0,0 +1,8 @@
+====== Move ======
+该插件允许您移动并/或重命名当前页面,或移动并/或重命名当前目录。您必须满足以下条件:
+*您必须拥有编辑当前页面/该目录中所有页面的权限;
+*页面未因编辑而处于锁定状态;
+*为移动页面,您必须拥有目的地目录的写权限;
+*您无法将页面移动至一个已有同名页面的目录中。
+
+所有来自和指向被移动页面的链接,都将被自动更新以反映新地址和/或新名字。
\ No newline at end of file
diff --git a/lang/zh/pagemove.txt b/lang/zh/pagemove.txt
deleted file mode 100644
index fcaab5c..0000000
--- a/lang/zh/pagemove.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-
\ No newline at end of file
diff --git a/lang/zh/progress.txt b/lang/zh/progress.txt
new file mode 100644
index 0000000..194f65e
--- /dev/null
+++ b/lang/zh/progress.txt
@@ -0,0 +1,3 @@
+====== 正在移动... ======
+
+移动正在进行中,请勿关闭本页面。
\ No newline at end of file
diff --git a/lang/zh/settings.php b/lang/zh/settings.php
new file mode 100644
index 0000000..6c8609b
--- /dev/null
+++ b/lang/zh/settings.php
@@ -0,0 +1,13 @@
+
+ * @author kuma
+ */
+$lang['allowrename'] = '允许这些用户/用户组重命名页面(用逗号分隔)';
+$lang['minor'] = '将链接修正标记为”细微变动“,这样就不会被列入RSS订阅或订阅邮件中。';
+$lang['autoskip'] = '在目录移动中,启用默认自动跳过错误';
+$lang['autorewrite'] = '在目录移动中,启用默认自动修正链接';
+$lang['pagetools_integration'] = '重命名按钮添加到页面工具';
diff --git a/lang/zh/tree.txt b/lang/zh/tree.txt
new file mode 100644
index 0000000..420cb21
--- /dev/null
+++ b/lang/zh/tree.txt
@@ -0,0 +1,3 @@
+====== 移动控制台 =====
+
+本页面允许您通过鼠标拖放,重新安排您wiki的目录、页面与媒体文件
\ No newline at end of file
diff --git a/plugin.info.txt b/plugin.info.txt
index 7181002..675c418 100644
--- a/plugin.info.txt
+++ b/plugin.info.txt
@@ -1,7 +1,7 @@
-base pagemove
-author Gary Owen, Arno Puschmann, Christoph Jähnigen
-email pagemove@gmail.com
-date 2010-06-17
-name Pagemove plugin
-desc Move and rename pages and namespaces whilst maintaining the links.
-url http://www.dokuwiki.org/plugin:pagemove
+base move
+author Michael Hamann, Gary Owen, Arno Puschmann, Christoph Jähnigen
+email michael@content-space.de
+date 2024-05-07
+name Move plugin
+desc Move and rename pages and media files whilst maintaining the links.
+url https://www.dokuwiki.org/plugin:move
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..9fba837
--- /dev/null
+++ b/script.js
@@ -0,0 +1,15 @@
+/**
+ * includes all needed JavaScript for the move plugin
+ *
+ * be sure to touch this file when one of the scripts has been updated to refresh caching
+ */
+
+/* DOKUWIKI:include_once script/json2.js */
+/* DOKUWIKI:include script/MoveMediaManager.js */
+
+jQuery(function() {
+ /* DOKUWIKI:include script/form.js */
+ /* DOKUWIKI:include script/progress.js */
+ /* DOKUWIKI:include script/tree.js */
+ /* DOKUWIKI:include script/rename.js */
+});
diff --git a/script/MoveMediaManager.js b/script/MoveMediaManager.js
new file mode 100644
index 0000000..cca7cdf
--- /dev/null
+++ b/script/MoveMediaManager.js
@@ -0,0 +1,174 @@
+/**
+ * Integrates move capability into the media manager
+ * Based on the implementation in diagrams plugin
+ */
+class MoveMediaManager {
+
+ constructor() {
+ // user is not allowed to move anything
+ if (!JSINFO.move_allowrename) return;
+
+ const filePanel = document.querySelector('#mediamanager__page .panel.file');
+ if (filePanel) {
+ const observer = new MutationObserver(this.#addMoveButton.bind(this));
+ observer.observe(filePanel, {childList: true, subtree: true});
+ }
+ }
+
+ /**
+ * Observer callback to add the move button in the detail panel of the media manager
+ *
+ * @param mutationsList
+ * @param observer
+ */
+ async #addMoveButton(mutationsList, observer) {
+ for (let mutation of mutationsList) {
+ // div.file has been filled with new content?
+ if (mutation.type !== 'childList') continue;
+
+ // check that the file panel contains a link to a file
+ if (mutation.target.classList.contains('file') === false) continue;
+ const link = mutation.target.querySelector('a.select.mediafile');
+ if (!link) continue;
+
+ const actionList = mutation.target.querySelector('ul.actions');
+ if (actionList.querySelector('button.move-btn')) continue; // already added
+
+ const deleteButton = actionList.querySelector('form#mediamanager__btn_delete');
+ if (deleteButton === null) continue; // no delete permissions
+
+ const src = link.textContent;
+
+ const moveButton = document.createElement('button');
+ moveButton.classList.add('move-btn');
+ moveButton.innerText = LANG.plugins.move.moveButton;
+
+ moveButton.addEventListener('click', this.#showDialog.bind(this, src));
+ actionList.appendChild(moveButton);
+ }
+ }
+
+ /**
+ * Show the move dialog
+ *
+ * Uses JQuery UI
+ *
+ * @param {string} src
+ * @param {Event} event
+ * @returns {Promise}
+ */
+ async #showDialog(src, event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const $form = jQuery(this.#buildForm(src));
+ $form.dialog({
+ title: LANG.plugins.move.moveButton,
+ width: 600,
+ appendTo: '.dokuwiki',
+ modal: true,
+ close: function () {
+ // do not reuse the dialog
+ // https://stackoverflow.com/a/2864783
+ jQuery(this).dialog('destroy').remove();
+ }
+ });
+ }
+
+ /**
+ * Create the form for the old and new file names
+ *
+ * @param {string} src
+ * @returns {HTMLDivElement}
+ */
+ #buildForm(src) {
+ const wrapper = document.createElement('div');
+ const form = document.createElement('form');
+ wrapper.appendChild(form);
+
+ const intro = document.createElement('p');
+ intro.innerText = LANG.plugins.move.dialogIntro;
+ form.appendChild(intro);
+
+ const errorContainer = document.createElement('div');
+ errorContainer.className = 'move-error';
+ form.appendChild(errorContainer);
+
+ const original = document.createElement('input');
+ original.type = 'hidden';
+ original.name = 'move-old-filename';
+ original.value = src;
+ form.appendChild(original);
+
+ const sectok = document.querySelector('form#mediamanager__btn_delete input[name=sectok]').cloneNode();
+ form.appendChild(sectok);
+
+ // strip file extension and put it in a readonly field so it may not be modified
+ const fileExt = document.createElement('input');
+ fileExt.type = 'text';
+ fileExt.readOnly = true;
+ fileExt.size = 5;
+ fileExt.name = 'move-file-ext';
+ fileExt.value = src.split('.').pop();
+
+ const destination = document.createElement('input');
+ destination.type = 'text';
+ destination.className = 'edit';
+ destination.name = 'move-new-filename';
+ destination.value = src.substring(0, src.length - (fileExt.value.length + 1));
+ destination.size = 50;
+ form.appendChild(destination);
+ form.appendChild(fileExt);
+
+ const button = document.createElement('button');
+ button.innerText = LANG.plugins.move.moveButton;
+ form.appendChild(button);
+
+ form.addEventListener('submit', this.#requestMove.bind(this, form));
+
+ return wrapper;
+ }
+
+ /**
+ * Send move request to backend
+ *
+ * @param {HTMLFormElement} form
+ * @param {Event} event
+ * @returns {Promise}
+ */
+ async #requestMove(form, event) {
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ const src = form.querySelector('input[name="move-old-filename"]').value;
+ const dst = form.querySelector('input[name="move-new-filename"]').value;
+ const ext = form.querySelector('input[name="move-file-ext"]').value;
+ const sectok = form.querySelector('input[name="sectok"]').value;
+ const err = form.querySelector('div.move-error');
+
+ jQuery.post(
+ DOKU_BASE + 'lib/exe/ajax.php',
+ {
+ call: 'plugin_move_rename_mediamanager',
+ src: src,
+ dst: dst + '.' + ext,
+ sectok: sectok
+ },
+ // redirect or display error
+ function (result) {
+ if (result.success) {
+ window.location.href = result.redirect_url;
+ } else {
+ err.classList.add('error');
+ err.innerText = result.error;
+ }
+ }
+ );
+ }
+}
+
+// initialize
+document.addEventListener('DOMContentLoaded', () => {
+ new MoveMediaManager();
+});
diff --git a/script/form.js b/script/form.js
new file mode 100644
index 0000000..31e8de5
--- /dev/null
+++ b/script/form.js
@@ -0,0 +1,14 @@
+jQuery('form.plugin_move_form').each(function(){
+ var $form = jQuery(this);
+
+ $form.find('.click-page').click(function() {
+ $form.find('input[name=dst]').val($form.find('.click-page code').text());
+ $form.find('.select').hide();
+ }).click();
+
+ $form.find('.click-ns').click(function() {
+ $form.find('input[name=dst]').val($form.find('.click-ns code').text());
+ $form.find('.select').show();
+ });
+
+});
\ No newline at end of file
diff --git a/script/json2.js b/script/json2.js
new file mode 100644
index 0000000..d89ecc7
--- /dev/null
+++ b/script/json2.js
@@ -0,0 +1,486 @@
+/*
+ json2.js
+ 2013-05-26
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value any JavaScript value, usually an object or array.
+
+ replacer an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or ' '),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the value
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+*/
+
+/*jslint evil: true, regexp: true */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== 'object') {
+ JSON = {};
+}
+
+(function () {
+ 'use strict';
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function () {
+
+ return isFinite(this.valueOf())
+ ? this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z'
+ : null;
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function () {
+ return this.valueOf();
+ };
+ }
+
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string'
+ ? c
+ : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0
+ ? '[]'
+ : gap
+ ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
+ : '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0
+ ? '{}'
+ : gap
+ ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
+ : '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== 'function') {
+ JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+ }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== 'function') {
+ JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function'
+ ? walk({'': j}, '')
+ : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+ }
+}());
diff --git a/script/progress.js b/script/progress.js
new file mode 100644
index 0000000..2a32d8b
--- /dev/null
+++ b/script/progress.js
@@ -0,0 +1,98 @@
+jQuery('#plugin_move__progress').each(function () {
+ var $this = jQuery(this);
+
+ // initialize the progress bar
+ var $progressbar = $this.find('.progress');
+ $progressbar.html('');
+ $progressbar.progressbar({
+ value: $progressbar.data('progress')
+ });
+
+ /**
+ * Set visibility of buttons according to current error state
+ *
+ * @param isError
+ */
+ var setButtons = function(isError) {
+ $this.find('.ctlfrm-start').addClass('hide');
+
+ if(isError) {
+ $this.find('.ctlfrm-skip').removeClass('hide');
+ $this.find('.ctlfrm-retry').removeClass('hide');
+ $this.find('.ctlfrm-continue').addClass('hide');
+ }else {
+ $this.find('.ctlfrm-skip').addClass('hide');
+ $this.find('.ctlfrm-retry').addClass('hide');
+ $this.find('.ctlfrm-continue').addClass('hide');
+ }
+ };
+
+ /**
+ * Execute the next steps
+ *
+ * @param {bool} skip should an error be skipped?
+ */
+ var nextStep = function(skip) {
+ // clear error output
+ $this.find('.output').html('');
+
+ $this.find('.controls img').removeClass('hide');
+ setButtons(false);
+
+ // execute AJAX
+ jQuery.post(
+ DOKU_BASE + 'lib/exe/ajax.php',
+ {
+ call: 'plugin_move_progress',
+ skip: skip
+ },
+ function (data) {
+ $progressbar.progressbar('option', 'value', data.progress);
+ $this.find('.controls img').addClass('hide');
+
+ if (data.error) {
+ $this.find('.output').html('
' + data.error + '
');
+ setButtons(true);
+ } else if (data.complete) {
+ $progressbar.progressbar('option', 'value', 100);
+ // redirect to start page
+ alert(LANG.plugins.move.complete);
+ window.location.href = DOKU_BASE;
+ } else {
+ // do it again
+ nextStep(skip);
+ }
+ }
+ );
+ };
+
+
+
+ // attach AJAX actions to buttons
+ $this.find('.ctl-continue').click(function (e) {
+ e.preventDefault();
+
+ // move in progress, no more preview
+ jQuery('#plugin_move__preview').remove();
+
+ // should the next error be skipped?
+ var skip = e.target.form.skip.value;
+
+ // step on it
+ nextStep(skip);
+ });
+
+});
+
+
+// hide preview list on namespace move
+jQuery('#plugin_move__preview').each(function () {
+ var $this = jQuery(this);
+ $this.find('ul').hide();
+ $this.find('span')
+ .click(function () {
+ $this.find('ul').dw_toggle();
+ $this.find('span').toggleClass('closed');
+ })
+ .addClass('closed');
+});
diff --git a/script/rename.js b/script/rename.js
new file mode 100644
index 0000000..03a8e5f
--- /dev/null
+++ b/script/rename.js
@@ -0,0 +1,129 @@
+/**
+ * Rename dialog for end users
+ *
+ * @author Andreas Gohr
+ */
+(function () {
+ if (!JSINFO || !JSINFO.move_renameokay) return;
+
+
+ // basic dialog template
+ const $dialog = jQuery(
+ '
' +
+ '' +
+ '
'
+ );
+
+ /**
+ * Executes the renaming based on the form contents
+ * @return {boolean}
+ */
+ const renameFN = function () {
+ const newid = $dialog.find('input[name=id]').val();
+ if (!newid) return false;
+
+ // remove buttons and show throbber
+ $dialog.html(
+ ' ' +
+ LANG.plugins.move.inprogress
+ );
+ $dialog.dialog('option', 'buttons', []);
+
+ // post the data
+ jQuery.post(
+ DOKU_BASE + 'lib/exe/ajax.php',
+ {
+ call: 'plugin_move_rename',
+ id: JSINFO.id,
+ newid: newid
+ },
+ // redirect or display error
+ function (result) {
+ if (result.error) {
+ $dialog.html(result.error.msg);
+ } else {
+ window.location.href = result.redirect_url;
+ }
+ }
+ );
+
+ return false;
+ };
+
+ /**
+ * Create the actual dialog modal and show it
+ */
+ const showDialog = function () {
+ $dialog.dialog({
+ title: LANG.plugins.move.rename + ' ' + JSINFO.id,
+ width: 800,
+ height: 200,
+ dialogClass: 'plugin_move_dialog',
+ modal: true,
+ buttons: [
+ {
+ text: LANG.plugins.move.cancel,
+ click: function () {
+ $dialog.dialog("close");
+ }
+ },
+ {
+ text: LANG.plugins.move.rename,
+ click: renameFN
+ }
+ ],
+ // remove HTML from DOM again
+ close: function () {
+ jQuery(this).remove();
+ }
+ });
+ $dialog.find('input[name=id]').val(JSINFO.id);
+ $dialog.find('form').submit(renameFN);
+ };
+
+ /**
+ * Bind an event handler as the first handler
+ *
+ * @param {jQuery} $owner
+ * @param {string} event
+ * @param {function} handler
+ * @link https://stackoverflow.com/a/4700103
+ */
+ const bindFirst = function ($owner, event, handler) {
+ $owner.unbind(event, handler);
+ $owner.bind(event, handler);
+
+ const events = jQuery._data($owner[0])['events'][event];
+ events.unshift(events.pop());
+
+ jQuery._data($owner[0])['events'][event] = events;
+ };
+
+
+ // attach handler to menu item
+ jQuery('.plugin_move_page')
+ .show()
+ .click(function (e) {
+ e.preventDefault();
+ showDialog();
+ });
+
+ // attach handler to mobile menu entry
+ const $mobileMenuOption = jQuery('form select[name=do] option[value=plugin_move]');
+ if ($mobileMenuOption.length === 1) {
+ bindFirst($mobileMenuOption.closest('select[name=do]'), 'change', function (e) {
+ const $select = jQuery(this);
+ if ($select.val() !== 'plugin_move') return;
+ e.preventDefault();
+ e.stopPropagation();
+ e.stopImmediatePropagation();
+ $select.val('');
+ showDialog();
+ });
+ }
+
+})();
diff --git a/script/tree.js b/script/tree.js
new file mode 100644
index 0000000..6dcd650
--- /dev/null
+++ b/script/tree.js
@@ -0,0 +1,260 @@
+/**
+ * Script for the tree management interface
+ */
+
+var $GUI = jQuery('#plugin_move__tree');
+
+$GUI.show();
+jQuery('#plugin_move__treelink').show();
+
+/**
+ * Checks if the given list item was moved in the tree
+ *
+ * Moved elements are highlighted and a title shows where they came from
+ *
+ * @param {jQuery} $li
+ */
+var checkForMovement = function ($li) {
+ // we need to check this LI and all previously moved sub LIs
+ var $all = $li.add($li.find('li.moved'));
+ $all.each(function () {
+ var $this = jQuery(this);
+ var oldid = $this.data('id');
+ var newid = determineNewID($this);
+
+ if (newid != oldid && !$this.hasClass('created')) {
+ $this.addClass('moved');
+ $this.children('div').attr('title', oldid + ' -> ' + newid);
+ } else {
+ $this.removeClass('moved');
+ $this.children('div').attr('title', '');
+ }
+ });
+};
+
+/**
+ * Check if the given name is allowed in the given parent
+ *
+ * @param {jQuery} $li the edited or moved LI
+ * @param {jQuery} $parent the (new) parent of the edited or moved LI
+ * @param {string} name the (new) name to check
+ * @returns {boolean}
+ */
+var checkNameAllowed = function ($li, $parent, name) {
+ var ok = true;
+ $parent.children('li').each(function () {
+ if (this === $li[0]) return;
+ var cname = 'type-f';
+ if ($li.hasClass('type-d')) cname = 'type-d';
+
+ var $this = jQuery(this);
+ if ($this.data('name') == name && $this.hasClass(cname)) ok = false;
+ });
+ return ok;
+};
+
+/**
+ * Returns the new ID of a given list item
+ *
+ * @param {jQuery} $li
+ * @returns {string}
+ */
+var determineNewID = function ($li) {
+ var myname = $li.data('name');
+
+ var $parent = $li.parent().closest('li');
+ if ($parent.length) {
+ return (determineNewID($parent) + ':' + myname).replace(/^:/, '');
+ } else {
+ return myname;
+ }
+};
+
+/**
+ * Very simplistic cleanID() in JavaScript
+ *
+ * Strips out namespaces
+ *
+ * @param {string} id
+ */
+var cleanID = function (id) {
+ if (!id) return '';
+
+ id = id.replace(/[!"#$%§&\'()+,/;<=>?@\[\]^`\{|\}~\\;:\/\*]+/g, '_');
+ id = id.replace(/^_+/, '');
+ id = id.replace(/_+$/, '');
+ id = id.toLowerCase();
+
+ return id;
+};
+
+/**
+ * Initialize the drag & drop-tree at the given li (must be this).
+ */
+var initTree = function () {
+ var $li = jQuery(this);
+ var my_root = $li.closest('.tree_root')[0];
+ $li.draggable({
+ revert: true,
+ revertDuration: 0,
+ opacity: 0.5,
+ stop : function(event, ui) {
+ ui.helper.css({height: "auto", width: "auto"});
+ }
+ }).droppable({
+ tolerance: 'pointer',
+ greedy: true,
+ accept : function(draggable) {
+ return my_root == draggable.closest('.tree_root')[0];
+ },
+ drop : function (event, ui) {
+ var $dropped = ui.draggable;
+ var $me = jQuery(this);
+
+ if ($dropped.children('div.li').children('input').prop('checked')) {
+ $dropped = $dropped.add(
+ jQuery(my_root)
+ .find('input')
+ .filter(function() {
+ return jQuery(this).prop('checked');
+ }).parent().parent()
+ );
+ }
+
+ if ($me.parents().addBack().is($dropped)) {
+ return;
+ }
+
+ var insert_child = !($me.hasClass("type-f") || $me.hasClass("closed"));
+ var $new_parent = insert_child ? $me.children('ul') : $me.parent();
+ var allowed = true;
+
+ $dropped.each(function () {
+ var $this = jQuery(this);
+ allowed &= checkNameAllowed($this, $new_parent, $this.data('name'));
+ });
+
+ if (allowed) {
+ if (insert_child) {
+ $dropped.prependTo($new_parent);
+ } else {
+ $dropped.insertAfter($me);
+ }
+ }
+
+ checkForMovement($dropped);
+ }
+ })
+ // add title to rename icon
+ .find('img.rename').attr('title', LANG.plugins.move.renameitem)
+ .end()
+ .find('img.add').attr('title', LANG.plugins.move.add);
+};
+
+var add_template = '