1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
<?php /** * Translation set relocation tool. * Moves PO/MO pair and all related files to a new location */ class Loco_admin_file_MoveController extends Loco_admin_file_BaseController {
/** * {@inheritdoc} */ public function init(){ parent::init(); $file = $this->get('file'); /* @var Loco_fs_File $file */ if( $file->exists() && ! $file->isDirectory() ){ $files = new Loco_fs_Siblings($file); $files->setDomain( $this->getDomain() ); // nonce action will be specific to file for extra security $path = $file->getPath(); $action = 'move:'.$path; // set up view now in case of late failure $fields = new Loco_mvc_HiddenFields( [] ); $fields->setNonce( $action ); $fields['auth'] = 'move'; $fields['path'] = $this->get('path'); $this->set('hidden',$fields ); // attempt move if valid nonce posted back while( $this->checkNonce($action) ){ $post = Loco_mvc_PostParams::get(); // Chosen location should be valid as a posted "dest" parameter if( ! $post->has('dest') ){ Loco_error_AdminNotices::err('No destination posted'); break; } $target = new Loco_fs_LocaleFile( $post->dest ); $ext = $target->extension(); // could be a directory when we wanted the full path to the file if( $target->isDirectory() ){ Loco_error_AdminNotices::err('Enter the full path to the .'.$file->extension().' file, not the directory'); break; } // primary file extension should only be permitted to change between po and pot if( $ext !== $file->extension() && 'po' !== $ext && 'pot' !== $ext ){ Loco_error_AdminNotices::err('Invalid file extension, .po or .pot only'); break; } $target->normalize( loco_constant('WP_CONTENT_DIR') ); $target_dir = $target->getParent()->getPath(); // Primary file gives template remapping, so all files are renamed with same stub. // this can only be one of three things: (en -> en) or (foo-en -> en) or (en -> foo-en) // suffix will then consist of file extension, plus any other stuff like backup file date. $target_base = $target->filename(); $source_snip = strlen( $file->filename() ); // buffer all files to move to preempt write failures $movable = []; $api = new Loco_api_WordPressFileSystem; foreach( $files->expand() as $source ){ $suffix = substr( $source->basename(), $source_snip ); // <- e.g. "-backup.po~" $target = new Loco_fs_File( $target_dir.'/'.$target_base.$suffix ); // permit valid change of file extension on primary source file (po/pot) if( $source === $files->getSource() && $target->extension() !== $ext ){ $target = $target->cloneExtension($ext); } if( ! $api->authorizeMove($source,$target) ) { Loco_error_AdminNotices::err('Failed to authorize relocation of '.$source->basename() ); break 2; } $movable[] = [$source,$target]; } // commit moves. If any fail we'll have separated the files, which is bad $count = 0; $total = count($movable); foreach( $movable as $pair ){ try { $pair[0]->move( $pair[1] ); $count++; } catch( Loco_error_Exception $e ){ Loco_error_AdminNotices::add($e); } } // flash messages for display after redirect try { if( $count ) { // translators: %s is the quantity of files which were successfully moved Loco_data_Session::get()->flash( 'success', sprintf( _n( '%s file moved', '%s files moved', $total, 'loco-translate' ), $total ) ); } if( $total > $count ){ $diff = $total - $count; // translators: %s is the quantity of files which failed to be moved Loco_data_Session::get()->flash( 'error', sprintf( _n( '%s file could not be moved', '%s files could not be moved', $diff, 'loco-translate' ), $diff ) ); } Loco_data_Session::close(); } catch( Exception $e ){ // tolerate session failure } // redirect to bundle overview $href = Loco_mvc_AdminRouter::generate( $this->get('type').'-view', [ 'bundle' => $this->get('bundle') ] ); if( wp_redirect($href) ){ exit; } // end pseudo loop break; } } // set page title before render sets inline title $bundle = $this->getBundle(); // translators: Page title where %s is the name of a file to be moved $this->set('title', sprintf( __('Move %s','loco-translate'), $file->basename() ).' ‹ '.$bundle->getName() ); }
/** * {@inheritdoc} */ public function render(){ $file = $this->get('file'); if( $fail = $this->getFileError($file) ){ return $fail; } // relocation requires knowing text domain and locale $files = new Loco_fs_Siblings($file); try { $project = $this->getProject(); $files->setDomain( $project->getDomain()->getName() ); } catch( Loco_error_Exception $e ){ Loco_error_AdminNotices::warn($e->getMessage()); $project = null; } $file = new Loco_fs_LocaleFile( $files->getSource() ); $locale = $file->getLocale(); // switch between canonical move and custom file path mode $custom = is_null($project) || $this->get('custom') || 'po' !== $file->extension() || ! $locale->isValid(); // common page elements: $this->set('files',$files->expand() ); // phpcs:ignore -- duplicate string $this->setFileTitle($file,__('Move %s','loco-translate')); $this->enqueueScript('move'); // set info for existing file location $content_dir = loco_constant('WP_CONTENT_DIR'); $current = $file->getRelativePath($content_dir); $parent = new Loco_fs_LocaleDirectory( $file->dirname() ); $typeId = $parent->getTypeId(); $this->set('current', new Loco_mvc_ViewParams([ 'path' => $parent->getRelativePath($content_dir), 'type' => $parent->getTypeLabel($typeId), ]) ); // moving files will require deletion permission on current file location // plus write permission on target location, but we don't know what that is yet. $fields = $this->prepareFsConnect('move',$current); $fields['path'] = ''; $fields['dest'] = ''; // custom file move template (POT mode) if( $custom ){ $this->get('hidden')->offsetSet('custom','1'); $this->set('file', Loco_mvc_FileParams::create($file) ); return $this->view('admin/file/move-pot'); } // establish valid locations for translation set, which may include current: $filechoice = $project->initLocaleFiles($locale); // start with current location so always first in list $locations = []; $locations[$typeId] = new Loco_mvc_ViewParams( [ 'label' => $parent->getTypeLabel($typeId), 'paths' => [ new Loco_mvc_ViewParams( [ 'path' => $current, 'active' => true, ] ) ] ] ); /* @var Loco_fs_File $pofile */ foreach( $filechoice as $pofile ){ $relpath = $pofile->getRelativePath($content_dir); if( $current === $relpath ){ continue; } // initialize location type (system, etc..) $parent = new Loco_fs_LocaleDirectory( $pofile->dirname() ); $typeId = $parent->getTypeId(); if( ! isset($locations[$typeId]) ){ $locations[$typeId] = new Loco_mvc_ViewParams( [ 'label' => $parent->getTypeLabel($typeId), 'paths' => [], ] ); } $choice = new Loco_mvc_ViewParams( [ 'path' => $relpath, ] ); $locations[$typeId]['paths'][] = $choice; } $this->set('locations', $locations ); $this->set('advanced', $_SERVER['REQUEST_URI'].'&custom=1' ); return $this->view('admin/file/move-po'); }
}
|