~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
609 by mattgiuca
browser: Now stats the file requested before doing anything else.
31
from common import (util, studpath)
907 by wagrant
browser: Display the current revision next to the current path if the
32
import common.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",
170 by mattgiuca
browser: Added CSS and JS files (not much in them).
61
    ]
849 by dcoles
Browser: Ported Browser to the new scripts_init framework. Started to decouple
62
    req.scripts_init = [
63
        "browser_init",
64
    ]
713 by dcoles
browser: Added console and run button to allow Python files to be executed in
65
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
66
    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.
67
    # The page title should contain the name of the file being browsed
68
    req.title = browsepath.rsplit('/', 1)[-1]
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
69
609 by mattgiuca
browser: Now stats the file requested before doing anything else.
70
    _, localpath = studpath.url_to_local(browsepath)
71
    if localpath is None:
72
        req.throw_error(req.HTTP_NOT_FOUND,
73
            "The path specified is invalid.")
74
136 by mattgiuca
Added File Browser (browser) application. (Currently just stub).
75
    # Start writing data
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
76
    req.write("""
77
<!-- Top bar section -->
78
79
<div id="topbar">
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
80
  <div id="path">
81
    """)
835 by wagrant
Cleanup from the last revision. Don't pass isdir if not necessary.
82
    # FIXME: This isn't completely reliable! We're not inside the jail, so we
83
    # can't know the type for sure. This is now only used for adding a / to the
84
    # 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.
85
    isdir = os.path.isdir(localpath)
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
86
    presentpath(req, browsepath, isdir)
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
87
    req.write("""
88
  </div>
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
89
  <div id="actions1">
90
""")
91
    present_actions1(req)
92
    req.write("""  </div>
93
  <div id="actions2">
94
""")
835 by wagrant
Cleanup from the last revision. Don't pass isdir if not necessary.
95
    present_actions2(req)
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
96
    req.write("""  </div>
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
97
</div>
98
<!-- End topbar -->
99
203 by mattgiuca
browser: Removed all directory-listing specific HTML from the Python-generated
100
<!-- Body. The JavaScript places content here relevant to the path -->
101
<div id="filesbody">
102
</div>
103
<!-- End body -->
104
""")
185 by mattgiuca
Integrated the (second) Prototype browser (HTML+CSS but no code) into the main
105
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
106
def presentpath(req, path, isdir):
107
    """
108
    Presents a path list (address bar inside the page) for clicking.
109
    Writes to req, expecting to have just written the opening div containing
110
    the listing.
907 by wagrant
browser: Display the current revision next to the current path if the
111
    This will also have a revision indicator on the end, if we are viewing a
112
    revision.
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
113
    """
114
    href_path = util.make_path(THIS_APP)
115
    nav_path = ""
116
907 by wagrant
browser: Display the current revision next to the current path if the
117
    revision = common.svn.revision_from_string(
118
                     req.get_fieldstorage().getfirst('r'))
119
120
    try: 
121
        revno = revision.number
122
    except:
123
        revno = None
124
       
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
125
    # Create all of the paths
126
    pathlist = path.split("/")
127
    segs_left = len(pathlist)
128
    for path_seg in pathlist:
129
        if path_seg == "":
130
            continue
131
        # Write a slash at the end unless this is the last path seg AND
132
        # it's not a directory.
133
        segs_left -= 1
134
        add_slash = segs_left != 0 or isdir
135
        # Make an 'a' element
136
        href_path = href_path + '/' + path_seg
137
        nav_path = nav_path + path_seg
138
        if add_slash:
139
            nav_path = nav_path + '/'
140
        link = '<a href="%s" title="Navigate to %s">%s</a>' % (
908 by wagrant
browser: Link breadcrumbs to the current revision.
141
            href_path if revno is None else href_path + '?r=%d' % revno,
142
            nav_path, path_seg)
595 by mattgiuca
browser: Removed the browser.js ability to generate path links at the top.
143
        req.write(link)
144
        if add_slash:
145
            req.write('/')
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
146
907 by wagrant
browser: Display the current revision next to the current path if the
147
    if revno is not None:
148
        req.write(' (revision %d)' % revno)
149
596 by mattgiuca
browser: In the Python code, writes out all required HTML contents for the top
150
def present_actions1(req):
151
    """
152
    Presents a set of links/buttons for the "actions1" row of the top bar.
153
    This is always exactly the same - the JavaScript will customize it later.
154
    """
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
155
    # Set up our actions. The second field of each group is whether to disable
156
    # the items by default.
157
    moreactions = [
158
      ('Publishing', True, [
159
        ('publish', ['Publish',          'Make it so this directory can be seen by anyone on the web']),
160
        ('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.
161
        ('submit',  ['Submit', 'Submit the selected files for an assignment'])
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
162
      ]),
163
      ('File actions', True, [
164
        ('rename',  ['Rename',           'Change the name of this file']),
165
        ('delete',  ['Delete',           'Delete the selected files']),
166
        ('copy',    ['Copy',             'Prepare to copy the selected files to another directory']),
167
        ('cut',     ['Cut',              'Prepare to move the selected files to another directory'])
168
      ]),
169
      ('Directory actions', False, [
170
        ('paste',   ['Paste',            'Paste the copied or cut files into the current directory']),
171
        ('newfile', ['New File',         'Open a new file for editing in the current directory']),
172
        ('mkdir',   ['New Directory',    'Make a new subdirectory in the current directory']),
173
        ('upload',  ['Upload File',      'Upload a file to the current directory'])
174
      ]),
175
      ('Subversion', True, [
176
        ('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
177
        ('svnremove',   ['Remove',       'Schedule the selected permanent files to be removed']),
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
178
        ('svndiff',     ['Diff',         'View any changes to the selected file since its last committed state']),
179
        ('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
180
        ('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
181
        ('svncommit',   ['Commit',       'Commit any changes to the permanent repository']),
919 by wagrant
fileservice_lib: Implement "svn resolved".
182
        ('svnresolved', ['Mark Resolved','Mark a conflicted file as being resolved']),
861 by wagrant
browser: Refactor the server side of actions1 generation. We now take
183
        ('svnlog',      ['View Log',     'View the log of commits of the selected file']),
184
        ('svncheckout', ['Re-checkout',  'Re-checkout your default directories'])
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">
226
      Select:
227
      <a onclick="action_selectall(true)"
228
          title="Select all files in this directory">All</a> :
229
      <a onclick="action_selectall(false)"
230
          title="Deselect all files in this directory">None</a>
231
232
      <span style="display:none" id="uploadpanel">| Upload file:
233
        <input type="hidden" value="putfiles" name="action" />
234
        <input type="hidden" value="" name="path" />
235
        <input type="file" name="data" />
236
        <input type="checkbox" checked="on" value="true" name="unpack"
237
          />Unpack zip file
238
        <input type="button" onclick="show_uploadpanel(false)" value="Hide" />
239
        <input type="submit" value="Send" />
240
      </span>
241
      <!-- This iframe is for making a call to upload the file without
242
           refreshing the page. (It will refresh the listing). -->
622 by mattgiuca
browser.js: Reinstated upload_callback (previously removed from listing.js).
243
      <iframe onload="upload_callback()" style="display: none;"
617 by mattgiuca
Added upload panel to the topbar instead of being on the side.
244
          name="upload_iframe" id="upload_iframe"></iframe>
245
    </form>
246
""" % 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
247
836 by wagrant
Split the editor's Save and Save As functionality. This makes things
248
    req.write("""    <p id="actions2_file">
249
      <input type="button" id="save_button" value="Save" onclick="save_file('%s')" />
250
      <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.
251
      <select id="highlighting_select" onchange="highlighting_changed(this)">
252
          <option value="text">Text</option>
253
          <option value="python">Python</option>
254
          <option value="html">HTML</option>
255
          <option value="javascript">JavaScript</option>
256
          <option value="css">CSS</option>
257
      </select>
806 by William Grant
www/apps/browser/__init__.py: Display the save widget in actions2 for
258
    </p>
836 by wagrant
Split the editor's Save and Save As functionality. This makes things
259
""" % ((cgi.escape(req.path),) * 2))