├── CHANGELOG.md ├── README.md ├── acp ├── upload_info.php └── upload_module.php ├── adm └── style │ ├── acp_ext_details.html │ ├── acp_upload.html │ ├── acp_upload_cdb.html │ ├── acp_upload_details.html │ ├── acp_upload_error.html │ ├── acp_upload_list.html │ ├── acp_upload_main.html │ ├── acp_upload_pagination.html │ ├── acp_upload_uninstalled.html │ ├── acp_upload_zip_packages.html │ ├── css │ ├── images │ │ ├── application.png │ │ ├── code.png │ │ ├── css.png │ │ ├── db.png │ │ ├── directory.png │ │ ├── doc.png │ │ ├── file.png │ │ ├── film.png │ │ ├── flash.png │ │ ├── html.png │ │ ├── indicator.gif │ │ ├── java.png │ │ ├── linux.png │ │ ├── music.png │ │ ├── pdf.png │ │ ├── php.png │ │ ├── picture.png │ │ ├── ppt.png │ │ ├── psd.png │ │ ├── ruby.png │ │ ├── script.png │ │ ├── txt.png │ │ ├── xls.png │ │ └── zip.png │ ├── jquery.qtip.min.css │ ├── upload.css │ └── upload.rtl.css │ └── js │ ├── jquery.form.min.js │ ├── jquery.qtip.min.js │ ├── loading_progress.js │ ├── php_file_tree_jquery.js │ └── upload.js ├── composer.json ├── config └── services.yml ├── event └── listener.php ├── includes ├── compatibility │ ├── base.php │ ├── v_3_1_x.php │ └── v_3_2_x.php ├── filetree │ ├── filedownload.php │ └── filetree.php ├── functions │ ├── extensions.php │ ├── files.php │ ├── languages.php │ ├── load.php │ └── updater.php ├── objects.php ├── sources │ ├── cache.php │ └── extensions_list.php ├── types │ └── zip.php └── upload │ ├── base.php │ ├── extension.php │ └── lang.php ├── language ├── README.md └── en │ ├── help_upload.php │ ├── info_acp_upload.php │ └── upload.php ├── license.txt ├── migrations ├── configuration │ └── add_zip_config.php └── install_upload.php ├── tmp ├── .htaccess └── index.htm └── vendor ├── autoload.php ├── composer ├── ClassLoader.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php └── installed.json ├── fortawesome └── font-awesome │ ├── README.md │ ├── composer.json │ ├── css │ └── font-awesome.min.css │ └── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 └── michelf └── php-markdown ├── License.md ├── Michelf ├── Markdown.inc.php ├── Markdown.php ├── MarkdownExtra.inc.php ├── MarkdownExtra.php ├── MarkdownInterface.inc.php └── MarkdownInterface.php ├── Readme.md ├── Readme.php └── composer.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Upload Extensions - Changelog 2 | ============================= 3 | This file contains the list of changes between different versions of Upload Extensions. 4 | 5 | # Version 3.2.0 6 | ## Changes since 3.1.2 7 | ### Bug fixes 8 | * Add support for phpBB 3.2. 9 | * Fix update tooltip for mobile devices. 10 | * Fix updating extensions with removed `ext.php`. 11 | * Fix code selection functionality in file trees. 12 | * Fix version check functionality. 13 | 14 | # Version 3.1.2 15 | ## Changes since 3.1.2-RC2 16 | ### Bug fixes 17 | * Add support for the new types of download links from the CDB on phpbb.com. 18 | * JavaScript fixes and improvements. 19 | 20 | ## Changes since 3.1.2-RC1 21 | ### Bug fixes 22 | * JavaScript fixes and improvements. 23 | 24 | ## Changes since 3.1.2-beta2 25 | ### New features 26 | * [FEATURE] Extension's description and requirements can be shown for suggested extensions. 27 | * [FEATURE] Full downloadable list of uploaded extensions. 28 | * [FEATURE] Integration with browser's history. 29 | 30 | ### Improvements 31 | * [LIST] List of suggested extensions is now taken directly from phpbb.com. 32 | * [CODE] Errors are now shown in the special modal box. 33 | * [DESIGN] Small design improvements: status icons and current file in file tree. 34 | 35 | ### Bug fixes 36 | * JavaScript fixes and improvements. 37 | 38 | ## Changes since 3.1.2-beta 39 | ### New features 40 | * [FEATURE] Language packages management page for each extension. 41 | * [FEATURE] Uploads of language packages for extensions. 42 | * [FEATURE] Integration with Upload Extensions Updater. 43 | 44 | ### Improvements 45 | * [SECURITY] Checksums are now checked for uploaded zip packages. 46 | * [ZIP] File dates and file sizes are now displayed on ZIP files management page. 47 | * [INTERFACE] A link to reload pages by JavaScript. 48 | 49 | ### Bug fixes 50 | * The received data for valid extensions is now sanitized. 51 | * The list of suggested extensions from phpbb.com is now obtained from extension's repository. 52 | * JavaScript fixes and improvements. 53 | 54 | ## Changes since 3.1.1 55 | ### New features 56 | * [FEATURE] Full Ajax support. 57 | * [FEATURE] Full noscript support. 58 | * [FEATURE] Built-in Extensions Manager. 59 | * [FEATURE] Loading status in JavaScript. 60 | * [FEATURE] Navigation and menu bars. 61 | * [FEATURE] Downloads of properly packaged extensions. 62 | * [FEATURE] Built-in FAQ for Upload Extensions. 63 | * [FEATURE] User-friendly status messages. 64 | 65 | ### Improvements 66 | * [DETAILS] Now details page is a **separate section** that is not displayed in a small popup window. 67 | * [DETAILS] Now **file tree** is showed on the details page and can be used even when JavaScript is disabled. 68 | * [DETAILS] Now `Changelog.md` and `Readme.md` files can be viewed on details page. 69 | * [ZIP] Now it is possible to delete **2 or more** zip packages at once. 70 | * [CLEANER] Now it is possible to delete **2 or more** extensions at once. 71 | * [CODE] The extension's code was restructured. 72 | * [CODE] Proper error handling. 73 | 74 | ### Bug fixes 75 | Already included in 3.1.1. 76 | 77 | # Version 3.1.1 78 | 79 | Initial stable release (changes can be found in the commits of our [repository on GitHub](https://github.com/BoardTools/upload)). 80 | 81 | © 2014 - 2019 Igor Lavrov (https://github.com/LavIgor) and John Peskens (http://ForumHulp.com) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Upload Extensions 2 | ================= 3 | Upload Extensions enables you to upload extensions' zip files or delete extensions' folders from the server. 4 | With this extension you can install/update/delete extensions without using FTP. If the uploaded extension already exists, it will be updated with the uploaded files. 5 | 6 | [![Build Status](https://travis-ci.org/BoardTools/upload.svg?branch=master)](https://travis-ci.org/BoardTools/upload) 7 | 8 | ## Requirements 9 | * phpBB 3.1.0 or higher (phpBB 3.2 is also supported) 10 | * PHP 5.3.3 or higher 11 | 12 | ## Sources 13 | You can get Upload Extensions from one of the following sources: 14 | 15 | * phpbb.com: https://www.phpbb.com/customise/db/extension/upload/ 16 | * github.com: https://github.com/BoardTools/upload 17 | 18 | The support from the authors is given only for the packages uploaded from the sources listed above. 19 | 20 | ## Website 21 | Find the information about Upload Extensions and its features on the special website: 22 | http://boardtools.github.io/upload/ 23 | 24 | ## Installation, update, uninstallation 25 | The information about the installation/update/uninstallation process can be found in FAQ and Wiki: 26 | 27 | * For the packages uploaded from phpbb.com: https://www.phpbb.com/customise/db/extension/upload/faq 28 | * For the packages uploaded from github.com: https://github.com/BoardTools/upload/wiki 29 | 30 | ## Language packages 31 | Available language packages and the information about translations can be found here: 32 | https://github.com/BoardTools/upload/wiki/Translations 33 | 34 | ### General installation information (for the case if you don't have access to the resources listed above) 35 | In general you'll need to copy the contents of the uploaded zip package to `ext/boardtools/upload`. 36 | As a result the path to the `composer.json` file should become `ext/boardtools/upload/composer.json`. 37 | Then navigate in the ACP to `Customise -> Extension Management -> Manage extensions -> Upload Extensions` and click `Enable`. 38 | 39 | ### General information about standard updates 40 | To update Upload Extensions in standard way navigate in the ACP to `Customise -> Extension Management -> Manage extensions -> Upload Extensions` and click `Disable`. 41 | Then you can install the updated version of the extension. 42 | 43 | ### General uninstallation information 44 | Navigate in the ACP to `Customise -> Extension Management -> Manage extensions -> Upload Extensions` and click `Disable`. 45 | For permanent uninstallation click also `Delete data` and then you can safely remove the `ext/boardtools/upload` folder. 46 | 47 | ## Usage 48 | 49 | ### User-friendly interface 50 | The brand new Ajax functionality and colourful design add new improvements to extension's features. 51 | You do not need to refresh the page every time: if you want to return to the main page of Upload Extensions, simply click on its logo on the top of its pages. 52 | You can also read the built-in FAQ if you click on the question mark button hidden behind the logo. 53 | In some places useful tooltips can be found, they can help you in making the right decisions in extension management. 54 | Most actions end with appearance of styled message boxes that tell you the result status. 55 | 56 | Even if JavaScript is not enabled in your browser, the extension's features can still be accessed in a slightly different but still effective way. 57 | 58 | ### Upload extensions 59 | To upload extensions navigate in the ACP to `Customise -> Extension Management -> Upload extensions`. 60 | Choose your extension zip file and click the upload button. The extension will unpack your file in the folder mentioned in composer.json. After that you can enable the uploaded extension by clicking on a red toggle. Some tooltips will help you in this process. 61 | 62 | #### Supported sources 63 | You can upload extensions from different types of sources: 64 | 65 | * Customisation database on phpbb.com: choose the extension in the special list and click `Upload`. 66 | * Remote sources: you can also perform uploads from other resources, e.g. GitHub. The link from those resources (not from phpbb.com database) should end with `.zip`. Copy the download link of the extension that you want to install on your board and paste it into the text field in the form "Upload an extension". 67 | * Local PC: simply click `Browse...` button in Upload Extensions and choose a file to upload. 68 | 69 | **You can upload only `zip` formatted files of extensions.** 70 | 71 | ### Update extensions 72 | You can update any of already installed extensions by uploading a zip file with the new version of the extension that you want to update. 73 | Note: that extension will be disabled automatically. The previous version will be saved in a zip file. 74 | 75 | If the link to the new version of the extension is provided by its developers in the version check file, then you can easily update the extension by clicking on the `Update` button that will appear if you click on a cogwheel near the version number on the extension details page. 76 | 77 | **You need to revise the uploaded files and enable the updated extension again if you still want to use it on your board.** 78 | 79 | ### Extensions management 80 | Now it is possible to do the standard actions with extensions faster than before. 81 | 82 | You can perform the following actions using new Extensions Manager: 83 | 84 | * Enable uploaded extensions: click on a red toggle; it will be grey during the process and it will turn green after the successful result. 85 | * Disable installed extensions: click on a green toggle; it will be grey during the process and it will turn red after the successful result. 86 | * Delete extensions' data: click on a trash bin button displayed near the red toggle; it will be grey during the process and it will disappear after the successful result. 87 | * Check updates for the current versions of extensions: click `Re-Check all versions` link in Extensions Manager of Upload Extensions. 88 | * View the full list of extensions sorted alphabetically (no matter if they are enabled or disabled): it is displayed in Extensions Manager of Upload Extensions. 89 | * View the details of any uploaded extension: choose the extension in the list and click on its row. 90 | 91 | The actions are performed faster because of the new Ajax functionality and improved design. 92 | 93 | Upload Extensions broadens the list of possibilities and it has some other features: 94 | 95 | * You can view Readme and Changelog files of extensions as well as look at their file trees. 96 | * You can view installed language packages for extensions and delete them. 97 | * You can upload new language packages for each of the uploaded extensions. 98 | * You can disable broken extensions and purge their data. 99 | * You can download proper zip packages of extensions (for example, to prepare them for the CDB on phpbb.com). 100 | 101 | Those actions can be done on the details page of your chosen extension. 102 | 103 | ### Delete extensions 104 | To delete extensions' folders from the server (to perform complete uninstallation) make sure that your extension is disabled and its data is deleted. The toggle of that extension should be red without trash bin button nearby. 105 | Then navigate to Extension Cleaner tool: `Upload Extensions -> Delete extensions (button with a brush)`. 106 | Choose the extension that you want to delete and click `Delete extension`. 107 | If you want to delete several extensions at once, mark those extensions by ticking the flags and click `Delete marked` button. 108 | 109 | ### Managing zip files 110 | You can do the following actions with zip files uploaded with Upload Extensions: 111 | 112 | * Save them in the directory of your choice. To do that tick the flag `Save uploaded zip file` near the upload button. 113 | * To change the directory for saving zip files of uploaded extensions navigate in the ACP to `General -> Server configuration -> Server settings -> Path settings -> Extensions' zip packages storage path`. 114 | * Unpack previously saved zip files of extensions. The unpacked extension will be ready for installation immediately. 115 | * Download saved zip files of extensions on your PC. 116 | * Delete zip files of extensions. You can delete a single zip file or several zip files at once. 117 | 118 | All uploaded zip files will contain the version numbers of the uploaded extensions. 119 | If you have uploaded different zip files with the same name, they will be renamed properly so that they all will be saved. 120 | 121 | ## License 122 | [GNU General Public License v2](http://opensource.org/licenses/GPL-2.0) 123 | 124 | © 2014 - 2019 Igor Lavrov (https://github.com/LavIgor) and John Peskens (http://ForumHulp.com) -------------------------------------------------------------------------------- /acp/upload_info.php: -------------------------------------------------------------------------------- 1 | 'boardtools\upload\acp\upload_module', 18 | 'title' => 'ACP_UPLOAD_EXT_TITLE', 19 | 'version' => '1.0.0', 20 | 'modes' => array( 21 | 'main' => array( 22 | 'title' => 'ACP_UPLOAD_EXT_CONFIG_TITLE', 23 | 'auth' => 'ext_boardtools/upload && acl_a_extensions', 24 | 'cat' => array('ACP_EXTENSION_MANAGEMENT') 25 | ), 26 | ), 27 | ); 28 | } 29 | 30 | function install() 31 | { 32 | } 33 | 34 | function uninstall() 35 | { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /adm/style/acp_upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
{L_ACP_UPLOAD_EXT_TITLE}
16 | 17 | {L_EXT_UPDATES_AVAILABLE} 18 |

{L_ACP_UPLOAD_EXT_DESCRIPTION}

19 |
{L_LOADING}
20 |
{L_EXT_LOAD_ERROR}{L_EXT_LOAD_TIMEOUT}
21 |
22 |
23 | 24 |
25 |

{S_NEXT_STEP}

26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
{L_EXT_REFRESH_NOTICE} {L_EXT_REFRESH_PAGE}×
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /adm/style/acp_upload_cdb.html: -------------------------------------------------------------------------------- 1 | 2 |
{L_VALID_PHPBB_EXTENSIONS_TITLE}
3 | 4 |
5 | 6 | 7 | 8 | 9 | {phpbb_cdb.EXT_NAME} 10 |
11 | {L_VERSION} 12 | {phpbb_cdb.EXT_VERSION} 13 |
14 |
15 | 16 |
17 |

18 | 19 |

20 | 21 |
22 | {L_VERSION} 23 | 24 |
25 |
26 | {L_DESCRIPTION} 27 |

28 |
29 |
30 | {L_REQUIREMENTS} 31 |
32 | {L_PHPBB_VERSION} 33 | 34 |
35 |
36 | {L_PHP_VERSION} 37 | 38 |
39 |
40 |
41 | 42 |
{L_VALID_PHPBB_EXTENSIONS_EMPTY_LIST}
43 | 44 | -------------------------------------------------------------------------------- /adm/style/acp_upload_error.html: -------------------------------------------------------------------------------- 1 | 2 | {L_BACK_TO_PREV} 3 | 4 | {L_EXT_UPLOAD_BACK} 5 | 6 | 7 |
8 |

{L_ERROR}

9 | {UPLOAD_ERROR} 10 |
11 | 12 | 13 |
14 |

{L_POSSIBLE_SOLUTIONS}

15 | {UPLOAD_ERROR_SOLUTIONS} 16 |
17 | 18 | -------------------------------------------------------------------------------- /adm/style/acp_upload_list.html: -------------------------------------------------------------------------------- 1 | {L_ACP_UPLOAD_EXT_TITLE} » {L_EXTENSIONS_ADMIN} 2 | 3 |
4 |

{L_ACP_UPLOAD_EXT_MANAGER_EXPLAIN}

5 |
style="display:none"> 6 |

{L_CONFIG_UPDATED}

7 |
8 |
9 | {L_EXT_LIST_DOWNLOAD}{L_VERSIONCHECK_FORCE_UPDATE_ALL}{L_SETTINGS} 10 |
11 |
style="display:none"> 12 |
13 | {L_EXTENSIONS_VERSION_CHECK_SETTINGS} 14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |

22 |   23 | 24 | 25 | {S_FORM_TOKEN} 26 |

27 |
28 |
29 |
30 | 31 |
{L_EXTENSIONS_UPLOADED}
32 | 33 |
34 | 35 | 36 | 37 | {available.META_DISPLAY_NAME} 38 | 39 |
40 | {L_VERSION} 41 | {available.META_VERSION} 42 |
43 | 44 |
45 | {L_VERSION} 46 | {available.META_VERSION} 47 |
48 | 49 |
50 | {L_VERSION} 51 | {available.META_VERSION} 52 |
53 | 54 |
55 |
56 |
57 |
58 | 59 | 60 | 61 |
{L_EXTENSIONS_UNAVAILABLE}
62 |
{L_EXTENSIONS_UNAVAILABLE_EXPLAIN}
63 |
64 | 65 | 66 | {unavailable.META_NAME} 67 | 68 |
69 |

{L_EXTENSION_BROKEN_ENABLED}{L_EXTENSION_BROKEN_DISABLED}

70 |

{L_EXTENSION_BROKEN_DETAILS}

71 |
72 | 73 |
{unavailable.NOT_AVAILABLE}
74 |
75 | 76 |
77 | 78 |
79 |
80 |

81 | {L_EXT_LIST_DOWNLOAD} 82 |

83 |
84 | 85 | 90 |
91 | 92 | 97 |
98 | 99 | 100 |
101 | 102 | {L_SELECT_ALL_CODE} 103 | 104 |
105 |
106 | -------------------------------------------------------------------------------- /adm/style/acp_upload_main.html: -------------------------------------------------------------------------------- 1 | 2 |
{UPLOAD_ERROR}
3 | 4 | 5 |
6 |

{S_UPDATED_SELF} - {L_EXT_UPDATED_LATEST_VERSION}

7 |
8 | 9 |
10 |
11 |
12 |
13 |
14 | {L_EXTENSION_UPLOAD} 15 |

{L_EXTENSION_UPLOAD_EXPLAIN}

16 | 17 | 18 | 19 | {S_FORM_TOKEN} 20 | {S_HIDDEN_FIELDS} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 | 35 | {L_SHOW_VALID_PHPBB_EXTENSIONS} 36 | 37 | 38 |
39 | 44 | 45 |
46 |
{L_VALID_PHPBB_EXTENSIONS}
47 | 48 |
49 | 50 |
51 |
-------------------------------------------------------------------------------- /adm/style/acp_upload_pagination.html: -------------------------------------------------------------------------------- 1 | 2 | {PAGE_NUMBER} • 3 | 13 | -------------------------------------------------------------------------------- /adm/style/acp_upload_uninstalled.html: -------------------------------------------------------------------------------- 1 | {L_ACP_UPLOAD_EXT_TITLE} » {L_ACP_UPLOAD_UNINSTALLED_TITLE} 2 | 3 |
4 | 5 |
6 |

{EXTS_DELETED}

7 |
8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 |
{L_EXTENSIONS_AVAILABLE}{L_MARK}
{disabled.META_DISPLAY_NAME}{disabled.META_VERSION} 22 | 23 | {L_EXTENSION_DELETE} 24 | 25 |
31 |
32 |
33 |

{L_MARK_ALL}{L_UNMARK_ALL}

34 |
35 |
36 | 37 | -------------------------------------------------------------------------------- /adm/style/acp_upload_zip_packages.html: -------------------------------------------------------------------------------- 1 | {L_ACP_UPLOAD_EXT_TITLE} » {L_ACP_UPLOAD_ZIP_TITLE} 2 | 3 |
4 | 5 |
6 |

{EXT_ZIPS_DELETED}

7 |
8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 |
{L_ZIP_UPLOADED} (1 {L_KB} = 1000 {L_BYTES}){L_MARK}
{zip.META_DISPLAY_NAME}
{zip.FILE_SIZE_KB} {L_KB}
{zip.FILE_DATE} 22 | {L_ACP_UPLOAD_EXT_UNPACK} | {L_DOWNLOAD} | {L_EXTENSION_ZIP_DELETE} 23 |
29 | 30 | 33 | 34 |
35 |
36 |

{L_MARK_ALL}{L_UNMARK_ALL}

37 |
38 |
39 | 40 | -------------------------------------------------------------------------------- /adm/style/css/images/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/application.png -------------------------------------------------------------------------------- /adm/style/css/images/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/code.png -------------------------------------------------------------------------------- /adm/style/css/images/css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/css.png -------------------------------------------------------------------------------- /adm/style/css/images/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/db.png -------------------------------------------------------------------------------- /adm/style/css/images/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/directory.png -------------------------------------------------------------------------------- /adm/style/css/images/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/doc.png -------------------------------------------------------------------------------- /adm/style/css/images/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/file.png -------------------------------------------------------------------------------- /adm/style/css/images/film.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/film.png -------------------------------------------------------------------------------- /adm/style/css/images/flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/flash.png -------------------------------------------------------------------------------- /adm/style/css/images/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/html.png -------------------------------------------------------------------------------- /adm/style/css/images/indicator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/indicator.gif -------------------------------------------------------------------------------- /adm/style/css/images/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/java.png -------------------------------------------------------------------------------- /adm/style/css/images/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/linux.png -------------------------------------------------------------------------------- /adm/style/css/images/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/music.png -------------------------------------------------------------------------------- /adm/style/css/images/pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/pdf.png -------------------------------------------------------------------------------- /adm/style/css/images/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/php.png -------------------------------------------------------------------------------- /adm/style/css/images/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/picture.png -------------------------------------------------------------------------------- /adm/style/css/images/ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/ppt.png -------------------------------------------------------------------------------- /adm/style/css/images/psd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/psd.png -------------------------------------------------------------------------------- /adm/style/css/images/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/ruby.png -------------------------------------------------------------------------------- /adm/style/css/images/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/script.png -------------------------------------------------------------------------------- /adm/style/css/images/txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/txt.png -------------------------------------------------------------------------------- /adm/style/css/images/xls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/xls.png -------------------------------------------------------------------------------- /adm/style/css/images/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoardTools/upload/cd72f3de0608c2051c397fbc722e749dd22b41e3/adm/style/css/images/zip.png -------------------------------------------------------------------------------- /adm/style/css/jquery.qtip.min.css: -------------------------------------------------------------------------------- 1 | /* qTip2 v2.2.1 | Plugins: tips viewport modal | Styles: core basic css3 | qtip2.com | Licensed MIT | Mon Sep 08 2014 03:32:32 */ 2 | 3 | .qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word}.qtip-titlebar{position:relative;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;cursor:pointer;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:400 bold 10px/13px Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}.qtip-light{background-color:#fff;border-color:#E2E2E2;color:#454545}.qtip-light .qtip-titlebar{background-color:#f1f1f1}.qtip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3}.qtip-dark .qtip-titlebar{background-color:#404040}.qtip-dark .qtip-icon{border-color:#444}.qtip-dark .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35}.qtip-cream .qtip-titlebar{background-color:#F0DE7D}.qtip-cream .qtip-close .qtip-icon{background-position:-82px 0}.qtip-red{background-color:#F78B83;border-color:#D95252;color:#912323}.qtip-red .qtip-titlebar{background-color:#F06D65}.qtip-red .qtip-close .qtip-icon{background-position:-102px 0}.qtip-red .qtip-icon,.qtip-red .qtip-titlebar .ui-state-hover{border-color:#D95252}.qtip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219}.qtip-green .qtip-titlebar{background-color:#B0DE78}.qtip-green .qtip-close .qtip-icon{background-position:-42px 0}.qtip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD}.qtip-blue .qtip-titlebar{background-color:#D0E9F5}.qtip-blue .qtip-close .qtip-icon{background-position:-2px 0}.qtip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,.15)}.qtip-bootstrap,.qtip-rounded,.qtip-tipsy{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.qtip-rounded .qtip-titlebar{-moz-border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.qtip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:#fff;border:0 solid transparent;background:#4A4A4A;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,#000));background-image:-webkit-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-moz-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,#000 100%)}.qtip-youtube .qtip-titlebar{background-color:transparent}.qtip-youtube .qtip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#4a4a4a, EndColorStr=#000000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);"}.qtip-youtube .qtip-icon{border-color:#222}.qtip-youtube .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-jtools{background:#232323;background:rgba(0,0,0,.7);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-linear-gradient(top,#717171,#232323);background-image:-ms-linear-gradient(top,#717171,#232323);background-image:-o-linear-gradient(top,#717171,#232323);border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333}.qtip-jtools .qtip-titlebar{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171, endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}.qtip-jtools .qtip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A, endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}.qtip-jtools .qtip-content,.qtip-jtools .qtip-titlebar{background:0 0;color:#fff;border:0 dashed transparent}.qtip-jtools .qtip-icon{border-color:#555}.qtip-jtools .qtip-titlebar .ui-state-hover{border-color:#333}.qtip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,.4);box-shadow:4px 4px 5px rgba(0,0,0,.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent}.qtip-cluetip .qtip-titlebar{background-color:#87876A;color:#fff;border:0 dashed transparent}.qtip-cluetip .qtip-icon{border-color:#808064}.qtip-cluetip .qtip-titlebar .ui-state-hover{border-color:#696952;color:#696952}.qtip-tipsy{background:#000;background:rgba(0,0,0,.87);color:#fff;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:700;line-height:16px;text-shadow:0 1px #000}.qtip-tipsy .qtip-titlebar{padding:6px 35px 0 10px;background-color:transparent}.qtip-tipsy .qtip-content{padding:6px 10px}.qtip-tipsy .qtip-icon{border-color:#222;text-shadow:none}.qtip-tipsy .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:400;font-family:serif}.qtip-tipped .qtip-titlebar{border-bottom-width:0;color:#fff;background:#3A79B8;background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D);background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-ms-linear-gradient(top,#3A79B8,#2E629D);background-image:-o-linear-gradient(top,#3A79B8,#2E629D);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8, endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}.qtip-tipped .qtip-icon{border:2px solid #285589;background:#285589}.qtip-tipped .qtip-icon .ui-icon{background-color:#FBFBFB;color:#555}.qtip-bootstrap{font-size:14px;line-height:20px;color:#333;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.qtip-bootstrap .qtip-titlebar{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.qtip-bootstrap .qtip-titlebar .qtip-close{right:11px;top:45%;border-style:none}.qtip-bootstrap .qtip-content{padding:9px 14px}.qtip-bootstrap .qtip-icon{background:0 0}.qtip-bootstrap .qtip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:700;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.qtip-bootstrap .qtip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}.qtip:not(.ie9haxors) div.qtip-content,.qtip:not(.ie9haxors) div.qtip-titlebar{filter:none;-ms-filter:none}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}.qtip .qtip-tip,x:-o-prefocus{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:0 0;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}#qtip-overlay{position:fixed;left:0;top:0;width:100%;height:100%}#qtip-overlay.blurs{cursor:pointer}#qtip-overlay div{position:absolute;left:0;top:0;width:100%;height:100%;background-color:#000;opacity:.7;filter:alpha(opacity=70);-ms-filter:"alpha(Opacity=70)"} -------------------------------------------------------------------------------- /adm/style/css/upload.rtl.css: -------------------------------------------------------------------------------- 1 | /* RTL adjustments by LavIgor for Upload Extensions */ 2 | .rtl .ext_upload_wrapper { 3 | padding: 0 80px 0 0; 4 | } 5 | .rtl .upload_ext_upload_button { 6 | float: left; 7 | left: -10px; 8 | right: auto; 9 | border-top-left-radius: 0; 10 | border-top-right-radius: 10px; 11 | box-shadow: 1px -1px 1px 1px #78cef7; 12 | } 13 | /*.rtl #ext_language_code, .rtl #ext_checksum { 14 | margin-right: 0; 15 | margin-left: 10px; 16 | }*/ 17 | .rtl .upload_ext_save_zip label { 18 | margin-left: 0; 19 | margin-right: 5px; 20 | } 21 | .rtl .upload-menu { 22 | left: auto; 23 | right: 0; 24 | } 25 | .rtl .upload_main_link i.fa { 26 | float: right; 27 | } 28 | .rtl .upload_main_link span { 29 | float: right; 30 | margin: 0 50px 0 -1000px; 31 | } 32 | .rtl .upload-menu:hover .upload_main_link span { 33 | margin: 0 8px 0 0; 34 | } 35 | .rtl #upload_load_main { 36 | padding-left: 0; 37 | padding-right: 5px; 38 | } 39 | .rtl #upload_load_main:hover { 40 | margin: 0 15px 0 5px; 41 | padding: 5px 10px; 42 | } 43 | .rtl .ext_reload_link { 44 | float: left; 45 | margin: 0 15px 0 5px; 46 | } 47 | .rtl #uploaded_ok { 48 | margin-right: 0; 49 | margin-left: 15px; 50 | } 51 | .rtl .upload_ext_update_button, .rtl .upload_ext_error_show { 52 | float: left; 53 | } 54 | .rtl .ext_error_notice i, .rtl .ext_solution_notice i { 55 | margin-right: 0; 56 | margin-left: 5px; 57 | } 58 | .rtl #upload_extensions_title_links { 59 | padding: 0 20px 0 0; 60 | margin: 0 -100px 0 0; 61 | border-top-right-radius: 0; 62 | border-bottom-right-radius: 0; 63 | border-top-left-radius: 15px; 64 | border-bottom-left-radius: 15px; 65 | } 66 | .rtl #upload_extensions_title_block:hover #upload_extensions_title_links { 67 | margin-left: 0; 68 | margin-right: -15px; 69 | } 70 | .rtl #upload_extensions_links_show_slider { 71 | border-top-right-radius: 0; 72 | border-bottom-right-radius: 0; 73 | border-top-left-radius: 15px; 74 | border-bottom-left-radius: 15px; 75 | } 76 | .rtl .upload_loading_element { 77 | -moz-transform: rotateY(-15deg); 78 | -ms-transform: rotateZ(-15deg); 79 | -o-transform: rotateY(-15deg); 80 | -webkit-transform: rotateY(-15deg); 81 | transform: rotateZ(-15deg); 82 | } 83 | .rtl #upload_loading_text { 84 | left: auto; 85 | right: 15px; 86 | } 87 | .rtl #upload_loading_status { 88 | right: auto; 89 | left: 15px; 90 | } 91 | .rtl #upload_modal_box .alert_close { 92 | float: left; 93 | margin-right: 0; 94 | margin-left: -35px; 95 | } 96 | .rtl #upload_refresh_notice .upload_refresh_notice_close { 97 | float: left; 98 | } 99 | .rtl .select_all_code { 100 | right: auto; 101 | left: 15px; 102 | } 103 | .rtl .select_all_content { 104 | float: left; 105 | } 106 | .rtl .upload_main_page_link_block i.fa { 107 | margin-right: 0; 108 | margin-left: 10px; 109 | } 110 | .rtl .upload_valid_ext_row { 111 | padding: 0 100px 0 0; 112 | } 113 | .rtl .upload_valid_ext_row a.upload_valid_ext_download_link { 114 | left: auto; 115 | right: 0; 116 | } 117 | .rtl .upload_valid_ext_row a.upload_valid_ext_announcement_link { 118 | left: auto; 119 | right: 50px; 120 | } 121 | .rtl .upload_valid_ext_row span.upload_valid_ext_name { 122 | margin: 5px 0 5px 5px; 123 | } 124 | .rtl .upload_valid_ext_row .ext_version_bubble { 125 | margin: 0; 126 | } 127 | .rtl .upload_ext_list .upload_ext_unavailable_name { 128 | left: auto; 129 | right: 0; 130 | } 131 | .rtl .upload_ext_list .upload_ext_unavailable_reason { 132 | padding: 0 15px 0 0; 133 | } 134 | .rtl .upload_ext_list .ext_version_bubble { 135 | margin: 0; 136 | } 137 | .rtl .upload_ext_list .upload_ext_list_content a.upload_get_details_link { 138 | padding: 0 100px 0 0; 139 | } 140 | .rtl .upload_ext_list .upload_ext_list_content a.upload_get_details_link span.upload_ext_list_name { 141 | margin: 5px 0 5px 5px; 142 | } 143 | .rtl #upload_main .upload_ext_list .extension_toggle_wrapper { 144 | left: auto; 145 | right: 5px; 146 | } 147 | .rtl #upload_main .upload_ext_list .extension_remove_data_button { 148 | left: auto; 149 | right: 65px; 150 | } 151 | .rtl #upload_main .upload_ext_list .upload_ext_list_update_success_wrapper i, 152 | .rtl #upload_main .upload_ext_list .upload_ext_list_update_error_wrapper i { 153 | margin: 1px 0 0 5px; 154 | } 155 | .rtl #filecontent_wrapper { 156 | float: left; 157 | } 158 | .rtl .php-file-tree ul { 159 | padding: 0 15px 0 0; 160 | } 161 | .rtl .php-file-tree li { 162 | direction: rtl; 163 | } 164 | .rtl .php-file-tree li.pft-directory span, .rtl .php-file-tree li.pft-directory a { 165 | padding: 0 30px 0 0; 166 | margin: 0 -30px 0 0; 167 | } 168 | .rtl .requirements_bubble { 169 | margin-left: 0; 170 | margin-right: 20px; 171 | } 172 | .rtl .requirements_value { 173 | direction: ltr; 174 | } 175 | .rtl .description_bubble { 176 | margin-right: 0; 177 | margin-left: 20px; 178 | } 179 | .rtl .description_value { 180 | direction: ltr; 181 | border-left: none; 182 | border-right: 1px solid #D7D7D7; 183 | } 184 | .rtl .requirements_value:before, .description_value_ok:before, 185 | .rtl .requirements_value_not_met:before, .description_value_old:before { 186 | margin-right: 0; 187 | margin-left: 5px; 188 | } 189 | .rtl h1.ExtensionName { 190 | margin: 5px 15px 5px 5px; 191 | } 192 | .rtl #upload_main .extension_toggle_wrapper { 193 | margin-left: 0; 194 | margin-right: 5px; 195 | } 196 | .rtl #upload_main .extension_toggle_disabled .extension_toggle { 197 | left: 33px; 198 | } 199 | .rtl #upload_main .extension_toggle_enabled .extension_toggle { 200 | left: 0; 201 | } 202 | .rtl #upload_main .extension_toggle i { 203 | padding: 3px 5px 0 0; 204 | } 205 | .rtl #upload_main .extension_remove_data_button { 206 | margin-left: 0; 207 | margin-right: 5px; 208 | } 209 | .rtl #upload_main .extension_remove_data_button i { 210 | padding: 3px 5.5px 0 0; 211 | } 212 | .rtl #ext_purge_confirm .ext_update_ok, 213 | .rtl #ext_force_unstable_confirm .ext_update_ok { 214 | margin-right: 0; 215 | margin-left: 25px; 216 | } 217 | .rtl .ext_details_tabs .tab { 218 | float: right; 219 | } 220 | .rtl .ext_details_tabs .tab > a { 221 | margin: 1px 0 2px 1px; 222 | } 223 | .rtl .ext_details_tabs .activetab > a, 224 | .rtl .ext_details_tabs .activetab > a:hover { 225 | margin: 0 0 0 1px; 226 | } 227 | .rtl #ext_details_faq h1 { 228 | margin: 10px 15px 0 0; 229 | } 230 | .rtl .upload_ext_faq_title { 231 | padding: 10px 25px 10px 0; 232 | } 233 | .rtl .upload_ext_faq ul, .upload_ext_faq ol { 234 | margin: 5px 15px 0 0; 235 | } 236 | .rtl .ext_language_actions { 237 | float: left; 238 | } 239 | .rtl .ext_version_bubble { 240 | float: left; 241 | margin: 5px 0 5px 15px; 242 | } 243 | .rtl .ext_version_bubble .show_ext_updates { 244 | border-top-left-radius: 15px; 245 | border-bottom-left-radius: 15px; 246 | border-top-right-radius: 0; 247 | border-bottom-right-radius: 0; 248 | padding: 7px 25px 8px 10px; 249 | margin: 0 -20px 0 0; 250 | } 251 | .rtl #description_updates .extension_update_link, 252 | .rtl #description_updates .extension_announcement_link { 253 | padding: 10px 0 10px 9px; 254 | } 255 | .rtl #description_updates .extension_update_link i, 256 | .rtl #description_updates .extension_announcement_link i { 257 | margin: 0 9px 0 5px; 258 | } 259 | .rtl .description_updates_latest_version { 260 | margin: 5px 0 5px 5px; 261 | } 262 | .rtl .extension_author { 263 | margin-left: 0; 264 | margin-right: 25px; 265 | } 266 | .rtl #upload_main .extension_author_link span { 267 | margin-left: 0; 268 | margin-right: 10px; 269 | } 270 | .rtl #ext_details_content .description_fieldset .ext_description_column2 { 271 | float: left; 272 | } 273 | .rtl #ext_details_content .extension_announcement_link span { 274 | margin-left: 0; 275 | margin-right: 10px; 276 | } 277 | .rtl #ext_details_content .extension_get_update_link, 278 | .rtl #upload_refresh_notice .page_refresh_link { 279 | padding: 6px 0 6px 15px; 280 | } 281 | .rtl #ext_details_content .extension_get_update_link i, 282 | .rtl #upload_refresh_notice .page_refresh_link i { 283 | margin: 0 15px 0 5px; 284 | } 285 | .rtl .extension_latest_version_wrapper { 286 | margin-left: 0; 287 | margin-right: 5px; 288 | } 289 | .rtl .qtip{direction:rtl}.rtl .qtip-content{text-align:right}.rtl .qtip-titlebar{padding:5px 10px 5px 35px}.rtl .qtip-close{left:-9px}.rtl .qtip-titlebar .qtip-close{left:4px}.rtl .qtip-icon .ui-icon,.qtip-titlebar .ui-icon{direction:rtl}.rtl .qtip-cream .qtip-close .qtip-icon{background-position:-82px 0}.rtl .qtip-red .qtip-close .qtip-icon{background-position:-102px 0}.rtl .qtip-green .qtip-close .qtip-icon{background-position:-42px 0}.rtl .qtip-blue .qtip-close .qtip-icon{background-position:-2px 0}.rtl .qtip-tipsy .qtip-titlebar{padding:6px 10px 0 35px}.rtl .qtip-bootstrap .qtip-titlebar .qtip-close{right:auto;left:11px}.rtl .qtip-bootstrap .qtip-icon .ui-icon{float:left}.rtl .qtip .qtip-tip canvas{left:auto;right:0} -------------------------------------------------------------------------------- /adm/style/js/loading_progress.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Loading Progress Plugin 3 | * A plugin originally designed for phpBB extension Upload Extensions. 4 | * version: 1.1.0 5 | * Copyright (c) 2015 LavIgor 6 | * Licensed under GPL license version 2 (license.txt file). 7 | */ 8 | ; (function ($, window, document) { 9 | // do stuff here and use $, window and document safely 10 | // https://www.phpbb.com/community/viewtopic.php?p=13589106#p13589106 11 | $.upload_loading_interval = ""; 12 | $.upload_loading_from = "left"; 13 | 14 | $.fn.upload_loading_process = function() { 15 | $(".upload_loading_element").each(function () { 16 | var pos = $(this).css($.upload_loading_from); 17 | pos = pos.substr(0, pos.length - 2); 18 | if (parseFloat(pos) >= $("#upload_loading").width() + 30) $(this).remove(); 19 | else if (parseFloat(pos) === 30) { 20 | $(this).css($.upload_loading_from, (parseFloat(pos) + 3) + "px"); 21 | $("
").css($.upload_loading_from, "-30px").appendTo("#upload_loading"); 22 | } else $(this).css($.upload_loading_from, (parseFloat(pos) + 3) + "px"); 23 | }); 24 | }; 25 | 26 | $.fn.upload_loading_start = function () { 27 | if ($("#upload_loading").css("display") !== "none") return; 28 | $.upload_loading_from = ($("body").hasClass("rtl")) ? "right" : "left"; 29 | $("#upload_loading").slideDown(700); 30 | $("
").css($.upload_loading_from, "-30px").appendTo("#upload_loading"); 31 | $.upload_loading_interval = setInterval($.fn.upload_loading_process, 30); 32 | }; 33 | 34 | $.fn.upload_loading_end = function() { 35 | $("#upload_loading").slideUp(700, function () { 36 | clearInterval($.upload_loading_interval); 37 | $("#upload_loading_status").html(""); 38 | $(".upload_loading_element").each(function () { 39 | $(this).remove(); 40 | }); 41 | }); 42 | }; 43 | 44 | $.fn.upload_loading_progress = function(percentComplete) { 45 | $("#upload_loading_status").html(percentComplete + " %"); 46 | }; 47 | })(jQuery, window, document); 48 | -------------------------------------------------------------------------------- /adm/style/js/php_file_tree_jquery.js: -------------------------------------------------------------------------------- 1 | function setFileTree() { 2 | // Hide all subfolders at startup 3 | $(".php-file-tree").find("UL").hide(); 4 | 5 | // Expand/collapse on click 6 | $(".pft-directory span").click(function() { 7 | $(this).parent().find("UL:first").slideToggle("medium"); 8 | if ($(this).parent().attr('className') == "pft-directory") { 9 | return false; 10 | } 11 | }); 12 | 13 | $(".php-file-tree [data-file-link]").click(function(event) { 14 | event.preventDefault(); 15 | var $this = $(this); 16 | $(".pft-active").removeClass("pft-active"); 17 | $this.addClass("pft-active"); 18 | $("#filecontent_wrapper").fadeOut(500, function() { 19 | $("#filecontent").load($this.attr("data-file-link"), function() { 20 | $("#filecontent_wrapper").fadeIn(500); 21 | }); 22 | }); 23 | }); 24 | 25 | $(".php-file-tree > li[title='composer.json']").addClass("pft-active"); 26 | 27 | $(".select_all_code").css("display", "inline-block").click(function(event) { 28 | event.preventDefault(); 29 | selectCode(this); 30 | }); 31 | } 32 | 33 | // Source: board style's forum_fn.js 34 | function selectCode(a) { 35 | 'use strict'; 36 | 37 | // Get ID of code block 38 | var e = a.parentNode.parentNode.getElementsByTagName('CODE')[0]; 39 | var s, r; 40 | 41 | // Not IE and IE9+ 42 | if (window.getSelection) { 43 | s = window.getSelection(); 44 | // Safari and Chrome 45 | if (s.setBaseAndExtent) { 46 | var l = (e.innerText.length > 1) ? e.innerText.length - 1 : 1; 47 | try { 48 | s.setBaseAndExtent(e, 0, e, l); 49 | } catch (error) { 50 | r = document.createRange(); 51 | r.selectNodeContents(e); 52 | s.removeAllRanges(); 53 | s.addRange(r); 54 | } 55 | } 56 | // Firefox and Opera 57 | else { 58 | // workaround for bug # 42885 59 | if (window.opera && e.innerHTML.substring(e.innerHTML.length - 4) === '
') { 60 | e.innerHTML = e.innerHTML + ' '; 61 | } 62 | 63 | r = document.createRange(); 64 | r.selectNodeContents(e); 65 | s.removeAllRanges(); 66 | s.addRange(r); 67 | } 68 | } 69 | // Some older browsers 70 | else if (document.getSelection) { 71 | s = document.getSelection(); 72 | r = document.createRange(); 73 | r.selectNodeContents(e); 74 | s.removeAllRanges(); 75 | s.addRange(r); 76 | } 77 | // IE 78 | else if (document.selection) { 79 | r = document.body.createTextRange(); 80 | r.moveToElementText(e); 81 | r.select(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "boardtools/upload", 3 | "type": "phpbb-extension", 4 | "description": "Upload Extensions enables you to upload extensions' zip files or delete extensions' folders from the server. With this extension you can install/update/delete extensions without using FTP. If the uploaded extension already exists, it will be updated with the uploaded files. Upload Extensions also has other built-in features such as the manager for extensions' language packages and the manager for extensions' zip files.", 5 | "homepage": "http://boardtools.github.io/upload/", 6 | "version": "3.2.0-RC", 7 | "keywords": ["boardtools", "forumhulp", "phpbb", "extension", "upload", "unzip", "zip", "delete", "unpack"], 8 | "license": "GPL-2.0", 9 | "authors": [ 10 | { 11 | "name": "Igor Lavrov", 12 | "homepage": "https://github.com/lavigor", 13 | "role": "Developer, maintainer" 14 | }, 15 | { 16 | "name": "John Peskens", 17 | "homepage": "http://forumhulp.com", 18 | "email": "info@forumhulp.com", 19 | "role": "Former developer" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=5.3.3", 24 | "michelf/php-markdown": "~1.4", 25 | "fortawesome/font-awesome": "~4.3" 26 | }, 27 | "require-dev": { 28 | "phpbb/epv": "dev-master" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "Michelf\\": "vendor/michelf/php-markdown/Michelf/" 33 | } 34 | }, 35 | "extra": { 36 | "display-name": "Upload Extensions", 37 | "soft-require": { 38 | "phpbb/phpbb": ">=3.1.0" 39 | }, 40 | "version-check": { 41 | "host": "boardtools.github.io", 42 | "directory": "/upload", 43 | "filename": "upload.json" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /config/services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | boardtools.upload.listener: 3 | class: boardtools\upload\event\listener 4 | arguments: 5 | - '@user' 6 | tags: 7 | - { name: event.listener } 8 | -------------------------------------------------------------------------------- /event/listener.php: -------------------------------------------------------------------------------- 1 | user = $user; 32 | } 33 | 34 | static public function getSubscribedEvents() 35 | { 36 | return array( 37 | 'core.acp_board_config_edit_add' => 'add_config', 38 | ); 39 | } 40 | 41 | public function add_config($event) 42 | { 43 | if ($event['mode'] == 'server') 44 | { 45 | $this->user->add_lang_ext('boardtools/upload', 'upload'); 46 | $display_vars = $event['display_vars']; 47 | $new_vars = array( 48 | 'upload_ext_dir' => array('lang' => 'ACP_UPLOAD_EXT_DIR', 'validate' => 'path', 'type' => 'text:20:255', 'explain' => true), 49 | ); 50 | $display_vars['vars'] = phpbb_insert_config_array($display_vars['vars'], $new_vars, array('after' => 'ranks_path')); 51 | $event['display_vars'] = $display_vars; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /includes/compatibility/base.php: -------------------------------------------------------------------------------- 1 | assign_var('FONTAWESOME_NEEDED', true); 22 | return; 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function get_exception_message($e) 29 | { 30 | return $e; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function get_faq() 37 | { 38 | objects::$user->add_lang_ext('boardtools/upload', 'upload', false, true); 39 | return objects::$user->help; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function get_upload_object() 46 | { 47 | if (!class_exists('\fileupload')) 48 | { 49 | include(objects::$phpbb_root_path . 'includes/functions_upload.' . objects::$phpEx); 50 | } 51 | return new \fileupload(); 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function form_upload($upload) 58 | { 59 | return $upload->form_upload('extupload'); 60 | } 61 | 62 | /** 63 | * Remote upload method 64 | * Uploads file from given url 65 | * 66 | * @param \fileupload $upload Files object 67 | * @param string $remote_url URL pointing to file to upload, for example http://www.foobar.com/example.gif 68 | * @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser 69 | * @return object $file Object "filespec" is returned, all further operations can be done with this object 70 | */ 71 | public function remote_upload($upload, $remote_url, \phpbb\mimetype\guesser $mimetype_guesser = null) 72 | { 73 | $upload_ary = array(); 74 | $upload_ary['local_mode'] = true; 75 | 76 | $upload_from_phpbb = preg_match(objects::$phpbb_link_template, $remote_url, $match_phpbb); 77 | 78 | if (!preg_match('#^(https?://).*?\.(' . implode('|', $upload->allowed_extensions) . ')$#i', $remote_url, $match) && !$upload_from_phpbb) 79 | { 80 | $file = new \fileerror(objects::$user->lang[$upload->error_prefix . 'URL_INVALID']); 81 | return $file; 82 | } 83 | 84 | if (empty($match[2]) && empty($match_phpbb[2])) 85 | { 86 | $file = new \fileerror(objects::$user->lang[$upload->error_prefix . 'URL_INVALID']); 87 | return $file; 88 | } 89 | 90 | $url = parse_url($remote_url); 91 | 92 | $host = $url['host']; 93 | $path = $url['path']; 94 | $port = (!empty($url['port'])) ? (int) $url['port'] : 80; 95 | 96 | $upload_ary['type'] = 'application/octet-stream'; 97 | 98 | $url['path'] = explode('.', $url['path']); 99 | $ext = array_pop($url['path']); 100 | 101 | $url['path'] = implode('', $url['path']); 102 | $upload_ary['name'] = utf8_basename($url['path']) . (($ext) ? '.' . $ext : ''); 103 | $filename = $url['path']; 104 | $filesize = 0; 105 | 106 | $remote_max_filesize = $upload->max_filesize; 107 | if (!$remote_max_filesize) 108 | { 109 | $max_filesize = @ini_get('upload_max_filesize'); 110 | 111 | if (!empty($max_filesize)) 112 | { 113 | $unit = strtolower(substr($max_filesize, -1, 1)); 114 | $remote_max_filesize = (int) $max_filesize; 115 | 116 | switch ($unit) 117 | { 118 | case 'g': 119 | $remote_max_filesize *= 1024; 120 | // no break 121 | case 'm': 122 | $remote_max_filesize *= 1024; 123 | // no break 124 | case 'k': 125 | $remote_max_filesize *= 1024; 126 | // no break 127 | } 128 | } 129 | } 130 | 131 | $errno = 0; 132 | $errstr = ''; 133 | 134 | if (!($fsock = @fopen($remote_url, "r"))) 135 | { 136 | $file = new \fileerror(objects::$user->lang[$upload->error_prefix . 'NOT_UPLOADED']); 137 | return $file; 138 | } 139 | 140 | // Make sure $path not beginning with / 141 | if (strpos($path, '/') === 0) 142 | { 143 | $path = substr($path, 1); 144 | } 145 | 146 | $get_info = false; 147 | $data = ''; 148 | $length = false; 149 | $timer_stop = time() + $upload->upload_timeout; 150 | 151 | while (!@feof($fsock)) 152 | { 153 | if ($length) 154 | { 155 | // Don't attempt to read past end of file if server indicated length 156 | $block = @fread($fsock, min($length - $filesize, 1024)); 157 | } 158 | else 159 | { 160 | $block = @fread($fsock, 1024); 161 | } 162 | 163 | $filesize += strlen($block); 164 | 165 | if ($remote_max_filesize && $filesize > $remote_max_filesize) 166 | { 167 | $max_filesize = get_formatted_filesize($remote_max_filesize, false); 168 | 169 | $file = new \fileerror(sprintf(objects::$user->lang[$upload->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit'])); 170 | return $file; 171 | } 172 | 173 | $data .= $block; 174 | 175 | // Cancel upload if we exceed timeout 176 | if (time() >= $timer_stop) 177 | { 178 | $file = new \fileerror(objects::$user->lang[$upload->error_prefix . 'REMOTE_UPLOAD_TIMEOUT']); 179 | return $file; 180 | } 181 | } 182 | @fclose($fsock); 183 | 184 | if (empty($data)) 185 | { 186 | $file = new \fileerror(objects::$user->lang[$upload->error_prefix . 'EMPTY_REMOTE_DATA']); 187 | return $file; 188 | } 189 | 190 | $tmp_path = (@is_writable('/tmp/')) ? '/tmp/' : objects::$phpbb_root_path . 'cache/'; 191 | $filename = tempnam($tmp_path, unique_id() . '-'); 192 | 193 | if (!($fp = @fopen($filename, 'wb'))) 194 | { 195 | $file = new \fileerror(objects::$user->lang[$upload->error_prefix . 'NOT_UPLOADED']); 196 | return $file; 197 | } 198 | 199 | $upload_ary['size'] = fwrite($fp, $data); 200 | fclose($fp); 201 | unset($data); 202 | 203 | $upload_ary['tmp_name'] = $filename; 204 | 205 | $file = new \filespec($upload_ary, $upload, $mimetype_guesser); 206 | if ($upload_from_phpbb) 207 | { 208 | $file->extension = 'zip'; 209 | } 210 | $upload->common_checks($file); 211 | 212 | return $file; 213 | } 214 | 215 | /** 216 | * {@inheritdoc} 217 | */ 218 | public function escape($var, $multibyte) 219 | { 220 | /* 221 | * $request->escape() was added in phpBB 3.1.2, 222 | * however there is no need to check the version because this is 223 | * exactly the same method implementation. 224 | * So we fall back to this method for the whole phpBB 3.1 branch. 225 | */ 226 | $type_cast_helper = new \phpbb\request\type_cast_helper(); 227 | if (is_array($var)) 228 | { 229 | $result = array(); 230 | foreach ($var as $key => $value) 231 | { 232 | $type_cast_helper->set_var($key, $key, gettype($key), $multibyte); 233 | $result[$key] = $this->escape($value, $multibyte); 234 | } 235 | $var = $result; 236 | } 237 | else 238 | { 239 | $type_cast_helper->set_var($var, $var, 'string', $multibyte); 240 | } 241 | 242 | return $var; 243 | } 244 | 245 | /** 246 | * Gets a parameter of filespec object. 247 | * 248 | * @param \filespec $file Filespec object 249 | * @param string $param 'init_error' for checking if there are any errors, 250 | * 'filename' or 'destination_file' for getting corresponding values 251 | * @return mixed 252 | */ 253 | public function filespec_get($file, $param) 254 | { 255 | switch ($param) 256 | { 257 | case 'init_error': 258 | return $file->init_error; 259 | break; 260 | case 'filename': 261 | return $file->filename; 262 | break; 263 | case 'destination_file': 264 | return $file->destination_file; 265 | break; 266 | } 267 | return false; 268 | } 269 | 270 | /** 271 | * {@inheritdoc} 272 | */ 273 | public function create_metadata_manager($name) 274 | { 275 | return objects::$phpbb_extension_manager->create_extension_metadata_manager($name, objects::$template); 276 | } 277 | 278 | /** 279 | * {@inheritdoc} 280 | */ 281 | public function output_template_data(\phpbb\extension\metadata_manager $metadata_manager) 282 | { 283 | $metadata_manager->output_template_data(); 284 | } 285 | 286 | /** 287 | * Gets the latest extension update for the current extension branch the user is on 288 | * Will suggest versions from newer branches when EoL has been reached 289 | * and/or version from newer branch is needed for having all known security 290 | * issues fixed. 291 | * 292 | * @param \phpbb\version_helper $version_helper Version helper object. 293 | * @param string $current_version Current version of the extension. 294 | * @param bool $force_update Ignores cached data. Defaults to false. 295 | * @param bool $force_cache Force the use of the cache. Override $force_update. 296 | * @return array Version info or empty array if there are no updates 297 | * @throws \RuntimeException 298 | */ 299 | protected function get_ext_update_on_branch($version_helper, $current_version, $force_update = false, $force_cache = false) 300 | { 301 | $versions = $version_helper->get_versions_matching_stability($force_update, $force_cache); 302 | 303 | // Get current branch from version, e.g.: 3.2 304 | preg_match('/^(\d+\.\d+).*$/', objects::$config['version'], $matches); 305 | $current_branch = $matches[1]; 306 | 307 | // Filter out any versions less than the current version 308 | $versions = array_filter($versions, function($data) use ($version_helper, $current_version) { 309 | return $version_helper->compare($data['current'], $current_version, '>='); 310 | }); 311 | 312 | // Filter out any phpbb branches less than the current version 313 | $branches = array_filter(array_keys($versions), function($branch) use ($version_helper, $current_branch) { 314 | return $version_helper->compare($branch, $current_branch, '>='); 315 | }); 316 | if (!empty($branches)) 317 | { 318 | $versions = array_intersect_key($versions, array_flip($branches)); 319 | } 320 | else 321 | { 322 | // If branches are empty, it means the current phpBB branch is newer than any branch the 323 | // extension was validated against. Reverse sort the versions array so we get the newest 324 | // validated release available. 325 | krsort($versions); 326 | } 327 | 328 | // Get the first available version from the previous list. 329 | $update_info = array_reduce($versions, function($value, $data) use ($version_helper, $current_version) { 330 | if ($value === null && $version_helper->compare($data['current'], $current_version, '>=')) 331 | { 332 | if (!$data['eol'] && (!$data['security'] || $version_helper->compare($data['security'], $data['current'], '<='))) 333 | { 334 | return $version_helper->compare($data['current'], $current_version, '>') ? $data : array(); 335 | } 336 | else 337 | { 338 | return null; 339 | } 340 | } 341 | 342 | return $value; 343 | }); 344 | 345 | return $update_info === null ? array() : $update_info; 346 | } 347 | 348 | /** 349 | * Check the version and return the available updates. 350 | * 351 | * @param \phpbb\extension\metadata_manager $md_manager The metadata manager for the version to check. 352 | * @param bool $force_update Ignores cached data. Defaults to false. 353 | * @param bool $force_cache Force the use of the cache. Override $force_update. 354 | * @param string $stability Force the stability (null by default). 355 | * @return array 356 | * @throws \RuntimeException 357 | */ 358 | public function version_check(\phpbb\extension\metadata_manager $md_manager, $force_update = false, $force_cache = false, $stability = null) 359 | { 360 | $cache = objects::$cache; 361 | $config = objects::$config; 362 | $user = objects::$user; 363 | $meta = $md_manager->get_metadata('all'); 364 | 365 | if (!isset($meta['extra']['version-check'])) 366 | { 367 | throw new \RuntimeException($user->lang('NO_VERSIONCHECK'), 1); 368 | } 369 | 370 | $version_check = $meta['extra']['version-check']; 371 | 372 | if (version_compare($config['version'], '3.1.1', '>')) 373 | { 374 | $version_helper = new \phpbb\version_helper($cache, $config, new \phpbb\file_downloader(), $user); 375 | } 376 | else 377 | { 378 | $version_helper = new \phpbb\version_helper($cache, $config, $user); 379 | } 380 | $version_helper->set_current_version($meta['version']); 381 | if (version_compare($config['version'], '3.1.7', '>')) 382 | { 383 | $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename'], isset($version_check['ssl']) ? $version_check['ssl'] : false); 384 | } 385 | else 386 | { 387 | $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename']); 388 | } 389 | $version_helper->force_stability($stability); 390 | 391 | return $this->get_ext_update_on_branch($version_helper, $meta['version'], $force_update, $force_cache); 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /includes/compatibility/v_3_2_x.php: -------------------------------------------------------------------------------- 1 | get('files.upload'); 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function get_exception_message($e) 28 | { 29 | return call_user_func_array(array(objects::$user, 'lang'), array_merge(array($e->getMessage()), $e->get_parameters())); 30 | } 31 | 32 | /** 33 | * Loads the FAQ language file. 34 | * Old FAQ style is used for easier compatibility with previous phpBB versions. 35 | * 36 | * @param array $help Reference to the array of FAQ strings. 37 | */ 38 | protected function load_faq(&$help) 39 | { 40 | // Determine path to language directory 41 | $path = objects::$phpbb_extension_manager->get_extension_path('boardtools/upload', true) . 'language/'; 42 | $faq_file = '/help_upload.' . objects::$phpEx; 43 | if (file_exists($path . objects::$user->data['user_lang'] . $faq_file)) 44 | { 45 | // Do not suppress error if in DEBUG mode 46 | if (defined('DEBUG')) 47 | { 48 | include $path . objects::$user->data['user_lang'] . $faq_file; 49 | } 50 | else 51 | { 52 | @include $path . objects::$user->data['user_lang'] . $faq_file; 53 | } 54 | } 55 | else if (file_exists($path . 'en' . $faq_file)) 56 | { 57 | // Do not suppress error if in DEBUG mode 58 | if (defined('DEBUG')) 59 | { 60 | include $path . 'en' . $faq_file; 61 | } 62 | else 63 | { 64 | @include $path . 'en' . $faq_file; 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | */ 72 | public function get_faq() 73 | { 74 | $faq_help = array(); 75 | $this->load_faq($faq_help); 76 | return $faq_help; 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function get_upload_object() 83 | { 84 | return objects::$upload; 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | */ 90 | public function form_upload($upload) 91 | { 92 | return $upload->handle_upload('files.types.form', 'extupload'); 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | public function remote_upload($upload, $remote_url) 99 | { 100 | /** @var \boardtools\upload\includes\types\zip */ 101 | $upload_zip = new \boardtools\upload\includes\types\zip( 102 | objects::$phpbb_container->get('files.factory'), 103 | objects::$phpbb_container->get('language'), 104 | objects::$phpbb_container->get('php_ini'), 105 | objects::$phpbb_container->get('request'), 106 | objects::$phpbb_container->getParameter('core.root_path') 107 | ); 108 | $upload_zip->set_upload(objects::$upload); 109 | 110 | return $upload_zip->upload($remote_url); 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | public function escape($var, $multibyte) 117 | { 118 | return objects::$request->escape($var, $multibyte); 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | */ 124 | public function filespec_get($file, $param) 125 | { 126 | switch ($param) 127 | { 128 | case 'init_error': 129 | return $file->init_error(); 130 | break; 131 | case 'filename': 132 | case 'destination_file': 133 | return $file->get($param); 134 | break; 135 | } 136 | return false; 137 | } 138 | 139 | /** 140 | * {@inheritdoc} 141 | */ 142 | public function create_metadata_manager($name) 143 | { 144 | return objects::$phpbb_extension_manager->create_extension_metadata_manager($name); 145 | } 146 | 147 | /** 148 | * {@inheritdoc} 149 | */ 150 | public function output_template_data(\phpbb\extension\metadata_manager $metadata_manager) 151 | { 152 | if (phpbb_version_compare(objects::$config['version'], '3.2.0', '>')) 153 | { 154 | $metadata = $metadata_manager->get_metadata('all'); 155 | $this->output_metadata_to_template($metadata); 156 | } 157 | else 158 | { 159 | $metadata_manager->output_template_data(objects::$template); 160 | } 161 | } 162 | 163 | /** 164 | * Outputs extension metadata into the template 165 | * 166 | * @param array $metadata Array with all metadata for the extension 167 | */ 168 | public function output_metadata_to_template($metadata) 169 | { 170 | objects::$template->assign_vars(array( 171 | 'META_NAME' => $metadata['name'], 172 | 'META_TYPE' => $metadata['type'], 173 | 'META_DESCRIPTION' => (isset($metadata['description'])) ? $metadata['description'] : '', 174 | 'META_HOMEPAGE' => (isset($metadata['homepage'])) ? $metadata['homepage'] : '', 175 | 'META_VERSION' => $metadata['version'], 176 | 'META_TIME' => (isset($metadata['time'])) ? $metadata['time'] : '', 177 | 'META_LICENSE' => $metadata['license'], 178 | 179 | 'META_REQUIRE_PHP' => (isset($metadata['require']['php'])) ? $metadata['require']['php'] : '', 180 | 'META_REQUIRE_PHP_FAIL' => (isset($metadata['require']['php'])) ? false : true, 181 | 182 | 'META_REQUIRE_PHPBB' => (isset($metadata['extra']['soft-require']['phpbb/phpbb'])) ? $metadata['extra']['soft-require']['phpbb/phpbb'] : '', 183 | 'META_REQUIRE_PHPBB_FAIL' => (isset($metadata['extra']['soft-require']['phpbb/phpbb'])) ? false : true, 184 | 185 | 'META_DISPLAY_NAME' => (isset($metadata['extra']['display-name'])) ? $metadata['extra']['display-name'] : '', 186 | )); 187 | 188 | foreach ($metadata['authors'] as $author) 189 | { 190 | objects::$template->assign_block_vars('meta_authors', array( 191 | 'AUTHOR_NAME' => $author['name'], 192 | 'AUTHOR_EMAIL' => (isset($author['email'])) ? $author['email'] : '', 193 | 'AUTHOR_HOMEPAGE' => (isset($author['homepage'])) ? $author['homepage'] : '', 194 | 'AUTHOR_ROLE' => (isset($author['role'])) ? $author['role'] : '', 195 | )); 196 | } 197 | } 198 | 199 | /** 200 | * Gets the latest extension update for the current extension branch the user is on 201 | * Will suggest versions from newer branches when EoL has been reached 202 | * and/or version from newer branch is needed for having all known security 203 | * issues fixed. 204 | * 205 | * @param \phpbb\version_helper $version_helper Version helper object. 206 | * @param string $current_version Current version of the extension. 207 | * @param bool $force_update Ignores cached data. Defaults to false. 208 | * @param bool $force_cache Force the use of the cache. Override $force_update. 209 | * @return array Version info or empty array if there are no updates 210 | * @throws \RuntimeException 211 | */ 212 | protected function get_ext_update_on_branch($version_helper, $current_version, $force_update = false, $force_cache = false) 213 | { 214 | $versions = $version_helper->get_versions_matching_stability($force_update, $force_cache); 215 | 216 | // Get current branch from version, e.g.: 3.2 217 | preg_match('/^(\d+\.\d+).*$/', objects::$config['version'], $matches); 218 | $current_branch = $matches[1]; 219 | 220 | // Filter out any versions less than the current version 221 | $versions = array_filter($versions, function($data) use ($version_helper, $current_version) { 222 | return $version_helper->compare($data['current'], $current_version, '>='); 223 | }); 224 | 225 | // Filter out any phpbb branches less than the current version 226 | $branches = array_filter(array_keys($versions), function($branch) use ($version_helper, $current_branch) { 227 | return $version_helper->compare($branch, $current_branch, '>='); 228 | }); 229 | if (!empty($branches)) 230 | { 231 | $versions = array_intersect_key($versions, array_flip($branches)); 232 | } 233 | else 234 | { 235 | // If branches are empty, it means the current phpBB branch is newer than any branch the 236 | // extension was validated against. Reverse sort the versions array so we get the newest 237 | // validated release available. 238 | krsort($versions); 239 | } 240 | 241 | // Get the first available version from the previous list. 242 | $update_info = array_reduce($versions, function($value, $data) use ($version_helper, $current_version) { 243 | if ($value === null && $version_helper->compare($data['current'], $current_version, '>=')) 244 | { 245 | if (!$data['eol'] && (!$data['security'] || $version_helper->compare($data['security'], $data['current'], '<='))) 246 | { 247 | return $version_helper->compare($data['current'], $current_version, '>') ? $data : array(); 248 | } 249 | else 250 | { 251 | return null; 252 | } 253 | } 254 | 255 | return $value; 256 | }); 257 | 258 | return $update_info === null ? array() : $update_info; 259 | } 260 | 261 | /** 262 | * {@inheritdoc} 263 | */ 264 | public function version_check(\phpbb\extension\metadata_manager $md_manager, $force_update = false, $force_cache = false, $stability = null) 265 | { 266 | if (phpbb_version_compare(objects::$config['version'], '3.2.0', '>')) 267 | { 268 | return objects::$phpbb_extension_manager->version_check($md_manager, $force_update, $force_cache, $stability); 269 | } 270 | 271 | $meta = $md_manager->get_metadata('all'); 272 | 273 | if (!isset($meta['extra']['version-check'])) 274 | { 275 | throw new \phpbb\exception\runtime_exception('NO_VERSIONCHECK'); 276 | } 277 | 278 | $version_check = $meta['extra']['version-check']; 279 | 280 | $version_helper = new \phpbb\version_helper(objects::$cache, objects::$config, new \phpbb\file_downloader()); 281 | $version_helper->set_current_version($meta['version']); 282 | $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename'], isset($version_check['ssl']) ? $version_check['ssl'] : false); 283 | $version_helper->force_stability($stability); 284 | 285 | return $this->get_ext_update_on_branch($version_helper, $meta['version'], $force_update, $force_cache); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /includes/filetree/filedownload.php: -------------------------------------------------------------------------------- 1 | ' . substr($file, strrpos($file, '/') + 1) . '
' . highlight_string($string, true) . '
'; 22 | exit(); 23 | } 24 | return false; 25 | } 26 | 27 | public static function php_file_tree($directory, $display_name, $uaction, $extensions = array()) 28 | { 29 | $code = '
' . $display_name . '
'; 30 | if (substr($directory, -1) == '/') 31 | { 32 | $directory = substr($directory, 0, strlen($directory) - 1); 33 | } 34 | $code .= self::php_file_tree_dir($directory, $uaction, $extensions); 35 | return $code; 36 | } 37 | 38 | public static function php_file_tree_dir($directory, $uaction, $extensions = array(), $first_call = true) 39 | { 40 | $file = @scandir($directory); 41 | if (!$file) 42 | { 43 | return false; 44 | } 45 | natcasesort($file); 46 | 47 | // Make directories first 48 | $files = $dirs = array(); 49 | foreach ($file as $this_file) 50 | { 51 | if (is_dir($directory . '/' . $this_file)) 52 | { 53 | $dirs[] = $this_file; 54 | } 55 | else 56 | { 57 | $files[] = $this_file; 58 | } 59 | } 60 | $file = array_merge($dirs, $files); 61 | 62 | // Filter unwanted extensions 63 | if (!empty($extensions)) 64 | { 65 | foreach (array_keys($file) as $key) 66 | { 67 | if (!is_dir($directory . '/' . $file[$key])) 68 | { 69 | $ext = substr($file[$key], strrpos($file[$key], '.') + 1); 70 | if (!in_array($ext, $extensions)) 71 | { 72 | unset($file[$key]); 73 | } 74 | } 75 | } 76 | } 77 | 78 | $php_file_tree = ''; 79 | if (count($file) > 2) 80 | { // Use 2 instead of 0 to account for . and .. directories 81 | $php_file_tree = ''; 95 | $php_file_tree .= self::php_file_tree_dir($directory . '/' . $this_file, $uaction, $extensions, false); 96 | $php_file_tree .= ''; 97 | } 98 | else 99 | { 100 | // File 101 | // Get extension (prepend 'ext-' to prevent invalid classes from extensions that begin with numbers) 102 | $ext = 'ext-' . substr($this_file, strrpos($this_file, '.') + 1); 103 | $link = $uaction . '&file=' . urlencode($directory . '/' . $this_file); 104 | // Noscript support 105 | $getlink = $uaction . '&action=details&ext_name=' . self::$ext_name . '&ext_show=filetree&ext_file=' . urlencode(substr($directory, strpos($directory, self::$ext_name) + strlen(self::$ext_name)) . '/' . $this_file); 106 | $show_link = (in_array($ext, array('ext-gif', 'ext-jpg', 'ext-jpeg', 'ext-tif', 'ext-png'))) ? false : true; 107 | $php_file_tree .= '
  • ' . htmlspecialchars($this_file) . '
  • '; 108 | } 109 | } 110 | } 111 | $php_file_tree .= ''; 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:' 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:' 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:
    1. The link should be direct: for uploads from resources other than phpbb.com it should have .zip at the end.
    2. 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.
    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.' 86 | ), 87 | array( 88 | 0 => 'What can I do with the extension on the details page?', 89 | 1 => 'You are able to:' 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 `