mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
move user image into profile settings
This commit is contained in:
parent
c2ea67d056
commit
53cd8b0eb8
36 changed files with 376 additions and 372 deletions
|
@ -1,10 +1,10 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
$(document).on('pageshow', "#aboutPage", function () {
|
||||
|
||||
var page = this;
|
||||
|
||||
apiClient.getSystemInfo().done(function (info) {
|
||||
ApiClient.getSystemInfo().done(function (info) {
|
||||
|
||||
var elem = $('#appVersionNumber', page);
|
||||
|
||||
|
@ -12,4 +12,4 @@
|
|||
});
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -34,7 +34,7 @@
|
|||
var options = {
|
||||
|
||||
SortBy: "IsFavoriteOrLiked,Random",
|
||||
Limit: 50,
|
||||
Limit: 20,
|
||||
Recursive: true,
|
||||
IncludeItemTypes: types,
|
||||
ImageTypes: "Backdrop",
|
||||
|
@ -61,7 +61,9 @@
|
|||
|
||||
function showBackdrop(type) {
|
||||
|
||||
getBackdropItemIds(Dashboard.getCurrentUserId(), type, LibraryMenu.getTopParentId()).done(function (images) {
|
||||
getBackdropItemIds(Dashboard.getCurrentUserId(),
|
||||
type,
|
||||
LibraryMenu.getTopParentId()).done(function (images) {
|
||||
|
||||
if (images.length) {
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function getSections() {
|
||||
|
||||
|
@ -112,4 +112,4 @@
|
|||
loadSections(page, userId);
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function loadSections(page, userId) {
|
||||
|
||||
|
@ -34,4 +34,4 @@
|
|||
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function getUserViews(userId) {
|
||||
|
||||
|
@ -440,7 +440,7 @@
|
|||
|
||||
})(jQuery, document, ApiClient);
|
||||
|
||||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function getDefaultSection(index) {
|
||||
|
||||
|
@ -595,4 +595,4 @@
|
|||
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -272,8 +272,6 @@
|
|||
'' :
|
||||
getTopParentId() || '';
|
||||
|
||||
sessionStore.setItem('topParentId', id);
|
||||
|
||||
$('.lnkMediaFolder').each(function () {
|
||||
|
||||
var itemId = this.getAttribute('data-itemid');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
var currentItem;
|
||||
var programs;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
var query = {
|
||||
|
||||
|
@ -60,7 +60,7 @@
|
|||
|
||||
showLoadingMessage(page);
|
||||
|
||||
apiClient.getLiveTvChannels(query).done(function (result) {
|
||||
ApiClient.getLiveTvChannels(query).done(function (result) {
|
||||
|
||||
renderChannels(page, result);
|
||||
|
||||
|
@ -134,4 +134,4 @@
|
|||
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
// 30 mins
|
||||
var cellCurationMinutes = 30;
|
||||
|
@ -52,7 +52,7 @@
|
|||
|
||||
channelQuery.userId = Dashboard.getCurrentUserId();
|
||||
|
||||
channelsPromise = channelsPromise || apiClient.getLiveTvChannels(channelQuery);
|
||||
channelsPromise = channelsPromise || ApiClient.getLiveTvChannels(channelQuery);
|
||||
|
||||
var date = currentDate;
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
|||
console.log(nextDay);
|
||||
channelsPromise.done(function (channelsResult) {
|
||||
|
||||
apiClient.getLiveTvPrograms({
|
||||
ApiClient.getLiveTvPrograms({
|
||||
UserId: Dashboard.getCurrentUserId(),
|
||||
MaxStartDate: nextDay.toISOString(),
|
||||
MinEndDate: date.toISOString(),
|
||||
|
@ -451,13 +451,13 @@
|
|||
|
||||
var page = this;
|
||||
|
||||
apiClient.getLiveTvGuideInfo().done(function (guideInfo) {
|
||||
ApiClient.getLiveTvGuideInfo().done(function (guideInfo) {
|
||||
|
||||
setDateRange(page, guideInfo);
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
||||
|
||||
(function ($, document, window) {
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
var currentProgram;
|
||||
|
||||
|
@ -47,8 +47,8 @@
|
|||
|
||||
var programId = getParameterByName('programid');
|
||||
|
||||
var promise1 = apiClient.getNewLiveTvTimerDefaults({ programId: programId });
|
||||
var promise2 = apiClient.getLiveTvProgram(programId, Dashboard.getCurrentUserId());
|
||||
var promise1 = ApiClient.getNewLiveTvTimerDefaults({ programId: programId });
|
||||
var promise2 = ApiClient.getLiveTvProgram(programId, Dashboard.getCurrentUserId());
|
||||
|
||||
$.when(promise1, promise2).done(function (response1, response2) {
|
||||
|
||||
|
@ -108,7 +108,7 @@
|
|||
|
||||
var programId = getParameterByName('programid');
|
||||
|
||||
apiClient.getNewLiveTvTimerDefaults({ programId: programId }).done(function (item) {
|
||||
ApiClient.getNewLiveTvTimerDefaults({ programId: programId }).done(function (item) {
|
||||
|
||||
item.PrePaddingSeconds = $('#txtPrePaddingSeconds', form).val() * 60;
|
||||
item.PostPaddingSeconds = $('#txtPostPaddingSeconds', form).val() * 60;
|
||||
|
@ -123,7 +123,7 @@
|
|||
|
||||
if ($('#chkRecordSeries', form).checked()) {
|
||||
|
||||
apiClient.createLiveTvSeriesTimer(item).done(function () {
|
||||
ApiClient.createLiveTvSeriesTimer(item).done(function () {
|
||||
|
||||
Dashboard.hideLoadingMsg();
|
||||
Dashboard.navigate('livetvseriestimers.html');
|
||||
|
@ -131,7 +131,7 @@
|
|||
});
|
||||
|
||||
} else {
|
||||
apiClient.createLiveTvTimer(item).done(function () {
|
||||
ApiClient.createLiveTvTimer(item).done(function () {
|
||||
|
||||
Dashboard.hideLoadingMsg();
|
||||
Dashboard.navigate('livetvtimers.html');
|
||||
|
@ -185,4 +185,4 @@
|
|||
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
var currentItem;
|
||||
|
||||
|
@ -132,4 +132,4 @@
|
|||
currentItem = null;
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
var currentItem;
|
||||
|
||||
|
@ -90,7 +90,7 @@
|
|||
|
||||
var id = getParameterByName('id');
|
||||
|
||||
apiClient.getLiveTvRecording(id, Dashboard.getCurrentUserId()).done(function (result) {
|
||||
ApiClient.getLiveTvRecording(id, Dashboard.getCurrentUserId()).done(function (result) {
|
||||
|
||||
renderRecording(page, result);
|
||||
|
||||
|
@ -115,4 +115,4 @@
|
|||
currentItem = null;
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function getRecordingGroupHtml(group) {
|
||||
|
||||
|
@ -67,7 +67,7 @@
|
|||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
apiClient.getLiveTvRecordings({
|
||||
ApiClient.getLiveTvRecordings({
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
IsInProgress: true
|
||||
|
@ -78,7 +78,7 @@
|
|||
|
||||
});
|
||||
|
||||
apiClient.getLiveTvRecordings({
|
||||
ApiClient.getLiveTvRecordings({
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
limit: 12,
|
||||
|
@ -90,7 +90,7 @@
|
|||
|
||||
});
|
||||
|
||||
apiClient.getLiveTvRecordingGroups({
|
||||
ApiClient.getLiveTvRecordingGroups({
|
||||
|
||||
userId: Dashboard.getCurrentUserId()
|
||||
|
||||
|
@ -109,4 +109,4 @@
|
|||
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function (window, $, document, apiClient) {
|
||||
(function (window, $, document) {
|
||||
|
||||
var currentItem;
|
||||
|
||||
|
@ -105,7 +105,7 @@
|
|||
|
||||
var form = this;
|
||||
|
||||
apiClient.getLiveTvSeriesTimer(currentItem.Id).done(function (item) {
|
||||
ApiClient.getLiveTvSeriesTimer(currentItem.Id).done(function (item) {
|
||||
|
||||
item.PrePaddingSeconds = $('#txtPrePaddingSeconds', form).val() * 60;
|
||||
item.PostPaddingSeconds = $('#txtPostPaddingSeconds', form).val() * 60;
|
||||
|
@ -235,13 +235,13 @@
|
|||
|
||||
var id = getParameterByName('id');
|
||||
|
||||
apiClient.getLiveTvSeriesTimer(id).done(function (result) {
|
||||
ApiClient.getLiveTvSeriesTimer(id).done(function (result) {
|
||||
|
||||
renderTimer(page, result);
|
||||
|
||||
});
|
||||
|
||||
apiClient.getLiveTvRecordings({
|
||||
ApiClient.getLiveTvRecordings({
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
seriesTimerId: id
|
||||
|
@ -252,7 +252,7 @@
|
|||
|
||||
});
|
||||
|
||||
apiClient.getLiveTvTimers({
|
||||
ApiClient.getLiveTvTimers({
|
||||
|
||||
seriesTimerId: id
|
||||
|
||||
|
@ -297,4 +297,4 @@
|
|||
|
||||
window.LiveTvSeriesTimerPage = new liveTvSeriesTimerPage();
|
||||
|
||||
})(window, jQuery, document, ApiClient);
|
||||
})(window, jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
var query = {
|
||||
|
||||
|
@ -93,7 +93,7 @@
|
|||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
apiClient.getLiveTvSeriesTimers(query).done(function (result) {
|
||||
ApiClient.getLiveTvSeriesTimers(query).done(function (result) {
|
||||
|
||||
renderTimers(page, result.Items);
|
||||
|
||||
|
@ -143,4 +143,4 @@
|
|||
updateFilterControls(this);
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,10 +1,10 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function reload(page) {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
apiClient.getLiveTvRecommendedPrograms({
|
||||
ApiClient.getLiveTvRecommendedPrograms({
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
IsAiring: true,
|
||||
|
@ -26,7 +26,7 @@
|
|||
$('.activeProgramItems', page).html(html).createCardMenus();
|
||||
});
|
||||
|
||||
apiClient.getLiveTvRecommendedPrograms({
|
||||
ApiClient.getLiveTvRecommendedPrograms({
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
IsAiring: false,
|
||||
|
@ -58,4 +58,4 @@
|
|||
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function (window, $, document, apiClient) {
|
||||
(function (window, $, document) {
|
||||
|
||||
var currentItem;
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
|||
|
||||
var form = this;
|
||||
|
||||
apiClient.getLiveTvTimer(currentItem.Id).done(function (item) {
|
||||
ApiClient.getLiveTvTimer(currentItem.Id).done(function (item) {
|
||||
|
||||
item.PrePaddingSeconds = $('#txtPrePaddingSeconds', form).val() * 60;
|
||||
item.PostPaddingSeconds = $('#txtPostPaddingSeconds', form).val() * 60;
|
||||
|
@ -96,7 +96,7 @@
|
|||
|
||||
var id = getParameterByName('id');
|
||||
|
||||
apiClient.getLiveTvTimer(id).done(function (result) {
|
||||
ApiClient.getLiveTvTimer(id).done(function (result) {
|
||||
|
||||
renderTimer(page, result);
|
||||
|
||||
|
@ -133,4 +133,4 @@
|
|||
|
||||
window.LiveTvTimerPage = new liveTvTimerPage();
|
||||
|
||||
})(window, jQuery, document, ApiClient);
|
||||
})(window, jQuery, document);
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function deleteTimer(page, id) {
|
||||
|
||||
|
@ -100,7 +100,7 @@
|
|||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
apiClient.getLiveTvTimers().done(function (result) {
|
||||
ApiClient.getLiveTvTimers().done(function (result) {
|
||||
|
||||
renderTimers(page, result.Items);
|
||||
|
||||
|
@ -114,4 +114,4 @@
|
|||
reload(page);
|
||||
});
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -237,6 +237,7 @@
|
|||
$('.lnkDisplayPreferences', page).attr('href', 'mypreferencesdisplay.html?userId=' + userId);
|
||||
$('.lnkLanguagePreferences', page).attr('href', 'mypreferenceslanguages.html?userId=' + userId);
|
||||
$('.lnkWebClientPreferences', page).attr('href', 'mypreferenceswebclient.html?userId=' + userId);
|
||||
$('.lnkMyProfile', page).attr('href', 'myprofile.html?userId=' + userId);
|
||||
});
|
||||
|
||||
window.DisplayPreferencesPage = {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
ApiClient.getUser(userId).done(function (user) {
|
||||
|
||||
$('.username', page).html(user.Name);
|
||||
$('#uploadUserImage', page).val('').trigger('change');
|
||||
|
||||
Dashboard.setPageTitle(user.Name);
|
||||
|
@ -22,24 +23,24 @@
|
|||
type: "Primary"
|
||||
});
|
||||
|
||||
$('#fldImage', page).show().html('').html("<img height='200px' src='" + imageUrl + "' />");
|
||||
$('#fldImage', page).show().html('').html("<img width='140px' src='" + imageUrl + "' />");
|
||||
}
|
||||
|
||||
if (user.ConnectLinkType == 'Guest') {
|
||||
|
||||
$('.newImageSection', page).hide();
|
||||
$('#fldDeleteImage', page).hide();
|
||||
$('.newImageForm', page).hide();
|
||||
$('#btnDeleteImage', page).hide();
|
||||
}
|
||||
else if (user.PrimaryImageTag) {
|
||||
|
||||
$('#fldDeleteImage', page).show();
|
||||
$('#btnDeleteImage', page).show();
|
||||
$('#headerUploadNewImage', page).show();
|
||||
$('.newImageSection', page).show();
|
||||
$('.newImageForm', page).show();
|
||||
|
||||
} else {
|
||||
$('.newImageSection', page).show();
|
||||
$('#fldImage', page).hide().html('');
|
||||
$('#fldDeleteImage', page).hide();
|
||||
$('#btnDeleteImage', page).hide();
|
||||
$('#headerUploadNewImage', page).hide();
|
||||
}
|
||||
|
||||
|
@ -139,11 +140,11 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
function userImagePage() {
|
||||
function myProfilePage() {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.onSubmit = function () {
|
||||
self.onImageSubmit = function () {
|
||||
|
||||
var file = currentFile;
|
||||
|
||||
|
@ -164,29 +165,13 @@
|
|||
return false;
|
||||
};
|
||||
|
||||
self.deleteImage = function () {
|
||||
|
||||
Dashboard.confirm(Globalize.translate('DeleteImageConfirmation'), Globalize.translate('DeleteImage'), function (result) {
|
||||
|
||||
if (result) {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
var userId = getParameterByName("userId");
|
||||
|
||||
ApiClient.deleteUserImage(userId, "primary").done(processImageChangeResult);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
self.onFileUploadChange = function (fileUpload) {
|
||||
|
||||
setFiles($.mobile.activePage, fileUpload.files);
|
||||
};
|
||||
}
|
||||
|
||||
window.UserImagePage = new userImagePage();
|
||||
window.MyProfilePage = new myProfilePage();
|
||||
|
||||
$(document).on('pagebeforeshow', "#userImagePage", function () {
|
||||
|
||||
|
@ -201,7 +186,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
}).on('pageshow', "#userImagePage", function () {
|
||||
}).on('pageinit', "#userImagePage", function () {
|
||||
|
||||
var page = this;
|
||||
|
||||
|
@ -209,11 +194,21 @@
|
|||
|
||||
$("#userImageDropZone", page).on('dragover', onImageDragOver).on('drop', onImageDrop);
|
||||
|
||||
}).on('pagehide', "#userImagePage", function () {
|
||||
$('#btnDeleteImage', page).on('click', function () {
|
||||
|
||||
var page = this;
|
||||
Dashboard.confirm(Globalize.translate('DeleteImageConfirmation'), Globalize.translate('DeleteImage'), function (result) {
|
||||
|
||||
$("#userImageDropZone", page).off('dragover', onImageDragOver).off('drop', onImageDrop);
|
||||
if (result) {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
var userId = getParameterByName("userId");
|
||||
|
||||
ApiClient.deleteUserImage(userId, "primary").done(processImageChangeResult);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
(function ($, document, apiClient) {
|
||||
(function ($, document) {
|
||||
|
||||
function revoke(page, key) {
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
ApiClient.ajax({
|
||||
type: "DELETE",
|
||||
url: apiClient.getUrl('Auth/Keys/' + key)
|
||||
url: ApiClient.getUrl('Auth/Keys/' + key)
|
||||
|
||||
}).done(function () {
|
||||
|
||||
|
@ -125,7 +125,7 @@
|
|||
|
||||
ApiClient.ajax({
|
||||
type: "POST",
|
||||
url: apiClient.getUrl('Auth/Keys/', {
|
||||
url: ApiClient.getUrl('Auth/Keys/', {
|
||||
|
||||
App: $('#txtAppName', form).val()
|
||||
|
||||
|
@ -142,4 +142,4 @@
|
|||
}
|
||||
};
|
||||
|
||||
})(jQuery, document, ApiClient);
|
||||
})(jQuery, document);
|
|
@ -97,6 +97,32 @@ var Dashboard = {
|
|||
return store.getItem('token');
|
||||
},
|
||||
|
||||
serverAddress: function (val) {
|
||||
|
||||
if (val != null) {
|
||||
store.setItem('serverAddress', val);
|
||||
}
|
||||
|
||||
var address = store.getItem('serverAddress');
|
||||
|
||||
if (!address) {
|
||||
var loc = window.location;
|
||||
|
||||
address = loc.protocol + '//' + loc.hostname;
|
||||
|
||||
if (loc.port) {
|
||||
address += ':' + loc.port;
|
||||
}
|
||||
}
|
||||
|
||||
return address;
|
||||
},
|
||||
|
||||
changeServer: function () {
|
||||
|
||||
|
||||
},
|
||||
|
||||
getCurrentUserId: function () {
|
||||
|
||||
var autoLoginUserId = getParameterByName('u');
|
||||
|
@ -1165,7 +1191,7 @@ var Dashboard = {
|
|||
alert(Globalize.translate('MessageBrowserDoesNotSupportWebSockets'));
|
||||
}
|
||||
|
||||
window.ApiClient = MediaBrowser.ApiClient.create("Dashboard", window.dashboardVersion, MediaBrowser.ApiClient.generateDeviceName(), MediaBrowser.ApiClient.generateDeviceId());
|
||||
window.ApiClient = new MediaBrowser.ApiClient(Dashboard.serverAddress(), "Dashboard", window.dashboardVersion, MediaBrowser.ApiClient.generateDeviceName(), MediaBrowser.ApiClient.generateDeviceId());
|
||||
|
||||
$(ApiClient).on("websocketopen", Dashboard.onWebSocketOpened)
|
||||
.on("websocketmessage", Dashboard.onWebSocketMessageReceived);
|
||||
|
|
|
@ -260,6 +260,7 @@
|
|||
var page = this;
|
||||
|
||||
loadData(page);
|
||||
|
||||
});
|
||||
|
||||
})(jQuery, window, document);
|
|
@ -58,6 +58,8 @@
|
|||
|
||||
function loadUser(page, user, loggedInUser, mediaFolders, channels) {
|
||||
|
||||
$(page).trigger('userloaded', [user]);
|
||||
|
||||
Dashboard.setPageTitle(user.Name);
|
||||
|
||||
loadChannels(page, user, channels);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue