3131
3232namespace GlpiPlugin \Formcreator \Command ;
3333
34+ use ITILFollowup ;
35+ use QuerySubQuery ;
3436use Symfony \Component \Console \Command \Command ;
37+ use Symfony \Component \Console \Helper \ProgressBar ;
3538use Symfony \Component \Console \Input \InputInterface ;
3639use Symfony \Component \Console \Output \OutputInterface ;
3740use Ticket ;
3841use Item_Ticket ;
3942use PluginFormcreatorFormAnswer ;
40- use Glpi \Toolbox \Sanitizer ;
4143
4244class CleanTicketsCommand extends Command
4345{
@@ -48,66 +50,108 @@ protected function configure() {
4850 }
4951
5052 protected function execute (InputInterface $ input , OutputInterface $ output ) {
51- $ output -> write ( " <info>-> Search tickets to clean ...</info> " );
52- $ output ->writeln ("" );
53+ $ message = __ ( " Searching for invalid items ..." , " formcreator " );
54+ $ output ->writeln ("<info> $ message </info> " );
5355
5456 $ this ->fixBadForm_1 ($ input , $ output );
5557 $ this ->fixBadForm_2 ($ input , $ output );
5658 $ this ->fixBadForm_3 ($ input , $ output );
5759
58- $ output ->writeln ('<info>Done.</info> ' );
59- return 0 ;
60+ $ output ->writeln ("" );
61+ $ message = __ ("Done. " , "formcreator " );
62+ $ output ->writeln ("<info> $ message</info> " );
63+
64+ return Command::SUCCESS ;
6065 }
6166
6267 /**
63- * fix HTML tags double encoded
64- * <p> => <p> => &lt;p&gt;
68+ * Get invalid data using a specific regex pattern to detect invalid content
6569 *
66- * @param InputInterface $input
67- * @param OutputInterface $output
68- * @return void
70+ * @param string $invalid_content_pattern
71+ *
72+ * @return iterable
6973 */
70- protected function fixBadForm_1 ( InputInterface $ input , OutputInterface $ output ) {
74+ protected function getInvalidData ( string $ invalid_content_pattern ): iterable {
7175 global $ DB ;
7276
73- // Search tickets having HTML tags in content in the following form
74- // &lt;p&gt;Hello world&lt;/p&gt;
75- // Hello world is between <p> and </p>, but with wrong escaping
76- $ itemTicketTable = Item_Ticket::getTable ();
77- $ ticketTable = Ticket::getTable ();
78- $ pattern = '&lt; ' ;
79- // $pattern = str_replace(';', '\\;', $pattern);
80- $ tickets = $ DB ->request ([
81- 'SELECT ' => [$ ticketTable => [Ticket::getIndexName (), 'content ' ]],
82- 'FROM ' => $ ticketTable ,
77+ $ item_ticket_table = Item_Ticket::getTable ();
78+ $ ticket_table = Ticket::getTable ();
79+ $ followup_table = ITILFollowup::getTable ();
80+
81+ // First source: tickets description
82+ $ tickets_query = new QuerySubQuery ([
83+ 'SELECT ' => [
84+ new \QueryExpression ($ DB ->quoteValue (Ticket::getType ()) . ' AS ' . $ DB ->quoteName ('itemtype ' )),
85+ $ ticket_table => [Ticket::getIndexName (), 'content ' ]
86+ ],
87+ 'FROM ' => $ ticket_table ,
8388 'INNER JOIN ' => [
84- $ itemTicketTable => [
89+ $ item_ticket_table => [
8590 'FKEY ' => [
86- $ ticketTable => Ticket::getIndexName (),
87- $ itemTicketTable => Ticket::getForeignKeyField (),
91+ $ ticket_table => Ticket::getIndexName (),
92+ $ item_ticket_table => Ticket::getForeignKeyField (),
8893 ],
8994 'AND ' => [
90- "$ itemTicketTable .itemtype " => PluginFormcreatorFormAnswer::getType (),
95+ "$ item_ticket_table .itemtype " => PluginFormcreatorFormAnswer::getType (),
9196 ]
9297 ],
9398 ],
9499 'WHERE ' => [
95- "$ ticketTable .content " => ['LIKE ' , '% ' . $ pattern . '% ' ], // Matches bad encoding for '<'
100+ "$ ticket_table .content " => ['LIKE ' , '% ' . $ invalid_content_pattern . '% ' ],
96101 ],
97102 ]);
98103
99- $ count = $ tickets ->count ();
100- if ($ count < 1 ) {
101- $ output ->writeln ('<info>-> No ticket to fix.</info> ' );
102- $ output ->writeln ("" );
103- return 0 ;
104- }
104+ // Second source: tickets that where merged into other tickets as a followup
105+ // These followups may have been a former ticket generated by formcreator
106+ $ followup_query = new QuerySubquery ([
107+ 'SELECT ' => [
108+ new \QueryExpression ($ DB ->quoteValue (ITILFollowup::getType ()) . ' AS ' . $ DB ->quoteName ('itemtype ' )),
109+ ITILFollowup::getIndexName (),
110+ 'content '
111+ ],
112+ 'FROM ' => $ followup_table ,
113+ 'WHERE ' => [
114+ "sourceitems_id " => ["> " , 0 ], // Former tickets merged as followups
115+ "$ followup_table.content " => ['LIKE ' , '% ' . $ invalid_content_pattern . '% ' ],
116+ ],
117+ ]);
105118
106- $ output ->write ("<info>-> Found $ count tickets to clean (double encoded < and > signs)</info> " );
107- $ output ->writeln ("" );
108- $ output ->write ("<info>-> Cleaning tickets...</info> " );
119+ return $ DB ->request (new \QueryUnion ([$ tickets_query , $ followup_query ]));
120+ }
121+
122+ /**
123+ * fix HTML tags double encoded
124+ * <p> => <p> => &lt;p&gt;
125+ *
126+ * @param InputInterface $input
127+ * @param OutputInterface $output
128+ * @return void
129+ */
130+ protected function fixBadForm_1 (InputInterface $ input , OutputInterface $ output ) {
131+ global $ DB ;
132+
133+ // Print step info
109134 $ output ->writeln ("" );
110- foreach ($ tickets as $ row ) {
135+ $ message = __ ("Step 1: double encoded < and > signs. " , "formcreator " );
136+ $ output ->writeln ("<info> $ message</info> " );
137+
138+ // Search tickets having HTML tags in content in the following form
139+ // &lt;p&gt;Hello world&lt;/p&gt;
140+ // Hello world is between <p> and </p>, but with wrong escaping
141+ $ items = $ this ->getInvalidData ('&lt; ' ); // Matches bad encoding for '<'
142+
143+ // No items found, nothing to do
144+ $ count = $ items ->count ();
145+ if ($ count === 0 ) {
146+ $ output ->writeln (__ ("No invalid items found. " , "formcreator " ));
147+ return ;
148+ }
149+
150+ // Init progress bar
151+ $ output ->writeln (__ ("Found $ count item(s) to clean. " , "formcreator " ));
152+ $ progress_bar = new ProgressBar ($ output );
153+
154+ foreach ($ progress_bar ->iterate ($ items ) as $ item ) {
111155 $ pattern = [
112156 '/&lt;([a-z0-9]+?)&gt;/ ' ,
113157 '/&lt;(\/[a-z0-9]+?)&gt;/ ' ,
@@ -116,18 +160,16 @@ protected function fixBadForm_1(InputInterface $input, OutputInterface $output)
116160 '<$1> ' ,
117161 '<$1> ' ,
118162 ];
119- $ row ['content ' ] = preg_replace ($ pattern , $ replace , $ row ['content ' ]);
163+ $ item ['content ' ] = preg_replace ($ pattern , $ replace , $ item ['content ' ]);
164+
120165 // Direct write to the table to avoid alteration of other fields
121166 $ DB ->update (
122- $ ticketTable ,
123- [
124- 'content ' => $ DB ->escape ($ row ['content ' ])
125- ],
126- [
127- 'id ' => $ row ['id ' ],
128- ]
167+ $ item ['itemtype ' ]::getTable (),
168+ ['content ' => $ DB ->escape ($ item ['content ' ])],
169+ ['id ' => $ item ['id ' ]]
129170 );
130171 }
172+ $ output ->writeln ("" );
131173 }
132174
133175 /**
@@ -140,72 +182,56 @@ protected function fixBadForm_1(InputInterface $input, OutputInterface $output)
140182 protected function fixBadForm_2 (InputInterface $ input , OutputInterface $ output ) {
141183 global $ DB ;
142184
185+ // Print step info
186+ $ output ->writeln ("" );
187+ $ message = __ ("Step 2: literal BR tag. " , "formcreator " );
188+ $ output ->writeln ("<info> $ message</info> " );
189+
143190 // Search tickets having HTML tags <br />
144- $ itemTicketTable = Item_Ticket::getTable ();
145- $ ticketTable = Ticket::getTable ();
146- $ pattern = '<br /> ' ;
147- $ tickets = $ DB ->request ([
148- 'SELECT ' => [$ ticketTable => [Ticket::getIndexName (), 'content ' ]],
149- 'FROM ' => $ ticketTable ,
150- 'INNER JOIN ' => [
151- $ itemTicketTable => [
152- 'FKEY ' => [
153- $ ticketTable => Ticket::getIndexName (),
154- $ itemTicketTable => Ticket::getForeignKeyField (),
155- ],
156- 'AND ' => [
157- "$ itemTicketTable.itemtype " => PluginFormcreatorFormAnswer::getType (),
158- ]
159- ],
160- ],
161- 'WHERE ' => [
162- "$ ticketTable.content " => ['LIKE ' , '% ' . $ pattern . '% ' ], // Matches bad encoding for 'br /'
163- ],
164- ]);
191+ $ items = $ this ->getInvalidData ('<br /> ' ); // Matches bad encoding for 'br /'
165192
166- $ count = $ tickets -> count ();
167- if ( $ count < 1 ) {
168- $ output -> writeln ( ' <info>-> No ticket to fix.</info> ' );
169- $ output ->writeln ("" );
170- return 0 ;
193+ // No items found, nothing to do
194+ $ count = $ items -> count ();
195+ if ( $ count === 0 ) {
196+ $ output ->writeln (__ ( " No invalid items found. " , " formcreator " ) );
197+ return ;
171198 }
172199
173- $ output -> write ( " <info>-> Found $ count tickets to clean (literal BR tag)</info> " );
174- $ output ->writeln ("" );
175- $ output -> write ( " <info>-> Cleaning tickets...</info> " );
176- $ output -> writeln ( "" );
177- foreach ($ tickets as $ row ) {
200+ // Init progress bar
201+ $ output ->writeln (__ ( " Found $ count item(s) to clean. " , " formcreator " ) );
202+ $ progress_bar = new ProgressBar ( $ output );
203+
204+ foreach ($ progress_bar -> iterate ( $ items ) as $ item ) {
178205 $ pattern = [
179206 '<br /> ' ,
180207 ];
181208 // Determine if we must use legacy or new encoding
182209 // @see Sanitizer::sanitize()
183210 $ replace = null ;
184- if (strpos ($ row ['content ' ], '< ' ) !== false && strpos ($ row ['content ' ], '#60; ' ) === false ) {
211+ if (strpos ($ item ['content ' ], '< ' ) !== false && strpos ($ item ['content ' ], '#60; ' ) === false ) {
185212 $ replace = [
186213 '<br /> ' ,
187214 ];
188- } else if (strpos ($ row ['content ' ], '#60 ' ) !== false && strpos ($ row ['content ' ], '< ' ) === false ) {
215+ } else if (strpos ($ item ['content ' ], '#60 ' ) !== false && strpos ($ item ['content ' ], '< ' ) === false ) {
189216 $ replace = [
190217 '<br /> ' ,
191218 ];
192219 }
193220 if ($ replace === null ) {
194- $ output ->write ("<error>-> Unable to determine the encoding type of ticket ID: " . $ row ['id ' ]. "</error> " );
221+ $ message = __ ("Unable to determine the encoding type of item ID: %1 $ d " , "formcreator " );
222+ $ output ->writeln ("<error> " . sprintf ($ message , $ item ['id ' ]) . "</error> " );
195223 continue ;
196224 }
197- $ row ['content ' ] = str_replace ($ pattern , $ replace , $ row ['content ' ]);
225+ $ item ['content ' ] = str_replace ($ pattern , $ replace , $ item ['content ' ]);
226+
198227 // Direct write to the table to avoid alteration of other fields
199228 $ DB ->update (
200- $ ticketTable ,
201- [
202- 'content ' => $ DB ->escape ($ row ['content ' ])
203- ],
204- [
205- 'id ' => $ row ['id ' ],
206- ]
229+ $ item ['itemtype ' ]::getTable (),
230+ ['content ' => $ DB ->escape ($ item ['content ' ])],
231+ ['id ' => $ item ['id ' ]]
207232 );
208233 }
234+ $ output ->writeln ("" );
209235 }
210236
211237 /**
@@ -220,71 +246,55 @@ protected function fixBadForm_2(InputInterface $input, OutputInterface $output)
220246 protected function fixBadForm_3 (InputInterface $ input , OutputInterface $ output ) {
221247 global $ DB ;
222248
249+ // Print step info
250+ $ output ->writeln ("" );
251+ $ message = __ ("Step 3: litteral > sign. " , "formcreator " );
252+ $ output ->writeln ("<info> $ message</info> " );
253+
223254 // Search tickets having HTML tags <br />
224- $ itemTicketTable = Item_Ticket::getTable ();
225- $ ticketTable = Ticket::getTable ();
226- $ pattern = ' > ' ; // greater than sign with a space before and after
227- $ tickets = $ DB ->request ([
228- 'SELECT ' => [$ ticketTable => [Ticket::getIndexName (), 'content ' ]],
229- 'FROM ' => $ ticketTable ,
230- 'INNER JOIN ' => [
231- $ itemTicketTable => [
232- 'FKEY ' => [
233- $ ticketTable => Ticket::getIndexName (),
234- $ itemTicketTable => Ticket::getForeignKeyField (),
235- ],
236- 'AND ' => [
237- "$ itemTicketTable.itemtype " => PluginFormcreatorFormAnswer::getType (),
238- ]
239- ],
240- ],
241- 'WHERE ' => [
242- "$ ticketTable.content " => ['LIKE ' , '% ' . $ pattern . '% ' ],
243- ],
244- ]);
255+ $ items = $ this ->getInvalidData (' > ' );
245256
246- $ count = $ tickets -> count ();
247- if ( $ count < 1 ) {
248- $ output -> writeln ( ' <info>-> No ticket to fix.</info> ' );
249- $ output ->writeln ("" );
250- return 0 ;
257+ // No items found, nothing to do
258+ $ count = $ items -> count ();
259+ if ( $ count === 0 ) {
260+ $ output ->writeln (__ ( " No invalid items found. " , " formcreator " ) );
261+ return ;
251262 }
252263
253- $ output -> write ( " <info>-> Found $ count tickets to clean (litteral > sign)</info> " );
254- $ output ->writeln ("" );
255- $ output -> write ( " <info>-> Cleaning tickets...</info> " );
256- $ output -> writeln ( "" );
257- foreach ($ tickets as $ row ) {
264+ // Init progress bar
265+ $ output ->writeln (__ ( " Found $ count item(s) to clean. " , " formcreator " ) );
266+ $ progress_bar = new ProgressBar ( $ output );
267+
268+ foreach ($ progress_bar -> iterate ( $ items ) as $ item ) {
258269 $ pattern = [
259270 ' > ' ,
260271 ];
261272 // Determine if we must use legacy or new encoding
262273 // @see Sanitizer::sanitize()
263274 $ replace = null ;
264- if (strpos ($ row ['content ' ], '< ' ) !== false && strpos ($ row ['content ' ], '#60; ' ) === false ) {
275+ if (strpos ($ item ['content ' ], '< ' ) !== false && strpos ($ item ['content ' ], '#60; ' ) === false ) {
265276 $ replace = [
266277 ' > ' ,
267278 ];
268- } else if (strpos ($ row ['content ' ], '#60 ' ) !== false && strpos ($ row ['content ' ], '< ' ) === false ) {
279+ } else if (strpos ($ item ['content ' ], '#60 ' ) !== false && strpos ($ item ['content ' ], '< ' ) === false ) {
269280 $ replace = [
270281 ' & ' ,
271282 ];
272283 }
273284 if ($ replace === null ) {
274- $ output ->write ("<error>-> Unable to determine the encoding type of ticket ID: " . $ row ['id ' ]. "</error> " );
285+ $ message = __ ("Unable to determine the encoding type of item ID: %1 $ d " , "formcreator " );
286+ $ output ->writeln ("<error> " . sprinf ($ message , $ item ['id ' ]) . "</error> " );
275287 continue ;
276288 }
277- $ row ['content ' ] = str_replace ($ pattern , $ replace , $ row ['content ' ]);
289+ $ item ['content ' ] = str_replace ($ pattern , $ replace , $ item ['content ' ]);
290+
278291 // Direct write to the table to avoid alteration of other fields
279292 $ DB ->update (
280- $ ticketTable ,
281- [
282- 'content ' => $ DB ->escape ($ row ['content ' ])
283- ],
284- [
285- 'id ' => $ row ['id ' ],
286- ]
293+ $ item ['itemtype ' ]::getTable (),
294+ ['content ' => $ DB ->escape ($ item ['content ' ])],
295+ ['id ' => $ item ['id ' ]]
287296 );
288297 }
298+ $ output ->writeln ("" );
289299 }
290300}
0 commit comments