@saooti/octopus-sdk 41.7.2 → 41.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +2 -1
- package/CHANGELOG.md +28 -0
- package/index.ts +6 -2
- package/package.json +1 -1
- package/src/api/emissionApi.ts +44 -1
- package/src/api/podcastApi.ts +27 -5
- package/src/components/composable/route/useAdvancedParamInit.ts +4 -3
- package/src/components/composable/useRights.ts +196 -0
- package/src/components/composable/useSeasonsManagement.ts +43 -0
- package/src/components/display/live/RadioPlanning.vue +6 -4
- package/src/components/display/podcasts/PodcastFilterList.vue +100 -18
- package/src/components/display/podcasts/PodcastInlineListTemplate.vue +4 -1
- package/src/components/display/podcasts/PodcastList.vue +5 -1
- package/src/components/display/podcasts/PodcastModuleBox.vue +21 -4
- package/src/components/display/podcasts/PodcastSwiperList.vue +9 -3
- package/src/components/form/ClassicRadio.vue +13 -14
- package/src/components/misc/TopBar.vue +7 -1
- package/src/components/pages/EmissionPage.vue +22 -10
- package/src/locale/de.json +7 -1
- package/src/locale/en.json +7 -1
- package/src/locale/es.json +7 -1
- package/src/locale/fr.json +7 -1
- package/src/locale/it.json +7 -1
- package/src/locale/sl.json +7 -1
- package/src/stores/class/general/emission.ts +20 -0
- package/src/stores/class/general/podcast.ts +17 -0
- package/src/style/general.scss +7 -0
- package/tests/api/podcastApi.spec.ts +43 -0
- package/tests/components/composable/useAdvancedParamInit.spec.ts +90 -0
- package/tests/components/composable/useRights.spec.ts +265 -0
- package/tests/components/composable/useSeasonsManagement.spec.ts +35 -0
- package/tests/components/display/podcasts/PodcastFilterList.spec.ts +33 -0
- package/tests/components/display/podcasts/PodcastInlineListTemplate.spec.ts +23 -0
- package/tests/components/display/podcasts/PodcastModuleBox.spec.ts +49 -22
- package/tests/components/pages/EmissionPage.spec.ts +86 -0
- package/tests/utils.ts +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 41.8.0 (18/03/2026)
|
|
4
|
+
|
|
5
|
+
**Features**
|
|
6
|
+
|
|
7
|
+
- **14083** - Ajout des fonctionnalités de saisons et types d'épisodes
|
|
8
|
+
- Ajout des propriétés relatives aux saisons sur les émissions et les épisodes
|
|
9
|
+
- `PodcastFilterList` permet de regrouper les épisodes en saisons
|
|
10
|
+
- `EmissionPage` affiche ses épisodes en saisons si définies
|
|
11
|
+
- `PodcastModuleBox` affiche le numéro de saison et d'épisode du podcast si
|
|
12
|
+
définis et que le `seasonMode` permet leur affichage
|
|
13
|
+
- Ajout d'une classe `required` pour afficher une asterisque sur les champs
|
|
14
|
+
requis
|
|
15
|
+
|
|
16
|
+
**Fix**
|
|
17
|
+
|
|
18
|
+
- **14291** - Activation du bouton de génération de la transcription suite à
|
|
19
|
+
correction des droits
|
|
20
|
+
|
|
21
|
+
## 41.7.3 (11/03/2026)
|
|
22
|
+
|
|
23
|
+
**Features**
|
|
24
|
+
|
|
25
|
+
- Intégration du composable de vérification des droits
|
|
26
|
+
|
|
27
|
+
**Fix**
|
|
28
|
+
|
|
29
|
+
- Correction affichage épisodes à valider pour `PODCAST_VALIDATION`.
|
|
30
|
+
|
|
3
31
|
## 41.7.2 (10/03/2026)
|
|
4
32
|
|
|
5
33
|
**Fix**
|
package/index.ts
CHANGED
|
@@ -118,6 +118,7 @@ export const getClassicTagInput = () => import("./src/components/form/ClassicTag
|
|
|
118
118
|
export const getClassicWysiwyg = () => import("./src/components/form/ClassicWysiwyg.vue");
|
|
119
119
|
|
|
120
120
|
//Composable
|
|
121
|
+
import { useRights, EditRight } from "./src/components/composable/useRights.ts";
|
|
121
122
|
import {useResizePhone} from "./src/components/composable/useResizePhone";
|
|
122
123
|
import {useTagOf} from "./src/components/composable/useTagOf.ts";
|
|
123
124
|
import {useSelenium} from "./src/components/composable/useSelenium.ts";
|
|
@@ -133,6 +134,7 @@ export { useSharePlatforms, SharePlatformName, type SharePlatform } from "./src/
|
|
|
133
134
|
export { useSharePath } from "./src/components/composable/share/useSharePath.ts";
|
|
134
135
|
export { useOrgaComputed } from "./src/components/composable/useOrgaComputed.ts";
|
|
135
136
|
export { useSeoTitleUrl } from "./src/components/composable/route/useSeoTitleUrl.ts";
|
|
137
|
+
export { useSeasonsManagement } from "./src/components/composable/useSeasonsManagement.ts";
|
|
136
138
|
|
|
137
139
|
//helper
|
|
138
140
|
import domHelper from "./src/helper/domHelper.ts";
|
|
@@ -165,8 +167,8 @@ export { playlistApi } from "./src/api/playlistApi.ts";
|
|
|
165
167
|
export { podcastApi, PodcastSort, type PodcastSearchOptions } from "./src/api/podcastApi.ts";
|
|
166
168
|
|
|
167
169
|
// Types
|
|
168
|
-
export { type Emission, emptyEmissionData } from "./src/stores/class/general/emission.ts";
|
|
169
|
-
export { type Podcast, type PodcastAvailability } from "./src/stores/class/general/podcast.ts";
|
|
170
|
+
export { type Emission, SeasonMode, emptyEmissionData } from "./src/stores/class/general/emission.ts";
|
|
171
|
+
export { type Podcast, type PodcastAvailability, PodcastType } from "./src/stores/class/general/podcast.ts";
|
|
170
172
|
export { type Playlist, type PlaylistRule } from "./src/stores/class/general/playlist.ts";
|
|
171
173
|
export { type Annotations } from "./src/stores/class/general";
|
|
172
174
|
|
|
@@ -202,6 +204,8 @@ import { ROUTE_PARAMS } from "./src/components/composable/route/types";
|
|
|
202
204
|
import { defineAsyncComponent } from "vue";
|
|
203
205
|
|
|
204
206
|
export {
|
|
207
|
+
useRights,
|
|
208
|
+
EditRight,
|
|
205
209
|
useResizePhone,
|
|
206
210
|
useTagOf,
|
|
207
211
|
useSelenium,
|
package/package.json
CHANGED
package/src/api/emissionApi.ts
CHANGED
|
@@ -2,6 +2,7 @@ import classicApi from "./classicApi";
|
|
|
2
2
|
import { ModuleApi } from "./apiConnection";
|
|
3
3
|
import { mapFromGetAll } from "./apiUtils";
|
|
4
4
|
import { Emission } from "@/stores/class/general/emission";
|
|
5
|
+
import { ListClassicReturn } from "@/stores/class/general/listReturn";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Retrieve an emission by ID
|
|
@@ -25,7 +26,49 @@ async function getAllById(emissionIds: Array<number>): Promise<Record<string, Em
|
|
|
25
26
|
return mapFromGetAll(emissionIds, get, 'emissionId');
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Search for emissions
|
|
32
|
+
* @param query A filter on the name of the emission
|
|
33
|
+
* @param options Optional options to filter the results
|
|
34
|
+
*/
|
|
35
|
+
async function search(query?: string, options?: {
|
|
36
|
+
/** The index of the first element to retrieve */
|
|
37
|
+
first?: number;
|
|
38
|
+
/** The number of elements to retrieve */
|
|
39
|
+
size?: number;
|
|
40
|
+
specialTreatment?: boolean;
|
|
41
|
+
distributedBy?: string;
|
|
42
|
+
/** Filter by organisation */
|
|
43
|
+
organisationId?: string|string[];
|
|
44
|
+
}): Promise<ListClassicReturn<Emission>> {
|
|
45
|
+
return classicApi.fetchData<ListClassicReturn<Emission>>({
|
|
46
|
+
api: ModuleApi.DEFAULT,
|
|
47
|
+
path: "emission/search",
|
|
48
|
+
parameters: {
|
|
49
|
+
query,
|
|
50
|
+
first: options?.first ?? 0,
|
|
51
|
+
size: options?.size ?? 10,
|
|
52
|
+
distributedBy: options?.distributedBy,
|
|
53
|
+
organisationId: options?.organisationId
|
|
54
|
+
},
|
|
55
|
+
specialTreatement: options?.specialTreatment ?? true
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Remove seasons data from emission
|
|
61
|
+
* @param emissionId The ID of the emission for which to remove the data
|
|
62
|
+
*/
|
|
63
|
+
async function resetSeasons(emissionId: number): Promise<void> {
|
|
64
|
+
return classicApi.putData({
|
|
65
|
+
api: ModuleApi.DEFAULT,
|
|
66
|
+
path: 'emission/seasons/reset/' + emissionId
|
|
67
|
+
});
|
|
68
|
+
}
|
|
28
69
|
export const emissionApi = {
|
|
29
70
|
get,
|
|
30
|
-
getAllById
|
|
71
|
+
getAllById,
|
|
72
|
+
search,
|
|
73
|
+
resetSeasons
|
|
31
74
|
};
|
package/src/api/podcastApi.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Podcast, PodcastProcessingStatus, SimplifiedPodcast } from '../stores/class/general/podcast';
|
|
1
|
+
import { Podcast, PodcastProcessingStatus, PodcastType, SimplifiedPodcast } from '../stores/class/general/podcast';
|
|
2
2
|
import { ListClassicReturn } from '../stores/class/general/listReturn';
|
|
3
3
|
import { useAuthStore } from '../stores/AuthStore';
|
|
4
4
|
import classicApi from './classicApi';
|
|
@@ -17,7 +17,9 @@ export enum PodcastSort {
|
|
|
17
17
|
POPULARITY = 'POPULARITY',
|
|
18
18
|
SCORE = 'SCORE',
|
|
19
19
|
UPDATE_ASC = 'UPDATE_ASC',
|
|
20
|
-
UPDATE_DESC = 'UPDATE_DESC'
|
|
20
|
+
UPDATE_DESC = 'UPDATE_DESC',
|
|
21
|
+
/** Smart sort using seasons settings */
|
|
22
|
+
SEASONAL = 'SEASONAL'
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export enum PodcastMonetisation {
|
|
@@ -68,11 +70,15 @@ export interface PodcastSearchOptions extends Paginable<PodcastSort> {
|
|
|
68
70
|
tags?: Array<string>;
|
|
69
71
|
/** Filter by beneficiaries/rights holder reference */
|
|
70
72
|
beneficiaries?: Array<string>;
|
|
73
|
+
/** Filter by seasons */
|
|
74
|
+
season?: Array<number>;
|
|
75
|
+
/** Filter by season episode number */
|
|
76
|
+
seasonEpisode?: Array<number>;
|
|
77
|
+
/** Filter by episode type */
|
|
78
|
+
episodeType?: PodcastType;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
async function downloadRegister(podcastId: number, parameters?: Record<string,unknown>): Promise<{ location: string; downloadId: number }> {
|
|
74
|
-
const authStore = useAuthStore();
|
|
75
|
-
|
|
76
82
|
return classicApi.fetchData<{
|
|
77
83
|
location: string;
|
|
78
84
|
downloadId: number;
|
|
@@ -137,7 +143,7 @@ function processSearchParameters(search: PodcastSearchOptions): FetchParam {
|
|
|
137
143
|
* @param adaptParameters If true, some adjustments will be made to the parameters
|
|
138
144
|
* @return A list of simplified podcasts
|
|
139
145
|
*/
|
|
140
|
-
function search(options: PodcastSearchOptions, adaptParameters?: boolean): Promise<ListClassicReturn<SimplifiedPodcast>> {
|
|
146
|
+
async function search(options: PodcastSearchOptions, adaptParameters?: boolean): Promise<ListClassicReturn<SimplifiedPodcast>> {
|
|
141
147
|
return classicApi.fetchData<ListClassicReturn<SimplifiedPodcast>>({
|
|
142
148
|
api: ModuleApi.DEFAULT,
|
|
143
149
|
path: 'v2/podcast/search',
|
|
@@ -146,6 +152,21 @@ function search(options: PodcastSearchOptions, adaptParameters?: boolean): Promi
|
|
|
146
152
|
});
|
|
147
153
|
}
|
|
148
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Count podcasts matching the filters
|
|
157
|
+
* @param options The search criterias
|
|
158
|
+
* @param adaptParameters If true, some adjustments will be made to the parameters
|
|
159
|
+
* @return A list of simplified podcasts
|
|
160
|
+
*/
|
|
161
|
+
async function count(options: PodcastSearchOptions, adaptParameters?: boolean): Promise<number> {
|
|
162
|
+
const result = await search({
|
|
163
|
+
...options,
|
|
164
|
+
size: 0
|
|
165
|
+
}, adaptParameters);
|
|
166
|
+
|
|
167
|
+
return result.count;
|
|
168
|
+
}
|
|
169
|
+
|
|
149
170
|
/**
|
|
150
171
|
* Retrieve the podcasts matching the search criterias, with all their data.
|
|
151
172
|
* This query is longer, because it also needs to retrieve organisations &
|
|
@@ -185,6 +206,7 @@ async function searchFull(options:PodcastSearchOptions, adaptParameters?: boolea
|
|
|
185
206
|
}
|
|
186
207
|
|
|
187
208
|
export const podcastApi = {
|
|
209
|
+
count,
|
|
188
210
|
downloadRegister,
|
|
189
211
|
get,
|
|
190
212
|
search,
|
|
@@ -9,6 +9,7 @@ import dayjs from "dayjs";
|
|
|
9
9
|
|
|
10
10
|
import { RouteProps } from "./types";
|
|
11
11
|
import { EmissionGroup, groupsApi } from "../../../api/groupsApi";
|
|
12
|
+
import { useRights } from "../useRights";
|
|
12
13
|
|
|
13
14
|
export const useAdvancedParamInit = (props: RouteProps, isEmission: boolean) => {
|
|
14
15
|
|
|
@@ -18,7 +19,7 @@ export const useAdvancedParamInit = (props: RouteProps, isEmission: boolean) =>
|
|
|
18
19
|
|
|
19
20
|
const filterStore = useFilterStore();
|
|
20
21
|
const authStore = useAuthStore();
|
|
21
|
-
|
|
22
|
+
const { canValidatePodcast } = useRights();
|
|
22
23
|
|
|
23
24
|
const isInit = ref(false);
|
|
24
25
|
const monetisable = ref("UNDEFINED");// UNDEFINED, YES, NO
|
|
@@ -125,8 +126,8 @@ export const useAdvancedParamInit = (props: RouteProps, isEmission: boolean) =>
|
|
|
125
126
|
includeHidden.value = undefined !== organisation.value && organisationRight.value && "false"!==props.routeIncludeHidden;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
function initValidity(){
|
|
129
|
-
const cantDisplay = isPodcastmaker.value || isEmission || !includeHidden.value || !
|
|
129
|
+
function initValidity() {
|
|
130
|
+
const cantDisplay = isPodcastmaker.value || isEmission || !includeHidden.value || !canValidatePodcast();
|
|
130
131
|
if(cantDisplay){
|
|
131
132
|
validity.value = "true";
|
|
132
133
|
}else{
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { useAuthStore } from "../../stores/AuthStore";
|
|
2
|
+
import type { Emission } from "../../stores/class/general/emission";
|
|
3
|
+
import type { Podcast } from "../../stores/class/general/podcast";
|
|
4
|
+
|
|
5
|
+
type Role =
|
|
6
|
+
'ADMIN'|'ORGANISATION'|
|
|
7
|
+
'PRODUCTION'|'RESTRICTED_PRODUCTION'|'PODCAST_CRUD'|'PODCAST_VALIDATION'|
|
|
8
|
+
'PLAYLISTS'|'RESTRICTED_ANIMATION';
|
|
9
|
+
|
|
10
|
+
export enum EditRight {
|
|
11
|
+
None, // User cannot edit
|
|
12
|
+
Restricted, // User cannot edit because element is used elsewhere
|
|
13
|
+
Full // User can edit
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Composable to manage rights.
|
|
17
|
+
* Based on AuthStore, but converts roles to easily usable tests for various
|
|
18
|
+
* actions.
|
|
19
|
+
*/
|
|
20
|
+
export const useRights = () => {
|
|
21
|
+
const authStore = useAuthStore();
|
|
22
|
+
|
|
23
|
+
function roleContainsAny(...roles: Role[]): boolean {
|
|
24
|
+
return (authStore.authRole as Role[]).findIndex((r: Role) => roles.includes(r)) > -1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Creation is limited by roles
|
|
28
|
+
function canCreateEmission(): boolean {
|
|
29
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'RESTRICTED_PRODUCTION');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function canEditEmission(emission: Emission): boolean {
|
|
33
|
+
if (
|
|
34
|
+
// Can edit new emissions
|
|
35
|
+
(!emission.emissionId && canCreateEmission()) ||
|
|
36
|
+
// Can edit when with sufficient rights
|
|
37
|
+
roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION')
|
|
38
|
+
) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Can only edit if has created the emission
|
|
43
|
+
return (roleContainsAny('RESTRICTED_PRODUCTION') && emission.createdByUserId === authStore.authProfile?.userId);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function canDeleteEmission(): boolean {
|
|
47
|
+
// In case of restricted production, it will only delete podcasts
|
|
48
|
+
// created by user, and delete the emission only if empty afterwards
|
|
49
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'RESTRICTED_PRODUCTION');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function canEditCommentsConfigEmission(): boolean {
|
|
53
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function canCreatePodcast(): boolean {
|
|
57
|
+
// All roles that can create podcasts
|
|
58
|
+
return roleContainsAny(
|
|
59
|
+
'ADMIN',
|
|
60
|
+
'ORGANISATION',
|
|
61
|
+
'PRODUCTION',
|
|
62
|
+
'PODCAST_CRUD',
|
|
63
|
+
'RESTRICTED_PRODUCTION',
|
|
64
|
+
'RESTRICTED_ANIMATION'
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function canDuplicatePodcast(): boolean {
|
|
69
|
+
// Same as creationm but notably without PODCAST_CRUD and
|
|
70
|
+
// RESTRICTED_ANIMATION
|
|
71
|
+
return roleContainsAny(
|
|
72
|
+
'ADMIN',
|
|
73
|
+
'ORGANISATION',
|
|
74
|
+
'PRODUCTION',
|
|
75
|
+
'RESTRICTED_PRODUCTION',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function canEditPodcast(podcast: Podcast): boolean {
|
|
80
|
+
// Full rights users can edit any podcast
|
|
81
|
+
if (roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION')) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// RESTRICTED users can only edit their own podcasts
|
|
86
|
+
if (roleContainsAny('RESTRICTED_PRODUCTION', 'RESTRICTED_ANIMATION')) {
|
|
87
|
+
return podcast.createdByUserId === authStore.authProfile?.userId;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// PODCAST_CRUD can only edit their own non-valid podcasts
|
|
91
|
+
if (roleContainsAny('PODCAST_CRUD')) {
|
|
92
|
+
return podcast.valid === false &&
|
|
93
|
+
podcast.publisher?.userId === authStore.authProfile?.userId;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function canDeletePodcast(podcast: Podcast): boolean {
|
|
100
|
+
// Same permissions as editing
|
|
101
|
+
return canEditPodcast(podcast);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function canValidatePodcast(): boolean {
|
|
105
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'PODCAST_VALIDATION');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function canEditCommentsConfigPodcast(): boolean {
|
|
109
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function canCreatePlaylist(): boolean {
|
|
113
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PLAYLISTS');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function canEditPlaylist(): boolean {
|
|
117
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PLAYLISTS');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function canDeletePlaylist(): boolean {
|
|
121
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PLAYLISTS');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function canCreateParticipant(): boolean {
|
|
125
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'RESTRICTED_PRODUCTION');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function getParticipantEditRight(participantId: number|undefined): Promise<EditRight> {
|
|
129
|
+
// New participants can be edited, and also with sufficient rights
|
|
130
|
+
if(!participantId || roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'RESTRICTED_PRODUCTION')) {
|
|
131
|
+
return EditRight.Full;
|
|
132
|
+
} else {
|
|
133
|
+
return EditRight.None;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function canEditParticipant(participantId: number|undefined): Promise<boolean> {
|
|
138
|
+
const editRight = await getParticipantEditRight(participantId);
|
|
139
|
+
return editRight === EditRight.Full;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async function canDeleteParticipant(participantId: number|undefined): Promise<boolean> {
|
|
144
|
+
const editRight = await getParticipantEditRight(participantId);
|
|
145
|
+
return editRight === EditRight.Full;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function canEditCodeInsertPlayer(): boolean {
|
|
149
|
+
return roleContainsAny('ADMIN', 'ORGANISATION');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function canEditTranscript(): boolean {
|
|
153
|
+
return roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION', 'RESTRICTED_PRODUCTION');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function canSeeHistory(): boolean {
|
|
157
|
+
return roleContainsAny('ADMIN', 'ORGANISATION');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function isRestrictedProduction(): boolean {
|
|
161
|
+
return roleContainsAny('RESTRICTED_PRODUCTION') && !roleContainsAny('ADMIN', 'ORGANISATION', 'PRODUCTION');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
// Emissions
|
|
166
|
+
canCreateEmission,
|
|
167
|
+
canEditEmission,
|
|
168
|
+
canDeleteEmission,
|
|
169
|
+
canEditCommentsConfigEmission,
|
|
170
|
+
|
|
171
|
+
// Podcasts
|
|
172
|
+
canCreatePodcast,
|
|
173
|
+
canDuplicatePodcast,
|
|
174
|
+
canEditPodcast,
|
|
175
|
+
canDeletePodcast,
|
|
176
|
+
canValidatePodcast,
|
|
177
|
+
canEditCommentsConfigPodcast,
|
|
178
|
+
|
|
179
|
+
// Playlists
|
|
180
|
+
canCreatePlaylist,
|
|
181
|
+
canEditPlaylist,
|
|
182
|
+
canDeletePlaylist,
|
|
183
|
+
|
|
184
|
+
// Participants
|
|
185
|
+
canCreateParticipant,
|
|
186
|
+
getParticipantEditRight,
|
|
187
|
+
canEditParticipant,
|
|
188
|
+
canDeleteParticipant,
|
|
189
|
+
|
|
190
|
+
// Other
|
|
191
|
+
canEditCodeInsertPlayer,
|
|
192
|
+
canEditTranscript,
|
|
193
|
+
canSeeHistory,
|
|
194
|
+
isRestrictedProduction
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Podcast } from "@/stores/class/general/podcast";
|
|
2
|
+
import { Emission, SeasonMode } from "../../stores/class/general/emission";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Composable to facilitate seasons operations
|
|
6
|
+
*/
|
|
7
|
+
export const useSeasonsManagement = () => {
|
|
8
|
+
/**
|
|
9
|
+
* Indicates that seasons are enabled on the given emission
|
|
10
|
+
* @param emission The emission to check for seasons
|
|
11
|
+
* @returns True if seasons are enabled, false otherwise
|
|
12
|
+
*/
|
|
13
|
+
function areSeasonsEnabled(emission: Emission): boolean {
|
|
14
|
+
return [
|
|
15
|
+
SeasonMode.SEASON_WITHOUT_PODCAST_NUMBERING,
|
|
16
|
+
SeasonMode.SEASON_WITH_PODCAST_NUMBERING
|
|
17
|
+
].includes(emission.seasonMode);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Simple formatter to display season/episode of the given podcast
|
|
22
|
+
* @param podcast The podcast to check
|
|
23
|
+
* @returns A string describing the season/episode of the podcast or null
|
|
24
|
+
* if no seasons are defined
|
|
25
|
+
*/
|
|
26
|
+
function formatSeason(podcast: Podcast): string|null {
|
|
27
|
+
switch (podcast.emission.seasonMode) {
|
|
28
|
+
case SeasonMode.NO_SEASON:
|
|
29
|
+
return null;
|
|
30
|
+
|
|
31
|
+
case SeasonMode.SEASON_WITHOUT_PODCAST_NUMBERING:
|
|
32
|
+
return `S${podcast.seasonNumber}`;
|
|
33
|
+
|
|
34
|
+
case SeasonMode.SEASON_WITH_PODCAST_NUMBERING:
|
|
35
|
+
return `S${podcast.seasonNumber}·E${podcast.seasonEpisodeNumber}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
areSeasonsEnabled,
|
|
41
|
+
formatSeason
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -254,9 +254,7 @@ function createArrayDays() {
|
|
|
254
254
|
});
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
|
-
async function fetchOccurrencesAndLives(): Promise<
|
|
258
|
-
Array<PlanningOccurrence | PlanningLive>
|
|
259
|
-
> {
|
|
257
|
+
async function fetchOccurrencesAndLives(): Promise<Array<PlanningOccurrence|PlanningLive>> {
|
|
260
258
|
const params = {
|
|
261
259
|
canalId: props.radio?.id,
|
|
262
260
|
from:startOfDay.value,
|
|
@@ -275,6 +273,7 @@ async function fetchOccurrencesAndLives(): Promise<
|
|
|
275
273
|
path: "live/list",
|
|
276
274
|
parameters: params,
|
|
277
275
|
});
|
|
276
|
+
|
|
278
277
|
if (lives.length) {
|
|
279
278
|
occurrences = occurrences.concat(lives);
|
|
280
279
|
occurrences.sort((a, b) => {
|
|
@@ -286,6 +285,7 @@ async function fetchOccurrencesAndLives(): Promise<
|
|
|
286
285
|
}
|
|
287
286
|
return occurrences;
|
|
288
287
|
}
|
|
288
|
+
|
|
289
289
|
async function fetchOccurrences(): Promise<void> {
|
|
290
290
|
if (planning.value[daySelected.value]) {
|
|
291
291
|
return;
|
|
@@ -310,6 +310,7 @@ async function fetchOccurrences(): Promise<void> {
|
|
|
310
310
|
) {
|
|
311
311
|
periodDayIndex += 1;
|
|
312
312
|
}
|
|
313
|
+
|
|
313
314
|
switch (periodOfDay.value[periodDayIndex].id) {
|
|
314
315
|
case "morning":
|
|
315
316
|
planning.value[daySelected.value].morning.push(occ);
|
|
@@ -325,8 +326,9 @@ async function fetchOccurrences(): Promise<void> {
|
|
|
325
326
|
}
|
|
326
327
|
planningLength.value[daySelected.value] += 1;
|
|
327
328
|
}
|
|
328
|
-
} catch {
|
|
329
|
+
} catch(e) {
|
|
329
330
|
error.value = true;
|
|
331
|
+
console.error(e);
|
|
330
332
|
}
|
|
331
333
|
loading.value = false;
|
|
332
334
|
}
|