Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

P4-3030: superuser user list search/sort #283

Merged
merged 35 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7b4da57
P4-4128: protect against schemes being None (python thing).
baracudda Dec 3, 2022
aabdf39
add debug line to vonage claim number.
baracudda Dec 4, 2022
e1e493b
WIP
baracudda Dec 4, 2022
b7112ee
query db for channel uuids to see what numbers are in use.
baracudda Dec 5, 2022
915478c
db query tweak, haml tweak, add to overrides.
baracudda Dec 5, 2022
a32ce13
blue circle for num avail; db query code tweak; comment out debug trace.
baracudda Dec 5, 2022
565806a
WIP
baracudda Dec 5, 2022
7088404
WIP
baracudda Dec 5, 2022
1810d1d
WIP
baracudda Dec 5, 2022
174cedf
WIP
baracudda Dec 5, 2022
30e6a05
WIP
baracudda Dec 5, 2022
3ceabec
WIP
baracudda Dec 5, 2022
6c767d2
WIP, regex working.
baracudda Dec 5, 2022
6474646
WIP
baracudda Dec 5, 2022
c8f3eaa
WIP, if key in list.
baracudda Dec 5, 2022
97f69ac
WIP, use correct list.
baracudda Dec 5, 2022
f2d77d9
WIP
baracudda Dec 5, 2022
256fafd
adjust haml to just use key.
baracudda Dec 5, 2022
c159f77
diff icon.
baracudda Dec 5, 2022
f2c7461
remove debug log.
baracudda Dec 5, 2022
ebb3515
P4-3928: org.release as transaction.
baracudda Dec 7, 2022
d9ab991
P4-3928: redirect superuser to main page from msg/inbox; fix user del…
baracudda Dec 7, 2022
9e4d07d
DockerHub api changed, use new endpoint instead.
baracudda Dec 7, 2022
4293ec0
P4-3928: redirect as exception required when not in a view method.
baracudda Dec 7, 2022
65560ee
P4-3928: middleware needs more methods.
baracudda Dec 7, 2022
d27cce7
P4-3030: see if haml is used for user list.
baracudda Dec 7, 2022
5b9c103
P4-3030: WIP
baracudda Dec 7, 2022
a7cd72c
P4-3030: WIP
baracudda Dec 7, 2022
141e31c
P4-3030: username, not name.
baracudda Dec 7, 2022
02c4413
P4-3030: click2sort headers, maybe.
baracudda Dec 8, 2022
41461d1
P4-3030: click2sort headers take 2.
baracudda Dec 8, 2022
dece1ac
P4-3030: css no like static load.
baracudda Dec 8, 2022
c0a6396
P4-3030: haml issue fix.
baracudda Dec 8, 2022
d250188
P4-3030: more haml fixing.
baracudda Dec 8, 2022
23b6c3c
P4-3030: revert haml title text.
baracudda Dec 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion engage/channels/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def create(
config = {}
#endif
# P4-3462
if URN.TEL_SCHEME in schemes:
if schemes and URN.TEL_SCHEME in schemes:
config[Channel.CONFIG_ALLOW_INTERNATIONAL] = True
#endif
return cls.getOrigClsAttr('create')(org, user, country, channel_type, name, address, config, role, schemes, **kwargs)
Expand Down
5 changes: 5 additions & 0 deletions engage/channels/types/vonage_client.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import logging

from django.urls import reverse

from engage.utils.class_overrides import ClassOverrideMixinMustBeFirst

from temba.channels.types.vonage.client import VonageClient


logger = logging.getLogger(__name__)

class VonageClientOverrides(ClassOverrideMixinMustBeFirst, VonageClient):

def create_application(self, domain, channel_uuid):
Expand Down
69 changes: 69 additions & 0 deletions engage/channels/types/vonage_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging
import phonenumbers
import re

from engage.utils.class_overrides import ClassOverrideMixinMustBeFirst

from temba.channels.models import Channel
from temba.channels.types.vonage.type import VonageType
from temba.channels.types.vonage.views import ClaimView


logger = logging.getLogger(__name__)

class ClaimViewOverrides(ClassOverrideMixinMustBeFirst, ClaimView):

def get_existing_numbers(self, org):
numbers = []
client = org.get_vonage_client()
if client:
try:
account_numbers = client.get_numbers(size=100)
#logger.debug(' TRACE[account_numbers]='+str(account_numbers))
uuid_pattern = r"(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/receive$"
account_uuids = []
for number in account_numbers:
if number["type"] == "mobile-shortcode": # pragma: needs cover
phone_number = number["msisdn"]
else:
parsed = phonenumbers.parse(number["msisdn"], number["country"])
phone_number = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
#endif shortcode

# mark accounts used/unused by checking the db for uuid
channel_uuid = '1'
if 'moHttpUrl' in number:
# 'moHttpUrl': 'https://engage.dev.istresearch.com/c/nx/742c11f1-72fb-4994-8156-8848e8a63e55/receive',
match = re.search(uuid_pattern, number["moHttpUrl"])
channel_uuid = match.group(1) if match else '1'
#endif key in list
account_uuids.append(channel_uuid)

numbers.append(dict(
number=phone_number,
country=number["country"],
uuid=channel_uuid,
in_use=False,
))
#endfor numbers

# query db for "in use" numbers
qs = Channel.objects.filter(
channel_type=VonageType.code,
uuid__in=account_uuids,
).values_list('uuid', flat=True)
for channel_uuid in qs:
idx = account_uuids.index(channel_uuid)
numbers[idx]['in_use'] = True
#endfor each channel found
#logger.debug(' TRACE[nums]='+str(numbers))
except Exception as e:
logger.error(f"fail: {str(e)}", exc_info=True)
raise e
#endtry

#endif client
return numbers
#enddef get_existing_numbers

#endclass ClaimViewOverrides
140 changes: 140 additions & 0 deletions engage/hamls/channels/channel_claim_number.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
-extends "smartmin/form.html"

-load compress temba
-load i18n

-block title-icon

-block title
-trans "Connect a Phone Number"

-block content
- block claim-numbers-description
-blocktrans trimmed
Once you connect a number you will immediately be able to send and receive messages. Contacts who send messages
to your number will be charged according to their plan.

- block account-trial-warning

- block numbers-search-form
.card.mt-6
%form#search-form
.flex.items-end
.country.w-64.mr-3
-block country-select
%temba-select#country(name="country" label='{{_("Country")|escapejs}}')
-for country in search_countries
%temba-option(name="{{country.label}}" value="{{country.key}}")

.pattern.w-32.mr-3
-block search-pattern
%temba-textinput#pattern(type="text" maxlength="3" name="pattern" label='{{_("Pattern")|escapejs}}')

%input.button-primary{ type:"submit", value:"{% trans 'Search' %}" }

.twilio-numbers-title

#throbber.my-6{ style: "display:none;" }
%temba-loading
#results.my-6



-if form.errors
-if form.errors.upgrade
:javascript
document.location.href = '{% url 'orgs.org_upgrade_plan' %}?from=twilio'
-else
.alert-error.my-4
{{ form.errors.phone_number }}

#claim-message.alert-warning{ style: "display:none;margin-top:10px;" }
-if error
{{ error }}

-if account_numbers
#account-numbers.card.mt-3.mb-3
.title
-trans "Existing Numbers"
.mb-3
-trans "Select a number you already own to connect it to your account."
-for number in account_numbers
- if number.country in supported_country_iso_codes or number.number|length <= 6
.lbl.mt-3.mr-2.linked{class:"phone-number", data-number:"{{ number.number}}", data-country:"{{ number.country}}" }
{{ number.number }}
(<span class="country">{{ number.country }}</span>)
{% if number.in_use %}
<span class="num_in_use"></span>
{% else %}
<span class="num_not_in_use"></span>
{% endif %}
- else
.lbl.mt-3.mr-2{class:"unsupported-number", data-number:"{{ number.number}}", data-country:"{{ number.country}}" }
{{ number.number }}
-trans "(Unsupported)"

%form#claim-form{ style: "display:none;", method:"POST", action:"{{ claim_url }}" }
{% csrf_token %}
%input#claim-country{ type:"text", name:"country" }
%input#phone-number{ type:"text", name:"phone_number" }

-block extra-script
{{ block.super }}

:javascript
$("#results").on('click', ".phone-number", function(e){
var country = document.querySelector("#country").values[0].value;

$("#phone-number").val($(this).data("number"));
$("#claim-country").val(country);
$("#claim-form").submit();
});

$("#account-numbers").on('click', ".phone-number", function(e){
$("#phone-number").val($(this).data("number"));
$("#claim-country").val($(this).data("country"));
$("#claim-form").submit();
});

function searchNumbers(e){
var pattern = document.querySelector('#pattern').value;
var country = document.querySelector("#country").values[0].value;

$("#claim-message").hide();
$("#results").empty();
$("#throbber").show();

$.ajax({
type: "POST",
url: "{{ search_url }}",
data: { pattern: pattern, country: country },
dataType: "json",
success: function(data, status, xhr){
$("#throbber").hide();
if (data.length > 0){
$("#claim-country").val(country);
for (var i=0; i < data.length; i++){
$("#results").append("<div class='lbl phone-number mt-3 mr-2 linked' data-number='" + data[i] + "'>" + data[i] + "</div>");
}
$("#results").show();
} else if ('error' in data) {
$("#claim-message").text(data['error']);
$("#claim-message").show();
} else {
$("#claim-message").text("{% trans 'Sorry, no numbers found, please enter another pattern and try again.' %}");
$("#claim-message").show();
}
},
failure: function(req){
$("#throbber").hide();
$("#claim-message").show();
}
});

e.preventDefault();
return false;
}

$(function(){
$("#search-form").on('submit', searchNumbers);
});
114 changes: 114 additions & 0 deletions engage/hamls/orgs/user_list.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
-extends "smartmin/list.html"
-load smartmin sms temba compress contacts i18n humanize

-block extra-style
{{block.super}}
:css
temba-button {
display: block;
}
tr:nth-child(even) {background: #FFF}
.button-light{
padding-bottom: 8px;
padding-top: 8px;
padding-left: 18px;
padding-right: 18px;
}

.list_channels_channel {
width:100%;
}
tr {
text-align: left;
}
table .headerSortUp, table .headerSortDown {
background-color: rgba(141, 192, 219, 0.25);
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
}

table .headerSortDown {
background-image: url({{STATIC_URL}}img/sort_dsc.png);
background-repeat: no-repeat;
background-position: 98% 50%;
}

table .headerSortUp {
background-image: url({{STATIC_URL}}img/sort_asc.png);
background-repeat: no-repeat;
background-position: 98% 50%;
}

-block page-top

-block content

.lp-frame
.right
.flex.w-full.items-end.mb-4
.flex-grow.ml-2.items-center
-block title-text
.page-title.leading-tight
{{title}}
.gear-links
-include "gear_links_include.haml"

%form#search-form.mb-4(method="get")
%temba-textinput.w-full(placeholder='{% trans "Search" %}' name="search" value="{{search}}")

-block user-list
%table.list.object-list.lined
%thead
-# extra: sortable header clickies!
%tr
- for field in fields
%th{ class:'{% if field not in view.non_sort_fields %}header{% if field == view.sort_field %} {% if not view.sort_order or view.sort_order == "asc" %}headerSortUp{% else %}headerSortDown{% endif %}{% endif %}{% endif %}', id:'header-{{field}}' }
- if field not in view.non_sort_fields
<a href="?sort_on={{field}}{% if field == view.sort_field %}&sort_order={% if view.sort_order == "desc" %}asc{% else %}desc{% endif %}{% endif %}{% if search is not null %}&search={{search}}{% endif %}">{% get_label field %}</a>
- else
{% get_label field %}
%tbody
-for object in object_list
%tr.object-row{id: 'id-row-{{object.id}}', data-object-id:'{{ object.id }}'}
%td.whitespace-nowrap
{% get_value object 'username' %}
%td.w-full
.flex.flex-wrap.flex-end.items-center.justify-end
.flex-grow.inline
{% get_value object 'orgs' %}
%td
.flex.w-full.items-end.justify-end.pr-4
.time.whitespace-nowrap
{% format_datetime object.date_joined %}

- block paginator
-if object_list.count
-block search-details
.flex.mx-4.mt-3.mb-16
.text-gray-700
-if not paginator or paginator.num_pages <= 1
-if search
-blocktrans trimmed with results_count=paginator.count|intcomma count cc=paginator.count
Found {{ results_count }} user matching <i>{{search}}</i>.
-plural
Found {{ results_count }} users matching <i>{{search}}</i>.
-else
-blocktrans trimmed with results_count=paginator.count|intcomma count cc=paginator.count
{{ results_count }} user.
-plural
{{ results_count }} users.

- else

-if search
-blocktrans trimmed with results_count=paginator.count|intcomma start=page_obj.start_index|intcomma end=page_obj.end_index|intcomma count cc=paginator.count
Found {{ results_count }} user matching <i>{{search}}</i>.
-plural
{{ start }} - {{ end }} of {{ results_count }} results for <i>{{search}}</i>.
-else
-blocktrans trimmed with results_count=paginator.count|intcomma start=page_obj.start_index|intcomma end=page_obj.end_index|intcomma count cc=paginator.count
{{ results_count }} user.
-plural
{{ start }} - {{ end }} of {{ results_count }} users.

.flex-grow
-include "includes/pages.html"
5 changes: 5 additions & 0 deletions engage/msgs/views/inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ def on_apply_overrides(under_cls) -> None:
#enddef on_apply_overrides

def get_context_data(self, **kwargs):
org = self.request.user.get_org()
if not org:
from engage.utils.middleware import redirect_to
redirect_to('/')
#endif superuser
context = self.orig_get_context_data(**kwargs)
context['object_list'] = self._sanitizeMsgList(context['object_list'])
return context
Expand Down
Loading