fixed null handling on client admin pages, added software ID and version to UI

pull/1161/merge
Justin Richer 2017-03-15 17:30:26 -04:00
parent 22a4addfc0
commit e57ea488b3
5 changed files with 126 additions and 66 deletions

View File

@ -34,26 +34,26 @@ var ClientModel = Backbone.Model.extend({
defaults:{ defaults:{
id:null, id:null,
clientId:"", clientId:null,
clientSecret:"", clientSecret:null,
redirectUris:[], redirectUris:[],
clientName:null, clientName:null,
clientUri:"", clientUri:null,
logoUri:"", logoUri:null,
contacts:[], contacts:[],
tosUri:"", tosUri:null,
tokenEndpointAuthMethod:null, tokenEndpointAuthMethod:null,
scope:[], scope:[],
grantTypes:[], grantTypes:[],
responseTypes:[], responseTypes:[],
policyUri:"", policyUri:null,
jwksUri:"", jwksUri:null,
jwks:null, jwks:null,
jwksType:"URI", jwksType:"URI",
applicationType:null, applicationType:null,
sectorIdentifierUri:"", sectorIdentifierUri:null,
subjectType:null, subjectType:null,
requestObjectSigningAlg:null, requestObjectSigningAlg:null,
@ -72,11 +72,15 @@ var ClientModel = Backbone.Model.extend({
requireAuthTime:false, requireAuthTime:false,
defaultACRvalues:null, defaultACRvalues:null,
initiateLoginUri:"", initiateLoginUri:null,
postLogoutRedirectUris:[], postLogoutRedirectUris:[],
requestUris:[], requestUris:[],
softwareStatement:null,
softwareId:null,
softwareVersion:null,
codeChallengeMethod:null, codeChallengeMethod:null,
authorities:[], authorities:[],
@ -87,7 +91,7 @@ var ClientModel = Backbone.Model.extend({
claimsRedirectUris:[], claimsRedirectUris:[],
clientDescription:"", clientDescription:null,
reuseRefreshToken:true, reuseRefreshToken:true,
clearAccessTokensOnRefresh:true, clearAccessTokensOnRefresh:true,
dynamicallyRegistered:false, dynamicallyRegistered:false,
@ -769,6 +773,15 @@ var ClientFormView = Backbone.View.extend({
} }
}, },
// returns "null" if the given value is falsy
emptyToNull:function(value) {
if (value) {
return value;
} else {
return null;
}
},
disableUnsupportedJOSEItems:function(serverSupported, query) { disableUnsupportedJOSEItems:function(serverSupported, query) {
var supported = ['default']; var supported = ['default'];
if (serverSupported) { if (serverSupported) {
@ -921,33 +934,35 @@ var ClientFormView = Backbone.View.extend({
var attrs = { var attrs = {
clientName:$('#clientName input').val(), clientName:this.emptyToNull($('#clientName input').val()),
clientId:$('#clientId input').val(), clientId:this.emptyToNull($('#clientId input').val()),
clientSecret: clientSecret, clientSecret: clientSecret,
generateClientSecret:generateClientSecret, generateClientSecret:generateClientSecret,
redirectUris: redirectUris, redirectUris: redirectUris,
clientDescription:$('#clientDescription textarea').val(), clientDescription:this.emptyToNull($('#clientDescription textarea').val()),
logoUri:$('#logoUri input').val(), logoUri:this.emptyToNull($('#logoUri input').val()),
grantTypes: grantTypes, grantTypes: grantTypes,
accessTokenValiditySeconds: accessTokenValiditySeconds, accessTokenValiditySeconds: accessTokenValiditySeconds,
refreshTokenValiditySeconds: refreshTokenValiditySeconds, refreshTokenValiditySeconds: refreshTokenValiditySeconds,
idTokenValiditySeconds: idTokenValiditySeconds, idTokenValiditySeconds: idTokenValiditySeconds,
deviceCodeValiditySeconds: deviceCodeValiditySeconds, deviceCodeValiditySeconds: deviceCodeValiditySeconds,
allowRefresh: $('#allowRefresh').is(':checked'), allowRefresh: $('#allowRefresh').is(':checked'),
allowIntrospection: $('#allowIntrospection input').is(':checked'), // <-- And here? --^ allowIntrospection: $('#allowIntrospection input').is(':checked'),
scope: scopes, scope: scopes,
tosUri: $('#tosUri input').val(), tosUri: this.emptyToNull($('#tosUri input').val()),
policyUri: $('#policyUri input').val(), policyUri: this.emptyToNull($('#policyUri input').val()),
clientUri: $('#clientUri input').val(), clientUri: this.emptyToNull($('#clientUri input').val()),
applicationType: $('#applicationType input').filter(':checked').val(), applicationType: $('#applicationType input').filter(':checked').val(),
jwksUri: jwksUri, jwksUri: jwksUri,
jwks: jwks, jwks: jwks,
subjectType: subjectType, subjectType: subjectType,
softwareStatement: $('#softwareStatement textarea').val(), softwareStatement: this.emptyToNull($('#softwareStatement textarea').val()),
softwareId: this.emptyToNull($('#softwareId input').val()),
softwareVersion: this.emptyToNull($('#softwareVersion input').val()),
tokenEndpointAuthMethod: tokenEndpointAuthMethod, tokenEndpointAuthMethod: tokenEndpointAuthMethod,
responseTypes: responseTypes, responseTypes: responseTypes,
sectorIdentifierUri: sectorIdentifierUri, sectorIdentifierUri: sectorIdentifierUri,
initiateLoginUri: $('#initiateLoginUri input').val(), initiateLoginUri: this.emptyToNull($('#initiateLoginUri input').val()),
postLogoutRedirectUris: this.postLogoutRedirectUrisCollection.pluck('item'), postLogoutRedirectUris: this.postLogoutRedirectUrisCollection.pluck('item'),
claimsRedirectUris: this.claimsRedirectUrisCollection.pluck('item'), claimsRedirectUris: this.claimsRedirectUrisCollection.pluck('item'),
reuseRefreshToken: $('#reuseRefreshToken').is(':checked'), reuseRefreshToken: $('#reuseRefreshToken').is(':checked'),

View File

@ -61,6 +61,10 @@ var DynRegClient = Backbone.Model.extend({
request_uris:[], request_uris:[],
software_statement:null,
software_id:null,
software_version:null,
code_challenge_method:null, code_challenge_method:null,
registration_access_token:null, registration_access_token:null,
@ -313,6 +317,15 @@ var DynRegEditView = Backbone.View.extend({
} }
}, },
// returns "null" if the given value is falsy
emptyToNull:function(value) {
if (value) {
return value;
} else {
return null;
}
},
// maps from a form-friendly name to the real grant parameter name // maps from a form-friendly name to the real grant parameter name
grantMap:{ grantMap:{
'authorization_code': 'authorization_code', 'authorization_code': 'authorization_code',
@ -405,24 +418,26 @@ var DynRegEditView = Backbone.View.extend({
} }
var attrs = { var attrs = {
client_name:$('#clientName input').val(), client_name:this.emptyToNull($('#clientName input').val()),
redirect_uris: redirectUris, redirect_uris: redirectUris,
logo_uri:$('#logoUri input').val(), logo_uri:this.emptyToNull($('#logoUri input').val()),
grant_types: grantTypes, grant_types: grantTypes,
scope: scopes, scope: scopes,
client_secret: null, // never send a client secret client_secret: null, // never send a client secret
tos_uri: $('#tosUri input').val(), tos_uri: this.emptyToNull($('#tosUri input').val()),
policy_uri: $('#policyUri input').val(), policy_uri: this.emptyToNull($('#policyUri input').val()),
client_uri: $('#clientUri input').val(), client_uri: this.emptyToNull($('#clientUri input').val()),
application_type: $('#applicationType input').filter(':checked').val(), application_type: $('#applicationType input').filter(':checked').val(),
jwks_uri: jwksUri, jwks_uri: jwksUri,
jwks: jwks, jwks: jwks,
subject_type: subjectType, subject_type: subjectType,
software_statement: $('#softwareStatement textarea').val(), software_statement: this.emptyToNull($('#softwareStatement textarea').val()),
softwareId: this.emptyToNull($('#softwareId input').val()),
softwareVersion: this.emptyToNull($('#softwareVersion input').val()),
token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(), token_endpoint_auth_method: $('#tokenEndpointAuthMethod input').filter(':checked').val(),
response_types: responseTypes, response_types: responseTypes,
sector_identifier_uri: sectorIdentifierUri, sector_identifier_uri: sectorIdentifierUri,
initiate_login_uri: $('#initiateLoginUri input').val(), initiate_login_uri: this.emptyToNull($('#initiateLoginUri input').val()),
post_logout_redirect_uris: this.postLogoutRedirectUrisCollection.pluck('item'), post_logout_redirect_uris: this.postLogoutRedirectUrisCollection.pluck('item'),
claims_redirect_uris: this.claimsRedirectUrisCollection.pluck('item'), claims_redirect_uris: this.claimsRedirectUrisCollection.pluck('item'),
require_auth_time: $('#requireAuthTime input').is(':checked'), require_auth_time: $('#requireAuthTime input').is(':checked'),

View File

@ -37,7 +37,7 @@
"client-description-placeholder": "Type a description", "client-description-placeholder": "Type a description",
"client-id": "Client ID", "client-id": "Client ID",
"client-id-help": "Unique identifier. If you leave this blank it will be automatically generated.", "client-id-help": "Unique identifier. If you leave this blank it will be automatically generated.",
"client-id-placeholder": "Type something", "client-id-placeholder": "Client ID will be automatically generated",
"client-name": "Client name", "client-name": "Client name",
"client-name-help": "Human-readable application name", "client-name-help": "Human-readable application name",
"client-name-placeholder": "Type something", "client-name-placeholder": "Type something",

View File

@ -215,7 +215,7 @@
<div class="control-group" id="clientName"> <div class="control-group" id="clientName">
<label class="control-label" data-i18n="client.client-form.client-name">Client name</label> <label class="control-label" data-i18n="client.client-form.client-name">Client name</label>
<div class="controls"> <div class="controls">
<input value="<%-client.clientName%>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.client-name-placeholder"> <input value="<%-client.clientName ? client.clientName : ''%>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.client-name-placeholder">
<p class="help-block" data-i18n="client.client-form.client-name-help">Human-readable application name</p> <p class="help-block" data-i18n="client.client-form.client-name-help">Human-readable application name</p>
</div> </div>
</div> </div>
@ -223,7 +223,7 @@
<div class="control-group" id="clientId"> <div class="control-group" id="clientId">
<label class="control-label" data-i18n="client.client-form.client-id">Client ID</label> <label class="control-label" data-i18n="client.client-form.client-id">Client ID</label>
<div class="controls"> <div class="controls">
<input value="<%-client.clientId%>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.client-id-placeholder"> <input value="<%-client.clientId ? client.clientId : ''%>" maxlength="100" type="text" class="" placeholder="Client ID will be generated automatically" data-i18n="[placeholder]client.client-form.client-id-placeholder">
<p class="help-block" data-i18n="client.client-form.client-id-help">Unique identifier. If you leave this blank it will be automatically generated.</p> <p class="help-block" data-i18n="client.client-form.client-id-help">Unique identifier. If you leave this blank it will be automatically generated.</p>
</div> </div>
</div> </div>
@ -246,7 +246,7 @@
<div class="control-group" id="logoUri"> <div class="control-group" id="logoUri">
<label class="control-label" data-i18n="client.client-form.logo">Logo</label> <label class="control-label" data-i18n="client.client-form.logo">Logo</label>
<div class="controls"> <div class="controls">
<input placeholder="https://" value="<%-client.logoUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.logoUri ? client.logoUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.logo-help">URL that points to a logo image, will be displayed on approval page</p> <p class="help-block" data-i18n="client.client-form.logo-help">URL that points to a logo image, will be displayed on approval page</p>
</div> </div>
</div> </div>
@ -260,7 +260,7 @@
<div class="control-group" id="tosUri"> <div class="control-group" id="tosUri">
<label class="control-label" data-i18n="client.client-form.terms">Terms of Service</label> <label class="control-label" data-i18n="client.client-form.terms">Terms of Service</label>
<div class="controls"> <div class="controls">
<input placeholder="https://" value="<%-client.tosUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.tosUri ? client.tosUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.terms-help">URL for the Terms of Service of this client, will be displayed to the user</p> <p class="help-block" data-i18n="client.client-form.terms-help">URL for the Terms of Service of this client, will be displayed to the user</p>
</div> </div>
</div> </div>
@ -268,7 +268,7 @@
<div class="control-group" id="policyUri"> <div class="control-group" id="policyUri">
<label class="control-label" data-i18n="client.client-form.policy">Policy Statement</label> <label class="control-label" data-i18n="client.client-form.policy">Policy Statement</label>
<div class="controls"> <div class="controls">
<input placeholder="https://" value="<%-client.policyUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.policyUri ? client.policyUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.policy-help">URL for the Policy Statement of this client, will be displayed to the user</p> <p class="help-block" data-i18n="client.client-form.policy-help">URL for the Policy Statement of this client, will be displayed to the user</p>
</div> </div>
</div> </div>
@ -276,21 +276,25 @@
<div class="control-group" id="clientUri"> <div class="control-group" id="clientUri">
<label class="control-label" data-i18n="client.client-form.home">Home Page</label> <label class="control-label" data-i18n="client.client-form.home">Home Page</label>
<div class="controls"> <div class="controls">
<input placeholder="https://" value="<%-client.clientUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.clientUri ? client.clientUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.home-help">URL for the client's home page, will be displayed to the user</p> <p class="help-block" data-i18n="client.client-form.home-help">URL for the client's home page, will be displayed to the user</p>
</div> </div>
</div> </div>
<div class="control-group" id="applicationType"> <div class="control-group" id="softwareId">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.type">Application Type</span></label> <label class="control-label" data-i18n="client.client-form.software-id">Software ID</label>
<div class="controls"> <div class="controls">
<div> <input value="<%-client.softwareId ? client.softwareId : ''%>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.software-id-placeholder">
<input id="app-type-native" type="radio" name="applicationType" value="NATIVE" <%-(client.applicationType == 'NATIVE' ? 'checked' : '')%>> <p class="help-block" data-i18n="client.client-form.software-id-help">Identifier for the software in this client</p>
<label for="app-type-native" class="radio inline" data-i18n="client.client-form.type-native">Native</label>
<input id="app-type-web" type="radio" name="applicationType" value="WEB" <%-(client.applicationType == 'WEB' ? 'checked' : '')%>>
<label for="app-type-web" class="radio inline" data-i18n="client.client-form.type-web">Web</label>
</div> </div>
</div> </div>
<div class="control-group" id="softwareVersion">
<label class="control-label" data-i18n="client.client-form.software-version">Software Version</label>
<div class="controls">
<input value="<%-client.softwareVersion ? client.softwareVersion : ''%>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.software-version-placeholder">
<p class="help-block" data-i18n="client.client-form.software-version-help">Version of the software in this client</p>
</div>
</div> </div>
<div class="control-group" id="contacts"> <div class="control-group" id="contacts">
@ -428,7 +432,7 @@
<div class="control-group" id="sectorIdentifierUri"> <div class="control-group" id="sectorIdentifierUri">
<label class="control-label" data-i18n="client.client-form.sector-identifier">Sector Identifier URI</label> <label class="control-label" data-i18n="client.client-form.sector-identifier">Sector Identifier URI</label>
<div class="controls"> <div class="controls">
<input placeholder="https://" value="<%-client.sectorIdentifierUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.sectorIdentifierUri ? client.sectorIdentifierUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.sector-identifier-help">Sector Identifier for JavaScript</p> <p class="help-block" data-i18n="client.client-form.sector-identifier-help">Sector Identifier for JavaScript</p>
</div> </div>
</div> </div>
@ -490,7 +494,7 @@
<div class="control-group"> <div class="control-group">
<div class="controls"> <div class="controls">
<div id="clientSecret" class="span3"> <div id="clientSecret" class="span3">
<input value="<%-client.clientSecret%>" maxlength="100" type="text" placeholder="Type a secret" data-i18n="[placeholder]client.client-form.client-secret-placeholder"> <input value="<%-client.clientSecret ? client.clientSecret : ''%>" maxlength="100" type="text" placeholder="Type a secret" data-i18n="[placeholder]client.client-form.client-secret-placeholder">
</div> </div>
<div id="clientSecretGenerated" class="span3"> <div id="clientSecretGenerated" class="span3">
<span class="uneditable-input" data-i18n="client.client-form.generate-on-save">Generate on Save</span> <span class="uneditable-input" data-i18n="client.client-form.generate-on-save">Generate on Save</span>
@ -535,11 +539,11 @@
</div> </div>
</div> </div>
<div class="controls" id="jwksUri"> <div class="controls" id="jwksUri">
<input placeholder="https://" value="<%-client.jwksUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.jwksUri ? client.jwksUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.jwk-set-help">URL for the client's JSON Web Key set (must be reachable by the server)</p> <p class="help-block" data-i18n="client.client-form.jwk-set-help">URL for the client's JSON Web Key set (must be reachable by the server)</p>
</div> </div>
<div class="controls" id="jwks"> <div class="controls" id="jwks">
<textarea class="input-xlarge" placeholder="{ &quot;keys&quot: [ ] }" maxlength="4000" type="text" rows="8"><%- (client.jwks != null ? JSON.stringify(client.jwks, null, ' ') : "") %></textarea> <textarea class="input-xlarge" placeholder="{ &quot;keys&quot: [ ] }" maxlength="4000" type="text" rows="8"><%- (client.jwks ? JSON.stringify(client.jwks, null, ' ') : "") %></textarea>
<p class="help-block" data-i18n="client.client-form.jwk-set-value-help">Key set value (must be a valid JWK Set formatted key)</p> <p class="help-block" data-i18n="client.client-form.jwk-set-value-help">Key set value (must be a valid JWK Set formatted key)</p>
</div> </div>
</div> </div>
@ -784,7 +788,7 @@
<div class="control-group" id="initiateLoginUri"> <div class="control-group" id="initiateLoginUri">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.initiate-login">Initiate Login</span></label> <label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.initiate-login">Initiate Login</span></label>
<div class="controls"> <div class="controls">
<input placeholder="https://" value="<%-client.initiateLoginUri%>" maxlength="1000" type="text" class=""/> <input placeholder="https://" value="<%-client.initiateLoginUri ? client.initiateLoginUri : ''%>" maxlength="1000" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.initiate-login-help">URL to initiate login on the client</p> <p class="help-block" data-i18n="client.client-form.initiate-login-help">URL to initiate login on the client</p>
</div> </div>
</div> </div>
@ -815,7 +819,7 @@
<div class="control-group" id="defaultMaxAge"> <div class="control-group" id="defaultMaxAge">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.max-age">Default Max Age</span></label> <label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.max-age">Default Max Age</span></label>
<div class="controls"> <div class="controls">
<input placeholder="" value="<%-client.defaultMaxAge%>" maxlength="10" type="text" class=""/> <input placeholder="" value="<%-client.defaultMaxAge ? client.defaultMaxAge : ''%>" maxlength="10" type="text" class=""/>
<p class="help-block" data-i18n="client.client-form.max-age-help">Default maximum session age before re-prompting</p> <p class="help-block" data-i18n="client.client-form.max-age-help">Default maximum session age before re-prompting</p>
</div> </div>
</div> </div>
@ -832,6 +836,18 @@
</div> </div>
</div> </div>
<div class="control-group" id="applicationType">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.type">Application Type</span></label>
<div class="controls">
<div>
<input id="app-type-native" type="radio" name="applicationType" value="NATIVE" <%-(client.applicationType == 'NATIVE' ? 'checked' : '')%>>
<label for="app-type-native" class="radio inline" data-i18n="client.client-form.type-native">Native</label>
<input id="app-type-web" type="radio" name="applicationType" value="WEB" <%-(client.applicationType == 'WEB' ? 'checked' : '')%>>
<label for="app-type-web" class="radio inline" data-i18n="client.client-form.type-web">Web</label>
</div>
</div>
</div>
</div> </div>

View File

@ -191,15 +191,19 @@
</div> </div>
</div> </div>
<div class="control-group" id="applicationType"> <div class="control-group" id="softwareId">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.type">Application Type</span></label> <label class="control-label" data-i18n="client.client-form.software-id">Software ID</label>
<div class="controls"> <div class="controls">
<label class="radio inline"> <input value="<%-client.software_id ? client.software_id : '' %>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.software-id-placeholder">
<input type="radio" name="applicationType" value="NATIVE" <%-(client.application_type == 'NATIVE' ? 'checked' : '')%>> <span data-i18n="client.client-form.type-native">Native</span> <p class="help-block" data-i18n="client.client-form.software-id-help">Identifier for the software in this client</p>
</label> </div>
<label class="radio inline"> </div>
<input type="radio" name="applicationType" value="WEB" <%-(client.application_type == 'WEB' ? 'checked' : '')%>> <span data-i18n="client.client-form.type-web">Web</span>
</label> <div class="control-group" id="softwareVersion">
<label class="control-label" data-i18n="client.client-form.software-version">Software Version</label>
<div class="controls">
<input value="<%-client.software_version ? client.sofware_version : ''%>" maxlength="100" type="text" class="" placeholder="Type something" data-i18n="[placeholder]client.client-form.software-version-placeholder">
<p class="help-block" data-i18n="client.client-form.software-version-help">Version of the software in this client</p>
</div> </div>
</div> </div>
@ -578,7 +582,17 @@
</div> </div>
</div> </div>
<div class="control-group" id="applicationType">
<label class="control-label"><span class="label label-default nyi"><i class="icon-road icon-white"></i> NYI </span> <span data-i18n="client.client-form.type">Application Type</span></label>
<div class="controls">
<label class="radio inline">
<input type="radio" name="applicationType" value="NATIVE" <%-(client.application_type == 'NATIVE' ? 'checked' : '')%>> <span data-i18n="client.client-form.type-native">Native</span>
</label>
<label class="radio inline">
<input type="radio" name="applicationType" value="WEB" <%-(client.application_type == 'WEB' ? 'checked' : '')%>> <span data-i18n="client.client-form.type-web">Web</span>
</label>
</div>
</div>
</div> </div>