';
112 | }
113 | return $php_file_tree;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/includes/functions/files.php:
--------------------------------------------------------------------------------
1 | If my_function returns true, continue.
22 | * Otherwise the result of my_function() is printed as an error string.
23 | * USAGE 2: files::catch_errors('MY_ERROR', my_function()); => If my_function returns true, continue.
24 | * If my_function returns false, print the string MY_ERROR.
25 | * USAGE 3: files::catch_errors('MY_ERROR'); => Print the string MY_ERROR.
26 | * @param bool|string $error The text to display in the case of an error. True if there were no errors.
27 | * @param bool $result The result of the function what we need to catch errors of. True if there were no errors.
28 | * @return bool $result
29 | */
30 | public static function catch_errors($error, $result = false)
31 | {
32 | if ($error === true)
33 | {
34 | return true;
35 | }
36 | if (!$result)
37 | {
38 | // Catch solutions.
39 | if (is_array($error) && isset($error['solution']))
40 | {
41 | self::$catched_solutions = (isset(self::$catched_solutions)) ? self::$catched_solutions . " " . $error['solution'] : $error['solution'];
42 | objects::$template->assign_var('UPLOAD_ERROR_SOLUTIONS', self::$catched_solutions);
43 | $error = $error['error'];
44 | }
45 | self::$catched_errors = $error = (isset(self::$catched_errors)) ? self::$catched_errors . " " . $error : $error;
46 | objects::$template->assign_var('UPLOAD_ERROR', $error);
47 | }
48 | return $result;
49 | }
50 |
51 | /**
52 | * The function that searches for composer.json file.
53 | * @param string $dir The directory to search in.
54 | * @return string/bool The path to composer.json file, false in case of an error.
55 | */
56 | public static function getComposer($dir)
57 | {
58 | if (@is_file($dir . '/composer.json'))
59 | {
60 | return $dir . '/composer.json';
61 | }
62 | $ffs = @scandir($dir);
63 | if (!$ffs)
64 | {
65 | return false;
66 | }
67 | $composer = false;
68 | foreach ($ffs as $ff)
69 | {
70 | if ($ff != '.' && $ff != '..')
71 | {
72 | if (@is_dir($dir . '/' . $ff))
73 | {
74 | $composer = self::getComposer($dir . '/' . $ff);
75 | }
76 | if ($composer !== false)
77 | {
78 | return $composer;
79 | }
80 | }
81 | }
82 | return $composer;
83 | }
84 |
85 | /**
86 | * Function to remove folders and files.
87 | * @param string $dir The directory for removal.
88 | * @param bool $no_errors Whether there were errors before.
89 | * @return bool|string True if there are no errors, error string otherwise.
90 | */
91 | public static function rrmdir($dir, $no_errors = true)
92 | {
93 | if (@is_dir($dir))
94 | {
95 | $files = @scandir($dir);
96 | if ($files === false)
97 | {
98 | return objects::$user->lang('ERROR_REMOVE_DIRECTORY', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dir));
99 | }
100 | foreach ($files as $file)
101 | {
102 | if ($file != '.' && $file != '..')
103 | {
104 | $no_errors = self::rrmdir($dir . '/' . $file, $no_errors);
105 | }
106 | }
107 | if (!(@rmdir($dir)))
108 | {
109 | return objects::$user->lang('ERROR_REMOVE_DIRECTORY', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dir));
110 | }
111 | }
112 | else if (@file_exists($dir))
113 | {
114 | if (!(@unlink($dir)))
115 | {
116 | return objects::$user->lang('ERROR_REMOVE_DIRECTORY', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dir));
117 | }
118 | }
119 | return $no_errors;
120 | }
121 |
122 | /**
123 | * Function to copy folders and files.
124 | * @param string $src The path 'from'.
125 | * @param string $dst The path 'to'.
126 | * @return bool|string True if there are no errors, error string otherwise.
127 | */
128 | public static function rcopy($src, $dst)
129 | {
130 | if (@file_exists($dst))
131 | {
132 | if (self::rrmdir($dst) !== true)
133 | {
134 | return objects::$user->lang('ERROR_COPY_FILE', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $src), str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dst));
135 | }
136 | }
137 | if (@is_dir($src))
138 | {
139 | if (self::recursive_mkdir($dst, 0755) !== true)
140 | {
141 | return objects::$user->lang('ERROR_COPY_FILE', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $src), str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dst));
142 | }
143 | $files = @scandir($src);
144 | if ($files === false)
145 | {
146 | return objects::$user->lang('ERROR_COPY_FILE', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $src), str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dst));
147 | }
148 | foreach ($files as $file)
149 | {
150 | if ($file != '.' && $file != '..')
151 | {
152 | if (self::rcopy($src . '/' . $file, $dst . '/' . $file) !== true)
153 | {
154 | return objects::$user->lang('ERROR_COPY_FILE', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $src), str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dst));
155 | }
156 | }
157 | }
158 | }
159 | else if (@file_exists($src))
160 | {
161 | if (!(@copy($src, $dst)))
162 | {
163 | return objects::$user->lang('ERROR_COPY_FILE', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $src), str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $dst));
164 | }
165 | }
166 | return true;
167 | }
168 |
169 | /**
170 | * Saves the contents of a file or a directory in a zip archive file.
171 | * @param string $dest_file The path to the contents for adding to the zip file.
172 | * @param string $dest_name The name of the zip file.
173 | * @param string $zip_dir The directory for saving zip files.
174 | */
175 | public static function save_zip_archive($dest_file, $dest_name, $zip_dir)
176 | {
177 | if (!class_exists('\compress_zip'))
178 | {
179 | include(objects::$phpbb_root_path . 'includes/functions_compress.' . objects::$phpEx);
180 | }
181 |
182 | // Remove additional ext/ prefix.
183 | $src_rm_prefix = (strpos($dest_file, 'ext/') === 0) ? substr($dest_file, 0, 4) : '';
184 | $zip = new \compress_zip('w', $zip_dir . '/' . $dest_name . '.zip');
185 | $zip->add_file($dest_file, $src_rm_prefix);
186 | $zip->close();
187 | }
188 |
189 | /**
190 | * @author Michal Nazarewicz (from the php manual)
191 | * Creates all non-existant directories in a path
192 | * @param $path - path to create
193 | * @param $mode - CHMOD the new dir to these permissions
194 | * @return bool|string True if there are no errors, error string otherwise.
195 | */
196 | public static function recursive_mkdir($path, $mode = 0755)
197 | {
198 | $dirs = explode('/', $path);
199 | $count = sizeof($dirs);
200 | $path = '.';
201 | for ($i = 0; $i < $count; $i++)
202 | {
203 | $path .= '/' . $dirs[$i];
204 |
205 | if (!is_dir($path))
206 | {
207 | @mkdir($path, $mode);
208 | @chmod($path, $mode);
209 |
210 | if (!is_dir($path))
211 | {
212 | return objects::$user->lang('ERROR_CREATE_DIRECTORY', str_replace(objects::$phpbb_root_path, 'PHPBB_ROOT/', $path));
213 | }
214 | }
215 | }
216 | return true;
217 | }
218 |
219 | /**
220 | * Gets languages in the specified directory.
221 | * @param string $path The path to the language directory without slash at the end.
222 | * @return array
223 | */
224 | public static function get_languages($path)
225 | {
226 | $return = array();
227 | if (@is_dir($path))
228 | {
229 | $files = @scandir($path);
230 | if ($files === false)
231 | {
232 | return $return;
233 | }
234 | $type_cast_helper = new \phpbb\request\type_cast_helper();
235 | foreach ($files as $file)
236 | {
237 | if ($file != '.' && $file != '..' && @is_dir($path . '/' . $file))
238 | {
239 | $type_cast_helper->set_var($file, $file, gettype($file), true);
240 | $return[] = $file;
241 | }
242 | }
243 | }
244 | return $return;
245 | }
246 |
247 | /**
248 | * Check whether acp/ and/or adm/ directories exist in the specified directory.
249 | * @param string $path The path to the directory without slash at the end.
250 | * @return bool
251 | */
252 | public static function check_acp_and_adm($path)
253 | {
254 | if (@is_dir($path))
255 | {
256 | $files = @scandir($path);
257 | if ($files === false)
258 | {
259 | return false;
260 | }
261 | foreach ($files as $file)
262 | {
263 | if (($file == 'acp' || $file == 'adm') && @is_dir($path . '/' . $file))
264 | {
265 | return true;
266 | }
267 | }
268 | }
269 | return false;
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/includes/functions/languages.php:
--------------------------------------------------------------------------------
1 | create_metadata_manager(objects::$upload_ext_name);
28 | try
29 | {
30 | // Validate extension's metadata
31 | objects::$md_manager->get_metadata('all');
32 | }
33 | catch (\phpbb\extension\exception $e)
34 | {
35 | $message = objects::$compatibility->get_exception_message($e);
36 | files::catch_errors($message);
37 | }
38 | }
39 |
40 | /**
41 | * Checks the availability of an update for Upload Extensions.
42 | */
43 | public static function check_updates()
44 | {
45 | $upload_extensions_download = false;
46 | try
47 | {
48 | $updates_available = objects::$compatibility->version_check(objects::$md_manager, objects::$request->variable('versioncheck_force', false), false, objects::$config['extension_force_unstable'] ? 'unstable' : null);
49 |
50 | objects::$template->assign_vars(array(
51 | 'UPLOAD_EXT_NEW_UPDATE' => !empty($updates_available),
52 | 'S_UPLOAD_UP_TO_DATE' => empty($updates_available),
53 | 'S_UPLOAD_VERSIONCHECK' => true,
54 | 'UPLOAD_UP_TO_DATE_MSG' => objects::$user->lang(empty($updates_available) ? 'UP_TO_DATE' : 'NOT_UP_TO_DATE', objects::$md_manager->get_metadata('display-name')),
55 | ));
56 |
57 | objects::$template->assign_block_vars('upload_updates_available', $updates_available);
58 |
59 | if (isset($updates_available['download']))
60 | {
61 | $upload_extensions_download = $updates_available['download'];
62 | }
63 | }
64 | catch (\RuntimeException $e)
65 | {
66 | objects::$template->assign_vars(array(
67 | 'S_UPLOAD_VERSIONCHECK_STATUS' => $e->getCode(),
68 | 'UPLOAD_VERSIONCHECK_FAIL_REASON' => ($e->getMessage() !== objects::$user->lang('VERSIONCHECK_FAIL')) ? $e->getMessage() : '',
69 | ));
70 | }
71 | objects::$self_update = $upload_extensions_download;
72 | }
73 |
74 | /**
75 | * Generates the link to Upload Extensions Updater.
76 | *
77 | * @return string Download link.
78 | */
79 | public static function get_update_link()
80 | {
81 | global $phpbb_admin_path, $phpEx;
82 | return append_sid("{$phpbb_admin_path}index.$phpEx", "i=-boardtools-updater-acp-updater_module&mode=main&action=upload");
83 | }
84 |
85 | /**
86 | * Sets the link to Upload Extensions Updater.
87 | */
88 | public static function set_update_link()
89 | {
90 | if (objects::$phpbb_extension_manager->is_enabled("boardtools/updater") && objects::$self_update != false)
91 | {
92 | objects::$template->assign_var('U_UPLOAD_EXT_UPDATE', self::get_update_link());
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/includes/objects.php:
--------------------------------------------------------------------------------
1 | init();
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/includes/sources/cache.php:
--------------------------------------------------------------------------------
1 | get_driver()->cache_dir . self::$cache_prefix;
45 | }
46 |
47 | public static function read($file)
48 | {
49 | $file = self::get_root() . preg_replace('{[^' . self::$whitelist . ']}i', '-', $file);
50 | if (self::is_enabled() && file_exists($file))
51 | {
52 | return file_get_contents($file);
53 | }
54 |
55 | return false;
56 | }
57 |
58 | public static function write($file, $data)
59 | {
60 | if (self::is_enabled())
61 | {
62 | $file = self::get_root() . preg_replace('{[^' . self::$whitelist . ']}i', '-', $file);
63 |
64 | $lock = new \phpbb\lock\flock($file);
65 | $lock->acquire();
66 |
67 | if ($handle = @fopen($file, 'wb'))
68 | {
69 | fwrite($handle, $data);
70 | fclose($handle);
71 |
72 | phpbb_chmod($file, CHMOD_READ | CHMOD_WRITE);
73 |
74 | $return_value = true;
75 | }
76 | else
77 | {
78 | $return_value = false;
79 | }
80 |
81 | $lock->release();
82 |
83 | return $return_value;
84 | }
85 |
86 | return false;
87 | }
88 |
89 | public static function sha1($file)
90 | {
91 | $file = self::get_root() . preg_replace('{[^' . self::$whitelist . ']}i', '-', $file);
92 | if (self::is_enabled() && file_exists($file))
93 | {
94 | return sha1_file($file);
95 | }
96 |
97 | return false;
98 | }
99 |
100 | public static function sha256($file)
101 | {
102 | $file = self::get_root() . preg_replace('{[^' . self::$whitelist . ']}i', '-', $file);
103 | if (self::is_enabled() && file_exists($file))
104 | {
105 | return hash_file('sha256', $file);
106 | }
107 |
108 | return false;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/includes/sources/extensions_list.php:
--------------------------------------------------------------------------------
1 | $versions)
171 | {
172 | foreach ($versions as $version => $metadata)
173 | {
174 | $packages[] = $metadata;
175 | }
176 | }
177 | }
178 |
179 | if (isset($data['includes']))
180 | {
181 | foreach ($data['includes'] as $include => $metadata)
182 | {
183 | if (cache::sha1($include) === $metadata['sha1'])
184 | {
185 | $includedData = json_decode(cache::read($include), true);
186 | }
187 | else
188 | {
189 | $includedData = self::fetchFile($include);
190 | }
191 | $packages = array_merge($packages, self::loadIncludes($includedData));
192 | }
193 | }
194 |
195 | return $packages;
196 | }
197 |
198 | protected static function fetchFile($filename, $cacheKey = null, $sha256 = null)
199 | {
200 | $file = $filename;
201 | if (null === $cacheKey)
202 | {
203 | $cacheKey = $filename;
204 | $file = self::$baseUrl . '/' . $filename;
205 | }
206 |
207 | // url-encode $ signs in URLs as bad proxies choke on them
208 | if (($pos = strpos($file, '$')) && preg_match('{^https?://.*}i', $file))
209 | {
210 | $file = substr($file, 0, $pos) . '%24' . substr($file, $pos + 1);
211 | }
212 |
213 | $json = @file_get_contents($file);
214 | if ($sha256 && $sha256 !== hash('sha256', $json))
215 | {
216 | return false;
217 | }
218 | $data = json_decode($json, true);
219 | if ($cacheKey && !empty($json))
220 | {
221 | cache::write($cacheKey, $json);
222 | }
223 |
224 | return $data;
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/includes/types/zip.php:
--------------------------------------------------------------------------------
1 | factory = $factory;
51 | $this->language = $language;
52 | $this->php_ini = $php_ini;
53 | $this->request = $request;
54 | $this->phpbb_root_path = $phpbb_root_path;
55 | }
56 |
57 | /**
58 | * {@inheritdoc}
59 | */
60 | public function upload()
61 | {
62 | $args = func_get_args();
63 | return $this->remote_upload($args[0]);
64 | }
65 |
66 | /**
67 | * Remote upload method
68 | * Uploads file from given url
69 | *
70 | * @param string $upload_url URL pointing to file to upload, for example http://www.foobar.com/example.gif
71 | * @return filespec $file Object "filespec" is returned, all further operations can be done with this object
72 | * @access public
73 | */
74 | protected function remote_upload($upload_url)
75 | {
76 | $upload_ary = array();
77 | $upload_ary['local_mode'] = true;
78 |
79 | $upload_from_phpbb = preg_match(objects::$phpbb_link_template, $upload_url, $match_phpbb);
80 |
81 | if (!preg_match('#^(https?://).*?\.(' . implode('|', $this->upload->allowed_extensions) . ')$#i', $upload_url, $match) && !$upload_from_phpbb)
82 | {
83 | return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'URL_INVALID'));
84 | }
85 |
86 | $url = parse_url($upload_url);
87 |
88 | $host = $url['host'];
89 | $path = $url['path'];
90 | $port = (!empty($url['port'])) ? (int) $url['port'] : 80;
91 |
92 | $upload_ary['type'] = 'application/octet-stream';
93 |
94 | $url['path'] = explode('.', $url['path']);
95 | $ext = array_pop($url['path']);
96 |
97 | $url['path'] = implode('', $url['path']);
98 | $upload_ary['name'] = utf8_basename($url['path']) . (($ext) ? '.' . $ext : '');
99 | $filename = $url['path'];
100 | $filesize = 0;
101 |
102 | $remote_max_filesize = $this->get_max_file_size();
103 |
104 | $errno = 0;
105 | $errstr = '';
106 |
107 | if (!($fsock = @fopen($upload_url, "r")))
108 | {
109 | return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED'));
110 | }
111 |
112 | // Make sure $path not beginning with /
113 | if (strpos($path, '/') === 0)
114 | {
115 | $path = substr($path, 1);
116 | }
117 |
118 | $get_info = false;
119 | $data = '';
120 | $length = false;
121 | $timer_stop = time() + $this->upload->upload_timeout;
122 |
123 | while (!@feof($fsock))
124 | {
125 | if ($length)
126 | {
127 | // Don't attempt to read past end of file if server indicated length
128 | $block = @fread($fsock, min($length - $filesize, 1024));
129 | }
130 | else
131 | {
132 | $block = @fread($fsock, 1024);
133 | }
134 |
135 | $filesize += strlen($block);
136 |
137 | if ($remote_max_filesize && $filesize > $remote_max_filesize)
138 | {
139 | $max_filesize = get_formatted_filesize($remote_max_filesize, false);
140 |
141 | return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']));
142 | }
143 |
144 | $data .= $block;
145 |
146 | // Cancel upload if we exceed timeout
147 | if (time() >= $timer_stop)
148 | {
149 | return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'REMOTE_UPLOAD_TIMEOUT');
150 | }
151 | }
152 | @fclose($fsock);
153 |
154 | if (empty($data))
155 | {
156 | return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'EMPTY_REMOTE_DATA');
157 | }
158 |
159 | $tmp_path = (@is_writable('/tmp/')) ? '/tmp/' : $this->phpbb_root_path . 'cache/';
160 | $filename = tempnam($tmp_path, unique_id() . '-');
161 |
162 | if (!($fp = @fopen($filename, 'wb')))
163 | {
164 | return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'NOT_UPLOADED');
165 | }
166 |
167 | $upload_ary['size'] = fwrite($fp, $data);
168 | fclose($fp);
169 | unset($data);
170 |
171 | $upload_ary['tmp_name'] = $filename;
172 | if ($upload_from_phpbb)
173 | {
174 | $upload_ary['name'] .= '.zip';
175 | }
176 |
177 | /** @var filespec $file */
178 | $file = $this->factory->get('filespec')
179 | ->set_upload_ary($upload_ary)
180 | ->set_upload_namespace($this->upload);
181 | $this->upload->common_checks($file);
182 |
183 | return $file;
184 | }
185 |
186 | /**
187 | * Get maximum file size for remote uploads
188 | *
189 | * @return int Maximum file size
190 | */
191 | protected function get_max_file_size()
192 | {
193 | $max_file_size = $this->upload->max_filesize;
194 | if (!$max_file_size)
195 | {
196 | $max_file_size = $this->php_ini->getString('upload_max_filesize');
197 |
198 | if (!empty($max_file_size))
199 | {
200 | $unit = strtolower(substr($max_file_size, -1, 1));
201 | $max_file_size = (int) $max_file_size;
202 |
203 | switch ($unit)
204 | {
205 | case 'g':
206 | $max_file_size *= 1024;
207 | // no break
208 | case 'm':
209 | $max_file_size *= 1024;
210 | // no break
211 | case 'k':
212 | $max_file_size *= 1024;
213 | // no break
214 | }
215 | }
216 | }
217 |
218 | return $max_file_size;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/includes/upload/base.php:
--------------------------------------------------------------------------------
1 | data['user_id'];
30 | }
31 |
32 | /**
33 | * Sets the path to the temporary storage directory.
34 | *
35 | * @param bool $clean Whether we need to delete any previous contents of temporary directory
36 | * @return bool Whether the path has been set correctly with no errors
37 | */
38 | protected function set_temp_path($clean = true)
39 | {
40 | // We need to use the user ID and the time to escape from problems with simultaneous uploads.
41 | // We suppose that one user can upload only one extension per session.
42 | $this->ext_tmp = $this->get_temp_path();
43 |
44 | // Ensure that we don't have any previous files in the working directory.
45 | if ($clean && is_dir($this->ext_tmp))
46 | {
47 | if (!(files::catch_errors(files::rrmdir($this->ext_tmp))))
48 | {
49 | return false;
50 | }
51 | }
52 |
53 | return true;
54 | }
55 |
56 | /**
57 | * Extracts the specified ZIP file to the temporary storage directory.
58 | *
59 | * @param string $dest_file Path to ZIP file that we need to extract
60 | */
61 | protected function extract_zip($dest_file)
62 | {
63 | if (!class_exists('\compress_zip'))
64 | {
65 | include(objects::$phpbb_root_path . 'includes/functions_compress.' . objects::$phpEx);
66 | }
67 |
68 | $zip = new \compress_zip('r', $dest_file);
69 | $zip->extract($this->ext_tmp . '/extracted/');
70 | $zip->close();
71 | }
72 |
73 | /**
74 | * Original copyright information for the function from AutoMOD.
75 | * The function was almost totally changed by the authors of Upload Extensions.
76 | * @package automod
77 | * @copyright (c) 2008 phpBB Group
78 | * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
79 | *
80 | * @param string $action Requested action.
81 | * @return \phpbb\files\filespec|\filespec|bool
82 | */
83 | public function proceed_upload($action)
84 | {
85 | //$can_upload = (@ini_get('file_uploads') == '0' || strtolower(@ini_get('file_uploads')) == 'off' || !@extension_loaded('zlib')) ? false : true;
86 |
87 | objects::$user->add_lang('posting'); // For error messages
88 | $upload = objects::$compatibility->get_upload_object();
89 | $upload->set_allowed_extensions(array('zip')); // Only allow ZIP files
90 |
91 | // Make sure the ext/ directory exists and if it doesn't, create it
92 | if (!is_dir(objects::$phpbb_root_path . 'ext'))
93 | {
94 | if (!files::catch_errors(files::recursive_mkdir(objects::$phpbb_root_path . 'ext')))
95 | {
96 | return false;
97 | }
98 | }
99 |
100 | if (!is_writable(objects::$phpbb_root_path . 'ext'))
101 | {
102 | files::catch_errors(objects::$user->lang['EXT_NOT_WRITABLE']);
103 | return false;
104 | }
105 |
106 | if (!is_dir(objects::$zip_dir))
107 | {
108 | if (!files::catch_errors(files::recursive_mkdir(objects::$zip_dir)))
109 | {
110 | return false;
111 | }
112 | }
113 |
114 | $tmp_dir = objects::$phpbb_root_path . 'ext/' . objects::$upload_ext_name . '/tmp';
115 | if (!is_writable($tmp_dir))
116 | {
117 | if (!phpbb_chmod($tmp_dir, CHMOD_READ | CHMOD_WRITE))
118 | {
119 | files::catch_errors(objects::$user->lang['EXT_TMP_NOT_WRITABLE']);
120 | return false;
121 | }
122 | }
123 |
124 | $file = false;
125 |
126 | // Proceed with the upload
127 | switch ($action)
128 | {
129 | case 'upload':
130 | if (!objects::$request->is_set("extupload", \phpbb\request\request_interface::FILES))
131 | {
132 | files::catch_errors(objects::$user->lang['NO_UPLOAD_FILE']);
133 | return false;
134 | }
135 | $file = objects::$compatibility->form_upload($upload);
136 | break;
137 | case 'upload_remote':
138 | $php_ini = new \phpbb\php\ini();
139 | if (!$php_ini->get_bool('allow_url_fopen'))
140 | {
141 | files::catch_errors(objects::$user->lang['EXT_ALLOW_URL_FOPEN_DISABLED']);
142 | return false;
143 | }
144 | $remote_url = objects::$request->variable('remote_upload', '');
145 | if (!extension_loaded('openssl') && 'https' === substr($remote_url, 0, 5))
146 | {
147 | files::catch_errors(objects::$user->lang['EXT_OPENSSL_DISABLED']);
148 | return false;
149 | }
150 | $file = objects::$compatibility->remote_upload($upload, $remote_url);
151 | break;
152 | }
153 |
154 | return $file;
155 | }
156 |
157 | /**
158 | * The function that uploads the specified extension.
159 | *
160 | * @param string $action Requested action.
161 | * @param \phpbb\files\filespec|\filespec $file Filespec object.
162 | * @param string $upload_dir The directory for zip files storage.
163 | * @return string|bool
164 | */
165 | public function get_dest_file($action, $file, $upload_dir)
166 | {
167 | if ($action == 'upload_local')
168 | {
169 | return $upload_dir . '/' . objects::$request->variable('local_upload', '');
170 | }
171 |
172 | if (!objects::$compatibility->filespec_get($file, 'filename'))
173 | {
174 | files::catch_errors((sizeof($file->error) ? implode(' ', $file->error) : objects::$user->lang['NO_UPLOAD_FILE']));
175 | return false;
176 | }
177 |
178 | if (objects::$compatibility->filespec_get($file, 'init_error') || sizeof($file->error))
179 | {
180 | $file->remove();
181 | files::catch_errors((sizeof($file->error) ? implode(' ', $file->error) : objects::$user->lang['EXT_UPLOAD_INIT_FAIL']));
182 | return false;
183 | }
184 |
185 | $file->clean_filename('real');
186 | $file->move_file(str_replace(objects::$phpbb_root_path, '', $upload_dir), true, true);
187 |
188 | if (sizeof($file->error))
189 | {
190 | $file->remove();
191 | files::catch_errors(implode(' ', $file->error));
192 | return false;
193 | }
194 | $dest_file = objects::$compatibility->filespec_get($file, 'destination_file');
195 |
196 | // Make security checks if checksum is provided.
197 | $checksum = objects::$request->variable('ext_checksum', '');
198 | if (!empty($checksum))
199 | {
200 | $generated_hash = '';
201 | $checksum_type = objects::$request->variable('ext_checksum_type', 'md5');
202 | switch ($checksum_type)
203 | {
204 | case 'sha1':
205 | $generated_hash = sha1_file($dest_file);
206 | break;
207 | case 'md5':
208 | $generated_hash = md5_file($dest_file);
209 | break;
210 | }
211 | if (strtolower($checksum) !== strtolower($generated_hash))
212 | {
213 | $file->remove();
214 | files::catch_errors(objects::$user->lang('ERROR_CHECKSUM_MISMATCH', $checksum_type));
215 | return false;
216 | }
217 | }
218 | objects::$template->assign_var('S_EXTENSION_CHECKSUM_NOT_PROVIDED', empty($checksum));
219 |
220 | return $dest_file;
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/includes/upload/extension.php:
--------------------------------------------------------------------------------
1 | proceed_upload($action);
33 | if (!$file && $action != 'upload_local' && $action != 'force_update')
34 | {
35 | files::catch_errors(objects::$user->lang['EXT_UPLOAD_ERROR']);
36 | return false;
37 | }
38 |
39 | // What is a safe limit of execution time? Half the max execution time should be safe.
40 | $safe_time_limit = (ini_get('max_execution_time') / 2);
41 | $start_time = time();
42 | // We skip working with a zip file if we are enabling/restarting the extension.
43 | if ($action != 'force_update')
44 | {
45 | $dest_file = $this->get_dest_file($action, $file, objects::$zip_dir);
46 | if (!$dest_file)
47 | {
48 | files::catch_errors(objects::$user->lang['EXT_UPLOAD_ERROR']);
49 | return false;
50 | }
51 |
52 | if (!$this->set_temp_path())
53 | {
54 | if ($action != 'upload_local')
55 | {
56 | $file->remove();
57 | }
58 | return false;
59 | }
60 |
61 | $this->extract_zip($dest_file);
62 |
63 | $composery = files::getComposer($this->ext_tmp);
64 | if (!$composery)
65 | {
66 | files::catch_errors(files::rrmdir($this->ext_tmp));
67 | if ($action != 'upload_local')
68 | {
69 | $file->remove();
70 | }
71 | files::catch_errors(objects::$user->lang['ACP_UPLOAD_EXT_ERROR_COMP']);
72 | return false;
73 | }
74 | $string = @file_get_contents($composery);
75 | if ($string === false)
76 | {
77 | files::catch_errors(files::rrmdir($this->ext_tmp));
78 | if ($action != 'upload_local')
79 | {
80 | $file->remove();
81 | }
82 | files::catch_errors(objects::$user->lang['EXT_UPLOAD_ERROR']);
83 | return false;
84 | }
85 | $json_a = json_decode($string, true);
86 | $destination = (isset($json_a['name'])) ? $json_a['name'] : '';
87 | $destination = str_replace('.', '', $destination);
88 | $ext_version = (isset($json_a['version'])) ? $json_a['version'] : '0.0.0';
89 |
90 | if (!$this->check_ext_name($destination))
91 | {
92 | if ($action != 'upload_local')
93 | {
94 | $file->remove();
95 | }
96 | return false;
97 | }
98 |
99 | $display_name = (isset($json_a['extra']['display-name'])) ? $json_a['extra']['display-name'] : $destination;
100 | if (!isset($json_a['type']) || $json_a['type'] != "phpbb-extension")
101 | {
102 | files::catch_errors(files::rrmdir($this->ext_tmp));
103 | if ($action != 'upload_local')
104 | {
105 | $file->remove();
106 | }
107 | files::catch_errors(objects::$user->lang['NOT_AN_EXTENSION']);
108 | return false;
109 | }
110 | $source = substr($composery, 0, -14);
111 |
112 | // Try to use the extracted path if it contains the necessary directory structure.
113 | $source_for_check = $this->get_temp_path(true) . '/extracted/' . $destination;
114 |
115 | // At first we need to change the directory structure to something like ext/tmp/vendor/extension.
116 | // We need it to escape from problems with dots on validation.
117 | if ($source != objects::$phpbb_root_path . 'ext/' . $source_for_check)
118 | {
119 | $source_for_check = $this->get_temp_path(true) . '/uploaded/' . $destination;
120 | if (!(files::catch_errors(files::rcopy($source, objects::$phpbb_root_path . 'ext/' . $source_for_check))))
121 | {
122 | files::catch_errors(files::rrmdir($this->ext_tmp));
123 | if ($action != 'upload_local')
124 | {
125 | $file->remove();
126 | }
127 | return false;
128 | }
129 | $source = objects::$phpbb_root_path . 'ext/' . $source_for_check;
130 | }
131 |
132 | // Validate the extension to check if it can be used on the board.
133 | $md_manager = objects::$compatibility->create_metadata_manager($source_for_check);
134 | try
135 | {
136 | if ($md_manager->get_metadata() === false || $md_manager->validate_require_phpbb() === false || $md_manager->validate_require_php() === false)
137 | {
138 | files::catch_errors(files::rrmdir($this->ext_tmp));
139 | if ($action != 'upload_local')
140 | {
141 | $file->remove();
142 | }
143 | files::catch_errors(objects::$user->lang['EXTENSION_NOT_AVAILABLE']);
144 | return false;
145 | }
146 | }
147 | catch (\phpbb\extension\exception $e)
148 | {
149 | $message = objects::$compatibility->get_exception_message($e);
150 | files::catch_errors(files::rrmdir($this->ext_tmp));
151 | if ($action != 'upload_local')
152 | {
153 | $file->remove();
154 | }
155 | files::catch_errors($message . ' ' . objects::$user->lang['ACP_UPLOAD_EXT_ERROR_NOT_SAVED']);
156 | return false;
157 | }
158 |
159 | // Save/remove the uploaded archive file.
160 | if ($action != 'upload_local')
161 | {
162 | if ((objects::$request->variable('keepext', false)) == false)
163 | {
164 | $file->remove();
165 | }
166 | else
167 | {
168 | $display_name = str_replace(array('/', '\\'), '_', $display_name);
169 | $ext_version = str_replace(array('/', '\\'), '_', $ext_version);
170 | $file_base_name = substr($dest_file, 0, strrpos($dest_file, '/') + 1) . $display_name . "_" . $ext_version;
171 | // Save this file and any other files that were uploaded with the same name.
172 | if (@file_exists($file_base_name . ".zip"))
173 | {
174 | $finder = 1;
175 | while (@file_exists($file_base_name . "(" . $finder . ").zip"))
176 | {
177 | $finder++;
178 | }
179 | @rename($dest_file, $file_base_name . "(" . $finder . ").zip");
180 | }
181 | else
182 | {
183 | @rename($dest_file, $file_base_name . ".zip");
184 | }
185 | }
186 | }
187 | // Here we can assume that all checks are done.
188 | // Now we are able to install the uploaded extension to the correct path.
189 | }
190 | else
191 | {
192 | // All checks were done previously. Now we only need to restore the variables.
193 | // We try to restore the data of the current upload.
194 | $this->set_temp_path(false);
195 | if (!is_dir($this->ext_tmp) || !($composery = files::getComposer($this->ext_tmp)) || !($string = @file_get_contents($composery)))
196 | {
197 | files::catch_errors(objects::$user->lang['ACP_UPLOAD_EXT_WRONG_RESTORE']);
198 | return false;
199 | }
200 | $json_a = json_decode($string, true);
201 | $destination = (isset($json_a['name'])) ? $json_a['name'] : '';
202 | $destination = str_replace('.', '', $destination);
203 | if (strpos($destination, '/') === false)
204 | {
205 | files::catch_errors(objects::$user->lang['ACP_UPLOAD_EXT_WRONG_RESTORE']);
206 | return false;
207 | }
208 | $source = substr($composery, 0, -14);
209 | }
210 | $made_update = false;
211 | // Delete the previous version of extension files - we're able to update them.
212 | if (is_dir(objects::$phpbb_root_path . 'ext/' . $destination))
213 | {
214 | // At first we need to disable the extension if it is enabled.
215 | if (objects::$phpbb_extension_manager->is_enabled($destination))
216 | {
217 | while (objects::$phpbb_extension_manager->disable_step($destination))
218 | {
219 | // Are we approaching the time limit? If so, we want to pause the update and continue after refreshing.
220 | if ((time() - $start_time) >= $safe_time_limit)
221 | {
222 | objects::$template->assign_var('S_NEXT_STEP', objects::$user->lang['EXTENSION_DISABLE_IN_PROGRESS']);
223 |
224 | // No need to specify the name of the extension. We suppose that it is the one in ext/tmp/USER_ID folder.
225 | if (objects::$request->is_ajax())
226 | {
227 | $response_object = new \phpbb\json_response;
228 | $response_object->send(array("FORCE_UPDATE" => true));
229 | }
230 | else
231 | {
232 | meta_refresh(0, objects::$u_action . '&action=force_update');
233 | }
234 | return false;
235 | }
236 | }
237 | objects::$log->add('admin', objects::$user->data['user_id'], objects::$user->ip, 'LOG_EXT_DISABLE', time(), array($destination));
238 | $made_update = true;
239 | }
240 |
241 | $saved_zip_file = $this->save_previous_version($destination);
242 |
243 | $this->check_missing_languages($source, $destination, $saved_zip_file);
244 |
245 | if (!(files::catch_errors(files::rrmdir(objects::$phpbb_root_path . 'ext/' . $destination))))
246 | {
247 | return false;
248 | }
249 | }
250 | if (!(files::catch_errors(files::rcopy($source, objects::$phpbb_root_path . 'ext/' . $destination))))
251 | {
252 | files::catch_errors(files::rrmdir($this->ext_tmp));
253 | return false;
254 | }
255 | // No enabling at this stage. Admins should have a chance to revise the uploaded scripts.
256 | if (!(files::catch_errors(files::rrmdir($this->ext_tmp))))
257 | {
258 | return false;
259 | }
260 |
261 | load::details($destination, (($made_update) ? 'updated' : 'uploaded'));
262 |
263 | // Clear phpBB cache after details page did its work.
264 | // Needed because some files like ext.php can be deleted in the new version.
265 | // Should be done at last because we need to remove class names from data_global cache file.
266 | objects::$cache->purge();
267 |
268 | return true;
269 | }
270 |
271 | protected function check_ext_name($destination)
272 | {
273 | if (strpos($destination, '/') === false)
274 | {
275 | files::catch_errors(files::rrmdir($this->ext_tmp));
276 | files::catch_errors(objects::$user->lang['ACP_UPLOAD_EXT_ERROR_DEST']);
277 | return false;
278 | }
279 |
280 | if (strpos($destination, objects::$upload_ext_name) !== false)
281 | {
282 | files::catch_errors(files::rrmdir($this->ext_tmp));
283 | files::catch_errors(objects::$user->lang['ACP_UPLOAD_EXT_ERROR_TRY_SELF']);
284 | return false;
285 | }
286 |
287 | return true;
288 | }
289 |
290 | protected function save_previous_version($destination)
291 | {
292 | $old_ext_name = $destination;
293 | if ($old_composery = files::getComposer(objects::$phpbb_root_path . 'ext/' . $destination))
294 | {
295 | if (!($old_string = @file_get_contents($old_composery)))
296 | {
297 | $old_ext_name = $old_ext_name . '_' . '0.0.0';
298 | }
299 | else
300 | {
301 | $old_json_a = json_decode($old_string, true);
302 | $old_display_name = (isset($old_json_a['extra']['display-name'])) ? $old_json_a['extra']['display-name'] : $old_ext_name;
303 | $old_ext_version = (isset($old_json_a['version'])) ? $old_json_a['version'] : '0.0.0';
304 | $old_ext_name = $old_display_name . '_' . $old_ext_version;
305 | }
306 | }
307 | $dest_name = str_replace(array('/', '\\'), '_', $old_ext_name) . '_old';
308 | $file_base_name = objects::$zip_dir . '/' . $dest_name;
309 | // Save this file and any other files that were uploaded with the same name.
310 | if (@file_exists($file_base_name . ".zip"))
311 | {
312 | $finder = 1;
313 | while (@file_exists($file_base_name . "(" . $finder . ").zip"))
314 | {
315 | $finder++;
316 | }
317 | $dest_name .= "(" . $finder . ")";
318 | }
319 | // Save the previous version of the extension that is being updated in a zip archive file.
320 | files::save_zip_archive('ext/' . $destination . '/', $dest_name, objects::$zip_dir);
321 | $saved_zip_file = $dest_name . ".zip";
322 | $saved_zip_file = objects::$compatibility->escape($saved_zip_file, true);
323 | objects::$template->assign_var('EXT_OLD_ZIP_SAVED', objects::$user->lang('EXT_SAVED_OLD_ZIP', $saved_zip_file));
324 |
325 | return $saved_zip_file;
326 | }
327 |
328 | protected function check_missing_languages($source, $destination, $saved_zip_file)
329 | {
330 | // Check languages missing in the new version.
331 | $old_langs = files::get_languages(objects::$phpbb_root_path . 'ext/' . $destination . '/language');
332 | $new_langs = files::get_languages($source . '/language');
333 | $old_langs = array_diff($old_langs, $new_langs);
334 | if (sizeof($old_langs))
335 | {
336 | $last_lang = array_pop($old_langs);
337 | objects::$template->assign_vars(array(
338 | 'S_EXT_LANGS_RESTORE_ZIP' => urlencode($saved_zip_file),
339 | 'EXT_RESTORE_DIRECTORIES' => (sizeof($old_langs)) ? objects::$user->lang('EXT_RESTORE_LANGUAGES', '' . implode(', ', $old_langs) . '', "$last_lang") : objects::$user->lang('EXT_RESTORE_LANGUAGE', "$last_lang"),
340 | ));
341 | }
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/includes/upload/lang.php:
--------------------------------------------------------------------------------
1 | lang('ERROR_LANGUAGE_NO_EXTENSION'));
30 | return false;
31 | }
32 |
33 | if (empty($lang_name))
34 | {
35 | files::catch_errors(objects::$user->lang('ERROR_LANGUAGE_NOT_DEFINED'));
36 | return false;
37 | }
38 |
39 | $file = $this->proceed_upload($action);
40 | if (!$file)
41 | {
42 | return false;
43 | }
44 |
45 | $dest_file = $this->get_dest_file($action, $file, objects::$zip_dir);
46 | if (!$dest_file)
47 | {
48 | return false;
49 | }
50 |
51 | if (!$this->set_temp_path())
52 | {
53 | if ($action != 'upload_local')
54 | {
55 | $file->remove();
56 | }
57 | return false;
58 | }
59 |
60 | $this->extract_zip($dest_file);
61 |
62 | if ($action != 'upload_local')
63 | {
64 | $file->remove();
65 | }
66 |
67 | // The files can be stored inside the $this->ext_tmp directory or up to two levels lower in the file tree.
68 | $lang_dir = '';
69 |
70 | // First level (the highest one).
71 | $files = @scandir($this->ext_tmp);
72 | if ($files === false)
73 | {
74 | files::catch_errors(objects::$user->lang('ERROR_LANGUAGE_UNKNOWN_STRUCTURE'));
75 | return false;
76 | }
77 | $files = array_diff($files, array('.', '..'));
78 | $last_file = array_pop($files);
79 |
80 | // Continue searching if we have a single directory.
81 | if (!sizeof($files) && !is_null($last_file) && @is_dir($this->ext_tmp . $lang_dir . '/' . $last_file))
82 | {
83 | $lang_dir .= '/' . $last_file;
84 |
85 | // Second level.
86 | $files = @scandir($this->ext_tmp . $lang_dir);
87 | if ($files === false)
88 | {
89 | files::catch_errors(objects::$user->lang('ERROR_LANGUAGE_UNKNOWN_STRUCTURE'));
90 | return false;
91 | }
92 | $files = array_diff($files, array('.', '..'));
93 |
94 | // Search for a directory with language ISO code (to escape from problems with unnecessary readme files).
95 | if (array_search($lang_name, $files) !== false && @is_dir($this->ext_tmp . $lang_dir . '/' . $lang_name))
96 | {
97 | $lang_dir .= '/' . $lang_name;
98 | }
99 | }
100 | $source = $this->ext_tmp . $lang_dir;
101 |
102 | if (!(files::catch_errors(files::rcopy($source, objects::$phpbb_root_path . 'ext/' . $ext_name . '/language/' . $lang_name))))
103 | {
104 | files::catch_errors(files::rrmdir($this->ext_tmp));
105 | return false;
106 | }
107 | if (!(files::catch_errors(files::rrmdir($this->ext_tmp))))
108 | {
109 | return false;
110 | }
111 | if (objects::$is_ajax && $ext_name === objects::$upload_ext_name && $lang_name === objects::$user->lang_name)
112 | {
113 | /*
114 | * Refresh the page if the uploaded language package
115 | * is currently used by the user of Upload Extensions.
116 | * Only for Ajax requests.
117 | */
118 | $response_object = new \phpbb\json_response;
119 | $response_object->send(array(
120 | "LANGUAGE" => urlencode($lang_name),
121 | "REFRESH" => true
122 | ));
123 | }
124 | objects::$template->assign_var('EXT_LANGUAGE_UPLOADED', objects::$user->lang('EXT_LANGUAGE_UPLOADED', $lang_name));
125 | return true;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/language/README.md:
--------------------------------------------------------------------------------
1 | ## Language packages
2 | Available language packages and the information about translations can be found here:
3 | https://github.com/BoardTools/upload/wiki/Translations
--------------------------------------------------------------------------------
/language/en/help_upload.php:
--------------------------------------------------------------------------------
1 | '--',
21 | 1 => 'General modules'
22 | ),
23 | array(
24 | 0 => 'What can I do with “Upload an extension” feature?',
25 | 1 => 'You are able to upload extensions from different sources without the necessity of using an FTP client. When you upload an extension that already exists on your board, its old version will be automatically saved in the specified directory on your board - check out “ZIP files management” module. You can also save zip file of currently uploaded version of the extension - tick the flag “Save uploaded zip file” before the upload process. You can make sure that you upload the true extension’s zip package if you specify its checksum in the corresponding form field.'
26 | ),
27 | array(
28 | 0 => 'What is the difference between “Extensions Manager of Upload Extensions” and standard “Extensions Manager”?',
29 | 1 => 'Just like standard “Extensions Manager”, “Extensions Manager of Upload Extensions” is a tool in your phpBB Board that allows you to manage all of your extensions and view information about them. But it can be determined as an “upgraded version” of the standard module.
Key benefits:
All uploaded extensions are sorted alphabetically, no matter if they are enabled, disabled or uninstalled. The exception: broken extensions.
Broken extensions are shown separately on the same page of “Extensions Manager” below the list of normal extensions. The reasons of unavailability are shown for each broken extension. Detailed warning message is added to those reasons when broken extension is installed or has some data saved in your database. You can click on the row of any broken extension to see its details just in the same way that is applicable for other extensions.
Any extension (if it is not a broken one) can be enabled with a single click on the toggle shown on the left of its name in the list.
'
30 | ),
31 | array(
32 | 0 => 'Why do I need “ZIP files management” module?',
33 | 1 => 'Sometimes you can find it useful if you can save archives of your extensions or share them. The archives can be old versions of uploaded extensions (that are packaged automatically for data safety), any packages that you have chosen to save by ticking the flag “Save uploaded zip file” before an upload process or any zip files of extensions that are stored in the specified directory (see question “Where can I specify the directory for saving zip files of extensions?” below). You have possibilities to unpack, to download and to delete those packages.'
34 | ),
35 | array(
36 | 0 => 'How can I use “Delete extensions” module?',
37 | 1 => '“Delete extensions” module lets you remove the files of uninstalled extensions from your server so that you can finish complete uninstallation without using FTP. When you do not need an extension anymore, you can remove it from your board completely. To do that you need to perform the following steps:
At first ensure that you really do not need a specific extension. It is recommended that you make some backups of the files and database before any removals.
Then navigate to “Extensions Manager of Upload Extensions”, find the extension that you want to delete and make sure that it is disabled: click on the toggle of that extension if the toggle is green.
Make sure that extension’s data is deleted: if the trash bin button of that extension is shown, click on it and confirm your action.
After that navigate to “Delete extensions” module, click on the link “Delete extension” shown in the row of your extension and confirm your action.
'
38 | ),
39 | array(
40 | 0 => '--',
41 | 1 => 'Uploading process'
42 | ),
43 | array(
44 | 0 => 'How can I upload validated extensions from the CDB on phpbb.com?',
45 | 1 => 'On the main page of Upload Extensions click on the link “Show validated extensions”. Select the extension that you want to upload and click on the “Download” button in the row of that extension. Note: wordplay here: the extension will be downloaded from the remote resource and uploaded to your server.'
46 | ),
47 | array(
48 | 0 => 'How can I perform an upload from other remote resources?',
49 | 1 => 'Copy the direct link to the extension’s zip package (if the link is not from the phpbb.com website, it should end with .zip) into the dedicated field of the form “Upload an extension” and click the “Upload” button.'
50 | ),
51 | array(
52 | 0 => 'How can I upload an extension from my local PC?',
53 | 1 => 'To do that click on the “Browse...” button in the form “Upload an extension”, select the extension’s zip file on your computer, then click the “Upload” button.'
54 | ),
55 | array(
56 | 0 => 'I have copied the link to the extension’s zip package into the field and clicked the “Upload” button, but I see an error. What’s wrong with the link?',
57 | 1 => 'To be able to upload the extension you should make sure that the following conditions are met:
The link should be direct: for uploads from resources other than phpbb.com it should have .zip at the end.
The link should lead to the zip file of the extension, not to its description page.
'
58 | ),
59 | array(
60 | 0 => 'What is the checksum? Where can I take it?',
61 | 1 => 'Checksum is used to verify the integrity of the uploaded file. It is checked to make sure that the file on the remote server and the file uploaded to your server are the same. Checksum can be usually obtained from the same resource where the original file is stored.'
62 | ),
63 | array(
64 | 0 => '--',
65 | 1 => 'Extensions Manager of Upload Extensions'
66 | ),
67 | array(
68 | 0 => 'How to use “Extensions Manager of Upload Extensions”?',
69 | 1 => 'The status of each extension is displayed as a toggle.
A green toggle means that the extension is enabled. When you click on a green toggle the extension will be disabled.
A red toggle means that the extension is disabled. When you click on a red toggle the extension will be enabled.
If the extension that has a red toggle is disabled but there is some extension’s data saved in the database, then you will have an option to delete its data by clicking on a trash bin near the toggle. Clicking on a trash bin is a way to uninstall the extension from the database. If you want to delete the files of the extension from the server, you will need to use Extension Cleaner tool.
You can also re-check all versions of the extensions by clicking on the corresponding button or set up version check settings just like in standard “Extensions Manager”.'
70 | ),
71 | array(
72 | 0 => 'What about broken extensions? Can I uninstall them?',
73 | 1 => 'Yes, sure! Broken extensions are displayed in “Extensions Manager of Upload Extensions” below the list of normal extensions. You can see the reasons why those extensions are broken and whether they have some data saved in your database. Click on a row of a broken extension to see its details and to manage it.'
74 | ),
75 | array(
76 | 0 => 'The toggle button of an extension is grey. Why?',
77 | 1 => 'The grey toggle button means that you cannot perform any actions with that extension at the moment. Probably another action is already in progress. Also Upload Extensions cannot disable itself - that is why its button is also grey.'
78 | ),
79 | array(
80 | 0 => '--',
81 | 1 => 'Extension details page'
82 | ),
83 | array(
84 | 0 => 'What information is shown for my extensions?',
85 | 1 => 'Displayed information depends on several circumstances.
General description provided by extension developers in the composer.json file (or warning message if the extension is broken).
The version number of the extension (if it is not broken).
The contents of the README.md file (if it exists in the extension’s directory).
The contents of the CHANGELOG.md file (if it exists in the extension’s directory).
Uploaded language packages for the extension.
The file tree for the extension and contents of its files.
'
86 | ),
87 | array(
88 | 0 => 'What can I do with the extension on the details page?',
89 | 1 => 'You are able to:
Enable the extension if its toggle is red.
Disable the extension if its toggle is green.
Delete extension’s data from the database if the red trash bin button is shown.
Check out the status of the current version of the extension if the link to the version check file is provided by extension developers. If extension’s version is shown in a green bubble - the extension is up-to-date. If the bubble is red - the extension is not up-to-date. Otherwise - the version check information could not be obtained.
Receive an update for the extension if you see a cogwheel near the extension’s version bubble. Click on the cogwheel: if an “Update” button is shown - then you can click on it, confirm your action and Upload Extensions will update your extension. You can also see the release announcement by clicking on the corresponding button if the link is provided by extension developers. NOTE: if JavaScript is disabled in your browser, those buttons will be located inside the extension details section block.
Manage extension’s language packages. You can upload a new language package for the extension - see question “What language packages can I upload for an extension?” below. You can also delete some already installed language packages.
Download the extension’s package (see question “What is the purpose of the feature “Download packaged extension”?” below).
'
90 | ),
91 | array(
92 | 0 => 'What language packages can I upload for an extension?',
93 | 1 => 'You can upload any zip packages that contain language files for the extension if those packages have one of the following structures:
For more information about the uploading process see section “Uploading process” above.'
94 | ),
95 | array(
96 | 0 => 'What is the purpose of the feature “Download packaged extension”?',
97 | 1 => 'Upload Extensions lets you download proper zip packages of any uploaded extensions on your board to your local PC. You can also tick a flag to delete the suffix of the development version - this action can help you, for example, to shorten the time for preparing the extension for the CDB. Navigate to the extension details page and click on the “Tools” section button. Then the “Download” button will be shown.'
98 | ),
99 | array(
100 | 0 => '--',
101 | 1 => 'ZIP files management'
102 | ),
103 | array(
104 | 0 => 'Where can I specify the directory for saving zip files of extensions?',
105 | 1 => 'Navigate in the ACP to General -> Server configuration -> Server settings -> Path settings -> Extensions’ zip packages storage path.'
106 | ),
107 | array(
108 | 0 => 'How can I delete several extensions’ zip packages at once?',
109 | 1 => 'At first make sure that you really need to perform such action; it is recommended that you have made necessary backups. Then navigate to “ZIP files management” module, tick the flags in the rows of zip packages that you want to delete, click on the “Delete marked” button and confirm your action.'
110 | ),
111 | array(
112 | 0 => '--',
113 | 1 => 'Extension Cleaner tool'
114 | ),
115 | array(
116 | 0 => 'What is “Extension Cleaner tool”?',
117 | 1 => '“Extension Cleaner tool” is the name of “Delete extensions” module of Upload Extensions sometimes used in its documentation.'
118 | ),
119 | array(
120 | 0 => 'An extension is installed on my board but I cannot delete it. Why?',
121 | 1 => 'The extension that you want to remove should be disabled and its data should be deleted from the database before you use “Extension Cleaner tool”. See question “How can I use “Delete extensions” module?” above.'
122 | ),
123 | array(
124 | 0 => 'How can I delete several extensions at once?',
125 | 1 => 'At first make sure that you really need to perform such action; it is recommended that you have made necessary backups. Then navigate to “Delete extensions” module, tick the flags in the rows of the extensions that you want to delete, click on the “Delete marked” button and confirm your action. Those extensions will not be saved as zip files! Their directories will be removed from the server entirely.'
126 | ),
127 | array(
128 | 0 => '--',
129 | 1 => 'Interactive interface'
130 | ),
131 | array(
132 | 0 => 'What are the benefits of the JavaScript functionality?',
133 | 1 => 'Pages are loaded faster, design elements are changed quickly when you interact with them, tooltips are shown to help you. All these features save your time and they are available only if JavaScript is enabled in your browser.'
134 | ),
135 | );
136 |
--------------------------------------------------------------------------------
/language/en/info_acp_upload.php:
--------------------------------------------------------------------------------
1 | 'Upload Extensions',
22 | 'ACP_UPLOAD_EXT_CONFIG_TITLE' => 'Upload extensions',
23 | ));
24 |
--------------------------------------------------------------------------------
/migrations/configuration/add_zip_config.php:
--------------------------------------------------------------------------------
1 | config['upload_ext_dir']);
17 | }
18 |
19 | static public function depends_on()
20 | {
21 | return array('\phpbb\db\migration\data\v310\dev');
22 | }
23 |
24 | public function update_data()
25 | {
26 | return array(
27 | array('config.add', array('upload_ext_dir', $this->upload_directory())),
28 | );
29 | }
30 |
31 | private function upload_directory()
32 | {
33 | $directory = 'store/ext';
34 |
35 | if (!is_dir($this->phpbb_root_path . $directory))
36 | {
37 | @mkdir($this->phpbb_root_path . $directory, 0755);
38 | @chmod($this->phpbb_root_path . $directory, 0755);
39 |
40 | if (!is_dir($this->phpbb_root_path . $directory))
41 | {
42 | $directory = 'ext';
43 | }
44 | }
45 | else if (!is_writable($this->phpbb_root_path . $directory))
46 | {
47 | @chmod($this->phpbb_root_path . $directory, 0755);
48 | }
49 |
50 | return $directory;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/migrations/install_upload.php:
--------------------------------------------------------------------------------
1 | config['upload_version']) && version_compare($this->config['upload_version'], '3.1.1', '>=');
17 | }
18 |
19 | static public function depends_on()
20 | {
21 | return array('\phpbb\db\migration\data\v310\dev');
22 | }
23 |
24 | public function update_data()
25 | {
26 | return array(
27 | array('config.add', array('upload_version', '3.1.1')),
28 | array('module.add', array(
29 | 'acp', 'ACP_EXTENSION_MANAGEMENT', array(
30 | 'module_basename' => '\boardtools\upload\acp\upload_module',
31 | 'auth' => 'ext_boardtools/upload && acl_a_extensions',
32 | 'modes' => array('main'),
33 | ),
34 | )),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tmp/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order Allow,Deny
3 | Deny from All
4 |
--------------------------------------------------------------------------------
/tmp/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/vendor/autoload.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer\Autoload;
14 |
15 | /**
16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 | *
18 | * $loader = new \Composer\Autoload\ClassLoader();
19 | *
20 | * // register classes with namespaces
21 | * $loader->add('Symfony\Component', __DIR__.'/component');
22 | * $loader->add('Symfony', __DIR__.'/framework');
23 | *
24 | * // activate the autoloader
25 | * $loader->register();
26 | *
27 | * // to enable searching the include path (eg. for PEAR packages)
28 | * $loader->setUseIncludePath(true);
29 | *
30 | * In this example, if you try to use a class in the Symfony\Component
31 | * namespace or one of its children (Symfony\Component\Console for instance),
32 | * the autoloader will first look for the class under the component/
33 | * directory, and it will then fallback to the framework/ directory if not
34 | * found before giving up.
35 | *
36 | * This class is loosely based on the Symfony UniversalClassLoader.
37 | *
38 | * @author Fabien Potencier
39 | * @author Jordi Boggiano
40 | * @see http://www.php-fig.org/psr/psr-0/
41 | * @see http://www.php-fig.org/psr/psr-4/
42 | */
43 | class ClassLoader
44 | {
45 | // PSR-4
46 | private $prefixLengthsPsr4 = array();
47 | private $prefixDirsPsr4 = array();
48 | private $fallbackDirsPsr4 = array();
49 |
50 | // PSR-0
51 | private $prefixesPsr0 = array();
52 | private $fallbackDirsPsr0 = array();
53 |
54 | private $useIncludePath = false;
55 | private $classMap = array();
56 |
57 | private $classMapAuthoritative = false;
58 |
59 | public function getPrefixes()
60 | {
61 | if (!empty($this->prefixesPsr0)) {
62 | return call_user_func_array('array_merge', $this->prefixesPsr0);
63 | }
64 |
65 | return array();
66 | }
67 |
68 | public function getPrefixesPsr4()
69 | {
70 | return $this->prefixDirsPsr4;
71 | }
72 |
73 | public function getFallbackDirs()
74 | {
75 | return $this->fallbackDirsPsr0;
76 | }
77 |
78 | public function getFallbackDirsPsr4()
79 | {
80 | return $this->fallbackDirsPsr4;
81 | }
82 |
83 | public function getClassMap()
84 | {
85 | return $this->classMap;
86 | }
87 |
88 | /**
89 | * @param array $classMap Class to filename map
90 | */
91 | public function addClassMap(array $classMap)
92 | {
93 | if ($this->classMap) {
94 | $this->classMap = array_merge($this->classMap, $classMap);
95 | } else {
96 | $this->classMap = $classMap;
97 | }
98 | }
99 |
100 | /**
101 | * Registers a set of PSR-0 directories for a given prefix, either
102 | * appending or prepending to the ones previously set for this prefix.
103 | *
104 | * @param string $prefix The prefix
105 | * @param array|string $paths The PSR-0 root directories
106 | * @param bool $prepend Whether to prepend the directories
107 | */
108 | public function add($prefix, $paths, $prepend = false)
109 | {
110 | if (!$prefix) {
111 | if ($prepend) {
112 | $this->fallbackDirsPsr0 = array_merge(
113 | (array) $paths,
114 | $this->fallbackDirsPsr0
115 | );
116 | } else {
117 | $this->fallbackDirsPsr0 = array_merge(
118 | $this->fallbackDirsPsr0,
119 | (array) $paths
120 | );
121 | }
122 |
123 | return;
124 | }
125 |
126 | $first = $prefix[0];
127 | if (!isset($this->prefixesPsr0[$first][$prefix])) {
128 | $this->prefixesPsr0[$first][$prefix] = (array) $paths;
129 |
130 | return;
131 | }
132 | if ($prepend) {
133 | $this->prefixesPsr0[$first][$prefix] = array_merge(
134 | (array) $paths,
135 | $this->prefixesPsr0[$first][$prefix]
136 | );
137 | } else {
138 | $this->prefixesPsr0[$first][$prefix] = array_merge(
139 | $this->prefixesPsr0[$first][$prefix],
140 | (array) $paths
141 | );
142 | }
143 | }
144 |
145 | /**
146 | * Registers a set of PSR-4 directories for a given namespace, either
147 | * appending or prepending to the ones previously set for this namespace.
148 | *
149 | * @param string $prefix The prefix/namespace, with trailing '\\'
150 | * @param array|string $paths The PSR-0 base directories
151 | * @param bool $prepend Whether to prepend the directories
152 | *
153 | * @throws \InvalidArgumentException
154 | */
155 | public function addPsr4($prefix, $paths, $prepend = false)
156 | {
157 | if (!$prefix) {
158 | // Register directories for the root namespace.
159 | if ($prepend) {
160 | $this->fallbackDirsPsr4 = array_merge(
161 | (array) $paths,
162 | $this->fallbackDirsPsr4
163 | );
164 | } else {
165 | $this->fallbackDirsPsr4 = array_merge(
166 | $this->fallbackDirsPsr4,
167 | (array) $paths
168 | );
169 | }
170 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
171 | // Register directories for a new namespace.
172 | $length = strlen($prefix);
173 | if ('\\' !== $prefix[$length - 1]) {
174 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
175 | }
176 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
177 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
178 | } elseif ($prepend) {
179 | // Prepend directories for an already registered namespace.
180 | $this->prefixDirsPsr4[$prefix] = array_merge(
181 | (array) $paths,
182 | $this->prefixDirsPsr4[$prefix]
183 | );
184 | } else {
185 | // Append directories for an already registered namespace.
186 | $this->prefixDirsPsr4[$prefix] = array_merge(
187 | $this->prefixDirsPsr4[$prefix],
188 | (array) $paths
189 | );
190 | }
191 | }
192 |
193 | /**
194 | * Registers a set of PSR-0 directories for a given prefix,
195 | * replacing any others previously set for this prefix.
196 | *
197 | * @param string $prefix The prefix
198 | * @param array|string $paths The PSR-0 base directories
199 | */
200 | public function set($prefix, $paths)
201 | {
202 | if (!$prefix) {
203 | $this->fallbackDirsPsr0 = (array) $paths;
204 | } else {
205 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
206 | }
207 | }
208 |
209 | /**
210 | * Registers a set of PSR-4 directories for a given namespace,
211 | * replacing any others previously set for this namespace.
212 | *
213 | * @param string $prefix The prefix/namespace, with trailing '\\'
214 | * @param array|string $paths The PSR-4 base directories
215 | *
216 | * @throws \InvalidArgumentException
217 | */
218 | public function setPsr4($prefix, $paths)
219 | {
220 | if (!$prefix) {
221 | $this->fallbackDirsPsr4 = (array) $paths;
222 | } else {
223 | $length = strlen($prefix);
224 | if ('\\' !== $prefix[$length - 1]) {
225 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
226 | }
227 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
228 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
229 | }
230 | }
231 |
232 | /**
233 | * Turns on searching the include path for class files.
234 | *
235 | * @param bool $useIncludePath
236 | */
237 | public function setUseIncludePath($useIncludePath)
238 | {
239 | $this->useIncludePath = $useIncludePath;
240 | }
241 |
242 | /**
243 | * Can be used to check if the autoloader uses the include path to check
244 | * for classes.
245 | *
246 | * @return bool
247 | */
248 | public function getUseIncludePath()
249 | {
250 | return $this->useIncludePath;
251 | }
252 |
253 | /**
254 | * Turns off searching the prefix and fallback directories for classes
255 | * that have not been registered with the class map.
256 | *
257 | * @param bool $classMapAuthoritative
258 | */
259 | public function setClassMapAuthoritative($classMapAuthoritative)
260 | {
261 | $this->classMapAuthoritative = $classMapAuthoritative;
262 | }
263 |
264 | /**
265 | * Should class lookup fail if not found in the current class map?
266 | *
267 | * @return bool
268 | */
269 | public function isClassMapAuthoritative()
270 | {
271 | return $this->classMapAuthoritative;
272 | }
273 |
274 | /**
275 | * Registers this instance as an autoloader.
276 | *
277 | * @param bool $prepend Whether to prepend the autoloader or not
278 | */
279 | public function register($prepend = false)
280 | {
281 | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
282 | }
283 |
284 | /**
285 | * Unregisters this instance as an autoloader.
286 | */
287 | public function unregister()
288 | {
289 | spl_autoload_unregister(array($this, 'loadClass'));
290 | }
291 |
292 | /**
293 | * Loads the given class or interface.
294 | *
295 | * @param string $class The name of the class
296 | * @return bool|null True if loaded, null otherwise
297 | */
298 | public function loadClass($class)
299 | {
300 | if ($file = $this->findFile($class)) {
301 | includeFile($file);
302 |
303 | return true;
304 | }
305 | }
306 |
307 | /**
308 | * Finds the path to the file where the class is defined.
309 | *
310 | * @param string $class The name of the class
311 | *
312 | * @return string|false The path if found, false otherwise
313 | */
314 | public function findFile($class)
315 | {
316 | // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
317 | if ('\\' == $class[0]) {
318 | $class = substr($class, 1);
319 | }
320 |
321 | // class map lookup
322 | if (isset($this->classMap[$class])) {
323 | return $this->classMap[$class];
324 | }
325 | if ($this->classMapAuthoritative) {
326 | return false;
327 | }
328 |
329 | $file = $this->findFileWithExtension($class, '.php');
330 |
331 | // Search for Hack files if we are running on HHVM
332 | if ($file === null && defined('HHVM_VERSION')) {
333 | $file = $this->findFileWithExtension($class, '.hh');
334 | }
335 |
336 | if ($file === null) {
337 | // Remember that this class does not exist.
338 | return $this->classMap[$class] = false;
339 | }
340 |
341 | return $file;
342 | }
343 |
344 | private function findFileWithExtension($class, $ext)
345 | {
346 | // PSR-4 lookup
347 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
348 |
349 | $first = $class[0];
350 | if (isset($this->prefixLengthsPsr4[$first])) {
351 | foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
352 | if (0 === strpos($class, $prefix)) {
353 | foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
354 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
355 | return $file;
356 | }
357 | }
358 | }
359 | }
360 | }
361 |
362 | // PSR-4 fallback dirs
363 | foreach ($this->fallbackDirsPsr4 as $dir) {
364 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
365 | return $file;
366 | }
367 | }
368 |
369 | // PSR-0 lookup
370 | if (false !== $pos = strrpos($class, '\\')) {
371 | // namespaced class name
372 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
373 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
374 | } else {
375 | // PEAR-like class name
376 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
377 | }
378 |
379 | if (isset($this->prefixesPsr0[$first])) {
380 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
381 | if (0 === strpos($class, $prefix)) {
382 | foreach ($dirs as $dir) {
383 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
384 | return $file;
385 | }
386 | }
387 | }
388 | }
389 | }
390 |
391 | // PSR-0 fallback dirs
392 | foreach ($this->fallbackDirsPsr0 as $dir) {
393 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
394 | return $file;
395 | }
396 | }
397 |
398 | // PSR-0 include paths.
399 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
400 | return $file;
401 | }
402 | }
403 | }
404 |
405 | /**
406 | * Scope isolated include.
407 | *
408 | * Prevents access to $this/self from included files.
409 | */
410 | function includeFile($file)
411 | {
412 | include $file;
413 | }
414 |
--------------------------------------------------------------------------------
/vendor/composer/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Copyright (c) 2015 Nils Adermann, Jordi Boggiano
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_classmap.php:
--------------------------------------------------------------------------------
1 | array($vendorDir . '/michelf/php-markdown'),
10 | );
11 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_psr4.php:
--------------------------------------------------------------------------------
1 | array($vendorDir . '/Markdown'),
10 | );
11 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_real.php:
--------------------------------------------------------------------------------
1 | $path) {
28 | $loader->set($namespace, $path);
29 | }
30 |
31 | $map = require __DIR__ . '/autoload_psr4.php';
32 | foreach ($map as $namespace => $path) {
33 | $loader->setPsr4($namespace, $path);
34 | }
35 |
36 | $classMap = require __DIR__ . '/autoload_classmap.php';
37 | if ($classMap) {
38 | $loader->addClassMap($classMap);
39 | }
40 |
41 | $loader->register(true);
42 |
43 | return $loader;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/vendor/composer/installed.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "michelf/php-markdown",
4 | "version": "1.5.0",
5 | "version_normalized": "1.5.0.0",
6 | "source": {
7 | "type": "git",
8 | "url": "https://github.com/michelf/php-markdown.git",
9 | "reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9"
10 | },
11 | "dist": {
12 | "type": "zip",
13 | "url": "https://api.github.com/repos/michelf/php-markdown/zipball/e1aabe18173231ebcefc90e615565742fc1c7fd9",
14 | "reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9",
15 | "shasum": ""
16 | },
17 | "require": {
18 | "php": ">=5.3.0"
19 | },
20 | "time": "2015-03-01 12:03:08",
21 | "type": "library",
22 | "extra": {
23 | "branch-alias": {
24 | "dev-lib": "1.4.x-dev"
25 | }
26 | },
27 | "installation-source": "dist",
28 | "autoload": {
29 | "psr-0": {
30 | "Michelf": ""
31 | }
32 | },
33 | "notification-url": "https://packagist.org/downloads/",
34 | "license": [
35 | "BSD-3-Clause"
36 | ],
37 | "authors": [
38 | {
39 | "name": "John Gruber",
40 | "homepage": "http://daringfireball.net/"
41 | },
42 | {
43 | "name": "Michel Fortin",
44 | "email": "michel.fortin@michelf.ca",
45 | "homepage": "https://michelf.ca/",
46 | "role": "Developer"
47 | }
48 | ],
49 | "description": "PHP Markdown",
50 | "homepage": "https://michelf.ca/projects/php-markdown/",
51 | "keywords": [
52 | "markdown"
53 | ]
54 | },
55 | {
56 | "name": "fortawesome/font-awesome",
57 | "version": "v4.5.0",
58 | "version_normalized": "4.5.0.0",
59 | "source": {
60 | "type": "git",
61 | "url": "https://github.com/FortAwesome/Font-Awesome.git",
62 | "reference": "dbc8d1b9b67daaab357bcc41dbf549c3684cf5f1"
63 | },
64 | "dist": {
65 | "type": "zip",
66 | "url": "https://api.github.com/repos/FortAwesome/Font-Awesome/zipball/dbc8d1b9b67daaab357bcc41dbf549c3684cf5f1",
67 | "reference": "dbc8d1b9b67daaab357bcc41dbf549c3684cf5f1",
68 | "shasum": ""
69 | },
70 | "require-dev": {
71 | "jekyll": "1.0.2",
72 | "lessc": "1.4.2"
73 | },
74 | "time": "2015-11-23 14:42:32",
75 | "type": "library",
76 | "extra": {
77 | "branch-alias": {
78 | "dev-master": "4.0.x-dev"
79 | }
80 | },
81 | "installation-source": "dist",
82 | "notification-url": "https://packagist.org/downloads/",
83 | "license": [
84 | "OFL-1.1",
85 | "MIT"
86 | ],
87 | "authors": [
88 | {
89 | "name": "Dave Gandy",
90 | "email": "dave@fontawesome.io",
91 | "homepage": "http://twitter.com/davegandy",
92 | "role": "Developer"
93 | }
94 | ],
95 | "description": "The iconic font and CSS framework",
96 | "homepage": "http://fontawesome.io/",
97 | "keywords": [
98 | "FontAwesome",
99 | "awesome",
100 | "bootstrap",
101 | "font",
102 | "icon"
103 | ]
104 | }
105 | ]
106 |
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/README.md:
--------------------------------------------------------------------------------
1 | #[Font Awesome v4.5.0](http://fontawesome.io)
2 | ###The iconic font and CSS framework
3 |
4 | Font Awesome is a full suite of 605 pictographic icons for easy scalable vector graphics on websites,
5 | created and maintained by [Dave Gandy](http://twitter.com/davegandy).
6 | Stay up to date with the latest release and announcements on Twitter:
7 | [@fontawesome](http://twitter.com/fontawesome).
8 |
9 | Get started at http://fontawesome.io!
10 |
11 | ##License
12 | - The Font Awesome font is licensed under the SIL OFL 1.1:
13 | - http://scripts.sil.org/OFL
14 | - Font Awesome CSS, LESS, and Sass files are licensed under the MIT License:
15 | - http://opensource.org/licenses/mit-license.html
16 | - The Font Awesome documentation is licensed under the CC BY 3.0 License:
17 | - http://creativecommons.org/licenses/by/3.0/
18 | - Attribution is no longer required as of Font Awesome 3.0, but much appreciated:
19 | - `Font Awesome by Dave Gandy - http://fontawesome.io`
20 | - Full details: http://fontawesome.io/license
21 |
22 | ##Changelog
23 | - [v4.5.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?q=milestone%3A4.5.0+is%3Aclosed)
24 | - [v4.4.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?q=milestone%3A4.4.0+is%3Aclosed)
25 | - [v4.3.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?q=milestone%3A4.3.0+is%3Aclosed)
26 | - [v4.2.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=12&page=1&state=closed)
27 | - [v4.1.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=6&page=1&state=closed)
28 | - [v4.0.3 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=9&page=1&state=closed)
29 | - [v4.0.2 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=8&page=1&state=closed)
30 | - [v4.0.1 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=7&page=1&state=closed)
31 | - [v4.0.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=2&page=1&state=closed)
32 | - [v3.2.1 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=5&page=1&state=closed)
33 | - [v3.2.0 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=3&page=1&state=closed)
34 | - [v3.1.1 GitHub milestones](https://github.com/FortAwesome/Font-Awesome/issues?milestone=4&page=1&state=closed)
35 | - v3.1.0 - Added 54 icons, icon stacking styles, flipping and rotating icons, removed Sass support
36 | - v3.0.2 - much improved rendering and alignment in IE7
37 | - v3.0.1 - much improved rendering in webkit, various bug fixes
38 | - v3.0.0 - all icons redesigned from scratch, optimized for Bootstrap's 14px default
39 |
40 | ## Contributing
41 |
42 | Please read through our [contributing guidelines](https://github.com/FortAwesome/Font-Awesome/blob/master/CONTRIBUTING.md).
43 | Included are directions for opening issues, coding standards, and notes on development.
44 |
45 | ##Versioning
46 |
47 | Font Awesome will be maintained under the Semantic Versioning guidelines as much as possible. Releases will be numbered
48 | with the following format:
49 |
50 | `..`
51 |
52 | And constructed with the following guidelines:
53 |
54 | * Breaking backward compatibility bumps the major (and resets the minor and patch)
55 | * New additions, including new icons, without breaking backward compatibility bumps the minor (and resets the patch)
56 | * Bug fixes and misc changes bumps the patch
57 |
58 | For more information on SemVer, please visit http://semver.org.
59 |
60 | ##Author
61 | - Email: dave@fontawesome.io
62 | - Twitter: http://twitter.com/davegandy
63 | - GitHub: https://github.com/davegandy
64 |
65 | ##Component
66 | To include as a [component](http://github.com/component/component), just run
67 |
68 | $ component install FortAwesome/Font-Awesome
69 |
70 | Or add
71 |
72 | "FortAwesome/Font-Awesome": "*"
73 |
74 | to the `dependencies` in your `component.json`.
75 |
76 | ## Hacking on Font Awesome
77 |
78 | **Before you can build the project**, you must first have the following installed:
79 |
80 | - [Ruby](https://www.ruby-lang.org/en/)
81 | - Ruby Development Headers
82 | - **Ubuntu:** `sudo apt-get install ruby-dev` *(Only if you're __NOT__ using `rbenv` or `rvm`)*
83 | - **Windows:** [DevKit](http://rubyinstaller.org/)
84 | - [Bundler](http://bundler.io/) (Run `gem install bundler` to install).
85 | - [Node Package Manager (AKA NPM)](https://docs.npmjs.com/getting-started/installing-node)
86 | - [Less](http://lesscss.org/) (Run `npm install -g less` to install).
87 | - [Less Plugin: Clean CSS](https://github.com/less/less-plugin-clean-css) (Run `npm install -g less-plugin-clean-css` to install).
88 |
89 | From the root of the repository, install the tools used to develop.
90 |
91 | $ bundle install
92 | $ npm install
93 |
94 | Build the project and documentation:
95 |
96 | $ bundle exec jekyll build
97 |
98 | Or serve it on a local server on http://localhost:7998/Font-Awesome/:
99 |
100 | $ bundle exec jekyll -w serve
101 |
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fortawesome/font-awesome",
3 | "description": "The iconic font and CSS framework",
4 | "keywords": ["font", "awesome", "fontawesome", "icon", "font", "bootstrap"],
5 | "homepage": "http://fontawesome.io/",
6 | "authors": [
7 | {
8 | "name": "Dave Gandy",
9 | "email": "dave@fontawesome.io",
10 | "role": "Developer",
11 | "homepage": "http://twitter.com/davegandy"
12 | }
13 | ],
14 | "extra": {
15 | "branch-alias": {
16 | "dev-master": "4.0.x-dev"
17 | }
18 | },
19 | "license": [
20 | "OFL-1.1",
21 | "MIT"
22 | ],
23 | "require-dev": {
24 | "jekyll": "1.0.2",
25 | "lessc": "1.4.2"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/vendor/fortawesome/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/vendor/fortawesome/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/vendor/michelf/php-markdown/License.md:
--------------------------------------------------------------------------------
1 | PHP Markdown Lib
2 | Copyright (c) 2004-2015 Michel Fortin
3 |
4 | All rights reserved.
5 |
6 | Based on Markdown
7 | Copyright (c) 2003-2006 John Gruber
8 |
9 | All rights reserved.
10 |
11 | Redistribution and use in source and binary forms, with or without
12 | modification, are permitted provided that the following conditions are
13 | met:
14 |
15 | * Redistributions of source code must retain the above copyright notice,
16 | this list of conditions and the following disclaimer.
17 |
18 | * Redistributions in binary form must reproduce the above copyright
19 | notice, this list of conditions and the following disclaimer in the
20 | documentation and/or other materials provided with the distribution.
21 |
22 | * Neither the name "Markdown" nor the names of its contributors may
23 | be used to endorse or promote products derived from this software
24 | without specific prior written permission.
25 |
26 | This software is provided by the copyright holders and contributors "as
27 | is" and any express or implied warranties, including, but not limited
28 | to, the implied warranties of merchantability and fitness for a
29 | particular purpose are disclaimed. In no event shall the copyright owner
30 | or contributors be liable for any direct, indirect, incidental, special,
31 | exemplary, or consequential damages (including, but not limited to,
32 | procurement of substitute goods or services; loss of use, data, or
33 | profits; or business interruption) however caused and on any theory of
34 | liability, whether in contract, strict liability, or tort (including
35 | negligence or otherwise) arising in any way out of the use of this
36 | software, even if advised of the possibility of such damage.
37 |
--------------------------------------------------------------------------------
/vendor/michelf/php-markdown/Michelf/Markdown.inc.php:
--------------------------------------------------------------------------------
1 |
8 | #
9 | # Original Markdown
10 | # Copyright (c) 2004-2006 John Gruber
11 | #
12 | #
13 | namespace Michelf;
14 |
15 |
16 | #
17 | # Markdown Parser Interface
18 | #
19 |
20 | interface MarkdownInterface {
21 |
22 | #
23 | # Initialize the parser and return the result of its transform method.
24 | # This will work fine for derived classes too.
25 | #
26 | public static function defaultTransform($text);
27 |
28 | #
29 | # Main function. Performs some preprocessing on the input text
30 | # and pass it through the document gamut.
31 | #
32 | public function transform($text);
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/vendor/michelf/php-markdown/Readme.md:
--------------------------------------------------------------------------------
1 | PHP Markdown
2 | ============
3 |
4 | PHP Markdown Lib 1.5.0 - 1 Mar 2015
5 |
6 | by Michel Fortin
7 |
8 |
9 | based on Markdown by John Gruber
10 |
11 |
12 |
13 | Introduction
14 | ------------
15 |
16 | This is a library package that includes the PHP Markdown parser and its
17 | sibling PHP Markdown Extra with additional features.
18 |
19 | Markdown is a text-to-HTML conversion tool for web writers. Markdown
20 | allows you to write using an easy-to-read, easy-to-write plain text
21 | format, then convert it to structurally valid XHTML (or HTML).
22 |
23 | "Markdown" is actually two things: a plain text markup syntax, and a
24 | software tool, originally written in Perl, that converts the plain text
25 | markup to HTML. PHP Markdown is a port to PHP of the original Markdown
26 | program by John Gruber.
27 |
28 | * [Full documentation of the Markdown syntax]()
29 | — Daring Fireball (John Gruber)
30 | * [Markdown Extra syntax additions]()
31 | — Michel Fortin
32 |
33 |
34 | Requirement
35 | -----------
36 |
37 | This library package requires PHP 5.3 or later.
38 |
39 | Note: The older plugin/library hybrid package for PHP Markdown and
40 | PHP Markdown Extra is still maintained and will work with PHP 4.0.5 and later.
41 |
42 | Before PHP 5.3.7, pcre.backtrack_limit defaults to 100 000, which is too small
43 | in many situations. You might need to set it to higher values. Later PHP
44 | releases defaults to 1 000 000, which is usually fine.
45 |
46 |
47 | Usage
48 | -----
49 |
50 | This library package is meant to be used with class autoloading. For autoloading
51 | to work, your project needs have setup a PSR-0-compatible autoloader. See the
52 | included Readme.php file for a minimal autoloader setup. (If you cannot use
53 | autoloading, see below.)
54 |
55 | With class autoloading in place, putting the 'Michelf' folder in your
56 | include path should be enough for this to work:
57 |
58 | use \Michelf\Markdown;
59 | $my_html = Markdown::defaultTransform($my_text);
60 |
61 | Markdown Extra syntax is also available the same way:
62 |
63 | use \Michelf\MarkdownExtra;
64 | $my_html = MarkdownExtra::defaultTransform($my_text);
65 |
66 | If you wish to use PHP Markdown with another text filter function
67 | built to parse HTML, you should filter the text *after* the `transform`
68 | function call. This is an example with [PHP SmartyPants][psp]:
69 |
70 | use \Michelf\Markdown, \Michelf\SmartyPants;
71 | $my_html = Markdown::defaultTransform($my_text);
72 | $my_html = SmartyPants::defaultTransform($my_html);
73 |
74 | All these examples are using the static `defaultTransform` static function
75 | found inside the parser class. If you want to customize the parser
76 | configuration, you can also instantiate it directly and change some
77 | configuration variables:
78 |
79 | use \Michelf\MarkdownExtra;
80 | $parser = new MarkdownExtra;
81 | $parser->fn_id_prefix = "post22-";
82 | $my_html = $parser->transform($my_text);
83 |
84 | To learn more, see the full list of [configuration variables].
85 |
86 | [configuration variables]: https://michelf.ca/projects/php-markdown/configuration/
87 |
88 |
89 | ### Usage without an autoloader
90 |
91 | If you cannot use class autoloading, you can still use `include` or `require`
92 | to access the parser. To load the `\Michelf\Markdown` parser, do it this way:
93 |
94 | require_once 'Michelf/Markdown.inc.php';
95 |
96 | Or, if you need the `\Michelf\MarkdownExtra` parser:
97 |
98 | require_once 'Michelf/MarkdownExtra.inc.php';
99 |
100 | While the plain `.php` files depend on autoloading to work correctly, using the
101 | `.inc.php` files instead will eagerly load the dependencies that would be
102 | loaded on demand if you were using autoloading.
103 |
104 |
105 | Public API and Versioning Policy
106 | ---------------------------------
107 |
108 | Version numbers are of the form *major*.*minor*.*patch*.
109 |
110 | The public API of PHP Markdown consist of the two parser classes `Markdown`
111 | and `MarkdownExtra`, their constructors, the `transform` and `defaultTransform`
112 | functions and their configuration variables. The public API is stable for
113 | a given major version number. It might get additions when the minor version
114 | number increments.
115 |
116 | **Protected members are not considered public API.** This is unconventional
117 | and deserves an explanation. Incrementing the major version number every time
118 | the underlying implementation of something changes is going to give
119 | nonessential version numbers for the vast majority of people who just use the
120 | parser. Protected members are meant to create parser subclasses that behave in
121 | different ways. Very few people create parser subclasses. I don't want to
122 | discourage it by making everything private, but at the same time I can't
123 | guarantee any stable hook between versions if you use protected members.
124 |
125 | **Syntax changes** will increment the minor number for new features, and the
126 | patch number for small corrections. A *new feature* is something that needs a
127 | change in the syntax documentation. Note that since PHP Markdown Lib includes
128 | two parsers, a syntax change for either of them will increment the minor
129 | number. Also note that there is nothing perfectly backward-compatible with the
130 | Markdown syntax: all inputs are always valid, so new features always replace
131 | something that was previously legal, although generally nonsensical to do.
132 |
133 |
134 | Bugs
135 | ----
136 |
137 | To file bug reports please send email to:
138 |
139 |
140 | Please include with your report: (1) the example input; (2) the output you
141 | expected; (3) the output PHP Markdown actually produced.
142 |
143 | If you have a problem where Markdown gives you an empty result, first check
144 | that the backtrack limit is not too low by running `php --info | grep pcre`.
145 | See Installation and Requirement above for details.
146 |
147 |
148 | Development and Testing
149 | -----------------------
150 |
151 | Pull requests for fixing bugs are welcome. Proposed new features are
152 | going meticulously reviewed -- taking into account backward compatibility,
153 | potential side effects, and future extensibility -- before deciding on
154 | acceptance or rejection.
155 |
156 | If you make a pull request that includes changes to the parser please add
157 | tests for what is being changed to [MDTest][] and make a pull request there
158 | too.
159 |
160 | [MDTest]: https://github.com/michelf/mdtest/
161 |
162 |
163 | Donations
164 | ---------
165 |
166 | If you wish to make a donation that will help me devote more time to
167 | PHP Markdown, please visit [michelf.ca/donate] or send Bitcoin to
168 | [1HiuX34czvVPPdhXbUAsAu7pZcesniDCGH].
169 |
170 | [michelf.ca/donate]: https://michelf.ca/donate/#!Thanks%20for%20PHP%20Markdown
171 | [1HiuX34czvVPPdhXbUAsAu7pZcesniDCGH]: bitcoin:1HiuX34czvVPPdhXbUAsAu7pZcesniDCGH
172 |
173 |
174 | Version History
175 | ---------------
176 |
177 | PHP Markdown Lib 1.5.0 (1 Mar 2015)
178 |
179 | * Added the ability start ordered lists with a number different from 1 and
180 | and have that reflected in the HTML output. This can be enabled with
181 | the `enhanced_ordered_lists` configuration variable for the Markdown
182 | parser; it is enabled by default for Markdown Extra.
183 | Credits to Matt Gorle for providing the implementation.
184 |
185 | * Added the ability to insert custom HTML attributes with simple values
186 | everywhere an extra attribute block is allowed (links, images, headers).
187 | The value must be unquoted, cannot contains spaces and is limited to
188 | alphanumeric ASCII characters.
189 | Credits to Peter Droogmans for providing the implementation.
190 |
191 | * Added a `header_id_func` configuration variable which takes a function
192 | that can generate an `id` attribute value from the header text.
193 | Credits to Evert Pot for providing the implementation.
194 |
195 | * Added a `url_filter_func` configuration variable which takes a function
196 | that can rewrite any link or image URL to something different.
197 |
198 |
199 | PHP Markdown Lib 1.4.1 (4 May 2014)
200 |
201 | * The HTML block parser will now treat `` as a block-level element
202 | (as it should) and no longer wrap it in `
` or parse it's content with
203 | the as Markdown syntax (although with Extra you can use `markdown="1"`
204 | if you wish to use the Markdown syntax inside it).
205 |
206 | * The content of `