Commit 61ce7e68 by Czémán Arnold

dashboard, circle, network, request: upgrade and rework autocompletion, remove…

dashboard, circle, network, request: upgrade and rework autocompletion, remove deprecated 'pattern' from url patterns
parent 90a5d98b
...@@ -183,7 +183,8 @@ PIPELINE = { ...@@ -183,7 +183,8 @@ PIPELINE = {
"template.less", "template.less",
"dashboard/dashboard.less", "dashboard/dashboard.less",
"network/network.less", "network/network.less",
"autocomplete_light/style.css", "autocomplete_light/vendor/select2/dist/css/select2.css",
"autocomplete_light/select2.css",
), ),
"output_filename": "all.css", "output_filename": "all.css",
} }
...@@ -198,6 +199,10 @@ PIPELINE = { ...@@ -198,6 +199,10 @@ PIPELINE = {
"jquery-simple-slider/js/simple-slider.js", "jquery-simple-slider/js/simple-slider.js",
"favico.js/favico.js", "favico.js/favico.js",
"datatables/media/js/jquery.dataTables.js", "datatables/media/js/jquery.dataTables.js",
"autocomplete_light/jquery.init.js",
"autocomplete_light/autocomplete.init.js",
"autocomplete_light/vendor/select2/dist/js/select2.js",
"autocomplete_light/select2.js",
"dashboard/dashboard.js", "dashboard/dashboard.js",
"dashboard/activity.js", "dashboard/activity.js",
"dashboard/group-details.js", "dashboard/group-details.js",
...@@ -217,11 +222,6 @@ PIPELINE = { ...@@ -217,11 +222,6 @@ PIPELINE = {
"js/network.js", "js/network.js",
"js/switch-port.js", "js/switch-port.js",
"js/host-list.js", "js/host-list.js",
"autocomplete_light/autocomplete.js",
"autocomplete_light/widget.js",
"autocomplete_light/addanother.js",
"autocomplete_light/text_widget.js",
"autocomplete_light/remote.js",
), ),
"output_filename": "all.js", "output_filename": "all.js",
}, },
......
...@@ -15,13 +15,16 @@ ...@@ -15,13 +15,16 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import patterns, include, url from django.conf.urls import include, url
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.contrib.auth.views import (
password_reset_confirm, password_reset
)
from circle.settings.base import get_env_variable from circle.settings.base import get_env_variable
...@@ -33,9 +36,7 @@ from firewall.views import add_blacklist_item ...@@ -33,9 +36,7 @@ from firewall.views import add_blacklist_item
admin.autodiscover() admin.autodiscover()
urlpatterns = patterns( urlpatterns = [
'',
url(r'^$', lambda x: redirect(reverse("dashboard.index"))), url(r'^$', lambda x: redirect(reverse("dashboard.index"))),
url(r'^network/', include('network.urls')), url(r'^network/', include('network.urls')),
url(r'^blacklist-add/', add_blacklist_item), url(r'^blacklist-add/', add_blacklist_item),
...@@ -45,12 +46,11 @@ urlpatterns = patterns( ...@@ -45,12 +46,11 @@ urlpatterns = patterns(
# django/contrib/auth/urls.py (care when new version) # django/contrib/auth/urls.py (care when new version)
url((r'^accounts/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/' url((r'^accounts/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/'
r'(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$'), r'(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$'),
'django.contrib.auth.views.password_reset_confirm', password_reset_confirm,
{'set_password_form': CircleSetPasswordForm}, {'set_password_form': CircleSetPasswordForm},
name='accounts.password_reset_confirm' name='accounts.password_reset_confirm'
), ),
url(r'^accounts/password/reset/$', ("django.contrib.auth.views." url(r'^accounts/password/reset/$', password_reset,
"password_reset"),
{'password_reset_form': CirclePasswordResetForm}, {'password_reset_form': CirclePasswordResetForm},
name="accounts.password-reset", name="accounts.password-reset",
), ),
...@@ -73,27 +73,24 @@ urlpatterns = patterns( ...@@ -73,27 +73,24 @@ urlpatterns = patterns(
name="info.support"), name="info.support"),
url(r'^info/resize-how-to/$', ResizeHelpView.as_view(), url(r'^info/resize-how-to/$', ResizeHelpView.as_view(),
name="info.resize"), name="info.resize"),
) ]
if 'rosetta' in settings.INSTALLED_APPS: if 'rosetta' in settings.INSTALLED_APPS:
urlpatterns += patterns( urlpatterns += [
'',
url(r'^rosetta/', include('rosetta.urls')), url(r'^rosetta/', include('rosetta.urls')),
) ]
if settings.ADMIN_ENABLED: if settings.ADMIN_ENABLED:
urlpatterns += patterns( urlpatterns += [
'',
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
) ]
if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
urlpatterns += patterns( urlpatterns += [
'',
(r'^saml2/', include('djangosaml2.urls')), (r'^saml2/', include('djangosaml2.urls')),
) ]
handler500 = 'common.views.handler500' handler500 = 'common.views.handler500'
handler403 = 'common.views.handler403' handler403 = 'common.views.handler403'
...@@ -31,7 +31,7 @@ from django.contrib.auth.models import User, Group ...@@ -31,7 +31,7 @@ from django.contrib.auth.models import User, Group
from django.core.validators import URLValidator from django.core.validators import URLValidator
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import PermissionDenied, ValidationError
import autocomplete_light from dal import autocomplete
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import ( from crispy_forms.layout import (
Layout, Div, BaseInput, Field, HTML, Submit, TEMPLATE_PACK, Fieldset Layout, Div, BaseInput, Field, HTML, Submit, TEMPLATE_PACK, Fieldset
...@@ -43,7 +43,6 @@ from crispy_forms.bootstrap import FormActions ...@@ -43,7 +43,6 @@ from crispy_forms.bootstrap import FormActions
from django import forms from django import forms
from django.contrib.auth.forms import UserCreationForm as OrgUserCreationForm from django.contrib.auth.forms import UserCreationForm as OrgUserCreationForm
from django.forms.widgets import TextInput, HiddenInput from django.forms.widgets import TextInput, HiddenInput
from django.template import Context
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
...@@ -67,6 +66,7 @@ from .validators import domain_validator ...@@ -67,6 +66,7 @@ from .validators import domain_validator
from dashboard.models import ConnectCommand, create_profile from dashboard.models import ConnectCommand, create_profile
LANGUAGES_WITH_CODE = ((l[0], string_concat(l[1], " (", l[0], ")")) LANGUAGES_WITH_CODE = ((l[0], string_concat(l[1], " (", l[0], ")"))
for l in LANGUAGES) for l in LANGUAGES)
...@@ -1180,8 +1180,7 @@ class AnyTag(Div): ...@@ -1180,8 +1180,7 @@ class AnyTag(Div):
fields += render_field(field, form, form_style, context, fields += render_field(field, form, form_style, context,
template_pack=template_pack) template_pack=template_pack)
return render_to_string(self.template, Context({'tag': self, return render_to_string(self.template, {'tag': self, 'fields': fields})
'fields': fields}))
class WorkingBaseInput(BaseInput): class WorkingBaseInput(BaseInput):
...@@ -1334,27 +1333,31 @@ class UserEditForm(forms.ModelForm): ...@@ -1334,27 +1333,31 @@ class UserEditForm(forms.ModelForm):
class AclUserOrGroupAddForm(forms.Form): class AclUserOrGroupAddForm(forms.Form):
name = forms.CharField(widget=autocomplete_light.TextWidget( name = forms.CharField(
'AclUserGroupAutocomplete', widget=autocomplete.ListSelect2(
url='autocomplete.acl.user-group',
attrs={'class': 'form-control', attrs={'class': 'form-control',
'placeholder': _("Name of group or user")})) 'data-html': 'true',
'data-placeholder': _("Name of group or user")}))
class TransferOwnershipForm(forms.Form): class TransferOwnershipForm(forms.Form):
name = forms.CharField( name = forms.CharField(
widget=autocomplete_light.TextWidget( widget=autocomplete.ListSelect2(
'AclUserAutocomplete', url='autocomplete.acl.user',
attrs={'class': 'form-control', attrs={'class': 'form-control',
'placeholder': _("Name of user")}), 'data-html': 'true',
'data-placeholder': _("Name of user")}),
label=_("E-mail address or identifier of user")) label=_("E-mail address or identifier of user"))
class AddGroupMemberForm(forms.Form): class AddGroupMemberForm(forms.Form):
new_member = forms.CharField( new_member = forms.CharField(
widget=autocomplete_light.TextWidget( widget=autocomplete.ListSelect2(
'AclUserAutocomplete', url='autocomplete.acl.user',
attrs={'class': 'form-control', attrs={'class': 'form-control',
'placeholder': _("Name of user")}), 'data-html': 'true',
'data-placeholder': _("Name of user")}),
label=_("E-mail address or identifier of user")) label=_("E-mail address or identifier of user"))
......
...@@ -508,13 +508,6 @@ $.ajaxSetup({ ...@@ -508,13 +508,6 @@ $.ajaxSetup({
} }
}); });
/* for autocomplete */
$(function() {
yourlabs.TextWidget.prototype.getValue = function(choice) {
return choice.children().html();
};
});
var tagsToReplace = { var tagsToReplace = {
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
......
...@@ -1031,7 +1031,7 @@ textarea[name="new_members"] { ...@@ -1031,7 +1031,7 @@ textarea[name="new_members"] {
font-weight: bold; font-weight: bold;
} }
.hilight .autocomplete-hl { .select2-results__option--highlighted .autocomplete-hl {
color: orange; color: orange;
} }
......
...@@ -16,9 +16,8 @@ ...@@ -16,9 +16,8 @@
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import from __future__ import absolute_import
from django.conf.urls import patterns, url, include from django.conf.urls import url
import autocomplete_light
from vm.models import Instance from vm.models import Instance
from .views import ( from .views import (
AclUpdateView, FavouriteView, GroupAclUpdateView, GroupDelete, AclUpdateView, FavouriteView, GroupAclUpdateView, GroupDelete,
...@@ -56,14 +55,13 @@ from .views import ( ...@@ -56,14 +55,13 @@ from .views import (
StorageDetail, DiskDetail, StorageDetail, DiskDetail,
MessageList, MessageDetail, MessageCreate, MessageDelete, MessageList, MessageDetail, MessageCreate, MessageDelete,
EnableTwoFactorView, DisableTwoFactorView, EnableTwoFactorView, DisableTwoFactorView,
AclUserGroupAutocomplete, AclUserAutocomplete,
) )
from .views.vm import vm_ops, vm_mass_ops from .views.vm import vm_ops, vm_mass_ops
from .views.node import node_ops from .views.node import node_ops
autocomplete_light.autodiscover()
urlpatterns = patterns( urlpatterns = [
'',
url(r'^$', IndexView.as_view(), name="dashboard.index"), url(r'^$', IndexView.as_view(), name="dashboard.index"),
url(r"^profile/list/$", UserList.as_view(), url(r"^profile/list/$", UserList.as_view(),
name="dashboard.views.user-list"), name="dashboard.views.user-list"),
...@@ -217,8 +215,6 @@ urlpatterns = patterns( ...@@ -217,8 +215,6 @@ urlpatterns = patterns(
ConnectCommandCreate.as_view(), ConnectCommandCreate.as_view(),
name="dashboard.views.connect-command-create"), name="dashboard.views.connect-command-create"),
url(r'^autocomplete/', include('autocomplete_light.urls')),
url(r"^store/list/$", StoreList.as_view(), url(r"^store/list/$", StoreList.as_view(),
name="dashboard.views.store-list"), name="dashboard.views.store-list"),
url(r"^store/download/$", store_download, url(r"^store/download/$", store_download,
...@@ -253,22 +249,26 @@ urlpatterns = patterns( ...@@ -253,22 +249,26 @@ urlpatterns = patterns(
name="dashboard.views.message-create"), name="dashboard.views.message-create"),
url(r'^message/delete/(?P<pk>\d+)/$', MessageDelete.as_view(), url(r'^message/delete/(?P<pk>\d+)/$', MessageDelete.as_view(),
name="dashboard.views.message-delete"), name="dashboard.views.message-delete"),
)
urlpatterns += patterns( url(r'^autocomplete/acl/user-group/$',
'', AclUserGroupAutocomplete.as_view(),
*(url(r'^vm/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname()) name='autocomplete.acl.user-group'),
for op, v in vm_ops.iteritems()) url(r'^autocomplete/acl/user/$',
) AclUserAutocomplete.as_view(),
name='autocomplete.acl.user'),
]
urlpatterns += patterns( urlpatterns += [
'', url(r'^vm/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname())
*(url(r'^vm/mass_op/%s/$' % op, v.as_view(), name=v.get_urlname()) for op, v in vm_ops.iteritems()
for op, v in vm_mass_ops.iteritems()) ]
)
urlpatterns += patterns( urlpatterns += [
'', url(r'^vm/mass_op/%s/$' % op, v.as_view(), name=v.get_urlname())
*(url(r'^node/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname()) for op, v in vm_mass_ops.iteritems()
for op, v in node_ops.iteritems()) ]
)
urlpatterns += [
url(r'^node/(?P<pk>\d+)/op/%s/$' % op, v.as_view(), name=v.get_urlname())
for op, v in node_ops.iteritems()
]
...@@ -15,3 +15,4 @@ from graph import * ...@@ -15,3 +15,4 @@ from graph import *
from storage import * from storage import *
from request import * from request import *
from message import * from message import *
from autocomplete import *
...@@ -15,13 +15,16 @@ ...@@ -15,13 +15,16 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import autocomplete_light import json
from dal import autocomplete
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db.models import Q
from django.http import HttpResponse
from .views import AclUpdateView from ..views import AclUpdateView
from .models import Profile from ..models import Profile
def highlight(field, q, none_wo_match=True): def highlight(field, q, none_wo_match=True):
...@@ -48,13 +51,21 @@ def highlight(field, q, none_wo_match=True): ...@@ -48,13 +51,21 @@ def highlight(field, q, none_wo_match=True):
return escape(field) return escape(field)
class AclUserGroupAutocomplete(autocomplete_light.AutocompleteGenericBase): class AclUserAutocomplete(autocomplete.Select2ListView):
search_fields = ( search_fields = ('first_name', 'last_name', 'username',
('first_name', 'last_name', 'username', 'email', 'profile__org_id'), 'email', 'profile__org_id')
('name', 'groupprofile__org_id'),
) def filter(self, qs, search_fields):
choice_html_format = (u'<span data-value="%s"><span style="display:none"' if self.q:
u'>%s</span>%s</span>') condition = Q()
for field in search_fields:
condition |= Q(**{field + '__icontains': unicode(self.q)})
return list(qs.filter(condition))
return []
def get_list(self):
users = AclUpdateView.get_allowed_users(self.request.user)
return self.filter(users, self.search_fields)
def choice_displayed_text(self, choice): def choice_displayed_text(self, choice):
q = unicode(self.request.GET.get('q', '')) q = unicode(self.request.GET.get('q', ''))
...@@ -71,35 +82,17 @@ class AclUserGroupAutocomplete(autocomplete_light.AutocompleteGenericBase): ...@@ -71,35 +82,17 @@ class AclUserGroupAutocomplete(autocomplete_light.AutocompleteGenericBase):
else: else:
return _('%s (group)') % name return _('%s (group)') % name
def choice_html(self, choice): def get(self, *args, **kwargs):
return self.choice_html_format % ( return HttpResponse(json.dumps({
self.choice_value(choice), self.choice_label(choice), 'results': [dict(id=unicode(r), text=self.choice_displayed_text(r))
self.choice_displayed_text(choice)) for r in self.get_list()]
}), content_type="application/json")
def choices_for_request(self):
user = self.request.user
self.choices = (AclUpdateView.get_allowed_users(user),
AclUpdateView.get_allowed_groups(user))
return super(AclUserGroupAutocomplete, self).choices_for_request()
def autocomplete_html(self):
html = []
for choice in self.choices_for_request():
html.append(self.choice_html(choice))
if not html:
html = self.empty_html_format % _('no matches found').capitalize()
return self.autocomplete_html_format % ''.join(html)
class AclUserAutocomplete(AclUserGroupAutocomplete):
def choices_for_request(self):
user = self.request.user
self.choices = (AclUpdateView.get_allowed_users(user), )
return super(AclUserGroupAutocomplete, self).choices_for_request()
class AclUserGroupAutocomplete(AclUserAutocomplete):
group_search_fields = ('name', 'groupprofile__org_id')
autocomplete_light.register(AclUserGroupAutocomplete) def get_list(self):
autocomplete_light.register(AclUserAutocomplete) groups = AclUpdateView.get_allowed_groups(self.request.user)
groups = self.filter(groups, self.group_search_fields)
return super(AclUserGroupAutocomplete, self).get_list() + groups
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import patterns, url from django.conf.urls import url
from .views import ( from .views import (
IndexView, IndexView,
HostList, HostDetail, HostCreate, HostDelete, HostList, HostDetail, HostCreate, HostDelete,
...@@ -33,8 +33,7 @@ from .views import ( ...@@ -33,8 +33,7 @@ from .views import (
VlanAclUpdateView VlanAclUpdateView
) )
urlpatterns = patterns( urlpatterns = [
'',
url('^$', IndexView.as_view(), name='network.index'), url('^$', IndexView.as_view(), name='network.index'),
# blacklist # blacklist
url('^blacklist/$', BlacklistList.as_view(), url('^blacklist/$', BlacklistList.as_view(),
...@@ -135,4 +134,4 @@ urlpatterns = patterns( ...@@ -135,4 +134,4 @@ urlpatterns = patterns(
remove_switch_port_device, name='network.remove_switch_port_device'), remove_switch_port_device, name='network.remove_switch_port_device'),
url('^switchports/(?P<pk>\d+)/add/$', add_switch_port_device, url('^switchports/(?P<pk>\d+)/add/$', add_switch_port_device,
name='network.add_switch_port_device'), name='network.add_switch_port_device'),
) ]
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>. # with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import from __future__ import absolute_import
from django.conf.urls import patterns, url from django.conf.urls import url
from .views import ( from .views import (
RequestList, RequestDetail, RequestTypeList, RequestList, RequestDetail, RequestTypeList,
...@@ -26,8 +26,7 @@ from .views import ( ...@@ -26,8 +26,7 @@ from .views import (
LeaseTypeDelete, TemplateAccessTypeDelete, ResizeRequestView, LeaseTypeDelete, TemplateAccessTypeDelete, ResizeRequestView,
) )
urlpatterns = patterns( urlpatterns = [
'',
url(r'^list/$', RequestList.as_view(), url(r'^list/$', RequestList.as_view(),
name="request.views.request-list"), name="request.views.request-list"),
url(r'^(?P<pk>\d+)/$', RequestDetail.as_view(), url(r'^(?P<pk>\d+)/$', RequestDetail.as_view(),
...@@ -62,4 +61,4 @@ urlpatterns = patterns( ...@@ -62,4 +61,4 @@ urlpatterns = patterns(
name="request.views.request-resource"), name="request.views.request-resource"),
url(r'resize/(?P<vm_pk>\d+)/(?P<disk_pk>\d+)/$', url(r'resize/(?P<vm_pk>\d+)/(?P<disk_pk>\d+)/$',
ResizeRequestView.as_view(), name="request.views.request-resize"), ResizeRequestView.as_view(), name="request.views.request-resize"),
) ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment