Page MenuHomePhabricator

Spike: Investigate creating landing page for reporting workflows
Closed, ResolvedPublicSpike

Description

Background information

As a step one, we are exploring linking to a configurable page that walks the user through the reporting workflow for that wiki.

Questions

  • Looking at GrowthExperiments home page, how have they implemented the ability to turn features on and off? - In the Growth extensions config file extension.json a key called config presents much of the features to configure the homepage as boolean, arrays, ints etc - see below : For example setting GEHelpPanelEnabled to false will remove Navigation links at the top of Special:Homepage
"config": {
		"GERefreshUserImpactDataMaintenanceScriptEnabled": {
			"description": "Feature flag to determine if the refreshUserImpactData.php maintenance script may run.",
			"value": false
		},
		"GEInfoboxTemplates": {
			"description": "List of templates that provide Infobox functionality.",
			"value": []
		},
		"GEInfoboxTemplatesTest": {
			"description": "List of templates that provide Infobox functionality. Used for manually testing changes.",
			"value": []
		},
		"GEDatabaseCluster": {
			"description": "Which database cluster to use. False means the cluster of the current wiki will be used. The database name will be the database name configured for the wiki, regardless of this setting. Only set this if you manage the DB schema manually.",
			"value": false
		},
		"GEWikiConfigPageTitle": {
			"description": "Name of page used for on-wiki config; must be in NS_MEDIAWIKI and end with .json; only variables listed in GEOnWikiConfigAllowList may be overriden on wiki",
			"value": "MediaWiki:GrowthExperimentsConfig.json"
		},
		"GEWikiConfigEnabled": {
			"description": "If set to false, on-wiki configuration will be ignored. WARNING: This will fallback to PHP-globals for all config. By setting this to false, you can break most of the features.",
			"value": true
		},
		"GEHelpPanelReadingModeNamespaces": {
			"description": "Numerical IDs of the MediaWiki namespaces in which to show the help panel in reading mode. Specifying a namespace will also include its talk namespace. Defaults to NS_PROJECT and NS_HELP namespaces.",
			"value": [ 2, 4, 12 ]
		},
		"GEHelpPanelExcludedNamespaces": {
			"description": "MediaWiki namespaces that the help panel will be excluded from.",
			"value": []
		},
		"GEHelpPanelNewAccountEnableWithHomepage": {
			"description": "By default, the help panel is enabled for new accounts in lockstep with the homepage, controlled by GEHomepageNewAccountEnablePercentage. To disable this behavior and use GEHelpPanelNewAccountEnablePercentage instead, set this to false.",
			"value": true
		},
		"GEHelpPanelNewAccountEnablePercentage": {
			"description": "Percentage of new accounts to enable help panel for. Must be a whole number. Ignored by default, only used if GEHelpPanelNewAccountEnableWithHomepage is false.",
			"value": 100
		},
		"GEHelpPanelHelpDeskTitle": {
			"description": "Mediawiki title for help panel CTA when users have disabled JS. Templates or magic words contained in this string will be expanded.",
			"value": "Main_Page"
		},
		"GEHelpPanelHelpDeskPostOnTop": {
			"description": "Whether to post new questions on the top of the help desk. Default is to post on the bottom (like section=new does). Only affects wikitext pages.",
			"value": false
		},
		"GEHelpPanelViewMoreTitle": {
			"description": "MediaWiki title for view more articles on help link.",
			"value": "Main_Page"
		},
		"GEHelpPanelEnabled": {
			"description": "Global ON/OFF switch for the help panel feature.",
			"value": true
		},
		"GEHelpPanelLoggingEnabled": {
			"description": "ON/OFF switch for the help panel event logging feature.",
			"value": true
		},
		"GEHelpPanelLinks": {
			"description": "List of links to display in the help panel.",
			"merge_strategy": "array_plus",
			"value": [ {
				"title": "Project:Help",
				"text": "Site help",
				"id": "help"
			} ]
		},
		"GEHelpPanelSearchNamespaces": {
			"description": "List of namespaces to include in the search for help content.",
			"value": [ 4, 12 ]
		},
		"GEHelpPanelSearchForeignAPI": {
			"description": "api.php URL of a foreign wiki to search instead of the current wiki. Used for testing/development to get more/better results.",
			"value": false
		},
		"GEHelpPanelSuggestedEditsPreferredEditor": {
			"description": "The preferred editor interface for suggested edits. The key is the task type handler ID (template-based or link-recommendation). Valid values for template-based are 'visualeditor' or 'wikitext'; for link-recommendation & image-recommendation only machineSuggestions is valid",
			"value": {
				"template-based": "visualeditor",
				"link-recommendation": "machineSuggestions",
				"image-recommendation": "machineSuggestions"
			}
		},
		"GEHelpPanelAskMentor": {
			"description": "When using the help panel's question-asking functionality, post on the mentor's talk page instead of on the help desk page.",
			"value": true
		},
		"WelcomeSurveyEnabled": {
			"description": "Global ON/OFF switch for the Welcome survey feature.",
			"value": true
		},
		"WelcomeSurveyAllowFreetextResponses": {
			"description": "If set to true, freetext responses with the welcome survey are allowed.",
			"value": false
		},
		"WelcomeSurveyPrivacyPolicyUrl": {
			"description": "URL of the privacy policy to be used on the Welcome survey.",
			"value": "https://meta.wikimedia.org/wiki/Data_retention_guidelines#Exceptions_to_these_guidelines"
		},
		"WelcomeSurveyReminderExpiry": {
			"description": "Number of days a reminder on Special:Homepage should be shown for if the user has not filled out the welcome survey yet. Set to 0 to disable the reminder.",
			"value": 30
		},
		"GEHomepageEnabled": {
			"description": "Global ON/OFF switch for the Homepage feature.",
			"value": true
		},
		"GEHomepageNewAccountEnablePercentage": {
			"description": "Percentage of new accounts to enable homepage for. Must be a whole number.",
			"value": 100
		},
		"GEHomepageNewAccountVariantsByPlatform": {
			"description": "Which percentage of new accounts to assign which A/B testing variant. For each variant, assign a percentage to \"mobile\" or \"desktop\" platforms. For legacy reasons this has 'Homepage' in the name and is only applied to new accounts for whom the homepage got enabled. Percentages must be whole numbers. If the percentages don't add up to 100, some new accounts will not be assigned a variant. Those accounts will fall back on GEHomepageDefaultVariant. The random choice can be overridden with the geForceVariant query parameter.",
			"value": {
				"control": {
					"mobile": 100,
					"desktop": 100
				}
			}
		},
		"GEHomepageDefaultVariant": {
			"description": "Experiment variant to use for users who weren't assigned a variant at account creation time, or the variant they got is not valid anymore. Valid values: 'control'.",
			"value": "control"
		},
		"GEHomepageLoggingEnabled": {
			"description": "ON/OFF switch for the homepage event logging feature.",
			"value": true
		},
		"GEMentorshipEnabled": {
			"description": "Are the mentorship features enabled?",
			"value": true
		},
		"GEMentorshipNewAccountEnablePercentage": {
			"description": "Percentage of new homepage-enabled users who will receive the mentorship module. Note that user must fall into _both_ GEHomepageNewAccountEnablePercentage and this percentage to be considered for mentorship.",
			"value": 100
		},
		"GEMentorshipAutomaticEligibility": {
			"description": "Automatically grant \"enrollasmentor\" to users who are eligible to enroll as mentors. See GEMentorshipMinimumAge and GEMentorshipMinimumEditcount to define eligibility rules (requirements set by both variables need to be met to make users eligible).",
			"value": true
		},
		"GEMentorshipMinimumAge": {
			"description": "Minimum tenure (in days) required for users to be eligible as mentors. Only used if GEMentorshipAutomaticEligibility is true and GEMentorProvider is structured. To make an user eligible for mentorship, they need to satisfy both GEMentorshipMinimumAge and GEMentorshipMinimumEditcount.",
			"value": 90
		},
		"GEMentorshipMinimumEditcount": {
			"description": "Minimum number of edits required for users to be eligible as mentors. Only used if GEMentorshipAutomaticEligibility is true and GEMentorProvider is structured. To make an user eligible for mentorship, they need to satisfy both GEMentorshipMinimumAge and GEMentorshipMinimumEditcount.",
			"value": 20
		},
		"GEHomepageMentorsList": {
			"description": "Title of the wiki page that contains the list of auto-assigned mentors (cf. GEHomepageManualAssignmentMentorsList). May be null if no such page exists.",
			"value": "Main_Page"
		},
		"GEHomepageManualAssignmentMentorsList": {
			"description": "Title of the wiki page that contains the list of manually assigned mentors (cf. GEHomepageMentorsList). Null if no such page exists.",
			"value": null
		},
		"GEStructuredMentorList": {
			"description": "Title of the wiki page that has the mentor list in JSON format (used by StructuredMentorProvider)",
			"value": "MediaWiki:GrowthMentors.json"
		},
		"GEMentorProvider": {
			"description": "Migration variable. Controls which MentorProvider is used. One of MentorProvider::PROVIDER_* constants",
			"value": "wikitext"
		},
		"GEMentorDashboardEnabled": {
			"description": "Should users be allowed to access Special:MentorDashboard?",
			"value": true
		},
		"GEMentorDashboardDeploymentMode": {
			"description": "One of stable/beta/alpha. Used by the mentor dashboard to display only features intended on that wiki.",
			"value": "alpha"
		},
		"GEHomepageSuggestedEditsEnabled": {
			"description": "Global ON/OFF switch for the suggested edits feature on the homepage.",
			"value": true
		},
		"GEHomepageSuggestedEditsIntroLinks": {
			"description": "Titles of local help pages linked from the suggested edits intro dialog.",
			"value": {
				"create": "Help:Creating pages",
				"image": "Help:Images"
			}
		},
		"GEHomepageSuggestedEditsEnableTopics": {
			"description": "Enable topic filters for suggested edits",
			"value": true
		},
		"GEConfirmEmailEnabled": {
			"description": "Global ON/OFF switch for the email confirmation changes.",
			"value": true
		},
		"GENewcomerTasksConfigTitle": {
			"description": "Title of a MediaWiki page which contains suggested edits configuration. If the title has an interwiki prefix, then its content will be loaded remotely via an HTTP request. Should be in the MediaWiki: namespace, have JSON content type and contain a map of task type -> task configuration where task configuration consists of the following fields: type  - a task type handler ID; group - 'easy', 'medium' or 'hard'. An optional 'disabled' field, when present and truthy, will make the system ignore that task type. Depending on the task type, there might be other fields, specifically:\n- type=template-based: templates - an array of template names (without namespace) for the maintenance templates\n- type=link-recommendation: minimumTasksPerTopic, minimumLinksPerTask, minimumLinkScore, maximumLinksPerTask, maximumLinksToShowPerTask, minimumTimeSinceLastEdit, minimumWordCount, maximumWordCount: parameters of acceptable tasks.\n\nThe system messages growthexperiments-homepage-suggestededits-tasktype-name-<tasktype> and growthexperiments-homepage-suggestededits-tasktype-description-<tasktype> will be used for task name and description; both must exist.",
			"value": "MediaWiki:NewcomerTasks.json"
		},
		"GENewcomerTasksTopicType": {
			"description": "How the topic of an article is identified. One of: morelike - use CirrusSearch morelikethis search with a predefined set of representetative articles for each topic; ores - use ORES articletopic predictions.",
			"value": "morelike"
		},
		"GENewcomerTasksOresTopicConfigTitle": {
			"description": "Title of a MediaWiki page which contains ORES topic configuration for suggested edits. If the title has an interwiki prefix, then its content will be loaded remotely via an HTTP request. Should be in the MediaWiki: namespace,  have JSON content type and contain an object like { topics: <topics>, groups: <groups> }. <topics> is a map of topic id -> topic configuration, with each topic an object with the properties 'group' (for grouping topics visually; each section name corresponds with a growthexperiments-homepage-suggestededits-topic-group-name-<id> system message which must exist) and 'oresTopics' (list of ORES topic IDs to use for this topic, ie. the values to use with the articletopic: search keyword). The system message growthexperiments-homepage-suggestededits-topic-name-<id> will be used for human-readable topic name and must exist. <groups> is a list of the group ids used in the topics section, to define the order of groups.",
			"value": "MediaWiki:NewcomerTopicsOres.json"
		},
		"GENewcomerTasksTopicConfigTitle": {
			"description": "Title of a MediaWiki page which contains morelike topic configuration for suggested edits. If the title has an interwiki prefix, then its content will be loaded remotely via an HTTP request. Should be in the MediaWiki: namespace, have JSON content type and contain a map of topic id -> topic configuration, which is an object with the properties 'titles' (list of mainspace page names of pages which are considered representative of the topic, to be used for text similarity comparision) and 'label' (human-readable name).",
			"value": "MediaWiki:NewcomerTopics.json"
		},
		"GENewcomerTasksTaskTypeHandlers": {
			"description": "Used for registering TaskTypeHandlers to the TaskTypeHandlerRegistry. An associative array with handler ID => handler specification, which is an ObjectFactory specification or a factory callable. Handlers IDs correspond to the 'type' parameters in the task config page.",
			"value": {
				"template-based": {
					"class": "GrowthExperiments\\NewcomerTasks\\TaskType\\TemplateBasedTaskTypeHandler",
					"services": [ "GrowthExperimentsNewcomerTasksConfigurationValidator", "GrowthExperimentsTemplateBasedTaskSubmissionHandler", "TitleParser" ]
				},
				"link-recommendation": {
					"class": "GrowthExperiments\\NewcomerTasks\\TaskType\\LinkRecommendationTaskTypeHandler",
					"services": [
						"GrowthExperimentsNewcomerTasksConfigurationValidator",
						"TitleParser",
						"GrowthExperimentsLinkRecommendationProvider",
						"GrowthExperimentsAddLinkSubmissionHandler"
					]
				},
				"image-recommendation": {
					"class": "GrowthExperiments\\NewcomerTasks\\TaskType\\ImageRecommendationTaskTypeHandler",
					"services": [
						"GrowthExperimentsNewcomerTasksConfigurationValidator",
						"TitleParser",
						"GrowthExperimentsImageRecommendationProvider",
						"GrowthExperimentsAddImageSubmissionHandler"
					]
				},
				"null": {
					"class": "GrowthExperiments\\NewcomerTasks\\TaskType\\NullTaskTypeHandler"
				}
			}
		},
		"GENewcomerTasksRemoteApiUrl": {
			"description": "URL of a remote API (ending with 'api.php') to use for identifying suggested edits. This is meant for developer setups.",
			"value": null
		},
		"GENewcomerTasksRemoteArticleOrigin": {
			"description": "Origin to append suggested article URLs to (e.g. 'https://en.wikipedia.org'). This is meant for developer setups.",
			"value": null
		},
		"GENewcomerTasksGuidanceEnabled": {
			"description": "Global ON/OFF switch for the newcomer tasks guidance feature.",
			"value": true
		},
		"GENewcomerTasksGuidanceRequiresOptIn": {
			"description": "If true, the guidance feature will only be displayed to users who have the (hidden) preference for it enabled. If false (default), it will be displayed to everyone.",
			"value": false
		},
		"GERestbaseUrl": {
			"description": "URL of the RESTBase service (PCS Summary API - see https://www.mediawiki.org/wiki/Page_Content_Service#/page/summary) used for task cards. Autogenerated from the wiki URL when not set.",
			"value": false
		},
		"GEMediaInfoRepos": {
			"description": "List of repo names which provide WikibaseMediaInfo data.",
			"value": [ "shared", "wikimediacommons" ]
		},
		"GEDeveloperSetup": {
			"description": "Set to true in developer (non-production) settings. In practice this will relax assumptions about page existence, which might be needed to approximate production behavior when e.g. a remote API is used for searching for pages, or a link recommendation service trained on a production wiki is used on a testing wiki.",
			"value": false
		},
		"GENewcomerTasksLinkRecommendationsEnabled": {
			"description": "Global ON/OFF switch for link recommendations.",
			"value": true
		},
		"GELinkRecommendationsFrontendEnabled": {
			"description": "Global ON/OFF switch for the link recommendations feature. Depending on the state, either the 'links' or the 'link-recommendation' task type is hidden. Can be overridden by SuggestedEdits::LINK_RECOMMENDATIONS_ENABLED_PREF.",
			"value": true
		},
		"GEUseNewImpactModule": {
			"description": "Whether the new impact module should be used instead of the old impact module",
			"value": false
		},
		"GETopicsMatchModeEnabled": {
			"description": "Global ON/OFF switch for the topics match mode UI feature.",
			"value": false
		},
		"GENewcomerTasksImageRecommendationsEnabled": {
			"description": "Global ON/OFF switch for image recommendations.",
			"value": true
		},
		"GEImageRecommendationServiceUrl": {
			"description": "Root URL of the image recommendation service (no trailing slash).",
			"value": false
		},
		"GEImageRecommendationServiceHttpProxy": {
			"description": "HTTP proxy for the service specified via GEImageRecommendationServiceUrl.",
			"value": null
		},
		"GEImageRecommendationServiceUseTitles": {
			"description": "Use titles instead of page IDs when calling the service. Less ideal but allows setting up test environments to use the production service.",
			"value": true
		},
		"GEImageRecommendationApiHandler": {
			"description": "Corresponding API handler to use for the image recommendation service (set via GEImageRecommendationServiceUrl); either 'mvp' or 'production'",
			"value": "mvp"
		},
		"GEImageRecommendationServiceWikiIdMasquerade": {
			"description": "The wiki ID to use in requests to the image recommendation service. Used for developer setups.",
			"value": null
		},
		"GELinkRecommendationServiceUrl": {
			"description": "Root URL of the link recommendation service.",
			"value": false
		},
		"GELinkRecommendationServiceAccessToken": {
			"description": "Personal access token from api.wikimedia.org to use when accessing the external production service. For developer setups only.",
			"value": null
		},
		"GELinkRecommendationServiceTimeout": {
			"description": "Request timeout when contacting the link recommendation service.",
			"value": null
		},
		"GELinkRecommendationFallbackOnDBMiss": {
			"description": "Fall back to the service at GELinkRecommendationServiceUrl when the recommendation is not cached in the database. This is only intended for simple local setups.",
			"value": false
		},
		"GELinkRecommendationsUseEventGate": {
			"description": "Whether to use EventGate for notifying about link recommendation creation. When set to false, uses CirrusSearch diretcly.",
			"value": false
		},
		"GELinkRecommendationServiceWikiIdMasquerade": {
			"description": "The wiki ID to use in requests to the link recommendation service. Used for developer setups.",
			"value": null
		},
		"GEStructuredTaskRejectionReasonTextInputEnabled": {
			"description": "Whether free text input should be shown when prompting the user for reasons why a suggestion was rejected.",
			"value": false
		},
		"GECampaigns": {
			"description": "A map of campaign ID to campaign configuration. Campaign configuration currently includes these fields:\n* pattern: a regexp matched against the \"campaign\" request parameter during signup to determine whether the user should be included in some campaign\n* signupPageTemplate: a template name to use for the \"benefits\" block of Special:CreateAccount\n* signupPageTemplateParameters: a map of parameters to pass to the template\n* topics: an array of topic IDs (which are defined in GECampaignTopics) to include on the top of the topic selector as custom topics\n\nExamples can be found at https://www.mediawiki.org/wiki/Extension:GrowthExperiments/Technical_documentation/Special:EditGrowthConfig.",
			"value": {}
		},
		"GECampaignTopics": {
			"description": "Mapping of topic IDs to its search expression, used to show campaign-specific topics in the suggested edits module. Examples can be found at https://www.mediawiki.org/wiki/Extension:GrowthExperiments/Technical_documentation/Special:EditGrowthConfig.",
			"value": {}
		},
		"WelcomeSurveyExperimentalGroups": {
			"description": "List of experimental groups. A group is defined with a range to randomly match users and the list of questions they will see on the survey.",
			"merge_strategy": "array_plus_2d",
			"value": {
				"exp1_group1": {
					"percentage": 0,
					"format": "specialpage",
					"questions": [
						"reason",
						"edited",
						"email"
					]
				},
				"exp1_group2": {
					"percentage": 0,
					"questions": []
				},
				"exp2_control": {
					"percentage": 0,
					"questions": []
				},
				"exp2_target_specialpage": {
					"percentage": 100,
					"format": "specialpage",
					"questions": [
						"reason",
						"edited",
						"email",
						"languages"
					]
				}
			}
		},
		"WelcomeSurveyEnableWithHomepage": {
			"description": "Display default welcome survey (defined in WelcomeSurvey::DEFAULT_SURVEY_GROUP) to all users with homepage enabled (usually enabled via GEHomepageNewAccountEnablePercentage). Users with no homepage won't be shown any survey.",
			"value": false
		}
	},
  • What other approaches to a modular, customizable page are there? - The Quiz extension presents an approach that uses Template Parser and .mustache templates which provides a pattern for template driven design - https://www.mediawiki.org/wiki/Extension:Quiz
  • How could we track who uses the links on the page? - We can use the MobileWebUIActionsTracking and the DesktopWebUIActionsTracking instruments. Some of the event data logged includes:
Wikimedia project
Page title
Page namespace
User edit count (can be bucketed; can be local or global)
User groups
User anonymous or logged-in

Event Timeline

Restricted Application changed the subtype of this task from "Task" to "Spike". · View Herald TranscriptJan 12 2023, 6:28 PM
Restricted Application added a subscriber: Aklapper. · View Herald Transcript

So far I have download the extension and supporting extensions and am in the process of setup/configuration. Once I get it running I will dive into the codebase and see how things work.

When installing the dependencies for Elastic search in docker this page was very helpful: https://www.elastic.co/guide/en/elasticsearch/reference/7.10/docker.html

The configuration for Growth Experiments(GE) lives in a special page called Special:EditGrowthConfig the page logic can be found here extensions/GrowthExperiments/includes/Specials/SpecialEditGrowthConfig.php. This special page extends FormSpecialPage as per the documentation is a

* Special page which uses an HTMLForm to handle processing.  This is mostly a
 * clone of FormAction.  More special pages should be built this way; maybe this could be
 * a new structure for SpecialPages.

https://doc.wikimedia.org/mediawiki-core/master/php/classFormSpecialPage.html#a3f2ee4e4d1d21dabd3575d56c02a9697

The storage mechanism seems to use both the DB and JSON to store extension data. I am currently investigating where/how MediaWiki:GrowthExperimentsConfig.json is edited on form submission.

OOUI is the display framework being used as the entire page is one big form taking inputs and submitting to an endpoint.

The thing I am trying to figure now is how the saving/submission to database and the MediaWiki:GrowthExperimentsConfig.json works. I see Growth Experiments(GE) adds some tables to the database as shown below. But those tables don't store the configuration payload coming in from the Edit Growth configuration pages submit button. From what I've gathered so far it looks a JSON payload can live in a page and be edited, if that is the case we would need a similar mechanism to edit/store the PIRS config in a similar JSON format.

MariaDB [my_database]> show tables;
+----------------------------------------+
| Tables_in_my_database                  |
+----------------------------------------+
| echo_push_provider                     |
| echo_push_subscription                 |
| echo_push_topic                        |
| echo_target_page                       |
| echo_unread_wikis                      |
| externallinks                          |
| filearchive                            |
| growthexperiments_link_recommendations |
| growthexperiments_link_submissions     |
| growthexperiments_mentee_data          |
| growthexperiments_mentor_mentee        |
| growthexperiments_user_impact          |