PDA

View Full Version : [Beta 1] Saki's FileTree for Ext 2.0



Pages : 1 2 [3] 4

jbird526
11 Sep 2008, 10:42 AM
Button disappearing seems to be BrowseButton problem. Try to ask loeppky to look at it.

Im sorry, upon further investigation, there are known issues with IE 6/7 with the BrowseButton.

sdrew
11 Sep 2008, 5:51 PM
Thsi is how I fixed the browse button disappearing issue in Ext.ux.UploadPanel.js in the onRemoveFile:function, comment out these lines:

// if (wrap) {
// wrap.remove();
//}

Steve.

sdrew
11 Sep 2008, 6:00 PM
Hmm, difficult to say anything reasonable on that. Is it UploadPanel, IE7 (why not IE6 then?) or Ext problem? No idea.

Somebody else?

Its not Ext as I use alot of Ext and windows/forms etc with IE7 and no leaks, only adding UploadPanel introduce that and as I explained your website shows the problem also.

If you notice the leaks they are elements added by Ext.ux.UploadPanel.js in the initComponent function.

It adds up to about 10-20 Meg each time you open the uploadPanel window and add a file to it. IE7 never gets that memory back so on workstations average memory they slowed down very quickly.

I put alerts in the destroy functions of UploadPanel, but they never get called, should they on page unload?

I'm sure its another IE7 bug, but must be some way to workaround that.

Thanks.
Steve.

mystix
11 Sep 2008, 7:17 PM
Thsi is how I fixed the browse button disappearing issue in Ext.ux.UploadPanel.js in the onRemoveFile:function, comment out these lines:

// if (wrap) {
// wrap.remove();
//}

Steve.

might better to simply check for IE (so you don't end up breaking the code):


if (wrap && !Ext.isIE) {
wrap.remove();
}

jbird526
12 Sep 2008, 5:34 AM
might better to simply check for IE (so you don't end up breaking the code):


if (wrap && !Ext.isIE) {
wrap.remove();
}


Ive tried both ways of commenting out and adding the isIE. Still not working for me but I really appreciate the response by the community. Im sure its my implementation, because I am still having problems with getting the browse button to click. The hovering DIV never fully overlays the button for me.

ferrenliu
14 Sep 2008, 11:10 AM
I catch an error, when I use this filetree to create 'NewDir'. The error just throw in IE8 beta2 like below
------------------------
Type not equal
------------------------
I trace and found out the error has be throw at the code of ExtJs 2.2. Detail is

node.ui.getEl().scrollIntoView(this.tree.body);

Any body know how to fix it?

jsakalos
14 Sep 2008, 1:47 PM
Generally, Ext doesn't support browser's beta releases, however, Ext team is glad to be informed on problems. If it's core Ext problem, find please an appropriate thread and post it there.

Arthur.Blake
15 Sep 2008, 8:02 AM
I admit it looks misleading. Upload file is menu item of UploadPanel, including everything below it.

Not exactly sure why that menu item is needed at all? Is it supposed to be a "header" for the display items below it? IMHO It is very misleading. Pressing Ctrl-U on FF 2/Linux causes View Source popup. Why not remove this menu item? Is not the 'Add' item by itself clear enough?

Also, unrelated, I noticed a little security issue on your server side... Creating a folder with a name such as "../aaa" underneath a folder named "hello" causes the folder to be created one level up as "helloaaa". Creating folders such as "../../../hello" doesn't do anything noticeable (presumably it created the folder on your server in a place that I shouldn't have access to!?!)

Another little irritating issue (FF2/Linux), creating a new folder, causes the window to scroll up to the point where New Folder is shown at the very top.

Thanks for your great ux!

wulfshayde
15 Sep 2008, 10:14 PM
Hi Saki,

Great plugin, though I've had an error arise that I can't figure out for the life of me. I'm using Coldfusion for the server side language and uploads were working, but recently they've stopped.

This is my .js


var upform = new Ext.ux.UploadPanel({
id:'uppanel',
url:'/index.cfm',
path:'/site/modules/IT/UserFiles/attachments/',
baseParams:{
event:'addAttachment',
returnFormat:'json'
},
maxFileSize:1048576,
bodyStyle: 'width: 99%; margin-bottom: 10px;'
});

And my back-end cf code:


<cffunction name="addAttachment" access="public" output="true" returntype="void">
<cfargument name="event" type="any" />
<cfoutput>{"success":true}</cfoutput>
<cfabort />
</cffunction>

I keep getting the error "cannot decode JSON object"...

I'm only responding to the request with that one line {"success":true} so what am I missing?

Thanks in advance...

jsakalos
16 Sep 2008, 1:49 AM
Put a breakpoint in the code and see what is real responseText received. Generally, if it had worked before, I'd search for the change that could have caused it is failing now. New version of something?

wulfshayde
16 Sep 2008, 11:25 PM
Not suprisingly, it was programmer error at the end of the day. I was sending the upload to a non-existent file via the url attribute. Forgot I moved a folder which contained the CFC's. My bad. Awesome tool by the way, and thanks so much for putting so much of your time into it!

kk_kkk
21 Sep 2008, 9:58 PM
hi,saki
does the jsonstring from backend must be like this:
[{"text":"New listing notes in doc format","iconCls":"file-doc","disabled":false,"leaf":true,"qtip":"Size: 1047552"},{"text":"meeting notes in pdf format","iconCls":"file-pdf","disabled":false,"leaf":true,"qtip":"Size: 1047552"}]

i mean ,if it's will work when like following:

[{"text":"New listing notes in doc format","disabled":false,"leaf":true,"iconCls":"file-doc","qtip":"Size: 1047552"},{"text":"meeting notes in pdf format","disabled":false,"leaf":true,"iconCls":"file-pdf","qtip":"Size: 1047552"}]

many thanks !

jsakalos
21 Sep 2008, 11:14 PM
I do not see any difference at the first glance.

kk_kkk
22 Sep 2008, 2:54 AM
well, it just change the order of the field. :)


i meet a big problem that trouble me (refer attached pic)

when i return the json string from backend like the following:

[{"text":"a","disabled":false,"leaf":false,"cls":"folder"},{"text":"a.txt","disabled":false,"leaf":true,"cls":"file-txt","qtip":"Size: 1047552"}]

the browser can show the right fold and file structure, so ,i think there is no errors in the backend although the order is a litter different from the one in the "FileTreePanel Client-Server Interface Specification" .

but there are some errors when tree showed in front of the broswer.

you may see from the attached pic, the tree looks strange.. i guess it's the errors of css file.so i try to del a line in the treefile.html

<link rel="stylesheet" type="text/css" href="./css/filetype.css">
when finished ,i find the leaf node of the tree is normal except showing the default icon of ext.

and i change a line in the filetree.css
from

.folder {
background-image:url(../ext/resources/images/default/tree/folder.gif) ! important;
/* background-repeat:no-repeat; */
}

to

.folder {
background-image:url(../ext/resources/images/default/s.gif) ! important;
/* background-repeat:no-repeat; */
}

well, everything is well when finished but when i create a new folder by the top menu, i notice there's no folder icon of the new line.

i think ,this is not correct method to solve the questions that i meet.

so any advices is kind. million thanks !

kk_kkk

jsakalos
22 Sep 2008, 6:58 AM
The order in Json doesn't matter. Your problem is related to missing/conflicting css.

kk_kkk
22 Sep 2008, 7:24 AM
hi,saki, thanks your quick reply and give the tip ,i'll try later

I entirely downloaded the filetree.zip and edited only the filetree.js and FiletreePanel.js
I doesn't change any other files.
and, I tested the filetree.html in the same project that running the Desktop project published in the commutity.

is it the css conflect in the desktop.html ?


<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Ext 2.0 Desktop Sample App</title>

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<!-- GC -->
<!-- LIBS -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<!-- ENDLIBS -->

<script type="text/javascript" src="../../ext-all.js"></script>

<!-- DESKTOP -->
<script type="text/javascript" src="js/StartMenu.js"></script>
<script type="text/javascript" src="js/TaskBar.js"></script>
<script type="text/javascript" src="js/Desktop.js"></script>
<script type="text/javascript" src="js/App.js"></script>
<script type="text/javascript" src="js/Module.js"></script>
<script type="text/javascript" src="sample.js"></script>

<link rel="stylesheet" type="text/css" href="css/desktop.css" />
.....
other my own css...
...



i think ,maybe i should first try to run the filetree.html in the new project and note if the errors will happen.

jsakalos
22 Sep 2008, 12:20 PM
Well, if you've changed FileTreePanel.js then you're on your own. Check with firebug if all your files are loaded from server.

kk_kkk
22 Sep 2008, 6:32 PM
HI saka
i try again , and solved the background of folder errors ,when i commented the code of the filetree.css like the following:

.folder {
/*background-image:url(../ext/resources/images/default/tree/folder.gif) ! important;
background-repeat:no-repeat; */
}

but errors related to the background of leaf node still existed. I notice that the icon of 'a.pdf' just the default icon of extjs. and not change at all during the whole process. I checked and tracked the code with firebug,but want to find what had hanppened when the json object was returned from the backend,but i failed.

so ,could you show me some tips that how the json object returned from backend is used and read by the js file in order to show correct icon related to special file-type. Is the orginal load of tree only related to the filetreepanel.js? i really don't find any code Or Callback fuction to deal with any field of this json.


Thanks in advance.



kk_kkk

jsakalos
23 Sep 2008, 12:00 AM
I have no other example than that on demo page. If you enter the thin ice of modifying sources, including css, then you're on your own. I cannot handle modifications of all users.

kk_kkk
23 Sep 2008, 3:09 AM
hi ,saki

i solved the questions i meeted.by reading the code carefully

i find that it's just the faults from the backend. and not be related to the source files in the treefile.zip. because I printed the json from backend like this



[
{"text":"b","cls":"folder","disabled":false,"leaf":false},
{"text":"a.txt","cls":"file-txt","disabled":false,"leaf":true,"qtip":"Size: 1047552"}
]

not like this:


[
{"text":"b","iconcls":"folder","disabled":false,"leaf":false},
{"text":"a.txt","iconCls":"file-txt","disabled":false,"leaf":true,"qtip":"Size: 1047552"}
]

thank your helps in advance!


kk_kkk

kk_kkk
25 Sep 2008, 5:30 AM
hi, Saki:

after straight 2 day , i've finished the most function except upload ,it make me crazing.
so ,i come here to look for your help.

it is very strange. i know that you code is server side independent.

so I selected java servlet as my serverside laguage. the structure of processing fuction of serverside was just like this:

function_name(.....){
String cmd = request.getParameter("cmd"); // get the cmd parameter from client.
if(cmd.equal("get")){
..... //refreshing the tree view.
}
if(cmd.equal("download")){
....//downloading the selected file..
}
if(cmd.equal("upload")){
....//upload files....
}
}

all the operation used this entrance function ,i tested most operation except upload, they worked well, but when i tried to test the file upload by selecting the file , and clicking the button "upload",,exception happened.
it jumped the function precisely but it can not even get the value of 'cmd' parameter, so it can not enter the related code to going on ....(comment: i don't modified any js code except filetree.js , pointing my root and url by constructor)

after tracked the code with firebug. i find something in the fileuploader.js

,uploadFile:function(record, params) {
// request upload
Ext.Ajax.request(o);
// todo:delete after devel
this.getIframe.defer(100, this, [record]);
} // eo function uploadFile

It caused by the line "COLOR="Red"]Ext.Ajax.request(o);[/COLOR]" .. i check the object 'o' by firebug, and find ,when excute this line. every element and their values was well ,but when it was excuted , it jumped the funtion mentioned above, and can not get any values of any parameter from the client ,such as the value of 'cmd' and 'dir' was null.... that makes me sadly.
finally ,i insteaded that line with following code:

var temp = '../../../../../ext/filetree.do?method=loadFiles'+'&cmd='+'upload'+'&path='+this path
// request upload
Ext.Ajax.request(o);
thus,it works when runing. i can get the value the parameters via this . but , only those parameters connected by char '&'..

is this a bug? or it's my faults?

i coundn't sure if i expressed clearly .

so any useful tip and help is appreciated!!! :)

thanks in advance,, Bye

kk_kkk

Dumbledore
25 Sep 2008, 6:08 AM
Hi,

i try to use automaticly close of the uploadpanel in the allfinished event:


listeners: {
'allfinished': {
fn: function(e){
this.fileStore.reload();

uploadWin.close();
},
scope: this
}
}

With firefox all runs fine, with IE a Error occurs in updateButtons(). Can someone confirm this?

Bye, Dumbledore

mikecc
30 Sep 2008, 8:40 PM
Hey Saki, I've been slowly getting your FileTreePanel to work with my Ruby on Rails setup.

There is one thing that I am doing that I probably shouldn't be doing: passing more than is required than the "text" "leaf" and other fields that your plugin uses. The reason being is I am converting my objects in Ruby on Rails to straight JSON, and there are other fields in the model that are used for other parts of the code. I figured FileTreePanel would just ignore these other fields, and it has. But lately I have been getting some weird errors when having more than one leaf (which has a ton of extra info) and I am getting this error:

I.attributes[F] is undefined

It doesn't let me open up the Root by pressing the '+'. Firebug has more info below, with all the "undefined" words highlighted.

TreeDropZone()([Node 194] loaded=false loading=false childrenRendered=false, [Node ynode-41] loaded=false loading=false childrenRendered=false)ext-all....217306295 (line 107)
clearGrouping()(function(), undefined)ext-all....217306295 (line 55)
AsyncTreeNode()(function(), undefined)ext-all....217306295 (line 102)
TreeDropZone()([Node ynode-7] loaded=true loading=false childrenRendered=false)ext-all....217306295 (line 107)
EventManager()()ext-all....217306295 (line 12)
EventManager()()ext-all....217306295 (line 12)
TreeEventModel()("beforechildrenrendered", [Node ynode-7] loaded=true loading=false childrenRendered=false, undefined, undefined, undefined, undefined, undefined)ext-all....217306295 (line 99)
clearGrouping()("beforechildrenrendered")ext-all....217306295 (line 55)
AsyncTreeNode()(undefined)ext-all....217306295 (line 102)
AsyncTreeNode()(undefined, undefined, undefined)ext-all....217306295 (line 102)
TreeNodeUI()(undefined, undefined, undefined)ext-all....217306295 (line 103)
TreeNodeUI()(undefined, undefined, undefined)ext-all....217306295 (line 103)
getViewWidth()()ext-base...217306295 (line 9)
TreeFilter()(Object tId=0 status=200 statusText=OK)ext-all....217306295 (line 105)
TreeFilter()(undefined, undefined, undefined)ext-all....217306295 (line 105)
TreeFilter()(Object tId=0 status=200 statusText=OK)ext-all....217306295 (line 105)
getViewWidth()(function(), Object baseParams=Object url=/account/ftp events=Object, Object 0=Object 1=Object, undefined)ext-base...217306295 (line 9)
Updater()(Object tId=0 status=200 statusText=OK)ext-all....217306295 (line 17)
getViewWidth()(Object conn=XMLHttpRequest tId=0, Object scope=Object argument=Object timeout=30000, undefined)ext-base...217306295 (line 10)
getViewWidth()()ext-base...217306295 (line 10)

[Break on this error]
Ext.tree.TreeSorter=function(B,C){Ext.ap...dered){this.doSort.defer(1,this,[B])}}};

Can you shed any light on what could be going on?

jsakalos
30 Sep 2008, 11:48 PM
I cannot spot the error from the above stack trace. First, include ext-all-debug.js for development phase - it can give you more human readable output.

mikecc
30 Sep 2008, 11:55 PM
Saki, I fixed it. Basically, my JSON response was kind of broken, or least not one that FileTreePanel wanted. It was [ {json..............} ], []. The empty brackets were screwing things up.

mikecc
1 Oct 2008, 12:04 AM
Is there a way to pass the "node" number that gets passed along with folders? What field do I have to populate to get that node passed for files as well?

sekundek
1 Oct 2008, 12:09 AM
A while ago I was asking about controling downloading. I manage to get this work in FF but not with IE. In file FileTreePanel.js in function downloadFile:function(path) there is

Ext.EventManager.on(frame, 'load', callback, this);

I add some code in this callback but in IE this load is not fired at all. If I output clasic text or html in my download backend instead of binary download file event is fired.

Maybe I set the wrong Headers while downloading. I'm pulling my hair the last hours so I would be glad if someone have any clue what might be wrong here.

regards

Uros

jsakalos
1 Oct 2008, 12:10 AM
You can pass anything - it is added to node.attributes, the only question is how do you want to process it at client side.

mikecc
1 Oct 2008, 12:14 AM
Well then my question becomes, is there an easy way for leaves to also send the server their node # like for folders? I'm sorry, I do not understand how I would use node.attributes

jsakalos
1 Oct 2008, 12:17 AM
@sekundek, yes it can be because of headers. First of all, you have to send Content-Disposition: attachment header.

sekundek
1 Oct 2008, 12:29 AM
@jsakalos

Here is my headers

Response.AddHeader("Content-Type", type);
Response.AddHeader("Content-Disposition", "attachment; filename=" + name + "; size=" + buffer.Length.ToString());

code is aspx but this shouldn't be a problem.

jsakalos
1 Oct 2008, 2:00 AM
I have no specific idea about aspx. Anyway, you can download files from demo page (http://filetree.extjs.eu - right click on a file, open, download) so you could analyze headers in Firebug.

sekundek
1 Oct 2008, 4:04 AM
Could you send me your code from php responsible for sending download files. I've checked header and I see no difference.

regards

Uros

sekundek
1 Oct 2008, 5:13 AM
I found the sollution on this forum and for the moment it works

all you need to change is

Ext.EventManager.on(frame, Ext.isIE ? 'readystatechange' : 'load', callback, this);

Ie somehov does not work ok with load even when loading IFRAME. I found searching google, that manny folks have the same kinda problems and this one seams to solve it.

regards,
U

mikecc
1 Oct 2008, 7:10 AM
Saki, does "node" automatically store an id? If so, could I just append onto every command in the params section something like ,node:node.id ? I can't find in the code where the get command populates its node parameter to the server

jsakalos
1 Oct 2008, 8:13 AM
Well, I'm quite lost here. What problem are you trying to solve?

mikecc
1 Oct 2008, 10:12 AM
Saki, I am trying to write my upload action, and I noticed that when I upload a file, it is only sending the server the path, not the filename. How can I get the filename?

glaforge
3 Oct 2008, 5:16 AM
Hello,

I found the Ext.ux.UploadPanel here (http://filetree.extjs.eu/filetree.js) and found it really awesome!
So I decided to use in my application which uses Ext JS 2.2.

I've created a page containing this:



<html>
<head>
<title>Title</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<link rel="stylesheet" type="text/css" href="/js/ext/2.2/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="/js/ext/2.2/resources/css/xtheme-silverCherry.css" /


<script type="text/javascript" src="/js/ext/2.2/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="/js/ext/2.2/ext-all-debug.js"></script>

<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.form.BrowseButton.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.FileUploader.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.UploadPanel.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.FileTreeMenu.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.FileTreePanel.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.ThemeCombo.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.IconCombo.js"></script>
<script type="text/javascript" src="/js/ext/ux/js/Ext.ux.LangSelectCombo.js"></script>
<script type="text/javascript" src="/js/ext/ux/locale/fr_FR.js"></script>

<script type="text/javascript">
Ext.onReady(function() {

Ext.QuickTips.init();

var panel = new Ext.Panel({
width: 400,
height: 250,
layout: 'fit',
title: 'Upload',
renderTo: 'mydiv',
items: [{
xtype: 'uploadpanel',
buttonsAt: 'tbar',
id: 'uppanel',
url: 'filetree.php',
path: 'root',
maxFileSize: 1048576
}]
});
});
</script>
</head>
<body>
<div id="mydiv">
</div>
</body>
</html>
But when loading this page, I get the following error in FireBug:


types[config.xtype || defaultType] is not a constructor
create()()ext-all-debug.js (ligne 12296)
createComponent()(Object xtype=uploadpanel)ext-all-debug.js (ligne 14198)
lookupComponent()(Object xtype=uploadpanel)ext-all-debug.js (ligne 14192)
add()(Object xtype=uploadpanel)ext-all-debug.js (ligne 14104)
initComponent()()ext-all-debug.js (ligne 14039)
initComponent()()ext-all-debug.js (ligne 15815)
Component()(Object width=400 height=250 layout=fit)ext-all-debug.js (ligne 12354)
getViewWidth()()ext-base.js (ligne 9)
getViewWidth()()ext-base.js (ligne 9)
getViewWidth()()ext-base.js (ligne 9)
(?)()()test (ligne 41)
fire()()ext-all-debug.js (ligne 1488)
fireDocReady()()ext-all-debug.js (ligne 1592)
getViewWidth()()ext-base.js (ligne 10)

chrome://firebug/content/blank.gif return new types[config.xtype || defaultType](config);
So it seems I'm doing something wrong, but I've not idea what :(

Can anyone shed some light on this?

Thanks in advance for your help.

Guillaume

jsakalos
3 Oct 2008, 3:20 PM
Check paths of include files.

glaforge
3 Oct 2008, 3:24 PM
Check paths of include files.

You're spot on, this was indeed a problem with my include files.
This was a stupid error :">

Thanks a lot for your help and for this great upload system!

kk_kkk
5 Oct 2008, 9:10 PM
great plugin!!

mikecc
6 Oct 2008, 6:34 PM
Saki, I know I keep asking questions, but I am able to recreate many weirdness issues on your website.

1.) If I select a file to upload, and then i press the individual button to the right of the file, it gets rid of the file and the OPTION to add files again. The button just disappears. This happens in both IE and Safari.

2.) It seems that once the file tree gets large enough, i can't place my mouse over some folders. Well I can, but instead of highlighting the folder when you click on it, it highlights some leaf node in an already expanded directory, not allowing me to collapse/expand the folder, or access the little menu associated to it.

dport
6 Oct 2008, 6:44 PM
Im noticing some of the same issues mikecc is, however I'm using IE7/8 on win 32/64), and FF3 on linux.

>>1.) If I select a file to upload, and then i press the individual button to the right of the >>file, it gets rid of the file and the OPTION to add files again. The button just >>disappears. This happens in both IE and Safari.

I see this as well.

>>2.) It seems that once the file tree gets large enough, i can't place my mouse over >>some folders. Well I can, but instead of highlighting the folder when you click on it, it >>highlights some leaf node in an already expanded directory, not allowing me to >>collapse/expand the folder, or access the little menu associated to it.

I see this also.

I would like to add another issue as well. When I am able to right click a specific directory and wish to add a sub dir to that, I can do it, though the new directory ends up having any subdirs of the parent it was added to inside of it, until I use right click--> reload (sometimes the reload option is not present due to scenario #2 by mikecc) the directory ends up having all the subdirs in the parent dir it was created in inside of it.

Saki, great plugin and I realize this is beta, so keep up the good work.

David P.

jsakalos
6 Oct 2008, 11:30 PM
1) is issue with BrowseButton - I'm thinking of replacing it with Ext FileUploadField.
2) I couldn't reproduce it in FF3@Linux - is it an IE only issue?
3) same as 2

dport
7 Oct 2008, 9:50 AM
Saki, Im attaching a screenshot of FF3/Linux, Im going to narrate it out first:
1. Expanded the entire directory Private (which is above the scroll area shown) and subdirs you see on the screen
2. Left click on anything once, I chose Public in this case
3. Right click on Public expecting to have the panel give options for that directory
4. The panel shows a file that is in a collapsed directory (Xbox)
5. If I upload a file, it will appear in the collapsed directory (Xbox)

I would expect my right click on Public to perform operation in Public not in Xbox.

Hope this helps to diagnose this problem, I can give you the URL if you would like to see this behavior yourself.
http://macd.shacknet.nu/~david/pics/Screenshot.jpg

jsakalos
7 Oct 2008, 9:52 AM
PM me please url and credentials (if required). I want to see it.

jsakalos
7 Oct 2008, 2:57 PM
I've taken look at your page, I see the behavior you describe, but I'm not able to reproduce it on my page (http://filetree.extjs.eu) by expanding tree (expand all from context menu). What is the difference?

jsakalos
7 Oct 2008, 2:58 PM
BTW, I've seen two same subdirectories "New Folder" in one directory. I don't know how that is possible but filesystems do not allow that. Are you using my backend?

dport
7 Oct 2008, 3:33 PM
We use a rails backend, but it links to physical locations on the filesystem, in this case (and Im not sure how its happening) New Folder exists in the database 2x, with a reference to the same physical directory. I think we just need to add some validation there to our database model to correct it.
As far as the context menu expand all, it cant seem to target all the top level directories at once, In my panel I have:

rootPath:"../public/uploads/<%=session[:user]%>"
which loads the root as the user id, say 9/ Physically on the filesystem it is 9/Private 9/Public 9/Etc....

rootVisible:false, should I set this to true to gain the ability to have the context menu affect all of the current top level directories?

Thanks

jsakalos
7 Oct 2008, 3:40 PM
Well, I know nothing about rails. In any case, follow my backend guide to the letter.

One more area to check: do you send node ids from the server? I don't use them but if you supply them and if they are duplicated (not unique) tree could get that crazy...

zombeerose
8 Oct 2008, 12:06 PM
Hi Saki,
I was curious if you were continuing the development of filetree or if you have reached a stable point where you are essentially only providing support?

I ask this because I'd like to see about expanding some of the failure events to include the response object. This way, I can examine the response for additional error information. Could this be something you add into the base or am I better off extending your FTP class?

Thanks for the awesome work!

incaic
8 Oct 2008, 12:30 PM
Great Extension Saki!

I was wanting to either append or replace baseParams.
Is there an easy way of doing this?

jsakalos
8 Oct 2008, 12:50 PM
Hi Saki,
I was curious if you were continuing the development of filetree or if you have reached a stable point where you are essentially only providing support?

I ask this because I'd like to see about expanding some of the failure events to include the response object. This way, I can examine the response for additional error information. Could this be something you add into the base or am I better off extending your FTP class?

Thanks for the awesome work!
I'll get back to FileTreePanel in a month or so as I'll need it in my app by then. Then it reaches a stable version.

jsakalos
8 Oct 2008, 12:52 PM
Great Extension Saki!

I was wanting to either append or replace baseParams.
Is there an easy way of doing this?
baseParams of what? FileTreePanel, FileUploader?

incaic
8 Oct 2008, 12:58 PM
FileUploader

I actually wanted to replace the whole baseParams format with something
custom to work with my backend, but noticed {path:this.path} was hard coded
in getParams function in Ext.ux.FileUploader.js.

Am I reading this correctly? No way to replace entire baseParams without
overriding function getParams?

jsakalos
8 Oct 2008, 1:38 PM
Hmmm, currently not.

JoyfulBobHome
10 Oct 2008, 5:53 PM
Look like a great extension! Thanks, Saki!

A question though. Does this just manage docs in a folder structure on the server? Or can it be adapted to be used over a network drive that Windows Explorer can access?

I ask because we have a need for a web-based document tree structure similar to Windows Explorer - that would build a tree folder structure from a network drive (that already holds lots of docs in a 2-level folder structure) and allow direct uploads/folder maintenance on that drive, and not on a server database file that stores just the structure without the actual PC docs.

I hope I explained that well.

Thanks in advance.

jsakalos
11 Oct 2008, 10:43 PM
FileTree doesn't assume any platform neither client OS/browser nor server side OS/Scripting Language.

JoyfulBobHome
15 Oct 2008, 4:41 AM
FileTree doesn't assume any platform neither client OS/browser nor server side OS/Scripting Language.

Thanks for the reply. So you're saying the tree is just a database file on the server that gets maintained? It's not actually uploading/changing locations of the actual files themselves, only the directory to them via the tree?

jsakalos
15 Oct 2008, 10:58 AM
Not exactly. I'm saying that FileTreePanel doesn't need to know how files are handled serverside. My example really saves them to the filesystem but FileTreePanel itself runs at client and if files were saved to a database, FileTreePanel wouldn't mind.

mxracer
30 Oct 2008, 1:04 PM
First I just wanted to say nice work.

I don't know if this has already been brought up or is a known issue to you. There are too many posts in this thread to read through. At least I don't have time to :)

I notice that while viewing 'http://filetree.extjs.eu/' in Internet Explorer 7:

In the Upload Panel (the dialog), if you add a file and then delete it, the 'Add' button in the toolbar disappears.

To fix this on my end I made the following change in Ext.ux.UploadPanel.js here:



,onRemoveFile:function(record) {
...

//var wrap = inp.up('em');
var wrap = inp.findParentNode('em', 1, true);

...
}


This is working form me now.

jsakalos
30 Oct 2008, 1:12 PM
Thanks for finding the patch.

ZooKeeper
4 Nov 2008, 8:12 AM
Are you planning to update the source on the examples page?
The date states march, so I think there're some improvements and fixes over this time.

Thanks

jsakalos
4 Nov 2008, 8:47 AM
I'm going to need this component in the upcoming phase of my main project so then should come out a stable version. Stay tuned.

zombeerose
13 Nov 2008, 7:42 AM
When working on your next release, would you consider providing a means by which feedback can be returned to the client during a download attempt? Since the actual d/l occurs inside a frame, I don't know the best way to accomplish this.

The approach we implemented here was to override your downloadFile method and make an initial ajax request to verify the file is valid and actually exists. Assuming this succeeds, then the original downloadFile method is called. This is better than nothing but could still fail if a) something happens to the file in between the time of the first request and the iframe/form post or b) something happens to the file while downloading since no feedback would be returned. Another drawback of our approach is it results in a few JS errors in the browser, which I believe are related to timing issues.

The reason for all of this is because we are working towards using your FTP for accessing shared folders. Essentially, we are trying to prevent, or minimize, any multiple user conflicts.

Thanks for everything Saki!

jsakalos
13 Nov 2008, 11:57 PM
Hmmm, I though download will never need that. I assumed that it's almost like a static link on a download page...

sanraj
14 Nov 2008, 12:11 AM
Hi ,

I am using Ext.ux.Uploadpanel extension.
Can anyone tell me how progress panel works (any demo).
Also can i get backend php code of this extension.

thanks in advance

jsakalos
14 Nov 2008, 1:00 AM
I've made progress indicator at http://filetree.extjs.eu a little bit more visible. It's moving background color (#e0e0e0) of file name being uploaded.

pflammer
15 Nov 2008, 2:59 PM
I've noticed a lot of question on this thread about the Ext.ux.form.BrowseButton concerning losing clicks in IE6/7. I am posting a link to the resolution here so that people who see the problem using the filetree can implement it.

The link is:
http://www.extjs.com/forum/showthread.php?p=251250#post251250

David

zombeerose
15 Nov 2008, 9:33 PM
Sounds like you wrote such an awesome component that it is being used in more extensive ways than originally intended :)


Hmmm, I though download will never need that. I assumed that it's almost like a static link on a download page...

ljaeren
16 Nov 2008, 1:13 AM
I am looking for detailed information on how to add a panel as a menu item, just like your context menu.

jsakalos
16 Nov 2008, 2:48 AM
There is not other such information in addition to the source code. Read the source please, it is neither difficult nor tricky.

ljaeren
16 Nov 2008, 10:29 AM
Jozef, I just overlooked the FileTreeMenu.js file. Thank you. Yes it looks simple.

I am an Information Architect working to build a prototype of a design concept that will radically change the design of my company's ecommerce site, and adopting Ext JS components is a key to the success of the design concept. I have successfully sold Ext JS to other companies and now I am trying to sell Ext JS again by demonstrating my design concept. I am hopefully that my company will purchase a site license to employ Ext JS components.

Unfortunately, I do not have the software engineering expertise to implement my designs by myself, but I know enough Javascript to build prototypes.

As a professional user interface designer & information architect with an expertise in designing desktop and online applications, I want to tell you that I am impressed with Ext JS. Ext JS has developed components that embrace sound design and usability principles that successfully migrate desktop interactions to the web, often improving on the interaction design because of your innovations and the advantages that web technologies offer.

Best regards

jsakalos
16 Nov 2008, 5:03 PM
Thank you and wishing you good luck with Ext. :)

sanraj
18 Nov 2008, 3:12 AM
I've made progress indicator at http://filetree.extjs.eu (http://filetree.extjs.eu/) a little bit more visible. It's moving background color (#e0e0e0) of file name being uploaded.


Hi
I have tried that one but progress bar is not working properly when "singleUpload: false"

Is there anything wrong in requestProgress function at following code
I get asynchronous response.




if(this.singleUpload) {
o.params[this.progressIdName] = this.progressId;
o.params.APC_UPLOAD_PROGRESS = this.progressId;
Ext.Ajax.request(o);
}
else {
records = this.store.query('status', 1);
records.each(function(r) {
console.log(this.progressIdName+"-"+r.get('progressId'))
o.params[this.progressIdName] = r.get('progressId');
o.params.APC_UPLOAD_PROGRESS = o.params[this.progressIdName];
o.record = r;
(function() {
Ext.Ajax.request(o);
}).defer(250);
}, this);
}




Can you please tell me what I need to change if I want to start second file upload when first uploading is done completely.

Thanks in advance......

jsakalos
18 Nov 2008, 4:15 AM
Does the indicator work on demo page but not for your implementation? If that is the case, you also need to install uploadprogress PHP extension if you use php and write a script that delivers progress information.

sanraj
18 Nov 2008, 4:44 AM
I have written the script which returns data like following

{success: true,bytes_total: 12796650,bytes_uploaded: 5294400}The script works fine when uploaded one file. But data return while uploading multiple files is inconsistent
so progress bar also inconsistent.Can you tell me how can i change inthe code at
records.each(this.uploadFile, this);
so i can upload next file only after upload of first in finished.

jsakalos
18 Nov 2008, 11:23 AM
You should be able to achieve sequential upload when you set singleUpload:true. In this case all file input fields are contained in one form and this one form is submitted. The rest depends on server implementation.

zombeerose
18 Nov 2008, 2:17 PM
Is there an easy way to customize the context menu? I figure I can override the getContextMenu method and append my new menu item(s) but what if I wanted to change the order or add/remove other items?

Your suggestions would be greatly appreciated. Thank you

jsakalos
18 Nov 2008, 5:36 PM
Just go ahead and override, extend or fully replace the provided context menu.

sanraj
19 Nov 2008, 7:56 AM
Hi saki,

I have done code for one by one upload




,manageUpload:function(){
var recordIndex = this.store.find( 'status', 0, 0 );

if(recordIndex != -1){
var record = this.store.getAt(recordIndex);
this.uploadFile(record, this);


if(true === this.enableProgress) {
this.startProgress();
}
}
}


But I want to call requestProgress(); method atleast one time while uploading to get file Size when upoading starts. It fails if file is small.

How can i get file size from the server for every upload?

Thanks

jsakalos
19 Nov 2008, 11:10 AM
It depends on your server implementation but generally you can send an Ajax request and process the response which should contain the size.

iDevelopment
20 Nov 2008, 9:03 AM
Hi Saki...

First: Nice work and nice support! Keep on the good work! :)

I've got a (i doubt very stupid) problem - I'm working on a Connection to our CMS to upload files with the UploadPanel...

My Object looks like this:

[CODE]

this.pn_Upload = new Ext.ux.UploadPanel
({
url:'/cgi-bin/index.pl',
cmd: 'upload',
id:'up_upload' + this.instanceName,
path:'root',

baseParams:
{
SID: this.sid,
COMMAND: 'INTOS_V5_UPLOADPANEL',
},

singleUpload: false,
enableProgress: false,
clickRemoveText:'Hier klicken zum entfernen.',
addText:'Hinzuf

jsakalos
20 Nov 2008, 9:54 AM
baseParams should be propagated down to FileUploader:


// create uploader
var config = {
store:this.store
,singleUpload:this.singleUpload
,maxFileSize:this.maxFileSize
,enableProgress:this.enableProgress
,url:this.url
,path:this.path
};
if(this.baseParams) {
config.baseParams = this.baseParams;
}
this.uploader = new Ext.ux.FileUploader(config);




You can check if they really are.

iDevelopment
21 Nov 2008, 12:12 AM
I did... And even in the Ext.ajax.Request they still exist... But i can't get them on the serverside and even the Firebug Console ( I can see the request ONLY under network ) tells me there are no post request params...


EDIT!!!!!!!!!!!!!

TESTED IN CHROME (SINCE I GOT SOME IE-BUGS, I CAN'T USE IE FOR TESTING) - IT SEEMS TO WORK - I'M GETTING THE SID PARAM...

IS THIS A FIREFOX V3 BUG???

EDIT2!!!!

G

iDevelopment
21 Nov 2008, 2:56 AM
Sorry for my shouting ^^ This made me crazy... Uploads working fine!

I've got something to contribute i'm missing:

Why is there no filefinished event? I relayed it down and it works really nice!!!



this.uploader = new Ext.ux.FileUploader(config);

// relay uploader events
this.relayEvents(this.uploader, [
'beforeallstart'
,'allfinished'
,'filefinished' // i think its very helpful
,'progress'
]);

// install event handlers


Maybe you can use it!

iDevelopment
21 Nov 2008, 4:49 AM
And another contribution...

I couldn't find a way to examine the server responses for each, is there a way? If not, now there may be:

File: Ext.ux.FileUpload.js



/**
* called for both success and failure. Does nearly nothing
* @private
* but dispatches processing to processSuccess and processFailure functions
*/
,uploadCallback:function(options, success, response) {

var o;
this.upCount--;
this.form = false;


// process ajax success
if(true === success) {
try {
o = Ext.decode(response.responseText);
}
catch(e) {
this.processFailure(options, response, this.jsonErrorText);
this.fireFinishEvents(options, response.responseText);
return;
}
// process command success
if(true === o.success) {
this.processSuccess(options, response, o);
}
// process command failure
else {
this.processFailure(options, response, o);
}
}
// process ajax failure
else {
this.processFailure(options, response);
}


this.fireFinishEvents(options, response.responseText);

} // eo function uploadCallback



and



/**
* Fires event(s) on upload finish/error
* @private
*/
,fireFinishEvents:function(options, responseText) {
if(true !== this.eventsSuspended && !this.singleUpload) {
this.fireEvent('filefinished', this, options && options.record, responseText);
}
if(true !== this.eventsSuspended && 0 === this.upCount) {
this.stopProgress();
this.fireEvent('allfinished', this);
}
} // eo function fireFinishEvents




You see i added the response.responseText as additional event parameter so i can return more than success:true from the server

(In our case i need to return the md5'ed filename for convienience ^^)

So i return a

{"success":true, "md5":{"myfile.csv":"2d4e9ab69526a036424c2596e4721e4e.csv"}}

and catch it from the response.responseText as third parameter in event filefinish

carl23934
21 Nov 2008, 5:18 AM
I have found when trying to debug HTTP with firebug, firebug can be extremely wrong sometimes. I settled on using a simple software proxy. You see the exact request, and the exact response.

http://www.siliconwold.com/interceptor/interceptor_home.htm

Download, start, point firefox to 127.0.0.1:81 for the proxy, and debug!

Dumbledore
24 Nov 2008, 4:22 AM
currently i debug my project and found that following in Ext.ux.FileTreePanel.js is never fired in Firefox 3.0.4:


/**
* requests file download from server
* @private
* @param {String} path Full path including file name but relative to server root path
*/
,downloadFile:function(path) {

[...]

var callback = function() {
Ext.EventManager.removeListener(frame, 'load', callback, this);
setTimeout(function() {document.body.removeChild(form);}, 100);
setTimeout(function() {document.body.removeChild(frame);}, 110);
};

Ext.EventManager.on(frame, 'load', callback, this);

form.submit();
}

Can someone confirm this?

jsakalos
24 Nov 2008, 9:46 AM
I've tested it while developing and it worked. This is important part as it cleans up hidden frame/form.

Dumbledore
24 Nov 2008, 11:04 PM
Hi Saki,


I've tested it while developing and it worked. This is important part as it cleans up hidden frame/form.

it worked not on the download-command. I try this on your demo page http://filetree.extjs.eu/.
In the Filetree chose a file and then click open/download. After the file is downloaded the iframe is still there...

Bye, Dumbledore

jsakalos
25 Nov 2008, 3:11 AM
Hmmm, it looks like browser doesn't fire the load event... No easy solution w/o digging in comes to my mind...

Pachat
27 Nov 2008, 9:08 AM
First, let me thank you for your extension. I made a donation and got back filetree.php and it works great.

The behaviour I would like to achieve is to show subtrees only to authorized users.
The subtree varies depending on the user

Root
|--Sub1
|--Sub2

Let say,
User1 can only see Sub1,
User2 can only see Sub2

As the filetree is only seen when connected, I can modifiy 'root' in path and rootPath in filetree.js so they will only see Sub1 (User1) or Sub2 (User2).

But when they double click on the file in order to see the file, then become able to see the complete url.
So User2 could change Sub2 in Sub1 in the url in order to find some files in Sub1 where he is not allow to go.
The properties on the server (LAMP) are set so he cannot list directories.
But he could still guess some commun names to get files normally forbiden for him.

juandj
28 Nov 2008, 10:44 AM
Hi Saki,

I've been looking at this for some time, and I haven't been able to find a way to retrieve a node of the filetree by just having its path. I'm integrating the filetree with a dataview to achieve something similar to a windows-explorer like interface..

I am trying to keep your code untouched to make it easier to update later on and so far that has worked. Concisely, is there a way to retrieve a node by path?


Any help would be greatly appreciated.

juandj
28 Nov 2008, 6:10 PM
Hi Saki,

I've been looking at this for some time, and I haven't been able to find a way to retrieve a node of the filetree by just having its path. I'm integrating the filetree with a dataview to achieve something similar to a windows-explorer like interface..

I am trying to keep your code untouched to make it easier to update later on and so far that has worked. Concisely, is there a way to retrieve a node by path?


Any help would be greatly appreciated.

I just realized that since nodes load live, it is not possible to retrieve nodes by path accurately unless the entire tree is loaded :|

Shockdoc1
2 Dec 2008, 12:18 PM
Is there any possibility to fix the BrowseButton in Opera? It won't show up the dialog.

Thanks in advance

jsakalos
2 Dec 2008, 1:09 PM
There have been some improvements of BrowseButton - try to see the BrowseButton's thread.

juandj
2 Dec 2008, 2:12 PM
I just realized that since nodes load live, it is not possible to retrieve nodes by path accurately unless the entire tree is loaded :|

In case somebody is interested, I solved my original problem (referencing treenodes by filepath) by adding this function to the filetreepanel. Im sure there are easier ways to do so, but this seems to do the trick.



getNodeByPath: function( path, rootNode ){

if( !rootNode ){
return null;
}

if( path[0] == '/' ){
path = path.substring(1);
}

if( this.getPath( rootNode ) == path ){
return rootNode;
}

if( rootNode.childNodes.length > 0 ){
for( childNode in rootNode.childNodes ){
if( typeof( rootNode.childNodes[childNode] ) == 'object' ){
foundNode = this.getNodeByPath( path, rootNode.childNodes[childNode] );
}

if( foundNode != null ){
return foundNode;
}
}
}

return null;
}

,selectNodeByPath: function( path, expand ){
treeNode = this.getNodeByPath( path, this.getRootNode() );

if( treeNode == null ){
return false;
}

treeNode.select();

if( expand ){
treeNode.expand();
}
}


Note that this code will only detect nodes that have been loaded (e.g. it will not find a child node of a folder that has never been expanded.. Despite that, the function is useful if you are integrating the filetree with some other component that requires more advanced interaction.

vmorale4
7 Dec 2008, 4:31 PM
Hi,

I noticed that when a folder is marked as disabled, the upload menu does not render properly in Firefox. Instead of graying out the menu, a big white rectangle appears

Attached is a screenshot comparing IE7, Safari 3.2 and Firefox 3.0.4 (All in Windows)



Terrific job otherwise! :)

Pachat
9 Dec 2008, 8:16 AM
How to remove automatically from the Upload panel a file that is correctly uploaded?
(instead of keeping it in the Upload panel with an "Ok" icon)

Pachat
9 Dec 2008, 11:49 AM
From an event in a grid, is it possible to set dynamically the path and rootpath of the uploadpanel and the FileTreePanel ?
I found how to refresh the filetreepanel, but I'd like it to show specific directories only, depending on the current row of the grid.

Any hint to achieve this is Welcome!

jsakalos
9 Dec 2008, 5:54 PM
tree.getRootNode().path = '/new/path';
tree.getRootNode().reload();

martinrame
17 Dec 2008, 6:05 AM
Hi Saki, thank you for this awesome component. What I'm trying to do is to add columns to the tree, to add some information such as number of files in a folder, or file size, for example.

Can you give me some pointers?

Thank in advance.

jsakalos
17 Dec 2008, 7:30 AM
File size is already there - in tooltip. Be in your shoes, I'd deliver the information from server and displayed in tooltip. This is unbelievably easy.

martinrame
17 Dec 2008, 8:27 AM
Yes, I found the tooltip, but what about adding columns?

jsakalos
17 Dec 2008, 9:28 AM
I don't use column trees at all => I don't know.

vmorale4
17 Dec 2008, 2:21 PM
I noticed that if add a listener to the contextmenu event after the treepanel object has been created my handler is not fired. However if I add the listener in the constructor it does work. Is this intended?

For example this works:


var treepanel = new Ext.ux.FileTreePanel({
height: 400
, width: 300
, url: 'filetree.ashx'
, id: 'ftp'
, title: 'Share files'
, renderTo: 'treepanel'
, rootPath: 'root'
, topMenu: true
, listeners: { 'contextmenu': function() { console.log('clic'); } }
});


But this doesn't:



var treepanel = new Ext.ux.FileTreePanel({
height: 400
, width: 300
, url: 'filetree.ashx'
, id: 'ftp'
, title: 'Share files'
, renderTo: 'treepanel'
, rootPath: 'root'
, topMenu: true
});

treepanel.on( {'contextmenu': function() { console.log('clic'); } });


Am I missing something?

jsakalos
17 Dec 2008, 6:57 PM
These should be equivalent...:-?

vmorale4
18 Dec 2008, 4:36 PM
These should be equivalent...:-?

Found the problem. The built-in 'onContextMenu' handler is returning a false value after executing. If you change it to true then it works (line 1125):




onContextMenu: function(node, e) {
if (this.readOnly) {
return false;
}
this.showContextMenu(node);

return true; //I changed this to true
} // eo function onContextMenu



I tested it in Chrome, Firefox 3, Opera, IE7 and I didn't notice any side effects of changing that value (in Windows)

vmorale4
18 Dec 2008, 5:00 PM
What do you think of adding an 'alias' attribute for tree nodes?

In the project that I'm working some of the 'physical directory' names are different, but for usability purposes I am displaying a 'User-friendly' name.

When the FileTreePanel executes a GET cmd, the server response normally looks like this:


[{"text":"simple_folder","iconcls":"folder","disabled":false,"leaf":false}]

However, let's say for example we have a directory in the server called 'ftrpnl' but I want my users to see 'File Tree Panel' instead. Hence the response from the server would be:


[{"text":"File Tree Panel","iconcls":"folder","disabled":false,"leaf":false, alias:'ftrpnl'}]

To make this work I updated the getPath method so that it first looks at the alias attribute (if defined). If alias is undefined is assumed that the directory name is identical in the server.

It is a simple change, (two lines of code):


/**
* returns path of node (file/directory)
* @private
*/
, getPath: function(node) {
var path, p, a;

// get path for non-root node
if (node !== this.root) {
p = node.parentNode;
a = [node.attributes.alias || node.text]; //changed vmorale4
while (p && p !== this.root) {
a.unshift(p.attributes.alias || p.text); //changed vmorale4
p = p.parentNode;
}
a.unshift(this.root.attributes.path || '');
path = a.join(this.pathSeparator);
}

// path for root node is it's path attribute
else {
path = node.attributes.path || '';
}

// a little bit of security: strip leading / or .
// full path security checking has to be implemented on server
path = path.replace(/^[\/\.]*/, '');
return path;
} // eo function getPath


I called the new attribute alias, but I'm open for ideas for other name (serverName, name, link, etc..)

It would be great if you could incorporate that change :D

mjlecomte
18 Dec 2008, 5:18 PM
From a security point of view wouldn't be better if the server returned an 'alias' in the first place?

vmorale4
18 Dec 2008, 5:35 PM
From a security point of view wouldn't be better if the server returned an 'alias' in the first place?


Yes that might be true but then determining the 'real' path becomes significantly more complex when you are using an alternate name for every subdirectory because the server would need to map-back every directory traversed in the 'virtual path' to determine the real location.

Instead I'm using a mixed approach. The root directory name for the application is 'fake', but every directory under it isn't.

So the URL to download a file would look something like this in my app:


http: //myserver.com/FileSharing/viewFile.ashx?path=root/0945340/presentation.ppt

viewFile.ashx only needs to 'map back' the root directory (which is an application setting pointing to C:\mystorage)

The real location of presentation.ppt in this case is c:\mystorage\0945340\presentation.ppt

jsakalos
19 Dec 2008, 9:24 AM
Hmmm, I'm not sure if I'm ever going the "alias" direction. I think it's space for further extensions of users.

vmorale4
19 Dec 2008, 1:17 PM
I hate to barge in an existing support conversation, but I was just curious if anyone has noticed the following issue in FF3 (could happen in other browsers too) :

After a file is uploaded via the UploadPanel , you will see the browser remain in a "transferring data" status. Im assuming the connection is not closed properly or is stuck in a loop somewhere.

I would try to pinpoint this myself but I am trying to meet a deadline with a project and I need to get other sections done atm.

Your help is always appreciated ;)


Does it happen only with UploadPanel or also with standard Ext upload?


I noticed this as well.

AFAIK it only happens with your UploadPanel, the standard Ext upload component doesn't exhibit this problem (at least in the example):

http://extjs.com/deploy/dev/examples/form/file-upload.html

jsakalos
19 Dec 2008, 2:04 PM
I have just tried to upload a file (a short one) to http://filetree.extjs.eu with FF-3.0.5@Linux and it works flawlessly. Does your server return a correct response?

vmorale4
19 Dec 2008, 2:11 PM
I have just tried to upload a file (a short one) to http://filetree.extjs.eu with FF-3.0.5@Linux and it works flawlessly. Does your server return a correct response?

Hmm, interesting... :-? It must be something related to the Windows port of Firefox only... I also just uploaded a small file (yserver.txt) to filetree.extjs.eu and I still get the 'Transfering data' message (FF-3.0.5@Windows XP)


The next step I'll take is compare the response headers when posting a file to your server vs posting a file in the Ext Upload component example

Rafael
21 Dec 2008, 5:24 PM
When i Load the file have a mesage error: Cannot decode Jsob object

upload.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="../css/ext-all.css">
<link rel="stylesheet" type="text/css" href="upload/css/icons.css">
<link rel="stylesheet" type="text/css" href="upload/css/webpage.css">
<link rel="stylesheet" type="text/css" href="upload/css/filetree.css">
<link rel="stylesheet" type="text/css" href="upload/css/filetype.css">
<link rel="stylesheet" type="text/css" href="upload/css/famflag.css">
<link rel="stylesheet" type="text/css" href="upload/css/Ext.ux.IconCombo.css">
<link rel="stylesheet" type="text/css" href="upload/css/empty.css" id="theme">

<script type="text/javascript" src="js/ext-base.js"></script>
<script type="text/javascript" src="js/ext-all-debug.js"></script>
<script type="text/javascript" src="upload/js/WebPage.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.form.BrowseButton.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.FileUploader.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.UploadPanel.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.FileTreeMenu.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.FileTreePanel.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.ThemeCombo.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.IconCombo.js"></script>
<script type="text/javascript" src="upload/js/Ext.ux.LangSelectCombo.js"></script>
<script type="text/javascript" src="upload/js/filetree.js"></script>
<title id="page-title">Gerar Boletos em PDF</title>
</head>

<body>
<div id="west-content" class="x-hidden">

</div>
<div id="center-content" class="x-hidden">

</div>
</body>
</html>

and my upalod.php

<?php
if( $_FILES ) {

require_once("../pdf/dompdf_config.inc.php");
$fp = fopen( $_FILES[file][tmp_name] ."/". $_FILES[file][name], "r");
while (!feof($fp)){
$old_limit = ini_set("memory_limit", "16M");
$dompdf = new DOMPDF();
$dompdf->load_html(fgetc($fp));
$dompdf->set_paper("a10", "portrait");
$dompdf->render();
$dompdf->stream("dompdf_out.pdf");
exit(0);
}

fclose($fp);
echo {"success":true};

}
?>

jsakalos
22 Dec 2008, 12:50 AM
Server returns an invalid response, a json (text?) that cannot be decoded.

vmorale4
22 Dec 2008, 9:17 AM
When i Load the file have a mesage error: Cannot decode Jsob object


Server returns an invalid response, a json (text?) that cannot be decoded.

@Rafael: Are you using IE? I too found this issue today (works fine in Firefox).

With a little help from Fiddler tool, I found out that the request sent by IE differs from the request send by Firefox, in particular the filename sent by IE is an absolute path.

Firefox:
-----------------------------27350115016941
Content-Disposition: form-data; name="ext-gen144"; filename="readers.txt"
Content-Type: text/plain

IE:
-----------------------------7d813714a07f2
Content-Disposition: form-data; name="ext-gen106"; filename="C:\temp\readers.txt"
Content-Type: text/plain



My guess right now is that Ext.Ux.FileUploader l is not correctly stripping the path stuff in IE (haven't finish debugging the code yet)

As workaround you can change your server code to strip any text before the last index of '\'

I'll post a follow-up post later with a patch to Ext.ux.FileUploader

vmorale4
22 Dec 2008, 12:11 PM
OK I found out that IE *ALWAYS* sends the entire file path when sending form upload requests, so unfortunately there's no client-side workaround. You have to parse the filename server-side.


http://www.velocityreviews.com/forums/t99467-input-typequotfilequot-grabs-entire-file-path-in-ie.html

Rafael
22 Dec 2008, 12:41 PM
I test in Firefox and IE dont work in IE and Firefox.
man i ready this topic in the link but dont understand.
Please help me.

vmorale4
22 Dec 2008, 1:04 PM
I test in Firefox and IE dont work in IE and Firefox.
man i ready this topic in the link but dont understand.
Please help me.

Then most likely your problem is that the JSON that you are serving to the client is malformed. Install Fiddler and check the response that your server is giving to the browser. There's a 9 minute tutorial that shows you how to use it.

http://www.fiddler2.com/Fiddler/help/video/default.asp

Pachat
22 Dec 2008, 1:37 PM
tree.getRootNode().path = '/new/path';
tree.getRootNode().reload();


Sorry, but this does not work.
it continues to show the same files, althought path has changed.

Is it because I call the tree from a listener ?


Ext.getCmp("ftp").getRootNode().path = '/new/path';
Ext.getCmp("ftp").getRootNode().reload();


or because my tree needs to be emptied before I set a new path ?
(In that case, how do I empty a tree ?)

or for any other reason ?

MrEnirO
26 Dec 2008, 9:01 AM
Hi, i need some help.
Is there a way to do next thing whit Upload Panel: user uploads files, leaves the page, then comes back and sees what files he has uploaded and can delete them?
I know that FileTreePanel allows this, but i need Upload Panel to do this, is this possible?

jsakalos
29 Dec 2008, 12:03 AM
Not out of the box. UploadPanel doesn't keep any history of uploaded files.

MrEnirO
29 Dec 2008, 12:22 AM
Maybe there is a way i could load this data by store? If so, the what data do i need to get, and how do i set the file as uploaded, so that user would not upload it second time?

jsakalos
29 Dec 2008, 2:35 AM
The purpose of upload panel is quite simple: Provide an UI for file upload control. It doesn't assume any restrictions, such as double upload, whatsoever. I've left that to users.

I do not implement such logic myself so I have neither examples nor idea of how to do that.

basshcm
30 Dec 2008, 8:18 PM
Has anyone resolved the issue of the missing upload panel when folder is disabled in Firefox3.0.4??


http://extjs.com/forum/showthread.php?p=260064#post260064

jsakalos
31 Dec 2008, 1:16 AM
I haven't, I plan release of a stable version for February. Does the problem persist also in FF-3.0.5?

basshcm
31 Dec 2008, 5:59 PM
I haven't, I plan release of a stable version for February. Does the problem persist also in FF-3.0.5?

Yes the problem still exist in FF-3.0.5.

Right now I am getting around it by just hidding the upload panel if it isGecko and isDisabled. Ideally I would like for it just to be disabled like the other browsers.

Thanks

vmorale4
5 Jan 2009, 12:45 PM
In our application we keep extensions under it's own subdirectory, i.e:

/MyApp/ux/FileTree
/MyApp/ux/FileTree/css
/MyApp/ux/FileTree/img


I noticed that some icons were missing when the application was running, and it turns out that filetree.css and icons.css are using some icons included with the Ext distribution and assuming an erroneuos path in our case.

As a workaround I had to manually change the path to were ext resides, for example:

.ux-up-icon-uploading {
background-image:url(../../../ext/resources/images/default/grid/wait.gif);
}

I was thinking that perhaps it's a better idea to include a copy of those files with the FileTree distribution without assuming the location of the Ext directory. They are not that many files.. What do you think?

jsakalos
5 Jan 2009, 2:31 PM
No, I won't include them. I think it could violate Ext JS license. Correcting a couple of paths is not a big deal, is it?

vmorale4
5 Jan 2009, 3:13 PM
No, I won't include them. I think it could violate Ext JS license. Correcting a couple of paths is not a big deal, is it?

Sounds fair to me... Instead maybe you could add note about paths to the first post of this thread..

jsakalos
5 Jan 2009, 5:03 PM
Added. :)

zombeerose
9 Jan 2009, 8:57 AM
Maybe there is a simpler way to accomplish this but I was having a visual alignment issue when the context menu appeared near the lower border of the screen and then I added a file to get uploaded, which essentially extended the menu beyond the viewable area. If the user hovers over the menu item below the upload panel, then the entire screen shifts up to display the rest of the menu.

I believe this is only an issue in my case because I customized the context menu with a new choice that appears below the upload panel. None the less, I thought I would mention.

I added the following commented line in Ext.ux.FileTreePanel.js:


,getContextMenu:function() {
...
this.uploadPanel.on({
...
//need to recalibrate the location of the menu when a file gets added
,fileadd:{scope:this, fn:function() { this.showContextMenu(); }}
});
...
}
Saki, are you still working towards a new release in February? Thanks!

jsakalos
11 Jan 2009, 2:04 PM
February, more toward the end of, still looks feasible here...

adam.jimenez
22 Jan 2009, 1:44 PM
Hi Saki,

I've added chmod and "create new file" option if you are interested...

jsakalos
22 Jan 2009, 3:35 PM
I was also thinking about that when writing but then I dropped the idea. For chmod you should have root access, if you have chmod you should also have chown, chgrp and possibly setfacl. Also, unfortunately for them, some still use Window$ server...

So, I do not plan to add these two to mainstream code, of course, if you need it it's fine that you can add those items.

adam.jimenez
22 Jan 2009, 4:07 PM
I was also thinking about that when writing but then I dropped the idea. For chmod you should have root access, if you have chmod you should also have chown, chgrp and possibly setfacl.

u don't need root access to chmod..

jsakalos
22 Jan 2009, 4:51 PM
True.

rballman
23 Jan 2009, 2:06 PM
Is there a plugin for just the file uploader? I like the small upload window and do not need the full-blown tree window. Any ideas?

jsakalos
23 Jan 2009, 2:22 PM
FileUploader is extension not plugin. It has been designed to be used standalone. Anyway, if you want a simple upload (one file at a time) even that would be an overkill.

rballman
23 Jan 2009, 2:27 PM
FileUploader is extension not plugin. It has been designed to be used standalone. Anyway, if you want a simple upload (one file at a time) even that would be an overkill.

well I would like to have more than one file at a time uploaded...so maybe it wouldnt be overkill then?

jsakalos
23 Jan 2009, 2:50 PM
Yeah, then it is suitable.

Dumbledore
24 Jan 2009, 2:29 AM
Hi,

is there any news about this behaviour?

Bye, Dumbledore




currently i debug my project and found that following in Ext.ux.FileTreePanel.js is never fired in Firefox 3.0.4:


/**
* requests file download from server
* @private
* @param {String} path Full path including file name but relative to server root path
*/
,downloadFile:function(path) {

[...]

var callback = function() {
Ext.EventManager.removeListener(frame, 'load', callback, this);
setTimeout(function() {document.body.removeChild(form);}, 100);
setTimeout(function() {document.body.removeChild(frame);}, 110);
};

Ext.EventManager.on(frame, 'load', callback, this);

form.submit();
}

Can someone confirm this?

jsakalos
24 Jan 2009, 4:40 AM
A complete update/debug is to come soon - I'll start with it in a week or so...

adam.jimenez
26 Jan 2009, 4:58 AM
Hi Saki,

In my ap I would like the filetreepanel to initialize with a path already open.

e.g. in your demo the following branch could already be opened and selected:
/dossier/crystal_project/3d.png

I was thinking that this could be achieved with a loop that searches through the nodes and opens one directory at a time - but this would be very slow and inefficient.

It would be better if the initial 'get' command could send back the specified subfolder contents. Altho I assume I will need to hack filetreepanel to get this to work.

Please could you suggest the best way to achieve this.

jsakalos
26 Jan 2009, 5:25 AM
You could maybe deliver children for the node(s) that should be open initially from the server and then open (expand) and select the node.

Untested.

adam.jimenez
26 Jan 2009, 5:32 AM
You could maybe deliver children for the node(s) that should be open initially from the server and then open (expand) and select the node.

Untested.


Thanks Saki, I will give it a go. BTW your extensions are excellent, keep up the great work!

jsakalos
26 Jan 2009, 5:38 AM
Thanks, it is always good to hear words of praise... :)

adam.jimenez
27 Jan 2009, 6:10 AM
You could maybe deliver children for the node(s) that should be open initially from the server and then open (expand) and select the node.

Untested.


worked a treat. thanks Saki, you are the man!

dbraiden
5 Feb 2009, 7:12 AM
Hi All,

Im hoping that someone will be able to help me.
Im trying to get the upload panel to work.
I have it displaying correctly and i can add files to the panel. However when i hit upload i get the following error:
[Exception... "Component returned failure code: 0x80520001 (NS_ERROR_FILE_UNRECOGNIZED_PATH) [nsIDOMHTMLFormElement.submit]" nsresult: "0x80520001 (NS_ERROR_FILE_UNRECOGNIZED_PATH)" location: "JS frame :: PATH_TO_EXT/ext-all-debug.js :: anonymous :: line 5228" data: no]
PATH_TO_EXT/ext-all-debug.js
Line 5228

Has anyone encountered this error before.

Many thanks
Dave

jsakalos
5 Feb 2009, 8:41 AM
Is your url correct?

dbraiden
5 Feb 2009, 9:14 AM
ehhhh.. im embarrassed to say that yes the url was incorrect:D

Just one other quick question.

On my currenlt file upload page in php i use
$fileName = $_FILES['file']['name'];
$tmpName = $_FILES['file']['tmp_name'];
$fileSize = $_FILES['file']['size'];
$fileType = $_FILES['file']['type'];
to get the details of the file.

Do you have an example of a php script for doing this using your method

Many thanks in advance
Dave

jsakalos
5 Feb 2009, 9:21 AM
I want Saki's FileTree backend (http://extjs.com/forum/showthread.php?p=147056)

Voronchuk
2 Mar 2009, 10:12 AM
Sorry if similar question was already mentioned, but it'll take a huge amount of time to read all 50+ pages of this thread.

At first, thank you, Saki, for this brilliant extension. I used it in my project and it worked fine if used standalone.

I tried to write a small plugin like this:



Ext.namespace('Ext.ux.plugins');
Ext.ux.plugins.TemplateTreePanel = function(config)
{
Ext.apply(this, config);
};
Ext.extend(Ext.ux.plugins.TemplateTreePanel, Ext.util.Observable,
{
init: function(oPanel)
{
Ext.apply(oPanel,
{
onDblClick: oPanel.onDblClick.createSequence(function(oNode, oEvent)
{
alert('1');
//some code here
})
});
}
});

It does not work :(
The "init" method triggers as expected, but "onDblClick" does not working. Do anyone has any suggestions?

Thanks.

jsakalos
2 Mar 2009, 10:41 AM
Your plugin does not install a listener. Use oPanel.on('dblclick', ....) instead. Don't forget to remove the listener on oPanel destroy.

Voronchuk
2 Mar 2009, 10:55 AM
Thank you for attention. Adding "dblclick" event works fine, but I also need to remove standart "dbclick" function, which is defined in "onDblClick" config option of FileTreePanel:


onDblClick:function(node, e) {
this.openNode(node);
}


Any suggestions how to do this with plagin, without editing FileTreePanel extension file?

jsakalos
2 Mar 2009, 11:22 AM
Easiest:



var ftp = new Ext.ux.FileTreePanel({...});
ftp.onDblClick = Ext.emptyFn;

Voronchuk
2 Mar 2009, 11:32 AM
Used "onDblClick: Ext.emptyFn" as config option for FileTreePanel, worked idealy with my plugin. Thank you, Saki, you were most helpfull.

jsakalos
2 Mar 2009, 12:38 PM
Yes, that's even cleaner.

cwford
23 Mar 2009, 9:32 AM
Saki,

First, let me add my thanks to the long list of those very grateful to you for your efforts in developing this component. It is a central aspect of an online application that I'm developing.

I need to instantiate two upload panels, each one in a separate tab panel. The two panels are created correctly with both showing buttons. But no files are displayed in the second panel after selecting a file to upload from the browser.

Simple code:


var tabs = new Ext.TabPanel({
region: 'center',
margins: '3 3 3 0',
activeTab: 0,
defaults: {
autoScroll: true
},
items: [
{
// Config settings for warehouse #1
title: 'Warehouse #1',
xtype: 'uploadpanel'
, buttonsAt: 'tbar'
, id: 'uploadpanel1'
, url: 'upload.php'
, path: 'root'
, maxFileSize: 1048576
}, {
// Config settings for warehouse #2
title: 'Warehouse #2',
xtype: 'uploadpanel'
, buttonsAt: 'tbar'
, id: 'uploadpanel2'
, url: 'upload.php'
, path: 'root'
, maxFileSize: 1048576
}
]
});
Cannot figure this one out. Seems like it should work pretty easily, but it doesn't. Any help you can give would be greatly appreciated.

Best,

Clyde

jsakalos
23 Mar 2009, 10:00 AM
Hopefully I haven't messed it up in the first place. Take a look in my source code if I don't assign some hard-coded ids or something. It's long time since I've written it...

cwford
23 Mar 2009, 10:05 AM
Hopefully I haven't messed it up in the first place. Take a look in my source code if I don't assign some hard-coded ids or something. It's long time since I've written it...

Looking at the source right now. Both data stores get loaded, but the second doesn't get shown. Thought it might be the view element remained hidden after the new tab panel was revealed, but wasn't it either. Will check for id assignment next.

Thanks,

Clyde

cwford
23 Mar 2009, 10:14 AM
Saki,

Checked and you had "hard coded" the id for the store. Changed that and still no joy. Wondering if it could be the class assignments resulting in multiple divs with the same class?

Clyde

cwford
23 Mar 2009, 10:36 AM
Changed the css styles so they were unique for each instantiation of the UploadPanel, still no joy. Backtracking to Loeppky's browser button and the output it supplies just before a record is added to the panel store.

Clyde

cwford
23 Mar 2009, 10:54 AM
Saki,

Found the problem, but don't know how to fix it. It's a rendering problem. The filename retrieved after coming back from the Browse button is placed in the view element of the 2nd panel but it's not visible, and it's not the CSS apparently. I discovered this by accident when I ran the simple example under FF. At first I didn't have Firebug open and nothing appeared in the 2nd tab panel after I'd selected a file to upload. Then, I just opened Firebug and the file appeared in the 2nd tab panel as it should even though I did not do another selection from the browser.

It's not a FF problem because the same behaviour shows up on IE. So, the data's in the DOM, it's just not getting displayed for any instantiation of the UploadPanel other than the 1st.

Any thoughts?

Clyde

jsakalos
23 Mar 2009, 10:59 AM
Some layout ('fit'?) missing somewhere? Or rendering into a hidden container? Or rendering too early? Could be in my code.

cwford
23 Mar 2009, 12:00 PM
Saki,

It was a layout problem. The container's doLayout method needs to be called after a file is added to the store. Here's my revised code with that statement added.


onAddFile: function(bb) {
if (true !== this.eventsSuspended && false === this.fireEvent('beforefileadd', this, bb.getInputFile())) {
return;
}
var inp = bb.detachInputFile();
inp.addClass('x-hidden-' + this.id);
var fileName = this.getFileName(inp);

// create new record and add it to store
var rec = new this.store.recordType({
input: inp
, fileName: fileName
, filePath: this.getFilePath(inp)
, shortName: Ext.util.Format.ellipsis(fileName, this.maxLength)
, fileCls: this.getFileCls(fileName)
, state: 'queued'
}, inp.id);
rec.commit();
this.store.add(rec);

this.syncShadow();

this.uploadBtn.enable();
this.removeAllBtn.enable();

if (true !== this.eventsSuspended) {
this.fireEvent('fileadd', this, this.store, rec);
}

// Add the following statement to prevent layout errors in multiple instantiations of component:
this.doLayout();


I'm glad that I could make a tiny contribution to a wonderful piece of code.

Best,

Clyde

jsakalos
23 Mar 2009, 12:37 PM
Thank you. I'm adding it.

captainfish
24 Mar 2009, 6:29 AM
Hello,

I've been using this extension for a while and have a question about accessing the size of the file being uploaded. I've found the 'fileSize' property in Firefox, but it is buried in the DOM (ex. 'uploadPanel.uploader.store.data.items[i].data.input.dom.files[0].fileSize') This obviously doesn't work in IE as the DOM is quite different. Is this property located somewhere else more easily accessible? Or is there a method to use which avoids the differences in the DOM between Firefox and IE? Any guidance would be appreciated.

jsakalos
24 Mar 2009, 6:33 AM
At the time of the FileTreePanel writing I didn't know that the file size is available in Firefox so there is no support for it in the current version. Also, I have no idea how to access, if possible, fileSize in other browsers.

captainfish
24 Mar 2009, 7:27 AM
Ok Saki, thanks anyway. I'll try to handle it on the server. My problem is that 0 byte files fail to upload and I haven't quite figured out why. I was going to prevent the user from uploading 0 byte files on the client, but I'll just pass back an error from the server. If anyone is having a similar problem I'd be interested in hearing about it.

gonzalez
26 Mar 2009, 10:39 AM
Hi!

Can you provide me the source code of the php back-end?
I would like to write it in perl.

Cheers,
Gonzalez

jsakalos
26 Mar 2009, 10:44 AM
I want Saki's FileTree backend (http://extjs.com/forum/showthread.php?p=147056)

zombeerose
30 Mar 2009, 9:53 AM
I'll get back to FileTreePanel in a month or so as I'll need it in my app by then. Then it reaches a stable version.

Have you had any more time to work on FileTreePanel? Just curious

jsakalos
30 Mar 2009, 10:07 AM
Not yet... ;)

triptonemeister
3 Apr 2009, 7:24 AM
Is there any chance for this control work in opera?

jsakalos
3 Apr 2009, 8:48 AM
You mean context menu? AFAIK, Opera disables context menu without a chance to enable it. Nevertheless, you can use actions array to implement other form of interface.

triptonemeister
3 Apr 2009, 9:44 AM
No, in Opera (Mac) the Add button dosen't work, the file open dialog is not appear.

chunkT
21 Apr 2009, 3:58 AM
thx for this awesome extension saki!
found a little bug. if you use a rootPath that has only numbers in it, the getPath method in FileTreePanel will fail cause path.replace() throws an error.

i fixed it by simply adding


path = path.toString();

before


path = path.replace(/^[\/\.]*/, '');

greetz chunk

jsakalos
21 Apr 2009, 9:54 AM
Thank you very much for finding the bug and for providing the patch.

keckeroo
13 May 2009, 12:34 PM
Just really starting to heavily implement your uploadpanel routine and I (after a few hours of frustration .. ;-) noticed that you don't relay all of the fileuploader events to the uploadpanel.



this.uploader = new Ext.ux.FileUploader(config);

// relay uploader events
this.relayEvents(this.uploader, [
'beforeallstart'
// next to lines i added ....
//, 'beforefilestart'
//, 'filefinished'
,'allfinished'
,'progress'
]);


Is there a specific reason why the other two events 'filefinished' and 'beforefilestart' are not relayed to the panel ?

I need the panel to listen for each file to finish so i can upload a store with data regarding the uploaded file, extended data I hope to return from the server in the 'success' response of the filemanager routine (which i hope to pass back to the upload panel through the filefinished listener.

Thanks

Kevin

jsakalos
13 May 2009, 2:18 PM
Because the store is updated by uploader, if I remember well; it's long time since I've written it. There is also status in the store and that says if the field was uploaded or failed.

There is no problem with relaying the events you added so go ahead if it's better for you.

zombeerose
19 May 2009, 2:47 PM
Hi Saki,
I was wondering if you were working on migrating the FileTreePanel to the Ext 3 library yet. Thx!

jsakalos
19 May 2009, 2:52 PM
Not yet, Ext 3.0 is not at stable version yet. They I'll go through my extensions an make them compatible. Not sure if this one will be first in the list.

elona
20 May 2009, 4:25 PM
hi Saki,

i find ur extension very cool and i'm trying to use it but i get stuck on the server side code.
here is the js file not too different from urs


function uploadFile(){
var win = new Ext.Window({
width:260
,minWidth:160
,id:'winid'
,height:220
,minHeight:200
,layout:'fit'
,border:false
,closable:true
,title:'UploadPanel'
,iconCls:'icon-upload'
,items:[{
xtype:'uploadpanel'
,buttonsAt:'tbar'
,id:'uppanel'
,url:'fileUpload.php'
, method: 'post'
, baseParams: {
filename:'file',
path: 'root'
}
,maxFileSize:1048576
}]
});
and the fileUpload.php is like this.


<?php
header('Content-type: application/json, charset=utf-8');
$uploaddir = '/uploads/';
$uploadfile = $uploaddir . basename($_FILES['file']['name']);
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
echo json_encode({"succes": true});
} else {
echo json_encode({"success":false,"error":"errore!!!"});
}
?>
i can't see any response from the php file, it just displays the error "impossible to decode json object" on uploading. can you pls give me any hint about this?

thnx,
e.

juandj
20 May 2009, 7:55 PM
Hi Saki,

I was wondering if an SWFUploader version was ever integrated with v2... Is flash still broken on Suse? :)


Thanks in advance

juandj
20 May 2009, 8:03 PM
Hi Saki,

I was wondering if an SWFUploader version was ever integrated with v2... Is flash still broken on Suse? :)


Thanks in advance

Saki,

I believe the latest SWFUpload should work on linux now.. They've worked on flash for linux quite a bit, http://swfupload.org/forum/news/1370

I think it'd make this extension so much better to have multifile upload capabilities...


Thanks again

jsakalos
20 May 2009, 11:51 PM
Thank you for info. I was still unable to upload a big file to their demo page (http://demo.swfupload.org/v220/speeddemo/index.php). Anyway, it may deserve another look when I'll update FileTree.

BrainDrain
29 May 2009, 6:00 AM
Hi
I'm trying to implement ASP.NET server side app for filetree & upload(panel)
At first steps I've created HttpHandler that successfully handles post requests from client and responds with json object. It uploads files correctly but synchronously (client is waiting for handler response), enableProgress=false.
After that I've created HttpModule that catches upload requests and initiates uploading in backgroung. It works with HttpHandler fine, client receives progress (changed in web session cache by HttpModule background thread) from HttpHandler. The problem is: when I abort uploading (of large file, for ex.) - uploading keep processing. I couldn't catch request cancelling on server (HttpPostedFile object stream is alive, request object has no disconnecting indicators) without some special command request from client. Am I right?
And one more thing. singleUpload = true caused request timed out exceptions on request object operations (in ASP.NET), even if posting files is very small. Could some one explain that? (and how to fix that?)
TIA

jsakalos
29 May 2009, 6:47 AM
Sorry, I'm on Linux and I don't use M$ "products" except hardware ones.

BrainDrain
30 May 2009, 5:32 AM
How you aborting background uploading process in your php frontend? Maybe your answer can help me.
+ a little 'bugs'
- right click menu (with embedded upload panel) doesn't appear in opera 9
- 'remove all uploads' button removes 'add' button from toolbars (instead of removing only records)
- 'add upload' button sometimes works only after 1-2-3 clicks (in IE, for ex.), and it won't work in Opera at all

jsakalos
30 May 2009, 6:16 AM
How you aborting background uploading process in your php frontend? Maybe your answer can help me.

I do nothing on server. Just stop() on hidden iframe at client.


+ a little 'bugs'
- right click menu (with embedded upload panel) doesn't appear in opera 9
It's Opera problem - they have blocked context menus.


- 'remove all uploads' button removes 'add' button from toolbars (instead of removing only records)
- 'add upload' button sometimes works only after 1-2-3 clicks (in IE, for ex.), and it won't work in Opera at all These are BrowseButton bugs - I won't use it in the next version.

romulodelazzari
3 Jun 2009, 7:03 AM
Hi Saki,

I'm using only the UploadPanel in my application and I need to pass a parameter when I click the upload button, I'm trying to use the baseParams without success, because in php this parameter don't exist.

Here is my code:



meuUploadPanel = new Ext.ux.UploadPanel({
buttonsAt:'tbar',
id:'uppanel',
url:'page.php',
path:'root',
maxFileSize:1048576,
listeners:{
allfinished: function(){
anexoSeStore.reload({
params:{
hidden_tt: numeroTTSelecionado
}
});
},
beforeallstart: function(){
this.baseParams = {
hidden_tt: '1234'
}
}
}
});
Thanks for any help :)

khurri.saleem
17 Jun 2009, 3:19 AM
Hello Saki,

I hope every one out there must have experienced the issue of browse button with IE8. However your example online works but a simple example locally dont works at all.

I need help with IE 8 and i am really stuck please help !



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<link rel="stylesheet" type="text/css" href="Ext/resources/css/ext-all.css"/>


<script type="text/javascript" src="Ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="Ext/ext-all.js"></script>

<script type="text/javascript" src="js/Ext.ux.FileUploader.js"></script>
<script type="text/javascript" src="js/Ext.ux.UploadPanel.js"></script>
<script type="text/javascript" src="js/Ext.ux.form.BrowseButton.js"></script>
</head>
<body>
<script language="javascript" type="text/javascript">
// window with uploadpanel
var win = new Ext.Window({
width: 180
, minWidth: 165
, id: 'winid'
, height: 220
, minHeight: 200
, layout: 'fit'
, border: false
, closable: false
, title: 'UploadPanel'
, iconCls: 'icon-upload'
, items: [{
xtype: 'uploadpanel'
, buttonsAt: 'tbar'
, id: 'uppanel'
, path: 'root'
, maxFileSize: 1048576

}]
});
win.show.defer(500, win);
</script>
</body>
</html>

jsakalos
17 Jun 2009, 11:36 AM
First, remove the doctype.

I will replace BrowseButton with Ext native file input in the next release - some time after Ext 3.0 final is released.

deemonas
24 Jun 2009, 6:41 AM
Hi all,

I just tried to run it under 3.0rc2.

The problem is that Ext.ux.UploadPanel is just a Ext.Panel rendered in place of the Ext.menu.BaseItem.

In this case Ext3.0 DOES NOT RENDER toolbar for the Panel. Try this under Ext 2.x and Ext 3.0:


viewport = new Ext.Viewport( {
layout: 'absolute',
renderTo: Ext.getBody(),
items :
[
{
region: 'center',
html: 'HA HA',
tbar:
new Ext.Toolbar
({
items:
[
{
text: 'Managers',
iconCls: 'bmenu',
// Menus can be built/referenced by using nested menu config objects
menu :
{
items:
[
{text: 'Template Manager' },
{text: 'Site &amp; Page Manager'},
{
xtype: 'panel',
id: 'mypanel',
html: 'you see just blue line and no button. Ugh.',
tbar: [{text: 'button ???'}]
}
]
}
}
]
})
}
]
});
It might be even a bug in EXT 3.0

To solve it I made 2 small changes
1. in Ext.ux.UploadPanel.onRender
add one line to force toolbar get rendered:


onRender:function() {
// call parent
Ext.ux.UploadPanel.superclass.onRender.apply(this, arguments);

// save useful references
var tb = 'tbar' === this.buttonsAt ? this.getTopToolbar() : this.getBottomToolbar();

this.addBtn = Ext.getCmp(tb.items.first().id);
this.uploadBtn = Ext.getCmp(tb.items.itemAt(1).id);
this.removeAllBtn = Ext.getCmp(tb.items.last().id);
//FIXME ExtJs 3.0 does not render it automatically !!!
// f*ck knows why, so force toolbar to be rendered
tb.doLayout(false,true);
}2. Ext 3.0 uses a bit different HTML for buttons, ajust code in
Ext.ux.form.BrowseButton.onRender

use

this.buttonCt = this.el.child('.x-btn-mc em');instead of

this.buttonCt = this.el.child('.x-btn-center em');At least it worked for me.

jsakalos
24 Jun 2009, 1:39 PM
Thank you for the data - I'll get back to it when I'll port it to Ext 3.0.

Phatnine
26 Jun 2009, 5:24 AM
Hello

Good job Saki on this great extension.

I'm using your extension in a webGIS project. I want users to be able to upload their GIS data into a temporary folder. The problem is I can't seem to filter the file extension. I just want GML, SHP, TIF, etc, but not EXE or ZIP... Is there a config option that lets me filter the file extensions? Is there a class documentation?

Also, how can I see the response from my php backend? I need to see it because I have a ''Cannot decode JSON object'' when I try uploading a file.

The window looks like this:


var win = new Ext.Window({
width:400,
height:300,
autoScroll:true,
title: 'Add a layer',
id:'window',
listeners:{
hide:function(){
gpxButton.enable();
}
},
items:[{
xtype:'uploadpanel'
,buttonsAt:'tbar'
,name:'uppanel'
,iconCls:'icon-upload'
,id:'uppanel'
,url:'filetree.php'
,maxFileSize:1048576
,enableProgress:false
,baseParams:[{
filename:'uppanel',
path: 'root'
}]
}]
})
win.show();


And my php backend is:


<?php
if ($_FILES["uppanel"]["error"] > 0)
{
echo "Error: " . $_FILES["uppanel"]["error"] . "<br />";
}
else
{
echo "Upload: " . $_FILES["uppanel"]["name"] . "<br />";
echo "Type: " . $_FILES["uppanel"]["type"] . "<br />";
echo "Size: " . ($_FILES["uppanel"]["size"] / 1024) . " Kb<br />";
echo "Stored in: " . $_FILES["uppanel"]["tmp_name"];
}
?>


Thank you,

Maxime

jsakalos
26 Jun 2009, 11:11 AM
1. I do not believe that you can filter out file extensions in a browser.
2. You cannot "echo" a plain text from php, it must be a json encoded object.

Phatnine
30 Jun 2009, 9:10 AM
Thanks, I was able to make the upload panel work. I'm now working on the filetreepanel and I can't seem to get it working.

Here's the javascript:


title:'Test',
border:false,
xtype:'filetreepanel',
url:'php_file_tree.php'
Here's php_file_tree.php


<?php

echo php_file_tree('.');
function php_file_tree($directory, $extensions = array()) {
// Generates a valid XHTML list of all directories, sub-directories, and files in $directory
// Remove trailing slash
if( substr($directory, -1) == "/" ) $directory = substr($directory, 0, strlen($directory) - 1);
$code .= php_file_tree_dir($directory, $extensions);
return $code;
}

function php_file_tree_dir($directory, $extensions = array(), $first_call = true) {
// Recursive function called by php_file_tree() to list directories/files

// Get and sort directories/files
if( function_exists("scandir") ) $file = scandir($directory); else $file = php4_scandir($directory);

natcasesort($file);

// Make directories first
$files = $dirs = array();
foreach($file as $this_file) {
if( is_dir("$directory/$this_file" ) ) $dirs[] = $this_file; else $files[] = $this_file;
}
$file = array_merge($dirs, $files);

// Filter unwanted extensions
if( !empty($extensions) ) {
foreach( array_keys($file) as $key ) {
if( !is_dir("$directory/$file[$key]") ) {
$ext = substr($file[$key], strrpos($file[$key], ".") + 1);
if( !in_array($ext, $extensions) ) unset($file[$key]);
}
}
}

if( count($file) > 2 ) { // Use 2 instead of 0 to account for . and .. "directories"
$php_file_tree = "<ul";
if( $first_call ) { $php_file_tree .= " class=\"php-file-tree\""; $first_call = false; }
$php_file_tree .= ">";
foreach( $file as $this_file ) {
if( $this_file != "." && $this_file != ".." ) {
if( is_dir("$directory/$this_file") ) {
// Directory
$php_file_tree .= "<li class=\"pft-directory\"><a href=\"#\">" . htmlspecialchars($this_file) . "</a>";
$php_file_tree .= php_file_tree_dir("$directory/$this_file", $extensions, false);
$php_file_tree .= "</li>";
} else {
// File
// Get extension (prepend 'ext-' to prevent invalid classes from extensions that begin with numbers)
$ext = "ext-" . substr($this_file, strrpos($this_file, ".") + 1);
$link = str_replace("[link]", "$directory/" . urlencode($this_file), "$directory/$this_file");
$php_file_tree .= "<li class=\"pft-file " . strtolower($ext) . "\"><a href=\"$link\" target='_blank'>" . htmlspecialchars($this_file) . "</a></li>";
}
}
}
$php_file_tree .= "</ul>";
}
return $php_file_tree;
}

// For PHP4 compatibility
function php4_scandir($dir) {
$dh = opendir($dir);
while( false !== ($filename = readdir($dh)) ) {
$files[] = $filename;
}
sort($files);
return($files);
}
I'm able to create a tree and see all files in the corresponding folder. The problem is that I can't pass the tree I create with php_file_tree back to the file tree panel. Do I need to encode the tree in JSON? Every suggestions and comments would be appreciated.

Thank you,
Maxime

jsakalos
30 Jun 2009, 10:21 AM
Sorry I do not provide any assistance related to (PHP) backend.

BIS
1 Jul 2009, 12:40 PM
hi saki


thumps up

jahman
14 Jul 2009, 11:39 PM
Hello,

i though by using the confih option Singleupload : true. I will be able to upload only one file and not as many as possible. Is that correct. I need some help about htis.

Thanks.



{
xtype:'uploadpanel'
,buttonsAt:'tbar'
,id:'uppanel'
,url:'filetree.php'
,path: 'root'
,iconCls:'icon-upload'
,maxFileSize:1048576
,enableProgress:true
,singleUpload:true}

jsakalos
14 Jul 2009, 11:43 PM
Not really. singleUpload:true uploads all files in one request and singleUpload:false uploads file one-by-one, but always all scheduled files.

jahman
14 Jul 2009, 11:51 PM
Not really. singleUpload:true uploads all files in one request and singleUpload:false uploads file one-by-one, but always all scheduled files.

Thanks for the explanation. But is there a way, I can restrict the uploader to uploading only one file? I need to this to prevent user from making mistakes in terms of uploading more than one files at the same time, because an uploaded file gets addtional information to it. Any suggestions?

jsakalos
14 Jul 2009, 11:55 PM
You won't need uploader in that case; a single <input type="file"> would be enough.

jahman
15 Jul 2009, 12:09 AM
You won't need uploader in that case; a single <input type="file"> would be enough.


I am not a javasript expert. Have you gotten any examples to you sugestions, so that I could see how to implement it. Thanks fotr the help.

Foggy
15 Jul 2009, 12:40 AM
Hi Saki

Not sure if this is a bug.
If i return

Array
(
[success] => false
[errors] => Array
(
[ext-gen697] => Test error
)

)
on a upload, the view is not showing the error icon, just run uploading.
So i going deeper in your code and found the reason i think...

In your processFailure method, wich is called on upload error return, you do following to get records with error:


records = this.store.queryBy(function(r){
var state = r.get('state');
return 'done' !== state && 'uploading' !== state;
});
After you for each all found records and set its state to failed. And thats the point, you cant find any other states in queryBy, cause you set it after at least.

So changing these lines into:


records = this.store.queryBy(function(r){
return error.errors ? error.errors[r.id] : false;
/*var state = r.get('state');
return 'done' !== state && 'uploading' !== state;*/
});


works well for me, can someone confirm that?

jsakalos
15 Jul 2009, 10:17 AM
Thank you for the info, I'll take deeper look when porting it to Ext 3.x final version.

Foggy
15 Jul 2009, 10:40 AM
Thank you for the info
You are very welcome...

I was playing around with your extension today. Done some changes, espacielly for the upload progress of much files, wich works fine for me now. On your demo page, if i choose, let me say 4 files, just the last one get the upload state.
When you porting this extension and you would like to see my file, just let me know please.
I would like it if my code helps you anyway ;)

BTW: i just played with the upload extension...

Foggy
15 Jul 2009, 12:08 PM
I decide to post my changes right here. Think some stuff is much asked in this thread.
Maybe other guys can confirm that this version work well for they too..

To see what i have done, just do a search with "@Foggy" as term.
Should be good commented...

So here are my changes in FilePanel.js

// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
* Ext.ux.form.UploadPanel
*
* @author Ing. Jozef Sakáloš
* @version $Id: Ext.ux.UploadPanel.js 310 2008-08-14 17:23:48Z jozo $
* @date 13. March 2008
*
* @license Ext.ux.form.UploadPanel is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/

/*global Ext */

/**
* @class Ext.ux.UploadPanel
* @extends Ext.Panel
*/
Ext.ux.UploadPanel = Ext.extend(Ext.Panel, {

// configuration options overridable from outside
// {{{
/**
* @cfg {String} addIconCls icon class for add (file browse) button
*/
addIconCls:'icon-add'

/**
* @cfg {String} addText Text on Add button
*/
,addText:'Add'

/**
* @cfg {Object} baseParams This object is not used directly by FileTreePanel but it is
* propagated to lower level objects instead. Included here for convenience.
*/

/**
* @cfg {String} bodyStyle style to use for panel body
*/
,bodyStyle:'padding:2px'

/**
* @cfg {String} buttonsAt Where buttons are placed. Valid values are tbar, bbar, body (defaults to 'tbar')
*/
,buttonsAt:'tbar'

/**
* @cfg {String} clickRemoveText
*/
,clickRemoveText:'Click to remove'

/**
* @cfg {String} clickStopText
*/
,clickStopText:'Click to stop'

/**
* @cfg {String} emptyText empty text for dataview
*/
,emptyText:'No files'

/**
* @cfg {Boolean} enableProgress true to enable querying server for progress information
* Passed to underlying uploader. Included here for convenience.
*/
,enableProgress:true

/**
* @cfg {String} errorText
*/
,errorText:'Error'

/**
* @cfg {String} fileCls class prefix to use for file type classes
*/
,fileCls:'file'

/**
* @cfg {String} fileQueuedText File upload status text
*/
,fileQueuedText:'File <b>{0}</b> is queued for upload'

/**
* @cfg {String} fileDoneText File upload status text
*/
,fileDoneText:'File <b>{0}</b> has been successfully uploaded'

/**
* @cfg {String} fileFailedText File upload status text
*/
,fileFailedText:'File <b>{0}</b> failed to upload'

/**
* @cfg {String} fileStoppedText File upload status text
*/
,fileStoppedText:'File <b>{0}</b> stopped by user'

/**
* @cfg {String} fileUploadingText File upload status text
*/
,fileUploadingText:'Uploading file <b>{0}</b>'

/**
* @cfg {Number} maxFileSize Maximum upload file size in bytes
* This config property is propagated down to uploader for convenience
*/
,maxFileSize:524288

/**
* @cfg {Number} Maximum file name length for short file names
*/
,maxLength:18

/**
* @cfg {String} removeAllIconCls iconClass to use for Remove All button (defaults to 'icon-cross'
*/
,removeAllIconCls:'icon-cancel'

/**
* @cfg {String} removeAllText text to use for Remove All button tooltip
*/
,removeAllText:'Remove All'

/**
* @cfg {String} removeIconCls icon class to use for remove file icon
*/
,removeIconCls:'icon-delete'

/**
* @cfg {String} removeText Remove text
*/
,removeText:'Remove'

/**
* @cfg {String} selectedClass class for selected item of DataView
*/
,selectedClass:'ux-up-item-selected'

/**
* @cfg {Boolean} singleUpload true to upload files in one form, false to upload one by one
* This config property is propagated down to uploader for convenience
*/
,singleUpload:false

/**
* @cfg {String} stopAllText
*/
,stopAllText:'Stop All'

/**
* @cfg {String} stopIconCls icon class to use for stop
*/
,stopIconCls:'icon-cancel'

/**
* @cfg {String/Ext.XTemplate} tpl Template for DataView.
*/

/**
* @cfg {String} uploadText Upload text
*/
,uploadText:'Upload'

/**
* @cfg {String} uploadIconCls icon class to use for upload button
*/
,uploadIconCls:'icon-upload'

/**
* @cfg {String} workingIconCls iconClass to use for busy indicator
*/
,workingIconCls:'icon-loading'

// }}}

// overrides
// {{{
,initComponent:function() {
// {{{
// create buttons
// add (file browse button) configuration
var addCfg = {
xtype:'browsebutton'
,text:this.addText + '...'
,iconCls:this.addIconCls
,scope:this
,handler:this.onAddFile
};

// upload button configuration
var upCfg = {
xtype:'button'
,iconCls:this.uploadIconCls
,text:this.uploadText
,scope:this
,handler:this.onUpload
,disabled:true
};

// remove all button configuration
var removeAllCfg = {
xtype:'button'
,iconCls:this.removeAllIconCls
,tooltip:this.removeAllText
,scope:this
,handler:this.onRemoveAllClick
,disabled:true
};

// todo: either to cancel buttons in body or implement it
if('body' !== this.buttonsAt) {
this[this.buttonsAt] = [addCfg, upCfg, '->', removeAllCfg];
}
// }}}
// {{{
// create store
// fields for record
var fields = [
{name:'id', type:'text', system:true}
,{name:'shortName', type:'text', system:true}
,{name:'fileName', type:'text', system:true}
,{name:'filePath', type:'text', system:true}
,{name:'fileCls', type:'text', system:true}
,{name:'input', system:true}
,{name:'form', system:true}
,{name:'state', type:'text', system:true}
,{name:'error', type:'text', system:true}
,{name:'progressId', type:'int', system:true}
,{name:'bytesTotal', type:'int', system:true}
,{name:'bytesUploaded', type:'int', system:true}
,{name:'estSec', type:'int', system:true}
,{name:'filesUploaded', type:'int', system:true}
,{name:'speedAverage', type:'int', system:true}
,{name:'speedLast', type:'int', system:true}
,{name:'timeLast', type:'int', system:true}
,{name:'timeStart', type:'int', system:true}
,{name:'pctComplete', type:'int', system:true}
];

// add custom fields if passed
if(Ext.isArray(this.customFields)) {
fields.push(this.customFields);
}

// create store
this.store = new Ext.data.SimpleStore({
id:0
,fields:fields
,data:[]
});
// }}}
// {{{
// create view
Ext.apply(this, {
items:[{
xtype:'dataview'
,itemSelector:'div.ux-up-item'
,store:this.store
,selectedClass:this.selectedClass
,singleSelect:true
,emptyText:this.emptyText
,tpl: this.tpl || new Ext.XTemplate(
'<tpl for=".">'
+ '<div class="ux-up-item">'
// + '<div class="ux-up-indicator"> </div>'
+ '<div class="ux-up-icon-file {fileCls}"> </div>'
+ '<div class="ux-up-text x-unselectable" qtip="{fileName}">{shortName}</div>'
+ '<div id="remove-{[values.input.id]}" class="ux-up-icon-state ux-up-icon-{state}"'
+ 'qtip="{[this.scope.getQtip(values)]}"> </div>'
+ '</div>'
+ '</tpl>'
, {scope:this}
)
,listeners:{click:{scope:this, fn:this.onViewClick}}

}]
});
// }}}

// call parent
Ext.ux.UploadPanel.superclass.initComponent.apply(this, arguments);

// save useful references
this.view = this.items.itemAt(0);

// {{{
// add events
this.addEvents(
/**
* Fires before the file is added to store. Return false to cancel the add
* @event beforefileadd
* @param {Ext.ux.UploadPanel} this
* @param {Ext.Element} input (type=file) being added
*/
'beforefileadd'
/**
* Fires after the file is added to the store
* @event fileadd
* @param {Ext.ux.UploadPanel} this
* @param {Ext.data.Store} store
* @param {Ext.data.Record} Record (containing the input) that has been added to the store
*/
,'fileadd'
/**
* Fires before the file is removed from the store. Return false to cancel the remove
* @event beforefileremove
* @param {Ext.ux.UploadPanel} this
* @param {Ext.data.Store} store
* @param {Ext.data.Record} Record (containing the input) that is being removed from the store
*/
,'beforefileremove'
/**
* Fires after the record (file) has been removed from the store
* @event fileremove
* @param {Ext.ux.UploadPanel} this
* @param {Ext.data.Store} store
*/
,'fileremove'
/**
* Fires before all files are removed from the store (queue). Return false to cancel the clear.
* Events for individual files being removed are suspended while clearing the queue.
* @event beforequeueclear
* @param {Ext.ux.UploadPanel} this
* @param {Ext.data.Store} store
*/
,'beforequeueclear'
/**
* Fires after the store (queue) has been cleared
* Events for individual files being removed are suspended while clearing the queue.
* @event queueclear
* @param {Ext.ux.UploadPanel} this
* @param {Ext.data.Store} store
*/
,'queueclear'
/**
* Fires after the upload button is clicked but before any upload is started
* Return false to cancel the event
* @param {Ext.ux.UploadPanel} this
*/
,'beforeupload'
);
// }}}
// {{{
// relay view events
this.relayEvents(this.view, [
'beforeclick'
,'beforeselect'
,'click'
,'containerclick'
,'contextmenu'
,'dblclick'
,'selectionchange'
]);
// }}}

// create uploader
var config = {
store:this.store
,singleUpload:this.singleUpload
,maxFileSize:this.maxFileSize
,enableProgress:this.enableProgress
,url:this.url
,path:this.path
};
if(this.baseParams) {
config.baseParams = this.baseParams;
}

if(this.progressUrl) {
config.progressUrl = this.progressUrl;
}

if(this.progressBaseParams) {
config.progressBaseParams = this.progressBaseParams;
}

this.uploader = new Ext.ux.FileUploader(config);

// relay uploader events
this.relayEvents(this.uploader, [
'beforeallstart'
,'allfinished'
,'progress'
]);

// install event handlers
this.on({
beforeallstart:{scope:this, fn:function() {
this.uploading = true;
this.updateButtons();
}}
,allfinished:{scope:this, fn:function() {
this.uploading = false;
this.updateButtons();
}}
,progress:{fn:this.onProgress.createDelegate(this)}
});
} // eo function initComponent
// }}}
// {{{
/**
* onRender override, saves references to buttons
* @private
*/
,onRender:function() {
// call parent
Ext.ux.UploadPanel.superclass.onRender.apply(this, arguments);

// save useful references
var tb = 'tbar' === this.buttonsAt ? this.getTopToolbar() : this.getBottomToolbar();
this.addBtn = Ext.getCmp(tb.items.first().id);
this.uploadBtn = Ext.getCmp(tb.items.itemAt(1).id);
this.removeAllBtn = Ext.getCmp(tb.items.last().id);
} // eo function onRender
// }}}

// added methods
// {{{
/**
* called by XTemplate to get qtip depending on state
* @private
* @param {Object} values XTemplate values
*/
,getQtip:function(values) {
var qtip = '';
switch(values.state) {
case 'queued':
qtip = String.format(this.fileQueuedText, values.fileName);
qtip += '<br>' + this.clickRemoveText;
break;

case 'uploading':
qtip = String.format(this.fileUploadingText, values.fileName);
qtip += '<br>' + values.pctComplete + '% done';
qtip += '<br>' + this.clickStopText;
break;

case 'done':
qtip = String.format(this.fileDoneText, values.fileName);
qtip += '<br>' + this.clickRemoveText;
break;

case 'failed':
qtip = String.format(this.fileFailedText, values.fileName);
qtip += '<br>' + this.errorText + ':' + values.error;
qtip += '<br>' + this.clickRemoveText;
break;

case 'stopped':
qtip = String.format(this.fileStoppedText, values.fileName);
qtip += '<br>' + this.clickRemoveText;
break;
}
return qtip;
} // eo function getQtip
// }}}
// {{{
/**
* get file name
* @private
* @param {Ext.Element} inp Input element containing the full file path
* @return {String}
*/
,getFileName:function(inp) {
return inp.getValue().split(/[\/\\]/).pop();
} // eo function getFileName
// }}}
// {{{
/**
* get file size in byte or 0
*
* @Foggy added file size check for supoorted browser
*
* @private
* @param {Ext.Element} inp Input element containing the full file
* @return {Int}
*/
,getFileBytes:function(inp) {
var size = 0;
if (Ext.isMozilla) {
var size = inp.dom.files[0].fileSize;
} else if (Ext.isSafari) {
/*console.log(inp.dom.PROCESSING_INSTRUCTION_NODE);
for (var p in inp.dom) {
console.log(p);
}*/
} else if (Ext.isOpera) {

} else if (Ext.isIE) {

}
return size;
} // eo function getFileBytes
// }}}
// {{{
/**
* get file path (excluding the file name)
* @private
* @param {Ext.Element} inp Input element containing the full file path
* @return {String}
*/
,getFilePath:function(inp) {
return inp.getValue().replace(/[^\/\\]+$/,'');
} // eo function getFilePath
// }}}
// {{{
/**
* returns file class based on name extension
* @private
* @param {String} name File name to get class of
* @return {String} class to use for file type icon
*/
,getFileCls: function(name) {
var atmp = name.split('.');
if(1 === atmp.length) {
return this.fileCls;
}
else {
return this.fileCls + '-' + atmp.pop().toLowerCase();
}
}
// }}}
// {{{
/**
* called when file is added - adds file to store
*
* @Foggy added file size check for supoorted browser
*
* @private
* @param {Ext.ux.BrowseButton}
*/
,onAddFile:function(bb) {
if(true !== this.eventsSuspended && false === this.fireEvent('beforefileadd', this, bb.getInputFile())) {
return;
}
var inp = bb.detachInputFile();
inp.addClass('x-hidden');
var fileName = this.getFileName(inp);

//@Foggy added -->
var fileBytes = this.getFileBytes(inp);
if (fileBytes > this.maxFileSize) {
//@todo got this in the localization file
Ext.Msg.alert('Error', 'Your choosen file is too large. ' + Ext.util.Format.fileSize(this.maxFileSize) + ' is maximum size allowed!');
return;
}
//@Foggy added <--

// create new record and add it to store
var rec = new this.store.recordType({
input:inp
,fileName:fileName
,filePath:this.getFilePath(inp)
,shortName: Ext.util.Format.ellipsis(fileName, this.maxLength)
,fileCls:this.getFileCls(fileName)
,state:'queued'
}, inp.id);
rec.commit();
this.store.add(rec);

this.syncShadow();

this.uploadBtn.enable();
this.removeAllBtn.enable();

if(true !== this.eventsSuspended) {
this.fireEvent('fileadd', this, this.store, rec);
}

this.doLayout();
} // eo onAddFile
// }}}
// {{{
/**
* destroys child components
* @private
*/
,onDestroy:function() {
// destroy uploader
if(this.uploader) {
this.uploader.stopAll();
this.uploader.purgeListeners();
this.uploader = null;
}

// destroy view
if(this.view) {
this.view.purgeListeners();
this.view.destroy();
this.view = null;
}

// destroy store
if(this.store) {
this.store.purgeListeners();
this.store.destroy();
this.store = null;
}
} // eo function onDestroy
// }}}
// {{{
/**
* progress event handler
* @private
* @param {Ext.ux.FileUploader} uploader
* @param {Object} data progress data
* @param {Ext.data.Record} record
*/
,onProgress:function(uploader, data, record) {
var bytesTotal, bytesUploaded, pctComplete, state, idx, item, width, pgWidth;
if(record) {
state = record.get('state');
bytesTotal = record.get('bytesTotal') || 1;
bytesUploaded = record.get('bytesUploaded') || 0;
if('uploading' === state) {
pctComplete = Math.round(1000 * bytesUploaded/bytesTotal) / 10;
}
else if('done' === state) {
pctComplete = 100;
}
else {
pctComplete = 0;
}
record.set('pctComplete', pctComplete);

idx = this.store.indexOf(record);
item = Ext.get(this.view.getNode(idx));
if(item) {
width = item.getWidth();
item.applyStyles({'background-position':width * pctComplete / 100 + 'px'});
}
}
} // eo function onProgress
// }}}
// {{{
/**
* called when file remove icon is clicked - performs the remove
* @private
* @param {Ext.data.Record}
*/
,onRemoveFile:function(record) {
if(true !== this.eventsSuspended && false === this.fireEvent('beforefileremove', this, this.store, record)) {
return;
}

// remove DOM elements
var inp = record.get('input');
var wrap = inp.up('em');
inp.remove();
if(wrap) {
wrap.remove();
}

// remove record from store
this.store.remove(record);

var count = this.store.getCount();
this.uploadBtn.setDisabled(!count);
this.removeAllBtn.setDisabled(!count);

if(true !== this.eventsSuspended) {
this.fireEvent('fileremove', this, this.store);
this.syncShadow();
}
} // eo function onRemoveFile
// }}}
// {{{
/**
* Remove All/Stop All button click handler
* @private
*/
,onRemoveAllClick:function(btn) {
if(true === this.uploading) {
this.stopAll();
}
else {
this.removeAll();
}
} // eo function onRemoveAllClick

,stopAll:function() {
this.uploader.stopAll();
} // eo function stopAll
// }}}
// {{{
/**
* DataView click handler
* @private
*/
,onViewClick:function(view, index, node, e) {
var t = e.getTarget('div:any(.ux-up-icon-queued|.ux-up-icon-failed|.ux-up-icon-done|.ux-up-icon-stopped)');
if(t) {
this.onRemoveFile(this.store.getAt(index));
}
t = e.getTarget('div.ux-up-icon-uploading');
if(t) {
this.uploader.stopUpload(this.store.getAt(index));
}
} // eo function onViewClick
// }}}
// {{{
/**
* tells uploader to upload
* @private
*/
,onUpload:function() {
if(true !== this.eventsSuspended && false === this.fireEvent('beforeupload', this)) {
return false;
}
this.uploader.upload();
} // eo function onUpload
// }}}
// {{{
/**
* url setter
*/
,setUrl:function(url) {
this.url = url;
this.uploader.setUrl(url);
} // eo function setUrl
// }}}
// {{{
/**
* path setter
*/
,setPath:function(path) {
this.uploader.setPath(path);
} // eo function setPath
// }}}
// {{{
/**
* Updates buttons states depending on uploading state
* @private
*/
,updateButtons:function() {
if(true === this.uploading) {
this.addBtn.disable();
this.uploadBtn.disable();
this.removeAllBtn.setIconClass(this.stopIconCls);
this.removeAllBtn.getEl().child(this.removeAllBtn.buttonSelector).dom[this.removeAllBtn.tooltipType] = this.stopAllText;
}
else {
this.addBtn.enable();
this.uploadBtn.enable();
this.removeAllBtn.setIconClass(this.removeAllIconCls);
this.removeAllBtn.getEl().child(this.removeAllBtn.buttonSelector).dom[this.removeAllBtn.tooltipType] = this.removeAllText;
}
} // eo function updateButtons
// }}}
// {{{
/**
* Removes all files from store and destroys file inputs
*/
,removeAll:function() {
var suspendState = this.eventsSuspended;
if(false !== this.eventsSuspended && false === this.fireEvent('beforequeueclear', this, this.store)) {
return false;
}
this.suspendEvents();

this.store.each(this.onRemoveFile, this);

this.eventsSuspended = suspendState;
if(true !== this.eventsSuspended) {
this.fireEvent('queueclear', this, this.store);
}
this.syncShadow();
} // eo function removeAll
// }}}
// {{{
/**
* synchronize context menu shadow if we're in contextmenu
* @private
*/
,syncShadow:function() {
if(this.contextmenu && this.contextmenu.shadow) {
this.contextmenu.getEl().shadow.show(this.contextmenu.getEl());
}
} // eo function syncShadow
// }}}

}); // eo extend

// register xtype
Ext.reg('uploadpanel', Ext.ux.UploadPanel);

// eof

FileUploader.js


// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
* Ext.ux.FileUploader
*
* @author Ing. Jozef Sakáloš
* @version $Id: Ext.ux.FileUploader.js 302 2008-08-03 20:57:33Z jozo $
* @date 15. March 2008
*
* @license Ext.ux.FileUploader is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/

/*global Ext */

/**
* @class Ext.ux.FileUploader
* @extends Ext.util.Observable
* @constructor
*/
Ext.ux.FileUploader = function(config) {
Ext.apply(this, config);

// call parent
Ext.ux.FileUploader.superclass.constructor.apply(this, arguments);

// add events
// {{{
this.addEvents(
/**
* @event beforeallstart
* Fires before an upload (of all files) is started. Return false to cancel the event.
* @param {Ext.ux.FileUploader} this
*/
'beforeallstart'
/**
* @event allfinished
* Fires after upload (of all files) is finished
* @param {Ext.ux.FileUploader} this
*/
,'allfinished'
/**
* @event beforefilestart
* Fires before the file upload is started. Return false to cancel the event.
* Fires only when singleUpload = false
* @param {Ext.ux.FileUploader} this
* @param {Ext.data.Record} record upload of which is being started
*/
,'beforefilestart'
/**
* @event filefinished
* Fires when file finished uploading.
* Fires only when singleUpload = false
* @param {Ext.ux.FileUploader} this
* @param {Ext.data.Record} record upload of which has finished
*/
,'filefinished'
/**
* @event progress
* Fires when progress has been updated
* @param {Ext.ux.FileUploader} this
* @param {Object} data Progress data object
* @param {Ext.data.Record} record Only if singleUpload = false
*/
,'progress'
);
// }}}
}; // eo constructor

Ext.extend(Ext.ux.FileUploader, Ext.util.Observable, {

// configuration options
// {{{
/**
* @cfg {Object} baseParams baseParams are sent to server in each request.
*/
baseParams:{}

// configuration options for progress request
// {{{
/**
* @cfg {Object} baseParams baseParams are sent to server in each progress request.
*
* @Foggy added base params for progress request as well
*
*/
,progressBaseParams:{}

/**
* @cfg {Boolean} concurrent true to start all requests upon upload start, false to start
* the next request only if previous one has been completed (or failed). Applicable only if
* singleUpload = false
*/
,concurrent:true

/**
* @cfg {Boolean} enableProgress true to enable querying server for progress information
*/
,enableProgress:true

/**
* @cfg {String} jsonErrorText Text to use for json error
*/
,jsonErrorText:'Cannot decode JSON object'

/**
* @cfg {Number} Maximum client file size in bytes
*/
,maxFileSize:524288

/**
* @cfg {String} progressIdName Name to give hidden field for upload progress identificator
*/
,progressIdName:'UPLOAD_IDENTIFIER'

/**
* @cfg {Number} progressInterval How often (in ms) is progress requested from server
*/
,progressInterval:2000

/**
* @cfg {String} progressUrl URL to request upload progress from
*/
,progressUrl:'progress.php'

/**
* @cfg {Object} progressMap Mapping of received progress fields to store progress fields
*/
,progressMap:{
bytes_total:'bytesTotal'
,bytes_uploaded:'bytesUploaded'
,est_sec:'estSec'
,files_uploaded:'filesUploaded'
,speed_average:'speedAverage'
,speed_last:'speedLast'
,time_last:'timeLast'
,time_start:'timeStart'
}
/**
* @cfg {Boolean} singleUpload true to upload files in one form, false to upload one by one
*/
,singleUpload:false

/**
* @cfg {Ext.data.Store} store Mandatory. Store that holds files to upload
*/

/**
* @cfg {String} unknownErrorText Text to use for unknow error
*/
,unknownErrorText:'Unknown error'

/**
* @cfg {String} url Mandatory. URL to upload to
*/

// }}}

// private
// {{{
/**
* uploads in progress count
* @private
*/
,upCount:0
// }}}

// methods
// {{{
/**
* creates form to use for upload.
* @private
* @return {Ext.Element} form
*/
,createForm:function(record) {
var progressId = parseInt(Math.random() * 1e10, 10);
var form = Ext.getBody().createChild({
tag:'form'
,action:this.url
,method:'post'
,cls:'x-hidden'
,id:Ext.id()
,cn:[{
tag:'input'
,type:'hidden'
,name:'APC_UPLOAD_PROGRESS'
,value:progressId
},{
tag:'input'
,type:'hidden'
,name:this.progressIdName
,value:progressId
},{
tag:'input'
,type:'hidden'
,name:'MAX_FILE_SIZE'
,value:this.maxFileSize
}]
});
if(record) {
record.set('form', form);
record.set('progressId', progressId);
}
else {
this.progressId = progressId;
}
return form;
} // eo function createForm
// }}}
// {{{
,deleteForm:function(form, record) {
form.remove();
if(record) {
record.set('form', null);
}
} // eo function deleteForm
// }}}
// {{{
/**
* Fires event(s) on upload finish/error
* @private
*/
,fireFinishEvents:function(options) {
if(true !== this.eventsSuspended && !this.singleUpload) {
this.fireEvent('filefinished', this, options && options.record);
}
if(true !== this.eventsSuspended && 0 === this.upCount) {
this.stopProgress();
this.fireEvent('allfinished', this);
}
} // eo function fireFinishEvents
// }}}
// {{{
/**
* Geg the iframe identified by record
* @private
* @param {Ext.data.Record} record
* @return {Ext.Element} iframe or null if not found
*/
,getIframe:function(record) {
var iframe = null;
var form = record.get('form');
if(form && form.dom && form.dom.target) {
iframe = Ext.get(form.dom.target);
}
return iframe;
} // eo function getIframe
// }}}
// {{{
/**
* returns options for Ajax upload request
* @private
* @param {Ext.data.Record} record
* @param {Object} params params to add
*/
,getOptions:function(record, params) {
var o = {
url:this.url
,method:'post'
,isUpload:true
,scope:this
,callback:this.uploadCallback
,record:record
,params:this.getParams(record, params)
};
return o;
} // eo function getOptions
// }}}
// {{{
/**
* get params to use for request
* @private
* @return {Object} params
*/
,getParams:function(record, params) {
var p = {path:this.path};
Ext.apply(p, this.baseParams || {}, params || {});
return p;
}
// }}}
// {{{
/**
* processes success response
* @private
* @param {Object} options options the request was called with
* @param {Object} response request response object
* @param {Object} o decoded response.responseText
*/
,processSuccess:function(options, response, o) {
var record = false;

// all files uploadded ok
if(this.singleUpload) {
this.store.each(function(r) {
r.set('state', 'done');
r.set('error', '');
r.commit();
});
}
else {
record = options.record;
record.set('state', 'done');
record.set('error', '');
record.commit();
}

this.deleteForm(options.form, record);
} // eo processSuccess
// }}}
// {{{
/**
* processes failure response
*
* @Foggy catch server errors
*
* @private
* @param {Object} options options the request was called with
* @param {Object} response request response object
* @param {String/Object} error Error text or JSON decoded object. Optional.
*/
,processFailure:function(options, response, error) {
var record = options.record;
var records;

// singleUpload - all files uploaded in one form
if(this.singleUpload) {
// some files may have been successful
records = this.store.queryBy(function(r){
//@Foggy changed -->
// changed that, im a bit curios, cause on your demo page, this seem to work well
// but my logic says, no, there is no way ;)
// anyhow, this way, it works for me
return error.errors ? error.errors[r.id] : false;
//@Foggy changed <--
});

records.each(function(record) {
var e = error.errors ? error.errors[record.id] : this.unknownErrorText;
if(e) {
record.set('state', 'failed');
record.set('error', e);
Ext.getBody().appendChild(record.get('input'));
}
else {
record.set('state', 'done');
record.set('error', '');
}
record.commit();
}, this);

this.deleteForm(options.form);
}
// multipleUpload - each file uploaded in it's own form
else {
if(error && 'object' === Ext.type(error)) {
record.set('error', error.errors && error.errors[record.id] ? error.errors[record.id] : this.unknownErrorText);
}
else if(error) {
record.set('error', error);
}
else if(response && response.responseText) {
record.set('error', response.responseText);
}
else {
record.set('error', this.unknownErrorText);
}
record.set('state', 'failed');
record.commit();
}
} // eof processFailure
// }}}
// {{{
/**
* Delayed task callback
*
* @Foggy changed some stuff to work with more files - Changes documented in code as well
*
*/
,requestProgress:function() {
var records, p;
var o = {
url:this.progressUrl
,method:'post'
,params: this.progressBaseParams
,scope:this
,callback:function(options, success, response) {
var o;
if(true !== success) {
return;
}
try {
o = Ext.decode(response.responseText);
}
catch(e) {
return;
}
if('object' !== Ext.type(o) || true !== o.success) {
return;
}

if(this.singleUpload) {
this.progress = {};
for(p in o) {
if(this.progressMap[p]) {
this.progress[this.progressMap[p]] = parseInt(o[p], 10);
}
}
if(true !== this.eventsSuspended) {
this.fireEvent('progress', this, this.progress);
}

}
else {
//@Foggy changed -->
// of some reason i got here the last record in store for every call in options.record
// so i decide to send back progressId from server to choose the correct record here
var recs = this.store.queryBy(function(r){
return r.get('progressId') == o.progressId;
});
// get first, cause just one record can be found
var rec = recs.first();
// append progressMap like you have done it
for(p in o) {
if(this.progressMap[p] && rec) {
rec.set(this.progressMap[p], parseInt(o[p], 10));
}
}
if(rec) {
rec.commit();
if(true !== this.eventsSuspended) {
this.fireEvent('progress', this, rec.data, rec);
}
}
//@Foggy changed <--
}
this.progressTask.delay(this.progressInterval);
}
};
if(this.singleUpload) {
o.params[this.progressIdName] = this.progressId;
o.params.APC_UPLOAD_PROGRESS = this.progressId;
Ext.Ajax.request(o);
}
else {
records = this.store.query('state', 'uploading');
records.each(function(r) {
o.params[this.progressIdName] = r.get('progressId');
o.params.APC_UPLOAD_PROGRESS = o.params[this.progressIdName];
// i wouldn't need that anymore
o.record = r;
//@Foggy changed -->
// commented out the timeout, cause in this 250ms o will be overwritten in this loop.
// this is cause o is instantiated outside, but thats just my opinion... ;)
// anyway, w/o the defer it works with the correct progressId
//(function() {
Ext.Ajax.request(o);
//}).defer(250);
//@Foggy changed <--
}, this);
}
} // eo function requestProgress
// }}}
// {{{
/**
* path setter
* @private
*/
,setPath:function(path) {
this.path = path;
} // eo setPath
// }}}
// {{{
/**
* url setter
* @private
*/
,setUrl:function(url) {
this.url = url;
} // eo setUrl
// }}}
// {{{
/**
* Starts progress fetching from server
* @private
*/
,startProgress:function() {
if(!this.progressTask) {
this.progressTask = new Ext.util.DelayedTask(this.requestProgress, this);
}
this.progressTask.delay.defer(this.progressInterval / 2, this.progressTask, [this.progressInterval]);
} // eo function startProgress
// }}}
// {{{
/**
* Stops progress fetching from server
* @private
*/
,stopProgress:function() {
if(this.progressTask) {
this.progressTask.cancel();
}
} // eo function stopProgress
// }}}
// {{{
/**
* Stops all currently running uploads
*/
,stopAll:function() {
var records = this.store.query('state', 'uploading');
records.each(this.stopUpload, this);
} // eo function stopAll
// }}}
// {{{
/**
* Stops currently running upload
* @param {Ext.data.Record} record Optional, if not set singleUpload = true is assumed
* and the global stop is initiated
*/
,stopUpload:function(record) {
// single abord
var iframe = false;
if(record) {
iframe = this.getIframe(record);
this.stopIframe(iframe);
this.upCount--;
this.upCount = 0 > this.upCount ? 0 : this.upCount;
record.set('state', 'stopped');
this.fireFinishEvents({record:record});
}
// all abort
else if(this.form) {
iframe = Ext.fly(this.form.dom.target);
this.stopIframe(iframe);
this.upCount = 0;
this.fireFinishEvents();
}
} // eo function abortUpload
// }}}
// {{{
/**
* Stops uploading in hidden iframe
* @private
* @param {Ext.Element} iframe
*/
,stopIframe:function(iframe) {
if(iframe) {
try {
iframe.dom.contentWindow.stop();
iframe.remove.defer(250, iframe);
}
catch(e){}
}
} // eo function stopIframe
// }}}
// {{{
/**
* Main public interface function. Preforms the upload
*/
,upload:function() {
var records = this.store.queryBy(function(r){return 'done' !== r.get('state');});
if(!records.getCount()) {
return;
}

// fire beforeallstart event
if(true !== this.eventsSuspended && false === this.fireEvent('beforeallstart', this)) {
return;
}
if(this.singleUpload) {
this.uploadSingle();
}
else {
records.each(this.uploadFile, this);
}

if(true === this.enableProgress) {
this.startProgress();
}
} // eo function upload
// }}}
// {{{
/**
* called for both success and failure. Does nearly nothing
* @private
* but dispatches processing to processSuccess and processFailure functions
*/
,uploadCallback:function(options, success, response) {
var o;
this.upCount--;
this.form = false;
console.log(this);
// process ajax success
if(true === success) {
try {
o = Ext.decode(response.responseText);
}
catch(e) {
this.processFailure(options, response, this.jsonErrorText);
this.fireFinishEvents(options);
return;
}
// process command success
if(true === o.success) {
this.processSuccess(options, response, o);
}
// process command failure
else {
this.processFailure(options, response, o);
}
}
// process ajax failure
else {
this.processFailure(options, response);
}

this.fireFinishEvents(options);
} // eo function uploadCallback
// }}}
// {{{
/**
* Uploads one file
* @param {Ext.data.Record} record
* @param {Object} params Optional. Additional params to use in request.
*/
,uploadFile:function(record, params) {
// fire beforestart event
if(true !== this.eventsSuspended && false === this.fireEvent('beforefilestart', this, record)) {
return;
}

// create form for upload
var form = this.createForm(record);

// append input to the form
var inp = record.get('input');
inp.set({name:inp.id});
form.appendChild(inp);

// get params for request
var o = this.getOptions(record, params);
o.form = form;

// set state
record.set('state', 'uploading');
record.set('pctComplete', 0);

// increment active uploads count
this.upCount++;

// request upload
Ext.Ajax.request(o);

// todo:delete after devel
this.getIframe.defer(100, this, [record]);
} // eo function uploadFile
// }}}
// {{{
/**
* Uploads all files in single request
*/
,uploadSingle:function() {
// get records to upload
var records = this.store.queryBy(function(r){return 'done' !== r.get('state');});
if(!records.getCount()) {
return;
}

// create form and append inputs to it
var form = this.createForm();
records.each(function(record) {
var inp = record.get('input');
inp.set({name:inp.id});
form.appendChild(inp);
record.set('state', 'uploading');
}, this);

// create options for request
var o = this.getOptions();
o.form = form;

// save form for stop
this.form = form;

// increment active uploads counter
this.upCount++;

// request upload
Ext.Ajax.request(o);
} // eo function uploadSingle
// }}}

}); // eo extend

// register xtype
Ext.reg('fileuploader', Ext.ux.FileUploader);

// eof

That should send correct progressId for each record. Just keep in mind to send that progressId back from the server. It is needed in the progressCallback now to setup the correct record...

Thanks Saki for this great UX...

calavera
21 Jul 2009, 5:32 AM
I am configuring my filetree like this:


treepanel = new Ext.ux.FileTreePanel({
height:300
,autoWidth:true
,id:'ftp'
,rootPath:'../root'
,topMenu:true
,autoScroll:true
,enableProgress:false
,singleExpand:true
});

I am using the filetree in a viewport, on the west region. I am trying to display one div under the filetree panel, both inside the west region like this:


items: [treepanel,{
height:100,
id:'newdiv',
border:false,
title:'New div',
autoScroll:true,
html: 'basic html here'
}]
}

My filetree panel is displayed correctly but the div not. I can inspect the west region element and I can see a div with the ID "newdiv" created but the filetree panel gets all the height space I think and the div don't have room to fit.. What should I change ?
Can anybody help ?

Thank you.

jsakalos
21 Jul 2009, 10:25 AM
What is layout of your west? If you're on Ext 2.x RowFit layout should do the job. See http://extjs.com/deploy/ext-2.2/examples/layout-browser/layout-browser.html

calavera
21 Jul 2009, 4:11 PM
What is layout of your west? If you're on Ext 2.x RowFit layout should do the job. See http://extjs.com/deploy/ext-2.2/examples/layout-browser/layout-browser.html
Thanks, Saki. It works with the Rowfit layout.

js_coder
22 Jul 2009, 9:59 AM
When do you think we'll have a 3.x port for this great extension?

jsakalos
22 Jul 2009, 10:21 AM
I do not want to promise a date but FileTreePanel is at the top of my priorities. I'm going to port my application soon and it cannot work w/o FTP.

js_coder
22 Jul 2009, 10:23 AM
Cool, let me know if there's anything I can help with.

tchitani
5 Aug 2009, 11:20 PM
Hi

Is it possible to expand tree to the specified path on, for example, button or grid row click and send path string?

var path = '/root/a/b/c';
Ext.getCmp('FileTree').getRootNode().reload(function(callback){
Ext.getCmp('FileTree').getRootNode().on({
load:{single:true, scope:this, fn:function() {
Ext.getCmp('FileTree').expandPath(path);
}}
})
});

This does not seem to work:(

Thanks

jsakalos
5 Aug 2009, 11:34 PM
Looks like it should work. Check if the path you're trying to expand really exists.

tchitani
5 Aug 2009, 11:40 PM
Thanks Saki for the prompt response.

The trick is the path does exist, but not loaded yet (I am retrieving this path info from the server call on grid render).
But, when I click on grid row and pass the path to expandPath, only tree root reload event is fired, nothing else.

jsakalos
5 Aug 2009, 11:42 PM
Hmm, expandPath should handle it. See http://examples.extjs.eu/?ex=treestate - there is similar, if not same, code to expand a path.

tchitani
5 Aug 2009, 11:50 PM
Yes, it should work, and the code is similar.
The only difference is that I'm preloading some children on the initial tree load, and then is using on-demand tree load.
Can this be the issue why expandPath does not work?

jsakalos
6 Aug 2009, 12:06 AM
No idea. Try to step into the code with FB to see what's really happening.

acidfilez
13 Aug 2009, 3:46 PM
I am using netbeans 6.7 java web app using the struts 1.3.8.

I wanted to use jsakalos FileTreePanel but I got some problems:

1) java apps comes in a WAR file. (Imposible to add/delete/rename/newdir/newfile without redeploying)
2) jboss for example support exploded deployment. But writing insde your jboss/deploy directory (i think would give you big problem)

Only solution is write to an external directory, I use this path: c:\home\users\factsweb (yes hard coded).

3) The files I wanted to used were html and images which were outside the deploy directory, so this arent expose to jboss, so they cant be link by url.

4) html and images files are private, only logged user can see them.

The solution, lest start, Keep up!


the struts-config-xml:



In the form-beans:

<form-bean name="FileTreeAjaxActionForm" type="cl.company.facts.form.diagrams.FileTreeAjaxActionForm"/>

In the action-mappings:

<action attribute="InstrumentoListActionForm"
input="/html/index.html"
name="FileTreeAjaxActionForm"
path="/FileTree"
scope="request"
validate="true"
type="cl.company.facts.action.diagrams.FileTreeAjaxAction" >
<forward name="continue" path="/jsonWriterTreeNodeItemDTO.do" />
</action>


In the global-forwards:

<forward name="jsonWriterTreeNodeItemDTO" path="/jsp_root/jsonWriterTreeNodeItemDTO.jsp" />

Note: Use this jsp to do the transformation to a json.




the jsonWriterTreeNodeItemDTO.jsp:


<%@ page contentType="application/json;charset=UTF-8" %>
<%@ page import="net.sf.sojo.interchange.Serializer" %>
<%@ page import="net.sf.sojo.interchange.json.JsonSerializer" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="cl.company.informs.util.JsonUtil" %>
<%@ page import="net.sf.sojo.core.filter.ClassPropertyFilter" %>
<%@ page import="net.sf.sojo.core.filter.ClassPropertyFilterHandlerImpl" %>
<%@ page import="cl.company.dto.factsweb.TreeNodeItemDTO" %>
<%
Serializer serializer = new JsonSerializer();
java.lang.String[] pvExcludedProperties = {"~unique-id~", "class"};

ClassPropertyFilterHandlerImpl handler = new ClassPropertyFilterHandlerImpl(
new ClassPropertyFilter(TreeNodeItemDTO.class, new String[]{"~unique-id~"}));
serializer.setClassPropertyFilterHandler(handler);

Object result = serializer.serialize(request.getAttribute("hmResult"));
String jsResult = null;

try{
jsResult = result.toString();
}
catch (Exception e){
System.out.println("Error jsonWriter.jsp:" + e.getMessage());
e.printStackTrace();
}
jsResult = JsonUtil.formatJson(jsResult);
%><%=jsResult%>



the Action (be free to chage SecurityAction to Action):


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package cl.company.facts.action.diagrams;

import cl.company.comun.util.StrUtil;
import cl.company.dto.factsweb.TreeNodeItemDTO;
import cl.company.facts.form.diagrams.FileTreeAjaxActionForm;
import java.util.Hashtable;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cl.company.informs.security.SecurityAction;
import cl.company.informs.util.ListFilesSubs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.sf.sojo.interchange.Serializer;
import net.sf.sojo.interchange.json.JsonSerializer;
import org.apache.struts.upload.FormFile;

public class FileTreeAjaxAction extends SecurityAction {

private static final String CMD_GET = "get";
private static final String CMD_UPLOAD = "upload";
private static final String CMD_DELETE = "delete";
private static final String CMD_DOWNLOAD = "download";
private static final String CMD_RENAME = "rename";
private static final String CMD_NEWDIR = "newdir";

/**
* This is the action returns the listing in a directory,
* FileTreePanel alwasys send a path as root/....
* So all files must go into the directory called root
*
* @param mapping The ActionMapping used to select this instance.
* @param form The optional ActionForm bean for this request.
* @param request The HTTP Request we are processing.
* @param response The HTTP Response we are processing.
* @throws java.lang.Exception
* @return
*/
public ActionForward doExecute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {

FileTreeAjaxActionForm uploadForm = (FileTreeAjaxActionForm) form;
String command = StrUtil.formatStr(uploadForm.getCmd());
if (FileTreeAjaxAction.CMD_GET.equals(command)) {

doGet(mapping, form, request, response);

} else if (FileTreeAjaxAction.CMD_UPLOAD.equals(command)) {

doUpload(mapping, form, request, response);

return null;

} else if (FileTreeAjaxAction.CMD_DELETE.equals(command)) {

doDelete(mapping, form, request, response);

} else if (FileTreeAjaxAction.CMD_NEWDIR.equals(command)) {

doNewDir(mapping, form, request, response);

} else if (FileTreeAjaxAction.CMD_DOWNLOAD.equals(command)) {

Map<String, Object> reply = new HashMap<String, Object>();
reply.put("success", new Boolean(true));
reply.put("cmd", "the command is download not implemented");

} else if (FileTreeAjaxAction.CMD_RENAME.equals(command)) {

doRename(mapping, form, request, response);

} else if (StrUtil.EMPTY.equals(command)) {

Map<String, Object> reply = new HashMap<String, Object>();
reply.put("success", new Boolean(true));
reply.put("cmd", "the command is empty we are returning a true");

}

response.setContentType("application/json;charset=UTF-8");
return mapping.findForward("jsonWriterTreeNodeItemDTO");
}

//Upload Multiple files
//try out
public void doUpload(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> reply = new HashMap<String, Object>();
FileTreeAjaxActionForm uploadForm = (FileTreeAjaxActionForm) form;
//dependin on the comman do this.
String root = "c:\\home\\users\\factsweb" + File.separator;
String path = StrUtil.formatStr(uploadForm.getPath());

try {
if (form.getMultipartRequestHandler() != null) {
Hashtable ble = form.getMultipartRequestHandler().getFileElements();

Enumeration elements = ble.elements();
while (elements.hasMoreElements()) {
FormFile myFile = (FormFile) elements.nextElement();
String contentType = myFile.getContentType();
String fileName = myFile.getFileName();
int fileSize = myFile.getFileSize();
byte[] fileData = myFile.getFileData();

String finalFilePath = root + path + File.separator + fileName;
finalFilePath = finalFilePath.replace("/".charAt(0), File.separatorChar);
finalFilePath = finalFilePath.replace("\\".charAt(0), File.separatorChar);

FileOutputStream file = new FileOutputStream(new File(finalFilePath));
file.write(myFile.getFileData());
file.close();
}
reply.put("success", new Boolean(true));

Serializer serializer = new JsonSerializer();
Object result = serializer.serialize(reply);

response.setContentType("text/html;charset=UTF-8");

PrintWriter out = response.getWriter();
try {
out.print(result.toString());
} finally {
out.close();
}

} else {
reply.put("success", new Boolean(false));
reply.put("error", "getMultipartRequestHandler() is null");


Serializer serializer = new JsonSerializer();
Object result = serializer.serialize(reply);

response.setContentType("text/html;charset=UTF-8");

PrintWriter out = response.getWriter();
try {
out.print(result.toString());
} finally {
out.close();
}
}

} catch (Exception e) {
//should never get here.
e.printStackTrace();
}
}

//GetFiles
public void doGet(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> reply = new HashMap<String, Object>();
FileTreeAjaxActionForm uploadForm = (FileTreeAjaxActionForm) form;
//dependin on the comman do this.
String root = "c:\\home\\users\\factsweb" + File.separator;
String path = StrUtil.formatStr(uploadForm.getPath());
//now add the logic to get the root list
List<TreeNodeItemDTO> noteItemList = new ArrayList<TreeNodeItemDTO>();

try {
TreeNodeItemDTO dto = new TreeNodeItemDTO();
// Call the simple file listing method
Map<String, String> myList = ListFilesSubs.getSimpleFileListing(root + path);
Iterator it = myList.entrySet().iterator();
while (it.hasNext()) {
dto = new TreeNodeItemDTO();
Map.Entry pairs = (Map.Entry) it.next();

dto.setText(pairs.getKey().toString());
dto.setDisabled(new Boolean(false));
//its a directory else a file
if (ListFilesSubs.DIRECTORY.equals(pairs.getValue())) {
dto.setIconcls("folder");
dto.setLeaf(new Boolean(false));
dto.setQtip("");
} else {
dto.setIconcls("file-txt");
dto.setLeaf(new Boolean(true));
dto.setQtip("Size: 1047552");
}
noteItemList.add(dto);
}
request.setAttribute("hmResult", noteItemList);
} catch (Exception e) {
reply.put("success", new Boolean(false));
reply.put("error", "Cannot get by exception:" + e.getMessage());
request.setAttribute("hmResult", reply);
}
}

//Delete Files
public void doDelete(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> reply = new HashMap<String, Object>();
FileTreeAjaxActionForm uploadForm = (FileTreeAjaxActionForm) form;
//dependin on the comman do this.
String root = "c:\\home\\users\\factsweb" + File.separator;
String file = StrUtil.formatStr(uploadForm.getFile());

try {
String finalFilePath = root + file;
finalFilePath = finalFilePath.replace("/".charAt(0), File.separatorChar);
finalFilePath = finalFilePath.replace("\\".charAt(0), File.separatorChar);
File theFile = new File(finalFilePath);
if (theFile.delete()) {
reply.put("success", new Boolean(true));
} else {
reply.put("success", new Boolean(false));
reply.put("error", "Cannot delete:" + file);
}

request.setAttribute("hmResult", reply);
} catch (Exception e) {
reply.put("success", new Boolean(false));

reply.put("error", "Cannot delete by exception:" + e.getMessage());
request.setAttribute("hmResult", reply);
}
}

//Rename Files
public void doRename(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> reply = new HashMap<String, Object>();
FileTreeAjaxActionForm uploadForm = (FileTreeAjaxActionForm) form;
//dependin on the comman do this.
String root = "c:\\home\\users\\factsweb" + File.separator;
String fileOld = StrUtil.formatStr(uploadForm.getOldname());
String fileNew = StrUtil.formatStr(uploadForm.getNewname());

try {

String finalFilePathNew = root + fileNew;
finalFilePathNew = finalFilePathNew.replace("/".charAt(0), File.separatorChar);
finalFilePathNew = finalFilePathNew.replace("\\".charAt(0), File.separatorChar);
File theFileNew = new File(finalFilePathNew);


String finalFilePathOld = root + fileOld;
finalFilePathOld = finalFilePathOld.replace("/".charAt(0), File.separatorChar);
finalFilePathOld = finalFilePathOld.replace("\\".charAt(0), File.separatorChar);
File theFileOld = new File(finalFilePathOld);

if (theFileOld.renameTo(theFileNew)) {
reply.put("success", new Boolean(true));
} else {
reply.put("success", new Boolean(false));
reply.put("error", "Cannot rename file:'" + finalFilePathOld + "' to '" + finalFilePathNew + "'");
}

request.setAttribute("hmResult", reply);
} catch (Exception e) {
reply.put("success", new Boolean(false));
reply.put("error", "Cannot renmae by exception:" + e.getMessage());
request.setAttribute("hmResult", reply);
}
}

//New Directory
public void doNewDir(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> reply = new HashMap<String, Object>();
FileTreeAjaxActionForm uploadForm = (FileTreeAjaxActionForm) form;
//dependin on the comman do this.
String root = "c:\\home\\users\\factsweb" + File.separator;
String nameDir = StrUtil.formatStr(uploadForm.getDir());

try {
String finalNewDirectory = root + nameDir;
finalNewDirectory = finalNewDirectory.replace("/".charAt(0), File.separatorChar);
finalNewDirectory = finalNewDirectory.replace("\\".charAt(0), File.separatorChar);
File theNewDirectory = new File(finalNewDirectory);

if (theNewDirectory.mkdir()) {
reply.put("success", new Boolean(true));
} else {
reply.put("success", new Boolean(false));
reply.put("error", "Cannot make dir: " + finalNewDirectory);
}

request.setAttribute("hmResult", reply);
} catch (Exception e) {
reply.put("success", new Boolean(false));
reply.put("error", "Cannot make dir by exception:" + e.getMessage());
request.setAttribute("hmResult", reply);
}
}
}



the ActionForm:


/*
* Generated by MyEclipse Struts
* Template path: templates/java/JavaClass.vtl
*/
package cl.company.facts.form.diagrams;

import java.util.ArrayList;
import org.apache.struts.upload.FormFile;

/**
* Registro de versiones: <ul>
*
* <li>FileTreeAjaxActionForm</li>
* <li>$Id: FileTreeAjaxActionForm.java,v 1.1 2008/09/26 22:25:49 mcardona Exp $</li>
* </ul>
*
* @author Magno Cardona Heck.
* @author Anon
* @version $Revision: 1.1 $,$Date: 2008/09/26 22:25:49 $
*
* @struts.form name="FileTreeAjaxActionForm"
*/
public class FileTreeAjaxActionForm extends org.apache.struts.action.ActionForm {

private static long serialVersionUID = 4793888812818184727L;


private ArrayList<FormFile> formFiles = new ArrayList<FormFile>();
private FormFile f;

public void setTestFile(int iIndex, FormFile formFile) {
f = formFile;
this.formFiles.add(formFile);
}

public FormFile getTestFile(int iIndex) {
while (this.formFiles.size() <= iIndex) {
this.formFiles.add(f);
}
return (FormFile) formFiles.get(iIndex);
}

public ArrayList getTestFiles() {
return this.formFiles;
}
/*
* Generated fields
*/
/** property */
private String cmd;
/** property */
private String node;
/** property */
private String path;
/** property */
private String file;
/** property */
private String newname;
/** property */
private String oldname;
/** property */
private String dir;




/**
* @return the cmd
*/
public String getCmd() {
return cmd;
}

/**
* @param cmd the cmd to set
*/
public void setCmd(String cmd) {
this.cmd = cmd;
}

/**
* @return the node
*/
public String getNode() {
return node;
}

/**
* @param node the node to set
*/
public void setNode(String node) {
this.node = node;
}

/**
* @return the path
*/
public String getPath() {
return path;
}

/**
* @param path the path to set
*/
public void setPath(String path) {
this.path = path;
}

/**
* @return the file
*/
public String getFile() {
return file;
}

/**
* @param file the file to set
*/
public void setFile(String file) {
this.file = file;
}

/**
* @return the newname
*/
public String getNewname() {
return newname;
}

/**
* @param newname the newname to set
*/
public void setNewname(String newname) {
this.newname = newname;
}

/**
* @return the oldname
*/
public String getOldname() {
return oldname;
}

/**
* @param oldname the oldname to set
*/
public void setOldname(String oldname) {
this.oldname = oldname;
}

/**
* @return the dir
*/
public String getDir() {
return dir;
}

/**
* @param dir the dir to set
*/
public void setDir(String dir) {
this.dir = dir;
}
}




the index.html (can be a jsp):


<html>
<head>
<title>Tree</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="../js/extjs/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="../css/application.css" />
<link rel="stylesheet" type="text/css" href="../js/extjs/resources/css/core.css" />

<link rel="stylesheet" type="text/css" href="../js/filetreepanel/css/icons.css">
<link rel="stylesheet" type="text/css" href="../js/filetreepanel/css/filetree.css">
<link rel="stylesheet" type="text/css" href="../js/filetreepanel/css/filetype.css">
<link rel="stylesheet" type="text/css" href="../js/filetreepanel/css/famflag.css">

<link rel="stylesheet" type="text/css" href="../js/filetreepanel/css/Ext.ux.IconCombo.css">


</head>
<body>
<div id="loading-mask" style=""></div>
<div id="loading">
<div class="loading-indicator">
<img src="../images/ajax-loader.gif" alt="" style="margin-right:8px;float:left;vertical-align:text-top;"/>
Cargando - Arbol<br/><span id="loading-msg">Loading styles and images...</span>
</div>
</div>
<div id="viewport">
<div id="bd">
<div class="left-column">
<br/>

<script type="text/javascript">document.getElementById('loading-msg').innerHTML = 'Cargando Core API...';</script>
<script type="text/javascript" src="../js/extjs//adapter/ext/ext-base.js"></script>

<script type="text/javascript">document.getElementById('loading-msg').innerHTML = 'Cargando UI Componentes...';</script>
<script type="text/javascript" src="../js/extjs/ext-all.js"></script>

<script type="text/javascript">document.getElementById('loading-msg').innerHTML = 'Cargando Locales ES...';</script>
<script type="text/javascript" src="../js/extjs/source/locale/ext-lang-es.js" charset="utf-8"></script>

<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.form.BrowseButton.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.FileUploader.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.UploadPanel.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.FileTreeMenu.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.FileTreePanel.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.form.ThemeCombo.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.form.IconCombo.js"></script>
<script type="text/javascript" src="../js/filetreepanel/js/Ext.ux.form.LangSelectCombo.js"></script>


<!-- Last Script to run -->
<script type="text/javascript">document.getElementById('loading-msg').innerHTML = 'Inicializando...';</script>
<script type="text/javascript" src="initialize.js"></script>

</div>
</div><!-- end bd -->
</div><!-- end viewport -->
</body>
</html>


the initialize.js (change this file to your needs because i am copying,deleting,mkdir,etc inside the jar, so i use /path/ in a servlet dipatcher to feed hmtl and images based if the user is authentificated to the server):


/*jslint adsafe:false, bitwise:false, browser:false, cap:false, debug:false, eqeqeq:true, evil:false, forin:false, fragment:false, glovar:true, laxbreak:false, nomen:true, on:false, passfail:false, plusplus:false, rhino:false, sidebar:false, undef:true, white:true, widget:false */
/*extern Ext console window netscape Components Tokenizer document setTimeout*/

/**
* An Application
*
* @author Magno C Heck
* @copyright (c) 2008, by Anon
* @date 30 April 2008
* @version $Id: initialize.js,v 1.2 2008/10/27 15:03:31 mcardona Exp $
*
* @license application.js is licensed under the terms of the Open Source
* LGPL 3.0 license. Commercial use is permitted to the extent that the
* code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/
Ext.namespace('Ext.myApp');

Ext.EventManager.on(window, 'load', function () {

//this is just a virtual place holder.
//all the files are read from the filesystem. check the manual.

var MyNewTree = Ext.extend(Ext.ux.FileTreePanel, {
constructor: function(config) {
config = Ext.apply({

}, config);
MyNewTree.superclass.constructor.call(this, config);
},


onDblClick:function(node, e) {
this.openNode(node,'_self', true);
}, // eo function onDblClick


//we need to change the /html/root/ to /path/ for the security reason.
//adding redirect to do a simple if and do its magic.
openNode:function(node, mode, redirect) {

if(!this.enableOpen) {
return;
}

mode = mode || this.openMode;

var url;
var path;
if(node.isLeaf()) {
path = this.getPath(node);
url = this.hrefPrefix + path + this.hrefSuffix;
if (redirect){
//chagne root/ to path/ but wait its contexts is still in the html/ directory
//force it to the base using ../
url = url.replace(/root\//,'../path/');
}

// fire beforeopen event
if(true !== this.eventsSuspended && false === this.fireEvent('beforeopen', this, node.text, url, mode)) {
return;
}

switch(mode) {
case 'popup':
if(!this.popup || this.popup.closed) {
this.popup = window.open(url, this.hrefTarget, this.popupFeatures);
}
this.popup.location = url;
if(this.focusPopup) {
this.popup.focus();
}
break;

case '_self':
window.location = url;
break;

case '_blank':
window.open(url);
break;

case 'download':
this.downloadFile(path, true);
break;
}

// fire open event
if(true !== this.eventsSuspended) {
this.fireEvent('open', this, node.text, url, mode);
}
}

},

downloadFile:function(path, redirect) {
if (redirect){
//chagne root/ to path/ but wait its contexts is still in the html/ directory
//force it to the base using ../
path = path.replace(/root\//,'../path/');
}
// create hidden target iframe
var id = Ext.id();
var frame = document.createElement('iframe');
frame.id = id;
frame.name = id;
frame.className = 'x-hidden';
if(Ext.isIE) {
frame.src = Ext.SSL_SECURE_URL;
}

document.body.appendChild(frame);

if(Ext.isIE) {
document.frames[id].name = id;
}

var form = Ext.DomHelper.append(document.body, {
tag:'form'
,
method:'post'
,
action:this.downloadUrl || this.url
,
target:id
});

document.body.appendChild(form);

var hidden;

// append cmd to form
hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'cmd';
hidden.value = 'download';
form.appendChild(hidden);

// append path to form
hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'path';
hidden.value = path;
form.appendChild(hidden);

var callback = function() {
Ext.EventManager.removeListener(frame, 'load', callback, this);
setTimeout(function() {
document.body.removeChild(form);
}, 100);
setTimeout(function() {
document.body.removeChild(frame);
}, 110);
};

Ext.EventManager.on(frame, 'load', callback, this);

form.submit();
}

});










//VIEWPORT APPLICATION
Ext.myApp.viewPort = new Ext.Viewport({
items: new MyNewTree({
height:700,
autoWidth: true,
id: 'ftpBrowser',
renderTo: 'viewport',
rootPath: 'root',
topMenu: true,
url:'../FileTree.do',
autoScroll: true,
enableProgress: false,
enableDD: false,
openMode: '_self', //is overwritten in the dblClick too
rootText: "inicio",
singleUpload: false
// baseParams:{additional:'haha'}
})
});

//hides the init loader
setTimeout(function () {
Ext.get('loading').remove();
Ext.get('loading-mask').fadeOut({
easing: 'easeOut',
duration: 3,
remove: true
});

//show the samart stuff here

Ext.myApp.viewPortHeight = Ext.myApp.viewPort.getSize().height;

//Some error will hunt me forever
Ext.getCmp('ftpBrowser').setHeight(Ext.myApp.viewPortHeight - 3);

// window with uploadpanel
Ext.myApp.uploadWin = new Ext.Window({
width:180,
minWidth:165,
id:'winid',
height:220,
minHeight:200,
layout:'fit',
border:false,
closable:false,
title:'UploadPanel',
iconCls:'icon-upload',
items:[{
xtype:'uploadpanel',
buttonsAt:'tbar',
id:'uppanel',
url:'../FileTree.do',
path:'root',
maxFileSize:1048576
}]
});
//Ext.myApp.uploadWin.show.defer(500, win);

}, 250);

});
//EOF


In case you are interested in the html/image dispatcher servlet:


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package cl.company.informs.controller;

import cl.company.comun.util.FileUtil;
import cl.company.comun.util.StrUtil;
import cl.company.j2ee.SessionUtil;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
*
* @author Magno
*/
public class WebContentDispatcher extends HttpServlet {

/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
*
* /capture all url after the /path url.
*
* <servlet-mapping>
* <servlet-name>WebContentDispatcher</servlet-name>
* <url-pattern>/path/*</url-pattern>
* </servlet-mapping>
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

String root = "c:\\home\\users\\factsweb" + File.separator + "root" + File.separator;

String contextPath = request.getContextPath();
String requestURL = request.getRequestURL().toString();

String path = requestURL.replaceAll("http.*?" + contextPath + "/path/", "");
String finalFilePath = root + path;
finalFilePath = finalFilePath.replace("/".charAt(0), File.separatorChar);
finalFilePath = finalFilePath.replace("\\".charAt(0), File.separatorChar);
//System.out.println("finalFilePath:" + finalFilePath);
String fileExtention = FileUtil.getFileExtention(finalFilePath);
//System.out.println("fileExtention:" + fileExtention);

String imagesTypes = "gif,jpg,jpeg,png,tif,tiff,pbm,bmp";

if (StrUtil.isNotEmpty(fileExtention)) {
fileExtention = fileExtention.toLowerCase();

// If the request is an image
if (imagesTypes.indexOf(fileExtention) >= 0) {
ServletOutputStream stream = null;
BufferedInputStream buf = null;
try {
stream = response.getOutputStream();
if (!SessionUtil.isAuthorize(request)) {
//change file to an error
fileExtention = "jpeg";
finalFilePath = root + ".." + File.separator + "global" +
File.separator + "access_denied.jpg";
}
File theFile = new File(finalFilePath);
response.setContentType("image/" + fileExtention);
response.setContentLength((int) theFile.length());
FileInputStream input = new FileInputStream(theFile);
buf = new BufferedInputStream(input);
int readBytes = 0;
while ((readBytes = buf.read()) != -1) {
stream.write(readBytes);
}
} catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
} finally {
if (stream != null) {
stream.close();
}
if (buf != null) {
buf.close();
}
}

// If the request is text/html, etc.
} else { //text/html
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (!SessionUtil.isAuthorize(request)) {
//change file to an error

finalFilePath = root + ".." + File.separator + "global" +
File.separator + "access_denied.html";
//System.out.println("final:" + finalFilePath);
}
String content = FileUtil.loadFile(finalFilePath);
if(StrUtil.isEmpty(content)){
content = "Error Reading File at Location: " + finalFilePath;
//example http://localhost:8080/FactsWEB/path/html/root/Applications/Prod/Prod.html
}
try {
out.print(content);
} finally {
out.close();
}
}

}


}

public static void main(String[] args) {
String finalFilePath = "c:\\home\\users\\factsweb\\root\\Applications/Prod/Prod.html";
finalFilePath = finalFilePath.replace("/".charAt(0), File.separatorChar);
finalFilePath = finalFilePath.replace("\\".charAt(0), File.separatorChar);
//System.out.println("final:" + finalFilePath);
}

// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Handles the HTTP <code>POST</code> method.
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Returns a short description of the servlet.
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Short description";
}// </editor-fold>
}


this servlet need to be configure in the web.xml:


<servlet>
<servlet-name>WebContentDispatcher</servlet-name>
<servlet-class>cl.company.informs.controller.WebContentDispatcher</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>WebContentDispatcher</servlet-name>
<url-pattern>/path/*</url-pattern>
</servlet-mapping>


If someone request this uri


http://localhost:8080/YourAppNameWEB/path/gif/someImage.gif



The WebContentDispatcher will go get the file from your root then go inside the folder gif, reads the someImage.gif and send the image via the servlet.

But before sending it it checks if the user is inside the session, if not it will read from root/global/access_denied.jpg and send a nice image saying "access denied". Same for the html access_denied.html.

The SessionUtil.isAuthorize have to be done by you:


public static boolean isAuthorize(HttpServletRequest request) { ..
//check if session expired
//check if user is logged.
}





Note: upload needs to send response.setContentType("text/html;charset=UTF-8");
otherwise it will download the .do file.

Note2: download is not implemented in this release.

Note3: i neede to add this image from your ext resources

FactsWEB\web\js\filetreepanel\ext\resources\images\default\grid\wait.gif
FactsWEB\web\js\filetreepanel\ext\resources\images\default\tree\folder.gif
FactsWEB\web\js\filetreepanel\ext\resources\images\default\tree\folder-open.gif

FactsWeb = the name of my java web app.

Note4: yes you will need to change my code to make it work for you. Lazy !!

Bobrovnik
18 Aug 2009, 8:33 AM
Hi, are you going to make version valid for Ext 3.

When I am trying to use your plugin and it throws me an exeptions. Like in topic https://extjs.com/forum/showthread.php?p=375574#post375574 Could you please suggest any solution?

Bobrovnik
19 Aug 2009, 8:10 AM
After a day of trying to find out mistake. I have made some changes to FileTree extention, so now it works with Extjs 3.0 version.

In file Ext.ux.FileTreeMenu.js



var uploadPanelConfig = {
contextmenu:this
,buttonsAt:config.buttonsAt || 'tbar'
,singleUpload:config.singleUpload || false
,maxFileSize:config.maxFileSize
,enableProgress:config.enableProgress
};
should be replaced with


var uploadPanelConfig = {
hideOnClick:false
,cmd:'upload-panel'
,contextmenu:this
,buttonsAt:config.buttonsAt || 'tbar'
,singleUpload:config.singleUpload || false
,maxFileSize:config.maxFileSize
,enableProgress:config.enableProgress
};


,new Ext.menu.Adapter(new Ext.ux.UploadPanel(uploadPanelConfig), {
hideOnClick:false
,cmd:'upload-panel'
})
with


,new Ext.ux.UploadPanel(uploadPanelConfig)
in file Ext.ux.FileTreePanel.js



this.uploadPanel = this.contextmenu.getItemByCmd('upload-panel').component;
replace with


this.uploadPanel = this.contextmenu.getItemByCmd('upload-panel');
in file Ext.ux.FileUploader.js



record.set('form', form);
with


if(Ext.isIE)
record.set('form', undefined); // IE fix, without this it throws an exception
else
record.set('form', form);
in file Ext.ux.form.BrowseButton.js



this.buttonCt = this.el.child('.x-btn-center em');
with


this.buttonCt = this.el.child('.x-btn-mc em');


style: {
position: 'absolute',
cursor: 'pointer',
right: '0px',
top: '10px'
}
with


style: {
position: 'absolute',
cursor: 'pointer',
right: '0px',
top: Ext.isIE ? '10px' :'0px' // Also another IE fix
}
in file Ext.ux.UploadPanel.js Add line



this.ownerCt.doLayout();


,onAddFile:function(bb) {

if(true !== this.eventsSuspended && false === this.fireEvent('beforefileadd', this, bb.getInputFile())) {
return;
}
var inp = bb.detachInputFile();
inp.addClass('x-hidden');
var fileName = this.getFileName(inp);

// create new record and add it to store
var rec = new this.store.recordType({
input:inp
,fileName:fileName
,filePath:this.getFilePath(inp)
,shortName: Ext.util.Format.ellipsis(fileName, this.maxLength)
,fileCls:this.getFileCls(fileName)
,state:'queued'
}, inp.id);
rec.commit();
this.store.add(rec);

this.syncShadow();

this.uploadBtn.enable();
this.removeAllBtn.enable();

this.ownerCt.doLayout();

if(true !== this.eventsSuspended) {
this.fireEvent('fileadd', this, this.store, rec);
}

} // eo onAddFile

jsakalos
19 Aug 2009, 10:07 AM
Thank you very much for finding and sharing these patches.

avsomeren
20 Aug 2009, 9:47 PM
In the last codeblocks of your patch you are writing :



in file Ext.ux.FileUploader.js Add line


this.ownerCt.doLayout();



i presume you mean Ext.ux.UploadPanel.js ...

Bobrovnik
20 Aug 2009, 11:18 PM
Yes, you are right, sorry. I will correct that

avsomeren
21 Aug 2009, 8:08 AM
Well, don't be sorry, you did a great job fixing these issues!

Thank you for that.

jpipas
22 Aug 2009, 10:16 AM
Thanks for those patches Bobrovnik. However, when I go to click the 'File' button or right-click on the filetreemenu, I get this exception

menu.getEl() is undefined - on line 1747 of FileTreePanel.js



menu.showAt(menu.getEl().getAlignToXY(alignEl, 'tl-bl?'));\n


I can confirm that the contextMenu gets created - however, the second time around when the function just returns it - I get the error.

Any ideas? Appreciate the help.

Bobrovnik
22 Aug 2009, 11:42 AM
That is also my mistake, I have forgotten to add to patches list one line. When you are using Ext 3.0 you will have to render this menu. If I am not mistaken I have added line menu.render(); after



menu.getItemByCmd('sep-collapse').setVisible(this.enableNewDir || this.enableDelete || this.enableRename);


in file FileTreeMenu.js

But I am not sure about it, I will confirm on monday, when I get to the source code

jpipas
22 Aug 2009, 2:59 PM
Excellent. The menu.render() actually goes in FileTreePanel.js - after the line (1467)



menu.getItemByCmd('sep-collapse').setVisible(this.enableNewDir || this.enableDelete || this.enableRename);


Now the menu renders - however, the upload panel within the menu is missing. The upload panel shows - but theres no buttons, or anything inside, as if it were blank.

I'll see if I can do some more investigating myself.

wallacer
17 Sep 2009, 2:13 PM
Can anyone give me some ideas as to why my uploads don't stop when I click the stop upload button? Has anyone else had this issue?



var newUploadPanel = new Ext.ux.UploadPanel({

url: PROXY_PATH+'newUpload.php',
path: navPanel.getSelectionModel().getSelectedNode().id,
method: 'post',
progressUrl: PROXY_PATH+'progress.php',
maxLength: 50,
width: 'auto',
minHeight: 500
});

jsakalos
21 Sep 2009, 10:50 AM
Stops are done calling browser's window.stop() method so it can be browser dependent. Is it?

fraric
24 Sep 2009, 7:34 AM
hi,
i want an information about Ext 2.3 and file uploader.
i've read in change log 2.3 ext have change Ext.data.Connection doFormUpload

http://www.extjs.com/deploy/ext-2.3.0/changelog.html

Ext.data.Connection
Document request's timeout option.
Document that the url config of Ext.data.Connection may be a function.
Restore form attributes after doFormUpload.
i want to say why you have write this fix/upgrade

i think are this add code

buf = {
target: form.target,
method: form.method,
encoding: form.encoding,
enctype: form.enctype,
action: form.action
};

and at the end function


form.target = buf.target;
form.method = buf.method;
form.enctype = buf.enctype;
form.encoding = buf.encoding;
form.action = buf.action;

this buffer clean target element i frame

in FileUploader coponent when i want to stop upload component find target of form but buffer have clean target and stop cannot find iframe and cannot stop upload

, stopUpload: function(record) in file uploader call getIframe

/**
* Geg the iframe identified by record
* @private
* @param {Ext.data.Record} record
* @return {Ext.Element} iframe or null if not found
*/
, getIframe: function(record)
{
var iframe = null;
var form = record.get('form');
if (form && form.dom && form.dom.target)/**/ this is empty value
{
iframe = Ext.get(form.dom.target);
}
return iframe;
} // eo function getIframe

what i can do for fix this problem?
i change doFormUpload???
i 've read Ext 3.0 doFormUpload already change? and dont use the same code...
(Restore form attributes after doFormUpload) it's a functionality for ext 3.0...


if i make an override of this function doFormUpload version 3.0 in Ext.2.3 upload i can have aproblem?
it's a correct approach?
thanks a lot for help
ric

smartlit
24 Sep 2009, 8:09 AM
jsakalos: the above post by fraric is quite difficult to understand, but it simply means that under Ext 2.3 the STOP function in your component stopped working. It was working in Ext 2.2.1, but it stopped under 2.3.
Can you please check why? What can we do to fix that?
Thanks,

Paul

jsakalos
24 Sep 2009, 4:41 PM
Which one of those three (form && form.dom && form.dom.target)is falsie?

It could be some timing or a changed sequence of something in 2.3

smartlit
25 Sep 2009, 12:16 AM
Which one of those three (form && form.dom && form.dom.target)is falsie?

It could be some timing or a changed sequence of something in 2.3

form.dom.target is an empty string, so it returns false. It's an empty string because it's overwritten by the following code (buf.target is empty):


form.target = buf.target;

as soon as the form has been submitted. So, when you try to stop it, you don't know the ID of the hidden iframe.

What can I do?

smartlit
25 Sep 2009, 12:18 AM
It also seems that version "3.0 final" has this problem (only the "3.0 RC1" did not have the problem).

jsakalos
25 Sep 2009, 12:33 AM
I haven't ported it to 3.x yet but I can take a brief look to find out why it's empty hopefully this weekend.

smartlit
25 Sep 2009, 12:35 AM
I haven't ported it to 3.x yet but I can take a brief look to find out why it's empty hopefully this weekend.

Thank you very much. When you solve it for 3.0, I think it will be automatically solved also for 2.3.

Eric24
1 Oct 2009, 5:59 AM
@Saki--Do you have any thoughts on when you might port to Ext 3.x? Tnx!

jsakalos
1 Oct 2009, 3:04 PM
I've started re-writing (not porting ;) ) my main project to support Ext 3 and FileTreePanel is a part of it. However, I must still keep old version (Ext 2.x + Ext 1.x in iframe) running and I still need to add some features and fix bugs - this is top priority.

So I cannot say or promise a date; the all I can say now is: "I'm working on it."

zombeerose
7 Oct 2009, 3:38 PM
After following the changes made by other users for Ext 3 compatibility, I revised the FileTreePanel to utilize the Ext.ux.form.FileUploadField extension instead. Below are the changes necessary thus far.

In the UploadPanel.js...
* replace


var addCfg = {
xtype:'browsebutton'
,text:this.addText + '...'
,iconCls:this.addIconCls
,scope:this
,handler:this.onAddFile
};


* with


var addCfg = {
buttonCfg: {
hideLabel: true
,iconCls:this.addIconCls
}
,buttonText:this.addText + '...'
,buttonOnly: true //no textfield
,listeners: {
fileselected: {fn:this.onAddFile, scope:this}
}
,style: { display: 'none' } //IE hack - can't use visibility b/c IE buffers the space
,xtype:'fileuploadfield'
};


* replace


/**
* called when file is added - adds file to store
* @private
* @param {Ext.ux.BrowseButton}
*/
,onAddFile:function(bb) {
if(true !== this.eventsSuspended && false === this.fireEvent('beforefileadd', this, bb.getInputFile())) {
return;
}
var inp = bb.detachInputFile();
inp.addClass('x-hidden');


* with


/**
* called when file is added - adds file to store
* @private
* @param {Ext.form.FileUploadField} fu
* @param {String} fileName
*/
,onAddFile:function(fu,fileName) {
var inp = fu.getFileInput();
if(true !== this.eventsSuspended && false === this.fireEvent('beforefileadd', this, inp)) {
return;
}
fu.detachFileInput();


Instead of adding this code to the onAddFile method per the recommendation of @ Bobrovnik, add it to the syncShadow method after the call to show.


this.ownerCt.doLayout();


It is also necessary to make the following changes to the FileUploadField.js
* New methods:


/**
* Detaches the input file without clearing the value so that it can be used for
* other purposes (e.g. uploading).
*
* The returned input file has all listeners applied to it by this class removed.
* @return {Ext.Element} the detached input file element.
*/
detachFileInput : function(){
var result = this.fileInput;

this.fileInput.removeAllListeners();
this.fileInput = null;
this.id = Ext.id(); //avoid dom conflicts
this.createFileInput();
this.bindListeners();

return result;
},

/**
* @return {Ext.Element} the input file element
*/
getFileInput: function(){
return this.fileInput;
},


* Change this line so the concatenation is disabled. Otherwise the icon will be overlayed with the text.


cls: 'x-form-file-btn' //+ (btnCfg.iconCls ? ' x-btn-icon' : '')


Thanks again Saki !!!

jsakalos
8 Oct 2009, 1:20 AM
Thank you very much.

zombeerose
8 Oct 2009, 7:58 AM
I encountered an issue in Chrome when trying to upload because it fails when trying to encode the form during the record.set operation. I think IE also bombs during the encode. I have reported this to ext (http://www.extjs.com/forum/showthread.php?p=395726)

In the meantime, below are my (hack) changes to the FileUploader.js that address both the IE & Chrome issues.

* Replace:

record.set('form', form);

* With:

record.form = form;

* Replace:

record.set('form', null);

* With:

delete record.form;

* Replace:

var form = record.get('form');

* With:

var form = record.form;

If anyone has a better suggestion, please feel free.

Stephan Schrade
13 Oct 2009, 8:29 AM
Thank you zombeerose,
your changes got at least the Add-Button working within ExtJs 3.0.

But I don't understand your extension of methods to FileUploadField.js
I don't have the functions
createFileInput();
bindListeners();

which you are calling in your detachFileInput method.
Am I missing something ?

TIA Stephan