Example #1
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = "Show freeze"
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    freeze_id = accepted['freeze_id'][-1]
    flavor = accepted['flavor'][-1]
    checksum_list = [i for i in accepted['checksum'] if i]
    operation = accepted['operation'][-1]

    if not flavor in freeze_flavors.keys():
        output_objects.append({
            'object_type': 'error_text',
            'text': 'Invalid freeze flavor: %s' % flavor
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    title = freeze_flavors[flavor]['showfreeze_title']
    title_entry['text'] = title
    output_objects.append({'object_type': 'header', 'text': title})

    sorted_algos = supported_hash_algos()
    sorted_algos.sort()
    for checksum in checksum_list:
        if not checksum in sorted_algos:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Invalid checksum algo(s): %s' % checksum
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    if not configuration.site_enable_freeze:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Freezing archives is disabled on this site.
Please contact the site admins %s if you think it should be enabled.
''' % configuration.admin_email
        })
        return (output_objects, returnvalues.OK)

    if not operation in allowed_operations:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Operation must be one of %s.''' % ', '.join(allowed_operations)
        })
        return (output_objects, returnvalues.OK)

    # We don't generally know checksum and edit status until AJAX returns
    hide_elems = {'edit': 'hidden', 'update': 'hidden', 'register': 'hidden'}
    for algo in sorted_algos:
        hide_elems['%ssum' % algo] = 'hidden'

    if operation in show_operations:

        # jquery support for tablesorter and confirmation dialog
        # table initially sorted by col. 0 (filename)

        refresh_call = 'ajax_showfreeze("%s", "%s", %s, "%s", "%s", "%s", "%s")' % \
                       (freeze_id, flavor, checksum_list, keyword_updating,
                        keyword_final, configuration.site_freeze_doi_url,
                        configuration.site_freeze_doi_url_field)
        table_spec = {
            'table_id': 'frozenfilestable',
            'sort_order': '[[0,0]]',
            'refresh_call': refresh_call
        }
        (add_import, add_init,
         add_ready) = man_base_js(configuration, [table_spec])
        if operation == "show":
            add_ready += '%s;' % refresh_call

        # Only show requested checksums
        for algo in sorted_algos:
            if algo in checksum_list:
                add_ready += """
        $('.%ssum').show();
""" % checksum
            else:
                add_ready += """
        $('.%ssum').hide();
        """ % algo

        title_entry['script']['advanced'] += add_import
        title_entry['script']['init'] += add_init
        title_entry['script']['ready'] += add_ready
        output_objects.append({
            'object_type': 'html_form',
            'text': man_base_html(configuration)
        })
        output_objects.append({
            'object_type': 'table_pager',
            'entry_name': 'frozen files',
            'default_entries': default_pager_entries,
            'refresh_button': False
        })

        # Helper form for removes

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        target_op = 'deletefreeze'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper(
            'delfreeze', '%s.py' % target_op, {
                'freeze_id': '__DYNAMIC__',
                'flavor': '__DYNAMIC__',
                'path': '__DYNAMIC__',
                'target': TARGET_PATH,
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})

    # NB: the restrictions on freeze_id prevents illegal directory traversal

    if not is_frozen_archive(client_id, freeze_id, configuration):
        logger.error("%s: invalid freeze '%s': %s" %
                     (op_name, client_id, freeze_id))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "'%s' is not an existing frozen archive!" % freeze_id
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if operation in list_operations:
        (load_status,
         freeze_dict) = get_frozen_archive(client_id, freeze_id, configuration,
                                           checksum_list)
        if not load_status:
            logger.error("%s: load failed for '%s': %s" %
                         (op_name, freeze_id, freeze_dict))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not read details for "%s"' % freeze_id
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

        if freeze_dict.get('FLAVOR', 'freeze') != flavor:
            logger.error("%s: flavor mismatch for '%s': %s vs %s" %
                         (op_name, freeze_id, flavor, freeze_dict))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'No such %s archive "%s"' % (flavor, freeze_id)
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        # Allow edit if not in updating/final state and allow request DOI if
        # finalized and not a backup archive.
        freeze_state = freeze_dict.get('STATE', keyword_final)
        if freeze_state == keyword_updating:
            hide_elems['update'] = ''
        elif freeze_state != keyword_final:
            hide_elems['edit'] = ''
        elif flavor != 'backup' and configuration.site_freeze_doi_url and \
                freeze_dict.get('PUBLISH_URL', ''):
            hide_elems['register'] = ''

        logger.debug("%s: build obj for '%s': %s" %
                     (op_name, freeze_id, brief_freeze(freeze_dict)))
        output_objects.append(
            build_freezeitem_object(configuration, freeze_dict))

    if operation == "show":
        # insert dummy placeholder to build table
        output_objects.append({
            'object_type': 'frozenarchive',
            'id': freeze_id,
            'creator': client_id,
            'flavor': flavor,
            'frozenfiles': [],
            'name': 'loading ...',
            'description': 'loading ...',
            'created': 'loading ...',
            'state': 'loading ...'
        })
    if operation in show_operations:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """<p>
Show archive with file checksums - might take quite a while to calculate:
</p>"""
        })
        for algo in sorted_algos:
            output_objects.append({'object_type': 'html_form', 'text': '<p>'})
            output_objects.append({
                'object_type':
                'link',
                'destination':
                "showfreeze.py?freeze_id=%s;flavor=%s;checksum=%s" %
                (freeze_id, flavor, algo),
                'class':
                'infolink iconspace genericbutton',
                'title':
                'View archive with %s checksums' % algo.upper(),
                'text':
                'Show with %s checksums' % algo.upper()
            })
            output_objects.append({'object_type': 'html_form', 'text': '</p>'})

        # We don't know state of archive in this case until AJAX returns
        # so we hide the section and let AJAX show it if relevant
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
<div class='updatearchive %(update)s'>
<p class='warn_message'>
Archive is currently in the process of being updated. No further changes can be
applied until running archive operations are completed. 
</p>
</div>
<div class='editarchive %(edit)s'>
<p>
You can continue inspecting and changing your archive until you're satisfied,
then finalize it for actual persistent freezing.
</p>
<p>""" % hide_elems
        })
        output_objects.append({
            'object_type':
            'link',
            'destination':
            "adminfreeze.py?freeze_id=%s;flavor=%s" % (freeze_id, flavor),
            'class':
            'editarchivelink iconspace genericbutton',
            'title':
            'Further modify your pending %s archive' % flavor,
            'text':
            'Edit archive'
        })
        output_objects.append({'object_type': 'html_form', 'text': '</p>'})
        form_method = 'post'
        target_op = 'createfreeze'
        csrf_limit = get_csrf_limit(configuration)
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper(
            'createfreeze', '%s.py' % target_op, {
                'freeze_id': freeze_id,
                'flavor': flavor,
                'freeze_state': keyword_final,
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})
        output_objects.append({
            'object_type':
            'link',
            'destination':
            "javascript: confirmDialog(%s, '%s');" %
            ('createfreeze', 'Really finalize %s?' % freeze_id),
            'class':
            'finalizearchivelink iconspace genericbutton',
            'title':
            'Finalize %s archive to prevent further changes' % flavor,
            'text':
            'Finalize archive',
        })
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
</div>
<div class='registerarchive %(register)s'>
<p>
You can register a <a href='http://www.doi.org/index.html'>Digital Object
Identifier (DOI)</a> for finalized archives. This may be useful in case you
want to reference the contents in a publication.
</p>
""" % hide_elems
        })
        form_method = 'post'
        target_op = 'registerfreeze'
        csrf_limit = get_csrf_limit(configuration)
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper(
            'registerfreeze', configuration.site_freeze_doi_url, {
                'freeze_id': freeze_id,
                'freeze_author': client_id,
                configuration.site_freeze_doi_url_field: '__DYNAMIC__',
                'callback_url': "%s.py" % target_op,
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})
        output_objects.append({
            'object_type': 'html_form',
            'text': configuration.site_freeze_doi_text
        })
        output_objects.append({
            'object_type':
            'link',
            'destination':
            "javascript: confirmDialog(%s, '%s');" %
            ('registerfreeze', 'Really request DOI for %s?' % freeze_id),
            'class':
            'registerarchivelink iconspace genericbutton',
            'title':
            'Register a DOI for %s archive %s' % (flavor, freeze_id),
            'text':
            'Request archive DOI',
        })
        output_objects.append({
            'object_type': 'html_form',
            'text': """
</div>"""
        })

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
    <div class='vertical-spacer'></div>"""
    })

    return (output_objects, returnvalues.OK)
Example #2
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flavor = accepted['flavor'][-1].strip()
    freeze_id = accepted['freeze_id'][-1].strip()

    if not flavor in freeze_flavors.keys():
        output_objects.append({
            'object_type': 'error_text',
            'text': 'Invalid freeze flavor: %s' % flavor
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    title = freeze_flavors[flavor]['adminfreeze_title']
    output_objects.append({'object_type': 'header', 'text': title})
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = title

    if not configuration.site_enable_freeze:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Freezing archives is disabled on this site.
Please contact the site admins %s if you think it should be enabled.
''' % configuration.admin_email
        })
        return (output_objects, returnvalues.OK)

    # Load existing freeze for stepwise construction if requested
    freeze_dict = {'ID': freeze_id, 'FLAVOR': flavor}
    if freeze_id != keyword_auto:
        (load_status, freeze_dict) = get_frozen_archive(client_id,
                                                        freeze_id,
                                                        configuration,
                                                        checksum_list=[])
        if not load_status:
            logger.error("%s: load failed for '%s': %s" %
                         (op_name, freeze_id, brief_freeze(freeze_dict)))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not read details for "%s"' % freeze_id
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

        logger.debug("%s: loaded freeze: %s" %
                     (op_name, brief_freeze(freeze_dict)))

        # Preserve already saved flavor
        flavor = freeze_dict.get('FLAVOR', 'freeze')

        freeze_state = freeze_dict.get('STATE', keyword_final)
        if freeze_state == keyword_final:
            logger.error("%s tried to edit finalized %s archive %s" %
                         (client_id, flavor, freeze_id))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You cannot edit finalized %s archive %s' % (flavor, freeze_id)
            })
            output_objects.append({
                'object_type':
                'link',
                'destination':
                'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor),
                'class':
                'viewarchivelink iconspace genericbutton',
                'title':
                'View details about your %s archive' % flavor,
                'text':
                'View details',
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        elif freeze_state == keyword_updating:
            logger.error("%s tried to edit %s archive %s under update" %
                         (client_id, flavor, freeze_id))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You cannot edit %s archive %s until active update completes' %
                (flavor, freeze_id)
            })
            output_objects.append({
                'object_type':
                'link',
                'destination':
                'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor),
                'class':
                'viewarchivelink iconspace genericbutton',
                'title':
                'View details about your %s archive' % flavor,
                'text':
                'View details',
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'flavor': flavor,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    target_op = 'uploadchunked'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
    lookup_map = {
        'freeze_id': 'ID',
        'freeze_name': 'NAME',
        'freeze_author': 'AUTHOR',
        'freeze_description': 'DESCRIPTION'
    }
    for (key, val) in lookup_map.items():
        fill_helpers[key] = freeze_dict.get(val, '')
    fill_helpers['publish_value'] = 'no'
    if freeze_dict.get('PUBLISH', False):
        fill_helpers['publish_value'] = 'yes'

    # jquery fancy upload

    (add_import, add_init, add_ready) = fancy_upload_js(configuration,
                                                        csrf_token=csrf_token)

    # We need filechooser deps and dynamic addition of copy/upload fields

    add_import += '''
<script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script>
<script type="text/javascript" src="/images/js/jquery.prettyprint.js"></script>
<script type="text/javascript" src="/images/js/jquery.contextmenu.js"></script>
<script type="text/javascript" src="/images/js/jquery.xbreadcrumbs.js"></script>
<!-- The preview image plugin -->
<script type="text/javascript" src="/images/js/preview.js"></script>
<!-- The image manipulation CamanJS plugin used by the preview image plugin -->
<script type="text/javascript" src="/images/lib/CamanJS/dist/caman.full.js"></script>
<!-- The nouislider plugin used by the preview image plugin -->
<script type="text/javascript" src="/images/lib/noUiSlider/jquery.nouislider.all.js"></script>
    '''
    add_init += '''
Caman.DEBUG = false

var copy_fields = 0;
var upload_fields = 0;
var open_file_chooser;
var open_upload_dialog;
/* default upload destination */
var remote_path = "%s";
var trash_linkname = "%s";
var copy_div_id = "copyfiles";
var upload_div_id = "uploadfiles";
/* for upload_callback */
var field_id, field_name, wrap_id, upload_path, on_remove;

function add_copy() {
    var div_id = copy_div_id;
    var field_id = "freeze_copy_"+copy_fields;
    var field_name = "freeze_copy_"+copy_fields;
    var wrap_id = field_id+"_wrap";
    var browse_id = field_id+"_browse";
    copy_entry = "<span id=\'"+wrap_id+"\'>";
    copy_entry += "<input type=\'button\' value=\'Remove\' ";
    copy_entry += " onClick=\'remove_field(\"+wrap_id+\");\'/>";
    // add browse button to mimic upload field
    copy_entry += "<input type=\'button\' id=\'"+browse_id+"\' ";
    copy_entry += " value=\'Browse...\' />";
    copy_entry += "<input type=\'text\' id=\'"+field_id+"\' ";
    copy_entry += " name=\'" + field_name + "\' size=80 /><br / >";
    copy_entry += "</span>";

    $("#"+div_id).append(copy_entry);
    $("#"+field_id).click(function() {
        open_file_chooser("Add file or directory (right click to select)",
            function(file) {
                $("#"+field_id).val(file);
            });
    });
    $("#"+browse_id).click(function() {
         $("#"+field_id).click();
    });
    $("#"+field_id).click();
    copy_fields += 1;
}

function upload_callback() {
    var div_id = upload_div_id;
    console.log("in upload callback");
    $(".uploadfileslist > tr > td > p.name > a").each(
        function(index) {
            console.log("callback for upload item no. "+index);
            upload_path = $(this).text();
            if ($(this).attr("href") == "") {
                console.log("skipping empty (error) upload: "+upload_path);
                // Continue to next iteration on errors
                return true;
            }
            console.log("callback for upload path "+upload_path);
            field_id = "freeze_move_"+upload_fields;
            field_name = "freeze_move_"+upload_fields;
            wrap_id = field_id+"_wrap";
            if ($("#"+div_id+" > span > input[value=\\""+upload_path+"\\"]").length) {
                console.log("skipping duplicate path: "+upload_path);
                // Continue to next iteration on errors
                return true;
            } else {
                console.log("adding new path: "+upload_path);
            }
            on_remove = "";
            on_remove += "remove_field("+wrap_id+");";
            on_remove += "$.fn.delete_upload(\\""+upload_path+"\\");";
            upload_entry = "<span id=\'"+wrap_id+"\'>";
            upload_entry += "<input type=\'button\' value=\'Remove\' ";
            upload_entry += " onClick=\'"+on_remove+"\'/>";
            upload_entry += "<input type=\'text\' id=\'"+field_id+"\' ";
            upload_entry += " name=\'" + field_name + "\' size=50 ";
            upload_entry += "value=\\""+upload_path+"\\" /><br / >";
            upload_entry += "</span>";
            $("#"+div_id).append(upload_entry);
            console.log("callback added upload: "+upload_entry);
            upload_fields += 1;
        });
    console.log("callback done");
}

function add_upload() {
    openFancyUpload("Upload Files", upload_callback, "", remote_path, true,
                    "", "%s");
}

function remove_field(field_id) {
    $(field_id).remove();
}

// init file chooser dialogs with directory selection support
function init_dialogs() {
    open_file_chooser = mig_filechooser_init("fm_filechooser",
        function(file) {
            return;
        }, false, "/");
    /* We reuse shared init function but use custom actual opener above
    to mangle resulting files into archive form. */
    initFancyUpload();
    /* Alias open_dialog to open_upload_dialog for clarity */
    open_upload_dialog = open_dialog;

    $("#addfilebutton").click(function() { add_copy(); });
    $("#adduploadbutton").click(function() { add_upload(); });
}

function init_page() {
    init_dialogs();
}

    ''' % (upload_tmp_dir, trash_linkname, csrf_token)
    add_ready += '''
         // do sequenced initialisation (separate function)
         init_page();
    '''

    # TODO: can we update style inline to avoid explicit themed_styles?
    title_entry['style'] = themed_styles(
        configuration,
        base=[
            'jquery.contextmenu.css', 'jquery.managers.contextmenu.css',
            'jquery.xbreadcrumbs.css', 'jquery.fmbreadcrumbs.css',
            'jquery.fileupload.css', 'jquery.fileupload-ui.css'
        ],
        skin=['fileupload-ui.custom.css', 'xbreadcrumbs.custom.css'],
        user_settings=title_entry.get('user_settings', {}))
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready

    if flavor == 'freeze':
        fill_helpers['freeze_name'] = fill_helpers.get('freeze_name', '')
        fill_helpers["archive_header"] = "Freeze Archive Files"
        fill_helpers["button_label"] = "Save and Preview"
        intro_text = """
Please enter your archive details below and select any files to be included in
the archive.
"""
    elif flavor == 'phd':
        fill_helpers['freeze_name'] = fill_helpers.get('freeze_name', '')
        fill_helpers[
            "archive_header"] = "Thesis and Associated Files to Archive"
        fill_helpers["button_label"] = "Save and Preview"
        intro_text = """
Please enter your PhD details below and select any files associated with your
thesis.
"""
    elif flavor == 'backup':
        fill_helpers['freeze_name'] = fill_helpers.get('freeze_name', '')
        if not fill_helpers['freeze_name']:
            now = datetime.datetime.now().isoformat().replace(':', '')
            fill_helpers['freeze_name'] = 'backup-%s' % now
        fill_helpers["archive_header"] = "Files and folders to Archive"
        fill_helpers["button_label"] = "Save and Preview"
        intro_text = """
Please select the files and folders to backup below.
"""
    else:
        output_objects.append({
            'object_type': 'error_text',
            'text': "unknown flavor: %s" % flavor
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # Only warn here if default state is final and persistent
    if freeze_flavors[flavor]['states'][0] == keyword_final and \
            flavor in configuration.site_permanent_freeze:
        intro_text += """
<p class='warn_message'>Note that frozen archives <em>cannot</em> be changed
after final creation and then they can only be manually removed by management,
so please be careful when filling in the details.
</p>
"""

    fill_helpers["fancy_dialog"] = fancy_upload_html(configuration)

    files_form = """
<!-- and now this... we do not want to see it, except in a dialog: -->
<div id='fm_filechooser' style='display:none'>
    <div class='tree-container container-fluid'>
        <div class='tree-row row'>
            <div class='tree-header col-3'></div>
            <div class='fm_path_breadcrumbs col-6'>
                <ul id='fm_xbreadcrumbs' class='xbreadcrumbs'><!-- dynamic --></ul>
            </div>
            <div class='fm_buttonbar col-3 d-none d-lg-block'>
                <ul id='fm_buttons' class='buttonbar'>
                    <!-- dynamically modified by js to show optional buttons -->
                    <li class='datatransfersbutton hidden' title='Manage Data Transfers'>&nbsp;</li>
                    <li class='sharelinksbutton hidden' title='Manage Share Links'>&nbsp;</li>
                    <li class='parentdirbutton' title='Open Parent Directory'>&nbsp;</li>
                    <li class='refreshbutton' title='Refresh'>&nbsp;</li>
                </ul>
            </div>
            <div class='fm_buttonbar-big col-6 d-block d-lg-none hidden'>
                <ul id='fm_buttons' class='buttonbar'>
                    <!-- dynamically modified by js to show optional buttons -->
                    <li class='datatransfersbutton hidden' title='Manage Data Transfers'>&nbsp;</li>
                    <li class='sharelinksbutton hidden' title='Manage Share Links'>&nbsp;</li>
                    <li class='parentdirbutton' title='Open Parent Directory'>&nbsp;</li>
                    <li class='refreshbutton' title='Refresh'>&nbsp;</li>
                </ul>
            </div>
        </div>
    </div>
    <div class='fm_addressbar'>
        <input type='hidden' value='/' name='fm_current_path' />
    </div>
    <div class='fm_folders'>
        <ul class='jqueryFileTree'>
            <li class='directory expanded'>
                <a>...</a>
            </li>
        </ul>
    </div>
    <div class='fm_files'>
        <table id='fm_filelisting' style='font-size:13px; border-spacing=0;'>
            <thead>
                <tr>
                    <th class='fm_name'>Name</th>
                    <th class='fm_size'>Size</th>
                    <th class='fm_type'>Type</th>
                    <th class='fm_date'>Date Modified</th>
                    <th class='fm_toolbox'>...</th>
                </tr>
            </thead>
            <tbody>
                <!-- this is a placeholder for contents: do not remove! -->
            </tbody>
         </table>
    </div>
    <div id='fm_statusbar' class='col-lg-12'>
        <div id='fm_statusprogress' class=' col-lg-3'>
            <div class='progress-label'>Loading...</div>
        </div>
        <div id='fm_statusinfo' class='col-lg-9'>&nbsp;</div>
    </div>
</div>
<div id='cmd_dialog' title='Command output' style='display: none;'></div>

%(fancy_dialog)s
    """ % fill_helpers

    target_op = 'createfreeze'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})

    freeze_form = """
<form enctype='multipart/form-data' method='%(form_method)s' action='%(target_op)s.py'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<input type='hidden' name='flavor' value='%(flavor)s' />
<input type='hidden' name='freeze_id' value='%(freeze_id)s' />
<b>Name:</b><br />
<input class='fillwidth padspace' type='text' name='freeze_name'
    value='%(freeze_name)s' autofocus required pattern='[a-zA-Z0-9_. -]+'
    title='unique name for the freeze archive. I.e. letters and digits separated only by spaces, underscores, periods and hyphens' />
"""
    if flavor != 'backup':
        # TODO: do these make sense to have here or just forced in backend?
        freeze_form += """
<input type='hidden' name='freeze_department' value='' />
<input type='hidden' name='freeze_organization' value='' />
<br><b>Author(s):</b><br />
<input class='fillwidth padspace' type='text' name='freeze_author'
    value='%(freeze_author)s' title='optional archive author(s) in case archive is created on behalf of one or more people' />
<br /><b>Description:</b><br />
<textarea class='fillwidth padspace' rows='20' name='freeze_description'>%(freeze_description)s</textarea>
<br />
"""
    freeze_form += """
<br />
<div id='freezefiles'>
<b>%(archive_header)s:</b><br/>
"""
    freeze_form += """
<input type='button' id='addfilebutton' value='Add file/directory' />
"""
    if flavor != 'backup':
        freeze_form += """
<input type='button' id='adduploadbutton' value='Add upload' />
"""
    freeze_form += """
<div id='copyfiles'>
<!-- Dynamically filled -->
</div>
"""
    if flavor != 'backup':
        freeze_form += """
<div id='uploadfiles'>
<!-- Dynamically filled -->
</div>
"""
    freeze_form += """
</div>
<br />
"""
    if flavor != 'backup':
        freeze_form += """
<div id='freezepublish'>
<b>Make Dataset Publicly Available</b>
"""
        for val in ('yes', 'no'):
            checked = ''
            if fill_helpers['publish_value'] == val:
                checked = 'checked="checked"'
            freeze_form += """
<input type='radio' name='freeze_publish' value='%s' %s />%s
""" % (val, checked, val)
        freeze_form += """
</div>
<br />
"""
    freeze_form += """
<input type='submit' value='%(button_label)s' />
</form>
"""

    # TODO: consider in-lining showfreeze file table for direct modify instead
    #       probably requires more AJAX handling of actions.

    # for rel_path in frozen_files:
    #    freeze_form += """%s<br/>""" % rel_path

    output_objects.append({'object_type': 'html_form', 'text': intro_text})
    output_objects.append({'object_type': 'html_form', 'text': files_form})
    output_objects.append({
        'object_type': 'html_form',
        'text': freeze_form % fill_helpers
    })

    # Link to view if archive already exists
    if freeze_id != keyword_auto:
        frozen_files = [
            i['name'] for i in freeze_dict.get('FILES', [])
            if i['name'] != public_archive_index
        ]
        # Info about contents and spacing before view button otherwise
        if frozen_files:
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """
<h3>Previously Added Files</h3>
<p>There are already %d file(s) saved in the archive and you can view and
manage those through the link below e.g. in case you change your mind
about including any of them.
</p>""" % len(frozen_files)
            })
        else:
            output_objects.append({'object_type': 'text', 'text': ''})

        output_objects.append({
            'object_type':
            'link',
            'destination':
            'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor),
            'class':
            'viewarchivelink iconspace genericbutton',
            'title':
            'View details about your %s archive' % flavor,
            'text':
            'View details',
            'target':
            '_blank',
        })

    # Spacing
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
            <div class="vertical-spacer"></div>
    '''
    })

    return (output_objects, returnvalues.OK)
Example #3
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Delete frozen archive'
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    flavor = accepted['flavor'][-1]

    if not flavor in freeze_flavors.keys():
        output_objects.append({'object_type': 'error_text', 'text':
                           'Invalid freeze flavor: %s' % flavor})
        return (output_objects, returnvalues.CLIENT_ERROR)

    title = freeze_flavors[flavor]['deletefreeze_title']
    output_objects.append({'object_type': 'header', 'text': title})
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = title

    if not configuration.site_enable_freeze:
        output_objects.append({'object_type': 'text', 'text':
                               '''Freezing archives is disabled on this site.
Please contact the Grid admins %s if you think it should be enabled.
''' % configuration.admin_email})
        return (output_objects, returnvalues.OK)

    freeze_id = accepted['freeze_id'][-1]

    # NB: the restrictions on freeze_id prevents illegal directory traversal

    if not is_frozen_archive(freeze_id, configuration):
        logger.error("%s: invalid freeze '%s': %s" % (op_name,
                                                      client_id, freeze_id))
        output_objects.append({'object_type': 'error_text',
                               'text': "No such frozen archive: '%s'"
                               % freeze_id})
        return (output_objects, returnvalues.CLIENT_ERROR)
    
    (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration)
    if not load_status:
        logger.error("%s: load failed for '%s': %s" % \
                     (op_name, freeze_id, freeze_dict))
        output_objects.append(
            {'object_type': 'error_text',
             'text': 'Could not read frozen archive details for %s'
             % freeze_id})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # Prevent easy delete if the frozen archive if configuration forbids it
    if configuration.site_permanent_freeze:
        output_objects.append(
            {'object_type': 'error_text', 'text':
             "Can't delete frozen archive '%s' yourself due to site policy"
             % freeze_id})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Make sure the frozen archive belongs to the user trying to delete it
    if client_id != freeze_dict['CREATOR']:
        logger.error("%s: illegal access attempt for '%s': %s" % \
                         (op_name, freeze_id, client_id))
        output_objects.append({'object_type': 'error_text', 'text': \
        'You are not the owner of frozen archive "%s"' % freeze_id})
        return (output_objects, returnvalues.CLIENT_ERROR)

    if freeze_dict.get('FLAVOR','freeze') != flavor:
        logger.error("%s: flavor mismatch for '%s': %s vs %s" % \
                     (op_name, freeze_id, flavor, freeze_dict))
        output_objects.append({'object_type': 'error_text', 'text'
                               : 'No such %s archive "%s"' % (flavor,
                                                              freeze_id)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Delete the frozen archive
    (status, msg) = delete_frozen_archive(freeze_id, configuration)
    
    # If something goes wrong when trying to delete frozen archive
    # freeze_id, an error is displayed.
    if not status:
        logger.error("%s: failed for '%s': %s" % (op_name,
                                                  freeze_id, msg))
        output_objects.append({'object_type': 'error_text', 'text'
                               : 'Could not remove %s frozen archive: %s'
                               % (freeze_id, msg)})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # If deletion of frozen archive freeze_id is successful, we just
    # return OK
    else:
        logger.info("%s: successful for '%s': %s" % (op_name,
                                                      freeze_id, client_id))
        output_objects.append(
            {'object_type': 'text', 'text'
             : 'Successfully deleted frozen archive: "%s"' % freeze_id})
        output_objects.append({'object_type': 'link', 'destination':
                               'freezedb.py',
                               'class': 'infolink',
                               'title': 'Show frozen archives',
                               'text': 'Show frozen archives'})
        return (output_objects, returnvalues.OK) 
Example #4
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Frozen Archives'
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    operation = accepted['operation'][-1]

    if not configuration.site_enable_freeze:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Freezing archives is disabled on this site.
Please contact the site admins %s if you think it should be enabled.
''' % configuration.admin_email
        })
        return (output_objects, returnvalues.OK)

    if not operation in allowed_operations:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Operation must be one of %s.''' % ', '.join(allowed_operations)
        })
        return (output_objects, returnvalues.OK)

    logger.info("%s %s begin for %s" % (op_name, operation, client_id))
    if operation in show_operations:

        # jquery support for tablesorter and confirmation on delete
        # table initially sorted by col. 5 (State), 3 (Created date), 2 (name)

        if client_id in configuration.site_freeze_admins:
            permanent_flavors = []
        else:
            permanent_flavors = configuration.site_permanent_freeze
        # NOTE: must insert permanent_flavors list as string here
        refresh_call = 'ajax_freezedb(%s, "%s")' % (str(permanent_flavors),
                                                    keyword_final)
        table_spec = {
            'table_id': 'frozenarchivetable',
            'sort_order': '[[5,1],[3,1],[2,0]]',
            'refresh_call': refresh_call
        }
        (add_import, add_init,
         add_ready) = man_base_js(configuration, [table_spec])
        if operation == "show":
            add_ready += '%s;' % refresh_call
        title_entry['script']['advanced'] += add_import
        title_entry['script']['init'] += add_init
        title_entry['script']['ready'] += add_ready
        output_objects.append({
            'object_type': 'html_form',
            'text': man_base_html(configuration)
        })

        output_objects.append({
            'object_type': 'header',
            'text': 'Frozen Archives'
        })

        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Frozen archives are write-once collections of files used e.g.
in relation to conference paper submissions. Please note that local policies
may prevent users from deleting frozen archives without explicit acceptance
from the management.
        '''
        })

        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Existing frozen archives'
        })

        # Helper form for removes

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        target_op = 'deletefreeze'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper(
            'delfreeze', '%s.py' % target_op, {
                'freeze_id': '__DYNAMIC__',
                'flavor': '__DYNAMIC__',
                'target': TARGET_ARCHIVE,
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})

        output_objects.append({
            'object_type': 'table_pager',
            'entry_name': 'frozen archives',
            'default_entries': default_pager_entries
        })

    frozenarchives = []
    if operation in list_operations:
        # NOTE: we do NOT enforce creator match here as edituser can't update
        #       without breaking any published archives
        (list_status, ret) = list_frozen_archives(configuration,
                                                  client_id,
                                                  strict_owner=False)
        if not list_status:
            logger.error("%s: failed for '%s': %s" % (op_name, client_id, ret))
            output_objects.append({'object_type': 'error_text', 'text': ret})
            return (output_objects, returnvalues.SYSTEM_ERROR)

        logger.debug("%s %s: building list of archives" % (op_name, operation))
        for freeze_id in ret:
            # TODO: add file count to meta and switch here
            # (load_status, freeze_dict) = get_frozen_meta(client_id, freeze_id,
            #                                             configuration)
            (load_status, freeze_dict) = get_frozen_archive(client_id,
                                                            freeze_id,
                                                            configuration,
                                                            checksum_list=[])
            if not load_status:
                logger.error("%s: load failed for '%s': %s" %
                             (op_name, freeze_id, freeze_dict))
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'Could not read details for "%s"' % freeze_id
                })
                return (output_objects, returnvalues.SYSTEM_ERROR)
            freeze_item = build_freezeitem_object(configuration,
                                                  freeze_dict,
                                                  summary=True)
            freeze_id = freeze_item['id']
            flavor = freeze_item.get('flavor', 'freeze')

            # Users may view all their archives
            freeze_item['viewfreezelink'] = {
                'object_type':
                'link',
                'destination':
                "showfreeze.py?freeze_id=%s;flavor=%s" % (freeze_id, flavor),
                'class':
                'infolink iconspace',
                'title':
                'View frozen archive %s' % freeze_id,
                'text':
                ''
            }
            # Users may edit pending archives
            if freeze_item['state'] != keyword_final:
                freeze_item['editfreezelink'] = {
                    'object_type': 'link',
                    'destination': "adminfreeze.py?freeze_id=%s" % freeze_id,
                    'class': 'adminlink iconspace',
                    'title': 'Edit archive %s' % freeze_id,
                    'text': ''
                }
            # Users may delete pending or non permanent archives.
            # Freeze admins may delete all their own archives.
            if freeze_item['state'] != keyword_final or \
                    flavor not in configuration.site_permanent_freeze or \
                    client_id in configuration.site_freeze_admins:
                freeze_item['delfreezelink'] = {
                    'object_type':
                    'link',
                    'destination':
                    "javascript: confirmDialog(%s, '%s', %s, %s);" %
                    ('delfreeze', 'Really remove %s?' % freeze_id, 'undefined',
                     "{freeze_id: '%s', flavor: '%s'}" % (freeze_id, flavor)),
                    'class':
                    'removelink iconspace',
                    'title':
                    'Remove %s' % freeze_id,
                    'text':
                    ''
                }

            frozenarchives.append(freeze_item)
        logger.debug("%s %s: inserting list of %d archives" %
                     (op_name, operation, len(frozenarchives)))

    output_objects.append({
        'object_type': 'frozenarchives',
        'frozenarchives': frozenarchives
    })

    if operation in show_operations:
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Additional Frozen Archives'
        })
        output_objects.append({
            'object_type':
            'text',
            'text':
            """
You can create frozen snapshots/archives of particular subsets of your data in
order to make sure a verbatim copy is preserved. The freeze archive method
includes support for persistent publishing, so that you can e.g. reference your
data in publications. Backup archives can be used as a basic backup mechanism,
so that you can manually recover from any erroneous file removals."""
        })

        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """<p>
Choose one of the archive methods below to make a manual archive:
</p>
<p>"""
        })
        output_objects.append({
            'object_type': 'link',
            'destination': 'adminfreeze.py?flavor=freeze',
            'class': 'addlink iconspace',
            'title': 'Make a new freeze archive of e.g. '
            'research data to be published',
            'text': 'Create a new freeze archive'
        })
        output_objects.append({'object_type': 'html_form', 'text': '</p><p>'})
        output_objects.append({
            'object_type':
            'link',
            'destination':
            'adminfreeze.py?flavor=backup',
            'class':
            'addlink iconspace',
            'title':
            'Make a new backup archive of %s data' % configuration.short_title,
            'text':
            'Create a new backup archive'
        })
        output_objects.append({
            'object_type': 'html_form',
            'text': "<br/><br/></p>"
        })

        if configuration.site_enable_duplicati:
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
Alternatively you can use Duplicati for traditional incremental backup/restore
with optional encryption of all your backup contents.'''
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """
<p>For further details please refer to the """
            })
            output_objects.append({
                'object_type': 'link',
                'destination': 'setup.py?topic=duplicati',
                'class': '',
                'title': 'Open Duplicati settings',
                'text': 'Duplicati Settings'
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """ and the %s documentation.<br/><br/></p>""" %
                configuration.short_title
            })

        if configuration.site_enable_seafile:
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
We recommend our Seafile sync solution for any small or medium sized data sets,
for which you want automatic file versioning and easy roll-back support.'''
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """
<p>For further details please refer to the """
            })
            output_objects.append({
                'object_type': 'link',
                'destination': 'setup.py?topic=seafile',
                'class': '',
                'title': 'Open Seafile settings',
                'text': 'Seafile Settings'
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """ and the %s documentation.</p>""" %
                configuration.short_title
            })
    logger.info("%s %s end for %s" % (op_name, operation, client_id))
    return (output_objects, returnvalues.OK)
Example #5
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Frozen Archives'

    # jquery support for tablesorter and confirmation on "leave":

    title_entry['style'] = themed_styles(configuration)
    title_entry['javascript'] = '''
<script type="text/javascript" src="/images/js/jquery.js"></script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js">
</script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script>
<script type="text/javascript" src="/images/js/jquery-ui.js"></script>
<script type="text/javascript" src="/images/js/jquery.confirm.js"></script>

<script type="text/javascript" >

$(document).ready(function() {

          // init confirmation dialog
          $( "#confirm_dialog" ).dialog(
              // see http://jqueryui.com/docs/dialog/ for options
              { autoOpen: false,
                modal: true, closeOnEscape: true,
                width: 500,
                buttons: {
                   "Cancel": function() { $( "#" + name ).dialog("close"); }
                }
              });

          // table initially sorted by col. 0 (ID)
          var sortOrder = [[0,1]];

          // use image path for sorting if there is any inside
          var imgTitle = function(contents) {
              var key = $(contents).find("a").attr("class");
              if (key == null) {
                  key = $(contents).html();
              }
              return key;
          }

          $("#frozenarchivetable").tablesorter({widgets: ["zebra", "saveSort"],
                                        sortList:sortOrder,
                                        textExtraction: imgTitle
                                        })
                               .tablesorterPager({ container: $("#pager"),
                                        size: %s
                                        });
     }
);
</script>
''' % default_pager_entries

    output_objects.append({'object_type': 'header', 'text'
                          : 'Frozen Archives'})
    output_objects.append({'object_type': 'html_form',
                           'text':'''
 <div id="confirm_dialog" title="Confirm" style="background:#fff;">
  <div id="confirm_text"><!-- filled by js --></div>
   <textarea cols="40" rows="4" id="confirm_input"
       style="display:none;"></textarea>
 </div>
'''                       })

    if not configuration.site_enable_freeze:
        output_objects.append({'object_type': 'text', 'text':
                               '''Freezing archives is disabled on this site.
Please contact the Grid admins %s if you think it should be enabled.
''' % configuration.admin_email})
        return (output_objects, returnvalues.OK)

    output_objects.append(
        {'object_type': 'text', 'text' :
         '''Frozen archives are write-once collections of files used e.g. in
 relation to conference paper submissions. Please note that local policies may
 prevent users from deleting frozen archives without explicit acceptance from
 the management.
         '''})

    output_objects.append({'object_type': 'sectionheader', 'text'
                          : 'Existing frozen archives'})

    (status, ret) = list_frozen_archives(configuration, client_id)
    if not status:
        logger.error("%s: failed for '%s': %s" % (op_name,
                                                  client_id, ret))
        output_objects.append({'object_type': 'error_text', 'text'
                              : ret})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    frozenarchives = []
    for freeze_id in ret:
        (load_status, freeze_dict) = get_frozen_archive(freeze_id,
                                                        configuration)
        if not load_status:
            logger.error("%s: load failed for '%s': %s" % \
                         (op_name, freeze_id, freeze_dict))
            output_objects.append({'object_type': 'error_text', 'text'
                                   : 'Could not read details for "%s"' % \
                                   freeze_id})
            return (output_objects, returnvalues.SYSTEM_ERROR)
        freeze_item = build_freezeitem_object(configuration, freeze_dict)
        freeze_id = freeze_item['id']
        flavor = freeze_item.get('flavor', 'freeze')
        
        freeze_item['viewfreezelink'] = {
            'object_type': 'link',
            'destination': "showfreeze.py?freeze_id=%s;flavor=%s" % \
            (freeze_id, flavor),
            'class': 'infolink', 
            'title': 'View frozen archive %s' % freeze_id, 
            'text': ''}
        if client_id == freeze_item['creator']:
            js_name = 'delete%s' % hexlify(freeze_id)
            helper = html_post_helper(js_name, 'deletefreeze.py',
                                      {'freeze_id': freeze_id,
                                       'flavor': flavor})
            output_objects.append({'object_type': 'html_form', 'text': helper})
            freeze_item['delfreezelink'] = {
                'object_type': 'link', 'destination':
                "javascript: confirmDialog(%s, '%s');" % \
                (js_name, 'Really remove %s?' % freeze_id),
                'class': 'removelink', 'title': 'Remove %s' % \
                freeze_id, 'text': ''}
        frozenarchives.append(freeze_item)

    output_objects.append({'object_type': 'table_pager', 'entry_name':
                           'frozen archives',
                           'default_entries': default_pager_entries})
    output_objects.append({'object_type': 'frozenarchives',
                          'frozenarchives': frozenarchives})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           'Additional Frozen Archives'})
    output_objects.append({'object_type': 'link',
                           'destination': 'adminfreeze.py',
                           'class': 'addlink',
                           'title': 'Specify a new frozen archive', 
                           'text': 'Create a new frozen archive'})

    return (output_objects, returnvalues.OK)
Example #6
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flavor = accepted["flavor"][-1]

    if not flavor in freeze_flavors.keys():
        output_objects.append({"object_type": "error_text", "text": "Invalid freeze flavor: %s" % flavor})
        return (output_objects, returnvalues.CLIENT_ERROR)

    title = freeze_flavors[flavor]["showfreeze_title"]
    output_objects.append({"object_type": "header", "text": title})
    title_entry = find_entry(output_objects, "title")
    title_entry["text"] = title

    if not configuration.site_enable_freeze:
        output_objects.append(
            {
                "object_type": "text",
                "text": """Freezing archives is disabled on this site.
Please contact the Grid admins %s if you think it should be enabled.
"""
                % configuration.admin_email,
            }
        )
        return (output_objects, returnvalues.OK)

    # jquery support for tablesorter

    css_helpers = {
        "css_base": os.path.join(configuration.site_images, "css"),
        "skin_base": configuration.site_skin_base,
    }
    title_entry["style"] = themed_styles(configuration)
    title_entry["javascript"] = (
        """
<script type="text/javascript" src="/images/js/jquery.js"></script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script>
<script type="text/javascript" src="/images/js/jquery-ui.js"></script>

<script type="text/javascript" >

$(document).ready(function() {

          // table initially sorted by col. 0 (filename)
          var sortOrder = [[0,1]];

          // use image path for sorting if there is any inside
          var imgTitle = function(contents) {
              var key = $(contents).find("a").attr("class");
              if (key == null) {
                  key = $(contents).html();
              }
              return key;
          }


          $("#frozenfilestable").tablesorter({widgets: ["zebra", "saveSort"],
                                        sortList:sortOrder,
                                        textExtraction: imgTitle
                                        })
                               .tablesorterPager({ container: $("#pager"),
                                        size: %s
                                        });
     }
);
</script>
"""
        % default_pager_entries
    )

    freeze_id = accepted["freeze_id"][-1]

    # NB: the restrictions on freeze_id prevents illegal directory traversal

    if not is_frozen_archive(freeze_id, configuration):
        logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id))
        output_objects.append(
            {"object_type": "error_text", "text": "'%s' is not an existing frozen archive!" % freeze_id}
        )
        return (output_objects, returnvalues.CLIENT_ERROR)

    (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration)
    if not load_status:
        logger.error("%s: load failed for '%s': %s" % (op_name, freeze_id, freeze_dict))
        output_objects.append({"object_type": "error_text", "text": 'Could not read details for "%s"' % freeze_id})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    if freeze_dict.get("FLAVOR", "freeze") != flavor:
        logger.error("%s: flavor mismatch for '%s': %s vs %s" % (op_name, freeze_id, flavor, freeze_dict))
        output_objects.append({"object_type": "error_text", "text": 'No such %s archive "%s"' % (flavor, freeze_id)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    output_objects.append(
        {"object_type": "table_pager", "entry_name": "frozen files", "default_entries": default_pager_entries}
    )
    output_objects.append(build_freezeitem_object(configuration, freeze_dict))

    return (output_objects, returnvalues.OK)
Example #7
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Delete frozen archive'
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flavor = accepted['flavor'][-1]

    if not safe_handler(configuration, 'post', op_name, client_id,
                        get_csrf_limit(configuration), accepted):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Only accepting
CSRF-filtered POST requests to prevent unintended updates'''
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if not flavor in freeze_flavors.keys():
        output_objects.append({
            'object_type': 'error_text',
            'text': 'Invalid freeze flavor: %s' % flavor
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    title = freeze_flavors[flavor]['deletefreeze_title']
    output_objects.append({'object_type': 'header', 'text': title})
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = title

    if not configuration.site_enable_freeze:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Freezing archives is disabled on this site.
Please contact the site admins %s if you think it should be enabled.
''' % configuration.admin_email
        })
        return (output_objects, returnvalues.OK)

    freeze_id = accepted['freeze_id'][-1]
    target = accepted['target'][-1]
    path_list = accepted['path']

    if not target in valid_targets:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Invalid delete freeze target: %s' % target
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # NB: the restrictions on freeze_id prevents illegal directory traversal

    if not is_frozen_archive(client_id, freeze_id, configuration):
        logger.error("%s: invalid freeze '%s': %s" %
                     (op_name, client_id, freeze_id))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "No such frozen archive: '%s'" % freeze_id
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    (load_status, freeze_dict) = get_frozen_archive(client_id,
                                                    freeze_id,
                                                    configuration,
                                                    checksum_list=[])
    if not load_status:
        logger.error("%s: load failed for '%s': %s" %
                     (op_name, freeze_id, freeze_dict))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Could not read frozen archive details for %s' % freeze_id
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # Make sure the frozen archive belongs to the user trying to delete it
    if client_id != freeze_dict['CREATOR']:
        logger.error("%s: illegal access attempt for '%s': %s" %
                     (op_name, freeze_id, client_id))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'You are not the owner of frozen archive "%s"' % freeze_id
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if freeze_dict.get('FLAVOR', 'freeze') != flavor:
        logger.error("%s: flavor mismatch for '%s': %s vs %s" %
                     (op_name, freeze_id, flavor, freeze_dict))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'No such %s archive "%s"' % (flavor, freeze_id)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Prevent user-delete of the frozen archive if configuration forbids it.
    # We exclude any archives in the pending intermediate freeze state.
    # Freeze admins are also excluded from the restrictions.
    state = freeze_dict.get('STATE', keyword_final)
    if state == keyword_updating:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "Can't change %s archive %s which is currently being updated" %
            (flavor, freeze_id)
        })
        output_objects.append({
            'object_type':
            'link',
            'destination':
            'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor),
            'class':
            'viewarchivelink iconspace genericbutton',
            'title':
            'View details about your %s archive' % flavor,
            'text':
            'View details',
        })
        return (output_objects, returnvalues.CLIENT_ERROR)
    elif state == keyword_final and \
            flavor in configuration.site_permanent_freeze and \
            not client_id in configuration.site_freeze_admins:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "Can't change %s archives like '%s' yourself due to site policy" %
            (flavor, freeze_id)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    client_dir = client_id_dir(client_id)
    user_archives = os.path.join(configuration.freeze_home, client_dir)

    # Please note that base_dir must end in slash to avoid access to other
    # user archive dirs if own name is a prefix of another archive name

    base_dir = os.path.abspath(os.path.join(user_archives, freeze_id)) + os.sep

    if target == TARGET_ARCHIVE:
        # Delete the entire freeze archive
        (del_status, msg) = delete_frozen_archive(freeze_dict, client_id,
                                                  configuration)

        # If something goes wrong when trying to delete freeze archive
        # freeze_id, an error is displayed.
        if not del_status:
            logger.error("%s: failed for '%s': %s" % (op_name, freeze_id, msg))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not remove entire %s archive %s: %s' %
                (flavor, freeze_id, msg)
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

        # If deletion of frozen archive freeze_id is successful, we just
        # return OK
        else:
            logger.info("%s: successful for '%s': %s" %
                        (op_name, freeze_id, client_id))
            output_objects.append({
                'object_type':
                'text',
                'text':
                'Successfully deleted %s archive: "%s"' % (flavor, freeze_id)
            })
    elif target == TARGET_PATH:
        # Delete individual files in non-final archive
        del_paths = []
        for path in path_list:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            server_path = os.path.join(base_dir, path)
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, False):

                # Out of bounds!

                logger.warning(
                    '%s tried to %s del restricted path %s ! ( %s)' %
                    (client_id, op_name, abs_path, path))
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'Not allowed to delete %s - outside archive %s !' %
                    (path, freeze_id)
                })
                continue
            del_paths.append(path)

        (del_status, msg_list) = delete_archive_files(freeze_dict, client_id,
                                                      del_paths, configuration)

        # If something goes wrong when trying to delete files from archive
        # freeze_id, an error is displayed.
        if not del_status:
            logger.error("%s: delete archive file(s) failed for '%s':\n%s" %
                         (op_name, freeze_id, '\n'.join(msg_list)))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not remove file(s) from archive %s: %s' %
                (freeze_id, '\n '.join(msg_list))
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

        # If deletion of files from archive freeze_id is successful, we just
        # return OK
        else:
            logger.info("%s: delete %d files successful for '%s': %s" %
                        (op_name, len(path_list), freeze_id, client_id))
            output_objects.append({
                'object_type':
                'text',
                'text':
                'Successfully deleted %d file(s) from archive: "%s"' %
                (len(path_list), freeze_id)
            })

    # Success - show link to overview
    output_objects.append({
        'object_type': 'link',
        'destination': 'freezedb.py',
        'class': 'infolink iconspace',
        'title': 'Show archives',
        'text': 'Show archives'
    })
    return (output_objects, returnvalues.OK)