Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
TipLoader
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 7
702
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 loadTipNodes
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 buildExtraData
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getTipTreeForTaskType
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
110
 getTipNodesForStep
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
56
 getMessageKeyWithFallback
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
30
 buildMessageKey
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GrowthExperiments\HelpPanel\Tips;
4
5use GrowthExperiments\NewcomerTasks\ConfigurationLoader\ConfigurationLoader;
6use GrowthExperiments\NewcomerTasks\TaskType\TaskType;
7use LogicException;
8use MessageLocalizer;
9
10class TipLoader {
11
12    /**
13     * @var ConfigurationLoader
14     */
15    private $configurationLoader;
16
17    private const DEFAULT_EDITOR = 'visualeditor';
18
19    private const DEFAULT_SKIN = 'vector';
20    /**
21     * @var MessageLocalizer
22     */
23    private $messageLocalizer;
24
25    /**
26     * @param ConfigurationLoader $configurationLoader
27     * @param MessageLocalizer $messageLocalizer
28     */
29    public function __construct(
30        ConfigurationLoader $configurationLoader, MessageLocalizer $messageLocalizer
31    ) {
32        $this->configurationLoader = $configurationLoader;
33        $this->messageLocalizer = $messageLocalizer;
34    }
35
36    /**
37     * Get an array of TipNode objects, each of which contain the step name
38     * and a TipNode tree to render.
39     *
40     * @param string $skinName
41     * @param string $editor
42     * @param TaskType[] $taskTypes
43     * @param string $taskTypeId
44     * @return array
45     */
46    public function loadTipNodes(
47        string $skinName, string $editor, array $taskTypes, string $taskTypeId ): array {
48        $tipTree = $this->getTipTreeForTaskType(
49            $taskTypeId,
50            $this->buildExtraData( $taskTypes )
51        );
52        return array_filter( array_map( function ( $stepName ) use (
53            $skinName, $editor, $tipTree, $taskTypeId
54        ) {
55            return $this->getTipNodesForStep(
56                $stepName,
57                $skinName,
58                $editor,
59                $taskTypeId,
60                $tipTree
61            );
62        }, $tipTree->getStepNames() ) );
63    }
64
65    /**
66     * @param array $taskTypes
67     * @return array
68     */
69    private function buildExtraData( array $taskTypes ): array {
70        return array_map( static function ( TaskType $taskType ) {
71            return [ 'learnMoreLink' => $taskType->getLearnMoreLink() ];
72        }, $taskTypes );
73    }
74
75    /**
76     * @param string $taskTypeId
77     * @param array $extraData
78     * @throws LogicException
79     * @return TipTree
80     */
81    private function getTipTreeForTaskType( string $taskTypeId, array $extraData ): TipTree {
82        switch ( $taskTypeId ) {
83            case 'copyedit':
84                return new CopyeditTipTree( $extraData );
85            case 'image-recommendation':
86                return new ImageRecommendationTipTree( $extraData );
87            case 'section-image-recommendation':
88                return new SectionImageRecommendationTipTree( $extraData );
89            case 'links':
90                return new LinkTipTree( $extraData );
91            case 'link-recommendation':
92                return new LinkRecommendationTipTree( $extraData );
93            case 'update':
94                return new UpdateTipTree( $extraData );
95            case 'references':
96                return new ReferencesTipTree( $extraData );
97            case 'expand':
98                return new ExpandTipTree( $extraData );
99            default:
100                throw new LogicException( $taskTypeId . ' does not have tip steps defined.' );
101        }
102    }
103
104    /**
105     * Get an array of TipNodes for a single step.
106     *
107     * @param string $stepName
108     * @param string $skinName
109     * @param string $editor
110     * @param string $taskTypeId
111     * @param TipTree $tipSteps
112     * @return TipNode[]
113     */
114    private function getTipNodesForStep(
115        string $stepName, string $skinName, string $editor, string $taskTypeId, TipTree $tipSteps
116    ): array {
117        $nodesPerTip = array_filter( array_map( function ( $tipTypeId ) use (
118            $tipSteps, $stepName, $editor, $taskTypeId, $skinName
119        ) {
120            $messageKey = null;
121            $steps = $tipSteps->getTree();
122            if ( !isset( $steps[$stepName][$tipTypeId] ) ) {
123                return null;
124            }
125            if ( $tipTypeId === "main-multiple" ) {
126                $nodes = [];
127                $i = 1;
128                while ( $messageKey !== "" && $i <= TipTree::TIP_TYPE_MAIN_MULTIPLE_MAX_NODES ) {
129                    $messageKey = $this->getMessageKeyWithFallback(
130                        $skinName,
131                        $editor,
132                        $taskTypeId,
133                        $tipTypeId . "-" . $i,
134                        $stepName
135                    );
136                    if ( $messageKey ) {
137                        $nodes[] = new TipNode( $tipTypeId, $messageKey, $steps[$stepName][$tipTypeId] ?? [] );
138                    }
139                    $i++;
140                }
141                return $nodes;
142            }
143            $messageKey = $this->getMessageKeyWithFallback(
144                $skinName,
145                $editor,
146                $taskTypeId,
147                $tipTypeId,
148                $stepName
149            );
150            if ( !$messageKey ) {
151                return null;
152            }
153            return [ new TipNode( $tipTypeId, $messageKey, $steps[$stepName][$tipTypeId] ?? [] ) ];
154        }, $tipSteps->getTipTypes() ) );
155
156        return array_merge( [], ...array_values( $nodesPerTip ) );
157    }
158
159    /**
160     * Get a Message for a particular skin, editor, task type, tip type, and step.
161     *
162     * If the message is disabled or doesn't exist, check variants in the
163     * following order:
164     * - Default editor with requested skin
165     * - Default skin with requested editor
166     * - Default skin and default editor
167     *
168     * @param string $skinName
169     * @param string $editor
170     * @param string $taskTypeId
171     * @param string $tipTypeId
172     * @param string $step
173     * @return string
174     */
175    private function getMessageKeyWithFallback(
176        string $skinName,
177        string $editor,
178        string $taskTypeId,
179        string $tipTypeId,
180        string $step
181    ): string {
182        $msg = $this->messageLocalizer->msg( $this->buildMessageKey(
183            $skinName, $editor, $taskTypeId, $tipTypeId, $step )
184        );
185        if ( $msg->isDisabled() ) {
186            $msg = $this->messageLocalizer->msg(
187                $this->buildMessageKey( $skinName, self::DEFAULT_EDITOR, $taskTypeId, $tipTypeId, $step )
188            );
189        }
190        if ( $msg->isDisabled() ) {
191            $msg = $this->messageLocalizer->msg(
192                $this->buildMessageKey( self::DEFAULT_SKIN, $editor, $taskTypeId, $tipTypeId, $step )
193            );
194        }
195        if ( $msg->isDisabled() ) {
196            $msg = $this->messageLocalizer->msg(
197                $this->buildMessageKey(
198                    self::DEFAULT_SKIN,
199                    self::DEFAULT_EDITOR,
200                    $taskTypeId,
201                    $tipTypeId,
202                    $step
203                )
204            );
205        }
206        return $msg->isDisabled() ? '' : $msg->getKey();
207    }
208
209    /**
210     * @param string $skinName
211     * @param string $editor
212     * @param string $taskTypeId
213     * @param string $tipTypeId
214     * @param string $step
215     * @return string
216     */
217    private function buildMessageKey(
218        string $skinName, string $editor, string $taskTypeId, string $tipTypeId, string $step
219    ): string {
220        return sprintf(
221            '%s-%s-%s-%s-%s-%s',
222            'growthexperiments-help-panel-suggestededits-tips',
223            $skinName,
224            $editor,
225            $taskTypeId,
226            $tipTypeId,
227            $step
228        );
229    }
230
231}