Jump to content

User:DannyS712/VueNPP.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by DannyS712 (talk | contribs) at 04:40, 26 May 2022 (menu to choose by page id). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <nowiki>
// Script to experiment with a Vue version of Special:NewPagesFeed
// @author DannyS712
/* jshint maxerr: 999 */
$(() => {
const VueNPP = {};
window.VueNPP = VueNPP;

VueNPP.init = function () {
	mw.loader.using(
		[
			'vue',
			'wvui',
			'mediawiki.util',
			'mediawiki.api',
			'es6-polyfills',
			'moment',
			// So that messages and styles are loaded
			'ext.pageTriage.views.list'
		],
		VueNPP.run
	);
};

VueNPP.run = function () {
	VueNPP.listItemComponent.components = mw.loader.require( 'wvui' );
	VueNPP.NPPFeedMenu.components = mw.loader.require( 'wvui' );
	VueNPP.NPPFeedMenu.components.listItem = VueNPP.listItemComponent;
	VueNPP.addStyle();
	VueNPP.renderInterface();
};

/**
 * Add styles for our interface.
 */
VueNPP.addStyle = function () {
	mw.util.addCSS(`
		.mwe-pt-metadata-warning:before {
			color: initial;
			content: " · ";
		}
	`);
};

/**
 * Component for rendering a list item.
 */
VueNPP.listItemComponent = {
	// wvuiButton is added later, once it has been loaded
	// defaults for props are from [[FDP Hamburg]] for now
    props: {
        position: { type: Number, default: 1 },
        afdStatus: { type: Boolean, default: false },
        blpProdStatus: { type: Boolean, default: false },
        csdStatus: { type: Boolean, default: false },
        prodStatus: { type: Boolean, default: false },
        patrolStatus: { type: Number, default: 0 },
        title: { type: String, default: 'FDP Hamburg' },
        isRedirect: { type: Boolean, default: false },
        categoryCount: { type: Number, default: 3 },
        linkCount: { type: Number, default: 0 },
        referenceCount: { type: Number, default: 1 },
        recreated: { type: Boolean, default: false },
        pageLen: { type: Number, default: 3760 },
        revCount: { type: Number, default: 2 },
        creationDateUTC: { type: String, default: '20220523131514' },
        creatorName: { type: String, default: 'Wanquanbiantai' },
        creatorAutoConfirmed: { type: Boolean, default: true },
        creatorRegistrationUTC: { type: String, default: '20220317205608' },
        creatorUserId: { type: Number, default: 43566998 },
        creatorEditCount: { type: Number, default: 66 },
        creatorIsBot: { type: Boolean, default: false },
        creatorBlocked: { type: Boolean, default: false },
        creatorUserPageExists: { type: Boolean, default: true },
        creatorTalkPageExists: { type: Boolean, default: true },
        afcState: { type: Number, default: 1 },
        reviewedUpdatedUTC: { type: String, default: '20220523131514' },
        snippet: { type: String, default: 'Chair Logo Michael Kruse FDP LV Hamburg Basis data Established: September 20, 1945 Place of establishment: Hamburg Chairman: Michael Kruse Vice chairmen: Katarina BlumeRia SchröderAndreas MoringSonja Jacobsen Treasurer: Ron Schumacher Executive direct...' },
        oresArticleQuality: { type: String, default: 'Start' },
        oresDraftQuality: { type: String, default: '' },
        copyvio: { type: Number, default: 0 },
    },
    data: function () {
        return {
            showOres: true || mw.config.get( 'wgShowOresFilters' ),
            showCopyvio: true || mw.config.get( 'wgShowCopyvio' ),
            enableReviewButton: true || mw.config.get( 'wgPageTriageEnableReviewButton' ),
            draftNamespaceId: mw.config.get( 'wgPageTriageDraftNamespaceId' ),
            timeOffset: parseInt( mw.user.options.get( 'timecorrection' ).split( '|' )[ 1 ] )
        };
    },
    methods: {
        prettyTimestamp: function ( utcTimestamp ) {
            const parsedTimestamp = moment.utc( utcTimestamp, 'YYYYMMDDHHmmss' );
            return parsedTimestamp.utcOffset( this.timeOffset ).format(
                mw.msg( 'pagetriage-creation-dateformat' )
            );
        },
        getjQueryLink: function ( url, text, exists ) {
            // Needed to be able to embed links in the byline
            const $link = $( '<a>' );
            if ( !exists ) {
                const uri = new mw.Uri( url );
                uri.query.action = 'edit';
                uri.query.redlink = 1;
                url = uri.toString();
                $link.addClass( 'new' );
            }
            $link.attr( 'href', url );
            $link.text( text );
            return $link;
        }
    },
    computed: {
        oddEvenClass: function () { return this.position % 2 == 0 ? 'mwe-pt-article-row-even' : 'mwe-pt-article-row-odd'; },
        isDraft: function () {
        	const pageNamespaceId = ( new mw.Title( this.title ) ).getNamespaceId();
        	return pageNamespaceId === this.draftNamespaceId;
        },
        iconImageSrc: function () {
            const imageBase = mw.config.get( 'wgExtensionAssetsPath' ) + '/PageTriage/modules/ext.pageTriage.views.list/images/';
            if ( this.isDraft ) {
                return imageBase + 'icon_not_reviewed.png';
            } else if ( this.afdStatus || this.blpProdStatus || this.csdStatus || this.prodStatus ) {
                return imageBase + 'icon_marked_for_deletion.png';
            } else if ( this.patrolStatus !== 0 ) {
                return imageBase + 'icon_reviewed.png';
            } else {
                return imageBase + 'icon_not_reviewed.png';
            }
        },
        titleUrl: function () {
            const params = {};
            if ( this.isRedirect ) {
                params.redirect = 'no';
            }
            return mw.util.getUrl( this.title, params );
        },
        titleUrlFormat: function () { return mw.util.wikiUrlencode( this.title ); },
        historyUrl: function () { return mw.config.get('wgScriptPath') + '/index.php?title=' + this.titleUrlFormat + '&action=history'; },
        creationDatePretty: function () {
            return this.prettyTimestamp( this.creationDateUTC );
        },
        creatorBylineHtml: function () {
            const bylineMessage = ( this.creatorUserId > 0 && !this.creatorAutoconfirmed )
                ? 'pagetriage-byline-new-editor'
                : 'pagetriage-byline';
            const creatorUserPageUrl = mw.util.getUrl( 'User:' + this.creatorName );
            const creatorTalkPageUrl = mw.util.getUrl( 'User talk:' + this.creatorName );
            const contribsUrl = mw.util.getUrl( 'Special:Contributions/' + this.creatorName );
            return mw.message(
                bylineMessage,
                this.getjQueryLink(
                    creatorUserPageUrl,
                    this.creatorName,
                    this.creatorUserPageExists
                ),
                this.getjQueryLink(
                    creatorTalkPageUrl,
                    mw.msg( 'sp-contributions-talk' ),
                    this.creatorTalkPageExists
                ),
                mw.msg( 'pipe-separator' ),
                this.getjQueryLink(
                    contribsUrl,
                    mw.msg( 'contribslink' ),
                    true
                )
            ).parse();
            
        },
        creatorRegistrationPretty: function () {
            return this.prettyTimestamp( this.creatorRegistrationUTC );
        },
        reviewedUpdatedPretty: function () {
            return this.prettyTimestamp( this.reviewedUpdatedUTC );
        },
        reviewRightHelpText: function () {
            if ( this.enableReviewButton ) {
                return '';
            }
            return this.$i18n( 'pagetriage-no-patrol-right' );
        },
        copyvioLink: function () {
            if ( this.copyvio === 0 ) {
                // Shouldn't be used
                return '';
            }
            return 'https://tools.wmflabs.org/copypatrol/en?filter=all&searchCriteria=page_exact'
                + '&searchText=' + ( new mw.Title( this.title ) ).getMainText()
                + '&drafts=' + ( this.isDraft ? '1' : '0' )
                + '&revision=' + this.copyvio;
        }
    },
    template: `<div class="mwe-pt-article-row" :class="oddEvenClass">
	<div class="mwe-pt-status-icon">
	    <img :src="iconImageSrc" width="21" height="21" />
	</div>
	<div class="mwe-pt-info-pane">
		<div class="mwe-pt-info-row">
			<div class="mwe-pt-article">
				<span class="mwe-pt-page-title"><a :href="titleUrl" target="_blank">{{ title }}</a></span>
				<span class="mwe-pt-histlink">
					(<a :href="historyUrl">{{ $i18n( "pagetriage-hist" ) }}</a>)
				</span>
				<span class="mwe-pt-metadata">
					·
					{{ $i18n( "pagetriage-bytes", pageLen ) }}
					·
					{{ $i18n( "pagetriage-edits", revCount ) }}
					<span v-if="!isDraft">
						<span v-if="categoryCount === 0 && !isRedirect" class="mwe-pt-metadata-warning">{{ $i18n( "pagetriage-no-categories" ) }}</span>
					    <template v-if="categoryCount !== 0">
							· {{ $i18n( "pagetriage-categories", categoryCount ) }}
					    </template>
					    <span v-if="linkCount === 0 && !isRedirect" class="mwe-pt-metadata-warning">{{ $i18n("pagetriage-orphan") }}</span>
					    <span v-if="recreated" class="mwe-pt-metadata-warning">{{ $i18n("pagetriage-recreated") }}</span>
					</span>
					<span v-if="referenceCount === 0 && !isRedirect" class="mwe-pt-metadata-warning">{{ $i18n( "pagetriage-no-reference" ) }}</span>
				</span>
			</div>
			<span class="mwe-pt-creation-date">{{ creationDatePretty }}</span>
		</div>
		<div class="mwe-pt-info-row">
			<div class="mwe-pt-author">
		    <span v-if="creatorName">
		        <!-- Using v-html because the messages used embed links within them -->
		        <span v-if="creatorBylineHtml" v-html="creatorBylineHtml"></span>
		        <span v-if="creatorUserId > 0">
					·
					{{ $i18n( 'pagetriage-editcount', creatorEditCount, creatorRegistrationPretty ) }}
					<span v-if="creatorIsBot">
						·
						{{ $i18n( 'pagetriage-author-bot' ) }}
					</span>
				</span>
				<span v-if="creatorBlocked" class="mwe-pt-metadata-warning">{{ $i18n( 'pagetriage-author-blocked' ) }}</span>
			</span>
			<span v-else>
			    {{ $i18n('pagetriage-no-author') }}
			</span>
			</div>
			<div class="mwe-pt-updated-date">
			    <span v-if="lastAfcAction">
			        <span>{{ $i18n( lastAfcAction ) }}</span>
			        <span>{{ reviewedUpdatedPretty }}</span>
			    </span>
			</div>
		</div>
		<div class="mwe-pt-info-row">
			<div class="mwe-pt-snippet">{{ snippet }}</div>
			<div class="mwe-pt-review">
				<a class="mwe-pt-list-triage-button" :href="titleUrl" target="_blank" :title="reviewRightHelpText">
				    <wvui-button action="progressive" type="primary">Review</wvui-button>
				</a>
			</div>
		</div>
		<div v-if="showOres" class="mwe-pt-info-row">
			<div class="mwe-pt-predicted-class">
				<span class="mwe-pt-article-ores-predicted-class-label">
					{{ $i18n( 'pagetriage-filter-predicted-class-heading' ) }}
				</span>
				<span class="mwe-pt-article-ores-predicted-class-value">{{ oresArticleQuality }}</span>
			</div>
			<div class="mwe-pt-potential-issues">
				<span>{{ $i18n( 'pagetriage-filter-predicted-issues-heading' ) }}</span>
				<span v-if="!oresDraftQuality && !( copyvio && showCopyvio )">
					{{ $i18n( 'pagetriage-filter-stat-predicted-issues-none' ) }}
				</span>
				<span v-if="oresDraftQuality" class="mwe-pt-issue">{{ oresDraftQuality }}</span>
				<span v-if="copyvio && showCopyvio">
				<span v-if="oresDraftQuality">·</span>
				<span class="mw-parser-output mwe-pt-issue">
					<a :href="copyvioLink" target="_blank" class="external">
						{{ $i18n( 'pagetriage-filter-stat-predicted-issues-copyvio' ) }}
					</a>
				</span>
				</span>
			</div>
		</div>
	</div>
</div>`
};

/**
 * Interface for user to choose an article
 */
VueNPP.NPPFeedMenu = {
	// wvui components are added separately
	components: {
		listItem: VueNPP.listItemComponent
	},
    data: function () {
        return {
        	// Default example is [[FDP Hamburg]] for now
            targetPageId: 70853005,
            api: new mw.Api(),
            apiError: false,
            showListItem: false,
            listItemProps: {}
        };
    },
	methods: {
		updatePageId: function ( newPageId ) {
		    this.targetPageId = newPageId;
		},
		updateEntry: function () {
		    console.log( this.targetPageId );
		    this.apiError = false;
		    this.showListItem = false;
		    this.listItemProps = {};
		    this.api.get( {
		        action: 'pagetriagelist',
		        page_id: this.targetPageId,
		        format: 'json',
		        formatversion: 2
		    } ).then(
		        ( res ) => this.processEntry( res ),
		        ( res ) => this.onApiFailure( res )
		    );
		},
		onApiFailure: function ( res ) {
			console.log( res );
			this.apiError = true;
		},
		processEntry: function ( res ) {
			console.log( res );
			if ( !res || !res.pagetriagelist || !res.pagetriagelist.pages
				|| !res.pagetriagelist.pages[0]
			) {
				this.onApiFailure( res );
				return;
			}
			const pageInfo = res.pagetriagelist.pages[0];
			// Properties for <list-item>
			const listItemProps = {};
			listItemProps.position = 1;
			listItemProps.afdStatus = pageInfo.afd_status === '1';
			listItemProps.blpProdStatus = pageInfo.blp_prod_status === '1';
			listItemProps.csdStatus = pageInfo.csd_status === '1';
			listItemProps.prodStatus = pageInfo.prod_status === '1';
			listItemProps.patrolStatus = parseInt( pageInfo.patrol_status );
			listItemProps.title = pageInfo.title;
			listItemProps.isRedirect = pageInfo.is_redirect === '1';
			listItemProps.categoryCount = parseInt( pageInfo.category_count );
			listItemProps.linkCount = parseInt( pageInfo.linkcount );
			listItemProps.referenceCount = parseInt( pageInfo.reference );
			listItemProps.recreated = !!pageInfo.recreated;
			listItemProps.pageLen = parseInt( pageInfo.page_len );
			listItemProps.revCount = parseInt( pageInfo.rev_count );
			listItemProps.creationDateUTC = pageInfo.creation_date_utc;
			listItemProps.creatorName = pageInfo.user_name;
			listItemProps.creatorAutoConfirmed = pageInfo.user_autoconfirmed === '1';
			listItemProps.creatorRegistrationUTC = pageInfo.user_creation_date;
			listItemProps.creatorUserId = parseInt( pageInfo.user_id );
			listItemProps.creatorEditCount = parseInt( pageInfo.user_editcount );
			listItemProps.creatorIsBot = pageInfo.user_bot === '1';
			listItemProps.creatorBlocked = pageInfo.user_block_status === '1';
			listItemProps.creatorUserPageExists = pageInfo.creator_user_page_exist;
			listItemProps.creatorTalkPageExists = pageInfo.creator_user_talk_page_exist;
			listItemProps.afcState = parseInt( pageInfo.afc_state );
			listItemProps.reviewedUpdatedUTC = pageInfo.ptrp_reviewed_updated;
			listItemProps.snippet = pageInfo.snippet;
			listItemProps.oresArticleQuality = pageInfo.ores_articlequality;
			listItemProps.oresDraftQuality = pageInfo.ores_draftquality;
			listItemProps.copyvio = !!pageInfo.copyvio;
			this.listItemProps = listItemProps;

			this.showListItem = true;
		}
	},
	template: `<div>
Page id: <wvui-input :value="targetPageId" v-on:input="updatePageId"></wvui-input>
<br>
<wvui-button action="progressive" type="primary" v-on:click="updateEntry">View entry</wvui-button>
<p v-show="apiError">Api error, see console</p>
<br>
<list-item v-show="showListItem" v-bind="listItemProps"></list-item>
</div>`
};

/**
 * Render VueNPP interface
 */
VueNPP.renderInterface = function () {
	Vue.createMwApp( VueNPP.NPPFeedMenu )
		.mount( '#mw-content-text' );
};

});

$( document ).ready( () => {
	if (
		mw.config.get( 'wgPageName' ) === 'Special:BlankPage/VueNPP'
	) {
		window.VueNPP.init();
	}
});

// </nowiki>