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

1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
1
# IVLE - Informatics Virtual Learning Environment
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
# Module: util
19
# Author: Matt Giuca
20
# Date: 12/12/2007
21
22
# Contains common utility functions.
23
24
import os
1165.3.75 by Matt Giuca
ivle/util.py: Added safe_rmtree, copied from Python2.6 shutil.rmtree (which
25
import sys
26
import stat
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
27
28
class IVLEJailError(Exception):
1213 by William Grant
Drop ivle.util.make_path (replaced by Request.make_path) and fix docstrings.
29
    """Exception proxying an in-jail error.
30
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
31
    This exception indicates an error that occurred inside an IVLE CGI script
32
    inside the jail. It should never be raised directly - only by the 
33
    interpreter.
34
35
    Information will be retrieved from it, and then treated as a normal
36
    error.
37
    """
38
    def __init__(self, type_str, message, info):
39
        self.type_str = type_str
40
        self.message = message
41
        self.info = info
42
43
class FakeObject(object):
44
    """ A representation of an object that can't be Pickled """
45
    def __init__(self, type, name, attrib={}):
46
        self.type = type
47
        self.name = name
48
        self.attrib = attrib
49
50
    def __repr__(self):
51
        return "<Fake %s %s>"%(self.type, self.name)
52
53
def split_path(path):
54
    """Given a path, returns a tuple consisting of the top-level directory in
55
    the path, and the rest of the path. Note that both items in the tuple will
56
    NOT begin with a slash, regardless of whether the original path did. Also
57
    normalises the path.
58
59
    Always returns a pair of strings, except for one special case, in which
60
    the path is completely empty (or just a single slash). In this case the
61
    return value will be (None, ''). But still always returns a pair.
62
63
    Examples:
64
65
    >>> split_path("")
66
    (None, '')
67
    >>> split_path("/")
68
    (None, '')
69
    >>> split_path("home")
70
    ('home', '')
71
    >>> split_path("home/docs/files")
72
    ('home', 'docs/files')
73
    >>> split_path("//home/docs/files")
74
    ('', 'home/docs/files')
75
    """
76
    path = os.path.normpath(path)
77
    # Ignore the opening slash
78
    if path.startswith(os.sep):
79
        path = path[len(os.sep):]
80
    if path == '' or path == '.':
81
        return (None, '')
82
    splitpath = path.split(os.sep, 1)
83
    if len(splitpath) == 1:
84
        return (splitpath[0], '')
85
    else:
86
        return tuple(splitpath)
87
1165.3.81 by Matt Giuca
ivle.util: Backported os.path.relpath from Python2.6 (required).
88
def relpath(path, start=os.path.curdir):
89
    """Return a relative version of a path.
90
    XXX Backported from Python 2.6 posixpath.py.
91
    """
92
93
    if not path:
94
        raise ValueError("no path specified")
95
96
    start_list = os.path.abspath(start).split(os.path.sep)
97
    path_list = os.path.abspath(path).split(os.path.sep)
98
99
    # Work out how much of the filepath is shared by start and path.
100
    i = len(os.path.commonprefix([start_list, path_list]))
101
102
    rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
103
    if not rel_list:
104
        return os.path.curdir
105
    return os.path.join(*rel_list)
106
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
107
def incomplete_utf8_sequence(byteseq):
1213 by William Grant
Drop ivle.util.make_path (replaced by Request.make_path) and fix docstrings.
108
    """Calculate the completeness of a UTF-8 encoded string.
109
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
110
    Given a UTF-8-encoded byte sequence (str), returns the number of bytes at
111
    the end of the string which comprise an incomplete UTF-8 character
112
    sequence.
113
114
    If the string is empty or ends with a complete character OR INVALID
115
    sequence, returns 0.
116
    Otherwise, returns 1-3 indicating the number of bytes in the final
117
    incomplete (but valid) character sequence.
118
119
    Does not check any bytes before the final sequence for correctness.
120
121
    >>> incomplete_utf8_sequence("")
122
    0
123
    >>> incomplete_utf8_sequence("xy")
124
    0
125
    >>> incomplete_utf8_sequence("xy\xc3\xbc")
126
    0
127
    >>> incomplete_utf8_sequence("\xc3")
128
    1
129
    >>> incomplete_utf8_sequence("\xbc\xc3")
130
    1
131
    >>> incomplete_utf8_sequence("xy\xbc\xc3")
132
    1
133
    >>> incomplete_utf8_sequence("xy\xe0\xa0")
134
    2
135
    >>> incomplete_utf8_sequence("xy\xf4")
136
    1
137
    >>> incomplete_utf8_sequence("xy\xf4\x8f")
138
    2
139
    >>> incomplete_utf8_sequence("xy\xf4\x8f\xa0")
140
    3
141
    """
142
    count = 0
143
    expect = None
144
    for b in byteseq[::-1]:
145
        b = ord(b)
146
        count += 1
147
        if b & 0x80 == 0x0:
148
            # 0xxxxxxx (single-byte character)
149
            expect = 1
150
            break
151
        elif b & 0xc0 == 0x80:
152
            # 10xxxxxx (subsequent byte)
153
            pass
154
        elif b & 0xe0 == 0xc0:
155
            # 110xxxxx (start of 2-byte sequence)
156
            expect = 2
157
            break
158
        elif b & 0xf0 == 0xe0:
159
            # 1110xxxx (start of 3-byte sequence)
160
            expect = 3
161
            break
162
        elif b & 0xf8 == 0xf0:
163
            # 11110xxx (start of 4-byte sequence)
164
            expect = 4
165
            break
166
        else:
167
            # Invalid byte
168
            return 0
169
170
        if count >= 4:
171
            # Seen too many "subsequent bytes", invalid
172
            return 0
173
174
    if expect is None:
175
        # We never saw a "first byte", invalid
176
        return 0
177
178
    # We now know expect and count
179
    if count >= expect:
180
        # Complete, or we saw an invalid sequence
181
        return 0
182
    elif count < expect:
183
        # Incomplete
184
        return count
1080.1.7 by matt.giuca
The new ivle.database.User class is now used in Request and usrmgt, which
185
1165.3.75 by Matt Giuca
ivle/util.py: Added safe_rmtree, copied from Python2.6 shutil.rmtree (which
186
def safe_rmtree(path, ignore_errors=False, onerror=None):
187
    """Recursively delete a directory tree.
188
189
    Copied from shutil.rmtree from Python 2.6, which does not follow symbolic
190
    links (it is otherwise unsafe to call as root on untrusted directories; do
191
    not use shutil.rmtree in this case, as you may be running Python 2.5).
192
193
    If ignore_errors is set, errors are ignored; otherwise, if onerror
194
    is set, it is called to handle the error with arguments (func,
195
    path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
196
    path is the argument to that function that caused it to fail; and
197
    exc_info is a tuple returned by sys.exc_info().  If ignore_errors
198
    is false and onerror is None, an exception is raised.
199
200
    """
201
    if ignore_errors:
202
        def onerror(*args):
203
            pass
204
    elif onerror is None:
205
        def onerror(*args):
206
            raise
207
    try:
208
        if os.path.islink(path):
209
            # symlinks to directories are forbidden, see bug #1669
210
            raise OSError("Cannot call safe_rmtree on a symbolic link")
211
    except OSError:
212
        onerror(os.path.islink, path, sys.exc_info())
213
        # can't continue even if onerror hook returns
214
        return
215
    names = []
216
    try:
217
        names = os.listdir(path)
218
    except os.error, err:
219
        onerror(os.listdir, path, sys.exc_info())
220
    for name in names:
221
        fullname = os.path.join(path, name)
222
        try:
223
            mode = os.lstat(fullname).st_mode
224
        except os.error:
225
            mode = 0
226
        if stat.S_ISDIR(mode):
227
            safe_rmtree(fullname, ignore_errors, onerror)
228
        else:
229
            try:
230
                os.remove(fullname)
231
            except os.error, err:
232
                onerror(os.remove, fullname, sys.exc_info())
233
    try:
234
        os.rmdir(path)
235
    except os.error:
236
        onerror(os.rmdir, path, sys.exc_info())
1515 by Matt Giuca
Submit view: The projects list is now identical (except for radio buttons) to the view on the subjects page. It is much clearer and contains more info. The code is somewhat different, because it's a table, not a list, so I didn't abstract it. Moved a function out of subject.py to ivle.util, as it is shared by both views.
237
238
def format_submission_principal(user, principal):
239
    """Render a list of users to fit in the offering project listing.
240
241
    Given a user and a list of submitters, returns 'solo' if the
242
    only submitter is the user, or a string of the form
243
    'with A, B and C' if there are any other submitters.
244
245
    If submitters is None, we assume that the list of members could
246
    not be determined, so we just return 'group'.
247
    """
248
    if principal is None:
249
        return 'group'
250
251
    if principal is user:
252
        return 'solo'
253
254
    display_names = sorted(
255
        member.display_name for member in principal.members
256
        if member is not user)
257
258
    if len(display_names) == 0:
259
        return 'solo (%s)' % principal.name
260
    elif len(display_names) == 1:
261
        return 'with %s (%s)' % (display_names[0], principal.name)
262
    elif len(display_names) > 5:
263
        return 'with %d others (%s)' % (len(display_names), principal.name)
264
    else:
265
        return 'with %s and %s (%s)' % (', '.join(display_names[:-1]),
266
                                        display_names[-1], principal.name)