Merge branch 'auditlog'

* auditlog: (38 commits)
  Move auditlog migration to 0028 for main branch merge
  Search.js now supports up to 3 search filters on a page. First search widget can be regular or an object selector, which is used on the activity stream widget. Activity stream now has 2 filter widgets allowing a search to combine object and responsible user in a single query.
  AC-640 Added additional help text explaining why an organization is required on a project.
  Added 'working' spinner to all save/delete/select actions. This is to compensate for places where activity log is slowing down the API. AC-646 changes Rackspace credentials to show API Key in place of password. Fixed a bug on machine credential that caused username to not be passed to the API when adding and editing.
  AC-611 Latest activity widget features. Tweaked current filter, but still does not include an object type. Added activty stream to Organization tab.
  Add a field to the activity stream model that more closely identifies the api object type for aid in api querying
  Add some initial activity stream tests
  Improve loading time for some module calls by reducing the number of queries against the activity stream table
  Implement AC-634 - Additional bits
  First attempt at accessing /activity_stream. Modified /list/Stream.js to match the API and to start providing some of the translation we'll need.
  Add job to the list of models that are filtered if a user didn't directly interface with it
  Make sure we are handling multiple activitystream instances from the middleware
  Fix a bug passing args to object 2 of the activitystream serializer
  Found and fixed another bug on the Credentials form. Changing the ssh password value was causing a hidden password field to become invalid. Form definition for ssh password cleared the incorrect associated confirmation field.
  Add activitystream table migration
  AC-625 while looking into issue surrounding non-running updates, discoverd that creating credentials from the Users and Teams tabs was not allowing the user to click Save. The Save button refused to become enabled. Turns out that when a user or team is preelected (using route params) form initialization routine needs to run and it was not.
  Make sure we show relative object information in the api
  Integrate a simple api list and detail display
  Use process_response to attach user information later in the middleware request processing
  Fix up some bugs with signal handling related to association/disassociation
  ...
This commit is contained in:
Matthew Jones
2013-11-18 09:22:38 -05:00
45 changed files with 1854 additions and 321 deletions

View File

@@ -28,7 +28,7 @@ from awx.main.utils import *
# FIXME: machinery for auto-adding audit trail logs to all CREATE/EDITS # FIXME: machinery for auto-adding audit trail logs to all CREATE/EDITS
__all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'ListCreateAPIView', __all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'SimpleListAPIView', 'ListCreateAPIView',
'SubListAPIView', 'SubListCreateAPIView', 'RetrieveAPIView', 'SubListAPIView', 'SubListCreateAPIView', 'RetrieveAPIView',
'RetrieveUpdateAPIView', 'RetrieveUpdateDestroyAPIView'] 'RetrieveUpdateAPIView', 'RetrieveUpdateDestroyAPIView']
@@ -172,6 +172,9 @@ class GenericAPIView(generics.GenericAPIView, APIView):
ret['search_fields'] = self.search_fields ret['search_fields'] = self.search_fields
return ret return ret
class SimpleListAPIView(generics.ListAPIView, GenericAPIView):
pass
class ListAPIView(generics.ListAPIView, GenericAPIView): class ListAPIView(generics.ListAPIView, GenericAPIView):
# Base class for a read-only list view. # Base class for a read-only list view.

View File

@@ -6,6 +6,7 @@ import json
import re import re
import socket import socket
import urlparse import urlparse
import logging
# PyYAML # PyYAML
import yaml import yaml
@@ -27,7 +28,9 @@ from rest_framework import serializers
# AWX # AWX
from awx.main.models import * from awx.main.models import *
from awx.main.utils import update_scm_url from awx.main.utils import update_scm_url, camelcase_to_underscore
logger = logging.getLogger('awx.api.serializers')
BASE_FIELDS = ('id', 'url', 'related', 'summary_fields', 'created', 'modified', BASE_FIELDS = ('id', 'url', 'related', 'summary_fields', 'created', 'modified',
'name', 'description') 'name', 'description')
@@ -1032,6 +1035,58 @@ class JobEventSerializer(BaseSerializer):
pass pass
return d return d
class ActivityStreamSerializer(BaseSerializer):
class Meta:
model = ActivityStream
fields = ('id', 'url', 'related', 'summary_fields', 'timestamp', 'operation', 'changes',
'object1_id', 'object1', 'object1_type', 'object2_id', 'object2', 'object2_type', 'object_relationship_type')
def get_related(self, obj):
if obj is None:
return {}
rel = {}
if obj.user is not None:
rel['user'] = reverse('api:user_detail', args=(obj.user.pk,))
obj1_resolution = camelcase_to_underscore(obj.object1_type.split(".")[-1])
rel['object1'] = reverse('api:' + obj1_resolution + '_detail', args=(obj.object1_id,))
if obj.operation in ('associate', 'disassociate'):
obj2_resolution = camelcase_to_underscore(obj.object2_type.split(".")[-1])
rel['object2'] = reverse('api:' + obj2_resolution + '_detail', args=(obj.object2_id,))
return rel
def get_summary_fields(self, obj):
if obj is None:
return {}
d = super(ActivityStreamSerializer, self).get_summary_fields(obj)
try:
short_obj_type = obj.object1_type.split(".")[-1]
under_short_obj_type = camelcase_to_underscore(short_obj_type)
obj1 = eval(obj.object1_type + ".objects.get(id=" + str(obj.object1_id) + ")")
if hasattr(obj1, "name"):
d['object1'] = {'name': obj1.name, 'description': obj1.description,
'base': under_short_obj_type, 'id': obj.object1_id}
else:
d['object1'] = {'base': under_short_obj_type, 'id': obj.object1_id}
if under_short_obj_type == "host" or under_short_obj_type == "group":
d['inventory'] = {'name': obj1.inventory.name, 'id': obj1.inventory.id}
except Exception, e:
logger.error("Error getting object 1 summary: " + str(e))
try:
short_obj_type = obj.object2_type.split(".")[-1]
under_short_obj_type = camelcase_to_underscore(short_obj_type)
if obj.operation in ('associate', 'disassociate'):
obj2 = eval(obj.object2_type + ".objects.get(id=" + str(obj.object2_id) + ")")
if hasattr(obj2, "name"):
d['object2'] = {'name': obj2.name, 'description': obj2.description,
'base': under_short_obj_type, 'id': obj.object2_id}
else:
d['object2'] = {'base': under_short_obj_type, 'id': obj.object2_id}
if under_short_obj_type == "host" or under_short_obj_type == "group":
d['inventory'] = {'name': obj2.inventory.name, 'id': obj2.inventory.id}
except Exception, e:
pass
return d
class AuthTokenSerializer(serializers.Serializer): class AuthTokenSerializer(serializers.Serializer):

View File

@@ -141,6 +141,11 @@ job_event_urls = patterns('awx.api.views',
url(r'^(?P<pk>[0-9]+)/hosts/$', 'job_event_hosts_list'), url(r'^(?P<pk>[0-9]+)/hosts/$', 'job_event_hosts_list'),
) )
activity_stream_urls = patterns('awx.api.views',
url(r'^$', 'activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/$', 'activity_stream_detail'),
)
v1_urls = patterns('awx.api.views', v1_urls = patterns('awx.api.views',
url(r'^$', 'api_v1_root_view'), url(r'^$', 'api_v1_root_view'),
url(r'^config/$', 'api_v1_config_view'), url(r'^config/$', 'api_v1_config_view'),
@@ -162,6 +167,7 @@ v1_urls = patterns('awx.api.views',
url(r'^jobs/', include(job_urls)), url(r'^jobs/', include(job_urls)),
url(r'^job_host_summaries/', include(job_host_summary_urls)), url(r'^job_host_summaries/', include(job_host_summary_urls)),
url(r'^job_events/', include(job_event_urls)), url(r'^job_events/', include(job_event_urls)),
url(r'^activity_stream/', include(activity_stream_urls)),
) )
urlpatterns = patterns('awx.api.views', urlpatterns = patterns('awx.api.views',

View File

@@ -88,6 +88,7 @@ class ApiV1RootView(APIView):
data['hosts'] = reverse('api:host_list') data['hosts'] = reverse('api:host_list')
data['job_templates'] = reverse('api:job_template_list') data['job_templates'] = reverse('api:job_template_list')
data['jobs'] = reverse('api:job_list') data['jobs'] = reverse('api:job_list')
data['activity_stream'] = reverse('api:activity_stream_list')
return Response(data) return Response(data)
class ApiV1ConfigView(APIView): class ApiV1ConfigView(APIView):
@@ -1055,6 +1056,15 @@ class JobJobEventsList(BaseJobEventsList):
headers=headers) headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ActivityStreamList(SimpleListAPIView):
model = ActivityStream
serializer_class = ActivityStreamSerializer
class ActivityStreamDetail(RetrieveAPIView):
model = ActivityStream
serializer_class = ActivityStreamSerializer
# Create view functions for all of the class-based views to simplify inclusion # Create view functions for all of the class-based views to simplify inclusion
# in URL patterns and reverse URL lookups, converting CamelCase names to # in URL patterns and reverse URL lookups, converting CamelCase names to

50
awx/main/middleware.py Normal file
View File

@@ -0,0 +1,50 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.signals import pre_save, post_save
from django.utils.functional import curry
from awx.main.models.activity_stream import ActivityStream
import json
import uuid
import urllib2
class ActivityStreamMiddleware(object):
def process_request(self, request):
self.isActivityStreamEvent = False
if hasattr(request, 'user') and hasattr(request.user, 'is_authenticated') and request.user.is_authenticated():
user = request.user
else:
user = None
self.instances = []
set_actor = curry(self.set_actor, user)
self.disp_uid = str(uuid.uuid1())
self.finished = False
post_save.connect(set_actor, sender=ActivityStream, dispatch_uid=self.disp_uid, weak=False)
def process_response(self, request, response):
post_save.disconnect(dispatch_uid=self.disp_uid)
self.finished = True
if self.isActivityStreamEvent:
for instance in self.instances:
if "current_user" in request.COOKIES and "id" in request.COOKIES["current_user"]:
userInfo = json.loads(urllib2.unquote(request.COOKIES['current_user']).decode('utf8'))
userActual = User.objects.get(id=int(userInfo['id']))
instance.user = userActual
instance.save()
else:
obj1_type_actual = instance.object1_type.split(".")[-1]
if obj1_type_actual in ("InventoryUpdate", "ProjectUpdate", "JobEvent", "Job") and instance.id is not None:
instance.delete()
return response
def set_actor(self, user, sender, instance, **kwargs):
if not self.finished:
if sender == ActivityStream:
if isinstance(user, User) and instance.user is None:
instance.user = user
else:
self.isActivityStreamEvent = True
self.instances.append(instance)
else:
self.isActivityStreamEvent = False

View File

@@ -0,0 +1,425 @@
# -*- 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 'ActivityStream'
db.create_table(u'main_activitystream', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='activity_stream', null=True, on_delete=models.SET_NULL, to=orm['auth.User'])),
('operation', self.gf('django.db.models.fields.CharField')(max_length=13)),
('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('changes', self.gf('django.db.models.fields.TextField')(blank=True)),
('object1_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
('object1', self.gf('django.db.models.fields.TextField')()),
('object1_type', self.gf('django.db.models.fields.TextField')()),
('object2_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, db_index=True)),
('object2', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
('object2_type', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
('object_relationship_type', self.gf('django.db.models.fields.TextField')(blank=True)),
))
db.send_create_signal('main', ['ActivityStream'])
def backwards(self, orm):
# Deleting model 'ActivityStream'
db.delete_table(u'main_activitystream')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'main.activitystream': {
'Meta': {'object_name': 'ActivityStream'},
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object1': ('django.db.models.fields.TextField', [], {}),
'object1_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'object1_type': ('django.db.models.fields.TextField', [], {}),
'object2': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'object2_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}),
'object2_type': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
},
'main.authtoken': {
'Meta': {'object_name': 'AuthToken'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
},
'main.credential': {
'Meta': {'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
},
'main.group': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.InventorySource']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.host': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'hosts'", 'blank': 'True', 'to': "orm['main.InventorySource']"}),
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Job']", 'blank': 'True', 'null': 'True'}),
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.inventory': {
'Meta': {'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
},
'main.inventorysource': {
'Meta': {'object_name': 'InventorySource'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventorysource\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Credential']"}),
'current_update': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_source_as_current_update+'", 'null': 'True', 'to': "orm['main.InventoryUpdate']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'group': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'inventory_source'", 'null': 'True', 'default': 'None', 'to': "orm['main.Group']", 'blank': 'True', 'unique': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
'last_update': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_source_as_last_update+'", 'null': 'True', 'to': "orm['main.InventoryUpdate']"}),
'last_update_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventorysource\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'none'", 'max_length': '32', 'null': 'True'}),
'update_interval': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'main.inventoryupdate': {
'Meta': {'object_name': 'InventoryUpdate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventoryupdate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventoryupdate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'result_stdout': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'})
},
'main.job': {
'Meta': {'object_name': 'Job'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'job\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'jobs'", 'blank': 'True', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'job\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'playbook': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
'result_stdout': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.jobevent': {
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now_add': 'True', 'blank': 'True'}),
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events_as_primary_host'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Host']", 'blank': 'True', 'null': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'job_events'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobEvent']", 'blank': 'True', 'null': 'True'}),
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
},
'main.jobhostsummary': {
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host')]", 'object_name': 'JobHostSummary'},
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now_add': 'True', 'blank': 'True'}),
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
},
'main.jobtemplate': {
'Meta': {'object_name': 'JobTemplate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'jobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'jobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.organization': {
'Meta': {'object_name': 'Organization'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.permission': {
'Meta': {'object_name': 'Permission'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
},
'main.profile': {
'Meta': {'object_name': 'Profile'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
},
'main.project': {
'Meta': {'object_name': 'Project'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'project\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'projects'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Credential']"}),
'current_update': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'project_as_current_update+'", 'null': 'True', 'to': "orm['main.ProjectUpdate']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_update': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'project_as_last_update+'", 'null': 'True', 'to': "orm['main.ProjectUpdate']"}),
'last_update_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'project\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'null': 'True', 'blank': 'True'}),
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'null': 'True', 'blank': 'True'}),
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32', 'null': 'True'})
},
'main.projectupdate': {
'Meta': {'object_name': 'ProjectUpdate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'projectupdate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'projectupdate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
'result_stdout': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'})
},
'main.team': {
'Meta': {'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
u'taggit.tag': {
'Meta': {'object_name': 'Tag'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
},
u'taggit.taggeditem': {
'Meta': {'object_name': 'TaggedItem'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"})
}
}
complete_apps = ['main']

View File

@@ -6,6 +6,8 @@ from awx.main.models.organization import *
from awx.main.models.projects import * from awx.main.models.projects import *
from awx.main.models.inventory import * from awx.main.models.inventory import *
from awx.main.models.jobs import * from awx.main.models.jobs import *
from awx.main.models.activity_stream import *
from awx.main.registrar import activity_stream_registrar
# Monkeypatch Django serializer to ignore django-taggit fields (which break # Monkeypatch Django serializer to ignore django-taggit fields (which break
# the dumpdata command; see https://github.com/alex/django-taggit/issues/155). # the dumpdata command; see https://github.com/alex/django-taggit/issues/155).
@@ -27,3 +29,20 @@ User.add_to_class('can_access', check_user_access)
# Import signal handlers only after models have been defined. # Import signal handlers only after models have been defined.
import awx.main.signals import awx.main.signals
activity_stream_registrar.connect(Organization)
activity_stream_registrar.connect(Inventory)
activity_stream_registrar.connect(Host)
activity_stream_registrar.connect(Group)
activity_stream_registrar.connect(InventorySource)
activity_stream_registrar.connect(InventoryUpdate)
activity_stream_registrar.connect(Credential)
activity_stream_registrar.connect(Team)
activity_stream_registrar.connect(Project)
activity_stream_registrar.connect(ProjectUpdate)
activity_stream_registrar.connect(Permission)
activity_stream_registrar.connect(JobTemplate)
activity_stream_registrar.connect(Job)
activity_stream_registrar.connect(JobHostSummary)
activity_stream_registrar.connect(JobEvent)
#activity_stream_registrar.connect(Profile)

View File

@@ -0,0 +1,41 @@
# Copyright (c) 2013 AnsibleWorks, Inc.
# All Rights Reserved.
from django.db import models
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
class ActivityStream(models.Model):
'''
Model used to describe activity stream (audit) events
'''
class Meta:
app_label = 'main'
OPERATION_CHOICES = [
('create', _('Entity Created')),
('update', _("Entity Updated")),
('delete', _("Entity Deleted")),
('associate', _("Entity Associated with another Entity")),
('disassociate', _("Entity was Disassociated with another Entity"))
]
user = models.ForeignKey('auth.User', null=True, on_delete=models.SET_NULL, related_name='activity_stream')
operation = models.CharField(max_length=13, choices=OPERATION_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
changes = models.TextField(blank=True)
object1_id = models.PositiveIntegerField(db_index=True)
object1 = models.TextField()
object1_type = models.TextField()
object2_id = models.PositiveIntegerField(db_index=True, null=True)
object2 = models.TextField(null=True, blank=True)
object2_type = models.TextField(null=True, blank=True)
object_relationship_type = models.TextField(blank=True)
def get_absolute_url(self):
return reverse('api:activity_stream_detail', args=(self.pk,))

View File

@@ -370,3 +370,28 @@ class CommonTask(PrimordialModel):
self.cancel_flag = True self.cancel_flag = True
self.save(update_fields=['cancel_flag']) self.save(update_fields=['cancel_flag'])
return self.cancel_flag return self.cancel_flag
class ActivityStream(models.Model):
'''
Model used to describe activity stream (audit) events
'''
OPERATION_CHOICES = [
('create', _('Entity Created')),
('update', _("Entity Updated")),
('delete', _("Entity Deleted")),
('associate', _("Entity Associated with another Entity")),
('disaassociate', _("Entity was Disassociated with another Entity"))
]
user = models.ForeignKey('auth.User', null=True, on_delete=models.SET_NULL)
operation = models.CharField(max_length=9, choices=OPERATION_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
changes = models.TextField(blank=True)
object1_id = models.PositiveIntegerField(db_index=True)
object1_type = models.TextField()
object2_id = models.PositiveIntegerField(db_index=True)
object2_type = models.TextField()
object_relationship_type = models.TextField()

42
awx/main/registrar.py Normal file
View File

@@ -0,0 +1,42 @@
import logging
from django.db.models.signals import pre_save, post_save, post_delete, m2m_changed
from signals import activity_stream_create, activity_stream_update, activity_stream_delete, activity_stream_associate
logger = logging.getLogger('awx.main.registrar')
class ActivityStreamRegistrar(object):
def __init__(self):
self.models = []
def connect(self, model):
#(receiver, sender=model, dispatch_uid=self._dispatch_uid(signal, model))
if model not in self.models:
self.models.append(model)
post_save.connect(activity_stream_create, sender=model, dispatch_uid=str(self.__class__) + str(model) + "_create")
pre_save.connect(activity_stream_update, sender=model, dispatch_uid=str(self.__class__) + str(model) + "_update")
post_delete.connect(activity_stream_delete, sender=model, dispatch_uid=str(self.__class__) + str(model) + "_delete")
for m2mfield in model._meta.many_to_many:
try:
m2m_attr = getattr(model, m2mfield.name)
m2m_changed.connect(activity_stream_associate, sender=m2m_attr.through,
dispatch_uid=str(self.__class__) + str(m2m_attr.through) + "_associate")
except AttributeError:
pass
#logger.warning("Failed to attach m2m activity stream tracker on class %s attribute %s" % (model, m2mfield.name))
def disconnect(self, model):
if model in self.models:
post_save.disconnect(dispatch_uid=str(self.__class__) + str(model) + "_create")
pre_save.disconnect(dispatch_uid=str(self.__class__) + str(model) + "_update")
post_delete.disconnect(dispatch_uid=str(self.__class__) + str(model) + "_delete")
self.models.pop(model)
for m2mfield in model._meta.many_to_many:
m2m_attr = getattr(model, m2mfield.name)
m2m_changed.disconnect(dispatch_uid=str(self.__class__) + str(m2m_attr.through) + "_associate")
activity_stream_registrar = ActivityStreamRegistrar()

View File

@@ -4,6 +4,7 @@
# Python # Python
import logging import logging
import threading import threading
import json
# Django # Django
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
@@ -11,6 +12,7 @@ from django.dispatch import receiver
# AWX # AWX
from awx.main.models import * from awx.main.models import *
from awx.main.utils import model_instance_diff, model_to_dict, camelcase_to_underscore
__all__ = [] __all__ = []
@@ -168,3 +170,65 @@ def update_host_last_job_after_job_deleted(sender, **kwargs):
hosts_pks = getattr(instance, '_saved_hosts_pks', []) hosts_pks = getattr(instance, '_saved_hosts_pks', [])
for host in Host.objects.filter(pk__in=hosts_pks): for host in Host.objects.filter(pk__in=hosts_pks):
_update_host_last_jhs(host) _update_host_last_jhs(host)
# Set via ActivityStreamRegistrar to record activity stream events
def activity_stream_create(sender, instance, created, **kwargs):
if created:
activity_entry = ActivityStream(
operation='create',
object1_id=instance.id,
object1=camelcase_to_underscore(instance.__class__.__name__),
object1_type=instance.__class__.__module__ + "." + instance.__class__.__name__,
changes=json.dumps(model_to_dict(instance)))
activity_entry.save()
def activity_stream_update(sender, instance, **kwargs):
try:
old = sender.objects.get(id=instance.id)
except sender.DoesNotExist:
return
new = instance
changes = model_instance_diff(old, new)
activity_entry = ActivityStream(
operation='update',
object1_id=instance.id,
object1=camelcase_to_underscore(instance.__class__.__name__),
object1_type=instance.__class__.__module__ + "." + instance.__class__.__name__,
changes=json.dumps(changes))
activity_entry.save()
def activity_stream_delete(sender, instance, **kwargs):
activity_entry = ActivityStream(
operation='delete',
object1_id=instance.id,
object1=camelcase_to_underscore(instance.__class__.__name__),
object1_type=instance.__class__.__module__ + "." + instance.__class__.__name__)
activity_entry.save()
def activity_stream_associate(sender, instance, **kwargs):
if 'pre_add' in kwargs['action'] or 'pre_remove' in kwargs['action']:
if kwargs['action'] == 'pre_add':
action = 'associate'
elif kwargs['action'] == 'pre_remove':
action = 'disassociate'
else:
return
obj1 = instance
obj1_id = obj1.id
obj_rel = sender.__module__ + "." + sender.__name__
for entity_acted in kwargs['pk_set']:
obj2 = kwargs['model']
obj2_id = entity_acted
activity_entry = ActivityStream(
operation=action,
object1_id=obj1_id,
object1=camelcase_to_underscore(obj1.__class__.__name__),
object1_type=obj1.__class__.__module__ + "." + obj1.__class__.__name__,
object2_id=obj2_id,
object2=camelcase_to_underscore(obj2.__name__),
object2_type=obj2.__module__ + "." + obj2.__name__,
object_relationship_type=obj_rel)
activity_entry.save()

View File

@@ -10,4 +10,4 @@ from awx.main.tests.scripts import *
from awx.main.tests.tasks import RunJobTest from awx.main.tests.tasks import RunJobTest
from awx.main.tests.licenses import LicenseTests from awx.main.tests.licenses import LicenseTests
from awx.main.tests.jobs import * from awx.main.tests.jobs import *
from awx.main.tests.activity_stream import *

View File

@@ -0,0 +1,61 @@
# Copyright (c) 2013 AnsibleWorks, Inc.
# All Rights Reserved.
# Python
import contextlib
import datetime
import json
import os
import shutil
import tempfile
from django.contrib.auth.models import User
import django.test
from django.test.client import Client
from django.core.urlresolvers import reverse
# AWX
from awx.main.models import *
from awx.main.tests.base import BaseTest
class ActivityStreamTest(BaseTest):
def collection(self):
return reverse('api:activity_stream_list')
def item(self, item_id):
return reverse('api:activity_stream_detail', args=(item_id,))
def setUp(self):
super(ActivityStreamTest, self).setUp()
self.setup_users()
self.organization = self.make_organizations(self.normal_django_user, 1)[0]
self.project = self.make_projects(self.normal_django_user, 1)[0]
self.organization.projects.add(self.project)
self.organization.users.add(self.normal_django_user)
def test_get_activity_stream_list(self):
url = self.collection()
with self.current_user(self.normal_django_user):
self.options(url, expect=200)
self.head(url, expect=200)
response = self.get(url, expect=200)
self.check_pagination_and_size(response, 4, previous=None, next=None)
def test_basic_fields(self):
org_item = self.item(self.organization.id)
with self.current_user(self.super_django_user):
response = self.get(org_item, expect=200)
self.assertEqual(response['object1_id'], self.organization.id)
self.assertEqual(response['object1_type'], "awx.main.models.organization.Organization")
self.assertEqual(response['object2_id'], None)
self.assertEqual(response['object2_type'], None)
self.assertTrue("related" in response)
self.assertTrue("object1" in response['related'])
self.assertTrue("summary_fields" in response)
self.assertTrue("object1" in response['summary_fields'])
self.assertEquals(response['summary_fields']['object1']['base'], "organization")

View File

@@ -10,6 +10,9 @@ import subprocess
import sys import sys
import urlparse import urlparse
# Django
from django.db.models import Model
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import ParseError, PermissionDenied from rest_framework.exceptions import ParseError, PermissionDenied
@@ -219,3 +222,45 @@ def update_scm_url(scm_type, url, username=True, password=True):
new_url = urlparse.urlunsplit([parts.scheme, netloc, parts.path, new_url = urlparse.urlunsplit([parts.scheme, netloc, parts.path,
parts.query, parts.fragment]) parts.query, parts.fragment])
return new_url return new_url
def model_instance_diff(old, new):
"""
Calculate the differences between two model instances. One of the instances may be None (i.e., a newly
created model or deleted model). This will cause all fields with a value to have changed (from None).
"""
if not(old is None or isinstance(old, Model)):
raise TypeError('The supplied old instance is not a valid model instance.')
if not(new is None or isinstance(new, Model)):
raise TypeError('The supplied new instance is not a valid model instance.')
diff = {}
if old is not None and new is not None:
fields = set(old._meta.fields + new._meta.fields)
elif old is not None:
fields = set(old._meta.fields)
elif new is not None:
fields = set(new._meta.fields)
else:
fields = set()
for field in fields:
old_value = str(getattr(old, field.name, None))
new_value = str(getattr(new, field.name, None))
if old_value != new_value:
diff[field.name] = (old_value, new_value)
if len(diff) == 0:
diff = None
return diff
def model_to_dict(obj):
"""
Serialize a model instance to a dictionary as best as possible
"""
attr_d = {}
for field in obj._meta.fields:
attr_d[field.name] = str(getattr(obj, field.name, None))
return attr_d

View File

@@ -105,6 +105,7 @@ TEMPLATE_CONTEXT_PROCESSORS += (
MIDDLEWARE_CLASSES += ( MIDDLEWARE_CLASSES += (
'django.middleware.transaction.TransactionMiddleware', 'django.middleware.transaction.TransactionMiddleware',
# Middleware loaded after this point will be subject to transactions. # Middleware loaded after this point will be subject to transactions.
'awx.main.middleware.ActivityStreamMiddleware'
) )
TEMPLATE_DIRS = ( TEMPLATE_DIRS = (

View File

@@ -846,7 +846,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-focus, .ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus { .ui-widget-header .ui-state-focus {
border: 1px solid #e3e3e3; border: 1px solid #e3e3e3;
background: #e5e3e3 url(/static/css/images/ui-bg_flat_75_e5e3e3_40x100.png) 50% 50% repeat-x; background: #e5e3e3 url(/static/css/custom-theme/images/ui-bg_flat_75_e5e3e3_40x100.png) 50% 50% repeat-x;
font-weight: bold; font-weight: bold;
color: #005580; color: #005580;
} }

View File

@@ -84,7 +84,8 @@ angular.module('ansible', [
'TimerService', 'TimerService',
'StreamListDefinition', 'StreamListDefinition',
'HomeGroupListDefinition', 'HomeGroupListDefinition',
'HomeHostListDefinition' 'HomeHostListDefinition',
'ActivityDetailDefinition'
]) ])
.config(['$routeProvider', function($routeProvider) { .config(['$routeProvider', function($routeProvider) {
$routeProvider. $routeProvider.

View File

@@ -12,7 +12,7 @@
function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList, function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, GetBasePath, SelectionInit, GetChoices) ClearScope, ProcessErrors, GetBasePath, SelectionInit, GetChoices, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -86,14 +86,17 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res
scope.deleteCredential = function(id, name) { scope.deleteCredential = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -109,13 +112,13 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res
CredentialsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'CredentialList', 'GenerateList', CredentialsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'CredentialList', 'GenerateList',
'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
'GetBasePath', 'SelectionInit', 'GetChoices']; 'GetBasePath', 'SelectionInit', 'GetChoices', 'Wait' ];
function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GenerateList, SearchInit, PaginateInit, LookUpInit, UserList, TeamList, GetBasePath, GenerateList, SearchInit, PaginateInit, LookUpInit, UserList, TeamList, GetBasePath,
GetChoices, Empty, KindChange, OwnerChange, FormSave) GetChoices, Empty, KindChange, OwnerChange, FormSave, DebugForm)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -158,6 +161,7 @@ function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
// Get the username based on incoming route // Get the username based on incoming route
scope['owner'] = 'user'; scope['owner'] = 'user';
scope['user'] = $routeParams.user_id; scope['user'] = $routeParams.user_id;
OwnerChange({ scope: scope });
var url = GetBasePath('users') + $routeParams.user_id + '/'; var url = GetBasePath('users') + $routeParams.user_id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()
@@ -173,6 +177,7 @@ function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
// Get the username based on incoming route // Get the username based on incoming route
scope['owner'] = 'team'; scope['owner'] = 'team';
scope['team'] = $routeParams.team_id; scope['team'] = $routeParams.team_id;
OwnerChange({ scope: scope });
var url = GetBasePath('teams') + $routeParams.team_id + '/'; var url = GetBasePath('teams') + $routeParams.team_id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()
@@ -238,7 +243,7 @@ function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
CredentialsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm', CredentialsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList',
'SearchInit', 'PaginateInit', 'LookUpInit', 'UserList', 'TeamList', 'GetBasePath', 'GetChoices', 'Empty', 'SearchInit', 'PaginateInit', 'LookUpInit', 'UserList', 'TeamList', 'GetBasePath', 'GetChoices', 'Empty',
'KindChange', 'OwnerChange', 'FormSave']; 'KindChange', 'OwnerChange', 'FormSave', 'DebugForm'];
function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm,
@@ -358,14 +363,16 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
master['secret_key'] = scope['secret_key']; master['secret_key'] = scope['secret_key'];
break; break;
case 'ssh': case 'ssh':
scope['ssh_username'] = data.username;
scope['ssh_password'] = data.password; scope['ssh_password'] = data.password;
master['ssh_username'] = scope['ssh_username'];
master['ssh_password'] = scope['ssh_password']; master['ssh_password'] = scope['ssh_password'];
break; break;
case 'scm': case 'scm':
scope['scm_key_unlock'] = data['ssh_key_unlock']; scope['scm_key_unlock'] = data['ssh_key_unlock'];
break; break;
case 'rax':
scope['api_key'] = data['password'];
master['api_key'] = scope['api_key'];
break;
} }
scope.$emit('credentialLoaded'); scope.$emit('credentialLoaded');

View File

@@ -16,28 +16,33 @@ function Home ($routeParams, $scope, $rootScope, $location, Wait, ObjectCount, J
ClearScope('home'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('home'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
var waitCount = 4; var load = function() {
var loadedCount = 0; var waitCount = 4;
var loadedCount = 0;
if (!$routeParams['login']) {
// If we're not logging in, start the Wait widget. Otherwise, it's already running. if (!$routeParams['login']) {
Wait('start'); // If we're not logging in, start the Wait widget. Otherwise, it's already running.
} Wait('start');
JobStatus({ target: 'container1' });
InventorySyncStatus({ target: 'container2' });
SCMSyncStatus({ target: 'container4' });
ObjectCount({ target: 'container3' });
$rootScope.showActivity = function() { Stream(); }
$rootScope.$on('WidgetLoaded', function() {
// Once all the widgets report back 'loaded', turn off Wait widget
loadedCount++;
if ( loadedCount == waitCount ) {
Wait('stop');
} }
});
JobStatus({ target: 'container1' });
InventorySyncStatus({ target: 'container2' });
SCMSyncStatus({ target: 'container4' });
ObjectCount({ target: 'container3' });
$rootScope.$on('WidgetLoaded', function() {
// Once all the widgets report back 'loaded', turn off Wait widget
loadedCount++;
if ( loadedCount == waitCount ) {
Wait('stop');
}
});
}
$rootScope.showActivity = function() { Stream(); }
$rootScope.refresh = function() { load(); }
load();
} }
Home.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', 'Wait', 'ObjectCount', 'JobStatus', 'InventorySyncStatus', Home.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', 'Wait', 'ObjectCount', 'JobStatus', 'InventorySyncStatus',
@@ -96,6 +101,14 @@ function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, Proce
PaginateInit({ scope: scope, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl });
// Process search params // Process search params
if ($routeParams['name']) {
scope[list.iterator + 'InputDisable'] = false;
scope[list.iterator + 'SearchValue'] = $routeParams['name'];
scope[list.iterator + 'SearchField'] = 'name';
scope[list.iterator + 'SearchFieldLabel'] = list.fields['name'].label;
scope[list.iterator + 'SearchSelectValue'] = null;
}
if ($routeParams['has_active_failures']) { if ($routeParams['has_active_failures']) {
scope[list.iterator + 'InputDisable'] = true; scope[list.iterator + 'InputDisable'] = true;
scope[list.iterator + 'SearchValue'] = $routeParams['has_active_failures']; scope[list.iterator + 'SearchValue'] = $routeParams['has_active_failures'];
@@ -180,7 +193,15 @@ function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, Process
SearchInit({ scope: scope, set: 'hosts', list: list, url: defaultUrl }); SearchInit({ scope: scope, set: 'hosts', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl });
// Process search params
if ($routeParams['name']) {
scope[HomeHostList.iterator + 'InputDisable'] = false;
scope[HomeHostListiterator + 'SearchValue'] = $routeParams['name'];
scope[HomeHostList.iterator + 'SearchField'] = 'name';
scope[lHomeHostList.iterator + 'SearchFieldLabel'] = list.fields['name'].label;
}
if ($routeParams['has_active_failures']) { if ($routeParams['has_active_failures']) {
scope[HomeHostList.iterator + 'InputDisable'] = true; scope[HomeHostList.iterator + 'InputDisable'] = true;
scope[HomeHostList.iterator + 'SearchValue'] = $routeParams['has_active_failures']; scope[HomeHostList.iterator + 'SearchValue'] = $routeParams['has_active_failures'];

View File

@@ -150,6 +150,7 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Res
scope.deleteInventory = function(id, name) { scope.deleteInventory = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
Wait('start'); Wait('start');
@@ -207,7 +208,7 @@ InventoriesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$route
function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GenerateList, OrganizationList, SearchInit, PaginateInit, LookUpInit, GetBasePath, GenerateList, OrganizationList, SearchInit, PaginateInit, LookUpInit, GetBasePath,
ParseTypeChange) ParseTypeChange, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -239,6 +240,7 @@ function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routePa
// Save // Save
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
try { try {
// Make sure we have valid variable data // Make sure we have valid variable data
if (scope.inventoryParseType == 'json') { if (scope.inventoryParseType == 'json') {
@@ -273,23 +275,28 @@ function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routePa
Rest.setUrl(data.related.variable_data); Rest.setUrl(data.related.variable_data);
Rest.put(json_data) Rest.put(json_data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$location.path('/inventories/' + inventory_id + '/groups'); $location.path('/inventories/' + inventory_id + '/groups');
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add inventory varaibles. PUT returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add inventory varaibles. PUT returned status: ' + status });
}); });
} }
else { else {
Wait('stop');
$location.path('/inventories/' + inventory_id + '/groups'); $location.path('/inventories/' + inventory_id + '/groups');
} }
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new inventory. Post returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new inventory. Post returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
Alert("Error", "Error parsing inventory variables. Parser returned: " + err); Alert("Error", "Error parsing inventory variables. Parser returned: " + err);
} }
@@ -304,13 +311,14 @@ function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routePa
InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryForm', 'GenerateForm', InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList',
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit', 'GetBasePath', 'ParseTypeChange']; 'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit', 'GetBasePath', 'ParseTypeChange', 'Wait'];
function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, OrganizationList, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, OrganizationList,
GetBasePath, LoadInventory, ParseTypeChange, EditInventory, SaveInventory, PostLoadInventory) GetBasePath, LoadInventory, ParseTypeChange, EditInventory, SaveInventory, PostLoadInventory
)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.

View File

@@ -13,7 +13,7 @@
function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobTemplateList, function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobTemplateList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, GetBasePath, PromptPasswords, JobTemplateForm, CredentialList, ClearScope, ProcessErrors, GetBasePath, PromptPasswords, JobTemplateForm, CredentialList,
LookUpInit, SubmitJob) LookUpInit, SubmitJob, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -49,14 +49,17 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re
scope.deleteJobTemplate = function(id, name) { scope.deleteJobTemplate = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -77,13 +80,13 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re
JobTemplatesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobTemplateList', JobTemplatesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobTemplateList',
'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors','GetBasePath', 'PromptPasswords', 'JobTemplateForm', 'CredentialList', 'LookUpInit', 'ProcessErrors','GetBasePath', 'PromptPasswords', 'JobTemplateForm', 'CredentialList', 'LookUpInit',
'SubmitJob' 'SubmitJob', 'Wait'
]; ];
function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GetBasePath, InventoryList, CredentialList, ProjectList, LookUpInit, GetBasePath, InventoryList, CredentialList, ProjectList, LookUpInit,
md5Setup, ParseTypeChange) md5Setup, ParseTypeChange, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -224,6 +227,7 @@ function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeP
// Save // Save
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
var data = {} var data = {}
try { try {
// Make sure we have valid variable data // Make sure we have valid variable data
@@ -258,16 +262,19 @@ function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeP
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new job template. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new job template. POST returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
Alert("Error", "Error parsing extra variables. Parser returned: " + err); Alert("Error", "Error parsing extra variables. Parser returned: " + err);
} }
}; };
@@ -286,14 +293,14 @@ function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeP
JobTemplatesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm', JobTemplatesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobTemplateForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope',
'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit',
'md5Setup', 'ParseTypeChange' ]; 'md5Setup', 'ParseTypeChange', 'Wait'];
function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, CredentialList, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, CredentialList,
ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, ParseTypeChange, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, ParseTypeChange,
JobStatusToolTip, FormatDate) JobStatusToolTip, FormatDate, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -539,6 +546,7 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
var data = {} var data = {}
try { try {
// Make sure we have valid variable data // Make sure we have valid variable data
@@ -573,16 +581,19 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route
Rest.setUrl(defaultUrl + id + '/'); Rest.setUrl(defaultUrl + id + '/');
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update job template. PUT returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to update job template. PUT returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
Alert("Error", "Error parsing extra variables. Parser returned: " + err); Alert("Error", "Error parsing extra variables. Parser returned: " + err);
} }
}; };
@@ -640,5 +651,5 @@ JobTemplatesEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList',
'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'ParseTypeChange',
'JobStatusToolTip', 'FormatDate' 'JobStatusToolTip', 'FormatDate', 'Wait'
]; ];

View File

@@ -12,7 +12,7 @@
function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, Rest, Alert, LoadBreadCrumbs, Prompt, function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, Rest, Alert, LoadBreadCrumbs, Prompt,
GenerateList, OrganizationList, SearchInit, PaginateInit, ClearScope, ProcessErrors, GenerateList, OrganizationList, SearchInit, PaginateInit, ClearScope, ProcessErrors,
GetBasePath, SelectionInit, Wait) GetBasePath, SelectionInit, Wait, Stream)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -36,6 +36,8 @@ function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, R
PaginateInit({ scope: scope, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl });
scope.search(list.iterator); scope.search(list.iterator);
scope.showActivity = function() { Stream(); }
scope.addOrganization = function() { scope.addOrganization = function() {
$location.path($location.path() + '/add'); $location.path($location.path() + '/add');
} }
@@ -47,14 +49,17 @@ function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, R
scope.deleteOrganization = function(id, name) { scope.deleteOrganization = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -70,12 +75,12 @@ function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, R
OrganizationsList.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', '$log', 'Rest', 'Alert', 'LoadBreadCrumbs', 'Prompt', OrganizationsList.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', '$log', 'Rest', 'Alert', 'LoadBreadCrumbs', 'Prompt',
'GenerateList', 'OrganizationList', 'SearchInit', 'PaginateInit', 'ClearScope', 'ProcessErrors', 'GenerateList', 'OrganizationList', 'SearchInit', 'PaginateInit', 'ClearScope', 'ProcessErrors',
'GetBasePath', 'SelectionInit', 'Wait' ]; 'GetBasePath', 'SelectionInit', 'Wait', 'Stream'];
function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, OrganizationForm, function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, OrganizationForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath,
ReturnToCaller) ReturnToCaller, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -92,12 +97,14 @@ function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $route
// Save // Save
scope.formSave = function() { scope.formSave = function() {
form.clearApiErrors(); form.clearApiErrors();
Wait('start');
var url = GetBasePath(base); var url = GetBasePath(base);
url += (base != 'organizations') ? $routeParams['project_id'] + '/organizations/' : ''; url += (base != 'organizations') ? $routeParams['project_id'] + '/organizations/' : '';
Rest.setUrl(url); Rest.setUrl(url);
Rest.post({ name: $scope.name, Rest.post({ name: $scope.name,
description: $scope.description }) description: $scope.description })
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
if (base == 'organizations') { if (base == 'organizations') {
$rootScope.flashMessage = "New organization successfully created!"; $rootScope.flashMessage = "New organization successfully created!";
$location.path('/organizations/' + data.id); $location.path('/organizations/' + data.id);
@@ -107,6 +114,7 @@ function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $route
} }
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, OrganizationForm, ProcessErrors(scope, data, status, OrganizationForm,
{ hdr: 'Error!', msg: 'Failed to add new organization. Post returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new organization. Post returned status: ' + status });
}); });
@@ -121,12 +129,12 @@ function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $route
OrganizationsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'OrganizationForm', OrganizationsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'OrganizationForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
'ReturnToCaller' ]; 'ReturnToCaller', 'Wait'];
function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, OrganizationForm, function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, OrganizationForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, Prompt, ClearScope, GetBasePath) RelatedPaginateInit, Prompt, ClearScope, GetBasePath, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -184,6 +192,7 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
var params = {}; var params = {};
for (var fld in form.fields) { for (var fld in form.fields) {
params[fld] = scope[fld]; params[fld] = scope[fld];
@@ -191,10 +200,12 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout
Rest.setUrl(defaultUrl + id + '/'); Rest.setUrl(defaultUrl + id + '/');
Rest.put(params) Rest.put(params)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
master = params; master = params;
$rootScope.flashMessage = "Your changes were successfully saved!"; $rootScope.flashMessage = "Your changes were successfully saved!";
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, OrganizationForm, ProcessErrors(scope, data, status, OrganizationForm,
{ hdr: 'Error!', msg: 'Failed to update organization: ' + id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update organization: ' + id + '. PUT status: ' + status });
}); });
@@ -226,14 +237,17 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout
$rootScope.flashMessage = null; $rootScope.flashMessage = null;
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + $routeParams.organization_id + '/' + set + '/'; var url = defaultUrl + $routeParams.organization_id + '/' + set + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.post({ id: itm_id, disassociate: 1 }) Rest.post({ id: itm_id, disassociate: 1 })
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(form.related[set].iterator); scope.search(form.related[set].iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status });
@@ -250,4 +264,4 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout
OrganizationsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'OrganizationForm', OrganizationsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'OrganizationForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath']; 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'Wait'];

View File

@@ -1,7 +1,7 @@
function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, PermissionList, function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, PermissionList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, GetBasePath, CheckAccess) ClearScope, ProcessErrors, GetBasePath, CheckAccess, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -35,14 +35,17 @@ function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Res
scope.deletePermission = function(id, name) { scope.deletePermission = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = GetBasePath('base') + 'permissions/' + id + '/'; var url = GetBasePath('base') + 'permissions/' + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -60,13 +63,14 @@ function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Res
PermissionsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'PermissionList', PermissionsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'PermissionList',
'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller',
'ClearScope', 'ProcessErrors', 'GetBasePath', 'CheckAccess' 'ClearScope', 'ProcessErrors', 'GetBasePath', 'CheckAccess', 'Wait'
]; ];
function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm, function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope,
GetBasePath, ReturnToCaller, InventoryList, ProjectList, LookUpInit, CheckAccess) GetBasePath, ReturnToCaller, InventoryList, ProjectList, LookUpInit, CheckAccess,
Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -110,6 +114,7 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
// Save // Save
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
if (scope.PermissionAddAllowed) { if (scope.PermissionAddAllowed) {
var data = {}; var data = {};
for (var fld in form.fields) { for (var fld in form.fields) {
@@ -119,9 +124,11 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
Rest.setUrl(url); Rest.setUrl(url);
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
ReturnToCaller(1); ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, PermissionsForm, ProcessErrors(scope, data, status, PermissionsForm,
{ hdr: 'Error!', msg: 'Failed to create new permission. Post returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to create new permission. Post returned status: ' + status });
}); });
@@ -153,13 +160,14 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
PermissionsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm', PermissionsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
'ReturnToCaller', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess' 'ReturnToCaller', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess', 'Wait'
]; ];
function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm, function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller,
ClearScope, Prompt, GetBasePath, InventoryList, ProjectList, LookUpInit, CheckAccess) ClearScope, Prompt, GetBasePath, InventoryList, ProjectList, LookUpInit, CheckAccess,
Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -247,6 +255,7 @@ function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
var data = {} var data = {}
for (var fld in form.fields) { for (var fld in form.fields) {
data[fld] = scope[fld]; data[fld] = scope[fld];
@@ -254,9 +263,11 @@ function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
ReturnToCaller(1); ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update Permission: ' + $routeParams.id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update Permission: ' + $routeParams.id + '. PUT status: ' + status });
}); });
@@ -286,6 +297,7 @@ function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
PermissionsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm', PermissionsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller',
'ClearScope', 'Prompt', 'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess' 'ClearScope', 'Prompt', 'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess',
'Wait'
]; ];

View File

@@ -13,7 +13,7 @@
function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, ProjectList, function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, ProjectList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, GetBasePath, SelectionInit, ProjectUpdate, ProjectStatus, ClearScope, ProcessErrors, GetBasePath, SelectionInit, ProjectUpdate, ProjectStatus,
FormatDate, Refresh) FormatDate, Refresh, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -112,14 +112,17 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
scope.deleteProject = function(id, name) { scope.deleteProject = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -225,13 +228,13 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList', ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList',
'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
'GetBasePath', 'SelectionInit', 'ProjectUpdate', 'ProjectStatus', 'FormatDate', 'Refresh' ]; 'GetBasePath', 'SelectionInit', 'ProjectUpdate', 'ProjectStatus', 'FormatDate', 'Refresh', 'Wait' ];
function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope,
GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit, OrganizationList, GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit, OrganizationList,
CredentialList, GetChoices, DebugForm) CredentialList, GetChoices, DebugForm, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -291,6 +294,7 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
// Save // Save
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
var data = {}; var data = {};
for (var fld in form.fields) { for (var fld in form.fields) {
if (form.fields[fld].type == 'checkbox_group') { if (form.fields[fld].type == 'checkbox_group') {
@@ -322,6 +326,7 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
Rest.setUrl(url); Rest.setUrl(url);
Rest.post(org) Rest.post(org)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$rootScope.flashMessage = "New project successfully created!"; $rootScope.flashMessage = "New project successfully created!";
(base == 'projects') ? ReturnToCaller() : ReturnToCaller(1); (base == 'projects') ? ReturnToCaller() : ReturnToCaller(1);
}) })
@@ -331,6 +336,7 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
}); });
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, ProjectsForm, ProcessErrors(scope, data, status, ProjectsForm,
{ hdr: 'Error!', msg: 'Failed to create new project. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to create new project. POST returned status: ' + status });
}); });
@@ -357,14 +363,14 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
ProjectsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm', ProjectsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
'ReturnToCaller', 'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList', 'GetChoices', 'ReturnToCaller', 'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList', 'GetChoices',
'DebugForm' 'DebugForm', 'Wait'
]; ];
function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, Prompt, ClearScope, GetBasePath, ReturnToCaller, GetProjectPath, RelatedPaginateInit, Prompt, ClearScope, GetBasePath, ReturnToCaller, GetProjectPath,
Authorization, CredentialList, LookUpInit, GetChoices, Empty, DebugForm) Authorization, CredentialList, LookUpInit, GetChoices, Empty, DebugForm, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -499,6 +505,7 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
$rootScope.flashMessage = null; $rootScope.flashMessage = null;
var params = {}; var params = {};
for (var fld in form.fields) { for (var fld in form.fields) {
@@ -525,9 +532,11 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.put(params) Rest.put(params)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
ReturnToCaller(); ReturnToCaller();
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update project: ' + id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update project: ' + id + '. PUT status: ' + status });
}); });
@@ -591,5 +600,5 @@ ProjectsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log'
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'ReturnToCaller', 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'ReturnToCaller',
'GetProjectPath', 'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty', 'GetProjectPath', 'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
'DebugForm' 'DebugForm', 'Wait'
]; ];

View File

@@ -12,7 +12,7 @@
function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, TeamList, function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, TeamList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, SetTeamListeners, GetBasePath, SelectionInit) ClearScope, ProcessErrors, SetTeamListeners, GetBasePath, SelectionInit, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -56,14 +56,17 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale
scope.deleteTeam = function(id, name) { scope.deleteTeam = function(id, name) {
var action = function() { var action = function() {
Wait('start');
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -90,12 +93,12 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale
TeamsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'TeamList', 'GenerateList', TeamsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'TeamList', 'GenerateList',
'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
'SetTeamListeners', 'GetBasePath', 'SelectionInit']; 'SetTeamListeners', 'GetBasePath', 'SelectionInit', 'Wait'];
function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, GenerateForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateList,
GenerateList, OrganizationList, SearchInit, PaginateInit, GetBasePath, LookUpInit) OrganizationList, SearchInit, PaginateInit, GetBasePath, LookUpInit, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -120,6 +123,7 @@ function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
// Save // Save
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
var data = {} var data = {}
for (var fld in form.fields) { for (var fld in form.fields) {
@@ -127,10 +131,12 @@ function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
} }
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$rootScope.flashMessage = "New team successfully created!"; $rootScope.flashMessage = "New team successfully created!";
$location.path('/teams/' + data.id); $location.path('/teams/' + data.id);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new team. Post returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new team. Post returned status: ' + status });
}); });
@@ -145,13 +151,13 @@ function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
TeamsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm', 'GenerateForm', TeamsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList',
'OrganizationList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'LookUpInit' ]; 'OrganizationList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'LookUpInit', 'Wait'];
function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt,
GetBasePath, CheckAccess, OrganizationList) GetBasePath, CheckAccess, OrganizationList, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -230,6 +236,7 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
$rootScope.flashMessage = null; $rootScope.flashMessage = null;
Rest.setUrl(defaultUrl + $routeParams.team_id +'/'); Rest.setUrl(defaultUrl + $routeParams.team_id +'/');
var data = {} var data = {}
@@ -238,10 +245,12 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
} }
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'teams') ? ReturnToCaller() : ReturnToCaller(1); (base == 'teams') ? ReturnToCaller() : ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update team: ' + $routeParams.team_id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update team: ' + $routeParams.team_id + '. PUT status: ' + status });
}); });
@@ -335,6 +344,6 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
TeamsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm', TeamsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt',
'GetBasePath', 'CheckAccess', 'OrganizationList' 'GetBasePath', 'CheckAccess', 'OrganizationList', 'Wait'
]; ];

View File

@@ -10,9 +10,9 @@
'use strict'; 'use strict';
function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, UserList,
Alert, UserList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit) ClearScope, ProcessErrors, GetBasePath, SelectionInit, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -47,14 +47,17 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest,
scope.deleteUser = function(id, name) { scope.deleteUser = function(id, name) {
var action = function() { var action = function() {
Wait('start')
var url = defaultUrl + id + '/'; var url = defaultUrl + id + '/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
@@ -70,12 +73,12 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest,
UsersList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'UserList', 'GenerateList', UsersList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'UserList', 'GenerateList',
'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors',
'GetBasePath', 'SelectionInit']; 'GetBasePath', 'SelectionInit', 'Wait' ];
function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm, function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GetBasePath, LookUpInit, OrganizationList, ResetForm) GetBasePath, LookUpInit, OrganizationList, ResetForm, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -121,6 +124,7 @@ function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
// Save // Save
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
if (scope.organization !== undefined && scope.organization !== null && scope.organization !== '') { if (scope.organization !== undefined && scope.organization !== null && scope.organization !== '') {
Rest.setUrl(defaultUrl + scope.organization + '/users/'); Rest.setUrl(defaultUrl + scope.organization + '/users/');
var data = {} var data = {}
@@ -138,6 +142,7 @@ function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
if (base == 'users') { if (base == 'users') {
$rootScope.flashMessage = 'New user successfully created!'; $rootScope.flashMessage = 'New user successfully created!';
@@ -148,6 +153,7 @@ function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
} }
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new user. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new user. POST returned status: ' + status });
}); });
@@ -174,12 +180,13 @@ function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams,
UsersAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm', 'GenerateForm', UsersAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
'LookUpInit', 'OrganizationList', 'ResetForm' ]; 'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait' ];
function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm, function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, ReturnToCaller, ClearScope, GetBasePath, Prompt, CheckAccess, ResetForm) RelatedPaginateInit, ReturnToCaller, ClearScope, GetBasePath, Prompt, CheckAccess,
ResetForm, Wait)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -252,6 +259,7 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
generator.clearApiErrors(); generator.clearApiErrors();
Wait('start');
$rootScope.flashMessage = null; $rootScope.flashMessage = null;
Rest.setUrl(defaultUrl + id + '/'); Rest.setUrl(defaultUrl + id + '/');
var data = {} var data = {}
@@ -269,10 +277,12 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'users') ? ReturnToCaller() : ReturnToCaller(1); (base == 'users') ? ReturnToCaller() : ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update users: ' + $routeParams.id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update users: ' + $routeParams.id + '. PUT status: ' + status });
}); });
@@ -425,5 +435,5 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
UsersEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm', UsersEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Prompt', 'CheckAccess', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Prompt', 'CheckAccess',
'ResetForm' ]; 'ResetForm', 'Wait' ];

View File

@@ -0,0 +1,64 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* ActivityDetail.js
* Form definition for Activity Stream detail
*
*/
angular.module('ActivityDetailDefinition', [])
.value(
'ActivityDetailForm', {
name: 'activity',
editTitle: 'Activity Detail',
well: false,
'class': 'horizontal-narrow',
fields: {
timestamp: {
label: 'Time',
type: 'text',
readonly: true
},
id: {
label: 'Event ID',
type: 'text',
readonly: true
},
operation: {
label: 'Operation',
type: 'text',
readonly: true
},
object1: {
label: 'Object 1',
type: 'text',
ngHide: '!object1',
readonly: true
},
object1_name: {
label: 'Name',
type: 'text',
ngHide: '!object1',
readonly: true
},
object2: {
label: 'Object 2',
type: 'text',
ngHide: '!object2',
readonly: true
},
object2_name: {
label: 'Name',
type: 'text',
ngHide: '!object2',
readonly: true
},
changes: {
label: 'Changes',
type: 'textarea',
readonly: true
}
}
}); //Form

View File

@@ -114,11 +114,22 @@ angular.module('CredentialFormDefinition', [])
awRequiredWhen: {variable: 'rackspace_required', init: false }, awRequiredWhen: {variable: 'rackspace_required', init: false },
autocomplete: false autocomplete: false
}, },
"api_key": {
label: 'API Key',
type: 'password',
ngShow: "kind.value == 'rax'",
awRequiredWhen: { variable: "rackspace_required", init: false },
autocomplete: false,
ask: false,
clear: false,
apiField: 'passwowrd'
},
"password": { "password": {
label: 'Password', label: 'Password',
type: 'password', type: 'password',
ngShow: "kind.value == 'rax' || kind.value == 'scm'", ngShow: "kind.value == 'scm'",
awRequiredWhen: {variable: 'rackspace_required', init: false }, addRequired: false,
editRequired: false,
ngChange: "clearPWConfirm('password_confirm')", ngChange: "clearPWConfirm('password_confirm')",
ask: false, ask: false,
clear: false, clear: false,
@@ -128,7 +139,7 @@ angular.module('CredentialFormDefinition', [])
"password_confirm": { "password_confirm": {
label: 'Confirm Password', label: 'Confirm Password',
type: 'password', type: 'password',
ngShow: "kind.value == 'rax' || kind.value == 'scm'", ngShow: "kind.value == 'scm'",
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
awPassMatch: true, awPassMatch: true,

View File

@@ -42,7 +42,9 @@ angular.module('ProjectFormDefinition', [])
ngClick: 'lookUpOrganization()', ngClick: 'lookUpOrganization()',
awRequiredWhen: {variable: "organizationrequired", init: "true" }, awRequiredWhen: {variable: "organizationrequired", init: "true" },
awPopOver: '<p>A project must have at least one organization. Pick one organization now to create the project, and then after ' + awPopOver: '<p>A project must have at least one organization. Pick one organization now to create the project, and then after ' +
'the project is created you can add additional organizations.' , 'the project is created you can add additional organizations.</p><p>Only super users and organization administrators are allowed ' +
'to make changes to projects. Associating one or more organizations to a project determins which organizations admins have ' +
'access to modify the project.',
dataTitle: 'Organization', dataTitle: 'Organization',
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right' dataPlacement: 'right'

View File

@@ -44,6 +44,7 @@ angular.module('CredentialsHelper', ['Utilities'])
if (reset) { if (reset) {
scope['access_key'] = null; scope['access_key'] = null;
scope['secret_key'] = null; scope['secret_key'] = null;
scope['api_key'] = null;
scope['username'] = null; scope['username'] = null;
scope['password'] = null; scope['password'] = null;
scope['password_confirm'] = null; scope['password_confirm'] = null;
@@ -94,14 +95,16 @@ angular.module('CredentialsHelper', ['Utilities'])
}]) }])
.factory('FormSave', ['$location', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', .factory('FormSave', ['$location', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', 'Wait',
function($location, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller) { function($location, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
var mode = params.mode; // add or edit var mode = params.mode; // add or edit
var form = CredentialForm; var form = CredentialForm;
var data = {} var data = {}
Wait('start');
for (var fld in form.fields) { for (var fld in form.fields) {
if (fld !== 'access_key' && fld !== 'secret_key' && fld !== 'ssh_username' && if (fld !== 'access_key' && fld !== 'secret_key' && fld !== 'ssh_username' &&
fld !== 'ssh_password') { fld !== 'ssh_password') {
@@ -127,7 +130,6 @@ angular.module('CredentialsHelper', ['Utilities'])
switch (data['kind']) { switch (data['kind']) {
case 'ssh': case 'ssh':
data['username'] = scope['ssh_username'];
data['password'] = scope['ssh_password']; data['password'] = scope['ssh_password'];
break; break;
case 'aws': case 'aws':
@@ -137,6 +139,9 @@ angular.module('CredentialsHelper', ['Utilities'])
case 'scm': case 'scm':
data['ssh_key_unlock'] = scope['scm_key_unlock']; data['ssh_key_unlock'] = scope['scm_key_unlock'];
break; break;
case 'rax':
data['password'] = scope['api_key'];
break;
} }
if (Empty(data.team) && Empty(data.user)) { if (Empty(data.team) && Empty(data.user)) {
@@ -150,10 +155,12 @@ angular.module('CredentialsHelper', ['Utilities'])
Rest.setUrl(url); Rest.setUrl(url);
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'credentials') ? ReturnToCaller() : ReturnToCaller(1); (base == 'credentials') ? ReturnToCaller() : ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to create new Credential. POST status: ' + status }); { hdr: 'Error!', msg: 'Failed to create new Credential. POST status: ' + status });
}); });
@@ -163,10 +170,12 @@ angular.module('CredentialsHelper', ['Utilities'])
Rest.setUrl(url); Rest.setUrl(url);
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'credentials') ? ReturnToCaller() : ReturnToCaller(1); (base == 'credentials') ? ReturnToCaller() : ReturnToCaller(1);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update Credential. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update Credential. PUT status: ' + status });
}); });

View File

@@ -564,9 +564,9 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
}]) }])
.factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'BuildTree', 'ClickNode', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'BuildTree', 'ClickNode', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, ParseTypeChange, GroupsEdit, BuildTree, ClickNode) { GetBasePath, ParseTypeChange, GroupsEdit, BuildTree, ClickNode, Wait) {
return function(params) { return function(params) {
var inventory_id = params.inventory_id; var inventory_id = params.inventory_id;
@@ -603,6 +603,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
// Save // Save
scope.formModalAction = function() { scope.formModalAction = function() {
Wait('start');
try { try {
scope.formModalActionDisabled = true; scope.formModalActionDisabled = true;
@@ -638,6 +639,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
groupCreated = true; groupCreated = true;
scope.formModalActionDisabled = false; scope.formModalActionDisabled = false;
scope.showGroupHelp = false; //get rid of the Hint scope.showGroupHelp = false; //get rid of the Hint
@@ -653,12 +655,14 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
}); });
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
scope.formModalActionDisabled = false; scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new group. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new group. POST returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
scope.formModalActionDisabled = false; scope.formModalActionDisabled = false;
Alert("Error", "Error parsing group variables. Parser returned: " + err); Alert("Error", "Error parsing group variables. Parser returned: " + err);
} }
@@ -675,10 +679,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', .factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate', 'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
'GetUpdateIntervalOptions', 'ClickNode', 'LookUpInit', 'CredentialList', 'Empty', 'GetUpdateIntervalOptions', 'ClickNode', 'LookUpInit', 'CredentialList', 'Empty', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, GetUpdateIntervalOptions, ClickNode, GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, GetUpdateIntervalOptions, ClickNode,
LookUpInit, CredentialList, Empty) { LookUpInit, CredentialList, Empty, Wait) {
return function(params) { return function(params) {
var group_id = params.group_id; var group_id = params.group_id;
@@ -942,6 +946,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
// Save changes to the parent // Save changes to the parent
scope.formSave = function() { scope.formSave = function() {
Wait('start');
try { try {
var refreshHosts = false; var refreshHosts = false;
@@ -967,6 +972,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
if (scope.variables) { if (scope.variables) {
//update group variables //update group variables
Rest.setUrl(scope.variable_url); Rest.setUrl(scope.variable_url);
@@ -979,11 +985,13 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
scope.$emit('formSaveSuccess', data.id); scope.$emit('formSaveSuccess', data.id);
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status }); { hdr: 'Error!', msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
Alert("Error", "Error parsing group variables. Parser returned: " + err); Alert("Error", "Error parsing group variables. Parser returned: " + err);
} }
}; };

View File

@@ -161,9 +161,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
.factory('HostsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', .factory('HostsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange) { GetBasePath, HostsReload, ParseTypeChange, Wait) {
return function(params) { return function(params) {
var inventory_id = params.inventory_id; var inventory_id = params.inventory_id;
@@ -202,6 +202,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
// Save // Save
scope.formModalAction = function() { scope.formModalAction = function() {
Wait('start');
function finished() { function finished() {
$('#form-modal').modal('hide'); $('#form-modal').modal('hide');
scope.$emit('hostsReload'); scope.$emit('hostsReload');
@@ -245,15 +247,18 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.post(data) Rest.post(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
finished(); finished();
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
scope.formModalActionDisabled = false; scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
scope.formModalActionDisabled = false; scope.formModalActionDisabled = false;
Alert("Error", "Error parsing host variables. Parser returned: " + err); Alert("Error", "Error parsing host variables. Parser returned: " + err);
} }
@@ -270,9 +275,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', .factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange) { GetBasePath, HostsReload, ParseTypeChange, Wait) {
return function(params) { return function(params) {
var host_id = params.host_id; var host_id = params.host_id;
@@ -366,6 +371,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
// Save changes to the parent // Save changes to the parent
scope.formModalAction = function() { scope.formModalAction = function() {
Wait('start');
function finished() { function finished() {
$('#form-modal').modal('hide'); $('#form-modal').modal('hide');
if (hostsReload) { if (hostsReload) {
@@ -406,14 +413,17 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.put(data) Rest.put(data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
finished(); finished();
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
Alert("Error", "Error parsing host variables. Parser returned: " + err); Alert("Error", "Error parsing host variables. Parser returned: " + err);
} }
}; };

View File

@@ -8,8 +8,9 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
'LookUpHelper', 'ProjectFormDefinition', 'JobSubmissionHelper', 'GroupFormDefinition', 'GroupsHelper' ]) 'LookUpHelper', 'ProjectFormDefinition', 'JobSubmissionHelper', 'GroupFormDefinition', 'GroupsHelper' ])
.factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', 'GroupForm', 'ProjectsForm', '$compile', 'Rest', '$location', 'ProcessErrors', .factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', 'GroupForm', 'ProjectsForm', '$compile', 'Rest', '$location', 'ProcessErrors',
'GetBasePath', 'Alert', 'Empty', 'GetBasePath', 'Alert', 'Empty', 'Wait',
function(CredentialForm, JobTemplateForm, ProjectsForm, GroupForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty) { function(CredentialForm, JobTemplateForm, ProjectsForm, GroupForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty,
Wait) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
@@ -65,6 +66,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
scope.startJob = function() { scope.startJob = function() {
$('#password-modal').modal('hide'); $('#password-modal').modal('hide');
Wait('start');
var pswd = {}; var pswd = {};
var value_supplied = false; var value_supplied = false;
$('.password-field').each(function(index) { $('.password-field').each(function(index) {
@@ -77,17 +79,20 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
Rest.setUrl(start_url); Rest.setUrl(start_url);
Rest.post(pswd) Rest.post(pswd)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
scope.$emit('UpdateSubmitted','started'); scope.$emit('UpdateSubmitted','started');
if (form.name == 'credential') { if (form.name == 'credential') {
navigate(false); navigate(false);
} }
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'POST to ' + start_url + ' failed with status: ' + status }); { hdr: 'Error!', msg: 'POST to ' + start_url + ' failed with status: ' + status });
}); });
} }
else { else {
Wait('stop');
Alert('No Passwords', 'Required password(s) not provided. The request was not submitted.', 'alert-info'); Alert('No Passwords', 'Required password(s) not provided. The request was not submitted.', 'alert-info');
if (form.name == 'credential') { if (form.name == 'credential') {
// No passwords provided, so we can't start the job. Rather than leave the job in a 'new' // No passwords provided, so we can't start the job. Rather than leave the job in a 'new'

View File

@@ -12,8 +12,8 @@
angular.module('SelectionHelper', ['Utilities', 'RestServices']) angular.module('SelectionHelper', ['Utilities', 'RestServices'])
.factory('SelectionInit', [ 'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', .factory('SelectionInit', [ 'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'Wait',
function(Rest, Alert, ProcessErrors, ReturnToCaller) { function(Rest, Alert, ProcessErrors, ReturnToCaller, Wait) {
return function(params) { return function(params) {
var scope = params.scope; // current scope var scope = params.scope; // current scope
@@ -74,6 +74,8 @@ angular.module('SelectionHelper', ['Utilities', 'RestServices'])
scope.queue = []; scope.queue = [];
scope.formModalActionDisabled = true; scope.formModalActionDisabled = true;
Wait('start');
function finished() { function finished() {
scope.selected = []; scope.selected = [];
if (returnToCaller !== undefined) { if (returnToCaller !== undefined) {
@@ -92,6 +94,7 @@ angular.module('SelectionHelper', ['Utilities', 'RestServices'])
// We call the API for each selected item. We need to hang out until all the api // We call the API for each selected item. We need to hang out until all the api
// calls are finished. // calls are finished.
if (scope.queue.length == scope.selected.length) { if (scope.queue.length == scope.selected.length) {
Wait('stop');
var errors = 0; var errors = 0;
for (var i=0; i < scope.queue.length; i++) { for (var i=0; i < scope.queue.length; i++) {
if (scope.queue[i].result == 'error') { if (scope.queue[i].result == 'error') {

View File

@@ -106,9 +106,9 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
}]) }])
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', .factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
'GetBasePath', 'ParseTypeChange', 'LoadInventory', 'GetBasePath', 'ParseTypeChange', 'LoadInventory', 'Wait',
function(InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, function(InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange,
LoadInventory) { LoadInventory, Wait) {
return function(params) { return function(params) {
// Save inventory property modifications // Save inventory property modifications
@@ -116,6 +116,8 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
var scope = params.scope; var scope = params.scope;
var form = InventoryForm; var form = InventoryForm;
var defaultUrl=GetBasePath('inventory'); var defaultUrl=GetBasePath('inventory');
Wait('start');
try { try {
// Make sure we have valid variable data // Make sure we have valid variable data
@@ -150,6 +152,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
Rest.setUrl(data.related.variable_data); Rest.setUrl(data.related.variable_data);
Rest.put(json_data) Rest.put(json_data)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
Wait('stop');
scope.$emit('inventorySaved'); scope.$emit('inventorySaved');
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
@@ -162,11 +165,13 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
} }
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update inventory. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to update inventory. POST returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
Wait('stop');
Alert("Error", "Error parsing inventory variables. Parser returned: " + err); Alert("Error", "Error parsing inventory variables. Parser returned: " + err);
} }
} }

View File

@@ -70,9 +70,11 @@ angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies'])
scope[iterator + 'Page'] = 0; scope[iterator + 'Page'] = 0;
var new_url = url.replace(/\?page_size\=\d+/,''); var new_url = url.replace(/\?page_size\=\d+/,'');
var connect = (/\/$/.test(new_url)) ? '?' : '&'; console.log('new_url: ' + new_url);
var connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] : new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] :
+ connect + 'page_size=' + scope[iterator + 'PageSize' ]; connect + 'page_size=' + scope[iterator + 'PageSize' ];
console.log('new_url: ' + new_url);
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url }); Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
} }
} }

View File

@@ -33,11 +33,16 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities'])
scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize'])); scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize']));
scope[iterator + 'SearchSpin'] = false; scope[iterator + 'SearchSpin'] = false;
scope[iterator + 'Loading'] = false; scope[iterator + 'Loading'] = false;
for (var i=1; i <= 3; i++) {
var modifier = (i == 1) ? '' : i;
scope[iterator + 'HoldInput' + modifier] = false;
}
scope[set] = data['results']; scope[set] = data['results'];
scope.$emit('PostRefresh'); scope.$emit('PostRefresh');
}) })
.error ( function(data, status, headers, config) { .error ( function(data, status, headers, config) {
scope[iterator + 'SearchSpin'] = false; scope[iterator + 'SearchSpin'] = false;
scope[iterator + 'HoldInput'] = false;
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });
}); });

View File

@@ -16,7 +16,8 @@
*/ */
angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', function(Alert, Rest, Refresh, $location) { .factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout',
function(Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
@@ -25,211 +26,355 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
var list = params.list; var list = params.list;
var iterator = (params.iterator) ? params.iterator : list.iterator; var iterator = (params.iterator) ? params.iterator : list.iterator;
var sort_order; var sort_order;
if (scope.searchTimer) {
$timeout.cancel(scope.searchTimer);
}
function setDefaults() { function setDefaults(widget) {
// Set default values // Set default values
var modifier = (widget == undefined || widget == 1) ? '' : widget;
scope[iterator + 'SearchField' + modifier] = '';
scope[iterator + 'SearchFieldLabel' + modifier] = '';
for (fld in list.fields) { for (fld in list.fields) {
if (list.fields[fld].key) { if (list.fields[fld].searchWidget == undefined && widget == 1 ||
if (list.fields[fld].sourceModel) { list.fields[fld].searchWidget == widget) {
var fka = list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField; if (list.fields[fld].key) {
sort_order = (list.fields[fld].desc) ? '-' + fka : fka; if (list.fields[fld].sourceModel) {
} var fka = list.fields[fld].sourceModel + '__' + list.fields[fld].sourceField;
else { sort_order = (list.fields[fld].desc) ? '-' + fka : fka;
sort_order = (list.fields[fld].desc) ? '-' + fld : fld; }
} else {
if (list.fields[fld].searchable == undefined || list.fields[fld].searchable == true) { sort_order = (list.fields[fld].desc) ? '-' + fld : fld;
scope[iterator + 'SearchField'] = fld; }
scope[iterator + 'SearchFieldLabel'] = list.fields[fld].label; if (list.fields[fld].searchable == undefined || list.fields[fld].searchable == true) {
} scope[iterator + 'SearchField' + modifier] = fld;
break; scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
}
break;
}
} }
} }
if (!scope[iterator + 'SearchField']) { if (Empty(scope[iterator + 'SearchField' + modifier])) {
// A field marked as key may not be 'searchable' // A field marked as key may not be 'searchable'. Find the first searchable field.
for (fld in list.fields) { for (fld in list.fields) {
if (list.fields[fld].searchable == undefined || list.fields[fld].searchable == true) { if (list.fields[fld].searchWidget == undefined && widget == 1 ||
scope[iterator + 'SearchField'] = fld; list.fields[fld].searchWidget == widget) {
scope[iterator + 'SearchFieldLabel'] = list.fields[fld].label; if (list.fields[fld].searchable == undefined || list.fields[fld].searchable == true) {
break; scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label;
break;
}
} }
} }
} }
scope[iterator + 'SearchType'] = 'icontains'; scope[iterator + 'SearchType' + modifier] = 'icontains';
scope[iterator + 'SearchTypeLabel'] = 'Contains'; scope[iterator + 'SearchTypeLabel' + modifier] = 'Contains';
scope[iterator + 'SearchParams'] = ''; scope[iterator + 'SearchParams' + modifier] = '';
scope[iterator + 'SearchValue'] = ''; scope[iterator + 'SearchValue' + modifier] = '';
scope[iterator + 'SelectShow'] = false; // show/hide the Select scope[iterator + 'SelectShow' + modifier] = false; // show/hide the Select
scope[iterator + 'HideSearchType'] = false; scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'InputDisable'] = false; scope[iterator + 'InputDisable' + modifier] = false;
scope[iterator + 'ExtraParms'] = ''; scope[iterator + 'ExtraParms' + modifier] = '';
scope[iterator + 'SearchPlaceholder' + modifier] =
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder) ?
list.fields[scope[iterator + 'SearchField' + modifier]].searchPlaceholder : 'Search';
scope[iterator + 'InputDisable' + modifier] =
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchObject == 'all') ? true : false;
var f = scope[iterator + 'SearchField'] var f = scope[iterator + 'SearchField' + modifier];
if (list.fields[f].searchType && ( list.fields[f].searchType == 'boolean' if (list.fields[f]) {
|| list.fields[f].searchType == 'select')) { if ( list.fields[f].searchType && (list.fields[f].searchType == 'boolean'
scope[iterator + 'SelectShow'] = true; || list.fields[f].searchType == 'select') ) {
scope[iterator + 'SearchSelectOpts'] = list.fields[f].searchOptions; scope[iterator + 'SelectShow' + modifier] = true;
} scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[f].searchOptions;
if (list.fields[f].searchType && list.fields[f].searchType == 'int') { }
scope[iterator + 'HideSearchType'] = true; if (list.fields[f].searchType && list.fields[f].searchType == 'int') {
} scope[iterator + 'HideSearchType' + modifier] = true;
if (list.fields[f].searchType && list.fields[f].searchType == 'gtzero') { }
scope[iterator + "InputHide"] = true; if (list.fields[f].searchType && list.fields[f].searchType == 'gtzero') {
scope[iterator + 'InputHide' + modifier] = true;
}
} }
} }
setDefaults(); for (var i=1; i <= 3; i++) {
var modifier = (i == 1) ? '' : i;
if ( $('#search-widget-container' + modifier) ) {
setDefaults(i);
}
}
// Functions to handle search widget changes // Functions to handle search widget changes
scope.setSearchField = function(iterator, fld, label) { scope.setSearchField = function(iterator, fld, label, widget) {
scope[iterator + 'SearchFieldLabel'] = label;
scope[iterator + 'SearchField'] = fld; var modifier = (widget == undefined || widget == 1) ? '' : widget;
scope[iterator + 'SearchValue'] = ''; scope[iterator + 'SearchFieldLabel' + modifier] = label;
scope[iterator + 'SelectShow'] = false; scope[iterator + 'SearchField' + modifier] = fld;
scope[iterator + 'HideSearchType'] = false; scope[iterator + 'SearchValue' + modifier] = '';
scope[iterator + 'InputHide'] = false; scope[iterator + 'SelectShow' + modifier] = false;
scope[iterator + 'InputDisable'] = false; scope[iterator + 'HideSearchType' + modifier] = false;
scope[iterator + 'SearchType'] = 'icontains'; scope[iterator + 'InputHide' + modifier] = false;
scope[iterator + 'SearchType' + modifier] = 'icontains';
scope[iterator + 'SearchPlaceholder' + modifier] = (list.fields[fld].searchPlaceholder) ? list.fields[fld].searchPlaceholder : 'Search';
scope[iterator + 'InputDisable' + modifier] = (list.fields[fld].searchObject == 'all') ? true : false;
if (list.fields[fld].searchType && list.fields[fld].searchType == 'gtzero') { if (list.fields[fld].searchType && list.fields[fld].searchType == 'gtzero') {
scope[iterator + "InputDisable"] = true; scope[iterator + "InputDisable" + modifier] = true;
} }
else if (list.fields[fld].searchSingleValue){ else if (list.fields[fld].searchSingleValue){
// Query a specific attribute for one specific value // Query a specific attribute for one specific value
// searchSingleValue: true // searchSingleValue: true
// searchType: 'boolean|int|etc.' // searchType: 'boolean|int|etc.'
// searchValue: < value to match for boolean use 'true'|'false' > // searchValue: < value to match for boolean use 'true'|'false' >
scope[iterator + 'InputDisable'] = true; scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + "SearchValue"] = list.fields[fld].searchValue; scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
// For boolean type, SearchValue must be an object // For boolean type, SearchValue must be an object
if (list.fields[fld].searchType == 'boolean' && list.fields[fld].searchValue == 'true') { if (list.fields[fld].searchType == 'boolean' && list.fields[fld].searchValue == 'true') {
scope[iterator + "SearchSelectValue"] = { value: 1 }; scope[iterator + "SearchSelectValue" + modifier] = { value: 1 };
} }
else if (list.fields[fld].searchType == 'boolean' && list.fields[fld].searchValue == 'false') { else if (list.fields[fld].searchType == 'boolean' && list.fields[fld].searchValue == 'false') {
scope[iterator + "SearchSelectValue"] = { value: 0 }; scope[iterator + "SearchSelectValue" + modifier] = { value: 0 };
} }
else { else {
scope[iterator + "SearchSelectValue"] = { value: list.fields[fld].searchValue }; scope[iterator + "SearchSelectValue" + modifier] = { value: list.fields[fld].searchValue };
} }
} }
else if (list.fields[fld].searchType == 'in') { else if (list.fields[fld].searchType == 'in') {
scope[iterator + "SearchType"] = 'in'; scope[iterator + "SearchType" + modifier] = 'in';
scope[iterator + "SearchValue"] = list.fields[fld].searchValue; scope[iterator + "SearchValue" + modifier] = list.fields[fld].searchValue;
scope[iterator + "InputDisable"] = true; scope[iterator + "InputDisable" + modifier] = true;
} }
else if (list.fields[fld].searchType && (list.fields[fld].searchType == 'boolean' else if (list.fields[fld].searchType && (list.fields[fld].searchType == 'boolean'
|| list.fields[fld].searchType == 'select')) { || list.fields[fld].searchType == 'select' || list.fields[fld].searchType == 'select_or')) {
scope[iterator + 'SelectShow'] = true; scope[iterator + 'SelectShow' + modifier] = true;
scope[iterator + 'SearchSelectOpts'] = list.fields[fld].searchOptions; scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[fld].searchOptions;
} }
else if (list.fields[fld].searchType && list.fields[fld].searchType == 'int') { else if (list.fields[fld].searchType && list.fields[fld].searchType == 'int') {
scope[iterator + 'HideSearchType'] = true; scope[iterator + 'HideSearchType' + modifier] = true;
} }
else if (list.fields[fld].searchType && list.fields[fld].searchType == 'isnull') {
scope[iterator + 'SearchType' + modifier] = 'isnull';
scope[iterator + 'InputDisable' + modifier] = true;
scope[iterator + 'SearchValue' + modifier] = 'true';
}
scope.search(iterator); scope.search(iterator);
} }
scope.resetSearch = function(iterator) { scope.resetSearch = function(iterator, widget) {
// Respdond to click of reset button // Respdond to click of reset button
setDefaults(); setDefaults(widget);
// Force removal of search keys from the URL // Force removal of search keys from the URL
window.location = '/#' + $location.path(); window.location = '/#' + $location.path();
scope.search(iterator);
} }
scope.setSearchType = function(iterator, type, label) { //scope.setSearchType = function(iterator, type, label) {
scope[iterator + 'SearchTypeLabel'] = label; // scope[iterator + 'SearchTypeLabel'] = label;
scope[iterator + 'SearchType'] = type; // scope[iterator + 'SearchType'] = type;
scope.search(iterator); // scope.search(iterator);
// }
if (scope.removeDoSearch) {
scope.removeDoSearch();
}
scope.removeDoSearch = scope.$on('doSearch', function(e, iterator, page, load, spin) {
//
// Execute the search
//
scope[iterator + 'SearchSpin'] = (spin == undefined || spin == true) ? true : false;
scope[iterator + 'Loading'] = (load == undefined || load == true) ? true : false;
var url = defaultUrl;
//finalize and execute the query
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
if (/\/$/.test(url)) {
url += '?' + scope[iterator + 'SearchParams'];
}
else {
url += '&' + scope[iterator + 'SearchParams'];
}
url = url.replace(/\&\&/,'&');
url += (scope[iterator + 'PageSize']) ? '&page_size=' + scope[iterator + 'PageSize'] : "";
if (page) {
url += '&page=' + page;
}
if (scope[iterator + 'ExtraParms']) {
url += scope[iterator + 'ExtraParms'];
}
Refresh({ scope: scope, set: set, iterator: iterator, url: url });
});
if (scope.removePrepareSearch) {
scope.removePrepareSearch();
}
scope.removePrepareSearch = scope.$on('prepareSearch', function(e, iterator, page, load, spin) {
//
// Start build the search key/value pairs. This will process the first search widget, if the
// selected field is an object type (used on activity stream).
//
scope[iterator + 'HoldInput'] = true;
scope[iterator + 'SearchParams'] = '';
if (list.fields[scope[iterator + 'SearchField']].searchObject &&
list.fields[scope[iterator + 'SearchField']].searchObject !== 'all') {
//This is specifically for activity stream. We need to identify which type of object is being searched
//and then provide a list of PK values corresponding to the list of objects the user is interested in.
var objs = list.fields[scope[iterator + 'SearchField']].searchObject;
var o = (objs == 'inventories') ? 'inventory' : objs.replace(/s$/,'');
scope[iterator + 'SearchParams'] = 'or__object1=' + o + '&or__object2=' + o;
if (scope[iterator + 'SearchValue']) {
var objUrl = GetBasePath('base') + objs + '/?name__icontains=' + scope[iterator + 'SearchValue'];
Rest.setUrl(objUrl);
Rest.get()
.success( function(data, status, headers, config) {
var list='';
for (var i=0; i < data.results.length; i++) {
list += "," + data.results[i].id;
}
list = list.replace(/^\,/,'');
if (!Empty(list)) {
scope[iterator + 'SearchParams'] += '&or__object1_id__in=' + list + '&or__object2_id__in=' + list;
}
//scope[iterator + 'SearchParams'] += (sort_order) ? '&order_by=' + escape(sort_order) : "";
scope.$emit('prepareSearch2', iterator, page, load, spin, 2);
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Retrieving list of ' + obj + ' where name contains: ' + scope[iterator + 'SearchValue'] +
' GET returned status: ' + status });
});
}
else {
scope.$emit('prepareSearch2', iterator, page, load, spin, 2);
}
}
else {
scope.$emit('prepareSearch2', iterator, page, load, spin, 1);
}
});
if (scope.removePrepareSearch2) {
scope.removePrepareSearch2();
}
scope.removePrepareSearch2 = scope.$on('prepareSearch2', function(e, iterator, page, load, spin, startingWidget) {
// Continue building the search by examining the remaining search widgets. If we're looking at activity_stream,
// there's more than one.
for (var i=startingWidget; i <= 3; i++) {
var modifier = (i == 1) ? '' : i;
scope[iterator + 'HoldInput' + modifier] = true;
if ( $('#search-widget-container' + modifier) ) {
// if the search widget exists, add its parameters to the query
if ( (!scope[iterator + 'SelectShow' + modifier] && !Empty(scope[iterator + 'SearchValue' + modifier])) ||
(scope[iterator + 'SelectShow' + modifier] && scope[iterator + 'SearchSelectValue' + modifier]) ||
(list.fields[scope[iterator + 'SearchField' + modifier]] &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'gtzero') ) {
if (list.fields[scope[iterator + 'SearchField' + modifier]].searchField) {
scope[iterator + 'SearchParams'] = list.fields[scope[iterator + 'SearchField' + modifier]].searchField + '__';
}
else if (list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] = list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel + '__' +
list.fields[scope[iterator + 'SearchField' + modifier]].sourceField + '__';
}
else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'select') &&
(scope[iterator + 'SearchSelectValue' + modifier].value == '' ||
scope[iterator + 'SearchSelectValue' + modifier].value == null) ) {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField' + modifier];
}
else {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField' + modifier] + '__';
}
if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'int' ||
list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'boolean' ) ) {
scope[iterator + 'SearchParams'] += 'int=';
}
else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'gtzero' ) {
scope[iterator + 'SearchParams'] += 'gt=0';
}
else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'select') &&
(scope[iterator + 'SearchSelectValue' + modifier].value == '' ||
scope[iterator + 'SearchSelectValue' + modifier].value == null) ) {
scope[iterator + 'SearchParams'] += 'iexact=';
}
else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'or')) ) {
scope[iterator + 'SearchParams'] = ''; //start over
var val = scope[iterator + 'SearchValue' + modifier];
for (var k=0; k < list.fields[scope[iterator + 'SearchField' + modifier]].searchFields.length; k++) {
scope[iterator + 'SearchParams'] += '&or__' +
list.fields[scope[iterator + 'SearchField' + modifier]].searchFields[k] +
'__icontains=' + escape(val);
}
scope[iterator + 'SearchParams'].replace(/^\&/,'');
}
else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '=';
}
if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'boolean'
|| list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'select') ) {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue' + modifier].value;
}
else {
if ( (!list.fields[scope[iterator + 'SearchField' + modifier]].searchType) ||
(list.fields[scope[iterator + 'SearchField' + modifier]].searchType &&
list.fields[scope[iterator + 'SearchField' + modifier]].searchType !== 'or') ) {
scope[iterator + 'SearchParams'] += escape(scope[iterator + 'SearchValue' + modifier]);
}
}
}
}
}
if ( (iterator == 'inventory' && scope.inventoryFailureFilter) ||
(iterator == 'host' && scope.hostFailureFilter) ) {
//Things that bypass the search widget. Should go back and add a second widget possibly on
//inventory pages and eliminate this
scope[iterator + 'SearchParams'] += '&has_active_failures=true';
}
if (sort_order) {
scope[iterator + 'SearchParams'] += (scope[iterator + 'SearchParams']) ? '&' : '';
scope[iterator + 'SearchParams'] += 'order_by=' + escape(sort_order);
}
scope.$emit('doSearch', iterator, page, load, spin);
});
scope.startSearch = function(iterator) {
//Called on each keydown event for seachValue field. Using a timer
//to prevent executing a search until user is finished typing.
if (scope.searchTimer) {
$timeout.cancel(scope.searchTimer);
}
scope.searchTimer = $timeout(
function() {
scope.$emit('prepareSearch', iterator);
}
, 1000);
} }
scope.search = function(iterator, page, load, spin) { scope.search = function(iterator, page, load, spin) {
// Called to initiate a searh.
// Page is optional. Added to accomodate back function on Job Events detail. // Page is optional. Added to accomodate back function on Job Events detail.
// Spin optional -set to false if spin not desired. // Spin optional -set to false if spin not desired.
// Load optional -set to false if loading message not desired // Load optional -set to false if loading message not desired
scope.$emit('prepareSearch', iterator, page, load, spin);
scope[iterator + 'SearchSpin'] = (spin == undefined || spin == true) ? true : false;
scope[iterator + 'Loading'] = (load == undefined || load == true) ? true : false;
scope[iterator + 'SearchParms'] = '';
var url = defaultUrl;
if ( (scope[iterator + 'SelectShow'] == false && scope[iterator + 'SearchValue'] != '' && scope[iterator + 'SearchValue'] != undefined) ||
(scope[iterator + 'SelectShow'] && scope[iterator + 'SearchSelectValue']) ||
(list.fields[scope[iterator + 'SearchField']].searchType && list.fields[scope[iterator + 'SearchField']].searchType == 'gtzero') ) {
if (list.fields[scope[iterator + 'SearchField']].searchField) {
scope[iterator + 'SearchParams'] = list.fields[scope[iterator + 'SearchField']].searchField + '__';
}
else if (list.fields[scope[iterator + 'SearchField']].sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] = list.fields[scope[iterator + 'SearchField']].sourceModel + '__' +
list.fields[scope[iterator + 'SearchField']].sourceField + '__';
}
else if ( (list.fields[scope[iterator + 'SearchField']].searchType == 'select') &&
(scope[iterator + 'SearchSelectValue'].value == '' ||
scope[iterator + 'SearchSelectValue'].value == null) ) {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'];
}
else {
scope[iterator + 'SearchParams'] = scope[iterator + 'SearchField'] + '__';
}
if ( list.fields[scope[iterator + 'SearchField']].searchType &&
(list.fields[scope[iterator + 'SearchField']].searchType == 'int' ||
list.fields[scope[iterator + 'SearchField']].searchType == 'boolean' ) ) {
scope[iterator + 'SearchParams'] += 'int=';
}
else if ( list.fields[scope[iterator + 'SearchField']].searchType &&
list.fields[scope[iterator + 'SearchField']].searchType == 'gtzero' ) {
scope[iterator + 'SearchParams'] += 'gt=0';
}
else if ( (list.fields[scope[iterator + 'SearchField']].searchType == 'select') &&
(scope[iterator + 'SearchSelectValue'].value == '' ||
scope[iterator + 'SearchSelectValue'].value == null) ) {
scope[iterator + 'SearchParams'] += 'iexact=';
}
else {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType'] + '=';
}
if ( list.fields[scope[iterator + 'SearchField']].searchType &&
(list.fields[scope[iterator + 'SearchField']].searchType == 'boolean'
|| list.fields[scope[iterator + 'SearchField']].searchType == 'select') ) {
scope[iterator + 'SearchParams'] += scope[iterator + 'SearchSelectValue'].value;
}
else {
//if ( list.fields[scope[iterator + 'SearchField']].searchType == undefined ||
// list.fields[scope[iterator + 'SearchField']].searchType == 'gtzero' ) {
scope[iterator + 'SearchParams'] += escape(scope[iterator + 'SearchValue']);
}
scope[iterator + 'SearchParams'] += (sort_order) ? '&order_by=' + escape(sort_order) : '';
}
else {
scope[iterator + 'SearchParams'] = (sort_order) ? 'order_by=' + escape(sort_order) : "";
}
if ( (iterator == 'inventory' && scope.inventoryFailureFilter) ||
(iterator == 'host' && scope.hostFailureFilter) ) {
scope[iterator + 'SearchParams'] += '&has_active_failures=true';
}
scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0;
if (/\/$/.test(url)) {
url += '?' + scope[iterator + 'SearchParams'];
}
else {
url += '&' + scope[iterator + 'SearchParams'];
}
url = url.replace(/\&\&/,'&');
url += (scope[iterator + 'PageSize']) ? '&page_size=' + scope[iterator + 'PageSize'] : "";
if (page) {
url += '&page=' + page;
}
if (scope[iterator + 'ExtraParms']) {
url += scope[iterator + 'ExtraParms'];
}
Refresh({ scope: scope, set: set, iterator: iterator, url: url });
} }
scope.sort = function(fld) { scope.sort = function(fld) {
// reset sort icons back to 'icon-sort' on all columns // reset sort icons back to 'icon-sort' on all columns
// except the one clicked // except the one clicked

View File

@@ -35,6 +35,15 @@ angular.module('OrganizationListDefinition', [])
ngClick: 'addOrganization()', ngClick: 'addOrganization()',
"class": 'btn-success btn-xs', "class": 'btn-success btn-xs',
awToolTip: 'Create a new organization' awToolTip: 'Create a new organization'
},
stream: {
'class': "btn-primary btn-xs activity-btn",
ngClick: "showActivity()",
awToolTip: "View Activity Stream",
dataPlacement: "top",
icon: "icon-comments-alt",
mode: 'all',
iconSize: 'large'
} }
}, },

View File

@@ -17,22 +17,102 @@ angular.module('StreamListDefinition', [])
index: false, index: false,
hover: true, hover: true,
"class": "table-condensed", "class": "table-condensed",
searchWidgetLabel: 'Object',
searchWidgetLabel2: 'Modified by',
fields: { fields: {
event_time: { timestamp: {
label: 'Event Time',
key: true, key: true,
label: 'When' desc: true,
noLink: true,
searchable: false
}, },
user: { user: {
label: 'Who', label: 'User',
ngBindHtml: 'activity.user',
sourceModel: 'user', sourceModel: 'user',
sourceField: 'username' sourceField: 'username',
awToolTip: "\{\{ userToolTip \}\}",
dataPlacement: 'top',
searchPlaceholder: 'Username',
searchWidget: 2
}, },
operation: { objects: {
label: 'Operation' label: 'Objects',
ngBindHtml: 'activity.objects',
nosort: true,
searchable: false
}, },
description: { description: {
label: 'Description' label: 'Description',
ngBindHtml: 'activity.description',
nosort: true,
searchable: false
},
system_event: {
label: 'System',
searchOnly: true,
searchType: 'isnull',
sourceModel: 'user',
sourceField: 'username',
searchWidget: 2
},
// The following fields exist to forces loading each type of object into the search
// dropdown
all_objects: {
label: 'All',
searchOnly: true,
searchObject: 'all',
searchPlaceholder: ' '
},
credential_search: {
label: 'Credential',
searchOnly: true,
searchObject: 'credentials',
searchPlaceholder: 'Credential name'
},
group_search: {
label: 'Group',
searchOnly: true,
searchObject: 'groups',
searchPlaceholder: 'Group name'
},
host_search: {
label: 'Host',
searchOnly: true,
searchObject: 'hosts',
searchPlaceholder: 'Host name'
},
inventory_search: {
label: 'Inventory',
searchOnly: true,
searchObject: 'inventories',
searchPlaceholder: 'Inventory name'
},
job_template_search: {
label: 'Job Template',
searchOnly: true,
searchObject: 'job_templates',
searchPlaceholder: 'Job template name'
},
organization_search: {
label: 'Organization',
searchOnly: true,
searchObject: 'organizations',
searchPlaceholder: 'Organization name'
},
project_search: {
label: 'Project',
searchOnly: true,
searchObject: 'projects',
searchPlaceholder: 'Project name'
},
user_search: {
label: 'User',
searchOnly: true,
searchObject: 'users',
searchPlaceholder: 'Username'
} }
}, },
@@ -58,5 +138,14 @@ angular.module('StreamListDefinition', [])
}, },
fieldActions: { fieldActions: {
edit: {
label: 'View',
ngClick: "showDetail(\{\{ activity.id \}\})",
icon: 'icon-zoom-in',
"class": 'btn-default btn-xs',
awToolTip: 'View event details',
dataPlacement: 'top'
}
} }
}); });

View File

@@ -27,7 +27,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
// Try not to overlap footer. Because stream is positioned absolute, the parent // Try not to overlap footer. Because stream is positioned absolute, the parent
// doesn't resize correctly when stream is loaded. // doesn't resize correctly when stream is loaded.
$('#tab-content-container').css({ 'min-height': stream.height() }); $('#tab-content-container').css({ 'min-height': stream.height() + 50 });
// Slide in stream // Slide in stream
stream.show('slide', {'direction': 'left'}, {'duration': 500, 'queue': false }); stream.show('slide', {'direction': 'left'}, {'duration': 500, 'queue': false });
@@ -35,9 +35,10 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
} }
}]) }])
.factory('HideStream', [ 'ClearScope', function(ClearScope) { .factory('HideStream', [ function() {
return function() { return function() {
// Remove the stream widget // Remove the stream widget
var stream = $('#stream-container'); var stream = $('#stream-container');
stream.hide('slide', {'direction': 'left'}, {'duration': 500, 'queue': false }); stream.hide('slide', {'direction': 'left'}, {'duration': 500, 'queue': false });
@@ -54,15 +55,140 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
} }
}]) }])
.factory('FixUrl', [ function() {
return function(u) {
return u.replace(/\/api\/v1\//,'/#/');
}
}])
.factory('BuildUrl', [ function() {
return function(obj) {
var url = '/#/';
switch(obj.base) {
case 'group':
case 'host':
url += 'home/' + obj.base + 's/?name=' + obj.name;
break;
case 'inventory':
url += 'inventories/' + obj.id;
break;
default:
url += obj.base + 's/' + obj.id;
}
return url;
}
}])
.factory('BuildDescription', ['FixUrl', 'BuildUrl', function(FixUrl, BuildUrl) {
return function(activity) {
var descr = '';
if (activity.summary_fields.user) {
// this is a user transaction
var usr = FixUrl(activity.related.user);
descr += 'User <a href=\"' + usr + '\">' + activity.summary_fields.user.username + '</a> ';
}
else {
descr += 'System ';
}
descr += activity.operation;
descr += (/e$/.test(activity.operation)) ? 'd ' : 'ed ';
if (activity.summary_fields.object2) {
descr += activity.summary_fields.object2.base + ' <a href=\"' + BuildUrl(activity.summary_fields.object2) + '\">'
+ activity.summary_fields.object2.name + '</a>' + [ (activity.operation == 'disassociate') ? ' from ' : ' to '];
}
if (activity.summary_fields.object1) {
descr += activity.summary_fields.object1.base + ' <a href=\"' + BuildUrl(activity.summary_fields.object1) + '\">'
+ activity.summary_fields.object1.name + '</a>';
}
return descr;
}
}])
.factory('ShowDetail', ['Rest', 'Alert', 'GenerateForm', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'ActivityDetailForm',
function(Rest, Alert, GenerateForm, ProcessErrors, GetBasePath, FormatDate, ActivityDetailForm) {
return function(activity_id) {
var generator = GenerateForm;
var form = ActivityDetailForm;
var scope;
var url = GetBasePath('activity_stream') + activity_id + '/';
// Retrieve detail record and prepopulate the form
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
// load up the form
var results = data;
$('#form-modal').on('show.bs.modal', function (e) {
$('#form-modal-body').css({
width:'auto', //probably not needed
height:'auto', //probably not needed
'max-height':'100%'
});
});
//var n = results['changes'].match(/\n/g);
//var rows = (n) ? n.length : 1;
//rows = (rows < 1) ? 3 : 10;
form.fields['changes'].rows = 10;
scope = generator.inject(form, { mode: 'edit', modal: true, related: false});
generator.reset();
for (var fld in form.fields) {
if (results[fld]) {
if (fld == 'timestamp') {
scope[fld] = FormatDate(new Date(results[fld]));
}
else {
scope[fld] = results[fld];
}
}
}
if (results.summary_fields.object1) {
scope['object1_name'] = results.summary_fields.object1.name;
}
if (results.summary_fields.object2) {
scope['object2_name'] = results.summary_fields.object2.name;
}
scope['changes'] = JSON.stringify(results['changes'], null, '\t');
scope.formModalAction = function() {
$('#form-modal').modal("hide");
}
scope.formModalActionLabel = 'OK';
scope.formModalCancelShow = false;
scope.formModalInfo = false;
//scope.formModalHeader = results.summary_fields.project.name + '<span class="subtitle"> - SCM Status</span>';
$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
$('#form-modal').addClass('skinny-modal');
if (!scope.$$phase) {
scope.$digest();
}
})
.error( function(data, status, headers, config) {
$('#form-modal').modal("hide");
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve activity: ' + activity_id + '. GET status: ' + status });
});
}
}])
.factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit', .factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit',
'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream', 'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream', 'BuildDescription', 'FixUrl', 'BuildUrl',
'ShowDetail',
function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList, function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList,
FormatDate, ShowStream, HideStream) { FormatDate, ShowStream, HideStream, BuildDescription, FixUrl, BuildUrl, ShowDetail) {
return function(params) { return function(params) {
var list = StreamList; var list = StreamList;
var defaultUrl = $basePath + 'html/event_log.html/'; var defaultUrl = GetBasePath('activity_stream');
var view = GenerateList; var view = GenerateList;
var base = $location.path().replace(/^\//,'').split('/')[0];
if (base !== 'home') {
var type = (base == 'inventories') ? 'inventory' : base.replace(/s$/,'');
defaultUrl += '?or__object1=' + type + '&or__object2=' + type;
}
// Push the current page onto browser histor. If user clicks back button, restore current page without // Push the current page onto browser histor. If user clicks back button, restore current page without
// stream widget // stream widget
@@ -70,23 +196,29 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
// Add a container for the stream widget // Add a container for the stream widget
$('#tab-content-container').append('<div id="stream-container"><div id=\"stream-content\"></div></div><!-- Stream widget -->'); $('#tab-content-container').append('<div id="stream-container"><div id=\"stream-content\"></div></div><!-- Stream widget -->');
ShowStream();
// Generate the list // Generate the list
var scope = view.inject(list, { var scope = view.inject(list, {
mode: 'edit', mode: 'edit',
id: 'stream-content', id: 'stream-content',
breadCrumbs: true, breadCrumbs: true,
searchSize: 'col-lg-4' searchSize: 'col-lg-3',
secondWidget: true
}); });
scope.closeStream = function() { scope.closeStream = function() {
HideStream(); HideStream();
} }
scope.refreshStream = function() { scope.refreshStream = function() {
scope['activities'].splice(10,10); scope.search(list.iterator);
//scope.search(list.iterator); }
}
scope.showDetail = function(id) {
ShowDetail(id);
}
if (scope.removePostRefresh) { if (scope.removePostRefresh) {
scope.removePostRefresh(); scope.removePostRefresh();
@@ -94,12 +226,41 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
scope.removePostRefresh = scope.$on('PostRefresh', function() { scope.removePostRefresh = scope.$on('PostRefresh', function() {
for (var i=0; i < scope['activities'].length; i++) { for (var i=0; i < scope['activities'].length; i++) {
// Convert event_time date to local time zone // Convert event_time date to local time zone
cDate = new Date(scope['activities'][i].event_time); cDate = new Date(scope['activities'][i].timestamp);
scope['activities'][i].event_time = FormatDate(cDate); scope['activities'][i].timestamp = FormatDate(cDate);
// Display username // Display username
scope['activities'][i].user = scope.activities[i].summary_fields.user.username; scope['activities'][i].user = (scope['activities'][i].summary_fields.user) ? scope['activities'][i].summary_fields.user.username :
'system';
if (scope['activities'][i].user !== 'system') {
// turn user into a link when not 'system'
scope['activities'][i].user = "<a href=\"" + FixUrl(scope['activities'][i].related.user) + "\">" +
scope['activities'][i].user + "</a>";
}
// Objects
var href;
var deleted = /^\_delete/;
if (scope['activities'][i].summary_fields.object1) {
if ( !deleted.test(scope['activities'][i].summary_fields.object1.name) ) {
href = BuildUrl(scope['activities'][i].summary_fields.object1);
scope['activities'][i].objects = "<a href=\"" + href + "\">" + scope['activities'][i].summary_fields.object1.name + "</a>";
}
else {
scope['activities'][i].objects = scope['activities'][i].summary_fields.object1.name;
}
}
if (scope['activities'][i].summary_fields.object2) {
if ( !deleted.test(scope['activities'][i].summary_fields.object2.name) ) {
href = BuildUrl(scope['activities'][i].summary_fields.object2);
scope['activities'][i].objects += ", <a href=\"" + href + "\">" + scope['activities'][i].summary_fields.object2.name + "</a>";
}
else {
scope['activities'][i].objects += scope['activities'][i].summary_fields.object2.name;
}
}
scope['activities'][i].description = BuildDescription(scope['activities'][i]);
} }
ShowStream();
}); });
// Initialize search and paginate pieces and load data // Initialize search and paginate pieces and load data
@@ -107,6 +268,15 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
PaginateInit({ scope: scope, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl });
scope.search(list.iterator); scope.search(list.iterator);
/*
scope.$watch(list.iterator + 'SearchField', function(newVal, oldVal) {
console.log('newVal: ' + newVal);
html += ""
html += "<input id=\"search_attribute_input\" type=\"text\" ng-show=\"" + iterator + "ShowAttribute\" class=\"form-control ";
html += "\" ng-model=\"" + iterator + "AttributeValue\" ng-change=\"search('" + iterator +
"')\" aw-placeholder=\"" + iterator + "AttributePlaceholder\" type=\"text\">\n";
});*/
} }
}]); }]);

View File

@@ -151,6 +151,20 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
} }
} }
}) })
// awPlaceholder: Dynamic placeholder set to a scope variable you want watched.
// Value will be place in field placeholder attribute.
.directive('awPlaceholder', [ function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
$(elm).attr('placeholder', scope[attrs.awPlaceholder]);
scope.$watch(attrs.awPlaceholder, function(newVal, oldVal) {
$(elm).attr('placeholder',newVal);
});
}
}
}])
// lookup Validate lookup value against API // lookup Validate lookup value against API
// //

View File

@@ -340,6 +340,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += (field.ngClass) ? Attr(field, 'ngClass') : ""; html += (field.ngClass) ? Attr(field, 'ngClass') : "";
html += (options.mode == 'lookup' || options.mode == 'select') ? " ng-click=\"toggle_" + list.iterator +"({{ " + list.iterator + ".id }})\"" : ""; html += (options.mode == 'lookup' || options.mode == 'select') ? " ng-click=\"toggle_" + list.iterator +"({{ " + list.iterator + ".id }})\"" : "";
html += (field.columnShow) ? Attr(field, 'columnShow') : ""; html += (field.columnShow) ? Attr(field, 'columnShow') : "";
html += (field.ngBindHtml) ? "ng-bind-html-unsafe=\"" + field.ngBindHtml + "\" " : "";
html += ">\n"; html += ">\n";
// Add ngShow // Add ngShow
@@ -359,7 +360,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
// Start the Link // Start the Link
if ( (field.key || field.link || field.linkTo || field.ngClick || field.ngHref) && if ( (field.key || field.link || field.linkTo || field.ngClick || field.ngHref) &&
options['mode'] != 'lookup' && options['mode'] != 'select' && !field.noLink ) { options['mode'] != 'lookup' && options['mode'] != 'select' && !field.noLink && !field.ngBindHtml) {
var cap=false; var cap=false;
if (field.linkTo) { if (field.linkTo) {
html += "<a href=\"" + field.linkTo + "\" "; html += "<a href=\"" + field.linkTo + "\" ";
@@ -397,7 +398,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
} }
// Add data binds // Add data binds
if (field.showValue == undefined || field.showValue == true) { if (!field.ngBindHtml && (field.showValue == undefined || field.showValue == true)) {
if (field.ngBind) { if (field.ngBind) {
html += "{{ " + field.ngBind + " }}"; html += "{{ " + field.ngBind + " }}";
} }
@@ -417,7 +418,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
// close the link // close the link
if ( (field.key || field.link || field.linkTo || field.ngClick || field.ngHref ) if ( (field.key || field.link || field.linkTo || field.ngClick || field.ngHref )
&& options.mode != 'lookup' && options.mode != 'select' && !field.noLink ) { && options.mode != 'lookup' && options.mode != 'select' && !field.noLink && !field.ngBindHtml ) {
html += "</a>"; html += "</a>";
} }
// close ngShow // close ngShow
@@ -476,20 +477,20 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
var iterator = params.iterator; var iterator = params.iterator;
var form = params.template; var form = params.template;
var useMini = params.mini; var useMini = params.mini;
var label = (params.label) ? params.label : null; //var label = (params.label) ? params.label : null;
var html= ''; var html= '';
var secondWidget = params.secondWidget;
html += "<div class=\"row search-widget\">\n"; html += "<div class=\"row search-widget\">\n";
html += "<div class=\""; html += "<div class=\"";
html += (params.size) ? params.size : "col-lg-4 col-md-6 col-sm-11 col-xs-11"; html += (params.size) ? params.size : "col-lg-4 col-md-6 col-sm-11 col-xs-11";
html += "\">\n"; html += "\" id=\"search-widget-container\">\n";
html += (label) ? "<label>" + label +"</label>" : ""; html += (form.searchWidgetLabel) ? "<label style=\"display:block\">" + form.searchWidgetLabel +"</label>" : "";
html += "<div class=\"input-group"; html += "<div class=\"input-group";
html += (useMini) ? " input-group-sm" : " input-group-sm"; html += (useMini) ? " input-group-sm" : " input-group-sm";
html += "\">\n"; html += "\">\n";
html += "<div class=\"input-group-btn dropdown\">\n"; html += "<div class=\"input-group-btn dropdown\">\n";
// Use standard button on mobile
html += "<button type=\"button\" "; html += "<button type=\"button\" ";
html += "id=\"search_field_ddown\" "; html += "id=\"search_field_ddown\" ";
html += "class=\"btn "; html += "class=\"btn ";
@@ -498,27 +499,22 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n"; html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n";
html += "<span class=\"caret\"></span>\n"; html += "<span class=\"caret\"></span>\n";
html += "</button>\n"; html += "</button>\n";
// Use link and hover activation on desktop
//html += "<a href=\"\" id=\"search_field_ddown\" class=\"btn btn-default visible-lg\">";
//html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n";
//html += "<span class=\"caret\"></span>\n";
//html += "</a>\n";
html += "<ul class=\"dropdown-menu\" id=\"" + iterator + "SearchDropdown\">\n"; html += "<ul class=\"dropdown-menu\" id=\"" + iterator + "SearchDropdown\">\n";
for ( var fld in form.fields) { for ( var fld in form.fields) {
if (form.fields[fld].searchable == undefined || form.fields[fld].searchable == true) { if ( (form.fields[fld].searchable == undefined || form.fields[fld].searchable == true)
html += "<li><a href=\"\" ng-click=\"setSearchField('" + iterator + "','"; && (form.fields[fld].searchWidget == undefined || form.fields[fld].searchWidget == 1) ) {
html += fld + "','"; html += "<li><a href=\"\" ng-click=\"setSearchField('" + iterator + "','";
if (form.fields[fld].searchLabel) { html += fld + "','";
html += form.fields[fld].searchLabel + "')\">" + if (form.fields[fld].searchLabel) {
form.fields[fld].searchLabel + "</a></li>\n"; html += form.fields[fld].searchLabel + "', 1)\">" +
} form.fields[fld].searchLabel + "</a></li>\n";
else { }
html += form.fields[fld].label.replace(/\<br\>/g,' ') + "')\">" + else {
form.fields[fld].label.replace(/\<br\>/g,' ') + "</a></li>\n"; html += form.fields[fld].label.replace(/\<br\>/g,' ') + "')\">" +
} form.fields[fld].label.replace(/\<br\>/g,' ') + "</a></li>\n";
} }
}
} }
html += "</ul>\n"; html += "</ul>\n";
html += "</div><!-- input-group-btn -->\n"; html += "</div><!-- input-group-btn -->\n";
@@ -528,37 +524,71 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += "\"></select>\n"; html += "\"></select>\n";
html += "<input id=\"search_value_input\" type=\"text\" ng-hide=\"" + iterator + "SelectShow || " + iterator + "InputHide\" class=\"form-control "; html += "<input id=\"search_value_input\" type=\"text\" ng-hide=\"" + iterator + "SelectShow || " + iterator + "InputHide\" class=\"form-control ";
html += "\" ng-model=\"" + iterator + "SearchValue\" ng-change=\"search('" + iterator + html += "\" ng-model=\"" + iterator + "SearchValue\" ng-keydown=\"startSearch('" + iterator + "')\" " +
"')\" placeholder=\"Search\" type=\"text\" ng-disabled=\"" + iterator + "InputDisable\">\n"; "aw-placeholder=\"" + iterator + "SearchPlaceholder\" type=\"text\" ng-disabled=\"" + iterator + "InputDisable || " + iterator +
"HoldInput\">\n";
/*
html += "<div class=\"input-group-btn dropdown\">\n";
html += "<button type=\"button\" ";
html += "id=\"search_option_ddown\" ";
html += "ng-hide=\"" + iterator + "SelectShow || " + iterator + "HideSearchType || " + iterator + "InputHide\" class=\"btn ";
html += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
html += "<span ng-bind=\"" + iterator + "SearchTypeLabel\"></span>\n";
html += "<span class=\"caret\"></span>\n";
html += "</button>\n";
html += "<ul class=\"dropdown-menu pull-right\">\n";
html += "<li><a href=\"\" ng-click=\"setSearchType('" + iterator + "','iexact','Exact Match')\">Exact Match</a></li>\n";
html += "<li><a href=\"\" ng-click=\"setSearchType('" + iterator + "','icontains','Contains')\">Contains</a></li>\n";
html += "</ul>\n";
html += "</div><!-- input-group-btn -->\n";
*/
// Reset button // Reset button
html += "<div class=\"input-group-btn\">\n"; html += "<div class=\"input-group-btn\">\n";
html += "<button type=\"button\" class=\"btn btn-default btn-small\" ng-click=\"resetSearch('" + iterator + "')\" " + html += "<button type=\"button\" class=\"btn btn-default btn-small\" ng-click=\"resetSearch('" + iterator + "', 1)\" " +
"aw-tool-tip=\"Reset filter\" data-placement=\"top\" " + "aw-tool-tip=\"Reset filter\" data-placement=\"top\" " +
"><i class=\"icon-undo\"></i></button>\n"; "><i class=\"icon-undo\"></i></button>\n";
html += "</div><!-- input-group-btn -->\n"; html += "</div><!-- input-group-btn -->\n";
html += "</div><!-- input-group -->\n"; html += "</div><!-- input-group -->\n";
html += "</div><!-- col-lg-x -->\n"; html += "</div><!-- col-lg-x -->\n";
// Search Widget 2
// Used on activity stream. Set 'searchWidget2: true' on fields to be included.
if (secondWidget) {
html += "<div class=\"col-lg-3\" id=\"search-widget-container2\">\n";
html += (form.searchWidgetLabel2) ? "<label style=\"display:block\">" + form.searchWidgetLabel2 +"</label>" : "";
html += "<div class=\"input-group input-group-sm\">\n";
html += "<div class=\"input-group-btn dropdown2\">\n";
html += "<button type=\"button\" ";
html += "id=\"search_field_ddown2\" ";
html += "class=\"btn dropdown-toggle\" data-toggle=\"dropdown\">\n";
html += "<span ng-bind=\"" + iterator + "SearchFieldLabel2\"></span>\n";
html += "<span class=\"caret\"></span>\n";
html += "</button>\n";
html += "<ul class=\"dropdown-menu\" id=\"" + iterator + "SearchDropdown2\">\n";
for ( var fld in form.fields) {
if ( (form.fields[fld].searchable == undefined || form.fields[fld].searchable == true)
&& form.fields[fld].searchWidget == 2 ) {
html += "<li><a href=\"\" ng-click=\"setSearchField('" + iterator + "','";
html += fld + "','";
if (form.fields[fld].searchLabel) {
html += form.fields[fld].searchLabel + "', 2)\">" +
form.fields[fld].searchLabel + "</a></li>\n";
}
else {
html += form.fields[fld].label.replace(/\<br\>/g,' ') + "', 2)\">" +
form.fields[fld].label.replace(/\<br\>/g,' ') + "</a></li>\n";
}
}
}
html += "</ul>\n";
html += "</div><!-- input-group-btn -->\n";
html += "<input id=\"search_value_input\" type=\"text\" ng-hide=\"" + iterator + "SelectShow2 || " + iterator + "InputHide2\" class=\"form-control ";
html += "\" ng-model=\"" + iterator + "SearchValue2\" ng-keydown=\"startSearch('" + iterator + "')\" " +
"aw-placeholder=\"" + iterator + "SearchPlaceholder2\" type=\"text\" ng-disabled=\"" + iterator + "InputDisable2 || " + iterator +
"HoldInput2\">\n";
// Reset button
html += "<div class=\"input-group-btn\">\n";
html += "<button type=\"button\" class=\"btn btn-default btn-small\" ng-click=\"resetSearch('" + iterator + "', 2)\" " +
"aw-tool-tip=\"Reset filter\" data-placement=\"top\" " +
"><i class=\"icon-undo\"></i></button>\n";
html += "</div><!-- input-group-btn -->\n";
html += "</div><!-- input-group -->\n";
html += "</div><!-- col-lg-x -->\n";
}
// Spinner
html += "<div class=\"col-lg-1 col-md-1 col-sm-1 col-xs-1\"><i class=\"icon-spinner icon-spin icon-large\" ng-show=\"" + iterator + html += "<div class=\"col-lg-1 col-md-1 col-sm-1 col-xs-1\"><i class=\"icon-spinner icon-spin icon-large\" ng-show=\"" + iterator +
"SearchSpin == true\"></i></div>\n"; "SearchSpin == true\"></i></div>\n";
return html; return html;
} }

View File

@@ -194,7 +194,8 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
*/ */
if (options.searchSize) { if (options.searchSize) {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize }); html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize,
secondWidget: options.secondWidget });
} }
else if (options.mode == 'summary') { else if (options.mode == 'summary') {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-6' }); html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-6' });
@@ -214,6 +215,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
if (options.searchSize) { if (options.searchSize) {
// User supplied searchSize, calc the remaining // User supplied searchSize, calc the remaining
var size = parseInt(options.searchSize.replace(/([A-Z]|[a-z]|\-)/g,'')); var size = parseInt(options.searchSize.replace(/([A-Z]|[a-z]|\-)/g,''));
size += (options.secondWidget) ? 3 : 0;
html += 'col-lg-' + (11 - size); html += 'col-lg-' + (11 - size);
} }
else if (options.mode == 'summary') { else if (options.mode == 'summary') {

View File

@@ -75,6 +75,7 @@
<script src="{{ STATIC_URL }}js/forms/License.js"></script> <script src="{{ STATIC_URL }}js/forms/License.js"></script>
<script src="{{ STATIC_URL }}js/forms/HostGroups.js"></script> <script src="{{ STATIC_URL }}js/forms/HostGroups.js"></script>
<script src="{{ STATIC_URL }}js/forms/InventoryStatus.js"></script> <script src="{{ STATIC_URL }}js/forms/InventoryStatus.js"></script>
<script src="{{ STATIC_URL }}js/forms/ActivityDetail.js"></script>
<script src="{{ STATIC_URL }}js/lists/Users.js"></script> <script src="{{ STATIC_URL }}js/lists/Users.js"></script>
<script src="{{ STATIC_URL }}js/lists/Organizations.js"></script> <script src="{{ STATIC_URL }}js/lists/Organizations.js"></script>
<script src="{{ STATIC_URL }}js/lists/Admins.js"></script> <script src="{{ STATIC_URL }}js/lists/Admins.js"></script>