summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_test/cases/inc/search/data/ns1/ns3/page3.txt0
-rw-r--r--_test/cases/inc/search/data/ns1/page1.txt0
-rw-r--r--_test/cases/inc/search/data/ns1/page2.txt0
-rw-r--r--_test/cases/inc/search/data/ns2/nopage.ext0
-rw-r--r--_test/cases/inc/search/data/ns2/page1.txt0
-rw-r--r--_test/cases/inc/search/search.test.php102
-rw-r--r--conf/dokuwiki.php1
-rw-r--r--conf/mediameta.php44
-rw-r--r--data/media_attic/_dummy0
-rw-r--r--data/media_meta/_dummy0
-rw-r--r--feed.php151
-rw-r--r--inc/HTTPClient.php1
-rw-r--r--inc/actions.php2
-rw-r--r--inc/changelog.php77
-rw-r--r--inc/common.php1
-rw-r--r--inc/html.php216
-rw-r--r--inc/init.php2
-rw-r--r--inc/lang/en/lang.php46
-rw-r--r--inc/lang/en/uploadmail.txt17
-rw-r--r--inc/lang/es/lang.php3
-rw-r--r--inc/lang/pl/lang.php6
-rw-r--r--inc/media.php1202
-rw-r--r--inc/pageutils.php26
-rw-r--r--inc/parser/xhtml.php6
-rw-r--r--inc/search.php59
-rw-r--r--inc/template.php146
-rw-r--r--lib/exe/ajax.php95
-rw-r--r--lib/exe/css.php4
-rw-r--r--lib/exe/fetch.php10
-rw-r--r--lib/exe/js.php2
-rw-r--r--lib/exe/mediamanager.php24
-rw-r--r--lib/images/icon-file.pngbin0 -> 3363 bytes
-rw-r--r--lib/images/icon-list.pngbin0 -> 3342 bytes
-rw-r--r--lib/images/icon-sort.pngbin0 -> 316 bytes
-rw-r--r--lib/images/icon-thumb.pngbin0 -> 969 bytes
-rw-r--r--lib/plugins/acl/lang/es/lang.php1
-rw-r--r--lib/plugins/acl/lang/pl/lang.php1
-rw-r--r--lib/plugins/acl/script.js12
-rw-r--r--lib/plugins/config/lang/es/lang.php2
-rw-r--r--lib/plugins/config/lang/pl/lang.php1
-rw-r--r--lib/plugins/plugin/lang/es/lang.php1
-rw-r--r--lib/plugins/plugin/lang/pl/lang.php1
-rw-r--r--lib/plugins/popularity/lang/es/lang.php1
-rw-r--r--lib/plugins/popularity/lang/pl/lang.php6
-rw-r--r--lib/plugins/popularity/lang/pl/submitted.txt3
-rw-r--r--lib/plugins/popularity/lang/uk/submitted.txt2
-rw-r--r--lib/plugins/revert/lang/es/lang.php1
-rw-r--r--lib/plugins/revert/lang/pl/lang.php1
-rw-r--r--lib/plugins/usermanager/lang/es/lang.php1
-rw-r--r--lib/plugins/usermanager/lang/pl/lang.php1
-rw-r--r--lib/scripts/behaviour.js42
-rw-r--r--lib/scripts/compatibility.js172
-rw-r--r--lib/scripts/cookie.js71
-rw-r--r--lib/scripts/fileuploader.js1247
-rw-r--r--lib/scripts/fileuploaderextended.js339
-rw-r--r--lib/scripts/helpers.js176
-rw-r--r--lib/scripts/jquery/jquery.cookie.js111
-rw-r--r--lib/scripts/locktimer.js56
-rw-r--r--lib/scripts/media.js465
-rw-r--r--lib/scripts/page.js70
-rw-r--r--lib/tpl/default/_fileuploader.css106
-rw-r--r--lib/tpl/default/design.css6
-rw-r--r--lib/tpl/default/detail.php48
-rw-r--r--lib/tpl/default/images/resizecol.pngbin0 -> 443 bytes
-rw-r--r--lib/tpl/default/main.php1
-rw-r--r--lib/tpl/default/media.css5
-rw-r--r--lib/tpl/default/mediamanager.css487
-rw-r--r--lib/tpl/default/style.ini2
68 files changed, 4943 insertions, 739 deletions
diff --git a/_test/cases/inc/search/data/ns1/ns3/page3.txt b/_test/cases/inc/search/data/ns1/ns3/page3.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/_test/cases/inc/search/data/ns1/ns3/page3.txt
diff --git a/_test/cases/inc/search/data/ns1/page1.txt b/_test/cases/inc/search/data/ns1/page1.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/_test/cases/inc/search/data/ns1/page1.txt
diff --git a/_test/cases/inc/search/data/ns1/page2.txt b/_test/cases/inc/search/data/ns1/page2.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/_test/cases/inc/search/data/ns1/page2.txt
diff --git a/_test/cases/inc/search/data/ns2/nopage.ext b/_test/cases/inc/search/data/ns2/nopage.ext
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/_test/cases/inc/search/data/ns2/nopage.ext
diff --git a/_test/cases/inc/search/data/ns2/page1.txt b/_test/cases/inc/search/data/ns2/page1.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/_test/cases/inc/search/data/ns2/page1.txt
diff --git a/_test/cases/inc/search/search.test.php b/_test/cases/inc/search/search.test.php
new file mode 100644
index 000000000..a6f15e9b6
--- /dev/null
+++ b/_test/cases/inc/search/search.test.php
@@ -0,0 +1,102 @@
+<?php
+require_once DOKU_INC.'inc/search.php';
+
+class search_test extends UnitTestCase {
+ function strip_index_data($entry) {
+ $n_entry = array();
+ foreach(array('id', 'type', 'level', 'open') as $k) {
+ $n_entry[$k] = $entry[$k];
+ }
+ return $n_entry;
+ }
+
+ function test_search_index(){
+ $data = array();
+ search($data, dirname(__FILE__) . '/data', 'search_index',
+ array('ns' => 'ns2'));
+ $this->assertEqual(array_map(array($this, 'strip_index_data'), $data),
+ array(
+ array(
+ 'id' => 'ns1',
+ 'type' => 'd',
+ 'level' => 1,
+ 'open' => false
+ ), array(
+ 'id' => 'ns2',
+ 'type' => 'd',
+ 'level' => 1,
+ 'open' => true
+ ), array(
+ 'id' => 'ns2:page1',
+ 'type' => 'f',
+ 'level' => 2,
+ 'open' => true,
+ ), ));
+ $data = array();
+ search($data, dirname(__FILE__) . '/data', 'search_index',
+ array('ns' => 'ns1/ns3'));
+ $this->assertEqual(array_map(array($this, 'strip_index_data'), $data),
+ array(
+ array(
+ 'id' => 'ns1',
+ 'type' => 'd',
+ 'level' => 1,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns1:ns3',
+ 'type' => 'd',
+ 'level' => 2,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns1:ns3:page3',
+ 'type' => 'f',
+ 'level' => 3,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns1:page1',
+ 'type' => 'f',
+ 'level' => 2,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns1:page2',
+ 'type' => 'f',
+ 'level' => 2,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns2',
+ 'type' => 'd',
+ 'level' => 1,
+ 'open' => false,
+ ), ));
+ $data = array();
+ search($data, dirname(__FILE__) . '/data', 'search_index',
+ array('ns' => 'ns1/ns3', 'nofiles' => true));
+ $this->assertEqual(array_map(array($this, 'strip_index_data'), $data),
+ array(
+ array(
+ 'id' => 'ns1',
+ 'type' => 'd',
+ 'level' => 1,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns1:ns3',
+ 'type' => 'd',
+ 'level' => 2,
+ 'open' => true,
+ ),
+ array(
+ 'id' => 'ns2',
+ 'type' => 'd',
+ 'level' => 1,
+ 'open' => false,
+ ), ));
+
+ }
+}
+//Setup VIM: ex: et ts=4 :
diff --git a/conf/dokuwiki.php b/conf/dokuwiki.php
index 5e185e9c2..298c8e572 100644
--- a/conf/dokuwiki.php
+++ b/conf/dokuwiki.php
@@ -20,6 +20,7 @@ $conf['basedir'] = ''; //absolute dir from serveroot - blank
$conf['baseurl'] = ''; //URL to server including protocol - blank for autodetect
$conf['savedir'] = './data'; //where to store all the files
$conf['allowdebug'] = 0; //allow debug output, enable if needed 0|1
+$conf['mediarevisions'] = 1; //enable/disable media revisions
/* Display Options */
diff --git a/conf/mediameta.php b/conf/mediameta.php
index ba7117643..0428a4b88 100644
--- a/conf/mediameta.php
+++ b/conf/mediameta.php
@@ -29,29 +29,63 @@ $fields = array(
'img_title',
'text'),
- 20 => array('Iptc.Caption',
+ 20 => array('',
+ 'img_date',
+ 'date',
+ array('Date.EarliestTime')),
+
+ 30 => array('',
+ 'img_fname',
+ 'text',
+ array('File.Name')),
+
+ 40 => array('Iptc.Caption',
'img_caption',
'textarea',
array('Exif.UserComment',
'Exif.TIFFImageDescription',
'Exif.TIFFUserComment')),
- 30 => array('Iptc.Byline',
+ 50 => array('Iptc.Byline',
'img_artist',
'text',
array('Exif.TIFFArtist',
'Exif.Artist',
'Iptc.Credit')),
- 40 => array('Iptc.CopyrightNotice',
+ 60 => array('Iptc.CopyrightNotice',
'img_copyr',
'text',
array('Exif.TIFFCopyright',
'Exif.Copyright')),
- 50 => array('Iptc.Keywords',
+ 70 => array('',
+ 'img_format',
+ 'text',
+ array('File.Format')),
+
+ 80 => array('',
+ 'img_fsize',
+ 'text',
+ array('File.NiceSize')),
+
+ 90 => array('',
+ 'img_width',
+ 'text',
+ array('File.Width')),
+
+ 100 => array('',
+ 'img_height',
+ 'text',
+ array('File.Height')),
+
+ 110 => array('',
+ 'img_camera',
+ 'text',
+ array('Simple.Camera')),
+
+ 120 => array('Iptc.Keywords',
'img_keywords',
'text',
array('Exif.Category')),
);
-
diff --git a/data/media_attic/_dummy b/data/media_attic/_dummy
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/data/media_attic/_dummy
diff --git a/data/media_meta/_dummy b/data/media_meta/_dummy
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/data/media_meta/_dummy
diff --git a/feed.php b/feed.php
index cca8661f4..298777eb9 100644
--- a/feed.php
+++ b/feed.php
@@ -117,6 +117,8 @@ function rss_parseOptions(){
'show_minor' => array('minor', false),
// String, only used in search mode
'search_query' => array('q', null),
+ // One of: pages, media, both
+ 'content_type' => array('view', 'both')
) as $name => $val) {
$opt[$name] = (isset($_REQUEST[$val[0]]) && !empty($_REQUEST[$val[0]]))
@@ -188,7 +190,9 @@ function rss_buildItems(&$rss,&$data,$opt){
$item = new FeedItem();
$id = $ditem['id'];
- $meta = p_get_metadata($id);
+ if(!$ditem['media']) {
+ $meta = p_get_metadata($id);
+ }
// add date
if($ditem['date']){
@@ -213,62 +217,134 @@ function rss_buildItems(&$rss,&$data,$opt){
// add item link
switch ($opt['link_to']){
case 'page':
- $item->link = wl($id,'rev='.$date,true,'&');
+ if ($ditem['media']) {
+ $item->link = media_managerURL(array('image' => $id,
+ 'ns' => getNS($id),
+ 'rev' => $date), '&', true);
+ } else {
+ $item->link = wl($id,'rev='.$date,true,'&', true);
+ }
break;
case 'rev':
- $item->link = wl($id,'do=revisions&rev='.$date,true,'&');
+ if ($ditem['media']) {
+ $item->link = media_managerURL(array('image' => $id,
+ 'ns' => getNS($id),
+ 'rev' => $date,
+ 'tab_details' => 'history'), '&', true);
+ } else {
+ $item->link = wl($id,'do=revisions&rev='.$date,true,'&');
+ }
break;
case 'current':
- $item->link = wl($id, '', true,'&');
+ if ($ditem['media']) {
+ $item->link = media_managerURL(array('image' => $id,
+ 'ns' => getNS($id)), '&', true);
+ } else {
+ $item->link = wl($id, '', true,'&');
+ }
break;
case 'diff':
default:
- $item->link = wl($id,'rev='.$date.'&do=diff',true,'&');
+ if ($ditem['media']) {
+ $item->link = media_managerURL(array('image' => $id,
+ 'ns' => getNS($id),
+ 'rev' => $date,
+ 'tab_details' => 'history',
+ 'mediado' => 'diff'), '&', true);
+ } else {
+ $item->link = wl($id,'rev='.$date.'&do=diff',true,'&');
+ }
}
// add item content
switch ($opt['item_content']){
case 'diff':
case 'htmldiff':
- require_once(DOKU_INC.'inc/DifferenceEngine.php');
- $revs = getRevisions($id, 0, 1);
- $rev = $revs[0];
-
- if($rev){
- $df = new Diff(explode("\n",htmlspecialchars(rawWiki($id,$rev))),
- explode("\n",htmlspecialchars(rawWiki($id,''))));
- }else{
- $df = new Diff(array(''),
- explode("\n",htmlspecialchars(rawWiki($id,''))));
- }
-
- if($opt['item_content'] == 'htmldiff'){
- $tdf = new TableDiffFormatter();
- $content = '<table>';
- $content .= '<tr><th colspan="2" width="50%">'.$rev.'</th>';
- $content .= '<th colspan="2" width="50%">'.$lang['current'].'</th></tr>';
- $content .= $tdf->format($df);
- $content .= '</table>';
- }else{
- $udf = new UnifiedDiffFormatter();
- $content = "<pre>\n".$udf->format($df)."\n</pre>";
+ if ($ditem['media']) {
+ $revs = getRevisions($id, 0, 1, 8192, true);
+ $rev = $revs[0];
+ $src_r = '';
+ $src_l = '';
+
+ if ($size = media_image_preview_size($id, false, new JpegMeta(mediaFN($id)), 300)) {
+ $more = 'w='.$size[0].'&h='.$size[1].'t='.@filemtime(mediaFN($id));
+ $src_r = ml($id, $more);
+ }
+ if ($rev && $size = media_image_preview_size($id, $rev, new JpegMeta(mediaFN($id, $rev)), 300)){
+ $more = 'rev='.$rev.'&w='.$size[0].'&h='.$size[1];
+ $src_l = ml($id, $more);
+ }
+ $content = '';
+ if ($src_r) {
+ $content = '<table>';
+ $content .= '<tr><th width="50%">'.$rev.'</th>';
+ $content .= '<th width="50%">'.$lang['current'].'</th></tr>';
+ $content .= '<tr align="center"><td><img src="'.$src_l.'" alt="" /></td><td>';
+ $content .= '<img src="'.$src_r.'" alt="'.$id.'" /></td></tr>';
+ $content .= '</table>';
+ }
+
+ } else {
+ require_once(DOKU_INC.'inc/DifferenceEngine.php');
+ $revs = getRevisions($id, 0, 1);
+ $rev = $revs[0];
+
+ if($rev){
+ $df = new Diff(explode("\n",htmlspecialchars(rawWiki($id,$rev))),
+ explode("\n",htmlspecialchars(rawWiki($id,''))));
+ }else{
+ $df = new Diff(array(''),
+ explode("\n",htmlspecialchars(rawWiki($id,''))));
+ }
+
+ if($opt['item_content'] == 'htmldiff'){
+ $tdf = new TableDiffFormatter();
+ $content = '<table>';
+ $content .= '<tr><th colspan="2" width="50%">'.$rev.'</th>';
+ $content .= '<th colspan="2" width="50%">'.$lang['current'].'</th></tr>';
+ $content .= $tdf->format($df);
+ $content .= '</table>';
+ }else{
+ $udf = new UnifiedDiffFormatter();
+ $content = "<pre>\n".$udf->format($df)."\n</pre>";
+ }
}
break;
case 'html':
- $content = p_wiki_xhtml($id,$date,false);
- // no TOC in feeds
- $content = preg_replace('/(<!-- TOC START -->).*(<!-- TOC END -->)/s','',$content);
-
- // make URLs work when canonical is not set, regexp instead of rerendering!
- if(!$conf['canonical']){
- $base = preg_quote(DOKU_REL,'/');
- $content = preg_replace('/(<a href|<img src)="('.$base.')/s','$1="'.DOKU_URL,$content);
+ if ($ditem['media']) {
+ if ($size = media_image_preview_size($id, false, new JpegMeta(mediaFN($id)))) {
+ $more = 'w='.$size[0].'&h='.$size[1].'t='.@filemtime(mediaFN($id));
+ $src = ml($id, $more);
+ $content = '<img src="'.$src.'" alt="'.$id.'" />';
+ } else {
+ $content = '';
+ }
+ } else {
+ $content = p_wiki_xhtml($id,$date,false);
+ // no TOC in feeds
+ $content = preg_replace('/(<!-- TOC START -->).*(<!-- TOC END -->)/s','',$content);
+
+ // make URLs work when canonical is not set, regexp instead of rerendering!
+ if(!$conf['canonical']){
+ $base = preg_quote(DOKU_REL,'/');
+ $content = preg_replace('/(<a href|<img src)="('.$base.')/s','$1="'.DOKU_URL,$content);
+ }
}
break;
case 'abstract':
default:
- $content = $meta['description']['abstract'];
+ if ($ditem['media']) {
+ if ($size = media_image_preview_size($id, false, new JpegMeta(mediaFN($id)))) {
+ $more = 'w='.$size[0].'&h='.$size[1].'t='.@filemtime(mediaFN($id));
+ $src = ml($id, $more);
+ $content = '<img src="'.$src.'" alt="'.$id.'" />';
+ } else {
+ $content = '';
+ }
+ } else {
+ $content = $meta['description']['abstract'];
+ }
}
$item->description = $content; //FIXME a plugin hook here could be senseful
@@ -334,8 +410,11 @@ function rss_buildItems(&$rss,&$data,$opt){
* @author Andreas Gohr <andi@splitbrain.org>
*/
function rssRecentChanges($opt){
+ global $conf;
$flags = RECENTS_SKIP_DELETED;
if(!$opt['show_minor']) $flags += RECENTS_SKIP_MINORS;
+ if($opt['content_type'] == 'media' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_CHANGES;
+ if($opt['content_type'] == 'both' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_PAGES_MIXED;
$recents = getRecents(0,$opt['items'],$opt['namespace'],$flags);
return $recents;
diff --git a/inc/HTTPClient.php b/inc/HTTPClient.php
index 372769b71..fdf95d113 100644
--- a/inc/HTTPClient.php
+++ b/inc/HTTPClient.php
@@ -368,6 +368,7 @@ class HTTPClient {
unset($this->connections[$connectionId]);
return false;
}
+ usleep(1000);
$r_headers .= fgets($socket,1024);
}while(!preg_match('/\r?\n\r?\n$/',$r_headers));
diff --git a/inc/actions.php b/inc/actions.php
index ecf09036f..1a0ae4028 100644
--- a/inc/actions.php
+++ b/inc/actions.php
@@ -227,7 +227,7 @@ function act_clean($act){
'preview','search','show','check','index','revisions',
'diff','recent','backlink','admin','subscribe','revert',
'unsubscribe','profile','resendpwd','recover',
- 'draftdel','subscribens','unsubscribens','sitemap')) && substr($act,0,7) != 'export_' ) {
+ 'draftdel','subscribens','unsubscribens','sitemap','media')) && substr($act,0,7) != 'export_' ) {
msg('Command unknown: '.htmlspecialchars($act),-1);
return 'show';
}
diff --git a/inc/changelog.php b/inc/changelog.php
index 15cd46d77..fea39f9f7 100644
--- a/inc/changelog.php
+++ b/inc/changelog.php
@@ -138,6 +138,7 @@ function addMediaLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='',
// add changelog lines
$logline = implode("\t", $logline)."\n";
io_saveFile($conf['media_changelog'],$logline,true); //global media changelog cache
+ io_saveFile(mediaMetaFN($id,'.changes'),$logline,true); //media file's changelog
}
/**
@@ -151,6 +152,7 @@ function addMediaLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='',
* RECENTS_SKIP_MINORS - don't include minor changes
* RECENTS_SKIP_SUBSPACES - don't include subspaces
* RECENTS_MEDIA_CHANGES - return media changes instead of page changes
+ * RECENTS_MEDIA_PAGES_MIXED - return both media changes and page changes
*
* @param int $first number of first entry returned (for paginating
* @param int $num return $num entries
@@ -158,6 +160,7 @@ function addMediaLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='',
* @param bool $flags see above
*
* @author Ben Coburn <btcoburn@silicodon.net>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
function getRecents($first,$num,$ns='',$flags=0){
global $conf;
@@ -173,20 +176,48 @@ function getRecents($first,$num,$ns='',$flags=0){
} else {
$lines = @file($conf['changelog']);
}
+ $lines_position = count($lines)-1;
+
+ if ($flags & RECENTS_MEDIA_PAGES_MIXED) {
+ $media_lines = @file($conf['media_changelog']);
+ $media_lines_position = count($media_lines)-1;
+ }
- // handle lines
$seen = array(); // caches seen lines, _handleRecent() skips them
- for($i = count($lines)-1; $i >= 0; $i--){
- $rec = _handleRecent($lines[$i], $ns, $flags, $seen);
- if($rec !== false) {
- if(--$first >= 0) continue; // skip first entries
- $recent[] = $rec;
- $count++;
- // break when we have enough entries
- if($count >= $num){ break; }
+
+ // handle lines
+ while ($lines_position >= 0 || (($flags & RECENTS_MEDIA_PAGES_MIXED) && $media_lines_position >=0)) {
+ if (empty($rec) && $lines_position >= 0) {
+ $rec = _handleRecent(@$lines[$lines_position], $ns, $flags & ~RECENTS_MEDIA_CHANGES, $seen);
+ if (!$rec) {
+ $lines_position --;
+ continue;
+ }
+ }
+ if (($flags & RECENTS_MEDIA_PAGES_MIXED) && empty($media_rec) && $media_lines_position >= 0) {
+ $media_rec = _handleRecent(@$media_lines[$media_lines_position], $ns, $flags | RECENTS_MEDIA_CHANGES, $seen);
+ if (!$media_rec) {
+ $media_lines_position --;
+ continue;
+ }
}
+ if (($flags & RECENTS_MEDIA_PAGES_MIXED) && @$media_rec['date'] >= @$rec['date']) {
+ $media_lines_position--;
+ $x = $media_rec;
+ $x['media'] = true;
+ $media_rec = false;
+ } else {
+ $lines_position--;
+ $x = $rec;
+ if ($flags & RECENTS_MEDIA_CHANGES) $x['media'] = true;
+ $rec = false;
+ }
+ if(--$first >= 0) continue; // skip first entries
+ $recent[] = $x;
+ $count++;
+ // break when we have enough entries
+ if($count >= $num){ break; }
}
-
return $recent;
}
@@ -281,7 +312,11 @@ function _handleRecent($line,$ns,$flags,&$seen){
if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($recent['id']) != $ns)) return false;
// check ACL
- $recent['perms'] = auth_quickaclcheck($recent['id']);
+ if ($flags & RECENTS_MEDIA_CHANGES) {
+ $recent['perms'] = auth_quickaclcheck(getNS($recent['id']).':*');
+ } else {
+ $recent['perms'] = auth_quickaclcheck($recent['id']);
+ }
if ($recent['perms'] < AUTH_READ) return false;
// check existance
@@ -300,8 +335,9 @@ function _handleRecent($line,$ns,$flags,&$seen){
* requested changelog line is read.
*
* @author Ben Coburn <btcoburn@silicodon.net>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function getRevisionInfo($id, $rev, $chunk_size=8192) {
+function getRevisionInfo($id, $rev, $chunk_size=8192, $media=false) {
global $cache_revinfo;
$cache =& $cache_revinfo;
if (!isset($cache[$id])) { $cache[$id] = array(); }
@@ -312,7 +348,11 @@ function getRevisionInfo($id, $rev, $chunk_size=8192) {
return $cache[$id][$rev];
}
- $file = metaFN($id, '.changes');
+ if ($media) {
+ $file = mediaMetaFN($id, '.changes');
+ } else {
+ $file = metaFN($id, '.changes');
+ }
if (!@file_exists($file)) { return false; }
if (filesize($file)<$chunk_size || $chunk_size==0) {
// read whole file
@@ -397,8 +437,9 @@ function getRevisionInfo($id, $rev, $chunk_size=8192) {
* lines are recieved.
*
* @author Ben Coburn <btcoburn@silicodon.net>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function getRevisions($id, $first, $num, $chunk_size=8192) {
+function getRevisions($id, $first, $num, $chunk_size=8192, $media=false) {
global $cache_revinfo;
$cache =& $cache_revinfo;
if (!isset($cache[$id])) { $cache[$id] = array(); }
@@ -406,11 +447,15 @@ function getRevisions($id, $first, $num, $chunk_size=8192) {
$revs = array();
$lines = array();
$count = 0;
- $file = metaFN($id, '.changes');
+ if ($media) {
+ $file = mediaMetaFN($id, '.changes');
+ } else {
+ $file = metaFN($id, '.changes');
+ }
$num = max($num, 0);
$chunk_size = max($chunk_size, 0);
if ($first<0) { $first = 0; }
- else if (@file_exists(wikiFN($id))) {
+ else if (!$media && @file_exists(wikiFN($id)) || $media && @file_exists(mediaFN($id))) {
// skip current revision if the page exists
$first = max($first+1, 0);
}
diff --git a/inc/common.php b/inc/common.php
index 239cfcf99..56a7fb060 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -15,6 +15,7 @@ define('RECENTS_SKIP_DELETED',2);
define('RECENTS_SKIP_MINORS',4);
define('RECENTS_SKIP_SUBSPACES',8);
define('RECENTS_MEDIA_CHANGES',16);
+define('RECENTS_MEDIA_PAGES_MIXED',32);
/**
* Wrapper around htmlspecialchars()
diff --git a/inc/html.php b/inc/html.php
index 6e9cce7df..1c48d6059 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -415,20 +415,28 @@ function html_locked(){
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Ben Coburn <btcoburn@silicodon.net>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function html_revisions($first=0){
+function html_revisions($first=0, $media_id = false){
global $ID;
global $INFO;
global $conf;
global $lang;
+ $id = $ID;
/* we need to get one additionally log entry to be able to
* decide if this is the last page or is there another one.
* see html_recent()
*/
- $revisions = getRevisions($ID, $first, $conf['recent']+1);
+ if (!$media_id) $revisions = getRevisions($ID, $first, $conf['recent']+1);
+ else {
+ $revisions = getRevisions($media_id, $first, $conf['recent']+1, 8192, true);
+ $id = $media_id;
+ }
+
if(count($revisions)==0 && $first!=0){
$first=0;
- $revisions = getRevisions($ID, $first, $conf['recent']+1);;
+ if (!$media_id) $revisions = getRevisions($ID, $first, $conf['recent']+1);
+ else $revisions = getRevisions($media_id, $first, $conf['recent']+1, 8192, true);
}
$hasNext = false;
if (count($revisions)>$conf['recent']) {
@@ -436,14 +444,22 @@ function html_revisions($first=0){
array_pop($revisions); // remove extra log entry
}
- $date = dformat($INFO['lastmod']);
+ if (!$media_id) $date = dformat($INFO['lastmod']);
+ else $date = dformat(@filemtime(mediaFN($id)));
+
+ if (!$media_id) print p_locale_xhtml('revisions');
- print p_locale_xhtml('revisions');
+ $params = array('id' => 'page__revisions');
+ if ($media_id) $params['action'] = media_managerURL(array('image' => $media_id), '&');
- $form = new Doku_Form(array('id' => 'page__revisions'));
+ $form = new Doku_Form($params);
$form->addElement(form_makeOpenTag('ul'));
- if($INFO['exists'] && $first==0){
- if (isset($INFO['meta']) && isset($INFO['meta']['last_change']) && $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT)
+
+ if (!$media_id) $exists = $INFO['exists'];
+ else $exists = @file_exists(mediaFN($id));
+
+ if($exists && $first==0){
+ if (!$media_id && isset($INFO['meta']) && isset($INFO['meta']['last_change']) && $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT)
$form->addElement(form_makeOpenTag('li', array('class' => 'minor')));
else
$form->addElement(form_makeOpenTag('li'));
@@ -459,30 +475,53 @@ function html_revisions($first=0){
$form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
+ if (!$media_id) $href = wl($id);
+ else $href = media_managerURL(array('image' => $id, 'tab_details' => 'view'), '&');
$form->addElement(form_makeOpenTag('a', array(
'class' => 'wikilink1',
- 'href' => wl($ID))));
- $form->addElement($ID);
+ 'href' => $href)));
+ $form->addElement($id);
$form->addElement(form_makeCloseTag('a'));
- $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
- $form->addElement(' &ndash; ');
- $form->addElement(htmlspecialchars($INFO['sum']));
- $form->addElement(form_makeCloseTag('span'));
+ if ($media_id) $form->addElement(form_makeOpenTag('div'));
+
+ if (!$media_id) {
+ $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
+ $form->addElement(' &ndash; ');
+ $form->addElement(htmlspecialchars($INFO['sum']));
+ $form->addElement(form_makeCloseTag('span'));
+ }
$form->addElement(form_makeOpenTag('span', array('class' => 'user')));
- $form->addElement((empty($INFO['editor']))?('('.$lang['external_edit'].')'):editorinfo($INFO['editor']));
+ if (!$media_id) $editor = $INFO['editor'];
+ else {
+ $revinfo = getRevisionInfo($id, @filemtime(fullpath(mediaFN($id))), 1024, true);
+ if($revinfo['user']){
+ $editor = $revinfo['user'];
+ }else{
+ $editor = $revinfo['ip'];
+ }
+ }
+ $form->addElement((empty($editor))?('('.$lang['external_edit'].')'):editorinfo($editor));
$form->addElement(form_makeCloseTag('span'));
$form->addElement('('.$lang['current'].')');
+
+ if ($media_id) $form->addElement(form_makeCloseTag('div'));
+
$form->addElement(form_makeCloseTag('div'));
$form->addElement(form_makeCloseTag('li'));
}
foreach($revisions as $rev){
- $date = dformat($rev);
- $info = getRevisionInfo($ID,$rev,true);
- $exists = page_exists($ID,$rev);
+ $date = dformat($rev);
+ if (!$media_id) {
+ $info = getRevisionInfo($id,$rev,true);
+ $exists = page_exists($id,$rev);
+ } else {
+ $info = getRevisionInfo($id,$rev,true,true);
+ $exists = @file_exists(mediaFN($id,$rev));
+ }
if ($info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT)
$form->addElement(form_makeOpenTag('li', array('class' => 'minor')));
@@ -503,7 +542,9 @@ function html_revisions($first=0){
$form->addElement(form_makeCloseTag('span'));
if($exists){
- $form->addElement(form_makeOpenTag('a', array('href' => wl($ID,"rev=$rev,do=diff", false, '&'), 'class' => 'diff_link')));
+ if (!$media_id) $href = wl($id,"rev=$rev,do=diff", false, '&');
+ else $href = media_managerURL(array('image' => $id, 'rev' => $rev, 'mediado' => 'diff'), '&');
+ $form->addElement(form_makeOpenTag('a', array('href' => $href, 'class' => 'diff_link')));
$form->addElement(form_makeTag('img', array(
'src' => DOKU_BASE.'lib/images/diff.png',
'width' => 15,
@@ -511,19 +552,24 @@ function html_revisions($first=0){
'title' => $lang['diff'],
'alt' => $lang['diff'])));
$form->addElement(form_makeCloseTag('a'));
-
- $form->addElement(form_makeOpenTag('a', array('href' => wl($ID,"rev=$rev",false,'&'), 'class' => 'wikilink1')));
- $form->addElement($ID);
+ if (!$media_id) $href = wl($id,"rev=$rev",false,'&');
+ else $href = media_managerURL(array('image' => $id, 'tab_details' => 'view', 'rev' => $rev), '&');
+ $form->addElement(form_makeOpenTag('a', array('href' => $href, 'class' => 'wikilink1')));
+ $form->addElement($id);
$form->addElement(form_makeCloseTag('a'));
}else{
$form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
- $form->addElement($ID);
+ $form->addElement($id);
}
- $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
- $form->addElement(' &ndash; ');
- $form->addElement(htmlspecialchars($info['sum']));
- $form->addElement(form_makeCloseTag('span'));
+ if ($media_id) $form->addElement(form_makeOpenTag('div'));
+
+ if ($info['sum']) {
+ $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
+ if (!$media_id) $form->addElement(' &ndash; ');
+ $form->addElement(htmlspecialchars($info['sum']));
+ $form->addElement(form_makeCloseTag('span'));
+ }
$form->addElement(form_makeOpenTag('span', array('class' => 'user')));
if($info['user']){
@@ -536,11 +582,18 @@ function html_revisions($first=0){
}
$form->addElement(form_makeCloseTag('span'));
+ if ($media_id) $form->addElement(form_makeCloseTag('div'));
+
$form->addElement(form_makeCloseTag('div'));
$form->addElement(form_makeCloseTag('li'));
}
$form->addElement(form_makeCloseTag('ul'));
- $form->addElement(form_makeButton('submit', 'diff', $lang['diff2']));
+ if (!$media_id) {
+ $form->addElement(form_makeButton('submit', 'diff', $lang['diff2']));
+ } else {
+ $form->addHidden('mediado', 'diff');
+ $form->addElement(form_makeButton('submit', '', $lang['diff2']));
+ }
html_form('revisions', $form);
print '<div class="pagenav">';
@@ -549,12 +602,20 @@ function html_revisions($first=0){
$first -= $conf['recent'];
if ($first < 0) $first = 0;
print '<div class="pagenav-prev">';
- print html_btn('newer',$ID,"p",array('do' => 'revisions', 'first' => $first));
+ if ($media_id) {
+ print html_btn('newer',$media_id,"p",media_managerURL(array('first' => $first), '&amp;', false, true));
+ } else {
+ print html_btn('newer',$id,"p",array('do' => 'revisions', 'first' => $first));
+ }
print '</div>';
}
if ($hasNext) {
print '<div class="pagenav-next">';
- print html_btn('older',$ID,"n",array('do' => 'revisions', 'first' => $last));
+ if ($media_id) {
+ print html_btn('older',$media_id,"n",media_managerURL(array('first' => $last), '&amp;', false, true));
+ } else {
+ print html_btn('older',$id,"n",array('do' => 'revisions', 'first' => $last));
+ }
print '</div>';
}
print '</div>';
@@ -567,8 +628,9 @@ function html_revisions($first=0){
* @author Andreas Gohr <andi@splitbrain.org>
* @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
* @author Ben Coburn <btcoburn@silicodon.net>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function html_recent($first=0){
+function html_recent($first=0, $show_changes='both'){
global $conf;
global $lang;
global $ID;
@@ -576,10 +638,20 @@ function html_recent($first=0){
* decide if this is the last page or is there another one.
* This is the cheapest solution to get this information.
*/
- $recents = getRecents($first,$conf['recent'] + 1,getNS($ID));
+ $flags = 0;
+ if ($show_changes == 'mediafiles' && $conf['mediarevisions']) {
+ $flags = RECENTS_MEDIA_CHANGES;
+ } elseif ($show_changes == 'pages') {
+ $flags = 0;
+ } elseif ($conf['mediarevisions']) {
+ $show_changes = 'both';
+ $flags = RECENTS_MEDIA_PAGES_MIXED;
+ }
+
+ $recents = getRecents($first,$conf['recent'] + 1,getNS($ID),$flags);
if(count($recents) == 0 && $first != 0){
$first=0;
- $recents = getRecents($first,$conf['recent'] + 1,getNS($ID));
+ $recents = getRecents($first,$conf['recent'] + 1,getNS($ID),$flags);
}
$hasNext = false;
if (count($recents)>$conf['recent']) {
@@ -596,6 +668,22 @@ function html_recent($first=0){
$form->addHidden('sectok', null);
$form->addHidden('do', 'recent');
$form->addHidden('id', $ID);
+
+ if ($conf['mediarevisions']) {
+ $form->addElement(form_makeListboxField(
+ 'show_changes',
+ array(
+ 'pages' => $lang['pages_changes'],
+ 'mediafiles' => $lang['media_changes'],
+ 'both' => $lang['both_changes']),
+ $show_changes,
+ $lang['changes_type'],
+ '','',
+ array('class'=>'quickselect')));
+
+ $form->addElement(form_makeButton('submit', 'recent', $lang['btn_apply']));
+ }
+
$form->addElement(form_makeOpenTag('ul'));
foreach($recents as $recent){
@@ -607,21 +695,48 @@ function html_recent($first=0){
$form->addElement(form_makeOpenTag('div', array('class' => 'li')));
+ if ($recent['media']) {
+ $form->addElement(media_printicon($recent['id']));
+ } else {
+ $icon = DOKU_BASE.'lib/images/fileicons/file.png';
+ $form->addElement('<img src="'.$icon.'" alt="'.$filename.'" class="icon" />');
+ }
+
$form->addElement(form_makeOpenTag('span', array('class' => 'date')));
$form->addElement($date);
$form->addElement(form_makeCloseTag('span'));
- $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => wl($recent['id'],"do=diff", false, '&'))));
- $form->addElement(form_makeTag('img', array(
- 'src' => DOKU_BASE.'lib/images/diff.png',
- 'width' => 15,
- 'height'=> 11,
- 'title' => $lang['diff'],
- 'alt' => $lang['diff']
- )));
- $form->addElement(form_makeCloseTag('a'));
+ if ($recent['media']) {
+ $diff = (count(getRevisions($recent['id'], 0, 1, 8192, true)) && @file_exists(mediaFN($recent['id'])));
+ if ($diff) {
+ $href = media_managerURL(array('tab_details' => 'history',
+ 'mediado' => 'diff', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&');
+ }
+ } else {
+ $href = wl($recent['id'],"do=diff", false, '&');
+ }
- $form->addElement(form_makeOpenTag('a', array('class' => 'revisions_link', 'href' => wl($recent['id'],"do=revisions",false,'&'))));
+ if ($recent['media'] && !$diff) {
+ $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
+ } else {
+ $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => $href)));
+ $form->addElement(form_makeTag('img', array(
+ 'src' => DOKU_BASE.'lib/images/diff.png',
+ 'width' => 15,
+ 'height'=> 11,
+ 'title' => $lang['diff'],
+ 'alt' => $lang['diff']
+ )));
+ $form->addElement(form_makeCloseTag('a'));
+ }
+
+ if ($recent['media']) {
+ $href = media_managerURL(array('tab_details' => 'history',
+ 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&');
+ } else {
+ $href = wl($recent['id'],"do=revisions",false,'&');
+ }
+ $form->addElement(form_makeOpenTag('a', array('class' => 'revisions_link', 'href' => $href)));
$form->addElement(form_makeTag('img', array(
'src' => DOKU_BASE.'lib/images/history.png',
'width' => 12,
@@ -631,8 +746,15 @@ function html_recent($first=0){
)));
$form->addElement(form_makeCloseTag('a'));
- $form->addElement(html_wikilink(':'.$recent['id'],useHeading('navigation')?null:$recent['id']));
-
+ if ($recent['media']) {
+ $href = media_managerURL(array('tab_details' => 'view', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&');
+ $class = (file_exists(mediaFN($recent['id']))) ? 'wikilink1' : $class = 'wikilink2';
+ $form->addElement(form_makeOpenTag('a', array('class' => $class, 'href' => $href)));
+ $form->addElement($recent['id']);
+ $form->addElement(form_makeCloseTag('a'));
+ } else {
+ $form->addElement(html_wikilink(':'.$recent['id'],useHeading('navigation')?null:$recent['id']));
+ }
$form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
$form->addElement(' &ndash; '.htmlspecialchars($recent['sum']));
$form->addElement(form_makeCloseTag('span'));
@@ -665,7 +787,7 @@ function html_recent($first=0){
'value' => $lang['btn_newer'],
'accesskey' => 'n',
'title' => $lang['btn_newer'].' [N]',
- 'class' => 'button'
+ 'class' => 'button show'
)));
$form->addElement(form_makeCloseTag('div'));
}
@@ -677,7 +799,7 @@ function html_recent($first=0){
'value' => $lang['btn_older'],
'accesskey' => 'p',
'title' => $lang['btn_older'].' [P]',
- 'class' => 'button'
+ 'class' => 'button show'
)));
$form->addElement(form_makeCloseTag('div'));
}
diff --git a/inc/init.php b/inc/init.php
index dfa25f8b1..b3acf2e33 100644
--- a/inc/init.php
+++ b/inc/init.php
@@ -231,7 +231,9 @@ function init_paths(){
$paths = array('datadir' => 'pages',
'olddir' => 'attic',
'mediadir' => 'media',
+ 'mediaolddir' => 'media_attic',
'metadir' => 'meta',
+ 'mediametadir' => 'media_meta',
'cachedir' => 'cache',
'indexdir' => 'index',
'lockdir' => 'locks',
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index de8385dd9..ab6a88497 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -48,6 +48,8 @@ $lang['btn_recover'] = 'Recover draft';
$lang['btn_draftdel'] = 'Delete draft';
$lang['btn_revert'] = 'Restore';
$lang['btn_register'] = 'Register';
+$lang['btn_apply'] = 'Apply';
+$lang['btn_media'] = 'Media Manager';
$lang['loggedinas'] = 'Logged in as';
$lang['user'] = 'Username';
@@ -127,7 +129,7 @@ $lang['js']['hidedetails'] = 'Hide Details';
$lang['mediausage'] = 'Use the following syntax to reference this file:';
$lang['mediaview'] = 'View original file';
$lang['mediaroot'] = 'root';
-$lang['mediaupload'] = 'Upload a file to the current namespace here. To create subnamespaces, prepend them to your "Upload as" filename separated by colons.';
+$lang['mediaupload'] = 'Upload a file to the current namespace here. To create subnamespaces, prepend them to your "Upload as" filename separated by colons. Files also can be selected by drag and drop.';
$lang['mediaextchange'] = 'Filextension changed from .%s to .%s!';
$lang['js']['mediatitle'] = 'Link settings';
@@ -180,6 +182,11 @@ $lang['summary'] = 'Edit summary';
$lang['noflash'] = 'The <a href="http://www.adobe.com/products/flashplayer/">Adobe Flash Plugin</a> is needed to display this content.';
$lang['download'] = 'Download Snippet';
+$lang['changes_type'] = 'View changes of';
+$lang['pages_changes'] = 'Pages';
+$lang['media_changes'] = 'Media files';
+$lang['both_changes'] = 'Both pages and media files';
+
$lang['mail_newpage'] = 'page added:';
$lang['mail_changed'] = 'page changed:';
$lang['mail_subscribe_list'] = 'pages changed in namespace:';
@@ -220,6 +227,7 @@ $lang['js']['linkwiz'] = 'Link Wizard';
$lang['js']['linkto'] = 'Link to:';
$lang['js']['del_confirm']= 'Really delete selected item(s)?';
+$lang['js']['restore_confirm']= 'Really restore this version?';
$lang['admin_register']= 'Add new user';
$lang['metaedit'] = 'Edit Metadata';
@@ -236,6 +244,9 @@ $lang['img_copyr'] = 'Copyright';
$lang['img_format'] = 'Format';
$lang['img_camera'] = 'Camera';
$lang['img_keywords']= 'Keywords';
+$lang['img_width'] = 'Width';
+$lang['img_height'] = 'Height';
+$lang['img_manager'] = 'View in media manager';
$lang['subscr_subscribe_success'] = 'Added %s to subscription list for %s';
$lang['subscr_subscribe_error'] = 'Error adding %s to subscription list for %s';
@@ -318,5 +329,38 @@ $lang['seconds'] = '%d seconds ago';
$lang['wordblock'] = 'Your change was not saved because it contains blocked text (spam).';
+$lang['media_uploadtab'] = 'Upload';
+$lang['media_searchtab'] = 'Search';
+$lang['media_viewtab'] = 'View';
+$lang['media_edittab'] = 'Edit';
+$lang['media_historytab'] = 'History';
+$lang['media_thumbsview'] = 'Thumbnails';
+$lang['media_listview'] = 'List';
+$lang['media_sort'] = 'Sort';
+$lang['media_sort_name'] = 'by name';
+$lang['media_sort_date'] = 'by date';
+$lang['media_upload'] = 'Upload to the <strong>%s</strong> namespace.';
+$lang['media_search'] = 'Search in the <strong>%s</strong> namespace.';
+$lang['media_edit'] = 'Edit';
+$lang['media_history'] = 'These are the older revisions of the file.';
+$lang['media_meta_edited']= 'metadata edited';
+$lang['media_perm_read'] = 'Sorry, you don\'t have enough rights to read files.';
+$lang['media_perm_upload']= 'Sorry, you don\'t have enough rights to upload files.';
+$lang['media_update'] = 'Upload new version';
+$lang['media_restore'] = 'Restore this version';
+
+$lang['js']['media_diff'] = 'View differences:';
+$lang['js']['media_diff_both'] = 'Side by Side';
+$lang['js']['media_diff_opacity'] = 'Overlay';
+$lang['js']['media_diff_portions'] = 'Slider';
+
+$lang['js']['media_select'] = 'Select files…';
+$lang['js']['media_upload_btn'] = 'Upload';
+$lang['js']['media_done_btn'] = 'Done';
+$lang['js']['media_drop'] = 'Drop files here to upload';
+$lang['js']['media_cancel'] = 'remove';
+$lang['js']['media_overwrt'] = 'Overwrite existing files';
+
$lang['plugin_install_err'] = "Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.";
+
//Setup VIM: ex: et ts=2 :
diff --git a/inc/lang/en/uploadmail.txt b/inc/lang/en/uploadmail.txt
index 6fa196730..932a1b704 100644
--- a/inc/lang/en/uploadmail.txt
+++ b/inc/lang/en/uploadmail.txt
@@ -1,13 +1,14 @@
A file was uploaded to your DokuWiki. Here are the details:
-File : @MEDIA@
-Date : @DATE@
-Browser : @BROWSER@
-IP-Address : @IPADDRESS@
-Hostname : @HOSTNAME@
-Size : @SIZE@
-MIME Type : @MIME@
-User : @USER@
+File : @MEDIA@
+Old revision : @OLD@
+Date : @DATE@
+Browser : @BROWSER@
+IP-Address : @IPADDRESS@
+Hostname : @HOSTNAME@
+Size : @SIZE@
+MIME Type : @MIME@
+User : @USER@
--
This mail was generated by DokuWiki at
diff --git a/inc/lang/es/lang.php b/inc/lang/es/lang.php
index 5dc6834e6..4c83f5d96 100644
--- a/inc/lang/es/lang.php
+++ b/inc/lang/es/lang.php
@@ -24,6 +24,7 @@
* @author Victor Castelan <victorcastelan@gmail.com>
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
@@ -109,7 +110,7 @@ $lang['txt_filename'] = 'Subir como (opcional)';
$lang['txt_overwrt'] = 'Sobreescribir archivo existente';
$lang['lockedby'] = 'Actualmente bloqueado por';
$lang['lockexpire'] = 'El bloqueo expira en';
-$lang['js']['willexpire'] = 'Tu bloqueo para editar esta página expira en un minuto.\nPara evitar conflictos usa el botón previsualizar para reiniciar el contador de tiempo.';
+$lang['willexpire'] = 'Tu bloqueo para editar esta página expira en un minuto.\nPara evitar conflictos usa el botón previsualizar para reiniciar el contador de tiempo.';
$lang['js']['notsavedyet'] = 'Los cambios que no se han guardado se perderán.
¿Realmente quieres continuar?';
$lang['js']['searchmedia'] = 'Buscar archivos';
diff --git a/inc/lang/pl/lang.php b/inc/lang/pl/lang.php
index 32e5bf80c..f9bf57852 100644
--- a/inc/lang/pl/lang.php
+++ b/inc/lang/pl/lang.php
@@ -11,6 +11,7 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
@@ -96,7 +97,7 @@ $lang['txt_filename'] = 'Nazwa pliku (opcjonalnie)';
$lang['txt_overwrt'] = 'Nadpisać istniejący plik?';
$lang['lockedby'] = 'Aktualnie zablokowane przez';
$lang['lockexpire'] = 'Blokada wygasa';
-$lang['js']['willexpire'] = 'Za minutę Twoja blokada tej strony wygaśnie.\nW celu uniknięcia konfliktów wyświetl podgląd aby odnowić blokadę.';
+$lang['willexpire'] = 'Za minutę Twoja blokada tej strony wygaśnie.\nW celu uniknięcia konfliktów wyświetl podgląd aby odnowić blokadę.';
$lang['js']['notsavedyet'] = 'Nie zapisane zmiany zostaną utracone.
Czy na pewno kontynuować?';
$lang['js']['searchmedia'] = 'Szukaj plików';
@@ -163,6 +164,9 @@ $lang['yours'] = 'Twoja wersja';
$lang['diff'] = 'Pokaż różnice między wersjami';
$lang['diff2'] = 'Pokaż różnice między zaznaczonymi wersjami';
$lang['difflink'] = 'Odnośnik do tego porównania';
+$lang['diff_type'] = 'Zobacz różnice:';
+$lang['diff_inline'] = 'W linii';
+$lang['diff_side'] = 'Jeden obok drugiego';
$lang['line'] = 'Linia';
$lang['breadcrumb'] = 'Ślad';
$lang['youarehere'] = 'Jesteś tutaj';
diff --git a/inc/media.php b/inc/media.php
index 731ba1668..451a205db 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -40,6 +40,7 @@ function media_filesinuse($data,$id){
* Handles the saving of image meta data
*
* @author Andreas Gohr <andi@splitbrain.org>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
function media_metasave($id,$auth,$data){
if($auth < AUTH_UPLOAD) return false;
@@ -60,8 +61,19 @@ function media_metasave($id,$auth,$data){
}
}
+ $old = @filemtime($src);
+ if(!@file_exists(mediaFN($id, $old)) && @file_exists($src)) {
+ // add old revision to the attic
+ media_saveOldRevision($id);
+ }
+
if($meta->save()){
if($conf['fperm']) chmod($src, $conf['fperm']);
+
+ $new = @filemtime($src);
+ // add a log entry to the media changelog
+ addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT, $lang['media_meta_edited']);
+
msg($lang['metasaveok'],1);
return $id;
}else{
@@ -74,33 +86,35 @@ function media_metasave($id,$auth,$data){
* Display the form to edit image meta data
*
* @author Andreas Gohr <andi@splitbrain.org>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
function media_metaform($id,$auth){
- if($auth < AUTH_UPLOAD) return false;
global $lang, $config_cascade;
+ if($auth < AUTH_UPLOAD) {
+ echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
+ return false;
+ }
+
// load the field descriptions
static $fields = null;
if(is_null($fields)){
-
- foreach (array('default','local') as $config_group) {
- if (empty($config_cascade['mediameta'][$config_group])) continue;
- foreach ($config_cascade['mediameta'][$config_group] as $config_file) {
- if(@file_exists($config_file)){
- include($config_file);
- }
- }
+ $config_files = getConfigFiles('mediameta');
+ foreach ($config_files as $config_file) {
+ if(@file_exists($config_file)) include($config_file);
}
}
$src = mediaFN($id);
// output
- echo '<h1>'.hsc(noNS($id)).'</h1>'.NL;
- echo '<form action="'.DOKU_BASE.'lib/exe/mediamanager.php" accept-charset="utf-8" method="post" class="meta">'.NL;
+ $action = media_managerURL(array('tab_details' => 'view'));
+ echo '<form action="'.$action.'" id="mediamanager__save_meta" accept-charset="utf-8" method="post" class="meta">'.NL;
+
formSecurityToken();
foreach($fields as $key => $field){
// get current value
+ if (empty($field[0])) continue;
$tags = array($field[0]);
if(is_array($field[3])) $tags = array_merge($tags,$field[3]);
$value = tpl_img_getTag($tags,'',$src);
@@ -132,10 +146,11 @@ function media_metaform($id,$auth){
}
echo '<div class="buttons">'.NL;
echo '<input type="hidden" name="img" value="'.hsc($id).'" />'.NL;
- echo '<input name="do[save]" type="submit" value="'.$lang['btn_save'].
+ echo '<input type="hidden" name="mediado" value="save" />';
+
+ $do = 'mediado';
+ echo '<input name="'.$do.'[save]" type="submit" value="'.$lang['btn_save'].
'" title="'.$lang['btn_save'].' [S]" accesskey="s" class="button" />'.NL;
- echo '<input name="do[cancel]" type="submit" value="'.$lang['btn_cancel'].
- '" title="'.$lang['btn_cancel'].' [C]" accesskey="c" class="button" />'.NL;
echo '</div>'.NL;
echo '</form>'.NL;
}
@@ -178,6 +193,7 @@ define('DOKU_MEDIA_EMPTY_NS', 8);
DOKU_MEDIA_INUSE
*/
function media_delete($id,$auth){
+ global $lang;
if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
if(media_inuse($id)) return DOKU_MEDIA_INUSE;
@@ -193,9 +209,15 @@ function media_delete($id,$auth){
$data['del'] = false;
$evt = new Doku_Event('MEDIA_DELETE_FILE',$data);
if ($evt->advise_before()) {
+ $old = @filemtime($file);
+ if(!@file_exists(mediaFN($id, $old)) && @file_exists($file)) {
+ // add old revision to the attic
+ media_saveOldRevision($id);
+ }
+
$data['unl'] = @unlink($file);
if($data['unl']){
- addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE);
+ addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE, $lang['deleted']);
$data['del'] = io_sweepNS($id,'mediadir');
}
}
@@ -210,19 +232,58 @@ function media_delete($id,$auth){
}
/**
+ * Handle file uploads via XMLHttpRequest
+ *
+ * @return mixed false on error, id of the new file on success
+ */
+function media_upload_xhr($ns,$auth){
+ if(!checkSecurityToken()) return false;
+
+ $id = $_GET['qqfile'];
+ list($ext,$mime,$dl) = mimetype($id);
+ $input = fopen("php://input", "r");
+ $temp = tmpfile();
+ $realSize = stream_copy_to_stream($input, $temp);
+ fclose($input);
+ if ($realSize != (int)$_SERVER["CONTENT_LENGTH"]) return false;
+ if (!($tmp = io_mktmpdir())) return false;
+ $path = $tmp.'/'.md5($id);
+ $target = fopen($path, "w");
+ fseek($temp, 0, SEEK_SET);
+ stream_copy_to_stream($temp, $target);
+ fclose($target);
+ $res = media_save(
+ array('name' => $path,
+ 'mime' => $mime,
+ 'ext' => $ext),
+ $ns.':'.$id,
+ (($_REQUEST['ow'] == 'checked') ? true : false),
+ $auth,
+ 'copy'
+ );
+ unlink($path);
+ if ($tmp) dir_delete($tmp);
+ if (is_array($res)) {
+ msg($res[0], $res[1]);
+ return false;
+ }
+ return $res;
+}
+
+/**
* Handles media file uploads
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Michael Klier <chi@chimeric.de>
* @return mixed false on error, id of the new file on success
*/
-function media_upload($ns,$auth){
+function media_upload($ns,$auth,$file=false){
if(!checkSecurityToken()) return false;
global $lang;
// get file and id
- $id = $_POST['id'];
- $file = $_FILES['upload'];
+ $id = $_POST['mediaid'];
+ if (!$file) $file = $_FILES['upload'];
if(empty($id)) $id = $file['name'];
// check for errors (messages are done in lib/exe/mediamanager.php)
@@ -280,7 +341,7 @@ function media_save($file, $id, $ow, $auth, $move) {
}
}
- global $lang;
+ global $lang, $conf;
// get filename
$id = cleanID($id,false,true);
@@ -298,7 +359,8 @@ function media_save($file, $id, $ow, $auth, $move) {
//check for overwrite
$overwrite = @file_exists($fn);
- if($overwrite && (!$ow || $auth < AUTH_DELETE)) {
+ $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
+ if($overwrite && (!$ow || $auth < $auth_ow)) {
return array($lang['uploadexist'], 0);
}
// check for valid content
@@ -341,26 +403,38 @@ function _media_upload_action($data) {
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Michael Klier <chi@chimeric.de>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
global $conf;
global $lang;
+ global $REV;
+
+ $old = @filemtime($fn);
+ if(!@file_exists(mediaFN($id, $old)) && @file_exists($fn)) {
+ // add old revision to the attic if missing
+ media_saveOldRevision($id);
+ }
// prepare directory
io_createNamespace($id, 'media');
if($move($fn_tmp, $fn)) {
+ @clearstatcache(true,$fn);
+ $new = @filemtime($fn);
// Set the correct permission here.
// Always chmod media because they may be saved with different permissions than expected from the php umask.
// (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
chmod($fn, $conf['fmode']);
msg($lang['uploadsucc'],1);
- media_notify($id,$fn,$imime);
+ media_notify($id,$fn,$imime,$old);
// add a log entry to the media changelog
- if ($overwrite) {
- addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_EDIT);
+ if ($REV){
+ addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_REVERT, $lang['restored'], $REV);
+ } elseif ($overwrite) {
+ addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_EDIT);
} else {
- addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_CREATE);
+ addMediaLogEntry($new, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created']);
}
return $id;
}else{
@@ -369,6 +443,43 @@ function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'mov
}
/**
+ * Moves the current version of media file to the media_attic
+ * directory
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param string $id
+ * @return int - revision date
+ */
+function media_saveOldRevision($id){
+ global $conf, $lang;
+
+ $oldf = mediaFN($id);
+ if(!@file_exists($oldf)) return '';
+ $date = filemtime($oldf);
+ if (!$conf['mediarevisions']) return $date;
+
+ if (!getRevisionInfo($id, $date, 8192, true)) {
+ // there was an external edit,
+ // there is no log entry for current version of file
+ if (!@file_exists(mediaMetaFN($id,'.changes'))) {
+ addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_CREATE, $lang['created']);
+ } else {
+ addMediaLogEntry($date, $id, DOKU_CHANGE_TYPE_EDIT);
+ }
+ }
+
+ $newf = mediaFN($id,$date);
+ io_makeFileDir($newf);
+ if(copy($oldf, $newf)) {
+ // Set the correct permission here.
+ // Always chmod media because they may be saved with different permissions than expected from the php umask.
+ // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
+ chmod($newf, $conf['fmode']);
+ }
+ return $date;
+}
+
+/**
* This function checks if the uploaded content is really what the
* mimetype says it is. We also do spam checking for text types here.
*
@@ -416,7 +527,7 @@ function media_contentcheck($file,$mime){
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
-function media_notify($id,$file,$mime){
+function media_notify($id,$file,$mime,$old_rev=false){
global $lang;
global $conf;
global $INFO;
@@ -434,6 +545,11 @@ function media_notify($id,$file,$mime){
$text = str_replace('@MIME@',$mime,$text);
$text = str_replace('@MEDIA@',ml($id,'',true,'&',true),$text);
$text = str_replace('@SIZE@',filesize_h(filesize($file)),$text);
+ if ($old_rev && $conf['mediarevisions']) {
+ $text = str_replace('@OLD@', ml($id, "rev=$old_rev", true, '&', true), $text);
+ } else {
+ $text = str_replace('@OLD@', '', $text);
+ }
if(empty($conf['mailprefix'])) {
$subject = '['.$conf['title'].'] '.$lang['mail_upload'].' '.$id;
@@ -447,7 +563,7 @@ function media_notify($id,$file,$mime){
/**
* List all files in a given Media namespace
*/
-function media_filelist($ns,$auth=null,$jump=''){
+function media_filelist($ns,$auth=null,$jump='',$fullscreenview=false,$sort=false){
global $conf;
global $lang;
$ns = cleanID($ns);
@@ -455,26 +571,727 @@ function media_filelist($ns,$auth=null,$jump=''){
// check auth our self if not given (needed for ajax calls)
if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
- echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
+ if (!$fullscreenview) echo '<h1 id="media__ns">:'.hsc($ns).'</h1>'.NL;
if($auth < AUTH_READ){
// FIXME: print permission warning here instead?
echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
}else{
- media_uploadform($ns, $auth);
+ if (!$fullscreenview) media_uploadform($ns, $auth);
$dir = utf8_encodeFN(str_replace(':','/',$ns));
$data = array();
search($data,$conf['mediadir'],'search_media',
- array('showmsg'=>true,'depth'=>1),$dir);
+ array('showmsg'=>true,'depth'=>1),$dir,1,$sort);
if(!count($data)){
echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
- }else foreach($data as $item){
- media_printfile($item,$auth,$jump);
+ }else {
+ if ($fullscreenview) {
+ $view = $_REQUEST['view'];
+ if ($view == 'list') {
+ echo '<ul class="mediamanager-list" id="mediamanager__file_list">';
+ } else {
+ echo '<ul class="mediamanager-thumbs" id="mediamanager__file_list">';
+ }
+ }
+ foreach($data as $item){
+ if (!$fullscreenview) {
+ media_printfile($item,$auth,$jump);
+ } else {
+ media_printfile_thumbs($item,$auth,$jump);
+ }
+ }
+ if ($fullscreenview) echo '</ul>';
+ }
+ }
+ if (!$fullscreenview) media_searchform($ns);
+}
+
+/**
+ * Prints mediamanager tab
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param string $link - tab href
+ * @param string $class - tab css class
+ * @param string $name - tab caption
+ * @param boolean $selected - is tab selected
+ */
+function media_tab($link, $class, $name, $selected=false) {
+ if ($selected) $class .= ' selected';
+ $tab = '<a href="'.$link.'" class="'.$class.'" >'.$name.'</a>';
+ echo $tab;
+}
+
+/**
+ * Prints tabs for files list actions
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param string $selected_tab - opened tab
+ */
+function media_tabs_files($selected_tab = ''){
+ global $lang;
+
+ echo '<div class="mediamanager-tabs" id="mediamanager__tabs_files">';
+
+ media_tab(media_managerURL(array('tab_files' => 'files')),
+ 'files', $lang['mediaselect'], ($selected_tab == 'files'));
+ media_tab(media_managerURL(array('tab_files' => 'upload')),
+ 'upload', $lang['media_uploadtab'], ($selected_tab == 'upload'));
+ media_tab(media_managerURL(array('tab_files' => 'search')),
+ 'search', $lang['media_searchtab'], ($selected_tab == 'search'));
+
+ echo '<div class="clearer"></div>';
+ echo '</div>';
+}
+
+/**
+ * Prints tabs for files details actions
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param string $selected_tab - opened tab
+ */
+function media_tabs_details($image, $selected_tab = ''){
+ global $lang, $conf;
+
+ echo '<div class="mediamanager-tabs" id="mediamanager__tabs_details">';
+
+ media_tab(media_managerURL(array('tab_details' => 'view')),
+ 'view', $lang['media_viewtab'], ($selected_tab == 'view'));
+
+ list($ext, $mime) = mimetype($image);
+ if ($mime == 'image/jpeg' && @file_exists(mediaFN($image))) {
+ media_tab(media_managerURL(array('tab_details' => 'edit')),
+ 'edit', $lang['media_edittab'], ($selected_tab == 'edit'));
+ }
+ if ($conf['mediarevisions']) {
+ media_tab(media_managerURL(array('tab_details' => 'history')),
+ 'history', $lang['media_historytab'], ($selected_tab == 'history'));
+ }
+
+ echo '<div class="clearer"></div>';
+ echo '</div>';
+}
+
+/**
+ * Prints options for the tab that displays a list of all files
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_files_options($ns, $sort){
+ global $lang;
+
+ echo '<div class="background-container">';
+
+ echo '<strong class="namespace">';
+ echo $ns ? $ns : '['.$lang['mediaroot'].']';
+ echo '</strong>';
+
+ echo '<div id="mediamanager__tabs_list">';
+
+ echo '<a href="'.media_managerURL(array('view' => 'thumbs')).'" id="mediamanager__link_thumbs" >';
+ echo $lang['media_thumbsview'];
+ echo '</a>';
+
+ echo '<a href="'.media_managerURL(array('view' => 'list')).'" id="mediamanager__link_list" >';
+ echo $lang['media_listview'];
+ echo '</a>';
+
+ echo '</div>';
+
+ echo '<div id="mediamanager__sort">';
+ $form = new Doku_Form(array('action'=>media_managerURL(array(), '&'), 'id' => 'mediamanager__form_sort'));
+ $form->addElement(form_makeListboxField(
+ 'sort',
+ array(
+ 'name' => $lang['media_sort_name'],
+ 'date' => $lang['media_sort_date']),
+ $sort,
+ $lang['media_sort']));
+ $form->addElement(form_makeButton('submit', '', $lang['btn_apply']));
+ $form->printForm();
+ echo '</div>';
+
+ echo '<div class="clearer"></div>';
+ echo '</div>';
+}
+
+/**
+ * Returns type of sorting for the list of files in media manager
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @return string - sort type
+ */
+function _media_get_sort_type() {
+ $sort = $_REQUEST['sort'];
+ if (!$sort && (strpos($_COOKIE['DOKU_PREFS'], 'sort') >= 0)) {
+ $parts = explode('#', $_COOKIE['DOKU_PREFS']);
+ for ($i = 0; $i < count($parts); $i+=2){
+ if ($parts[$i] == 'sort') $sort = $parts[$i+1];
+ }
+ }
+ return $sort;
+}
+
+/**
+ * Prints tab that displays a list of all files
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_files($ns,$auth=null,$jump='') {
+ global $lang;
+ if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
+
+ $sort = _media_get_sort_type();
+ media_tab_files_options($ns, $sort);
+
+ echo '<div class="scroll-container" >';
+ if($auth < AUTH_READ){
+ echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
+ }else{
+ media_filelist($ns,$auth,$jump,true,$sort);
+ }
+ echo '</div>';
+}
+
+/**
+ * Prints tab that displays uploading form
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_upload($ns,$auth=null,$jump='') {
+ global $lang;
+ if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
+
+ echo '<div class="background-container">';
+ echo sprintf($lang['media_upload'], $ns ? $ns : '['.$lang['mediaroot'].']');
+ echo '</div>';
+
+ echo '<div class="scroll-container">';
+ if ($auth >= AUTH_UPLOAD) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
+ media_uploadform($ns, $auth, true);
+ echo '</div>';
+}
+
+/**
+ * Prints tab that displays search form
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_search($ns,$auth=null) {
+ global $lang;
+
+ $do = $_REQUEST['mediado'];
+ $query = $_REQUEST['q'];
+ if (!$query) $query = '';
+
+ $sort = _media_get_sort_type();
+ media_tab_files_options($ns, $sort);
+
+ echo '<div class="scroll-container">';
+ media_searchform($ns, $query, true);
+ if ($do == 'searchlist') media_searchlist($query,$ns,$auth,true,$sort);
+ echo '</div>';
+}
+
+/**
+ * Prints tab that displays mediafile details
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_view($image, $ns, $auth=null, $rev=false) {
+ global $lang, $conf;
+ if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
+
+ echo '<div class="background-container">';
+ list($ext,$mime,$dl) = mimetype($image,false);
+ $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
+ $class = 'select mediafile mf_'.$class;
+ echo '<span class="'.$class.'" >'.$image.'</span>';
+ echo '</div>';
+
+ echo '<div class="scroll-container">';
+ if ($image && $auth >= AUTH_READ) {
+ $meta = new JpegMeta(mediaFN($image, $rev));
+ media_preview($image, $auth, $rev, $meta);
+ media_preview_buttons($image, $auth, $rev);
+ media_details($image, $auth, $rev, $meta);
+
+ } else {
+ echo '<div class="nothing">'.$lang['media_perm_read'].'</div>';
+ }
+ echo '</div>';
+}
+
+/**
+ * Prints tab that displays form for editing mediafile metadata
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_edit($image, $ns, $auth=null) {
+ global $lang;
+ if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
+
+ echo '<div class="background-container">';
+ echo $lang['media_edit'];
+ echo '</div>';
+
+ echo '<div class="scroll-container">';
+ if ($image) {
+ list($ext, $mime) = mimetype($image);
+ if ($mime == 'image/jpeg') media_metaform($image,$auth);
+ }
+ echo '</div>';
+}
+
+/**
+ * Prints tab that displays mediafile revisions
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_tab_history($image, $ns, $auth=null) {
+ global $lang;
+ if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*");
+ $do = $_REQUEST['mediado'];
+
+ echo '<div class="background-container">';
+ echo $lang['media_history'];
+ echo '</div>';
+
+ echo '<div class="scroll-container">';
+ if ($auth >= AUTH_READ && $image) {
+ if ($do == 'diff'){
+ media_diff($image, $ns, $auth);
+ } else {
+ $first = isset($_REQUEST['first']) ? intval($_REQUEST['first']) : 0;
+ html_revisions($first, $image);
+ }
+ } else {
+ echo '<div class="nothing">'.$lang['media_perm_read'].'</div>'.NL;
+ }
+ echo '</div>';
+}
+
+/**
+ * Prints mediafile details
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_preview($image, $auth, $rev=false, $meta=false) {
+ global $lang;
+
+ echo '<div class="mediamanager__preview">';
+
+ $size = media_image_preview_size($image, $rev, $meta);
+
+ if ($size) {
+ $more = array();
+ if ($rev) {
+ $more['rev'] = $rev;
+ } else {
+ $t = @filemtime(mediaFN($image));
+ $more['t'] = $t;
+ }
+
+ $more['w'] = $size[0];
+ $more['h'] = $size[1];
+ $src = ml($image, $more);
+ echo '<img src="'.$src.'" alt="'.$image.'" style="max-width: '.$size[0].'px;" />';
+ }
+
+ echo '</div>';
+}
+
+/**
+ * Prints mediafile action buttons
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_preview_buttons($image, $auth, $rev=false) {
+ global $lang, $conf;
+
+ echo '<div class="mediamanager__preview_buttons">';
+
+ $more = '';
+ if ($rev) {
+ $more = "rev=$rev";
+ } else {
+ $t = @filemtime(mediaFN($image));
+ $more = "t=$t";
+ }
+ $link = ml($image,$more,true,'&');
+
+ if (@file_exists(mediaFN($image, $rev))) {
+
+ // view original file button
+ $form = new Doku_Form(array('action'=>$link, 'target'=>'_blank'));
+ $form->addElement(form_makeButton('submit','',$lang['mediaview']));
+ $form->printForm();
+ }
+
+ if($auth >= AUTH_DELETE && !$rev && @file_exists(mediaFN($image))){
+
+ // delete button
+ $form = new Doku_Form(array('id' => 'mediamanager__btn_delete',
+ 'action'=>media_managerURL(array('delete' => $image), '&')));
+ $form->addElement(form_makeButton('submit','',$lang['btn_delete']));
+ $form->printForm();
+
+ }
+
+ $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
+ if($auth >= $auth_ow && !$rev){
+
+ // upload new version button
+ $form = new Doku_Form(array('id' => 'mediamanager__btn_update',
+ 'action'=>media_managerURL(array('image' => $image, 'mediado' => 'update'), '&')));
+ $form->addElement(form_makeButton('submit','',$lang['media_update']));
+ $form->printForm();
+ }
+
+ if($auth >= AUTH_UPLOAD && $rev && $conf['mediarevisions'] && @file_exists(mediaFN($image, $rev))){
+
+ // restore button
+ $form = new Doku_Form(array('id' => 'mediamanager__btn_restore',
+ 'action'=>media_managerURL(array('image' => $image), '&')));
+ $form->addHidden('mediado','restore');
+ $form->addHidden('rev',$rev);
+ $form->addElement(form_makeButton('submit','',$lang['media_restore']));
+ $form->printForm();
+ }
+
+ echo '</div>';
+}
+
+/**
+ * Returns image width and height for mediamanager preview panel
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param string $image
+ * @param int $rev
+ * @param JpegMeta $meta
+ * @return array
+ */
+function media_image_preview_size($image, $rev, $meta, $size = 500) {
+ if (!preg_match("/\.(jpe?g|gif|png)$/", $image) || !file_exists(mediaFN($image, $rev))) return false;
+
+ $info = getimagesize(mediaFN($image, $rev));
+ $w = (int) $info[0];
+ $h = (int) $info[1];
+
+ if($meta && ($w > $size || $h > $size)){
+ $ratio = $meta->getResizeRatio($size, $size);
+ $w = floor($w * $ratio);
+ $h = floor($h * $ratio);
+ }
+ return array($w, $h);
+}
+
+/**
+ * Returns the requested EXIF/IPTC tag from the image meta
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param array $tags
+ * @param JpegMeta $meta
+ * @param string $alt
+ * @return string
+ */
+function media_getTag($tags,$meta,$alt=''){
+ if($meta === false) return $alt;
+ $info = $meta->getField($tags);
+ if($info == false) return $alt;
+ return $info;
+}
+
+/**
+ * Returns mediafile tags
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param JpegMeta $meta
+ * @return array
+ */
+function media_file_tags($meta) {
+ global $config_cascade;
+
+ // load the field descriptions
+ static $fields = null;
+ if(is_null($fields)){
+ $config_files = getConfigFiles('mediameta');
+ foreach ($config_files as $config_file) {
+ if(@file_exists($config_file)) include($config_file);
+ }
+ }
+
+ $tags = array();
+
+ foreach($fields as $key => $tag){
+ $t = array();
+ if (!empty($tag[0])) $t = array($tag[0]);
+ if(is_array($tag[3])) $t = array_merge($t,$tag[3]);
+ $value = media_getTag($t, $meta);
+ $tags[] = array('tag' => $tag, 'value' => $value);
+ }
+
+ return $tags;
+}
+
+/**
+ * Prints mediafile tags
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_details($image, $auth, $rev=false, $meta=false) {
+ global $lang;
+
+ if (!$meta) $meta = new JpegMeta(mediaFN($image, $rev));
+ $tags = media_file_tags($meta);
+
+ echo '<dl class="img_tags">';
+ foreach($tags as $tag){
+ if ($tag['value']) {
+ $value = cleanText($tag['value']);
+ echo '<dt>'.$lang[$tag['tag'][1]].':</dt><dd>';
+ if ($tag['tag'][2] == 'date') echo dformat($value);
+ else echo hsc($value);
+ echo '</dd>';
}
}
- media_searchform($ns);
+ echo '</dl>';
+}
+
+/**
+ * Shows difference between two revisions of file
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_diff($image, $ns, $auth, $fromajax = false) {
+ global $lang;
+ global $conf;
+
+ if ($auth < AUTH_READ || !$image || !$conf['mediarevisions']) return '';
+
+ $rev1 = (int) $_REQUEST['rev'];
+
+ if(is_array($_REQUEST['rev2'])){
+ $rev1 = (int) $_REQUEST['rev2'][0];
+ $rev2 = (int) $_REQUEST['rev2'][1];
+
+ if(!$rev1){
+ $rev1 = $rev2;
+ unset($rev2);
+ }
+ }else{
+ $rev2 = (int) $_REQUEST['rev2'];
+ }
+
+ if ($rev1 && !file_exists(mediaFN($image, $rev1))) $rev1 = false;
+ if ($rev2 && !file_exists(mediaFN($image, $rev2))) $rev2 = false;
+
+ if($rev1 && $rev2){ // two specific revisions wanted
+ // make sure order is correct (older on the left)
+ if($rev1 < $rev2){
+ $l_rev = $rev1;
+ $r_rev = $rev2;
+ }else{
+ $l_rev = $rev2;
+ $r_rev = $rev1;
+ }
+ }elseif($rev1){ // single revision given, compare to current
+ $r_rev = '';
+ $l_rev = $rev1;
+ }else{ // no revision was given, compare previous to current
+ $r_rev = '';
+ $revs = getRevisions($image, 0, 1, 8192, true);
+ if (file_exists(mediaFN($image, $revs[0]))) {
+ $l_rev = $revs[0];
+ } else {
+ $l_rev = '';
+ }
+ }
+
+ // prepare event data
+ $data[0] = $image;
+ $data[1] = $l_rev;
+ $data[2] = $r_rev;
+ $data[3] = $ns;
+ $data[4] = $auth;
+ $data[5] = $fromajax;
+
+ // trigger event
+ return trigger_event('MEDIA_DIFF', $data, '_media_file_diff', true);
+
+}
+
+function _media_file_diff($data) {
+ if(is_array($data) && count($data)===6) {
+ return media_file_diff($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Shows difference between two revisions of image
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_file_diff($image, $l_rev, $r_rev, $ns, $auth, $fromajax){
+ global $lang, $config_cascade;
+
+ $l_meta = new JpegMeta(mediaFN($image, $l_rev));
+ $r_meta = new JpegMeta(mediaFN($image, $r_rev));
+
+ $is_img = preg_match("/\.(jpe?g|gif|png)$/", $image);
+ if ($is_img) {
+ $l_size = media_image_preview_size($image, $l_rev, $l_meta);
+ $r_size = media_image_preview_size($image, $r_rev, $r_meta);
+ $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
+
+ $difftype = $_REQUEST['difftype'];
+
+ if (!$fromajax) {
+ $form = new Doku_Form(array('action'=>media_managerURL(array(), '&'),
+ 'id' => 'mediamanager__form_diffview'));
+ $form->addElement('<input type="hidden" name="rev2[]" value="'.$l_rev.'" ></input>');
+ $form->addElement('<input type="hidden" name="rev2[]" value="'.$r_rev.'" ></input>');
+ $form->addHidden('mediado', 'diff');
+ $form->printForm();
+
+ echo '<div id="mediamanager__diff" >';
+ }
+
+ if ($difftype == 'opacity' || $difftype == 'portions') {
+ media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $difftype);
+ if (!$fromajax) echo '</div>';
+ return '';
+ }
+ }
+
+ echo '<div class="mediamanager-preview">';
+ echo '<ul id="mediamanager__diff_table">';
+
+ echo '<li>';
+ media_preview($image, $auth, $l_rev, $l_meta);
+ echo '</li>';
+
+ echo '<li>';
+ media_preview($image, $auth, $r_rev, $r_meta);
+ echo '</li>';
+
+ echo '<li>';
+ media_preview_buttons($image, $auth, $l_rev);
+ echo '</li>';
+
+ echo '<li>';
+ media_preview_buttons($image, $auth, $r_rev);
+ echo '</li>';
+
+ $l_tags = media_file_tags($l_meta);
+ $r_tags = media_file_tags($r_meta);
+ foreach ($l_tags as $key => $l_tag) {
+ if ($l_tag['value'] != $r_tags[$key]['value']) {
+ $r_tags[$key]['class'] = 'highlighted';
+ $l_tags[$key]['class'] = 'highlighted';
+ } else if (!$l_tag['value'] || !$r_tags[$key]['value']) {
+ unset($r_tags[$key]);
+ unset($l_tags[$key]);
+ }
+ }
+
+ foreach(array($l_tags,$r_tags) as $tags){
+ echo '<li><div>';
+
+ echo '<dl class="img_tags">';
+ foreach($tags as $tag){
+ $value = cleanText($tag['value']);
+ if (!$value) $value = '-';
+ echo '<dt>'.$lang[$tag['tag'][1]].':</dt>';
+ echo '<dd class="'.$tag['class'].'" >';
+ if ($tag['tag'][2] == 'date') echo dformat($value);
+ else echo hsc($value);
+ echo '</dd>';
+ }
+ echo '</dl>';
+
+ echo '</div></li>';
+ }
+
+ echo '</ul>';
+ echo '</div>';
+
+ if ($is_img && !$fromajax) echo '</div>';
+}
+
+/**
+ * Prints two images side by side
+ * and slider
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param string $image
+ * @param int $l_rev
+ * @param int $r_rev
+ * @param array $l_size
+ * @param array $r_size
+ * @param string $type
+ */
+function media_image_diff($image, $l_rev, $r_rev, $l_size, $r_size, $type) {
+ if ($l_size != $r_size) {
+ if ($r_size[0] > $l_size[0]) {
+ $l_size = $r_size;
+ }
+ }
+
+ echo '<div class="mediamanager-preview">';
+
+ $l_more = array('rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
+ $r_more = array('rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]);
+
+ $l_src = ml($image, $l_more);
+ $r_src = ml($image, $r_more);
+
+ // slider
+ echo '<div id="mediamanager__'.$type.'_slider" style="max-width: '.($l_size[0]-20).'px;" ></div>';
+
+ // two image's in div's
+ echo '<div id="mediamanager__diff_layout">';
+ echo '<div id="mediamanager__diff_'.$type.'_image1" style="max-width: '.$l_size[0].'px;">';
+ echo '<img src="'.$l_src.'" alt="" />';
+ echo '</div>';
+ echo '<div id="mediamanager__diff_'.$type.'_image2" style="max-width: '.$l_size[0].'px;">';
+ echo '<img src="'.$r_src.'" alt="" />';
+ echo '</div>';
+ echo '</div>';
+
+ echo '</div>';
+}
+
+/**
+ * Restores an old revision of a media file
+ *
+ * @param string $image
+ * @param int $rev
+ * @param int $auth
+ * @return string - file's id
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_restore($image, $rev, $auth){
+ global $conf;
+ if ($auth < AUTH_UPLOAD || !$conf['mediarevisions']) return false;
+ $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')));
+ if (!$image || (!file_exists(mediaFN($image)) && !$removed)) return false;
+ if (!$rev || !file_exists(mediaFN($image, $rev))) return false;
+ list($iext,$imime,$dl) = mimetype($image);
+ $res = media_upload_finish(mediaFN($image, $rev),
+ mediaFN($image),
+ $image,
+ $imime,
+ true,
+ 'copy');
+ if (is_array($res)) {
+ msg($res[0], $res[1]);
+ return false;
+ }
+ return $res;
}
/**
@@ -482,11 +1299,13 @@ function media_filelist($ns,$auth=null,$jump=''){
*
* @author Tobias Sarnowski <sarnowski@cosmocode.de>
* @author Andreas Gohr <gohr@cosmocode.de>
+ * @author Kate Arzamastseva <pshns@ukr.net>
* @triggers MEDIA_SEARCH
*/
-function media_searchlist($query,$ns,$auth=null){
+function media_searchlist($query,$ns,$auth=null,$fullscreen=false,$sort=''){
global $conf;
global $lang;
+
$ns = cleanID($ns);
if ($query) {
@@ -505,50 +1324,39 @@ function media_searchlist($query,$ns,$auth=null){
array('showmsg'=>false,'pattern'=>$pattern),
$dir);
}
+
+ $data = array();
+ foreach ($evdata['data'] as $k => $v) {
+ $data[$k] = ($sort == 'date') ? $v['mtime'] : $v['id'];
+ }
+ array_multisort($data, SORT_DESC, SORT_NUMERIC, $evdata['data']);
+
$evt->advise_after();
unset($evt);
}
- echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
- media_searchform($ns,$query);
+ if (!$fullscreen) {
+ echo '<h1 id="media__ns">'.sprintf($lang['searchmedia_in'],hsc($ns).':*').'</h1>'.NL;
+ media_searchform($ns,$query);
+ }
if(!count($evdata['data'])){
echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL;
- }else foreach($evdata['data'] as $item){
- media_printfile($item,$item['perm'],'',true);
- }
-}
-
-/**
- * Print action links for a file depending on filetype
- * and available permissions
- */
-function media_fileactions($item,$auth){
- global $lang;
-
- // view button
- $link = ml($item['id'],'',true);
- echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
- 'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
-
- // no further actions if not writable
- if(!$item['writable']) return;
-
- // delete button
- if($auth >= AUTH_DELETE){
- echo ' <a href="'.DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
- '&amp;sectok='.getSecurityToken().'" class="btn_media_delete" title="'.$item['id'].'">'.
- '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '.
- 'title="'.$lang['btn_delete'].'" class="btn" /></a>';
- }
-
- // edit button
- if($auth >= AUTH_UPLOAD && $item['isimg'] && $item['meta']->getField('File.Mime') == 'image/jpeg'){
- echo ' <a href="'.DOKU_BASE.'lib/exe/mediamanager.php?edit='.rawurlencode($item['id']).'">'.
- '<img src="'.DOKU_BASE.'lib/images/pencil.png" alt="'.$lang['metaedit'].'" '.
- 'title="'.$lang['metaedit'].'" class="btn" /></a>';
+ }else {
+ if ($fullscreen) {
+ $view = $_REQUEST['view'];
+ if ($view == 'list') {
+ echo '<ul class="mediamanager-list" id="mediamanager__file_list">';
+ } else {
+ echo '<ul class="mediamanager-thumbs" id="mediamanager__file_list">';
+ }
+ }
+ foreach($evdata['data'] as $item){
+ if (!$fullscreen) media_printfile($item,$item['perm'],'',true);
+ else media_printfile_thumbs($item,$item['perm'],false,true);
+ }
+ if ($fullscreen) echo '</ul>';
}
-
}
/**
@@ -599,7 +1407,12 @@ function media_printfile($item,$auth,$jump,$display_namespace=false){
echo '<a name="h_:'.$item['id'].'" class="'.$class.'">'.hsc($item['id']).'</a><br/>';
}
echo '<span class="info">('.$info.')</span>'.NL;
- media_fileactions($item,$auth);
+
+ // view button
+ $link = ml($item['id'],'',true);
+ echo ' <a href="'.$link.'" target="_blank"><img src="'.DOKU_BASE.'lib/images/magnifier.png" '.
+ 'alt="'.$lang['mediaview'].'" title="'.$lang['mediaview'].'" class="btn" /></a>';
+
echo '<div class="example" id="ex_'.str_replace(':','_',$item['id']).'">';
echo $lang['mediausage'].' <code>{{:'.$item['id'].'}}</code>';
echo '</div>';
@@ -608,27 +1421,113 @@ function media_printfile($item,$auth,$jump,$display_namespace=false){
echo '</div>'.NL;
}
+function media_printicon($filename){
+ list($ext,$mime,$dl) = mimetype(mediaFN($filename),false);
+
+ if (@file_exists(DOKU_INC.'lib/images/fileicons/'.$ext.'.png')) {
+ $icon = DOKU_BASE.'lib/images/fileicons/'.$ext.'.png';
+ } else {
+ $icon = DOKU_BASE.'lib/images/fileicons/file.png';
+ }
+
+ return '<img src="'.$icon.'" alt="'.$filename.'" class="icon" />';
+
+}
+
+/**
+ * Formats and prints one file in the list in the thumbnails view
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function media_printfile_thumbs($item,$auth,$jump=false,$display_namespace=false){
+ global $lang;
+ global $conf;
+
+ // Prepare filename
+ $file = utf8_decodeFN($item['file']);
+
+ // output
+ echo '<li><div>';
+
+ if($item['isimg']) {
+ media_printimgdetail($item, true);
+
+ } else {
+ echo '<a name="d_:'.$item['id'].'" class="image" title="'.$item['id'].'" href="'.
+ media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
+ 'tab_details' => 'view')).'"><span>';
+ echo media_printicon($item['id']);
+ echo '</span></a>';
+ }
+ //echo '<input type=checkbox />';
+ if (!$display_namespace) {
+ $name = hsc($file);
+ } else {
+ $name = hsc($item['id']);
+ }
+ echo '<a href="'.media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']),
+ 'tab_details' => 'view')).'" name="h_:'.$item['id'].'" class="name">'.$name.'</a>';
+
+ if($item['isimg']){
+ $size = '';
+ $size .= (int) $item['meta']->getField('File.Width');
+ $size .= '&#215;';
+ $size .= (int) $item['meta']->getField('File.Height');
+ echo '<span class="size">'.$size.'</span>';
+ } else {
+ echo '<span class="size">&nbsp;</span>';
+ }
+ $date = dformat($item['mtime']);
+ echo '<span class="date">'.$date.'</span>';
+ $filesize = filesize_h($item['size']);
+ echo '<span class="filesize">'.$filesize.'</span>';
+ echo '<div class="clearer"></div>';
+ echo '</div></li>'.NL;
+}
+
/**
* Prints a thumbnail and metainfos
*/
-function media_printimgdetail($item){
+function media_printimgdetail($item, $fullscreen=false){
// prepare thumbnail
- $w = (int) $item['meta']->getField('File.Width');
- $h = (int) $item['meta']->getField('File.Height');
- if($w>120 || $h>120){
- $ratio = $item['meta']->getResizeRatio(120);
- $w = floor($w * $ratio);
- $h = floor($h * $ratio);
+ if (!$fullscreen) {
+ $size_array[] = 120;
+ } else {
+ $size_array = array(90, 40);
+ }
+ foreach ($size_array as $index => $size) {
+ $w = (int) $item['meta']->getField('File.Width');
+ $h = (int) $item['meta']->getField('File.Height');
+ if($w>$size || $h>$size){
+ if (!$fullscreen) {
+ $ratio = $item['meta']->getResizeRatio($size);
+ } else {
+ $ratio = $item['meta']->getResizeRatio($size,$size);
+ }
+ $w = floor($w * $ratio);
+ $h = floor($h * $ratio);
+ }
+ $src = ml($item['id'],array('w'=>$w,'h'=>$h,'t'=>$item['mtime']));
+ $p = array();
+ if (!$fullscreen) {
+ $p['width'] = $w;
+ $p['height'] = $h;
+ }
+ $p['alt'] = $item['id'];
+ $p['class'] = 'thumb';
+ $att = buildAttributes($p);
+
+ // output
+ if ($fullscreen) {
+ echo '<a name="'.($index ? 'd' : 'l').'_:'.$item['id'].'" class="image'.$index.'" title="'.$item['id'].'" href="'.
+ media_managerURL(array('image' => hsc($item['id']), 'ns' => getNS($item['id']), 'tab_details' => 'view')).'">';
+ echo '<span><img src="'.$src.'" '.$att.' /></span>';
+ echo '</a>';
+ }
}
- $src = ml($item['id'],array('w'=>$w,'h'=>$h));
- $p = array();
- $p['width'] = $w;
- $p['height'] = $h;
- $p['alt'] = $item['id'];
- $p['class'] = 'thumb';
- $att = buildAttributes($p);
- // output
+ if ($fullscreen) return '';
+
echo '<div class="detail">';
echo '<div class="thumb">';
echo '<a name="d_:'.$item['id'].'" class="select">';
@@ -656,90 +1555,118 @@ function media_printimgdetail($item){
}
/**
+ * Build link based on the current, adding/rewriting
+ * parameters
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ * @param array $params
+ * @param string $amp - separator
+ * @return string - link
+ */
+function media_managerURL($params=false, $amp='&amp;', $abs=false, $params_array=false) {
+ global $conf;
+ global $ID;
+
+ $gets = array('do' => 'media');
+ $media_manager_params = array('tab_files', 'tab_details', 'image', 'ns', 'view');
+ foreach ($media_manager_params as $x) {
+ if (isset($_REQUEST[$x])) $gets[$x] = $_REQUEST[$x];
+ }
+
+ if ($params) {
+ foreach ($params as $k => $v) {
+ $gets[$k] = $v;
+ }
+ }
+ unset($gets['id']);
+ if ($gets['delete']) {
+ unset($gets['image']);
+ unset($gets['tab_details']);
+ }
+
+ if ($params_array) return $gets;
+
+ return wl($ID,$gets,$abs,$amp);
+}
+
+/**
* Print the media upload form if permissions are correct
*
* @author Andreas Gohr <andi@splitbrain.org>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function media_uploadform($ns, $auth){
- global $lang;
+function media_uploadform($ns, $auth, $fullscreen = false){
+ global $lang, $conf;
- if($auth < AUTH_UPLOAD) return; //fixme print info on missing permissions?
+ if($auth < AUTH_UPLOAD) {
+ echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
+ return;
+ }
+ $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
+
+ $update = false;
+ $id = '';
+ if ($auth >= $auth_ow && $fullscreen && $_REQUEST['mediado'] == 'update') {
+ $update = true;
+ $id = cleanID($_REQUEST['image']);
+ }
// The default HTML upload form
- $form = new Doku_Form(array('id' => 'dw__upload',
- 'action' => DOKU_BASE.'lib/exe/mediamanager.php',
- 'enctype' => 'multipart/form-data'));
- $form->addElement('<div class="upload">' . $lang['mediaupload'] . '</div>');
+ $params = array('id' => 'dw__upload',
+ 'enctype' => 'multipart/form-data');
+ if (!$fullscreen) {
+ $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
+ } else {
+ $params['action'] = media_managerURL(array('tab_files' => 'files',
+ 'tab_details' => 'view'), '&');
+ }
+
+ $form = new Doku_Form($params);
+ if (!$fullscreen) echo '<div class="upload">' . $lang['mediaupload'] . '</div>';
$form->addElement(formSecurityToken());
$form->addHidden('ns', hsc($ns));
$form->addElement(form_makeOpenTag('p'));
$form->addElement(form_makeFileField('upload', $lang['txt_upload'].':', 'upload__file'));
$form->addElement(form_makeCloseTag('p'));
$form->addElement(form_makeOpenTag('p'));
- $form->addElement(form_makeTextField('id', '', $lang['txt_filename'].':', 'upload__name'));
+ $form->addElement(form_makeTextField('mediaid', noNS($id), $lang['txt_filename'].':', 'upload__name'));
$form->addElement(form_makeButton('submit', '', $lang['btn_upload']));
$form->addElement(form_makeCloseTag('p'));
- if($auth >= AUTH_DELETE){
+ if($auth >= $auth_ow){
$form->addElement(form_makeOpenTag('p'));
- $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check'));
+ $attrs = array();
+ if ($update) $attrs['checked'] = 'checked';
+ $form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check', $attrs));
$form->addElement(form_makeCloseTag('p'));
}
- html_form('upload', $form);
- // prepare flashvars for multiupload
- $opt = array(
- 'L_gridname' => $lang['mu_gridname'] ,
- 'L_gridsize' => $lang['mu_gridsize'] ,
- 'L_gridstat' => $lang['mu_gridstat'] ,
- 'L_namespace' => $lang['mu_namespace'] ,
- 'L_overwrite' => $lang['txt_overwrt'],
- 'L_browse' => $lang['mu_browse'],
- 'L_upload' => $lang['btn_upload'],
- 'L_toobig' => $lang['mu_toobig'],
- 'L_ready' => $lang['mu_ready'],
- 'L_done' => $lang['mu_done'],
- 'L_fail' => $lang['mu_fail'],
- 'L_authfail' => $lang['mu_authfail'],
- 'L_progress' => $lang['mu_progress'],
- 'L_filetypes' => $lang['mu_filetypes'],
- 'L_info' => $lang['mu_info'],
- 'L_lasterr' => $lang['mu_lasterr'],
-
- 'O_ns' => ":$ns",
- 'O_backend' => 'mediamanager.php?'.session_name().'='.session_id(),
- 'O_maxsize' => php_to_byte(ini_get('upload_max_filesize')),
- 'O_extensions'=> join('|',array_keys(getMimeTypes())),
- 'O_overwrite' => ($auth >= AUTH_DELETE),
- 'O_sectok' => getSecurityToken(),
- 'O_authtok' => auth_createToken(),
- );
- $var = buildURLparams($opt);
- // output the flash uploader
- ?>
- <div id="dw__flashupload" style="display:none">
- <div class="upload"><?php echo $lang['mu_intro']?></div>
- <?php echo html_flashobject('multipleUpload.swf','500','190',null,$opt); ?>
- </div>
- <?php
+ echo '<div id="mediamanager__uploader">';
+ html_form('upload', $form);
+ echo '</div>';
}
/**
* Print the search field form
*
* @author Tobias Sarnowski <sarnowski@cosmocode.de>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function media_searchform($ns,$query=''){
+function media_searchform($ns,$query='',$fullscreen=false){
global $lang;
// The default HTML search form
- $form = new Doku_Form(array('id' => 'dw__mediasearch', 'action' => DOKU_BASE.'lib/exe/mediamanager.php'));
- $form->addElement('<div class="upload">' . $lang['mediasearch'] . '</div>');
+ $params = array('id' => 'dw__mediasearch');
+ if (!$fullscreen) $params['action'] = DOKU_BASE.'lib/exe/mediamanager.php';
+ else $params['action'] = media_managerURL(array(), '&');
+ $form = new Doku_Form($params);
+ if (!$fullscreen) $form->addElement('<div class="upload">' . $lang['mediasearch'] . '</div>');
$form->addElement(formSecurityToken());
$form->addHidden('ns', $ns);
- $form->addHidden('do', 'searchlist');
+ if (!$fullscreen) $form->addHidden('do', 'searchlist');
+ else $form->addHidden('mediado', 'searchlist');
$form->addElement(form_makeOpenTag('p'));
- $form->addElement(form_makeTextField('q', $query,$lang['searchmedia'],'','',array('title'=>sprintf($lang['searchmedia_in'],hsc($ns).':*'))));
+ $form->addElement(form_makeTextField('q', $query,$lang['searchmedia'],'mediamanager__sort_textfield','',array('title'=>sprintf($lang['searchmedia_in'],hsc($ns).':*'))));
$form->addElement(form_makeButton('submit', '', $lang['btn_search']));
$form->addElement(form_makeCloseTag('p'));
html_form('searchmedia', $form);
@@ -786,7 +1713,10 @@ function media_nstree_item($item){
if(!$item['label']) $item['label'] = $label;
$ret = '';
+ if (!($_REQUEST['do'] == 'media'))
$ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">';
+ else $ret .= '<a href="'.media_managerURL(array('ns' => idfilter($item['id']), 'tab_files' => 'files'))
+ .'" class="idx_dir">';
$ret .= $item['label'];
$ret .= '</a>';
return $ret;
diff --git a/inc/pageutils.php b/inc/pageutils.php
index c9bf60135..81dcb66e7 100644
--- a/inc/pageutils.php
+++ b/inc/pageutils.php
@@ -295,8 +295,6 @@ function wikiLockFN($id) {
/**
* returns the full path to the meta file specified by ID and extension
*
- * The filename is URL encoded to protect Unicode chars
- *
* @author Steven Danz <steven-danz@kc.rr.com>
*/
function metaFN($id,$ext){
@@ -308,6 +306,19 @@ function metaFN($id,$ext){
}
/**
+ * returns the full path to the media's meta file specified by ID and extension
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function mediaMetaFN($id,$ext){
+ global $conf;
+ $id = cleanID($id);
+ $id = str_replace(':','/',$id);
+ $fn = $conf['mediametadir'].'/'.utf8_encodeFN($id).$ext;
+ return $fn;
+}
+
+/**
* returns an array of full paths to all metafiles of a given ID
*
* @author Esther Brunner <esther@kaffeehaus.ch>
@@ -326,12 +337,19 @@ function metaFiles($id){
* The filename is URL encoded to protect Unicode chars
*
* @author Andreas Gohr <andi@splitbrain.org>
+ * @author Kate Arzamastseva <pshns@ukr.net>
*/
-function mediaFN($id){
+function mediaFN($id, $rev=''){
global $conf;
$id = cleanID($id);
$id = str_replace(':','/',$id);
- $fn = $conf['mediadir'].'/'.utf8_encodeFN($id);
+ if(empty($rev)){
+ $fn = $conf['mediadir'].'/'.utf8_encodeFN($id);
+ }else{
+ $ext = mimetype($id);
+ $name = substr($id,0, -1*strlen($ext[0])-1);
+ $fn = $conf['mediaolddir'].'/'.utf8_encodeFN($name .'.'.( (int) $rev ).'.'.$ext[0]);
+ }
return $fn;
}
diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index 83359cd55..ea1756803 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -805,8 +805,10 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
if($hash) $link['url'] .= '#'.$hash;
//markup non existing files
- if (!$exists)
- $link['class'] .= ' wikilink2';
+ if (!$exists) {
+ $link['class'] .= ' wikilink2';
+ $link['url'] = media_managerURL(array('tab_details' => 'view', 'image' => $src, 'ns' => getNS($src)), '&');
+ }
//output formatted
if ($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
diff --git a/inc/search.php b/inc/search.php
index 7b53edabe..a26ae4808 100644
--- a/inc/search.php
+++ b/inc/search.php
@@ -21,9 +21,10 @@ if(!defined('DOKU_INC')) die('meh.');
* @param int $lvl Recursion Level
* @author Andreas Gohr <andi@splitbrain.org>
*/
-function search(&$data,$base,$func,$opts,$dir='',$lvl=1){
+function search(&$data,$base,$func,$opts,$dir='',$lvl=1,$sort=false){
$dirs = array();
$files = array();
+ $filepaths = array();
//read in directories and files
$dh = @opendir($base.'/'.$dir);
@@ -35,9 +36,14 @@ function search(&$data,$base,$func,$opts,$dir='',$lvl=1){
continue;
}
$files[] = $dir.'/'.$file;
+ $filepaths[] = $base.'/'.$dir.'/'.$file;
}
closedir($dh);
- sort($files);
+ if ($sort == 'date') {
+ @array_multisort(array_map('filemtime', $filepaths), SORT_NUMERIC, SORT_DESC, $files);
+ } else {
+ sort($files);
+ }
sort($dirs);
//give directories to userfunction then recurse
@@ -78,8 +84,8 @@ function search_callback($func,&$data,$base,$file,$type,$lvl,$opts){
* return values for files are ignored
*
* All functions should check the ACL for document READ rights
- * namespaces (directories) are NOT checked as this would break
- * the recursion (You can have an nonreadable dir over a readable
+ * namespaces (directories) are NOT checked (when sneaky_index is 0) as this
+ * would break the recursion (You can have an nonreadable dir over a readable
* one deeper nested) also make sure to check the file type (for example
* in case of lockfiles).
*/
@@ -101,45 +107,22 @@ function search_qsearch(&$data,$base,$file,$type,$lvl,$opts){
/**
* Build the browsable index of pages
*
- * $opts['ns'] is the current namespace
+ * $opts['ns'] is the currently viewed namespace
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function search_index(&$data,$base,$file,$type,$lvl,$opts){
global $conf;
- $return = true;
-
- $item = array();
-
- if($type == 'd' && !preg_match('#^'.$file.'(/|$)#','/'.$opts['ns'])){
- //add but don't recurse
- $return = false;
- }elseif($type == 'f' && ($opts['nofiles'] || substr($file,-4) != '.txt')){
- //don't add
- return false;
- }
-
- $id = pathID($file,($type == 'd'));
-
- if($type=='d' && $conf['sneaky_index'] && auth_quickaclcheck($id.':') < AUTH_READ){
- return false;
- }
-
- //check hidden
- if(isHiddenPage($id)){
- return false;
- }
-
- //check ACL
- if($type=='f' && auth_quickaclcheck($id) < AUTH_READ){
- return false;
- }
-
- $data[]=array( 'id' => $id,
- 'type' => $type,
- 'level' => $lvl,
- 'open' => $return );
- return $return;
+ $opts = array(
+ 'pagesonly' => true,
+ 'listdirs' => true,
+ 'listfiles' => !$opts['nofiles'],
+ 'sneakyacl' => $conf['sneaky_index'],
+ // Hacky, should rather use recmatch
+ 'depth' => preg_match('#^'.$file.'(/|$)#','/'.$opts['ns']) ? 0 : -1
+ );
+
+ return search_universal($data, $base, $file, $type, $lvl, $opts);
}
/**
diff --git a/inc/template.php b/inc/template.php
index 5184929b8..96e0668c2 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -89,7 +89,8 @@ function tpl_content_core(){
$_REQUEST['first'] = $_REQUEST['first'][0];
}
$first = is_numeric($_REQUEST['first']) ? intval($_REQUEST['first']) : 0;
- html_recent($first);
+ $show_changes = $_REQUEST['show_changes'];
+ html_recent($first, $show_changes);
break;
case 'index':
html_index($IDX); #FIXME can this be pulled from globals? is it sanitized correctly?
@@ -122,6 +123,9 @@ function tpl_content_core(){
case 'subscribe':
tpl_subscribe();
break;
+ case 'media':
+ tpl_media();
+ break;
default:
$evt = new Doku_Event('TPL_ACT_UNKNOWN',$ACT);
if ($evt->advise_before())
@@ -626,6 +630,8 @@ function tpl_get_action($type) {
// Superseded by subscribe/subscription
return '';
break;
+ case 'media':
+ break;
default:
return '[unknown %s type]';
break;
@@ -1103,9 +1109,7 @@ function tpl_mediaContent($fromajax=false){
$evt = new Doku_Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
if ($evt->advise_before()) {
$do = $data['do'];
- if($do == 'metaform'){
- media_metaform($IMG,$AUTH);
- }elseif($do == 'filesinuse'){
+ if($do == 'filesinuse'){
media_filesinuse($INUSE,$IMG);
}elseif($do == 'filelist'){
media_filelist($NS,$AUTH,$JUMPTO);
@@ -1122,6 +1126,93 @@ function tpl_mediaContent($fromajax=false){
}
/**
+ * Prints the central column in full-screen media manager
+ * Depending on the opened tab this may be a list of
+ * files in a namespace, upload form or search form
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function tpl_mediaFileList(){
+ global $AUTH;
+ global $NS;
+ global $JUMPTO;
+
+ $opened_tab = $_REQUEST['tab_files'];
+ if (!$opened_tab || !in_array($opened_tab, array('files', 'upload', 'search'))) $opened_tab = 'files';
+ if ($_REQUEST['mediado'] == 'update') $opened_tab = 'upload';
+
+ media_tabs_files($opened_tab);
+
+ if ($opened_tab == 'files') {
+ echo '<div id="mediamanager__files">';
+ media_tab_files($NS,$AUTH,$JUMPTO);
+ echo '</div>';
+
+ } elseif ($opened_tab == 'upload') {
+ echo '<div id="mediamanager__files">';
+ media_tab_upload($NS,$AUTH,$JUMPTO);
+ echo '</div>';
+
+ } elseif ($opened_tab == 'search') {
+ echo '<div id="mediamanager__files">';
+ media_tab_search($NS,$AUTH);
+ echo '</div>';
+ }
+
+}
+
+/**
+ * Prints the third column in full-screen media manager
+ * Depending on the opened tab this may be details of the
+ * selected file, the meta editing dialog or
+ * list of file revisions
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function tpl_mediaFileDetails($image, $rev){
+ global $AUTH, $NS, $conf, $DEL;
+
+ $removed = (!file_exists(mediaFN($image)) && file_exists(mediaMetaFN($image, '.changes')) && $conf['mediarevisions']);
+ if (!$image || (!file_exists(mediaFN($image)) && !$removed) || $DEL) return '';
+ if ($rev && !file_exists(mediaFN($image, $rev))) $rev = false;
+ if (isset($NS) && getNS($image) != $NS) return '';
+ $do = $_REQUEST['mediado'];
+
+ $opened_tab = $_REQUEST['tab_details'];
+
+ $tab_array = array('view');
+ list($ext, $mime) = mimetype($image);
+ if ($mime == 'image/jpeg') {
+ $tab_array[] = 'edit';
+ }
+ if ($conf['mediarevisions']) {
+ $tab_array[] = 'history';
+ }
+
+ if (!$opened_tab || !in_array($opened_tab, $tab_array)) $opened_tab = 'view';
+ if ($_REQUEST['edit']) $opened_tab = 'edit';
+ if ($do == 'restore') $opened_tab = 'view';
+
+ media_tabs_details($image, $opened_tab);
+
+ if ($opened_tab == 'view') {
+ echo '<div id="mediamanager__details">';
+ media_tab_view($image, $NS, $AUTH, $rev);
+ echo '</div>';
+
+ } elseif ($opened_tab == 'edit' && !$removed) {
+ echo '<div id="mediamanager__details">';
+ media_tab_edit($image, $NS, $AUTH);
+ echo '</div>';
+
+ } elseif ($opened_tab == 'history' && $conf['mediarevisions']) {
+ echo '<div id="mediamanager__details">';
+ media_tab_history($image,$NS,$AUTH);
+ echo '</div>';
+ }
+}
+
+/**
* prints the namespace tree in the mediamanger popup
*
* Only allowed in mediamanager.php
@@ -1130,7 +1221,6 @@ function tpl_mediaContent($fromajax=false){
*/
function tpl_mediaTree(){
global $NS;
-
ptln('<div id="media__tree">');
media_nstree($NS);
ptln('</div>');
@@ -1386,6 +1476,52 @@ function tpl_favicon($types=array('favicon')) {
return $return;
}
+/**
+ * Prints full-screen media manager
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function tpl_media() {
+ //
+ global $DEL, $NS, $IMG, $AUTH, $JUMPTO, $REV, $lang, $fullscreen, $conf;
+ $fullscreen = true;
+ require_once(DOKU_INC.'lib/exe/mediamanager.php');
+
+ if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']);
+ if (isset($IMG)) $image = $IMG;
+ if (isset($JUMPTO)) $image = $JUMPTO;
+ if (isset($REV) && !$JUMPTO) $rev = $REV;
+
+ echo '<div id="mediamanager__page">';
+ echo '<h1>'.$lang['btn_media'].'</h1>';
+ echo '<div id="mediamanager__layout">';
+
+ echo '<div id="mediamanager__layout_namespaces" class="layout-resizable" >';
+ html_msgarea();
+ echo '<div class="mediamanager-tabs">';
+ echo '<a href="#" class="selected">'.hsc($lang['namespaces']).'</a>';
+ echo '<div class="clearer"></div>';
+ echo '</div>';
+ echo '<div class="background-container">';
+ echo hsc($lang['namespaces']);
+ echo '</div>';
+ echo '<div class="scroll-container">';
+ tpl_mediaTree();
+ echo '</div>';
+ echo '</div>';
+
+ echo '<div id="mediamanager__layout_list" class="layout-resizable" >';
+ tpl_mediaFileList();
+ echo '</div>';
+
+ echo '<div id="mediamanager__layout_detail" class="layout" >';
+ tpl_mediaFileDetails($image, $rev);
+ echo '</div>';
+
+ echo '<div class="clearer"></div>';
+ echo '</div>';
+ echo '</div>';
+}
//Setup VIM: ex: et ts=4 :
diff --git a/lib/exe/ajax.php b/lib/exe/ajax.php
index 2bfa3680c..d4ef8dc11 100644
--- a/lib/exe/ajax.php
+++ b/lib/exe/ajax.php
@@ -208,7 +208,100 @@ function ajax_medialist(){
global $NS;
$NS = $_POST['ns'];
- tpl_mediaContent(true);
+ if ($_POST['do'] == 'media') {
+ tpl_mediaFileList();
+ } else {
+ tpl_mediaContent(true);
+ }
+}
+
+/**
+ * Return the content of the right column
+ * (image details) for the Mediamanager
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function ajax_mediadetails(){
+ global $DEL, $NS, $IMG, $AUTH, $JUMPTO, $REV, $lang, $fullscreen, $conf;
+ $fullscreen = true;
+ require_once(DOKU_INC.'lib/exe/mediamanager.php');
+
+ if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']);
+ if (isset($IMG)) $image = $IMG;
+ if (isset($JUMPTO)) $image = $JUMPTO;
+ if (isset($REV) && !$JUMPTO) $rev = $REV;
+
+ html_msgarea();
+ tpl_mediaFileDetails($image, $rev);
+}
+
+/**
+ * Returns image diff representation for mediamanager
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+function ajax_mediadiff(){
+ global $NS;
+
+ if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']);
+ $NS = $_POST['ns'];
+ $auth = auth_quickaclcheck("$ns:*");
+ media_diff($image, $NS, $auth, true);
+}
+
+function ajax_mediaupload(){
+ global $NS, $MSG;
+
+ if ($_FILES['qqfile']['tmp_name']) {
+ $id = ((empty($_POST['mediaid'])) ? $_FILES['qqfile']['name'] : $_POST['mediaid']);
+ } elseif (isset($_GET['qqfile'])) {
+ $id = $_GET['qqfile'];
+ }
+
+ $id = cleanID($id, false, true);
+
+ $NS = $_REQUEST['ns'];
+ $ns = $NS.':'.getNS($id);
+
+ $AUTH = auth_quickaclcheck("$ns:*");
+ if($AUTH >= AUTH_UPLOAD) { io_createNamespace("$ns:xxx", 'media'); }
+
+ if ($_FILES['qqfile']['error']) unset($_FILES['qqfile']);
+
+ if ($_FILES['qqfile']['tmp_name']) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
+ if (isset($_GET['qqfile'])) $res = media_upload_xhr($NS, $AUTH);
+
+ if ($res) $result = array('success' => true,
+ 'link' => media_managerURL(array('ns' => $ns, 'image' => $NS.':'.$id), '&'),
+ 'id' => $NS.':'.$id, 'ns' => $NS);
+
+ if (!$result) {
+ $error = '';
+ if (isset($MSG)) {
+ foreach($MSG as $msg) $error .= $msg['msg'];
+ }
+ $result = array('error' => $msg['msg'], 'ns' => $NS);
+ }
+ echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
+}
+
+function dir_delete($path) {
+ if (!is_string($path) || $path == "") return false;
+
+ if (is_dir($path) && !is_link($path)) {
+ if (!$dh = @opendir($path)) return false;
+
+ while ($f = readdir($dh)) {
+ if ($f == '..' || $f == '.') continue;
+ dir_delete("$path/$f");
+ }
+
+ closedir($dh);
+ return @rmdir($path);
+ } else {
+ return @unlink($path);
+ }
+
+ return false;
}
/**
diff --git a/lib/exe/css.php b/lib/exe/css.php
index 8f86f2433..81f47d8fa 100644
--- a/lib/exe/css.php
+++ b/lib/exe/css.php
@@ -189,7 +189,7 @@ function css_interwiki(){
function css_filetypes(){
// default style
- echo 'a.mediafile {';
+ echo '.mediafile {';
echo ' background: transparent url('.DOKU_BASE.'lib/images/fileicons/file.png) 0px 1px no-repeat;';
echo ' padding-left: 18px;';
echo ' padding-bottom: 1px;';
@@ -212,7 +212,7 @@ function css_filetypes(){
}
foreach($exts as $ext=>$type){
$class = preg_replace('/[^_\-a-z0-9]+/','_',$ext);
- echo "a.mf_$class {";
+ echo ".mf_$class {";
echo ' background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.$type.')';
echo '}';
}
diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php
index 3ad4f1937..143d40f22 100644
--- a/lib/exe/fetch.php
+++ b/lib/exe/fetch.php
@@ -20,6 +20,10 @@
$CACHE = calc_cache($_REQUEST['cache']);
$WIDTH = (int) $_REQUEST['w'];
$HEIGHT = (int) $_REQUEST['h'];
+ $REV = (int) @$_REQUEST['rev'];
+ //sanitize revision
+ $REV = preg_replace('/[^0-9]/','',$REV);
+
list($EXT,$MIME,$DL) = mimetype($MEDIA,false);
if($EXT === false){
$EXT = 'unknown';
@@ -28,7 +32,7 @@
}
// check for permissions, preconditions and cache external files
- list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE);
+ list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV);
// prepare data for plugin events
$data = array('media' => $MEDIA,
@@ -147,7 +151,7 @@ function sendFile($file,$mime,$dl,$cache){
* @param $file reference to the file variable
* @returns array(STATUS, STATUSMESSAGE)
*/
-function checkFileStatus(&$media, &$file) {
+function checkFileStatus(&$media, &$file, $rev='') {
global $MIME, $EXT, $CACHE;
//media to local file
@@ -172,7 +176,7 @@ function checkFileStatus(&$media, &$file) {
if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ){
return array( 403, 'Forbidden' );
}
- $file = mediaFN($media);
+ $file = mediaFN($media, $rev);
}
//check file existance
diff --git a/lib/exe/js.php b/lib/exe/js.php
index e96d45ee6..33f8c695d 100644
--- a/lib/exe/js.php
+++ b/lib/exe/js.php
@@ -43,6 +43,8 @@ function js_out(){
DOKU_INC."lib/scripts/jquery/jquery$min.js",
DOKU_INC.'lib/scripts/jquery/jquery.cookie.js',
DOKU_INC."lib/scripts/jquery/jquery-ui$min.js",
+ DOKU_INC."lib/scripts/fileuploader.js",
+ DOKU_INC."lib/scripts/fileuploaderextended.js",
DOKU_INC.'lib/scripts/helpers.js',
DOKU_INC.'lib/scripts/delay.js',
DOKU_INC.'lib/scripts/cookie.js',
diff --git a/lib/exe/mediamanager.php b/lib/exe/mediamanager.php
index 02fde5a8d..5f09fe1f8 100644
--- a/lib/exe/mediamanager.php
+++ b/lib/exe/mediamanager.php
@@ -35,7 +35,7 @@
$AUTH = auth_quickaclcheck("$NS:*");
// do not display the manager if user does not have read access
- if($AUTH < AUTH_READ) {
+ if($AUTH < AUTH_READ && !$fullscreen) {
header('HTTP/1.0 403 Forbidden');
die($lang['accessdenied']);
}
@@ -76,10 +76,20 @@
}
// handle meta saving
- if($IMG && $_REQUEST['do']['save']){
+ if($IMG && @array_key_exists('save', $_REQUEST['do'])){
$JUMPTO = media_metasave($IMG,$AUTH,$_REQUEST['meta']);
}
+ if($IMG && ($_REQUEST['mediado'] == 'save' || @array_key_exists('save', $_REQUEST['mediado']))) {
+ $JUMPTO = media_metasave($IMG,$AUTH,$_REQUEST['meta']);
+ }
+
+ if ($_REQUEST['rev'] && $conf['mediarevisions']) $REV = (int) $_REQUEST['rev'];
+
+ if($_REQUEST['mediado'] == 'restore' && $conf['mediarevisions']){
+ $JUMPTO = media_restore($_REQUEST['image'], $REV, $AUTH);
+ }
+
// handle deletion
if($DEL) {
$res = 0;
@@ -88,7 +98,7 @@
}
if ($res & DOKU_MEDIA_DELETED) {
$msg = sprintf($lang['deletesucc'], noNS($DEL));
- if ($res & DOKU_MEDIA_EMPTY_NS) {
+ if ($res & DOKU_MEDIA_EMPTY_NS && !$fullscreen) {
// current namespace was removed. redirecting to root ns passing msg along
send_redirect(DOKU_URL.'lib/exe/mediamanager.php?msg1='.
rawurlencode($msg).'&edid='.$_REQUEST['edid']);
@@ -102,9 +112,11 @@
msg(sprintf($lang['deletefail'],noNS($DEL)),-1);
}
}
-
// finished - start output
- header('Content-Type: text/html; charset=utf-8');
- include(template('mediamanager.php'));
+
+ if (!$fullscreen) {
+ header('Content-Type: text/html; charset=utf-8');
+ include(template('mediamanager.php'));
+ }
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
diff --git a/lib/images/icon-file.png b/lib/images/icon-file.png
new file mode 100644
index 000000000..d350c8c31
--- /dev/null
+++ b/lib/images/icon-file.png
Binary files differ
diff --git a/lib/images/icon-list.png b/lib/images/icon-list.png
new file mode 100644
index 000000000..ecfeed92d
--- /dev/null
+++ b/lib/images/icon-list.png
Binary files differ
diff --git a/lib/images/icon-sort.png b/lib/images/icon-sort.png
new file mode 100644
index 000000000..c6403dd3c
--- /dev/null
+++ b/lib/images/icon-sort.png
Binary files differ
diff --git a/lib/images/icon-thumb.png b/lib/images/icon-thumb.png
new file mode 100644
index 000000000..ccc7a101d
--- /dev/null
+++ b/lib/images/icon-thumb.png
Binary files differ
diff --git a/lib/plugins/acl/lang/es/lang.php b/lib/plugins/acl/lang/es/lang.php
index 096320af9..91a4ca1a1 100644
--- a/lib/plugins/acl/lang/es/lang.php
+++ b/lib/plugins/acl/lang/es/lang.php
@@ -20,6 +20,7 @@
* @author Victor Castelan <victorcastelan@gmail.com>
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['admin_acl'] = 'Administración de lista de control de acceso';
$lang['acl_group'] = 'Grupo';
diff --git a/lib/plugins/acl/lang/pl/lang.php b/lib/plugins/acl/lang/pl/lang.php
index c954abe2a..a72b6af11 100644
--- a/lib/plugins/acl/lang/pl/lang.php
+++ b/lib/plugins/acl/lang/pl/lang.php
@@ -11,6 +11,7 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['admin_acl'] = 'Zarządzanie uprawnieniami';
$lang['acl_group'] = 'Grupa';
diff --git a/lib/plugins/acl/script.js b/lib/plugins/acl/script.js
index 4fc97aeb5..2598bcef8 100644
--- a/lib/plugins/acl/script.js
+++ b/lib/plugins/acl/script.js
@@ -33,7 +33,7 @@ var dw_acl = {
show_sublist,
'html'
);
- },
+ },
toggle_display: function ($clicky, opening) {
$clicky.attr('src',
@@ -93,13 +93,13 @@ var dw_acl = {
$link = jQuery(this);
- // remove highlighting
- jQuery('#acl__tree a.cur').removeClass('cur');
+ // remove highlighting
+ jQuery('#acl__tree a.cur').removeClass('cur');
- // add new highlighting
+ // add new highlighting
$link.addClass('cur');
- // set new page to detail form
+ // set new page to detail form
$frm = jQuery('#acl__detail form');
if ($link.hasClass('wikilink1')) {
$frm.find('input[name=ns]').val('');
@@ -107,7 +107,7 @@ var dw_acl = {
} else if ($link.hasClass('idx_dir')) {
$frm.find('input[name=ns]').val(dw_acl.parseatt($link[0].search).ns);
$frm.find('input[name=id]').val('');
- }
+ }
dw_acl.loadinfo();
return false;
diff --git a/lib/plugins/config/lang/es/lang.php b/lib/plugins/config/lang/es/lang.php
index 5355d64ad..ccb963ff1 100644
--- a/lib/plugins/config/lang/es/lang.php
+++ b/lib/plugins/config/lang/es/lang.php
@@ -20,6 +20,7 @@
* @author Victor Castelan <victorcastelan@gmail.com>
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['menu'] = 'Parámetros de configuración';
$lang['error'] = 'Los parámetros no han sido actualizados a causa de un valor inválido, por favor revise los cambios y re-envíe el formulario. <br /> Los valores incorrectos se mostrarán con un marco rojo alrededor.';
@@ -124,6 +125,7 @@ $lang['jpg_quality'] = 'Calidad de compresión de JPG (0-100)';
$lang['subscribers'] = 'Habilitar soporte para suscripción a páginas';
$lang['subscribe_time'] = 'Tiempo después que alguna lista de suscripción fue enviada (seg); Debe ser menor que el tiempo especificado en días recientes.';
$lang['compress'] = 'Compactar la salida de CSS y javascript';
+$lang['cssdatauri'] = 'Tamaño en bytes hasta el cual las imágenes referenciadas en archivos CSS deberían ir incrustadas en la hoja de estilos para reducir el número de cabeceras de petición HTTP. ¡Esta técnica no funcionará en IE < 8! De <code>400</code> a <code>600</code> bytes es un valor adecuado. Establezca <code>0</code> para deshabilitarlo.';
$lang['hidepages'] = 'Ocultar páginas con coincidencias (expresiones regulares)';
$lang['send404'] = 'Enviar "HTTP 404/Page Not Found" para páginas no existentes';
$lang['sitemap'] = 'Generar sitemap de Google (días)';
diff --git a/lib/plugins/config/lang/pl/lang.php b/lib/plugins/config/lang/pl/lang.php
index c051e9e13..6e94a2e3d 100644
--- a/lib/plugins/config/lang/pl/lang.php
+++ b/lib/plugins/config/lang/pl/lang.php
@@ -12,6 +12,7 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['menu'] = 'Ustawienia';
$lang['error'] = 'Ustawienia nie zostały zapisane z powodu błędnych wartości, przejrzyj je i ponów próbę zapisu. <br/> Niepoprawne wartości są wyróżnione kolorem czerwonym.';
diff --git a/lib/plugins/plugin/lang/es/lang.php b/lib/plugins/plugin/lang/es/lang.php
index 207c48d2c..2726a5ffa 100644
--- a/lib/plugins/plugin/lang/es/lang.php
+++ b/lib/plugins/plugin/lang/es/lang.php
@@ -20,6 +20,7 @@
* @author Victor Castelan <victorcastelan@gmail.com>
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['menu'] = 'Administración de Plugins';
$lang['download'] = 'Descargar e instalar un nuevo plugin';
diff --git a/lib/plugins/plugin/lang/pl/lang.php b/lib/plugins/plugin/lang/pl/lang.php
index d4ccc7751..f48426bf6 100644
--- a/lib/plugins/plugin/lang/pl/lang.php
+++ b/lib/plugins/plugin/lang/pl/lang.php
@@ -12,6 +12,7 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['menu'] = 'Menadżer wtyczek';
$lang['download'] = 'Ściągnij i zainstaluj nową wtyczkę';
diff --git a/lib/plugins/popularity/lang/es/lang.php b/lib/plugins/popularity/lang/es/lang.php
index 5e42cd45c..d78343a36 100644
--- a/lib/plugins/popularity/lang/es/lang.php
+++ b/lib/plugins/popularity/lang/es/lang.php
@@ -17,6 +17,7 @@
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author Oscar M. Lage <r0sk10@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['name'] = 'Retroinformación (Feedback) plugin Popularity';
$lang['submit'] = 'Enviar datos';
diff --git a/lib/plugins/popularity/lang/pl/lang.php b/lib/plugins/popularity/lang/pl/lang.php
index fbbe6dab2..d96254fd8 100644
--- a/lib/plugins/popularity/lang/pl/lang.php
+++ b/lib/plugins/popularity/lang/pl/lang.php
@@ -10,6 +10,12 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['name'] = 'Informacja o popularności (ładowanie może zająć dłuższą chwilę)';
$lang['submit'] = 'Wyślij dane';
+$lang['autosubmit'] = 'Automatycznie wysyłaj dane raz na miesiąc';
+$lang['submissionFailed'] = 'Dane nie mogły być przesłane ze względu na następujące błędy:';
+$lang['submitDirectly'] = 'Możesz wysłać dane ręcznie poprzez następujący formularz:';
+$lang['autosubmitError'] = 'Ostatnie wysyłanie automatyczne nie powiodło się ze względu na następujące błędy:';
+$lang['lastSent'] = 'Dane zostały wysłane:';
diff --git a/lib/plugins/popularity/lang/pl/submitted.txt b/lib/plugins/popularity/lang/pl/submitted.txt
new file mode 100644
index 000000000..195e81388
--- /dev/null
+++ b/lib/plugins/popularity/lang/pl/submitted.txt
@@ -0,0 +1,3 @@
+====== Informacje o popularności ======
+
+Wysyłanie danych powiodło się. \ No newline at end of file
diff --git a/lib/plugins/popularity/lang/uk/submitted.txt b/lib/plugins/popularity/lang/uk/submitted.txt
new file mode 100644
index 000000000..90213858d
--- /dev/null
+++ b/lib/plugins/popularity/lang/uk/submitted.txt
@@ -0,0 +1,2 @@
+====== Відгук популярності ======
+Дані були успішно відправлені. \ No newline at end of file
diff --git a/lib/plugins/revert/lang/es/lang.php b/lib/plugins/revert/lang/es/lang.php
index 7e357e3db..f793eb06d 100644
--- a/lib/plugins/revert/lang/es/lang.php
+++ b/lib/plugins/revert/lang/es/lang.php
@@ -18,6 +18,7 @@
* @author Victor Castelan <victorcastelan@gmail.com>
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['menu'] = 'Restaurador';
$lang['filter'] = 'Buscar páginas con spam';
diff --git a/lib/plugins/revert/lang/pl/lang.php b/lib/plugins/revert/lang/pl/lang.php
index 8166250b4..f36dc591a 100644
--- a/lib/plugins/revert/lang/pl/lang.php
+++ b/lib/plugins/revert/lang/pl/lang.php
@@ -9,6 +9,7 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['menu'] = 'Menadżer przywracania';
$lang['filter'] = 'Wyszukaj uszkodzone strony';
diff --git a/lib/plugins/usermanager/lang/es/lang.php b/lib/plugins/usermanager/lang/es/lang.php
index 1e79c6826..50138bf2b 100644
--- a/lib/plugins/usermanager/lang/es/lang.php
+++ b/lib/plugins/usermanager/lang/es/lang.php
@@ -19,6 +19,7 @@
* @author Victor Castelan <victorcastelan@gmail.com>
* @author Mauro Javier Giamberardino <mgiamberardino@gmail.com>
* @author emezeta <emezeta@infoprimo.com>
+ * @author Oscar Ciudad <oscar@jacho.net>
*/
$lang['menu'] = 'Administración de usuarios';
$lang['noauth'] = '(la autenticación de usuarios no está disponible)';
diff --git a/lib/plugins/usermanager/lang/pl/lang.php b/lib/plugins/usermanager/lang/pl/lang.php
index 0c4ecbcdf..7c79c5d1f 100644
--- a/lib/plugins/usermanager/lang/pl/lang.php
+++ b/lib/plugins/usermanager/lang/pl/lang.php
@@ -10,6 +10,7 @@
* @author Leszek Stachowski <shazarre@gmail.com>
* @author maros <dobrimaros@yahoo.pl>
* @author Grzegorz Widła <dzesdzes@gmail.com>
+ * @author Łukasz Chmaj <teachmeter@gmail.com>
*/
$lang['menu'] = 'Menadżer użytkowników';
$lang['noauth'] = '(uwierzytelnienie użytkownika niemożliwe)';
diff --git a/lib/scripts/behaviour.js b/lib/scripts/behaviour.js
index 1580ae86f..cfdc89157 100644
--- a/lib/scripts/behaviour.js
+++ b/lib/scripts/behaviour.js
@@ -1,6 +1,3 @@
-/*jslint sloppy: true */
-/*global jQuery, LANG, document, alert */
-
/**
* Automatic behaviours
*
@@ -16,7 +13,6 @@ var dw_behaviour = {
dw_behaviour.removeHighlightOnClick();
dw_behaviour.quickSelect();
dw_behaviour.checkWindowsShares();
- dw_behaviour.initTocToggle();
dw_behaviour.subscription();
dw_behaviour.revisionBoxHandler();
@@ -67,7 +63,9 @@ var dw_behaviour = {
quickSelect: function(){
jQuery('select.quickselect')
.change(function(e){ e.target.form.submit(); })
- .parents('form').find('input[type=submit]').hide();
+ .parents('form').find('input[type=submit]').each(function(){
+ if (!jQuery(this).hasClass('show')) jQuery(this).hide();
+ });
},
/**
@@ -87,36 +85,6 @@ var dw_behaviour = {
},
/**
- * Adds the toggle switch to the TOC
- */
- initTocToggle: function() {
- var $header = jQuery('#toc__header');
- if(!$header.length) return;
- var $toc = jQuery('#toc__inside');
-
- var $clicky = jQuery(document.createElement('span'))
- .attr('id','toc__toggle')
- .css('cursor','pointer')
- .click(function(){
- $toc.slideToggle(200);
- setClicky();
- });
- $header.prepend($clicky);
-
- var setClicky = function(){
- if($toc.css('display') == 'none'){
- $clicky.html('<span>+</span>');
- $clicky[0].className = 'toc_open';
- }else{
- $clicky.html('<span>&minus;</span>');
- $clicky[0].className = 'toc_close';
- }
- };
-
- setClicky();
- },
-
- /**
* Hide list subscription style if target is a page
*
* @author Adrian Lang <lang@cosmocode.de>
@@ -201,13 +169,13 @@ jQuery.fn.dw_show = function(fn) {
*
* @param bool the current state of the element (optional)
*/
-jQuery.fn.dw_toggle = function(bool) {
+jQuery.fn.dw_toggle = function(bool, fn) {
return this.each(function() {
var $this = jQuery(this);
if (typeof bool === 'undefined') {
bool = $this.is(':hidden');
}
- $this[bool ? "dw_show" : "dw_hide" ]();
+ $this[bool ? "dw_show" : "dw_hide" ](fn);
});
};
diff --git a/lib/scripts/compatibility.js b/lib/scripts/compatibility.js
index ddc8823d3..39f703c71 100644
--- a/lib/scripts/compatibility.js
+++ b/lib/scripts/compatibility.js
@@ -1,6 +1,3 @@
-/*jslint sloppy: true */
-/*global dw_index, dw_qsearch, DEPRECATED_WRAP */
-
/**
* Mark a JavaScript function as deprecated
*
@@ -209,4 +206,173 @@ function jsEscape(text){
return text;
}
+/**
+ * Simple function to check if a global var is defined
+ *
+ * @author Kae Verens
+ * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835
+ */
+function isset(varname){
+ DEPRECATED("Use `typeof var !== 'undefined'` instead");
+ return(typeof(window[varname])!='undefined');
+}
+
+/**
+ * Checks if property is undefined
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isUndefined (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'undefined'` instead");
+ return (typeof prop == 'undefined');
+}
+
+/**
+ * Checks if property is function
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isFunction (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'function'` instead");
+ return (typeof prop == 'function');
+}
+/**
+ * Checks if property is string
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isString (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'string'` instead");
+ return (typeof prop == 'string');
+}
+
+/**
+ * Checks if property is number
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isNumber (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'number'` instead");
+ return (typeof prop == 'number');
+}
+
+/**
+ * Checks if property is the calculable number
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isNumeric (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'number' && !isNaN(var) && isFinite(var)` instead");
+ return isNumber(prop)&&!isNaN(prop)&&isFinite(prop);
+}
+
+/**
+ * Checks if property is array
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isArray (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `var instanceof Array` instead");
+ return (prop instanceof Array);
+}
+
+/**
+ * Checks if property is regexp
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isRegExp (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `var instanceof RegExp` instead");
+ return (prop instanceof RegExp);
+}
+
+/**
+ * Checks if property is a boolean value
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isBoolean (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'boolean'` instead");
+ return ('boolean' == typeof prop);
+}
+
+/**
+ * Checks if property is a scalar value (value that could be used as the hash key)
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isScalar (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED("Use `typeof var === 'string' || (typeof var === 'number' &&" +
+ " !isNaN(var) && isFinite(var))` instead");
+ return isNumeric(prop)||isString(prop);
+}
+
+/**
+ * Checks if property is empty
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ */
+function isEmpty (prop /* :Object */) /* :Boolean */ {
+ DEPRECATED();
+ var i;
+ if (isBoolean(prop)) {
+ return false;
+ } else if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) {
+ return true;
+ } else if (isString(prop) || isNumber(prop)) {
+ return !prop;
+ } else if (Boolean(prop) && false != prop) {
+ for (i in prop) {
+ if(prop.hasOwnProperty(i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Get the computed style of a node.
+ *
+ * @link https://acidmartin.wordpress.com/2008/08/26/style-get-any-css-property-value-of-an-object/
+ * @link http://svn.dojotoolkit.org/src/dojo/trunk/_base/html.js
+ */
+function gcs(node){
+ DEPRECATED('Use jQuery(node).style() instead');
+ if(node.currentStyle){
+ return node.currentStyle;
+ }else{
+ return node.ownerDocument.defaultView.getComputedStyle(node, null);
+ }
+}
diff --git a/lib/scripts/cookie.js b/lib/scripts/cookie.js
index f7d9b5ffb..4dd77beea 100644
--- a/lib/scripts/cookie.js
+++ b/lib/scripts/cookie.js
@@ -2,7 +2,7 @@
* Handles the cookie used by several JavaScript functions
*
* Only a single cookie is written and read. You may only save
-* sime name-value pairs - no complex types!
+* simple name-value pairs - no complex types!
*
* You should only use the getValue and setValue methods
*
@@ -10,7 +10,7 @@
* @author Michal Rezler <m.rezler@centrum.cz>
*/
DokuCookie = {
- data: Array(),
+ data: {},
name: 'DOKU_PREFS',
/**
@@ -19,21 +19,17 @@ DokuCookie = {
* @author Andreas Gohr <andi@splitbrain.org>
*/
setValue: function(key,val){
+ var text = '';
this.init();
this.data[key] = val;
- // prepare expire date
- var now = new Date();
- this.fixDate(now);
- now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); //expire in a year
-
//save the whole data array
- var text = '';
- for(var key in this.data){
- if (!this.data.hasOwnProperty(key)) continue;
- text += '#'+escape(key)+'#'+this.data[key];
+ jQuery.each(this.data, function (key, val) {
+ if (DokuCookie.data.hasOwnProperty(key)) {
+ text += '#'+encodeURIComponent(key)+'#'+encodeURIComponent(val);
}
- this.setCookie(this.name,text.substr(1),now,DOKU_BASE);
+ });
+ jQuery.cookie(this.name,text.substr(1), {expires: 365, path: DOKU_BASE});
},
/**
@@ -52,51 +48,16 @@ DokuCookie = {
* @author Andreas Gohr <andi@splitbrain.org>
*/
init: function(){
- if(this.data.length) return;
- var text = this.getCookie(this.name);
+ var text, parts, i;
+ if(!jQuery.isEmptyObject(this.data)) {
+ return;
+ }
+ text = jQuery.cookie(this.name);
if(text){
- var parts = text.split('#');
- for(var i=0; i<parts.length; i+=2){
- this.data[unescape(parts[i])] = unescape(parts[i+1]);
+ parts = text.split('#');
+ for(i = 0; i < parts.length; i += 2){
+ this.data[decodeURIComponent(parts[i])] = decodeURIComponent(parts[i+1]);
}
}
- },
-
- /**
- * This sets a cookie by JavaScript
- *
- * @link http://www.webreference.com/js/column8/functions.html
- */
- setCookie: function(name, value, expires_, path_, domain_, secure_) {
- var params = {
- expires: expires_,
- path: path_,
- domain: domain_,
- secure: secure_,
- };
-
- jQuery.cookie(name, value, params);
- },
-
- /**
- * This reads a cookie by JavaScript
- *
- * @link http://www.webreference.com/js/column8/functions.html
- */
- getCookie: function(name) {
- return unescape(jQuery.cookie(name));
- },
-
- /**
- * This is needed for the cookie functions
- *
- * @link http://www.webreference.com/js/column8/functions.html
- */
- fixDate: function(date) {
- var base = new Date(0);
- var skew = base.getTime();
- if (skew > 0){
- date.setTime(date.getTime() - skew);
}
- }
};
diff --git a/lib/scripts/fileuploader.js b/lib/scripts/fileuploader.js
new file mode 100644
index 000000000..e75b8d3a5
--- /dev/null
+++ b/lib/scripts/fileuploader.js
@@ -0,0 +1,1247 @@
+/**
+ * http://github.com/valums/file-uploader
+ *
+ * Multiple file upload component with progress-bar, drag-and-drop.
+ * © 2010 Andrew Valums ( andrew(at)valums.com )
+ *
+ * Licensed under GNU GPL 2 or later and GNU LGPL 2 or later, see license.txt.
+ */
+
+//
+// Helper functions
+//
+
+var qq = qq || {};
+
+/**
+ * Adds all missing properties from second obj to first obj
+ */
+qq.extend = function(first, second){
+ for (var prop in second){
+ first[prop] = second[prop];
+ }
+};
+
+/**
+ * Searches for a given element in the array, returns -1 if it is not present.
+ * @param {Number} [from] The index at which to begin the search
+ */
+qq.indexOf = function(arr, elt, from){
+ if (arr.indexOf) return arr.indexOf(elt, from);
+
+ from = from || 0;
+ var len = arr.length;
+
+ if (from < 0) from += len;
+
+ for (; from < len; from++){
+ if (from in arr && arr[from] === elt){
+ return from;
+ }
+ }
+ return -1;
+};
+
+qq.getUniqueId = (function(){
+ var id = 0;
+ return function(){ return id++; };
+})();
+
+//
+// Events
+
+qq.attach = function(element, type, fn){
+ if (element.addEventListener){
+ element.addEventListener(type, fn, false);
+ } else if (element.attachEvent){
+ element.attachEvent('on' + type, fn);
+ }
+};
+qq.detach = function(element, type, fn){
+ if (element.removeEventListener){
+ element.removeEventListener(type, fn, false);
+ } else if (element.attachEvent){
+ element.detachEvent('on' + type, fn);
+ }
+};
+
+qq.preventDefault = function(e){
+ if (e.preventDefault){
+ e.preventDefault();
+ } else{
+ e.returnValue = false;
+ }
+};
+
+//
+// Node manipulations
+
+/**
+ * Insert node a before node b.
+ */
+qq.insertBefore = function(a, b){
+ b.parentNode.insertBefore(a, b);
+};
+qq.remove = function(element){
+ element.parentNode.removeChild(element);
+};
+
+qq.contains = function(parent, descendant){
+ // compareposition returns false in this case
+ if (parent == descendant) return true;
+
+ if (parent.contains){
+ return parent.contains(descendant);
+ } else {
+ return !!(descendant.compareDocumentPosition(parent) & 8);
+ }
+};
+
+/**
+ * Creates and returns element from html string
+ * Uses innerHTML to create an element
+ */
+qq.toElement = (function(){
+ var div = document.createElement('div');
+ return function(html){
+ div.innerHTML = html;
+ var element = div.firstChild;
+ div.removeChild(element);
+ return element;
+ };
+})();
+
+//
+// Node properties and attributes
+
+/**
+ * Sets styles for an element.
+ * Fixes opacity in IE6-8.
+ */
+qq.css = function(element, styles){
+ if (styles.opacity != null){
+ if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){
+ styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')';
+ }
+ }
+ qq.extend(element.style, styles);
+};
+qq.hasClass = function(element, name){
+ var re = new RegExp('(^| )' + name + '( |$)');
+ return re.test(element.className);
+};
+qq.addClass = function(element, name){
+ if (!qq.hasClass(element, name)){
+ element.className += ' ' + name;
+ }
+};
+qq.removeClass = function(element, name){
+ var re = new RegExp('(^| )' + name + '( |$)');
+ element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, "");
+};
+qq.setText = function(element, text){
+ element.innerText = text;
+ element.textContent = text;
+};
+
+//
+// Selecting elements
+
+qq.children = function(element){
+ var children = [],
+ child = element.firstChild;
+
+ while (child){
+ if (child.nodeType == 1){
+ children.push(child);
+ }
+ child = child.nextSibling;
+ }
+
+ return children;
+};
+
+qq.getByClass = function(element, className){
+ if (element.querySelectorAll){
+ return element.querySelectorAll('.' + className);
+ }
+
+ var result = [];
+ var candidates = element.getElementsByTagName("*");
+ var len = candidates.length;
+
+ for (var i = 0; i < len; i++){
+ if (qq.hasClass(candidates[i], className)){
+ result.push(candidates[i]);
+ }
+ }
+ return result;
+};
+
+/**
+ * obj2url() takes a json-object as argument and generates
+ * a querystring. pretty much like jQuery.param()
+ *
+ * how to use:
+ *
+ * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');`
+ *
+ * will result in:
+ *
+ * `http://any.url/upload?otherParam=value&a=b&c=d`
+ *
+ * @param Object JSON-Object
+ * @param String current querystring-part
+ * @return String encoded querystring
+ */
+qq.obj2url = function(obj, temp, prefixDone){
+ var uristrings = [],
+ prefix = '&',
+ add = function(nextObj, i){
+ var nextTemp = temp
+ ? (/\[\]$/.test(temp)) // prevent double-encoding
+ ? temp
+ : temp+'['+i+']'
+ : i;
+ if ((nextTemp != 'undefined') && (i != 'undefined')) {
+ uristrings.push(
+ (typeof nextObj === 'object')
+ ? qq.obj2url(nextObj, nextTemp, true)
+ : (Object.prototype.toString.call(nextObj) === '[object Function]')
+ ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj())
+ : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj)
+ );
+ }
+ };
+
+ if (!prefixDone && temp) {
+ prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?';
+ uristrings.push(temp);
+ uristrings.push(qq.obj2url(obj));
+ } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) {
+ // we wont use a for-in-loop on an array (performance)
+ for (var i = 0, len = obj.length; i < len; ++i){
+ add(obj[i], i);
+ }
+ } else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){
+ // for anything else but a scalar, we will use for-in-loop
+ for (var i in obj){
+ add(obj[i], i);
+ }
+ } else {
+ uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj));
+ }
+
+ return uristrings.join(prefix)
+ .replace(/^&/, '')
+ .replace(/%20/g, '+');
+};
+
+//
+//
+// Uploader Classes
+//
+//
+
+var qq = qq || {};
+
+/**
+ * Creates upload button, validates upload, but doesn't create file list or dd.
+ */
+qq.FileUploaderBasic = function(o){
+ this._options = {
+ // set to true to see the server response
+ debug: false,
+ action: '/server/upload',
+ params: {},
+ button: null,
+ multiple: true,
+ maxConnections: 3,
+ // validation
+ allowedExtensions: [],
+ sizeLimit: 0,
+ minSizeLimit: 0,
+ // events
+ // return false to cancel submit
+ onSubmit: function(id, fileName){},
+ onProgress: function(id, fileName, loaded, total){},
+ onComplete: function(id, fileName, responseJSON){},
+ onCancel: function(id, fileName){},
+ // messages
+ messages: {
+ typeError: "{file} has invalid extension. Only {extensions} are allowed.",
+ sizeError: "{file} is too large, maximum file size is {sizeLimit}.",
+ minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.",
+ emptyError: "{file} is empty, please select files again without it.",
+ onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
+ },
+ showMessage: function(message){
+ alert(message);
+ }
+ };
+ qq.extend(this._options, o);
+
+ // number of files being uploaded
+ this._filesInProgress = 0;
+ this._handler = this._createUploadHandler();
+
+ if (this._options.button){
+ this._button = this._createUploadButton(this._options.button);
+ }
+
+ this._preventLeaveInProgress();
+};
+
+qq.FileUploaderBasic.prototype = {
+ setParams: function(params){
+ this._options.params = params;
+ },
+ getInProgress: function(){
+ return this._filesInProgress;
+ },
+ _createUploadButton: function(element){
+ var self = this;
+
+ return new qq.UploadButton({
+ element: element,
+ multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(),
+ onChange: function(input){
+ self._onInputChange(input);
+ }
+ });
+ },
+ _createUploadHandler: function(){
+ var self = this,
+ handlerClass;
+
+ if(qq.UploadHandlerXhr.isSupported()){
+ handlerClass = 'UploadHandlerXhr';
+ } else {
+ handlerClass = 'UploadHandlerForm';
+ }
+
+ var handler = new qq[handlerClass]({
+ debug: this._options.debug,
+ action: this._options.action,
+ maxConnections: this._options.maxConnections,
+ onProgress: function(id, fileName, loaded, total){
+ self._onProgress(id, fileName, loaded, total);
+ self._options.onProgress(id, fileName, loaded, total);
+ },
+ onComplete: function(id, fileName, result){
+ self._onComplete(id, fileName, result);
+ self._options.onComplete(id, fileName, result);
+ },
+ onCancel: function(id, fileName){
+ self._onCancel(id, fileName);
+ self._options.onCancel(id, fileName);
+ }
+ });
+
+ return handler;
+ },
+ _preventLeaveInProgress: function(){
+ var self = this;
+
+ qq.attach(window, 'beforeunload', function(e){
+ if (!self._filesInProgress){return;}
+
+ var e = e || window.event;
+ // for ie, ff
+ e.returnValue = self._options.messages.onLeave;
+ // for webkit
+ return self._options.messages.onLeave;
+ });
+ },
+ _onSubmit: function(id, fileName){
+ this._filesInProgress++;
+ },
+ _onProgress: function(id, fileName, loaded, total){
+ },
+ _onComplete: function(id, fileName, result){
+ this._filesInProgress--;
+ if (result.error){
+ this._options.showMessage(result.error);
+ }
+ },
+ _onCancel: function(id, fileName){
+ this._filesInProgress--;
+ },
+ _onInputChange: function(input){
+ if (this._handler instanceof qq.UploadHandlerXhr){
+ this._uploadFileList(input.files);
+ } else {
+ if (this._validateFile(input)){
+ this._uploadFile(input);
+ }
+ }
+ this._button.reset();
+ },
+ _uploadFileList: function(files){
+ for (var i=0; i<files.length; i++){
+ if ( !this._validateFile(files[i])){
+ return;
+ }
+ }
+
+ for (var i=0; i<files.length; i++){
+ this._uploadFile(files[i]);
+ }
+ },
+ _uploadFile: function(fileContainer){
+ var id = this._handler.add(fileContainer);
+ var fileName = this._handler.getName(id);
+
+ if (this._options.onSubmit(id, fileName) !== false){
+ this._onSubmit(id, fileName);
+ this._handler.upload(id, this._options.params);
+ }
+ },
+ _validateFile: function(file){
+ var name, size;
+
+ if (file.value){
+ // it is a file input
+ // get input value and remove path to normalize
+ name = file.value.replace(/.*(\/|\\)/, "");
+ } else {
+ // fix missing properties in Safari
+ name = file.fileName != null ? file.fileName : file.name;
+ size = file.fileSize != null ? file.fileSize : file.size;
+ }
+
+ if (! this._isAllowedExtension(name)){
+ this._error('typeError', name);
+ return false;
+
+ } else if (size === 0){
+ this._error('emptyError', name);
+ return false;
+
+ } else if (size && this._options.sizeLimit && size > this._options.sizeLimit){
+ this._error('sizeError', name);
+ return false;
+
+ } else if (size && size < this._options.minSizeLimit){
+ this._error('minSizeError', name);
+ return false;
+ }
+
+ return true;
+ },
+ _error: function(code, fileName){
+ var message = this._options.messages[code];
+ function r(name, replacement){ message = message.replace(name, replacement); }
+
+ r('{file}', this._formatFileName(fileName));
+ r('{extensions}', this._options.allowedExtensions.join(', '));
+ r('{sizeLimit}', this._formatSize(this._options.sizeLimit));
+ r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit));
+
+ this._options.showMessage(message);
+ },
+ _formatFileName: function(name){
+ if (name.length > 33){
+ name = name.slice(0, 19) + '...' + name.slice(-13);
+ }
+ return name;
+ },
+ _isAllowedExtension: function(fileName){
+ var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : '';
+ var allowed = this._options.allowedExtensions;
+
+ if (!allowed.length){return true;}
+
+ for (var i=0; i<allowed.length; i++){
+ if (allowed[i].toLowerCase() == ext){ return true;}
+ }
+
+ return false;
+ },
+ _formatSize: function(bytes){
+ var i = -1;
+ do {
+ bytes = bytes / 1024;
+ i++;
+ } while (bytes > 99);
+
+ return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i];
+ }
+};
+
+
+/**
+ * Class that creates upload widget with drag-and-drop and file list
+ * @inherits qq.FileUploaderBasic
+ */
+qq.FileUploader = function(o){
+ // call parent constructor
+ qq.FileUploaderBasic.apply(this, arguments);
+
+ // additional options
+ qq.extend(this._options, {
+ element: null,
+ // if set, will be used instead of qq-upload-list in template
+ listElement: null,
+
+ template: '<div class="qq-uploader">' +
+ '<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' +
+ '<div class="qq-upload-button">Upload a file</div>' +
+ '<ul class="qq-upload-list"></ul>' +
+ '</div>',
+
+ // template for one item in file list
+ fileTemplate: '<li>' +
+ '<span class="qq-upload-file"></span>' +
+ '<span class="qq-upload-spinner"></span>' +
+ '<span class="qq-upload-size"></span>' +
+ '<a class="qq-upload-cancel" href="#">Cancel</a>' +
+ '<span class="qq-upload-failed-text">Failed</span>' +
+ '</li>',
+
+ classes: {
+ // used to get elements from templates
+ button: 'qq-upload-button',
+ drop: 'qq-upload-drop-area',
+ dropActive: 'qq-upload-drop-area-active',
+ list: 'qq-upload-list',
+
+ file: 'qq-upload-file',
+ spinner: 'qq-upload-spinner',
+ size: 'qq-upload-size',
+ cancel: 'qq-upload-cancel',
+
+ // added to list item when upload completes
+ // used in css to hide progress spinner
+ success: 'qq-upload-success',
+ fail: 'qq-upload-fail'
+ }
+ });
+ // overwrite options with user supplied
+ qq.extend(this._options, o);
+
+ this._element = this._options.element;
+ this._element.innerHTML = this._options.template;
+ this._listElement = this._options.listElement || this._find(this._element, 'list');
+
+ this._classes = this._options.classes;
+
+ this._button = this._createUploadButton(this._find(this._element, 'button'));
+
+ this._bindCancelEvent();
+ this._setupDragDrop();
+};
+
+// inherit from Basic Uploader
+qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype);
+
+qq.extend(qq.FileUploader.prototype, {
+ /**
+ * Gets one of the elements listed in this._options.classes
+ **/
+ _find: function(parent, type){
+ var element = qq.getByClass(parent, this._options.classes[type])[0];
+ if (!element){
+ throw new Error('element not found ' + type);
+ }
+
+ return element;
+ },
+ _setupDragDrop: function(){
+ var self = this,
+ dropArea = this._find(this._element, 'drop');
+
+ var dz = new qq.UploadDropZone({
+ element: dropArea,
+ onEnter: function(e){
+ qq.addClass(dropArea, self._classes.dropActive);
+ e.stopPropagation();
+ },
+ onLeave: function(e){
+ e.stopPropagation();
+ },
+ onLeaveNotDescendants: function(e){
+ qq.removeClass(dropArea, self._classes.dropActive);
+ },
+ onDrop: function(e){
+ dropArea.style.display = 'none';
+ qq.removeClass(dropArea, self._classes.dropActive);
+ self._uploadFileList(e.dataTransfer.files);
+ }
+ });
+
+ dropArea.style.display = 'none';
+
+ qq.attach(document, 'dragenter', function(e){
+ if (!dz._isValidFileDrag(e)) return;
+
+ dropArea.style.display = 'block';
+ });
+ qq.attach(document, 'dragleave', function(e){
+ if (!dz._isValidFileDrag(e)) return;
+
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
+ // only fire when leaving document out
+ if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){
+ dropArea.style.display = 'none';
+ }
+ });
+ },
+ _onSubmit: function(id, fileName){
+ qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments);
+ this._addToList(id, fileName);
+ },
+ _onProgress: function(id, fileName, loaded, total){
+ qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments);
+
+ var item = this._getItemByFileId(id);
+ var size = this._find(item, 'size');
+ size.style.display = 'inline';
+
+ var text;
+ if (loaded != total){
+ text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total);
+ } else {
+ text = this._formatSize(total);
+ }
+
+ qq.setText(size, text);
+ },
+ _onComplete: function(id, fileName, result){
+ qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
+
+ // mark completed
+ var item = this._getItemByFileId(id);
+ qq.remove(this._find(item, 'cancel'));
+ qq.remove(this._find(item, 'spinner'));
+
+ if (result.success){
+ qq.addClass(item, this._classes.success);
+ } else {
+ qq.addClass(item, this._classes.fail);
+ }
+ },
+ _addToList: function(id, fileName){
+ var item = qq.toElement(this._options.fileTemplate);
+ item.qqFileId = id;
+
+ var fileElement = this._find(item, 'file');
+ qq.setText(fileElement, this._formatFileName(fileName));
+ this._find(item, 'size').style.display = 'none';
+
+ this._listElement.appendChild(item);
+ },
+ _getItemByFileId: function(id){
+ var item = this._listElement.firstChild;
+
+ // there can't be txt nodes in dynamically created list
+ // and we can use nextSibling
+ while (item){
+ if (item.qqFileId == id) return item;
+ item = item.nextSibling;
+ }
+ },
+ /**
+ * delegate click event for cancel link
+ **/
+ _bindCancelEvent: function(){
+ var self = this,
+ list = this._listElement;
+
+ qq.attach(list, 'click', function(e){
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+
+ if (qq.hasClass(target, self._classes.cancel)){
+ qq.preventDefault(e);
+
+ var item = target.parentNode;
+ self._handler.cancel(item.qqFileId);
+ qq.remove(item);
+ }
+ });
+ }
+});
+
+qq.UploadDropZone = function(o){
+ this._options = {
+ element: null,
+ onEnter: function(e){},
+ onLeave: function(e){},
+ // is not fired when leaving element by hovering descendants
+ onLeaveNotDescendants: function(e){},
+ onDrop: function(e){}
+ };
+ qq.extend(this._options, o);
+
+ this._element = this._options.element;
+
+ this._disableDropOutside();
+ this._attachEvents();
+};
+
+qq.UploadDropZone.prototype = {
+ _disableDropOutside: function(e){
+ // run only once for all instances
+ if (!qq.UploadDropZone.dropOutsideDisabled ){
+
+ qq.attach(document, 'dragover', function(e){
+ if (e.dataTransfer){
+ e.dataTransfer.dropEffect = 'none';
+ e.preventDefault();
+ }
+ });
+
+ qq.UploadDropZone.dropOutsideDisabled = true;
+ }
+ },
+ _attachEvents: function(){
+ var self = this;
+
+ qq.attach(self._element, 'dragover', function(e){
+ if (!self._isValidFileDrag(e)) return;
+
+ var effect = e.dataTransfer.effectAllowed;
+ if (effect == 'move' || effect == 'linkMove'){
+ e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
+ } else {
+ e.dataTransfer.dropEffect = 'copy'; // for Chrome
+ }
+
+ e.stopPropagation();
+ e.preventDefault();
+ });
+
+ qq.attach(self._element, 'dragenter', function(e){
+ if (!self._isValidFileDrag(e)) return;
+
+ self._options.onEnter(e);
+ });
+
+ qq.attach(self._element, 'dragleave', function(e){
+ if (!self._isValidFileDrag(e)) return;
+
+ self._options.onLeave(e);
+
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
+ // do not fire when moving a mouse over a descendant
+ if (qq.contains(this, relatedTarget)) return;
+
+ self._options.onLeaveNotDescendants(e);
+ });
+
+ qq.attach(self._element, 'drop', function(e){
+ if (!self._isValidFileDrag(e)) return;
+
+ e.preventDefault();
+ self._options.onDrop(e);
+ });
+ },
+ _isValidFileDrag: function(e){
+ var dt = e.dataTransfer,
+ // do not check dt.types.contains in webkit, because it crashes safari 4
+ isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;
+
+ // dt.effectAllowed is none in Safari 5
+ // dt.types.contains check is for firefox
+ return dt && dt.effectAllowed != 'none' &&
+ (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files')));
+
+ }
+};
+
+qq.UploadButton = function(o){
+ this._options = {
+ element: null,
+ // if set to true adds multiple attribute to file input
+ multiple: false,
+ // name attribute of file input
+ name: 'file',
+ onChange: function(input){},
+ hoverClass: 'qq-upload-button-hover',
+ focusClass: 'qq-upload-button-focus'
+ };
+
+ qq.extend(this._options, o);
+
+ this._element = this._options.element;
+
+ // make button suitable container for input
+ qq.css(this._element, {
+ position: 'relative',
+ overflow: 'hidden',
+ // Make sure browse button is in the right side
+ // in Internet Explorer
+ direction: 'ltr'
+ });
+
+ this._input = this._createInput();
+};
+
+qq.UploadButton.prototype = {
+ /* returns file input element */
+ getInput: function(){
+ return this._input;
+ },
+ /* cleans/recreates the file input */
+ reset: function(){
+ if (this._input.parentNode){
+ qq.remove(this._input);
+ }
+
+ qq.removeClass(this._element, this._options.focusClass);
+ this._input = this._createInput();
+ },
+ _createInput: function(){
+ var input = document.createElement("input");
+
+ if (this._options.multiple){
+ input.setAttribute("multiple", "multiple");
+ }
+
+ input.setAttribute("type", "file");
+ input.setAttribute("name", this._options.name);
+
+ qq.css(input, {
+ position: 'absolute',
+ // in Opera only 'browse' button
+ // is clickable and it is located at
+ // the right side of the input
+ right: 0,
+ top: 0,
+ fontFamily: 'Arial',
+ // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118
+ fontSize: '118px',
+ margin: 0,
+ padding: 0,
+ cursor: 'pointer',
+ opacity: 0
+ });
+
+ this._element.appendChild(input);
+
+ var self = this;
+ qq.attach(input, 'change', function(){
+ self._options.onChange(input);
+ });
+
+ qq.attach(input, 'mouseover', function(){
+ qq.addClass(self._element, self._options.hoverClass);
+ });
+ qq.attach(input, 'mouseout', function(){
+ qq.removeClass(self._element, self._options.hoverClass);
+ });
+ qq.attach(input, 'focus', function(){
+ qq.addClass(self._element, self._options.focusClass);
+ });
+ qq.attach(input, 'blur', function(){
+ qq.removeClass(self._element, self._options.focusClass);
+ });
+
+ // IE and Opera, unfortunately have 2 tab stops on file input
+ // which is unacceptable in our case, disable keyboard access
+ if (window.attachEvent){
+ // it is IE or Opera
+ input.setAttribute('tabIndex', "-1");
+ }
+
+ return input;
+ }
+};
+
+/**
+ * Class for uploading files, uploading itself is handled by child classes
+ */
+qq.UploadHandlerAbstract = function(o){
+ this._options = {
+ debug: false,
+ action: '/upload.php',
+ // maximum number of concurrent uploads
+ maxConnections: 999,
+ onProgress: function(id, fileName, loaded, total){},
+ onComplete: function(id, fileName, response){},
+ onCancel: function(id, fileName){}
+ };
+ qq.extend(this._options, o);
+
+ this._queue = [];
+ // params for files in queue
+ this._params = [];
+};
+qq.UploadHandlerAbstract.prototype = {
+ log: function(str){
+ if (this._options.debug && window.console) console.log('[uploader] ' + str);
+ },
+ /**
+ * Adds file or file input to the queue
+ * @returns id
+ **/
+ add: function(file){},
+ /**
+ * Sends the file identified by id and additional query params to the server
+ */
+ upload: function(id, params){
+ var len = this._queue.push(id);
+
+ var copy = {};
+ qq.extend(copy, params);
+ this._params[id] = copy;
+
+ // if too many active uploads, wait...
+ if (len <= this._options.maxConnections){
+ this._upload(id, this._params[id]);
+ }
+ },
+ /**
+ * Cancels file upload by id
+ */
+ cancel: function(id){
+ this._cancel(id);
+ this._dequeue(id);
+ },
+ /**
+ * Cancells all uploads
+ */
+ cancelAll: function(){
+ for (var i=0; i<this._queue.length; i++){
+ this._cancel(this._queue[i]);
+ }
+ this._queue = [];
+ },
+ /**
+ * Returns name of the file identified by id
+ */
+ getName: function(id){},
+ /**
+ * Returns size of the file identified by id
+ */
+ getSize: function(id){},
+ /**
+ * Returns id of files being uploaded or
+ * waiting for their turn
+ */
+ getQueue: function(){
+ return this._queue;
+ },
+ /**
+ * Actual upload method
+ */
+ _upload: function(id){},
+ /**
+ * Actual cancel method
+ */
+ _cancel: function(id){},
+ /**
+ * Removes element from queue, starts upload of next
+ */
+ _dequeue: function(id){
+ var i = qq.indexOf(this._queue, id);
+ this._queue.splice(i, 1);
+
+ var max = this._options.maxConnections;
+
+ if (this._queue.length >= max && i < max){
+ var nextId = this._queue[max-1];
+ this._upload(nextId, this._params[nextId]);
+ }
+ }
+};
+
+/**
+ * Class for uploading files using form and iframe
+ * @inherits qq.UploadHandlerAbstract
+ */
+qq.UploadHandlerForm = function(o){
+ qq.UploadHandlerAbstract.apply(this, arguments);
+
+ this._inputs = {};
+};
+// @inherits qq.UploadHandlerAbstract
+qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
+
+qq.extend(qq.UploadHandlerForm.prototype, {
+ add: function(fileInput){
+ fileInput.setAttribute('name', 'qqfile');
+ var id = 'qq-upload-handler-iframe' + qq.getUniqueId();
+
+ this._inputs[id] = fileInput;
+
+ // remove file input from DOM
+ if (fileInput.parentNode){
+ qq.remove(fileInput);
+ }
+
+ return id;
+ },
+ getName: function(id){
+ // get input value and remove path to normalize
+ return this._inputs[id].value.replace(/.*(\/|\\)/, "");
+ },
+ _cancel: function(id){
+ this._options.onCancel(id, this.getName(id));
+
+ delete this._inputs[id];
+
+ var iframe = document.getElementById(id);
+ if (iframe){
+ // to cancel request set src to something else
+ // we use src="javascript:false;" because it doesn't
+ // trigger ie6 prompt on https
+ iframe.setAttribute('src', 'javascript:false;');
+
+ qq.remove(iframe);
+ }
+ },
+ _upload: function(id, params){
+ var input = this._inputs[id];
+
+ if (!input){
+ throw new Error('file with passed id was not added, or already uploaded or cancelled');
+ }
+
+ var fileName = this.getName(id);
+
+ var iframe = this._createIframe(id);
+ var form = this._createForm(iframe, params);
+ form.appendChild(input);
+
+ var self = this;
+ this._attachLoadEvent(iframe, function(){
+ self.log('iframe loaded');
+
+ var response = self._getIframeContentJSON(iframe);
+
+ self._options.onComplete(id, fileName, response);
+ self._dequeue(id);
+
+ delete self._inputs[id];
+ // timeout added to fix busy state in FF3.6
+ setTimeout(function(){
+ qq.remove(iframe);
+ }, 1);
+ });
+
+ form.submit();
+ qq.remove(form);
+
+ return id;
+ },
+ _attachLoadEvent: function(iframe, callback){
+ qq.attach(iframe, 'load', function(){
+ // when we remove iframe from dom
+ // the request stops, but in IE load
+ // event fires
+ if (!iframe.parentNode){
+ return;
+ }
+
+ // fixing Opera 10.53
+ if (iframe.contentDocument &&
+ iframe.contentDocument.body &&
+ iframe.contentDocument.body.innerHTML == "false"){
+ // In Opera event is fired second time
+ // when body.innerHTML changed from false
+ // to server response approx. after 1 sec
+ // when we upload file with iframe
+ return;
+ }
+
+ callback();
+ });
+ },
+ /**
+ * Returns json object received by iframe from server.
+ */
+ _getIframeContentJSON: function(iframe){
+ // iframe.contentWindow.document - for IE<7
+ var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document,
+ response;
+
+ this.log("converting iframe's innerHTML to JSON");
+ this.log("innerHTML = " + doc.body.innerHTML);
+
+ try {
+ response = eval("(" + doc.body.innerHTML + ")");
+ } catch(err){
+ response = {};
+ }
+
+ return response;
+ },
+ /**
+ * Creates iframe with unique name
+ */
+ _createIframe: function(id){
+ // We can't use following code as the name attribute
+ // won't be properly registered in IE6, and new window
+ // on form submit will open
+ // var iframe = document.createElement('iframe');
+ // iframe.setAttribute('name', id);
+
+ var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />');
+ // src="javascript:false;" removes ie6 prompt on https
+
+ iframe.setAttribute('id', id);
+
+ iframe.style.display = 'none';
+ document.body.appendChild(iframe);
+
+ return iframe;
+ },
+ /**
+ * Creates form, that will be submitted to iframe
+ */
+ _createForm: function(iframe, params){
+ // We can't use the following code in IE6
+ // var form = document.createElement('form');
+ // form.setAttribute('method', 'post');
+ // form.setAttribute('enctype', 'multipart/form-data');
+ // Because in this case file won't be attached to request
+ var form = qq.toElement('<form method="post" enctype="multipart/form-data"></form>');
+
+ var queryString = qq.obj2url(params, this._options.action);
+
+ form.setAttribute('action', queryString);
+ form.setAttribute('target', iframe.name);
+ form.style.display = 'none';
+ document.body.appendChild(form);
+
+ return form;
+ }
+});
+
+/**
+ * Class for uploading files using xhr
+ * @inherits qq.UploadHandlerAbstract
+ */
+qq.UploadHandlerXhr = function(o){
+ qq.UploadHandlerAbstract.apply(this, arguments);
+
+ this._files = [];
+ this._xhrs = [];
+
+ // current loaded size in bytes for each file
+ this._loaded = [];
+};
+
+// static method
+qq.UploadHandlerXhr.isSupported = function(){
+ var input = document.createElement('input');
+ input.type = 'file';
+
+ return (
+ 'multiple' in input &&
+ typeof File != "undefined" &&
+ typeof (new XMLHttpRequest()).upload != "undefined" );
+};
+
+// @inherits qq.UploadHandlerAbstract
+qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype);
+
+qq.extend(qq.UploadHandlerXhr.prototype, {
+ /**
+ * Adds file to the queue
+ * Returns id to use with upload, cancel
+ **/
+ add: function(file){
+ if (!(file instanceof File)){
+ throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)');
+ }
+
+ return this._files.push(file) - 1;
+ },
+ getName: function(id){
+ var file = this._files[id];
+ // fix missing name in Safari 4
+ return file.fileName != null ? file.fileName : file.name;
+ },
+ getSize: function(id){
+ var file = this._files[id];
+ return file.fileSize != null ? file.fileSize : file.size;
+ },
+ /**
+ * Returns uploaded bytes for file identified by id
+ */
+ getLoaded: function(id){
+ return this._loaded[id] || 0;
+ },
+ /**
+ * Sends the file identified by id and additional query params to the server
+ * @param {Object} params name-value string pairs
+ */
+ _upload: function(id, params){
+ var file = this._files[id],
+ name = this.getName(id),
+ size = this.getSize(id);
+
+ this._loaded[id] = 0;
+
+ var xhr = this._xhrs[id] = new XMLHttpRequest();
+ var self = this;
+
+ xhr.upload.onprogress = function(e){
+ if (e.lengthComputable){
+ self._loaded[id] = e.loaded;
+ self._options.onProgress(id, name, e.loaded, e.total);
+ }
+ };
+
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState == 4){
+ self._onComplete(id, xhr);
+ }
+ };
+
+ // build query string
+ params = params || {};
+ params['qqfile'] = name;
+ var queryString = qq.obj2url(params, this._options.action);
+
+ xhr.open("POST", queryString, true);
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
+ xhr.setRequestHeader("Content-Type", "application/octet-stream");
+ xhr.send(file);
+ },
+ _onComplete: function(id, xhr){
+ // the request was aborted/cancelled
+ if (!this._files[id]) return;
+
+ var name = this.getName(id);
+ var size = this.getSize(id);
+
+ this._options.onProgress(id, name, size, size);
+
+ if (xhr.status == 200){
+ this.log("xhr - server response received");
+ this.log("responseText = " + xhr.responseText);
+
+ var response;
+
+ try {
+ response = eval("(" + xhr.responseText + ")");
+ } catch(err){
+ response = {};
+ }
+
+ this._options.onComplete(id, name, response);
+
+ } else {
+ this._options.onComplete(id, name, {});
+ }
+
+ this._files[id] = null;
+ this._xhrs[id] = null;
+ this._dequeue(id);
+ },
+ _cancel: function(id){
+ this._options.onCancel(id, this.getName(id));
+
+ this._files[id] = null;
+
+ if (this._xhrs[id]){
+ this._xhrs[id].abort();
+ this._xhrs[id] = null;
+ }
+ }
+}); \ No newline at end of file
diff --git a/lib/scripts/fileuploaderextended.js b/lib/scripts/fileuploaderextended.js
new file mode 100644
index 000000000..ed631a9ea
--- /dev/null
+++ b/lib/scripts/fileuploaderextended.js
@@ -0,0 +1,339 @@
+qq.extend(qq.FileUploader.prototype, {
+ _createUploadHandler: function(){
+ var self = this,
+ handlerClass;
+
+ if(qq.UploadHandlerXhr.isSupported()){
+ handlerClass = 'UploadHandlerXhr';
+ //handlerClass = 'UploadHandlerForm';
+ } else {
+ handlerClass = 'UploadHandlerForm';
+ }
+
+ var handler = new qq[handlerClass]({
+ debug: this._options.debug,
+ action: this._options.action,
+ maxConnections: this._options.maxConnections,
+ onProgress: function(id, fileName, loaded, total){
+ self._onProgress(id, fileName, loaded, total);
+ self._options.onProgress(id, fileName, loaded, total);
+ },
+ onComplete: function(id, fileName, result){
+ self._onComplete(id, fileName, result);
+ self._options.onComplete(id, fileName, result);
+ },
+ onCancel: function(id, fileName){
+ self._onCancel(id, fileName);
+ self._options.onCancel(id, fileName);
+ },
+ onUpload: function(){
+ self._onUpload();
+ }
+ });
+
+ return handler;
+ },
+
+ _onUpload: function(){
+ this._handler.uploadAll(this._options.params);
+ },
+
+ _uploadFile: function(fileContainer){
+ var id = this._handler.add(fileContainer);
+ var fileName = this._handler.getName(id);
+
+ if (this._options.onSubmit(id, fileName) !== false){
+ this._onSubmit(id, fileName);
+ }
+ },
+
+ _addToList: function(id, fileName){
+ var item = qq.toElement(this._options.fileTemplate);
+ item.qqFileId = id;
+
+ var fileElement = this._find(item, 'file');
+ qq.setText(fileElement, fileName);
+ this._find(item, 'size').style.display = 'none';
+
+ var nameElement = this._find(item, 'nameInput');
+ fileName = fileName.toLowerCase();
+ fileName = fileName.replace(/([^a-z0-9_\.\-]+)/g, '_');
+ nameElement.value = fileName;
+ nameElement.id = 'mediamanager__upload_item'+id;
+
+ this._listElement.appendChild(item);
+ }
+
+});
+
+qq.FileUploaderExtended = function(o){
+ // call parent constructor
+ qq.FileUploaderBasic.apply(this, arguments);
+
+ qq.extend(this._options, {
+ element: null,
+ // if set, will be used instead of qq-upload-list in template
+ listElement: null,
+
+ template: '<div class="qq-uploader">' +
+ '<div class="qq-upload-drop-area"><span>' + LANG.media_drop + '</span></div>' +
+ '<div class="qq-upload-button">' + LANG.media_select + '</div>' +
+ '<div class="qq-upload-list"></div>' +
+ '<div><input class="button" type="submit" value="' + LANG.media_upload_btn + '" id="mediamanager__upload_button">' +
+ '<label class="check"><input class="dw__ow" type="checkbox" value="1" name="ow"><span>' + LANG.media_overwrt + '</span></label>' +
+ '</div>' +
+ '</div>',
+
+ // template for one item in file list
+ fileTemplate: '<div class="li">' +
+ '<span class="qq-upload-file qq-upload-file-hidden"></span>' +
+ '<input class="qq-upload-name-input edit" type="text">' +
+ '<span class="qq-upload-spinner-hidden"></span>' +
+ '<span class="qq-upload-size"></span>' +
+ '<a class="qq-upload-cancel" href="#">' + LANG.media_cancel + '</a>' +
+ '<span class="qq-upload-failed-text">Failed</span>' +
+ '</div>',
+
+ classes: {
+ // used to get elements from templates
+ button: 'qq-upload-button',
+ drop: 'qq-upload-drop-area',
+ dropActive: 'qq-upload-drop-area-active',
+ list: 'qq-upload-list',
+ nameInput: 'qq-upload-name-input',
+ file: 'qq-upload-file',
+
+ spinner: 'qq-upload-spinner',
+ size: 'qq-upload-size',
+ cancel: 'qq-upload-cancel',
+
+ // added to list item when upload completes
+ // used in css to hide progress spinner
+ success: 'qq-upload-success',
+ fail: 'qq-upload-fail',
+ failedText : 'qq-upload-failed-text'
+ }
+ });
+
+ qq.extend(this._options, o);
+
+ this._element = this._options.element;
+ this._element.innerHTML = this._options.template;
+ this._listElement = this._options.listElement || this._find(this._element, 'list');
+
+ this._classes = this._options.classes;
+
+ this._button = this._createUploadButton(this._find(this._element, 'button'));
+
+ this._bindCancelEvent();
+ this._bindUploadEvent();
+ this._setupDragDrop();
+};
+
+qq.extend(qq.FileUploaderExtended.prototype, qq.FileUploader.prototype);
+
+qq.extend(qq.FileUploaderExtended.prototype, {
+ _bindUploadEvent: function(){
+ var self = this,
+ list = this._listElement;
+
+ qq.attach(document.getElementById('mediamanager__upload_button'), 'click', function(e){
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+ qq.preventDefault(e);
+ self._handler._options.onUpload();
+
+ jQuery(".qq-upload-name-input").each(function (i) {
+ jQuery(this).attr('disabled', 'disabled');
+ });
+ });
+ },
+
+ _onComplete: function(id, fileName, result){
+ this._filesInProgress--;
+
+ // mark completed
+ var item = this._getItemByFileId(id);
+ qq.remove(this._find(item, 'cancel'));
+ qq.remove(this._find(item, 'spinner'));
+
+ var nameInput = this._find(item, 'nameInput');
+ var fileElement = this._find(item, 'file');
+ qq.setText(fileElement, nameInput.value);
+ qq.removeClass(fileElement, 'qq-upload-file-hidden');
+ qq.remove(nameInput);
+ jQuery('.qq-upload-button, #mediamanager__upload_button').remove();
+ jQuery('.dw__ow').parent().hide();
+ jQuery('.qq-upload-drop-area').remove();
+
+ if (result.success){
+ qq.addClass(item, this._classes.success);
+ $link = '<a href="' + result.link + '" name="h_:' + result.id + '" class="select">' + nameInput.value + '</a>';
+ jQuery(fileElement).html($link);
+
+ } else {
+ qq.addClass(item, this._classes.fail);
+ var fail = this._find(item, 'failedText');
+ if (result.error) qq.setText(fail, result.error);
+ }
+
+ if (document.getElementById('media__content') && !document.getElementById('mediamanager__done_form')) {
+ var action = document.location.href;
+ var i = action.indexOf('?');
+ if (i) action = action.substr(0, i);
+ var button = '<form method="post" action="' + action + '" id="mediamanager__done_form"><div>';
+ button += '<input type="hidden" value="' + result.ns + '" name="ns">';
+ button += '<input class="button" type="submit" value="' + LANG.media_done_btn + '"></div></form>';
+ jQuery('#mediamanager__uploader').append(button);
+ }
+ }
+
+});
+
+qq.extend(qq.UploadHandlerForm.prototype, {
+ uploadAll: function(params){
+ this._uploadAll(params);
+ },
+
+ getName: function(id){
+ var file = this._inputs[id];
+ var name = document.getElementById('mediamanager__upload_item'+id);
+ if (name != null) {
+ return name.value;
+ } else {
+ if (file != null) {
+ // get input value and remove path to normalize
+ return file.value.replace(/.*(\/|\\)/, "");
+ } else {
+ return null;
+ }
+ }
+ },
+
+ _uploadAll: function(params){
+ jQuery(".qq-upload-spinner-hidden").each(function (i) {
+ jQuery(this).addClass('qq-upload-spinner');
+ });
+ for (key in this._inputs) {
+ this.upload(key, params);
+ }
+
+ },
+
+ _upload: function(id, params){
+ var input = this._inputs[id];
+
+ if (!input){
+ throw new Error('file with passed id was not added, or already uploaded or cancelled');
+ }
+
+ var fileName = this.getName(id);
+
+ var iframe = this._createIframe(id);
+ var form = this._createForm(iframe, params);
+ form.appendChild(input);
+
+ var nameInput = qq.toElement('<input name="mediaid" value="' + fileName + '" type="text">');
+ form.appendChild(nameInput);
+
+ var checked = jQuery('.dw__ow').attr('checked');
+ var owCheckbox = jQuery('.dw__ow').clone();
+ owCheckbox.attr('checked', checked);
+ jQuery(form).append(owCheckbox);
+
+ var self = this;
+ this._attachLoadEvent(iframe, function(){
+ self.log('iframe loaded');
+
+ var response = self._getIframeContentJSON(iframe);
+
+ self._options.onComplete(id, fileName, response);
+ self._dequeue(id);
+
+ delete self._inputs[id];
+ // timeout added to fix busy state in FF3.6
+ setTimeout(function(){
+ qq.remove(iframe);
+ }, 1);
+ });
+
+ form.submit();
+ qq.remove(form);
+
+ return id;
+ }
+});
+
+qq.extend(qq.UploadHandlerXhr.prototype, {
+ uploadAll: function(params){
+ this._uploadAll(params);
+ },
+
+ getName: function(id){
+ var file = this._files[id];
+ var name = document.getElementById('mediamanager__upload_item'+id);
+ if (name != null) {
+ return name.value;
+ } else {
+ if (file != null) {
+ // fix missing name in Safari 4
+ return file.fileName != null ? file.fileName : file.name;
+ } else {
+ return null;
+ }
+ }
+ },
+
+ getSize: function(id){
+ var file = this._files[id];
+ if (file == null) return null;
+ return file.fileSize != null ? file.fileSize : file.size;
+ },
+
+ _upload: function(id, params){
+ var file = this._files[id],
+ name = this.getName(id),
+ size = this.getSize(id);
+ if (name == null || size == null) return;
+
+ this._loaded[id] = 0;
+
+ var xhr = this._xhrs[id] = new XMLHttpRequest();
+ var self = this;
+
+ xhr.upload.onprogress = function(e){
+ if (e.lengthComputable){
+ self._loaded[id] = e.loaded;
+ self._options.onProgress(id, name, e.loaded, e.total);
+ }
+ };
+
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState == 4){
+ self._onComplete(id, xhr);
+ }
+ };
+
+ // build query string
+ params = params || {};
+ params['qqfile'] = name;
+ params['ow'] = jQuery('.dw__ow').attr('checked');
+ var queryString = qq.obj2url(params, this._options.action);
+
+ xhr.open("POST", queryString, true);
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
+ xhr.setRequestHeader("Content-Type", "application/octet-stream");
+ xhr.send(file);
+ },
+
+ _uploadAll: function(params){
+ jQuery(".qq-upload-spinner-hidden").each(function (i) {
+ jQuery(this).addClass('qq-upload-spinner');
+ });
+ for (key in this._files) {
+ this.upload(key, params);
+ }
+
+ }
+});
diff --git a/lib/scripts/helpers.js b/lib/scripts/helpers.js
index b0f76cdb0..d6f36967d 100644
--- a/lib/scripts/helpers.js
+++ b/lib/scripts/helpers.js
@@ -2,156 +2,6 @@
* Various helper functions
*/
-
-/**
- * Simple function to check if a global var is defined
- *
- * @author Kae Verens
- * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835
- */
-function isset(varname){
- return(typeof(window[varname])!='undefined');
-}
-
-/**
- * Checks if property is undefined
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isUndefined (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'undefined');
-}
-
-/**
- * Checks if property is function
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isFunction (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'function');
-}
-/**
- * Checks if property is string
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isString (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'string');
-}
-
-/**
- * Checks if property is number
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isNumber (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'number');
-}
-
-/**
- * Checks if property is the calculable number
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isNumeric (prop /* :Object */) /* :Boolean */ {
- return isNumber(prop)&&!isNaN(prop)&&isFinite(prop);
-}
-
-/**
- * Checks if property is array
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isArray (prop /* :Object */) /* :Boolean */ {
- return (prop instanceof Array);
-}
-
-/**
- * Checks if property is regexp
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isRegExp (prop /* :Object */) /* :Boolean */ {
- return (prop instanceof RegExp);
-}
-
-/**
- * Checks if property is a boolean value
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isBoolean (prop /* :Object */) /* :Boolean */ {
- return ('boolean' == typeof prop);
-}
-
-/**
- * Checks if property is a scalar value (value that could be used as the hash key)
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isScalar (prop /* :Object */) /* :Boolean */ {
- return isNumeric(prop)||isString(prop);
-}
-
-/**
- * Checks if property is empty
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-function isEmpty (prop /* :Object */) /* :Boolean */ {
- if (isBoolean(prop)) return false;
- if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) return true;
- if (isString(prop) || isNumber(prop)) return !prop;
- if (Boolean(prop)&&false != prop) {
- for (var i in prop) if(prop.hasOwnProperty(i)) return false;
- }
- return true;
-}
-
-/**
- * Checks if property is derived from prototype, applies method if it is not exists
- *
- * @param string property name
- * @return bool true if prototyped
- * @access public
- * @author Ilya Lebedev <ilya@lebedev.net>
- */
-if ('undefined' == typeof Object.hasOwnProperty) {
- Object.prototype.hasOwnProperty = function (prop) {
- return !('undefined' == typeof this[prop] || this.constructor && this.constructor.prototype[prop] && this[prop] === this.constructor.prototype[prop]);
- };
-}
-
/**
* Very simplistic Flash plugin check, probably works for Flash 8 and higher only
*
@@ -163,13 +13,12 @@ function hasFlash(version){
if(navigator.plugins != null && navigator.plugins.length > 0){
ver = navigator.plugins["Shockwave Flash"].description.split(' ')[2].split('.')[0];
}else{
- var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
- ver = axo.GetVariable("$version").split(' ')[1].split(',')[0];
+ ver = (new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))
+ .GetVariable("$version").split(' ')[1].split(',')[0];
}
}catch(e){ }
- if(ver >= version) return true;
- return false;
+ return ver >= version;
}
/**
@@ -204,11 +53,11 @@ function substr_replace(str, replace, start, length) {
* @returns functionref
*/
function bind(fnc/*, ... */) {
- var Aps = Array.prototype.slice;
+ var Aps = Array.prototype.slice,
// Store passed arguments in this scope.
// Since arguments is no Array nor has an own slice method,
// we have to apply the slice method from the Array.prototype
- var static_args = Aps.call(arguments, 1);
+ static_args = Aps.call(arguments, 1);
// Return a function evaluating the passed function with the
// given args and optional arguments passed on invocation.
@@ -219,18 +68,3 @@ function bind(fnc/*, ... */) {
static_args.concat(Aps.call(arguments, 0)));
};
}
-
-/**
- * Get the computed style of a node.
- *
- * @link https://acidmartin.wordpress.com/2008/08/26/style-get-any-css-property-value-of-an-object/
- * @link http://svn.dojotoolkit.org/src/dojo/trunk/_base/html.js
- */
-function gcs(node){
- if(node.currentStyle){
- return node.currentStyle;
- }else{
- return node.ownerDocument.defaultView.getComputedStyle(node, null);
- }
-}
-
diff --git a/lib/scripts/jquery/jquery.cookie.js b/lib/scripts/jquery/jquery.cookie.js
index 6df1faca2..6a3e394b4 100644
--- a/lib/scripts/jquery/jquery.cookie.js
+++ b/lib/scripts/jquery/jquery.cookie.js
@@ -1,96 +1,41 @@
/**
- * Cookie plugin
+ * jQuery Cookie plugin
*
- * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
+ * Copyright (c) 2010 Klaus Hartl (stilbuero.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
+jQuery.cookie = function (key, value, options) {
-/**
- * Create a cookie with the given name and value and other optional parameters.
- *
- * @example $.cookie('the_cookie', 'the_value');
- * @desc Set the value of a cookie.
- * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
- * @desc Create a cookie with all available options.
- * @example $.cookie('the_cookie', 'the_value');
- * @desc Create a session cookie.
- * @example $.cookie('the_cookie', null);
- * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
- * used when the cookie was set.
- *
- * @param String name The name of the cookie.
- * @param String value The value of the cookie.
- * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
- * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
- * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
- * If set to null or omitted, the cookie will be a session cookie and will not be retained
- * when the the browser exits.
- * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
- * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
- * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
- * require a secure protocol (like HTTPS).
- * @type undefined
- *
- * @name $.cookie
- * @cat Plugins/Cookie
- * @author Klaus Hartl/klaus.hartl@stilbuero.de
- */
+ // key and at least value given, set cookie...
+ if (arguments.length > 1 && String(value) !== "[object Object]") {
+ options = jQuery.extend({}, options);
-/**
- * Get the value of a cookie with the given name.
- *
- * @example $.cookie('the_cookie');
- * @desc Get the value of a cookie.
- *
- * @param String name The name of the cookie.
- * @return The value of the cookie.
- * @type String
- *
- * @name $.cookie
- * @cat Plugins/Cookie
- * @author Klaus Hartl/klaus.hartl@stilbuero.de
- */
-jQuery.cookie = function(name, value, options) {
- if (typeof value != 'undefined') { // name and value given, set cookie
- options = options || {};
- if (value === null) {
- value = '';
+ if (value === null || value === undefined) {
options.expires = -1;
}
- var expires = '';
- if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
- var date;
- if (typeof options.expires == 'number') {
- date = new Date();
- date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
- } else {
- date = options.expires;
- }
- expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
- }
- // CAUTION: Needed to parenthesize options.path and options.domain
- // in the following expressions, otherwise they evaluate to undefined
- // in the packed version for some reason...
- var path = options.path ? '; path=' + (options.path) : '';
- var domain = options.domain ? '; domain=' + (options.domain) : '';
- var secure = options.secure ? '; secure' : '';
- document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
- } else { // only name given, get cookie
- var cookieValue = null;
- if (document.cookie && document.cookie != '') {
- var cookies = document.cookie.split(';');
- for (var i = 0; i < cookies.length; i++) {
- var cookie = jQuery.trim(cookies[i]);
- // Does this cookie string begin with the name we want?
- if (cookie.substring(0, name.length + 1) == (name + '=')) {
- cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
- break;
- }
- }
+
+ if (typeof options.expires === 'number') {
+ var days = options.expires, t = options.expires = new Date();
+ t.setDate(t.getDate() + days);
}
- return cookieValue;
+
+ value = String(value);
+
+ return (document.cookie = [
+ encodeURIComponent(key), '=',
+ options.raw ? value : encodeURIComponent(value),
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
+ options.path ? '; path=' + options.path : '',
+ options.domain ? '; domain=' + options.domain : '',
+ options.secure ? '; secure' : ''
+ ].join(''));
}
-}; \ No newline at end of file
+
+ // key and possibly options given, get cookie...
+ options = value || {};
+ var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
+ return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
+};
diff --git a/lib/scripts/locktimer.js b/lib/scripts/locktimer.js
index b83840557..857002abf 100644
--- a/lib/scripts/locktimer.js
+++ b/lib/scripts/locktimer.js
@@ -17,8 +17,9 @@ var dw_locktimer = {
*/
init: function(timeout,draft){ //FIXME which elements to pass here?
var $edit = jQuery('#wiki__text');
- if(!$edit.length) return;
- if($edit.attr('readonly')) return;
+ if($edit.length === 0 || $edit.attr('readonly')) {
+ return;
+ }
// init values
dw_locktimer.timeout = timeout*1000;
@@ -26,10 +27,12 @@ var dw_locktimer = {
dw_locktimer.lasttime = new Date();
dw_locktimer.pageid = jQuery('#dw__editform input[name=id]').val();
- if(!dw_locktimer.pageid) return;
+ if(!dw_locktimer.pageid) {
+ return;
+ }
// register refresh event
- jQuery('#wiki__text').keypress(dw_locktimer.refresh);
+ $edit.keypress(dw_locktimer.refresh);
// start timer
dw_locktimer.reset();
},
@@ -66,30 +69,29 @@ var dw_locktimer = {
* Called on keypresses in the edit area
*/
refresh: function(){
- var now = new Date();
- var params = {};
- // refresh every minute only
- if(now.getTime() - dw_locktimer.lasttime.getTime() > 30*1000){
- params['call'] = 'lock';
- params['id'] = dw_locktimer.pageid;
+ var now = new Date(),
+ params = 'call=lock&id=' + dw_locktimer.pageid + '&';
- if(dw_locktimer.draft && jQuery('#dw__editform textarea[name=wikitext]').length > 0){
- params['prefix'] = jQuery('#dw__editform input[name=prefix]').val();
- params['wikitext'] = jQuery('#dw__editform textarea[name=wikitext]').val();
- params['suffix'] = jQuery('#dw__editform input[name=suffix]').val();
- if(jQuery('#dw__editform input[name=date]').length > 0) {
- params['date'] = jQuery('#dw__editform input[name=id]').val();
- }
- }
+ // refresh every minute only
+ if(now.getTime() - dw_locktimer.lasttime.getTime() <= 30*1000) {
+ return;
+ }
- jQuery.post(
- DOKU_BASE + 'lib/exe/ajax.php',
- params,
- dw_locktimer.refreshed,
- 'html'
- );
- dw_locktimer.lasttime = now;
+ // POST everything necessary for draft saving
+ if(dw_locktimer.draft && jQuery('#dw__editform textarea[name=wikitext]').length > 0){
+ params += jQuery('#dw__editform').find('input[name=prefix], ' +
+ 'textarea[name=wikitext], ' +
+ 'input[name=suffix], ' +
+ 'input[name=date]').serialize();
}
+
+ jQuery.post(
+ DOKU_BASE + 'lib/exe/ajax.php',
+ params,
+ dw_locktimer.refreshed,
+ 'html'
+ );
+ dw_locktimer.lasttime = now;
},
/**
@@ -100,7 +102,9 @@ var dw_locktimer = {
data = data.substring(1);
jQuery('#draft__status').html(data);
- if(error != '1') return; // locking failed
+ if(error != '1') {
+ return; // locking failed
+ }
dw_locktimer.reset();
}
};
diff --git a/lib/scripts/media.js b/lib/scripts/media.js
index 1402ad4bf..f4064efd5 100644
--- a/lib/scripts/media.js
+++ b/lib/scripts/media.js
@@ -22,6 +22,12 @@ var dw_mediamanager = {
size: false,
forbidden_opts: {},
+ // File list view type
+ view: false,
+
+ layout_width: 0,
+ layout_height: 0,
+
init: function () {
var $content, $tree;
$content = jQuery('#media__content');
@@ -38,7 +44,8 @@ var dw_mediamanager = {
.delegate('a.select', 'click', dw_mediamanager.select)
// Attach deletion confirmation dialog to the delete buttons
.delegate('#media__content a.btn_media_delete', 'click',
- dw_mediamanager.confirmattach);
+ dw_mediamanager.confirmattach)
+ .delegate('#mediamanager__done_form', 'submit', dw_mediamanager.list);
$tree.dw_tree({toggle_selector: 'img',
load_data: function (show_sublist, $clicky) {
@@ -59,6 +66,43 @@ var dw_mediamanager = {
(opening ? 'minus' : 'plus') + '.gif');
}});
$tree.delegate('a', 'click', dw_mediamanager.list);
+
+ dw_mediamanager.set_filelist_view(dw_mediamanager.view, false);
+ jQuery('#mediamanager__form_sort').find('input[type=submit]').hide();
+ dw_mediamanager.image_diff();
+ dw_mediamanager.init_ajax_uploader();
+
+ // changing opened tab in the file list panel
+ jQuery('#mediamanager__layout_list').delegate('#mediamanager__tabs_files a', 'click', dw_mediamanager.list)
+ // changing type of the file list view
+ .delegate('#mediamanager__tabs_list a', 'click', dw_mediamanager.list_view)
+ // loading file details
+ .delegate('#mediamanager__file_list a', 'click', dw_mediamanager.details)
+ // search form
+ .delegate('#dw__mediasearch', 'submit', dw_mediamanager.list)
+ // "upload as" field autofill
+ .delegate('#upload__file', 'change', dw_mediamanager.suggest)
+ // sort type selection
+ .delegate('#mediamanager__form_sort select', 'change', dw_mediamanager.list)
+ // uploaded images
+ .delegate('.qq-upload-file a', 'click', dw_mediamanager.details);
+
+ // changing opened tab in the file details panel
+ jQuery('#mediamanager__layout_detail').delegate('#mediamanager__tabs_details a', 'click', dw_mediamanager.details)
+ // "update new version" button
+ .delegate('#mediamanager__btn_update', 'submit', dw_mediamanager.list)
+ // revisions form
+ .delegate('#page__revisions', 'submit', dw_mediamanager.details)
+ .delegate('#page__revisions a', 'click', dw_mediamanager.details)
+ // meta edit form
+ .delegate('#mediamanager__save_meta', 'submit', dw_mediamanager.details)
+ // delete button
+ .delegate('#mediamanager__btn_delete', 'submit', dw_mediamanager.details)
+ // "restore this version" button
+ .delegate('#mediamanager__btn_restore', 'submit', dw_mediamanager.details)
+ // less/more recent buttons in media revisions form
+ .delegate('.btn_newer, .btn_older', 'submit', dw_mediamanager.details);
+
},
/**
@@ -201,6 +245,9 @@ var dw_mediamanager = {
$file = jQuery(this);
$name = jQuery('#upload__name');
+
+ if ($name.val() != '') return;
+
if(!$file.length || !$name.length) {
return;
}
@@ -218,33 +265,431 @@ var dw_mediamanager = {
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
list: function (event) {
- var $link, $content;
+ var $link, $content, params;
+ $link = jQuery(this);
event.preventDefault();
jQuery('div.success, div.info, div.error, div.notify').remove();
- $link = jQuery(this);
- $content = jQuery('#media__content');
- $content.html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />');
+ if (document.getElementById('media__content')) {
+ //popup
+ $content = jQuery('#media__content');
+ } else {
+ //fullscreen media manager
+ $content = jQuery('#mediamanager__layout_list');
+
+ if ($link.hasClass('idx_dir')) {
+ //changing namespace
+ jQuery('#mediamanager__layout_detail').empty();
+ jQuery('#media__tree .selected').each(function(){
+ jQuery(this).removeClass('selected');
+ });
+ $link.addClass('selected');
+ }
+ }
+
+ params = '';
+
+ if ($link[0].search) {
+ params = $link[0].search.substr(1)+'&call=medialist';
+ } else if ($link[0].action) {
+ params = dw_mediamanager.form_params($link)+'&call=medialist';
+ } else if ($link.parents('form')) {
+ params = dw_mediamanager.form_params($link.parents('form'))+'&call=medialist';
+
+ if ($link.parents('form')[0].id == 'mediamanager__form_sort') {
+ DokuCookie.setValue('sort', $link[0].value);
+ params += '&q=' + jQuery('#mediamanager__sort_textfield').val();
+ params += '&mediado=searchlist';
+ }
+ }
// fetch the subtree
+ dw_mediamanager.update_content($content, params);
+
+ if (document.getElementById('media__content')) {
+ //popup
+ $content = jQuery('#media__content');
+ $content.html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />');
+ } else {
+ //fullscreen media manager
+ jQuery('.scroll-container', $content).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />');
+ }
+ },
+
+ /**
+ * Returns form parameters
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ form_params: function ($form) {
+ if (!$form.length) return;
+ var elements = $form.serialize();
+ var action = '';
+ var i = $form[0].action.indexOf('?');
+ if (i >= 0) action = $form[0].action.substr(i+1);
+ return elements+'&'+action;
+ },
+
+ /**
+ * Changes view of media files list
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ list_view: function (event) {
+ var $link, $content;
+ $link = jQuery(this);
+
+ event.preventDefault();
+
+ $content = jQuery('#mediamanager__file_list');
+
+ if ($link[0].id == 'mediamanager__link_thumbs') {
+ dw_mediamanager.set_filelist_view('thumbs', true);
+
+ } else if ($link[0].id == 'mediamanager__link_list') {
+ dw_mediamanager.set_filelist_view('list', true);
+ }
+ },
+
+ set_filelist_view: function (type, cookies) {
+ var $content = jQuery('#mediamanager__file_list');
+ if (!type) type = DokuCookie.getValue('view');
+
+ if (type == 'thumbs') {
+ $content.removeClass('mediamanager-list');
+ $content.addClass('mediamanager-thumbs');
+ if (cookies) DokuCookie.setValue('view', 'thumbs');
+ dw_mediamanager.view = 'thumbs';
+
+ } else if (type == 'list') {
+ $content.removeClass('mediamanager-thumbs');
+ $content.addClass('mediamanager-list');
+ if (cookies) DokuCookie.setValue('view', 'list');
+ dw_mediamanager.view = 'list';
+ }
+ },
+
+ /**
+ * Lists the content of the right column (image details) using AJAX
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ details: function (event) {
+ var $link, $content, params, update_list;
+ $link = jQuery(this);
+
+ event.preventDefault();
+
+ jQuery('div.success, div.info, div.error, div.notify').remove();
+
+ if ($link[0].id == 'mediamanager__btn_delete' && !confirm(LANG['del_confirm'])) return false;
+ if ($link[0].id == 'mediamanager__btn_restore' && !confirm(LANG['restore_confirm'])) return false;
+
+ $content = jQuery('#mediamanager__layout_detail');
+ params = '';
+
+ if ($link[0].search) {
+ params = $link[0].search.substr(1)+'&call=mediadetails';
+ } else if ($link[0].action) {
+ params = dw_mediamanager.form_params($link)+'&call=mediadetails';
+ } else if ($link.parents('form')) {
+ params = dw_mediamanager.form_params($link.parents('form'))+'&call=mediadetails';
+ }
+
+ update_list = ($link[0].id == 'mediamanager__btn_delete' || $link[0].id == 'mediamanager__btn_restore');
+ dw_mediamanager.update_content($content, params, update_list);
+
+ if (jQuery('.scroll-container', $content).length) {
+ jQuery('.scroll-container', $content).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />');
+ } else {
+ jQuery($content).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />');
+ }
+ },
+
+ update_content: function ($content, params, update_list) {
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
- $link[0].search.substr(1)+'&call=medialist',
+ params,
function (data) {
+ jQuery('.ui-resizable').each(function(){
+ jQuery(this).resizable('destroy');
+ });
+
$content.html(data);
+
dw_mediamanager.prepare_content($content);
dw_mediamanager.updatehide();
+
+ dw_mediamanager.update_resizable();
+ dw_behaviour.revisionBoxHandler();
+ jQuery('#mediamanager__form_sort').find('input[type=submit]').hide();
+ dw_mediamanager.set_filelist_view(dw_mediamanager.view, false);
+ dw_mediamanager.image_diff();
+ dw_mediamanager.init_ajax_uploader();
+
+ if (update_list) {
+ var $link1, $content1, params1;
+ $link1 = jQuery('a.files');
+ params1 = $link1[0].search.substr(1)+'&call=medialist';
+ $content1 = jQuery('#mediamanager__layout_list');
+ dw_mediamanager.update_content($content1, params1);
+ jQuery('.scroll-container', $content1).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />');
+ }
},
'html'
);
},
+ window_resize: function () {
+ if (jQuery('#mediamanager__layout').width() == dw_mediamanager.layout_width) {
+ return;
+ }
+
+ dw_mediamanager.layout_width = jQuery('#mediamanager__layout').width();
+ $r = jQuery("#mediamanager__layout .layout-resizable, #mediamanager__layout .layout");
+
+ var w = 0, wSum = 0, mCount = 0, mArray = [];
+ $r.each(function() {
+ w = jQuery(this).width();
+ if (w == parseFloat(jQuery(this).css("min-width"))) {
+ wSum += w;
+ } else {
+ mArray[mCount] = jQuery(this);
+ mCount++;
+ }
+ });
+
+ if (mCount > 0) {
+ var width = (0.95 * jQuery('#mediamanager__layout').width() - wSum - 30);
+ wSum = 0;
+ for(var i = 0; i < mArray.length; i++) {
+ wSum += mArray[i].width();
+ }
+ for(var i = 0; i < mArray.length; i++) {
+ w = mArray[i].width();
+ w = 100*w / wSum;
+ mArray[i].width(width*w/100);
+ }
+ }
+
+ $r.each(function() {
+ w = jQuery(this).width();
+ w = (100 * w / jQuery('#mediamanager__layout').width());
+ w += "%";
+ jQuery(this).width(w);
+ });
+
+ var windowHeight = jQuery(window).height();
+ var height = windowHeight - 300;
+ if (layout_height < height) {
+ layout_height = height;
+ jQuery('#mediamanager__layout .scroll-container').each(function (i) {
+ jQuery(this).height(height);
+ });
+ $resizable.each(function() {
+ jQuery(this).height(height+100);
+ });
+ }
+
+ dw_mediamanager.opacity_slider();
+ dw_mediamanager.portions_slider();
+ },
+
+ /**
+ * Updates mediamanager layout
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ update_resizable: function () {
+ $resizable = jQuery("#mediamanager__layout .layout-resizable");
+
+ $resizable.resizable({ handles: 'e' ,
+ resize: function(event, ui){
+ var w = 0;
+ $resizable.each(function() {
+ w += jQuery(this).width();
+ });
+ wSum = w + parseFloat(jQuery('#mediamanager__layout_detail').css("min-width"));
+
+ // max width of resizable column
+ var maxWidth = 0.95 * jQuery('#mediamanager__layout').width() - wSum + jQuery(this).width() - 30;
+ $resizable.resizable( "option", "maxWidth", maxWidth );
+
+ // percentage width of the first two columns
+ var wLeft = ( 100*(w+30) / jQuery('#mediamanager__layout').width() );
+
+ // width of the third column
+ var wRight = 95-wLeft;
+ wRight += "%";
+ jQuery('#mediamanager__layout_detail').width(wRight);
+
+ $resizable.each(function() {
+ w = jQuery(this).width();
+ w = (100 * w / jQuery('#mediamanager__layout').width());
+ w += "%";
+ jQuery(this).width(w);
+ });
+
+ dw_mediamanager.opacity_slider();
+ dw_mediamanager.portions_slider();
+ }
+ });
+
+ var windowHeight = jQuery(window).height();
+ var height = windowHeight - 300;
+ layout_height = height;
+ jQuery('#mediamanager__layout .scroll-container').each(function (i) {
+ jQuery(this).height(height);
+ });
+ $resizable.each(function() {
+ jQuery(this).height(height+100);
+ });
+ },
+
+ /**
+ * Prints 'select' for image difference representation type
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ image_diff: function () {
+ if (jQuery('#mediamanager__difftype').length) return;
+
+ $form = jQuery('#mediamanager__form_diffview');
+ if (!$form.length) return;
+
+ $label = jQuery(document.createElement('label'));
+ $label.append('<span>'+LANG.media_diff+'</span>');
+ $select = jQuery(document.createElement('select'))
+ .attr('id', 'mediamanager__difftype')
+ .attr('name', 'difftype')
+ .change(dw_mediamanager.change_diff_type);
+ $select.append(new Option(LANG.media_diff_both, "both"));
+ $select.append(new Option(LANG.media_diff_opacity, "opacity"));
+ $select.append(new Option(LANG.media_diff_portions, "portions"));
+ $label.append($select);
+ $form.append($label);
+
+ // for IE
+ var select = document.getElementById('mediamanager__difftype');
+ select.options[0].text = LANG.media_diff_both;
+ select.options[1].text = LANG.media_diff_opacity;
+ select.options[2].text = LANG.media_diff_portions;
+ },
+
+ /**
+ * Handles selection of image difference representation type
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ change_diff_type: function () {
+ $select = jQuery('#mediamanager__difftype');
+ $content = jQuery('#mediamanager__diff');
+
+ params = dw_mediamanager.form_params($select.parents('form'))+'&call=mediadiff';
+ jQuery.post(
+ DOKU_BASE + 'lib/exe/ajax.php',
+ params,
+ function (data) {
+ $content.html(data);
+ dw_mediamanager.portions_slider();
+ dw_mediamanager.opacity_slider();
+ },
+ 'html'
+ );
+ },
+
+ /**
+ * Sets options for opacity diff slider
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ opacity_slider: function () {
+ var $slider = jQuery( "#mediamanager__opacity_slider" );
+ if (!$slider.length) return;
+
+ var $image = jQuery('#mediamanager__diff_opacity_image1 img');
+ if (!$image.length) return;
+ $slider.width($image.width()-20);
+
+ $slider.slider();
+ $slider.slider("option", "min", 0);
+ $slider.slider("option", "max", 0.999);
+ $slider.slider("option", "step", 0.001);
+ $slider.slider("option", "value", 0.5);
+ $slider.bind("slide", function(event, ui) {
+ jQuery('#mediamanager__diff_opacity_image2').css({ opacity: $slider.slider("option", "value")});
+ });
+ },
+
+ /**
+ * Sets options for red line diff slider
+ *
+ * @author Kate Arzamastseva <pshns@ukr.net>
+ */
+ portions_slider: function () {
+ var $image1 = jQuery('#mediamanager__diff_portions_image1 img');
+ var $image2 = jQuery('#mediamanager__diff_portions_image2 img');
+ if (!$image1.length || !$image2.length) return;
+
+ var $div = jQuery("#mediamanager__diff_layout");
+ if (!$div.length) return;
+
+ $div.width('100%');
+ $image2.parent().width('97%');
+ $image1.width('100%');
+ $image2.width('100%');
+
+ if ($image1.width() < $div.width()) {
+ $div.width($image1.width());
+ }
+
+ $image2.parent().width('50%');
+ $image2.width($image1.width());
+ $image1.width($image1.width());
+
+ var $slider = jQuery("#mediamanager__portions_slider");
+ if (!$slider.length) return;
+ $slider.width($image1.width()-20);
+
+ $slider.slider();
+ $slider.slider("option", "min", 0);
+ $slider.slider("option", "max", 97);
+ $slider.slider("option", "step", 1);
+ $slider.slider("option", "value", 50);
+ $slider.bind("slide", function(event, ui) {
+ jQuery('#mediamanager__diff_portions_image2').css({ width: $slider.slider("option", "value")+'%'});
+ });
+ },
+
+ params_toarray: function (str) {
+ var vars = [], hash;
+ var hashes = str.split('&');
+ for(var i = 0; i < hashes.length; i++) {
+ hash = hashes[i].split('=');
+ vars[hash[0]] = hash[1];
+ }
+ return vars;
+ },
+
+ init_ajax_uploader: function () {
+ if (!jQuery('#mediamanager__uploader').length) return;
+ if (jQuery('.qq-upload-list').length) return;
+
+ var params = dw_mediamanager.form_params(jQuery('#dw__upload'))+'&call=mediaupload';
+ params = dw_mediamanager.params_toarray(params);
+
+ var uploader = new qq.FileUploaderExtended({
+ element: document.getElementById('mediamanager__uploader'),
+ action: DOKU_BASE + 'lib/exe/ajax.php',
+ params: params
+ });
+ },
+
prepare_content: function ($content) {
// hide syntax example
$content.find('div.example:visible').hide();
- dw_mediamanager.initFlashUpload();
},
/**
@@ -521,4 +966,10 @@ function hasFlash(version){
return ver >= version;
}
+jQuery(document).ready(function() {
+ dw_mediamanager.update_resizable();
+ dw_mediamanager.layout_width = jQuery("#mediamanager__layout").width();
+ jQuery(window).resize(dw_mediamanager.window_resize);
+});
+
jQuery(dw_mediamanager.init);
diff --git a/lib/scripts/page.js b/lib/scripts/page.js
index 05c5ece20..e4033b76d 100644
--- a/lib/scripts/page.js
+++ b/lib/scripts/page.js
@@ -10,6 +10,7 @@ dw_page = {
init: function(){
dw_page.sectionHighlight();
jQuery('a.fn_top').mouseover(dw_page.footnoteDisplay);
+ dw_page.initTocToggle();
},
/**
@@ -20,8 +21,8 @@ dw_page = {
sectionHighlight: function() {
jQuery('form.btn_secedit')
.mouseover(function(){
- var $tgt = jQuery(this).parent();
- var nr = $tgt.attr('class').match(/(\s+|^)editbutton_(\d+)(\s+|$)/)[2];
+ var $tgt = jQuery(this).parent(),
+ nr = $tgt.attr('class').match(/(\s+|^)editbutton_(\d+)(\s+|$)/)[2];
// Walk the DOM tree up (first previous siblings, then parents)
// until boundary element
@@ -73,26 +74,67 @@ dw_page = {
* @author Andreas Gohr <andi@splitbrain.org>
* @author Chris Smith <chris@jalakai.co.uk>
*/
- footnoteDisplay: function(e){
- var $fndiv = dw_page.insituPopup(this, 'insitu__fn');
+ footnoteDisplay: function () {
+ var content = jQuery(jQuery(this).attr('href')) // Footnote text anchor
+ .closest('div.fn').html();
- // locate the footnote anchor element
- var $a = jQuery("#fn__" + e.target.id.substr(5));
- if (!$a.length){ return; }
-
- // anchor parent is the footnote container, get its innerHTML
- var content = new String ($a.parent().parent().html());
+ if (content === null){
+ return;
+ }
// strip the leading content anchors and their comma separators
- content = content.replace(/<sup>.*<\/sup>/gi, '');
- content = content.replace(/^\s+(,\s+)+/,'');
+ content = content.replace(/((^|\s*,\s*)<sup>.*?<\/sup>)+\s*/gi, '');
// prefix ids on any elements with "insitu__" to ensure they remain unique
content = content.replace(/\bid=(['"])([^"']+)\1/gi,'id="insitu__$2');
// now put the content into the wrapper
- $fndiv.html(content);
- $fndiv.show();
+ dw_page.insituPopup(this, 'insitu__fn').html(content).show();
+ },
+
+ /**
+ * Adds the toggle switch to the TOC
+ */
+ initTocToggle: function() {
+ var $header, $clicky, $toc, $tocul, setClicky;
+ $header = jQuery('#toc__header');
+ if(!$header.length) {
+ return;
+ }
+ $toc = jQuery('#toc__inside');
+ $tocul = $toc.children('ul.toc');
+
+ setClicky = function(hiding){
+ if(hiding){
+ $clicky.html('<span>+</span>');
+ $clicky[0].className = 'toc_open';
+ }else{
+ $clicky.html('<span>&minus;</span>');
+ $clicky[0].className = 'toc_close';
+ }
+ };
+
+ $clicky = jQuery(document.createElement('span'))
+ .attr('id','toc__toggle');
+ $header.css('cursor','pointer')
+ .click(function () {
+ var hidden;
+
+ // Assert that $toc instantly takes the whole TOC space
+ $toc.css('height', $toc.height()).show();
+
+ hidden = $tocul.stop(true, true).is(':hidden');
+
+ setClicky(!hidden);
+
+ // Start animation and assure that $toc is hidden/visible
+ $tocul.dw_toggle(hidden, function () {
+ $toc.toggle(hidden);
+ });
+ })
+ .prepend($clicky);
+
+ setClicky();
}
};
diff --git a/lib/tpl/default/_fileuploader.css b/lib/tpl/default/_fileuploader.css
new file mode 100644
index 000000000..9e60393a0
--- /dev/null
+++ b/lib/tpl/default/_fileuploader.css
@@ -0,0 +1,106 @@
+.qq-uploader {
+ position: relative;
+ width: 100%;
+}
+
+.qq-upload-button {
+ display: inline-block;
+ border: 1px solid __border__;
+ color: __text__;
+ background-color: __background__;
+ text-decoration: none;
+ font-size: 100%;
+ cursor: pointer;
+ margin: 1px;
+ margin-bottom: 5px;
+ padding: 0.125em 0.4em;
+ background: __background__ url(images/buttonshadow.png) repeat-x bottom;
+}
+
+* html .qq-upload-button,
+*+html .qq-upload-button {
+ display: inline;
+}
+
+.qq-upload-button-focus {
+ outline: 1px dotted black;
+}
+
+.qq-upload-drop-area {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ min-height: 70px;
+ z-index: 2;
+ background: __background_neu__;
+ text-align: center;
+}
+
+.qq-upload-drop-area span {
+ display:block;
+ position:absolute;
+ top: 50%;
+ width:100%;
+ margin-top:-8px;
+ font-size: 120%;
+}
+
+.qq-upload-drop-area-active {
+ background: __background_alt__;
+}
+
+.qq-upload-file, .qq-upload-spinner, .qq-upload-size, .qq-upload-cancel, .qq-upload-failed-text {
+ margin-right: 7px;
+}
+
+.qq-upload-spinner {
+ display: inline-block;
+ background: url("../../images/throbber.gif");
+ width: 15px;
+ height: 15px;
+ vertical-align: text-bottom;
+}
+
+.qq-upload-size,.qq-upload-cancel {
+ font-size: 85%;
+}
+
+.qq-upload-failed-text {
+ display:none;
+}
+
+.qq-upload-fail .qq-upload-failed-text {
+ display:inline;
+}
+
+.qq-upload-file {
+ display: block;
+ font-weight: bold;
+}
+
+.qq-upload-file-hidden {
+ display:none;
+}
+
+.qq-upload-cancel {
+ padding-left: 10px;
+}
+
+.qq-uploader .dw__ow {
+ margin-left: 10px;
+ margin-right: 3px;
+}
+
+.qq-uploader .check {
+ line-height: 18px;
+}
+
+.qq-uploader #mediamanager__upload_button {
+ margin-bottom: 10px;
+}
+
+.qq-uploader div.li {
+ margin-bottom: 5px;
+} \ No newline at end of file
diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css
index 1fdf2bfac..a94f814aa 100644
--- a/lib/tpl/default/design.css
+++ b/lib/tpl/default/design.css
@@ -246,6 +246,12 @@ div.dokuwiki div.pagenav-next {
width: 49%
}
+/* ----------- type of recent changes select -------- */
+
+div.dokuwiki form#dw__recent select {
+ margin-bottom: 10px;
+}
+
/* --------------- Links ------------------ */
div.dokuwiki a:link,
diff --git a/lib/tpl/default/detail.php b/lib/tpl/default/detail.php
index 4f42b116e..503c0ef71 100644
--- a/lib/tpl/default/detail.php
+++ b/lib/tpl/default/detail.php
@@ -49,33 +49,33 @@ if (!defined('DOKU_INC')) die();
</p>
<p>&larr; <?php echo $lang['img_backto']?> <?php tpl_pagelink($ID)?></p>
+ <?php
+ $imgNS = getNS($IMG);
+ $authNS = auth_quickaclcheck("$imgNS:*");
+ if ($authNS >= AUTH_UPLOAD) {
+ echo '<p><a href="'.media_managerURL(array('ns' => $imgNS, 'image' => $IMG)).'">'.$lang['img_manager'].'</a></p>';
+ }
+ ?>
<dl class="img_tags">
<?php
- $t = tpl_img_getTag('Date.EarliestTime');
- if($t) print '<dt>'.$lang['img_date'].':</dt><dd>'.dformat($t).'</dd>';
-
- $t = tpl_img_getTag('File.Name');
- if($t) print '<dt>'.$lang['img_fname'].':</dt><dd>'.hsc($t).'</dd>';
-
- $t = tpl_img_getTag(array('Iptc.Byline','Exif.TIFFArtist','Exif.Artist','Iptc.Credit'));
- if($t) print '<dt>'.$lang['img_artist'].':</dt><dd>'.hsc($t).'</dd>';
-
- $t = tpl_img_getTag(array('Iptc.CopyrightNotice','Exif.TIFFCopyright','Exif.Copyright'));
- if($t) print '<dt>'.$lang['img_copyr'].':</dt><dd>'.hsc($t).'</dd>';
-
- $t = tpl_img_getTag('File.Format');
- if($t) print '<dt>'.$lang['img_format'].':</dt><dd>'.hsc($t).'</dd>';
-
- $t = tpl_img_getTag('File.NiceSize');
- if($t) print '<dt>'.$lang['img_fsize'].':</dt><dd>'.hsc($t).'</dd>';
-
- $t = tpl_img_getTag('Simple.Camera');
- if($t) print '<dt>'.$lang['img_camera'].':</dt><dd>'.hsc($t).'</dd>';
-
- $t = tpl_img_getTag(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject'));
- if($t) print '<dt>'.$lang['img_keywords'].':</dt><dd>'.hsc($t).'</dd>';
-
+ $config_files = getConfigFiles('mediameta');
+ foreach ($config_files as $config_file) {
+ if(@file_exists($config_file)) include($config_file);
+ }
+
+ foreach($fields as $key => $tag){
+ $t = array();
+ if (!empty($tag[0])) $t = array($tag[0]);
+ if(is_array($tag[3])) $t = array_merge($t,$tag[3]);
+ $value = tpl_img_getTag($t);
+ if ($value) {
+ echo '<dt>'.$lang[$tag[1]].':</dt><dd>';
+ if ($tag[2] == 'date') echo dformat($value);
+ else echo hsc($value);
+ echo '</dd>';
+ }
+ }
?>
</dl>
<?php //Comment in for Debug// dbg(tpl_img_getTag('Simple.Raw'));?>
diff --git a/lib/tpl/default/images/resizecol.png b/lib/tpl/default/images/resizecol.png
new file mode 100644
index 000000000..79fea0770
--- /dev/null
+++ b/lib/tpl/default/images/resizecol.png
Binary files differ
diff --git a/lib/tpl/default/main.php b/lib/tpl/default/main.php
index 26e84fde6..3e85c58f2 100644
--- a/lib/tpl/default/main.php
+++ b/lib/tpl/default/main.php
@@ -116,6 +116,7 @@ if (!defined('DOKU_INC')) die();
</div>
<div class="bar-right" id="bar__bottomright">
<?php tpl_button('subscribe')?>
+ <?php tpl_button('media')?>
<?php tpl_button('admin')?>
<?php tpl_button('profile')?>
<?php tpl_button('login')?>
diff --git a/lib/tpl/default/media.css b/lib/tpl/default/media.css
index 7b558a80b..b325a1ef6 100644
--- a/lib/tpl/default/media.css
+++ b/lib/tpl/default/media.css
@@ -151,6 +151,11 @@ it's dirty, so any "real" fixes are welcome */
border-bottom: solid 1px __border__;
padding: 0 0.5em 1em 0.5em;
}
+
+#media__content form#dw__upload {
+ border-bottom: 0;
+}
+
#media__content form#dw__upload fieldset {
padding: 0;
margin: 0;
diff --git a/lib/tpl/default/mediamanager.css b/lib/tpl/default/mediamanager.css
new file mode 100644
index 000000000..ea69dbeb9
--- /dev/null
+++ b/lib/tpl/default/mediamanager.css
@@ -0,0 +1,487 @@
+/* Layout */
+
+#mediamanager__page {
+ width: 100%;
+ overflow-x: auto;
+}
+
+#mediamanager__page h1 {
+ margin: 0 0 .5em;
+}
+
+#mediamanager__layout {
+ min-width: 840px;
+}
+
+#mediamanager__layout .layout,
+#mediamanager__layout .layout-resizable {
+ float: left;
+}
+
+#mediamanager__layout .layout-resizable {
+ margin-right: 10px;
+ float: left;
+}
+
+#mediamanager__layout .scroll-container {
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 0;
+ margin: 0;
+ text-align: left;
+ position: relative;
+}
+
+#mediamanager__layout .background-container {
+ background-color: __background_alt__;
+ margin-bottom: 10px;
+ padding: 10px 10px 8px;
+ text-align: left;
+ min-height: 20px;
+}
+
+#mediamanager__layout .background-container .icon {
+ margin-right: 5px;
+}
+
+#mediamanager__layout_namespaces {
+ width: 15%;
+ min-width: 125px;
+}
+
+#mediamanager__layout_list {
+ width: 45%;
+ min-width: 375px;
+}
+
+#mediamanager__layout_detail {
+ width: 35%;
+ min-width: 290px;
+}
+
+#mediamanager__page .ui-resizable-e {
+ width: 16px;
+ right: -13px;
+ /* icon from Crystal Clear icon set (LGPL) */
+ background: transparent url(images/resizecol.png) 0 25px no-repeat;
+}
+
+#mediamanager__page .ui-resizable-e:hover {
+ width: 6px;
+ right: -8px;
+ background-image: none;
+ background-color: __background_alt__;
+}
+
+/* Namespaces tree */
+
+#mediamanager__page ul.idx {
+ margin-left: .2em;
+}
+
+#mediamanager__page ul.idx ul {
+ margin-left: 1em;
+}
+
+.idx .selected {
+ background-color: __highlight__;
+ font-weight: bold;
+}
+
+/* Tabs */
+
+.mediamanager-tabs a {
+ font-weight: bold;
+ display: block;
+ float: left;
+ padding: 10px;
+ padding-bottom: 5px;
+ padding-top: 5px;
+ margin-right: 2px;
+ -moz-border-radius-topright: 10px;
+ -webkit-border-top-right-radius: 10px;
+ -moz-border-radius-topleft: 10px;
+ -webkit-border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+ border-top-left-radius: 10px;
+}
+
+.mediamanager-tabs .selected {
+ background-color: __background_alt__;
+}
+
+.mediamanager-tabs a:hover {
+ background-color: __background_alt__;
+ opacity: 0.5;
+}
+
+/* Title links */
+
+#mediamanager__files .namespace {
+ float: left;
+ font-weight: normal;
+}
+
+#mediamanager__tabs_list {
+ float: left;
+}
+
+#mediamanager__link_thumbs,
+#mediamanager__link_list {
+ padding-left: 30px;
+ display: inline-block;
+ width: 0;
+ overflow: hidden;
+}
+
+#mediamanager__link_thumbs {
+ background: url('../../images/icon-thumb.png') 0 -4px no-repeat;
+ margin-left: 10px;
+}
+
+#mediamanager__link_list {
+ background: url('../../images/icon-list.png') 0 -4px no-repeat;
+}
+
+#mediamanager__link_thumbs:hover,
+#mediamanager__link_list:hover {
+ width: auto;
+ margin-right: 10px;
+}
+
+#mediamanager__sort {
+ background: url('../../images/icon-sort.png') 0 -4px no-repeat;
+ padding-left: 30px;
+ display: block;
+ float: right;
+}
+
+* html #mediamanager__sort,
+*+html #mediamanager__sort {
+ position: relative;
+ margin-top: -18px;
+}
+
+/* File list */
+
+#mediamanager__file_list {
+ padding: 0;
+ margin: 0 !important;
+}
+
+#mediamanager__file_list li:hover {
+ background-color: __background_alt__;
+}
+
+/* Files thumbs view */
+
+.mediamanager-thumbs li {
+ width: 100px;
+ min-height: 130px;
+ display: inline-block;
+ display: -moz-inline-stack;
+ /* the right margin should visually be 10px, but because of its inline-block nature the whitespace inbetween is about 4px more */
+ margin: 0 6px 10px 0;
+ background-color: __background_neu__;
+ color: __text__;
+ padding: 5px;
+ vertical-align: top;
+ text-align: center;
+ zoom: 1;
+ position: relative;
+ line-height: 1.2;
+}
+
+* html .mediamanager-thumbs li {
+ display: inline;
+}
+
+*+html .mediamanager-thumbs li {
+ display: inline;
+}
+
+.mediamanager-thumbs li .image,
+.mediamanager-thumbs li .image0 {
+ width: 100%;
+ height: 90px;
+ display: block;
+ overflow: hidden;
+}
+
+.mediamanager-thumbs li .image1 {
+ display: none;
+}
+
+.mediamanager-thumbs li .image span,
+.mediamanager-thumbs li .image0 span {
+ vertical-align: middle;
+ display: table-cell;
+ width: 100px;
+ height: 90px;
+}
+
+.mediamanager-thumbs li .name,
+.mediamanager-thumbs li .size,
+.mediamanager-thumbs li .filesize,
+.mediamanager-thumbs li .date {
+ display: block;
+ overflow: hidden;
+ width: 90px;
+ white-space: nowrap;
+}
+
+.mediamanager-thumbs li .name {
+ padding: 5px 0;
+ font-weight: bold;
+}
+
+.mediamanager-thumbs li .date {
+ font-style: italic;
+ white-space: normal;
+}
+
+.mediamanager-thumbs li input[type=checkbox] {
+ display: none;
+ float: left;
+ margin: 3px;
+}
+
+.mediamanager-thumbs li:hover input[type=checkbox],
+.mediamanager-thumbs li input[type=checkbox]:checked {
+ display: block;
+}
+
+/* Files list view */
+
+.mediamanager-list li {
+ list-style: none;
+ display: block;
+ position: relative;
+ max-height: 50px;
+ margin: 0;
+ margin-bottom: 3px;
+ background-color: __background__;
+ color: __text__;
+}
+
+.mediamanager-list li:nth-child(2n+1) {
+ background-color: __background_neu__;
+}
+
+.mediamanager-list li .image,
+.mediamanager-list li .image1 {
+ width: 10%;
+ display: block;
+ overflow: hidden;
+ float: left;
+ height: 40px;
+ text-align: center;
+}
+
+.mediamanager-list li .image0 {
+ display: none;
+}
+
+.mediamanager-list li .name,
+.mediamanager-list li .size,
+.mediamanager-list li .filesize,
+.mediamanager-list li .date {
+ overflow: hidden;
+ float: left;
+ margin-left: 1%;
+ white-space: nowrap;
+}
+
+.mediamanager-list li .name {
+ width: 30%;
+ font-weight: bold;
+}
+
+.mediamanager-list li .size,
+.mediamanager-list li .filesize {
+ width: 15%;
+}
+
+.mediamanager-list li .date {
+ width: 20%;
+}
+
+.mediamanager-list li .date {
+ font-style: italic;
+ white-space: normal;
+}
+
+.mediamanager-list .icon {
+ max-width: 16px;
+ max-height: 16px;
+}
+
+.mediamanager-list li .image span,
+.mediamanager-list li .image1 span {
+ vertical-align: middle;
+ text-align: center;
+ display: table-cell;
+ width: 100px;
+ height: 40px;
+}
+
+.mediamanager-list li input[type=checkbox] {
+ display: none;
+ float: left;
+ margin: 3px;
+}
+
+.mediamanager-list li:hover input[type=checkbox],
+.mediamanager-list li input[type=checkbox]:checked {
+ display: block;
+}
+
+/* Upload panel */
+
+#mediamanager__layout div.upload {
+ padding-bottom: 0.5em;
+}
+
+#media__content #mediamanager__uploader {
+ border-bottom: 1px solid __border__;
+ padding-bottom: 0.5em;
+}
+
+/* File preview */
+
+.mediamanager__preview,
+.mediamanager__preview_buttons {
+ text-align: center;
+ margin-bottom: 5px;
+}
+
+.mediamanager__preview img {
+ width: 99%;
+}
+
+/* Meta data edit form */
+
+#mediamanager__details div.metafield {
+ margin-bottom: 5px;
+}
+
+#mediamanager__details label {
+ display: block;
+}
+
+#mediamanager__details form.meta input {
+ width: 50%;
+}
+
+#mediamanager__details form.meta input.button {
+ width: auto;
+}
+
+#mediamanager__details form.meta textarea.edit {
+ height: 6em;
+ width: 95%;
+ min-width: 95%;
+ max-width: 95%;
+}
+
+/* Revisions form */
+
+#mediamanager__details #page__revisions ul {
+ margin-left: 10px;
+ list-style-type: none;
+}
+
+#mediamanager__details #page__revisions ul li div.li div {
+ font-size: 90%;
+ color: __text_neu__;
+ padding-left: 18px;
+}
+
+#mediamanager__details #page__revisions ul li div.li input {
+ position: relative;
+ top: 1px;
+}
+
+/* File diff */
+
+#mediamanager__diff {
+ margin-top: 10px;
+}
+
+#mediamanager__diff_table {
+ padding: 0;
+ margin: 0;
+ margin-top: 10px;
+}
+
+#mediamanager__diff_table li {
+ width: 48%;
+ display: inline-block;
+ margin: 0;
+ margin-bottom: 10px;
+ padding: 2px;
+ vertical-align: top;
+ zoom: 1;
+ color: __text__;
+}
+
+* html #mediamanager__diff_table li {
+ display: inline;
+}
+
+*+html #mediamanager__diff_table li {
+ display: inline;
+}
+
+/* For IE7 */
+*:first-child+html #mediamanager__diff_table li {
+ width: 45%;
+}
+
+/* Image diff */
+
+#mediamanager__layout dl.img_tags dd.highlighted{
+ background-color: __highlight__;
+}
+
+#mediamanager__form_diffview {
+ margin-bottom: 10px;
+}
+
+#mediamanager__diff_layout {
+ position: relative;
+}
+
+#mediamanager__diff_layout div {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+#mediamanager__diff_opacity_image1,
+#mediamanager__diff_portions_image1 {
+ width: 97%;
+}
+
+#mediamanager__diff_layout div img {
+ width: 100%;
+}
+
+#mediamanager__diff_opacity_image2 {
+ width: 97%;
+ -moz-opacity: 0.5;
+ -khtml-opacity: 0.5;
+ opacity: 0.5;
+}
+
+#mediamanager__diff_portions_image2 {
+ width: 97%;
+ border-right: 1px solid red;
+ overflow: hidden;
+}
+
+#mediamanager__opacity_slider,
+#mediamanager__portions_slider {
+ margin: 10px;
+ width: 95%;
+}
diff --git a/lib/tpl/default/style.ini b/lib/tpl/default/style.ini
index c5b2c31a5..05914012e 100644
--- a/lib/tpl/default/style.ini
+++ b/lib/tpl/default/style.ini
@@ -15,6 +15,8 @@ _mediaoptions.css = screen
_admin.css = screen
_linkwiz.css = screen
_subscription.css = screen
+mediamanager.css = screen
+_fileuploader.css = screen
rtl.css = rtl
print.css = print