wikimedia/mediawiki-extensions-Flow

View on GitHub
FlowActions.php

Summary

Maintainability
D
1 day
Test Coverage
<?php

use Flow\Data\Listener\RecentChangesListener;
use Flow\Log\ModerationLogger;
use Flow\Model\AbstractRevision;
use Flow\Model\Header;
use Flow\Model\PostRevision;
use Flow\Model\PostSummary;
use Flow\RevisionActionPermissions;

/**
 * Flow actions: key => value map with key being the action name.
 * The value consists of an array of these below keys (and appropriate values):
 * * performs-writes: Must be boolean true for any action that writes to the wiki.
 *     actions with this set will additionally require the core 'edit' permission.
 * * log_type: the Special:Log filter to save actions to; false means 'not logged'.
 * * rc_insert: whether or not to insert the write action into RC table.
 * * permissions: array of permissions, where each key is the existing post
 *     state and the value is the right required to execute the action.  A blank
 *     value means anyone can take the action.  However, an omitted key means
 *     no one can perform the action described by that key.
 * * root-permissions: similar to 'permissions', but applies to the last revision
 *   of the root post (= the topic) for the revision the action is executed against.
 *   If root-permissions is omitted entirely, it doesn't affect what is allowed.
 *   However, if any keys are set, omitted keys are treated as prohibited.
 * * core-delete-permissions: array of rights, where any of those rights will
 *     give you permission to do the action on a deleted board (isAllowedAny).
 *     Empty string and omitted behave like 'permissions'.
 * * links: the set of read links to generate and return in API responses
 * * actions: the set of write links to generate and return in API responses
 * * history: all history-related information:
 *   * i18n-message: the i18n message key for this change type
 *   * i18n-params: array of i18n parameters for the provided message (see
 *     AbstractFormatter::processParam phpdoc for more details)
 *   * class: classname to be added to the list-item for this changetype
 *   * bundle: array with, again, all of the above information if multiple types
 *     should be bundled (then the bundle i18n & class will be used to generate
 *     the list-item; clicking on it will reveal the individual history entries)
 * * watch: Used by the WatchTopicListener to auto-subscribe users to topics. Only
 *   value value currently is immediate.
 *   * immediate: watchlist the title in the current process
 * * rc_title: Either 'article' or 'owner' to select between Workflow::getArticleTitle
 *     or Workflow::getOwnerTitle being used as the related recentchanges entry on insert
 * * editcount: True to increment user's edit count for this action
 * * modules: Modules to insert with RL to html page for this action instead of the defaults.
 * *   All actions other than view should have an array here, unless the default
 * *   modules are known to work.  You can specify an empty array, or a custom set of modules.
 * * moduleStyles: Style modules to insert with RL to html page for this action instead of the defaults
 * * hasUserGeneratedContent: Whether this action renders a page consisting of user-generated content
 */
return [
    'create-header' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            Header::MODERATED_NONE => '',
        ],
        'links' => [ 'board-history', 'workflow', 'header-revision' ],
        'actions' => [ 'edit-header' ],
        'history' => [
            'i18n-message' => 'flow-rev-message-create-header',
            'i18n-params' => [
                'user-links',
                'user-text',
            ],
            'class' => 'flow-history-create-header',
        ],
        'editcount' => true,
    ],

    'edit-header' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            Header::MODERATED_NONE => '',
        ],
        'links' => [ 'board-history', 'diff-header', 'workflow', 'header-revision' ],
        'actions' => [ 'edit-header', 'undo-edit-header' ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-header',
            'i18n-params' => [
                'user-links',
                'user-text',
            ],
            'class' => 'flow-history-edit-header',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
        'editcount' => true,
    ],

    // @todo this is almost copy/paste from edit-header except the handler-class. find
    // a way to share.
    'undo-edit-header' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            Header::MODERATED_NONE => '',
        ],
        'links' => [ 'board-history', 'diff-header', 'workflow', 'header-revision' ],
        'actions' => [ 'edit-header', 'undo-edit-header' ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-header',
            'i18n-params' => [
                'user-links',
                'user-text',
            ],
            'class' => 'flow-history-edit-header',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'editcount' => true,
        // theis modules/moduleStyles is repeated in all the undo-* actions. Find a way to share.
        'moduleStyles' => [
            'mediawiki.ui.button',
            'mediawiki.ui.input',
            'ext.flow.styles.base',
        ],
    ],

    'create-topic-summary' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            PostSummary::MODERATED_NONE => '',
            PostSummary::MODERATED_LOCKED => [ 'flow-lock', 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_HIDDEN => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_SUPPRESSED => [ 'flow-suppress' ],
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_LOCKED => '',
        ],
        'links' => [
            'topic', 'topic-history', 'diff-post-summary', 'watch-topic', 'unwatch-topic',
            'summary-revision'
        ],
        'actions' => [ 'edit-topic-summary', 'lock-topic', 'restore-topic' ],
        'history' => [
            'i18n-message' => 'flow-rev-message-create-topic-summary',
            'i18n-params' => [
                'user-links',
                'user-text',
                'post-of-summary',
            ],
            'class' => 'flow-history-create-topic-summary',
        ],
        'editcount' => true,
    ],

    'edit-topic-summary' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            PostSummary::MODERATED_NONE => '',
            PostSummary::MODERATED_LOCKED => [ 'flow-lock', 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_HIDDEN => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_SUPPRESSED => [ 'flow-suppress' ],
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_LOCKED => '',
        ],
        'links' => [
            'topic', 'topic-history', 'diff-post-summary', 'watch-topic', 'unwatch-topic',
            'summary-revision'
        ],
        'actions' => [
            'edit-topic-summary', 'lock-topic', 'restore-topic', 'undo-edit-topic-summary'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-topic-summary',
            'i18n-params' => [
                'user-links',
                'user-text',
                'post-of-summary',
            ],
            'class' => 'flow-history-edit-topic-summary',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
        'editcount' => true,
    ],

    // @todo this is almost copy/paste from edit-topic-summary except the handler class. find a
    // way to share
    'undo-edit-topic-summary' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            PostSummary::MODERATED_NONE => '',
            PostSummary::MODERATED_LOCKED => [ 'flow-lock', 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_HIDDEN => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostSummary::MODERATED_SUPPRESSED => [ 'flow-suppress' ],
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
        ],
        'links' => [ 'topic', 'topic-history', 'diff-post-summary', 'watch-topic', 'unwatch-topic' ],
        'actions' => [ 'edit-topic-summary', 'lock-topic', 'restore-topic', 'undo-edit-topic-summary' ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-topic-summary',
            'i18n-params' => [
                'user-links',
                'user-text',
                'post-of-summary',
            ],
            'class' => 'flow-history-edit-topic-summary',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'editcount' => true,
        'moduleStyles' => [
            'mediawiki.ui.button',
            'mediawiki.ui.input',
            'ext.flow.styles.base',
        ],
    ],

    'edit-title' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            // no permissions needed for own posts
            PostRevision::MODERATED_NONE => static function (
                PostRevision $post, RevisionActionPermissions $permissions
            ) {
                return $post->isCreator( $permissions->getUser() ) ? '' : 'flow-edit-title';
            }
        ],
        'links' => [
            'topic', 'topic-history', 'diff-post', 'topic-revision', 'watch-topic', 'unwatch-topic'
        ],
        'actions' => [
            'reply', 'thank', 'edit-title', 'lock-topic', 'hide-topic', 'delete-topic',
            'suppress-topic', 'edit-topic-summary', 'lock-topic', 'restore-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-title',
            'i18n-params' => [
                'user-links',
                'user-text',
                'workflow-url',
                'plaintext',
                'prev-plaintext',
            ],
            'class' => 'flow-history-edit-title',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
        'watch' => [
            'immediate' => [ \Flow\Data\Listener\ImmediateWatchTopicListener::class, 'getCurrentUser' ],
        ],
        'editcount' => true,
    ],

    'new-topic' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_title' => 'owner',
        'rc_insert' => true,
        'exclude_from_contributions' => true,

        // If you add exclude_from_history to new change types, you *must* update
        // the *HistoryQuery's (to use doInternalQueries with a good overfetch factor).
        // You should also adjust the memcached indices for best results.
        'exclude_from_history' => true,

        // exclude_from_recentchanges only refers to the actual Special:RecentChanges.
        // It does not affect Special:Watchlist.
        'exclude_from_recentchanges' => true,
        'permissions' => [
            PostRevision::MODERATED_NONE => '',
        ],
        'links' => [
            'topic-history', 'topic', 'post', 'topic-revision', 'watch-topic', 'unwatch-topic'
        ],
        'actions' => [
            'reply', 'thank', 'edit-title', 'hide-topic', 'delete-topic', 'suppress-topic',
            'edit-topic-summary', 'lock-topic', 'restore-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-new-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'workflow-url',
                'wikitext',
            ],
            'class' => 'flow-history-new-post',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
        'watch' => [
            'immediate' => [ \Flow\Data\Listener\ImmediateWatchTopicListener::class, 'getCurrentUser' ],
        ],
        'editcount' => true,
    ],

    'edit-post' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            // no permissions needed for own posts
            PostRevision::MODERATED_NONE => static function (
                PostRevision $post, RevisionActionPermissions $permissions
            ) {
                return $post->isCreator( $permissions->getUser() ) ? '' : 'flow-edit-post';
            }
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
        ],
        'links' => [ 'post-history', 'topic-history', 'topic', 'post', 'diff-post', 'post-revision' ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'restore-post', 'hide-post', 'delete-post',
            'suppress-post', 'undo-edit-post'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'post-url',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-edit-post',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
        'watch' => [
            'immediate' => [ \Flow\Data\Listener\ImmediateWatchTopicListener::class, 'getCurrentUser' ],
        ],
        'editcount' => true,
    ],

    // @todo this is almost (but not quite) copy/paste from 'edit-post'. find a way to share?
    'undo-edit-post' => [
        'performs-writes' => true,
        'log_type' => false, // maybe?
        'rc_insert' => true,
        'permissions' => [
            // no permissions needed for own posts
            PostRevision::MODERATED_NONE => static function (
                PostRevision $post, RevisionActionPermissions $permissions
            ) {
                return $post->isCreator( $permissions->getUser() ) ? '' : 'flow-edit-post';
            }
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
        ],
        'links' => [ 'post-history', 'topic-history', 'topic', 'post', 'diff-post', 'post-revision' ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'restore-post', 'hide-post', 'delete-post',
            'suppress-post', 'undo-edit-post'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-edit-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'post-url',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-edit-post',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'watch' => [
            'immediate' => [ \Flow\Data\Listener\ImmediateWatchTopicListener::class, 'getCurrentUser' ],
        ],
        'editcount' => true,
        'moduleStyles' => [
            'mediawiki.ui.button',
            'mediawiki.ui.input',
            'ext.flow.styles.base',
        ],
    ],

    'hide-post' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            // Permissions required to perform action. The key is the moderation state
            // of the post to perform the action against. The value is a string or array
            // of user rights that can allow this action.
            PostRevision::MODERATED_NONE => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
        ],
        'root-permissions' => [
            // Can only hide within an unmoderated or hidden topic. This doesn't check for a specific
            // permissions because thats already done above in 'permissions', this just ensures the
            // topic is in an appropriate state.
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_HIDDEN => '',
        ],
        'links' => [ 'topic', 'post', 'post-history', 'topic-history', 'post-revision' ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'restore-post', 'hide-post', 'delete-post', 'suppress-post'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-hid-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'post-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-hide-post',
        ],
    ],

    'hide-topic' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            PostRevision::MODERATED_NONE => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
        ],
        'links' => [
            'topic', 'post', 'topic-history', 'post-history', 'topic-revision', 'watch-topic', 'unwatch-topic'
        ],
        'actions' => [
            'reply', 'thank', 'edit-title', 'restore-topic', 'hide-topic', 'delete-topic', 'suppress-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-hid-topic',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'workflow-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-hide-topic',
        ],
    ],

    'delete-post' => [
        'performs-writes' => true,
        'log_type' => 'delete',
        'rc_insert' => true,
        'permissions' => [
            PostRevision::MODERATED_NONE => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_HIDDEN => [ 'flow-delete', 'flow-suppress' ],
        ],
        'links' => [
            'topic', 'post', 'post-history', 'topic-history', 'post-revision', 'watch-topic', 'unwatch-topic'
        ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'restore-post', 'hide-post', 'delete-post', 'suppress-post'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-deleted-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'post-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-delete-post',
        ],
    ],

    'delete-topic' => [
        'performs-writes' => true,
        'log_type' => 'delete',
        'rc_insert' => true,
        'permissions' => [
            PostRevision::MODERATED_NONE => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_HIDDEN => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_LOCKED => [ 'flow-delete', 'flow-suppress' ],
        ],
        'links' => [ 'topic', 'topic-history', 'topic-revision', 'watch-topic', 'unwatch-topic' ],
        'actions' => [
            'reply', 'thank', 'edit-title', 'hide-topic', 'delete-topic', 'suppress-topic',
            'edit-topic-summary', 'lock-topic', 'restore-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-deleted-topic',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'workflow-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-delete-topic',
        ],
    ],

    'suppress-post' => [
        'performs-writes' => true,
        'log_type' => 'suppress',
        'rc_insert' => false,
        'permissions' => [
            PostRevision::MODERATED_NONE => 'flow-suppress',
            PostRevision::MODERATED_HIDDEN => 'flow-suppress',
            PostRevision::MODERATED_DELETED => 'flow-suppress',
        ],
        'links' => [ 'topic', 'post', 'topic-history', 'post-revision' ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'restore-post', 'hide-post', 'delete-post', 'suppress-post'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-suppressed-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'post-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-suppress-post',
        ],
    ],

    'suppress-topic' => [
        'performs-writes' => true,
        'log_type' => 'suppress',
        'rc_insert' => false,
        'permissions' => [
            PostRevision::MODERATED_NONE => 'flow-suppress',
            PostRevision::MODERATED_HIDDEN => 'flow-suppress',
            PostRevision::MODERATED_DELETED => 'flow-suppress',
            PostRevision::MODERATED_LOCKED => 'flow-suppress',
        ],
        'links' => [ 'topic', 'topic-history', 'topic-revision', 'watch-topic', 'unwatch-topic' ],
        'actions' => [
            'reply', 'thank', 'edit-title', 'hide-topic', 'delete-topic', 'suppress-topic',
            'edit-topic-summary', 'lock-topic', 'restore-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-suppressed-topic',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'workflow-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-suppress-topic',
        ],
    ],

    'lock-topic' => [
        'performs-writes' => true,
        'log_type' => 'lock',
        'rc_insert' => true,
        'permissions' => [
            // Only non-moderated topic can be locked
            PostRevision::MODERATED_NONE => [ 'flow-lock', 'flow-delete', 'flow-suppress' ],
        ],
        'links' => [ 'topic', 'topic-history', 'watch-topic', 'unwatch-topic', 'topic-revision' ],
        'actions' => [ 'edit-topic-summary', 'restore-topic', 'delete-topic', 'suppress-topic' ],
        'history' => [
            'i18n-message' => 'flow-rev-message-locked-topic',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'workflow-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => 'flow-history-locked-topic',
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],

    'restore-post' => [
        'performs-writes' => true,
        'log_type' => static function ( PostRevision $revision, ModerationLogger $logger ) {
            $post = $revision->getCollection();
            $previousRevision = $post->getPrevRevision( $revision );
            if ( $previousRevision ) {
                // Kind of log depends on the previous change type:
                // * if post was deleted, restore should go to deletion log
                // * if post was suppressed, restore should go to suppression log
                return $previousRevision->getModerationState();
            }

            return '';
        },
        'rc_insert' => static function ( PostRevision $revision, RecentChangesListener $recentChanges ) {
            $post = $revision->getCollection();
            $previousRevision = $post->getPrevRevision( $revision );
            if ( $previousRevision ) {
                // * if post was hidden/deleted, restore can go to RC
                // * if post was suppressed, restore can not go to RC
                return $previousRevision->getModerationState() !== 'suppress';
            }

            return true;
        },
        'permissions' => [
            PostRevision::MODERATED_HIDDEN => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_SUPPRESSED => 'flow-suppress',
        ],
        'links' => [ 'topic', 'post', 'post-history', 'post-revision' ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'restore-post', 'hide-post', 'delete-post', 'suppress-post'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-restored-post',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'post-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => static function ( PostRevision $revision ) {
                $previous = $revision->getCollection()->getPrevRevision( $revision );
                $state = $previous->getModerationState();
                return "flow-history-un$state-post";
            }
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],

    'restore-topic' => [
        'performs-writes' => true,
        'log_type' => static function ( PostRevision $revision, ModerationLogger $logger ) {
            $post = $revision->getCollection();
            $previousRevision = $post->getPrevRevision( $revision );
            if ( $previousRevision ) {
                // Kind of log depends on the previous change type:
                // * if topic was deleted, restore should go to deletion log
                // * if topic was suppressed, restore should go to suppression log
                return $previousRevision->getModerationState();
            }

            return '';
        },
        'rc_insert' => static function ( PostRevision $revision, RecentChangesListener $recentChanges ) {
            $post = $revision->getCollection();
            $previousRevision = $post->getPrevRevision( $revision );
            if ( $previousRevision ) {
                // * if topic was hidden/deleted, restore can go to RC
                // * if topic was suppressed, restore can not go to RC
                return $previousRevision->getModerationState() !== 'suppress';
            }

            return true;
        },
        'permissions' => [
            PostRevision::MODERATED_LOCKED => [ 'flow-lock', 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_HIDDEN => [ 'flow-hide', 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_SUPPRESSED => 'flow-suppress',
        ],
        'links' => [ 'topic', 'topic-history', 'topic-revision', 'watch-topic', 'unwatch-topic' ],
        'actions' => [
            'reply', 'thank', 'edit-title', 'hide-topic', 'delete-topic', 'suppress-topic',
            'edit-topic-summary', 'lock-topic', 'restore-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-restored-topic',
            'i18n-params' => [
                'user-links',
                'user-text',
                'creator-text',
                'workflow-url',
                'moderated-reason',
                'topic-of-post-text-from-html',
            ],
            'class' => static function ( PostRevision $revision ) {
                $previous = $revision->getCollection()->getPrevRevision( $revision );
                $state = $previous->getModerationState();
                return "flow-history-un$state-topic";
            }
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],

    'view' => [
        'performs-writes' => false,
        'hasUserGeneratedContent' => true,
        'log_type' => false, // don't log views
        'rc_insert' => false, // won't even be called, actually; only for writes
        'permissions' => [
            PostRevision::MODERATED_NONE => '',
            // Everyone has permission to see this,
            // but hidden comments are only visible (collapsed) on permalinks directly to them.
            PostRevision::MODERATED_HIDDEN => '',
            PostRevision::MODERATED_LOCKED => '',
            PostRevision::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_SUPPRESSED => 'flow-suppress',
        ],
        'core-delete-permissions' => [ 'deletedtext' ],
        'links' => [], // @todo
        'actions' => [], // view is not a recorded change type, no actions will be requested
        'history' => [], // views don't generate history
        'handler-class' => \Flow\Actions\ViewAction::class,
    ],

    'reply' => [
        'performs-writes' => true,
        'log_type' => false,
        'rc_insert' => true,
        'permissions' => [
            PostRevision::MODERATED_NONE => '',
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
        ],
        'links' => [ 'topic-history', 'topic', 'post', 'post-revision', 'watch-topic', 'unwatch-topic' ],
        'actions' => [
            'reply', 'thank', 'edit-post', 'hide-post', 'delete-post', 'suppress-post',
            'edit-topic-summary', 'lock-topic', 'restore-topic'
        ],
        'history' => [
            'i18n-message' => 'flow-rev-message-reply',
            'i18n-params' => [
                'user-links',
                'user-text',
                'post-url',
                'topic-of-post-text-from-html',
                'summary',
            ],
            'class' => 'flow-history-reply',
            'bundle' => [
                'i18n-message' => 'flow-rev-message-reply-bundle',
                'i18n-params' => [
                    'bundle-count'
                ],
                'class' => 'flow-history-bundle',
            ],
        ],
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
        'watch' => [
            'immediate' => [ \Flow\Data\Listener\ImmediateWatchTopicListener::class, 'getCurrentUser' ],
        ],
        'editcount' => true,
    ],

    'history' => [
        'performs-writes' => false,
        'log_type' => false,
        'rc_insert' => false, // won't even be called, actually; only for writes
        'permissions' => [
            PostRevision::MODERATED_NONE => function (
                AbstractRevision $revision,
                RevisionActionPermissions $permissions
            ) {
                static $previousCollectionId;

                /*
                 * To check permissions, both the current revision (revision-
                 * specific moderation state) & the last revision (global
                 * collection moderation state) will always be checked.
                 * This one has special checks to make sure "restore" actions
                 * are hidden when the user has no permissions to see the
                 * moderation state they were restored from.
                 * We don't want that test to happen; otherwise, when a post
                 * has just been restored in the most recent revisions, that
                 * would result in none of the previous revisions being
                 * available (because a user would need permissions for the
                 * state the last revision was restored from)
                 */
                $collection = $revision->getCollection();
                if ( $previousCollectionId && $collection->getId()->equals( $previousCollectionId ) ) {
                    // doublecheck that this run is indeed against the most
                    // recent revision, to get the global collection state
                    try {
                        /** @var Flow\Collection\CollectionCache $cache */
                        $cache = \Flow\Container::get( 'collection.cache' );
                        $lastRevision = $cache->getLastRevisionFor( $revision );
                        if ( $revision->getRevisionId()->equals( $lastRevision->getRevisionId() ) ) {
                            $previousCollectionId = null;
                            return '';
                        }
                    } catch ( Exception $e ) {
                        // nothing to do here; if fetching last revision failed,
                        // we're just not testing any stored revision; that's ok
                    }
                }
                $previousCollectionId = $collection->getId();

                /*
                 * If a revision was the result of a restore-action, we have
                 * to look at the previous revision what the original moderation
                 * status was; permissions for the restore-actions visibility
                 * is the same as the moderation (e.g. if user can't see
                 * suppress actions, he can't see restores from suppress.
                 */
                if ( strpos( $revision->getChangeType(), 'restore-' ) === 0 ) {
                    $previous = $collection->getPrevRevision( $revision );

                    if ( $previous === null ||
                        $previous->getModerationState() === AbstractRevision::MODERATED_NONE
                    ) {
                        return '';
                    }

                    return $permissions->getPermission( $previous, 'history' );
                }

                return '';
            },
            PostRevision::MODERATED_HIDDEN => '',
            PostRevision::MODERATED_LOCKED => '',
            PostRevision::MODERATED_DELETED => '',
            PostRevision::MODERATED_SUPPRESSED => 'flow-suppress',
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_LOCKED => '',
            PostRevision::MODERATED_HIDDEN => '',
            // No data should be shown for other moderation levels: if a topic
            // has been deleted, we don't want a bunch of irrelevant
            // "new reply", "edit", ... spam in there.
            // All we want is the "topic has been deleted", which will still be
            // displayed (root-permissions won't be tested for the topic, since
            // it is the root)
        ],
        'core-delete-permissions' => [ 'deletedhistory' ],
        'history' => [], // views don't generate history
        'handler-class' => \Flow\Actions\FlowAction::class,
    ],

    // Pseudo-action to determine when to show thank links,
    // currently no limitation. if you can see revision you
    // can thank.
    'thank' => [
        'performs-writes' => false,
        'permissions' => [
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_HIDDEN => '',
            PostRevision::MODERATED_LOCKED => '',
            PostRevision::MODERATED_DELETED => '',
            PostRevision::MODERATED_SUPPRESSED => '',
        ],
    ],

    'view-topic-summary' => [
        'performs-writes' => false,
        'hasUserGeneratedContent' => true,
        'log_type' => false, // don't log views
        'rc_insert' => false, // won't even be called, actually; only for writes
        'permissions' => [
            PostRevision::MODERATED_NONE => '',
            // Everyone has permission to see this,
            // but hidden comments are only visible (collapsed) on permalinks directly to them.
            PostRevision::MODERATED_HIDDEN => '',
            PostRevision::MODERATED_LOCKED => '',
            PostRevision::MODERATED_DELETED => [ 'flow-delete', 'flow-suppress' ],
            PostRevision::MODERATED_SUPPRESSED => 'flow-suppress',
        ],
        'root-permissions' => [
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_HIDDEN => '',
            PostRevision::MODERATED_LOCKED => '',
        ],
        'core-delete-permissions' => [ 'deletedtext' ],
        'links' => [], // @todo
        'actions' => [], // view is not a recorded change type, no actions will be requested
        'history' => [], // views don't generate history
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],

    // This is only used when we specifically want to see the topic title.  If we're
    // cascading from a post (to view a post we need to be able to view the topic),
    // we'll use 'view' for both the post and topic root.  Unprivileged users shouldn't
    // be able to view a post in a deleted topic, but should be able to view the topic
    // title.
    'view-topic-title' => [
        'performs-writes' => false,
        'hasUserGeneratedContent' => true,
        'log_type' => false, // don't log views
        'rc_insert' => false, // won't even be called, actually; only for writes
        'permissions' => [
            // Everyone can see topic titles on existent boards, unless the
            // version you're viewing is suppressed, or the most recent version
            // is
            PostRevision::MODERATED_NONE => '',
            PostRevision::MODERATED_HIDDEN => '',
            PostRevision::MODERATED_LOCKED => '',
            PostRevision::MODERATED_DELETED => '',
            PostRevision::MODERATED_SUPPRESSED => 'flow-suppress',
        ],
        'core-delete-permissions' => [ 'deletedtext' ],
        'links' => [], // @todo
        'actions' => [], // view is not a recorded change type, no actions will be requested
        'history' => [], // views don't generate history
        'modules' => [],
    ],

    // Actions not tied to a particular revision change_type
    // or just move these to a different file
    // @todo: we should probably at least add 'permissions' in these below
    'compare-header-revisions' => [
        'hasUserGeneratedContent' => true,
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],
    'view-header' => [
        'hasUserGeneratedContent' => true,
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],
    'compare-post-revisions' => [
        'hasUserGeneratedContent' => true,
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],
    // @todo - This is a very bad action name, consolidate with view-post action
    'single-view' => [
        'hasUserGeneratedContent' => true,
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],
    'compare-postsummary-revisions' => [
        'hasUserGeneratedContent' => true,
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],
    'moderate-topic' => [
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],
    'moderate-post' => [
        'handler-class' => \Flow\Actions\FlowAction::class,
        'modules' => [],
    ],

    // Other formatters have the same config as history
    'recentchanges' => 'history',
    'contributions' => 'history',
    'checkuser' => 'history',

    /*
     * Backwards compatibility; these are old values that may have made their
     * way into the database.
     * Instead of having the correct config-array as value, you can just
     * reference another action.
     */
    'flow-rev-message-edit-title' => 'edit-title',
    'flow-edit-title' => 'edit-title',
    'flow-rev-message-new-post' => 'new-topic',
    'flow-new-post' => 'new-topic',
    'flow-rev-message-edit-post' => 'edit-post',
    'flow-edit-post' => 'edit-post',
    'flow-rev-message-reply' => 'reply',
    'flow-reply' => 'reply',
    'flow-rev-message-restored-post' => 'restore-post',
    'flow-post-restored' => 'restore-post',
    'flow-rev-message-hid-post' => 'hide-post',
    'flow-post-hidden' => 'hide-post',
    'flow-rev-message-deleted-post' => 'delete-post',
    'flow-post-deleted' => 'delete-post',
    'flow-rev-message-censored-post' => 'suppress-post',
    'flow-post-censored' => 'suppress-post',
    'flow-rev-message-edit-header' => 'edit-header',
    'flow-edit-summary' => 'edit-header',
    'flow-rev-message-create-header' => 'create-header',
    'flow-create-summary' => 'create-header',
    'flow-create-header' => 'create-header',
    /*
     * Backwards compatibility for previous suppression terminology (=censor).
     */
    'censor-post' => 'suppress-post',
    'censor-topic' => 'suppress-topic',
    /*
     * Backwards compatibility for old (separated) history actions
     */
    'post-history' => 'history',
    'topic-history' => 'history',
    'board-history' => 'history',

    // The new-topic type used to be called new-post
    'new-post' => 'new-topic',

    // BC for lock-topic, which used to be called differently
    'close-topic' => 'lock-topic',
    'close-open-topic' => 'lock-topic',
];