added file op:rename, cp, mv, upload, create
@@ -5,7 +5,7 @@ a:hover { color: #ff9933; text-decoration: underline; }
|
||||
img { border:none; }
|
||||
h2 { font-size:18px; color:#808; }
|
||||
h3 { font-size:15px; color:#808; font-weight:normal; margin:12px 0 2px; }
|
||||
h4 { font-size:14px; color:#000; margin:2px 0 0; }
|
||||
h4 { font-size:14px; color:#000; font-weight:normal; margin:2px 0 0; }
|
||||
ol { padding-left:2em; }
|
||||
/* input button */
|
||||
textarea { border: 1px solid #80B0B0; }
|
||||
@@ -127,7 +127,6 @@ table img {
|
||||
}
|
||||
#left-panel { float:right; width:220px; }
|
||||
#right-panel { float:left; width:680px; }
|
||||
#main-panel { width:100%; }
|
||||
.side {
|
||||
color:#333;
|
||||
width:220px;
|
||||
@@ -178,8 +177,7 @@ table img {
|
||||
/* footer */
|
||||
#footer a { color:#333; text-decoration:none; }
|
||||
/* main */
|
||||
h2.subject {}
|
||||
p.path, p.access-notice { margin: 12px 0 6px 0; }
|
||||
.path, .access-notice { margin: 12px 0 6px 0; }
|
||||
.with-bg li {
|
||||
padding-left:10px;
|
||||
line-height:20px;
|
||||
@@ -190,7 +188,8 @@ p.path, p.access-notice { margin: 12px 0 6px 0; }
|
||||
width:321px;
|
||||
margin:45px 0 0 300px;
|
||||
}
|
||||
.avatar-op h2 {
|
||||
.avatar-op h2,
|
||||
.upload-file-panel h3 {
|
||||
padding:2px 5px;
|
||||
background:#E9F1F4;
|
||||
border-radius:3px;
|
||||
@@ -289,6 +288,10 @@ p.path, p.access-notice { margin: 12px 0 6px 0; }
|
||||
color:#e83;
|
||||
font-weight:normal;
|
||||
}
|
||||
#simplemodal-container .jstree a {
|
||||
color:#000;
|
||||
font-weight:normal;
|
||||
}
|
||||
#simplemodal-container h3 {
|
||||
margin:0 0 4px;
|
||||
}
|
||||
@@ -339,6 +342,45 @@ p.path, p.access-notice { margin: 12px 0 6px 0; }
|
||||
}
|
||||
|
||||
/*repo page*/
|
||||
.repo-top-bar {
|
||||
margin-bottom:-5px;
|
||||
}
|
||||
.repo-op {
|
||||
margin-top:12px;
|
||||
}
|
||||
.repo-op a {
|
||||
margin-left:8px;
|
||||
background:#fff scroll no-repeat left 50%;
|
||||
}
|
||||
.repo-op .upload-file {
|
||||
padding-left:17px;
|
||||
background-image:url('../img/upload.jpg');
|
||||
}
|
||||
#add-new-dir {
|
||||
padding-left:20px;
|
||||
background-image:url('../img/add-new-folder.jpg');
|
||||
}
|
||||
.more-op {
|
||||
cursor:pointer;
|
||||
}
|
||||
.op-list {
|
||||
position:absolute;
|
||||
background:#fff;
|
||||
padding:6px 1px;
|
||||
border:1px solid #eee;
|
||||
border-radius:5px;
|
||||
-moz-border-radius:5px;
|
||||
z-index:10;
|
||||
}
|
||||
.op-list li {
|
||||
padding:0 12px;
|
||||
}
|
||||
.op-target {
|
||||
color:#c39;
|
||||
}
|
||||
#rename-form .new-name {
|
||||
margin-top:8px;
|
||||
}
|
||||
.latest-commit .more:hover {
|
||||
text-decoration:none;
|
||||
}
|
||||
@@ -360,7 +402,7 @@ p.path, p.access-notice { margin: 12px 0 6px 0; }
|
||||
color:#666;
|
||||
margin-left:2px;
|
||||
}
|
||||
h2.repo-history {
|
||||
.repo-history {
|
||||
font-weight:normal;
|
||||
font-size:16px;
|
||||
margin: 4px 0 12px 0;
|
||||
@@ -381,6 +423,10 @@ h2.repo-history {
|
||||
#ls-ch ul {
|
||||
padding:0 0 6px 0;
|
||||
}
|
||||
/*file mv*/
|
||||
#dirs {
|
||||
margin-top:8px;
|
||||
}
|
||||
/*repo-share-form*/
|
||||
#email_or_group,
|
||||
#share-link,
|
||||
@@ -509,3 +555,24 @@ h2.repo-history {
|
||||
.reply-form .text-input {
|
||||
width:82%;
|
||||
}
|
||||
|
||||
/*file upload*/
|
||||
.upload-file-panel {
|
||||
width:400px;
|
||||
margin:70px auto 0;
|
||||
}
|
||||
#upload-file-form {
|
||||
margin-top:10px;
|
||||
}
|
||||
#task-progress-bar {/*for progress container*/
|
||||
width:95%;
|
||||
height:1em;
|
||||
}
|
||||
#task-progress-bar .ui-progressbar-value {/*for progress*/
|
||||
background:#e83;
|
||||
margin:0;
|
||||
}
|
||||
#task-progress-bar,
|
||||
#upload-cancel {
|
||||
margin-top:8px;
|
||||
}
|
||||
|
BIN
media/img/add-new-folder.jpg
Normal file
After Width: | Height: | Size: 515 B |
BIN
media/img/more-option.png
Normal file
After Width: | Height: | Size: 346 B |
BIN
media/img/upload-20.png
Normal file
After Width: | Height: | Size: 807 B |
BIN
media/img/upload.jpg
Normal file
After Width: | Height: | Size: 439 B |
@@ -73,3 +73,36 @@ function addAutocomplete(ele_id, container_id, data) {
|
||||
});
|
||||
}
|
||||
|
||||
function filesizeformat(bytes, precision)
|
||||
{
|
||||
var kilobyte = 1024;
|
||||
var megabyte = kilobyte * 1024;
|
||||
var gigabyte = megabyte * 1024;
|
||||
var terabyte = gigabyte * 1024;
|
||||
|
||||
if (precision === undefined)
|
||||
precision = 0;
|
||||
|
||||
if ((bytes >= 0) && (bytes < kilobyte)) {
|
||||
return bytes + ' B';
|
||||
|
||||
} else if ((bytes >= kilobyte) && (bytes < megabyte)) {
|
||||
return (bytes / kilobyte).toFixed(precision) + ' KB';
|
||||
|
||||
} else if ((bytes >= megabyte) && (bytes < gigabyte)) {
|
||||
return (bytes / megabyte).toFixed(precision) + ' MB';
|
||||
|
||||
} else if ((bytes >= gigabyte) && (bytes < terabyte)) {
|
||||
return (bytes / gigabyte).toFixed(precision) + ' GB';
|
||||
|
||||
} else if (bytes >= terabyte) {
|
||||
return (bytes / terabyte).toFixed(precision) + ' TB';
|
||||
|
||||
} else {
|
||||
return bytes + ' B';
|
||||
}
|
||||
}
|
||||
|
||||
function e(str) {
|
||||
return encodeURIComponent(str);
|
||||
}
|
||||
|
96
media/jstree_pre1.0_stable/_lib/jquery.cookie.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Cookie plugin
|
||||
*
|
||||
* Copyright (c) 2006 Klaus Hartl (stilbuero.de)
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a cookie with the given name and value and other optional parameters.
|
||||
*
|
||||
* @example $.cookie('the_cookie', 'the_value');
|
||||
* @desc Set the value of a cookie.
|
||||
* @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
|
||||
* @desc Create a cookie with all available options.
|
||||
* @example $.cookie('the_cookie', 'the_value');
|
||||
* @desc Create a session cookie.
|
||||
* @example $.cookie('the_cookie', null);
|
||||
* @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
|
||||
* used when the cookie was set.
|
||||
*
|
||||
* @param String name The name of the cookie.
|
||||
* @param String value The value of the cookie.
|
||||
* @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
|
||||
* @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
|
||||
* If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
|
||||
* If set to null or omitted, the cookie will be a session cookie and will not be retained
|
||||
* when the the browser exits.
|
||||
* @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
|
||||
* @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
|
||||
* @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
|
||||
* require a secure protocol (like HTTPS).
|
||||
* @type undefined
|
||||
*
|
||||
* @name $.cookie
|
||||
* @cat Plugins/Cookie
|
||||
* @author Klaus Hartl/klaus.hartl@stilbuero.de
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the value of a cookie with the given name.
|
||||
*
|
||||
* @example $.cookie('the_cookie');
|
||||
* @desc Get the value of a cookie.
|
||||
*
|
||||
* @param String name The name of the cookie.
|
||||
* @return The value of the cookie.
|
||||
* @type String
|
||||
*
|
||||
* @name $.cookie
|
||||
* @cat Plugins/Cookie
|
||||
* @author Klaus Hartl/klaus.hartl@stilbuero.de
|
||||
*/
|
||||
jQuery.cookie = function(name, value, options) {
|
||||
if (typeof value != 'undefined') { // name and value given, set cookie
|
||||
options = options || {};
|
||||
if (value === null) {
|
||||
value = '';
|
||||
options.expires = -1;
|
||||
}
|
||||
var expires = '';
|
||||
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
|
||||
var date;
|
||||
if (typeof options.expires == 'number') {
|
||||
date = new Date();
|
||||
date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
|
||||
} else {
|
||||
date = options.expires;
|
||||
}
|
||||
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
|
||||
}
|
||||
// CAUTION: Needed to parenthesize options.path and options.domain
|
||||
// in the following expressions, otherwise they evaluate to undefined
|
||||
// in the packed version for some reason...
|
||||
var path = options.path ? '; path=' + (options.path) : '';
|
||||
var domain = options.domain ? '; domain=' + (options.domain) : '';
|
||||
var secure = options.secure ? '; secure' : '';
|
||||
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
|
||||
} else { // only name given, get cookie
|
||||
var cookieValue = null;
|
||||
if (document.cookie && document.cookie != '') {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = jQuery.trim(cookies[i]);
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
};
|
99
media/jstree_pre1.0_stable/_lib/jquery.hotkeys.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* jQuery Hotkeys Plugin
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Based upon the plugin by Tzury Bar Yochay:
|
||||
* http://github.com/tzuryby/hotkeys
|
||||
*
|
||||
* Original idea by:
|
||||
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
|
||||
*/
|
||||
|
||||
(function(jQuery){
|
||||
|
||||
jQuery.hotkeys = {
|
||||
version: "0.8",
|
||||
|
||||
specialKeys: {
|
||||
8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
|
||||
20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
|
||||
37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
|
||||
96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
|
||||
104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
|
||||
112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
|
||||
120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
|
||||
},
|
||||
|
||||
shiftNums: {
|
||||
"`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
|
||||
"8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
|
||||
".": ">", "/": "?", "\\": "|"
|
||||
}
|
||||
};
|
||||
|
||||
function keyHandler( handleObj ) {
|
||||
// Only care when a possible input has been specified
|
||||
if ( typeof handleObj.data !== "string" ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var origHandler = handleObj.handler,
|
||||
keys = handleObj.data.toLowerCase().split(" ");
|
||||
|
||||
handleObj.handler = function( event ) {
|
||||
// Don't fire in text-accepting inputs that we didn't directly bind to
|
||||
if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
|
||||
event.target.type === "text") ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keypress represents characters, not special keys
|
||||
var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
|
||||
character = String.fromCharCode( event.which ).toLowerCase(),
|
||||
key, modif = "", possible = {};
|
||||
|
||||
// check combinations (alt|ctrl|shift+anything)
|
||||
if ( event.altKey && special !== "alt" ) {
|
||||
modif += "alt+";
|
||||
}
|
||||
|
||||
if ( event.ctrlKey && special !== "ctrl" ) {
|
||||
modif += "ctrl+";
|
||||
}
|
||||
|
||||
// TODO: Need to make sure this works consistently across platforms
|
||||
if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
|
||||
modif += "meta+";
|
||||
}
|
||||
|
||||
if ( event.shiftKey && special !== "shift" ) {
|
||||
modif += "shift+";
|
||||
}
|
||||
|
||||
if ( special ) {
|
||||
possible[ modif + special ] = true;
|
||||
|
||||
} else {
|
||||
possible[ modif + character ] = true;
|
||||
possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
|
||||
|
||||
// "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
|
||||
if ( modif === "shift+" ) {
|
||||
possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ( var i = 0, l = keys.length; i < l; i++ ) {
|
||||
if ( possible[ keys[i] ] ) {
|
||||
return origHandler.apply( this, arguments );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
jQuery.each([ "keydown", "keyup", "keypress" ], function() {
|
||||
jQuery.event.special[ this ] = { add: keyHandler };
|
||||
});
|
||||
|
||||
})( jQuery );
|
4544
media/jstree_pre1.0_stable/jquery.jstree.js
Normal file
BIN
media/jstree_pre1.0_stable/themes/apple/bg.jpg
Normal file
After Width: | Height: | Size: 331 B |
BIN
media/jstree_pre1.0_stable/themes/apple/d.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
media/jstree_pre1.0_stable/themes/apple/dot_for_ie.gif
Normal file
After Width: | Height: | Size: 43 B |
61
media/jstree_pre1.0_stable/themes/apple/style.css
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* jsTree apple theme 1.0
|
||||
* Supported features: dots/no-dots, icons/no-icons, focused, loading
|
||||
* Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
|
||||
*/
|
||||
|
||||
.jstree-apple > ul { background:url("bg.jpg") left top repeat; }
|
||||
.jstree-apple li,
|
||||
.jstree-apple ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
|
||||
.jstree-apple li { background-position:-90px 0; background-repeat:repeat-y; }
|
||||
.jstree-apple li.jstree-last { background:transparent; }
|
||||
.jstree-apple .jstree-open > ins { background-position:-72px 0; }
|
||||
.jstree-apple .jstree-closed > ins { background-position:-54px 0; }
|
||||
.jstree-apple .jstree-leaf > ins { background-position:-36px 0; }
|
||||
|
||||
.jstree-apple a { border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px; text-shadow:1px 1px 1px white; }
|
||||
.jstree-apple .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 3px 0 1px; text-shadow:1px 1px 1px silver; }
|
||||
.jstree-apple .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 3px 0 1px; }
|
||||
.jstree-apple a .jstree-icon { background-position:-56px -20px; }
|
||||
.jstree-apple a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
|
||||
|
||||
.jstree-apple.jstree-focused { background:white; }
|
||||
|
||||
.jstree-apple .jstree-no-dots li,
|
||||
.jstree-apple .jstree-no-dots .jstree-leaf > ins { background:transparent; }
|
||||
.jstree-apple .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
|
||||
.jstree-apple .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
|
||||
|
||||
.jstree-apple .jstree-no-icons a .jstree-icon { display:none; }
|
||||
|
||||
.jstree-apple .jstree-search { font-style:italic; }
|
||||
|
||||
.jstree-apple .jstree-no-icons .jstree-checkbox { display:inline-block; }
|
||||
.jstree-apple .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
|
||||
.jstree-apple .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
|
||||
.jstree-apple .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
|
||||
.jstree-apple .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
|
||||
.jstree-apple .jstree-checked > a > .checkbox:hover { background-position:-38px -37px; }
|
||||
.jstree-apple .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
|
||||
.jstree-apple .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
|
||||
|
||||
#vakata-dragged.jstree-apple ins { background:transparent !important; }
|
||||
#vakata-dragged.jstree-apple .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
|
||||
#vakata-dragged.jstree-apple .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
|
||||
#jstree-marker.jstree-apple { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }
|
||||
|
||||
.jstree-apple a.jstree-search { color:aqua; }
|
||||
.jstree-apple .jstree-locked a { color:silver; cursor:default; }
|
||||
|
||||
#vakata-contextmenu.jstree-apple-context,
|
||||
#vakata-contextmenu.jstree-apple-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
|
||||
#vakata-contextmenu.jstree-apple-context li { }
|
||||
#vakata-contextmenu.jstree-apple-context a { color:black; }
|
||||
#vakata-contextmenu.jstree-apple-context a:hover,
|
||||
#vakata-contextmenu.jstree-apple-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
|
||||
#vakata-contextmenu.jstree-apple-context li.jstree-contextmenu-disabled a,
|
||||
#vakata-contextmenu.jstree-apple-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
|
||||
#vakata-contextmenu.jstree-apple-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
|
||||
#vakata-contextmenu.jstree-apple-context li ul { margin-left:-4px; }
|
||||
|
||||
/* TODO: IE6 support - the `>` selectors */
|
BIN
media/jstree_pre1.0_stable/themes/apple/throbber.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
media/jstree_pre1.0_stable/themes/classic/d.gif
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
media/jstree_pre1.0_stable/themes/classic/d.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
media/jstree_pre1.0_stable/themes/classic/dot_for_ie.gif
Normal file
After Width: | Height: | Size: 43 B |
77
media/jstree_pre1.0_stable/themes/classic/style.css
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* jsTree classic theme 1.0
|
||||
* Supported features: dots/no-dots, icons/no-icons, focused, loading
|
||||
* Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
|
||||
*/
|
||||
|
||||
.jstree-classic li,
|
||||
.jstree-classic ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
|
||||
.jstree-classic li { background-position:-90px 0; background-repeat:repeat-y; }
|
||||
.jstree-classic li.jstree-last { background:transparent; }
|
||||
.jstree-classic .jstree-open > ins { background-position:-72px 0; }
|
||||
.jstree-classic .jstree-closed > ins { background-position:-54px 0; }
|
||||
.jstree-classic .jstree-leaf > ins { background-position:-36px 0; }
|
||||
|
||||
.jstree-classic .jstree-hovered { background:#e7f4f9; border:1px solid #e7f4f9; padding:0 2px 0 1px; }
|
||||
.jstree-classic .jstree-clicked { background:navy; border:1px solid navy; padding:0 2px 0 1px; color:white; }
|
||||
.jstree-classic a .jstree-icon { background-position:-56px -19px; }
|
||||
.jstree-classic .jstree-open > a .jstree-icon { background-position:-56px -36px; }
|
||||
.jstree-classic a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
|
||||
|
||||
.jstree-classic.jstree-focused { background:white; }
|
||||
|
||||
.jstree-classic .jstree-no-dots li,
|
||||
.jstree-classic .jstree-no-dots .jstree-leaf > ins { background:transparent; }
|
||||
.jstree-classic .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
|
||||
.jstree-classic .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
|
||||
|
||||
.jstree-classic .jstree-no-icons a .jstree-icon { display:none; }
|
||||
|
||||
.jstree-classic .jstree-search { font-style:italic; }
|
||||
|
||||
.jstree-classic .jstree-no-icons .jstree-checkbox { display:inline-block; }
|
||||
.jstree-classic .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
|
||||
.jstree-classic .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
|
||||
.jstree-classic .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
|
||||
.jstree-classic .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
|
||||
.jstree-classic .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
|
||||
.jstree-classic .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
|
||||
.jstree-classic .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
|
||||
|
||||
#vakata-dragged.jstree-classic ins { background:transparent !important; }
|
||||
#vakata-dragged.jstree-classic .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
|
||||
#vakata-dragged.jstree-classic .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
|
||||
#jstree-marker.jstree-classic { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }
|
||||
|
||||
.jstree-classic a.jstree-search { color:aqua; }
|
||||
.jstree-classic .jstree-locked a { color:silver; cursor:default; }
|
||||
|
||||
#vakata-contextmenu.jstree-classic-context,
|
||||
#vakata-contextmenu.jstree-classic-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
|
||||
#vakata-contextmenu.jstree-classic-context li { }
|
||||
#vakata-contextmenu.jstree-classic-context a { color:black; }
|
||||
#vakata-contextmenu.jstree-classic-context a:hover,
|
||||
#vakata-contextmenu.jstree-classic-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
|
||||
#vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a,
|
||||
#vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
|
||||
#vakata-contextmenu.jstree-classic-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
|
||||
#vakata-contextmenu.jstree-classic-context li ul { margin-left:-4px; }
|
||||
|
||||
/* IE6 BEGIN */
|
||||
.jstree-classic li,
|
||||
.jstree-classic ins,
|
||||
#vakata-dragged.jstree-classic .jstree-invalid,
|
||||
#vakata-dragged.jstree-classic .jstree-ok,
|
||||
#jstree-marker.jstree-classic { _background-image:url("d.gif"); }
|
||||
.jstree-classic .jstree-open ins { _background-position:-72px 0; }
|
||||
.jstree-classic .jstree-closed ins { _background-position:-54px 0; }
|
||||
.jstree-classic .jstree-leaf ins { _background-position:-36px 0; }
|
||||
.jstree-classic .jstree-open a ins.jstree-icon { _background-position:-56px -36px; }
|
||||
.jstree-classic .jstree-closed a ins.jstree-icon { _background-position:-56px -19px; }
|
||||
.jstree-classic .jstree-leaf a ins.jstree-icon { _background-position:-56px -19px; }
|
||||
#vakata-contextmenu.jstree-classic-context ins { _display:none; }
|
||||
#vakata-contextmenu.jstree-classic-context li { _zoom:1; }
|
||||
.jstree-classic .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; }
|
||||
.jstree-classic .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; }
|
||||
.jstree-classic .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; }
|
||||
/* IE6 END */
|
BIN
media/jstree_pre1.0_stable/themes/classic/throbber.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
media/jstree_pre1.0_stable/themes/default-rtl/d.gif
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
media/jstree_pre1.0_stable/themes/default-rtl/d.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
media/jstree_pre1.0_stable/themes/default-rtl/dots.gif
Normal file
After Width: | Height: | Size: 132 B |
84
media/jstree_pre1.0_stable/themes/default-rtl/style.css
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* jsTree default-rtl theme 1.0
|
||||
* Supported features: dots/no-dots, icons/no-icons, focused, loading
|
||||
* Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
|
||||
*/
|
||||
|
||||
.jstree-default-rtl li,
|
||||
.jstree-default-rtl ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
|
||||
.jstree-default-rtl li { background-position:-90px 0; background-repeat:repeat-y; }
|
||||
.jstree-default-rtl li.jstree-last { background:transparent; }
|
||||
.jstree-default-rtl .jstree-open > ins { background-position:-72px 0; }
|
||||
.jstree-default-rtl .jstree-closed > ins { background-position:-54px 0; }
|
||||
.jstree-default-rtl .jstree-leaf > ins { background-position:-36px 0; }
|
||||
|
||||
.jstree-default-rtl .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
|
||||
.jstree-default-rtl .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
|
||||
.jstree-default-rtl a .jstree-icon { background-position:-56px -19px; }
|
||||
.jstree-default-rtl a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
|
||||
|
||||
.jstree-default-rtl.jstree-focused { background:#ffffee; }
|
||||
|
||||
.jstree-default-rtl .jstree-no-dots li,
|
||||
.jstree-default-rtl .jstree-no-dots .jstree-leaf > ins { background:transparent; }
|
||||
.jstree-default-rtl .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
|
||||
.jstree-default-rtl .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
|
||||
|
||||
.jstree-default-rtl .jstree-no-icons a .jstree-icon { display:none; }
|
||||
|
||||
.jstree-default-rtl .jstree-search { font-style:italic; }
|
||||
|
||||
.jstree-default-rtl .jstree-no-icons .jstree-checkbox { display:inline-block; }
|
||||
.jstree-default-rtl .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
|
||||
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
|
||||
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
|
||||
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
|
||||
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
|
||||
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
|
||||
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
|
||||
|
||||
#vakata-dragged.jstree-default-rtl ins { background:transparent !important; }
|
||||
#vakata-dragged.jstree-default-rtl .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
|
||||
#vakata-dragged.jstree-default-rtl .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
|
||||
#jstree-marker.jstree-default-rtl { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }
|
||||
|
||||
.jstree-default-rtl a.jstree-search { color:aqua; }
|
||||
.jstree-default-rtl .jstree-locked a { color:silver; cursor:default; }
|
||||
|
||||
#vakata-contextmenu.jstree-default-rtl-context,
|
||||
#vakata-contextmenu.jstree-default-rtl-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context li { }
|
||||
#vakata-contextmenu.jstree-default-rtl-context a { color:black; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context a:hover,
|
||||
#vakata-contextmenu.jstree-default-rtl-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a,
|
||||
#vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context li ul { margin-left:-4px; }
|
||||
|
||||
/* IE6 BEGIN */
|
||||
.jstree-default-rtl li,
|
||||
.jstree-default-rtl ins,
|
||||
#vakata-dragged.jstree-default-rtl .jstree-invalid,
|
||||
#vakata-dragged.jstree-default-rtl .jstree-ok,
|
||||
#jstree-marker.jstree-default-rtl { _background-image:url("d.gif"); }
|
||||
.jstree-default-rtl .jstree-open ins { _background-position:-72px 0; }
|
||||
.jstree-default-rtl .jstree-closed ins { _background-position:-54px 0; }
|
||||
.jstree-default-rtl .jstree-leaf ins { _background-position:-36px 0; }
|
||||
.jstree-default-rtl a ins.jstree-icon { _background-position:-56px -19px; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context ins { _display:none; }
|
||||
#vakata-contextmenu.jstree-default-rtl-context li { _zoom:1; }
|
||||
.jstree-default-rtl .jstree-undetermined a .jstree-checkbox { _background-position:-18px -19px; }
|
||||
.jstree-default-rtl .jstree-checked a .jstree-checkbox { _background-position:-36px -19px; }
|
||||
.jstree-default-rtl .jstree-unchecked a .jstree-checkbox { _background-position:0px -19px; }
|
||||
/* IE6 END */
|
||||
|
||||
/* RTL part */
|
||||
.jstree-default-rtl .jstree-hovered, .jstree-default-rtl .jstree-clicked { padding:0 1px 0 2px; }
|
||||
.jstree-default-rtl li { background-image:url("dots.gif"); background-position: 100% 0px; }
|
||||
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-36px -19px; margin-left:2px; }
|
||||
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:0px -19px; margin-left:2px; }
|
||||
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-18px -19px; margin-left:2px; }
|
||||
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-36px -37px; }
|
||||
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:0px -37px; }
|
||||
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-18px -37px; }
|
BIN
media/jstree_pre1.0_stable/themes/default-rtl/throbber.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
media/jstree_pre1.0_stable/themes/default/d.gif
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
media/jstree_pre1.0_stable/themes/default/d.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
74
media/jstree_pre1.0_stable/themes/default/style.css
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* jsTree default theme 1.0
|
||||
* Supported features: dots/no-dots, icons/no-icons, focused, loading
|
||||
* Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
|
||||
*/
|
||||
|
||||
.jstree-default li,
|
||||
.jstree-default ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
|
||||
.jstree-default li { background-position:-90px 0; background-repeat:repeat-y; }
|
||||
.jstree-default li.jstree-last { background:transparent; }
|
||||
.jstree-default .jstree-open > ins { background-position:-72px 0; }
|
||||
.jstree-default .jstree-closed > ins { background-position:-54px 0; }
|
||||
.jstree-default .jstree-leaf > ins { background-position:-36px 0; }
|
||||
|
||||
.jstree-default .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
|
||||
.jstree-default .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
|
||||
.jstree-default a .jstree-icon { background-position:-56px -19px; }
|
||||
.jstree-default a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
|
||||
|
||||
.jstree-default.jstree-focused { background:#ffffee; }
|
||||
|
||||
.jstree-default .jstree-no-dots li,
|
||||
.jstree-default .jstree-no-dots .jstree-leaf > ins { background:transparent; }
|
||||
.jstree-default .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
|
||||
.jstree-default .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
|
||||
|
||||
.jstree-default .jstree-no-icons a .jstree-icon { display:none; }
|
||||
|
||||
.jstree-default .jstree-search { font-style:italic; }
|
||||
|
||||
.jstree-default .jstree-no-icons .jstree-checkbox { display:inline-block; }
|
||||
.jstree-default .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
|
||||
.jstree-default .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
|
||||
.jstree-default .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
|
||||
.jstree-default .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
|
||||
.jstree-default .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
|
||||
.jstree-default .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
|
||||
.jstree-default .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
|
||||
|
||||
#vakata-dragged.jstree-default ins { background:transparent !important; }
|
||||
#vakata-dragged.jstree-default .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
|
||||
#vakata-dragged.jstree-default .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
|
||||
#jstree-marker.jstree-default { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }
|
||||
|
||||
.jstree-default a.jstree-search { color:aqua; }
|
||||
.jstree-default .jstree-locked a { color:silver; cursor:default; }
|
||||
|
||||
#vakata-contextmenu.jstree-default-context,
|
||||
#vakata-contextmenu.jstree-default-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
|
||||
#vakata-contextmenu.jstree-default-context li { }
|
||||
#vakata-contextmenu.jstree-default-context a { color:black; }
|
||||
#vakata-contextmenu.jstree-default-context a:hover,
|
||||
#vakata-contextmenu.jstree-default-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
|
||||
#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a,
|
||||
#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
|
||||
#vakata-contextmenu.jstree-default-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
|
||||
#vakata-contextmenu.jstree-default-context li ul { margin-left:-4px; }
|
||||
|
||||
/* IE6 BEGIN */
|
||||
.jstree-default li,
|
||||
.jstree-default ins,
|
||||
#vakata-dragged.jstree-default .jstree-invalid,
|
||||
#vakata-dragged.jstree-default .jstree-ok,
|
||||
#jstree-marker.jstree-default { _background-image:url("d.gif"); }
|
||||
.jstree-default .jstree-open ins { _background-position:-72px 0; }
|
||||
.jstree-default .jstree-closed ins { _background-position:-54px 0; }
|
||||
.jstree-default .jstree-leaf ins { _background-position:-36px 0; }
|
||||
.jstree-default a ins.jstree-icon { _background-position:-56px -19px; }
|
||||
#vakata-contextmenu.jstree-default-context ins { _display:none; }
|
||||
#vakata-contextmenu.jstree-default-context li { _zoom:1; }
|
||||
.jstree-default .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; }
|
||||
.jstree-default .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; }
|
||||
.jstree-default .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; }
|
||||
/* IE6 END */
|
BIN
media/jstree_pre1.0_stable/themes/default/throbber.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
23
settings.py
@@ -112,6 +112,7 @@ INSTALLED_APPS = (
|
||||
'seahub.group',
|
||||
'seahub.share',
|
||||
'seahub.subdomain',
|
||||
'gunicorn',
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
@@ -203,5 +204,27 @@ NOTIFICATION_CACHE_TIMEOUT = 0
|
||||
|
||||
LOGIN_URL = SITE_ROOT + 'accounts/login'
|
||||
|
||||
FILE_UPLOAD_MAX_MEMORY_SIZE = 0
|
||||
|
||||
FILE_UPLOAD_TEMP_DIR = "/tmp/seafile-upload"
|
||||
|
||||
if not os.access(FILE_UPLOAD_TEMP_DIR, os.F_OK):
|
||||
os.mkdir(FILE_UPLOAD_TEMP_DIR)
|
||||
|
||||
FILE_UPLOAD_HANDLERS = (
|
||||
"seahub.utils.UploadProgressCachedHandler",
|
||||
"django.core.files.uploadhandler.MemoryFileUploadHandler",
|
||||
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
|
||||
)
|
||||
|
||||
# profile
|
||||
#AUTH_PROFILE_MODULE = "profile.UserProfile"
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
|
||||
'LOCATION': '/tmp/seahub_cache',
|
||||
}
|
||||
}
|
||||
|
||||
MAX_UPLOAD_FILE_SIZE = 1024 * 1024 * 1024 # 1GB
|
||||
MAX_UPLOAD_FILE_NAME_LEN = 256
|
||||
|
@@ -77,7 +77,7 @@
|
||||
<div id="right-panel">
|
||||
{% block right_panel %}{% endblock %}
|
||||
</div>
|
||||
<div id="main-panel" class="clear ovhd">
|
||||
<div id="main-panel" class="clear w100">
|
||||
{% block main_panel %}{% endblock %}
|
||||
</div>
|
||||
<div id="dialog-confirm" class="hide">
|
||||
|
@@ -4,5 +4,5 @@
|
||||
|
||||
{% block main_panel %}
|
||||
|
||||
<p>{{ error_msg }}</p>
|
||||
<p class="error">{{ error_msg }}</p>
|
||||
{% endblock %}
|
||||
|
27
templates/file_upload_progress_page.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
// Update progress bar
|
||||
function update_progress_info() {
|
||||
$.ajax({
|
||||
url: '{{ SITE_ROOT }}file_upload_progress/?X-Progress-ID=' + '{{ uuid }}',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
success: function(data) {
|
||||
if (data) {
|
||||
$('#upload-progress-text', window.parent.document).html(filesizeformat(data.uploaded) + ' / ' + filesizeformat(data.length));
|
||||
$('#task-progress-bar', window.parent.document).removeClass('hide').progressbar({
|
||||
value: data.uploaded / data.length * 100
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeout(update_progress_info, 1000);
|
||||
};
|
||||
update_progress_info();
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -12,9 +12,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block main_panel %}
|
||||
<h2 class="subject">
|
||||
{{repo.props.name}}
|
||||
</h2>
|
||||
<h2>{{repo.props.name}}</h2>
|
||||
|
||||
<div class="side fright">
|
||||
<h3>基本信息</h3>
|
||||
@@ -61,7 +59,8 @@
|
||||
{% if not can_access %}
|
||||
<p class="access-notice">无法在线查看该同步目录。</p>
|
||||
{% else %}
|
||||
<p class="path">
|
||||
<div class="repo-top-bar w100 ovhd">
|
||||
<p class="path fleft">
|
||||
当前路径:
|
||||
{% for name, link in zipped %}
|
||||
{% if not forloop.last %}
|
||||
@@ -71,12 +70,18 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</p>
|
||||
<div class="repo-op fright">
|
||||
<a href="{{ SITE_ROOT }}repo/upload_file/{{repo.id}}/?p={{ path }}" class="upload-file">上传</a>
|
||||
<a id="add-new-dir" href="#">新建目录</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="5%"></th>
|
||||
<th width="69%">名字</th>
|
||||
<th width="67%">名字</th>
|
||||
<th width="13%">大小</th>
|
||||
<th width="13%">操作</th>
|
||||
<th width="15%">操作</th>
|
||||
</tr>
|
||||
|
||||
{% for dirent in dir_list %}
|
||||
@@ -84,18 +89,32 @@
|
||||
<td class="icon-container"><img src="{{ MEDIA_URL }}img/folder-icon-24.png" alt="目录" /></td>
|
||||
<td><a href="{{ SITE_ROOT }}repo/{{ repo.id }}/?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.obj_name }}</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<img src="{{ MEDIA_URL }}img/more-option.png" alt="更多操作" class="more-op hide" />
|
||||
<ul class="op-list hide">
|
||||
<li><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?p={{ path|urlencode }}&file_name={{ dirent.props.obj_name|urlencode }}&op=del">删除</a></li>
|
||||
<li><a class="op dir-rename" href="#" data="{{ dirent.obj_name }}">重命名</a></li>
|
||||
<li><a class="op dir-mv" href="#" data="{{ dirent.obj_name }}">移动</a></li>
|
||||
<li><a class="op dir-cp" href="#" data="{{ dirent.obj_name }}">复制</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{% for dirent in file_list %}
|
||||
<tr>
|
||||
<td class="icon-container"><img src="{{ MEDIA_URL }}img/{{ dirent.obj_name|file_icon_filter }}" alt="文件" /></td>
|
||||
<td>{{ dirent.props.obj_name }}</td>
|
||||
<td><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&op=view">{{ dirent.props.obj_name }}</a></td>
|
||||
<td>{{ dirent.file_size|filesizeformat }}</td>
|
||||
<td>
|
||||
<a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&op=view">查看</a>
|
||||
<a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&op=download">下载</a>
|
||||
<img src="{{ MEDIA_URL }}img/more-option.png" alt="更多操作" class="more-op hide" />
|
||||
<ul class="op-list hide">
|
||||
<li><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?file_name={{ dirent.props.obj_name }}&op=download">下载</a></li>
|
||||
<li><a class="op" href="{{ SITE_ROOT }}repo/{{ repo.props.id }}/{{ dirent.props.obj_id }}/?p={{ path|urlencode }}&file_name={{ dirent.props.obj_name|urlencode }}&op=del">删除</a></li>
|
||||
<li><a class="op file-rename" href="#" data="{{ dirent.obj_name }}">重命名</a></li>
|
||||
<li><a class="op file-mv" href="#" data="{{ dirent.obj_name }}">移动</a></li>
|
||||
<li><a class="op file-cp" href="#" data="{{ dirent.obj_name }}">复制</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -103,9 +122,206 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form id="add-new-dir-form" action="{{ SITE_ROOT}}repo/new_dir/" method="post" class="hide">
|
||||
<h4>新目录名称:</h4>
|
||||
<input type="hidden" name="repo_id" value="{{ repo.id }}" />
|
||||
<input type="hidden" name="parent_dir" value="{{ path }}" />
|
||||
<input type="text" name="new_dir_name" value="" /><br />
|
||||
<p class="error hide">输入不能为空。</p>
|
||||
<input type="submit" value="提交" class="submit" />
|
||||
<button class="simplemodal-close">取消</button>
|
||||
</form>
|
||||
|
||||
<form id="mv-form" action="{{ SITE_ROOT }}file/move/" method="post" class="hide">
|
||||
<p id="mv-hd"></p>
|
||||
<div id="dirs"></div>
|
||||
<input type="hidden" name="operation" id="operation" value="" />
|
||||
<input type="hidden" name="src_repo" value="{{ repo.id }}" />
|
||||
<input type="hidden" name="src_path" value="{{ path }}" />
|
||||
<input type="hidden" name="obj_name" value="" />
|
||||
<input type="hidden" name="obj_type" value="" />
|
||||
<input type="hidden" name="dst_repo" value="" />
|
||||
<input type="hidden" name="dst_path" value="" />
|
||||
<p class="error hide">请点击选择目标目录。</p>
|
||||
<input type="submit" value="提交" class="submit" />
|
||||
<button class="simplemodal-close">取消</button>
|
||||
</form>
|
||||
|
||||
<form id="rename-form" action="{{ SITE_ROOT}}repo/file_rename/" method="post" class="hide">
|
||||
<p>将 <span id="rename-target" class="op-target"></span> 重命名为:</p>
|
||||
<input type="hidden" name="repo_id" value="{{ repo.id }}" />
|
||||
<input type="hidden" name="parent_dir" value="{{ path }}" />
|
||||
<input type="hidden" name="oldname" value="" />
|
||||
<input type="text" name="newname" value="" class="new-name" /><br />
|
||||
<p class="error hide">输入不能为空。</p>
|
||||
<input type="submit" value="提交" class="submit" />
|
||||
<button class="simplemodal-close">取消</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}jstree_pre1.0_stable/jquery.jstree.js"></script>
|
||||
<script type="text/javascript">
|
||||
$('#add-new-dir').click(function () {
|
||||
$('#add-new-dir-form').modal({appendTo:'#main'});
|
||||
return false;
|
||||
});
|
||||
|
||||
$("table tr:gt(0)").hover(
|
||||
function() {
|
||||
$(this).find('.more-op').removeClass('hide');
|
||||
},
|
||||
function() {
|
||||
$(this).find('.more-op').addClass('hide');
|
||||
}
|
||||
);
|
||||
|
||||
var Hide = '';
|
||||
$('.more-op').hover(
|
||||
function() {
|
||||
$(this).parent().css('position','relative');
|
||||
$('.op-list').attr('class', 'op-list hide');
|
||||
if ($(this).offset().top + $(this).next().height() > $('#main').offset().top + $('#main').height()) {
|
||||
$(this).next().css('bottom', 25);
|
||||
}
|
||||
$(this).next().removeClass('hide');
|
||||
clearTimeout(Hide);
|
||||
return false;
|
||||
},
|
||||
function() {
|
||||
var op_list = $(this).next();
|
||||
Hide = setTimeout(function() { op_list.addClass('hide'); }, 1000);
|
||||
}
|
||||
);
|
||||
|
||||
$('.op-list').hover(
|
||||
function(){
|
||||
clearTimeout(Hide);
|
||||
},
|
||||
function(){
|
||||
$(this).addClass('hide');
|
||||
}
|
||||
);
|
||||
|
||||
$('.op-list li').hover(
|
||||
function() {
|
||||
$(this).css('background', '#eee');
|
||||
},
|
||||
function() {
|
||||
$(this).css('background', '#fff');
|
||||
}
|
||||
);
|
||||
|
||||
$('.file-rename, .dir-rename').click(function () {
|
||||
var type = $(this).hasClass('file-rename') ? '文件 ' : '目录 ',
|
||||
name = $(this).attr('data');
|
||||
$('#rename-target').html(type + name);
|
||||
$('input[name="oldname"]').val(name);
|
||||
$('#rename-form').modal({appendTo:'#main'});
|
||||
return false;
|
||||
});
|
||||
|
||||
var accessible_repos = [];
|
||||
{% for repo in accessible_repos %}
|
||||
{% if repo.props.has_subdir %}
|
||||
accessible_repos.push({
|
||||
'data': '{{ repo.props.name }}',
|
||||
'attr': {'repo_id': '{{ repo.props.id }}'},
|
||||
'state': 'closed'
|
||||
});
|
||||
{% else %}
|
||||
accessible_repos.push({
|
||||
'data': '{{ repo.props.name }}',
|
||||
'attr': {'repo_id': '{{ repo.props.id }}'}
|
||||
});
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
$('.file-cp, .file-mv, .dir-cp, .dir-mv').click(function () {
|
||||
var obj_name = $(this).attr('data'),
|
||||
mv_type = '',
|
||||
file_type = '';
|
||||
if ($(this).hasClass('file-cp') || $(this).hasClass('dir-cp')) {
|
||||
$('#operation').val('cp');
|
||||
mv_type = '复制';
|
||||
} else {
|
||||
$('#operation').val('mv');
|
||||
mv_type = '移动';
|
||||
}
|
||||
|
||||
if ($(this).hasClass('file-cp') || $(this).hasClass('file-mv')) {
|
||||
file_type = '文件';
|
||||
$('input[name="obj_type"]').val('file');
|
||||
} else {
|
||||
file_type = '目录';
|
||||
$('input[name="obj_type"]').val('dir');
|
||||
}
|
||||
|
||||
$('input[name="obj_name"]').val(obj_name);
|
||||
$('#mv-hd').html('将 <span class="op-target">' + file_type + ' ' + obj_name + '</span> ' + mv_type + '到:');
|
||||
|
||||
$('#dirs')
|
||||
.bind('loaded.jstree', function(event,data) {
|
||||
$('#mv-form').modal({appendTo:'#main'});
|
||||
})
|
||||
.bind('after_open.jstree after_close.jstree set_focus.jstree', function(event, data) {
|
||||
//resize the popup when open/close/focus a dir
|
||||
$('#simplemodal-container').css('height', $('#mv-form').height()).css('width', $('#mv-form').width());
|
||||
})
|
||||
.bind('select_node.jstree', function(event,data) {
|
||||
var repo_id = data.rslt.obj.attr('repo_id') || data.inst._get_parent(data.rslt.obj).attr('repo_id');
|
||||
$('input[name="dst_repo"]').attr('value', repo_id);
|
||||
var path = data.inst.get_path(data.rslt.obj);
|
||||
var mv_dst_path = '';
|
||||
if (path.length == 1) {
|
||||
mv_dst_path = '/';
|
||||
} else {
|
||||
path.shift();
|
||||
mv_dst_path = '/' + path.join('/') + '/';
|
||||
}
|
||||
$('input[name="dst_path"]').attr('value', mv_dst_path);
|
||||
})
|
||||
.jstree({
|
||||
'json_data': {
|
||||
'data': accessible_repos,
|
||||
'ajax': {
|
||||
'url': function(data) {
|
||||
var path = this.get_path(data);
|
||||
var repo_id = data.attr('repo_id');
|
||||
if (path.length == 1) {
|
||||
path = '/';
|
||||
} else {
|
||||
path.shift();
|
||||
path = '/' + path.join('/') + '/';
|
||||
}
|
||||
return '{{ SITE_ROOT }}file/move/get_subdir/?repo_id=' + encodeURIComponent(repo_id) + '&path=' + encodeURIComponent(path);
|
||||
}
|
||||
}
|
||||
},
|
||||
'core': {
|
||||
'animation': 100
|
||||
},
|
||||
'plugins': ['themes', 'json_data', 'ui']
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#add-new-dir-form .submit, #rename-form .submit').click(function() {
|
||||
if (!$.trim($(this).prev().prev().prev().val())) {//if the input is empty
|
||||
$(this).prev().removeClass('hide');//show error msg
|
||||
$('#simplemodal-container').css('height', $(this).parent().height()).css('width', $(this).parent().width());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$('#mv-form .submit').click(function() {
|
||||
if (!$.trim($(this).prev().prev().val())) {//if the input is empty
|
||||
$(this).prev().removeClass('hide');//show error msg
|
||||
$('#simplemodal-container').css('height', $(this).parent().height()).css('width', $(this).parent().width());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
106
templates/repo_upload_file.html
Normal file
@@ -0,0 +1,106 @@
|
||||
{% extends "myhome_base.html" %}
|
||||
{% load seahub_tags %}
|
||||
|
||||
{% block info_bar_message %}
|
||||
{% if request.user.is_authenticated %}
|
||||
{{ block.super }}
|
||||
{% else %}
|
||||
<div id="info-bar">
|
||||
<span class="info">当前链接会在短期内失效,欢迎您 <a href="http://seafile.com/" target="_blank">加入Seafile </a>体验更多功能。</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main_panel %}
|
||||
{% if used_space < total_space %}
|
||||
<div class="upload-file-panel">
|
||||
<h3>上传文件到
|
||||
{% for name, link in zipped %}
|
||||
<a href="{{ SITE_ROOT }}repo/{{ repo.id }}/?p={{ link|urlencode }}">{{ name }}</a> /
|
||||
{% endfor %}
|
||||
</h3>
|
||||
<form id="upload-file-form" enctype="multipart/form-data" method="post">
|
||||
<input type="hidden" name="parent_dir" id="parent_dir" value="{{ parent_dir }}" />
|
||||
<input type="file" name="file" id="file" /><br />
|
||||
<p id="error-msg" class="error">{{ error_msg }}</p>
|
||||
<input id="upload-submit" type="submit" value="提交" />
|
||||
</form>
|
||||
|
||||
<div id="upload-progress" class="hide">
|
||||
<p>上传进度: <span id="upload-progress-text">获取中,请稍侯...</span></p>
|
||||
<div id="task-progress-bar" class="hide"></div>
|
||||
<button id="upload-cancel">取消</button>
|
||||
</div>
|
||||
<iframe id="request-progress" class="hide"><!--for chrome--></iframe>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-panel">
|
||||
<p class="error">您的空间已经用完。</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
function gen_uuid() {
|
||||
var uuid = "";
|
||||
for (var i=0; i < 32; i++) {
|
||||
uuid += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
// NOTE: In order to real time show upload progress, we must deploy seahub in
|
||||
// ngnix or gunicorn, since we need more than one seahub process to be
|
||||
// running: one for receiving file, one for handling ajax request.
|
||||
function submit_and_real_time_show () {
|
||||
var form = $('#upload-file-form')[0],
|
||||
uuid = gen_uuid(); // id for this upload so we can fetch progress info.
|
||||
$('#upload-progress').removeClass('hide');
|
||||
|
||||
// Append X-Progress-ID uuid form action
|
||||
form.action += (form.action.indexOf('?') == -1 ? '?' : '&') + 'X-Progress-ID=' + uuid;
|
||||
form.submit();
|
||||
$('#upload-submit').addClass('hide');
|
||||
|
||||
// Update progress bar: not work in chrome. for ff.
|
||||
function update_progress_info() {
|
||||
$.ajax({
|
||||
url: '{{ SITE_ROOT }}file_upload_progress/?X-Progress-ID=' + uuid,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
success: function(data) {
|
||||
if (data) {
|
||||
$('#upload-progress-text').html(filesizeformat(data.uploaded) + ' / ' + filesizeformat(data.length));
|
||||
$('#task-progress-bar').removeClass('hide').progressbar({
|
||||
value: data.uploaded / data.length * 100
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeout(update_progress_info, 1000);
|
||||
};
|
||||
update_progress_info();
|
||||
|
||||
// Update progress bar: for chrome
|
||||
$('#request-progress').attr('src', '{{ SITE_ROOT }}file_upload_progress_page/?uuid=' + uuid);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
$('#upload-submit').click(function () {
|
||||
if (!$.trim($('#file').val())) {
|
||||
$('#error-msg').html('请先选择一个文件');
|
||||
return false;
|
||||
}
|
||||
$('#error-msg').addClass('hide');
|
||||
submit_and_real_time_show();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#upload-cancel').click(function() {
|
||||
location.reload(true);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
13
urls.py
@@ -9,7 +9,10 @@ from seahub.views import root, peers, myhome, \
|
||||
ownerhome, repo_history_dir, repo_history_revert, \
|
||||
user_info, repo_set_access_property, repo_access_file, \
|
||||
repo_remove_share, repo_download, org_info, \
|
||||
seafile_access_check, back_local, repo_history_changes
|
||||
seafile_access_check, back_local, repo_history_changes, \
|
||||
repo_upload_file, file_upload_progress, file_upload_progress_page, get_subdir, file_move, \
|
||||
repo_new_dir, repo_rename_file, validate_filename
|
||||
|
||||
from seahub.notifications.views import notification_list
|
||||
from seahub.share.views import share_admin
|
||||
from seahub.group.views import group_list
|
||||
@@ -40,6 +43,12 @@ urlpatterns = patterns('',
|
||||
url(r'^shareadmin/$', share_admin, name='share_admin'),
|
||||
(r'^shareadmin/removeshare/$', repo_remove_share),
|
||||
|
||||
(r'^file_upload_progress/$', file_upload_progress),
|
||||
(r'^file_upload_progress_page/$', file_upload_progress_page),
|
||||
(r'^repo/new_dir/$', repo_new_dir),
|
||||
(r'^repo/upload_check/$', validate_filename),
|
||||
(r'^repo/file_rename/$', repo_rename_file),
|
||||
url(r'^repo/upload_file/(?P<repo_id>[^/]+)/$', repo_upload_file, name='repo_upload_file'),
|
||||
url(r'^repo/(?P<repo_id>[^/]+)/$', repo, name='repo'),
|
||||
(r'^repo/history/(?P<repo_id>[^/]+)/$', repo_history),
|
||||
(r'^repo/history/dir/(?P<repo_id>[^/]+)/$', repo_history_dir),
|
||||
@@ -51,6 +60,8 @@ urlpatterns = patterns('',
|
||||
# (r'^repo/setap/(?P<repo_id>[^/]+)/$', repo_set_access_property),
|
||||
(r'^repo/(?P<repo_id>[^/]+)/(?P<obj_id>[^/]+)/$', repo_access_file),
|
||||
(r'^download/repo/$', repo_download),
|
||||
(r'^file/move/get_subdir/$', get_subdir),
|
||||
(r'^file/move/$', file_move),
|
||||
(r'^seafile_access_check/$', seafile_access_check),
|
||||
url(r'^org/remove/(?P<org_id>[\d]+)/$', org_remove, name="org_remove"),
|
||||
(r'^org/$', org_info),
|
||||
|
145
utils.py
@@ -1,13 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
import re
|
||||
import settings
|
||||
import re
|
||||
import time
|
||||
import os
|
||||
import stat
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.utils.hashcompat import sha_constructor
|
||||
|
||||
from seaserv import get_commits
|
||||
from django.core.files.uploadhandler import FileUploadHandler, StopFutureHandlers, StopUpload
|
||||
from django.core.cache import cache
|
||||
|
||||
from seaserv import seafserv_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \
|
||||
get_repo, get_commits, get_group_repoids
|
||||
|
||||
EMPTY_SHA1 = '0000000000000000000000000000000000000000'
|
||||
|
||||
def go_permission_error(request, msg=None):
|
||||
"""
|
||||
@@ -85,3 +93,136 @@ def calculate_repo_last_modify(repo_list):
|
||||
repo.latest_modify = get_commits(repo.id, 0, 1)[0].ctime
|
||||
except:
|
||||
repo.latest_modify = None
|
||||
|
||||
class UploadProgressCachedHandler(FileUploadHandler):
|
||||
"""Tracks progress for file uploads. The http post request must contain a
|
||||
header or query parameter, 'X-Progress-ID', which should contain a unique
|
||||
string to identify the upload to be tracked.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, request=None):
|
||||
super(UploadProgressCachedHandler, self).__init__(request)
|
||||
self.progress_id = None
|
||||
self.cache_key = None
|
||||
self.chunk_size = 1024
|
||||
|
||||
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
|
||||
self.content_length = content_length
|
||||
if 'X-Progress-ID' in self.request.GET :
|
||||
self.progress_id = self.request.GET['X-Progress-ID']
|
||||
elif 'X-Progress-ID' in self.request.META:
|
||||
self.progress_id = self.request.META['X-Progress-ID']
|
||||
print "handle_raw_input: ", self.progress_id
|
||||
if self.progress_id:
|
||||
self.cache_key = "%s_%s" % (self.request.user.username, self.progress_id )
|
||||
# make cache expiring in 30 seconds
|
||||
cache.set(self.cache_key, {
|
||||
'length': self.content_length,
|
||||
'uploaded' : 0
|
||||
}, 30)
|
||||
|
||||
def new_file(self, field_name, file_name, content_type, content_length, charset=None):
|
||||
pass
|
||||
|
||||
def receive_data_chunk(self, raw_data, start):
|
||||
time.sleep(1)
|
||||
if self.cache_key:
|
||||
data = cache.get(self.cache_key)
|
||||
data['uploaded'] += self.chunk_size
|
||||
cache.set(self.cache_key, data, 30)
|
||||
return raw_data
|
||||
|
||||
def file_complete(self, file_size):
|
||||
pass
|
||||
|
||||
def upload_complete(self):
|
||||
if self.cache_key:
|
||||
cache.delete(self.cache_key)
|
||||
|
||||
def check_filename_with_rename(repo_id, parent_dir, filename):
|
||||
latest_commit = get_commits(repo_id, 0, 1)[0]
|
||||
dirents = seafserv_rpc.list_dir_by_path(latest_commit.id,
|
||||
parent_dir.encode('utf-8'))
|
||||
|
||||
def no_duplicate(name):
|
||||
for dirent in dirents:
|
||||
if dirent.obj_name == name:
|
||||
return False
|
||||
return True
|
||||
|
||||
def make_new_name(filename, i):
|
||||
base, ext = os.path.splitext(filename)
|
||||
if ext:
|
||||
new_base = "%s (%d)" % (base, i)
|
||||
return new_base + ext
|
||||
else:
|
||||
return "%s (%d)" % (filename, i)
|
||||
|
||||
if no_duplicate(filename):
|
||||
return filename
|
||||
else:
|
||||
i = 1
|
||||
while True:
|
||||
new_name = make_new_name (filename, i)
|
||||
if no_duplicate(new_name):
|
||||
return new_name
|
||||
else:
|
||||
i += 1
|
||||
|
||||
|
||||
def get_accessible_repos(request, repo):
|
||||
"""Get all repos the current user can access when coping/moving files
|
||||
online. If the repo is encrypted, then files can only be copied/moved
|
||||
within the same repo. Otherwise, files can be copied/moved between
|
||||
owned/shared/group repos of the current user.
|
||||
|
||||
"""
|
||||
def check_has_subdir(repo):
|
||||
latest_commit = get_commits(repo.props.id, 0, 1)[0]
|
||||
if latest_commit.root_id == EMPTY_SHA1:
|
||||
return False
|
||||
|
||||
dirs = seafserv_rpc.list_dir_by_path(latest_commit.id, '/')
|
||||
|
||||
for dirent in dirs:
|
||||
if stat.S_ISDIR(dirent.props.mode):
|
||||
return True
|
||||
|
||||
if repo.encrypted:
|
||||
repo.has_subdir = check_has_subdir(repo)
|
||||
accessible_repos = [repo]
|
||||
return accessible_repos
|
||||
|
||||
accessible_repos = []
|
||||
|
||||
email = request.user.username
|
||||
owned_repos = seafserv_threaded_rpc.list_owned_repos(email)
|
||||
shared_repos = seafserv_threaded_rpc.list_share_repos(email, 'to_email', -1, -1)
|
||||
groups_repos = []
|
||||
groups = ccnet_threaded_rpc.get_groups(email)
|
||||
for group in groups:
|
||||
group_repo_ids = get_group_repoids(group_id=group.id)
|
||||
for repo_id in group_repo_ids:
|
||||
if not repo_id:
|
||||
continue
|
||||
group_repo = get_repo(repo_id)
|
||||
if not group_repo:
|
||||
continue
|
||||
group_repo.share_from = seafserv_threaded_rpc.get_group_repo_share_from(repo_id)
|
||||
if email != group_repo.share_from:
|
||||
groups_repos.append(group_repo)
|
||||
|
||||
def has_repo(repos, repo):
|
||||
for r in repos:
|
||||
if repo.id == r.id:
|
||||
return True
|
||||
return False
|
||||
|
||||
for repo in owned_repos + shared_repos + groups_repos:
|
||||
if not has_repo(accessible_repos, repo):
|
||||
if not repo.props.encrypted:
|
||||
accessible_repos.append(repo)
|
||||
repo.props.has_subdir = check_has_subdir(repo)
|
||||
|
||||
return accessible_repos
|
||||
|
306
views.py
@@ -1,5 +1,6 @@
|
||||
# encoding: utf-8
|
||||
import settings
|
||||
import os
|
||||
import stat
|
||||
import simplejson as json
|
||||
import sys
|
||||
@@ -14,6 +15,9 @@ from django.shortcuts import render_to_response, redirect
|
||||
from django.template import Context, loader, RequestContext
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.http import HttpResponse, HttpResponseServerError
|
||||
|
||||
from auth.decorators import login_required
|
||||
from auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, \
|
||||
PasswordChangeForm
|
||||
@@ -30,7 +34,8 @@ from seahub.notifications.models import UserNotification
|
||||
from forms import AddUserForm
|
||||
from utils import go_permission_error, go_error, list_to_string, \
|
||||
get_httpserver_root, get_ccnetapplet_root, gen_token, \
|
||||
calculate_repo_last_modify
|
||||
calculate_repo_last_modify, \
|
||||
check_filename_with_rename, get_accessible_repos, EMPTY_SHA1
|
||||
from seahub.profile.models import Profile
|
||||
|
||||
@login_required
|
||||
@@ -191,7 +196,7 @@ def render_repo(request, repo_id, error=''):
|
||||
if path[-1] != '/':
|
||||
path = path + '/'
|
||||
|
||||
if latest_commit.root_id == '0000000000000000000000000000000000000000':
|
||||
if latest_commit.root_id == EMPTY_SHA1:
|
||||
dirs = []
|
||||
else:
|
||||
try:
|
||||
@@ -213,6 +218,12 @@ def render_repo(request, repo_id, error=''):
|
||||
file_list.sort(lambda x, y : cmp(x.obj_name.lower(),
|
||||
y.obj_name.lower()))
|
||||
|
||||
try:
|
||||
accessible_repos = get_accessible_repos(request, repo)
|
||||
except SearpcError, e:
|
||||
error_msg = e.msg
|
||||
return go_error(request, error_msg)
|
||||
|
||||
# generate path and link
|
||||
zipped = gen_path_link(path, repo.name)
|
||||
|
||||
@@ -229,8 +240,115 @@ def render_repo(request, repo_id, error=''):
|
||||
"path" : path,
|
||||
"zipped" : zipped,
|
||||
"error" : error,
|
||||
"accessible_repos" : accessible_repos,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def repo_upload_file(request, repo_id):
|
||||
repo = get_repo(repo_id)
|
||||
total_space = pow(2,30)
|
||||
used_space = seafserv_threaded_rpc.get_user_quota_usage(request.user.username)
|
||||
############ GET ############
|
||||
if request.method == 'GET':
|
||||
parent_dir = request.GET.get('p', '/')
|
||||
zipped = gen_path_link (parent_dir, repo.name)
|
||||
# TODO: per user quota
|
||||
return render_to_response ('repo_upload_file.html', {
|
||||
"repo": repo,
|
||||
"parent_dir": parent_dir,
|
||||
"used_space": used_space,
|
||||
"total_space": total_space,
|
||||
"zipped": zipped,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
############ POST ############
|
||||
parent_dir = request.POST.get('parent_dir', '/')
|
||||
def render_upload_error(error_msg):
|
||||
zipped = gen_path_link (parent_dir, repo.name)
|
||||
return render_to_response ('repo_upload_file.html', {
|
||||
"error_msg": error_msg,
|
||||
"repo": repo,
|
||||
"used_space": used_space,
|
||||
"total_space": total_space,
|
||||
"zipped": zipped,
|
||||
"parent_dir": parent_dir,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
try:
|
||||
tmp_file = request.FILES['file']
|
||||
except:
|
||||
error_msg = u'请选择一个文件'
|
||||
return render_upload_error(error_msg)
|
||||
|
||||
tmp_file_path = tmp_file.temporary_file_path()
|
||||
if not os.access(tmp_file_path, os.F_OK):
|
||||
error_msg = u'上传文件失败'
|
||||
return render_upload_error(error_msg)
|
||||
|
||||
# rename the file if there is name conflicts
|
||||
filename = check_filename_with_rename(repo_id, parent_dir, tmp_file.name)
|
||||
if len(filename) > settings.MAX_UPLOAD_FILE_NAME_LEN:
|
||||
error_msg = u"您上传的文件名称太长"
|
||||
return go_error(request, error_msg)
|
||||
|
||||
if tmp_file.size > settings.MAX_UPLOAD_FILE_SIZE:
|
||||
error_msg = u"您上传的文件太大"
|
||||
return go_error(request, error_msg)
|
||||
|
||||
try:
|
||||
seafserv_threaded_rpc.put_file (repo_id, tmp_file_path, parent_dir,
|
||||
filename, request.user.username);
|
||||
except SearpcError, e:
|
||||
error_msg = e.msg
|
||||
return render_upload_error(error_msg)
|
||||
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def get_subdir(request):
|
||||
repo_id = request.GET.get('repo_id', '')
|
||||
path = request.GET.get('path', '')
|
||||
|
||||
if not (repo_id and path):
|
||||
return go_error(request)
|
||||
|
||||
latest_commit = get_commits(repo_id, 0, 1)[0]
|
||||
try:
|
||||
dirents = seafserv_rpc.list_dir_by_path(latest_commit.id, path.encode('utf-8'))
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
subdirs = []
|
||||
for dirent in dirents:
|
||||
if not stat.S_ISDIR(dirent.props.mode):
|
||||
continue
|
||||
|
||||
dirent.has_subdir = False
|
||||
path_ = os.path.join (path, dirent.obj_name)
|
||||
try:
|
||||
dirs_ = seafserv_rpc.list_dir_by_path(latest_commit.id, path_.encode('utf-8'))
|
||||
except SearpcError, e:
|
||||
return go_error(request, e.msg)
|
||||
|
||||
for dirent_ in dirs_:
|
||||
if stat.S_ISDIR(dirent_.props.mode):
|
||||
dirent.has_subdir = True
|
||||
break
|
||||
|
||||
if dirent.has_subdir:
|
||||
subdir = {
|
||||
'data': dirent.obj_name,
|
||||
'attr': {'repo_id': repo_id },
|
||||
'state': 'closed'
|
||||
}
|
||||
subdirs.append(subdir)
|
||||
else:
|
||||
subdirs.append(dirent.obj_name)
|
||||
|
||||
content_type = 'application/json; charset=utf-8'
|
||||
return HttpResponse(json.dumps(subdirs),
|
||||
content_type=content_type)
|
||||
|
||||
def repo(request, repo_id):
|
||||
if request.method == 'GET':
|
||||
return render_repo(request, repo_id)
|
||||
@@ -544,7 +662,7 @@ def myhome(request):
|
||||
owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
|
||||
|
||||
# Repos that are share to me
|
||||
in_repos = seafserv_threaded_rpc.list_share_repos(request.user.username,
|
||||
in_repos = seafserv_threaded_rpc.list_share_repos(email,
|
||||
'to_email', -1, -1)
|
||||
calculate_repo_last_modify(in_repos)
|
||||
in_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
|
||||
@@ -620,6 +738,19 @@ def repo_set_access_property(request, repo_id):
|
||||
|
||||
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
|
||||
|
||||
@login_required
|
||||
def repo_del_file(request, repo_id):
|
||||
parent_dir = request.GET.get("p", "/")
|
||||
file_name = request.GET.get("file_name")
|
||||
user = request.user.username
|
||||
try:
|
||||
seafserv_threaded_rpc.del_file(repo_id, parent_dir,file_name, user)
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def repo_access_file(request, repo_id, obj_id):
|
||||
if repo_id:
|
||||
repo = get_repo(repo_id)
|
||||
@@ -638,6 +769,12 @@ def repo_access_file(request, repo_id, obj_id):
|
||||
if repo.props.encrypted and not password_set:
|
||||
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
|
||||
|
||||
op = request.GET.get('op', 'view')
|
||||
file_name = request.GET.get('file_name', '')
|
||||
|
||||
if op == 'del':
|
||||
return repo_del_file(request, repo_id)
|
||||
|
||||
# if a repo doesn't have access property in db, then assume it's 'own'
|
||||
repo_ap = seafserv_threaded_rpc.repo_query_access_property(repo_id)
|
||||
if not repo_ap:
|
||||
@@ -663,8 +800,6 @@ def repo_access_file(request, repo_id, obj_id):
|
||||
raise Http404
|
||||
|
||||
http_server_root = get_httpserver_root()
|
||||
file_name = request.GET.get('file_name', '')
|
||||
op = request.GET.get('op', 'view')
|
||||
|
||||
redirect_url = '%s/access?repo_id=%s&id=%s&filename=%s&op=%s&t=%s&u=%s' % (http_server_root,
|
||||
repo_id, obj_id,
|
||||
@@ -697,6 +832,53 @@ def repo_download(request):
|
||||
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
@login_required
|
||||
def file_move(request):
|
||||
src_repo_id = request.POST.get('src_repo')
|
||||
src_path = request.POST.get('src_path')
|
||||
dst_repo_id = request.POST.get('dst_repo')
|
||||
dst_path = request.POST.get('dst_path')
|
||||
obj_name = request.POST.get('obj_name')
|
||||
obj_type = request.POST.get('obj_type') # dir or file
|
||||
op = request.POST.get('operation')
|
||||
|
||||
if not (src_repo_id and src_path and dst_repo_id \
|
||||
and dst_path and obj_name and obj_type and op):
|
||||
return go_error(request)
|
||||
|
||||
# do nothing when dst is the same as src
|
||||
if src_repo_id == dst_repo_id and src_path == dst_path:
|
||||
url = reverse('repo', args=[src_repo_id]) + ('?p=%s' % src_path)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Error when moving/copying a dir to its subdir
|
||||
if obj_type == 'dir':
|
||||
src_dir = os.path.join(src_path, obj_name)
|
||||
if dst_path.startswith(src_dir):
|
||||
error_msg = u"不能把目录 %s %s到它的子目录 %s" \
|
||||
% (src_dir, u"复制" if op == 'cp' else u"移动", dst_path)
|
||||
return go_error(request, error_msg)
|
||||
|
||||
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
|
||||
|
||||
try:
|
||||
if op == 'cp':
|
||||
seafserv_threaded_rpc.copy_file (src_repo_id, src_path, obj_name,
|
||||
dst_repo_id, dst_path, new_obj_name,
|
||||
request.user.username)
|
||||
elif op == 'mv':
|
||||
seafserv_threaded_rpc.move_file (src_repo_id, src_path, obj_name,
|
||||
dst_repo_id, dst_path, new_obj_name,
|
||||
request.user.username)
|
||||
except Exception, e:
|
||||
return go_error(request, str(e))
|
||||
|
||||
url = reverse('repo', args=[src_repo_id]) + ('?p=%s' % src_path)
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
|
||||
def seafile_access_check(request):
|
||||
repo_id = request.GET.get('repo_id', '')
|
||||
applet_root = get_ccnetapplet_root()
|
||||
@@ -1141,3 +1323,117 @@ def org_info(request):
|
||||
'groups': groups,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def file_upload_progress(request):
|
||||
"""
|
||||
Return JSON object with information about the progress of an upload.
|
||||
"""
|
||||
progress_id = None
|
||||
if 'X-Progress-ID' in request.GET:
|
||||
progress_id = request.GET['X-Progress-ID']
|
||||
elif 'X-Progress-ID' in request.META:
|
||||
progress_id = request.META['X-Progress-ID']
|
||||
|
||||
if progress_id:
|
||||
cache_key = "%s_%s" % (request.user.username, progress_id)
|
||||
data = cache.get(cache_key)
|
||||
return HttpResponse(json.dumps(data))
|
||||
else:
|
||||
return HttpResponseServerError('Server Error: You must provide X-Progress-ID header or query param.')
|
||||
|
||||
@login_required
|
||||
def file_upload_progress_page(request):
|
||||
'''
|
||||
As iframe in repo_upload_file.html, for solving problem in chrome.
|
||||
|
||||
'''
|
||||
uuid = request.GET.get('uuid', '')
|
||||
|
||||
return render_to_response('file_upload_progress_page.html', {
|
||||
'uuid': uuid,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@login_required
|
||||
def repo_new_dir(request):
|
||||
repo_id = request.POST.get("repo_id")
|
||||
parent_dir = request.POST.get("parent_dir")
|
||||
new_dir_name = request.POST.get("new_dir_name")
|
||||
user = request.user.username
|
||||
|
||||
if not new_dir_name:
|
||||
error_msg = u"请输入新目录名"
|
||||
return go_error(request, error_msg)
|
||||
|
||||
if not (repo_id and parent_dir and user):
|
||||
return go_error(request)
|
||||
|
||||
if len(new_dir_name) > settings.MAX_UPLOAD_FILE_NAME_LEN:
|
||||
error_msg = u"您输入的目录名称过长"
|
||||
return go_error (request, error_msg)
|
||||
|
||||
try:
|
||||
if not seafserv_threaded_rpc.is_valid_filename(repo_id, new_dir_name):
|
||||
error_msg = (u"您输入的目录名称 %s 包含非法字符" % new_dir_name)
|
||||
return go_error (request, error_msg)
|
||||
except SearpcError,e:
|
||||
return go_error (request, e.msg)
|
||||
|
||||
new_dir_name = check_filename_with_rename(repo_id, parent_dir, new_dir_name)
|
||||
|
||||
try:
|
||||
seafserv_threaded_rpc.put_dir(repo_id, parent_dir, new_dir_name, user)
|
||||
except Exception, e:
|
||||
return go_error(request, str(e))
|
||||
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@login_required
|
||||
def repo_rename_file(request):
|
||||
repo_id = request.POST.get("repo_id")
|
||||
parent_dir = request.POST.get("parent_dir")
|
||||
oldname = request.POST.get("oldname")
|
||||
newname = request.POST.get("newname")
|
||||
user = request.user.username
|
||||
|
||||
print repo_id, parent_dir, oldname, newname, user
|
||||
|
||||
if not newname:
|
||||
error_msg = u"新文件名不能为空"
|
||||
return go_error(request, error_msg)
|
||||
|
||||
if len(newname) > settings.MAX_UPLOAD_FILE_NAME_LEN:
|
||||
error_msg = u"新文件名太长"
|
||||
return go_error(request, error_msg)
|
||||
|
||||
if not (repo_id and parent_dir and oldname):
|
||||
return go_error(request)
|
||||
|
||||
try:
|
||||
seafserv_threaded_rpc.rename_file (repo_id, parent_dir,
|
||||
oldname, newname, user)
|
||||
except Exception, e:
|
||||
return go_error(request, str(e))
|
||||
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % parent_dir)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@login_required
|
||||
def validate_filename(request):
|
||||
repo_id = request.GET.get('repo_id')
|
||||
filename = request.GET.get('filename')
|
||||
|
||||
if not (repo_id and filename):
|
||||
return go_error(request)
|
||||
|
||||
result = {'ret':'yes'}
|
||||
|
||||
try:
|
||||
ret = seafserv_threaded_rpc.is_valid_filename (repo_id, filename);
|
||||
except SearpcError:
|
||||
result['ret'] = 'error'
|
||||
else:
|
||||
result['ret'] = 'yes' if ret == 1 else 'no'
|
||||
|
||||
content_type = 'application/json; charset=utf-8'
|
||||
return HttpResponse(json.dumps(result), content_type=content_type)
|
||||
|