summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/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.php44
-rw-r--r--inc/lang/en/uploadmail.txt17
-rw-r--r--inc/media.php1157
-rw-r--r--inc/pageutils.php26
-rw-r--r--inc/search.php10
-rw-r--r--inc/template.php141
-rw-r--r--lib/exe/ajax.php92
-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/script.js12
-rw-r--r--lib/plugins/popularity/lang/uk/submitted.txt2
-rw-r--r--lib/scripts/behaviour.js4
-rw-r--r--lib/scripts/cookie.js4
-rw-r--r--lib/scripts/fileuploader.js1247
-rw-r--r--lib/scripts/fileuploaderextended.js339
-rw-r--r--lib/scripts/media.js449
-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/main.php1
-rw-r--r--lib/tpl/default/media.css5
-rw-r--r--lib/tpl/default/mediamanager.css419
-rw-r--r--lib/tpl/default/style.ini2
39 files changed, 4387 insertions, 278 deletions
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/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 95356f7b4..6a02bbe0b 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,36 @@ $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';
//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/media.php b/inc/media.php
index 731ba1668..cbb17ac21 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,40 @@ 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;
+function media_metaform($id,$auth,$fullscreen = 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;
+ if (!$fullscreen) {
+ echo '<h1>'.hsc(noNS($id)).'</h1>'.NL;
+ $action = DOKU_BASE.'lib/exe/mediamanager.php';
+ } else {
+ $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,8 +151,15 @@ 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'].
+ if (!$fullscreen) {
+ $do = 'do';
+ } else {
+ 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;
+ if (!$fullscreen)
echo '<input name="do[cancel]" type="submit" value="'.$lang['btn_cancel'].
'" title="'.$lang['btn_cancel'].' [C]" accesskey="c" class="button" />'.NL;
echo '</div>'.NL;
@@ -178,6 +204,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 +220,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 +243,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.'/'.$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 +352,7 @@ function media_save($file, $id, $ow, $auth, $move) {
}
}
- global $lang;
+ global $lang, $conf;
// get filename
$id = cleanID($id,false,true);
@@ -298,7 +370,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 +414,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 +454,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 +538,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 +556,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 +574,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 +582,714 @@ 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 $ns ? $ns : '['.$lang['mediaroot'].']';
+
+ 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>';
+}
+
+/**
+ * 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 = $_REQUEST['sort'];
+ if (!$sort && @strpos($_COOKIE['DOKU_PREFS'], 'sort')) {
+ $parts = explode('#', $_COOKIE['DOKU_PREFS']);
+ for ($i = 0; $i < count($parts); $i+=2){
+ if ($parts[$i] == 'sort') $sort = $parts[$i+1];
+ }
+ }
+ 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 = '';
+
+ echo '<div class="background-container">';
+ echo sprintf($lang['media_search'], $ns ? $ns : '['.$lang['mediaroot'].']');
+ echo'</div>';
+
+ echo '<div class="scroll-container">';
+ media_searchform($ns, $query, true);
+ if ($do == 'searchlist') media_searchlist($query,$ns,$auth,true);
+ 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,true);
+ }
+ 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>';
+ }
+ }
+ 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;
}
- media_searchform($ns);
+}
+
+/**
+ * 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 +1297,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){
global $conf;
global $lang;
+
$ns = cleanID($ns);
if ($query) {
@@ -509,13 +1326,27 @@ function media_searchlist($query,$ns,$auth=null){
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);
+ }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>';
}
}
@@ -536,15 +1367,17 @@ function media_fileactions($item,$auth){
// 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'].'">'.
+ $link = DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).
+ '&amp;sectok='.getSecurityToken();
+ echo ' <a href="'.$link.'" 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']).'">'.
+ $link = DOKU_BASE.'lib/exe/mediamanager.php?edit='.rawurlencode($item['id']);
+ echo ' <a href="'.$link.'">'.
'<img src="'.DOKU_BASE.'lib/images/pencil.png" alt="'.$lang['metaedit'].'" '.
'title="'.$lang['metaedit'].'" class="btn" /></a>';
}
@@ -608,27 +1441,112 @@ 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']))).'"><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']))).'" 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']))).'">';
+ 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,88 +1574,116 @@ 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) {
+ echo '<div class="nothing">'.$lang['media_perm_upload'].'</div>'.NL;
+ return;
+ }
+ $auth_ow = (($conf['mediarevisions']) ? AUTH_UPLOAD : AUTH_DELETE);
- if($auth < AUTH_UPLOAD) return; //fixme print info on missing permissions?
+ $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_makeButton('submit', '', $lang['btn_search']));
@@ -786,7 +1732,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/search.php b/inc/search.php
index 7b53edabe..a6089c479 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
diff --git a/inc/template.php b/inc/template.php
index 5184929b8..0dfc5a526 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;
@@ -1122,6 +1128,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 +1223,6 @@ function tpl_mediaContent($fromajax=false){
*/
function tpl_mediaTree(){
global $NS;
-
ptln('<div id="media__tree">');
media_nstree($NS);
ptln('</div>');
@@ -1386,6 +1478,51 @@ 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 '<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..a6e45c4de 100644
--- a/lib/exe/ajax.php
+++ b/lib/exe/ajax.php
@@ -208,7 +208,97 @@ 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;
+
+ $NS = $_REQUEST['ns'];
+ $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']);
+ $id = ((empty($_POST['mediaid'])) ? $_FILES['qqfile']['name'] : $_POST['mediaid']);
+ }
+ if (isset($_GET['qqfile'])) {
+ $res = media_upload_xhr($NS, $AUTH);
+ $id = $_GET['qqfile'];
+ }
+ $id = cleanID($id, false, true);
+
+ 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/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/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/scripts/behaviour.js b/lib/scripts/behaviour.js
index afb210840..cfdc89157 100644
--- a/lib/scripts/behaviour.js
+++ b/lib/scripts/behaviour.js
@@ -63,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();
+ });
},
/**
diff --git a/lib/scripts/cookie.js b/lib/scripts/cookie.js
index f97882855..3f3375fa7 100644
--- a/lib/scripts/cookie.js
+++ b/lib/scripts/cookie.js
@@ -27,7 +27,7 @@ DokuCookie = {
jQuery.each(this.data, function (val, key) {
if (this.data.hasOwnProperty(key)) {
text += '#'+encodeURIComponent(key)+'#'+encodeURIComponent(val);
- }
+ }
});
jQuery.cookie(this.name,text.substr(1), {expires: 365, path: DOKU_BASE});
},
@@ -59,5 +59,5 @@ DokuCookie = {
this.data[decodeURIComponent(parts[i])] = decodeURIComponent(parts[i+1]);
}
}
- }
+ }
};
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/media.js b/lib/scripts/media.js
index 1402ad4bf..7103727c5 100644
--- a/lib/scripts/media.js
+++ b/lib/scripts/media.js
@@ -22,6 +22,11 @@ var dw_mediamanager = {
size: false,
forbidden_opts: {},
+ // File list view type
+ view: false,
+
+ layout_width: 0,
+
init: function () {
var $content, $tree;
$content = jQuery('#media__content');
@@ -38,7 +43,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 +65,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 +244,9 @@ var dw_mediamanager = {
$file = jQuery(this);
$name = jQuery('#upload__name');
+
+ if ($name.val() != '') return;
+
if(!$file.length || !$name.length) {
return;
}
@@ -218,33 +264,416 @@ 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);
+ }
+ }
// 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);
+ });
+
+ 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;
+ 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 +950,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/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/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..16982d98d
--- /dev/null
+++ b/lib/tpl/default/mediamanager.css
@@ -0,0 +1,419 @@
+/* Layout */
+
+#mediamanager__page {
+ width: 100%;
+ overflow-x: auto;
+}
+
+#mediamanager__layout {
+ min-width: 840px;
+}
+
+#mediamanager__layout .layout,
+#mediamanager__layout .layout-resizable {
+ margin-left: 5px;
+ margin-right: 5px;
+ 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;
+ text-align: left;
+}
+
+#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;
+}
+
+.ui-resizable-e:hover {
+ background-color: #dadada;
+}
+
+/* Namespaces tree */
+
+.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__tabs_list {
+ display: inline;
+}
+
+#mediamanager__link_thumbs {
+ background: url('../../images/icon-thumb.png') 0 -4px no-repeat;
+ padding-left: 30px;
+ display: inline-block;
+ width: 0;
+ overflow: hidden;
+ margin-left: 10px;
+}
+
+#mediamanager__link_list {
+ background: url('../../images/icon-list.png') 0 -4px no-repeat;
+ padding-left: 30px;
+ display: inline-block;
+ width: 0;
+ overflow: hidden;
+}
+
+#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: #dadada;
+}
+
+/* Files thumbs view */
+
+.mediamanager-thumbs li {
+ width: 100px;
+ min-height: 130px;
+ display: inline-block;
+ margin: 0;
+ margin-right: 10px;
+ margin-bottom: 10px;
+ background-color: __background_alt__;
+ padding: 10px;
+ vertical-align: top;
+ display: -moz-inline-stack;
+ text-align: center;
+ zoom: 1;
+ position: relative;
+}
+
+* 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 .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;
+}
+
+.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;
+ width: 19%;
+ margin-left: 1%;
+ white-space: nowrap;
+}
+
+.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 */
+
+form.meta textarea.edit {
+ height: 8em;
+ 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