From: Loic Baron Date: Mon, 9 Mar 2015 18:53:49 +0000 (+0100) Subject: Merge branch 'onelab' of ssh://git.onelab.eu/git/myslice into onelab X-Git-Tag: myslice-1.3~58^2^2~3 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=111d0fcb9681bf94ff184fe43bf636b6d672cbff;hp=f8bf9fe459d94a73dff2c1f4fdcda88ac5031a95;p=unfold.git Merge branch 'onelab' of ssh://git.onelab.eu/git/myslice into onelab Conflicts: portal/joinview.py portal/registrationview.py --- diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index f73c991a..dad90097 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -2,7 +2,10 @@ eclipse.preferences.version=1 encoding//portal/django_passresetview.py=utf-8 encoding//portal/forms.py=utf-8 encoding//portal/migrations/0002_extend_slice.py=utf-8 +encoding//portal/migrations/0005_extend_user.py=utf-8 +encoding//portal/migrations/0008_extend_user.py=utf-8 +encoding//portal/migrations/0009_initial.py=utf-8 +encoding//portal/migrations/0010_project.py=utf-8 encoding//portal/models.py=utf-8 encoding//portal/urls.py=utf-8 encoding//portal/validationview.py=utf-8 -encoding//portal/views.py=utf-8 diff --git a/Makefile b/Makefile index 69725db5..80d49c4c 100644 --- a/Makefile +++ b/Makefile @@ -110,8 +110,9 @@ ftags: force #################### sync : push current code on a box running myslice # this for now targets deployments based on the debian packaging -SSHURL:=root@$(MYSLICEBOX):/ -SSHCOMMAND:=ssh root@$(MYSLICEBOX) +SSHUSER ?= root +SSHURL := $(SSHUSER)@$(MYSLICEBOX): +SSHCOMMAND := ssh $(SSHUSER)@$(MYSLICEBOX) ### rsync options # the config file should probably not be overridden ?? @@ -163,3 +164,14 @@ ifeq (,$(MYSLICEBOX)) else +$(SSHCOMMAND) apachectl restart endif + +#SSHUSER=tparment +#MYSLICEBOX=srv-diana.inria.fr +sync-devel: +ifeq (,$(MYSLICEBOX)) + @echo "you need to set MYSLICEBOX, like in e.g." + @echo " $(MAKE) MYSLICEBOX=srv-diana.inria.fr "$@"" + @exit 1 +else + +$(RSYNC) --relative $$(git ls-files) $(SSHURL)myslice/ +endif diff --git a/README b/README index 8c6e7eff..4e4e1955 100644 --- a/README +++ b/README @@ -1,48 +1,66 @@ This file documents the contents of this module change -Last update 18 DEC. 2014 +Last update 20 FEB. 2015 -See the devel/ subdir for more devel-oriented doc. +Installation +================================================================= +Recommended OS +=============== +Debian GNU/Linux 7.5 (wheezy) x64 -==================== 1 minute howto +PYTHON DEPENDENCIES +===================== +sudo apt-get install python-pip or sudo easy_install pip==1.4.1 +sudo apt-get install python-dev (for paramiko and pyOpenSSL) +sudo apt-get install libffi-dev (for pyOpenSSL) -* REQUIREMENTS is to have python + django (1.5.2) installed django -** should be straightforward -** see devel/django-install.txt in case of trouble -$ apt-get install python-pip or sudo easy_install pip==1.4.1 -$ pip install django=="1.5.2 +$ pip install django=="1.5.2" $ apt-get install python-django-south $ pip install requests $ pip install djangorestframework $ pip install django-celery $ pip install geopy $ pip install paramiko +$ pip install pyparsing +$ pip install python-dateutil +$ pip instal pyOpenSSL -* git clone git://git.onelab.eu/myslice.git --- or -- -* git clone ssh://yourlogin@git.onelab.eu/git/myslice.git +MYSLICE +======= +git clone ssh://yourlogin@git.onelab.eu/git/myslice.git +cd myslice +git checkout onelab -* edit/create myslice/myslice.ini and enter the details of your manifold backend +edit/create myslice/myslice.ini and enter the details of your manifold backend + +mkdir /var/unfold +copy unfold.sqlite3 to /var/unfold -$ apt-get install python-django-south -* init django -** when django prompts for creating an admin account, create it and -** keep the username and password safe $ ./manage.py syncdb $ ./manage.py migrate -* gather static files -$ ./manage.py collectstatic --- or -- -$ ./manage.py collectstatic --noinput --- or -- -$ make static (which is a shorthand for cleaning up and run manage collectstatic --noinput) +use the unfold.sqlite3 i gave to u + +$ make redo +$ ./devel/server-loop.sh + +MANIFOLD +========== +git clone git://git.onelab.eu/manifold.git +cd manifold +git checkout devel +make && make install + +SFA +=== +$ git clone -b geni-v3 git://git.onelab.eu/sfa.git +$ cd sfa +$ git checkout geni-v3 -* gather templates files - for now we still seem to rely on a make-based templates-collection process - that creates templates/ -$ make templates [$ make redo (each time when you pull, do that and restart the server)] +$ make version +$ python ./setup.py install +===================================================================== ## Whenever doing a git pull the following operations are recommended: diff --git a/auth/static/js/logout.js b/auth/static/js/logout.js index 593eb588..3419b9d2 100644 --- a/auth/static/js/logout.js +++ b/auth/static/js/logout.js @@ -5,7 +5,7 @@ function logout () { var msg="Are you sure you want to logout as " + username + " ?"; /* redirect to /logout, see urls.py */ if (confirm(msg)){ - localStorage.removeItem('user'); + localStorage.clear(); window.location="/logout/"; } } diff --git a/debian/deb-cheat-sheet b/debian/deb-cheat-sheet deleted file mode 100644 index 3d3f1bc1..00000000 --- a/debian/deb-cheat-sheet +++ /dev/null @@ -1,27 +0,0 @@ ---- search package name -apt-cache search emacs ---- install one package -apt-get install emacs23-nox ---- preparing upgrade (fetch and index latest versions of the repos) -apt-get update ---- actually update one package -apt-get upgrade myslice ---- list packages -dpkg -l -dpkg -l '*foo*' ---- list (files for) one package -dpkg -L myslice -dpkg-deb -c myslice_0.2.4.lxc.2013.11.26_amd64.deb ---- detailed info (e.g. to see complete version that is truncated in dpkg -l) -dpkg -s myslice -dpkg --info -c myslice_0.2.4.lxc.2013.11.26_amd64.deb - ---- install from a local .deb -dpkg -i foo.deb -- or, if this has deps that need to be pulled through apt-get: -gdebi foo.deb -(install with apt-get install -y gdebi-core) - ---- find which package a file belongs to -apt-file search /path/to/file -(can be installed with : sudo apt-get install apt-file) diff --git a/devel/server-loop.sh b/devel/server-loop.sh index c77258fc..78e1da15 100755 --- a/devel/server-loop.sh +++ b/devel/server-loop.sh @@ -2,9 +2,8 @@ DIRNAME=$(dirname $0) cd $DIRNAME/.. -# default port : if hostname starts with z -> use 8080 ; otherwise take 80 -#hostname | grep -q '^z' && port=8080 || port=8080 -hostname | grep -q '^z' && port=8080 || port=80 +# default port : if hostname starts with z or with srv- -> use 8000 ; otherwise take 80 +hostname | egrep -q '^(z|srv-)' && port=8000 || port=80 [[ -n "$@" ]] && port=$1 while true; do diff --git a/manifoldapi/manifoldapi.py b/manifoldapi/manifoldapi.py index a8b2ab9a..8ba7e155 100644 --- a/manifoldapi/manifoldapi.py +++ b/manifoldapi/manifoldapi.py @@ -41,6 +41,7 @@ class ManifoldAPI: self.calls = {} self.multicall = False self.url = ConfigEngine().manifold_url() + # for more debug on this link, set verbose=True self.server = xmlrpclib.Server(self.url, verbose=False, allow_none=True) def __repr__ (self): return "ManifoldAPI[%s]"%self.url diff --git a/manifoldapi/static/js/manifold.js b/manifoldapi/static/js/manifold.js index c880831f..517ab693 100644 --- a/manifoldapi/static/js/manifold.js +++ b/manifoldapi/static/js/manifold.js @@ -737,6 +737,12 @@ var manifold = { } else { console.log('Unknown field'); } + // FIX if the record contains a string instead of a key:value + // example: select resource, slice_hrn from slice where slice_hrn=='xxx' + // Result will be {slice_hrn:'xxx', resource:'urn+zzz'} + // We don't have this resource:{urn:'urn+zzz'} + } else if(typeof(record) === 'string'){ + return record; } else { return record[fields]; } @@ -979,7 +985,8 @@ var manifold = { Loop per platform, allows a progressive loading per AM platform Update is run on all platforms at the same time to get a final answer, we don't manage partial answers yet... */ - if((query.object == 'resource' || query.object == 'lease' || query.object == 'slice') && query.action != "update"){ + // Removed slice from the per platform query - it's quick enough... + if((query.object == 'resource' || query.object == 'lease') && query.action != "update"){ var obj = query.object; $.post("/rest/platform/", function( data ) { $.each(data, function(index, p) { diff --git a/myslice/configengine.py b/myslice/configengine.py index 3d92bb16..4b40cb24 100644 --- a/myslice/configengine.py +++ b/myslice/configengine.py @@ -36,7 +36,7 @@ class ConfigEngine(object): default_myslice_theme = 'onelab' #iotlab dev url - default_iotlab_url = "https://devgrenoble.senslab.info/rest/admin/users" + default_iotlab_url = "https://devwww.iot-lab.info/rest/admin/users" default_iotlab_admin_user = "xxx" default_iotlab_admin_password= "yyy" diff --git a/myslice/myslice.ini.onelab b/myslice/myslice.ini.onelab new file mode 100644 index 00000000..7cfe4949 --- /dev/null +++ b/myslice/myslice.ini.onelab @@ -0,0 +1,2 @@ +[manifold] +url = https://portal.onelab.eu:7080/ diff --git a/myslice/settings.py b/myslice/settings.py index 5c75970b..f9f439e1 100644 --- a/myslice/settings.py +++ b/myslice/settings.py @@ -1,5 +1,7 @@ # Django settings for unfold project. +from __future__ import print_function + import os.path import djcelery @@ -13,7 +15,9 @@ except: building=True DEBUG = True -TEMPLATE_DEBUG = DEBUG + +# show the various settings as we go +DEBUG_SETTINGS = False # compute ROOT from where this file is installed # should fit every need including developers @@ -48,6 +52,10 @@ except: HTTPROOT="/var/www/myslice/" # the place to store local data, like e.g. the sqlite db DATAROOT="/var/unfold" +if not os.path.isdir(DATAROOT): + print("WARNING: {} is a non-existing directory".format(DATAROOT)) + print("consequently we assume development mode and re-route DATAROOT to {}".format(ROOT)) + DATAROOT=ROOT # if not there, then we assume it's from a devel tree if not os.path.isdir (os.path.join(HTTPROOT,"static")): HTTPROOT=ROOT @@ -55,6 +63,11 @@ if not os.path.isdir (os.path.join(HTTPROOT,"static")): if not os.path.isdir(ROOT): raise Exception,"Cannot find ROOT %s for unfold"%ROOT if not os.path.isdir(HTTPROOT): raise Exception,"Cannot find HTTPROOT %s for unfold"%HTTPROOT +if DEBUG_SETTINGS: + print('ROOT', ROOT) + print('DATAROOT', DATAROOT) + print('HTTPROOT', HTTPROOT) + # dec 2013 - we currently have 2 auxiliary subdirs with various utilities # that we do not wish to package # * sandbox is for plugin developers @@ -101,6 +114,9 @@ DATABASES = { } } +if DEBUG_SETTINGS: + print('DATABASE NAME',DATABASES['default']['NAME']) + # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. @@ -252,7 +268,7 @@ BROKER_URL = "amqp://myslice:myslice@localhost:5672/myslice" for aux in auxiliaries: if os.path.isdir(os.path.join(ROOT,aux)): - print "Using devel auxiliary",aux + print("Using devel auxiliary",aux) INSTALLED_APPS.append(aux) ACCOUNT_ACTIVATION_DAYS = 7 # One-week activation window; you may, of course, use a different value. diff --git a/myslice/urls.py b/myslice/urls.py index c815148e..ca150a40 100644 --- a/myslice/urls.py +++ b/myslice/urls.py @@ -92,6 +92,7 @@ urls = [ (r'^create/(?P[^/]+)/(?P[^/]+)?/?$', 'rest.create.dispatch'), (r'^delete/(?P[^/]+)/(?P[^/]+)?/?$', 'rest.delete.dispatch'), (r'^credentials/(?P[^/]+)/?$', 'rest.credentials.dispatch'), + (r'^cache/(?P[^/]+)/?$', 'rest.cache.dispatch'), (r'^initscript/(?P[^/]+)/?$', 'rest.initscript.dispatch'), # # REST monitoring @@ -131,7 +132,7 @@ urls = [ url(r'^portal/', include('portal.urls')), # SLA - url(r'^sla/', include('sla.urls')), +# url(r'^sla/', include('sla.urls')), ] #this one would not match the convention diff --git a/plugins/apply/templates/apply.html b/plugins/apply/templates/apply.html index f05af49d..9e251b7e 100644 --- a/plugins/apply/templates/apply.html +++ b/plugins/apply/templates/apply.html @@ -1,16 +1,18 @@ -
- +
+ +
+ - - - - - -
+ \ No newline at end of file diff --git a/plugins/filter_status/templates/filter_status.html b/plugins/filter_status/templates/filter_status.html index 8350eb11..3d1c44b4 100644 --- a/plugins/filter_status/templates/filter_status.html +++ b/plugins/filter_status/templates/filter_status.html @@ -1,5 +1,5 @@ -
- View: + diff --git a/plugins/scheduler2/static/js/scheduler2.js b/plugins/scheduler2/static/js/scheduler2.js index 6b55b92a..a72341e3 100755 --- a/plugins/scheduler2/static/js/scheduler2.js +++ b/plugins/scheduler2/static/js/scheduler2.js @@ -1,925 +1,929 @@ -/* -# -# Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# -# This is a MySlice plugin for the NITOS Scheduler -# Nitos Scheduler v1 -# -*/ - -// XXX groupid = all slots those that go with a min granularity - -/* some params */ -var scheduler2; -var scheduler2Instance; -//is ctrl keyboard button pressed -var schedulerCtrlPressed = false; -//table Id -var schedulerTblId = "scheduler-reservation-table"; -var SCHEDULER_FIRST_COLWIDTH = 200; - - -/* Number of scheduler slots per hour. Used to define granularity. Should be inferred from resources XXX */ -var schedulerSlotsPerHour = 6; -var RESOURCE_DEFAULT_GRANULARITY = 1800 /* s */; // should be computed automatically from resource information -var DEFAULT_GRANULARITY = 1800 /* s */; // should be computed automatically from resource information. Test with 600 -var DEFAULT_PAGE_RANGE = 5; - -var schedulerMaxRows = 12; - -/* All resources */ -var SchedulerData = []; - -/* ??? */ -var SchedulerSlots = []; - -var SchedulerDateSelected = new Date(); -// Round to midnight -SchedulerDateSelected.setHours(0,0,0,0); - -/* Filtered resources */ -var SchedulerDataViewData = []; - -var SchedulerSlotsViewData = []; -//Help Variables -var _schedulerCurrentCellPosition = 0; -//Enable Debug -var schedulerDebug = true; -//tmp to delete -var tmpSchedulerLeases = []; - -var SCHEDULER_COLWIDTH = 50; - - -/****************************************************************************** - * ANGULAR CONTROLLER * - ******************************************************************************/ - -// Create a private execution space for our controller. When -// executing this function expression, we're going to pass in -// the Angular reference and our application module. -(function (ng, app) { - - // Define our Controller constructor. - function Controller($scope) { - - // Store the scope so we can reference it in our - // class methods - this.scope = $scope; - - // Set up the default scope value. - this.scope.errorMessage = null; - this.scope.name = ""; - - //Pagin - $scope.current_page = 1; - this.scope.items_per_page = 10; - $scope.from = 0; // JORDAN - - $scope.instance = null; - $scope.resources = new Array(); - $scope.slots = SchedulerSlotsViewData; - $scope.granularity = DEFAULT_GRANULARITY; /* Will be setup */ - //$scope.msg = "hello"; - - angular.element(document).ready(function() { - //console.log('Hello World'); - //alert('Hello World'); - //afterAngularRendered(); - }); - - // Pagination - - $scope.range = function() { - var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count(); - var ret = []; - var start; - - start = $scope.current_page; - if ( start > $scope.page_count()-range_size ) { - start = $scope.page_count()-range_size+1; - } - - for (var i=start; i 1) { - $scope.current_page--; - } - }; - - $scope.prevPageDisabled = function() { - return $scope.current_page === 1 ? "disabled" : ""; - }; - - $scope.page_count = function() - { - // XXX need visible resources only - var query_ext, visible_resources_length; - if (!$scope.instance) - return 0; - query_ext = manifold.query_store.find_analyzed_query_ext($scope.instance.options.query_uuid); - var visible_resources_length = 0; - query_ext.state.each(function(i, state) { - if (state[STATE_VISIBLE]) - visible_resources_length++; - }); - return Math.ceil(visible_resources_length/$scope.items_per_page); - }; - - $scope.nextPage = function() { - if ($scope.current_page < $scope.page_count()) { - $scope.current_page++; - } - }; - - $scope.nextPageDisabled = function() { - return $scope.current_page === $scope.page_count() ? "disabled" : ""; - }; - - $scope.setPage = function(n) { - $scope.current_page = n; - }; - // END pagination - - // FILTER - - $scope.filter_visible = function(resource) - { - return manifold.query_store.get_record_state($scope.instance.options.query_uuid, resource['urn'], STATE_VISIBLE); - }; - - // SELECTION - - $scope._create_new_lease = function(resource_urn, start_time, end_time) - { - var lease_key, new_lease, data; - - lease_key = manifold.metadata.get_key('lease'); - - new_lease = { - resource: resource_urn, - start_time: start_time, - end_time: end_time, - }; - - // This is needed to create a hashable object - new_lease.hashCode = manifold.record_hashcode(lease_key.sort()); - new_lease.equals = manifold.record_equals(lease_key); - - data = { - state: STATE_SET, - key : null, - op : STATE_SET_ADD, - value: new_lease - } - manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); - /* Add to local cache also, unless we listen to events from outside */ - if (!(resource_urn in $scope._leases_by_resource)) - $scope._leases_by_resource[resource_urn] = []; - $scope._leases_by_resource[resource_urn].push(new_lease); - } - - $scope._remove_lease = function(other) - { - var lease_key, other_key, data; - - lease_key = manifold.metadata.get_key('lease'); - - // XXX This could be a manifold.record_get_value - other_key = { - resource: other.resource, - start_time: other.start_time, - end_time: other.end_time - } - other_key.hashCode = manifold.record_hashcode(lease_key.sort()); - other_key.equals = manifold.record_equals(lease_key); - - data = { - state: STATE_SET, - key : null, - op : STATE_SET_REMOVE, - value: other_key - } - manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); - /* Remove from local cache also, unless we listen to events from outside */ - $scope._leases_by_resource[other.resource] = $.grep($scope._leases_by_resource[other.resource], function(x) { return x != other; }); - - } - - $scope.select = function(index, model_lease, model_resource) - { - var data, resource_granularity; - - //resource_granularity = model_resource.granularity === undefined ? RESOURCE_DEFAULT_GRANULARITY : model_resource.granularity; - - console.log("Selected", index, model_lease, model_resource); - - var day_timestamp = SchedulerDateSelected.getTime() / 1000; - var start_time = day_timestamp + index * model_resource.granularity; // XXX resource_granularity - var end_time = day_timestamp + (index + 1) * model_resource.granularity; // - var start_date = new Date(start_time * 1000); - var end_date = new Date(end_time * 1000); - - var lease_key = manifold.metadata.get_key('lease'); - - // We search for leases in the cache we previously constructed - var resource_leases = $scope._leases_by_resource[model_resource.urn]; - - switch (model_lease.status) - { - case 'free': // out - case 'pendingout': - if (resource_leases) { - /* Search for leases before */ - $.each(resource_leases, function(i, other) { - if (other.end_time != start_time) - return true; // ~ continue - - /* The lease 'other' is just before, and there should not exist - * any other lease before it */ - start_time = other.start_time; - - other_key = { - resource: other.resource, - start_time: other.start_time, - end_time: other.end_time - } - // This is needed to create a hashable object - other_key.hashCode = manifold.record_hashcode(lease_key.sort()); - other_key.equals = manifold.record_equals(lease_key); - - data = { - state: STATE_SET, - key : null, - op : STATE_SET_REMOVE, - value: other_key - } - manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); - /* Remove from local cache also, unless we listen to events from outside */ - $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; }); - return false; // ~ break - }); - - /* Search for leases after */ - $.each(resource_leases, function(i, other) { - if (other.start_time != end_time) - return true; // ~ continue - - /* The lease 'other' is just after, and there should not exist - * any other lease after it */ - end_time = other.end_time; - other_key = { - resource: other.resource, - start_time: other.start_time, - end_time: other.end_time - } - // This is needed to create a hashable object - other_key.hashCode = manifold.record_hashcode(lease_key.sort()); - other_key.equals = manifold.record_equals(lease_key); - - data = { - state: STATE_SET, - key : null, - op : STATE_SET_REMOVE, - value: other_key - } - manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); - /* Remove from local cache also, unless we listen to events from outside */ - $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; }); - return false; // ~ break - }); - } - - $scope._create_new_lease(model_resource.urn, start_time, end_time); - model_lease.status = (model_lease.status == 'free') ? 'pendingin' : 'selected'; - // unless the exact same lease already existed (pending_out status for the lease, not the cell !!) - - break; - - case 'selected': - case 'pendingin': - // We remove the cell - - /* We search for leases including this cell. Either 0, 1 or 2. - * 0 : NOT POSSIBLE, should be checked. - * 1 : either IN or OUT, we have make no change in the session - * 2 : both will be pending, since we have made a change in the session - * /!\ need to properly remove pending_in leases when removed again - */ - if (resource_leases) { - $.each(resource_leases, function(i, other) { - if ((other.start_time <= start_time) && (other.end_time >= end_time)) { - // The cell is part of this lease. - - // If the cell is not at the beginning of the lease, we recreate a lease with cells before - if (start_time > other.start_time) { - $scope._create_new_lease(model_resource.urn, other.start_time, start_time); - } - - // If the cell is not at the end of the lease, we recreate a lease with cells after - if (end_time < other.end_time) { - $scope._create_new_lease(model_resource.urn, end_time, other.end_time); - } - - // The other lease will be removed - $scope._remove_lease(other); - } - // NOTE: We can interrupt the search if we know that there is a single lease (depending on the status). - }); - } - - // cf comment in previous switch case - model_lease.status = (model_lease.status == 'selected') ? 'pendingout' : 'free'; - - break; - - case 'reserved': - case 'maintainance': - // Do nothing - break; - } - - - $scope._dump_leases(); - }; - - $scope._dump_leases = function() - { - // DEBUG: display all leases and their status in the log - var leases = manifold.query_store.get_records($scope.instance.options.query_lease_uuid); - console.log("--------------------"); - $.each(leases, function(i, lease) { - var key = manifold.metadata.get_key('lease'); - var lease_key = manifold.record_get_value(lease, key); - var state = manifold.query_store.get_record_state($scope.instance.options.query_lease_uuid, lease_key, STATE_SET); - var state_str; - switch(state) { - case STATE_SET_IN: - state_str = 'STATE_SET_IN'; - break; - case STATE_SET_OUT: - state_str = 'STATE_SET_OUT'; - break; - case STATE_SET_IN_PENDING: - state_str = 'STATE_SET_IN_PENDING'; - break; - case STATE_SET_OUT_PENDING: - state_str = 'STATE_SET_OUT_PENDING'; - break; - case STATE_SET_IN_SUCCESS: - state_str = 'STATE_SET_IN_SUCCESS'; - break; - case STATE_SET_OUT_SUCCESS: - state_str = 'STATE_SET_OUT_SUCCESS'; - break; - case STATE_SET_IN_FAILURE: - state_str = 'STATE_SET_IN_FAILURE'; - break; - case STATE_SET_OUT_FAILURE: - state_str = 'STATE_SET_OUT_FAILURE'; - break; - } - console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str); - }); - }; - - // Return this object reference. - return (this); - - } - - // Define the Controller as the constructor function. - app.controller("SchedulerCtrl", Controller); - -})(angular, ManifoldApp); - -/****************************************************************************** - * MANIFOLD PLUGIN * - ******************************************************************************/ - -(function($) { - scheduler2 = Plugin.extend({ - - /** XXX to check - * @brief Plugin constructor - * @param options : an associative array of setting values - * @param element : - * @return : a jQuery collection of objects on which the plugin is - * applied, which allows to maintain chainability of calls - */ - init: function(options, element) { - // Call the parent constructor, see FAQ when forgotten - this._super(options, element); - - var scope = this._get_scope() - scope.instance = this; - - // XXX not needed - scheduler2Instance = this; - - // We need to remember the active filter for datatables filtering - // XXX not needed - this.filters = Array(); - - // XXX BETTER !!!! - $(window).delegate('*', 'keypress', function (evt){ - alert("erm"); - }); - - $(window).keydown(function(evt) { - if (evt.which == 17) { // ctrl - schedulerCtrlPressed = true; - } - }).keyup(function(evt) { - if (evt.which == 17) { // ctrl - schedulerCtrlPressed = false; - } - }); - - // XXX naming - //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove); - - this._resources_received = false; - this._leases_received = false; - - scope._leases_by_resource = {}; - - /* Listening to queries */ - this.listen_query(options.query_uuid, 'resources'); - this.listen_query(options.query_lease_uuid, 'leases'); - - this.elmt().on('show', this, this.on_show); - this.elmt().on('shown.bs.tab', this, this.on_show); - this.elmt().on('resize', this, this.on_resize); - - /* Generate slots according to the default granularity. Should - * be updated when resources arrive. Should be the pgcd in fact XXX */ - this._granularity = DEFAULT_GRANULARITY; - scope.granularity = this._granularity; - this.scope_resources_by_key = {}; - - this.do_resize(); - - scope.from = 0; - - this._initUI(); - - }, - - do_resize: function() - { - var scope = this._get_scope(); - var num_hidden_cells, new_max, lcm; - - // do_resize has to be called when the window is resized, or one parameter changes - // e.g. when new resources have been received - // - this.resource_granularities = [3600, 1800]; //, 2400]; /* s */ - - /* Compute the slot length to accommodate all resources. This - * is the GCD of all resource granularities. */ - this._slot_length = this._gcdn(this.resource_granularities); - - $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH); - //self get width might need fix depending on the template - var tblwidth = $('#scheduler-reservation-table').parent().outerWidth(); - - /* Number of visible cells...*/ - this._num_visible_cells = parseInt((tblwidth - SCHEDULER_FIRST_COLWIDTH) / SCHEDULER_COLWIDTH); - - /* ...should be a multiple of the lcm of all encountered granularities. */ - lcm = this._lcmn(this.resource_granularities) / this._slot_length; - this._num_visible_cells = this._num_visible_cells - this._num_visible_cells % lcm; - - // A list of {id, time} dictionaries representing the slots for the given day - this._all_slots = this._generate_all_slots(); - - /* scope also needs this value */ - scope.slots = this._all_slots; - scope.slot_length = this._slot_length; - scope.num_visible_cells = this._num_visible_cells; - scope.lcm_colspan = this._lcmn(this.resource_granularities); // XXX WHY ? - - /* Redraw... */ - this._scope_clear_leases(); - this._set_all_lease_slots(); - - // Slider max value - if ($('#tblSlider').data('slider') != undefined) { - num_hidden_cells = this._all_slots.length - this._num_visible_cells; - - $('#tblSlider').slider('setAttribute', 'max', num_hidden_cells); - $('#tblSlider').slider('setValue', scope.from, true); - } - this._get_scope().$apply(); - - - }, - - on_show: function(e) - { - var self = e.data; - self.do_resize(); - self._get_scope().$apply(); - }, - - on_resize: function(e) - { - var self = e.data; - self.do_resize(); - self._get_scope().$apply(); - }, - - /* Handlers */ - - _get_scope : function() - { - return angular.element(document.getElementById('SchedulerCtrl')).scope(); - }, - - _scope_set_resources : function() - { - var self = this; - var scope = this._get_scope(); - - var records = manifold.query_store.get_records(this.options.query_uuid); - - scope.resources = []; - - $.each(records, function(i, record) { - if (!record.exclusive) - return true; // ~ continue - - // copy not to modify original record - var resource = jQuery.extend(true, {}, record); - - // Fix granularity - //resource_granularity = ((resource.granularity === undefined) || (typeof(resource.granularity) != "number")) ? RESOURCE_DEFAULT_GRANULARITY : resource.granularity; - if (typeof(resource.granularity) != "number") - resource.granularity = RESOURCE_DEFAULT_GRANULARITY; - resource.leases = []; // a list of occupied timeslots - - self.scope_resources_by_key[resource['urn']] = resource; - scope.resources.push(resource); - }); - }, - - _scope_clear_leases: function() - { - var time, now; - var self = this; - var scope = this._get_scope(); - - now = new Date().getTime(); - - // Setup leases with a default free status... - $.each(this.scope_resources_by_key, function(resource_key, resource) { - resource.leases = []; - var colspan_lease = resource.granularity / self._slot_length; //eg. 3600 / 1800 => 2 cells - time = SchedulerDateSelected.getTime(); - for (i=0; i < self._all_slots.length / colspan_lease; i++) { // divide by granularity - resource.leases.push({ - id: 'coucou', - status: (time < now) ? 'disabled': 'free', // 'selected', 'reserved', 'maintenance' XXX pending ?? - }); - time += resource.granularity * 1000; - } - }); - - }, - - _scope_set_leases: function() - { - var status; - var self = this; - var scope = this._get_scope(); - - manifold.query_store.iter_records(this.options.query_lease_uuid, function(lease_key, lease) { - console.log("SET LEASES", lease.resource, new Date(lease.start_time* 1000), new Date(lease.end_time* 1000)); - // XXX We should ensure leases are correctly merged, otherwise our algorithm won't work - - // Populate leases by resource array: this will help us merging leases later - - // let's only put _our_ leases - lease_status = manifold.query_store.get_record_state(self.options.query_lease_uuid, lease_key, STATE_SET); - if (lease_status != STATE_SET_IN) - return true; // ~continue - if (!(lease.resource in scope._leases_by_resource)) - scope._leases_by_resource[lease.resource] = []; - scope._leases_by_resource[lease.resource].push(lease); - - }); - - this._set_all_lease_slots(); - }, - - _set_all_lease_slots: function() - { - var self = this; - - manifold.query_store.iter_records(this.options.query_lease_uuid, function(lease_key, lease) { - self._set_lease_slots(lease_key, lease); - }); - }, - - on_resources_query_done: function(data) - { - this._resources_received = true; - this._scope_set_resources(); - this._scope_clear_leases(); - if (this._leases_received) - this._scope_set_leases(); - - this._get_scope().$apply(); - }, - - on_leases_query_done: function(data) - { - this._leases_received = true; - if (this._resources_received) { - this._scope_set_leases(); - this._get_scope().$apply(); - } - }, - - /* Filters on resources */ - on_resources_filter_added: function(filter) { this._get_scope().$apply(); }, - on_resources_filter_removed: function(filter) { this._get_scope().$apply(); }, - on_resources_filter_clear: function() { this._get_scope().$apply(); }, - - /* Filters on leases ? */ - on_leases_filter_added: function(filter) { this._get_scope().$apply(); }, - on_leases_filter_removed: function(filter) { this._get_scope().$apply(); }, - on_leases_filter_clear: function() { this._get_scope().$apply(); }, - - on_field_state_changed: function(data) - { - /* - this._set_lease_slots(lease_key, lease); - - switch(data.state) { - case STATE_SET: - switch(data.op) { - case STATE_SET_IN: - case STATE_SET_IN_SUCCESS: - case STATE_SET_OUT_FAILURE: - this.set_checkbox_from_data(data.value, true); - this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET); - break; - case STATE_SET_OUT: - case STATE_SET_OUT_SUCCESS: - case STATE_SET_IN_FAILURE: - this.set_checkbox_from_data(data.value, false); - this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET); - break; - case STATE_SET_IN_PENDING: - this.set_checkbox_from_data(data.key, true); - this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_ADDED); - break; - case STATE_SET_OUT_PENDING: - this.set_checkbox_from_data(data.key, false); - this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_REMOVED); - break; - } - break; - - case STATE_WARNINGS: - this.change_status(data.key, data.value); - break; - } - */ - }, - - - /* INTERNAL FUNCTIONS */ - - _set_lease_slots: function(lease_key, lease) - { - var resource, lease_status, lease_class; - var day_timestamp, id_start, id_end, colspan_lease; - - resource = this.scope_resources_by_key[lease.resource]; - day_timestamp = SchedulerDateSelected.getTime() / 1000; - id_start = Math.floor((lease.start_time - day_timestamp) / resource.granularity); - - /* Some leases might be in the past */ - if (id_start < 0) - id_start = 0; - /* Leases in the future: ignore */ - if (id_start >= this._all_slots.length) - return true; // ~ continue - - id_end = Math.ceil((lease.end_time - day_timestamp) / resource.granularity); - colspan_lease = resource.granularity / this._slot_length; //eg. 3600 / 1800 => 2 cells - if (id_end >= this._all_slots.length / colspan_lease) { - /* Limit the display to the current day */ - id_end = this._all_slots.length / colspan_lease - } - lease_status = manifold.query_store.get_record_state(this.options.query_lease_uuid, lease_key, STATE_SET); - // the same slots might be affected multiple times. - // PENDING_IN + PENDING_OUT => IN - // - // RESERVED vs SELECTED ! - // - // PENDING !! - switch(lease_status) { - case STATE_SET_IN: - lease_class = 'selected'; // my leases - lease_success = ''; - break; - case STATE_SET_IN_SUCCESS: - lease_class = 'selected'; // my leases - lease_success = 'success'; - case STATE_SET_OUT_FAILURE: - lease_class = 'selected'; // my leases - lease_success = 'failure'; - break; - case STATE_SET_OUT: - lease_class = 'reserved'; // other leases - lease_success = ''; - break; - case STATE_SET_OUT_SUCCESS: - lease_class = 'free'; // other leases - lease_success = 'success'; - break; - case STATE_SET_IN_FAILURE: - lease_class = 'free'; // other leases - lease_success = 'failure'; - break; - case STATE_SET_IN_PENDING: - lease_class = 'pendingin'; - lease_success = ''; - break; - case STATE_SET_OUT_PENDING: - // pending_in & pending_out == IN == replacement - if (resource.leases[i].status == 'pendingin') - lease_class = 'pendingin' - else - lease_class = 'pendingout'; - lease_success = ''; - break; - - } - - for (i = id_start; i < id_end; i++) { - resource.leases[i].status = lease_class; - resource.leases[i].success = lease_success; - } - }, - -/* XXX IN TEMPLATE XXX - if (SchedulerDataViewData.length == 0) { - $("#plugin-scheduler").hide(); - $("#plugin-scheduler-empty").show(); - tmpScope.clearStuff(); - } else { - $("#plugin-scheduler-empty").hide(); - $("#plugin-scheduler").show(); - // initSchedulerResources - tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length); - } -*/ - - /** - * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler. - */ - _initUI: function() - { - var self = this; - var scope = self._get_scope(); - - var num_hidden_cells; - - $("#DateToRes").datepicker({ - dateFormat: "D, d M yy", - onRender: function(date) { - return date.valueOf() < now.valueOf() ? 'disabled' : ''; - } - }).on('changeDate', function(ev) { - SchedulerDateSelected = new Date(ev.date); - SchedulerDateSelected.setHours(0,0,0,0); - // Set slider to origin - //$('#tblSlider').slider('setValue', 0); // XXX - // Refresh leases - self._scope_clear_leases(); - self._set_all_lease_slots(); - // Refresh display - self._get_scope().$apply(); - }).datepicker('setValue', SchedulerDateSelected); //.data('datepicker'); - - //init Slider - num_hidden_cells = self._all_slots.length - self._num_visible_cells; - init_cell = (new Date().getHours() - 1) * 3600 / self._granularity; - if (init_cell > num_hidden_cells) - init_cell = num_hidden_cells; - - $('#tblSlider').slider({ - min: 0, - max: num_hidden_cells, - value: init_cell, - }).on('slide', function(ev) { - var scope = self._get_scope(); - scope.from = ev.value; - scope.$apply(); - }); - scope.from = init_cell; - scope.$apply(); - - $("#plugin-scheduler-loader").hide(); - $("#plugin-scheduler").show(); - }, - - // PRIVATE METHODS - - /** - * Greatest common divisor - */ - _gcd : function(x, y) - { - return (y==0) ? x : this._gcd(y, x % y); - }, - - _gcdn : function(array) - { - var self = this; - return array.reduce(function(prev, cur, idx, arr) { return self._gcd(prev, cur); }); - }, - - /** - * Least common multiple - */ - _lcm : function(x, y) - { - return x * y / this._gcd(x, y); - }, - - _lcmn : function(array) - { - var self = this; - return array.reduce(function(prev, cur, idx, arr) { return self._lcm(prev, cur); }); - }, - - _pad_str : function(i) - { - return (i < 10) ? "0" + i : "" + i; - }, - - /** - * Member variables used: - * _granularity - * - * Returns: - * A list of {id, time} dictionaries. - */ - _generate_all_slots: function() - { - var slots = []; - // Start with a random date (a first of a month), only time will matter - var d = new Date(2014, 1, 1, 0, 0, 0, 0); - var i = 0; - // Loop until we change the day - while (d.getDate() == 1) { - // Nicely format the time... - var tmpTime = this._pad_str(d.getHours()) + ':' + this._pad_str(d.getMinutes()); - /// ...and add the slot to the list of results - slots.push({ id: i, time: tmpTime }); - // Increment the date with the granularity - d = new Date(d.getTime() + this._slot_length * 1000); - i++; - } - return slots; - - }, - }); - - /* Plugin registration */ - $.plugin('Scheduler2', scheduler2); - -})(jQuery); - - - +/* +# +# Copyright (c) 2013 NITLab, University of Thessaly, CERTH, Greece +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# +# This is a MySlice plugin for the NITOS Scheduler +# Nitos Scheduler v1 +# +*/ + +// XXX groupid = all slots those that go with a min granularity + +/* some params */ +var scheduler2; +var scheduler2Instance; +//is ctrl keyboard button pressed +var schedulerCtrlPressed = false; +//table Id +var schedulerTblId = "scheduler-reservation-table"; +var SCHEDULER_FIRST_COLWIDTH = 200; + + +/* Number of scheduler slots per hour. Used to define granularity. Should be inferred from resources XXX */ +var schedulerSlotsPerHour = 6; +var RESOURCE_DEFAULT_GRANULARITY = 1800 /* s */; // should be computed automatically from resource information +var DEFAULT_GRANULARITY = 1800 /* s */; // should be computed automatically from resource information. Test with 600 +var DEFAULT_PAGE_RANGE = 5; + +var schedulerMaxRows = 12; + +/* All resources */ +var SchedulerData = []; + +/* ??? */ +var SchedulerSlots = []; + +var SchedulerDateSelected = new Date(); +// Round to midnight +SchedulerDateSelected.setHours(0,0,0,0); + +/* Filtered resources */ +var SchedulerDataViewData = []; + +var SchedulerSlotsViewData = []; +//Help Variables +var _schedulerCurrentCellPosition = 0; +//Enable Debug +var schedulerDebug = true; +//tmp to delete +var tmpSchedulerLeases = []; + +var SCHEDULER_COLWIDTH = 50; + + +/****************************************************************************** + * ANGULAR CONTROLLER * + ******************************************************************************/ + +// Create a private execution space for our controller. When +// executing this function expression, we're going to pass in +// the Angular reference and our application module. +(function (ng, app) { + + // Define our Controller constructor. + function Controller($scope) { + + // Store the scope so we can reference it in our + // class methods + this.scope = $scope; + + // Set up the default scope value. + this.scope.errorMessage = null; + this.scope.name = ""; + + //Pagin + $scope.current_page = 1; + this.scope.items_per_page = 10; + $scope.from = 0; // JORDAN + + $scope.instance = null; + $scope.resources = new Array(); + $scope.slots = SchedulerSlotsViewData; + $scope.granularity = DEFAULT_GRANULARITY; /* Will be setup */ + //$scope.msg = "hello"; + + angular.element(document).ready(function() { + //console.log('Hello World'); + //alert('Hello World'); + //afterAngularRendered(); + }); + + // Pagination + + $scope.range = function() { + var range_size = $scope.page_count() > DEFAULT_PAGE_RANGE ? DEFAULT_PAGE_RANGE : $scope.page_count(); + var ret = []; + var start; + + start = $scope.current_page; + if ( start > $scope.page_count()-range_size ) { + start = $scope.page_count()-range_size+1; + } + + for (var i=start; i 1) { + $scope.current_page--; + } + }; + + $scope.prevPageDisabled = function() { + return $scope.current_page === 1 ? "disabled" : ""; + }; + + $scope.page_count = function() + { + // XXX need visible resources only + var query_ext, visible_resources_length; + if (!$scope.instance) + return 0; + query_ext = manifold.query_store.find_analyzed_query_ext($scope.instance.options.query_uuid); + var visible_resources_length = 0; + query_ext.state.each(function(i, state) { + if (state[STATE_VISIBLE]) + visible_resources_length++; + }); + return Math.ceil(visible_resources_length/$scope.items_per_page); + }; + + $scope.nextPage = function() { + if ($scope.current_page < $scope.page_count()) { + $scope.current_page++; + } + }; + + $scope.nextPageDisabled = function() { + return $scope.current_page === $scope.page_count() ? "disabled" : ""; + }; + + $scope.setPage = function(n) { + $scope.current_page = n; + }; + // END pagination + + // FILTER + + $scope.filter_visible = function(resource) + { + return manifold.query_store.get_record_state($scope.instance.options.query_uuid, resource['urn'], STATE_VISIBLE); + }; + + // SELECTION + + $scope._create_new_lease = function(resource_urn, start_time, end_time) + { + var lease_key, new_lease, data; + + lease_key = manifold.metadata.get_key('lease'); + + new_lease = { + resource: resource_urn, + start_time: start_time, + end_time: end_time, + }; + + // This is needed to create a hashable object + new_lease.hashCode = manifold.record_hashcode(lease_key.sort()); + new_lease.equals = manifold.record_equals(lease_key); + + data = { + state: STATE_SET, + key : null, + op : STATE_SET_ADD, + value: new_lease + } + manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); + /* Add to local cache also, unless we listen to events from outside */ + if (!(resource_urn in $scope._leases_by_resource)) + $scope._leases_by_resource[resource_urn] = []; + $scope._leases_by_resource[resource_urn].push(new_lease); + } + + $scope._remove_lease = function(other) + { + var lease_key, other_key, data; + + lease_key = manifold.metadata.get_key('lease'); + + // XXX This could be a manifold.record_get_value + other_key = { + resource: other.resource, + start_time: other.start_time, + end_time: other.end_time + } + other_key.hashCode = manifold.record_hashcode(lease_key.sort()); + other_key.equals = manifold.record_equals(lease_key); + + data = { + state: STATE_SET, + key : null, + op : STATE_SET_REMOVE, + value: other_key + } + manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); + /* Remove from local cache also, unless we listen to events from outside */ + $scope._leases_by_resource[other.resource] = $.grep($scope._leases_by_resource[other.resource], function(x) { return x != other; }); + + } + + $scope.select = function(index, model_lease, model_resource) + { + var data, resource_granularity; + + //resource_granularity = model_resource.granularity === undefined ? RESOURCE_DEFAULT_GRANULARITY : model_resource.granularity; + + console.log("Selected", index, model_lease, model_resource); + + var day_timestamp = SchedulerDateSelected.getTime() / 1000; + var start_time = day_timestamp + index * model_resource.granularity; // XXX resource_granularity + var end_time = day_timestamp + (index + 1) * model_resource.granularity; // + var start_date = new Date(start_time * 1000); + var end_date = new Date(end_time * 1000); + + var lease_key = manifold.metadata.get_key('lease'); + + // We search for leases in the cache we previously constructed + var resource_leases = $scope._leases_by_resource[model_resource.urn]; + + switch (model_lease.status) + { + case 'free': // out + case 'pendingout': + if (resource_leases) { + /* Search for leases before */ + $.each(resource_leases, function(i, other) { + if (other.end_time != start_time) + return true; // ~ continue + + /* The lease 'other' is just before, and there should not exist + * any other lease before it */ + start_time = other.start_time; + + other_key = { + resource: other.resource, + start_time: other.start_time, + end_time: other.end_time + } + // This is needed to create a hashable object + other_key.hashCode = manifold.record_hashcode(lease_key.sort()); + other_key.equals = manifold.record_equals(lease_key); + + data = { + state: STATE_SET, + key : null, + op : STATE_SET_REMOVE, + value: other_key + } + manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); + /* Remove from local cache also, unless we listen to events from outside */ + $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; }); + return false; // ~ break + }); + + /* Search for leases after */ + $.each(resource_leases, function(i, other) { + if (other.start_time != end_time) + return true; // ~ continue + + /* The lease 'other' is just after, and there should not exist + * any other lease after it */ + end_time = other.end_time; + other_key = { + resource: other.resource, + start_time: other.start_time, + end_time: other.end_time + } + // This is needed to create a hashable object + other_key.hashCode = manifold.record_hashcode(lease_key.sort()); + other_key.equals = manifold.record_equals(lease_key); + + data = { + state: STATE_SET, + key : null, + op : STATE_SET_REMOVE, + value: other_key + } + manifold.raise_event($scope.instance.options.query_lease_uuid, FIELD_STATE_CHANGED, data); + /* Remove from local cache also, unless we listen to events from outside */ + $scope._leases_by_resource[model_resource.urn] = $.grep($scope._leases_by_resource[model_resource.urn], function(x) { return x != other; }); + return false; // ~ break + }); + } + + $scope._create_new_lease(model_resource.urn, start_time, end_time); + model_lease.status = (model_lease.status == 'free') ? 'pendingin' : 'selected'; + // unless the exact same lease already existed (pending_out status for the lease, not the cell !!) + + break; + + case 'selected': + case 'pendingin': + // We remove the cell + + /* We search for leases including this cell. Either 0, 1 or 2. + * 0 : NOT POSSIBLE, should be checked. + * 1 : either IN or OUT, we have make no change in the session + * 2 : both will be pending, since we have made a change in the session + * /!\ need to properly remove pending_in leases when removed again + */ + if (resource_leases) { + $.each(resource_leases, function(i, other) { + if ((other.start_time <= start_time) && (other.end_time >= end_time)) { + // The cell is part of this lease. + + // If the cell is not at the beginning of the lease, we recreate a lease with cells before + if (start_time > other.start_time) { + $scope._create_new_lease(model_resource.urn, other.start_time, start_time); + } + + // If the cell is not at the end of the lease, we recreate a lease with cells after + if (end_time < other.end_time) { + $scope._create_new_lease(model_resource.urn, end_time, other.end_time); + } + + // The other lease will be removed + $scope._remove_lease(other); + } + // NOTE: We can interrupt the search if we know that there is a single lease (depending on the status). + }); + } + + // cf comment in previous switch case + model_lease.status = (model_lease.status == 'selected') ? 'pendingout' : 'free'; + + break; + + case 'reserved': + case 'maintainance': + // Do nothing + break; + } + + + $scope._dump_leases(); + }; + + $scope._dump_leases = function() + { + // DEBUG: display all leases and their status in the log + var leases = manifold.query_store.get_records($scope.instance.options.query_lease_uuid); + console.log("--------------------"); + $.each(leases, function(i, lease) { + var key = manifold.metadata.get_key('lease'); + var lease_key = manifold.record_get_value(lease, key); + var state = manifold.query_store.get_record_state($scope.instance.options.query_lease_uuid, lease_key, STATE_SET); + var state_str; + switch(state) { + case STATE_SET_IN: + state_str = 'STATE_SET_IN'; + break; + case STATE_SET_OUT: + state_str = 'STATE_SET_OUT'; + break; + case STATE_SET_IN_PENDING: + state_str = 'STATE_SET_IN_PENDING'; + break; + case STATE_SET_OUT_PENDING: + state_str = 'STATE_SET_OUT_PENDING'; + break; + case STATE_SET_IN_SUCCESS: + state_str = 'STATE_SET_IN_SUCCESS'; + break; + case STATE_SET_OUT_SUCCESS: + state_str = 'STATE_SET_OUT_SUCCESS'; + break; + case STATE_SET_IN_FAILURE: + state_str = 'STATE_SET_IN_FAILURE'; + break; + case STATE_SET_OUT_FAILURE: + state_str = 'STATE_SET_OUT_FAILURE'; + break; + } + console.log("LEASE", new Date(lease.start_time * 1000), new Date(lease.end_time * 1000), lease.resource, state_str); + }); + }; + + // Return this object reference. + return (this); + + } + + // Define the Controller as the constructor function. + app.controller("SchedulerCtrl", Controller); + +})(angular, ManifoldApp); + +/****************************************************************************** + * MANIFOLD PLUGIN * + ******************************************************************************/ + +(function($) { + scheduler2 = Plugin.extend({ + + /** XXX to check + * @brief Plugin constructor + * @param options : an associative array of setting values + * @param element : + * @return : a jQuery collection of objects on which the plugin is + * applied, which allows to maintain chainability of calls + */ + init: function(options, element) { + // Call the parent constructor, see FAQ when forgotten + this._super(options, element); + + var scope = this._get_scope() + scope.instance = this; + + // XXX not needed + scheduler2Instance = this; + + // We need to remember the active filter for datatables filtering + // XXX not needed + this.filters = Array(); + + // XXX BETTER !!!! + $(window).delegate('*', 'keypress', function (evt){ + alert("erm"); + }); + + $(window).keydown(function(evt) { + if (evt.which == 17) { // ctrl + schedulerCtrlPressed = true; + } + }).keyup(function(evt) { + if (evt.which == 17) { // ctrl + schedulerCtrlPressed = false; + } + }); + + // XXX naming + //$("#" + schedulerTblId).on('mousedown', 'td', rangeMouseDown).on('mouseup', 'td', rangeMouseUp).on('mousemove', 'td', rangeMouseMove); + + this._resources_received = false; + this._leases_received = false; + + scope._leases_by_resource = {}; + + /* Listening to queries */ + this.listen_query(options.query_uuid, 'resources'); + this.listen_query(options.query_lease_uuid, 'leases'); + + this.elmt().on('show', this, this.on_show); + this.elmt().on('shown.bs.tab', this, this.on_show); + this.elmt().on('resize', this, this.on_resize); + + /* Generate slots according to the default granularity. Should + * be updated when resources arrive. Should be the pgcd in fact XXX */ + this._granularity = DEFAULT_GRANULARITY; + scope.granularity = this._granularity; + this.scope_resources_by_key = {}; + + this.do_resize(); + + scope.from = 0; + + this._initUI(); + + }, + + do_resize: function() + { + var scope = this._get_scope(); + var num_hidden_cells, new_max, lcm; + + // do_resize has to be called when the window is resized, or one parameter changes + // e.g. when new resources have been received + // + this.resource_granularities = [3600, 1800]; //, 2400]; /* s */ + + /* Compute the slot length to accommodate all resources. This + * is the GCD of all resource granularities. */ + this._slot_length = this._gcdn(this.resource_granularities); + + $('#' + schedulerTblId + ' thead tr th:eq(0)').css("width", SCHEDULER_FIRST_COLWIDTH); + //self get width might need fix depending on the template + var tblwidth = $('#scheduler-reservation-table').parent().outerWidth(); + + /* Number of visible cells...*/ + this._num_visible_cells = parseInt((tblwidth - SCHEDULER_FIRST_COLWIDTH) / SCHEDULER_COLWIDTH); + + /* ...should be a multiple of the lcm of all encountered granularities. */ + lcm = this._lcmn(this.resource_granularities) / this._slot_length; + this._num_visible_cells = this._num_visible_cells - this._num_visible_cells % lcm; + + // A list of {id, time} dictionaries representing the slots for the given day + this._all_slots = this._generate_all_slots(); + + /* scope also needs this value */ + scope.slots = this._all_slots; + scope.slot_length = this._slot_length; + scope.num_visible_cells = this._num_visible_cells; + scope.lcm_colspan = this._lcmn(this.resource_granularities); // XXX WHY ? + + /* Redraw... */ + this._scope_clear_leases(); + this._set_all_lease_slots(); + + // Slider max value + if ($('#tblSlider').data('slider') != undefined) { + num_hidden_cells = this._all_slots.length - this._num_visible_cells; + + $('#tblSlider').slider('setAttribute', 'max', num_hidden_cells); + $('#tblSlider').slider('setValue', scope.from, true); + } + this._get_scope().$apply(); + + + }, + + on_show: function(e) + { + var self = e.data; + self.do_resize(); + self._get_scope().$apply(); + }, + + on_resize: function(e) + { + var self = e.data; + self.do_resize(); + self._get_scope().$apply(); + }, + + /* Handlers */ + + _get_scope : function() + { + return angular.element(document.getElementById('SchedulerCtrl')).scope(); + }, + + _scope_set_resources : function() + { + var self = this; + var scope = this._get_scope(); + + var records = manifold.query_store.get_records(this.options.query_uuid); + + scope.resources = []; + + $.each(records, function(i, record) { + if (!record.exclusive) + return true; // ~ continue + + // copy not to modify original record + var resource = jQuery.extend(true, {}, record); + + // Fix granularity + //resource_granularity = ((resource.granularity === undefined) || (typeof(resource.granularity) != "number")) ? RESOURCE_DEFAULT_GRANULARITY : resource.granularity; + if (typeof(resource.granularity) != "number") + resource.granularity = RESOURCE_DEFAULT_GRANULARITY; + resource.leases = []; // a list of occupied timeslots + + self.scope_resources_by_key[resource['urn']] = resource; + scope.resources.push(resource); + }); + }, + + _scope_clear_leases: function() + { + var time, now; + var self = this; + var scope = this._get_scope(); + + now = new Date().getTime(); + + // Setup leases with a default free status... + $.each(this.scope_resources_by_key, function(resource_key, resource) { + resource.leases = []; + var colspan_lease = resource.granularity / self._slot_length; //eg. 3600 / 1800 => 2 cells + time = SchedulerDateSelected.getTime(); + for (i=0; i < self._all_slots.length / colspan_lease; i++) { // divide by granularity + resource.leases.push({ + id: 'coucou', + status: (time < now) ? 'disabled': 'free', // 'selected', 'reserved', 'maintenance' XXX pending ?? + }); + time += resource.granularity * 1000; + } + }); + + }, + + _scope_set_leases: function() + { + var status; + var self = this; + var scope = this._get_scope(); + + manifold.query_store.iter_records(this.options.query_lease_uuid, function(lease_key, lease) { + //console.log("SET LEASES", lease.resource, new Date(lease.start_time* 1000), new Date(lease.end_time* 1000)); + // XXX We should ensure leases are correctly merged, otherwise our algorithm won't work + + // Populate leases by resource array: this will help us merging leases later + + // let's only put _our_ leases + lease_status = manifold.query_store.get_record_state(self.options.query_lease_uuid, lease_key, STATE_SET); + if (lease_status != STATE_SET_IN) + return true; // ~continue + if (!(lease.resource in scope._leases_by_resource)) + scope._leases_by_resource[lease.resource] = []; + scope._leases_by_resource[lease.resource].push(lease); + + }); + + this._set_all_lease_slots(); + }, + + _set_all_lease_slots: function() + { + var self = this; + + manifold.query_store.iter_records(this.options.query_lease_uuid, function(lease_key, lease) { + self._set_lease_slots(lease_key, lease); + }); + }, + + on_resources_query_done: function(data) + { + this._resources_received = true; + this._scope_set_resources(); + this._scope_clear_leases(); + if (this._leases_received) + this._scope_set_leases(); + + this._get_scope().$apply(); + }, + + on_leases_query_done: function(data) + { + this._leases_received = true; + if (this._resources_received) { + this._scope_set_leases(); + this._get_scope().$apply(); + } + }, + + /* Filters on resources */ + on_resources_filter_added: function(filter) { this._get_scope().$apply(); }, + on_resources_filter_removed: function(filter) { this._get_scope().$apply(); }, + on_resources_filter_clear: function() { this._get_scope().$apply(); }, + + /* Filters on leases ? */ + on_leases_filter_added: function(filter) { this._get_scope().$apply(); }, + on_leases_filter_removed: function(filter) { this._get_scope().$apply(); }, + on_leases_filter_clear: function() { this._get_scope().$apply(); }, + + on_field_state_changed: function(data) + { + /* + this._set_lease_slots(lease_key, lease); + + switch(data.state) { + case STATE_SET: + switch(data.op) { + case STATE_SET_IN: + case STATE_SET_IN_SUCCESS: + case STATE_SET_OUT_FAILURE: + this.set_checkbox_from_data(data.value, true); + this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET); + break; + case STATE_SET_OUT: + case STATE_SET_OUT_SUCCESS: + case STATE_SET_IN_FAILURE: + this.set_checkbox_from_data(data.value, false); + this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_RESET); + break; + case STATE_SET_IN_PENDING: + this.set_checkbox_from_data(data.key, true); + this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_ADDED); + break; + case STATE_SET_OUT_PENDING: + this.set_checkbox_from_data(data.key, false); + this.set_bgcolor(data.value, QUERYTABLE_BGCOLOR_REMOVED); + break; + } + break; + + case STATE_WARNINGS: + this.change_status(data.key, data.value); + break; + } + */ + }, + + + /* INTERNAL FUNCTIONS */ + + _set_lease_slots: function(lease_key, lease) + { + var resource, lease_status, lease_class; + var day_timestamp, id_start, id_end, colspan_lease; + + resource = this.scope_resources_by_key[lease.resource]; + day_timestamp = SchedulerDateSelected.getTime() / 1000; + if(resource === undefined){ + console.log('resource undefined = '+lease.resource); + return; + } + id_start = Math.floor((lease.start_time - day_timestamp) / resource.granularity); + + /* Some leases might be in the past */ + if (id_start < 0) + id_start = 0; + /* Leases in the future: ignore */ + if (id_start >= this._all_slots.length) + return true; // ~ continue + + id_end = Math.ceil((lease.end_time - day_timestamp) / resource.granularity); + colspan_lease = resource.granularity / this._slot_length; //eg. 3600 / 1800 => 2 cells + if (id_end >= this._all_slots.length / colspan_lease) { + /* Limit the display to the current day */ + id_end = this._all_slots.length / colspan_lease + } + lease_status = manifold.query_store.get_record_state(this.options.query_lease_uuid, lease_key, STATE_SET); + // the same slots might be affected multiple times. + // PENDING_IN + PENDING_OUT => IN + // + // RESERVED vs SELECTED ! + // + // PENDING !! + switch(lease_status) { + case STATE_SET_IN: + lease_class = 'selected'; // my leases + lease_success = ''; + break; + case STATE_SET_IN_SUCCESS: + lease_class = 'selected'; // my leases + lease_success = 'success'; + case STATE_SET_OUT_FAILURE: + lease_class = 'selected'; // my leases + lease_success = 'failure'; + break; + case STATE_SET_OUT: + lease_class = 'reserved'; // other leases + lease_success = ''; + break; + case STATE_SET_OUT_SUCCESS: + lease_class = 'free'; // other leases + lease_success = 'success'; + break; + case STATE_SET_IN_FAILURE: + lease_class = 'free'; // other leases + lease_success = 'failure'; + break; + case STATE_SET_IN_PENDING: + lease_class = 'pendingin'; + lease_success = ''; + break; + case STATE_SET_OUT_PENDING: + // pending_in & pending_out == IN == replacement + if (resource.leases[i].status == 'pendingin') + lease_class = 'pendingin' + else + lease_class = 'pendingout'; + lease_success = ''; + break; + + } + + for (i = id_start; i < id_end; i++) { + resource.leases[i].status = lease_class; + resource.leases[i].success = lease_success; + } + }, + +/* XXX IN TEMPLATE XXX + if (SchedulerDataViewData.length == 0) { + $("#plugin-scheduler").hide(); + $("#plugin-scheduler-empty").show(); + tmpScope.clearStuff(); + } else { + $("#plugin-scheduler-empty").hide(); + $("#plugin-scheduler").show(); + // initSchedulerResources + tmpScope.initSchedulerResources(schedulerMaxRows < SchedulerDataViewData.length ? schedulerMaxRows : SchedulerDataViewData.length); + } +*/ + + /** + * Initialize the date picker, the table, the slider and the buttons. Once done, display scheduler. + */ + _initUI: function() + { + var self = this; + var scope = self._get_scope(); + + var num_hidden_cells; + + $("#DateToRes").datepicker({ + dateFormat: "D, d M yy", + onRender: function(date) { + return date.valueOf() < now.valueOf() ? 'disabled' : ''; + } + }).on('changeDate', function(ev) { + SchedulerDateSelected = new Date(ev.date); + SchedulerDateSelected.setHours(0,0,0,0); + // Set slider to origin + //$('#tblSlider').slider('setValue', 0); // XXX + // Refresh leases + self._scope_clear_leases(); + self._set_all_lease_slots(); + // Refresh display + self._get_scope().$apply(); + }).datepicker('setValue', SchedulerDateSelected); //.data('datepicker'); + + //init Slider + num_hidden_cells = self._all_slots.length - self._num_visible_cells; + init_cell = (new Date().getHours() - 1) * 3600 / self._granularity; + if (init_cell > num_hidden_cells) + init_cell = num_hidden_cells; + + $('#tblSlider').slider({ + min: 0, + max: num_hidden_cells, + value: init_cell, + }).on('slide', function(ev) { + var scope = self._get_scope(); + scope.from = ev.value; + scope.$apply(); + }); + scope.from = init_cell; + scope.$apply(); + + $("#plugin-scheduler-loader").hide(); + $("#plugin-scheduler").show(); + }, + + // PRIVATE METHODS + + /** + * Greatest common divisor + */ + _gcd : function(x, y) + { + return (y==0) ? x : this._gcd(y, x % y); + }, + + _gcdn : function(array) + { + var self = this; + return array.reduce(function(prev, cur, idx, arr) { return self._gcd(prev, cur); }); + }, + + /** + * Least common multiple + */ + _lcm : function(x, y) + { + return x * y / this._gcd(x, y); + }, + + _lcmn : function(array) + { + var self = this; + return array.reduce(function(prev, cur, idx, arr) { return self._lcm(prev, cur); }); + }, + + _pad_str : function(i) + { + return (i < 10) ? "0" + i : "" + i; + }, + + /** + * Member variables used: + * _granularity + * + * Returns: + * A list of {id, time} dictionaries. + */ + _generate_all_slots: function() + { + var slots = []; + // Start with a random date (a first of a month), only time will matter + var d = new Date(2014, 1, 1, 0, 0, 0, 0); + var i = 0; + // Loop until we change the day + while (d.getDate() == 1) { + // Nicely format the time... + var tmpTime = this._pad_str(d.getHours()) + ':' + this._pad_str(d.getMinutes()); + /// ...and add the slot to the list of results + slots.push({ id: i, time: tmpTime }); + // Increment the date with the granularity + d = new Date(d.getTime() + this._slot_length * 1000); + i++; + } + return slots; + + }, + }); + + /* Plugin registration */ + $.plugin('Scheduler2', scheduler2); + +})(jQuery); + + + diff --git a/plugins/testbeds/static/js/testbeds.js b/plugins/testbeds/static/js/testbeds.js index e628a679..ba40d00a 100644 --- a/plugins/testbeds/static/js/testbeds.js +++ b/plugins/testbeds/static/js/testbeds.js @@ -51,6 +51,10 @@ $scope._testbed_active[facility] = new Object(); $scope._testbed_active[facility][testbed] = value; }; + + $scope.tolower = function(string) { + return string.toLowerCase(string); + }; /* Click event */ @@ -58,30 +62,34 @@ { var selected, prev_selected, num, num_selected, num_prev_selected, filter; - prev_selected = $.map($scope.facility_names, function(x, i) { - return $scope.is_facility_active(x) ? x : null; - }); + // prev_selected = $.map($scope.facility_names, function(x, i) { + // return $scope.is_facility_active(x) ? x : null; + // }); $scope.set_facility_active(facility, ! $scope.is_facility_active(facility)); - - selected = $.map($scope.facility_names, function(x, i) { - return $scope.is_facility_active(x) ? x : null; + + $.each($scope.testbed_names[facility], function(j, testbed_name) { + $scope.select_testbed(facility, testbed_name); }); - - num = $scope.facility_names.length; - prev_num_selected = prev_selected.length; - num_selected = selected.length; - - if ((prev_num_selected != 0) && (prev_num_selected != num)) { - // Remove previous filter - filter = ['facility_name', 'included', prev_selected]; - manifold.raise_event($scope.instance.options.query_uuid, FILTER_REMOVED, filter); - } - - if (num_selected != num) { - filter = ['facility_name', 'included', selected]; - manifold.raise_event($scope.instance.options.query_uuid, FILTER_ADDED, filter); - } + console.log($scope); + // selected = $.map($scope.facility_names, function(x, i) { + // return $scope.is_facility_active(x) ? x : null; + // }); + + // num = $scope.facility_names.length; + // prev_num_selected = prev_selected.length; + // num_selected = selected.length; + + // if ((prev_num_selected != 0) && (prev_num_selected != num)) { + // // Remove previous filter + // filter = ['facility_name', 'included', prev_selected]; + // manifold.raise_event($scope.instance.options.query_uuid, FILTER_REMOVED, filter); + // } +// + // if (num_selected != num) { + // filter = ['facility_name', 'included', selected]; + // manifold.raise_event($scope.instance.options.query_uuid, FILTER_ADDED, filter); + // } }; $scope.select_testbed = function(facility, testbed) @@ -240,7 +248,7 @@ _get_scope : function() { - return angular.element('[ng-controller=TestbedsCtrl]').scope() + return angular.element('[ng-controller=TestbedsCtrl]').scope(); }, /* diff --git a/plugins/testbeds/templates/testbeds.html b/plugins/testbeds/templates/testbeds.html index 296e5efb..755bc355 100644 --- a/plugins/testbeds/templates/testbeds.html +++ b/plugins/testbeds/templates/testbeds.html @@ -1,34 +1,27 @@ -
- -
Facilities
- - - - + diff --git a/portal/accountview.py b/portal/accountview.py index 4d25c611..dfda1826 100644 --- a/portal/accountview.py +++ b/portal/accountview.py @@ -1,7 +1,5 @@ from unfold.loginrequired import LoginRequiredAutoLogoutView # -from sfa.trust.credential import Credential -from sfa.trust.certificate import Keypair # from manifold.core.query import Query from manifoldapi.manifoldapi import execute_query @@ -40,8 +38,8 @@ class AccountView(LoginRequiredAutoLogoutView, ThemeView): page.add_css_files ( [ "css/onelab.css", "css/account_view.css","css/plugin.css" ] ) # Execute a Query to delegate credentials if necessary - sfa_user_query = Query().get('myslice:user').select('user_hrn').filter_by('user_hrn','==','$user_hrn') - sfa_user_result = execute_query(self.request, sfa_user_query) + #sfa_user_query = Query().get('myslice:user').select('user_hrn').filter_by('user_hrn','==','$user_hrn') + #sfa_user_result = execute_query(self.request, sfa_user_query) user_query = Query().get('local:user').select('config','email','status') user_details = execute_query(self.request, user_query) @@ -271,6 +269,9 @@ def get_myslice_account(request): @login_required #my_acc form value processing def account_process(request): + from sfa.trust.credential import Credential + from sfa.trust.certificate import Keypair + user_query = Query().get('local:user').select('user_id','email','password','config') user_details = execute_query(request, user_query) @@ -297,7 +298,7 @@ def account_process(request): for account_detail in account_details: for platform_detail in platform_details: # Add reference account to the platforms - if 'add_'+platform_detail['platform'] in request.POST: + if 'add_'+platform_detail['platform'] in request.POST or request.POST['button_value'] == 'add_'+platform_detail['platform']: platform_id = platform_detail['platform_id'] user_params = {'platform_id': platform_id, 'user_id': user_id, 'auth_type': "reference", 'config': '{"reference_platform": "myslice"}'} manifold_add_account(request,user_params) @@ -305,7 +306,7 @@ def account_process(request): return HttpResponseRedirect("/portal/account/") # Delete reference account from the platforms - if 'delete_'+platform_detail['platform'] in request.POST: + if 'delete_'+platform_detail['platform'] in request.POST or request.POST['button_value'] == 'delete_'+platform_detail['platform']: platform_id = platform_detail['platform_id'] user_params = {'user_id':user_id} manifold_delete_account(request,platform_id, user_id, user_params) @@ -330,7 +331,7 @@ def account_process(request): slice_cred.append(value) # special case: download each slice credentials separately for i in range(0, len(slice_list)): - if 'dl_'+slice_list[i] in request.POST: + if 'dl_'+slice_list[i] in request.POST or request.POST['button_value'] == 'dl_'+slice_list[i]: slice_detail = "Slice name: " + slice_list[i] +"\nSlice Credentials: \n"+ slice_cred[i] response = HttpResponse(slice_detail, content_type='text/plain') response['Content-Disposition'] = 'attachment; filename="slice_credential.txt"' @@ -345,7 +346,7 @@ def account_process(request): auth_cred.append(value) # special case: download each slice credentials separately for i in range(0, len(auth_list)): - if 'dl_'+auth_list[i] in request.POST: + if 'dl_'+auth_list[i] in request.POST or request.POST['button_value'] == 'dl_'+auth_list[i]: auth_detail = "Authority: " + auth_list[i] +"\nAuthority Credentials: \n"+ auth_cred[i] response = HttpResponse(auth_detail, content_type='text/plain') response['Content-Disposition'] = 'attachment; filename="auth_credential.txt"' @@ -417,7 +418,6 @@ def account_process(request): sfa_update_user(request, user_hrn, user_pub_key) result_sfa_user = sfa_get_user(request, user_hrn, public_key) try: - result_sfa_user = result_sfa_user[0] if 'keys' in result_sfa_user and result_sfa_user['keys'][0] == public_key: # updating manifold updated_config = json.dumps(account_config) @@ -426,7 +426,7 @@ def account_process(request): messages.success(request, 'Sucess: New Keypair Generated! Delegation of your credentials will be automatic.') else: raise Exception,"Keys are not matching" - except Exception,e: + except Exception, e: messages.error(request, 'Error: An error occured during the update of your public key at the Registry, or your public key is not matching the one stored.') print "Exception in accountview ", e return HttpResponseRedirect("/portal/account/") @@ -467,7 +467,7 @@ def account_process(request): messages.error(request, 'Account error: You need an account in myslice platform to perform this action') return HttpResponseRedirect("/portal/account/") - elif 'dl_pubkey' in request.POST: + elif 'dl_pubkey' in request.POST or request.POST['button_value'] == 'dl_pubkey': for account_detail in account_details: for platform_detail in platform_details: if platform_detail['platform_id'] == account_detail['platform_id']: @@ -482,7 +482,7 @@ def account_process(request): messages.error(request, 'Account error: You need an account in myslice platform to perform this action') return HttpResponseRedirect("/portal/account/") - elif 'dl_pkey' in request.POST: + elif 'dl_pkey' in request.POST or request.POST['button_value'] == 'dl_pkey': for account_detail in account_details: for platform_detail in platform_details: if platform_detail['platform_id'] == account_detail['platform_id']: @@ -501,7 +501,7 @@ def account_process(request): messages.error(request, 'Account error: You need an account in myslice platform to perform this action') return HttpResponseRedirect("/portal/account/") - elif 'delete' in request.POST: + elif 'delete' in request.POST or request.POST['button_value'] == 'delete': for account_detail in account_details: for platform_detail in platform_details: if platform_detail['platform_id'] == account_detail['platform_id']: @@ -527,7 +527,7 @@ def account_process(request): return HttpResponseRedirect("/portal/account/") # download identity for jfed - elif 'dl_identity' in request.POST: + elif 'dl_identity' in request.POST or request.POST['button_value'] == 'dl_identity': for account_detail in account_details: for platform_detail in platform_details: if platform_detail['platform_id'] == account_detail['platform_id']: @@ -550,7 +550,7 @@ def account_process(request): return HttpResponseRedirect("/portal/account/") # Download sfi_config - elif 'dl_sfi_config' in request.POST: + elif 'dl_sfi_config' in request.POST or request.POST['button_value'] == 'dl_sfi_config': platform_detail = get_myslice_platform(request) platform_config = json.loads(platform_detail['config']) account_detail = get_myslice_account(request) @@ -584,7 +584,7 @@ def account_process(request): return response #clear all creds - elif 'clear_cred' in request.POST: + elif 'clear_cred' in request.POST or request.POST['button_value'] == 'clear_cred': try: result = clear_user_creds(request, user_email) if result is not None: @@ -597,7 +597,7 @@ def account_process(request): return HttpResponseRedirect("/portal/account/") # Download delegated_user_cred - elif 'dl_user_cred' in request.POST: + elif 'dl_user_cred' in request.POST or request.POST['button_value'] == 'dl_user_cred': if 'delegated_user_credential' in account_config: user_cred = account_config['delegated_user_credential'] response = HttpResponse(user_cred, content_type='text/plain') @@ -608,7 +608,7 @@ def account_process(request): return HttpResponseRedirect("/portal/account/") # Download user_cert - elif 'dl_user_cert' in request.POST: + elif 'dl_user_cert' in request.POST or request.POST['button_value'] == 'dl_user_cert': if 'user_credential' in account_config: user_cred = account_config['user_credential'] obj_cred = Credential(string=user_cred) @@ -631,7 +631,7 @@ def account_process(request): return HttpResponseRedirect("/portal/account/") # Download user p12 = private_key + Certificate - elif 'dl_user_p12' in request.POST: + elif 'dl_user_p12' in request.POST or request.POST['button_value'] == 'dl_user_p12': if 'user_credential' in account_config and 'user_private_key' in account_config: user_cred = account_config['user_credential'] obj_cred = Credential(string=user_cred) @@ -673,8 +673,6 @@ def account_process(request): messages.error(request, 'Download error: User private key or credential is not stored in the server') return HttpResponseRedirect("/portal/account/") - - else: messages.info(request, 'Under Construction. Please try again later!') return HttpResponseRedirect("/portal/account/") diff --git a/portal/actions.py b/portal/actions.py index e8a4d018..047288d2 100644 --- a/portal/actions.py +++ b/portal/actions.py @@ -1,17 +1,19 @@ -from django.http import HttpResponse -from manifold.core.query import Query -from manifoldapi.manifoldapi import execute_query,execute_admin_query -from portal.models import PendingUser, PendingSlice, PendingAuthority +from django.http import HttpResponse +from manifold.core.query import Query +from manifoldapi.manifoldapi import execute_query,execute_admin_query +from portal.models import PendingUser, PendingSlice, PendingAuthority, PendingProject, PendingJoin +from unfold.page import Page + import json -from django.contrib.auth.models import User -from django.contrib.sites.models import Site -from django.contrib.auth import get_user_model -from django.template.loader import render_to_string -from django.core.mail import EmailMultiAlternatives, send_mail +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.contrib.auth import get_user_model +from django.template.loader import render_to_string +from django.core.mail import EmailMultiAlternatives, send_mail -from myslice.theme import ThemeView -from myslice.configengine import ConfigEngine +from myslice.theme import ThemeView +from myslice.configengine import ConfigEngine theme = ThemeView() @@ -24,14 +26,18 @@ import activity.slice #from sfa.util.xrn import Xrn -# Get the list of authorities - +# Get the list of pis in a given authority def authority_get_pis(request, authority_hrn): + # CACHE PB with fields + page = Page(request) + metadata = page.get_metadata() + auth_md = metadata.details_by_object('authority') + auth_fields = [column['name'] for column in auth_md['column']] # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 - query = Query.get('authority').filter_by('authority_hrn', '==', authority_hrn).select('pi_users') + query = Query.get('myslice:authority').filter_by('authority_hrn', '==', authority_hrn).select(auth_fields) results = execute_admin_query(request, query) - print "authority_get_pis = %s" % results + #print "authority_get_pis = %s" % results # NOTE: temporarily commented. Because results is giving empty list. # Needs more debugging #if not results: @@ -40,6 +46,80 @@ def authority_get_pis(request, authority_hrn): #return result['pi_users'] return results +#check the user is pi or not in the registry +def authority_check_pis(request, user_email): + try: + user_query = Query().get('local:user').filter_by('email', '==', user_email).select('user_id','email','password','config') + user_details = execute_admin_query(request, user_query) + + # getting the authority_hrn + for user_detail in user_details: + user_id = user_detail['user_id'] + if user_detail['config']: + config = json.loads(user_detail['config']) + authority_hrn = config.get('authority','Unknown Authority') + + account_query = Query().get('local:account').filter_by('user_id', '==', user_id).select('user_id','platform_id','auth_type','config') + account_details = execute_admin_query(request, account_query) + + platform_query = Query().get('local:platform').select('platform_id','platform') + platform_details = execute_admin_query(request, platform_query) + + for account_detail in account_details: + for platform_detail in platform_details: + if platform_detail['platform_id'] == account_detail['platform_id']: + if 'myslice' in platform_detail['platform']: + account_config = json.loads(account_detail['config']) + user_hrn = account_config.get('user_hrn','N/A') + + pi_status = False + pis = authority_get_pis (request, authority_hrn) + for pi in pis: + pi_list = pi['pi_users'] + + if user_hrn in pi_list: + pi_status = True + return pi_status + + except Exception,e: + print "Exception in actions.py in authority_check_pis %s" % e + return None + + +def authority_add_pis(request, authority_hrn,user_hrn): + try: + # getting pis of the authority of the user + pis = authority_get_pis (request, authority_hrn) + for pi in pis: + pi_list = pi['pi_users'] + + updated_pi_list = pi_list.append(user_hrn) + query = Query.update('myslice:authority').filter_by('authority_hrn', '==', authority_hrn).set({'pi_users':pi_list}) + results = execute_query(request,query) + newpis = authority_get_pis (request, authority_hrn) + return newpis + except Exception,e: + print "Exception in actions.py in authority_add_pis %s" % e + return None + + +def authority_remove_pis(request, authority_hrn,user_hrn): + try: + # getting pis of the authority of the user + pis = authority_get_pis (request, authority_hrn) + for pi in pis: + pi_list = pi['pi_users'] + + updated_pi_list = pi_list.remove(user_hrn) + query = Query.update('authority').filter_by('authority_hrn', '==', authority_hrn).set({'pi_users':pi_list}) + results = execute_query(request,query) + newpis = authority_get_pis (request, authority_hrn) + return newpis + except Exception,e: + print "Exception in actions.py in authority_remove_pis %s" % e + return None + + def authority_get_pi_emails(request, authority_hrn): pi_users = authority_get_pis(request,authority_hrn) print "pi_users = %s" % pi_users @@ -99,28 +179,35 @@ def clear_user_creds(request, user_email): return None def is_pi(wsgi_request, user_hrn, authority_hrn): - # XXX could be done in a single query ! - - # seauthorities from user where user_hrn == "ple.upmc.jordan_auge" - - # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 - query = Query.get('myslice:user').filter_by('user_hrn', '==', user_hrn).select('pi_authorities') - results = execute_admin_query(wsgi_request, query) - if not results: - # XXX Warning ? - return False - result = results[0] - user_authority_hrns = result.get('pi_authorities', []) - return authority_hrn in user_authority_hrns + # authorities from user where user_hrn == "ple.upmc.jordan_auge" + print "#### actions.py is_pi authority_hrn = ", authority_hrn + try: + # CACHE PB with fields + page = Page(wsgi_request) + metadata = page.get_metadata() + user_md = metadata.details_by_object('user') + user_fields = [column['name'] for column in user_md['column']] + + # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 + query = Query().get('myslice:user').select(user_fields).filter_by('user_hrn','==',user_hrn) + #query = Query.get('myslice:user').filter_by('user_hrn', '==', user_hrn).select('pi_authorities') + results = execute_query(wsgi_request, query) + print "is_pi results = ", results + for user_detail in results: + if authority_hrn in user_detail['pi_authorities']: + return True + except Exception,e: + print "Exception in actions.py in is_pi %s" % e + return False # SFA get record -def sfa_get_user(request, user_hrn, pub): +def sfa_get_user(request, user_hrn, pub=None): # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 query_sfa_user = Query.get('myslice:user').filter_by('user_hrn', '==', user_hrn) result_sfa_user = execute_admin_query(request, query_sfa_user) - return result_sfa_user + return result_sfa_user[0] def sfa_update_user(request, user_hrn, user_params): # user_params: keys [public_key] @@ -135,7 +222,7 @@ def sfa_update_user(request, user_hrn, user_params): def sfa_add_authority(request, authority_params): # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 - query = Query.create('authority').set(authority_params).select('authority_hrn') + query = Query.create('myslice:authority').set(authority_params).select('authority_hrn') results = execute_query(request, query) print "sfa_add_auth results=",results if not results: @@ -267,6 +354,29 @@ def make_request_slice(slice): request['purpose'] = slice.purpose return request +def make_request_project(project): + request = {} + request['type'] = 'project' + request['id'] = project.id + request['user_hrn'] = project.user_hrn + request['email'] = project.email + request['timestamp'] = project.created + request['authority_hrn'] = project.authority_hrn + request['project_name'] = project.project_name + request['purpose'] = project.purpose + return request + +def make_request_join(join): + request = {} + request['type'] = 'join' + request['id'] = join.id + request['user_hrn'] = join.user_hrn + request['email'] = join.email + request['timestamp'] = join.created + request['authority_hrn'] = join.authority_hrn + request['project_name'] = join.project_name + return request + def make_request_authority(authority): request = {} request['type'] = 'authority' @@ -288,7 +398,8 @@ def make_request_authority(authority): request['timestamp'] = authority.created return request -def make_requests(pending_users, pending_slices, pending_authorities): +def make_requests(pending_users, pending_slices, pending_authorities, pending_projects, pending_joins): + print "$$$$$$$$$$$$$$$ make_request" requests = [] for user in pending_users: requests.append(make_request_user(user)) @@ -296,10 +407,15 @@ def make_requests(pending_users, pending_slices, pending_authorities): requests.append(make_request_slice(slice)) for authority in pending_authorities: requests.append(make_request_authority(authority)) + for project in pending_projects: + requests.append(make_request_project(project)) + for join in pending_joins: + requests.append(make_request_join(join)) return requests def get_request_by_id(ids): - sorted_ids = { 'user': [], 'slice': [], 'authority': [] } + print "$$$$$$$$$$$$$$$$ get_request_by_id" + sorted_ids = { 'user': [], 'slice': [], 'authority': [], 'project': [], 'join': [] } for type__id in ids: type, id = type__id.split('__') sorted_ids[type].append(id) @@ -308,40 +424,53 @@ def get_request_by_id(ids): pending_users = PendingUser.objects.all() pending_slices = PendingSlice.objects.all() pending_authorities = PendingAuthority.objects.all() + pending_projects = PendingProject.objects.all() + pending_joins = PendingJoin.objects.all() else: pending_users = PendingUser.objects.filter(id__in=sorted_ids['user']).all() pending_slices = PendingSlice.objects.filter(id__in=sorted_ids['slice']).all() pending_authorities = PendingAuthority.objects.filter(id__in=sorted_ids['authority']).all() + pending_projects = PendingProject.objects.filter(id__in=sorted_ids['project']).all() + pending_joins = PendingJoin.objects.filter(id__in=sorted_ids['join']).all() - return make_requests(pending_users, pending_slices, pending_authorities) + return make_requests(pending_users, pending_slices, pending_authorities, pending_projects, pending_joins) def get_requests(authority_hrns=None): - print "get_request_by_authority auth_hrns = ", authority_hrns + print "$$$$$$$$$$$$$ get_request_by_authority auth_hrns = ", authority_hrns if not authority_hrns: ## get those pending users who have confirmed their emails pending_users = PendingUser.objects.filter(status__iexact = 'True') pending_slices = PendingSlice.objects.all() pending_authorities = PendingAuthority.objects.all() + pending_projects = PendingProject.objects.all() + pending_joins = PendingJoin.objects.all() else: pending_users = PendingUser.objects pending_slices = PendingSlice.objects pending_authorities = PendingAuthority.objects + pending_projects = PendingProject.objects + pending_joins = PendingJoin.objects from django.db.models import Q list_user_Q = list() list_slice_Q = list() list_auth_Q = list() + list_proj_Q = list() + list_join_Q = list() for hrn in authority_hrns: list_user_Q.append(Q(authority_hrn__startswith=hrn, status__iexact = 'True')) list_slice_Q.append(Q(authority_hrn__startswith=hrn)) list_auth_Q.append(Q(site_authority__startswith=hrn)) - print "startswith hrn = ",hrn + list_proj_Q.append(Q(authority_hrn__startswith=hrn)) + list_join_Q.append(Q(authority_hrn__startswith=hrn)) from operator import __or__ as OR pending_users = pending_users.filter(reduce(OR, list_user_Q)) pending_slices = pending_slices.filter(reduce(OR, list_slice_Q)) pending_authorities = pending_authorities.filter(reduce(OR, list_auth_Q)) + pending_projects = pending_projects.filter(reduce(OR, list_proj_Q)) + pending_joins = pending_joins.filter(reduce(OR, list_join_Q)) #pending_authorities = pending_authorities.all() #filter(reduce(OR, list_Q)) - return make_requests(pending_users, pending_slices, pending_authorities) + return make_requests(pending_users, pending_slices, pending_authorities, pending_projects, pending_joins) # XXX Is it in sync with the form fields ? @@ -391,6 +520,10 @@ def portal_validate_request(wsgi_request, request_ids): request_status['SFA slice'] = {'status': True } PendingSlice.objects.get(id=request['id']).delete() + # Clear user's Credentials + sfa_user = sfa_get_user(wsgi_request, request['user_hrn']) + clear_user_creds(wsgi_request,sfa_user['user_email']) + except Exception, e: request_status['SFA slice'] = {'status': False, 'description': str(e)} @@ -418,6 +551,43 @@ def portal_validate_request(wsgi_request, request_ids): except Exception, e: request_status['SFA authority'] = {'status': False, 'description': str(e)} + elif request['type'] == 'project': + try: + hrn = request['authority_hrn'] + '.' + request['project_name'] + + # Only hrn is required for Manifold Query + sfa_authority_params = { + 'authority_hrn' : hrn + } + sfa_add_authority(wsgi_request, sfa_authority_params) + request_status['SFA project'] = {'status': True } + PendingProject.objects.get(id=request['id']).delete() + + # Add user as a PI of the project + authority_add_pis(wsgi_request, hrn , request['user_hrn']) + + # Clear user's Credentials + #sfa_user = sfa_get_user(wsgi_request, request['user_hrn']) + clear_user_creds(wsgi_request,request['email']) + + except Exception, e: + request_status['SFA project'] = {'status': False, 'description': str(e)} + + elif request['type'] == 'join': + try: + # Add user as a PI of the project + authority_add_pis(wsgi_request, request['authority_hrn'] , request['user_hrn']) + + request_status['SFA join'] = {'status': True } + PendingJoin.objects.get(id=request['id']).delete() + + # Clear user's Credentials + clear_user_creds(wsgi_request,request['email']) + + except Exception, e: + request_status['SFA join'] = {'status': False, 'description': str(e)+' - '+str(request)} + else: + request_status['other'] = {'status': False, 'description': 'unknown type of request'} # XXX Remove from Pendings in database status['%s__%s' % (request['type'], request['id'])] = request_status @@ -591,6 +761,15 @@ def portal_reject_request(wsgi_request, request_ids): PendingAuthority.objects.get(id=request['id']).delete() + # XXX TMP we should send an email to the user to inform him/her + elif request['type'] == 'project': + request_status['SFA project'] = {'status': True } + PendingProject.objects.get(id=request['id']).delete() + + elif request['type'] == 'join': + request_status['SFA join'] = {'status': True } + PendingJoin.objects.get(id=request['id']).delete() + status['%s__%s' % (request['type'], request['id'])] = request_status return status @@ -621,9 +800,16 @@ def create_slice(wsgi_request, request): # Add User to Slice if we have the user_hrn in pendingslice table user_hrn = request.get('user_hrn', None) user_hrns = list([user_hrn]) if user_hrn else list() - + + # CACHE PB with fields + page = Page(wsgi_request) + metadata = page.get_metadata() + user_md = metadata.details_by_object('user') + user_fields = [column['name'] for column in user_md['column']] + # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 - user_query = Query().get('myslice:user').select('user_hrn','user_email').filter_by('user_hrn','==',user_hrn) + #user_query = Query().get('myslice:user').select('user_hrn','user_email').filter_by('user_hrn','==',user_hrn) + user_query = Query().get('myslice:user').select(user_fields).filter_by('user_hrn','==',user_hrn) user_details_sfa = execute_admin_query(wsgi_request, user_query) if not user_details_sfa: raise Exception, "User %s doesn't exist, validate user before validating slice" % user_hrn @@ -710,6 +896,57 @@ def create_pending_slice(wsgi_request, request, email): except Exception, e: print "Failed to send email, please check the mail templates and the SMTP configuration of your server" + +def create_pending_project(wsgi_request, request): + """ + """ + + # Insert an entry in the PendingProject table + s = PendingProject( + project_name = request['project_name'], + user_hrn = request['user_hrn'], + email = request['email'], + authority_hrn = request['authority_hrn'], + purpose = request['purpose'], + ) + s.save() + +def create_pending_join(wsgi_request, request): + """ + """ + + # Insert an entry in the PendingJoin table + s = PendingJoin( + user_hrn = request['user_hrn'], + email = request['email'], + project_name = request['project_name'], + authority_hrn = request['authority_hrn'], + ) + s.save() + + +# try: +# # Send an email: the recipients are the PI of the authority +# recipients = authority_get_pi_emails(wsgi_request, request['authority_hrn']) +# +# theme.template_name = 'slice_request_email.txt' +# text_content = render_to_string(theme.template, request) +# +# theme.template_name = 'slice_request_email.html' +# html_content = render_to_string(theme.template, request) +# +# theme.template_name = 'slice_request_email_subject.txt' +# subject = render_to_string(theme.template, request) +# subject = subject.replace('\n', '') +# +# sender = email +# msg = EmailMultiAlternatives(subject, text_content, sender, recipients) +# msg.attach_alternative(html_content, "text/html") +# msg.send() +# except Exception, e: +# print "Failed to send email, please check the mail templates and the SMTP configuration of your server" + + #------------------------------------------------------------------------------- # REQUESTS - Users #------------------------------------------------------------------------------- @@ -823,10 +1060,11 @@ def iotlab_create_user (wsgi_request, request, namespace = None, as_admin=False) import requests import time from requests.auth import HTTPBasicAuth - - URL_REST = ConfigEngine.default_iotlab_url - LOGIN_ADMIN = ConfigEngine.default_iotlab_admin_user - PASSWORD_ADMIN = ConfigEngine.default_iotlab_admin_password + + engine = ConfigEngine() + URL_REST = engine.iotlab_url() + LOGIN_ADMIN = engine.iotlab_admin_user() + PASSWORD_ADMIN = engine.iotlab_admin_password() auth = HTTPBasicAuth(LOGIN_ADMIN,PASSWORD_ADMIN) headers = {'content-type': 'application/json'} diff --git a/portal/dashboardview.py b/portal/dashboardview.py index 06f02f83..0de9b93f 100644 --- a/portal/dashboardview.py +++ b/portal/dashboardview.py @@ -49,7 +49,7 @@ class DashboardView (LoginRequiredAutoLogoutView, ThemeView): # else: print "SLICE QUERY" print "-" * 80 - slice_query = Query().get('user').filter_by('user_hrn', '==', '$user_hrn').select('slices.slice_hrn') + slice_query = Query().get('myslice:user').filter_by('user_hrn', '==', '$user_hrn').select('slices.slice_hrn') page.enqueue_query(slice_query) page.enqueue_query(testbed_query) diff --git a/portal/emailactivationview.py b/portal/emailactivationview.py index 533cccfa..4424c2e5 100644 --- a/portal/emailactivationview.py +++ b/portal/emailactivationview.py @@ -29,12 +29,16 @@ class ActivateEmailView(FreeAccessView, ThemeView): if pending_authorities: return False pending_user_email = pending_user.email - query = Query.get('myplcuser').filter_by('email', '==', pending_user_email).select('enabled') - results = execute_admin_query(self.request, query) - for result in results: - # User is enabled in PLE - if 'enabled' in result and result['enabled']==True: - return True + try: + query = Query.get('myplcuser').filter_by('email', '==', pending_user_email).select('enabled') + results = execute_admin_query(self.request, query) + for result in results: + # User is enabled in PLE + if 'enabled' in result and result['enabled']==True: + return True + except Exception, e: + print "Exception in myplc query = ",e + return False def dispatch(self, *args, **kwargs): diff --git a/portal/homeview.py b/portal/homeview.py index 3e9514f3..1fe1d2a3 100644 --- a/portal/homeview.py +++ b/portal/homeview.py @@ -19,6 +19,7 @@ from myslice.configengine import ConfigEngine from myslice.theme import ThemeView from portal.account import Account, get_expiration from portal.models import PendingSlice +from portal.actions import authority_check_pis import json, time import activity.user @@ -89,10 +90,12 @@ class HomeView (FreeAccessView, ThemeView): acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') acc_user_cred = account_config.get('delegated_user_credential','N/A') # assigning values - if acc_auth_cred=={} or acc_auth_cred=='N/A': - pi = "is_not_pi" - else: - pi = "is_pi" + #if acc_auth_cred=={} or acc_auth_cred=='N/A': + # pi = "is_not_pi" + #else: + # pi = "is_pi" + user_email = str(self.request.user) + pi = authority_check_pis(self.request, user_email) # check if the user has creds or not if acc_user_cred == {} or acc_user_cred == 'N/A': @@ -156,11 +159,12 @@ class HomeView (FreeAccessView, ThemeView): acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') acc_user_cred = account_config.get('delegated_user_credential','N/A') # assigning values - if acc_auth_cred=={} or acc_auth_cred=='N/A': - pi = "is_not_pi" - else: - pi = "is_pi" - + #if acc_auth_cred=={} or acc_auth_cred=='N/A': + # pi = "is_not_pi" + #else: + # pi = "is_pi" + user_email = str(self.request.user) + pi = authority_check_pis(self.request, user_email) # check if the user has creds or not if acc_user_cred == {} or acc_user_cred == 'N/A': user_cred = 'no_creds' diff --git a/portal/institution.py b/portal/institution.py index 6dd1b20a..8a004356 100644 --- a/portal/institution.py +++ b/portal/institution.py @@ -5,6 +5,7 @@ from django.template import RequestContext from django.shortcuts import render_to_response from django.shortcuts import render +from unfold.page import Page from unfold.loginrequired import LoginRequiredAutoLogoutView from manifold.core.query import Query @@ -13,7 +14,8 @@ from manifoldapi.manifoldresult import ManifoldResult from ui.topmenu import topmenu_items, the_user from myslice.configengine import ConfigEngine -from myslice.theme import ThemeView +from portal.actions import is_pi, authority_check_pis +from myslice.theme import ThemeView import json class InstitutionView (LoginRequiredAutoLogoutView, ThemeView): @@ -30,50 +32,46 @@ class InstitutionView (LoginRequiredAutoLogoutView, ThemeView): env['theme'] = self.theme return render_to_response(self.template, env, context_instance=RequestContext(request)) - def get (self, request, state=None): + def get (self, request, authority_hrn=None, state=None): env = self.default_env() - if request.user.is_authenticated(): env['person'] = self.request.user - user_query = Query().get('user').select('user_hrn','parent_authority').filter_by('user_hrn','==','$user_hrn') - user_details = execute_query(self.request, user_query) - try: - env['user_details'] = user_details[0] - except Exception,e: - env['error'] = "Please check your Credentials" - - try: - user_local_query = Query().get('local:user').select('config').filter_by('email','==',str(env['person'])) - user_local_details = execute_query(self.request, user_local_query) - user_local = user_local_details[0] - user_local_config = user_local['config'] - user_local_config = json.loads(user_local_config) - user_local_authority = user_local_config.get('authority') - if 'user_details' not in env or 'parent_authority' not in env['user_details'] or env['user_details']['parent_authority'] is None: - env['user_details'] = {'parent_authority': user_local_authority} - except Exception,e: - env['error'] = "Please check your Manifold user config" - ## check user is pi or not - platform_query = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled') - account_query = Query().get('local:account').select('user_id','platform_id','auth_type','config') - platform_details = execute_query(self.request, platform_query) - account_details = execute_query(self.request, account_query) - for platform_detail in platform_details: - for account_detail in account_details: - if platform_detail['platform_id'] == account_detail['platform_id']: - if 'config' in account_detail and account_detail['config'] is not '': - account_config = json.loads(account_detail['config']) - if 'myslice' in platform_detail['platform']: - acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') - # assigning values - if acc_auth_cred == {} or acc_auth_cred == 'N/A': - pi = "is_not_pi" + if authority_hrn is None: + # CACHE PB with fields + page = Page(request) + metadata = page.get_metadata() + user_md = metadata.details_by_object('user') + user_fields = [column['name'] for column in user_md['column']] + + # REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 + user_query = Query().get('myslice:user').select(user_fields).filter_by('user_hrn','==','$user_hrn') + #user_query = Query().get('myslice:user').select('user_hrn','parent_authority').filter_by('user_hrn','==','$user_hrn') + user_details = execute_query(self.request, user_query) + try: + env['user_details'] = user_details[0] + except Exception,e: + # If the Query fails, check in local DB + try: + user_local_query = Query().get('local:user').select('config').filter_by('email','==',str(env['person'])) + user_local_details = execute_query(self.request, user_local_query) + user_local = user_local_details[0] + user_local_config = user_local['config'] + user_local_config = json.loads(user_local_config) + user_local_authority = user_local_config.get('authority') + if 'user_details' not in env or 'parent_authority' not in env['user_details'] or env['user_details']['parent_authority'] is None: + env['user_details'] = {'parent_authority': user_local_authority} + except Exception,e: + env['error'] = "Please check your Credentials" else: - pi = "is_pi" + env['project'] = True + env['user_details'] = {'parent_authority': authority_hrn} else: env['person'] = None - + print "BEFORE ####------#### is_pi" + pi = is_pi(self.request, '$user_hrn', env['user_details']['parent_authority']) + print "is_pi = ",is_pi + env['theme'] = self.theme env['section'] = "Institution" env['pi'] = pi diff --git a/portal/managementtababout.py b/portal/managementtababout.py index 8bd1cdc1..ec39f8fc 100644 --- a/portal/managementtababout.py +++ b/portal/managementtababout.py @@ -21,23 +21,22 @@ class ManagementAboutView (FreeAccessView, ThemeView): def get (self, request): + authority_contacts = {} + authority = {'authority_hrn':'fed4fire.upmc'} if request.user.is_authenticated(): - user_query = Query().get('user').select('user_hrn','parent_authority').filter_by('user_hrn','==','$user_hrn') - user_details = execute_query(self.request, user_query) - user_local_query = Query().get('local:user').select('config').filter_by('email','==',str(self.request.user)) user_local_details = execute_query(self.request, user_local_query) user_authority = json.loads(user_local_details[0]['config']).get('authority') + print "**************________ management about = ",user_authority # XXX Should be done using Metadata # select column.name from local:object where table=='authority' authority_query = Query().get('authority').select('authority_hrn', 'name', 'address', 'enabled','description', 'scientific', 'city', 'name', 'url', 'country', 'enabled', 'longitude', - 'tech', 'latitude', 'pi_users', 'parent_authority', 'onelab_membership', + 'tech', 'latitude', 'pi_users', 'onelab_membership', 'postcode').filter_by('authority_hrn','==',user_authority) authority_details = execute_query(self.request, authority_query) if authority_details : - authority_contacts = {} authority = authority_details[0] if 'scientific' in authority and authority['scientific'] is not None: authority_contacts['scientific'] = [ x.strip()[1:-1] for x in authority['scientific'][1:-1].split(',') ] diff --git a/portal/managementtabrequests.py b/portal/managementtabrequests.py index 7c0b222e..2c80e41f 100644 --- a/portal/managementtabrequests.py +++ b/portal/managementtabrequests.py @@ -7,6 +7,8 @@ from manifoldapi.manifoldapi import execute_query from django.views.generic.base import TemplateView from unfold.loginrequired import LoginRequiredView +from unfold.page import Page + from django.http import HttpResponse from django.shortcuts import render @@ -85,9 +87,15 @@ class ManagementRequestsView (LoginRequiredView, ThemeView): for authority_hrn, credential in config['delegated_authority_credentials'].items(): credential_authorities.add(authority_hrn) + # CACHE PB with fields + page = Page(self.request) + metadata = page.get_metadata() + user_md = metadata.details_by_object('user') + user_fields = [column['name'] for column in user_md['column']] + # ** Where am I a PI ** # For this we need to ask SFA (of all authorities) = PI function - pi_authorities_query = Query.get('user').filter_by('user_hrn', '==', '$user_hrn').select('pi_authorities') + pi_authorities_query = Query.get('myslice:user').filter_by('user_hrn', '==', '$user_hrn').select(user_fields) pi_authorities_tmp = execute_query(self.request, pi_authorities_query) pi_authorities = set() try: diff --git a/portal/manageuserview.py b/portal/manageuserview.py index 52a69daf..1be4e822 100644 --- a/portal/manageuserview.py +++ b/portal/manageuserview.py @@ -2,7 +2,8 @@ from unfold.loginrequired import LoginRequiredAutoLogoutView # from manifold.core.query import Query from manifoldapi.manifoldapi import execute_query, execute_admin_query -from portal.actions import manifold_update_user, manifold_update_account, manifold_add_account, manifold_delete_account, sfa_update_user +from portal.actions import manifold_update_user, manifold_update_account, manifold_add_account, manifold_delete_account +from portal.actions import sfa_update_user, authority_get_pis, authority_add_pis, authority_remove_pis,authority_check_pis ,clear_user_creds # from unfold.page import Page from ui.topmenu import topmenu_items_live, the_user @@ -52,6 +53,8 @@ class UserView(LoginRequiredAutoLogoutView, ThemeView): #email = user_detail['email'] if user_detail['config']: config = json.loads(user_detail['config']) + authority_hrn = config.get('authority','Unknown Authority') + platform_query = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled') account_query = Query().get('local:account').filter_by('user_id', '==', user_id).select('user_id','platform_id','auth_type','config') @@ -101,11 +104,15 @@ class UserView(LoginRequiredAutoLogoutView, ThemeView): account_usr_hrn = account_config.get('user_hrn','N/A') account_pub_key = account_config.get('user_public_key','N/A') account_reference = account_config.get ('reference_platform','N/A') + # credentials of myslice platform if 'myslice' in platform_detail['platform']: acc_user_cred = account_config.get('delegated_user_credential','N/A') acc_slice_cred = account_config.get('delegated_slice_credentials','N/A') acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') + #usr_hrn of myslice platform. used to check pi or no + account_usr_hrn_myslice = account_config.get('user_hrn','N/A') + if 'N/A' not in acc_user_cred: exp_date = re.search('(.*)', acc_user_cred) @@ -140,8 +147,7 @@ class UserView(LoginRequiredAutoLogoutView, ThemeView): my_auths = [{'auth_name': t[0], 'cred_exp': t[1]} for t in zip(auth_list, auth_cred_exp_list)] - - + # for reference accounts if 'reference' in account_detail['auth_type']: account_type = 'Reference' @@ -184,6 +190,9 @@ class UserView(LoginRequiredAutoLogoutView, ThemeView): platform_list = [{'platform_no_access': t[0]} for t in itertools.izip_longest(total_platform_list)] + ## check pi or no + pi_status = authority_check_pis(self.request, user_email) + context = super(UserView, self).get_context_data(**kwargs) context['principal_acc'] = principal_acc_list context['ref_acc'] = ref_acc_list @@ -198,6 +207,7 @@ class UserView(LoginRequiredAutoLogoutView, ThemeView): context['fullname'] = context['firstname'] +' '+ context['lastname'] context['authority'] = config.get('authority',"Unknown Authority") context['user_private_key'] = account_priv_key + context['pi'] = pi_status # XXX This is repeated in all pages # more general variables expected in the template @@ -462,27 +472,51 @@ def user_process(request, **kwargs): #clear all creds elif 'clear_cred' in request.POST: + clear_user_creds(request, user_email) + messages.success(request, 'All Credentials cleared') + return HttpResponseRedirect(redirect_url) + + #make a user PI + elif 'makepi' in request.POST: + # getting user's authority_hrn + config={} + for user_config in user_details: + if user_config['config']: + user_config = json.loads(user_config['config']) + authority_hrn = user_config.get('authority','Unknown Authority') + + #getting user_hrn for account_detail in account_details: for platform_detail in platform_details: if platform_detail['platform_id'] == account_detail['platform_id']: if 'myslice' in platform_detail['platform']: account_config = json.loads(account_detail['config']) - user_cred = account_config.get('delegated_user_credential','N/A') - if 'N/A' not in user_cred: - user_hrn = account_config.get('user_hrn','N/A') - user_pub_key = json.dumps(account_config.get('user_public_key','N/A')) - user_priv_key = json.dumps(account_config.get('user_private_key','N/A')) - updated_config = '{"user_public_key":'+ user_pub_key + ', "user_private_key":'+ user_priv_key + ', "user_hrn":"'+ user_hrn + '"}' - user_params = { 'config': updated_config} - manifold_update_account(request, user_id,user_params) - messages.success(request, 'All Credentials cleared') - return HttpResponseRedirect(redirect_url) - else: - messages.error(request, 'Delete error: Credentials are not stored in the server') - return HttpResponseRedirect(redirect_url) - else: - messages.error(request, 'Account error: You need an account in myslice platform to perform this action') - return HttpResponseRedirect(redirect_url) + user_hrn = account_config.get('user_hrn','N/A') + + authority_add_pis(request, authority_hrn, user_hrn) + clear_user_creds(request, user_email) + messages.success(request, 'User upgraded to PI') + return HttpResponseRedirect(redirect_url) + + elif 'removepi' in request.POST: + # getting user's authority_hrn + config={} + for user_config in user_details: + if user_config['config']: + user_config = json.loads(user_config['config']) + authority_hrn = user_config.get('authority','Unknown Authority') + #getting user_hrn + for account_detail in account_details: + for platform_detail in platform_details: + if platform_detail['platform_id'] == account_detail['platform_id']: + if 'myslice' in platform_detail['platform']: + account_config = json.loads(account_detail['config']) + user_hrn = account_config.get('user_hrn','N/A') + authority_remove_pis(request, authority_hrn, user_hrn) + clear_user_creds(request, user_email) + messages.success(request, 'PI downgraded to user') + return HttpResponseRedirect(redirect_url) + # Download delegated_user_cred diff --git a/portal/migrations/0010_project.py b/portal/migrations/0010_project.py new file mode 100644 index 00000000..70462042 --- /dev/null +++ b/portal/migrations/0010_project.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'PendingProject' + db.create_table(u'portal_pendingproject', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('project_name', self.gf('django.db.models.fields.TextField')()), + ('user_hrn', self.gf('django.db.models.fields.TextField')()), + ('authority_hrn', self.gf('django.db.models.fields.TextField')(null=True)), + ('purpose', self.gf('django.db.models.fields.TextField')(default='NA')), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal(u'portal', ['PendingProject']) + + + def backwards(self, orm): + # Deleting model 'PendingProject' + db.delete_table(u'portal_pendingproject') + + + models = { + u'portal.institution': { + 'Meta': {'object_name': 'Institution'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingauthority': { + 'Meta': {'object_name': 'PendingAuthority'}, + 'address_city': ('django.db.models.fields.TextField', [], {}), + 'address_country': ('django.db.models.fields.TextField', [], {}), + 'address_line1': ('django.db.models.fields.TextField', [], {}), + 'address_line2': ('django.db.models.fields.TextField', [], {}), + 'address_line3': ('django.db.models.fields.TextField', [], {}), + 'address_postalcode': ('django.db.models.fields.TextField', [], {}), + 'address_state': ('django.db.models.fields.TextField', [], {}), + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'site_abbreviated_name': ('django.db.models.fields.TextField', [], {}), + 'site_authority': ('django.db.models.fields.TextField', [], {}), + 'site_latitude': ('django.db.models.fields.TextField', [], {}), + 'site_longitude': ('django.db.models.fields.TextField', [], {}), + 'site_name': ('django.db.models.fields.TextField', [], {}), + 'site_url': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingproject': { + 'Meta': {'object_name': 'PendingProject'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project_name': ('django.db.models.fields.TextField', [], {}), + 'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingslice': { + 'Meta': {'object_name': 'PendingSlice'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number_of_nodes': ('django.db.models.fields.TextField', [], {'default': '0'}), + 'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'slice_name': ('django.db.models.fields.TextField', [], {}), + 'type_of_nodes': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendinguser': { + 'Meta': {'object_name': 'PendingUser'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'email_hash': ('django.db.models.fields.TextField', [], {}), + 'first_name': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.TextField', [], {}), + 'login': ('django.db.models.fields.TextField', [], {}), + 'password': ('django.db.models.fields.TextField', [], {}), + 'pi': ('django.db.models.fields.TextField', [], {}), + 'private_key': ('django.db.models.fields.TextField', [], {}), + 'public_key': ('django.db.models.fields.TextField', [], {}), + 'status': ('django.db.models.fields.TextField', [], {}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + } + } + + complete_apps = ['portal'] \ No newline at end of file diff --git a/portal/migrations/0011_join.py b/portal/migrations/0011_join.py new file mode 100644 index 00000000..f4032897 --- /dev/null +++ b/portal/migrations/0011_join.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + # Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..." + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + u'portal.institution': { + 'Meta': {'object_name': 'Institution'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingauthority': { + 'Meta': {'object_name': 'PendingAuthority'}, + 'address_city': ('django.db.models.fields.TextField', [], {}), + 'address_country': ('django.db.models.fields.TextField', [], {}), + 'address_line1': ('django.db.models.fields.TextField', [], {}), + 'address_line2': ('django.db.models.fields.TextField', [], {}), + 'address_line3': ('django.db.models.fields.TextField', [], {}), + 'address_postalcode': ('django.db.models.fields.TextField', [], {}), + 'address_state': ('django.db.models.fields.TextField', [], {}), + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'site_abbreviated_name': ('django.db.models.fields.TextField', [], {}), + 'site_authority': ('django.db.models.fields.TextField', [], {}), + 'site_latitude': ('django.db.models.fields.TextField', [], {}), + 'site_longitude': ('django.db.models.fields.TextField', [], {}), + 'site_name': ('django.db.models.fields.TextField', [], {}), + 'site_url': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingjoin': { + 'Meta': {'object_name': 'PendingJoin'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project_name': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingproject': { + 'Meta': {'object_name': 'PendingProject'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project_name': ('django.db.models.fields.TextField', [], {}), + 'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingslice': { + 'Meta': {'object_name': 'PendingSlice'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number_of_nodes': ('django.db.models.fields.TextField', [], {'default': '0'}), + 'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'slice_name': ('django.db.models.fields.TextField', [], {}), + 'type_of_nodes': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendinguser': { + 'Meta': {'object_name': 'PendingUser'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'email_hash': ('django.db.models.fields.TextField', [], {}), + 'first_name': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.TextField', [], {}), + 'login': ('django.db.models.fields.TextField', [], {}), + 'password': ('django.db.models.fields.TextField', [], {}), + 'pi': ('django.db.models.fields.TextField', [], {}), + 'private_key': ('django.db.models.fields.TextField', [], {}), + 'public_key': ('django.db.models.fields.TextField', [], {}), + 'status': ('django.db.models.fields.TextField', [], {}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + } + } + + complete_apps = ['portal'] + symmetrical = True diff --git a/portal/migrations/0012_initial.py b/portal/migrations/0012_initial.py new file mode 100644 index 00000000..9b58fae3 --- /dev/null +++ b/portal/migrations/0012_initial.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Institution' + db.create_table(u'portal_institution', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal(u'portal', ['Institution']) + + # Adding model 'PendingUser' + db.create_table(u'portal_pendinguser', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('first_name', self.gf('django.db.models.fields.TextField')()), + ('last_name', self.gf('django.db.models.fields.TextField')()), + ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)), + ('password', self.gf('django.db.models.fields.TextField')()), + ('user_hrn', self.gf('django.db.models.fields.TextField')()), + ('public_key', self.gf('django.db.models.fields.TextField')()), + ('private_key', self.gf('django.db.models.fields.TextField')()), + ('authority_hrn', self.gf('django.db.models.fields.TextField')()), + ('login', self.gf('django.db.models.fields.TextField')()), + ('pi', self.gf('django.db.models.fields.TextField')()), + ('email_hash', self.gf('django.db.models.fields.TextField')()), + ('status', self.gf('django.db.models.fields.TextField')()), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal(u'portal', ['PendingUser']) + + # Adding model 'PendingAuthority' + db.create_table(u'portal_pendingauthority', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('site_name', self.gf('django.db.models.fields.TextField')()), + ('site_authority', self.gf('django.db.models.fields.TextField')()), + ('site_abbreviated_name', self.gf('django.db.models.fields.TextField')()), + ('site_url', self.gf('django.db.models.fields.TextField')()), + ('site_latitude', self.gf('django.db.models.fields.TextField')()), + ('site_longitude', self.gf('django.db.models.fields.TextField')()), + ('address_line1', self.gf('django.db.models.fields.TextField')()), + ('address_line2', self.gf('django.db.models.fields.TextField')()), + ('address_line3', self.gf('django.db.models.fields.TextField')()), + ('address_city', self.gf('django.db.models.fields.TextField')()), + ('address_postalcode', self.gf('django.db.models.fields.TextField')()), + ('address_state', self.gf('django.db.models.fields.TextField')()), + ('address_country', self.gf('django.db.models.fields.TextField')()), + ('authority_hrn', self.gf('django.db.models.fields.TextField')()), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal(u'portal', ['PendingAuthority']) + + # Adding model 'PendingSlice' + db.create_table(u'portal_pendingslice', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('slice_name', self.gf('django.db.models.fields.TextField')()), + ('user_hrn', self.gf('django.db.models.fields.TextField')()), + ('authority_hrn', self.gf('django.db.models.fields.TextField')(null=True)), + ('number_of_nodes', self.gf('django.db.models.fields.TextField')(default=0)), + ('type_of_nodes', self.gf('django.db.models.fields.TextField')(default='NA')), + ('purpose', self.gf('django.db.models.fields.TextField')(default='NA')), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal(u'portal', ['PendingSlice']) + + # Adding model 'PendingProject' + db.create_table(u'portal_pendingproject', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('project_name', self.gf('django.db.models.fields.TextField')()), + ('user_hrn', self.gf('django.db.models.fields.TextField')()), + ('email', self.gf('django.db.models.fields.TextField')()), + ('authority_hrn', self.gf('django.db.models.fields.TextField')(null=True)), + ('purpose', self.gf('django.db.models.fields.TextField')(default='NA')), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal(u'portal', ['PendingProject']) + + # Adding model 'PendingJoin' + db.create_table(u'portal_pendingjoin', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user_hrn', self.gf('django.db.models.fields.TextField')()), + ('email', self.gf('django.db.models.fields.TextField')()), + ('project_name', self.gf('django.db.models.fields.TextField')(null=True)), + ('authority_hrn', self.gf('django.db.models.fields.TextField')()), + ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + )) + db.send_create_signal(u'portal', ['PendingJoin']) + + + def backwards(self, orm): + # Deleting model 'Institution' + db.delete_table(u'portal_institution') + + # Deleting model 'PendingUser' + db.delete_table(u'portal_pendinguser') + + # Deleting model 'PendingAuthority' + db.delete_table(u'portal_pendingauthority') + + # Deleting model 'PendingSlice' + db.delete_table(u'portal_pendingslice') + + # Deleting model 'PendingProject' + db.delete_table(u'portal_pendingproject') + + # Deleting model 'PendingJoin' + db.delete_table(u'portal_pendingjoin') + + + models = { + u'portal.institution': { + 'Meta': {'object_name': 'Institution'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingauthority': { + 'Meta': {'object_name': 'PendingAuthority'}, + 'address_city': ('django.db.models.fields.TextField', [], {}), + 'address_country': ('django.db.models.fields.TextField', [], {}), + 'address_line1': ('django.db.models.fields.TextField', [], {}), + 'address_line2': ('django.db.models.fields.TextField', [], {}), + 'address_line3': ('django.db.models.fields.TextField', [], {}), + 'address_postalcode': ('django.db.models.fields.TextField', [], {}), + 'address_state': ('django.db.models.fields.TextField', [], {}), + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'site_abbreviated_name': ('django.db.models.fields.TextField', [], {}), + 'site_authority': ('django.db.models.fields.TextField', [], {}), + 'site_latitude': ('django.db.models.fields.TextField', [], {}), + 'site_longitude': ('django.db.models.fields.TextField', [], {}), + 'site_name': ('django.db.models.fields.TextField', [], {}), + 'site_url': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingjoin': { + 'Meta': {'object_name': 'PendingJoin'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project_name': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingproject': { + 'Meta': {'object_name': 'PendingProject'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project_name': ('django.db.models.fields.TextField', [], {}), + 'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendingslice': { + 'Meta': {'object_name': 'PendingSlice'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number_of_nodes': ('django.db.models.fields.TextField', [], {'default': '0'}), + 'purpose': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'slice_name': ('django.db.models.fields.TextField', [], {}), + 'type_of_nodes': ('django.db.models.fields.TextField', [], {'default': "'NA'"}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + }, + u'portal.pendinguser': { + 'Meta': {'object_name': 'PendingUser'}, + 'authority_hrn': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'email_hash': ('django.db.models.fields.TextField', [], {}), + 'first_name': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.TextField', [], {}), + 'login': ('django.db.models.fields.TextField', [], {}), + 'password': ('django.db.models.fields.TextField', [], {}), + 'pi': ('django.db.models.fields.TextField', [], {}), + 'private_key': ('django.db.models.fields.TextField', [], {}), + 'public_key': ('django.db.models.fields.TextField', [], {}), + 'status': ('django.db.models.fields.TextField', [], {}), + 'user_hrn': ('django.db.models.fields.TextField', [], {}) + } + } + + complete_apps = ['portal'] \ No newline at end of file diff --git a/portal/models.py b/portal/models.py index cc484b5b..f578d934 100644 --- a/portal/models.py +++ b/portal/models.py @@ -96,3 +96,18 @@ class PendingSlice(models.Model): type_of_nodes = models.TextField(default='NA') purpose = models.TextField(default='NA') created = models.DateTimeField(auto_now_add = True) + +class PendingProject(models.Model): + project_name = models.TextField() + user_hrn = models.TextField() + email = models.TextField() + authority_hrn = models.TextField(null=True) + purpose = models.TextField(default='NA') + created = models.DateTimeField(auto_now_add = True) + +class PendingJoin(models.Model): + user_hrn = models.TextField() + email = models.TextField() + project_name = models.TextField(null=True) + authority_hrn = models.TextField() + created = models.DateTimeField(auto_now_add = True) diff --git a/portal/projectrequestview.py b/portal/projectrequestview.py new file mode 100644 index 00000000..8a484282 --- /dev/null +++ b/portal/projectrequestview.py @@ -0,0 +1,320 @@ +from django.shortcuts import render +from django.contrib.sites.models import Site + +from manifold.core.query import Query +from manifoldapi.manifoldapi import execute_admin_query, execute_query + +from unfold.loginrequired import LoginRequiredAutoLogoutView + +from portal.actions import create_pending_project, create_pending_join, sfa_add_authority, authority_add_pis, is_pi +from portal.models import PendingProject, PendingJoin + +from myslice.theme import ThemeView + +import json, time, re + +class ProjectRequestView(LoginRequiredAutoLogoutView, ThemeView): + template_name = 'projectrequest_view.html' + + def getAuthorities(self, request): + authorities_query = Query.get('authority').select('name', 'authority_hrn') + authorities = execute_admin_query(request, authorities_query) + if authorities is not None: + authorities = sorted(authorities, key=lambda k: k['authority_hrn']) + authorities = sorted(authorities, key=lambda k: k['name']) + return authorities + + def getUserAuthority(self, request): + # Get user_email (XXX Would deserve to be simplified) + user_query = Query().get('local:user').select('email','config') + user_details = execute_query(request, user_query) + for user_detail in user_details: + user_config = json.loads(user_detail['config']) + user_authority = user_config.get('authority','N/A') + return user_authority + + def getUserHrn(self, request): + user_hrn = None + + account_query = Query().get('local:account').select('user_id','platform_id','auth_type','config') + account_details = execute_query(request, account_query) + + platform_query = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled') + platform_details = execute_query(request, platform_query) + + # getting user_hrn from local:account + for account_detail in account_details: + for platform_detail in platform_details: + if platform_detail['platform_id'] == account_detail['platform_id']: + # taking user_hrn only from myslice account + # NOTE: we should later handle accounts filter_by auth_type= managed OR user + if 'myslice' in platform_detail['platform']: + account_config = json.loads(account_detail['config']) + user_hrn = account_config.get('user_hrn','N/A') + return user_hrn + + def getUserEmail(self, request): + # Get user_email (XXX Would deserve to be simplified) + user_query = Query().get('local:user').select('email','config') + user_details = execute_query(request, user_query) + user_email = user_details[0].get('email') + return user_email + + def post(self, request): + return self.handle_request(request, 'POST') + + def get(self, request): + return self.handle_request(request, 'GET') + + def handle_request(self, wsgi_request, method): + errors = [] + authority_hrn = None + authority_name = None + + #errors.append(wsgi_request.POST) + + user_hrn = self.getUserHrn(wsgi_request) + + user_email = self.getUserEmail(wsgi_request) + + authorities = self.getAuthorities(wsgi_request) + + user_authority = self.getUserAuthority(wsgi_request) + + # getting the org from authority + for authority in authorities: + if authority['authority_hrn'] == user_authority: + authority_name = authority['name'] + + if method == 'POST' : + + if 'join' in wsgi_request.POST: + post = { + 'user_hrn' : user_hrn, + 'email' : user_email, + 'project_name' : wsgi_request.POST.get('project_name', ''), + 'authority_hrn' : wsgi_request.POST.get('project_name', ''), + } + + else: + post = { + 'user_hrn' : user_hrn, + 'email' : user_email, + 'authority_hrn' : wsgi_request.POST.get('authority_name', ''), + 'project_name' : wsgi_request.POST.get('project_name', ''), + 'purpose' : wsgi_request.POST.get('purpose', ''), + } + + if (post['authority_hrn'] is None or post['authority_hrn'] == ''): + errors.append('Organization is mandatory') + + if (post['purpose'] is None or post['purpose'] == ''): + errors.append('Project purpose is mandatory') + + if (re.search(r'^[A-Za-z0-9_]*$', post['project_name']) == None): + errors.append('Project name may contain only letters, numbers, and underscore.') + + # What kind of project name is valid? + if (post['project_name'] is None or post['project_name'] == ''): + errors.append('Project name is mandatory') + + if not errors: + print "is_pi on auth_hrn = ", user_authority + if is_pi(wsgi_request, user_hrn, user_authority): + # PIs can directly create/join project in their own authority... + if 'join' in wsgi_request.POST: + authority_add_pis(wsgi_request, post['project_name'], user_hrn) + else: + hrn = post['authority_hrn'] + '.' + post['project_name'] + sfa_add_authority(wsgi_request, {'authority_hrn':hrn}) + authority_add_pis(wsgi_request, hrn, user_hrn) + self.template_name = 'project-request-done-view.html' + else: + # Otherwise a wsgi_request is sent to the PI + if 'join' in wsgi_request.POST: + create_pending_join(wsgi_request, post) + else: + create_pending_project(wsgi_request, post) + self.template_name = 'project-request-ack-view.html' + + # retrieves the pending projects creation list + pending_projects = PendingProject.objects.all().filter(user_hrn=user_hrn) + # retrieves the pending join a project list + pending_join_projects = PendingJoin.objects.all().filter(user_hrn=user_hrn) + + root_authority = user_authority.split('.', 1)[0] + env = { + 'errors': errors, + 'username': wsgi_request.user, + 'theme': self.theme, + 'authorities': authorities, + 'authority_hrn': user_authority, + 'root_authority_hrn': root_authority, + 'pending_projects': pending_projects, + 'pending_join_projects': pending_join_projects, + } + return render(wsgi_request, self.template, env) + + + + """ + """ + errors = [] + slice_name ='' + purpose='' + url='' + authority_hrn = None + authority_name = None + # Retrieve the list of authorities + authorities_query = Query.get('authority').select('name', 'authority_hrn') + authorities = execute_admin_query(wsgi_request, authorities_query) + if authorities is not None: + authorities = sorted(authorities, key=lambda k: k['authority_hrn']) + authorities = sorted(authorities, key=lambda k: k['name']) + + # Get user_email (XXX Would deserve to be simplified) + user_query = Query().get('local:user').select('email','config') + user_details = execute_query(wsgi_request, user_query) + user_email = user_details[0].get('email') + # getting user_hrn + for user_detail in user_details: + user_config = json.loads(user_detail['config']) + user_authority = user_config.get('authority','N/A') + # getting the org from authority + for authority in authorities: + if authority['authority_hrn'] == user_authority: + authority_name = authority['name'] + + # Handle the case when we use only hrn and not name + if authority_name is None: + authority_name = user_authority + # + account_query = Query().get('local:account').select('user_id','platform_id','auth_type','config') + account_details = execute_query(wsgi_request, account_query) + # + platform_query = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled') + platform_details = execute_query(wsgi_request, platform_query) + + user_hrn = None + # getting user_hrn from local:account + for account_detail in account_details: + for platform_detail in platform_details: + if platform_detail['platform_id'] == account_detail['platform_id']: + # taking user_hrn only from myslice account + # NOTE: we should later handle accounts filter_by auth_type= managed OR user + if 'myslice' in platform_detail['platform']: + account_config = json.loads(account_detail['config']) + user_hrn = account_config.get('user_hrn','N/A') + acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') + + + # checking if pi or not + if acc_auth_cred == {} or acc_auth_cred == 'N/A': + pi = "is_not_pi" + else: + pi = "is_pi" + + + # Page rendering +# page = Page(wsgi_request) +# page.add_js_files ( [ "js/jquery.validate.js", "js/jquery-ui.js" ] ) +# page.add_css_files ( [ "https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" ] ) +# page.expose_js_metadata() + + if method == 'POST': + # The form has been submitted + + # get the domain url +# current_site = Site.objects.get_current() +# current_site = current_site.domain + + # getting the authority_hrn from the selected organization + for authority in authorities: + if authority['name'] == wsgi_request.POST.get('org_name', ''): + authority_hrn = authority['authority_hrn'] + + # Handle the case when we use only hrn and not name + if authority_hrn is None: + authority_hrn = wsgi_request.POST.get('org_name', '') + + slice_request = { + 'type' : 'slice', + 'id' : None, + 'user_hrn' : user_hrn, + 'email' : user_email, + 'timestamp' : time.time(), + 'authority_hrn' : authority_hrn, + 'organization' : wsgi_request.POST.get('org_name', ''), + 'slice_name' : wsgi_request.POST.get('slice_name', ''), + 'url' : wsgi_request.POST.get('url', ''), + 'purpose' : wsgi_request.POST.get('purpose', ''), + 'current_site' : current_site + } + + # create slice_hrn based on authority_hrn and slice_name +# slice_name = slice_request['slice_name'] + req_slice_hrn = authority_hrn + '.' + slice_name + # comparing requested slice_hrn with the existing slice_hrn + slice_query = Query().get('myslice:slice').select('slice_hrn','parent_authority').filter_by('parent_authority','==',authority_hrn) + slice_details_sfa = execute_admin_query(wsgi_request, slice_query) + for _slice in slice_details_sfa: + if _slice['slice_hrn'] == req_slice_hrn: + errors.append('Slice already exists. Please use a different slice name.') + + + # What kind of slice name is valid? + if (slice_name is None or slice_name == ''): + errors.append('Slice name is mandatory') + + if (re.search(r'^[A-Za-z0-9_]*$', slice_name) == None): + errors.append('Slice name may contain only letters, numbers, and underscore.') + + organization = slice_request['organization'] + if (organization is None or organization == ''): + errors.append('Organization is mandatory') + + + + purpose = slice_request['purpose'] + if (purpose is None or purpose == ''): + errors.append('Experiment purpose is mandatory') + + url = slice_request['url'] + + if not errors: + if is_pi(wsgi_request, user_hrn, authority_hrn): + # PIs can directly create slices in their own authority... + create_slice(wsgi_request, slice_request) + clear_user_creds(wsgi_request, user_email) + self.template_name = 'slice-request-done-view.html' + else: + # Otherwise a wsgi_request is sent to the PI + create_pending_slice(wsgi_request, slice_request, user_email) + self.template_name = 'slice-request-ack-view.html' + + # log user activity + activity.user.slice(wsgi_request) + + return render(wsgi_request, self.template, {'theme': self.theme}) # Redirect after POST + else: + slice_request = {} + + template_env = { + 'username': wsgi_request.user.email, + 'errors': errors, + 'slice_name': slice_name, + 'purpose': purpose, + 'email': user_email, + 'user_hrn': user_hrn, + 'url': url, + 'pi': pi, + 'authority_name': authority_name, + 'authority_hrn': user_authority, + 'cc_myself': True, + 'authorities': authorities, + 'theme': self.theme, + 'section': "Slice request" + } + template_env.update(slice_request) + template_env.update(page.prelude_env()) + return render(wsgi_request, self.template, template_env) diff --git a/portal/registrationview.py b/portal/registrationview.py index 8749f537..c54bcab2 100644 --- a/portal/registrationview.py +++ b/portal/registrationview.py @@ -45,6 +45,7 @@ class RegistrationView (FreeAccessView, ThemeView): authorities_query = Query.get('authority').select('name', 'authority_hrn') authorities = execute_admin_query(wsgi_request, authorities_query) if authorities is not None: + authorities = sorted(authorities, key=lambda k: k['authority_hrn']) authorities = sorted(authorities, key=lambda k: k['name']) print "############ BREAKPOINT 1 #################" @@ -138,7 +139,7 @@ class RegistrationView (FreeAccessView, ThemeView): # sqlite3 /var/unfold/unfold.sqlite3 # select email from auth_user; if UserModel._default_manager.filter(email__iexact = user_request['email']): - errors.append('Contact OneLab support or try with another email.') + errors.append('Contact support or try with another email.') # XXX TODO: Factorize with portal/accountview.py # XXX TODO: Factorize with portal/registrationview.py diff --git a/portal/slicerequestview.py b/portal/slicerequestview.py index e6683bd4..a880d391 100644 --- a/portal/slicerequestview.py +++ b/portal/slicerequestview.py @@ -7,7 +7,7 @@ from unfold.page import Page from manifold.core.query import Query from manifoldapi.manifoldapi import execute_admin_query, execute_query -from portal.actions import is_pi, create_slice, create_pending_slice, clear_user_creds +from portal.actions import is_pi, create_slice, create_pending_slice, clear_user_creds, authority_check_pis #from portal.forms import SliceRequestForm from unfold.loginrequired import LoginRequiredAutoLogoutView from ui.topmenu import topmenu_items_live, the_user @@ -42,7 +42,8 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView): authorities_query = Query.get('authority').select('name', 'authority_hrn') authorities = execute_admin_query(wsgi_request, authorities_query) if authorities is not None: - authorities = sorted(authorities) + authorities = sorted(authorities, key=lambda k: k['authority_hrn']) + authorities = sorted(authorities, key=lambda k: k['name']) # Get user_email (XXX Would deserve to be simplified) user_query = Query().get('local:user').select('email','config') @@ -60,14 +61,14 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView): # Handle the case when we use only hrn and not name if authority_name is None: authority_name = user_authority - # + account_query = Query().get('local:account').select('user_id','platform_id','auth_type','config') account_details = execute_query(wsgi_request, account_query) - # + platform_query = Query().get('local:platform').select('platform_id','platform','gateway_type','disabled') platform_details = execute_query(wsgi_request, platform_query) user_hrn = None - # getting user_hrn from local:account + #getting user_hrn from local:account for account_detail in account_details: for platform_detail in platform_details: if platform_detail['platform_id'] == account_detail['platform_id']: @@ -76,20 +77,22 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView): if 'myslice' in platform_detail['platform']: account_config = json.loads(account_detail['config']) user_hrn = account_config.get('user_hrn','N/A') - acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') + # acc_auth_cred = account_config.get('delegated_authority_credentials','N/A') # checking if pi or not - if acc_auth_cred == {} or acc_auth_cred == 'N/A': - pi = "is_not_pi" - else: - pi = "is_pi" + #if acc_auth_cred == {} or acc_auth_cred == 'N/A': + # pi = "is_not_pi" + #else: + # pi = "is_pi" + pi = authority_check_pis (wsgi_request, user_email) + print "SLICEREQUESTVIEW.PY ----- pi=",pi # Page rendering page = Page(wsgi_request) page.add_js_files ( [ "js/jquery.validate.js", "js/jquery-ui.js" ] ) - page.add_css_files ( [ "https://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" ] ) + page.add_css_files ( [ "css/jquery-ui.css" ] ) page.expose_js_metadata() if method == 'POST': @@ -108,6 +111,11 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView): if authority_hrn is None: authority_hrn = wsgi_request.POST.get('org_name', '') + # Handle project if used + project = wsgi_request.POST.get('project', None) + if project is not None and project != '': + authority_hrn = project + slice_request = { 'type' : 'slice', 'id' : None, @@ -126,7 +134,7 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView): slice_name = slice_request['slice_name'] req_slice_hrn = authority_hrn + '.' + slice_name # comparing requested slice_hrn with the existing slice_hrn - slice_query = Query().get('slice').select('slice_hrn','parent_authority').filter_by('parent_authority','==',authority_hrn) + slice_query = Query().get('myslice:slice').select('slice_hrn','parent_authority').filter_by('parent_authority','==',authority_hrn) slice_details_sfa = execute_admin_query(wsgi_request, slice_query) for _slice in slice_details_sfa: if _slice['slice_hrn'] == req_slice_hrn: diff --git a/portal/sliceresourceview.py b/portal/sliceresourceview.py index 3f0c3b47..c3943453 100644 --- a/portal/sliceresourceview.py +++ b/portal/sliceresourceview.py @@ -60,7 +60,7 @@ class SliceResourceView (LoginRequiredView, ThemeView): user_fields = ['user_hrn'] # [column['name'] for column in user_md['column']] query_resource_all = Query.get('resource').select(resource_fields) - page.enqueue_query(query_resource_all) + #page.enqueue_query(query_resource_all) # leases query #lease_md = metadata.details_by_object('lease') diff --git a/portal/slicetabexperiment.py b/portal/slicetabexperiment.py index ca942315..324ee0a9 100644 --- a/portal/slicetabexperiment.py +++ b/portal/slicetabexperiment.py @@ -17,6 +17,8 @@ from myslice.configengine import ConfigEngine from myslice.theme import ThemeView from myslice.configengine import ConfigEngine +from sfa.planetlab.plxrn import hash_loginbase + import urllib2,json class ExperimentView (FreeAccessView, ThemeView): @@ -26,12 +28,14 @@ class ExperimentView (FreeAccessView, ThemeView): username = self.request.user - split_slicename = slicename.split('.') - ple_slicename = split_slicename[0] + '8' + split_slicename[1] + '_' + split_slicename[2] - - query_current_resources = Query.get('slice').select('resource').filter_by('slice_hrn','==',slicename) + query_current_resources = Query.get('slice').select('resource','parent_authority').filter_by('slice_hrn','==',slicename) current_resources = execute_query(request, query_current_resources) + parent_authority = current_resources[0]['parent_authority'] + + split_slicename = slicename.split('.') + ple_slicename = hash_loginbase(parent_authority) + '_' + split_slicename[-1] + ple_resource_list=[] nitos_resource_list=[] nitos_paris_resource_list=[] diff --git a/portal/static/css/account_view.css b/portal/static/css/account_view.css index bcb29506..f1ba3d82 100644 --- a/portal/static/css/account_view.css +++ b/portal/static/css/account_view.css @@ -128,6 +128,15 @@ table.profile form{ margin-right: 15px; } + +#makepi { + position:relative; + width: 215px; + float: right; + margin-right: 15px; + +} + #edit_password table { display:inline; margin: auto; diff --git a/portal/static/css/fed4fire.css b/portal/static/css/fed4fire.css index 49771c2f..70de2a8f 100644 --- a/portal/static/css/fed4fire.css +++ b/portal/static/css/fed4fire.css @@ -13,10 +13,9 @@ a, a:active, a:focus { h1 { border-bottom:1px solid #DDDDDD; - padding:0 0 0 0; - margin:0 0 0 0; + padding:0; + margin:25px 0; font-size:14pt; - margin-top: 12px; } h1 img { vertical-align:middle; @@ -31,6 +30,23 @@ h3 { font-size:13pt; color:#201E62; } +input[type=checkbox] { + min-width:5px !important; + margin-left:0 !important; +} +input[type=text], input[type=password], input[type=email], input[type=tel], input[type=number] { + min-width:260px; + padding:6px; + border:1pt solid #CCCCCC; + vertical-align:bottom; + border-radius:0; +} + +textarea { + padding:6px; + border:1pt solid #CCCCCC !important; + border-radius:0 !important; +} div.wrapper { width:980px; @@ -44,7 +60,7 @@ div.wrapper { div.wide { - margin:25px auto; + margin:0 auto 25px auto; padding:0 25px; } @@ -70,13 +86,10 @@ div#header { } div#secondary { - } div#secondary ul { - position:absolute; - top:20px; - right:0; + float:right; } div#secondary li { @@ -100,6 +113,7 @@ div#navigation { background-color:orange; width:100%; height:40px; + text-align: center; } div#navigation div.wrapper { text-align:center; @@ -115,21 +129,26 @@ div#navigation ul { div#navigation li { color:white; font-family:helvetica, sans-serif; - font-size:10pt ; - font-weight:normal; + font-size:9pt ; + font-weight:bold; line-height:0.8em; letter-spacing:0.6pt; list-style:none; float:left; padding:0; - margin:15px 25px 0 0; + margin:16px 25px 0 0; } div#navigation li a { - color:white; + color:#003333; +} +div#navigation .iconlogout { + margin-top:-10px; + margin-right:2px; + color:#003333; } div#navigation li a:hover { text-decoration:none; - color:#B8B2FF; + color:white; } div#navigation li:last-child { margin-right:0; @@ -337,6 +356,7 @@ ul.nav-section li a { ul.nav-section li:first-child { padding:0; } +/* it used to give space for the icon, which has been removed ul.nav-section li:first-child a { font-weight:bold; padding:6px 15px 4px 15px; @@ -344,6 +364,8 @@ ul.nav-section li:first-child a { ul.nav-section li:first-child.active a { padding:6px 15px 3px 15px; } + +*/ ul.nav-section li:first-child img { margin:0 4px 1px 0; padding:0; @@ -363,37 +385,54 @@ ul.nav-resources a { div#slice-view { margin:0; } -div.list-group-item { - border:0; - background-color:white; - font-weight:bold; - padding-left:0; + +/* FACILITY/TESTBED filters */ +div.sl-filter-facilities { + padding:0 5px; } -a.list-group-item { - border:0; - background-color:white; - padding:3px 0 3px 10px; - border-left:2pt white solid; +div.sl-filter-facilities h4 { + margin-bottom:15px; + } -a.list-group-item.active, a.list-group-item.active:hover { - font-weight: bold; +img.sl-image { + margin:0 5px 5px 0; + padding:0; + vertical-align:middle; +} +a.sl-facility { + color:gray; + text-decoration:none; +} +a.sl-facility:hover { + color:#342961; +} +a.sl-facility.active { color:black; - background-color:transparent; - border-left:2pt blue solid; + text-decoration:none; } -a.list-group-item.active:hover { - background-color:#dddddd; +a.sl-facility::before { + content: " "; } -a.list-group-item:hover { - border-left:2pt blue solid; +div.sl-facilities { + border-bottom:#CCCCCC 1px solid; + padding-bottom:15px; + margin-bottom:15px; } -a.list-group-item p.list-group-item-text { - font-size:9pt; - font-style:italic; - font-weight: normal; - color: black !important; +div.sl-facilities:last-child { + border:0; +} +a.sl-testbed { + color:gray; + text-decoration:none; + margin-left:25px; + margin-bottom:5px; +} +a.sl-testbed:hover { + color:#342961; +} +a.sl-testbed.active { + color:black; } - div#slice-info { margin-top:25px; } @@ -483,12 +522,51 @@ div.dataTables_filter label{ width:400px; } +div.sl-filter-resources { + margin:10px 0; + text-align:center; +} +span.sl-resources { + font-size:9pt; + color:gray; +} +a.sl-resources { + font-size:9pt; + border:0; + padding:2px 8px; + margin:0 5px; + -moz-border-radius: 2px; + border-radius: 2px; + text-align: center; +} +a.sl-resources.active, a.sl-resources.active:hover, a.sl-resources.active:focus { + border:0; + padding:2px 8px; + -moz-border-radius: 2px; + border-radius: 2px; + background-color:#FFA500; + color:#000000; +} +a.sl-resources:first-child { +} +button.btn-apply { + background-color:#FFA500; + border-bottom: 2pt solid #FFCA00; + color:black; + font-size:13px; + padding:2px 8px; + margin:0 5px; + -moz-border-radius: 2px; + border-radius: 2px; + text-align: center; +} + /**/ .header { -moz-box-shadow: 0 0 1px rgba(82,82,82,0.6); -webkit-box-shadow: 0 0 1px rgba(82,82,82,0.6); box-shadow: 0 0 1px rgba(82,82,82,0.6); - height:61px; + height:60#px; background-color:white; } div.navigation { @@ -588,7 +666,11 @@ div.dataTables_filter label{ float:left; width:400px; } - +div.breadcrumbs { + margin:15px 0; + color:gray; + font-size:10pt; +} /* Service Directory */ div#appservices div.row { diff --git a/portal/static/css/jquery-ui.css b/portal/static/css/jquery-ui.css new file mode 100644 index 00000000..ec69f79b --- /dev/null +++ b/portal/static/css/jquery-ui.css @@ -0,0 +1,1225 @@ +/*! jQuery UI - v1.11.2 - 2014-10-16 +* http://jqueryui.com +* Includes: core.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, draggable.css, menu.css, progressbar.css, resizable.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin: 2px 0 0 0; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ + font-size: 100%; +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 45%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} +.ui-dialog { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 20px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-draggable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-menu { + list-style: none; + padding: 0; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + position: absolute; +} +.ui-menu .ui-menu-item { + position: relative; + margin: 0; + padding: 3px 1em 3px .4em; + cursor: pointer; + min-height: 0; /* support: IE7 */ + /* support: IE10, see #8844 */ + list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); +} +.ui-menu .ui-menu-divider { + margin: 5px 0; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-state-focus, +.ui-menu .ui-state-active { + margin: -1px; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item { + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: 0; + bottom: 0; + left: .2em; + margin: auto 0; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + left: auto; + right: 0; +} +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw=="); + height: 100%; + filter: alpha(opacity=25); /* support: IE8 */ + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-selectable { + -ms-touch-action: none; + touch-action: none; +} +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} +.ui-selectmenu-menu { + padding: 0; + margin: 0; + position: absolute; + top: 0; + left: 0; + display: none; +} +.ui-selectmenu-menu .ui-menu { + overflow: auto; + /* Support: IE7 */ + overflow-x: hidden; + padding-bottom: 1px; +} +.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { + font-size: 1em; + font-weight: bold; + line-height: 1.5; + padding: 2px 0.4em; + margin: 0.5em 0 0 0; + height: auto; + border: 0; +} +.ui-selectmenu-open { + display: block; +} +.ui-selectmenu-button { + display: inline-block; + overflow: hidden; + position: relative; + text-decoration: none; + cursor: pointer; +} +.ui-selectmenu-button span.ui-icon { + right: 0.5em; + left: auto; + margin-top: -8px; + position: absolute; + top: 50%; +} +.ui-selectmenu-button span.ui-selectmenu-text { + text-align: left; + padding: 0.4em 2.1em 0.4em 1em; + display: block; + line-height: 1.4; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; + -ms-touch-action: none; + touch-action: none; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* support: IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} +.ui-sortable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to override default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertically center icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom-width: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav .ui-tabs-anchor { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { + cursor: text; +} +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Verdana,Arial,sans-serif; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #aaaaaa; + background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x; + color: #222222; +} +.ui-widget-content a { + color: #222222; +} +.ui-widget-header { + border: 1px solid #aaaaaa; + background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; + color: #222222; + font-weight: bold; +} +.ui-widget-header a { + color: #222222; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #d3d3d3; + background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #555555; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #555555; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #999999; + background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #212121; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited { + color: #212121; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #aaaaaa; + background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #212121; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #212121; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fcefa1; + background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; + color: #363636; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; + color: #cd0a0a; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #cd0a0a; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #cd0a0a; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); /* support: IE8 */ + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); /* support: IE8 */ + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-widget-header .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-state-default .ui-icon { + background-image: url("images/ui-icons_888888_256x240.png"); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url("images/ui-icons_454545_256x240.png"); +} +.ui-state-active .ui-icon { + background-image: url("images/ui-icons_454545_256x240.png"); +} +.ui-state-highlight .ui-icon { + background-image: url("images/ui-icons_2e83ff_256x240.png"); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url("images/ui-icons_cd0a0a_256x240.png"); +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; + opacity: .3; + filter: Alpha(Opacity=30); /* support: IE8 */ +} +.ui-widget-shadow { + margin: -8px 0 0 -8px; + padding: 8px; + background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; + opacity: .3; + filter: Alpha(Opacity=30); /* support: IE8 */ + border-radius: 8px; +} diff --git a/portal/static/css/onelab.css b/portal/static/css/onelab.css index 27c6b47a..45f5efcf 100644 --- a/portal/static/css/onelab.css +++ b/portal/static/css/onelab.css @@ -82,6 +82,10 @@ span.version { font-size:8pt; color:#888888; } +input[type=checkbox] { + min-width:5px !important; + margin-left:0 !important; +} input[type=text], input[type=password], input[type=email], input[type=tel], input[type=number], select, option { min-width:260px; padding:6px; @@ -378,71 +382,87 @@ div.container-resource, div.container-slice { div#slice-view { margin:0; } -div.list-group-item { - border:0; - -moz-border-radius: 0; - border-radius: 0; - background-color:white; - font-weight:bold; - padding-left:0; + +div.sl-filter-facilities { + padding:0 5px; } -a.list-group-item { - -moz-border-radius: 0; - border-radius: 0; - border:0; - background-color:white; - padding:3px 2px 3px 10px; - border-left:2pt white solid; +div.sl-filter-facilities h4 { + margin-bottom:15px; + +} +img.sl-image { + margin:0 5px 5px 0; + padding:0; + vertical-align:middle; +} +a.sl-facility { + color:gray; + text-decoration:none; } -a.list-group-item.active, a.list-group-item.active:hover, a.list-group-item.active:focus { - -moz-border-radius: 0; - border-radius: 0; - font-weight: bold; +a.sl-facility:hover { + color:#342961; +} +a.sl-facility.active { color:black; - background-color:#F5F5F5; - border-left:2pt blue solid; + text-decoration:none; } - -a.list-group-item:hover { - -moz-border-radius: 0; - border-radius: 0; - border-left:2pt blue solid; +a.sl-facility::before { + content: " "; } -a.list-group-item p.list-group-item-text { - -moz-border-radius: 0; - border-radius: 0; - font-size:9pt; - font-style:italic; - font-weight: normal; - color: black !important; +div.sl-facilities { + border-bottom:#CCCCCC 1px solid; + padding-bottom:15px; + margin-bottom:15px; +} +div.sl-facilities:last-child { + border:0; +} +a.sl-testbed { + color:gray; + text-decoration:none; + margin-left:25px; + margin-bottom:5px; +} +a.sl-testbed:hover { + color:#342961; +} +a.sl-testbed.active { + color:black; +} +div.sl-filter-resources { + margin:0 0 10px 0; + text-align:center; } - span.sl-resources { font-size:9pt; color:gray; } -a.sl-resources, a.sl-resources:hover { +a.sl-resources { font-size:9pt; border:0; - padding:2px 4px; - -moz-border-radius: 4px; - border-radius: 4px; - width:105px; - margin-left:4px; - margin-bottom:8px; + padding:2px 8px; + margin:0 5px; + -moz-border-radius: 2px; + border-radius: 2px; text-align: center; } a.sl-resources.active, a.sl-resources.active:hover, a.sl-resources.active:focus { border:0; - -moz-border-radius: 4px; - border-radius: 4px; + padding:2px 8px; + -moz-border-radius: 2px; + border-radius: 2px; + background-color:#E7A6E7; + color:#000000; } a.sl-resources:first-child { - margin-left:12px; } button.btn-apply { font-size:13px; padding:2px 8px; + margin:0 5px; + -moz-border-radius: 2px; + border-radius: 2px; + text-align: center; } div#slice-info { margin-top:25px; diff --git a/portal/static/css/smartfire.css b/portal/static/css/smartfire.css index 190924e4..46271241 100644 --- a/portal/static/css/smartfire.css +++ b/portal/static/css/smartfire.css @@ -13,7 +13,7 @@ body { a, a:active, a:focus { outline: 0; text-decoration:none; - color:#760073; + color:#FF4400; } a:hover { color:#0D0049; @@ -82,6 +82,10 @@ span.version { font-size:8pt; color:#888888; } +input[type=checkbox] { + min-width:5px !important; + margin-left:0 !important; +} input[type=text], input[type=password], input[type=email], input[type=tel], input[type=number], select, option { min-width:260px; padding:6px; @@ -174,14 +178,14 @@ button.btn-danger:active { } button.btn-onelab, input.btn-onelab { border:0; - border-bottom:3px solid #760073; - background-color:#302562; + border-bottom:3px solid #FFA315; + background-color:#FFA315; color:white; } button.btn-onelab:hover, input.btn-onelab:hover { border:0; - border-bottom:3px solid #760073; - background-color:#302562; + border-bottom:3px solid #FF7E00; + background-color:#FF7E00; color:white; } button.btn-onelab:active, input.btn-onelab:active { @@ -378,43 +382,53 @@ div.container-resource, div.container-slice { div#slice-view { margin:0; } -div.list-group-item { - border:0; - -moz-border-radius: 0; - border-radius: 0; - background-color:white; - font-weight:bold; - padding-left:0; + +/* FACILITY/TESTBED filters */ +div.sl-filter-facilities { + padding:0 5px; } -a.list-group-item { - -moz-border-radius: 0; - border-radius: 0; - border:0; - background-color:white; - padding:3px 2px 3px 10px; - border-left:2pt white solid; +div.sl-filter-facilities h4 { + margin-bottom:15px; + +} +img.sl-image { + margin:0 5px 5px 0; + padding:0; + vertical-align:middle; +} +a.sl-facility { + color:gray; + text-decoration:none; +} +a.sl-facility:hover { + color:#342961; } -a.list-group-item.active, a.list-group-item.active:hover, a.list-group-item.active:focus { - -moz-border-radius: 0; - border-radius: 0; - font-weight: bold; +a.sl-facility.active { color:black; - background-color:#F5F5F5; - border-left:2pt blue solid; + text-decoration:none; } - -a.list-group-item:hover { - -moz-border-radius: 0; - border-radius: 0; - border-left:2pt blue solid; +a.sl-facility::before { + content: " "; } -a.list-group-item p.list-group-item-text { - -moz-border-radius: 0; - border-radius: 0; - font-size:9pt; - font-style:italic; - font-weight: normal; - color: black !important; +div.sl-facilities { + border-bottom:#CCCCCC 1px solid; + padding-bottom:15px; + margin-bottom:15px; +} +div.sl-facilities:last-child { + border:0; +} +a.sl-testbed { + color:gray; + text-decoration:none; + margin-left:25px; + margin-bottom:5px; +} +a.sl-testbed:hover { + color:#342961; +} +a.sl-testbed.active { + color:black; } span.sl-resources { @@ -576,7 +590,7 @@ div.navigation li a { color:#0C0047; } div.navigation li a:hover, div.navigation li a.current { - color:#760073; + color:#FF4400; text-decoration:none; } @@ -715,7 +729,7 @@ div.home { line-height:1.2em; letter-spacing:0.3pt; min-height:500px; - //background-image: url('../img/bg-experiment.png'); + background-image: url('../img/bg-smartfire.png'); background-repeat:no-repeat; background-size:cover; background-position:center top; @@ -798,7 +812,6 @@ div.registration-form { div.slogan { text-align:center; color:white; - padding-top:60px; text-shadow: 1px 1px #013540; } diff --git a/portal/static/icons/iotlab.png b/portal/static/icons/iotlab.png new file mode 100644 index 00000000..56a8e564 Binary files /dev/null and b/portal/static/icons/iotlab.png differ diff --git a/portal/static/icons/mobile.png b/portal/static/icons/mobile.png new file mode 100644 index 00000000..81ee438b Binary files /dev/null and b/portal/static/icons/mobile.png differ diff --git a/portal/static/icons/planetlab.png b/portal/static/icons/planetlab.png new file mode 100644 index 00000000..a74082a9 Binary files /dev/null and b/portal/static/icons/planetlab.png differ diff --git a/portal/static/icons/wireless.png b/portal/static/icons/wireless.png new file mode 100644 index 00000000..4db0aa37 Binary files /dev/null and b/portal/static/icons/wireless.png differ diff --git a/portal/static/img/bg-smartfire.png b/portal/static/img/bg-smartfire.png new file mode 100644 index 00000000..f5340fc4 Binary files /dev/null and b/portal/static/img/bg-smartfire.png differ diff --git a/portal/static/js/institution.js b/portal/static/js/institution.js index 2b2cfa3d..e8d5121c 100644 --- a/portal/static/js/institution.js +++ b/portal/static/js/institution.js @@ -36,11 +36,13 @@ $(document).ready(function() { /* TODO: factorize into functions */ $('button#deleteslices').click(function() { + var flag = false; $('input:checkbox.slice').each(function (index) { if(this.checked){ var record_id = this.id; $.post("/delete/slice/",{'filters':{'slice_hrn':this.id}}, function(data) { if(data.success){ + localStorage.clear(); $('tr[id="'+record_id+'"]').fadeOut("slow"); $('tr[id="'+record_id+'"]').remove(); mysliceAlert('Success: slice deleted','success', true); @@ -76,10 +78,34 @@ $(document).ready(function() { // TODO: refresh table //window.location="/portal/institution#slices"; }); + $('button#deleteprojects').click(function() { + var flag = false; + $('input:checkbox.project').each(function (index) { + if(this.checked){ + var record_id = this.id; + console.log(record_id); + $.post("/delete/myslice:authority/",{'filters':{'authority_hrn':this.id}}, function(data) { + if(data.success){ + localStorage.clear(); + $('tr[id="'+record_id+'"]').fadeOut("slow"); + $('tr[id="'+record_id+'"]').remove(); + mysliceAlert('Success: project deleted','success', true); + }else{ + mysliceAlert('Rest Error for: '+data.error,'warning', true); + //alert("Rest Error for "+record_id+": "+data.error); + } + }); + } + }); + }); $('button#createslice').click(function() { window.location="/portal/slice_request/"; }); + + $('button#createproject').click(function() { + window.location="/portal/project_request/"; + }); $('button#slicerequestbtn').click(function() { /* window.location="/portal/slice_request/"; diff --git a/portal/static/js/myslice.js b/portal/static/js/myslice.js index 1ccc1c1a..548d5dab 100644 --- a/portal/static/js/myslice.js +++ b/portal/static/js/myslice.js @@ -1,6 +1,10 @@ /* * MySlice Class */ +function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; +} function list() { this.elements = []; @@ -114,6 +118,20 @@ var myslice = { } return this.user; }, + projects: {}, + + projects: function() { + if ($.isEmptyObject(this.projects)) { + //this.login(function() { return this.user; }); + if(localStorage.getItem('projects')!='undefined'){ + this.projects = JSON.parse(localStorage.getItem('projects')); + }else{ + return false; + } + } + return this.projects; + }, + loadSlices: function(slices) { if (typeof(slices) == "undefined"){ @@ -154,12 +172,46 @@ var myslice = { if($.isEmptyObject(user)){ // REGISTRY ONLY TO BE REMOVED WITH MANIFOLD-V2 $.post("/rest/myslice:user/",{'filters':{'user_hrn':'$user_hrn'}}, function( data ) { - //myslice.user = new user(data[0]); - localStorage.setItem('user', JSON.stringify(data[0])); - myslice.loadSlices(data[0].slices); + if (data.length > 0) { + localStorage.setItem('user', JSON.stringify(data[0])); + projects = []; + $.each(data[0].pi_authorities, function(idx, auth) { + // PI on projects + if(auth.split('.').length>2){ + if($.inArray(auth,projects) == -1){ + projects.push(auth); + } + }else if (auth.split('.').length>1){ + // PI on authorities + // What are the projects under this authority? + $.post("/rest/myslice:authority/",{'fields':['authority_hrn'],'filters':{'authority_hrn':'CONTAINS'+auth}}, function( data ) { + $.each(data, function(idx, project) { + console.log(project.authority_hrn); + if($.inArray(project.authority_hrn,projects) == -1){ + projects.push(project.authority_hrn); + } + }); + }); + }else{ + console.log("admin account - we don't list all from root"); + } + }); + localStorage.setItem('projects', JSON.stringify(projects)); + myslice.loadSlices(data[0].slices); + if(isFunction(fn)){ + fn(); + } + } }); + }else{ + if(isFunction(fn)){ + fn(); + } } + }, + + getSlices: function(name) { }, diff --git a/portal/templates/_widget-no_credentials.html b/portal/templates/_widget-no_credentials.html new file mode 100644 index 00000000..ab07ce7f --- /dev/null +++ b/portal/templates/_widget-no_credentials.html @@ -0,0 +1,66 @@ + + + + + diff --git a/portal/templates/_widget-slice-sections.html b/portal/templates/_widget-slice-sections.html index 175b09c0..0856bd89 100644 --- a/portal/templates/_widget-slice-sections.html +++ b/portal/templates/_widget-slice-sections.html @@ -4,7 +4,7 @@
  • Testbeds
  • Resources
  • Users
  • - +
  • Statistics
  • Measurements
  • Experiment
  • SLA
  • @@ -15,7 +15,7 @@
  • Testbeds
  • Resources
  • Users
  • - +
  • Statistics
  • Measurements
  • Experiment
  • SLA
  • diff --git a/portal/templates/_widget-topmenu.html b/portal/templates/_widget-topmenu.html index 6236344a..668c3d32 100644 --- a/portal/templates/_widget-topmenu.html +++ b/portal/templates/_widget-topmenu.html @@ -1,51 +1,49 @@ - -