31
31
32
32
namespace GlpiPlugin \Formcreator \Command ;
33
33
34
+ use ITILFollowup ;
35
+ use QuerySubQuery ;
34
36
use Symfony \Component \Console \Command \Command ;
37
+ use Symfony \Component \Console \Helper \ProgressBar ;
35
38
use Symfony \Component \Console \Input \InputInterface ;
36
39
use Symfony \Component \Console \Output \OutputInterface ;
37
40
use Ticket ;
38
41
use Item_Ticket ;
39
42
use PluginFormcreatorFormAnswer ;
40
- use Glpi \Toolbox \Sanitizer ;
41
43
42
44
class CleanTicketsCommand extends Command
43
45
{
@@ -48,66 +50,108 @@ protected function configure() {
48
50
}
49
51
50
52
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> " );
53
55
54
56
$ this ->fixBadForm_1 ($ input , $ output );
55
57
$ this ->fixBadForm_2 ($ input , $ output );
56
58
$ this ->fixBadForm_3 ($ input , $ output );
57
59
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 ;
60
65
}
61
66
62
67
/**
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
65
69
*
66
- * @param InputInterface $input
67
- * @param OutputInterface $output
68
- * @return void
70
+ * @param string $invalid_content_pattern
71
+ *
72
+ * @return iterable
69
73
*/
70
- protected function fixBadForm_1 ( InputInterface $ input , OutputInterface $ output ) {
74
+ protected function getInvalidData ( string $ invalid_content_pattern ): iterable {
71
75
global $ DB ;
72
76
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 ,
83
88
'INNER JOIN ' => [
84
- $ itemTicketTable => [
89
+ $ item_ticket_table => [
85
90
'FKEY ' => [
86
- $ ticketTable => Ticket::getIndexName (),
87
- $ itemTicketTable => Ticket::getForeignKeyField (),
91
+ $ ticket_table => Ticket::getIndexName (),
92
+ $ item_ticket_table => Ticket::getForeignKeyField (),
88
93
],
89
94
'AND ' => [
90
- "$ itemTicketTable .itemtype " => PluginFormcreatorFormAnswer::getType (),
95
+ "$ item_ticket_table .itemtype " => PluginFormcreatorFormAnswer::getType (),
91
96
]
92
97
],
93
98
],
94
99
'WHERE ' => [
95
- "$ ticketTable .content " => ['LIKE ' , '% ' . $ pattern . '% ' ], // Matches bad encoding for '<'
100
+ "$ ticket_table .content " => ['LIKE ' , '% ' . $ invalid_content_pattern . '% ' ],
96
101
],
97
102
]);
98
103
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
+ ]);
105
118
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
109
134
$ 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 ) {
111
155
$ pattern = [
112
156
'/&lt;([a-z0-9]+?)&gt;/ ' ,
113
157
'/&lt;(\/[a-z0-9]+?)&gt;/ ' ,
@@ -116,18 +160,16 @@ protected function fixBadForm_1(InputInterface $input, OutputInterface $output)
116
160
'<$1> ' ,
117
161
'<$1> ' ,
118
162
];
119
- $ row ['content ' ] = preg_replace ($ pattern , $ replace , $ row ['content ' ]);
163
+ $ item ['content ' ] = preg_replace ($ pattern , $ replace , $ item ['content ' ]);
164
+
120
165
// Direct write to the table to avoid alteration of other fields
121
166
$ 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 ' ]]
129
170
);
130
171
}
172
+ $ output ->writeln ("" );
131
173
}
132
174
133
175
/**
@@ -140,72 +182,56 @@ protected function fixBadForm_1(InputInterface $input, OutputInterface $output)
140
182
protected function fixBadForm_2 (InputInterface $ input , OutputInterface $ output ) {
141
183
global $ DB ;
142
184
185
+ // Print step info
186
+ $ output ->writeln ("" );
187
+ $ message = __ ("Step 2: literal BR tag. " , "formcreator " );
188
+ $ output ->writeln ("<info> $ message</info> " );
189
+
143
190
// 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 /'
165
192
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 ;
171
198
}
172
199
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 ) {
178
205
$ pattern = [
179
206
'<br /> ' ,
180
207
];
181
208
// Determine if we must use legacy or new encoding
182
209
// @see Sanitizer::sanitize()
183
210
$ replace = null ;
184
- if (strpos ($ row ['content ' ], '< ' ) !== false && strpos ($ row ['content ' ], '#60; ' ) === false ) {
211
+ if (strpos ($ item ['content ' ], '< ' ) !== false && strpos ($ item ['content ' ], '#60; ' ) === false ) {
185
212
$ replace = [
186
213
'<br /> ' ,
187
214
];
188
- } else if (strpos ($ row ['content ' ], '#60 ' ) !== false && strpos ($ row ['content ' ], '< ' ) === false ) {
215
+ } else if (strpos ($ item ['content ' ], '#60 ' ) !== false && strpos ($ item ['content ' ], '< ' ) === false ) {
189
216
$ replace = [
190
217
'<br /> ' ,
191
218
];
192
219
}
193
220
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> " );
195
223
continue ;
196
224
}
197
- $ row ['content ' ] = str_replace ($ pattern , $ replace , $ row ['content ' ]);
225
+ $ item ['content ' ] = str_replace ($ pattern , $ replace , $ item ['content ' ]);
226
+
198
227
// Direct write to the table to avoid alteration of other fields
199
228
$ 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 ' ]]
207
232
);
208
233
}
234
+ $ output ->writeln ("" );
209
235
}
210
236
211
237
/**
@@ -220,71 +246,55 @@ protected function fixBadForm_2(InputInterface $input, OutputInterface $output)
220
246
protected function fixBadForm_3 (InputInterface $ input , OutputInterface $ output ) {
221
247
global $ DB ;
222
248
249
+ // Print step info
250
+ $ output ->writeln ("" );
251
+ $ message = __ ("Step 3: litteral > sign. " , "formcreator " );
252
+ $ output ->writeln ("<info> $ message</info> " );
253
+
223
254
// 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 (' > ' );
245
256
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 ;
251
262
}
252
263
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 ) {
258
269
$ pattern = [
259
270
' > ' ,
260
271
];
261
272
// Determine if we must use legacy or new encoding
262
273
// @see Sanitizer::sanitize()
263
274
$ replace = null ;
264
- if (strpos ($ row ['content ' ], '< ' ) !== false && strpos ($ row ['content ' ], '#60; ' ) === false ) {
275
+ if (strpos ($ item ['content ' ], '< ' ) !== false && strpos ($ item ['content ' ], '#60; ' ) === false ) {
265
276
$ replace = [
266
277
' > ' ,
267
278
];
268
- } else if (strpos ($ row ['content ' ], '#60 ' ) !== false && strpos ($ row ['content ' ], '< ' ) === false ) {
279
+ } else if (strpos ($ item ['content ' ], '#60 ' ) !== false && strpos ($ item ['content ' ], '< ' ) === false ) {
269
280
$ replace = [
270
281
' & ' ,
271
282
];
272
283
}
273
284
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> " );
275
287
continue ;
276
288
}
277
- $ row ['content ' ] = str_replace ($ pattern , $ replace , $ row ['content ' ]);
289
+ $ item ['content ' ] = str_replace ($ pattern , $ replace , $ item ['content ' ]);
290
+
278
291
// Direct write to the table to avoid alteration of other fields
279
292
$ 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 ' ]]
287
296
);
288
297
}
298
+ $ output ->writeln ("" );
289
299
}
290
300
}
0 commit comments