aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzsloan2020-06-05 16:52:56 -0500
committerzsloan2020-06-05 16:52:56 -0500
commita302a2b0ac0e7c0f26a0d063c3f2b057f61d47f1 (patch)
treea9f5c88f7d2818bf4e0481b85872c7580a8d577c
parent218576a04f90cc0bc9e53685323e1caa8cffe986 (diff)
downloadgenenetwork2-a302a2b0ac0e7c0f26a0d063c3f2b057f61d47f1.tar.gz
Commiting other current group/resource management code, plus the new files
-rw-r--r--wqflask/base/trait.py2
-rw-r--r--wqflask/maintenance/set_resource_defaults.py155
-rw-r--r--wqflask/utility/authentication_tools.py46
-rw-r--r--wqflask/utility/redis_tools.py37
-rw-r--r--wqflask/wqflask/group_manager.py77
-rw-r--r--wqflask/wqflask/resource_manager.py72
-rw-r--r--wqflask/wqflask/static/new/javascript/group_manager.js38
-rw-r--r--wqflask/wqflask/templates/admin/create_group.html89
-rw-r--r--wqflask/wqflask/templates/admin/group_manager.html68
-rw-r--r--wqflask/wqflask/templates/admin/manage_resource.html92
-rw-r--r--wqflask/wqflask/templates/admin/search_for_groups.html64
-rw-r--r--wqflask/wqflask/templates/admin/select_group_to_add.html54
-rw-r--r--wqflask/wqflask/templates/new_security/not_authenticated.html11
-rw-r--r--wqflask/wqflask/templates/show_trait_details.html5
-rw-r--r--wqflask/wqflask/views.py3
15 files changed, 764 insertions, 49 deletions
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py
index 405c4ebf..2a945588 100644
--- a/wqflask/base/trait.py
+++ b/wqflask/base/trait.py
@@ -391,6 +391,8 @@ def retrieve_trait_info(trait, dataset, get_qtl_info=False):
if response.strip() == "no-access":
trait.view = False
return trait
+ else:
+ trait_info = json.loads(response)
except:
resource_info = get_resource_info(resource_id)
default_permissions = resource_info['default_mask']['data']
diff --git a/wqflask/maintenance/set_resource_defaults.py b/wqflask/maintenance/set_resource_defaults.py
new file mode 100644
index 00000000..ba102d9c
--- /dev/null
+++ b/wqflask/maintenance/set_resource_defaults.py
@@ -0,0 +1,155 @@
+"""
+
+Script that sets default resource access masks for use with the DB proxy
+
+Defaults will be:
+Owner - omni_gn
+Mask - Public/non-confidential: { data: "view",
+ metadata: "view",
+ admin: "not-admin" }
+ Private/confidentia: { data: "no-access",
+ metadata: "no-access",
+ admin: "not-admin" }
+
+To run:
+./bin/genenetwork2 ~/my_settings.py -c ./wqflask/maintenance/gen_select_dataset.py
+
+"""
+
+from __future__ import print_function, division
+
+import sys
+import json
+
+# NEW: Note we prepend the current path - otherwise a guix instance of GN2 may be used instead
+sys.path.insert(0,'./')
+
+# NEW: import app to avoid a circular dependency on utility.tools
+from wqflask import app
+
+from utility.tools import SQL_URI
+from utility.redis_tools import get_redis_conn, get_user_id, add_resource, get_resources
+Redis = get_redis_conn()
+
+import MySQLdb
+
+import urlparse
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+def parse_db_uri():
+ """Converts a database URI to the db name, host name, user name, and password"""
+
+ parsed_uri = urlparse.urlparse(SQL_URI)
+
+ db_conn_info = dict(
+ db = parsed_uri.path[1:],
+ host = parsed_uri.hostname,
+ user = parsed_uri.username,
+ passwd = parsed_uri.password)
+
+ print(db_conn_info)
+ return db_conn_info
+
+def insert_probeset_resources(default_owner_id):
+ current_resources = Redis.hgetall("resources")
+ Cursor.execute(""" SELECT
+ ProbeSetFreeze.Id, ProbeSetFreeze.Name, ProbeSetFreeze.confidentiality, ProbeSetFreeze.public
+ FROM
+ ProbeSetFreeze""")
+
+ resource_results = Cursor.fetchall()
+ for i, resource in enumerate(resource_results):
+ if i % 20 == 0:
+ print(i)
+ resource_ob = {}
+ resource_ob['name'] = resource[1]
+ resource_ob['owner_id'] = default_owner_id
+ resource_ob['data'] = { "dataset" : str(resource[0])}
+ resource_ob['type'] = "dataset-probeset"
+ if resource[2] < 1 and resource[3] > 0:
+ resource_ob['default_mask'] = { "data": ["no-access", "view"] }
+ else:
+ resource_ob['default_mask'] = { "data": ["no-access"] }
+ resource_ob['group_masks'] = {}
+
+ add_resource(resource_ob)
+
+def insert_publish_resources(default_owner_id):
+ current_resources = Redis.hgetall("resources")
+ Cursor.execute(""" SELECT
+ PublishXRef.Id, PublishFreeze.Id, InbredSet.InbredSetCode
+ FROM
+ PublishXRef, PublishFreeze, InbredSet, Publication
+ WHERE
+ PublishFreeze.InbredSetId = PublishXRef.InbredSetId AND
+ InbredSet.Id = PublishXRef.InbredSetId AND
+ Publication.Id = PublishXRef.PublicationId""")
+
+ resource_results = Cursor.fetchall()
+ for resource in resource_results:
+ if resource[2]:
+ resource_ob = {}
+ if resource[2]:
+ resource_ob['name'] = resource[2] + "_" + str(resource[0])
+ else:
+ resource_ob['name'] = str(resource[0])
+ resource_ob['owner_id'] = default_owner_id
+ resource_ob['data'] = { "dataset" : str(resource[1]) ,
+ "trait" : str(resource[0])}
+ resource_ob['type'] = "dataset-publish"
+ resource_ob['default_mask'] = { "data": "view" }
+
+ resource_ob['group_masks'] = {}
+
+ add_resource(resource_ob)
+ else:
+ continue
+
+def insert_geno_resources(default_owner_id):
+ current_resources = Redis.hgetall("resources")
+ Cursor.execute(""" SELECT
+ GenoFreeze.Id, GenoFreeze.ShortName, GenoFreeze.confidentiality
+ FROM
+ GenoFreeze""")
+
+ resource_results = Cursor.fetchall()
+ for i, resource in enumerate(resource_results):
+ if i % 20 == 0:
+ print(i)
+ resource_ob = {}
+ resource_ob['name'] = resource[1]
+ resource_ob['owner_id'] = default_owner_id
+ resource_ob['data'] = { "dataset" : str(resource[0]) }
+ resource_ob['type'] = "dataset-geno"
+ if resource[2] < 1:
+ resource_ob['default_mask'] = { "data": "view" }
+ else:
+ resource_ob['default_mask'] = { "data": "no-access" }
+ resource_ob['group_masks'] = {}
+
+ add_resource(resource_ob)
+
+def insert_resources(default_owner_id):
+ current_resources = get_resources()
+ print("START")
+ insert_publish_resources(default_owner_id)
+ print("AFTER PUBLISH")
+ insert_geno_resources(default_owner_id)
+ print("AFTER GENO")
+ insert_probeset_resources(default_owner_id)
+ print("AFTER PROBESET")
+
+def main():
+ """Generates and outputs (as json file) the data for the main dropdown menus on the home page"""
+
+ Redis.delete("resources")
+
+ owner_id = get_user_id("email_address", "zachary.a.sloan@gmail.com")
+ insert_resources(owner_id)
+
+if __name__ == '__main__':
+ Conn = MySQLdb.Connect(**parse_db_uri())
+ Cursor = Conn.cursor()
+ main() \ No newline at end of file
diff --git a/wqflask/utility/authentication_tools.py b/wqflask/utility/authentication_tools.py
new file mode 100644
index 00000000..537881a5
--- /dev/null
+++ b/wqflask/utility/authentication_tools.py
@@ -0,0 +1,46 @@
+from __future__ import absolute_import, print_function, division
+
+import json
+import requests
+
+from base import data_set
+
+from utility import hmac
+from utility.redis_tools import get_redis_conn, get_resource_info, get_resource_id
+
+from flask import Flask, g, redirect, url_for
+
+import logging
+logger = logging.getLogger(__name__ )
+
+def check_resource_availability(dataset, trait_id=None):
+ resource_id = get_resource_id(dataset, trait_id)
+
+ if resource_id:
+ the_url = "http://localhost:8080/available?resource={}&user={}".format(resource_id, g.user_session.user_id)
+ try:
+ response = json.loads(requests.get(the_url).content)['data']
+ except:
+ resource_info = get_resource_info(resource_id)
+ response = resource_info['default_mask']['data']
+
+ if 'view' in response:
+ return True
+ else:
+ return redirect(url_for("no_access_page"))
+
+ return True
+
+def check_owner(dataset=None, trait_id=None, resource_id=None):
+ if resource_id:
+ resource_info = get_resource_info(resource_id)
+ if g.user_session.user_id == resource_info['owner_id']:
+ return resource_id
+ else:
+ resource_id = get_resource_id(dataset, trait_id)
+ if resource_id:
+ resource_info = get_resource_info(resource_id)
+ if g.user_session.user_id == resource_info['owner_id']:
+ return resource_id
+
+ return False \ No newline at end of file
diff --git a/wqflask/utility/redis_tools.py b/wqflask/utility/redis_tools.py
index 0ad96879..bc30a0af 100644
--- a/wqflask/utility/redis_tools.py
+++ b/wqflask/utility/redis_tools.py
@@ -95,14 +95,17 @@ def get_user_groups(user_id):
user_group_ids = [] #ZS: Group IDs where user is a regular user
groups_list = Redis.hgetall("groups")
for key in groups_list:
- group_ob = json.loads(groups_list[key])
- group_admins = set(group_ob['admins'])
- group_members = set(group_ob['members'])
- if user_id in group_admins:
- admin_group_ids.append(group_ob['id'])
- elif user_id in group_members:
- user_group_ids.append(group_ob['id'])
- else:
+ try:
+ group_ob = json.loads(groups_list[key])
+ group_admins = set(group_ob['admins'])
+ group_members = set(group_ob['members'])
+ if user_id in group_admins:
+ admin_group_ids.append(group_ob['id'])
+ elif user_id in group_members:
+ user_group_ids.append(group_ob['id'])
+ else:
+ continue
+ except:
continue
admin_groups = []
@@ -122,6 +125,24 @@ def get_group_info(group_id):
return group_info
+def get_group_by_unique_column(column_name, column_value):
+ """ Get group by column; not sure if there's a faster way to do this """
+
+ matched_groups = []
+
+ all_group_list = Redis.hgetall("groups")
+ for key in all_group_list:
+ group_info = json.loads(all_group_list[key])
+ if column_name == "admins" or column_name == "members": #ZS: Since these fields are lists, search in the list
+ if column_value in group_info[column_name]:
+ matched_groups.append(group_info)
+ else:
+ if group_info[column_name] == column_value:
+ matched_groups.append(group_info)
+
+ return matched_groups
+
+
def create_group(admin_user_ids, member_user_ids = [], group_name = "Default Group Name"):
group_id = str(uuid.uuid4())
new_group = {
diff --git a/wqflask/wqflask/group_manager.py b/wqflask/wqflask/group_manager.py
new file mode 100644
index 00000000..f41ae56d
--- /dev/null
+++ b/wqflask/wqflask/group_manager.py
@@ -0,0 +1,77 @@
+
+from __future__ import print_function, division, absolute_import
+
+from flask import (Flask, g, render_template, url_for, request, make_response,
+ redirect, flash)
+
+from wqflask import app
+from wqflask.user_login import send_verification_email
+
+from utility.redis_tools import get_user_groups, get_group_info, create_group, delete_group, add_users_to_group, remove_users_from_group, \
+ change_group_name, save_verification_code, check_verification_code, get_user_by_unique_column
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+@app.route("/groups/manage", methods=('GET', 'POST'))
+def manage_groups():
+ params = request.form if request.form else request.args
+ if "add_new_group" in params:
+ return redirect(url_for('add_group'))
+ else:
+ admin_groups, user_groups = get_user_groups(g.user_session.user_id)
+ return render_template("admin/group_manager.html", admin_groups=admin_groups, user_groups=user_groups)
+
+@app.route("/groups/remove", methods=('POST',))
+def remove_groups():
+ group_ids_to_remove = request.form['selected_group_ids']
+ for group_id in group_ids_to_remove.split(":"):
+ delete_group(g.user_session.user_id, group_id)
+
+ return redirect(url_for('manage_groups'))
+
+@app.route("/groups/create", methods=('GET', 'POST'))
+def add_group():
+ params = request.form if request.form else request.args
+ if "group_name" in params:
+ member_user_ids = set()
+ admin_user_ids = set()
+ admin_user_ids.add(g.user_session.user_id) #ZS: Always add the user creating the group as an admin
+ if "admin_emails" in params:
+ admin_emails = params['admin_emails_to_add'].split(",")
+ for email in admin_emails:
+ user_details = get_user_by_unique_column("email_address", email)
+ if user_details:
+ admin_user_ids.add(user_details['user_id'])
+ #send_group_invites(params['group_id'], user_email_list = admin_emails, user_type="admins")
+ if "user_emails" in params:
+ member_emails = params['member_emails_to_add'].split(",")
+ for email in member_emails:
+ user_details = get_user_by_unique_column("email_address", email)
+ if user_details:
+ member_user_ids.add(user_details['user_id'])
+ #send_group_invites(params['group_id'], user_email_list = user_emails, user_type="members")
+
+ create_group(list(admin_user_ids), list(member_user_ids), params['group_name'])
+ return redirect(url_for('manage_groups'))
+ else:
+ return render_template("admin/create_group.html")
+
+#ZS: Will integrate this later, for now just letting users be added directly
+def send_group_invites(group_id, user_email_list = [], user_type="members"):
+ for user_email in user_email_list:
+ user_details = get_user_by_unique_column("email_address", user_email)
+ if user_details:
+ group_info = get_group_info(group_id)
+ #ZS: Probably not necessary since the group should normally always exist if group_id is being passed here,
+ # but it's technically possible to hit it if Redis is cleared out before submitting the new users or something
+ if group_info:
+ #ZS: Don't add user if they're already an admin or if they're being added a regular user and are already a regular user,
+ # but do add them if they're a regular user and are added as an admin
+ if (user_details['user_id'] in group_info['admins']) or \
+ ((user_type == "members") and (user_details['user_id'] in group_info['members'])):
+ continue
+ else:
+ send_verification_email(user_details, template_name = "email/group_verification.txt", key_prefix = "verification_code", subject = "You've been invited to join a GeneNetwork user group")
+
+#@app.route() \ No newline at end of file
diff --git a/wqflask/wqflask/resource_manager.py b/wqflask/wqflask/resource_manager.py
new file mode 100644
index 00000000..7d88b8ed
--- /dev/null
+++ b/wqflask/wqflask/resource_manager.py
@@ -0,0 +1,72 @@
+from __future__ import print_function, division, absolute_import
+
+from flask import (Flask, g, render_template, url_for, request, make_response,
+ redirect, flash)
+
+from wqflask import app
+
+from utility.authentication_tools import check_owner
+from utility.redis_tools import get_resource_info, get_group_info, get_group_by_unique_column, get_user_id
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+@app.route("/resources/manage", methods=('GET', 'POST'))
+def view_resource():
+ params = request.form if request.form else request.args
+ if 'resource_id' in request.args:
+ resource_id = request.args['resource_id']
+ if check_owner(resource_id=resource_id):
+ resource_info = get_resource_info(resource_id)
+ group_masks = resource_info['group_masks']
+ group_masks_with_names = get_group_names(group_masks)
+ default_mask = resource_info['default_mask']['data']
+ return render_template("admin/manage_resource.html", resource_id = resource_id, resource_info=resource_info, default_mask=default_mask, group_masks=group_masks_with_names)
+ else:
+ return redirect(url_for("no_access_page"))
+
+@app.route("/resources/add_group", methods=('POST',))
+def add_group_to_resource():
+ resource_id = request.form['resource_id']
+ if check_owner(resource_id=resource_id):
+ if all(key in request.form for key in ('group_id', 'group_name', 'user_name', 'user_email')):
+ group_list = []
+ if request.form['group_id'] != "":
+ the_group = get_group_info(request.form['group_id'])
+ if the_group:
+ group_list.append(the_group)
+ if request.form['group_name'] != "":
+ matched_groups = get_group_by_unique_column("name", request.form['group_name'])
+ for group in matched_groups:
+ group_list.append(group)
+ if request.form['user_name'] != "":
+ user_id = get_user_id("user_name", request.form['user_name'])
+ if user_id:
+ matched_groups = get_group_by_unique_column("admins", user_id)
+ matched_groups += get_group_by_unique_column("members", user_id)
+ for group in matched_groups:
+ group_list.append(group)
+ if request.form['user_email'] != "":
+ user_id = get_user_id("email_address", request.form['user_email'])
+ if user_id:
+ matched_groups = get_group_by_unique_column("admins", user_id)
+ matched_groups += get_group_by_unique_column("members", user_id)
+ for group in matched_groups:
+ group_list.append(group)
+ return render_template("admin/select_group_to_add.html", group_list=group_list, resource_id = resource_id)
+ elif 'selected_group' in request.form:
+ group_id = request.form['selected_group']
+ return render_template("admin/set_group_privileges.html", resource_id = resource_id, group_id = group_id)
+ else:
+ return render_template("admin/search_for_groups.html", resource_id = resource_id)
+ else:
+ return redirect(url_for("no_access_page"))
+
+def get_group_names(group_masks):
+ group_masks_with_names = {}
+ for group_id, group_mask in group_masks.iteritems():
+ this_mask = group_mask
+ group_name = get_group_info(group_id)['name']
+ this_mask['name'] = group_name
+
+ return group_masks_with_names \ No newline at end of file
diff --git a/wqflask/wqflask/static/new/javascript/group_manager.js b/wqflask/wqflask/static/new/javascript/group_manager.js
new file mode 100644
index 00000000..5e82d104
--- /dev/null
+++ b/wqflask/wqflask/static/new/javascript/group_manager.js
@@ -0,0 +1,38 @@
+$('#add_to_admins').click(function() {
+ add_emails('admin')
+})
+
+$('#add_to_members').click(function() {
+ add_emails('member')
+})
+
+$('#clear_admins').click(function(){
+ clear_emails('admin')
+})
+
+$('#clear_members').click(function(){
+ clear_emails('member')
+})
+
+
+function add_emails(user_type){
+ var email_address = $('input[name=user_email]').val();
+ var email_list_string = $('input[name=' + user_type + '_emails_to_add]').val()
+ console.log(email_list_string)
+ if (email_list_string == ""){
+ var email_set = new Set();
+ } else {
+ var email_set = new Set(email_list_string.split(","))
+ }
+ email_set.add(email_address)
+
+ $('input[name=' + user_type + '_emails_to_add]').val(Array.from(email_set).join(','))
+
+ var emails_display_string = Array.from(email_set).join('\n')
+ $('.added_' + user_type + 's').val(emails_display_string)
+}
+
+function clear_emails(user_type){
+ $('input[name=' + user_type + '_emails_to_add]').val("")
+ $('.added_' + user_type + 's').val("")
+} \ No newline at end of file
diff --git a/wqflask/wqflask/templates/admin/create_group.html b/wqflask/wqflask/templates/admin/create_group.html
new file mode 100644
index 00000000..55c3fa0b
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/create_group.html
@@ -0,0 +1,89 @@
+{% extends "base.html" %}
+{% block title %}Group Manager{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>Create Group</h1>
+ </div>
+ <form action="/groups/create" method="POST">
+ <input type="hidden" name="admin_emails_to_add" value="">
+ <input type="hidden" name="member_emails_to_add" value="">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px; margin-bottom: 50px;">
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Group Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-12">
+ <input name="group_name" type="text" style="width:100%;"></input>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">Add User Email:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-12">
+ <input name="user_email" type="text" style="width:100%;"></input>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <button type="button" id="add_to_admins" class="btn btn-default">Add to Admins</button>
+ </div>
+ <div class="col-xs-6">
+ <button type="button" id="add_to_members" class="btn btn-default">Add to Members</button>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="font-size: 18px;">Members to be added:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <textarea rows="8" cols="60" readonly placeholder="No users added" style="overflow-y: scroll; resize: none; width: 200px;" class="added_admins"></textarea>
+ </div>
+ <div class="col-xs-6">
+ <textarea rows="8" cols="60" readonly placeholder="No users added" style="overflow-y: scroll; resize: none; width: 200px;" class="added_members"></textarea>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <button type="button" id="clear_admins" class="btn btn-default">Clear</button>
+ </div>
+ <div class="col-xs-6">
+ <button type="button" id="clear_members" class="btn btn-default">Clear</button>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="create_group" class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <button type="button" id="create_group" class="btn btn-primary">Create Group</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+
+
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/new/javascript/group_manager.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/group_manager.html b/wqflask/wqflask/templates/admin/group_manager.html
index b7df1aad..23d8205a 100644
--- a/wqflask/wqflask/templates/admin/group_manager.html
+++ b/wqflask/wqflask/templates/admin/group_manager.html
@@ -1,15 +1,23 @@
{% extends "base.html" %}
{% block title %}Group Manager{% endblock %}
+{% block css %}
+ <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
+ <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{% endblock %}
{% block content %}
<!-- Start of body -->
<div class="container">
<div class="page-header">
<h1>Manage Groups</h1>
- <button type="button" id="remove_groups" class="btn btn-primary" data-url="/groups/remove">Remove Selected Groups</button>
+ <div style="display: inline;">
+ <button type="button" id="create_group" class="btn btn-primary" data-url="/groups/create">Create Group</button>
+ <button type="button" id="remove_groups" class="btn btn-primary" data-url="/groups/remove">Remove Selected Groups</button>
+ </div>
</div>
<form id="groups_form" action="/groups/manage" method="POST">
<input type="hidden" name="selected_group_ids" value="">
- <div class="container" style="margin-bottom: 30px;">
+ <div style="min-width: 800px; max-width: 1000px;">
{% if admin_groups|length == 0 and user_groups|length == 0 %}
<h4>You currently aren't a member or admin of any groups.</h4>
<br>
@@ -20,7 +28,7 @@
{% if admin_groups|length == 0 %}
<h4>You currently aren't the administrator of any groups.</h4>
{% else %}
- <table id="admin_groups" class="table table-hover" style="min-width: 800px; max-width: 1000px;">
+ <table id="admin_groups" class="table-hover table-striped cell-border" style="float: left;">
<thead>
<tr>
<th></th>
@@ -29,17 +37,19 @@
<th># Members</th>
<th>Created</th>
<th>Last Changed</th>
+ <th>Group ID</th>
</tr>
</thead>
<tbody>
{% for group in admin_groups %}
<tr>
<td><input type="checkbox" name="group_id" value="{{ group.id }}"></td>
- <td>{{ loop.index }}</td>
+ <td align="right">{{ loop.index }}</td>
<td>{{ group.name }}</td>
- <td>{{ group.admins|length + group.users|length }}</td>
+ <td align="right">{{ group.admins|length + group.users|length }}</td>
<td>{{ group.created_timestamp }}</td>
<td>{{ group.changed_timestamp }}</td>
+ <td>{{ group.id }}</td>
</tr>
{% endfor %}
</tbody>
@@ -47,13 +57,13 @@
{% endif %}
</div>
<hr>
- <div class="container">
+ <div style="min-width: 800px; max-width: 1000px;">
<div><h3>User Groups</h3></div>
<hr>
{% if user_groups|length == 0 %}
<h4>You currently aren't a member of any groups.</h4>
{% else %}
- <table id="user_groups" class="table table-hover" style="min-width: 800px; max-width: 1000px;">
+ <table id="user_groups" class="table-hover table-striped cell-border" style="float: left;">
<thead>
<tr>
<th></th>
@@ -88,48 +98,26 @@
{% endblock %}
{% block js %}
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
- <script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
-
<script type="text/javascript" charset="utf-8">
$(document).ready( function () {
- $('#admin_groups, #user_groups').dataTable( {
- "drawCallback": function( settings ) {
- $('#admin_groups tr').click(function(event) {
- if (event.target.type !== 'checkbox') {
- $(':checkbox', this).trigger('click');
- }
- });
- },
- "columns": [
- { "type": "natural" },
- { "type": "natural" },
- { "type": "natural" },
- { "type": "natural" },
- { "type": "natural" },
- { "type": "natural" }
- ],
- "columnDefs": [ {
- "targets": 0,
- "orderable": false
- } ],
- "order": [[1, "asc" ]],
- "sDom": "Ztr",
- "iDisplayLength": -1,
- "autoWidth": true,
- "bDeferRender": true,
- "bSortClasses": false,
- "paging": false,
- "orderClasses": true
- } );
-
+ {% if admin_groups|length != 0 %}
+ $('#admin_groups').dataTable();
+ {% endif %}
+ {% if user_groups|length != 0 %}
+ $('#user_groups').dataTable();
+ {% endif %}
submit_special = function(url) {
$("#groups_form").attr("action", url);
return $("#groups_form").submit();
};
+ $("#create_group").on("click", function() {
+ url = $(this).data("url")
+ return submit_special(url)
+ });
+
$("#remove_groups").on("click", function() {
url = $(this).data("url")
groups = []
diff --git a/wqflask/wqflask/templates/admin/manage_resource.html b/wqflask/wqflask/templates/admin/manage_resource.html
new file mode 100644
index 00000000..a47f47ad
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/manage_resource.html
@@ -0,0 +1,92 @@
+{% extends "base.html" %}
+{% block title %}Resource Manager{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>Resource Manager</h1>
+ </div>
+ <form id="manage_resource" action="/resources/manage" method="POST">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div class="col-xs-6" style="min-width: 600px; max-width: 800px;">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px; margin-bottom: 50px;">
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Resource Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ {{ resource_info.name }}
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">Open to Public:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <label class="radio-inline">
+ <input type="radio" name="default_mask" value="True" checked="">
+ Yes
+ </label>
+ <label class="radio-inline">
+ <input type="radio" name="default_mask" value="False">
+ No
+ </label>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <button id="save_changes" class="btn btn-primary">Save Changes</button>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ <div class="col-xs-6" style="min-width: 600px; max-width: 800px;">
+ <button id="add_group_to_resource" class="btn btn-primary" style="margin-bottom: 30px;" data-url="/resources/add_group">Add Group</button>
+ <br>
+ {% if group_masks|length > 0 %}
+ <h3>Current Group Permissions</h3>
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Data</th>
+ <th>Metadata</th>
+ <th>Admin</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in group_masks.iteritems() %}
+ <tr>
+ <td>{{ value.name }}</td>
+ <td>{{ value.data }}</td>
+ <td>{{ value.metadata }}</td>
+ <td>{{ value.admin }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <h3>No groups are currently added to this resource.</h3>
+ {% endif %}
+ </div>
+ </form>
+ </div>
+
+
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/new/javascript/group_manager.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ $('#add_group_to_resource').click(function(){
+ url = $(this).data("url");
+ $('#manage_resource').attr("action", url)
+ $('#manage_resource').submit()
+ })
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/search_for_groups.html b/wqflask/wqflask/templates/admin/search_for_groups.html
new file mode 100644
index 00000000..89eb11dd
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/search_for_groups.html
@@ -0,0 +1,64 @@
+{% extends "base.html" %}
+{% block title %}Resource Manager{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>Find Groups</h1>
+ </div>
+ <form id="find_groups" action="/resources/add_group" method="POST">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div style="min-width: 600px; max-width: 800px;">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px;">
+ <div style="margin-bottom: 30px;">
+ <h2>Search by:</h2>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Group ID:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="group_id" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Group Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="group_name" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_name" class="col-xs-3" style="float: left; font-size: 18px;">User Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="user_name" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">User E-mail:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="user_email" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <button type="submit" id="find_groups" class="btn btn-primary">Search</button>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/new/javascript/group_manager.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/select_group_to_add.html b/wqflask/wqflask/templates/admin/select_group_to_add.html
new file mode 100644
index 00000000..df70fb2f
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/select_group_to_add.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+{% block title %}Matched Groups{% endblock %}
+{% block css %}
+ <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
+ <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <h1>The following groups were found:</h1>
+ <br>
+ <form id="add_groups">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div id="groups_list" style="min-width: 600px; max-width: 800px;">
+ {% if group_list|length > 0 %}
+ <button type="submit" class="btn btn-primary" style="margin-bottom: 40px;">Add Selected Group</button>
+ <table id="groups_table" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Name</th>
+ <th>Created</th>
+ <th>Last Changed</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for group in group_list %}
+ <tr>
+ <td align="center" style="padding: 0px;"><input type="radio" name="selected_group" VALUE="{{ group.id }}"></td>
+ <td>{% if 'name' in group %}{{ group.name }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'created_timestamp' in group %}{{ group.created_timestamp }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'changed_timestamp' in group %}{{ group.changed_timestamp }}{% else %}N/A{% endif %}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <h2>No matching groups were found.</h2>
+ {% endif %}
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+ <script>
+ $('#groups_table').dataTable();
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/not_authenticated.html b/wqflask/wqflask/templates/new_security/not_authenticated.html
new file mode 100644
index 00000000..7d0d3060
--- /dev/null
+++ b/wqflask/wqflask/templates/new_security/not_authenticated.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% block title %}Authentication Needed{% endblock %}
+{% block content %}
+ <div class="container">
+ <div class="page-header">
+ <h3>You lack the permissions to view this data.</h3>
+ </div>
+ <p>Please contact the data's owner or GN administrators if you believe you should have access to this data.</p>
+ </div>
+
+{% endblock %} \ No newline at end of file
diff --git a/wqflask/wqflask/templates/show_trait_details.html b/wqflask/wqflask/templates/show_trait_details.html
index 878b6ced..5c315878 100644
--- a/wqflask/wqflask/templates/show_trait_details.html
+++ b/wqflask/wqflask/templates/show_trait_details.html
@@ -248,6 +248,11 @@
<a target="_blank" href="http://gn1.genenetwork.org/webqtl/main.py?cmd=show&db={{ this_trait.dataset.name }}&probeset={{ this_trait.name }}">
<button type="button" id="view_in_gn1" class="btn btn-primary" title="View Trait in GN1">View in GN1</button>
</a>
+ {% if resource_id %}
+ <a target="_blank" href="./resources/manage?resource_id={{ resource_id }}">
+ <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource">Edit</button>
+ </a>
+ {% endif %}
</div>
</div>
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 24a4dcee..ee827ba3 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -30,6 +30,7 @@ import sqlalchemy
from wqflask import app
from flask import g, Response, request, make_response, render_template, send_from_directory, jsonify, redirect, url_for
from wqflask import group_manager
+from wqflask import resource_manager
from wqflask import search_results
from wqflask import export_traits
from wqflask import gsearch
@@ -89,13 +90,13 @@ def connect_db():
@app.before_request
def check_access_permissions():
logger.debug("@app.before_request check_access_permissions")
+ available = True
if "temp_trait" in request.args:
if request.args['temp_trait'] == "True":
pass
else:
if 'dataset' in request.args:
dataset = create_dataset(request.args['dataset'])
- logger.debug("USER:", Redis.hget("users"))
if 'trait_id' in request.args:
available = check_resource_availability(dataset, request.args['trait_id'])
else: