Completed
Push — file-actions ( b2ad98...ca0603 )
by Felipe A.
29s
created

clipboard_paste()   C

Complexity

Conditions 7

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
c 2
b 0
f 0
dl 0
loc 30
rs 5.5
1
#!/usr/bin/env python
2
# -*- coding: UTF-8 -*-
3
4
import os
5
import os.path
6
import logging
7
8
from flask import Blueprint, render_template, request, redirect, url_for, \
9
                  make_response
10
from werkzeug.exceptions import NotFound
11
12
from browsepy import get_cookie_browse_sorting, browse_sortkey_reverse
13
from browsepy.file import Node, abspath_to_urlpath, secure_filename, \
14
                          current_restricted_chars, common_path_separators
15
from browsepy.compat import re_escape, FileNotFoundError
16
from browsepy.exceptions import OutsideDirectoryBase
17
18
from .clipboard import Clipboard
19
from .exceptions import FileActionsException, \
20
                        InvalidClipboardItemsError, \
21
                        InvalidDirnameError, \
22
                        DirectoryCreationError
23
from .paste import paste_clipboard
24
25
26
__basedir__ = os.path.dirname(os.path.abspath(__file__))
27
28
logger = logging.getLogger(__name__)
29
30
actions = Blueprint(
31
    'file_actions',
32
    __name__,
33
    url_prefix='/file-actions',
34
    template_folder=os.path.join(__basedir__, 'templates'),
35
    static_folder=os.path.join(__basedir__, 'static'),
36
    )
37
38
re_basename = '^[^ {0}]([^{0}]*[^ {0}])?$'.format(
39
    re_escape(current_restricted_chars + common_path_separators)
40
    )
41
42
43
@actions.route('/create/directory', methods=('GET', 'POST'),
44
               defaults={'path': ''})
45
@actions.route('/create/directory/<path:path>', methods=('GET', 'POST'))
46
def create_directory(path):
47
    try:
48
        directory = Node.from_urlpath(path)
49
    except OutsideDirectoryBase:
50
        return NotFound()
51
52
    if not directory.is_directory or not directory.can_upload:
53
        return NotFound()
54
55
    if request.method == 'GET':
56
        return render_template(
57
            'create_directory.file_actions.html',
58
            file=directory,
59
            re_basename=re_basename,
60
            )
61
62
    basename = request.form['name']
63
    if secure_filename(basename) != basename or not basename:
64
        raise InvalidDirnameError(
65
            path=directory.path,
66
            name=basename,
67
            )
68
69
    try:
70
        os.mkdir(os.path.join(directory.path, basename))
71
    except OSError as e:
72
        raise DirectoryCreationError.from_exception(
73
            e,
74
            path=directory.path,
75
            name=basename
76
            )
77
78
    return redirect(url_for('browse', path=directory.urlpath))
79
80
81
@actions.route('/selection', methods=('GET', 'POST'), defaults={'path': ''})
82
@actions.route('/selection/<path:path>', methods=('GET', 'POST'))
83
def selection(path):
84
    sort_property = get_cookie_browse_sorting(path, 'text')
85
    sort_fnc, sort_reverse = browse_sortkey_reverse(sort_property)
86
87
    try:
88
        directory = Node.from_urlpath(path)
89
    except OutsideDirectoryBase:
90
        return NotFound()
91
92
    if directory.is_excluded or not directory.is_directory:
93
        return NotFound()
94
95
    if request.method == 'POST':
96
        action_fmt = 'action-{}'.format
97
        mode = None
98
        for action in ('cut', 'copy'):
99
            if request.form.get(action_fmt(action)):
100
                mode = action
101
                break
102
103
        if mode in ('cut', 'copy'):
104
            response = redirect(url_for('browse', path=directory.urlpath))
105
            clipboard = Clipboard(request.form.getlist('path'), mode)
106
            clipboard.to_response(response)
107
            return response
108
109
        return redirect(request.path)
110
111
    clipboard = Clipboard.from_request()
112
    clipboard.mode = 'select'  # disables exclusion
113
    return render_template(
114
        'selection.file_actions.html',
115
        file=directory,
116
        clipboard=clipboard,
117
        cut_support=any(node.can_remove for node in directory.listdir()),
118
        sort_property=sort_property,
119
        sort_fnc=sort_fnc,
120
        sort_reverse=sort_reverse,
121
        )
122
123
124
@actions.route('/clipboard/paste', defaults={'path': ''})
125
@actions.route('/clipboard/paste/<path:path>')
126
def clipboard_paste(path):
127
    try:
128
        directory = Node.from_urlpath(path)
129
    except OutsideDirectoryBase:
130
        return NotFound()
131
132
    if (
133
      not directory.is_directory or
134
      not directory.can_upload or
135
      directory.is_excluded
136
      ):
137
        return NotFound()
138
139
    clipboard = Clipboard.from_request()
140
    success, issues = paste_clipboard(directory, clipboard)
141
    if issues:
142
        raise InvalidClipboardItemsError(
143
            path=directory.path,
144
            clipboard=clipboard,
145
            issues=issues
146
            )
147
148
    if clipboard.mode == 'cut':
149
        clipboard.clear()
150
151
    response = redirect(url_for('browse', path=directory.urlpath))
152
    clipboard.to_response(response)
153
    return response
154
155
156
@actions.route('/clipboard/clear', defaults={'path': ''})
157
@actions.route('/clipboard/clear/<path:path>')
158
def clipboard_clear(path):
159
    response = redirect(url_for('browse', path=path))
160
    clipboard = Clipboard.from_request()
161
    clipboard.clear()
162
    clipboard.to_response(response)
163
    return response
164
165
166
@actions.errorhandler(FileActionsException)
167
def clipboard_error(e):
168
    file = Node(e.path) if hasattr(e, 'path') else None
169
    clipboard = getattr(e, 'clipboard', None)
170
    issues = getattr(e, 'issues', ())
171
172
    response = make_response((
173
        render_template(
174
            '400.file_actions.html',
175
            error=e, file=file, clipboard=clipboard, issues=issues,
176
            ),
177
        400
178
        ))
179
    if clipboard:
180
        for issue in issues:
181
            if isinstance(issue.error, FileNotFoundError):
182
                clipboard.remove(issue.item.urlpath)
183
        clipboard.to_response(response)
184
    return response
185
186
187
def register_plugin(manager):
188
    '''
189
    Register blueprints and actions using given plugin manager.
190
191
    :param manager: plugin manager
192
    :type manager: browsepy.manager.PluginManager
193
    '''
194
    def detect_upload(directory):
195
        return directory.is_directory and directory.can_upload
196
197
    def detect_clipboard(directory):
198
        return directory.is_directory and Clipboard.from_request()
199
200
    def excluded_clipboard(path):
201
        clipboard = Clipboard.from_request(request)
202
        if clipboard.mode == 'cut':
203
            base = manager.app.config['directory_base']
204
            return abspath_to_urlpath(path, base) in clipboard
205
206
    manager.register_exclude_function(excluded_clipboard)
207
    manager.register_blueprint(actions)
208
    manager.register_widget(
209
        place='styles',
210
        type='stylesheet',
211
        endpoint='file_actions.static',
212
        filename='browse.css',
213
        filter=detect_clipboard,
214
        )
215
    manager.register_widget(
216
        place='header',
217
        type='button',
218
        endpoint='file_actions.create_directory',
219
        text='Create directory',
220
        filter=detect_upload,
221
        )
222
    manager.register_widget(
223
        place='header',
224
        type='button',
225
        endpoint='file_actions.selection',
226
        filter=lambda directory: directory.is_directory,
227
        text='Selection...',
228
        )
229
    manager.register_widget(
230
        place='header',
231
        type='html',
232
        html=lambda file: render_template(
233
            'widget.clipboard.file_actions.html',
234
            file=file,
235
            clipboard=Clipboard.from_request()
236
            ),
237
        filter=detect_clipboard,
238
        )
239