Merge "Ability to add and remove subscriptions"
This commit is contained in:
commit
de2fb80dd0
@ -26,12 +26,19 @@
|
||||
'horizon.framework.widgets.toast.service'
|
||||
];
|
||||
|
||||
function ZaqarAPI(apiService, toastService) {
|
||||
function ZaqarAPI(apiService, toast) {
|
||||
|
||||
var queuePath = '/api/zaqar/queues/';
|
||||
var subPath = '/api/zaqar/queues/%s/subscriptions/';
|
||||
|
||||
var service = {
|
||||
getQueues: getQueues,
|
||||
createQueue: createQueue,
|
||||
deleteQueue: deleteQueue,
|
||||
updateQueue: updateQueue
|
||||
updateQueue: updateQueue,
|
||||
getSubscriptions: getSubscriptions,
|
||||
addSubscription: addSubscription,
|
||||
deleteSubscription: deleteSubscription
|
||||
};
|
||||
|
||||
return service;
|
||||
@ -39,28 +46,47 @@
|
||||
//////////
|
||||
|
||||
function getQueues() {
|
||||
return apiService.get('/api/zaqar/queues/')
|
||||
.error(function() {
|
||||
toastService.add('error', gettext('Unable to retrieve the Queues.'));
|
||||
});
|
||||
var msg = gettext('Unable to retrieve the Queues.');
|
||||
return apiService.get(queuePath).error(error(msg));
|
||||
}
|
||||
|
||||
function createQueue(newQueue) {
|
||||
return apiService.put('/api/zaqar/queues/', newQueue)
|
||||
.error(function() {
|
||||
toastService.add('error', gettext('Unable to create the queue.'));
|
||||
});
|
||||
var msg = gettext('Unable to create the queue.');
|
||||
return apiService.put(queuePath, newQueue).error(error(msg));
|
||||
}
|
||||
|
||||
function deleteQueue(queueName) {
|
||||
return apiService.delete('/api/zaqar/queues/', [queueName]);
|
||||
return apiService.delete(queuePath, [queueName]);
|
||||
}
|
||||
|
||||
function updateQueue(queue) {
|
||||
return apiService.post('/api/zaqar/queue/' + queue.queue_name, {"metadata": queue.metadata})
|
||||
.error(function() {
|
||||
toastService.add('error', gettext('Unable to update the queue.'));
|
||||
});
|
||||
var msg = gettext('Unable to update the queue.');
|
||||
var url = '/api/zaqar/queue/' + queue.queue_name;
|
||||
var form = { metadata: queue.metadata };
|
||||
return apiService.post(url, form).error(error(msg));
|
||||
}
|
||||
|
||||
function getSubscriptions(queue) {
|
||||
var url = interpolate(subPath, [queue.name]);
|
||||
return apiService.get(url);
|
||||
}
|
||||
|
||||
function addSubscription(sub) {
|
||||
var msg = gettext('Unable to add subscription.');
|
||||
var url = interpolate(subPath, [sub.queueName]);
|
||||
return apiService.put(url, sub).error(error(msg));
|
||||
}
|
||||
|
||||
function deleteSubscription(queue, subscription) {
|
||||
var msg = gettext('Unable to delete subscription.');
|
||||
var url = interpolate(subPath, [queue.name]);
|
||||
return apiService.delete(url, subscription).error(error(msg));
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
return function() {
|
||||
toast.add('error', message);
|
||||
};
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
@ -32,6 +32,7 @@
|
||||
'horizon.dashboard.project.queues.actions.createService',
|
||||
'horizon.dashboard.project.queues.actions.deleteService',
|
||||
'horizon.dashboard.project.queues.actions.updateService',
|
||||
'horizon.dashboard.project.queues.actions.createSubscriptionService',
|
||||
'horizon.dashboard.project.queues.resourceType'
|
||||
];
|
||||
|
||||
@ -40,6 +41,7 @@
|
||||
createService,
|
||||
deleteService,
|
||||
updateService,
|
||||
createSubscriptionService,
|
||||
resourceType
|
||||
) {
|
||||
|
||||
@ -52,6 +54,13 @@
|
||||
text: gettext('Update')
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'subscriptionsCreate',
|
||||
service: createSubscriptionService,
|
||||
template: {
|
||||
text: gettext('Create Subscription')
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'queuesItemDelete',
|
||||
service: deleteService,
|
||||
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.project.queues')
|
||||
.factory('horizon.dashboard.project.queues.actions.createSubscriptionService',
|
||||
createSubscriptionService);
|
||||
|
||||
createSubscriptionService.$inject = [
|
||||
'$q',
|
||||
'horizon.app.core.metadata.service',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.dashboard.project.queues.events',
|
||||
'horizon.dashboard.project.queues.actions.createSubscriptionWorkflow',
|
||||
'horizon.app.core.openstack-service-api.zaqar',
|
||||
'horizon.framework.widgets.modal.wizard-modal.service',
|
||||
'horizon.framework.widgets.toast.service'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngDoc factory
|
||||
* @name horizon.dashboard.project.queues.actions.createSubscriptionService
|
||||
* @Description A service to open the subscriptions wizard.
|
||||
*/
|
||||
function createSubscriptionService(
|
||||
$q, meta, policy, events, createWorkflow, zaqar, wizard, toast) {
|
||||
|
||||
var message = {
|
||||
success: gettext('Subscription %s was successfully created.')
|
||||
};
|
||||
|
||||
var scope;
|
||||
var model = null;
|
||||
|
||||
var service = {
|
||||
initScope: initScope,
|
||||
perform: perform,
|
||||
allowed: allowed
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
// we define initScope so that the table controller
|
||||
// will know when a new subscription has been created
|
||||
function initScope($scope) {
|
||||
scope = $scope;
|
||||
var subWatcher = $scope.$on(events.SUBSCRIPTION_CHANGED, onSubscriptionChange);
|
||||
$scope.$on('$destroy', function destroy() {
|
||||
subWatcher();
|
||||
});
|
||||
}
|
||||
|
||||
function onSubscriptionChange(e, subscription) {
|
||||
angular.extend(model, subscription);
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function perform(queue) {
|
||||
model = { subscriber: null, ttl: null, options: {} };
|
||||
model.queueName = queue.name;
|
||||
wizard.modal({
|
||||
scope: scope,
|
||||
workflow: createWorkflow,
|
||||
submit: submit
|
||||
});
|
||||
}
|
||||
|
||||
function allowed(queue) {
|
||||
return policy.ifAllowed({ rules: [['queue', 'add_subscriptions']] });;
|
||||
}
|
||||
|
||||
function submit() {
|
||||
return zaqar.addSubscription(model).then(success, error);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
angular.extend(model, response.data);
|
||||
toast.add('success', interpolate(message.success, [model.subscriber]));
|
||||
scope.$emit(events.SUBSCRIPTION_CREATE_SUCCESS, model);
|
||||
}
|
||||
|
||||
function error(response) {
|
||||
// TODO: Currently, when server throws an error
|
||||
// close the modal dialog and display the error message
|
||||
// In the future, display the error message inside the dialog
|
||||
// and allow user to continue with workflow
|
||||
return;
|
||||
}
|
||||
|
||||
} // end of createSubscriptionService
|
||||
})(); // end of IIFE
|
||||
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.project.queues')
|
||||
.factory('horizon.dashboard.project.queues.actions.createSubscriptionWorkflow',
|
||||
createSubscriptionWorkflow);
|
||||
|
||||
createSubscriptionWorkflow.$inject = [
|
||||
'horizon.app.core.workflow.factory',
|
||||
'horizon.dashboard.project.queues.basePath',
|
||||
'horizon.framework.util.i18n.gettext'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngdoc factory
|
||||
* @name horizon.dashboard.project.queues.actions.createSubscriptionWorkflow
|
||||
* @description A workflow for the create subscription action.
|
||||
*/
|
||||
function createSubscriptionWorkflow(workflowService, basePath, gettext) {
|
||||
|
||||
var workflow = workflowService({
|
||||
title: gettext('Create Subscription'),
|
||||
btnText: { finish: gettext('Create') },
|
||||
steps: [
|
||||
{
|
||||
title: gettext('Subscription'),
|
||||
templateUrl: basePath + 'steps/subscription/subscription.html',
|
||||
formName: 'subscriptionForm'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return workflow;
|
||||
}
|
||||
|
||||
})();
|
@ -46,7 +46,8 @@
|
||||
DETAILS_CHANGED: 'horizon.dashboard.project.queues.DETAILS_CHANGED',
|
||||
METADATA_CHANGED: 'horizon.dashboard.project.queues.METADATA_CHANGED',
|
||||
DELETE_SUCCESS: 'horizon.dashboard.project.queues.DELETE_SUCCESS',
|
||||
UPDATE_SUCCESS: 'horizon.dashboard.project.queues.UPDATE_SUCCESS'
|
||||
UPDATE_SUCCESS: 'horizon.dashboard.project.queues.UPDATE_SUCCESS',
|
||||
SUBSCRIPTION_CREATE_SUCCESS: 'horizon.dashboard.project.queues.SUBSCRIPTION_CREATE_SUCCESS'
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
|
||||
.subtitle {
|
||||
margin: 2em 0;
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.project.queues')
|
||||
.controller('horizon.dashboard.project.queues.steps.SubscriptionController',
|
||||
SubscriptionController);
|
||||
|
||||
SubscriptionController.$inject = [
|
||||
'$scope',
|
||||
'horizon.app.core.openstack-service-api.zaqar',
|
||||
'horizon.dashboard.project.queues.events'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name horizon.dashboard.project.queues.steps.SubscriptionController
|
||||
* @description This controller is use for creating a subscription.
|
||||
*/
|
||||
function SubscriptionController($scope, zaqar, events) {
|
||||
|
||||
var ctrl = this;
|
||||
ctrl.subscription = {};
|
||||
ctrl.update = false;
|
||||
|
||||
////////////////////////
|
||||
|
||||
// watch this object, when it changes, emit to parent listeners
|
||||
var watcher = $scope.$watchCollection(getSubscription, onSubscriptionChange);
|
||||
$scope.$on('$destroy', function() {
|
||||
watcher();
|
||||
})
|
||||
|
||||
////////////////////////
|
||||
|
||||
function getSubscription() {
|
||||
return ctrl.subscription;
|
||||
}
|
||||
|
||||
function onSubscriptionChange(newValue, oldValue){
|
||||
if (newValue !== oldValue) {
|
||||
$scope.$emit(events.SUBSCRIPTION_CHANGED, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
} // end of SubscriptionController
|
||||
|
||||
})();
|
@ -0,0 +1,48 @@
|
||||
|
||||
<div ng-controller="horizon.dashboard.project.queues.steps.SubscriptionController as detailsCtrl">
|
||||
|
||||
<h1 translate>Subscription</h1>
|
||||
<div class="content">
|
||||
|
||||
<div class="subtitle" translate>
|
||||
Note that you can create a subscription without defining options.
|
||||
Subscribers must be in the form of mailto, HTTP, or HTTPS.
|
||||
The TTL for a subscription must be at least 60 seconds long.
|
||||
</div>
|
||||
|
||||
<div class="selected-source clearfix">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<div class="form-group required">
|
||||
<label class="control-label required">
|
||||
<translate>Subscriber</translate>
|
||||
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||
</label>
|
||||
<input required
|
||||
id="subscriptionForm-subscriber" name="subscriber"
|
||||
type="text" class="form-control"
|
||||
ng-model="detailsCtrl.subscription.subscriber"
|
||||
ng-maxlength="255"
|
||||
ng-disabled="detailsCtrl.update"
|
||||
placeholder="{$ 'Enter a subscriber'|translate $}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<div class="form-group required">
|
||||
<label class="control-label required" translate>
|
||||
<translate>Time To Live</translate>
|
||||
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||
</label>
|
||||
<input required
|
||||
id="" name="ttl" min="60"
|
||||
type="number" class="form-control"
|
||||
ng-model="detailsCtrl.subscription.ttl"
|
||||
ng-disabled="detailsCtrl.update"
|
||||
placeholder="{$ 'Enter TTL for subscription'|translate $}">
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- row -->
|
||||
</div><!-- clearfix -->
|
||||
|
||||
</div><!-- content -->
|
||||
</div><!-- controller -->
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name queuesTableController
|
||||
* @ngController
|
||||
*
|
||||
* @description
|
||||
* Controller for the queues table
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.project.queues')
|
||||
.controller('horizon.dashboard.project.queues.SubscriptionTableController',
|
||||
SubscriptionTableController);
|
||||
|
||||
SubscriptionTableController.$inject = [
|
||||
'$scope',
|
||||
'horizon.app.core.openstack-service-api.zaqar',
|
||||
'horizon.dashboard.project.queues.events',
|
||||
'horizon.framework.widgets.toast.service'
|
||||
];
|
||||
|
||||
function SubscriptionTableController($scope, zaqar, events, toast) {
|
||||
|
||||
var ctrl = this;
|
||||
ctrl.queuesMap = {};
|
||||
ctrl.deleteSubscription = deleteSubscription;
|
||||
|
||||
init();
|
||||
initScope();
|
||||
|
||||
//////////
|
||||
|
||||
function initScope() {
|
||||
var expandWatcher = $scope.$on('hzTable:rowExpanded', getSubscriptions);
|
||||
var createWatcher = $scope.$on(events.SUBSCRIPTION_CREATE_SUCCESS, addSubscription);
|
||||
$scope.$on('$destroy', function destroy() {
|
||||
expandWatcher();
|
||||
createWatcher();
|
||||
});
|
||||
}
|
||||
|
||||
function init() {}
|
||||
|
||||
//////////
|
||||
|
||||
function checkAndInitMap(id) {
|
||||
if (!ctrl.queuesMap.hasOwnProperty(id)) {
|
||||
ctrl.queuesMap[id] = {
|
||||
subscriptions: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function addSubscription(event, sub){
|
||||
checkAndInitMap(sub.queueName);
|
||||
ctrl.queuesMap[sub.queueName].subscriptions.push(sub);
|
||||
}
|
||||
|
||||
function deleteSubscription(queue, sub){
|
||||
var msg = gettext('Removed %(subscriber)s subscriber from the %(queue)s queue.');
|
||||
var context = { subscriber: sub.subscriber, queue: queue.name };
|
||||
zaqar.deleteSubscription(queue.name, sub).success(deleteSuccess);
|
||||
|
||||
function deleteSuccess(){
|
||||
toast.add('success', interpolate(msg, context, true));
|
||||
var index = ctrl.queuesMap[queue.name].subscriptions.indexOf(sub);
|
||||
if (index >= 0){ ctrl.queuesMap[queue.name].subscriptions.splice(index, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
function getSubscriptions(event, queue) {
|
||||
zaqar.getSubscriptions(queue).success(function (response) {
|
||||
checkAndInitMap(queue.name);
|
||||
ctrl.queuesMap[queue.name].subscriptions = response;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,28 @@
|
||||
|
||||
<table class="table tabler-inner"
|
||||
ng-controller="horizon.dashboard.project.queues.SubscriptionTableController as subCtrl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>Subscriber</th>
|
||||
<th translate>Time to Live</th>
|
||||
<th translate>Options</th>
|
||||
<th translate>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="subCtrl.queuesMap[q.name].subscriptions.length === 0">
|
||||
<td colspan="100">No subscribers to show.</td>
|
||||
</tr>
|
||||
<tr ng-repeat="sub in subCtrl.queuesMap[q.name].subscriptions">
|
||||
<td>{$ sub.subscriber $}</td>
|
||||
<td>{$ sub.ttl $}</td>
|
||||
<td>{$ sub.options $}</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-danger"
|
||||
ng-click="subCtrl.deleteSubscription(q, sub)">
|
||||
<span class="fa fa-trash"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
@ -30,19 +30,20 @@
|
||||
queuesTableController.$inject = [
|
||||
'$scope',
|
||||
'horizon.app.core.openstack-service-api.zaqar',
|
||||
'horizon.dashboard.project.queues.basePath',
|
||||
'horizon.dashboard.project.queues.events',
|
||||
'horizon.dashboard.project.queues.resourceType',
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
];
|
||||
|
||||
function queuesTableController($scope, zaqar, events, type, registry) {
|
||||
function queuesTableController($scope, zaqar, base, events, type, registry) {
|
||||
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.queues = [];
|
||||
ctrl.queuesSrc = [];
|
||||
|
||||
ctrl.resourceType = registry.getResourceType(type);
|
||||
ctrl.subsTemplate = base + 'table/subscriptionTable.html';
|
||||
|
||||
init();
|
||||
initScope();
|
||||
@ -53,11 +54,13 @@
|
||||
var createWatcher = $scope.$on(events.CREATE_SUCCESS, onCreateSuccess);
|
||||
var deleteWatcher = $scope.$on(events.DELETE_SUCCESS, onDeleteSuccess);
|
||||
var updateWatcher = $scope.$on(events.UPDATE_SUCCESS, onUpdateSuccess);
|
||||
var subWatcher = $scope.$on(events.SUBSCRIPTION_CREATE_SUCCESS, broadcastEvents);
|
||||
$scope.$on('$destroy', function destroy() {
|
||||
createWatcher();
|
||||
deleteWatcher();
|
||||
updateWatcher();
|
||||
})
|
||||
subWatcher();
|
||||
});
|
||||
}
|
||||
|
||||
//////////
|
||||
@ -67,6 +70,12 @@
|
||||
zaqar.getQueues().then(showQueues);
|
||||
}
|
||||
|
||||
function broadcastEvents(event, data) {
|
||||
if (event.targetScope !== $scope) {
|
||||
$scope.$broadcast(event.name, data);
|
||||
}
|
||||
}
|
||||
|
||||
function showQueues(response) {
|
||||
// hz-table expects all items to have the id field
|
||||
// so we need to manually add name as id here
|
||||
|
@ -59,7 +59,7 @@
|
||||
|
||||
<td class="expander">
|
||||
<span class="fa fa-chevron-right"
|
||||
hz-expand-detail duration="200"></span>
|
||||
hz-expand-detail item="q" duration="200"></span>
|
||||
</td>
|
||||
|
||||
<td class="rsp-p1">{$ q.name $}</td>
|
||||
@ -71,7 +71,7 @@
|
||||
Table-row-action-column:
|
||||
Actions taken here applies to a single item/row.
|
||||
-->
|
||||
<actions allowed="table.resourceType.itemActions" type="row" item="q" />
|
||||
<actions allowed="table.resourceType.itemActions" type="row" item="q"></actions>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
@ -88,8 +88,8 @@
|
||||
E.g. table header with rsp-p2 should be here with rsp-alt-p2
|
||||
The layout should minimize vertical space to reduce scrolling.
|
||||
-->
|
||||
<div class="row">
|
||||
|
||||
<div class="row">
|
||||
<!-- Important metadata should go here and table header.
|
||||
It will appear in the table above if there is room, otherwise
|
||||
it will appear in the drawer when user resizes the window.
|
||||
@ -100,16 +100,16 @@
|
||||
<dd>{$ q.free $}</dd>
|
||||
</dl>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<!-- additional unimportant metadata can be place here -->
|
||||
<dl class="col-sm-12">
|
||||
<dt translate>Metadata</dt>
|
||||
<dd>{$ q.metadata $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<ng-include src="table.subsTemplate"></ng-include>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user