/var/www/html_it/wp-content/plugins/loco-translate/src/ajax/FsReferenceController.php


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
<?php
/**
 * Ajax service that returns source code for a given file system reference
 * Currently this is only PHP, but could theoretically be any file type.
 */
class Loco_ajax_FsReferenceController extends Loco_ajax_common_BundleController {


    
/**
     * @param string $refpath
     * @return Loco_fs_File
     */
    
private function findSourceFile$refpath ){

        
// reference may be resolvable via referencing PO file's location
        
$pofile = new Loco_fs_File$this->get('path') );
        
$pofile->normalizeloco_constant('WP_CONTENT_DIR') );
        if( ! 
$pofile->exists() ){
            throw new 
InvalidArgumentException('PO/POT file required to resolve reference');
        }
        
$search = new Loco_gettext_SearchPaths;
        
$search->init($pofile);
        if( 
$srcfile $search->match($refpath) ){
            return 
$srcfile;
        }

        
// check against PO file location when no search paths or search paths failed
        
$srcfile = new Loco_fs_File($refpath);
        
$srcfile->normalize$pofile->dirname() );
        if( 
$srcfile->exists() ){
            return 
$srcfile;
        }

        
// reference may be resolvable via known project roots
        
try {
            
$bundle $this->getBundle();
            
// Loco extractions will always be relative to bundle root
            
$srcfile = new Loco_fs_File$refpath );
            
$srcfile->normalize$bundle->getDirectoryPath() );
            if( 
$srcfile->exists() ){
                return 
$srcfile;
            }
            
            
// check relative to parent theme root
            
if( $bundle->isTheme() && ( $parent $bundle->getParent() ) ){
                
$srcfile = new Loco_fs_File$refpath );
                
$srcfile->normalize$parent->getDirectoryPath() );
                if( 
$srcfile->exists() ){
                    return 
$srcfile;
                }
            }
    
            
// final attempt - search all project source roots
            // TODO is there too large a risk of false positives? especially with files like index.php
            /* @var $root Loco_fs_Directory */
            /*foreach( $this->getProject($bundle)->getConfiguredSources() as $root ){
                if( $root->isDirectory() ){
                    $srcfile = new Loco_fs_File( $refpath );
                    $srcfile->normalize( $root->getPath() );
                    if( $srcfile->exists() ){
                        return $srcfile;
                    }
                }
            }*/
        
}
        catch( 
Loco_error_Exception $e ){
            
// permitted for there to be no bundle or project when viewing orphaned file
        
}
        
        throw new 
Loco_error_Exceptionsprintf('Failed to find source file matching "%s"',$refpath) );
    }



    
/**
     * {@inheritdoc}
     */
    
public function render(){
        
$post $this->validate();
        
        
// at the very least we need a reference to examine
        
if( ! $post->has('ref') ){
            throw new 
InvalidArgumentException('ref parameter required');
        }
        
        
// reference must parse as <path>:<line>
        
$refpath $post->ref;
        if( 
preg_match('/^(.+):(\\d+)$/'$refpath$r ) ){
            
$refpath $r[1];
            
$refline = (int) $r[2];
        }
        else {
            
$refline 0;
        }
        
        
// find file or fail
        
$srcfile $this->findSourceFile($refpath);
        
        
// deny access to sensitive files
        
if( 'wp-config.php' === $srcfile->basename() ){
            throw new 
InvalidArgumentException('File access disallowed');
        }
        
        
// validate allowed source file types, including custom aliases
        
$conf Loco_data_Settings::get();
        
$ext strtolower$srcfile->extension() );
        
$type $conf->ext2type($ext,'none');
        if( 
'none' === $type ){
            throw new 
InvalidArgumentException('File extension disallowed, '.$ext );
        }

        
$this->set('type'$type );
        
$this->set('line'$refline );
        
$this->set('path'$srcfile->getRelativePathloco_constant('WP_CONTENT_DIR') ) );
        
        
// source code will be HTML-tokenized into multiple lines
        
$code = [];
        
        
// observe the same size limits for source highlighting as for string extraction as tokenizing will use the same amount of juice
        
$maxbytes wp_convert_hr_to_bytes$conf->max_php_size );
        
        
// tokenizers require gettext utilities, easiest just to ping the extraction library
        
if( ! class_exists('Loco_gettext_Extraction',true) ){
            throw new 
RuntimeException('Failed to load tokenizers'); // @codeCoverageIgnore
        
}
        
        
// PHP is the most likely format. 
        
if( 'php' === $type && ( $srcfile->size() <= $maxbytes ) && loco_check_extension('tokenizer') ) {
            
$tokens = new LocoPHPTokenstoken_get_all$srcfile->getContents() ) );
        }
        else if( 
'js' === $type ){
            
$tokens = new LocoJsTokens$srcfile->getContents() );
            
$tokens->allow(T_WHITESPACE);
        }
        else {
            
$tokens null;
        }

        
// highlighting on back end because tokenizer provides more control than highlight.js
        
if( $tokens instanceof LocoTokensInterface ){
            
$thisline 1;
            while( 
$tok $tokens->advance() ){
                if( 
is_array($tok) ){
                    list( 
$t$str$startline ) = $tok;
                    
$clss token_name($t);
                    
// tokens can span multiple lines (whitespace/html/comments)
                    
$lines preg_split('/\\R/'$str );
                }
                else {
                    
// scalar symbol will always start on the line that the previous token ended on
                    
$clss 'T_NONE';
                    
$lines = [ $tok ];
                    
$startline $thisline;
                }
                
// token can span multiple lines, so include only bytes on required line[s]
                
foreach( $lines as $i => $line ){
                    
// pad missing lines. $code must be contiguous
                    
$thisline $startline $i;
                    
$j $thisline 1;
                    while( 
count($code) < $j ){
                        
$code[] = '<code class="T_NONE"> </code>';
                    }
                    
// append highlighted token to current line
                    
$html '<code class="'.$clss.'">'.htmlentities($line,ENT_COMPAT,'UTF-8').'</code>';
                    if( isset(
$code[$j]) ){
                        
$code[$j] .= $html;
                    }
                    else {
                        
$code[$j] = $html;
                    }
                }
            }
        }
        
// permit limited other file types, but without back end highlighting
        
else {
            foreach( 
preg_split'/\\R/u'$srcfile->getContents() ) as $line ){
                
$code[] = '<code>'.htmlentities($line,ENT_COMPAT,'UTF-8').'</code>';
            }
        }
        
        
// allow 0 line reference when line is unknown (e.g. block.json) else it must exist
        
if( $refline && ! isset($code[$refline-1]) ){
            throw new 
Loco_error_Exceptionsprintf('Line %u not in source file'$refline) );
        }
 
        
$this->set'code'$code );

        return 
parent::render();
    }
    
    
}