Commit 1e7247b4 authored by JC Brand's avatar JC Brand

Add client info modal showing name and version number

parent 0baed977
......@@ -5,17 +5,18 @@
- Error `FATAL: TypeError: Cannot read property 'extend' of undefined` when using `embedded` view mode.
- Default paths in converse-notifications.js are now relative
- Add a button to regenerate OMEMO keys
- Add client info modal which shows Converse's version number
- #141 XEP-0184: Message Delivery Receipts
- #1033 Setting show_send_button to true didn't work
- #1188 Feature request: drag and drop file to HTTP Upload
- #1268 Switch from SASS variables to CSS custom properties
- #1278 Replace the default avatar with a SVG version
- #1033 Setting show_send_button to true didn't work
- #1306 added option `notification_delay`
- #1305 added value 'all' for 'show_desktop_notifications' to notifiy even if converse.js is open
- #1306 added option `notification_delay`
- #1312 Error `unrecognized expression` in Safari
- #1316 show version info in login dialog
- #1318 added values 'on' and 'off' for 'trusted' option which removes the "This is a trusted device" checkbox from the login form
- #1319 Implement sending of presences according to XEP-0319: Last User Interaction in Presence
- #1316 show version info in login dialog
## 4.0.4 (2018-10-29)
......
......@@ -9502,10 +9502,11 @@ body.reset {
left: -15px; }
#conversejs.converse-overlayed {
height: 3em; }
#conversejs .brand-heading-container {
text-align: center; }
#conversejs .brand-heading {
font-size: 200%;
font-family: var(--heading-font); }
#conversejs .brand-heading .icon-conversejs {
font-size: 80%; }
#conversejs .popover {
position: fixed; }
#conversejs .converse-chatboxes {
......@@ -10697,7 +10698,7 @@ body.reset {
#conversejs #controlbox .controlbox-section .controlbox-heading__btn {
cursor: pointer;
align-self: flex-start;
font-size: 1.1em;
font-size: 1em;
padding: 0;
margin: 0.75em 0 0.75em 0.75em; }
#conversejs #controlbox .controlbox-section .controlbox-heading__btn.fa-vcard {
......
......@@ -21,9 +21,6 @@
<div class="converse-brand__padding"></div>
<div class="converse-brand__heading">
<div class="converse-brand__text"><i class="icon-conversejs"></i>Converse</div>
<div class="converse-brand__byline">
brought to you by <a target="_blank" rel="nofollow" href="https://opkode.com">Opkode</a>
</div>
</div>
</div>
</div>
......
......@@ -60401,10 +60401,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
}
}
this.el.innerHTML = templates_controlbox_html__WEBPACK_IMPORTED_MODULE_8___default()(_.extend(this.model.toJSON(), {
'version_name': _converse.VERSION_NAME,
'view_mode': _converse.view_mode
}));
this.el.innerHTML = templates_controlbox_html__WEBPACK_IMPORTED_MODULE_8___default()(_.extend(this.model.toJSON()));
if (!this.model.get('closed')) {
this.show();
......@@ -66562,12 +66559,14 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @converse/headless/converse-core */ "./src/headless/converse-core.js");
/* harmony import */ var templates_chat_status_modal_html__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! templates/chat_status_modal.html */ "./src/templates/chat_status_modal.html");
/* harmony import */ var templates_chat_status_modal_html__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(templates_chat_status_modal_html__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! templates/profile_modal.html */ "./src/templates/profile_modal.html");
/* harmony import */ var templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var templates_profile_view_html__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! templates/profile_view.html */ "./src/templates/profile_view.html");
/* harmony import */ var templates_profile_view_html__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(templates_profile_view_html__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! templates/status_option.html */ "./src/templates/status_option.html");
/* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(templates_status_option_html__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var templates_client_info_modal_html__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! templates/client_info_modal.html */ "./src/templates/client_info_modal.html");
/* harmony import */ var templates_client_info_modal_html__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(templates_client_info_modal_html__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! templates/profile_modal.html */ "./src/templates/profile_modal.html");
/* harmony import */ var templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var templates_profile_view_html__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! templates/profile_view.html */ "./src/templates/profile_view.html");
/* harmony import */ var templates_profile_view_html__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(templates_profile_view_html__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! templates/status_option.html */ "./src/templates/status_option.html");
/* harmony import */ var templates_status_option_html__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(templates_status_option_html__WEBPACK_IMPORTED_MODULE_9__);
// Converse.js (A browser based XMPP chat client)
// http://conversejs.org
//
......@@ -66585,6 +66584,7 @@ __webpack_require__.r(__webpack_exports__);
const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].env,
Strophe = _converse$env.Strophe,
Backbone = _converse$env.Backbone,
......@@ -66618,7 +66618,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].plugins
},
toHTML() {
return templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_6___default()(_.extend(this.model.toJSON(), this.model.vcard.toJSON(), {
return templates_profile_modal_html__WEBPACK_IMPORTED_MODULE_7___default()(_.extend(this.model.toJSON(), this.model.vcard.toJSON(), {
'_': _,
'__': __,
'_converse': _converse,
......@@ -66750,12 +66750,25 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].plugins
this.modal.hide();
}
});
_converse.ClientInfoModal = _converse.BootstrapModal.extend({
toHTML() {
return templates_client_info_modal_html__WEBPACK_IMPORTED_MODULE_6___default()(_.extend(this.model.toJSON(), this.model.vcard.toJSON(), {
'__': __,
'modal_title': __('About'),
'version_name': _converse.VERSION_NAME,
'first_subtitle': __('%1$s Open Source %2$s XMPP chat client brought to you by %3$s Opkode %2$s', '<a target="_blank" rel="nofollow" href="https://conversejs.org">', '</a>', '<a target="_blank" rel="nofollow" href="https://opkode.com">'),
'second_subtitle': __('%1$s Translate %2$s it into your own language', '<a target="_blank" rel="nofollow" href="https://hosted.weblate.org/projects/conversejs/#languages">', '</a>')
}));
}
});
_converse.XMPPStatusView = _converse.VDOMViewWithAvatar.extend({
tagName: "div",
events: {
"click a.show-profile": "showProfileModal",
"click a.change-status": "showStatusChangeModal",
"click .show-client-info": "showClientInfoModal",
"click .logout": "logOut"
},
......@@ -66766,7 +66779,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].plugins
toHTML() {
const chat_status = this.model.get('status') || 'offline';
return templates_profile_view_html__WEBPACK_IMPORTED_MODULE_7___default()(_.extend(this.model.toJSON(), this.model.vcard.toJSON(), {
return templates_profile_view_html__WEBPACK_IMPORTED_MODULE_8___default()(_.extend(this.model.toJSON(), this.model.vcard.toJSON(), {
'__': __,
'fullname': this.model.vcard.get('fullname') || _converse.bare_jid,
'status_message': this.model.get('status_message') || __("I am %1$s", this.getPrettyStatus(chat_status)),
......@@ -66775,6 +66788,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].plugins
'title_change_settings': __('Change settings'),
'title_change_status': __('Click to change your chat status'),
'title_log_out': __('Log out'),
'info_details': __('Show details about this chat client'),
'title_your_profile': __('Your profile')
}));
},
......@@ -66803,6 +66817,16 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_4__["default"].plugins
this.status_modal.show(ev);
},
showClientInfoModal(ev) {
if (_.isUndefined(this.client_info_modal)) {
this.client_info_modal = new _converse.ClientInfoModal({
model: this.model
});
}
this.client_info_modal.show(ev);
},
logOut(ev) {
ev.preventDefault();
const result = confirm(__("Are you sure you want to log out?"));
......@@ -72158,9 +72182,6 @@ _converse.initialize = function (settings, callback) {
* Parameters:
* (String) stat: The user's chat status
*/
/* Send out a Chat Status Notification (XEP-0352) */
// XXX if (converse.features[Strophe.NS.CSI] || true) {
_converse.api.send(Object(strophe_js__WEBPACK_IMPORTED_MODULE_0__["$build"])(stat, {
xmlns: strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].NS.CSI
}));
......@@ -72176,7 +72197,7 @@ _converse.initialize = function (settings, callback) {
if (!_converse.connection.authenticated) {
// We can't send out any stanzas when there's no authenticated connection.
// converse can happen when the connection reconnects.
// This can happen when the connection reconnects.
return;
}
......@@ -72243,7 +72264,7 @@ _converse.initialize = function (settings, callback) {
}
_converse.idle_seconds = 0;
_converse.auto_changed_status = false; // Was the user's status changed by _converse.js?
_converse.auto_changed_status = false; // Was the user's status changed by Converse?
window.addEventListener('click', _converse.onUserActivity);
window.addEventListener('focus', _converse.onUserActivity);
......@@ -101778,6 +101799,32 @@ return __p
/***/ }),
/***/ "./src/templates/client_info_modal.html":
/*!**********************************************!*\
!*** ./src/templates/client_info_modal.html ***!
\**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./node_modules/lodash/escape.js")};
module.exports = function(o) {
var __t, __p = '', __e = _.escape;
__p += '<!-- src/templates/client_info_modal.html -->\n<!-- Change status Modal -->\n<div class="modal fade" id="modal-status-change" tabindex="-1" role="dialog" aria-labelledby="changeStatusModalLabel" aria-hidden="true">\n <div class="modal-dialog" role="document">\n <div class="modal-content">\n <div class="modal-header">\n <h5 class="modal-title" id="changeStatusModalLabel">' +
__e(o.modal_title) +
'</h5>\n <button type="button" class="close" data-dismiss="modal" aria-label="' +
__e(o.label_close) +
'">\n <span aria-hidden="true">&times;</span>\n </button>\n </div>\n <div class="modal-body">\n <div class="container brand-heading-container">\n <h6 class="brand-heading"><i class="icon-conversejs"></i>Converse</h6>\n <p class="brand-subtitle">' +
__e(o.version_name) +
'</p>\n <p class="brand-subtitle">' +
((__t = (o.first_subtitle)) == null ? '' : __t) +
'</a> </p>\n <p class="brand-subtitle">' +
((__t = (o.second_subtitle)) == null ? '' : __t) +
'</p>\n <div>\n </div>\n </div>\n </div>\n </div>\n</div>\n';
return __p
};
/***/ }),
/***/ "./src/templates/controlbox.html":
/*!***************************************!*\
!*** ./src/templates/controlbox.html ***!
......@@ -101787,19 +101834,13 @@ return __p
var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./node_modules/lodash/escape.js")};
module.exports = function(o) {
var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
var __t, __p = '', __j = Array.prototype.join;
function print() { __p += __j.call(arguments, '') }
__p += '<!-- src/templates/controlbox.html -->\n<div class="flyout box-flyout">\n <div class="chat-head controlbox-head">\n ';
if (!o.sticky_controlbox) { ;
__p += '\n <a class="chatbox-btn close-chatbox-button fa fa-times"></a>\n ';
} ;
__p += '\n </div>\n <div class="controlbox-panes"></div>\n ';
if (o.view_mode != 'fullscreen') { ;
__p += '\n <div class="controlbox-subtitle">\n ' +
__e(o.version_name) +
'\n </div>\n ';
} ;
__p += '\n</div>\n';
__p += '\n </div>\n <div class="controlbox-panes"></div>\n</div>\n';
return __p
};
......@@ -102913,15 +102954,13 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no
module.exports = function(o) {
var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
function print() { __p += __j.call(arguments, '') }
__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="profile d-flex">\n <a class="show-profile" href="#">\n <canvas alt="o.__(\'Your avatar\')" class="avatar align-self-center" height="40" width="40"></canvas>\n </a>\n <span class="username w-100 align-self-center">' +
__p += '<!-- src/templates/profile_view.html -->\n<div class="userinfo controlbox-padded">\n<div class="controlbox-section profile d-flex">\n <a class="show-profile" href="#">\n <canvas alt="o.__(\'Your avatar\')" class="avatar align-self-center" height="40" width="40"></canvas>\n </a>\n <span class="username w-100 align-self-center">' +
__e(o.fullname) +
'</span>\n <!-- <a class="chatbox-btn fa fa-id-card align-self-center" title="' +
__e(o.title_your_profile) +
'" data-toggle="modal" data-target="#userProfileModal"></a> -->\n <!-- <a class="chatbox-btn fa fa-cog align-self-center" title="' +
__e(o.title_change_status) +
'" data-toggle="modal" data-target="#settingsModal"></a> -->\n ';
'</span>\n <a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="' +
__e(o.info_details) +
'"></a>\n ';
if (o._converse.allow_logout) { ;
__p += '\n <a class="chatbox-btn logout fa fa-sign-out-alt align-self-center" title="' +
__p += '\n <a class="controlbox-heading__btn logout fa fa-sign-out-alt align-self-center" title="' +
__e(o.title_log_out) +
'"></a>\n ';
} ;
......@@ -102951,7 +102990,7 @@ __p += ' fa fa-circle chat-status chat-status--offline';
} ;
__p += '"></span> ' +
__e(o.status_message) +
'</span>\n <a class="chatbox-btn change-status fa fa-pencil-alt" title="' +
'</span>\n <a class="controlbox-heading__btn change-status fa fa-pencil-alt" title="' +
__e(o.title_change_status) +
'" data-toggle="modal" data-target="#changeStatusModal"></a>\n</div>\n</div>\n';
return __p
......@@ -18,9 +18,6 @@
<div class="converse-brand__padding"></div>
<div class="converse-brand__heading">
<div class="converse-brand__text"><i class="icon-conversejs"></i>Converse</div>
<div class="converse-brand__byline">
brought to you by <a target="_blank" rel="nofollow" href="https://opkode.com">Opkode</a>
</div>
</div>
</div>
</div>
......
......@@ -224,7 +224,7 @@
.controlbox-heading__btn {
cursor: pointer;
align-self: flex-start;
font-size: 1.1em;
font-size: 1em;
padding: 0;
margin: 0.75em 0 0.75em 0.75em;
&.fa-vcard {
......
......@@ -136,11 +136,13 @@ body.reset {
height: 3em;
}
.brand-heading-container {
text-align: center;
}
.brand-heading {
font-size: 200%;
font-family: var(--heading-font);
.icon-conversejs {
font-size: 80%;
}
}
.popover {
......
......@@ -213,10 +213,7 @@ converse.plugins.add('converse-controlbox', {
this.model.set('closed', !_converse.show_controlbox_by_default);
}
}
this.el.innerHTML = tpl_controlbox(_.extend(this.model.toJSON(), {
'version_name': _converse.VERSION_NAME,
'view_mode': _converse.view_mode
}));
this.el.innerHTML = tpl_controlbox(_.extend(this.model.toJSON()));
if (!this.model.get('closed')) {
this.show();
......
......@@ -12,6 +12,7 @@ import _FormData from "formdata-polyfill";
import bootstrap from "bootstrap";
import converse from "@converse/headless/converse-core";
import tpl_chat_status_modal from "templates/chat_status_modal.html";
import tpl_client_info_modal from "templates/client_info_modal.html";
import tpl_profile_modal from "templates/profile_modal.html";
import tpl_profile_view from "templates/profile_view.html";
import tpl_status_option from "templates/status_option.html";
......@@ -185,11 +186,37 @@ converse.plugins.add('converse-profile', {
}
});
_converse.ClientInfoModal = _converse.BootstrapModal.extend({
toHTML () {
return tpl_client_info_modal(
_.extend(
this.model.toJSON(),
this.model.vcard.toJSON(), {
'__': __,
'modal_title': __('About'),
'version_name': _converse.VERSION_NAME,
'first_subtitle': __( '%1$s Open Source %2$s XMPP chat client brought to you by %3$s Opkode %2$s',
'<a target="_blank" rel="nofollow" href="https://conversejs.org">',
'</a>',
'<a target="_blank" rel="nofollow" href="https://opkode.com">'
),
'second_subtitle': __('%1$s Translate %2$s it into your own language',
'<a target="_blank" rel="nofollow" href="https://hosted.weblate.org/projects/conversejs/#languages">',
'</a>'
)
}
)
);
}
});
_converse.XMPPStatusView = _converse.VDOMViewWithAvatar.extend({
tagName: "div",
events: {
"click a.show-profile": "showProfileModal",
"click a.change-status": "showStatusChangeModal",
"click .show-client-info": "showClientInfoModal",
"click .logout": "logOut"
},
......@@ -212,6 +239,7 @@ converse.plugins.add('converse-profile', {
'title_change_settings': __('Change settings'),
'title_change_status': __('Click to change your chat status'),
'title_log_out': __('Log out'),
'info_details': __('Show details about this chat client'),
'title_your_profile': __('Your profile')
}));
},
......@@ -234,6 +262,13 @@ converse.plugins.add('converse-profile', {
this.status_modal.show(ev);
},
showClientInfoModal(ev) {
if (_.isUndefined(this.client_info_modal)) {
this.client_info_modal = new _converse.ClientInfoModal({model: this.model});
}
this.client_info_modal.show(ev);
},
logOut (ev) {
ev.preventDefault();
const result = confirm(__("Are you sure you want to log out?"));
......
......@@ -401,8 +401,6 @@ _converse.initialize = function (settings, callback) {
* Parameters:
* (String) stat: The user's chat status
*/
/* Send out a Chat Status Notification (XEP-0352) */
// XXX if (converse.features[Strophe.NS.CSI] || true) {
_converse.api.send($build(stat, {xmlns: Strophe.NS.CSI}));
_converse.inactive = (stat === _converse.INACTIVE) ? true : false;
};
......@@ -414,7 +412,7 @@ _converse.initialize = function (settings, callback) {
}
if (!_converse.connection.authenticated) {
// We can't send out any stanzas when there's no authenticated connection.
// converse can happen when the connection reconnects.
// This can happen when the connection reconnects.
return;
}
if (_converse.inactive) {
......@@ -476,7 +474,7 @@ _converse.initialize = function (settings, callback) {
return;
}
_converse.idle_seconds = 0
_converse.auto_changed_status = false; // Was the user's status changed by _converse.js?
_converse.auto_changed_status = false; // Was the user's status changed by Converse?
window.addEventListener('click', _converse.onUserActivity);
window.addEventListener('focus', _converse.onUserActivity);
window.addEventListener('keypress', _converse.onUserActivity);
......
<!-- Change status Modal -->
<div class="modal fade" id="modal-status-change" tabindex="-1" role="dialog" aria-labelledby="changeStatusModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="changeStatusModalLabel">{{{o.modal_title}}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="container brand-heading-container">
<h6 class="brand-heading"><i class="icon-conversejs"></i>Converse</h6>
<p class="brand-subtitle">{{{o.version_name}}}</p>
<p class="brand-subtitle">{{o.first_subtitle}}</a> </p>
<p class="brand-subtitle">{{o.second_subtitle}}</p>
<div>
</div>
</div>
</div>
</div>
</div>
......@@ -5,9 +5,4 @@
{[ } ]}
</div>
<div class="controlbox-panes"></div>
{[ if (o.view_mode != 'fullscreen') { ]}
<div class="controlbox-subtitle">
{{{o.version_name}}}
</div>
{[ } ]}
</div>
<div class="userinfo controlbox-padded">
<div class="profile d-flex">
<div class="controlbox-section profile d-flex">
<a class="show-profile" href="#">
<canvas alt="o.__('Your avatar')" class="avatar align-self-center" height="40" width="40"></canvas>
</a>
<span class="username w-100 align-self-center">{{{o.fullname}}}</span>
<!-- <a class="chatbox-btn fa fa-id-card align-self-center" title="{{{o.title_your_profile}}}" data-toggle="modal" data-target="#userProfileModal"></a> -->
<!-- <a class="chatbox-btn fa fa-cog align-self-center" title="{{{o.title_change_status}}}" data-toggle="modal" data-target="#settingsModal"></a> -->
<a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="{{{o.info_details}}}"></a>
{[ if (o._converse.allow_logout) { ]}
<a class="chatbox-btn logout fa fa-sign-out-alt align-self-center" title="{{{o.title_log_out}}}"></a>
<a class="controlbox-heading__btn logout fa fa-sign-out-alt align-self-center" title="{{{o.title_log_out}}}"></a>
{[ } ]}
</div>
<div class="d-flex xmpp-status">
......@@ -18,6 +17,6 @@
{[ if (o.chat_status === 'away') { ]} fa fa-circle chat-status chat-status--away{[ } ]}
{[ if (o.chat_status === 'xa') { ]} far fa-circle chat-status chat-status--xa {[ } ]}
{[ if (o.chat_status === 'offline') { ]} fa fa-circle chat-status chat-status--offline{[ } ]}"></span> {{{o.status_message}}}</span>
<a class="chatbox-btn change-status fa fa-pencil-alt" title="{{{o.title_change_status}}}" data-toggle="modal" data-target="#changeStatusModal"></a>
<a class="controlbox-heading__btn change-status fa fa-pencil-alt" title="{{{o.title_change_status}}}" data-toggle="modal" data-target="#changeStatusModal"></a>
</div>
</div>
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