~azzar1/unity/add-show-desktop-key

136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
1
# IVLE
2
# Copyright (C) 2007-2008 The University of Melbourne
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18
# App: File Browser
19
# Author: Matt Giuca
20
# Date: 9/1/2008
21
22
# The file browser application. Presents an Ajax-based interface to the
23
# student's subversion workspace.
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
24
# Note that there is virtually no server-side code for this application. It
25
# simply presents static HTML and JavaScript, and all server-side activities
26
# take place in the FileService app (for handling Ajax requests).
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
27
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
28
import os.path
29
import cgi
30
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
31
from ivle import (util, studpath)
32
import ivle.svn
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
33
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
34
# url path for this app
35
THIS_APP = "files"
36
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
37
def handle(req):
38
    """Handler for the File Browser application."""
39
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
40
    # Work out where we are browsing
41
    browsepath = req.path
42
    if len(browsepath) == 0:
43
        # If no path specified, default to the user's home directory
752 by dcoles
Browser: Redirect to 'files/LOGIN' when URL is just 'files' rather than just
44
        redirectPath = util.make_path(os.path.join(THIS_APP,req.user.login))
45
        req.throw_redirect(util.make_path(redirectPath))
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
46
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
47
    # Set request attributes
48
    req.content_type = "text/html"
383 by mattgiuca
Split media/browser/browser.css into 3 files (listing.css and editor.css).
49
    req.styles = [
50
        "media/browser/browser.css",
51
        "media/browser/listing.css",
52
        "media/browser/editor.css",
53
    ]
170 by mattgiuca
browser: Added CSS and JS files (not much in them).
54
    req.scripts = [
55
        "media/common/json2.js",
809 by William Grant
Merge killall-editarea branch. We now use CodePress instead, which is
56
        "media/common/codepress/codepress.js",
171 by mattgiuca
Added "util.js" in common. Contains useful utility functions.
57
        "media/common/util.js",
170 by mattgiuca
browser: Added CSS and JS files (not much in them).
58
        "media/browser/browser.js",
209 by mattgiuca
browser.js: Split out dir-listing code into listing.js (it's going to get
59
        "media/browser/listing.js",
220 by mattgiuca
browser: Removed 3 buttons which didn't do anything.
60
        "media/browser/editor.js",
944 by dcoles
Special Home Directory: Work to create a special home directory that shows the
61
        "media/browser/specialhome.js",
170 by mattgiuca
browser: Added CSS and JS files (not much in them).
62
    ]
849 by dcoles
Browser: Ported Browser to the new scripts_init framework. Started to decouple
63
    req.scripts_init = [
64
        "browser_init",
65
    ]
713 by dcoles
browser: Added console and run button to allow Python files to be executed in
66
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
67
    req.write_html_head_foot = True     # Have dispatch print head and foot
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
68
    # The page title should contain the name of the file being browsed
69
    req.title = browsepath.rsplit('/', 1)[-1]
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
70
609 by mattgiuca
browser: Now stats the file requested before doing anything else.
71
    _, localpath = studpath.url_to_local(browsepath)
72
    if localpath is None:
73
        req.throw_error(req.HTTP_NOT_FOUND,
74
            "The path specified is invalid.")
75
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
76
    # Start writing data
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
77
    req.write("""
78
<!-- Top bar section -->
79
80
<div id="topbar">
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
81
  <div id="path">
82
    """)
835 by wagrant
Cleanup from the last revision. Don't pass isdir if not necessary.
83
    # FIXME: This isn't completely reliable! We're not inside the jail, so we
84
    # can't know the type for sure. This is now only used for adding a / to the
85
    # end of displayed paths, so I'm leaving this although it will often break.
609 by mattgiuca
browser: Now stats the file requested before doing anything else.
86
    isdir = os.path.isdir(localpath)
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
87
    presentpath(req, browsepath, isdir)
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
88
    req.write("""
89
  </div>
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
90
  <div id="actions1">
91
""")
92
    present_actions1(req)
93
    req.write("""  </div>
94
  <div id="actions2">
95
""")
835 by wagrant
Cleanup from the last revision. Don't pass isdir if not necessary.
96
    present_actions2(req)
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
97
    req.write("""  </div>
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
98
</div>
99
<!-- End topbar -->
100
203 by mattgiuca
browser: Removed all directory-listing specific HTML from the Python-generated
101
<!-- Body. The JavaScript places content here relevant to the path -->
102
<div id="filesbody">
103
</div>
104
<!-- End body -->
105
""")
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
106
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
107
def presentpath(req, path, isdir):
108
    """
109
    Presents a path list (address bar inside the page) for clicking.
110
    Writes to req, expecting to have just written the opening div containing
111
    the listing.
907 by wagrant
browser: Display the current revision next to the current path if the
112
    This will also have a revision indicator on the end, if we are viewing a
113
    revision.
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
114
    """
115
    href_path = util.make_path(THIS_APP)
116
    nav_path = ""
117
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
118
    revision = ivle.svn.revision_from_string(
907 by wagrant
browser: Display the current revision next to the current path if the
119
                     req.get_fieldstorage().getfirst('r'))
120
121
    try: 
122
        revno = revision.number
123
    except:
124
        revno = None
125
       
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
126
    # Create all of the paths
127
    pathlist = path.split("/")
128
    segs_left = len(pathlist)
129
    for path_seg in pathlist:
130
        if path_seg == "":
131
            continue
132
        # Write a slash at the end unless this is the last path seg AND
133
        # it's not a directory.
134
        segs_left -= 1
135
        add_slash = segs_left != 0 or isdir
136
        # Make an 'a' element
137
        href_path = href_path + '/' + path_seg
138
        nav_path = nav_path + path_seg
139
        if add_slash:
140
            nav_path = nav_path + '/'
141
        link = '<a href="%s" title="Navigate to %s">%s</a>' % (
908 by wagrant
browser: Link breadcrumbs to the current revision.
142
            href_path if revno is None else href_path + '?r=%d' % revno,
143
            nav_path, path_seg)
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
144
        req.write(link)
145
        if add_slash:
146
            req.write('/')
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
147
907 by wagrant
browser: Display the current revision next to the current path if the
148
    if revno is not None:
149
        req.write(' (revision %d)' % revno)
150
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
151
def present_actions1(req):
152
    """
153
    Presents a set of links/buttons for the "actions1" row of the top bar.
154
    This is always exactly the same - the JavaScript will customize it later.
155
    """
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
156
    # Set up our actions. The second field of each group is whether to disable
157
    # the items by default.
158
    moreactions = [
159
      ('Publishing', True, [
160
        ('publish', ['Publish',          'Make it so this directory can be seen by anyone on the web']),
161
        ('share',   ['Share this file',  'Get a link to this published file, to give to friends']),
864 by wagrant
browser: Fix moreactions' Submit option's name.
162
        ('submit',  ['Submit', 'Submit the selected files for an assignment'])
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
163
      ]),
164
      ('File actions', True, [
165
        ('rename',  ['Rename',           'Change the name of this file']),
166
        ('delete',  ['Delete',           'Delete the selected files']),
167
        ('copy',    ['Copy',             'Prepare to copy the selected files to another directory']),
168
        ('cut',     ['Cut',              'Prepare to move the selected files to another directory'])
169
      ]),
170
      ('Directory actions', False, [
171
        ('paste',   ['Paste',            'Paste the copied or cut files into the current directory']),
172
        ('newfile', ['New File',         'Open a new file for editing in the current directory']),
173
        ('mkdir',   ['New Directory',    'Make a new subdirectory in the current directory']),
174
        ('upload',  ['Upload File',      'Upload a file to the current directory'])
175
      ]),
176
      ('Subversion', True, [
177
        ('svnadd',      ['Add',          'Schedule the selected temporary files to be added permanently']),
921 by wagrant
fileservice_lib, browser: Rename the old remove action to delete, to
178
        ('svnremove',   ['Remove',       'Schedule the selected permanent files to be removed']),
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
179
        ('svndiff',     ['Diff',         'View any changes to the selected file since its last committed state']),
180
        ('svnrevert',   ['Revert',       'Restore the selected files back to their last committed state']),
918 by wagrant
browser: Expose svn update functionality. This brings a need for an
181
        ('svnupdate',   ['Update',       'Update your files with changes from the permanent repository']),
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
182
        ('svncommit',   ['Commit',       'Commit any changes to the permanent repository']),
919 by wagrant
fileservice_lib: Implement "svn resolved".
183
        ('svnresolved', ['Mark Resolved','Mark a conflicted file as being resolved']),
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
184
        ('svnlog',      ['View Log',     'View the log of commits of the selected file']),
185
      ])
186
    ]
187
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
188
    req.write("""    <a id="act_open" class="disabled">Open</a> :
597 by mattgiuca
Major refactor of actions in File browser.
189
    <a id="act_serve"
190
        title="View this file on the web, running your code if this is a CGI file"
776 by mattgiuca
browser: "Serve" button now opens links with target="_blank".
191
        class="disabled" target="_blank">Serve</a> :
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
192
    <a id="act_run" title="Run the selected Python file in the console"
193
        class="disabled">Run</a> :
194
    <a id="act_download" class="choice">Download</a> :
195
    <a title="Refresh the current page" onclick="refresh()"
777 by mattgiuca
browser: Now hides the "More actions" box altogether if the current file is
196
        class="choice">Refresh</a><span id="moreactions_area"> :
597 by mattgiuca
Major refactor of actions in File browser.
197
    <select id="moreactions" onchange="handle_moreactions()"
198
        onblur="handle_moreactions()">
199
      <option class="moreactions" value="top"
200
        selected="selected">More actions...</option>
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
201
""")
202
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
203
    for (name, disablement, group) in moreactions:
204
        if disablement:
205
            disable = 'class="disabled" disabled="disabled"'
206
        else:
207
            disable = ''
208
        req.write('<optgroup label="%s">' % name)
209
        for (id, bits) in group:
210
            req.write('<option id="act_%s" %s title="%s" value="%s">%s</option>'
211
                      % (id, disable, bits[1], id, bits[0]))
212
        req.write('</optgroup>')
213
214
    req.write('</select></span>')
215
835 by wagrant
Cleanup from the last revision. Don't pass isdir if not necessary.
216
def present_actions2(req):
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
217
    """
218
    Presents a set of links/buttons for the "actions2" row of the top bar.
219
    This depends on whether it is a directory (listing) or normal file
834 by wagrant
Let the client decide whether to show file or directory actions2, as
220
    (editor), but we'll let the JavaScript decide which.
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
221
    """
834 by wagrant
Let the client decide whether to show file or directory actions2, as
222
    req.write("""    <form id="actions2_directory"
223
          target="upload_iframe"
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
224
          action="%s"
225
          enctype="multipart/form-data" method="post">
928 by wagrant
browser: Make actions2 a bit more standards-compliant.
226
      <p>
227
      <span id="selectallpanel">
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
228
      Select:
229
      <a onclick="action_selectall(true)"
230
          title="Select all files in this directory">All</a> :
231
      <a onclick="action_selectall(false)"
232
          title="Deselect all files in this directory">None</a>
928 by wagrant
browser: Make actions2 a bit more standards-compliant.
233
      </span>
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
234
928 by wagrant
browser: Make actions2 a bit more standards-compliant.
235
      <span style="display:none" id="uploadpanel">|
236
        <label for="upload_file">Upload file:</label>
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
237
        <input type="hidden" value="putfiles" name="action" />
238
        <input type="hidden" value="" name="path" />
928 by wagrant
browser: Make actions2 a bit more standards-compliant.
239
        <input type="file" name="data" id="upload_file" />
240
        <input type="checkbox" checked="checked" value="true" name="unpack" id="unpack" />
241
        <label title="Extract files and directories from a ZIP file if one is uploaded" for="unpack">Unpack ZIP</label>
242
        <input type="button" onclick="show_uploadpanel(false)" value="Cancel" />
243
        <input type="submit" value="Upload" />
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
244
      </span>
245
      <!-- This iframe is for making a call to upload the file without
246
           refreshing the page. (It will refresh the listing). -->
622 by mattgiuca
browser.js: Reinstated upload_callback (previously removed from listing.js).
247
      <iframe onload="upload_callback()" style="display: none;"
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
248
          name="upload_iframe" id="upload_iframe"></iframe>
928 by wagrant
browser: Make actions2 a bit more standards-compliant.
249
      </p>
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
250
    </form>
251
""" % cgi.escape(util.make_path(os.path.join("fileservice", req.path))))
834 by wagrant
Let the client decide whether to show file or directory actions2, as
252
836 by wagrant
Split the editor's Save and Save As functionality. This makes things
253
    req.write("""    <p id="actions2_file">
254
      <input type="button" id="save_button" value="Save" onclick="save_file('%s')" />
255
      <input type="button" id="saveas_button" value="Save As..." onclick="save_file_as('%s')" />
837 by wagrant
Add a highlighting-language selector to the editor.
256
      <select id="highlighting_select" onchange="highlighting_changed(this)">
257
          <option value="text">Text</option>
258
          <option value="python">Python</option>
259
          <option value="html">HTML</option>
260
          <option value="javascript">JavaScript</option>
261
          <option value="css">CSS</option>
262
      </select>
806 by William Grant
www/apps/browser/__init__.py: Display the save widget in actions2 for
263
    </p>
836 by wagrant
Split the editor's Save and Save As functionality. This makes things
264
""" % ((cgi.escape(req.path),) * 2))