Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.special.block / components / BlockLog.vue
bloba0a9668bc11b9bebdd2f28276e378c0ef400d3fe
1 <template>
2         <cdx-accordion
3                 :class="`mw-block-log mw-block-log__type-${ blockLogType }`"
4                 :open="open || ( blockLogType === 'active' && !formVisible )"
5         >
6                 <template #title>
7                         {{ title }}
8                         <cdx-info-chip :icon="infoChipIcon" :status="infoChipStatus">
9                                 {{ logEntriesCount }}
10                         </cdx-info-chip>
11                 </template>
12                 <cdx-table
13                         :caption="title"
14                         :columns="!!logEntries.length ? columns : []"
15                         :data="logEntries"
16                         :use-row-headers="false"
17                         :hide-caption="true"
18                 >
19                         <template v-if="shouldShowAddBlockButton" #header>
20                                 <cdx-button
21                                         type="button"
22                                         action="destructive"
23                                         weight="primary"
24                                         class="mw-block-log__create-button"
25                                         @click="$emit( 'create-block' )"
26                                 >
27                                         <cdx-icon :icon="cdxIconCancel"></cdx-icon>
28                                         {{ $i18n( 'block-create' ).text() }}
29                                 </cdx-button>
30                         </template>
31                         <template #empty-state>
32                                 {{ emptyState }}
33                         </template>
34                         <template #item-modify="{ item }">
35                                 <a @click="$emit( 'edit-block', item )">
36                                         {{ $i18n( 'block-item-edit' ).text() }}
37                                 </a>
38                                 {{ $i18n( 'pipe-separator' ).text() }}
39                                 <a
40                                         :href="mw.util.getUrl( 'Special:Unblock/' + targetUser )"
41                                 >
42                                         {{ $i18n( 'block-item-remove' ).text() }}
43                                 </a>
44                         </template>
45                         <template #item-hide="{ item }">
46                                 <a
47                                         :href="mw.util.getUrl( 'Special:RevisionDelete', { type: 'logging', ['ids[' + item + ']']: 1 } )"
48                                 >
49                                         {{ $i18n( 'block-change-visibility' ).text() }}
50                                 </a>
51                         </template>
52                         <template #item-timestamp="{ item }">
53                                 <a
54                                         v-if="item.logid"
55                                         :href="mw.util.getUrl( 'Special:Log', { logid: item.logid } )"
56                                 >
57                                         {{ util.formatTimestamp( item.timestamp ) }}
58                                 </a>
59                                 <a
60                                         v-else-if="item.blockid"
61                                         :href="mw.util.getUrl( 'Special:BlockList', { wpTarget: `#${ item.blockid }` } )"
62                                 >
63                                         {{ util.formatTimestamp( item.timestamp ) }}
64                                 </a>
65                                 <span v-else>
66                                         {{ util.formatTimestamp( item.timestamp ) }}
67                                 </span>
68                         </template>
69                         <template v-if="blockLogType !== 'active'" #item-type="{ item }">
70                                 {{ util.getBlockActionMessage( item ) }}
71                         </template>
72                         <template v-if="blockLogType === 'active'" #item-target="{ item }">
73                                 <!-- eslint-disable-next-line vue/no-v-html -->
74                                 <span v-html="$i18n( 'userlink-with-contribs', item ).parse()"></span>
75                         </template>
76                         <template #item-expiry="{ item }">
77                                 <span v-if="item.expires || item.duration">
78                                         {{ util.formatTimestamp( item.expires || item.duration ) }}
79                                 </span>
80                                 <span v-else></span>
81                         </template>
82                         <template #item-blockedby="{ item }">
83                                 <!-- eslint-disable-next-line vue/no-v-html -->
84                                 <span v-html="$i18n( 'userlink-with-contribs', item ).parse()"></span>
85                         </template>
86                         <template #item-parameters="{ item }">
87                                 <ul v-if="item && item.length">
88                                         <li v-for="( parameter, index ) in item" :key="index">
89                                                 {{ util.getBlockFlagMessage( parameter ) }}
90                                         </li>
91                                 </ul>
92                                 <span v-else></span>
93                         </template>
94                         <template #item-reason="{ item }">
95                                 {{ item }}
96                         </template>
97                 </cdx-table>
98                 <div v-if="moreBlocks" class="mw-block-log-fulllog">
99                         <a
100                                 :href="mw.util.getUrl( 'Special:Log', { page: 'User:' + targetUser, type: blockLogType === 'suppress' ? 'suppress' : 'block' } )"
101                         >
102                                 {{ $i18n( 'log-fulllog' ).text() }}
103                         </a>
104                 </div>
105         </cdx-accordion>
106 </template>
108 <script>
109 const util = require( '../util.js' );
110 const { computed, defineComponent, ref, watch } = require( 'vue' );
111 const { CdxAccordion, CdxTable, CdxButton, CdxIcon, CdxInfoChip } = require( '@wikimedia/codex' );
112 const { storeToRefs } = require( 'pinia' );
113 const useBlockStore = require( '../stores/block.js' );
114 const { cdxIconCancel, cdxIconClock, cdxIconAlert } = require( '../icons.json' );
116 module.exports = exports = defineComponent( {
117         name: 'BlockLog',
118         components: { CdxAccordion, CdxTable, CdxButton, CdxIcon, CdxInfoChip },
119         props: {
120                 open: {
121                         type: Boolean,
122                         default: false
123                 },
124                 blockLogType: {
125                         type: String,
126                         default: 'recent'
127                 },
128                 canDeleteLogEntry: {
129                         type: Boolean,
130                         default: false
131                 }
132         },
133         emits: [
134                 'create-block',
135                 'edit-block'
136         ],
137         setup( props ) {
138                 const store = useBlockStore();
139                 const { alreadyBlocked, formVisible, targetUser } = storeToRefs( store );
140                 let title = mw.message( 'block-user-previous-blocks' ).text();
141                 let emptyState = mw.message( 'block-user-no-previous-blocks' ).text();
142                 if ( props.blockLogType === 'active' ) {
143                         title = mw.message( 'block-user-active-blocks' ).text();
144                         emptyState = mw.message( 'block-user-no-active-blocks' ).text();
145                 } else if ( props.blockLogType === 'suppress' ) {
146                         title = mw.message( 'block-user-suppressed-blocks' ).text();
147                         emptyState = mw.message( 'block-user-no-suppressed-blocks' ).text();
148                 }
150                 const columns = [
151                         ...( props.blockLogType === 'active' || props.canDeleteLogEntry ?
152                                 [ { id: props.blockLogType === 'active' ? 'modify' : 'hide', label: '', minWidth: '100px' } ] : [] ),
153                         { id: 'timestamp', label: mw.message( 'blocklist-timestamp' ).text(), minWidth: '112px' },
154                         props.blockLogType === 'recent' || props.blockLogType === 'suppress' ?
155                                 { id: 'type', label: mw.message( 'blocklist-type-header' ).text(), minWidth: '112px' } :
156                                 { id: 'target', label: mw.message( 'blocklist-target' ).text(), minWidth: '200px' },
157                         { id: 'expiry', label: mw.message( 'blocklist-expiry' ).text(), minWidth: '112px' },
158                         { id: 'blockedby', label: mw.message( 'blocklist-by' ).text(), minWidth: '200px' },
159                         { id: 'parameters', label: mw.message( 'blocklist-params' ).text(), minWidth: '160px' },
160                         { id: 'reason', label: mw.message( 'blocklist-reason' ).text(), minWidth: '160px' }
161                 ];
163                 const logEntries = ref( [] );
164                 const moreBlocks = ref( false );
165                 const FETCH_LIMIT = 10;
167                 const logEntriesCount = computed( () => {
168                         if ( moreBlocks.value ) {
169                                 return mw.msg(
170                                         'block-user-label-count-exceeds-limit',
171                                         mw.language.convertNumber( FETCH_LIMIT )
172                                 );
173                         }
174                         return mw.language.convertNumber( logEntries.value.length );
175                 } );
177                 const infoChipIcon = computed( () => props.blockLogType === 'active' ? cdxIconAlert : cdxIconClock );
178                 const infoChipStatus = computed( () => logEntries.value.length > 0 && props.blockLogType === 'active' ? 'warning' : 'notice' );
180                 /**
181                  * Construct the data object needed for a template row, from a logentry API response.
182                  *
183                  * @param {Array} logevents
184                  * @return {Array}
185                  */
186                 function logentriesToRows( logevents ) {
187                         const rows = [];
188                         for ( let i = 0; i < logevents.length; i++ ) {
189                                 const logevent = logevents[ i ];
190                                 rows.push( {
191                                         timestamp: {
192                                                 timestamp: logevent.timestamp,
193                                                 logid: logevent.logid
194                                         },
195                                         type: logevent.action,
196                                         expiry: {
197                                                 expires: logevent.params.expiry,
198                                                 duration: logevent.params.duration,
199                                                 type: logevent.action
200                                         },
201                                         blockedby: logevent.user,
202                                         parameters: logevent.params.flags,
203                                         reason: logevent.comment,
204                                         hide: logevent.logid
205                                 } );
206                         }
207                         return rows;
208                 }
210                 watch( targetUser, ( newValue ) => {
211                         if ( newValue ) {
212                                 store.getBlockLogData( props.blockLogType ).then( ( responses ) => {
213                                         let newData = [];
214                                         const data = responses[ 0 ].query;
216                                         if ( props.blockLogType === 'recent' ) {
217                                                 // List of recent block entries.
218                                                 newData = logentriesToRows( data.logevents );
219                                                 moreBlocks.value = newData.length >= FETCH_LIMIT;
221                                         } else if ( props.blockLogType === 'suppress' ) {
222                                                 // List of suppress/block or suppress/reblock log entries.
223                                                 newData.push( ...logentriesToRows( data.logevents ) );
224                                                 newData.push( ...logentriesToRows( responses[ 1 ].query.logevents ) );
225                                                 newData.sort( ( a, b ) => b.timestamp.logid - a.timestamp.logid );
226                                                 moreBlocks.value = newData.length >= FETCH_LIMIT;
227                                                 // Re-apply limit, as each may have been longer.
228                                                 newData = newData.slice( 0, FETCH_LIMIT );
230                                         } else {
231                                                 // List of active blocks.
232                                                 for ( let i = 0; i < data.blocks.length; i++ ) {
233                                                         newData.push( {
234                                                                 // Store the entire API response, for passing in when editing the block.
235                                                                 modify: data.blocks[ i ],
236                                                                 timestamp: {
237                                                                         timestamp: data.blocks[ i ].timestamp,
238                                                                         blockid: data.blocks[ i ].id
239                                                                 },
240                                                                 target: data.blocks[ i ].user,
241                                                                 expiry: {
242                                                                         expires: data.blocks[ i ].expiry,
243                                                                         duration: mw.util.isInfinity( data.blocks[ i ].expiry ) ? 'infinity' : null
244                                                                 },
245                                                                 blockedby: data.blocks[ i ].by,
246                                                                 parameters:
247                                                                         [
248                                                                                 data.blocks[ i ].anononly ? 'anononly' : null,
249                                                                                 data.blocks[ i ].nocreate ? 'nocreate' : null,
250                                                                                 data.blocks[ i ].autoblock ? null : 'noautoblock',
251                                                                                 data.blocks[ i ].noemail ? 'noemail' : null,
252                                                                                 data.blocks[ i ].allowusertalk ? null : 'nousertalk',
253                                                                                 data.blocks[ i ].hidden ? 'hiddenname' : null
254                                                                         ].filter( ( e ) => e !== null ),
255                                                                 reason: data.blocks[ i ].reason
256                                                         } );
257                                                 }
258                                         }
260                                         logEntries.value = newData;
261                                 } );
262                         } else {
263                                 moreBlocks.value = false;
264                                 logEntries.value = [];
265                         }
266                 }, { immediate: true } );
268                 // Show the 'Add block' button in the active blocks accordion if:
269                 // * Multiblocks is enabled
270                 // * Multiblocks is disabled and the user is not already blocked
271                 const shouldShowAddBlockButton = computed(
272                         () => props.blockLogType === 'active' && (
273                                 store.enableMultiblocks || !alreadyBlocked.value
274                         )
275                 );
277                 return {
278                         mw,
279                         util,
280                         title,
281                         emptyState,
282                         columns,
283                         cdxIconCancel,
284                         logEntries,
285                         moreBlocks,
286                         targetUser,
287                         logEntriesCount,
288                         infoChipIcon,
289                         infoChipStatus,
290                         formVisible,
291                         shouldShowAddBlockButton
292                 };
293         }
294 } );
295 </script>
297 <style lang="less">
298 @import 'mediawiki.skin.variables.less';
300 .mw-block-log {
301         word-break: auto-phrase;
303         // Align the new-block button to the left, because there's no table caption.
304         .cdx-table__header {
305                 justify-content: flex-start;
306         }
308         table ul {
309                 margin: 0;
310         }
313 .mw-block-log-fulllog {
314         margin-top: @spacing-50;
317 .mw-block-active-blocks__menu {
318         text-align: center;
320 </style>