├── .gitignore ├── LICENSE ├── README.md ├── assets └── index.php ├── config.php ├── install ├── files │ └── README.txt ├── info.php └── install.sql ├── modules ├── Helloworld │ └── Helloworld.module ├── InputfieldCKEditor │ ├── README.txt │ ├── config-body.js │ ├── config.js │ ├── contents-inline.css │ ├── contents.css │ ├── mystyles.js │ └── plugins │ │ └── README.txt ├── ProcessWireUpgrade │ ├── ProcessWireUpgrade.css │ ├── ProcessWireUpgrade.module │ ├── ProcessWireUpgradeCheck.config.php │ ├── ProcessWireUpgradeCheck.module │ └── README.md └── README.txt └── templates ├── README.txt ├── admin.php ├── api.php ├── api ├── ApiHelper.php ├── Auth.php ├── Router.php └── Test.php ├── basic-page.php ├── client ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── logo.png │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ ├── Content.vue │ │ └── Login.vue │ ├── main.js │ ├── router │ │ └── index.js │ └── store │ │ ├── actions.js │ │ ├── getters.js │ │ ├── index.js │ │ ├── mutations.js │ │ └── state.js └── static │ └── .gitkeep ├── errors ├── 500.html └── README.txt └── home.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Thomas Aull 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Update 2 | 3 | There is now a module for creation of a REST API in ProcessWire: https://github.com/thomasaull/RestApi Support and Updates for this site profile will be discontinued from now on 4 | 5 | # RestApiProfile 6 | Build a rest API with ProcessWire. Including JWT-Auth and a Vue SPA example 7 | 8 | **Disclaimer** 9 | 10 | This is an example, there is no guarantee this code is secure! Use at your own risk and/or send me PRs with improvements. 11 | 12 | **Credits…** 13 | 14 | …go to [Benjamin Milde](https://github.com/LostKobrakai) for his code example on how to use FastRoute with ProcessWire and [Camilo Castro](https://gist.github.com/clsource) for this [Gist](https://gist.github.com/clsource/dc7be74afcbfc5fe752c) 15 | 16 | ### Install 17 | 18 | Grab a copy of processwire and place the site-restapi directory in the root of your ProcessWire directory. Install ProcessWire as usual (don’t forget to pick the site profile). 19 | 20 | Then: 21 | 22 | If you have composer installed run the following commands: 23 | ``` 24 | composer require nikic/fast-route 25 | composer require firebase/php-jwt 26 | ``` 27 | 28 | Alternatively, you can grab the /vendor folder over here: https://github.com/thomasaull/RestApiProfile-Src 29 | 30 | The Rest-API should work now. To check you can use [Postman](https://www.getpostman.com/) or [Insomnia](https://insomnia.rest/) and run a GET Request: 31 | 32 | `http://your-dev-host.dev/api/test` 33 | 34 | You should get the following error: 35 | 36 | ``` 37 | { 38 | "error": "No Authorization Header found" 39 | } 40 | ``` 41 | 42 | Because you’re not authenticated yet. To disable authentication, go to /site/templates/api/Router.php and in the function *handle* set the variable $authActive to false for now. 43 | 44 | If you run the same Request again, you’ll get the following 45 | ``` 46 | { 47 | "user": "guest" 48 | } 49 | ``` 50 | 51 | To use JWT-Auth you have to send a GET Request to http://yourhost/api/auth with two parameters, username and password. The API will log your user in and return you the JWT-Token, which you have to add to every following request. 52 | 53 | An example for a simple login form is implemented as a Vue SPA based on the [Vue Webpack Template](https://github.com/vuejs-templates/webpack). To install, go to /site/templates/client and run `npm install` 54 | 55 | Go to /site/templates/client/config/index.js and change the target in *proxyTable* to match your URL: 56 | 57 | ``` 58 | proxyTable: { 59 | '/api': { 60 | target: 'http://change-to-your-dev-host.dev', 61 | changeOrigin: true 62 | } 63 | }, 64 | ``` 65 | 66 | Now run `npm run dev`, point your browser to http://localhost:8080 and you should be able to perform a login with your user. 67 | 68 | Check the files components/Login.vue, components/Content.vue and the main.js inside /site/templates/client to learn how the login process works. 69 | 70 | As a last step you should change your JWT Secret in your config.php. You can basically use any string but a good idea is to create a random string with the following PHP command: 71 | 72 | `echo base64_encode(openssl_random_pseudo_bytes(64));` 73 | 74 | ### Helper 75 | 76 | There is a small helper class, which exposes some often used functionality. At the moment there's basically just one function available, but I for my part use it all the time: `checkAndSanitizeRequiredParameters`. This function checks if the client send all the parameters required and sanitizes them against a specified ProcessWire sanitizer. To use it call it first thing in your Api endpoint function: 77 | ``` 78 | public static function postWithSomeData($data) { 79 | // Check for required parameter "message" and sanitize with PW Sanitizer 80 | $data = ApiHelper::checkAndSanitizeRequiredParameters($data, ['message|text']); 81 | 82 | return "Your message is: " . $data->message; 83 | } 84 | ``` 85 | 86 | An example can be found here: https://github.com/thomasaull/RestApiProfile/blob/master/templates/api/Test.php#L15 87 | -------------------------------------------------------------------------------- /assets/index.php: -------------------------------------------------------------------------------- 1 | debug = true; 36 | 37 | $config->defaultAdminTheme = 'AdminThemeUikit'; 38 | $config->jwtSecret = "ZxDWh8rXl16lrpX6jHBNUh+9uEv9g7jv3ebVHthTUnM2x30Lq2aMr1Ekij8LDgde/D/K04clCV9/gNxivEibjQ=="; 39 | 40 | 41 | /*** INSTALLER CONFIG ********************************************************************/ 42 | 43 | 44 | -------------------------------------------------------------------------------- /install/files/README.txt: -------------------------------------------------------------------------------- 1 | This file is here to ensure Git adds the dir to the repo. You may delete this file. 2 | -------------------------------------------------------------------------------- /install/info.php: -------------------------------------------------------------------------------- 1 | "RestApi", 4 | 'summary' => "Build a rest API with ProcessWire. Including JWT-Auth and a Vue SPA example", 5 | 'screenshot' => "" 6 | ); 7 | -------------------------------------------------------------------------------- /install/install.sql: -------------------------------------------------------------------------------- 1 | # --- WireDatabaseBackup {"time":"2018-02-26 18:44:25","user":"","dbName":"pwrestapi.dev","description":"","tables":[],"excludeTables":["pages_drafts","pages_roles","permissions","roles","roles_permissions","users","users_roles","user","role","permission"],"excludeCreateTables":[],"excludeExportTables":["field_roles","field_permissions","field_email","field_pass","caches","session_login_throttle","page_path_history"]} 2 | 3 | DROP TABLE IF EXISTS `caches`; 4 | CREATE TABLE `caches` ( 5 | `name` varchar(250) NOT NULL, 6 | `data` mediumtext NOT NULL, 7 | `expires` datetime NOT NULL, 8 | PRIMARY KEY (`name`), 9 | KEY `expires` (`expires`) 10 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 11 | 12 | DROP TABLE IF EXISTS `field_admin_theme`; 13 | CREATE TABLE `field_admin_theme` ( 14 | `pages_id` int(10) unsigned NOT NULL, 15 | `data` int(11) NOT NULL, 16 | PRIMARY KEY (`pages_id`), 17 | KEY `data` (`data`) 18 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 19 | 20 | 21 | DROP TABLE IF EXISTS `field_email`; 22 | CREATE TABLE `field_email` ( 23 | `pages_id` int(10) unsigned NOT NULL, 24 | `data` varchar(250) NOT NULL DEFAULT '', 25 | PRIMARY KEY (`pages_id`), 26 | KEY `data_exact` (`data`), 27 | FULLTEXT KEY `data` (`data`) 28 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 29 | 30 | DROP TABLE IF EXISTS `field_pass`; 31 | CREATE TABLE `field_pass` ( 32 | `pages_id` int(10) unsigned NOT NULL, 33 | `data` char(40) NOT NULL, 34 | `salt` char(32) NOT NULL, 35 | PRIMARY KEY (`pages_id`), 36 | KEY `data` (`data`) 37 | ) ENGINE=MyISAM DEFAULT CHARSET=ascii; 38 | 39 | DROP TABLE IF EXISTS `field_permissions`; 40 | CREATE TABLE `field_permissions` ( 41 | `pages_id` int(10) unsigned NOT NULL, 42 | `data` int(11) NOT NULL, 43 | `sort` int(10) unsigned NOT NULL, 44 | PRIMARY KEY (`pages_id`,`sort`), 45 | KEY `data` (`data`,`pages_id`,`sort`) 46 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 47 | 48 | DROP TABLE IF EXISTS `field_process`; 49 | CREATE TABLE `field_process` ( 50 | `pages_id` int(11) NOT NULL DEFAULT '0', 51 | `data` int(11) NOT NULL DEFAULT '0', 52 | PRIMARY KEY (`pages_id`), 53 | KEY `data` (`data`) 54 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 55 | 56 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('6', '17'); 57 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('3', '12'); 58 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('8', '12'); 59 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('9', '14'); 60 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('10', '7'); 61 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('11', '47'); 62 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('16', '48'); 63 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('300', '104'); 64 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('21', '50'); 65 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('29', '66'); 66 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('23', '10'); 67 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('304', '138'); 68 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('31', '136'); 69 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('22', '76'); 70 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('30', '68'); 71 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('303', '129'); 72 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('2', '87'); 73 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('302', '121'); 74 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('301', '109'); 75 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('28', '76'); 76 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1007', '150'); 77 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1010', '159'); 78 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1012', '161'); 79 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1016', '163'); 80 | INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1017', '164'); 81 | 82 | DROP TABLE IF EXISTS `field_roles`; 83 | CREATE TABLE `field_roles` ( 84 | `pages_id` int(10) unsigned NOT NULL, 85 | `data` int(11) NOT NULL, 86 | `sort` int(10) unsigned NOT NULL, 87 | PRIMARY KEY (`pages_id`,`sort`), 88 | KEY `data` (`data`,`pages_id`,`sort`) 89 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 90 | 91 | DROP TABLE IF EXISTS `field_title`; 92 | CREATE TABLE `field_title` ( 93 | `pages_id` int(10) unsigned NOT NULL, 94 | `data` text NOT NULL, 95 | PRIMARY KEY (`pages_id`), 96 | KEY `data_exact` (`data`(255)), 97 | FULLTEXT KEY `data` (`data`) 98 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 99 | 100 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('11', 'Templates'); 101 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('16', 'Fields'); 102 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('22', 'Setup'); 103 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('3', 'Pages'); 104 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('6', 'Add Page'); 105 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('8', 'Tree'); 106 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('9', 'Save Sort'); 107 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('10', 'Edit'); 108 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('21', 'Modules'); 109 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('29', 'Users'); 110 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('30', 'Roles'); 111 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('2', 'Admin'); 112 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('7', 'Trash'); 113 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('27', '404 Not Found'); 114 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('302', 'Insert Link'); 115 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('23', 'Login'); 116 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('304', 'Profile'); 117 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('301', 'Empty Trash'); 118 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('300', 'Search'); 119 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('303', 'Insert Image'); 120 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('28', 'Access'); 121 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('31', 'Permissions'); 122 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('32', 'Edit pages'); 123 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('34', 'Delete pages'); 124 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('35', 'Move pages (change parent)'); 125 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('36', 'View pages'); 126 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('50', 'Sort child pages'); 127 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('51', 'Change templates on pages'); 128 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('52', 'Administer users'); 129 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('53', 'User can update profile/password'); 130 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('54', 'Lock or unlock a page'); 131 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1', 'Home'); 132 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1006', 'Use Page Lister'); 133 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1007', 'Find'); 134 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1010', 'Recent'); 135 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1011', 'Can see recently edited pages'); 136 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1012', 'Logs'); 137 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1013', 'Can view system logs'); 138 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1014', 'Can manage system logs'); 139 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1015', 'api'); 140 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1016', 'Export Site Profile'); 141 | INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1017', 'Upgrades'); 142 | 143 | DROP TABLE IF EXISTS `fieldgroups`; 144 | CREATE TABLE `fieldgroups` ( 145 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 146 | `name` varchar(250) CHARACTER SET ascii NOT NULL, 147 | PRIMARY KEY (`id`), 148 | UNIQUE KEY `name` (`name`) 149 | ) ENGINE=MyISAM AUTO_INCREMENT=98 DEFAULT CHARSET=utf8; 150 | 151 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('2', 'admin'); 152 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('3', 'user'); 153 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('4', 'role'); 154 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('5', 'permission'); 155 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('1', 'home'); 156 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('83', 'basic-page'); 157 | INSERT INTO `fieldgroups` (`id`, `name`) VALUES('97', 'api'); 158 | 159 | DROP TABLE IF EXISTS `fieldgroups_fields`; 160 | CREATE TABLE `fieldgroups_fields` ( 161 | `fieldgroups_id` int(10) unsigned NOT NULL DEFAULT '0', 162 | `fields_id` int(10) unsigned NOT NULL DEFAULT '0', 163 | `sort` int(11) unsigned NOT NULL DEFAULT '0', 164 | `data` text, 165 | PRIMARY KEY (`fieldgroups_id`,`fields_id`) 166 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 167 | 168 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('2', '2', '1', NULL); 169 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('2', '1', '0', NULL); 170 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '4', '2', NULL); 171 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '92', '1', NULL); 172 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('4', '5', '0', NULL); 173 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('5', '1', '0', NULL); 174 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '3', '0', NULL); 175 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '1', '0', NULL); 176 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '1', '0', NULL); 177 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '97', '3', NULL); 178 | INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '1', '0', NULL); 179 | 180 | DROP TABLE IF EXISTS `fields`; 181 | CREATE TABLE `fields` ( 182 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 183 | `type` varchar(128) CHARACTER SET ascii NOT NULL, 184 | `name` varchar(250) CHARACTER SET ascii NOT NULL, 185 | `flags` int(11) NOT NULL DEFAULT '0', 186 | `label` varchar(250) NOT NULL DEFAULT '', 187 | `data` text NOT NULL, 188 | PRIMARY KEY (`id`), 189 | UNIQUE KEY `name` (`name`), 190 | KEY `type` (`type`) 191 | ) ENGINE=MyISAM AUTO_INCREMENT=98 DEFAULT CHARSET=utf8; 192 | 193 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('1', 'FieldtypePageTitle', 'title', '13', 'Title', '{\"required\":1,\"textformatters\":[\"TextformatterEntities\"],\"size\":0,\"maxlength\":255}'); 194 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('2', 'FieldtypeModule', 'process', '25', 'Process', '{\"description\":\"The process that is executed on this page. Since this is mostly used by ProcessWire internally, it is recommended that you don\'t change the value of this unless adding your own pages in the admin.\",\"collapsed\":1,\"required\":1,\"moduleTypes\":[\"Process\"],\"permanent\":1}'); 195 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('3', 'FieldtypePassword', 'pass', '24', 'Set Password', '{\"collapsed\":1,\"size\":50,\"maxlength\":128}'); 196 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('5', 'FieldtypePage', 'permissions', '24', 'Permissions', '{\"derefAsPage\":0,\"parent_id\":31,\"labelFieldName\":\"title\",\"inputfield\":\"InputfieldCheckboxes\"}'); 197 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('4', 'FieldtypePage', 'roles', '24', 'Roles', '{\"derefAsPage\":0,\"parent_id\":30,\"labelFieldName\":\"name\",\"inputfield\":\"InputfieldCheckboxes\",\"description\":\"User will inherit the permissions assigned to each role. You may assign multiple roles to a user. When accessing a page, the user will only inherit permissions from the roles that are also assigned to the page\'s template.\"}'); 198 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('92', 'FieldtypeEmail', 'email', '9', 'E-Mail Address', '{\"size\":70,\"maxlength\":255}'); 199 | INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('97', 'FieldtypeModule', 'admin_theme', '8', 'Admin Theme', '{\"moduleTypes\":[\"AdminTheme\"],\"labelField\":\"title\",\"inputfieldClass\":\"InputfieldRadios\"}'); 200 | 201 | DROP TABLE IF EXISTS `modules`; 202 | CREATE TABLE `modules` ( 203 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 204 | `class` varchar(128) CHARACTER SET ascii NOT NULL, 205 | `flags` int(11) NOT NULL DEFAULT '0', 206 | `data` text NOT NULL, 207 | `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 208 | PRIMARY KEY (`id`), 209 | UNIQUE KEY `class` (`class`) 210 | ) ENGINE=MyISAM AUTO_INCREMENT=166 DEFAULT CHARSET=utf8; 211 | 212 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('1', 'FieldtypeTextarea', '0', '', '2017-12-03 18:00:49'); 213 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('2', 'FieldtypeNumber', '0', '', '2017-12-03 18:00:49'); 214 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('3', 'FieldtypeText', '0', '', '2017-12-03 18:00:49'); 215 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('4', 'FieldtypePage', '0', '', '2017-12-03 18:00:49'); 216 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('30', 'InputfieldForm', '0', '', '2017-12-03 18:00:49'); 217 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('6', 'FieldtypeFile', '0', '', '2017-12-03 18:00:49'); 218 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('7', 'ProcessPageEdit', '1', '', '2017-12-03 18:00:49'); 219 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('10', 'ProcessLogin', '0', '', '2017-12-03 18:00:49'); 220 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('12', 'ProcessPageList', '0', '{\"pageLabelField\":\"title\",\"paginationLimit\":25,\"limit\":50}', '2017-12-03 18:00:49'); 221 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('121', 'ProcessPageEditLink', '1', '', '2017-12-03 18:00:49'); 222 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('14', 'ProcessPageSort', '0', '', '2017-12-03 18:00:49'); 223 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('15', 'InputfieldPageListSelect', '0', '', '2017-12-03 18:00:49'); 224 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('117', 'JqueryUI', '1', '', '2017-12-03 18:00:49'); 225 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('17', 'ProcessPageAdd', '0', '', '2017-12-03 18:00:49'); 226 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('125', 'SessionLoginThrottle', '11', '', '2017-12-03 18:00:49'); 227 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('122', 'InputfieldPassword', '0', '', '2017-12-03 18:00:49'); 228 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('25', 'InputfieldAsmSelect', '0', '', '2017-12-03 18:00:49'); 229 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('116', 'JqueryCore', '1', '', '2017-12-03 18:00:49'); 230 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('27', 'FieldtypeModule', '0', '', '2017-12-03 18:00:49'); 231 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('28', 'FieldtypeDatetime', '0', '', '2017-12-03 18:00:49'); 232 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('29', 'FieldtypeEmail', '0', '', '2017-12-03 18:00:49'); 233 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('108', 'InputfieldURL', '0', '', '2017-12-03 18:00:49'); 234 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('32', 'InputfieldSubmit', '0', '', '2017-12-03 18:00:49'); 235 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('33', 'InputfieldWrapper', '0', '', '2017-12-03 18:00:49'); 236 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('34', 'InputfieldText', '0', '', '2017-12-03 18:00:49'); 237 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('35', 'InputfieldTextarea', '0', '', '2017-12-03 18:00:49'); 238 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('36', 'InputfieldSelect', '0', '', '2017-12-03 18:00:49'); 239 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('37', 'InputfieldCheckbox', '0', '', '2017-12-03 18:00:49'); 240 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('38', 'InputfieldCheckboxes', '0', '', '2017-12-03 18:00:49'); 241 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('39', 'InputfieldRadios', '0', '', '2017-12-03 18:00:49'); 242 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('40', 'InputfieldHidden', '0', '', '2017-12-03 18:00:49'); 243 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('41', 'InputfieldName', '0', '', '2017-12-03 18:00:49'); 244 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('43', 'InputfieldSelectMultiple', '0', '', '2017-12-03 18:00:49'); 245 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('45', 'JqueryWireTabs', '0', '', '2017-12-03 18:00:49'); 246 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('46', 'ProcessPage', '0', '', '2017-12-03 18:00:49'); 247 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('47', 'ProcessTemplate', '0', '', '2017-12-03 18:00:49'); 248 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('48', 'ProcessField', '32', '', '2017-12-03 18:00:49'); 249 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('50', 'ProcessModule', '0', '', '2017-12-03 18:00:49'); 250 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('114', 'PagePermissions', '3', '', '2017-12-03 18:00:49'); 251 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('97', 'FieldtypeCheckbox', '1', '', '2017-12-03 18:00:49'); 252 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('115', 'PageRender', '3', '{\"clearCache\":1}', '2017-12-03 18:00:49'); 253 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('55', 'InputfieldFile', '0', '', '2017-12-03 18:00:49'); 254 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('56', 'InputfieldImage', '0', '', '2017-12-03 18:00:49'); 255 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('57', 'FieldtypeImage', '0', '', '2017-12-03 18:00:49'); 256 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('60', 'InputfieldPage', '0', '{\"inputfieldClasses\":[\"InputfieldSelect\",\"InputfieldSelectMultiple\",\"InputfieldCheckboxes\",\"InputfieldRadios\",\"InputfieldAsmSelect\",\"InputfieldPageListSelect\",\"InputfieldPageListSelectMultiple\"]}', '2017-12-03 18:00:49'); 257 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('61', 'TextformatterEntities', '0', '', '2017-12-03 18:00:49'); 258 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('66', 'ProcessUser', '0', '{\"showFields\":[\"name\",\"email\",\"roles\"]}', '2017-12-03 18:00:49'); 259 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('67', 'MarkupAdminDataTable', '0', '', '2017-12-03 18:00:49'); 260 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('68', 'ProcessRole', '0', '{\"showFields\":[\"name\"]}', '2017-12-03 18:00:49'); 261 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('76', 'ProcessList', '0', '', '2017-12-03 18:00:49'); 262 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('78', 'InputfieldFieldset', '0', '', '2017-12-03 18:00:49'); 263 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('79', 'InputfieldMarkup', '0', '', '2017-12-03 18:00:49'); 264 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('80', 'InputfieldEmail', '0', '', '2017-12-03 18:00:49'); 265 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('89', 'FieldtypeFloat', '1', '', '2017-12-03 18:00:49'); 266 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('83', 'ProcessPageView', '0', '', '2017-12-03 18:00:49'); 267 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('84', 'FieldtypeInteger', '0', '', '2017-12-03 18:00:49'); 268 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('85', 'InputfieldInteger', '0', '', '2017-12-03 18:00:49'); 269 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('86', 'InputfieldPageName', '0', '', '2017-12-03 18:00:49'); 270 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('87', 'ProcessHome', '0', '', '2017-12-03 18:00:49'); 271 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('90', 'InputfieldFloat', '0', '', '2017-12-03 18:00:49'); 272 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('94', 'InputfieldDatetime', '0', '', '2017-12-03 18:00:49'); 273 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('98', 'MarkupPagerNav', '0', '', '2017-12-03 18:00:49'); 274 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('129', 'ProcessPageEditImageSelect', '1', '', '2017-12-03 18:00:49'); 275 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('103', 'JqueryTableSorter', '1', '', '2017-12-03 18:00:49'); 276 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('104', 'ProcessPageSearch', '1', '{\"searchFields\":\"title\",\"displayField\":\"title path\"}', '2017-12-03 18:00:49'); 277 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('105', 'FieldtypeFieldsetOpen', '1', '', '2017-12-03 18:00:49'); 278 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('106', 'FieldtypeFieldsetClose', '1', '', '2017-12-03 18:00:49'); 279 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('107', 'FieldtypeFieldsetTabOpen', '1', '', '2017-12-03 18:00:49'); 280 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('109', 'ProcessPageTrash', '1', '', '2017-12-03 18:00:49'); 281 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('111', 'FieldtypePageTitle', '1', '', '2017-12-03 18:00:49'); 282 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('112', 'InputfieldPageTitle', '0', '', '2017-12-03 18:00:49'); 283 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('113', 'MarkupPageArray', '3', '', '2017-12-03 18:00:49'); 284 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('131', 'InputfieldButton', '0', '', '2017-12-03 18:00:49'); 285 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('133', 'FieldtypePassword', '1', '', '2017-12-03 18:00:49'); 286 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('134', 'ProcessPageType', '33', '{\"showFields\":[]}', '2017-12-03 18:00:49'); 287 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('135', 'FieldtypeURL', '1', '', '2017-12-03 18:00:49'); 288 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('136', 'ProcessPermission', '1', '{\"showFields\":[\"name\",\"title\"]}', '2017-12-03 18:00:49'); 289 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('137', 'InputfieldPageListSelectMultiple', '0', '', '2017-12-03 18:00:49'); 290 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('138', 'ProcessProfile', '1', '{\"profileFields\":[\"pass\",\"email\",\"admin_theme\"]}', '2017-12-03 18:00:49'); 291 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('139', 'SystemUpdater', '1', '{\"systemVersion\":16,\"coreVersion\":\"3.0.90\"}', '2017-12-03 18:00:49'); 292 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('148', 'AdminThemeDefault', '10', '{\"colors\":\"classic\"}', '2017-12-03 18:00:49'); 293 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('149', 'InputfieldSelector', '42', '', '2017-12-03 18:00:49'); 294 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('150', 'ProcessPageLister', '32', '', '2017-12-03 18:00:49'); 295 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('151', 'JqueryMagnific', '1', '', '2017-12-03 18:00:49'); 296 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('155', 'InputfieldCKEditor', '0', '', '2017-12-03 18:00:49'); 297 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('156', 'MarkupHTMLPurifier', '0', '', '2017-12-03 18:00:49'); 298 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('159', 'ProcessRecentPages', '1', '', '2017-12-03 18:01:04'); 299 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('160', 'AdminThemeUikit', '10', '', '2017-12-03 18:01:04'); 300 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('161', 'ProcessLogger', '1', '', '2017-12-03 18:01:08'); 301 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('162', 'InputfieldIcon', '0', '', '2017-12-03 18:01:08'); 302 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('164', 'ProcessWireUpgrade', '1', '', '2018-02-06 18:14:34'); 303 | INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('165', 'ProcessWireUpgradeCheck', '11', '', '2018-02-06 18:14:34'); 304 | 305 | DROP TABLE IF EXISTS `pages`; 306 | CREATE TABLE `pages` ( 307 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 308 | `parent_id` int(11) unsigned NOT NULL DEFAULT '0', 309 | `templates_id` int(11) unsigned NOT NULL DEFAULT '0', 310 | `name` varchar(128) CHARACTER SET ascii NOT NULL, 311 | `status` int(10) unsigned NOT NULL DEFAULT '1', 312 | `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 313 | `modified_users_id` int(10) unsigned NOT NULL DEFAULT '2', 314 | `created` timestamp NOT NULL DEFAULT '2015-12-18 06:09:00', 315 | `created_users_id` int(10) unsigned NOT NULL DEFAULT '2', 316 | `published` datetime DEFAULT NULL, 317 | `sort` int(11) NOT NULL DEFAULT '0', 318 | PRIMARY KEY (`id`), 319 | UNIQUE KEY `name_parent_id` (`name`,`parent_id`), 320 | KEY `parent_id` (`parent_id`), 321 | KEY `templates_id` (`templates_id`), 322 | KEY `modified` (`modified`), 323 | KEY `created` (`created`), 324 | KEY `status` (`status`), 325 | KEY `published` (`published`) 326 | ) ENGINE=MyISAM AUTO_INCREMENT=1018 DEFAULT CHARSET=utf8; 327 | 328 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1', '0', '1', 'home', '9', '2017-12-03 18:06:31', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 329 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('2', '1', '2', 'processwire', '1035', '2017-12-03 18:01:05', '40', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '5'); 330 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('3', '2', '2', 'page', '21', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 331 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('6', '3', '2', 'add', '21', '2017-12-03 18:01:11', '40', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '1'); 332 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('7', '1', '2', 'trash', '1039', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '6'); 333 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('8', '3', '2', 'list', '21', '2017-12-03 18:01:14', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 334 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('9', '3', '2', 'sort', '1047', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '3'); 335 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('10', '3', '2', 'edit', '1045', '2017-12-03 18:01:13', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '4'); 336 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('11', '22', '2', 'template', '21', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 337 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('16', '22', '2', 'field', '21', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '2'); 338 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('21', '2', '2', 'module', '21', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '2'); 339 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('22', '2', '2', 'setup', '21', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '1'); 340 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('23', '2', '2', 'login', '1035', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '4'); 341 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('27', '1', '29', 'http404', '1035', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '3', '2017-12-03 18:00:49', '4'); 342 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('28', '2', '2', 'access', '13', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '3'); 343 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('29', '28', '2', 'users', '29', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 344 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('30', '28', '2', 'roles', '29', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '1'); 345 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('31', '28', '2', 'permissions', '29', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '2'); 346 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('32', '31', '5', 'page-edit', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '2'); 347 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('34', '31', '5', 'page-delete', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '3'); 348 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('35', '31', '5', 'page-move', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '4'); 349 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('36', '31', '5', 'page-view', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 350 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('37', '30', '4', 'guest', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 351 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('38', '30', '4', 'superuser', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '1'); 352 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('41', '29', '3', 'thomasaull', '1', '2017-12-03 18:01:05', '40', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '0'); 353 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('40', '29', '3', 'guest', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '1'); 354 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('50', '31', '5', 'page-sort', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '5'); 355 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('51', '31', '5', 'page-template', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '6'); 356 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('52', '31', '5', 'user-admin', '25', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '10'); 357 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('53', '31', '5', 'profile-edit', '1', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '13'); 358 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('54', '31', '5', 'page-lock', '1', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '8'); 359 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('300', '3', '2', 'search', '1045', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '6'); 360 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('301', '3', '2', 'trash', '1047', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '6'); 361 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('302', '3', '2', 'link', '1041', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '7'); 362 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('303', '3', '2', 'image', '1041', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '2', '2017-12-03 18:00:49', '8'); 363 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('304', '2', '2', 'profile', '1025', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '41', '2017-12-03 18:00:49', '5'); 364 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1006', '31', '5', 'page-lister', '1', '2017-12-03 18:00:49', '40', '2017-12-03 18:00:49', '40', '2017-12-03 18:00:49', '9'); 365 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1007', '3', '2', 'lister', '1', '2017-12-03 18:00:49', '40', '2017-12-03 18:00:49', '40', '2017-12-03 18:00:49', '9'); 366 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1010', '3', '2', 'recent-pages', '1', '2017-12-03 18:01:04', '40', '2017-12-03 18:01:04', '40', '2017-12-03 18:01:04', '10'); 367 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1011', '31', '5', 'page-edit-recent', '1', '2017-12-03 18:01:04', '40', '2017-12-03 18:01:04', '40', '2017-12-03 18:01:04', '10'); 368 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1012', '22', '2', 'logs', '1', '2017-12-03 18:01:08', '40', '2017-12-03 18:01:08', '40', '2017-12-03 18:01:08', '2'); 369 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1013', '31', '5', 'logs-view', '1', '2017-12-03 18:01:08', '40', '2017-12-03 18:01:08', '40', '2017-12-03 18:01:08', '11'); 370 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1014', '31', '5', 'logs-edit', '1', '2017-12-03 18:01:08', '40', '2017-12-03 18:01:08', '40', '2017-12-03 18:01:08', '12'); 371 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1015', '1', '43', 'api', '1025', '2017-12-03 18:06:31', '41', '2017-12-03 18:06:07', '41', '2017-12-03 18:06:07', '3'); 372 | INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1017', '22', '2', 'upgrades', '1', '2018-02-06 18:14:34', '41', '2018-02-06 18:14:34', '41', '2018-02-06 18:14:34', '4'); 373 | 374 | DROP TABLE IF EXISTS `pages_access`; 375 | CREATE TABLE `pages_access` ( 376 | `pages_id` int(11) NOT NULL, 377 | `templates_id` int(11) NOT NULL, 378 | `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 379 | PRIMARY KEY (`pages_id`), 380 | KEY `templates_id` (`templates_id`) 381 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 382 | 383 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('37', '2', '2017-12-03 18:00:49'); 384 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('38', '2', '2017-12-03 18:00:49'); 385 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('32', '2', '2017-12-03 18:00:49'); 386 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('34', '2', '2017-12-03 18:00:49'); 387 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('35', '2', '2017-12-03 18:00:49'); 388 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('36', '2', '2017-12-03 18:00:49'); 389 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('50', '2', '2017-12-03 18:00:49'); 390 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('51', '2', '2017-12-03 18:00:49'); 391 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('52', '2', '2017-12-03 18:00:49'); 392 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('53', '2', '2017-12-03 18:00:49'); 393 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('54', '2', '2017-12-03 18:00:49'); 394 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1006', '2', '2017-12-03 18:00:49'); 395 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1011', '2', '2017-12-03 18:01:04'); 396 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1013', '2', '2017-12-03 18:01:08'); 397 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1014', '2', '2017-12-03 18:01:08'); 398 | INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1015', '1', '2017-12-03 18:06:07'); 399 | 400 | DROP TABLE IF EXISTS `pages_parents`; 401 | CREATE TABLE `pages_parents` ( 402 | `pages_id` int(10) unsigned NOT NULL, 403 | `parents_id` int(10) unsigned NOT NULL, 404 | PRIMARY KEY (`pages_id`,`parents_id`) 405 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 406 | 407 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('2', '1'); 408 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('3', '1'); 409 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('3', '2'); 410 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('7', '1'); 411 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('22', '1'); 412 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('22', '2'); 413 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('28', '1'); 414 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('28', '2'); 415 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('29', '1'); 416 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('29', '2'); 417 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('29', '28'); 418 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('30', '1'); 419 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('30', '2'); 420 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('30', '28'); 421 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('31', '1'); 422 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('31', '2'); 423 | INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('31', '28'); 424 | 425 | DROP TABLE IF EXISTS `pages_sortfields`; 426 | CREATE TABLE `pages_sortfields` ( 427 | `pages_id` int(10) unsigned NOT NULL DEFAULT '0', 428 | `sortfield` varchar(20) NOT NULL DEFAULT '', 429 | PRIMARY KEY (`pages_id`) 430 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 431 | 432 | 433 | DROP TABLE IF EXISTS `session_login_throttle`; 434 | CREATE TABLE `session_login_throttle` ( 435 | `name` varchar(128) NOT NULL, 436 | `attempts` int(10) unsigned NOT NULL DEFAULT '0', 437 | `last_attempt` int(10) unsigned NOT NULL, 438 | PRIMARY KEY (`name`) 439 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 440 | 441 | DROP TABLE IF EXISTS `templates`; 442 | CREATE TABLE `templates` ( 443 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 444 | `name` varchar(250) CHARACTER SET ascii NOT NULL, 445 | `fieldgroups_id` int(10) unsigned NOT NULL DEFAULT '0', 446 | `flags` int(11) NOT NULL DEFAULT '0', 447 | `cache_time` mediumint(9) NOT NULL DEFAULT '0', 448 | `data` text NOT NULL, 449 | PRIMARY KEY (`id`), 450 | UNIQUE KEY `name` (`name`), 451 | KEY `fieldgroups_id` (`fieldgroups_id`) 452 | ) ENGINE=MyISAM AUTO_INCREMENT=44 DEFAULT CHARSET=utf8; 453 | 454 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('2', 'admin', '2', '8', '0', '{\"useRoles\":1,\"parentTemplates\":[2],\"allowPageNum\":1,\"redirectLogin\":23,\"slashUrls\":1,\"noGlobal\":1,\"compile\":3,\"modified\":1512384903,\"ns\":\"ProcessWire\"}'); 455 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('3', 'user', '3', '8', '0', '{\"useRoles\":1,\"noChildren\":1,\"parentTemplates\":[2],\"slashUrls\":1,\"pageClass\":\"User\",\"noGlobal\":1,\"noMove\":1,\"noTrash\":1,\"noSettings\":1,\"noChangeTemplate\":1,\"nameContentTab\":1}'); 456 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('4', 'role', '4', '8', '0', '{\"noChildren\":1,\"parentTemplates\":[2],\"slashUrls\":1,\"pageClass\":\"Role\",\"noGlobal\":1,\"noMove\":1,\"noTrash\":1,\"noSettings\":1,\"noChangeTemplate\":1,\"nameContentTab\":1}'); 457 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('5', 'permission', '5', '8', '0', '{\"noChildren\":1,\"parentTemplates\":[2],\"slashUrls\":1,\"guestSearchable\":1,\"pageClass\":\"Permission\",\"noGlobal\":1,\"noMove\":1,\"noTrash\":1,\"noSettings\":1,\"noChangeTemplate\":1,\"nameContentTab\":1}'); 458 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('1', 'home', '1', '0', '0', '{\"useRoles\":1,\"noParents\":1,\"slashUrls\":1,\"compile\":3,\"modified\":1512384903,\"ns\":\"\\\\\",\"roles\":[37]}'); 459 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('29', 'basic-page', '83', '0', '0', '{\"slashUrls\":1,\"compile\":3,\"modified\":1512384903,\"ns\":\"\\\\\"}'); 460 | INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('43', 'api', '97', '0', '0', '{\"noChildren\":1,\"noParents\":-1,\"urlSegments\":1,\"slashUrls\":1,\"compile\":3,\"modified\":1513550412,\"ns\":\"ProcessWire\"}'); 461 | 462 | UPDATE pages SET created_users_id=41, modified_users_id=41, created=NOW(), modified=NOW(); 463 | 464 | # --- /WireDatabaseBackup {"numTables":12,"numCreateTables":18,"numInserts":269,"numSeconds":0} -------------------------------------------------------------------------------- /modules/Helloworld/Helloworld.module: -------------------------------------------------------------------------------- 1 | 'Hello World', 32 | 33 | // version number 34 | 'version' => 3, 35 | 36 | // summary is brief description of what this module is 37 | 'summary' => 'An example module used for demonstration purposes.', 38 | 39 | // Optional URL to more information about the module 40 | 'href' => 'https://processwire.com', 41 | 42 | // singular=true: indicates that only one instance of the module is allowed. 43 | // This is usually what you want for modules that attach hooks. 44 | 'singular' => true, 45 | 46 | // autoload=true: indicates the module should be started with ProcessWire. 47 | // This is necessary for any modules that attach runtime hooks, otherwise those 48 | // hooks won't get attached unless some other code calls the module on it's own. 49 | // Note that autoload modules are almost always also 'singular' (seen above). 50 | 'autoload' => true, 51 | 52 | // Optional font-awesome icon name, minus the 'fa-' part 53 | 'icon' => 'smile-o', 54 | ); 55 | } 56 | 57 | /** 58 | * Initialize the module 59 | * 60 | * ProcessWire calls this when the module is loaded. For 'autoload' modules, this will be called 61 | * when ProcessWire's API is ready. As a result, this is a good place to attach hooks. 62 | * 63 | */ 64 | public function init() { 65 | 66 | // add a hook after the $pages->save, to issue a notice every time a page is saved 67 | $this->pages->addHookAfter('save', $this, 'example1'); 68 | 69 | // add a hook after each page is rendered and modify the output 70 | $this->addHookAfter('Page::render', $this, 'example2'); 71 | 72 | // add a 'hello' method to every page that returns "Hello World" 73 | // use "echo $page->hello();" in your template file to display output 74 | $this->addHook('Page::hello', $this, 'example3'); 75 | 76 | // add a 'hello_world' property to every page that returns "Hello [user]" 77 | // use "echo $page->hello_world;" in your template file to display output 78 | $this->addHookProperty('Page::hello_world', $this, 'example4'); 79 | } 80 | 81 | /** 82 | * Example1 hooks into the pages->save method and displays a notice every time a page is saved 83 | * 84 | * @param HookEvent $event 85 | * 86 | */ 87 | public function example1($event) { 88 | /** @var Page $page */ 89 | $page = $event->arguments[0]; 90 | $this->message("Hello World! You saved {$page->path}."); 91 | } 92 | 93 | 94 | /** 95 | * Example2 hooks into every page after it's rendered and adds "Hello World" text at the bottom 96 | * 97 | * @param HookEvent $event 98 | * 99 | */ 100 | public function example2($event) { 101 | 102 | /** @var Page $page */ 103 | $page = $event->object; 104 | 105 | // don't add this to the admin pages 106 | if($page->template == 'admin') return; 107 | 108 | // add a "Hello World" paragraph right before the closing body tag 109 | $event->return = str_replace("
9 |", "
Hello World!
", $event->return); 110 | } 111 | 112 | /** 113 | * Example3 adds a 'hello' method (not property) to every page that simply returns "Hello World" 114 | * 115 | * @param HookEvent $event 116 | * 117 | */ 118 | public function example3($event) { 119 | $event->return = "Hello World"; 120 | } 121 | 122 | /** 123 | * Example 4 adds a 'hello_world' property (not method) to every page that returns "Hello [user]" 124 | * 125 | * @param HookEvent $event 126 | * 127 | */ 128 | public function example4($event) { 129 | $event->return = "Hello " . $this->user->name; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/README.txt: -------------------------------------------------------------------------------- 1 | This InputfieldCKEditor directory is here to provide optional extra configuration options 2 | and plugins to the CKEditor Inputfield module. 3 | 4 | 5 | plugins/ 6 | ======== 7 | Directory to place additional CKEditor plugins in. You can then activate them 8 | from your CKEditor field settings. 9 | 10 | 11 | contents.css 12 | ============ 13 | Example CSS file for the admin editor. To make CKEditor use this file, go to your CKEditor 14 | field settings and specify /site/modules/InputfieldCKEditor/contents.css as the regular 15 | mode Contents CSS file. 16 | 17 | 18 | contents-inline.css 19 | =================== 20 | Same as contents.css but for the inline mode editor. 21 | 22 | 23 | mystyles.js 24 | =========== 25 | Optional configuration for the CKEditor Styles option. To use this file, go to your 26 | CKEditor field settings and set the Custom Styles Set to be this file. 27 | 28 | 29 | config.js 30 | ========= 31 | Custom config file used by all CKEditor instances (except instances configured by their 32 | own custom config file, see below...) 33 | 34 | 35 | config-body.js 36 | ============== 37 | Example of field-specific custom config file. This one applies to a field named "body". 38 | Note that these config settings can also be specified directly in your CKEditor field 39 | settings in the admin, which many may prefer. 40 | 41 | -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/config-body.js: -------------------------------------------------------------------------------- 1 | /** 2 | * CKEditor field-specific (body) custom config file for ProcessWire 3 | * 4 | * Use this file to specify additional config options to a field named "body". 5 | * This is here just for example purposes. If you wanted to create a config 6 | * specific to some other field, like "sidebar", you would create another file 7 | * exactly like this named: config-sidebar.js 8 | * 9 | * If you wanted to use the same config.js for all of your CKEditor fields, 10 | * you would remove this file and just use the config.js file instead. Meaning, 11 | * this file completely overrides config.js if the field being edited is named 12 | * "body". The regular config.js file is not loaded when this one is loaded. 13 | * 14 | */ 15 | 16 | /** 17 | * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. 18 | * For licensing, see LICENSE.html or http://ckeditor.com/license 19 | */ 20 | 21 | CKEDITOR.editorConfig = function( config ) { 22 | // Define changes to default configuration here. For example: 23 | // config.uiColor = '#AADC6E'; 24 | }; -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * CKEditor custom config file for ProcessWire 3 | * 4 | * Use this file to specify additional config options to all CKEditor instances, 5 | * except those that have field-specific config files, i.e. config-body.js for 6 | * config specific to a field named "body". 7 | * 8 | */ 9 | 10 | /** 11 | * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. 12 | * For licensing, see LICENSE.html or http://ckeditor.com/license 13 | */ 14 | 15 | CKEDITOR.editorConfig = function( config ) { 16 | // Define changes to default configuration here. For example: 17 | // config.uiColor = '#AADC6E'; 18 | }; -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/contents-inline.css: -------------------------------------------------------------------------------- 1 | /** 2 | * contents-inline.css 3 | * 4 | * CKEditor editor styles for inline mode editor 5 | * 6 | * See also: contents-inline.scss 7 | * 8 | * PLEASE NOTE: 9 | * 10 | * It's possible this file may be out of date since it is in /site/ rather than /wire/, 11 | * and the version of this file will reflect whatever version you had when you first 12 | * installed this copy of ProcessWire. 13 | * 14 | * If you intend to use this, you may first want to get the newest copy out of: 15 | * /wire/modules/Inputfield/InputfieldCKEditor/contents-inline.css 16 | * 17 | * Original file copyright (as included with CKEditor): 18 | * Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. 19 | * For licensing, see LICENSE.html or http://ckeditor.com/license 20 | * 21 | */ 22 | 23 | .InputfieldForm .InputfieldCKEditorInline { 24 | font-family: Arial, sans-serif; 25 | } 26 | .InputfieldForm .InputfieldCKEditorInline p, 27 | .InputfieldForm .InputfieldCKEditorInline li, 28 | .InputfieldForm .InputfieldCKEditorInline dl, 29 | .InputfieldForm .InputfieldCKEditorInline td { 30 | font-size: 1em; 31 | color: #333333; 32 | background: white; 33 | } 34 | .InputfieldForm .InputfieldCKEditorInline a { 35 | color: #444444; 36 | background: none; 37 | text-decoration: underline; 38 | } 39 | .InputfieldForm .InputfieldCKEditorInline a:hover { 40 | color: #222222; 41 | background: #ffffdd; 42 | } 43 | .InputfieldForm .InputfieldCKEditorInline i, 44 | .InputfieldForm .InputfieldCKEditorInline em { 45 | font-style: italic; 46 | } 47 | .InputfieldForm .InputfieldCKEditorInline b, 48 | .InputfieldForm .InputfieldCKEditorInline strong { 49 | font-weight: bold; 50 | } 51 | .InputfieldForm .InputfieldCKEditorInline strong em, 52 | .InputfieldForm .InputfieldCKEditorInline em strong { 53 | font-weight: bold; 54 | font-style: italic; 55 | } 56 | .InputfieldForm .InputfieldCKEditorInline small { 57 | font-size: 0.875em; 58 | } 59 | .InputfieldForm .InputfieldCKEditorInline pre, 60 | .InputfieldForm .InputfieldCKEditorInline code { 61 | font-family: Menlo, Monaco, 'Andale Mono', 'Lucida Console', 'Courier New', monospace; 62 | } 63 | .InputfieldForm .InputfieldCKEditorInline code { 64 | display: inline; 65 | background: #fff2a8; 66 | } 67 | .InputfieldForm .InputfieldCKEditorInline ul li, 68 | .InputfieldForm .InputfieldCKEditorInline ol li { 69 | list-style: disc; 70 | display: list-item; 71 | margin: 0 0 0 2em; 72 | } 73 | .InputfieldForm .InputfieldCKEditorInline ol li { 74 | list-style: decimal; 75 | } 76 | .InputfieldForm .InputfieldCKEditorInline blockquote { 77 | padding-left: 1em; 78 | border-left: 3px solid #ccc; 79 | } 80 | .InputfieldForm .InputfieldCKEditorInline h1, 81 | .InputfieldForm .InputfieldCKEditorInline h2, 82 | .InputfieldForm .InputfieldCKEditorInline h3, 83 | .InputfieldForm .InputfieldCKEditorInline h4, 84 | .InputfieldForm .InputfieldCKEditorInline h5 { 85 | color: #222222; 86 | font-family: Arial, sans-serif; 87 | font-weight: normal; 88 | text-transform: none; 89 | } 90 | .InputfieldForm .InputfieldCKEditorInline h1 { 91 | font-size: 2.0em; 92 | } 93 | .InputfieldForm .InputfieldCKEditorInline h2 { 94 | font-size: 1.7em; 95 | } 96 | .InputfieldForm .InputfieldCKEditorInline h3 { 97 | font-size: 1.5em; 98 | } 99 | .InputfieldForm .InputfieldCKEditorInline h4 { 100 | font-size: 1.3em; 101 | } 102 | .InputfieldForm .InputfieldCKEditorInline h5 { 103 | font-size: 1.2em; 104 | } 105 | .InputfieldForm .InputfieldCKEditorInline h6 { 106 | font-size: 1.1em; 107 | } 108 | .InputfieldForm .InputfieldCKEditorInline table td, 109 | .InputfieldForm .InputfieldCKEditorInline table th { 110 | padding: 3px; 111 | } 112 | .InputfieldForm .InputfieldCKEditorInline table th { 113 | font-weight: bold; 114 | } 115 | .InputfieldForm .InputfieldCKEditorInline img { 116 | max-width: 100%; 117 | } 118 | .InputfieldForm .InputfieldCKEditorInline img.cke_anchor { 119 | display: inline; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/contents.css: -------------------------------------------------------------------------------- 1 | /** 2 | * contents.css 3 | * 4 | * CKEditor editor styles for regular (non-inline) editor 5 | * See contents-inline.css for inline editor styles. 6 | * 7 | * Note that this file is not in use unless you configure your editor settings to use it 8 | * in the "Custom Editor CSS File (regular mode)" option. As a result, this file is here 9 | * primarily as a placeholder and for instructions, though you may certainly modify and 10 | * use it as-is. 11 | * 12 | * PLEASE NOTE: 13 | * 14 | * It's possible this file may be out of date since it is in /site/ rather than /wire/, 15 | * and the version of this file will reflect whatever version you had when you first 16 | * installed this copy of ProcessWire. 17 | * 18 | * If you intend to use this, you may first want to get the newest copy out of: 19 | * /wire/modules/Inputfield/InputfieldCKEditor/contents.css 20 | * 21 | * Original file copyright (as included with CKEditor): 22 | * Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. 23 | * For licensing, see LICENSE.html or http://ckeditor.com/license 24 | * 25 | */ 26 | 27 | body { 28 | /* Font */ 29 | font-family: sans-serif, Arial, Verdana, "Trebuchet MS"; 30 | font-size: 14px; 31 | 32 | /* Text color */ 33 | color: #333; 34 | 35 | /* Remove the background color to make it transparent */ 36 | background-color: #fff; 37 | 38 | margin: 10px; 39 | } 40 | 41 | .cke_editable { 42 | font-size: 14px; 43 | line-height: 1.6em; 44 | } 45 | 46 | blockquote { 47 | font-style: italic; 48 | font-family: Georgia, Times, "Times New Roman", serif; 49 | padding: 2px 0; 50 | border-style: solid; 51 | border-color: #ccc; 52 | border-width: 0; 53 | } 54 | 55 | .cke_contents_ltr blockquote { 56 | padding-left: 20px; 57 | padding-right: 8px; 58 | border-left-width: 5px; 59 | } 60 | 61 | .cke_contents_rtl blockquote { 62 | padding-left: 8px; 63 | padding-right: 20px; 64 | border-right-width: 5px; 65 | } 66 | 67 | a { 68 | color: #0782C1; 69 | } 70 | 71 | ol,ul,dl { 72 | /* IE7: reset rtl list margin. (#7334) */ 73 | *margin-right: 0px; 74 | /* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/ 75 | padding: 0 40px; 76 | } 77 | 78 | h1,h2,h3,h4,h5,h6 { 79 | font-weight: bold; 80 | line-height: 1.2em; 81 | } 82 | 83 | hr { 84 | border: 0px; 85 | border-top: 1px solid #ccc; 86 | } 87 | 88 | img { 89 | max-width: 100%; 90 | } 91 | 92 | img.right, 93 | img.align_right, 94 | img.align-right { 95 | /* RCD */ 96 | border: 1px solid #ccc; 97 | float: right; 98 | margin-left: 15px; 99 | padding: 5px; 100 | } 101 | 102 | img.left, 103 | img.align_left, 104 | img.align-left { 105 | /* RCD */ 106 | border: 1px solid #ccc; 107 | float: left; 108 | margin-right: 15px; 109 | padding: 5px; 110 | } 111 | 112 | img.align_center, 113 | img.align-center { 114 | /* RCD */ 115 | display: block; 116 | margin-left: auto; 117 | margin-right: auto; 118 | } 119 | 120 | img:hover { 121 | opacity: .9; 122 | filter: alpha(opacity = 90); 123 | } 124 | 125 | pre { 126 | white-space: pre-wrap; 127 | word-wrap: break-word; 128 | -moz-tab-size: 4; 129 | -o-tab-size: 4; 130 | -webkit-tab-size: 4; 131 | tab-size: 4; 132 | } 133 | 134 | .marker { 135 | background-color: Yellow; 136 | } 137 | 138 | span[lang] { 139 | font-style: italic; 140 | } 141 | 142 | figure { 143 | text-align: center; 144 | border: solid 1px #ccc; 145 | border-radius: 2px; 146 | background: rgba(0,0,0,0.05); 147 | padding: 10px; 148 | margin: 10px 20px; 149 | display: inline-block; 150 | } 151 | 152 | figure > figcaption { 153 | text-align: center; 154 | display: block; /* For IE8 */ 155 | } 156 | 157 | code { 158 | /* RCD */ 159 | background: #fff2a8; 160 | } 161 | 162 | a > img { 163 | padding: 1px; 164 | margin: 1px; 165 | border: none; 166 | outline: 1px solid #0782C1; 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/mystyles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * mystyles.js - for ProcessWire CKEditor "Custom Editor Styles Set" option 3 | * 4 | * Example file for "Custom Editor Styles Set" as seen in your CKEditor field config. 5 | * This file is not in use unless you specify it for that configuration item. 6 | * 7 | * PLEASE NOTE: 8 | * 9 | * It's possible this file may be out of date since it is in /site/ rather than /wire/, 10 | * and the version of this file will reflect whatever version you had when you first 11 | * installed this copy of ProcessWire. 12 | * 13 | * If you intend to use this, you may first want to get the newest copy out of: 14 | * /wire/modules/Inputfield/InputfieldCKEditor/mystyles.js 15 | * 16 | * For a more comprehensive example, see: 17 | * /wire/modules/Inputfield/InputfieldCKEditor/ckeditor-[version]/styles.js 18 | * 19 | */ 20 | 21 | CKEDITOR.stylesSet.add( 'mystyles', [ 22 | { name: 'Inline Code', element: 'code' }, 23 | { name: 'Inline Quotation', element: 'q' }, 24 | { name: 'Left Aligned Photo', element: 'img', attributes: { 'class': 'align_left' } }, 25 | { name: 'Right Aligned Photo', element: 'img', attributes: { 'class': 'align_right' } }, 26 | { name: 'Centered Photo', element: 'img', attributes: { 'class': 'align_center' } }, 27 | { name: 'Small', element: 'small' }, 28 | { name: 'Deleted Text', element: 'del' }, 29 | { name: 'Inserted Text', element: 'ins' }, 30 | { name: 'Cited Work', element: 'cite' } 31 | ]); 32 | 33 | -------------------------------------------------------------------------------- /modules/InputfieldCKEditor/plugins/README.txt: -------------------------------------------------------------------------------- 1 | Directory to place additional CKEditor plugins in. You can then activate them 2 | from your CKEditor field settings. Place each plugin in its own directory 3 | having the same name as the plugin. 4 | -------------------------------------------------------------------------------- /modules/ProcessWireUpgrade/ProcessWireUpgrade.css: -------------------------------------------------------------------------------- 1 | .content ul.bullets li { 2 | display: list-item; 3 | list-style: disc; 4 | margin-left: 2em; 5 | padding-left: 0; 6 | margin-top: 0; 7 | margin-bottom: 0; 8 | } 9 | -------------------------------------------------------------------------------- /modules/ProcessWireUpgrade/ProcessWireUpgrade.module: -------------------------------------------------------------------------------- 1 | 'Upgrades', 23 | 'summary' => 'Tool that helps you identify and install core and module upgrades.', 24 | 'version' => 7, 25 | 'author' => 'Ryan Cramer', 26 | 'installs' => 'ProcessWireUpgradeCheck', 27 | 'requires' => 'ProcessWire>=2.5.20', 28 | 'icon' => 'coffee' 29 | ); 30 | } 31 | 32 | const debug = false; 33 | const pageName = 'upgrades'; 34 | const minVersionPHP = '5.3.8'; 35 | 36 | protected $indexHashes = array( 37 | '2.4.0' => 'ae121ccc9c14a2cd5fa57e8786bdbb3f', 38 | '2.5.0' => '9b20ce2898be505608d54a1e0dd81215', 39 | '2.7.0' => '756b8a5685ce2b6b6e92062dcf040973', 40 | '2.7.2' => '756b8a5685ce2b6b6e92062dcf040973', 41 | '2.8.0' => '00a912363499a60af71011fd9c736726', 42 | '3.0.0' => '3a29d29b4ff7b2273b0739a82305ff71', 43 | '3.0.34' => 'b4f5308a9a3d53409393d875344c914b', 44 | ); 45 | 46 | protected $htaccessHashes = array( 47 | '2.4.0' => '5114479740cb1e79a8004f3eddeecb54', 48 | '2.5.0' => 'f8229ef5e26221226844d461e1a4d8d2', 49 | '2.7.0' => 'd659abbf6c035b462b735743c007b17a', 50 | '2.7.2' => '31a04ba76f50c94bcf1f848d334d62c5', 51 | '2.8.0' => 'ae2d8985fc4205297a89f821474dd6ad', 52 | '3.0.0' => 'd659abbf6c035b462b735743c007b17a', 53 | '3.0.34' => '22aee112acd42dfc8d89ac246848457e', 54 | ); 55 | 56 | /** 57 | * Path to /wire/ 58 | * 59 | */ 60 | protected $wirePath = ''; 61 | 62 | /** 63 | * Temporary path used by installer for download and ZIP extraction 64 | * 65 | */ 66 | protected $tempPath = ''; 67 | 68 | /** 69 | * Temporary path used by installer for file storage when file system isn't writable 70 | * 71 | */ 72 | protected $cachePath = ''; 73 | 74 | /** 75 | * Array of renames (oldPath => newPath) scheduled for __destruct 76 | * 77 | */ 78 | protected $renames = array(); // scheduled renames to occur after page render 79 | 80 | /** 81 | * Instance of ProcessWireUpgradeCheck 82 | * 83 | * @var ProcessWireUpgradeCheck 84 | * 85 | */ 86 | protected $checker = null; 87 | 88 | /** 89 | * Construct 90 | * 91 | */ 92 | public function __construct() { 93 | $this->wirePath = $this->config->paths->root . 'wire/'; 94 | $this->tempPath = $this->config->paths->cache . $this->className() . '/'; 95 | $this->cachePath = $this->config->paths->cache . 'core-upgrade/'; 96 | parent::__construct(); 97 | } 98 | 99 | /** 100 | * Initialize and perform access checks 101 | * 102 | */ 103 | public function init() { 104 | if($this->config->demo) throw new WireException("This module cannot be used in demo mode"); 105 | if(!$this->user->isSuperuser()) throw new WireException("This module requires superuser"); 106 | set_time_limit(3600); 107 | $this->checker = $this->modules->getInstall('ProcessWireUpgradeCheck'); 108 | if(!$this->checker) throw new WireException("Please go to Modules and click 'Check for new modules' - this will auto-update ProcessWireUpgrade."); 109 | parent::init(); 110 | } 111 | 112 | /** 113 | * Get info for either a specific core branch, or for the currently selected core branch 114 | * 115 | * @param string $name 116 | * @return array 117 | * 118 | */ 119 | protected function getBranch($name = '') { 120 | $branches = $this->checker->getCoreBranches(); 121 | if(empty($name)) $name = $this->session->get('ProcessWireUpgrade_branch'); 122 | return isset($branches[$name]) ? $branches[$name] : array(); 123 | } 124 | 125 | /** 126 | * Set the current core branch 127 | * 128 | * @param string $name 129 | * 130 | */ 131 | protected function setBranch($name) { 132 | $this->session->set('ProcessWireUpgrade_branch', $name); 133 | } 134 | 135 | /** 136 | * Ask user to select branch or make them remove existing installation files 137 | * 138 | */ 139 | public function execute() { 140 | 141 | if(self::debug) { 142 | $this->message('index.php ' . $this->config->version . ': ' . md5(file_get_contents($this->config->paths->root . 'index.php'))); 143 | $this->message('.htaccess ' . $this->config->version . ': ' . md5(file_get_contents($this->config->paths->root . '.htaccess'))); 144 | } 145 | 146 | if(version_compare(PHP_VERSION, self::minVersionPHP) >= 0) { 147 | // good 148 | } else { 149 | $this->error("Please note that your current PHP version (" . PHP_VERSION . ") is not adequate to upgrade to the latest ProcessWire."); 150 | } 151 | 152 | if(!extension_loaded('pdo_mysql')) { 153 | $this->error("Your PHP is not compiled with PDO support. PDO is required by ProcessWire 2.4+."); 154 | } 155 | 156 | if(!class_exists('ZipArchive')) { 157 | $this->error("Your PHP does not have ZipArchive support. This is required to install core or module upgrades with this tool."); 158 | } 159 | 160 | $out = ''; 161 | 162 | if(file_exists($this->cachePath) || file_exists($this->tempPath)) { 163 | $out = "
"; 164 | /** @var InputfieldButton $btn */ 165 | $btn = $this->modules->get('InputfieldButton'); 166 | $btn->href = "./remove"; 167 | $btn->value = $this->_('Remove'); 168 | $out .= $btn->render(); 169 | return $out; 170 | } 171 | 172 | if(method_exists($this->modules, 'resetCache')) { 173 | $this->modules->resetCache(); 174 | } 175 | 176 | /** @var MarkupAdminDataTable $table */ 177 | $table = $this->modules->get('MarkupAdminDataTable'); 178 | $table->setEncodeEntities(false); 179 | $table->headerRow(array( 180 | $this->_('Module'), 181 | $this->_('Class'), 182 | $this->_('Installed'), 183 | $this->_('Latest'), 184 | $this->_('Status') 185 | )); 186 | 187 | $items = $this->checker->getVersions(); 188 | if(count($items)) { 189 | foreach($items as $name => $item) { 190 | if(empty($item['remote'])) continue; // not in directory 191 | $remote = $this->sanitizer->entities($item['remote']); 192 | $upgradeLabel = $this->_('Up-to-date'); 193 | 194 | if($item['new'] > 0) { 195 | $upgradeLabel = $this->_('Upgrade available'); 196 | $remote = "$remote"; 197 | } else if($item['new'] < 0) { 198 | $upgradeLabel .= '+'; 199 | } 200 | if(empty($item['branch'])) { 201 | $upgradeURL = $this->wire('config')->urls->admin . "module/?update=$name"; 202 | } else { 203 | $upgradeURL = "./check?branch=$item[branch]"; 204 | } 205 | if($item['new'] > 0) $upgrade = " $upgradeLabel"; 206 | else $upgrade = "$upgradeLabel"; 207 | // else if(!$item['remote']) $upgrade = "" . $this->_('Not in directory') . ""; 208 | $table->row(array( 209 | "" . $this->sanitizer->entities($item['title']) . "", 210 | $this->sanitizer->entities($name), 211 | $this->sanitizer->entities($item['local']), 212 | $remote, 213 | $upgrade 214 | )); 215 | } 216 | } 217 | 218 | $out .= $table->render(); 219 | 220 | return $out; 221 | } 222 | 223 | /** 224 | * Remove existing installation files 225 | * 226 | */ 227 | public function executeRemove() { 228 | if(wireRmdir($this->cachePath, true)) $this->message("Removed $this->cachePath"); 229 | if(wireRmdir($this->tempPath, true)) $this->message("Removed $this->tempPath"); 230 | $this->session->redirect("./"); 231 | } 232 | 233 | /** 234 | * Check the selected branch and compare to current version to see if user wants to continue 235 | * 236 | */ 237 | public function executeCheck() { 238 | 239 | if(!$this->config->debug) { 240 | $this->error( 241 | "While optional, we recommend that you enable debug mode during the upgrade " . 242 | "so that you will see detailed error messages, should they occur. " . 243 | "Do this by editing /site/config.php and setting the debug " . 244 | "option to true. Example: \$config->debug = true;" 245 | ); 246 | } 247 | 248 | $name = $this->input->get('branch'); 249 | if(empty($name)) throw new WireException("No branch selected"); 250 | $branch = $this->getBranch($name); 251 | if(!count($branch)) throw new WireException("Unknown branch"); 252 | $this->setBranch($name); 253 | $result = version_compare($this->config->version, $branch['version']); 254 | 255 | if($result < 0) { 256 | $msg = "The available version ($branch[version]) is newer than the one you currently have ({$this->config->version})."; 257 | 258 | } else if($result > 0) { 259 | $msg = "The available version ($branch[version]) is older than the one you currently have ({$this->config->version})."; 260 | 261 | } else { 262 | $msg = "The available version is the same as the one you currently have."; 263 | } 264 | 265 | $out = "
$msg
"; 266 | /** @var InputfieldButton $btn */ 267 | $btn = $this->modules->get('InputfieldButton'); 268 | $btn->href = "./download"; 269 | $btn->value = $this->_('Download Now'); 270 | $btn->icon = 'cloud-download'; 271 | $out .= $btn->render(); 272 | 273 | $btn = $this->modules->get('InputfieldButton'); 274 | $btn->href = "./"; 275 | $btn->value = $this->_('Abort'); 276 | $btn->icon = 'times-circle'; 277 | $btn->addClass('ui-priority-secondary'); 278 | $out .= $btn->render(); 279 | 280 | $out .= "
After clicking the download button, be patient, as this may take a minute.
"; 281 | 282 | return $out; 283 | } 284 | 285 | /** 286 | * Download the selected branch ZIP file 287 | * 288 | */ 289 | public function executeDownload() { 290 | 291 | $branch = $this->getBranch(); 292 | if(empty($branch)) throw new WireException("No branch selected"); 293 | 294 | wireMkdir($this->tempPath); 295 | $http = new WireHttp(); 296 | $zipfile = $http->download($branch['zipURL'], $this->tempPath . "$branch[name].zip"); 297 | 298 | if(!$zipfile || !file_exists($zipfile) || !filesize($zipfile)) { 299 | throw new WireException("Unable to download $branch[zipURL]"); 300 | } 301 | 302 | $this->message("Downloaded version $branch[version] (" . number_format(filesize($zipfile)) . " bytes)"); 303 | $this->session->redirect('./database'); 304 | } 305 | 306 | /** 307 | * Export database or instruct them to do it 308 | * 309 | */ 310 | public function executeDatabase() { 311 | 312 | $this->session->remove('ProcessWireUpgrade_database'); 313 | $branch = $this->getBranch(); 314 | $canBackup = class_exists('WireDatabaseBackup') || class_exists('\\ProcessWire\\WireDatabaseBackup'); 315 | 316 | if($this->input->get('backup') && $canBackup) { 317 | $options = array( 318 | 'filename' => $this->config->dbName . "-" . $this->config->version . "-" . date('Y-m-d_H-i-s') . ".sql", 319 | 'description' => "Automatic backup made by ProcessWireUpgrade before upgrading from {$this->config->version} to $branch[version]." 320 | ); 321 | $backups = $this->database->backups(); 322 | $file = $backups->backup($options); 323 | $errors = $backups->errors(); 324 | if(count($errors)) foreach($errors as $error) $this->error($error); 325 | if($file) { 326 | clearstatcache(); 327 | $bytes = filesize($file); 328 | $file = str_replace($this->config->paths->root, '/', $file); 329 | $this->message( 330 | "Backup saved to: $file ($bytes bytes) - " . 331 | "Please note this location should you later need to restore it."); 332 | $this->session->set('ProcessWireUpgrade_database', $file); 333 | } else { 334 | $this->error("Database backup failed"); 335 | } 336 | $this->session->redirect('./prepare'); 337 | } 338 | 339 | $out = "
"; 340 | 341 | if($canBackup) { 342 | 343 | $out .= "
Your version of ProcessWire supports automatic database backups.*
"; 344 | /** @var InputfieldButton $btn */ 345 | $btn = $this->modules->get('InputfieldButton'); 346 | $btn->href = "./database?backup=1"; 347 | $btn->value = $this->_('Backup Database Now'); 348 | $btn->icon = 'database'; 349 | $out .= $btn->render(); 350 | 351 | $btn = $this->modules->get('InputfieldButton'); 352 | $btn->href = "./prepare"; 353 | $btn->icon = 'angle-right'; 354 | $btn->value = $this->_('Skip Database Backup'); 355 | $btn->addClass('ui-priority-secondary'); 356 | $out .= $btn->render(); 357 | 358 | $out .= "
*For additional safety, we recommend creating an independent database backup, for instance with PhpMyAdmin
"; 359 | 360 | } else { 361 | 362 | $out .= 363 | "
Your current version of ProcessWire does not support automatic database backups. " . 364 | "We recommend making a backup of database {$this->config->dbName} " . 365 | "using a tool like PhpMyAdmin. Click the button below once you have saved a backup.
"; 366 | 367 | /** @var InputfieldButton $btn */ 368 | $btn = $this->modules->get('InputfieldButton'); 369 | $btn->href = "./prepare"; 370 | $btn->icon = 'angle-right'; 371 | $btn->value = $this->_('Confirm'); 372 | $out .= $btn->render(); 373 | } 374 | 375 | return $out; 376 | } 377 | 378 | /** 379 | * Unzip files and prepare them for installation 380 | * 381 | */ 382 | public function executePrepare() { 383 | 384 | $error = ''; 385 | $branch = $this->getBranch(); 386 | if(empty($branch)) throw new WireException("No branch selected"); 387 | $zipfile = $this->tempPath . "$branch[name].zip"; // site/assets/cache/ProcessWireUpgrade/branch-dev.zip 388 | 389 | if(!file_exists($zipfile)) throw new WireException("Unable to locate ZIP: $zipfile"); 390 | $files = wireUnzipFile($zipfile, $this->tempPath); 391 | if(!count($files)) $error = "No files were found in $zipfile"; 392 | 393 | $oldVersion = $this->config->version; 394 | $newVersion = $branch['version']; 395 | 396 | $rootPath = dirname(rtrim($this->wirePath, '/')) . '/'; 397 | $rootTempPath = $this->tempPath; // site/assets/cache/ProcessWireUpgrade/ 398 | $wireTempPath = $this->tempPath . 'wire/'; // site/assets/cache/ProcessWireUpgrade/wire/ 399 | $wireNewName = "wire-$newVersion"; // wire-2.5.0 400 | 401 | //$indexFile = $rootPath . "index.php"; // /path/to/root/index.php 402 | //$htaccessFile = $rootPath . ".htaccess"; // /path/to/root/.htaccess 403 | $indexNewName = "index-$newVersion.php"; // index-2.5.0.php 404 | $htaccessNewName = "htaccess-$newVersion.txt"; // htaccess-2.5.0.txt 405 | 406 | $indexVersion = array_search(md5(file_get_contents($rootPath . 'index.php')), $this->indexHashes); 407 | $htaccessVersion = array_search(md5(file_get_contents($rootPath . '.htaccess')), $this->htaccessHashes); 408 | 409 | $rootWritable = is_writable($rootPath) && is_writable($rootPath . "wire/"); 410 | 411 | // determine where we will be moving upgrade files to 412 | if($rootWritable) { 413 | // if root path is writable, we can place new dirs/files in the same 414 | // location as what they are replacing, i.e. /wire/ and /wire-2.5.0/ 415 | $wireNewPath = $rootPath . $wireNewName . "/"; 416 | $htaccessNewFile = $rootPath . $htaccessNewName; 417 | $indexNewFile = $rootPath . $indexNewName; 418 | 419 | } else { 420 | // if root is not writable, we will place dirs/files in /site/assets/cache/core-upgrade/ instead. 421 | $cacheUpgradePath = $this->cachePath; 422 | $cacheUpgradeURL = str_replace($this->config->paths->root, '/', $cacheUpgradePath); 423 | $this->error( 424 | "Your file system is not writable, so we are installing files to $cacheUpgradeURL instead. " . 425 | "You will have to copy them manually to your web root." 426 | ); 427 | wireMkdir($cacheUpgradePath); 428 | $wireNewPath = $cacheUpgradePath . 'wire/'; 429 | $htaccessNewFile = $cacheUpgradePath . 'htaccess.txt'; 430 | $indexNewFile = $cacheUpgradePath . 'index.php'; 431 | } 432 | 433 | if(!$error && !is_dir($wireTempPath)) { 434 | // adjust paths according to where they were unzipped, as needed 435 | // need to drill down a level from extracted archive 436 | // i.e. files[0] may be a dir like /ProcessWire-dev/ 437 | $rootTempPath = $this->tempPath . trim($files[0], '/') . '/'; 438 | $wireTempPath = $rootTempPath . "wire/"; 439 | } 440 | 441 | if(!$error && !is_dir($wireTempPath)) $error = "Unable to find /wire/ directory in archive"; 442 | 443 | if(!$error) { 444 | $this->renameNow($wireTempPath, $wireNewPath); // /temp/path/wire/ => /wire-2.5.0/ 445 | $this->renameNow($rootTempPath . "index.php", $indexNewFile); // /temp/path/index.php => /index-2.5.0.php 446 | $this->renameNow($rootTempPath . "htaccess.txt", $htaccessNewFile); // /temp/path/htaccess.txt => /htaccess-2.5.0.txt 447 | // remove /temp/path/ as no longer needed since we've taken everything we need out of it above 448 | wireRmdir($this->tempPath, true); 449 | } 450 | 451 | if($error) throw new WireException($error); 452 | 453 | $out = 454 | "
" . 455 | "
We have prepared copies of upgrade files for installation. At this point, " . 456 | "you may install them yourself by replacing the existing /wire/ directory, and " . 457 | "optionally index.php and .htaccess files with the new versions indicated. "; 458 | 459 | if($rootWritable) $out .= "Or, since your file system is writable, we can install them for you."; 460 | $out .= "
"; 461 | 462 | if($indexVersion) { 463 | $out .= 464 | "
Your index.php file is confirmed identical to the one included " . 465 | "with ProcessWire version $indexVersion so it should be safe to replace " . 466 | "without further analysis.
"; 467 | } else { 468 | $out .= 469 | "
We have detected that your index.php file may contain site-specific " . 470 | "customizations. Please double check before replacing it.
"; 471 | } 472 | 473 | if($htaccessVersion) { 474 | $out .= 475 | "
Your .htaccess file is confirmed identical to the one included " . 476 | "with ProcessWire version $htaccessVersion so it should be safe to replace " . 477 | "without further analysis.
"; 478 | } else { 479 | $out .= 480 | "
We have detected that your .htaccess file may contain site-specific " . 481 | "customizations. Please double check before replacing it.
"; 482 | } 483 | 484 | if($rootWritable) { 485 | 486 | $indexChecked = $indexVersion ? "checked" : ""; 487 | $htaccessChecked = $htaccessVersion ? "checked" : ""; 488 | 489 | $out .= 490 | "
" . 491 | "Check the boxes below for what you'd like us to install. "; 492 | 493 | if(empty($htaccessChecked)) $out .= 494 | "Since the .htaccess file may have site-specific customizations, you may " . 495 | "consider handling that one manually, unless you know it hasn't been modified."; 496 | 497 | $out .= "
" . 498 | "
"; 515 | 516 | } else { 517 | // root not writable 518 | 519 | $backupInfo = array( 520 | "wire" => ".wire-$oldVersion", 521 | "index.php" => ".index-$oldVersion.php *", 522 | ".htaccess" => ".htaccess-$oldVersion *", 523 | ); 524 | $renameInfo = array( 525 | "" . rtrim($wireNewPath, '/') . "" => "/wire", 526 | "$indexNewFile" => "index.php *", 527 | "$htaccessNewFile" => ".htaccess *", 528 | ); 529 | 530 | $out .= 531 | "
Your file system is not writable so we can't automatically " . 532 | "install the upgrade files for you. However, the files are ready for you to move to " . 533 | "their destinations.
"; 534 | 535 | $out .= 536 | "
" . 537 | "
While optional, we strongly recommend making backups of " . 538 | "everything replaced so that you can always revert back to it if needed. We recommend " . 539 | "doing this by performing the following file rename operations:
" . 547 | "
" . 548 | "
Now you can migrate the new files, renaming them from their " . 549 | "temporary location to their destination.
" . 557 | "
Once you've completed the above steps, your upgrade " . 558 | "will be complete.
" . $this->completionNotes(); 559 | } 560 | 561 | $out .= 562 | "
*In many cases, it is not necessary to upgrade the index.php " . 563 | "and htaccess files since they don't always change between versions.
"; 564 | 565 | return $out; 566 | } 567 | 568 | /** 569 | * Install prepared files 570 | * 571 | */ 572 | public function executeInstall() { 573 | 574 | if(!$this->input->post('submit_install')) throw new WireException('No form received'); 575 | $branch = $this->getBranch(); 576 | if(empty($branch)) throw new WireException("No branch selected"); 577 | $oldVersion = $this->config->version; 578 | $newVersion = $branch['version']; 579 | $rootPath = dirname(rtrim($this->wirePath, '/')) . '/'; 580 | $renames = array(); 581 | 582 | if($this->input->post('wire')) { 583 | $renames["wire"] = ".wire-$oldVersion"; 584 | $renames["wire-$newVersion"] = "wire"; 585 | } 586 | if($this->input->post('index')) { 587 | $renames["index.php"] = ".index-$oldVersion.php"; 588 | $renames["index-$newVersion.php"] = "index.php"; 589 | } 590 | if($this->input->post('htaccess')) { 591 | $renames[".htaccess"] = ".htaccess-$oldVersion"; 592 | $renames["htaccess-$newVersion.txt"] = ".htaccess"; 593 | } 594 | 595 | foreach($renames as $old => $new) { 596 | $this->renameLater($rootPath . $old, $rootPath . $new); 597 | } 598 | 599 | $out = "
"; 600 | $out .= "
Now double check that everything works before you leave this page.
"; 601 | $out .= $this->completionNotes(); 602 | 603 | return $out; 604 | } 605 | 606 | /** 607 | * Completion notes 608 | * 609 | */ 610 | protected function completionNotes() { 611 | 612 | $frontURL = $this->config->urls->root; 613 | $adminURL = $this->config->urls->admin; 614 | $branch = $this->getBranch(); 615 | $newVersion = $branch['version']; 616 | $oldVersion = $this->config->version; 617 | $dbFile = $this->session->get('ProcessWireUpgrade_database'); 618 | $dbNote = ''; 619 | if($dbFile) $dbNote = "Your database was backed up to this file: $dbFile"; 620 | 621 | $out = "
Test out both the front-end and 622 | admin of your site in full 623 | to make sure everything works.
624 | 625 |
The installed files carry the permissions defined in your /site/config.php file 626 | which are {$this->config->chmodDir} for directories and {$this->config->chmodFile} 627 | for files. Double check that these are safe with your web host, especially in a 628 | shared hosting environment.
629 | "; 630 | 631 | if($this->config->debug) { 632 | $out .= "
For production sites, remember to turn off debug mode once your 633 | upgrade is complete.
"; 634 | } 635 | 636 | $out .= " 637 |
If your upgrade did not work…
638 | 639 |
If you encounter an error message, hit reload in your browser 2-3 times, as it may take a couple of 640 | requests for ProcessWire to apply any necessary database schema changes. Should you determine that 641 | the upgrade failed for some reason and you want to revert back to the previous version, here are the 642 | steps to undo what was just applied.
643 | 644 |
First you would remove the installed updates by renaming or deleting them:
645 | 646 |
651 | 652 |
Next you would restore the backed up versions:
653 | 654 |
660 | "; 661 | 662 | 663 | return $out; 664 | } 665 | 666 | /** 667 | * Schedule a rename operation, which will occur at __destruct 668 | * 669 | * @param string $oldPath 670 | * @param string $newPath 671 | * 672 | */ 673 | protected function renameLater($oldPath, $newPath) { 674 | $this->renames[$oldPath] = $newPath; 675 | $old = basename(rtrim($oldPath, '/')); 676 | $new = basename(rtrim($newPath, '/')); 677 | $this->message("Rename $old => $new"); 678 | } 679 | 680 | /** 681 | * Perform a rename now 682 | * 683 | * @param string $old 684 | * @param string $new 685 | * @return bool 686 | * @throws WireException 687 | * 688 | */ 689 | protected function renameNow($old, $new) { 690 | 691 | $result = true; 692 | 693 | // for labels 694 | $_old = str_replace($this->config->paths->root, '/', $old); 695 | $_new = str_replace($this->config->paths->root, '/', $new); 696 | 697 | if(!file_exists($old)) { 698 | $this->error("$_old does not exist"); 699 | return $result; 700 | } 701 | 702 | if(file_exists($new)) { 703 | $this->message("$_new already exists (we left it untouched)"); 704 | return $result; 705 | } 706 | 707 | $result = rename($old, $new); 708 | 709 | if($result) { 710 | $this->message("Renamed $_old => $_new"); 711 | } else { 712 | $this->error("Unable to rename $_old => $_new"); 713 | if(basename(rtrim($new, '/')) == 'wire') { 714 | throw new WireException("Upgrade aborted. Unable to rename $_old => $_new"); 715 | } 716 | } 717 | 718 | return $result; 719 | } 720 | 721 | /** 722 | * Process rename operations 723 | * 724 | */ 725 | public function __destruct() { 726 | if(!count($this->renames)) return; 727 | //$rootPath = dirname(rtrim($this->wirePath, '/')) . '/'; 728 | foreach($this->renames as $oldPath => $newPath) { 729 | if(file_exists($newPath)) { 730 | $n = 0; 731 | do { $newPath2 = $newPath . "-" . (++$n); } while(file_exists($newPath2)); 732 | if(rename($newPath, $newPath2)) $this->message("Renamed $newPath => $newPath2"); 733 | } 734 | $old = basename(rtrim($oldPath, '/')); 735 | $new = basename(rtrim($newPath, '/')); 736 | if(rename($oldPath, $newPath)) { 737 | $this->message("Renamed $old => $new"); 738 | } else { 739 | $this->error("Unable to rename $old => $new"); 740 | } 741 | } 742 | $this->renames = array(); 743 | } 744 | 745 | /** 746 | * Install 747 | * 748 | */ 749 | public function ___install() { 750 | 751 | // create the page our module will be assigned to 752 | $page = new Page(); 753 | $page->template = 'admin'; 754 | $page->name = self::pageName; 755 | 756 | // installs to the admin "Setup" menu ... change as you see fit 757 | $page->parent = $this->pages->get($this->config->adminRootPageID)->child('name=setup'); 758 | $page->process = $this; 759 | 760 | // we will make the page title the same as our module title 761 | // but you can make it whatever you want 762 | $info = self::getModuleInfo(); 763 | $page->title = $info['title']; 764 | 765 | // save the page 766 | $page->save(); 767 | 768 | // tell the user we created this page 769 | $this->message("Created Page: {$page->path}"); 770 | } 771 | 772 | /** 773 | * Uninstall 774 | * 775 | */ 776 | public function ___uninstall() { 777 | 778 | // find the page we installed, locating it by the process field (which has the module ID) 779 | // it would probably be sufficient just to locate by name, but this is just to be extra sure. 780 | $moduleID = $this->modules->getModuleID($this); 781 | $page = $this->pages->get("template=admin, process=$moduleID, name=" . self::pageName . "|core-upgrade"); 782 | 783 | if($page->id) { 784 | // if we found the page, let the user know and delete it 785 | $this->message("Deleting Page: {$page->path}"); 786 | $page->delete(); 787 | } 788 | 789 | wireRmdir($this->tempPath, true); 790 | } 791 | 792 | } 793 | 794 | -------------------------------------------------------------------------------- /modules/ProcessWireUpgrade/ProcessWireUpgradeCheck.config.php: -------------------------------------------------------------------------------- 1 | add(array( 6 | array( 7 | 'name' => 'useLoginHook', 8 | 'type' => 'radios', 9 | 'label' => $this->_('Check for upgrades on superuser login?'), 10 | 'description' => $this->_('If "No" is selected, then upgrades will only be checked manually when you click to Setup > Upgrades.'), 11 | 'notes' => $this->_('Automatic upgrade check requires ProcessWire 2.5.20 or newer.'), 12 | 'options' => array( 13 | 1 => $this->_('Yes'), 14 | 0 => $this->_('No') 15 | ), 16 | 'optionColumns' => 1, 17 | 'value' => 0 18 | ) 19 | )); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /modules/ProcessWireUpgrade/ProcessWireUpgradeCheck.module: -------------------------------------------------------------------------------- 1 | 'Upgrades Checker', 28 | 'summary' => 'Automatically checks for core and installed module upgrades at routine intervals.', 29 | 'version' => 7, 30 | 'autoload' => "template=admin", 31 | 'singular' => true, 32 | 'author' => 'Ryan Cramer', 33 | 'icon' => 'coffee' 34 | ); 35 | } 36 | 37 | const tagsURL = 'https://api.github.com/repos/processwire/processwire/tags'; 38 | const branchesURL = 'https://api.github.com/repos/processwire/processwire/branches'; 39 | const versionURL = 'https://raw.githubusercontent.com/processwire/processwire/{branch}/wire/core/ProcessWire.php'; 40 | const zipURL = 'https://github.com/processwire/processwire/archive/{branch}.zip'; 41 | const timeout = 4.5; 42 | 43 | /** 44 | * Repository information indexed by branch 45 | * 46 | * @var array 47 | * 48 | */ 49 | protected $repos = array(); 50 | 51 | /** 52 | * Construct 53 | * 54 | */ 55 | public function __construct() { 56 | $this->set('useLoginHook', 1); 57 | $this->repos = array( 58 | 'main' => array( 59 | 'branchesURL' => self::branchesURL, 60 | 'zipURL' => self::zipURL, 61 | 'versionURL' => self::versionURL, 62 | 'tagsURL' => self::tagsURL, 63 | ), 64 | 'legacy' => array( 65 | 'branchesURL' => $this->legacyURL(self::branchesURL), 66 | 'zipURL' => $this->legacyURL(self::zipURL), 67 | 'versionURL' => $this->legacyURL(self::versionURL), 68 | 'tagsURL' => $this->legacyURL(self::tagsURL), 69 | ), 70 | 'prev' => array( 71 | 'branchesURL' => 'https://api.github.com/repos/ryancramerdesign/ProcessWire/branches', 72 | 'zipURL' => 'https://github.com/ryancramerdesign/ProcessWire/archive/{branch}.zip', 73 | 'versionURL' => 'https://raw.githubusercontent.com/ryancramerdesign/ProcessWire/{branch}/wire/core/ProcessWire.php', 74 | 'tagsURL' => self::tagsURL, 75 | ), 76 | ); 77 | } 78 | 79 | /** 80 | * Initialize and perform access checks 81 | * 82 | */ 83 | public function init() { 84 | if($this->useLoginHook) { 85 | $this->session->addHookAfter('login', $this, 'loginHook'); 86 | } 87 | } 88 | 89 | /** 90 | * Check for upgrades at login (superuser only) 91 | * 92 | * @param null|HookEvent $e 93 | * 94 | */ 95 | public function loginHook($e = null) { 96 | 97 | if($e) {} // ignored HookEvent 98 | if(!$this->user->isSuperuser()) return; // only show messages to superuser 99 | 100 | $moduleVersions = array(); 101 | $cache = $this->cache; 102 | $cacheName = $this->className() . "_loginHook"; 103 | $cacheData = $cache ? $cache->get($cacheName) : null; 104 | if(!empty($cacheData) && is_string($cacheData)) $cache = json_decode($cacheData, true); 105 | 106 | $branches = empty($cacheData) ? $this->getCoreBranches(false) : $cacheData['branches']; 107 | if(empty($branches)) return; 108 | $master = $branches['master']; 109 | $branch = null; 110 | $new = version_compare($master['version'], $this->config->version); 111 | 112 | if($new > 0) { 113 | // master is newer than current 114 | $branch = $master; 115 | } else if($new < 0 && isset($branches['dev'])) { 116 | // we will assume dev branch 117 | $dev = $branches['dev']; 118 | $new = version_compare($dev['version'], $this->config->version); 119 | if($new > 0) $branch = $dev; 120 | } 121 | 122 | if($branch) { 123 | $versionStr = "$branch[name] $branch[version]"; 124 | $msg = $this->_('A ProcessWire core upgrade is available') . " ($versionStr)"; 125 | $this->message($msg); 126 | } else { 127 | $this->message($this->_('Your ProcessWire core is up-to-date')); 128 | } 129 | 130 | if($this->config->moduleServiceKey) { 131 | $n = 0; 132 | if(empty($cacheData) || empty($cacheData['moduleVersions'])) { 133 | $moduleVersions = $this->getModuleVersions(true); 134 | } else { 135 | $moduleVersions = $cacheData['moduleVersions']; 136 | } 137 | foreach($moduleVersions as $name => $info) { 138 | $msg = sprintf($this->_('An upgrade for %s is available'), $name) . " ($info[remote])"; 139 | $this->message($msg); 140 | $n++; 141 | } 142 | if(!$n) $this->message($this->_('Your modules are up-to-date')); 143 | } 144 | 145 | if($cache) { 146 | $cacheData = array( 147 | 'branches' => $branches, 148 | 'moduleVersions' => $moduleVersions 149 | ); 150 | $cache->save($cacheName, $cacheData, 43200); // 43200=12hr 151 | } 152 | } 153 | 154 | /** 155 | * Get versions of core or modules 156 | * 157 | * @return array of array( 158 | * 'ModuleName' => array( 159 | * 'title' => 'Module Title', 160 | * 'local' => '1.2.3', // current installed version 161 | * 'remote' => '1.2.4', // directory version available, or boolean false if not found in directory 162 | * 'new' => true|false, // true if newer version available, false if not 163 | * 'requiresVersions' => array('ModuleName' => array('>', '1.2.3')), // module requirements (for modules only) 164 | * 'branch' => 'master', // branch name (for core only) 165 | * ) 166 | * ) 167 | * 168 | */ 169 | public function getVersions() { 170 | $versions = array(); 171 | $branches = $this->getCoreBranches(false);//, true); // true is TMP 172 | foreach($branches as $branchName => $branch) { 173 | $name = "ProcessWire $branchName"; 174 | $new = version_compare($branch['version'], $this->config->version); 175 | $versions[$name] = array( 176 | 'title' => "ProcessWire Core ($branch[title])", 177 | 'local' => $this->config->version, 178 | 'remote' => $branch['version'], 179 | 'new' => $new, 180 | 'branch' => $branch['name'], 181 | ); 182 | } 183 | 184 | if($this->config->moduleServiceKey) { 185 | foreach($this->getModuleVersions(false) as $name => $info) { 186 | $versions[$name] = $info; 187 | } 188 | } 189 | 190 | return $versions; 191 | } 192 | 193 | /** 194 | * Cached module versions data 195 | * 196 | * @param array 197 | * 198 | */ 199 | protected $getModuleVersionsData = array(); 200 | 201 | /** 202 | * Check all site modules for newer versions from the directory 203 | * 204 | * @param bool $onlyNew Only return array of modules with new versions available 205 | * @return array of array( 206 | * 'ModuleName' => array( 207 | * 'title' => 'Module Title', 208 | * 'local' => '1.2.3', // current installed version 209 | * 'remote' => '1.2.4', // directory version available, or boolean false if not found in directory 210 | * 'new' => true|false, // true if newer version available, false if not 211 | * 'requiresVersions' => array('ModuleName' => array('>', '1.2.3')), // module requirements 212 | * ) 213 | * ) 214 | * @throws WireException 215 | * 216 | */ 217 | public function getModuleVersions($onlyNew = false) { 218 | 219 | if(!$this->config->moduleServiceKey) throw new WireException("This feature requires ProcessWire 2.4.19+"); 220 | 221 | $url = $this->config->moduleServiceURL . 222 | "?apikey=" . $this->config->moduleServiceKey . 223 | "&limit=100" . 224 | "&field=module_version,version,requires_versions" . 225 | "&class_name="; 226 | 227 | $names = array(); 228 | $versions = array(); 229 | 230 | foreach($this->modules as $module) { 231 | $name = $module->className(); 232 | $info = $this->modules->getModuleInfoVerbose($name); 233 | if($info['core']) continue; 234 | $names[] = $name; 235 | $versions[$name] = array( 236 | 'title' => $info['title'], 237 | 'local' => $this->modules->formatVersion($info['version']), 238 | 'remote' => false, 239 | 'new' => 0, 240 | 'requiresVersions' => $info['requiresVersions'] 241 | ); 242 | } 243 | 244 | if(!count($names)) return array(); 245 | $url .= implode(',', $names); 246 | 247 | $data = $this->getModuleVersionsData; 248 | if(empty($data)) { 249 | // if not cached 250 | $http = new WireHttp(); 251 | $http->setTimeout(self::timeout); 252 | $data = $http->getJSON($url); 253 | $this->getModuleVersionsData = $data; 254 | 255 | if(!is_array($data)) { 256 | $error = $http->getError(); 257 | if(!$error) $error = $this->_('Error retrieving modules directory data'); 258 | $this->error($error . " (" . $this->className() . ")"); 259 | return array(); 260 | } 261 | } 262 | 263 | foreach($data['items'] as $item) { 264 | $name = $item['class_name']; 265 | $versions[$name]['remote'] = $item['module_version']; 266 | $new = version_compare($versions[$name]['remote'], $versions[$name]['local']); 267 | $versions[$name]['new'] = $new; 268 | if($new <= 0) { 269 | // local is up-to-date or newer than remote 270 | if($onlyNew) unset($versions[$name]); 271 | } else { 272 | // remote is newer than local 273 | $versions[$name]['requiresVersions'] = $item['requires_versions']; 274 | } 275 | } 276 | 277 | if($onlyNew) foreach($versions as $name => $data) { 278 | if($data['remote'] === false) unset($versions[$name]); 279 | } 280 | 281 | return $versions; 282 | } 283 | 284 | /** 285 | * Get all available branches with info for each 286 | * 287 | * @param bool $throw Whether or not to throw exceptions on error (default=true) 288 | * @param bool $refresh Specify true to refresh data from web service 289 | * @return array of branches each with: 290 | * - name (string) i.e. dev 291 | * - title (string) i.e. Development 292 | * - zipURL (string) URL to zip download file 293 | * - version (string) i.e. 2.5.0 294 | * - versionURL (string) URL to we pull version from 295 | * @throws WireException 296 | * 297 | */ 298 | public function getCoreBranches($throw = true, $refresh = false) { 299 | 300 | if(!$refresh) { 301 | $branches = $this->session->get('ProcessWireUpgrade_branches'); 302 | if($branches && count($branches)) return $branches; 303 | } 304 | 305 | $branches = array(); 306 | 307 | foreach($this->repos as $repoName => $repo) { 308 | 309 | $http = new WireHttp(); 310 | $http->setTimeout(self::timeout); 311 | $http->setHeader('User-Agent', 'ProcessWireUpgrade'); 312 | $json = $http->get($repo['branchesURL']); 313 | 314 | $loadError = $this->_('Error loading GitHub branches') . ' - ' . $repo['branchesURL']; 315 | 316 | if(!$json) { 317 | $error = $loadError; 318 | $error .= ' - ' . $this->_('HTTP error(s):') . ' ' . $http->getError(); 319 | $error .= ' - ' . $this->_('Check that HTTP requests are not blocked by your server.'); 320 | if($throw) throw new WireException($error); 321 | $this->error($error); 322 | return array(); 323 | } 324 | 325 | $data = json_decode($json, true); 326 | 327 | if(!$data) { 328 | $error = $loadError; 329 | if($throw) throw new WireException($error); 330 | $this->error($error); 331 | return array(); 332 | } 333 | 334 | foreach($data as $key => $info) { 335 | 336 | $name = $info['name']; 337 | 338 | $branch = array( 339 | 'name' => $name, 340 | 'title' => ucfirst($name), 341 | 'zipURL' => str_replace('{branch}', $name, $repo['zipURL']), 342 | 'version' => '', 343 | 'versionURL' => str_replace('{branch}', $name, $repo['versionURL']), 344 | ); 345 | 346 | $content = $http->get($branch['versionURL']); 347 | if(!preg_match_all('/const\s+version(Major|Minor|Revision)\s*=\s*(\d+)/', $content, $matches)) { 348 | $branch['version'] = '?'; 349 | continue; 350 | } 351 | 352 | $version = array(); 353 | foreach($matches[1] as $k => $var) { 354 | $version[$var] = (int) $matches[2][$k]; 355 | } 356 | 357 | $branch['version'] = "$version[Major].$version[Minor].$version[Revision]"; 358 | 359 | if($repoName != 'main') { 360 | $name = "$repoName-$name"; 361 | $branch['name'] = $name; 362 | $branch['title'] = ucfirst($repoName) . "/$branch[title]"; 363 | } 364 | 365 | $branches[$name] = $branch; 366 | } 367 | } 368 | 369 | $this->session->set('ProcessWireUpgrade_branches', $branches); 370 | 371 | return $branches; 372 | } 373 | 374 | /** 375 | * Convert given URL from master to legacy repo 376 | * 377 | * @param string $url 378 | * @return string 379 | * 380 | */ 381 | protected function legacyURL($url) { 382 | return str_replace('processwire/processwire/', 'processwire/processwire-legacy/', $url); 383 | } 384 | 385 | } 386 | 387 | -------------------------------------------------------------------------------- /modules/ProcessWireUpgrade/README.md: -------------------------------------------------------------------------------- 1 | # ProcessWire Upgrade 2 | 3 | Provides core and module upgrade notifications and optionally 4 | installation from the admin. 5 | 6 | Can be used to upgrade your ProcessWire core or any module that 7 | is available from http://modules.processwire.com. 8 | 9 | ## Please note before using this tool 10 | 11 | Files installed by this tool are readable and writable by Apache, 12 | which may be a security problem in some hosting environments. 13 | Especially shared hosting environments where Apache runs as the 14 | same user across all hosting accounts. If in doubt, you should 15 | instead install core upgrades and/or modules and module upgrades 16 | manually through your hosting account (FTP, SSH, etc.), which 17 | is already very simple to do. This ensures that any installed files 18 | are owned and writable by your user account rather than Apache. 19 | 20 | ## Core Upgrades 21 | 22 | This tool checks if upgrades are available for your ProcessWire installation. 23 | If available, it will download the update. If your file system is 24 | writable, it will install the update for you. If your file system is 25 | not writable, then it will install upgrade files in a writable 26 | location (under /site/assets/cache/) and give you instructions on 27 | what files to move. 28 | 29 | Options to upgrade from the master or dev branch are available. 30 | 31 | This utility can be also used to upgrade any PW 2.5.20+ or newer 32 | site to the latest version. 33 | 34 | This utility makes versioned backup copies of any files it 35 | overwrites during the upgrade. Should an upgrade fail for some 36 | reason, you can manually restore from the backups should you 37 | need to. 38 | 39 | If your ProcessWire version is new enough to have the 40 | WireDatabaseBackup class (PW 2.5.14+) then this module will 41 | also give you the option of backing up your database. 42 | 43 | After installing a core upgrade, you may want to manually update 44 | the permissions of installed files to be non-writable to Apache, 45 | depending on your environment. 46 | 47 | 48 | ## Module Upgrades 49 | 50 | Uses web services from modules.processwire.com to compare your 51 | current installed versions of modules to the latest remote 52 | versions available. Provides upgrade links when it finds newer 53 | versions of modules you have installed. 54 | 55 | After installing module upgrades, you may want to manually update 56 | the permissions of installed files to be non-writable to Apache, 57 | depending on your environment. 58 | 59 | 60 | ## Requirements 61 | 62 | - ProcessWire 2.5.20 or newer 63 | 64 | 65 | -------------------------------------------------------------------------------- /modules/README.txt: -------------------------------------------------------------------------------- 1 | ABOUT /SITE/MODULES/ 2 | ==================== 3 | This directory /site/modules/ is where you may install additional plugin modules. 4 | These modules are specific to your site only. There is also a corresponding 5 | /wire/modules/ directory, which contains ProcessWire's core modules (and best to 6 | leave those alone). 7 | 8 | If safe for your hosting environment, you may wish to make this directory 9 | writable to PHP so that the installation of your modules can be managed from 10 | ProcessWire's admin. However, this is not necessarily safe in all shared hosting 11 | environments and is completely optional. 12 | 13 | 14 | Where to get modules? 15 | --------------------- 16 | Visit the modules directory at: http://modules.processwire.com 17 | 18 | 19 | Installing modules from the ProcessWire admin 20 | --------------------------------------------- 21 | If your /site/modules/ directory is writable, you can install modules from 22 | ProcessWire's admin directly from the Modules Directory, from a ZIP file or from 23 | a URL to a ZIP file. In your ProcessWire admin, see Modules > New for 24 | installation options. 25 | 26 | 27 | Installing modules from the file system 28 | --------------------------------------- 29 | Each module (and any related files) should live in a directory of its own. The 30 | directory should generally carry the same name as the module. For instance, if 31 | you are installing a module named ProcessDatabaseBackups.module, then it should 32 | live in the directory /site/modules/ProcessDatabaseBackups/. 33 | 34 | Once you have placed a new module in this directory, you need to let ProcessWire 35 | know about it. Login to the admin and click "Modules". Then click the "Check for 36 | new modules" button. It will find your new module(s). Click the "Install" button 37 | next to any new modules that you want to install. 38 | 39 | 40 | Removing modules 41 | ---------------- 42 | The first step in removing a module is to uninstall it from ProcessWire (if it 43 | isn't already). You do this by going to the "Modules" page, and "Site" tab in 44 | your ProcessWire admin. Click the "Uninstall" button next to the module you 45 | want to remove. 46 | 47 | After the module is uninstalled, you may remove the module files. If your 48 | modules file system is writable to ProcessWire, it will give you a "Delete" 49 | button next to the module in your "Modules" admin page. You may click that to 50 | remove the module files. 51 | 52 | If your file system is not writable, you may remove the module files manually 53 | from the file system (via SFTP or whatever tool you are using to manage your 54 | files on the server). 55 | 56 | 57 | Interested in learning how to make your own modules? 58 | ---------------------------------------------------- 59 | We've created two "Hello World" modules as examples for those interested in 60 | learning module development: 61 | 62 | - Helloworld.module demonstrates the basics of modules and hooks. 63 | http://modules.processwire.com/modules/helloworld/ 64 | 65 | - ProcessHello.module demonstrates the basics of how to create a Process 66 | module. Process modules are those that create applications in the admin. 67 | http://modules.processwire.com/modules/process-hello/ 68 | 69 | There is a module development forum located at: 70 | https://processwire.com/talk/forum/19-moduleplugin-development/ 71 | 72 | For a tutorial on how to create modules, see: 73 | http://wiki.processwire.com/index.php/Module_Creation 74 | 75 | 76 | Additional resources 77 | -------------------- 78 | 79 | To find and download new modules, see the modules directory at: 80 | http://modules.processwire.com/ 81 | 82 | For more information about modules, see the documentation at: 83 | http://processwire.com/api/modules/ 84 | 85 | For discussion and support of modules, see: 86 | http://processwire.com/talk/forum/4-modulesplugins/ 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /templates/README.txt: -------------------------------------------------------------------------------- 1 | This file is here to ensure Git adds the dir to the repo. You may delete this file. 2 | -------------------------------------------------------------------------------- /templates/admin.php: -------------------------------------------------------------------------------- 1 | paths->adminTemplates . 'controller.php'); 16 | -------------------------------------------------------------------------------- /templates/api.php: -------------------------------------------------------------------------------- 1 | paths->templates}api/Router.php"; 4 | -------------------------------------------------------------------------------- /templates/api/ApiHelper.php: -------------------------------------------------------------------------------- 1 | $name)) throw new \Exception("Required parameter: '$param' missing!", 400); 16 | 17 | $sanitizer = explode('|', $param); 18 | 19 | // Sanitize Data 20 | // If no sanitizer is defined, use the text sanitizer as default 21 | if (!isset($sanitizer[1])) $sanitizer = 'text'; 22 | else $sanitizer = $sanitizer[1]; 23 | 24 | if(!method_exists(wire('sanitizer'), $sanitizer)) throw new \Exception("Sanitizer: '$sanitizer' ist no valid sanitizer", 400); 25 | 26 | $data->$name = wire('sanitizer')->$sanitizer($data->$name); 27 | } 28 | 29 | return $data; 30 | } 31 | 32 | public static function baseUrl() { 33 | // $site->urls->httpRoot 34 | return (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]/"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /templates/api/Auth.php: -------------------------------------------------------------------------------- 1 | isGuest()) 10 | throw new \Exception('user is not logged in', 401); 11 | 12 | if(!isset(wire('config')->jwtSecret)) { 13 | throw new \Exception('incorrect site config', 500); 14 | } 15 | 16 | $issuedAt = time(); 17 | $notBefore = $issuedAt; 18 | $expire = $notBefore + wire('config')->sessionExpireSeconds; 19 | $serverName = wire('config')->httpHost; 20 | 21 | $token = array( 22 | "iss" => $serverName, // issuer 23 | "aud" => $serverName, // audience 24 | "iat" => $issuedAt, // issued at 25 | "nbf" => $notBefore, // valid not before 26 | "exp" => $expire, // token expire time 27 | ); 28 | 29 | $jwt = JWT::encode($token, wire('config')->jwtSecret); 30 | 31 | $response = new \StdClass(); 32 | $response->jwt = $jwt; 33 | return $response; 34 | } 35 | 36 | public static function login($data) { 37 | ApiHelper::checkAndSanitizeRequiredParameters($data, ['username|selectorValue', 'password|string']); 38 | 39 | $user = wire('users')->get($data->username); 40 | 41 | // if(!$user->id) throw new \Exception("User with username: $data->username not found", 404); 42 | // prevent username sniffing by just throwing a general exception: 43 | if(!$user->id) throw new \Exception("Login not successful", 401); 44 | 45 | $loggedIn = wire('session')->login($data->username, $data->password); 46 | 47 | if($loggedIn) return self::auth(); 48 | else throw new \Exception("Login not successful", 401); 49 | } 50 | 51 | public static function logout() { 52 | wire('session')->logout(wire('user')); 53 | return 'user logged out'; 54 | } 55 | } -------------------------------------------------------------------------------- /templates/api/Router.php: -------------------------------------------------------------------------------- 1 | paths->root}/vendor/autoload.php"; 8 | require_once dirname(__FILE__) . "/ApiHelper.php"; 9 | require_once dirname(__FILE__) . "/Auth.php"; 10 | require_once dirname(__FILE__) . "/Test.php"; 11 | 12 | use \Firebase\JWT\JWT; 13 | 14 | $content = Router::go(function(\FastRoute\RouteCollector $r) 15 | { 16 | $r->addRoute('GET', '/', ApiHelper::class . '@noEndpoint'); 17 | $r->addRoute('POST', '/', ApiHelper::class . '@noEndpoint'); 18 | $r->addRoute('PUT', '/', ApiHelper::class . '@noEndpoint'); 19 | $r->addRoute('PATCH', '/', ApiHelper::class . '@noEndpoint'); 20 | $r->addRoute('DELETE', '/', ApiHelper::class . '@noEndpoint'); 21 | 22 | $r->addGroup('/auth', function (\FastRoute\RouteCollector $r) 23 | { 24 | $r->addRoute('GET', '', Auth::class . '@auth'); 25 | $r->addRoute('POST', '', Auth::class . '@login'); 26 | $r->addRoute('DELETE', '', Auth::class . '@logout'); 27 | }); 28 | 29 | $r->addGroup('/test', function (\FastRoute\RouteCollector $r) 30 | { 31 | $r->addRoute('GET', '', Test::class . '@getSomeData'); 32 | $r->addRoute('POST', '', Test::class . '@postWithSomeData'); 33 | }); 34 | }); 35 | 36 | class Router 37 | { 38 | /** 39 | * @param callable $callback Route configurator 40 | * @param string $path Optionally overwrite the default of using the whole urlSegmentStr 41 | * @throws Wire404Exception 42 | */ 43 | public static function go(callable $callback, $path = '') 44 | { 45 | $dispatcher = \FastRoute\simpleDispatcher($callback); 46 | 47 | $routeInfo = $dispatcher->dispatch( 48 | $_SERVER['REQUEST_METHOD'], 49 | strlen($path) 50 | ? '/' . trim($path, '/') 51 | : '/' . wire('input')->urlSegmentStr 52 | ); 53 | 54 | switch ($routeInfo[0]) { 55 | case \FastRoute\Dispatcher::NOT_FOUND: 56 | case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED: 57 | throw new Wire404Exception(); 58 | return; 59 | 60 | case \FastRoute\Dispatcher::FOUND: 61 | $handler = $routeInfo[1]; 62 | $vars = $routeInfo[2]; 63 | list($class, $method) = explode('@', $handler, 2); 64 | return Router::handle($class, $method, $vars); 65 | } 66 | } 67 | 68 | 69 | public static function handle($class, $method, $vars) 70 | { 71 | $authActive = true; 72 | 73 | header("Content-Type: application/json"); 74 | $return = new \StdClass(); 75 | $vars = (object) $vars; 76 | 77 | // if regular and not auth request, check Authorization: 78 | // otherwise go right through regular api handling 79 | if($authActive === true && $class !== Auth::class) 80 | { 81 | // convert all headers to lowercase: 82 | $headers = array(); 83 | foreach(apache_request_headers() as $key => $value) { 84 | $headers[strtolower($key)] = $value; 85 | } 86 | 87 | // check for auth header 88 | if(!array_key_exists('authorization', $headers)) { 89 | http_response_code(400); 90 | $return->error = 'No Authorization Header found'; 91 | echo json_encode($return); 92 | return; 93 | }; 94 | 95 | // Check if jwtSecret is in config 96 | if(!isset(wire('config')->jwtSecret)) { 97 | http_response_code(500); 98 | $return->error = 'incorrect site config'; 99 | echo json_encode($return); 100 | return; 101 | } 102 | 103 | try { 104 | $secret = wire('config')->jwtSecret; 105 | list($jwt) = sscanf($headers['authorization'], 'Bearer %s'); 106 | $decoded = JWT::decode($jwt, $secret, array('HS256')); 107 | } 108 | catch (\Exception $e) 109 | { 110 | http_response_code(401); 111 | return; 112 | } 113 | } 114 | 115 | // If the code runs until here, the request is authenticated 116 | // or the request does not need authentication 117 | // Get Data: 118 | try { 119 | // merge url $vars with params 120 | $vars = (object) array_merge((array) Router::params(), (array) $vars); 121 | $data = $class::$method($vars); 122 | 123 | if(gettype($data) == "string") $return->message = $data; 124 | else $return = $data; 125 | } 126 | catch (\Exception $e) { 127 | $responseCode = 404; 128 | $return->error = $e->getMessage(); 129 | \ProcessWire\wire('log')->error($e->getMessage()); 130 | 131 | if($e->getCode()) $responseCode = $e->getCode(); 132 | http_response_code($responseCode); 133 | } 134 | 135 | echo json_encode($return); 136 | } 137 | 138 | 139 | public static function params($index=null, $default = null, $source = null) 140 | { 141 | // check for php://input and merge with $_REQUEST 142 | if ((isset($_SERVER["CONTENT_TYPE"]) && 143 | stripos($_SERVER["CONTENT_TYPE"],'application/json') !== false) || 144 | (isset($_SERVER["HTTP_CONTENT_TYPE"]) && 145 | stripos($_SERVER["HTTP_CONTENT_TYPE"],'application/json') !== false) // PHP build in Webserver !? 146 | ) { 147 | if ($json = json_decode(@file_get_contents('php://input'), true)) { 148 | $_REQUEST = array_merge($_REQUEST, $json); 149 | } 150 | } 151 | 152 | $src = $source ? $source : $_REQUEST; 153 | 154 | //Basic HTTP Authetication 155 | if (isset($_SERVER['PHP_AUTH_USER'])) { 156 | $credentials = [ 157 | "uname" => $_SERVER['PHP_AUTH_USER'], 158 | "upass" => $_SERVER['PHP_AUTH_PW'] 159 | ]; 160 | $src = array_merge($src, $credentials); 161 | } 162 | 163 | return Router::fetch_from_array($src, $index, $default); 164 | } 165 | 166 | 167 | public static function fetch_from_array(&$array, $index=null, $default = null) 168 | { 169 | if (is_null($index)) 170 | { 171 | return $array; 172 | } 173 | elseif (isset($array[$index])) 174 | { 175 | return $array[$index]; 176 | } 177 | elseif (strpos($index, '/')) 178 | { 179 | $keys = explode('/', $index); 180 | 181 | switch(count($keys)) 182 | { 183 | case 1: 184 | if (isset($array[$keys[0]])){ 185 | return $array[$keys[0]]; 186 | } 187 | break; 188 | 189 | case 2: 190 | if (isset($array[$keys[0]][$keys[1]])){ 191 | return $array[$keys[0]][$keys[1]]; 192 | } 193 | break; 194 | 195 | case 3: 196 | if (isset($array[$keys[0]][$keys[1]][$keys[2]])){ 197 | return $array[$keys[0]][$keys[1]][$keys[2]]; 198 | } 199 | break; 200 | 201 | case 4: 202 | if (isset($array[$keys[0]][$keys[1]][$keys[2]][$keys[3]])){ 203 | return $array[$keys[0]][$keys[1]][$keys[2]][$keys[3]]; 204 | } 205 | break; 206 | } 207 | } 208 | 209 | return $default; 210 | } 211 | 212 | 213 | public static $statusCodes = array( 214 | // Informational 1xx 215 | 100 => 'Continue', 216 | 101 => 'Switching Protocols', 217 | // Successful 2xx 218 | 200 => 'OK', 219 | 201 => 'Created', 220 | 202 => 'Accepted', 221 | 203 => 'Non-Authoritative Information', 222 | 204 => 'No Content', 223 | 205 => 'Reset Content', 224 | 206 => 'Partial Content', 225 | // Redirection 3xx 226 | 300 => 'Multiple Choices', 227 | 301 => 'Moved Permanently', 228 | 302 => 'Found', 229 | 303 => 'See Other', 230 | 304 => 'Not Modified', 231 | 305 => 'Use Proxy', 232 | 307 => 'Temporary Redirect', 233 | // Client Error 4xx 234 | 400 => 'Bad Request', 235 | 401 => 'Unauthorized', 236 | 402 => 'Payment Required', 237 | 403 => 'Forbidden', 238 | 404 => 'Not Found', 239 | 405 => 'Method Not Allowed', 240 | 406 => 'Not Acceptable', 241 | 407 => 'Proxy Authentication Required', 242 | 408 => 'Request Timeout', 243 | 409 => 'Conflict', 244 | 410 => 'Gone', 245 | 411 => 'Length Required', 246 | 412 => 'Precondition Failed', 247 | 413 => 'Request Entity Too Large', 248 | 414 => 'Request-URI Too Long', 249 | 415 => 'Unsupported Media Type', 250 | 416 => 'Request Range Not Satisfiable', 251 | 417 => 'Expectation Failed', 252 | // Server Error 5xx 253 | 500 => 'Internal Server Error', 254 | 501 => 'Not Implemented', 255 | 502 => 'Bad Gateway', 256 | 503 => 'Service Unavailable', 257 | 504 => 'Gateway Timeout', 258 | 505 => 'HTTP Version Not Supported' 259 | ); 260 | } -------------------------------------------------------------------------------- /templates/api/Test.php: -------------------------------------------------------------------------------- 1 | user = wire('user')->name; 11 | 12 | return $data; 13 | } 14 | 15 | public static function postWithSomeData($data) { 16 | // Check for required parameter "message" and sanitize with PW Sanitizer 17 | $data = ApiHelper::checkAndSanitizeRequiredParameters($data, ['message|text']); 18 | 19 | return "Your message is: " . $data->message; 20 | } 21 | } -------------------------------------------------------------------------------- /templates/basic-page.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 | 8 |
13 |