mirror of
https://github.com/ansible/awx.git
synced 2026-05-23 08:37:48 -02:30
Merge branch 'rbac' of github.com:ansible/ansible-tower into rbac
This commit is contained in:
@@ -924,7 +924,6 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
|||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(ProjectSerializer, self).get_related(obj)
|
res = super(ProjectSerializer, self).get_related(obj)
|
||||||
res.update(dict(
|
res.update(dict(
|
||||||
organizations = reverse('api:project_organizations_list', args=(obj.pk,)),
|
|
||||||
teams = reverse('api:project_teams_list', args=(obj.pk,)),
|
teams = reverse('api:project_teams_list', args=(obj.pk,)),
|
||||||
playbooks = reverse('api:project_playbooks', args=(obj.pk,)),
|
playbooks = reverse('api:project_playbooks', args=(obj.pk,)),
|
||||||
update = reverse('api:project_update_view', args=(obj.pk,)),
|
update = reverse('api:project_update_view', args=(obj.pk,)),
|
||||||
@@ -936,6 +935,9 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
|||||||
notifiers_error = reverse('api:project_notifiers_error_list', args=(obj.pk,)),
|
notifiers_error = reverse('api:project_notifiers_error_list', args=(obj.pk,)),
|
||||||
access_list = reverse('api:project_access_list', args=(obj.pk,)),
|
access_list = reverse('api:project_access_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
|
if obj.organization:
|
||||||
|
res['organization'] = reverse('api:organization_detail',
|
||||||
|
args=(obj.organization.pk,))
|
||||||
# Backwards compatibility.
|
# Backwards compatibility.
|
||||||
if obj.current_update:
|
if obj.current_update:
|
||||||
res['current_update'] = reverse('api:project_update_detail',
|
res['current_update'] = reverse('api:project_update_detail',
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ project_urls = patterns('awx.api.views',
|
|||||||
url(r'^$', 'project_list'),
|
url(r'^$', 'project_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/$', 'project_detail'),
|
url(r'^(?P<pk>[0-9]+)/$', 'project_detail'),
|
||||||
url(r'^(?P<pk>[0-9]+)/playbooks/$', 'project_playbooks'),
|
url(r'^(?P<pk>[0-9]+)/playbooks/$', 'project_playbooks'),
|
||||||
url(r'^(?P<pk>[0-9]+)/organizations/$', 'project_organizations_list'),
|
|
||||||
url(r'^(?P<pk>[0-9]+)/teams/$', 'project_teams_list'),
|
url(r'^(?P<pk>[0-9]+)/teams/$', 'project_teams_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/update/$', 'project_update_view'),
|
url(r'^(?P<pk>[0-9]+)/update/$', 'project_update_view'),
|
||||||
url(r'^(?P<pk>[0-9]+)/project_updates/$', 'project_updates_list'),
|
url(r'^(?P<pk>[0-9]+)/project_updates/$', 'project_updates_list'),
|
||||||
|
|||||||
@@ -829,13 +829,6 @@ class ProjectPlaybooks(RetrieveAPIView):
|
|||||||
model = Project
|
model = Project
|
||||||
serializer_class = ProjectPlaybooksSerializer
|
serializer_class = ProjectPlaybooksSerializer
|
||||||
|
|
||||||
class ProjectOrganizationsList(SubListCreateAttachDetachAPIView):
|
|
||||||
|
|
||||||
model = Organization
|
|
||||||
serializer_class = OrganizationSerializer
|
|
||||||
parent_model = Project
|
|
||||||
relationship = 'organizations'
|
|
||||||
|
|
||||||
class ProjectTeamsList(SubListCreateAttachDetachAPIView):
|
class ProjectTeamsList(SubListCreateAttachDetachAPIView):
|
||||||
|
|
||||||
model = Team
|
model = Team
|
||||||
|
|||||||
@@ -1048,7 +1048,7 @@ class JobHostSummaryAccess(BaseAccess):
|
|||||||
model = JobHostSummary
|
model = JobHostSummary
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = self.model.accessible_objects(self.user, {'read':True})
|
qs = self.model.objects
|
||||||
qs = qs.select_related('job', 'job__job_template', 'host')
|
qs = qs.select_related('job', 'job__job_template', 'host')
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
@@ -1073,7 +1073,7 @@ class JobEventAccess(BaseAccess):
|
|||||||
model = JobEvent
|
model = JobEvent
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = self.model.accessible_objects(self.user, {'read':True})
|
qs = self.model.objects
|
||||||
qs = qs.select_related('job', 'job__job_template', 'host', 'parent')
|
qs = qs.select_related('job', 'job__job_template', 'host', 'parent')
|
||||||
qs = qs.prefetch_related('hosts', 'children')
|
qs = qs.prefetch_related('hosts', 'children')
|
||||||
|
|
||||||
@@ -1110,7 +1110,7 @@ class UnifiedJobTemplateAccess(BaseAccess):
|
|||||||
model = UnifiedJobTemplate
|
model = UnifiedJobTemplate
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = self.model.accessible_objects(self.user, {'read':True})
|
qs = self.model.objects
|
||||||
project_qs = self.user.get_queryset(Project).filter(scm_type__in=[s[0] for s in Project.SCM_TYPE_CHOICES])
|
project_qs = self.user.get_queryset(Project).filter(scm_type__in=[s[0] for s in Project.SCM_TYPE_CHOICES])
|
||||||
inventory_source_qs = self.user.get_queryset(InventorySource).filter(source__in=CLOUD_INVENTORY_SOURCES)
|
inventory_source_qs = self.user.get_queryset(InventorySource).filter(source__in=CLOUD_INVENTORY_SOURCES)
|
||||||
job_template_qs = self.user.get_queryset(JobTemplate)
|
job_template_qs = self.user.get_queryset(JobTemplate)
|
||||||
@@ -1142,7 +1142,7 @@ class UnifiedJobAccess(BaseAccess):
|
|||||||
model = UnifiedJob
|
model = UnifiedJob
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = self.model.accessible_objects(self.user, {'read':True})
|
qs = self.model.objects
|
||||||
project_update_qs = self.user.get_queryset(ProjectUpdate)
|
project_update_qs = self.user.get_queryset(ProjectUpdate)
|
||||||
inventory_update_qs = self.user.get_queryset(InventoryUpdate).filter(source__in=CLOUD_INVENTORY_SOURCES)
|
inventory_update_qs = self.user.get_queryset(InventoryUpdate).filter(source__in=CLOUD_INVENTORY_SOURCES)
|
||||||
job_qs = self.user.get_queryset(Job)
|
job_qs = self.user.get_queryset(Job)
|
||||||
@@ -1263,7 +1263,7 @@ class ActivityStreamAccess(BaseAccess):
|
|||||||
model = ActivityStream
|
model = ActivityStream
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = self.model.accessible_objects(self.user, {'read':True})
|
qs = self.model.objects
|
||||||
qs = qs.select_related('actor')
|
qs = qs.select_related('actor')
|
||||||
qs = qs.prefetch_related('organization', 'user', 'inventory', 'host', 'group', 'inventory_source',
|
qs = qs.prefetch_related('organization', 'user', 'inventory', 'host', 'group', 'inventory_source',
|
||||||
'inventory_update', 'credential', 'team', 'project', 'project_update',
|
'inventory_update', 'credential', 'team', 'project', 'project_update',
|
||||||
|
|||||||
@@ -200,4 +200,19 @@ class Migration(migrations.Migration):
|
|||||||
name='rolepermission',
|
name='rolepermission',
|
||||||
index_together=set([('content_type', 'object_id')]),
|
index_together=set([('content_type', 'object_id')]),
|
||||||
),
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='organization',
|
||||||
|
old_name='projects',
|
||||||
|
new_name='deprecated_projects',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='organization',
|
||||||
|
name='deprecated_projects',
|
||||||
|
field=models.ManyToManyField(related_name='deprecated_organizations', to='main.Project', blank=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='project',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(related_name='projects', to='main.Organization', blank=True, null=True),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -694,9 +694,9 @@ class ProjectAccess(BaseAccess):
|
|||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
team_ids = set(Team.objects.filter(deprecated_users__in=[self.user]).values_list('id', flat=True))
|
team_ids = set(Team.objects.filter(deprecated_users__in=[self.user]).values_list('id', flat=True))
|
||||||
qs = qs.filter(Q(created_by=self.user, organizations__isnull=True) |
|
qs = qs.filter(Q(created_by=self.user, deprecated_organizations__isnull=True) |
|
||||||
Q(organizations__deprecated_admins__in=[self.user], organizations__active=True) |
|
Q(deprecated_organizations__deprecated_admins__in=[self.user], deprecated_organizations__active=True) |
|
||||||
Q(organizations__deprecated_users__in=[self.user], organizations__active=True) |
|
Q(deprecated_organizations__deprecated_users__in=[self.user], deprecated_organizations__active=True) |
|
||||||
Q(teams__in=team_ids))
|
Q(teams__in=team_ids))
|
||||||
allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
|
allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY]
|
||||||
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
||||||
@@ -726,9 +726,9 @@ class ProjectAccess(BaseAccess):
|
|||||||
def can_change(self, obj, data):
|
def can_change(self, obj, data):
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
if obj.created_by == self.user and not obj.organizations.filter(active=True).count():
|
if obj.created_by == self.user and not obj.deprecated_organizations.filter(active=True).count():
|
||||||
return True
|
return True
|
||||||
if obj.organizations.filter(active=True, deprecated_admins__in=[self.user]).exists():
|
if obj.deprecated_organizations.filter(active=True, deprecated_admins__in=[self.user]).exists():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -880,7 +880,7 @@ class JobTemplateAccess(BaseAccess):
|
|||||||
Q(cloud_credential_id__in=credential_ids) | Q(cloud_credential__isnull=True),
|
Q(cloud_credential_id__in=credential_ids) | Q(cloud_credential__isnull=True),
|
||||||
)
|
)
|
||||||
org_admin_ids = base_qs.filter(
|
org_admin_ids = base_qs.filter(
|
||||||
Q(project__organizations__deprecated_admins__in=[self.user]) |
|
Q(project__deprecated_organizations__deprecated_admins__in=[self.user]) |
|
||||||
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__deprecated_admins__in=[self.user]))
|
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__deprecated_admins__in=[self.user]))
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1097,7 +1097,7 @@ class JobAccess(BaseAccess):
|
|||||||
credential_id__in=credential_ids,
|
credential_id__in=credential_ids,
|
||||||
)
|
)
|
||||||
org_admin_ids = base_qs.filter(
|
org_admin_ids = base_qs.filter(
|
||||||
Q(project__organizations__deprecated_admins__in=[self.user]) |
|
Q(project__deprecated_organizations__deprecated_admins__in=[self.user]) |
|
||||||
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__deprecated_admins__in=[self.user]))
|
(Q(project__isnull=True) & Q(job_type=PERM_INVENTORY_SCAN) & Q(inventory__organization__deprecated_admins__in=[self.user]))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -131,9 +131,49 @@ def migrate_projects(apps, schema_editor):
|
|||||||
|
|
||||||
Project = apps.get_model('main', 'Project')
|
Project = apps.get_model('main', 'Project')
|
||||||
Permission = apps.get_model('main', 'Permission')
|
Permission = apps.get_model('main', 'Permission')
|
||||||
|
JobTemplate = apps.get_model('main', 'JobTemplate')
|
||||||
|
|
||||||
for project in Project.objects.all():
|
# Migrate projects to single organizations, duplicating as necessary
|
||||||
if project.organizations.count() == 0 and project.created_by is not None:
|
for project in [p for p in Project.objects.all()]:
|
||||||
|
original_project_name = project.name
|
||||||
|
project_orgs = project.deprecated_organizations.distinct().all()
|
||||||
|
|
||||||
|
if project_orgs.count() > 1:
|
||||||
|
first_org = None
|
||||||
|
for org in project_orgs:
|
||||||
|
if first_org is None:
|
||||||
|
# For the first org, re-use our existing Project object, so don't do the below duplication effort
|
||||||
|
first_org = org
|
||||||
|
project.name = first_org.name + ' - ' + original_project_name
|
||||||
|
project.organization = first_org
|
||||||
|
project.save()
|
||||||
|
else:
|
||||||
|
new_prj = Project.objects.create(
|
||||||
|
created = project.created,
|
||||||
|
description = project.description,
|
||||||
|
name = org.name + ' - ' + original_project_name,
|
||||||
|
old_pk = project.old_pk,
|
||||||
|
created_by_id = project.created_by_id,
|
||||||
|
scm_type = project.scm_type,
|
||||||
|
scm_url = project.scm_url,
|
||||||
|
scm_branch = project.scm_branch,
|
||||||
|
scm_clean = project.scm_clean,
|
||||||
|
scm_delete_on_update = project.scm_delete_on_update,
|
||||||
|
scm_delete_on_next_update = project.scm_delete_on_next_update,
|
||||||
|
scm_update_on_launch = project.scm_update_on_launch,
|
||||||
|
scm_update_cache_timeout = project.scm_update_cache_timeout,
|
||||||
|
credential = project.credential,
|
||||||
|
organization = org
|
||||||
|
)
|
||||||
|
migrations[original_project_name]['projects'].add(new_prj)
|
||||||
|
job_templates = JobTemplate.objects.filter(inventory__organization=org).all()
|
||||||
|
for jt in job_templates:
|
||||||
|
jt.project = new_prj
|
||||||
|
jt.save()
|
||||||
|
|
||||||
|
# Migrate permissions
|
||||||
|
for project in [p for p in Project.objects.all()]:
|
||||||
|
if project.organization is None and project.created_by is not None:
|
||||||
project.admin_role.members.add(project.created_by)
|
project.admin_role.members.add(project.created_by)
|
||||||
migrations[project.name]['users'].add(project.created_by)
|
migrations[project.name]['users'].add(project.created_by)
|
||||||
|
|
||||||
@@ -141,11 +181,10 @@ def migrate_projects(apps, schema_editor):
|
|||||||
team.member_role.children.add(project.member_role)
|
team.member_role.children.add(project.member_role)
|
||||||
migrations[project.name]['teams'].add(team)
|
migrations[project.name]['teams'].add(team)
|
||||||
|
|
||||||
if project.organizations.count() > 0:
|
if project.organization is not None:
|
||||||
for org in project.organizations.all():
|
for user in project.organization.deprecated_users.all():
|
||||||
for user in org.deprecated_users.all():
|
project.member_role.members.add(user)
|
||||||
project.member_role.members.add(user)
|
migrations[project.name]['users'].add(user)
|
||||||
migrations[project.name]['users'].add(user)
|
|
||||||
|
|
||||||
for perm in Permission.objects.filter(project=project, active=True):
|
for perm in Permission.objects.filter(project=project, active=True):
|
||||||
# All perms at this level just imply a user or team can read
|
# All perms at this level just imply a user or team can read
|
||||||
|
|||||||
@@ -362,9 +362,9 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
|
|||||||
success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success__in=[self, self.project]))
|
success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success__in=[self, self.project]))
|
||||||
any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any__in=[self, self.project]))
|
any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any__in=[self, self.project]))
|
||||||
# Get Organization Notifiers
|
# Get Organization Notifiers
|
||||||
error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors__in=self.project.organizations.all())))
|
error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors=self.project.organization)))
|
||||||
success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success__in=self.project.organizations.all())))
|
success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success=self.project.organization)))
|
||||||
any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any__in=self.project.organizations.all())))
|
any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any=self.project.organization)))
|
||||||
return dict(error=list(error_notifiers), success=list(success_notifiers), any=list(any_notifiers))
|
return dict(error=list(error_notifiers), success=list(success_notifiers), any=list(any_notifiers))
|
||||||
|
|
||||||
class Job(UnifiedJob, JobOptions):
|
class Job(UnifiedJob, JobOptions):
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='admin_of_organizations',
|
related_name='admin_of_organizations',
|
||||||
)
|
)
|
||||||
projects = models.ManyToManyField(
|
deprecated_projects = models.ManyToManyField(
|
||||||
'Project',
|
'Project',
|
||||||
blank=True,
|
blank=True,
|
||||||
related_name='organizations',
|
related_name='deprecated_organizations',
|
||||||
)
|
)
|
||||||
admin_role = ImplicitRoleField(
|
admin_role = ImplicitRoleField(
|
||||||
role_name='Organization Administrator',
|
role_name='Organization Administrator',
|
||||||
|
|||||||
@@ -198,6 +198,13 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
|||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
ordering = ('id',)
|
ordering = ('id',)
|
||||||
|
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
'Organization',
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='projects',
|
||||||
|
)
|
||||||
scm_delete_on_next_update = models.BooleanField(
|
scm_delete_on_next_update = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
editable=False,
|
editable=False,
|
||||||
@@ -212,13 +219,13 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
|||||||
admin_role = ImplicitRoleField(
|
admin_role = ImplicitRoleField(
|
||||||
role_name='Project Administrator',
|
role_name='Project Administrator',
|
||||||
role_description='May manage this project',
|
role_description='May manage this project',
|
||||||
parent_role='organizations.admin_role',
|
parent_role='organization.admin_role',
|
||||||
permissions = {'all': True}
|
permissions = {'all': True}
|
||||||
)
|
)
|
||||||
auditor_role = ImplicitRoleField(
|
auditor_role = ImplicitRoleField(
|
||||||
role_name='Project Auditor',
|
role_name='Project Auditor',
|
||||||
role_description='May read all settings associated with this project',
|
role_description='May read all settings associated with this project',
|
||||||
parent_role='organizations.auditor_role',
|
parent_role='organization.auditor_role',
|
||||||
permissions = {'read': True}
|
permissions = {'read': True}
|
||||||
)
|
)
|
||||||
member_role = ImplicitRoleField(
|
member_role = ImplicitRoleField(
|
||||||
@@ -343,9 +350,9 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
|||||||
success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success=self))
|
success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success=self))
|
||||||
any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any=self))
|
any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any=self))
|
||||||
# Get Organization Notifiers
|
# Get Organization Notifiers
|
||||||
error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors__in=self.organizations.all())))
|
error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors=self.organization)))
|
||||||
success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success__in=self.organizations.all())))
|
success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success=self.organization)))
|
||||||
any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any__in=self.organizations.all())))
|
any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any=self.organization)))
|
||||||
return dict(error=list(error_notifiers), success=list(success_notifiers), any=list(any_notifiers))
|
return dict(error=list(error_notifiers), success=list(success_notifiers), any=list(any_notifiers))
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ def _test_user_access_control(hosts, fact_scans, get, user_obj, team_obj):
|
|||||||
hosts = hosts(host_count=1)
|
hosts = hosts(host_count=1)
|
||||||
fact_scans(fact_scans=1)
|
fact_scans(fact_scans=1)
|
||||||
|
|
||||||
team_obj.users.add(user_obj)
|
team_obj.member_role.members.add(user_obj)
|
||||||
|
|
||||||
url = reverse('api:host_fact_versions_list', args=(hosts[0].pk,))
|
url = reverse('api:host_fact_versions_list', args=(hosts[0].pk,))
|
||||||
response = get(url, user_obj)
|
response = get(url, user_obj)
|
||||||
@@ -235,7 +235,7 @@ def test_super_user_ok(hosts, fact_scans, get, user, team):
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_user_admin_ok(organization, hosts, fact_scans, get, user, team):
|
def test_user_admin_ok(organization, hosts, fact_scans, get, user, team):
|
||||||
user_admin = user('johnson', False)
|
user_admin = user('johnson', False)
|
||||||
organization.admins.add(user_admin)
|
organization.admin_role.members.add(user_admin)
|
||||||
|
|
||||||
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ def test_user_admin_ok(organization, hosts, fact_scans, get, user, team):
|
|||||||
def test_user_admin_403(organization, organizations, hosts, fact_scans, get, user, team):
|
def test_user_admin_403(organization, organizations, hosts, fact_scans, get, user, team):
|
||||||
user_admin = user('johnson', False)
|
user_admin = user('johnson', False)
|
||||||
org2 = organizations(1)
|
org2 = organizations(1)
|
||||||
org2[0].admins.add(user_admin)
|
org2[0].admin_role.members.add(user_admin)
|
||||||
|
|
||||||
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ def _test_user_access_control(hosts, fact_scans, get, user_obj, team_obj):
|
|||||||
hosts = hosts(host_count=1)
|
hosts = hosts(host_count=1)
|
||||||
fact_scans(fact_scans=1)
|
fact_scans(fact_scans=1)
|
||||||
|
|
||||||
team_obj.users.add(user_obj)
|
team_obj.member_role.members.add(user_obj)
|
||||||
|
|
||||||
url = reverse('api:host_fact_compare_view', args=(hosts[0].pk,))
|
url = reverse('api:host_fact_compare_view', args=(hosts[0].pk,))
|
||||||
response = get(url, user_obj)
|
response = get(url, user_obj)
|
||||||
@@ -162,7 +162,7 @@ def test_super_user_ok(hosts, fact_scans, get, user, team):
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_user_admin_ok(organization, hosts, fact_scans, get, user, team):
|
def test_user_admin_ok(organization, hosts, fact_scans, get, user, team):
|
||||||
user_admin = user('johnson', False)
|
user_admin = user('johnson', False)
|
||||||
organization.admins.add(user_admin)
|
organization.admin_role.members.add(user_admin)
|
||||||
|
|
||||||
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ def test_user_admin_ok(organization, hosts, fact_scans, get, user, team):
|
|||||||
def test_user_admin_403(organization, organizations, hosts, fact_scans, get, user, team):
|
def test_user_admin_403(organization, organizations, hosts, fact_scans, get, user, team):
|
||||||
user_admin = user('johnson', False)
|
user_admin = user('johnson', False)
|
||||||
org2 = organizations(1)
|
org2 = organizations(1)
|
||||||
org2[0].admins.add(user_admin)
|
org2[0].admin_role.members.add(user_admin)
|
||||||
|
|
||||||
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)
|
||||||
|
|
||||||
|
|||||||
@@ -97,8 +97,9 @@ def project(instance, organization):
|
|||||||
prj = Project.objects.create(name="test-proj",
|
prj = Project.objects.create(name="test-proj",
|
||||||
description="test-proj-desc",
|
description="test-proj-desc",
|
||||||
scm_type="git",
|
scm_type="git",
|
||||||
scm_url="https://github.com/jlaska/ansible-playbooks")
|
scm_url="https://github.com/jlaska/ansible-playbooks",
|
||||||
prj.organizations.add(organization)
|
organization=organization
|
||||||
|
)
|
||||||
return prj
|
return prj
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ def test_inherited_notifiers(get, post, user, organization, project):
|
|||||||
u)
|
u)
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
notifiers.append(response.data['id'])
|
notifiers.append(response.data['id'])
|
||||||
organization.projects.add(project)
|
|
||||||
i = Inventory.objects.create(name='test', organization=organization)
|
i = Inventory.objects.create(name='test', organization=organization)
|
||||||
i.save()
|
i.save()
|
||||||
g = Group.objects.create(name='test', inventory=i)
|
g = Group.objects.create(name='test', inventory=i)
|
||||||
@@ -109,7 +108,6 @@ def test_inherited_notifiers(get, post, user, organization, project):
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_notifier_merging(get, post, user, organization, project, notifier):
|
def test_notifier_merging(get, post, user, organization, project, notifier):
|
||||||
user('admin-poster', True)
|
user('admin-poster', True)
|
||||||
organization.projects.add(project)
|
|
||||||
organization.notifiers_any.add(notifier)
|
organization.notifiers_any.add(notifier)
|
||||||
project.notifiers_any.add(notifier)
|
project.notifiers_any.add(notifier)
|
||||||
assert len(project.notifiers['any']) == 1
|
assert len(project.notifiers['any']) == 1
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from awx.main.models import (
|
|||||||
Role,
|
Role,
|
||||||
RolePermission,
|
RolePermission,
|
||||||
Organization,
|
Organization,
|
||||||
|
Group,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -97,20 +98,24 @@ def test_team_symantics(organization, team, alice):
|
|||||||
assert organization.accessible_by(alice, {'read': True}) is False
|
assert organization.accessible_by(alice, {'read': True}) is False
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_auto_m2m_adjuments(organization, project, alice):
|
def test_auto_m2m_adjuments(organization, inventory, group, alice):
|
||||||
'Ensures the auto role reparenting is working correctly through m2m maps'
|
'Ensures the auto role reparenting is working correctly through m2m maps'
|
||||||
organization.admin_role.members.add(alice)
|
g1 = group(name='g1')
|
||||||
assert project.accessible_by(alice, {'read': True}) is True
|
g1.admin_role.members.add(alice)
|
||||||
|
assert g1.accessible_by(alice, {'read': True}) is True
|
||||||
|
g2 = group(name='g2')
|
||||||
|
assert g2.accessible_by(alice, {'read': True}) is False
|
||||||
|
|
||||||
project.organizations.remove(organization)
|
g2.parents.add(g1)
|
||||||
assert project.accessible_by(alice, {'read': True}) is False
|
assert g2.accessible_by(alice, {'read': True}) is True
|
||||||
project.organizations.add(organization)
|
g2.parents.remove(g1)
|
||||||
assert project.accessible_by(alice, {'read': True}) is True
|
assert g2.accessible_by(alice, {'read': True}) is False
|
||||||
|
|
||||||
|
g1.children.add(g2)
|
||||||
|
assert g2.accessible_by(alice, {'read': True}) is True
|
||||||
|
g1.children.remove(g2)
|
||||||
|
assert g2.accessible_by(alice, {'read': True}) is False
|
||||||
|
|
||||||
organization.projects.remove(project)
|
|
||||||
assert project.accessible_by(alice, {'read': True}) is False
|
|
||||||
organization.projects.add(project)
|
|
||||||
assert project.accessible_by(alice, {'read': True}) is True
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_auto_field_adjuments(organization, inventory, team, alice):
|
def test_auto_field_adjuments(organization, inventory, team, alice):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def test_job_template_migration_check(deploy_jobtemplate, check_jobtemplate, use
|
|||||||
joe = user('joe')
|
joe = user('joe')
|
||||||
|
|
||||||
|
|
||||||
check_jobtemplate.project.organizations.all()[0].deprecated_users.add(joe)
|
check_jobtemplate.project.organization.deprecated_users.add(joe)
|
||||||
|
|
||||||
Permission(user=joe, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
Permission(user=joe, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
||||||
Permission(user=joe, inventory=check_jobtemplate.inventory,
|
Permission(user=joe, inventory=check_jobtemplate.inventory,
|
||||||
@@ -45,7 +45,7 @@ def test_job_template_migration_deploy(deploy_jobtemplate, check_jobtemplate, us
|
|||||||
joe = user('joe')
|
joe = user('joe')
|
||||||
|
|
||||||
|
|
||||||
deploy_jobtemplate.project.organizations.all()[0].deprecated_users.add(joe)
|
deploy_jobtemplate.project.organization.deprecated_users.add(joe)
|
||||||
|
|
||||||
Permission(user=joe, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
Permission(user=joe, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
||||||
Permission(user=joe, inventory=deploy_jobtemplate.inventory,
|
Permission(user=joe, inventory=deploy_jobtemplate.inventory,
|
||||||
@@ -77,7 +77,7 @@ def test_job_template_team_migration_check(deploy_jobtemplate, check_jobtemplate
|
|||||||
team.organization = organization
|
team.organization = organization
|
||||||
team.save()
|
team.save()
|
||||||
|
|
||||||
check_jobtemplate.project.organizations.all()[0].deprecated_users.add(joe)
|
check_jobtemplate.project.organization.deprecated_users.add(joe)
|
||||||
|
|
||||||
Permission(team=team, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
Permission(team=team, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
||||||
Permission(team=team, inventory=check_jobtemplate.inventory,
|
Permission(team=team, inventory=check_jobtemplate.inventory,
|
||||||
@@ -112,7 +112,7 @@ def test_job_template_team_deploy_migration(deploy_jobtemplate, check_jobtemplat
|
|||||||
team.organization = organization
|
team.organization = organization
|
||||||
team.save()
|
team.save()
|
||||||
|
|
||||||
deploy_jobtemplate.project.organizations.all()[0].deprecated_users.add(joe)
|
deploy_jobtemplate.project.organization.deprecated_users.add(joe)
|
||||||
|
|
||||||
Permission(team=team, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
Permission(team=team, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
||||||
Permission(team=team, inventory=deploy_jobtemplate.inventory,
|
Permission(team=team, inventory=deploy_jobtemplate.inventory,
|
||||||
|
|||||||
@@ -1,12 +1,95 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from awx.main.migrations import _rbac as rbac
|
from awx.main.migrations import _rbac as rbac
|
||||||
from awx.main.models import Role
|
from awx.main.models import Role, Permission, Project, Organization, Credential, JobTemplate, Inventory
|
||||||
from awx.main.models.organization import Permission
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from awx.main.migrations import _old_access as old_access
|
from awx.main.migrations import _old_access as old_access
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_project_migration():
|
||||||
|
'''
|
||||||
|
|
||||||
|
o1 o2 o3 with o1 -- i1 o2 -- i2
|
||||||
|
\ | /
|
||||||
|
\ | /
|
||||||
|
c1 ---- p1
|
||||||
|
/ | \
|
||||||
|
/ | \
|
||||||
|
jt1 jt2 jt3
|
||||||
|
| | |
|
||||||
|
i1 i2 i1
|
||||||
|
|
||||||
|
|
||||||
|
goes to
|
||||||
|
|
||||||
|
|
||||||
|
o1
|
||||||
|
|
|
||||||
|
|
|
||||||
|
c1 ---- p1
|
||||||
|
/ |
|
||||||
|
/ |
|
||||||
|
jt1 jt3
|
||||||
|
| |
|
||||||
|
i1 i1
|
||||||
|
|
||||||
|
|
||||||
|
o2
|
||||||
|
|
|
||||||
|
|
|
||||||
|
c1 ---- p2
|
||||||
|
|
|
||||||
|
|
|
||||||
|
jt2
|
||||||
|
|
|
||||||
|
i2
|
||||||
|
|
||||||
|
o3
|
||||||
|
|
|
||||||
|
|
|
||||||
|
c1 ---- p3
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
o1 = Organization.objects.create(name='o1')
|
||||||
|
o2 = Organization.objects.create(name='o2')
|
||||||
|
o3 = Organization.objects.create(name='o3')
|
||||||
|
|
||||||
|
c1 = Credential.objects.create(name='c1')
|
||||||
|
|
||||||
|
p1 = Project.objects.create(name='p1', credential=c1)
|
||||||
|
p1.deprecated_organizations.add(o1, o2, o3)
|
||||||
|
|
||||||
|
i1 = Inventory.objects.create(name='i1', organization=o1)
|
||||||
|
i2 = Inventory.objects.create(name='i2', organization=o2)
|
||||||
|
|
||||||
|
jt1 = JobTemplate.objects.create(name='jt1', project=p1, inventory=i1)
|
||||||
|
jt2 = JobTemplate.objects.create(name='jt2', project=p1, inventory=i2)
|
||||||
|
jt3 = JobTemplate.objects.create(name='jt3', project=p1, inventory=i1)
|
||||||
|
|
||||||
|
assert o1.projects.count() == 0
|
||||||
|
assert o2.projects.count() == 0
|
||||||
|
assert o3.projects.count() == 0
|
||||||
|
|
||||||
|
rbac.migrate_projects(apps, None)
|
||||||
|
|
||||||
|
jt1 = JobTemplate.objects.get(pk=jt1.pk)
|
||||||
|
jt2 = JobTemplate.objects.get(pk=jt2.pk)
|
||||||
|
jt3 = JobTemplate.objects.get(pk=jt3.pk)
|
||||||
|
|
||||||
|
assert jt1.project == jt3.project
|
||||||
|
assert jt1.project != jt2.project
|
||||||
|
|
||||||
|
assert o1.projects.count() == 1
|
||||||
|
assert o2.projects.count() == 1
|
||||||
|
assert o3.projects.count() == 1
|
||||||
|
assert o1.projects.all()[0].jobtemplates.count() == 2
|
||||||
|
assert o2.projects.all()[0].jobtemplates.count() == 1
|
||||||
|
assert o3.projects.all()[0].jobtemplates.count() == 0
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_project_user_project(user_project, project, user):
|
def test_project_user_project(user_project, project, user):
|
||||||
u = user('owner')
|
u = user('owner')
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class BaseAdHocCommandTest(BaseJobExecutionTest):
|
|||||||
self.setup_instances()
|
self.setup_instances()
|
||||||
self.setup_users()
|
self.setup_users()
|
||||||
self.organization = self.make_organizations(self.super_django_user, 1)[0]
|
self.organization = self.make_organizations(self.super_django_user, 1)[0]
|
||||||
self.organization.admins.add(self.normal_django_user)
|
self.organization.admin_role.members.add(self.normal_django_user)
|
||||||
self.inventory = self.organization.inventories.create(name='test-inventory', description='description for test-inventory')
|
self.inventory = self.organization.inventories.create(name='test-inventory', description='description for test-inventory')
|
||||||
self.host = self.inventory.hosts.create(name='host.example.com')
|
self.host = self.inventory.hosts.create(name='host.example.com')
|
||||||
self.host2 = self.inventory.hosts.create(name='host2.example.com')
|
self.host2 = self.inventory.hosts.create(name='host2.example.com')
|
||||||
|
|||||||
@@ -634,6 +634,13 @@ dd {
|
|||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(255, 88, 80, 0.6);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(255, 88, 80, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-control.ng-dirty.ng-invalid + .select2 .select2-selection,
|
||||||
|
.form-control.ng-dirty.ng-invalid + .select2 .select2-selection:focus {
|
||||||
|
border-color: rgba(255, 88, 80, 0.8) !important;
|
||||||
|
outline: 0 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-control.ng-dirty.ng-pristine {
|
.form-control.ng-dirty.ng-pristine {
|
||||||
border-color: @default-second-border;
|
border-color: @default-second-border;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -2008,15 +2015,33 @@ tr td button i {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-control + .select2 .select2-selection {
|
||||||
|
border-color: @default-second-border !important;
|
||||||
|
background-color: #f6f6f6 !important;
|
||||||
|
color: @default-data-txt !important;
|
||||||
|
transition: border-color 0.3s !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-control:active, .form-control:focus {
|
.form-control:active, .form-control:focus {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-color: #167ec4;
|
border-color: #167ec4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-control:active + .select2 .select2-selection, .form-control:focus + .select2 .select2-selection {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-color: #167ec4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-control.ng-dirty.ng-invalid, .form-control.ng-dirty.ng-invalid:focus {
|
.form-control.ng-dirty.ng-invalid, .form-control.ng-dirty.ng-invalid:focus {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-control.ng-dirty.ng-invalid + .select2 .select2-selection, .form-control.ng-dirty.ng-invalid:focus + .select2 .select2-selection {
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
@@ -2041,3 +2066,7 @@ tr td button i {
|
|||||||
.select2-container--disabled {
|
.select2-container--disabled {
|
||||||
opacity: .35;
|
opacity: .35;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.is-modalOpen {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ table, tbody {
|
|||||||
|
|
||||||
.List-tableHeader:last-of-type {
|
.List-tableHeader:last-of-type {
|
||||||
border-top-right-radius: 5px;
|
border-top-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.List-tableHeader--actions {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,6 +323,11 @@ table, tbody {
|
|||||||
height: 34px;
|
height: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.List-searchWidget--compact {
|
||||||
|
max-width: ~"calc(100% - 91px)";
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.List-searchRow {
|
.List-searchRow {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
@import "../../shared/branding/colors.default.less";
|
||||||
|
|
||||||
|
/** @define AddPermissions */
|
||||||
|
|
||||||
|
.AddPermissions-backDrop {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1041;
|
||||||
|
opacity: 0.2;
|
||||||
|
transition: 0.5s opacity;
|
||||||
|
background: @login-backdrop;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-dialog {
|
||||||
|
margin: 30px auto;
|
||||||
|
margin-top: 95px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-content {
|
||||||
|
max-width: 750px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: @login-bg;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: opacity 0.5s;
|
||||||
|
z-index: 1042;
|
||||||
|
position: relative;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-header {
|
||||||
|
padding: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-body {
|
||||||
|
padding: 0px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap-reverse;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-list .List-searchRow {
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-list .List-searchWidget {
|
||||||
|
height: 66px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-list .List-tableHeader:last-child {
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-list select-all {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-title {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-buttons {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-directions {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #848992;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-directionNumber {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: @default-list-header-bg;
|
||||||
|
padding: 2px 6px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-separator {
|
||||||
|
margin-top: 20px 0px;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px solid @default-second-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleRow {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleName {
|
||||||
|
width: 30%;
|
||||||
|
padding-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleNameVal {
|
||||||
|
font-size: 14px;
|
||||||
|
max-width: ~"calc(100% - 46px)";
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleType {
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0px 6px;
|
||||||
|
border: 1px solid @default-second-border;
|
||||||
|
font-size: 10px;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: @default-bg;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleSelect {
|
||||||
|
width: ~"calc(70% - 40px)";
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleSelect .Form-dropDown {
|
||||||
|
height: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleRemove {
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 5px 3px;
|
||||||
|
line-height: 11px;
|
||||||
|
color: @default-icon;
|
||||||
|
background-color: @default-tertiary-bg;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-roleRemove:hover {
|
||||||
|
background-color: @default-err;
|
||||||
|
color: @default-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-selectHide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions .select2-search__field {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyToggle {
|
||||||
|
margin-left: auto;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 3px 9px;
|
||||||
|
font-size: 12px;
|
||||||
|
background-color: @default-bg;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
border: 1px solid @default-second-border;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyToggle:hover {
|
||||||
|
background-color: @default-tertiary-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyToggle.is-active {
|
||||||
|
background-color: @default-link;
|
||||||
|
border-color: @default-link;
|
||||||
|
color: @default-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyPane {
|
||||||
|
margin: 20px 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
border: 1px solid @default-second-border;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyRow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyName {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddPermissions-keyDescription {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name controllers.function:Access
|
||||||
|
* @description
|
||||||
|
* Controller for handling permissions adding
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default ['$rootScope', '$scope', 'GetBasePath', 'Rest', '$q', 'Wait', 'ProcessErrors', function (rootScope, scope, GetBasePath, Rest, $q, Wait, ProcessErrors) {
|
||||||
|
var manuallyUpdateChecklists = function(list, id, isSelected) {
|
||||||
|
var elemScope = angular
|
||||||
|
.element("#" +
|
||||||
|
list + "s_table #" + id + ".List-tableRow input")
|
||||||
|
.scope();
|
||||||
|
if (elemScope) {
|
||||||
|
elemScope.isSelected = !!isSelected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.allSelected = [];
|
||||||
|
|
||||||
|
// the object permissions are being added to
|
||||||
|
scope.object = scope[scope.$parent.list
|
||||||
|
.iterator + "_obj"];
|
||||||
|
|
||||||
|
// array for all possible roles for the object
|
||||||
|
scope.roles = Object
|
||||||
|
.keys(scope.object.summary_fields.roles)
|
||||||
|
.map(function(key) {
|
||||||
|
return {
|
||||||
|
value: scope.object.summary_fields
|
||||||
|
.roles[key].id,
|
||||||
|
label: scope.object.summary_fields
|
||||||
|
.roles[key].name };
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: get working with api
|
||||||
|
// array w roles and descriptions for key
|
||||||
|
scope.roleKey = Object
|
||||||
|
.keys(scope.object.summary_fields.roles)
|
||||||
|
.map(function(key) {
|
||||||
|
return {
|
||||||
|
name: scope.object.summary_fields
|
||||||
|
.roles[key].name,
|
||||||
|
description: scope.object.summary_fields
|
||||||
|
.roles[key].description };
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.showKeyPane = false;
|
||||||
|
|
||||||
|
scope.toggleKeyPane = function() {
|
||||||
|
scope.showKeyPane = !scope.showKeyPane;
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle form tab changes
|
||||||
|
scope.toggleFormTabs = function(list) {
|
||||||
|
scope.usersSelected = (list === 'users');
|
||||||
|
scope.teamsSelected = !scope.usersSelected;
|
||||||
|
};
|
||||||
|
|
||||||
|
// manually handle selection/deselection of user/team checkboxes
|
||||||
|
scope.$on("selectedOrDeselected", function(e, val) {
|
||||||
|
val = val.value;
|
||||||
|
if (val.isSelected) {
|
||||||
|
// deselected, so remove from the allSelected list
|
||||||
|
scope.allSelected = scope.allSelected.filter(function(i) {
|
||||||
|
// return all but the object who has the id and type
|
||||||
|
// of the element to deselect
|
||||||
|
return (!(val.id === i.id && val.type === i.type));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// selected, so add to the allSelected list
|
||||||
|
scope.allSelected.push({
|
||||||
|
name: function() {
|
||||||
|
if (val.type === "user") {
|
||||||
|
return (val.first_name &&
|
||||||
|
val.last_name) ?
|
||||||
|
val.first_name + " " +
|
||||||
|
val.last_name :
|
||||||
|
val.username;
|
||||||
|
} else {
|
||||||
|
return val .name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: val.type,
|
||||||
|
roles: [],
|
||||||
|
id: val.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// used to handle changes to the itemsSelected scope var on "next page",
|
||||||
|
// "sorting etc."
|
||||||
|
scope.$on("itemsSelected", function(e, inList) {
|
||||||
|
// compile a list of objects that needed to be checked in the lists
|
||||||
|
scope.updateLists = scope.allSelected.filter(function(inMemory) {
|
||||||
|
var notInList = true;
|
||||||
|
inList.forEach(function(val) {
|
||||||
|
// if the object is part of the allSelected list and is
|
||||||
|
// selected,
|
||||||
|
// you don't need to add it updateLists
|
||||||
|
if (inMemory.id === val.id &&
|
||||||
|
inMemory.type === val.type) {
|
||||||
|
notInList = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return notInList;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle changes to the updatedLists by manually selected those values in
|
||||||
|
// the UI
|
||||||
|
scope.$watch("updateLists", function(toUpdate) {
|
||||||
|
(toUpdate || []).forEach(function(obj) {
|
||||||
|
manuallyUpdateChecklists(obj.type, obj.id, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
delete scope.updateLists;
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove selected user/team
|
||||||
|
scope.removeObject = function(obj) {
|
||||||
|
manuallyUpdateChecklists(obj.type, obj.id, false);
|
||||||
|
|
||||||
|
scope.allSelected = scope.allSelected.filter(function(i) {
|
||||||
|
return (!(obj.id === i.id && obj.type === i.type));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// update post url list
|
||||||
|
scope.$watch("allSelected", function(val) {
|
||||||
|
scope.posts = _
|
||||||
|
.flatten((val || [])
|
||||||
|
.map(function (owner) {
|
||||||
|
var url = GetBasePath(owner.type + "s") + owner.id +
|
||||||
|
"/roles/";
|
||||||
|
|
||||||
|
return (owner.roles || [])
|
||||||
|
.map(function (role) {
|
||||||
|
return {url: url,
|
||||||
|
id: role.value};
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// post roles to api
|
||||||
|
scope.updatePermissions = function() {
|
||||||
|
Wait('start');
|
||||||
|
|
||||||
|
var requests = scope.posts
|
||||||
|
.map(function(post) {
|
||||||
|
Rest.setUrl(post.url);
|
||||||
|
return Rest.post({"id": post.id});
|
||||||
|
});
|
||||||
|
|
||||||
|
$q.all(requests)
|
||||||
|
.then(function () {
|
||||||
|
Wait('stop');
|
||||||
|
rootScope.$broadcast("refreshList", "permission");
|
||||||
|
scope.closeModal();
|
||||||
|
}, function (error) {
|
||||||
|
Wait('stop');
|
||||||
|
rootScope.$broadcast("refreshList", "permission");
|
||||||
|
scope.closeModal();
|
||||||
|
ProcessErrors(null, error.data, error.status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to post role(s): POST returned status' +
|
||||||
|
error.status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}];
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
import addPermissionsController from './addPermissions.controller';
|
||||||
|
|
||||||
|
/* jshint unused: vars */
|
||||||
|
export default
|
||||||
|
[ 'templateUrl',
|
||||||
|
'Wait',
|
||||||
|
function(templateUrl, Wait) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: true,
|
||||||
|
controller: addPermissionsController,
|
||||||
|
templateUrl: templateUrl('access/addPermissions/addPermissions'),
|
||||||
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
scope.toggleFormTabs('users');
|
||||||
|
|
||||||
|
$("body").addClass("is-modalOpen");
|
||||||
|
|
||||||
|
$("body").append(element);
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
|
||||||
|
scope.$broadcast("linkLists");
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
$('#add-permissions-modal').modal("show");
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
$('.modal[aria-hidden=false]').each(function () {
|
||||||
|
if ($(this).attr('id') !== 'add-permissions-modal') {
|
||||||
|
$(this).modal('hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.closeModal = function() {
|
||||||
|
$("body").removeClass("is-modalOpen");
|
||||||
|
$('#add-permissions-modal').on('hidden.bs.modal',
|
||||||
|
function () {
|
||||||
|
$('.AddPermissions').remove();
|
||||||
|
});
|
||||||
|
$('#add-permissions-modal').modal('hide');
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.$on('closePermissionsModal', function() {
|
||||||
|
scope.closeModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
Wait('stop');
|
||||||
|
|
||||||
|
window.scrollTo(0,0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<div id="add-permissions-modal" class="AddPermissions modal fade">
|
||||||
|
<div class="AddPermissions-backDrop is-loggedOut"></div>
|
||||||
|
<div class="AddPermissions-dialog">
|
||||||
|
<div class="AddPermissions-content is-loggedOut">
|
||||||
|
<div class="AddPermissions-header">
|
||||||
|
<div class="List-header">
|
||||||
|
<div class="List-title">
|
||||||
|
<div class="List-titleText ng-binding">
|
||||||
|
{{ object.name }}
|
||||||
|
<div class="List-titleLockup"></div>
|
||||||
|
Add Permissions
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Form-exitHolder">
|
||||||
|
<button class="Form-exit" ng-click="closeModal()">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="AddPermissions-body">
|
||||||
|
<div class="AddPermissions-directions">
|
||||||
|
<span class="AddPermissions-directionNumber">
|
||||||
|
1.
|
||||||
|
</span>
|
||||||
|
Please select Users / Teams from the lists below.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="Form-tabHolder">
|
||||||
|
<div id="users_tab" class="Form-tab"
|
||||||
|
ng-click="toggleFormTabs('users')"
|
||||||
|
ng-class="{'is-selected': usersSelected }">
|
||||||
|
Users
|
||||||
|
</div>
|
||||||
|
<div id="teams_tab" class="Form-tab"
|
||||||
|
ng-click="toggleFormTabs('teams')"
|
||||||
|
ng-class="{'is-selected': teamsSelected }"
|
||||||
|
>
|
||||||
|
Teams
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="AddPermissions-list" ng-show="usersSelected">
|
||||||
|
<add-permissions-list type="users">
|
||||||
|
</add-permissions-list>
|
||||||
|
</div>
|
||||||
|
<div class="AddPermissions-list" ng-show="teamsSelected">
|
||||||
|
<add-permissions-list type="teams">
|
||||||
|
</add-permissions-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="AddPermissions-separator"
|
||||||
|
ng-show="allSelected && allSelected.length > 0"></div>
|
||||||
|
|
||||||
|
<div class="AddPermissions-directions"
|
||||||
|
ng-show="allSelected && allSelected.length > 0">
|
||||||
|
<span class="AddPermissions-directionNumber">
|
||||||
|
2.
|
||||||
|
</span>
|
||||||
|
Please assign roles to the selected users/teams
|
||||||
|
<div class="AddPermissions-keyToggle"
|
||||||
|
ng-class="{'is-active': showKeyPane}"
|
||||||
|
ng-click="toggleKeyPane()">
|
||||||
|
Key
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="AddPermissions-keyPane"
|
||||||
|
ng-show="showKeyPane">
|
||||||
|
<div class="AddPermissions-keyRow"
|
||||||
|
ng-repeat="key in roleKey">
|
||||||
|
<div class="AddPermissions-keyName">
|
||||||
|
{{ key.name }}
|
||||||
|
</div>
|
||||||
|
<div class="AddPermissions-keyDescription">
|
||||||
|
{{ key.description || "No description provided" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form name="userForm" novalidate>
|
||||||
|
<ng-form name="userRoleForm">
|
||||||
|
<div class="AddPermissions-roleRow"
|
||||||
|
ng-repeat="obj in allSelected">
|
||||||
|
<div class="AddPermissions-roleName">
|
||||||
|
<span class="AddPermissions-roleNameVal">
|
||||||
|
{{ obj.name }}
|
||||||
|
</span>
|
||||||
|
<span class="AddPermissions-roleType">
|
||||||
|
{{ obj.type }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<role-select class="AddPermissions-roleSelect">
|
||||||
|
</role-select>
|
||||||
|
<button class="AddPermissions-roleRemove"
|
||||||
|
ng-click="removeObject(obj)">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="AddPermissions-footer">
|
||||||
|
<div class="buttons Form-buttons AddPermissions-buttons">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm Form-saveButton"
|
||||||
|
ng-click="updatePermissions()"
|
||||||
|
ng-disabled="userRoleForm.$invalid || !allSelected || !allSelected.length">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm Form-cancelButton"
|
||||||
|
ng-click="closeModal()">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/* jshint unused: vars */
|
||||||
|
export default
|
||||||
|
['addPermissionsTeamsList', 'addPermissionsUsersList', 'generateList', 'GetBasePath', 'SelectionInit', 'SearchInit',
|
||||||
|
'PaginateInit', function(addPermissionsTeamsList,
|
||||||
|
addPermissionsUsersList, generateList,
|
||||||
|
GetBasePath, SelectionInit, SearchInit, PaginateInit) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
},
|
||||||
|
template: "<div class='addPermissionsList-inner'></div>",
|
||||||
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
scope.$on("linkLists", function(e) {
|
||||||
|
var generator = generateList,
|
||||||
|
list = addPermissionsTeamsList,
|
||||||
|
url = GetBasePath("teams"),
|
||||||
|
set = "teams",
|
||||||
|
id = "addPermissionsTeamsList",
|
||||||
|
mode = "edit";
|
||||||
|
|
||||||
|
if (attrs.type === 'users') {
|
||||||
|
list = addPermissionsUsersList;
|
||||||
|
url = GetBasePath("users") + "?is_superuser=false";
|
||||||
|
set = "users";
|
||||||
|
id = "addPermissionsUsersList";
|
||||||
|
mode = "edit";
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.id = id;
|
||||||
|
|
||||||
|
scope.$watch("selectedItems", function() {
|
||||||
|
scope.$emit("itemsSelected", scope.selectedItems);
|
||||||
|
});
|
||||||
|
|
||||||
|
element.find(".addPermissionsList-inner")
|
||||||
|
.attr("id", id);
|
||||||
|
|
||||||
|
generator.inject(list, { id: id,
|
||||||
|
title: false, mode: mode, scope: scope });
|
||||||
|
|
||||||
|
SearchInit({ scope: scope, set: set,
|
||||||
|
list: list, url: url });
|
||||||
|
|
||||||
|
PaginateInit({ scope: scope,
|
||||||
|
list: list, url: url, pageSize: 5 });
|
||||||
|
|
||||||
|
scope.search(list.iterator);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import addPermissionsListDirective from './addPermissionsList.directive';
|
||||||
|
import teamsList from './permissionsTeams.list';
|
||||||
|
import usersList from './permissionsUsers.list';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('addPermissionsListModule', [])
|
||||||
|
.directive('addPermissionsList', addPermissionsListDirective)
|
||||||
|
.factory('addPermissionsTeamsList', teamsList)
|
||||||
|
.factory('addPermissionsUsersList', usersList);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
name: 'teams',
|
||||||
|
iterator: 'team',
|
||||||
|
listTitleBadge: false,
|
||||||
|
multiSelect: true,
|
||||||
|
multiSelectExtended: true,
|
||||||
|
index: false,
|
||||||
|
hover: true,
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
key: true,
|
||||||
|
label: 'name'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
name: 'users',
|
||||||
|
iterator: 'user',
|
||||||
|
title: false,
|
||||||
|
listTitleBadge: false,
|
||||||
|
multiSelect: true,
|
||||||
|
multiSelectExtended: true,
|
||||||
|
index: false,
|
||||||
|
hover: true,
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
first_name: {
|
||||||
|
label: 'First Name',
|
||||||
|
columnClass: 'col-md-3 col-sm-3 hidden-xs'
|
||||||
|
},
|
||||||
|
last_name: {
|
||||||
|
label: 'Last Name',
|
||||||
|
columnClass: 'col-md-3 col-sm-3 hidden-xs'
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
key: true,
|
||||||
|
label: 'Username',
|
||||||
|
columnClass: 'col-md-3 col-sm-3 col-xs-9'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
14
awx/ui/client/src/access/addPermissions/main.js
Normal file
14
awx/ui/client/src/access/addPermissions/main.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import addPermissionsDirective from './addPermissions.directive';
|
||||||
|
import roleSelect from './roleSelect.directive';
|
||||||
|
import addPermissionsList from './addPermissionsList/main';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('AddPermissions', [addPermissionsList.name])
|
||||||
|
.directive('addPermissions', addPermissionsDirective)
|
||||||
|
.directive('roleSelect', roleSelect);
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/* jshint unused: vars */
|
||||||
|
export default
|
||||||
|
[
|
||||||
|
'CreateSelect2',
|
||||||
|
function(CreateSelect2) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: false,
|
||||||
|
template: '<select ng-cloak class="AddPermissions-selectHide roleSelect2 form-control" ng-model="obj.roles" ng-options="role.label for role in roles track by role.value" multiple required></select>',
|
||||||
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
CreateSelect2({
|
||||||
|
element: '.roleSelect2',
|
||||||
|
multiple: true,
|
||||||
|
placeholder: 'Select roles'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
12
awx/ui/client/src/access/main.js
Normal file
12
awx/ui/client/src/access/main.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import roleList from './roleList.directive';
|
||||||
|
import addPermissions from './addPermissions/main';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('access', [addPermissions.name])
|
||||||
|
.directive('roleList', roleList);
|
||||||
72
awx/ui/client/src/access/roleList.block.less
Normal file
72
awx/ui/client/src/access/roleList.block.less
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/** @define RoleList */
|
||||||
|
@import "../shared/branding/colors.default.less";
|
||||||
|
|
||||||
|
.RoleList {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-tagContainer {
|
||||||
|
display: flex;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-tag {
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
margin: 4px 0px;
|
||||||
|
border: 1px solid @default-second-border;
|
||||||
|
font-size: 12px;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background-color: @default-bg;
|
||||||
|
margin-right: 5px;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-tag--deletable {
|
||||||
|
margin-right: 0px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
border-right: 0;
|
||||||
|
max-wdith: ~"calc(100% - 23px)";
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-deleteContainer {
|
||||||
|
border: 1px solid @default-second-border;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
margin: 4px 0px;
|
||||||
|
margin-right: 5px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-tagDelete {
|
||||||
|
font-size: 13px;
|
||||||
|
color: @default-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-name {
|
||||||
|
flex: initial;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-tag--deletable > .RoleList-name {
|
||||||
|
max-width: ~"calc(100% - 23px)";
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-deleteContainer:hover, {
|
||||||
|
border-color: @default-err;
|
||||||
|
background-color: @default-err;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoleList-deleteContainer:hover > .RoleList-tagDelete {
|
||||||
|
color: @default-bg;
|
||||||
|
}
|
||||||
44
awx/ui/client/src/access/roleList.directive.js
Normal file
44
awx/ui/client/src/access/roleList.directive.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/* jshint unused: vars */
|
||||||
|
export default
|
||||||
|
[ 'templateUrl',
|
||||||
|
function(templateUrl) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: false,
|
||||||
|
templateUrl: templateUrl('access/roleList'),
|
||||||
|
link: function(scope, element, attrs) {
|
||||||
|
// given a list of roles (things like "project
|
||||||
|
// auditor") which are pulled from two different
|
||||||
|
// places in summary fields, and creates a
|
||||||
|
// concatenated/sorted list
|
||||||
|
scope.roles = []
|
||||||
|
.concat(scope.permission.summary_fields
|
||||||
|
.direct_access.map(function(i) {
|
||||||
|
return {
|
||||||
|
name: i.role.name,
|
||||||
|
roleId: i.role.id,
|
||||||
|
resourceName: i.role.resource_name,
|
||||||
|
explicit: true
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
.concat(scope.permission.summary_fields
|
||||||
|
.indirect_access.map(function(i) {
|
||||||
|
return {
|
||||||
|
name: i.role.name,
|
||||||
|
roleId: i.role.id,
|
||||||
|
explicit: false
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
.sort(function(a, b) {
|
||||||
|
if (a.name
|
||||||
|
.toLowerCase() > b.name
|
||||||
|
.toLowerCase()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
13
awx/ui/client/src/access/roleList.partial.html
Normal file
13
awx/ui/client/src/access/roleList.partial.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="RoleList-tagContainer"
|
||||||
|
ng-repeat="role in roles">
|
||||||
|
<div class="RoleList-tag"
|
||||||
|
ng-class="{'RoleList-tag--deletable': role.explicit}">
|
||||||
|
<span class="RoleList-name">{{ role.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="RoleList-deleteContainer"
|
||||||
|
ng-if="role.explicit"
|
||||||
|
ng-click="deletePermission(permission.id, role.roleId, permission.username, role.name, role.resourceName)">
|
||||||
|
<i ng-if="role.explicit"
|
||||||
|
class="fa fa-times RoleList-tagDelete"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -31,6 +31,7 @@ import permissions from './permissions/main';
|
|||||||
import managementJobs from './management-jobs/main';
|
import managementJobs from './management-jobs/main';
|
||||||
import jobDetail from './job-detail/main';
|
import jobDetail from './job-detail/main';
|
||||||
import notifications from './notifications/main';
|
import notifications from './notifications/main';
|
||||||
|
import access from './access/main';
|
||||||
|
|
||||||
// modules
|
// modules
|
||||||
import about from './about/main';
|
import about from './about/main';
|
||||||
@@ -101,6 +102,7 @@ var tower = angular.module('Tower', [
|
|||||||
jobDetail.name,
|
jobDetail.name,
|
||||||
notifications.name,
|
notifications.name,
|
||||||
standardOut.name,
|
standardOut.name,
|
||||||
|
access.name,
|
||||||
'templates',
|
'templates',
|
||||||
'Utilities',
|
'Utilities',
|
||||||
'OrganizationFormDefinition',
|
'OrganizationFormDefinition',
|
||||||
@@ -884,16 +886,42 @@ var tower = angular.module('Tower', [
|
|||||||
}]);
|
}]);
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.run(['$q', '$compile', '$cookieStore', '$rootScope', '$log', '$state', 'CheckLicense',
|
.run(['$q', '$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'Socket',
|
||||||
'$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'Socket',
|
'LoadConfig', 'Store', 'ShowSocketHelp', 'pendoService', 'Prompt', 'Rest', 'Wait', 'ProcessErrors', '$state', 'GetBasePath',
|
||||||
'LoadConfig', 'Store', 'ShowSocketHelp', 'pendoService',
|
function ($q, $compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, Socket,
|
||||||
function (
|
LoadConfig, Store, ShowSocketHelp, pendoService, Prompt, Rest, Wait, ProcessErrors, $state, GetBasePath) {
|
||||||
$q, $compile, $cookieStore, $rootScope, $log, $state, CheckLicense,
|
|
||||||
$location, Authorization, LoadBasePaths, Timer, ClearScope, Socket,
|
|
||||||
LoadConfig, Store, ShowSocketHelp, pendoService)
|
|
||||||
{
|
|
||||||
var sock;
|
var sock;
|
||||||
|
|
||||||
|
$rootScope.addPermission = function (scope) {
|
||||||
|
$compile("<add-permissions class='AddPermissions'></add-permissions>")(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootScope.deletePermission = function (user, role, userName,
|
||||||
|
roleName, resourceName) {
|
||||||
|
var action = function () {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
Wait('start');
|
||||||
|
var url = GetBasePath("users") + user + "/roles/";
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.post({"disassociate": true, "id": role})
|
||||||
|
.success(function () {
|
||||||
|
Wait('stop');
|
||||||
|
$rootScope.$broadcast("refreshList", "permission");
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Could not disacssociate user from role. Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Prompt({
|
||||||
|
hdr: 'Remove Role from ' + resourceName,
|
||||||
|
body: '<div class="Prompt-bodyQuery">Confirm the removal of the <span class="Prompt-emphasis">' + roleName + '</span> role associated with ' + userName + '.</div>',
|
||||||
|
action: action,
|
||||||
|
actionText: 'REMOVE'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function activateTab() {
|
function activateTab() {
|
||||||
// Make the correct tab active
|
// Make the correct tab active
|
||||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||||
@@ -1027,6 +1055,7 @@ var tower = angular.module('Tower', [
|
|||||||
|
|
||||||
|
|
||||||
$rootScope.$on("$stateChangeStart", function (event, next, nextParams, prev) {
|
$rootScope.$on("$stateChangeStart", function (event, next, nextParams, prev) {
|
||||||
|
$rootScope.$broadcast("closePermissionsModal");
|
||||||
// this line removes the query params attached to a route
|
// this line removes the query params attached to a route
|
||||||
if(prev && prev.$$route &&
|
if(prev && prev.$$route &&
|
||||||
prev.$$route.name === 'systemTracking'){
|
prev.$$route.name === 'systemTracking'){
|
||||||
|
|||||||
@@ -649,7 +649,6 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
relatedSets = form.relatedSets(data.related);
|
relatedSets = form.relatedSets(data.related);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -210,36 +210,40 @@ export function TeamsEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
$scope.$emit("RefreshTeamsList");
|
$scope.$emit("RefreshTeamsList");
|
||||||
|
|
||||||
// return a promise from the options request with the permission type choices (including adhoc) as a param
|
// return a promise from the options request with the permission type choices (including adhoc) as a param
|
||||||
var permissionsChoice = fieldChoices({
|
// var permissionsChoice = fieldChoices({
|
||||||
scope: $scope,
|
// scope: $scope,
|
||||||
url: 'api/v1/' + base + '/' + id + '/permissions/',
|
// url: 'api/v1/' + base + '/' + id + '/permissions/',
|
||||||
field: 'permission_type'
|
// field: 'permission_type'
|
||||||
});
|
// });
|
||||||
|
|
||||||
// manipulate the choices from the options request to be set on
|
// // manipulate the choices from the options request to be set on
|
||||||
// scope and be usable by the list form
|
// // scope and be usable by the list form
|
||||||
permissionsChoice.then(function (choices) {
|
// permissionsChoice.then(function (choices) {
|
||||||
choices =
|
// choices =
|
||||||
fieldLabels({
|
// fieldLabels({
|
||||||
choices: choices
|
// choices: choices
|
||||||
});
|
// });
|
||||||
_.map(choices, function(n, key) {
|
// _.map(choices, function(n, key) {
|
||||||
$scope.permission_label[key] = n;
|
// $scope.permission_label[key] = n;
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
// manipulate the choices from the options request to be usable
|
// manipulate the choices from the options request to be usable
|
||||||
// by the search option for permission_type, you can't inject the
|
// by the search option for permission_type, you can't inject the
|
||||||
// list until this is done!
|
// list until this is done!
|
||||||
permissionsChoice.then(function (choices) {
|
// permissionsChoice.then(function (choices) {
|
||||||
form.related.permissions.fields.permission_type.searchOptions =
|
// form.related.permissions.fields.permission_type.searchOptions =
|
||||||
permissionsSearchSelect({
|
// permissionsSearchSelect({
|
||||||
choices: choices
|
// choices: choices
|
||||||
});
|
// });
|
||||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||||
generator.reset();
|
generator.reset();
|
||||||
$scope.$emit('loadTeam');
|
$scope.$emit('loadTeam');
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||||
|
generator.reset();
|
||||||
|
$scope.$emit('loadTeam');
|
||||||
|
|
||||||
$scope.team_id = id;
|
$scope.team_id = id;
|
||||||
|
|
||||||
|
|||||||
@@ -240,37 +240,38 @@ export function UsersEdit($scope, $rootScope, $compile, $location, $log,
|
|||||||
|
|
||||||
$scope.$emit("RefreshUsersList");
|
$scope.$emit("RefreshUsersList");
|
||||||
|
|
||||||
// return a promise from the options request with the permission type choices (including adhoc) as a param
|
// // return a promise from the options request with the permission type choices (including adhoc) as a param
|
||||||
var permissionsChoice = fieldChoices({
|
// var permissionsChoice = fieldChoices({
|
||||||
scope: $scope,
|
// scope: $scope,
|
||||||
url: 'api/v1/' + base + '/' + id + '/permissions/',
|
// url: 'api/v1/' + base + '/' + id + '/permissions/',
|
||||||
field: 'permission_type'
|
// field: 'permission_type'
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
// manipulate the choices from the options request to be set on
|
// // manipulate the choices from the options request to be set on
|
||||||
// scope and be usable by the list form
|
// // scope and be usable by the list form
|
||||||
permissionsChoice.then(function (choices) {
|
// permissionsChoice.then(function (choices) {
|
||||||
choices =
|
// choices =
|
||||||
fieldLabels({
|
// fieldLabels({
|
||||||
choices: choices
|
// choices: choices
|
||||||
});
|
// });
|
||||||
_.map(choices, function(n, key) {
|
// _.map(choices, function(n, key) {
|
||||||
$scope.permission_label[key] = n;
|
// $scope.permission_label[key] = n;
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
// manipulate the choices from the options request to be usable
|
// manipulate the choices from the options request to be usable
|
||||||
// by the search option for permission_type, you can't inject the
|
// by the search option for permission_type, you can't inject the
|
||||||
// list until this is done!
|
// list until this is done!
|
||||||
permissionsChoice.then(function (choices) {
|
// permissionsChoice.then(function (choices) {
|
||||||
form.related.permissions.fields.permission_type.searchOptions =
|
// form.related.permissions.fields.permission_type.searchOptions =
|
||||||
permissionsSearchSelect({
|
// permissionsSearchSelect({
|
||||||
choices: choices
|
// choices: choices
|
||||||
});
|
// });
|
||||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
// });
|
||||||
generator.reset();
|
|
||||||
$scope.$emit("loadForm");
|
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||||
});
|
generator.reset();
|
||||||
|
$scope.$emit("loadForm");
|
||||||
|
|
||||||
if ($scope.removeFormReady) {
|
if ($scope.removeFormReady) {
|
||||||
$scope.removeFormReady();
|
$scope.removeFormReady();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
import sanitizeFilter from './shared/xss-sanitizer.filter';
|
import sanitizeFilter from './shared/xss-sanitizer.filter';
|
||||||
import capitalizeFilter from './shared/capitalize.filter';
|
import capitalizeFilter from './shared/capitalize.filter';
|
||||||
import longDateFilter from './shared/long-date.filter';
|
import longDateFilter from './shared/long-date.filter';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
sanitizeFilter,
|
sanitizeFilter,
|
||||||
capitalizeFilter,
|
capitalizeFilter,
|
||||||
|
|||||||
@@ -278,83 +278,38 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
permissions: {
|
||||||
schedules: {
|
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
title: 'Schedules',
|
title: 'Permissions',
|
||||||
iterator: 'schedule',
|
iterator: 'permission',
|
||||||
index: false,
|
index: false,
|
||||||
open: false,
|
open: false,
|
||||||
|
searchType: 'select',
|
||||||
actions: {
|
actions: {
|
||||||
refresh: {
|
|
||||||
mode: 'all',
|
|
||||||
awToolTip: "Refresh the page",
|
|
||||||
ngClick: "refreshSchedules()",
|
|
||||||
actionClass: 'btn List-buttonDefault',
|
|
||||||
buttonContent: 'REFRESH',
|
|
||||||
ngHide: 'scheduleLoading == false && schedule_active_search == false && schedule_total_rows < 1'
|
|
||||||
},
|
|
||||||
add: {
|
add: {
|
||||||
mode: 'all',
|
ngClick: "addPermission",
|
||||||
ngClick: 'addSchedule()',
|
label: 'Add',
|
||||||
awToolTip: 'Add a new schedule',
|
awToolTip: 'Add a permission',
|
||||||
actionClass: 'btn List-buttonSubmit',
|
actionClass: 'btn List-buttonSubmit',
|
||||||
buttonContent: '+ ADD'
|
buttonContent: '+ ADD'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
name: {
|
username: {
|
||||||
key: true,
|
key: true,
|
||||||
label: 'Name',
|
label: 'User',
|
||||||
ngClick: "editSchedule(schedule.id)",
|
linkBase: 'users',
|
||||||
columnClass: "col-md-3 col-sm-3 col-xs-3"
|
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
|
||||||
},
|
},
|
||||||
dtstart: {
|
role: {
|
||||||
label: 'First Run',
|
label: 'Role',
|
||||||
filter: "longDate",
|
type: 'role',
|
||||||
searchable: false,
|
noSort: true,
|
||||||
columnClass: "col-md-2 col-sm-3 hidden-xs"
|
class: 'col-lg-9 col-md-9 col-sm-9 col-xs-8'
|
||||||
},
|
|
||||||
next_run: {
|
|
||||||
label: 'Next Run',
|
|
||||||
filter: "longDate",
|
|
||||||
searchable: false,
|
|
||||||
columnClass: "col-md-2 col-sm-3 col-xs-3"
|
|
||||||
},
|
|
||||||
dtend: {
|
|
||||||
label: 'Final Run',
|
|
||||||
filter: "longDate",
|
|
||||||
searchable: false,
|
|
||||||
columnClass: "col-md-2 col-sm-3 hidden-xs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fieldActions: {
|
|
||||||
"play": {
|
|
||||||
mode: "all",
|
|
||||||
ngClick: "toggleSchedule($event, schedule.id)",
|
|
||||||
awToolTip: "{{ schedule.play_tip }}",
|
|
||||||
dataTipWatch: "schedule.play_tip",
|
|
||||||
iconClass: "{{ 'fa icon-schedule-enabled-' + schedule.enabled }}",
|
|
||||||
dataPlacement: "top"
|
|
||||||
},
|
|
||||||
edit: {
|
|
||||||
label: 'Edit',
|
|
||||||
ngClick: "editSchedule(schedule.id)",
|
|
||||||
icon: 'icon-edit',
|
|
||||||
awToolTip: 'Edit schedule',
|
|
||||||
dataPlacement: 'top'
|
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
label: 'Delete',
|
|
||||||
ngClick: "deleteSchedule(schedule.id)",
|
|
||||||
icon: 'icon-trash',
|
|
||||||
awToolTip: 'Delete schedule',
|
|
||||||
dataPlacement: 'top'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
relatedSets: function(urls) {
|
relatedSets: function(urls) {
|
||||||
@@ -363,9 +318,9 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
|
|||||||
iterator: 'organization',
|
iterator: 'organization',
|
||||||
url: urls.organizations
|
url: urls.organizations
|
||||||
},
|
},
|
||||||
schedules: {
|
permissions: {
|
||||||
iterator: 'schedule',
|
iterator: 'permission',
|
||||||
url: urls.schedules
|
url: urls.resource_access_list
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,69 +158,69 @@ export default
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
permissions: {
|
// permissions: {
|
||||||
type: 'collection',
|
// type: 'collection',
|
||||||
title: 'Permissions',
|
// title: 'Permissions',
|
||||||
iterator: 'permission',
|
// iterator: 'permission',
|
||||||
open: false,
|
// open: false,
|
||||||
index: false,
|
// index: false,
|
||||||
|
//
|
||||||
actions: {
|
// actions: {
|
||||||
add: {
|
// add: {
|
||||||
ngClick: "add('permissions')",
|
// ngClick: "add('permissions')",
|
||||||
label: 'Add',
|
// label: 'Add',
|
||||||
awToolTip: 'Add a permission for this user',
|
// awToolTip: 'Add a permission for this user',
|
||||||
ngShow: 'PermissionAddAllowed',
|
// ngShow: 'PermissionAddAllowed',
|
||||||
actionClass: 'btn List-buttonSubmit',
|
// actionClass: 'btn List-buttonSubmit',
|
||||||
buttonContent: '+ ADD'
|
// buttonContent: '+ ADD'
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
|
//
|
||||||
fields: {
|
// fields: {
|
||||||
name: {
|
// name: {
|
||||||
key: true,
|
// key: true,
|
||||||
label: 'Name',
|
// label: 'Name',
|
||||||
ngClick: "edit('permissions', permission.id, permission.name)"
|
// ngClick: "edit('permissions', permission.id, permission.name)"
|
||||||
},
|
// },
|
||||||
inventory: {
|
// inventory: {
|
||||||
label: 'Inventory',
|
// label: 'Inventory',
|
||||||
sourceModel: 'inventory',
|
// sourceModel: 'inventory',
|
||||||
sourceField: 'name',
|
// sourceField: 'name',
|
||||||
ngBind: 'permission.summary_fields.inventory.name'
|
// ngBind: 'permission.summary_fields.inventory.name'
|
||||||
},
|
// },
|
||||||
project: {
|
// project: {
|
||||||
label: 'Project',
|
// label: 'Project',
|
||||||
sourceModel: 'project',
|
// sourceModel: 'project',
|
||||||
sourceField: 'name',
|
// sourceField: 'name',
|
||||||
ngBind: 'permission.summary_fields.project.name'
|
// ngBind: 'permission.summary_fields.project.name'
|
||||||
},
|
// },
|
||||||
permission_type: {
|
// permission_type: {
|
||||||
label: 'Permission',
|
// label: 'Permission',
|
||||||
ngBind: 'getPermissionText()',
|
// ngBind: 'getPermissionText()',
|
||||||
searchType: 'select'
|
// searchType: 'select'
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
|
//
|
||||||
fieldActions: {
|
// fieldActions: {
|
||||||
edit: {
|
// edit: {
|
||||||
label: 'Edit',
|
// label: 'Edit',
|
||||||
ngClick: "edit('permissions', permission.id, permission.name)",
|
// ngClick: "edit('permissions', permission.id, permission.name)",
|
||||||
icon: 'icon-edit',
|
// icon: 'icon-edit',
|
||||||
awToolTip: 'Edit the permission',
|
// awToolTip: 'Edit the permission',
|
||||||
'class': 'btn btn-default'
|
// 'class': 'btn btn-default'
|
||||||
},
|
// },
|
||||||
|
//
|
||||||
"delete": {
|
// "delete": {
|
||||||
label: 'Delete',
|
// label: 'Delete',
|
||||||
ngClick: "delete('permissions', permission.id, permission.name, 'permission')",
|
// ngClick: "delete('permissions', permission.id, permission.name, 'permission')",
|
||||||
icon: 'icon-trash',
|
// icon: 'icon-trash',
|
||||||
"class": 'btn-danger',
|
// "class": 'btn-danger',
|
||||||
awToolTip: 'Delete the permission',
|
// awToolTip: 'Delete the permission',
|
||||||
ngShow: 'PermissionAddAllowed'
|
// ngShow: 'PermissionAddAllowed'
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
},
|
// },
|
||||||
|
|
||||||
admin_of_organizations: { // Assumes a plural name (e.g. things)
|
admin_of_organizations: { // Assumes a plural name (e.g. things)
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default
|
|||||||
// Which page are we on?
|
// Which page are we on?
|
||||||
if (Empty(next) && previous) {
|
if (Empty(next) && previous) {
|
||||||
// no next page, but there is a previous page
|
// no next page, but there is a previous page
|
||||||
scope[iterator + '_page'] = scope[iterator + '_num_pages'];
|
scope[iterator + '_page'] = /page=\d+/.test(previous) ? parseInt(previous.match(/page=(\d+)/)[1]) + 1 : 2;
|
||||||
} else if (next && Empty(previous)) {
|
} else if (next && Empty(previous)) {
|
||||||
// next page available, but no previous page
|
// next page available, but no previous page
|
||||||
scope[iterator + '_page'] = 1;
|
scope[iterator + '_page'] = 1;
|
||||||
|
|||||||
@@ -230,10 +230,15 @@ export default
|
|||||||
url += (url.match(/\/$/)) ? '?' : '&';
|
url += (url.match(/\/$/)) ? '?' : '&';
|
||||||
url += scope[iterator + 'SearchParams'];
|
url += scope[iterator + 'SearchParams'];
|
||||||
url += (scope[iterator + '_page_size']) ? '&page_size=' + scope[iterator + '_page_size'] : "";
|
url += (scope[iterator + '_page_size']) ? '&page_size=' + scope[iterator + '_page_size'] : "";
|
||||||
|
scope[iterator + '_active_search'] = true;
|
||||||
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
|
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
scope.$on("refreshList", function(e, iterator) {
|
||||||
|
scope.search(iterator);
|
||||||
|
});
|
||||||
|
|
||||||
scope.sort = function (iterator, fld) {
|
scope.sort = function (iterator, fld) {
|
||||||
var sort_order, icon, direction, set;
|
var sort_order, icon, direction, set;
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ export default
|
|||||||
}
|
}
|
||||||
Store('sessionTime', x);
|
Store('sessionTime', x);
|
||||||
|
|
||||||
$rootScope.lastUser = $cookieStore.get('current_user').id;
|
if ($cookieStore.get('current_user')) {
|
||||||
|
$rootScope.lastUser = $cookieStore.get('current_user').id;
|
||||||
|
}
|
||||||
$cookieStore.remove('token_expires');
|
$cookieStore.remove('token_expires');
|
||||||
$cookieStore.remove('current_user');
|
$cookieStore.remove('current_user');
|
||||||
$cookieStore.remove('token');
|
$cookieStore.remove('token');
|
||||||
|
|||||||
@@ -136,6 +136,10 @@
|
|||||||
.MainMenu-itemText--username {
|
.MainMenu-itemText--username {
|
||||||
padding-left: 13px;
|
padding-left: 13px;
|
||||||
margin-top: -4px;
|
margin-top: -4px;
|
||||||
|
max-width: 85px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MainMenu-itemImage {
|
.MainMenu-itemImage {
|
||||||
|
|||||||
@@ -614,7 +614,8 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
|
|||||||
|
|
||||||
var element = params.element,
|
var element = params.element,
|
||||||
options = params.opts,
|
options = params.opts,
|
||||||
multiple = (params.multiple!==undefined) ? params.multiple : true;
|
multiple = (params.multiple!==undefined) ? params.multiple : true,
|
||||||
|
placeholder = params.placeholder;
|
||||||
|
|
||||||
$.fn.select2.amd.require([
|
$.fn.select2.amd.require([
|
||||||
'select2/utils',
|
'select2/utils',
|
||||||
@@ -632,6 +633,7 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
|
|||||||
}, Dropdown);
|
}, Dropdown);
|
||||||
|
|
||||||
$(element).select2({
|
$(element).select2({
|
||||||
|
placeholder: placeholder,
|
||||||
multiple: multiple,
|
multiple: multiple,
|
||||||
containerCssClass: 'Form-dropDown',
|
containerCssClass: 'Form-dropDown',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
@@ -1548,7 +1548,9 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
|
|
||||||
html += "<div class=\"buttons Form-buttons\" ";
|
html += "<div class=\"buttons Form-buttons\" ";
|
||||||
html += "id=\"" + this.form.name + "_controls\" ";
|
html += "id=\"" + this.form.name + "_controls\" ";
|
||||||
|
if (options.mode === 'edit' && this.form.tabs) {
|
||||||
|
html += "ng-show=\"" + this.form.name + "Selected\"; "
|
||||||
|
}
|
||||||
html += ">\n";
|
html += ">\n";
|
||||||
|
|
||||||
if (this.form.horizontal) {
|
if (this.form.horizontal) {
|
||||||
@@ -1723,32 +1725,52 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
html += "<tr class=\"List-tableHeaderRow\">\n";
|
html += "<tr class=\"List-tableHeaderRow\">\n";
|
||||||
html += (collection.index === undefined || collection.index !== false) ? "<th class=\"col-xs-1\">#</th>\n" : "";
|
html += (collection.index === undefined || collection.index !== false) ? "<th class=\"col-xs-1\">#</th>\n" : "";
|
||||||
for (fld in collection.fields) {
|
for (fld in collection.fields) {
|
||||||
html += "<th class=\"List-tableHeader list-header\" id=\"" + collection.iterator + '-' + fld + "-header\" " +
|
html += "<th class=\"List-tableHeader list-header ";
|
||||||
"ng-click=\"sort('" + collection.iterator + "', '" + fld + "')\">" +
|
html += (collection.fields[fld].class) ? collection.fields[fld].class : "";
|
||||||
collection.fields[fld].label;
|
html += "\" id=\"" + collection.iterator + '-' + fld + "-header\" ";
|
||||||
html += " <i class=\"";
|
|
||||||
if (collection.fields[fld].key) {
|
if (!collection.fields[fld].noSort) {
|
||||||
if (collection.fields[fld].desc) {
|
html += "ng-click=\"sort('" + collection.iterator + "', '" + fld + "')\">"
|
||||||
html += "fa fa-sort-down";
|
|
||||||
} else {
|
|
||||||
html += "fa fa-sort-up";
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
html += "fa fa-sort";
|
html += ">";
|
||||||
}
|
}
|
||||||
html += "\"></i></a></th>\n";
|
|
||||||
|
|
||||||
|
html += collection.fields[fld].label;
|
||||||
|
|
||||||
|
if (!collection.fields[fld].noSort) {
|
||||||
|
html += " <i class=\"";
|
||||||
|
|
||||||
|
|
||||||
|
if (collection.fields[fld].key) {
|
||||||
|
if (collection.fields[fld].desc) {
|
||||||
|
html += "fa fa-sort-down";
|
||||||
|
} else {
|
||||||
|
html += "fa fa-sort-up";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html += "fa fa-sort";
|
||||||
|
}
|
||||||
|
html += "\"></i>"
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</a></th>\n";
|
||||||
|
}
|
||||||
|
if (collection.fieldActions) {
|
||||||
|
html += "<th class=\"List-tableHeader List-tableHeader--actions\">Actions</th>\n";
|
||||||
}
|
}
|
||||||
html += "<th class=\"List-tableHeader\">Actions</th>\n";
|
|
||||||
html += "</tr>\n";
|
html += "</tr>\n";
|
||||||
html += "</thead>";
|
html += "</thead>";
|
||||||
html += "<tbody>\n";
|
html += "<tbody>\n";
|
||||||
|
|
||||||
html += "<tr class=\"List-tableHeaderRow\" ng-repeat=\"" + collection.iterator + " in " + itm + "\" ";
|
html += "<tr class=\"List-tableRow\" ng-repeat=\"" + collection.iterator + " in " + itm + "\" ";
|
||||||
html += "ng-class-odd=\"'List-tableRow--oddRow'\" ";
|
html += "ng-class-odd=\"'List-tableRow--oddRow'\" ";
|
||||||
html += "ng-class-even=\"'List-tableRow--evenRow'\" ";
|
html += "ng-class-even=\"'List-tableRow--evenRow'\" ";
|
||||||
html += "id=\"{{ " + collection.iterator + ".id }}\">\n";
|
html += "id=\"{{ " + collection.iterator + ".id }}\">\n";
|
||||||
if (collection.index === undefined || collection.index !== false) {
|
if (collection.index === undefined || collection.index !== false) {
|
||||||
html += "<td class=\"List-tableCell\">{{ $index + ((" + collection.iterator + "_page - 1) * " +
|
html += "<td class=\"List-tableCell";
|
||||||
|
html += (collection.fields[fld].class) ? collection.fields[fld].class : "";
|
||||||
|
html += "\">{{ $index + ((" + collection.iterator + "_page - 1) * " +
|
||||||
collection.iterator + "_page_size) + 1 }}.</td>\n";
|
collection.iterator + "_page_size) + 1 }}.</td>\n";
|
||||||
}
|
}
|
||||||
cnt = 1;
|
cnt = 1;
|
||||||
@@ -1765,31 +1787,33 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Row level actions
|
// Row level actions
|
||||||
html += "<td class=\"List-tableCell List-actionButtonCell actions\">";
|
if (collection.fieldActions) {
|
||||||
for (act in collection.fieldActions) {
|
html += "<td class=\"List-tableCell List-actionButtonCell actions\">";
|
||||||
fAction = collection.fieldActions[act];
|
for (act in collection.fieldActions) {
|
||||||
html += "<button id=\"" + ((fAction.id) ? fAction.id : act + "-action") + "\" ";
|
fAction = collection.fieldActions[act];
|
||||||
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
|
html += "<button id=\"" + ((fAction.id) ? fAction.id : act + "-action") + "\" ";
|
||||||
html += (fAction.ngClick) ? this.attr(fAction, 'ngClick') : "";
|
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
|
||||||
html += (fAction.ngHref) ? this.attr(fAction, 'ngHref') : "";
|
html += (fAction.ngClick) ? this.attr(fAction, 'ngClick') : "";
|
||||||
html += (fAction.ngShow) ? this.attr(fAction, 'ngShow') : "";
|
html += (fAction.ngHref) ? this.attr(fAction, 'ngHref') : "";
|
||||||
html += " class=\"List-actionButton ";
|
html += (fAction.ngShow) ? this.attr(fAction, 'ngShow') : "";
|
||||||
html += (act === 'delete') ? "List-actionButton--delete" : "";
|
html += " class=\"List-actionButton ";
|
||||||
html += "\"";
|
html += (act === 'delete') ? "List-actionButton--delete" : "";
|
||||||
html += ">";
|
html += "\"";
|
||||||
if (fAction.iconClass) {
|
html += ">";
|
||||||
html += "<i class=\"" + fAction.iconClass + "\"></i>";
|
if (fAction.iconClass) {
|
||||||
} else {
|
html += "<i class=\"" + fAction.iconClass + "\"></i>";
|
||||||
html += SelectIcon({
|
} else {
|
||||||
action: act
|
html += SelectIcon({
|
||||||
});
|
action: act
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// html += SelectIcon({ action: act });
|
||||||
|
//html += (fAction.label) ? "<span class=\"list-action-label\"> " + fAction.label + "</span>": "";
|
||||||
|
html += "</button>";
|
||||||
}
|
}
|
||||||
// html += SelectIcon({ action: act });
|
html += "</td>";
|
||||||
//html += (fAction.label) ? "<span class=\"list-action-label\"> " + fAction.label + "</span>": "";
|
html += "</tr>\n";
|
||||||
html += "</button>";
|
|
||||||
}
|
}
|
||||||
html += "</td>";
|
|
||||||
html += "</tr>\n";
|
|
||||||
|
|
||||||
// Message for loading
|
// Message for loading
|
||||||
html += "<tr ng-show=\"" + collection.iterator + "Loading == true\">\n";
|
html += "<tr ng-show=\"" + collection.iterator + "Loading == true\">\n";
|
||||||
|
|||||||
@@ -449,6 +449,8 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
|
|
||||||
if (field.type !== undefined && field.type === 'DropDown') {
|
if (field.type !== undefined && field.type === 'DropDown') {
|
||||||
html = DropDown(params);
|
html = DropDown(params);
|
||||||
|
} else if (field.type === 'role') {
|
||||||
|
html += "<td class=\"List-tableCell\"><role-list class=\"RoleList\"></role-list></td>";
|
||||||
} else if (field.type === 'badgeCount') {
|
} else if (field.type === 'badgeCount') {
|
||||||
html = BadgeCount(params);
|
html = BadgeCount(params);
|
||||||
} else if (field.type === 'badgeOnly') {
|
} else if (field.type === 'badgeOnly') {
|
||||||
@@ -520,7 +522,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
list: list,
|
list: list,
|
||||||
field: field,
|
field: field,
|
||||||
fld: fld,
|
fld: fld,
|
||||||
base: base
|
base: field.linkBase || base
|
||||||
}) + ' ';
|
}) + ' ';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -532,7 +534,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
list: list,
|
list: list,
|
||||||
field: field,
|
field: field,
|
||||||
fld: fld,
|
fld: fld,
|
||||||
base: base
|
base: field.linkBase || base
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -633,6 +635,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
var iterator = params.iterator,
|
var iterator = params.iterator,
|
||||||
form = params.template,
|
form = params.template,
|
||||||
size = params.size,
|
size = params.size,
|
||||||
|
mini = params.mini,
|
||||||
includeSize = (params.includeSize === undefined) ? true : params.includeSize,
|
includeSize = (params.includeSize === undefined) ? true : params.includeSize,
|
||||||
ngShow = (params.ngShow) ? params.ngShow : false,
|
ngShow = (params.ngShow) ? params.ngShow : false,
|
||||||
i, html = '',
|
i, html = '',
|
||||||
@@ -666,6 +669,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
|
|
||||||
if (includeSize) {
|
if (includeSize) {
|
||||||
html += "<div class=\"List-searchWidget ";
|
html += "<div class=\"List-searchWidget ";
|
||||||
|
html += (mini) ? "List-searchWidget--compact " : "";
|
||||||
html += (size) ? size : "col-lg-4 col-md-8 col-sm-12 col-xs-12";
|
html += (size) ? size : "col-lg-4 col-md-8 col-sm-12 col-xs-12";
|
||||||
html += "\" id=\"search-widget-container" + modifier + "\">\n";
|
html += "\" id=\"search-widget-container" + modifier + "\">\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -416,6 +416,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
|||||||
function buildTable() {
|
function buildTable() {
|
||||||
var extraClasses = list['class'];
|
var extraClasses = list['class'];
|
||||||
var multiSelect = list.multiSelect ? 'multi-select-list' : null;
|
var multiSelect = list.multiSelect ? 'multi-select-list' : null;
|
||||||
|
var multiSelectExtended = list.multiSelectExtended ? 'true' : 'false';
|
||||||
|
|
||||||
if (options.mode === 'summary') {
|
if (options.mode === 'summary') {
|
||||||
extraClasses += ' table-summary';
|
extraClasses += ' table-summary';
|
||||||
@@ -425,7 +426,8 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
|||||||
.attr('id', list.name + '_table')
|
.attr('id', list.name + '_table')
|
||||||
.addClass('List-table')
|
.addClass('List-table')
|
||||||
.addClass(extraClasses)
|
.addClass(extraClasses)
|
||||||
.attr('multi-select-list', multiSelect);
|
.attr('multi-select-list', multiSelect)
|
||||||
|
.attr('is-extended', multiSelectExtended);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +462,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (list.multiSelect) {
|
if (list.multiSelect) {
|
||||||
innerTable += '<td class="col-xs-1 select-column List-tableCell"><select-list-item item=\"' + list.iterator + '\"></select-list-item></td>';
|
innerTable += '<td class="col-xs-1 select-column List- List-staticColumn--smallStatus"><select-list-item item=\"' + list.iterator + '\"></select-list-item></td>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change layout if a lookup list, place radio buttons before labels
|
// Change layout if a lookup list, place radio buttons before labels
|
||||||
@@ -609,7 +611,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
|||||||
|
|
||||||
function buildSelectAll() {
|
function buildSelectAll() {
|
||||||
return $('<th>')
|
return $('<th>')
|
||||||
.addClass('col-xs-1 select-column List-tableHeader')
|
.addClass('col-xs-1 select-column List-tableHeader List-staticColumn--smallStatus')
|
||||||
.append(
|
.append(
|
||||||
$('<select-all>')
|
$('<select-all>')
|
||||||
.attr('selections-empty', 'selectedItems.length === 0')
|
.attr('selections-empty', 'selectedItems.length === 0')
|
||||||
@@ -665,10 +667,9 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.mode === 'select') {
|
if (options.mode === 'select') {
|
||||||
html += "<th class=\"List-tableHeader col-lg-1 col-md-1 col-sm-2 col-xs-2\">Select</th>";
|
html += "<th class=\"List-tableHeader col-lg-1 col-md-1 col-sm-2 col-xs-2\">Select</th>";
|
||||||
}
|
} else if (options.mode === 'edit' && list.fieldActions) {
|
||||||
else if (options.mode === 'edit' && list.fieldActions) {
|
html += "<th class=\"List-tableHeader List-tableHeader--actions actions-column";
|
||||||
html += "<th class=\"List-tableHeader actions-column";
|
|
||||||
html += (list.fieldActions && list.fieldActions.columnClass) ? " " + list.fieldActions.columnClass : "";
|
html += (list.fieldActions && list.fieldActions.columnClass) ? " " + list.fieldActions.columnClass : "";
|
||||||
html += "\">";
|
html += "\">";
|
||||||
html += (list.fieldActions.label === undefined || list.fieldActions.label) ? "Actions" : "";
|
html += (list.fieldActions.label === undefined || list.fieldActions.label) ? "Actions" : "";
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default
|
|||||||
item: '=item'
|
item: '=item'
|
||||||
},
|
},
|
||||||
require: '^multiSelectList',
|
require: '^multiSelectList',
|
||||||
template: '<input type="checkbox" data-multi-select-list-item ng-model="isSelected">',
|
template: '<input type="checkbox" data-multi-select-list-item ng-model="isSelected" ng-change="userInteractionSelect()">',
|
||||||
link: function(scope, element, attrs, multiSelectList) {
|
link: function(scope, element, attrs, multiSelectList) {
|
||||||
|
|
||||||
scope.isSelected = false;
|
scope.isSelected = false;
|
||||||
@@ -52,6 +52,10 @@ export default
|
|||||||
multiSelectList.deregisterItem(scope.decoratedItem);
|
multiSelectList.deregisterItem(scope.decoratedItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
scope.userInteractionSelect = function() {
|
||||||
|
scope.$emit("selectedOrDeselected", scope.decoratedItem);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -8,3 +8,8 @@
|
|||||||
.Prompt-bodyTarget {
|
.Prompt-bodyTarget {
|
||||||
color: @default-data-txt;
|
color: @default-data-txt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Prompt-emphasis {
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user