diff options
28 files changed, 1210 insertions, 641 deletions
diff --git a/conf/mediameta.php b/conf/mediameta.php new file mode 100644 index 000000000..1ad2587ee --- /dev/null +++ b/conf/mediameta.php @@ -0,0 +1,57 @@ +<?php +/** + * This configures which meta data will be editable through + * the media manager. Each field of the array is an array with the + * following contents: + * fieldname - Where data will be saved (EXIF or IPTC field) + * label - key to lookup in the $lang var, if not found printed as is + * htmltype - 'text' or 'textarea' + * lookups - array additional fields to lookup the data (EXIF or IPTC fields) + * + * The fields are not ordered continously to make inserting additional items + * in between simpler. + * + * This is a PHP snippet, so PHP syntax applies. + * + * Note: $fields is not a global variable and will not be available to any + * other functions or templates later + * + * You may extend or overwrite this variable in a optional + * conf/mediameta.local.php file + * + * For a list of available EXIF/IPTC fields refer to + * http://wiki.splitbrain.org/wiki:tpl:detail.php + */ + + +$fields = array( + 10 => array('Iptc.Headline', + 'img_title', + 'text'), + + 20 => array('Iptc.Caption', + 'img_caption', + 'textarea', + array('Exif.UserComment', + 'Exif.TIFFImageDescription', + 'Exif.TIFFUserComment')), + + 30 => array('Iptc.Byline', + 'img_artist', + 'text', + array('Exif.TIFFArtist', + 'Exif.Artist', + 'Iptc.Credit')), + + 40 => array('Iptc.CopyrightNotice', + 'img_copyr', + 'text', + array('Exif.TIFFCopyright', + 'Exif.Copyright')), + + 50 => array('Iptc.Keywords', + 'img_keywords', + 'text', + array('Exif.Category')), +); + diff --git a/inc/html.php b/inc/html.php index 928da9cef..5dbfcd921 100644 --- a/inc/html.php +++ b/inc/html.php @@ -191,7 +191,7 @@ function html_backtomedia_button($params,$akey=''){ global $conf; global $lang; - $ret = '<form class="button" method="get" action="'.DOKU_BASE.'lib/exe/media.php"><div class="no">'; + $ret = '<form class="button" method="get" action="'.DOKU_BASE.'lib/exe/mediamanager.php"><div class="no">'; reset($params); while (list($key, $val) = each($params)) { diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index c1aa6bd6c..7d067ee25 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -91,7 +91,7 @@ $lang['notsavedyet'] = 'Unsaved changes will be lost.\nReally continue?'; $lang['rssfailed'] = 'An error occured while fetching this feed: '; $lang['nothingfound']= 'Nothing was found.'; -$lang['mediaselect'] = 'Mediafile Selection'; +$lang['mediaselect'] = 'Mediafiles'; $lang['fileupload'] = 'Mediafile Upload'; $lang['uploadsucc'] = 'Upload successful'; $lang['uploadfail'] = 'Upload failed. Maybe wrong permissions?'; @@ -103,6 +103,9 @@ $lang['mediainuse'] = 'The file "%s" hasn\'t been deleted - it is still in use. $lang['namespaces'] = 'Namespaces'; $lang['mediafiles'] = 'Available files in'; +$lang['js']['keepopen'] = 'Keep window open on selection'; +$lang['js']['idtouse'] = 'Please use the following ID to reference this file:'; + $lang['reference'] = 'References for'; $lang['ref_inuse'] = 'The file can\'t be deleted, because it\'s still used by the following pages:'; $lang['ref_hidden'] = 'Some references are on pages you don\'t have permission to read'; diff --git a/inc/media.php b/inc/media.php new file mode 100644 index 000000000..39f76e311 --- /dev/null +++ b/inc/media.php @@ -0,0 +1,484 @@ +<?php +/** + * All output and handler function needed for the media management popup + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Andreas Gohr <andi@splitbrain.org> + */ + +if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); +if(!defined('NL')) define('NL',"\n"); + +require_once(DOKU_INC.'inc/html.php'); +require_once(DOKU_INC.'inc/search.php'); +require_once(DOKU_INC.'inc/JpegMeta.php'); + +/** + * Lists pages which currently use a media file selected for deletion + * + * References uses the same visual as search results and share + * their CSS tags except pagenames won't be links. + * + * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> + */ +function media_filesinuse($data,$id){ + global $lang; + echo '<h1>'.$lang['reference'].' <code>'.hsc(noNS($id)).'</code></h1>'; + echo '<p>'.hsc($lang['ref_inuse']).'</p>'; + + $hidden=0; //count of hits without read permission + usort($data,'sort_search_fulltext'); + foreach($data as $row){ + if(auth_quickaclcheck($row['id']) >= AUTH_READ){ + echo '<div class="search_result">'; + echo '<span class="mediaref_ref">'.$row['id'].'</span>'; + echo ': <span class="search_cnt">'.$row['count'].' '.$lang['hits'].'</span><br />'; + echo '<div class="search_snippet">'.$row['snippet'].'</div>'; + echo '</div>'; + }else + $hidden++; + } + if ($hidden){ + print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>'; + } +} + +/** + * Handles the saving of image meta data + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function media_metasave($id,$auth,$data){ + if($auth < AUTH_UPLOAD) return false; + global $lang; + $src = mediaFN($id); + + $meta = new JpegMeta($src); + $meta->_parseAll(); + + foreach($data as $key => $val){ + $val=trim($val); + if(empty($val)){ + $meta->deleteField($key); + }else{ + $meta->setField($key,$val); + } + } + + if($meta->save()){ + msg($lang['metasaveok'],1); + return $id; + }else{ + msg($lang['metasaveerr'],-1); + return false; + } +} + +/** + * Display the form to edit image meta data + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function media_metaform($id,$auth){ + if($auth < AUTH_UPLOAD) return false; + global $lang; + + // load the field descriptions + static $fields = null; + if(is_null($fields)){ + include(DOKU_CONF.'mediameta.php'); + if(@file_exists(DOKU_CONF.'mediameta.local.php')){ + include(DOKU_CONF.'mediameta.local.php'); + } + } + + $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; + echo '<input type="hidden" name="img" value="'.hsc($id).'" />'.NL; + foreach($fields as $key => $field){ + // get current value + $tags = array($field[0]); + if(is_array($field[3])) $tags = array_merge($tags,$field[3]); + $value = tpl_img_getTag($tags,'',$src); + + // prepare attributes + $p = array(); + $p['class'] = 'edit'; + $p['id'] = 'meta__'.$key; + $p['name'] = 'meta['.$field[0].']'; + + // put label + echo '<div class="metafield">'; + echo '<label for="meta__'.$key.'">'; + echo ($lang[$field[1]]) ? $lang[$field[1]] : $field[1]; + echo '</label>'; + + // put input field + if($field[2] == 'text'){ + $p['value'] = $value; + $p['type'] = 'text'; + $att = buildAttributes($p); + echo "<input $att/>".NL; + }else{ + $att = buildAttributes($p); + echo "<textarea $att>".formText($value).'</textarea>'.NL; + } + echo '</div>'.NL; + } + echo '<div class="buttons">'.NL; + echo '<input name="do[save]" type="submit" value="'.$lang['btn_save']. + '" title="ALT+S" accesskey="s" class="button" />'.NL; + echo '<input name="do[cancel]" type="submit" value="'.$lang['btn_cancel']. + '" title="ALT+C" accesskey="c" class="button" />'.NL; + echo '</form>'.NL; + echo '</div>'; +} + +/** + * Handles media file deletions + * + * If configured, checks for media references before deletion + * + * @author Andreas Gohr <andi@splitbrain.org> + * @return mixed false on error, true on delete or array with refs + */ +function media_delete($id,$auth){ + if($auth < AUTH_DELETE) return false; + global $conf; + global $lang; + + $mediareferences = array(); + if($conf['refcheck']){ + search($mediareferences,$conf['datadir'],'search_reference',array('query' => $id)); + } + + if(!count($mediareferences)){ + $file = mediaFN($id); + if(@unlink($file)){ + msg(str_replace('%s',noNS($id),$lang['deletesucc']),1); + io_sweepNS($id,'mediadir'); + return true; + } + //something went wrong + msg(str_replace('%s',$file,$lang['deletefail']),-1); + return false; + }elseif(!$conf['refshow']){ + msg(str_replace('%s',noNS($id),$lang['mediainuse']),0); + return false; + } + + return $mediareferences; +} + +/** + * Handles media file uploads + * + * @author Andreas Gohr <andi@splitbrain.org> + * @return mixed false on error, id of the new file on success + */ +function media_upload($ns,$auth){ + if($auth < AUTH_UPLOAD) return false; + require_once(DOKU_INC.'inc/confutils.php'); + global $lang; + global $conf; + + // get file + $id = $_POST['id']; + $file = $_FILES['upload']; + // get id + if(empty($id)) $id = $file['name']; + $id = cleanID($ns.':'.$id); //FIXME handle relative and absolute names here + // get filename + $fn = mediaFN($id); + + // get filetype regexp + $types = array_keys(getMimeTypes()); + $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types); + $regex = join('|',$types); + + // because a temp file was created already + if(preg_match('/\.('.$regex.')$/i',$fn)){ + //check for overwrite + if(@file_exists($fn) && (!$_POST['ow'] || $auth < AUTH_DELETE)){ + msg($lang['uploadexist'],0); + return false; + } + // prepare directory + io_makeFileDir($fn); + if(move_uploaded_file($file['tmp_name'], $fn)) { + // set the correct permission here + if($conf['fperm']) chmod($fn, $conf['fperm']); + msg($lang['uploadsucc'],1); + return $id; + }else{ + msg($lang['uploadfail'],-1); + } + }else{ + msg($lang['uploadwrong'],-1); + } + return false; +} + + + +/** + * List all files in a given Media namespace + */ +function media_filelist($ns,$auth=null,$jump=''){ + global $conf; + global $lang; + $ns = cleanID($ns); + + // check auth our self if not given (needed for ajax calls) + if(is_null($auth)) $auth = auth_quickaclcheck("$ns:*"); + + echo '<h1>:'.hsc($ns).'</h1>'.NL; + + if($auth < AUTH_READ){ + // FIXME: print permission warning here instead? + echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; + return; + } + + media_uploadform($ns, $auth); + + $dir = utf8_encodeFN(str_replace(':','/',$ns)); + $data = array(); + search($data,$conf['mediadir'],'search_media',array(),$dir); + + if(!count($data)){ + echo '<div class="nothing">'.$lang['nothingfound'].'</div>'.NL; + return; + } + + foreach($data as $item){ + media_printfile($item,$auth,$jump); + } +} + +/** + * Print action links for a file depending on filetype + * and available permissions + * + * @todo contains inline javascript + */ +function media_fileactions($item,$auth){ + global $lang; + + // no actions if not writable + if(!$item['writable']) return; + + // delete button + if($auth >= AUTH_DELETE){ + $ask = addslashes($lang['del_confirm']).'\\n'; + $ask .= addslashes($item['id']); + + echo ' <a href="'.DOKU_BASE.'lib/exe/mediamanager.php?delete='.rawurlencode($item['id']).'" '. + 'onclick="return confirm(\''.$ask.'\')" onkeypress="return confirm(\''.$ask.'\')">'. + '<img src="'.DOKU_BASE.'lib/images/trash.png" alt="'.$lang['btn_delete'].'" '. + 'title="'.$lang['btn_delete'].'" class="btn" /></a>'; + } + + // edit button + if($auth >= AUTH_UPLOAD && $item['isimg'] && $item['meta']->getField('File.Mime') == 'image/jpeg'){ + echo ' <a href="'.DOKU_BASE.'lib/exe/mediamanager.php?edit='.rawurlencode($item['id']).'">'. + '<img src="'.DOKU_BASE.'lib/images/pencil.png" alt="'.$lang['metaedit'].'" '. + 'title="'.$lang['metaedit'].'" class="btn" /></a>'; + } + +} + +/** + * Formats and prints one file in the list + */ +function media_printfile($item,$auth,$jump){ + // Prepare zebra coloring + // I always wanted to use this variable name :-D + static $twibble = 1; + $twibble *= -1; + $zebra = ($twibble == -1) ? 'odd' : 'even'; + + // Automatically jump to recent action + if($jump == $item['id']) { + $jump = ' id="scroll__here" '; + }else{ + $jump = ''; + } + + // Prepare fileicons + list($ext,$mime) = mimetype($item['file']); + $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); + $class = 'select mediafile mf_'.$class; + + // Prepare filename + $file = utf8_decodeFN($item['file']); + + // Prepare info + $info = ''; + if($item['isimg']){ + $info .= (int) $item['meta']->getField('File.Width'); + $info .= '×'; + $info .= (int) $item['meta']->getField('File.Height'); + $info .= ' '; + } + $info .= filesize_h($item['size']); + + // ouput + echo '<div class="'.$zebra.'"'.$jump.'>'.NL; + echo '<a name="h_'.$item['id'].'" class="'.$class.'">'.$file.'</a> '; + echo '<span class="info">('.$info.')</span>'.NL; + media_fileactions($item,$auth); + if($item['isimg']) media_printimgdetail($item); + echo '<div class="clearer"></div>'.NL; + echo '</div>'.NL; +} + +/** + * Prints a thumbnail and metainfos + */ +function media_printimgdetail($item){ + // 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); + } + $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 + echo '<div class="detail">'; + echo '<div class="thumb">'; + echo '<a name="d_'.$item['id'].'" class="select">'; + echo '<img src="'.$src.'" '.$att.' />'; + echo '</a>'; + echo '</div>'; + + //read EXIF/IPTC data + echo '<p>'; + $t = $item['meta']->getField('IPTC.Headline'); + if($t) echo '<strong>'.htmlspecialchars($t).'</strong><br />'; + + $t = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment', + 'EXIF.TIFFImageDescription', + 'EXIF.TIFFUserComment')); + if($t) echo htmlspecialchars($t).'<br />'; + + $t = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category')); + if($t) echo '<em>'.htmlspecialchars($t).'</em>'; + echo '</p>'; + echo '</div>'; +} + +/** + * Print the media upload form if permissions are correct + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function media_uploadform($ns, $auth){ + global $lang; + + if($auth < AUTH_UPLOAD) return; //fixme print info on missing permissions? + + ?> + <form action="<?php echo DOKU_BASE?>lib/exe/mediamanager.php" + method="post" enctype="multipart/form-data" class="upload"> + <input type="hidden" name="ns" value="<?php echo hsc($ns)?>" /> + + <?php echo $lang['txt_upload']?>: + <input type="file" name="upload" class="edit" id="upload__file" /><br /> + + <?php echo $lang['txt_filename']?>: + <input type="text" name="id" class="edit" id="upload__name" /> + <input type="submit" class="button" value="<?php echo $lang['btn_upload']?>" accesskey="s" /> + + <?php if($auth >= AUTH_DELETE){?> + <br /> + <label for="dw__ow"> + <input type="checkbox" name="ow" value="1" id="dw__ow" /><?php echo $lang['txt_overwrt']?> + </label> + <?php }?> + </form> + <?php +} + + + +/** + * Build a tree outline of available media namespaces + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function media_nstree($ns){ + global $conf; + + // currently selected namespace + $ns = cleanID($ns); + if(empty($ns)){ + $ns = dirname(str_replace(':','/',$ID)); + if($ns == '.') $ns =''; + } + $ns = utf8_encodeFN(str_replace(':','/',$ns)); + + $data = array(); + search($data,$conf['mediadir'],'search_index',array('ns' => $ns)); + + // wrap a list with the root level around the other namespaces + $item = array( 'level' => 0, 'id' => '', 'open' =>'true', 'label' => ':*'); + + echo '<ul class="idx">'; + echo media_nstree_li($item); + echo media_nstree_item($item); + echo html_buildlist($data,'idx','media_nstree_item','media_nstree_li'); + echo '</li>'; + echo '</ul>'; +} + +/** + * Userfunction for html_buildlist + * + * Prints a media namespace tree item + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function media_nstree_item($item){ + $pos = strrpos($item['id'], ':'); + $label = substr($item['id'], $pos > 0 ? $pos + 1 : 0); + if(!$item['label']) $item['label'] = $label; + + $ret = ''; + $ret .= '<a href="'.DOKU_BASE.'lib/exe/mediamanager.php?ns='.idfilter($item['id']).'" class="idx_dir">'; + $ret .= $item['label']; + $ret .= '</a>'; + return $ret; +} + +/** + * Userfunction for html_buildlist + * + * Prints a media namespace tree item opener + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function media_nstree_li($item){ + $class='media level'.$item['level']; + if($item['open']){ + $class .= ' open'; + $img = DOKU_BASE.'lib/images/minus.gif'; + }else{ + $class .= ' closed'; + $img = DOKU_BASE.'lib/images/plus.gif'; + } + return '<li class="'.$class.'">'. + '<img src="'.$img.'" alt="*" />'; +} diff --git a/inc/search.php b/inc/search.php index 5f1ae6f1a..3c473daee 100644 --- a/inc/search.php +++ b/inc/search.php @@ -207,6 +207,7 @@ function search_media(&$data,$base,$file,$type,$lvl,$opts){ $info['file'] = basename($file); $info['size'] = filesize($base.'/'.$file); + $info['writable'] = is_writable($base.'/'.$file); if(preg_match("/\.(jpe?g|gif|png)$/",$file)){ $info['isimg'] = true; require_once(DOKU_INC.'inc/JpegMeta.php'); diff --git a/inc/template.php b/inc/template.php index 1af735382..7a7e751a9 100644 --- a/inc/template.php +++ b/inc/template.php @@ -210,7 +210,11 @@ function tpl_metaheaders($alt=true){ // load javascript $js_edit = ($ACT=='edit' || $ACT=='preview' || $ACT=='recover') ? 1 : 0; $js_write = ($INFO['writable']) ? 1 : 0; - if($js_edit && $js_write){ + if(defined('DOKU_MEDIAMANAGER')){ + $js_edit = 1; + $js_write = 0; + } + if(($js_edit && $js_write) || defined('DOKU_MEDIAMANAGER')){ ptln('<script type="text/javascript" charset="utf-8">',$it); ptln("NS='".$INFO['namespace']."';",$it+2); if($conf['useacl'] && $_SERVER['REMOTE_USER']){ @@ -666,183 +670,6 @@ function tpl_pageinfo(){ } /** - * Print a list of namespaces containing media files - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function tpl_medianamespaces(){ - global $conf; - - $data = array(); - search($data,$conf['mediadir'],'search_namespaces',array()); - print html_buildlist($data,'idx',media_html_list_namespaces); -} - -/** - * Print a list of mediafiles in the current namespace - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function tpl_mediafilelist(){ - global $conf; - global $lang; - global $NS; - global $AUTH; - $dir = utf8_encodeFN(str_replace(':','/',$NS)); - - $data = array(); - search($data,$conf['mediadir'],'search_media',array(),$dir); - - if(!count($data)){ - ptln('<div class="nothing">'.$lang['nothingfound'].'</div>'); - return; - } - - ptln('<ul>',2); - foreach($data as $item){ - if(!$item['isimg']){ - // add file icons - list($ext,$mime) = mimetype($item['file']); - $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext); - $class = ' class="mediafile mf_'.$class.'"'; - }else{ - $class = ''; - } - - ptln('<li><div class="li">',4); - ptln('<a href="javascript:mediaSelect(\':'.$item['id'].'\')"'.$class.'>'. - utf8_decodeFN($item['file']). - '</a>',6); - - //prepare deletion button - if($AUTH >= AUTH_DELETE){ - $ask = addslashes($lang['del_confirm']).'\\n'; - $ask .= addslashes($item['id']); - - $del = '<a href="'.DOKU_BASE.'lib/exe/media.php?delete='.rawurlencode($item['id']).'" '. - 'onclick="return confirm(\''.$ask.'\')" onkeypress="return confirm(\''.$ask.'\')">'. - '<img src="'.DOKU_BASE.'lib/images/del.png" alt="'.$lang['btn_delete'].'" '. - 'title="'.$lang['btn_delete'].'" /></a>'; - }else{ - $del = ''; - } - - if($item['isimg']){ - $w = (int) $item['meta']->getField('File.Width'); - $h = (int) $item['meta']->getField('File.Height'); - - ptln('('.$w.'×'.$h.' '.filesize_h($item['size']).')',6); - ptln($del.'<br />',6); - ptln('<div class="imagemeta">',6); - - //build thumbnail - print '<a href="javascript:mediaSelect(\':'.$item['id'].'\')">'; - - if($w>120 || $h>120){ - $ratio = $item['meta']->getResizeRatio(120); - $w = floor($w * $ratio); - $h = floor($h * $ratio); - } - - $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); - - print '<img src="'.$src.'" '.$att.' />'; - print '</a>'; - - //read EXIF/IPTC data - $t = $item['meta']->getField('IPTC.Headline'); - if($t) print '<strong>'.htmlspecialchars($t).'</strong><br />'; - - $t = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment', - 'EXIF.TIFFImageDescription', - 'EXIF.TIFFUserComment')); - if($t) print htmlspecialchars($t).'<br />'; - - $t = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category')); - if($t) print '<em>'.htmlspecialchars($t).'</em><br />'; - - //add edit button - if($AUTH >= AUTH_UPLOAD && $item['meta']->getField('File.Mime') == 'image/jpeg'){ - print '<a href="'.DOKU_BASE.'lib/exe/media.php?edit='.rawurlencode($item['id']).'">'; - print '<img src="'.DOKU_BASE.'lib/images/edit.gif" alt="'.$lang['metaedit'].'" title="'.$lang['metaedit'].'" />'; - print '</a>'; - } - - ptln('</div>',6); - }else{ - ptln ('('.filesize_h($item['size']).')',6); - ptln($del,6); - } - ptln('</div></li>',4); - } - ptln('</ul>',2); -} - -/** - * show references to a media file - * References uses the same visual as search results and share - * their CSS tags except pagenames won't be links. - * - * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> - */ -function tpl_showreferences(&$data){ - global $lang; - - $hidden=0; //count of hits without read permission - - if(count($data)){ - usort($data,'sort_search_fulltext'); - foreach($data as $row){ - if(auth_quickaclcheck($row['id']) >= AUTH_READ){ - print '<div class="search_result">'; - print '<span class="mediaref_ref">'.$row['id'].'</span>'; - print ': <span class="search_cnt">'.$row['count'].' '.$lang['hits'].'</span><br />'; - print '<div class="search_snippet">'.$row['snippet'].'</div>'; - print '</div>'; - }else - $hidden++; - } - if ($hidden){ - print '<div class="mediaref_hidden">'.$lang['ref_hidden'].'</div>'; - } - } -} - -/** - * Print the media upload form if permissions are correct - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function tpl_mediauploadform(){ - global $NS; - global $UPLOADOK; - global $AUTH; - global $lang; - - if(!$UPLOADOK) return; - - ptln('<form action="'.DOKU_BASE.'lib/exe/media.php" id="dw__upload"'. - ' method="post" enctype="multipart/form-data">',2); - ptln($lang['txt_upload'].':<br />',4); - ptln('<input type="file" name="upload" class="edit" onchange="suggestWikiname();" />',4); - ptln('<input type="hidden" name="ns" value="'.hsc($NS).'" /><br />',4); - ptln($lang['txt_filename'].'<br />',4); - ptln('<input type="text" name="id" class="edit" />',4); - ptln('<input type="submit" class="button" value="'.$lang['btn_upload'].'" accesskey="s" />',4); - if($AUTH >= AUTH_DELETE){ - ptln('<label for="dw__ow"><input type="checkbox" name="ow" value="1" id="dw__ow" />'.$lang['txt_overwrt'].'</label>',4); - } - ptln('</form>',2); -} - -/** * Prints or returns the name of the given page (current one if none given). * * If useheading is enabled this will use the first headline else @@ -880,15 +707,18 @@ function tpl_pagetitle($id=null, $ret=false){ * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC * to the names of the latter one) * - * Only allowed in: detail.php, mediaedit.php + * Only allowed in: detail.php * * @author Andreas Gohr <andi@splitbrain.org> */ -function tpl_img_getTag($tags,$alt=''){ +function tpl_img_getTag($tags,$alt='',$src=null){ // Init Exif Reader global $SRC; + + if(is_null($src)) $src = $SRC; + static $meta = null; - if(is_null($meta)) $meta = new JpegMeta($SRC); + if(is_null($meta)) $meta = new JpegMeta($src); if($meta === false) return $alt; $info = $meta->getField($tags); if($info == false) return $alt; @@ -975,15 +805,15 @@ function tpl_indexerWebBug(){ // configuration methods /** * tpl_getConf($id) - * + * * use this function to access template configuration variables */ function tpl_getConf($id){ global $conf; global $tpl_configloaded; - + $tpl = $conf['template']; - + if (!$tpl_configloaded){ $tconf = tpl_loadConfig(); if ($tconf !== false){ @@ -1004,16 +834,60 @@ function tpl_getConf($id){ * this function is automatically called by tpl_getConf() */ function tpl_loadConfig(){ - + $file = DOKU_TPLINC.'/conf/default.php'; $conf = array(); - + if (!@file_exists($file)) return false; - + // load default config file include($file); - + return $conf; } +/** + * prints the "main content" in the mediamanger popup + * + * Depending on the user's actions this may be a list of + * files in a namespace, the meta editing dialog or + * a message of referencing pages + * + * Only allowed in mediamanager.php + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function tpl_mediaContent(){ + global $IMG; + global $AUTH; + global $INUSE; + global $NS; + global $JUMPTO; + + ptln('<div id="media__content">'); + if($_REQUEST['edit']){ + media_metaform($IMG,$AUTH); + }elseif($INUSE){ + media_filesinuse($INUSE,$IMG); + }else{ + media_filelist($NS,$AUTH,$JUMPTO); + } + ptln('</div>'); +} + +/** + * prints the namespace tree in the mediamanger popup + * + * Only allowed in mediamanager.php + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function tpl_mediaTree(){ + global $NS; + + ptln('<div id="media__tree">'); + media_nstree($NS); + ptln('</div>'); +} + //Setup VIM: ex: et ts=2 enc=utf-8 : diff --git a/inc/toolbar.php b/inc/toolbar.php index fbe6b3609..072b6f420 100644 --- a/inc/toolbar.php +++ b/inc/toolbar.php @@ -145,9 +145,9 @@ function toolbar_JSdefines($varname){ 'type' => 'mediapopup', 'title' => $lang['qb_media'], 'icon' => 'image.png', - 'url' => DOKU_BASE.'lib/exe/media.php?ns=', + 'url' => DOKU_BASE.'lib/exe/mediamanager.php?ns=', 'name' => 'mediaselect', - 'options'=> 'width=600,height=320,left=70,top=50,scrollbars=yes,resizable=yes', + 'options'=> 'width=750,height=500,left=20,top=20,scrollbars=yes,resizable=yes', ), array( 'type' => 'picker', diff --git a/lib/exe/ajax.php b/lib/exe/ajax.php index 886e9829d..c9b93a4b8 100644 --- a/lib/exe/ajax.php +++ b/lib/exe/ajax.php @@ -97,6 +97,8 @@ function ajax_lock(){ /** * Delete a draft + * + * @author Andreas Gohr <andi@splitbrain.org> */ function ajax_draftdel(){ $id = cleanID($_POST['id']); @@ -109,5 +111,39 @@ function ajax_draftdel(){ @unlink($cname); } +/** + * Return subnamespaces for the Mediamanager + */ +function ajax_medians(){ + global $conf; + require_once(DOKU_INC.'inc/search.php'); + require_once(DOKU_INC.'inc/media.php'); + + // wanted namespace + $ns = cleanID($_POST['ns']); + $dir = utf8_encodeFN(str_replace(':','/',$ns)); + + $lvl = count(explode(':',$ns)); + + $data = array(); + search($data,$conf['mediadir'],'search_index',array(),$dir); + foreach($data as $item){ + $item['level'] = $lvl+1; + echo media_nstree_li($item); + echo media_nstree_item($item); + echo '</div></li>'; + } +} + +/** + * Return subnamespaces for the Mediamanager + */ +function ajax_medialist(){ + global $conf; + require_once(DOKU_INC.'inc/media.php'); + + media_filelist($_POST['ns']); +} + //Setup VIM: ex: et ts=2 enc=utf-8 : ?> diff --git a/lib/exe/js.php b/lib/exe/js.php index 05effd31a..583faa5ad 100644 --- a/lib/exe/js.php +++ b/lib/exe/js.php @@ -12,6 +12,7 @@ if(!defined('NL')) define('NL',"\n"); require_once(DOKU_INC.'inc/init.php'); require_once(DOKU_INC.'inc/pageutils.php'); require_once(DOKU_INC.'inc/io.php'); +require_once(DOKU_INC.'inc/JSON.php'); // Main (don't run when UNIT test) if(!defined('SIMPLE_TEST')){ @@ -39,17 +40,21 @@ function js_out(){ // Array of needed files $files = array( DOKU_INC.'lib/scripts/events.js', + DOKU_INC.'lib/scripts/cookie.js', DOKU_INC.'lib/scripts/script.js', DOKU_INC.'lib/scripts/tw-sack.js', DOKU_INC.'lib/scripts/ajax.js', DOKU_INC.'lib/scripts/domLib.js', DOKU_INC.'lib/scripts/domTT.js', ); - if($edit && $write){ - $files[] = DOKU_INC.'lib/scripts/edit.js'; - if($conf['spellchecker']){ - $files[] = DOKU_INC.'lib/scripts/spellcheck.js'; + if($edit){ + if($write){ + $files[] = DOKU_INC.'lib/scripts/edit.js'; + if($conf['spellchecker']){ + $files[] = DOKU_INC.'lib/scripts/spellcheck.js'; + } } + $files[] = DOKU_INC.'lib/scripts/media.js'; } $files[] = DOKU_TPLINC.'script.js'; @@ -70,11 +75,17 @@ function js_out(){ // start output buffering and build the script ob_start(); - // add some translation strings and global variables + // add some global variables + print "var DOKU_BASE = '".DOKU_BASE."';"; + + //FIXME: move thes into LANG print "var alertText = '".js_escape($lang['qb_alert'])."';"; print "var notSavedYet = '".js_escape($lang['notsavedyet'])."';"; print "var reallyDel = '".js_escape($lang['del_confirm'])."';"; - print "var DOKU_BASE = '".DOKU_BASE."';"; + + // load JS specific translations + $json = new JSON(); + echo 'LANG = '.$json->encode($lang['js']).";\n"; // load files foreach($files as $file){ diff --git a/lib/exe/media.php b/lib/exe/media.php deleted file mode 100644 index cc78d6b72..000000000 --- a/lib/exe/media.php +++ /dev/null @@ -1,183 +0,0 @@ -<?php - if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); - require_once(DOKU_INC.'inc/init.php'); - require_once(DOKU_INC.'inc/common.php'); - require_once(DOKU_INC.'inc/lang/en/lang.php'); - require_once(DOKU_INC.'inc/lang/'.$conf['lang'].'/lang.php'); - require_once(DOKU_INC.'inc/html.php'); - require_once(DOKU_INC.'inc/JpegMeta.php'); - require_once(DOKU_INC.'inc/search.php'); - require_once(DOKU_INC.'inc/template.php'); - require_once(DOKU_INC.'inc/auth.php'); - //close sesseion - session_write_close(); - - //get namespace to display (either direct or from deletion order) - if($_REQUEST['delete']){ - $DEL = cleanID($_REQUEST['delete']); - $NS = getNS($DEL); - }elseif($_REQUEST['edit']){ - $IMG = cleanID($_REQUEST['edit']); - $SRC = mediaFN($IMG); - $NS = getNS($IMG); - }else{ - $NS = $_REQUEST['ns']; - $NS = cleanID($NS); - } - - //check upload permissions - $AUTH = auth_quickaclcheck("$NS:*"); - if($AUTH >= AUTH_UPLOAD){ - $UPLOADOK = true; - //create the given namespace (just for beautification) - $mdir = $conf['mediadir'].'/'.utf8_encodeFN(str_replace(':','/',$NS)); - io_makeFileDir("$mdir/xxx"); - }else{ - $UPLOADOK = false; - } - - //handle deletion - $mediareferences = array(); - if($DEL && $AUTH >= AUTH_DELETE){ - if($conf['refcheck']){ - search($mediareferences,$conf['datadir'],'search_reference',array('query' => $DEL)); - } - if(!count($mediareferences)){ - media_delete($DEL); - }elseif(!$conf['refshow']){ - msg(str_replace('%s',noNS($DEL),$lang['mediainuse']),0); - } - } - - //handle metadatasaving - if($UPLOADOK && $SRC && $_REQUEST['save']){ - media_metasave($SRC,$_REQUEST['meta']); - } - - //handle upload - if($_FILES['upload']['tmp_name'] && $UPLOADOK){ - media_upload($NS,$AUTH); - } - - //start output and load template - header('Content-Type: text/html; charset=utf-8'); - if($conf['refshow'] && count($mediareferences)){ - include(template('mediaref.php')); - }elseif($IMG){ - include(template('mediaedit.php')); - }else{ - include(template('media.php')); - } - -/**********************************************/ - -/** - * Deletes mediafiles - Auth is not handled here! - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function media_delete($delid){ - global $lang; - - $file = mediaFN($delid); - if(@unlink($file)){ - msg(str_replace('%s',noNS($delid),$lang['deletesucc']),1); - io_sweepNS($delid,'mediadir'); - return true; - } - //something went wrong - msg(str_replace('%s',$file,$lang['deletefail']),-1); - return false; -} - -/** - * Handles Mediafile uploads - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function media_upload($NS,$AUTH){ - require_once(DOKU_INC.'inc/confutils.php'); - global $lang; - global $conf; - - // get file - $id = $_POST['id']; - $file = $_FILES['upload']; - // get id - if(empty($id)) $id = $file['name']; - $id = cleanID($NS.':'.$id); - // get filename - $fn = mediaFN($id); - - // get filetype regexp - $types = array_keys(getMimeTypes()); - $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types); - $regex = join('|',$types); - - // because a temp file was created already - if(preg_match('/\.('.$regex.')$/i',$fn)){ - //check for overwrite - if(@file_exists($fn) && (!$_POST['ow'] || $AUTH < AUTH_DELETE)){ - msg($lang['uploadexist'],0); - return false; - } - // prepare directory - io_makeFileDir($fn); - if(move_uploaded_file($file['tmp_name'], $fn)) { - // set the correct permission here - if($conf['fperm']) chmod($fn, $conf['fperm']); - msg($lang['uploadsucc'],1); - return true; - }else{ - msg($lang['uploadfail'],-1); - } - }else{ - msg($lang['uploadwrong'],-1); - } - return false; -} - -/** - * Userfunction for html_buildlist - * - * Prints available media namespaces - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function media_html_list_namespaces($item){ - $ret = ''; - $ret .= '<a href="'.DOKU_BASE.'lib/exe/media.php?ns='.idfilter($item['id']).'" class="idx_dir">'; - $pos = strrpos($item['id'], ':'); - $ret .= substr($item['id'], $pos > 0 ? $pos + 1 : 0); - $ret .= '</a>'; - return $ret; -} - -/** - * Saves image meta data - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -function media_metasave($src,$data){ - global $lang; - - $meta = new JpegMeta($src); - $meta->_parseAll(); - - foreach($data as $key => $val){ - $val=trim($val); - if(empty($val)){ - $meta->deleteField($key); - }else{ - $meta->setField($key,$val); - } - } - - if($meta->save()){ - msg($lang['metasaveok'],1); - }else{ - msg($lang['metasaveerr'],-1); - } -} - -?> diff --git a/lib/exe/mediamanager.php b/lib/exe/mediamanager.php new file mode 100644 index 000000000..8b645e434 --- /dev/null +++ b/lib/exe/mediamanager.php @@ -0,0 +1,53 @@ +<? + if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); + define('DOKU_MEDIAMANAGER',1); + require_once(DOKU_INC.'inc/init.php'); + require_once(DOKU_INC.'inc/lang/en/lang.php'); + require_once(DOKU_INC.'inc/lang/'.$conf['lang'].'/lang.php'); + require_once(DOKU_INC.'inc/media.php'); + require_once(DOKU_INC.'inc/common.php'); + require_once(DOKU_INC.'inc/search.php'); + require_once(DOKU_INC.'inc/template.php'); + require_once(DOKU_INC.'inc/auth.php'); + session_write_close(); //close session + + + // get namespace to display (either direct or from deletion order) + if($_REQUEST['delete']){ + $DEL = cleanID($_REQUEST['delete']); + $NS = getNS($DEL); + }elseif($_REQUEST['edit']){ + $IMG = cleanID($_REQUEST['edit']); + $NS = getNS($IMG); + }elseif($_REQUEST['img']){ + $IMG = cleanID($_REQUEST['img']); + $NS = getNS($IMG); + }else{ + $NS = $_REQUEST['ns']; + $NS = cleanID($NS); + } + + // check auth + $AUTH = auth_quickaclcheck("$NS:*"); + + // create the given namespace (just for beautification) + if($AUTH >= AUTH_UPLOAD) io_makeFileDir(mediaFN("$NS:xxx")); + + // handle upload + if($_FILES['upload']['tmp_name']){ + $JUMPTO = media_upload($NS,$AUTH); + } + + // handle meta saving + if($IMG && $_REQUEST['do']['save']){ + $JUMPTO = media_metasave($IMG,$AUTH,$_REQUEST['meta']); + } + + // handle deletion + if($DEL) { + $INUSE = media_delete($DEL,$AUTH); + } + + // finished - start output + header('Content-Type: text/html; charset=utf-8'); + include(template('mediamanager.php')); diff --git a/lib/images/fileicons/gif.png b/lib/images/fileicons/gif.png Binary files differnew file mode 100644 index 000000000..3c32f3016 --- /dev/null +++ b/lib/images/fileicons/gif.png diff --git a/lib/images/fileicons/jpeg.png b/lib/images/fileicons/jpeg.png Binary files differnew file mode 100644 index 000000000..3c32f3016 --- /dev/null +++ b/lib/images/fileicons/jpeg.png diff --git a/lib/images/fileicons/jpg.png b/lib/images/fileicons/jpg.png Binary files differnew file mode 100644 index 000000000..3c32f3016 --- /dev/null +++ b/lib/images/fileicons/jpg.png diff --git a/lib/images/fileicons/png.png b/lib/images/fileicons/png.png Binary files differnew file mode 100644 index 000000000..3c32f3016 --- /dev/null +++ b/lib/images/fileicons/png.png diff --git a/lib/images/fileicons/swf.png b/lib/images/fileicons/swf.png Binary files differnew file mode 100644 index 000000000..03bc8ab48 --- /dev/null +++ b/lib/images/fileicons/swf.png diff --git a/lib/images/fileicons/tar.png b/lib/images/fileicons/tar.png Binary files differnew file mode 100644 index 000000000..dfa8b586e --- /dev/null +++ b/lib/images/fileicons/tar.png diff --git a/lib/images/fileicons/txt.png b/lib/images/fileicons/txt.png Binary files differindex 2bd690434..758950fd2 100644 --- a/lib/images/fileicons/txt.png +++ b/lib/images/fileicons/txt.png diff --git a/lib/scripts/cookie.js b/lib/scripts/cookie.js new file mode 100644 index 000000000..c236eeb79 --- /dev/null +++ b/lib/scripts/cookie.js @@ -0,0 +1,111 @@ +/** + * Handles the cookie used by several JavaScript functions + * + * Only a single cookie is written and read. You may only save + * sime name-value pairs - no complex types! + * + * You should only use the getValue and setValue methods + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +DokuCookie = { + data: Array(), + name: 'DOKU_PREFS', + + /** + * Save a value to the cookie + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + setValue: function(key,val){ + DokuCookie.init(); + DokuCookie.data[key] = val; + + // prepare expire date + var now = new Date(); + DokuCookie.fixDate(now); + now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); //expire in a year + + //save the whole data array + var text = ''; + for(var key in DokuCookie.data){ + text += '#'+escape(key)+'#'+DokuCookie.data[key]; + } + DokuCookie.setCookie(DokuCookie.name,text.substr(1),now,DOKU_BASE); + }, + + /** + * Get a Value from the Cookie + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + getValue: function(key){ + DokuCookie.init(); + return DokuCookie.data[key]; + }, + + /** + * Loads the current set cookie + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + init: function(){ + if(DokuCookie.data.length) return; + var text = DokuCookie.getCookie(DokuCookie.name); + if(text){ + var parts = text.split('#'); + for(var i=0; i<parts.length; i+=2){ + DokuCookie.data[unescape(parts[i])] = unescape(parts[i+1]); + } + } + }, + + /** + * This sets a cookie by JavaScript + * + * @link http://www.webreference.com/js/column8/functions.html + */ + setCookie: function(name, value, expires, path, domain, secure) { + var curCookie = name + "=" + escape(value) + + ((expires) ? "; expires=" + expires.toGMTString() : "") + + ((path) ? "; path=" + path : "") + + ((domain) ? "; domain=" + domain : "") + + ((secure) ? "; secure" : ""); + document.cookie = curCookie; + }, + + /** + * This reads a cookie by JavaScript + * + * @link http://www.webreference.com/js/column8/functions.html + */ + getCookie: function(name) { + var dc = document.cookie; + var prefix = name + "="; + var begin = dc.indexOf("; " + prefix); + if (begin == -1) { + begin = dc.indexOf(prefix); + if (begin !== 0){ return null; } + } else { + begin += 2; + } + var end = document.cookie.indexOf(";", begin); + if (end == -1){ + end = dc.length; + } + return unescape(dc.substring(begin + prefix.length, end)); + }, + + /** + * This is needed for the cookie functions + * + * @link http://www.webreference.com/js/column8/functions.html + */ + fixDate: function(date) { + var base = new Date(0); + var skew = base.getTime(); + if (skew > 0){ + date.setTime(date.getTime() - skew); + } + } +}; diff --git a/lib/scripts/edit.js b/lib/scripts/edit.js index a8cf33594..48464017e 100644 --- a/lib/scripts/edit.js +++ b/lib/scripts/edit.js @@ -125,8 +125,9 @@ function showPicker(pickerid,btn){ * @author Andreas Gohr <andi@splitbrain.org> */ function initToolbar(tbid,edid,tb){ - if(!document.getElementById){ return; } - var toolbar = document.getElementById(tbid); + var toolbar = $(tbid); + if(!toolbar) return; + var cnt = tb.length; for(var i=0; i<cnt; i++){ // create new button diff --git a/lib/scripts/media.js b/lib/scripts/media.js new file mode 100644 index 000000000..f7c709907 --- /dev/null +++ b/lib/scripts/media.js @@ -0,0 +1,191 @@ +/** + * JavaScript functionalitiy for the media management popup + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +media = { + keepopen: false, + + /** + * Attach event handlers to all "folders" below the given element + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + treeattach: function(obj){ + if(!obj) return; + + var items = obj.getElementsByTagName('li'); + for(var i=0; i<items.length; i++){ + var elem = items[i]; + + // attach action to make the +/- clickable + var clicky = elem.getElementsByTagName('img')[0]; + clicky.style.cursor = 'pointer'; + addEvent(clicky,'click',function(event){ return media.toggle(event,this); }); + + // attach action load folder list via AJAX + var link = elem.getElementsByTagName('a')[0]; + link.style.cursor = 'pointer'; + addEvent(link,'click',function(event){ return media.list(event,this); }); + } + }, + + /** + * Attach the image selector action to all links below the given element + * also add the action to autofill the "upload as" field + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + selectorattach: function(obj){ + if(!obj) return; + + var items = getElementsByClass('select',obj,'a'); + for(var i=0; i<items.length; i++){ + var elem = items[i]; + elem.style.cursor = 'pointer'; + addEvent(elem,'click',function(event){ return media.select(event,this); }); + } + + var file = $('upload__file'); + if(!file) return; + addEvent(file,'change',media.suggest); + }, + + /** + * Creates a checkbox for keeping the popup on selection + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + attachkeepopen: function(obj){ + if(!obj) return; + + var cbox = document.createElement('input'); + cbox.type = 'checkbox'; + cbox.id = 'media__keepopen'; + if(DokuCookie.getValue('keepopen')){ + cbox.checked = true; + } + addEvent(cbox,'change',function(event){ return media.togglekeepopen(event,this); }); + + var clbl = document.createElement('label'); + clbl.htmlFor = 'media__keepopen'; + clbl.innerHTML = LANG['keepopen']; + + obj.appendChild(cbox); + obj.appendChild(clbl); + }, + + /** + * Toggles the keep open state + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + togglekeepopen: function(event,cb){ + if(cb.checked){ + DokuCookie.setValue('keepopen',1); + media.keepopen = true; + }else{ + DokuCookie.setValue('keepopen',''); + media.keepopen = false; + } + }, + + /** + * Insert the clicked image into the opener's textarea + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + select: function(event,link){ + var id = link.name.substr(2); + + if(!opener){ + alert(LANG['idtouse']+"\n:"+id); + return false; + } + opener.insertTags('wiki__text','{{'+id+'|','}}',''); + + if(!media.keepopen) window.close(); + return false; + }, + + /** + * list the content of a namespace using AJAX + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + list: function(event,link){ + // prepare an AJAX call to fetch the subtree + var ajax = new sack(DOKU_BASE + 'lib/exe/ajax.php'); + ajax.AjaxFailedAlert = ''; + ajax.encodeURIString = false; + if(ajax.failed) return true; + + cleanMsgArea(); + + var content = $('media__content'); + content.innerHTML = '<img src="'+DOKU_BASE+'lib/images/loading.gif" alt="..." class="load" />'; + + ajax.elementObj = content; + ajax.afterCompletion = function(){ media.selectorattach(content); }; + ajax.runAJAX(link.search.substr(1)+'&call=medialist'); + return false; + }, + + + /** + * Open or close a subtree using AJAX + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + toggle: function(event,clicky){ + var listitem = clicky.parentNode; + + // if already open, close by removing the sublist + var sublists = listitem.getElementsByTagName('ul'); + if(sublists.length){ + listitem.removeChild(sublists[0]); + clicky.src = DOKU_BASE+'lib/images/plus.gif'; + return false; + } + + // get the enclosed link (is always the first one) + var link = listitem.getElementsByTagName('a')[0]; + + // prepare an AJAX call to fetch the subtree + var ajax = new sack(DOKU_BASE + 'lib/exe/ajax.php'); + ajax.AjaxFailedAlert = ''; + ajax.encodeURIString = false; + if(ajax.failed) return true; + + //prepare the new ul + var ul = document.createElement('ul'); + //fixme add classname here + listitem.appendChild(ul); + ajax.elementObj = ul; + ajax.afterCompletion = function(){ media.treeattach(ul); }; + ajax.runAJAX(link.search.substr(1)+'&call=medians'); + clicky.src = DOKU_BASE+'lib/images/minus.gif'; + return false; + }, + + /** + * Prefills the wikiname. + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + suggest: function(){ + var file = $('upload__file'); + var name = $('upload__name'); + if(!file || !name) return; + + var text = file.value; + text = text.substr(text.lastIndexOf('/')+1); + text = text.substr(text.lastIndexOf('\\')+1); + name.value = text; + } + +} + +addInitEvent(function(){media.treeattach($('media__tree'));}); +addInitEvent(function(){media.selectorattach($('media__content'));}); +addInitEvent(function(){media.attachkeepopen($('media__opts'));}); diff --git a/lib/scripts/script.js b/lib/scripts/script.js index bd7af48da..df12a29fb 100644 --- a/lib/scripts/script.js +++ b/lib/scripts/script.js @@ -78,6 +78,30 @@ function isset(varname){ } /** + * Select elements by their class name + * + * @author Dustin Diaz <dustin [at] dustindiaz [dot] com> + * @link http://www.dustindiaz.com/getelementsbyclass/ + */ +function getElementsByClass(searchClass,node,tag) { + var classElements = new Array(); + if ( node == null ) + node = document; + if ( tag == null ) + tag = '*'; + var els = node.getElementsByTagName(tag); + var elsLen = els.length; + var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)"); + for (i = 0, j = 0; i < elsLen; i++) { + if ( pattern.test(els[i].className) ) { + classElements[j] = els[i]; + j++; + } + } + return classElements; +} + +/** * Get the X offset of the top left corner of the given object * * @link http://www.quirksmode.org/index.html?/js/findpos.html @@ -186,31 +210,6 @@ function hideLoadBar(id){ if(obj) obj.style.display="none"; } -/* - * Insert the selected filename and close the window - * - * @see http://www.alexking.org/index.php?content=software/javascript/content.php - */ -function mediaSelect(file){ - opener.insertTags('wiki__text','{{'+file+'|','}}',''); - window.close(); -} - -/** - * For the upload Dialog. Prefills the wikiname. - */ -function suggestWikiname(){ - var form = $('dw__upload'); - if(!form) return; - - var file = form.elements.upload.value; - - file = file.substr(file.lastIndexOf('/')+1); - file = file.substr(file.lastIndexOf('\\')+1); - - form.elements.id.value = file; -} - /** * Adds the toggle switch to the TOC */ @@ -255,55 +254,6 @@ function toggleToc() { } /* - * This sets a cookie by JavaScript - * - * @see http://www.webreference.com/js/column8/functions.html - */ -function setCookie(name, value, expires, path, domain, secure) { - var curCookie = name + "=" + escape(value) + - ((expires) ? "; expires=" + expires.toGMTString() : "") + - ((path) ? "; path=" + path : "") + - ((domain) ? "; domain=" + domain : "") + - ((secure) ? "; secure" : ""); - document.cookie = curCookie; -} - -/* - * This reads a cookie by JavaScript - * - * @see http://www.webreference.com/js/column8/functions.html - */ -function getCookie(name) { - var dc = document.cookie; - var prefix = name + "="; - var begin = dc.indexOf("; " + prefix); - if (begin == -1) { - begin = dc.indexOf(prefix); - if (begin !== 0){ return null; } - } else { - begin += 2; - } - var end = document.cookie.indexOf(";", begin); - if (end == -1){ - end = dc.length; - } - return unescape(dc.substring(begin + prefix.length, end)); -} - -/* - * This is needed for the cookie functions - * - * @see http://www.webreference.com/js/column8/functions.html - */ -function fixDate(date) { - var base = new Date(0); - var skew = base.getTime(); - if (skew > 0){ - date.setTime(date.getTime() - skew); - } -} - -/* * This enables/disables checkboxes for acl-administration * * @author Frank Schubert <frank@schokilade.de> @@ -374,24 +324,25 @@ function fnt(id, e, evt) { * Add the edit window size controls */ function initSizeCtl(ctlid,edid){ - if(!document.getElementById){ return; } + if(!document.getElementById){ return; } var ctl = $(ctlid); var textarea = $(edid); + if(!ctl || !textarea) return; - var hgt = getCookie('DokuWikisizeCtl'); - if(hgt === null || hgt === ''){ - textarea.style.height = '300px'; - }else{ + var hgt = DokuCookie.getValue('sizeCtl'); + if(hgt){ textarea.style.height = hgt; + }else{ + textarea.style.height = '300px'; } var l = document.createElement('img'); var s = document.createElement('img'); l.src = DOKU_BASE+'lib/images/larger.gif'; s.src = DOKU_BASE+'lib/images/smaller.gif'; - addEvent(l,'click',function(){sizeCtl(edid,100);}); - addEvent(s,'click',function(){sizeCtl(edid,-100);}); + addEvent(l,'click',function(){sizeCtl(edid,100);}); + addEvent(s,'click',function(){sizeCtl(edid,-100);}); ctl.appendChild(l); ctl.appendChild(s); } @@ -405,10 +356,7 @@ function sizeCtl(edid,val){ height += val; textarea.style.height = height+'px'; - var now = new Date(); - fixDate(now); - now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); //expire in a year - setCookie('DokuWikisizeCtl',textarea.style.height,now); + DokuCookie.setValue('sizeCtl',textarea.style.height); } /** @@ -432,3 +380,15 @@ function scrollToMarker(){ var obj = $('scroll__here'); if(obj) obj.scrollIntoView(); } + +/** + * Remove messages + */ +function cleanMsgArea(){ + var elems = getElementsByClass('(success|info|error)',document,'div'); + if(elems){ + for(var i=0; i<elems.length; i++){ + elems[i].style.display = 'none'; + } + } +} diff --git a/lib/scripts/tw-sack.js b/lib/scripts/tw-sack.js index 0c7e81bf1..cfcbe0ea9 100644 --- a/lib/scripts/tw-sack.js +++ b/lib/scripts/tw-sack.js @@ -15,6 +15,7 @@ function sack(file){ this.onLoaded = function() { }; this.onInteractive = function() { }; this.onCompletion = function() { }; + this.afterCompletion = function() { }; this.createAJAX = function() { try { @@ -30,10 +31,10 @@ function sack(file){ this.xmlhttp = new XMLHttpRequest(); } if (!this.xmlhttp){ - this.failed = true; + this.failed = true; } }; - + this.setVar = function(name, value){ if (this.URLString.length < 3){ this.URLString = name + "=" + value; @@ -41,12 +42,12 @@ function sack(file){ this.URLString += "&" + name + "=" + value; } }; - + this.encVar = function(name, value){ var varString = encodeURIComponent(name) + "=" + encodeURIComponent(value); return varString; }; - + this.encodeURLString = function(string){ varArray = string.split('&'); for (i = 0; i < varArray.length; i++){ @@ -58,25 +59,25 @@ function sack(file){ } return varArray.join('&'); }; - + this.runResponse = function(){ eval(this.response); }; - + this.runAJAX = function(urlstring){ this.responseStatus = new Array(2); - if(this.failed && this.AjaxFailedAlert){ - alert(this.AjaxFailedAlert); + if(this.failed && this.AjaxFailedAlert){ + alert(this.AjaxFailedAlert); } else { - if (urlstring){ + if (urlstring){ if (this.URLString.length){ - this.URLString = this.URLString + "&" + urlstring; + this.URLString = this.URLString + "&" + urlstring; } else { - this.URLString = urlstring; + this.URLString = urlstring; } } if (this.encodeURIString){ - var timeval = new Date().getTime(); + var timeval = new Date().getTime(); this.URLString = this.encodeURLString(this.URLString); this.setVar("rndval", timeval); } @@ -93,7 +94,7 @@ function sack(file){ try { this.xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8'); } catch (e) {} - } + } this.xmlhttp.onreadystatechange = function() { switch (self.xmlhttp.readyState){ @@ -122,6 +123,7 @@ function sack(file){ self.elementObj.innerHTML = self.response; } } + self.afterCompletion(); self.URLString = ""; break; } diff --git a/lib/tpl/default/media.css b/lib/tpl/default/media.css new file mode 100644 index 000000000..96aac41ce --- /dev/null +++ b/lib/tpl/default/media.css @@ -0,0 +1,115 @@ +/** + * The CSS in here cotrols the appearance of the media manager + */ + +#media__left { + width: 30%; + float: left; + border-right: solid 1px __dark__; +} + +#media__right { + width: 68%; + float: left; + border-left: solid 1px __dark__; + margin-left: -1px; +} + +#media__tree img { + float:left; + padding: 0.5em 0.3em 0 0; +} + +#media__tree ul { + list-style-type: none; + list-style-image: none; +} + +#media__tree li { + clear: left; + list-style-type: none; + list-style-image: none; +} + +/* --- file list --- */ + +#media__content img.load { + margin: 1em auto; +} + +#media__content #scroll__here { + border: 1px dashed __dark__; +} + +#media__content .odd { + background-color: __lighter__; + padding: 0.4em; +} + +#media__content .even { + padding: 0.4em; +} + +#media__content a.mediafile { + margin-right: 1.5em; + font-weight: bold; +} + +#media__content div.detail { + padding: 0.3em 0 0.3em 2em; +} + +#media__content div.detail div.thumb { + float: left; + width: 130px; + text-align: center; + margin-right: 0.4em; +} + + +#media__content img.btn { + vertical-align: text-bottom; +} + +/* --- upload form --- */ + +#media__content form.upload { + display: block; + border-bottom: solid 1px __dark__; + padding: 0 0 1em 2em; +} + +/* --- meta edit form --- */ + +#media__content form.meta { + display: block; + padding: 0 0 1em 0; +} + +#media__content form.meta label { + display: block; + width: 20%; + float: left; + text-align: right; + font-weight: bold; + padding-right: 1em; +} + +#media__content form.meta .edit { + float: left; + width: 75%; +} + +#media__content form.meta textarea.edit { + height: 6em; +} + +#media__content form.meta div.metafield { + clear: left; +} + +#media__content form.meta div.buttons { + clear: left; + margin-left: 20%; + padding-left: 1em; +} diff --git a/lib/tpl/default/media.php b/lib/tpl/default/media.php deleted file mode 100644 index c2c632e46..000000000 --- a/lib/tpl/default/media.php +++ /dev/null @@ -1,54 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<?php -/** - * DokuWiki Default Template - * - * This is the template for the media selection popup. - * - * You should leave the doctype at the very top - It should - * always be the very first line of a document. - * - * @link http://wiki.splitbrain.org/wiki:tpl:templates - * @author Andreas Gohr <andi@splitbrain.org> - */ -?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $conf['lang']?>" lang="<?php echo $conf['lang']?>" dir="ltr"> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title><?php echo hsc($lang['mediaselect'])?> [<?php echo hsc($conf['title'])?>]</title> - - <?php tpl_metaheaders()?> - - <link rel="shortcut icon" href="<?php echo DOKU_TPL?>images/favicon.ico" /> - -</head> - -<body> -<div class="dokuwiki"> - <?php html_msgarea()?> - - <h1><?php echo hsc($lang['mediaselect'])?> <code><?php echo hsc($NS)?></code></h1> - - <div class="mediaselect"> - - <div class="mediaselect-left"> - <strong><a href="<?php echo DOKU_BASE?>lib/exe/media.php?ns="><?php echo hsc($lang['namespaces'])?></a></strong> - - <?php tpl_medianamespaces()?> - </div> - - <div class="mediaselect-right"> - <?php tpl_mediafilelist()?> - - <div class="uploadform"> - <?php tpl_mediauploadform()?> - </div> - </div> - - </div> - -</div> -</body> -</html> - diff --git a/lib/tpl/default/mediaedit.php b/lib/tpl/default/mediaedit.php deleted file mode 100644 index 79175e009..000000000 --- a/lib/tpl/default/mediaedit.php +++ /dev/null @@ -1,87 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<?php -/** - * DokuWiki Default Template - * - * This is the template for editing image meta data. - * It is displayed in the media popup. - * - * You should leave the doctype at the very top - It should - * always be the very first line of a document. - * - * @link http://wiki.splitbrain.org/wiki:tpl:templates - * @author Andreas Gohr <andi@splitbrain.org> - */ -?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $conf['lang']?>" lang="<?php echo $conf['lang']?>" dir="ltr"> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title><?php echo hsc($lang['mediaselect'])?> [<?php echo hsc($conf['title'])?>]</title> - - <?php tpl_metaheaders()?> - - <link rel="shortcut icon" href="<?php echo DOKU_TPL?>images/favicon.ico" /> -</head> - -<body> -<div class="dokuwiki"> - <?php html_msgarea()?> - - <h1><?php echo hsc($lang['metaedit'])?> <code><?php echo hsc(noNS($IMG))?></code></h1> - - <div class="mediaedit"> - <?php/* everything in meta array is tried to save and read */?> - - <div class="data"> - <form action="<?php echo DOKU_BASE?>lib/exe/media.php" accept-charset="utf-8" method="post"> - <input type="hidden" name="edit" value="<?php echo hsc($IMG)?>" /> - <input type="hidden" name="save" value="1" /> - - <label class="block" for="img__title"><?php echo $lang['img_title']?></label> - <input type="text" name="meta[Iptc.Headline]" id="img__title" class="edit" - value="<?php echo hsc(tpl_img_getTag('IPTC.Headline'))?>" /><br /> - - <label class="block" for="img__caption"><?php echo $lang['img_caption']?></label> - <textarea name="meta[Iptc.Caption]" id="img__caption" class="edit" rows="5"><?php - echo hsc(tpl_img_getTag(array('IPTC.Caption', - 'EXIF.UserComment', - 'EXIF.TIFFImageDescription', - 'EXIF.TIFFUserComment'))); - ?></textarea><br /> - - <label class="block" for="img__artist"><?php echo $lang['img_artist']?></label> - <input type="text" name="meta[Iptc.Byline]" id="img__artist" class="edit" - value="<?php echo hsc(tpl_img_getTag(array('Iptc.Byline', - 'Exif.TIFFArtist', - 'Exif.Artist', - 'Iptc.Credit')))?>" /><br /> - - <label class="block" for="img__copy"><?php echo $lang['img_copyr']?></label> - <input type="text" name="meta[Iptc.CopyrightNotice]" id="img__copy" class="edit" - value="<?php echo hsc(tpl_img_getTag(array('Iptc.CopyrightNotice','Exif.TIFFCopyright','Exif.Copyright')))?>" /><br /> - - - <label class="block" for="img__keywords"><?php echo $lang['img_keywords']?></label> - <textarea name="meta[Iptc.Keywords]" id="img__keywords" class="edit"><?php - echo hsc(tpl_img_getTag(array('IPTC.Keywords', - 'EXIF.Category'))); - ?></textarea><br /> - - - <input type="submit" value="<?php echo $lang['btn_save']?>" title="ALT+S" - accesskey="s" class="button" /> - - </form> - </div> - - - <div class="footer"> - <hr /> - <?php tpl_button('backtomedia')?> - </div> - </div> - -</div> -</body> -</html> diff --git a/lib/tpl/default/mediaref.php b/lib/tpl/default/mediamanager.php index 06af5764c..10b31ba63 100644 --- a/lib/tpl/default/mediaref.php +++ b/lib/tpl/default/mediamanager.php @@ -4,8 +4,7 @@ /** * DokuWiki Default Template * - * This is the template for displaying references to a media file. - * It is displayed in the media popup. + * This is the template for the media manager popup * * You should leave the doctype at the very top - It should * always be the very first line of a document. @@ -18,33 +17,25 @@ <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title><?php echo hsc($lang['mediaselect'])?> [<?php echo hsc($conf['title'])?>]</title> - <?php tpl_metaheaders()?> - <link rel="shortcut icon" href="<?php echo DOKU_TPL?>images/favicon.ico" /> - </head> <body> -<div class="dokuwiki"> - <?php html_msgarea()?> +<div id="media__manager" class="dokuwiki"> + <?html_msgarea()?> + <div id="media__left"> + <h1><?php echo hsc($lang['mediaselect'])?></h1> - <h1><?php echo hsc($lang['reference'])?> <code><?php echo hsc(noNS($DEL))?></code></h1> + <?php tpl_mediaTree() ?> - <div class="mediaref"> - <div class="mediaref_head"> - <p><?php echo hsc($lang['ref_inuse'])?></p> + <?php /* keep the id! additional elements are inserted via JS here */?> + <div id="media__opts"></div> </div> - <?php tpl_showreferences($mediareferences)?> - - <div class="mediaref_footer"> - <hr /> - <?php tpl_button('backtomedia')?> + <div id="media__right"> + <?php tpl_mediaContent() ?> </div> - </div> - </div> </body> </html> - diff --git a/lib/tpl/default/style.ini b/lib/tpl/default/style.ini index e6452ad90..bb4cd2305 100644 --- a/lib/tpl/default/style.ini +++ b/lib/tpl/default/style.ini @@ -9,6 +9,9 @@ layout.css = screen design.css = screen style.css = screen + +media.css = screen + rtl.css = rtl print.css = print |