Commit cbfea651 by Diana Huang Committed by Andy Armstrong

Add new info message to profile page.

parent 39cf0b44
...@@ -13,7 +13,13 @@ ...@@ -13,7 +13,13 @@
// Alerts // Alerts
.alert { .alert {
display: flex;
.icon-alert { .icon-alert {
margin-right: $baseline / 4; margin-right: $baseline;
}
.message-actions {
margin-left: $baseline;
} }
} }
...@@ -3,98 +3,114 @@ $full-width-banner-img-width: 1140px !default; ...@@ -3,98 +3,114 @@ $full-width-banner-img-width: 1140px !default;
$full-width-banner-margin: 20px; $full-width-banner-margin: 20px;
.full-width-banner { .full-width-banner {
position: relative;
.banner-background-wrapper {
height: $full-width-banner-img-height;
width: 100%;
overflow: hidden;
position: relative; position: relative;
background: $black;
&::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: $black;
opacity: 0.65;
@media (min-width: $bp-screen-md) {
opacity: 0.4;
}
}
}
.banner-background-image {
height: $full-width-banner-img-height;
.banner-background-wrapper { @media (min-width: $full-width-banner-img-width) {
height: $full-width-banner-img-height; height: auto;
width: 100%; width: 100%;
overflow: hidden;
position: relative;
background: $black;
&:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: $black;
opacity: 0.65;
@media(min-width: $bp-screen-md) {
opacity: 0.4;
}
}
} }
}
.banner-content {
position: absolute;
top: 0;
left: $full-width-banner-margin;
right: $full-width-banner-margin;
}
}
.banner-background-image { .page-banner {
height: $full-width-banner-img-height; max-width: $lms-max-width;
margin: 0 auto;
@media(min-width: $full-width-banner-img-width) { .user-messages {
height: auto; padding-top: $baseline;
width: 100%;
} // Hack: force override the global important rule
// that courseware links don't have an underline.
a:hover {
color: $link-color;
text-decoration: underline !important;
} }
}
.alert {
display: flex;
padding: $baseline;
border: 1px solid;
.banner-content { &:not(:last-child) {
position: absolute; margin-bottom: $baseline / 2;
top: 0;
left: $full-width-banner-margin;
right: $full-width-banner-margin;
} }
}
.page-banner { .icon-alert {
max-width: $lms-max-width; @include margin-right($baseline);
margin: 0 auto; }
.user-messages { .message-actions {
padding-top: $baseline; @include margin-left($baseline);
}
// Hack: force override the global important rule
// that courseware links don't have an underline. &.alert-info {
a:hover { color: $state-info-text;
color: $link-color; background-color: $state-info-bg;
text-decoration: underline !important; border-color: $state-info-border;
} box-shadow: none;
line-height: initial;
a,
a:visited,
.btn-link {
color: $state-info-text-link;
font-weight: bold;
}
}
&.alert-success {
color: $state-success-text;
background-color: $state-success-bg;
border-color: $state-success-border;
box-shadow: none;
}
&.alert-warning {
color: $state-warning-text;
background-color: $state-warning-bg;
border-color: $state-warning-border;
box-shadow: none;
} }
.alert { &.alert-danger {
margin-bottom: $baseline !important; color: $state-danger-text;
padding: $baseline; background-color: $state-danger-bg;
border: 1px solid; border-color: $state-danger-border;
box-shadow: none;
.icon-alert {
margin-right: $baseline / 4;
}
&.alert-info {
color: $state-info-text;
background-color: $state-info-bg;
border-color: $state-info-border;
box-shadow: none;
}
&.alert-success {
color: $state-success-text;
background-color: $state-success-bg;
border-color: $state-success-border;
box-shadow: none;
}
&.alert-warning {
color: $state-warning-text;
background-color: $state-warning-bg;
border-color: $state-warning-border;
box-shadow: none;
}
&.alert-danger {
color: $state-danger-text;
background-color: $state-danger-bg;
border-color: $state-danger-border;
box-shadow: none;
}
} }
}
} }
...@@ -112,6 +112,16 @@ ...@@ -112,6 +112,16 @@
background-color: $white; background-color: $white;
} }
.page-banner {
background-color: $gray-l4;
max-width: none;
.user-messages {
max-width: $lms-max-width;
margin: auto;
}
}
.ui-loading-indicator { .ui-loading-indicator {
@extend .ui-loading-base; @extend .ui-loading-base;
...@@ -253,7 +263,7 @@ ...@@ -253,7 +263,7 @@
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
border: 1px solid $gray-l3; border-bottom: 1px solid $gray-l3;
background-color: $gray-l4; background-color: $gray-l4;
padding: ($baseline*0.75) 0; padding: ($baseline*0.75) 0;
...@@ -262,7 +272,7 @@ ...@@ -262,7 +272,7 @@
border: none; border: none;
box-shadow: none; box-shadow: none;
padding: 0 ($baseline*3); padding: 0;
} }
.u-field-title { .u-field-title {
...@@ -297,18 +307,21 @@ ...@@ -297,18 +307,21 @@
.wrapper-profile-sections { .wrapper-profile-sections {
@extend .container; @extend .container;
@include padding($baseline*1.5, $baseline*1.5, $baseline*1.5, 0); @include padding($baseline*1.5, 0, $baseline*1.5, 0);
display: flex;
flex-wrap: wrap;
min-width: 0; min-width: 0;
@media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap
@include margin-left(0); @include margin-left(0);
@include padding($baseline*1.5, 0, $baseline*1.5, 0);
} }
} }
.profile-header { .profile-header {
@include padding(0, $baseline*2, $baseline, $baseline*3); max-width: $lms-max-width;
margin: auto;
padding: $baseline 0 0;
@media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap
@include padding(0, $baseline*2, $baseline, $baseline*0.75); @include padding(0, $baseline*2, $baseline, $baseline*0.75);
...@@ -333,9 +346,6 @@ ...@@ -333,9 +346,6 @@
} }
.wrapper-profile-section-one { .wrapper-profile-section-one {
@include float(left);
@include margin-left($baseline*3);
width: 300px; width: 300px;
background-color: $white; background-color: $white;
border-top: 5px solid $blue; border-top: 5px solid $blue;
...@@ -459,9 +469,8 @@ ...@@ -459,9 +469,8 @@
@include float(left); @include float(left);
@include padding-left($baseline); @include padding-left($baseline);
width: calc(100% - 380px);
max-width: $learner-profile-container-flex; // Switch to map-get($grid-breakpoints,md) for bootstrap
font-family: $sans-serif; font-family: $sans-serif;
flex-grow: 1;
@media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap
width: 90%; width: 90%;
......
...@@ -227,7 +227,8 @@ $state-success-text: $black !default; ...@@ -227,7 +227,8 @@ $state-success-text: $black !default;
$state-success-bg: #dff0d8 !default; $state-success-bg: #dff0d8 !default;
$state-success-border: darken($state-success-bg, 5%) !default; $state-success-border: darken($state-success-bg, 5%) !default;
$state-info-text: $black !default; $state-info-text: #31708f !default;
$state-info-text-link: #245269 !default;
$state-info-bg: #d9edf7 !default; $state-info-bg: #d9edf7 !default;
$state-info-border: darken($state-info-bg, 7%) !default; $state-info-border: darken($state-info-bg, 7%) !default;
......
// TODO: tabs should be added to the Pattern Library // TODO: tabs should be added to the Pattern Library
.tabs { .tabs {
@include clearfix(); @include clearfix();
@extend %reset-lists;
@include border-top-radius(4px);
padding: ($baseline*0.75) 0 ($baseline*0.75) 0;
.tab {
@include float(left);
list-style: none;
margin-bottom: 0;
&.prominent { @extend %reset-lists;
@include margin-right(16px);
background: rgba(255, 255, 255, 0.5);
border-radius: 3px;
}
&.prominent + li { @include border-top-radius(4px);
@include border-left(1px solid $lms-border-color);
@include padding-left($baseline*0.75);
}
a, padding: ($baseline*0.75) 0 ($baseline*0.75) 0;
a:visited {
@include padding($baseline/2, $baseline*0.75, 13px, $baseline*0.75); .tab {
display: block; @include float(left);
text-align: center;
text-decoration: none; list-style: none;
border-style: solid; margin-bottom: 0;
border-width: 0 0 4px 0;
border-bottom-color: transparent; &.prominent {
color: $lms-inactive-color; @include margin-right(16px);
font-size: 14px;
background: rgba(255, 255, 255, 0.5);
&:hover, border-radius: 3px;
&:focus { }
color: $lms-active-color;
border-bottom-color: $lms-active-color; &.prominent + li {
} @include border-left(1px solid $lms-border-color);
@include padding-left($baseline*0.75);
&.active { }
color: $uxpl-blue-hover-active;
border-bottom-color: $uxpl-blue-hover-active; a,
background-color: transparent; a:visited {
} @include padding($baseline/2, $baseline*0.75, 13px, $baseline*0.75);
}
display: block;
text-align: center;
text-decoration: none;
border-style: solid;
border-width: 0 0 4px;
border-bottom-color: transparent;
color: $lms-inactive-color;
font-size: 14px;
&:hover,
&:focus {
color: $lms-active-color;
border-bottom-color: $lms-active-color;
}
&.active {
color: $uxpl-blue-hover-active;
border-bottom-color: $uxpl-blue-hover-active;
background-color: transparent;
}
} }
}
} }
// TODO: search box should be in the Pattern Library // TODO: search box should be in the Pattern Library
.page-header-search { .page-header-search {
display: inline-block;
.search-form {
display: inline-block; display: inline-block;
}
.search-form { .search-box {
display: inline-block; display: inline-block;
} position: relative;
vertical-align: middle;
}
.search-box { .search-input {
display: inline-block; width: 12rem;
position: relative; }
vertical-align: middle;
}
.search-input { .action-search {
width: 12rem; text-shadow: none;
} vertical-align: middle;
padding: $baseline/5 $baseline/2;
}
.action-search { .action-clear {
text-shadow: none; @include right(0);
vertical-align: middle; @include margin(0, ($baseline/4), 0, 0);
padding: $baseline/5 $baseline/2;
}
.action-clear { position: absolute;
@include right(0); top: 0;
@include margin(0, ($baseline/4), 0, 0); color: $lms-gray;
position: absolute; padding: $baseline/4;
top: 0;
color: $lms-gray; // STATE: hover and focus
padding: $baseline/4; &:hover,
&:focus {
// STATE: hover and focus color: $lms-label-color;
&:hover, border-width: 0;
&:focus {
color: $lms-label-color;
border-width: 0;
}
} }
}
} }
.page-banner { .page-banner {
...@@ -94,11 +101,16 @@ ...@@ -94,11 +101,16 @@
margin: 0 auto; margin: 0 auto;
.alert { .alert {
display: flex;
margin-top: $baseline; margin-top: $baseline;
border: 1px solid; border: 1px solid;
.icon-alert { .icon-alert {
margin-right: $baseline / 4; @include margin-right($baseline);
}
.message-actions {
@include margin-left($baseline);
} }
&.alert-info { &.alert-info {
...@@ -134,7 +146,8 @@ ...@@ -134,7 +146,8 @@
.wrapper-preview-menu { .wrapper-preview-menu {
@include clearfix(); @include clearfix();
@include box-sizing(border-box); @include box-sizing(border-box);
margin: 0 auto 0;
margin: 0 auto;
padding: ($baseline*0.75); padding: ($baseline*0.75);
background-color: $lms-preview-menu-color; background-color: $lms-preview-menu-color;
...@@ -150,6 +163,7 @@ ...@@ -150,6 +163,7 @@
.preview-actions { .preview-actions {
@include margin-left(0); @include margin-left(0);
display: inline-block; display: inline-block;
margin-bottom: 0; margin-bottom: 0;
...@@ -158,6 +172,7 @@ ...@@ -158,6 +172,7 @@
.action-preview-label { .action-preview-label {
@include margin-right($baseline/2); @include margin-right($baseline/2);
display: inline-block; display: inline-block;
margin-bottom: 0; margin-bottom: 0;
vertical-align: middle; vertical-align: middle;
...@@ -189,12 +204,12 @@ ...@@ -189,12 +204,12 @@
} }
.section { .section {
.icon { .icon {
width: 20px; width: 20px;
text-align: center; text-align: center;
} }
} }
.section:not(:first-child) { .section:not(:first-child) {
margin-top: $baseline; margin-top: $baseline;
} }
...@@ -30,9 +30,9 @@ class UserMessagesTestCase(TestCase): ...@@ -30,9 +30,9 @@ class UserMessagesTestCase(TestCase):
MessageMiddleware().process_request(self.request) MessageMiddleware().process_request(self.request)
@ddt.data( @ddt.data(
('Rock & Roll', 'Rock &amp; Roll'), ('Rock & Roll', '<div class="message-content">Rock &amp; Roll</div>'),
(Text('Rock & Roll'), 'Rock &amp; Roll'), (Text('Rock & Roll'), '<div class="message-content">Rock &amp; Roll</div>'),
(HTML('<p>Hello, world!</p>'), '<p>Hello, world!</p>') (HTML('<p>Hello, world!</p>'), '<div class="message-content"><p>Hello, world!</p></div>')
) )
@ddt.unpack @ddt.unpack
def test_message_escaping(self, message, expected_message_html): def test_message_escaping(self, message, expected_message_html):
......
...@@ -87,7 +87,7 @@ class UserMessageCollection(): ...@@ -87,7 +87,7 @@ class UserMessageCollection():
raise NotImplementedError('Subclasses must define a namespace for messages.') raise NotImplementedError('Subclasses must define a namespace for messages.')
@classmethod @classmethod
def get_message_html(self, body_html, title=None): def get_message_html(self, body_html, title=None, dismissable=False):
""" """
Returns the entire HTML snippet for the message. Returns the entire HTML snippet for the message.
...@@ -96,16 +96,35 @@ class UserMessageCollection(): ...@@ -96,16 +96,35 @@ class UserMessageCollection():
not use a title can just pass the body_html. not use a title can just pass the body_html.
""" """
if title: if title:
return Text(_('{header_open}{title}{header_close}{body}')).format( title_area = Text(_('{header_open}{title}{header_close}')).format(
header_open=HTML('<div class="message-header">'), header_open=HTML('<div class="message-header">'),
title=title, title=title,
body=body_html,
header_close=HTML('</div>') header_close=HTML('</div>')
) )
return body_html else:
title_area = ''
if dismissable:
dismiss_button = HTML(
'<div class="message-actions">'
'<button class="btn-link action-dismiss">'
'<span class="sr">{dismiss_text}</span>'
'<span class="icon fa fa-times" aria-hidden="true"></span></button>'
'</div>'
).format(
dismiss_text=Text(_("Dismiss"))
)
else:
dismiss_button = ''
return Text('{title_area}{body_area}{dismiss_button}').format(
title_area=title_area,
body_area=HTML('<div class="message-content">{body_html}</div>').format(
body_html=body_html,
),
dismiss_button=dismiss_button,
)
@classmethod @classmethod
def register_user_message(self, request, message_type, body_html, title=None): def register_user_message(self, request, message_type, body_html, **kwargs):
""" """
Register a message to be shown to the user in the next page. Register a message to be shown to the user in the next page.
...@@ -113,9 +132,10 @@ class UserMessageCollection(): ...@@ -113,9 +132,10 @@ class UserMessageCollection():
message_type (UserMessageType): the user message type message_type (UserMessageType): the user message type
body_html (str): body of the message in html format body_html (str): body of the message in html format
title (str): optional title for the message as plain text title (str): optional title for the message as plain text
dismissable (bool): shows a dismiss button (defaults to no button)
""" """
assert isinstance(message_type, UserMessageType) assert isinstance(message_type, UserMessageType)
message = Text(self.get_message_html(body_html, title)) message = Text(self.get_message_html(body_html, **kwargs))
messages.add_message(request, message_type.value, Text(message), extra_tags=self.get_namespace()) messages.add_message(request, message_type.value, Text(message), extra_tags=self.get_namespace())
@classmethod @classmethod
......
...@@ -11,3 +11,7 @@ WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='learner_profile') ...@@ -11,3 +11,7 @@ WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='learner_profile')
# Waffle flag to show achievements on the learner profile. # Waffle flag to show achievements on the learner profile.
# TODO: LEARNER-2443: 08/2017: Remove flag after rollout. # TODO: LEARNER-2443: 08/2017: Remove flag after rollout.
SHOW_ACHIEVEMENTS_FLAG = WaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_achievements') SHOW_ACHIEVEMENTS_FLAG = WaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_achievements')
# Waffle flag for showing a message about the new profile features.
# TODO: LEARNER-2554: 09/2017: Remove flag once message is no longer needed.
SHOW_PROFILE_MESSAGE = WaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_message')
...@@ -25,6 +25,11 @@ ...@@ -25,6 +25,11 @@
return function(options) { return function(options) {
var $learnerProfileElement = $('.wrapper-profile'); var $learnerProfileElement = $('.wrapper-profile');
// TODO: LEARNER-2554: 09/2017: Remove this hiding logic when the message is removed.
$('.action-dismiss').click(function() {
$('.user-messages').hide();
});
var accountSettingsModel = new AccountSettingsModel( var accountSettingsModel = new AccountSettingsModel(
_.extend( _.extend(
options.account_settings_data, options.account_settings_data,
......
...@@ -57,6 +57,13 @@ ...@@ -57,6 +57,13 @@
$('.wrapper-profile-section-container-one').removeClass('is-hidden'); $('.wrapper-profile-section-container-one').removeClass('is-hidden');
$('.wrapper-profile-section-container-two').removeClass('is-hidden'); $('.wrapper-profile-section-container-two').removeClass('is-hidden');
// Only show accomplishments if this is a full profile
if (this.showFullProfile()) {
$('.learner-achievements').removeClass('is-hidden');
} else {
$('.learner-achievements').addClass('is-hidden');
}
if (this.showFullProfile() && (this.options.accountSettingsModel.get('accomplishments_shared'))) { if (this.showFullProfile() && (this.options.accountSettingsModel.get('accomplishments_shared'))) {
tabs = [ tabs = [
{view: this.sectionTwoView, title: gettext('About Me'), url: 'about_me'}, {view: this.sectionTwoView, title: gettext('About Me'), url: 'about_me'},
......
...@@ -26,17 +26,17 @@ from openedx.core.djangolib.markup import HTML ...@@ -26,17 +26,17 @@ from openedx.core.djangolib.markup import HTML
<div class="wrapper-profile"> <div class="wrapper-profile">
<div class="profile ${'profile-self' if own_profile else 'profile-other'}"> <div class="profile ${'profile-self' if own_profile else 'profile-other'}">
<div class="wrapper-profile-field-account-privacy"></div> <div class="wrapper-profile-field-account-privacy"></div>
<div class="wrapper-profile-sections account-settings-container"> % if own_profile:
% if own_profile: <div class="profile-header">
<div class="profile-header"> <div class="header">${_("My Profile")}</div>
<div class="header">${_("My Profile")}</div> <div class="subheader">
<div class="subheader"> ${_('Build out your profile to personalize your identity on {platform_name}.').format(
${_('Build out your profile to personalize your identity on {platform_name}.').format( platform_name=platform_name,
platform_name=platform_name, )}
)}
</div>
</div> </div>
% endif </div>
% endif
<div class="wrapper-profile-sections account-settings-container">
<div class="ui-loading-indicator"> <div class="ui-loading-indicator">
<p><span class="spin"><span class="icon fa fa-refresh" aria-hidden="true"></span></span> <span class="copy">${_("Loading")}</span></p> <p><span class="spin"><span class="icon fa fa-refresh" aria-hidden="true"></span></span> <span class="copy">${_("Loading")}</span></p>
</div> </div>
......
...@@ -8,6 +8,7 @@ from django.core.exceptions import ObjectDoesNotExist ...@@ -8,6 +8,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import Http404 from django.http import Http404
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils.translation import ugettext as _
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django_countries import countries from django_countries import countries
from edxmako.shortcuts import marketing_link from edxmako.shortcuts import marketing_link
...@@ -15,9 +16,11 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_ ...@@ -15,9 +16,11 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
from openedx.core.djangoapps.user_api.errors import UserNotAuthorized, UserNotFound from openedx.core.djangoapps.user_api.errors import UserNotAuthorized, UserNotFound
from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences
from openedx.core.djangoapps.util.user_messages import PageLevelMessages
from openedx.core.djangolib.markup import HTML, Text
from student.models import User from student.models import User
from .. import SHOW_ACHIEVEMENTS_FLAG from .. import SHOW_ACHIEVEMENTS_FLAG, SHOW_PROFILE_MESSAGE
from learner_achievements import LearnerAchievementsFragmentView from learner_achievements import LearnerAchievementsFragmentView
...@@ -42,10 +45,33 @@ def learner_profile(request, username): ...@@ -42,10 +45,33 @@ def learner_profile(request, username):
GET /account/profile GET /account/profile
""" """
try: try:
return render_to_response( context = learner_profile_context(request, username, request.user.is_staff)
# TODO: LEARNER-2554: 09/2017: Remove message and cookie logic when we no longer want this message
message_viewed = False
if (context['own_profile'] and
SHOW_PROFILE_MESSAGE.is_enabled() and
request.COOKIES.get('profile-message-viewed', '') != 'True'):
message_text = Text(_(
'Welcome to the new learner profile page. Your full profile now displays more '
'information to other learners. You can instead choose to display a limited '
'profile. {learn_more_link_start}Learn more{learn_more_link_end}'
)).format(
learn_more_link_start=HTML(
'<a href="http://edx.readthedocs.io/projects/open-edx-learner-guide/en/'
'latest/SFD_dashboard_profile_SectionHead.html#adding-profile-information">'
),
learn_more_link_end=HTML('</a>')
)
PageLevelMessages.register_info_message(request, message_text, dismissable=True)
message_viewed = True
response = render_to_response(
'learner_profile/learner_profile.html', 'learner_profile/learner_profile.html',
learner_profile_context(request, username, request.user.is_staff) context
) )
if message_viewed:
response.set_cookie('profile-message-viewed', 'True')
return response
except (UserNotAuthorized, UserNotFound, ObjectDoesNotExist): except (UserNotAuthorized, UserNotFound, ObjectDoesNotExist):
raise Http404 raise Http404
......
...@@ -13,7 +13,7 @@ set -e ...@@ -13,7 +13,7 @@ set -e
# Violations thresholds for failing the build # Violations thresholds for failing the build
export PYLINT_THRESHOLD=3600 export PYLINT_THRESHOLD=3600
export ESLINT_THRESHOLD=9134 export ESLINT_THRESHOLD=9134
export STYLELINT_THRESHOLD=15579 export STYLELINT_THRESHOLD=15852
XSSLINT_THRESHOLDS=`cat scripts/xsslint_thresholds.json` XSSLINT_THRESHOLDS=`cat scripts/xsslint_thresholds.json`
export XSSLINT_THRESHOLDS=${XSSLINT_THRESHOLDS//[[:space:]]/} export XSSLINT_THRESHOLDS=${XSSLINT_THRESHOLDS//[[:space:]]/}
......
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 to comment