├── LICENSE.txt ├── README.md ├── composer.json ├── gui.php ├── inc └── admin │ └── class-alias-list-table.php └── network-admin.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Mercator - WordPress multisite domain mapping for the modern era 2 | 3 | Copyright 2014 by the contributors 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | This program incorporates work covered by the following copyright and 20 | permission notices: 21 | 22 | WordPress MU Domain Mapping 23 | 24 | Copyright Donncha O'Caoimh (donncha), Ron Rennick (wpmuguru), and contributors 25 | 26 | WordPress MU Domain Mapping is released under the GPL 27 | 28 | and 29 | 30 | Mercator - WordPress multisite domain mapping for the modern era 31 | 32 | Copyright 2003-2010 by the contributors 33 | 34 | Mercator is released under the GPL 35 | 36 | =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 37 | 38 | GNU GENERAL PUBLIC LICENSE 39 | Version 2, June 1991 40 | 41 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 42 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 43 | Everyone is permitted to copy and distribute verbatim copies 44 | of this license document, but changing it is not allowed. 45 | 46 | Preamble 47 | 48 | The licenses for most software are designed to take away your 49 | freedom to share and change it. By contrast, the GNU General Public 50 | License is intended to guarantee your freedom to share and change free 51 | software--to make sure the software is free for all its users. This 52 | General Public License applies to most of the Free Software 53 | Foundation's software and to any other program whose authors commit to 54 | using it. (Some other Free Software Foundation software is covered by 55 | the GNU Lesser General Public License instead.) You can apply it to 56 | your programs, too. 57 | 58 | When we speak of free software, we are referring to freedom, not 59 | price. Our General Public Licenses are designed to make sure that you 60 | have the freedom to distribute copies of free software (and charge for 61 | this service if you wish), that you receive source code or can get it 62 | if you want it, that you can change the software or use pieces of it 63 | in new free programs; and that you know you can do these things. 64 | 65 | To protect your rights, we need to make restrictions that forbid 66 | anyone to deny you these rights or to ask you to surrender the rights. 67 | These restrictions translate to certain responsibilities for you if you 68 | distribute copies of the software, or if you modify it. 69 | 70 | For example, if you distribute copies of such a program, whether 71 | gratis or for a fee, you must give the recipients all the rights that 72 | you have. You must make sure that they, too, receive or can get the 73 | source code. And you must show them these terms so they know their 74 | rights. 75 | 76 | We protect your rights with two steps: (1) copyright the software, and 77 | (2) offer you this license which gives you legal permission to copy, 78 | distribute and/or modify the software. 79 | 80 | Also, for each author's protection and ours, we want to make certain 81 | that everyone understands that there is no warranty for this free 82 | software. If the software is modified by someone else and passed on, we 83 | want its recipients to know that what they have is not the original, so 84 | that any problems introduced by others will not reflect on the original 85 | authors' reputations. 86 | 87 | Finally, any free program is threatened constantly by software 88 | patents. We wish to avoid the danger that redistributors of a free 89 | program will individually obtain patent licenses, in effect making the 90 | program proprietary. To prevent this, we have made it clear that any 91 | patent must be licensed for everyone's free use or not licensed at all. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. 95 | 96 | GNU GENERAL PUBLIC LICENSE 97 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 98 | 99 | 0. This License applies to any program or other work which contains 100 | a notice placed by the copyright holder saying it may be distributed 101 | under the terms of this General Public License. The "Program", below, 102 | refers to any such program or work, and a "work based on the Program" 103 | means either the Program or any derivative work under copyright law: 104 | that is to say, a work containing the Program or a portion of it, 105 | either verbatim or with modifications and/or translated into another 106 | language. (Hereinafter, translation is included without limitation in 107 | the term "modification".) Each licensee is addressed as "you". 108 | 109 | Activities other than copying, distribution and modification are not 110 | covered by this License; they are outside its scope. The act of 111 | running the Program is not restricted, and the output from the Program 112 | is covered only if its contents constitute a work based on the 113 | Program (independent of having been made by running the Program). 114 | Whether that is true depends on what the Program does. 115 | 116 | 1. You may copy and distribute verbatim copies of the Program's 117 | source code as you receive it, in any medium, provided that you 118 | conspicuously and appropriately publish on each copy an appropriate 119 | copyright notice and disclaimer of warranty; keep intact all the 120 | notices that refer to this License and to the absence of any warranty; 121 | and give any other recipients of the Program a copy of this License 122 | along with the Program. 123 | 124 | You may charge a fee for the physical act of transferring a copy, and 125 | you may at your option offer warranty protection in exchange for a fee. 126 | 127 | 2. You may modify your copy or copies of the Program or any portion 128 | of it, thus forming a work based on the Program, and copy and 129 | distribute such modifications or work under the terms of Section 1 130 | above, provided that you also meet all of these conditions: 131 | 132 | a) You must cause the modified files to carry prominent notices 133 | stating that you changed the files and the date of any change. 134 | 135 | b) You must cause any work that you distribute or publish, that in 136 | whole or in part contains or is derived from the Program or any 137 | part thereof, to be licensed as a whole at no charge to all third 138 | parties under the terms of this License. 139 | 140 | c) If the modified program normally reads commands interactively 141 | when run, you must cause it, when started running for such 142 | interactive use in the most ordinary way, to print or display an 143 | announcement including an appropriate copyright notice and a 144 | notice that there is no warranty (or else, saying that you provide 145 | a warranty) and that users may redistribute the program under 146 | these conditions, and telling the user how to view a copy of this 147 | License. (Exception: if the Program itself is interactive but 148 | does not normally print such an announcement, your work based on 149 | the Program is not required to print an announcement.) 150 | 151 | These requirements apply to the modified work as a whole. If 152 | identifiable sections of that work are not derived from the Program, 153 | and can be reasonably considered independent and separate works in 154 | themselves, then this License, and its terms, do not apply to those 155 | sections when you distribute them as separate works. But when you 156 | distribute the same sections as part of a whole which is a work based 157 | on the Program, the distribution of the whole must be on the terms of 158 | this License, whose permissions for other licensees extend to the 159 | entire whole, and thus to each and every part regardless of who wrote it. 160 | 161 | Thus, it is not the intent of this section to claim rights or contest 162 | your rights to work written entirely by you; rather, the intent is to 163 | exercise the right to control the distribution of derivative or 164 | collective works based on the Program. 165 | 166 | In addition, mere aggregation of another work not based on the Program 167 | with the Program (or with a work based on the Program) on a volume of 168 | a storage or distribution medium does not bring the other work under 169 | the scope of this License. 170 | 171 | 3. You may copy and distribute the Program (or a work based on it, 172 | under Section 2) in object code or executable form under the terms of 173 | Sections 1 and 2 above provided that you also do one of the following: 174 | 175 | a) Accompany it with the complete corresponding machine-readable 176 | source code, which must be distributed under the terms of Sections 177 | 1 and 2 above on a medium customarily used for software interchange; or, 178 | 179 | b) Accompany it with a written offer, valid for at least three 180 | years, to give any third party, for a charge no more than your 181 | cost of physically performing source distribution, a complete 182 | machine-readable copy of the corresponding source code, to be 183 | distributed under the terms of Sections 1 and 2 above on a medium 184 | customarily used for software interchange; or, 185 | 186 | c) Accompany it with the information you received as to the offer 187 | to distribute corresponding source code. (This alternative is 188 | allowed only for noncommercial distribution and only if you 189 | received the program in object code or executable form with such 190 | an offer, in accord with Subsection b above.) 191 | 192 | The source code for a work means the preferred form of the work for 193 | making modifications to it. For an executable work, complete source 194 | code means all the source code for all modules it contains, plus any 195 | associated interface definition files, plus the scripts used to 196 | control compilation and installation of the executable. However, as a 197 | special exception, the source code distributed need not include 198 | anything that is normally distributed (in either source or binary 199 | form) with the major components (compiler, kernel, and so on) of the 200 | operating system on which the executable runs, unless that component 201 | itself accompanies the executable. 202 | 203 | If distribution of executable or object code is made by offering 204 | access to copy from a designated place, then offering equivalent 205 | access to copy the source code from the same place counts as 206 | distribution of the source code, even though third parties are not 207 | compelled to copy the source along with the object code. 208 | 209 | 4. You may not copy, modify, sublicense, or distribute the Program 210 | except as expressly provided under this License. Any attempt 211 | otherwise to copy, modify, sublicense or distribute the Program is 212 | void, and will automatically terminate your rights under this License. 213 | However, parties who have received copies, or rights, from you under 214 | this License will not have their licenses terminated so long as such 215 | parties remain in full compliance. 216 | 217 | 5. You are not required to accept this License, since you have not 218 | signed it. However, nothing else grants you permission to modify or 219 | distribute the Program or its derivative works. These actions are 220 | prohibited by law if you do not accept this License. Therefore, by 221 | modifying or distributing the Program (or any work based on the 222 | Program), you indicate your acceptance of this License to do so, and 223 | all its terms and conditions for copying, distributing or modifying 224 | the Program or works based on it. 225 | 226 | 6. Each time you redistribute the Program (or any work based on the 227 | Program), the recipient automatically receives a license from the 228 | original licensor to copy, distribute or modify the Program subject to 229 | these terms and conditions. You may not impose any further 230 | restrictions on the recipients' exercise of the rights granted herein. 231 | You are not responsible for enforcing compliance by third parties to 232 | this License. 233 | 234 | 7. If, as a consequence of a court judgment or allegation of patent 235 | infringement or for any other reason (not limited to patent issues), 236 | conditions are imposed on you (whether by court order, agreement or 237 | otherwise) that contradict the conditions of this License, they do not 238 | excuse you from the conditions of this License. If you cannot 239 | distribute so as to satisfy simultaneously your obligations under this 240 | License and any other pertinent obligations, then as a consequence you 241 | may not distribute the Program at all. For example, if a patent 242 | license would not permit royalty-free redistribution of the Program by 243 | all those who receive copies directly or indirectly through you, then 244 | the only way you could satisfy both it and this License would be to 245 | refrain entirely from distribution of the Program. 246 | 247 | If any portion of this section is held invalid or unenforceable under 248 | any particular circumstance, the balance of the section is intended to 249 | apply and the section as a whole is intended to apply in other 250 | circumstances. 251 | 252 | It is not the purpose of this section to induce you to infringe any 253 | patents or other property right claims or to contest validity of any 254 | such claims; this section has the sole purpose of protecting the 255 | integrity of the free software distribution system, which is 256 | implemented by public license practices. Many people have made 257 | generous contributions to the wide range of software distributed 258 | through that system in reliance on consistent application of that 259 | system; it is up to the author/donor to decide if he or she is willing 260 | to distribute software through any other system and a licensee cannot 261 | impose that choice. 262 | 263 | This section is intended to make thoroughly clear what is believed to 264 | be a consequence of the rest of this License. 265 | 266 | 8. If the distribution and/or use of the Program is restricted in 267 | certain countries either by patents or by copyrighted interfaces, the 268 | original copyright holder who places the Program under this License 269 | may add an explicit geographical distribution limitation excluding 270 | those countries, so that distribution is permitted only in or among 271 | countries not thus excluded. In such case, this License incorporates 272 | the limitation as if written in the body of this License. 273 | 274 | 9. The Free Software Foundation may publish revised and/or new versions 275 | of the General Public License from time to time. Such new versions will 276 | be similar in spirit to the present version, but may differ in detail to 277 | address new problems or concerns. 278 | 279 | Each version is given a distinguishing version number. If the Program 280 | specifies a version number of this License which applies to it and "any 281 | later version", you have the option of following the terms and conditions 282 | either of that version or of any later version published by the Free 283 | Software Foundation. If the Program does not specify a version number of 284 | this License, you may choose any version ever published by the Free Software 285 | Foundation. 286 | 287 | 10. If you wish to incorporate parts of the Program into other free 288 | programs whose distribution conditions are different, write to the author 289 | to ask for permission. For software which is copyrighted by the Free 290 | Software Foundation, write to the Free Software Foundation; we sometimes 291 | make exceptions for this. Our decision will be guided by the two goals 292 | of preserving the free status of all derivatives of our free software and 293 | of promoting the sharing and reuse of software generally. 294 | 295 | NO WARRANTY 296 | 297 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 298 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 299 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 300 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 301 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 302 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 303 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 304 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 305 | REPAIR OR CORRECTION. 306 | 307 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 308 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 309 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 310 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 311 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 312 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 313 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 314 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 315 | POSSIBILITY OF SUCH DAMAGES. 316 | 317 | END OF TERMS AND CONDITIONS 318 | 319 | How to Apply These Terms to Your New Programs 320 | 321 | If you develop a new program, and you want it to be of the greatest 322 | possible use to the public, the best way to achieve this is to make it 323 | free software which everyone can redistribute and change under these terms. 324 | 325 | To do so, attach the following notices to the program. It is safest 326 | to attach them to the start of each source file to most effectively 327 | convey the exclusion of warranty; and each file should have at least 328 | the "copyright" line and a pointer to where the full notice is found. 329 | 330 | 331 | Copyright (C) 332 | 333 | This program is free software; you can redistribute it and/or modify 334 | it under the terms of the GNU General Public License as published by 335 | the Free Software Foundation; either version 2 of the License, or 336 | (at your option) any later version. 337 | 338 | This program is distributed in the hope that it will be useful, 339 | but WITHOUT ANY WARRANTY; without even the implied warranty of 340 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 341 | GNU General Public License for more details. 342 | 343 | You should have received a copy of the GNU General Public License along 344 | with this program; if not, write to the Free Software Foundation, Inc., 345 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 346 | 347 | Also add information on how to contact you by electronic and paper mail. 348 | 349 | If the program is interactive, make it output a short notice like this 350 | when it starts in an interactive mode: 351 | 352 | Gnomovision version 69, Copyright (C) year name of author 353 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 354 | This is free software, and you are welcome to redistribute it 355 | under certain conditions; type `show c' for details. 356 | 357 | The hypothetical commands `show w' and `show c' should show the appropriate 358 | parts of the General Public License. Of course, the commands you use may 359 | be called something other than `show w' and `show c'; they could even be 360 | mouse-clicks or menu items--whatever suits your program. 361 | 362 | You should also get your employer (if you work as a programmer) or your 363 | school, if any, to sign a "copyright disclaimer" for the program, if 364 | necessary. Here is a sample; alter the names: 365 | 366 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 367 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 368 | 369 | , 1 April 1989 370 | Ty Coon, President of Vice 371 | 372 | This General Public License does not permit incorporating your program into 373 | proprietary programs. If your program is a subroutine library, you may 374 | consider it more useful to permit linking proprietary applications with the 375 | library. If this is what you want to do, use the GNU Lesser General 376 | Public License instead of this License. 377 | 378 | WRITTEN OFFER 379 | 380 | The source code for any program binaries or compressed scripts that are 381 | included with Mercator can be freely obtained at the following URL: 382 | 383 | https://github.com/humanmade/Mercator 384 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mercator GUI 2 | Admin interface component for [Mercator](https://github.com/humanmade/Mercator). 3 | 4 | Allows you to manage aliases via the WP network admin. 5 | 6 | ## Requirements 7 | Mercator requires WordPress 3.9 or newer for the new sunrise processes. Mercator 8 | also requires PHP 5.3+ due to the use of namespaced code. 9 | 10 | ## Installation 11 | Include the file `gui.php` from your `sunrise.php` in the same way you include Mercator itself. 12 | 13 | **NOTE** include `gui.php` before `mercator.php` itself! 14 | 15 | For example: 16 | 17 | ```php 18 | items = array(); 22 | 23 | if ( empty( $this->_args['site_id'] ) ) { 24 | return; 25 | } 26 | 27 | $id = $this->_args['site_id']; 28 | $mappings = Mapping::get_by_site( $id ); 29 | if ( is_wp_error( $mappings ) ) { 30 | \Mercator\warn_with_message( __( 'Could not fetch aliases for the site. This may indicate a database error.', 'mercator' ) ); 31 | } 32 | if ( ! empty( $mappings ) ) { 33 | $this->items = $mappings; 34 | } 35 | } 36 | 37 | /** 38 | * Get columns for the table 39 | * 40 | * @return array Map of column ID => title 41 | */ 42 | public function get_columns() { 43 | return array( 44 | 'cb' => '', 45 | 'domain' => _x( 'Domain', 'mercator' ), 46 | 'active' => _x( 'Active', 'mercator' ), 47 | ); 48 | } 49 | 50 | /** 51 | * Get an associative array ( option_name => option_title ) with the list 52 | * of bulk actions available on this table. 53 | * 54 | * @since 3.1.0 55 | * @access protected 56 | * 57 | * @return array 58 | */ 59 | protected function get_bulk_actions() { 60 | $actions = array( 61 | 'activate' => _x( 'Activate', 'mercator' ), 62 | 'deactivate' => _x( 'Deactivate', 'mercator' ), 63 | 'delete' => _x( 'Delete', 'mercator' ), 64 | ); 65 | 66 | return apply_filters( 'mercator_alias_bulk_actions', $actions ); 67 | } 68 | 69 | /** 70 | * Display the bulk actions dropdown. 71 | * 72 | * @since 3.1.0 73 | * @access protected 74 | * 75 | * @param string $which The location of the bulk actions: 'top' or 'bottom'. 76 | * This is designated as optional for backwards-compatibility. 77 | */ 78 | protected function bulk_actions( $which = '' ) { 79 | if ( is_null( $this->_actions ) ) { 80 | $no_new_actions = $this->_actions = $this->get_bulk_actions(); 81 | /** 82 | * Filter the list table Bulk Actions drop-down. 83 | * 84 | * The dynamic portion of the hook name, $this->screen->id, refers 85 | * to the ID of the current screen, usually a string. 86 | * 87 | * This filter can currently only be used to remove bulk actions. 88 | * 89 | * @since 3.5.0 90 | * 91 | * @param array $actions An array of the available bulk actions. 92 | */ 93 | $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); 94 | $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions ); 95 | $two = ''; 96 | echo ''; 97 | wp_nonce_field( 'mercator-aliases-bulk-' . $this->_args['site_id'] ); 98 | } else { 99 | $two = '2'; 100 | } 101 | 102 | if ( empty( $this->_actions ) ) 103 | return; 104 | 105 | echo ""; 106 | echo "\n"; 116 | 117 | submit_button( __( 'Apply' ), 'action', false, false, array( 'id' => "doaction$two" ) ); 118 | echo "\n"; 119 | } 120 | 121 | /** 122 | * Get the current action selected from the bulk actions dropdown. 123 | * 124 | * @since 3.1.0 125 | * @access public 126 | * 127 | * @return string|bool The action name or False if no action was selected 128 | */ 129 | public function current_action() { 130 | if ( isset( $_REQUEST['bulk_action'] ) && -1 != $_REQUEST['bulk_action'] ) 131 | return $_REQUEST['bulk_action']; 132 | 133 | if ( isset( $_REQUEST['bulk_action2'] ) && -1 != $_REQUEST['bulk_action2'] ) 134 | return $_REQUEST['bulk_action2']; 135 | 136 | return false; 137 | } 138 | 139 | /** 140 | * Output extra "navigation" fields (section before/after the table) 141 | * 142 | * Outputs our Add New button above the table 143 | * 144 | * @param string $which Which tablenav to use (top/bottom) 145 | */ 146 | protected function extra_tablenav( $which ) { 147 | global $status; 148 | 149 | if ( $which !== 'top' ) 150 | return; 151 | 152 | $add_link = add_query_arg( 153 | array( 154 | 'action' => 'mercator-add', 155 | 'id' => $this->_args['site_id'], 156 | ), 157 | network_admin_url( 'admin.php' ) 158 | ); 159 | echo ''; 162 | } 163 | 164 | /** 165 | * Get cell value for the checkbox column 166 | * 167 | * @param Mapping $mapping Current mapping item 168 | * @return string HTML for the cell 169 | */ 170 | protected function column_cb( $mapping ) { 171 | return '' 173 | . ''; 175 | } 176 | 177 | /** 178 | * Get cell value for the domain column 179 | * 180 | * @param Mapping $mapping Current mapping item 181 | * @return string HTML for the cell 182 | */ 183 | protected function column_domain( $mapping ) { 184 | $domain = esc_html( $mapping->get_domain() ); 185 | if ( substr( $domain, 0, 4 ) === 'www.' ) { 186 | $domain = substr( $domain, 4 ); 187 | } 188 | 189 | $args = array( 190 | 'action' => 'mercator-aliases', 191 | 'id' => $mapping->get_site_id(), 192 | 'mappings' => $mapping->get_id(), 193 | '_wpnonce' => wp_create_nonce( 'mercator-aliases-bulk-' . $this->_args['site_id'] ), 194 | ); 195 | if ( ! $mapping->is_active() ) { 196 | $text = __( 'Activate', 'mercator' ); 197 | $action = 'activate'; 198 | } 199 | else { 200 | $text = __( 'Deactivate', 'mercator' ); 201 | $action = 'deactivate'; 202 | } 203 | $args['bulk_action'] = $action; 204 | 205 | $link = add_query_arg( $args, network_admin_url( 'admin.php' ) ); 206 | 207 | $delete_args = $args; 208 | $delete_args['bulk_action'] = 'delete'; 209 | $delete_link = add_query_arg( $delete_args, network_admin_url( 'admin.php' ) ); 210 | 211 | $edit_link = add_query_arg( 212 | array( 213 | 'action' => 'mercator-edit', 214 | 'id' => $mapping->get_site_id(), 215 | 'mapping' => $mapping->get_id(), 216 | ), 217 | network_admin_url( 'admin.php' ) 218 | ); 219 | 220 | $actions = array( 221 | 'edit' => sprintf( '%s', esc_url( $edit_link ), esc_html__( 'Edit', 'mercator' ) ), 222 | $action => sprintf( '%s', esc_url( $link ), esc_html( $text ) ), 223 | 'delete' => sprintf( '%s', esc_url( $delete_link ), esc_html__( 'Delete', 'mercator' ) ), 224 | ); 225 | $actions = apply_filters( 'mercator_alias_actions', $actions, $mapping ); 226 | $action_html = $this->row_actions( $actions, false ); 227 | 228 | return '' . $domain . '' . $action_html; 229 | } 230 | 231 | /** 232 | * Get cell value for the active column 233 | * 234 | * @param Mapping $mapping Current mapping item 235 | * @return string HTML for the cell 236 | */ 237 | protected function column_active( $mapping ) { 238 | $active = $mapping->is_active(); 239 | if ( $active ) { 240 | return esc_html__( 'Active', 'mercator' ); 241 | } 242 | return esc_html__( 'Inactive', 'mercator'); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /network-admin.php: -------------------------------------------------------------------------------- 1 | title 25 | * @return array 26 | */ 27 | function add_site_list_column( $columns ) { 28 | $columns['mercator_aliases'] = __( 'Aliases', 'mercator' ); 29 | return $columns; 30 | } 31 | 32 | /** 33 | * Output the site list column 34 | * 35 | * @param string $column Column ID 36 | * @param int $site_id Site ID 37 | */ 38 | function output_site_list_column( $column, $site_id ) { 39 | switch ( $column ) { 40 | case 'mercator_aliases': 41 | $mappings = Mapping::get_by_site( $site_id ); 42 | if ( ! empty( $mappings ) ) { 43 | foreach ( $mappings as $mapping ) { 44 | // Kinda horrible formatting, but matches the existing 45 | echo esc_html( $mapping->get_domain() ) . '
'; 46 | } 47 | } 48 | 49 | break; 50 | } 51 | } 52 | 53 | /** 54 | * Output the site tab if we're on the right page 55 | * 56 | * Outputs the link, then moves it into place using JS, as there are no hooks to 57 | * speak of. 58 | */ 59 | function maybe_output_site_tab() { 60 | if ( ! is_network_admin() ) { 61 | return; 62 | } 63 | 64 | if ( $GLOBALS['parent_file'] !== 'sites.php' || $GLOBALS['submenu_file'] !== 'sites.php' ) { 65 | return; 66 | } 67 | 68 | $id = isset( $_REQUEST['id'] ) ? absint( $_REQUEST['id'] ) : 0; 69 | if ( empty( $id ) ) { 70 | return; 71 | } 72 | 73 | $class = ( ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] === 'mercator-aliases' ) ? ' nav-tab-active' : ''; 74 | 75 | ?> 76 | 77 | 80 | %2$s'), get_blogaddress_by_id( $id ), $site_url_no_http ); 93 | 94 | // Load the page header 95 | global $title, $parent_file, $submenu_file; 96 | $title = sprintf( __( 'Aliases: %s', 'mercator' ), $site_url_no_http ); 97 | $parent_file = 'sites.php'; 98 | $submenu_file = 'sites.php'; 99 | require_once(ABSPATH . 'wp-admin/admin-header.php'); 100 | 101 | $add_link = add_query_arg( 102 | array( 103 | 'action' => 'mercator-add', 104 | 'id' => $id, 105 | ), 106 | network_admin_url( 'admin.php' ) 107 | ); 108 | ?> 109 | 110 |
111 |

112 | 113 | 114 | 115 | 116 |

117 | 131 |

' . $msg . '

'; 135 | } 136 | } 137 | 138 | /** 139 | * Output the admin page footer 140 | */ 141 | function output_page_footer() { 142 | echo ''; 143 | 144 | require_once(ABSPATH . 'wp-admin/admin-footer.php'); 145 | } 146 | 147 | /** 148 | * Handle submission of the list page 149 | * 150 | * Handles bulk actions for the list page. Redirects back to itself after 151 | * processing, and exits. 152 | * 153 | * @param int $id Site ID 154 | * @param string $action Action to perform 155 | */ 156 | function handle_list_page_submit( $id, $action ) { 157 | check_admin_referer( 'mercator-aliases-bulk-' . $id ); 158 | 159 | $sendback = remove_query_arg( array( 'did_action', 'mappings', '_wpnonce' ), wp_get_referer() ); 160 | if ( ! $sendback ) 161 | $sendback = admin_url( $parent_file ); 162 | 163 | $mappings = empty( $_REQUEST['mappings'] ) ? array() : (array) $_REQUEST['mappings']; 164 | $mappings = array_map( 'absint', $mappings ); 165 | 166 | if ( ! isset( $mappings ) ) { 167 | wp_redirect( $sendback ); 168 | exit; 169 | } 170 | 171 | $processed = 0; 172 | $args = array( 173 | 'did_action' => $action, 174 | 'mappings' => join( ',', $mappings ), 175 | ); 176 | switch ( $action ) { 177 | case 'activate': 178 | foreach ( $mappings as $id ) { 179 | $mapping = Mapping::get( $id ); 180 | if ( is_wp_error( $mapping ) ) { 181 | continue; 182 | } 183 | 184 | if ( $mapping->set_active( true ) ) { 185 | $processed++; 186 | } 187 | } 188 | break; 189 | 190 | case 'deactivate': 191 | foreach ( $mappings as $id ) { 192 | $mapping = Mapping::get( $id ); 193 | if ( is_wp_error( $mapping ) ) { 194 | continue; 195 | } 196 | 197 | if ( $mapping->set_active( false ) ) { 198 | $processed++; 199 | } 200 | } 201 | break; 202 | 203 | case 'delete': 204 | $args['domains'] = array(); 205 | foreach ( $mappings as $id ) { 206 | $mapping = Mapping::get( $id ); 207 | if ( is_wp_error( $mapping ) ) { 208 | continue; 209 | } 210 | 211 | if ( $mapping->delete() ) { 212 | // Mappings don't exist after we delete them, so pass the 213 | // domain for messages and such 214 | $args['domains'][] = $mapping->get_domain(); 215 | $processed++; 216 | } 217 | } 218 | break; 219 | 220 | default: 221 | do_action_ref_array( "mercator_aliases_bulk_action-{$action}", array( $mappings, &$processed, $action ) ); 222 | break; 223 | } 224 | 225 | $args['processed'] = $processed; 226 | $sendback = add_query_arg( $args, $sendback ); 227 | 228 | wp_safe_redirect( $sendback ); 229 | exit(); 230 | } 231 | 232 | /** 233 | * Output alias editing page 234 | */ 235 | function output_list_page() { 236 | 237 | $id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; 238 | 239 | if ( ! $id ) 240 | wp_die( __('Invalid site ID.') ); 241 | 242 | $id = absint( $id ); 243 | 244 | $details = get_blog_details( $id ); 245 | if ( ! can_edit_network( $details->site_id ) || (int) $details->blog_id !== $id ) 246 | wp_die( __( 'You do not have permission to access this page.' ) ); 247 | 248 | $wp_list_table = new Alias_List_Table( array( 249 | 'site_id' => $id, 250 | ) ); 251 | 252 | $messages = array(); 253 | 254 | $bulk_action = $wp_list_table->current_action(); 255 | if ( $bulk_action ) { 256 | $messages = handle_list_page_submit( $id, $bulk_action ); 257 | } 258 | 259 | $pagenum = $wp_list_table->get_pagenum(); 260 | 261 | $wp_list_table->prepare_items( $id ); 262 | 263 | // Add messages for bulk actions 264 | if ( ! empty( $_REQUEST['did_action'] ) ) { 265 | $processed = empty( $_REQUEST['processed'] ) ? 0 : absint( $_REQUEST['processed'] ); 266 | $did_action = $_REQUEST['did_action']; 267 | 268 | $mappings = empty( $_REQUEST['mappings'] ) ? array() : wp_parse_id_list( $_REQUEST['mappings'] ); 269 | $mappings = array_map( 'absint', $mappings ); 270 | 271 | // Special case for single, as it's not really a "bulk" action 272 | if ( $processed === 1 ) { 273 | $bulk_messages = array( 274 | 'activate' => __( 'Activated %s', 'mercator' ), 275 | 'deactivate' => __( 'Deactivated %s', 'mercator' ), 276 | 'delete' => __( 'Deleted %s', 'mercator' ), 277 | 'add' => __( 'Created %s', 'mercator' ), 278 | 'edit' => __( 'Updated %s', 'mercator' ), 279 | ); 280 | if ( $did_action !== 'delete' ) { 281 | $mapping = Mapping::get( $mappings[0] ); 282 | $domain = $mapping->get_domain(); 283 | } 284 | else { 285 | $domain = empty( $_REQUEST['domains'] ) ? array() : $_REQUEST['domains'][0]; 286 | } 287 | $placeholder = '' . $domain . ''; 288 | } 289 | else { 290 | // Note: we still use _n for languages which have special cases on 291 | // e.g. 3, 5, 10, etc 292 | $bulk_messages = array( 293 | 'activate' => _n( '%s alias activated.', '%s aliases activated.', $processed ), 294 | 'deactivate' => _n( '%s alias deactivated.', '%s aliases deactiaved.', $processed ), 295 | 'delete' => _n( '%s alias deleted.', '%s aliases deleted.', $processed ), 296 | 'add' => _n( '%s alias created.', '%s aliases created.', $processed ), 297 | 'edit' => _n( '%s alias updated.', '%s aliases updated.', $processed ), 298 | ); 299 | $placeholder = number_format_i18n( $processed ); 300 | } 301 | $bulk_messages = apply_filters( 'mercator_aliases_bulk_messages', $bulk_messages, $processed ); 302 | 303 | if ( ! empty( $bulk_messages[ $did_action ] ) ) { 304 | $messages[] = sprintf( $bulk_messages[ $did_action ], $placeholder ); 305 | } 306 | } 307 | 308 | output_page_header( $id, $messages ); 309 | 310 | ?> 311 |
312 | display(); ?> 313 |
314 | site_id ) ) { 351 | return new WP_Error( 'mercator.params.cannot_edit', __( 'You do not have permission to edit this site', 'mercator' ) ); 352 | } 353 | } 354 | 355 | // Validate active flag 356 | $valid['active'] = empty( $params['active'] ) ? false : true; 357 | 358 | return $valid; 359 | } 360 | 361 | /** 362 | * Handle submission of the add page 363 | * 364 | * @return array|null List of errors. Issues a redirect and exits on success. 365 | */ 366 | function handle_edit_page_submit( $id, $mapping ) { 367 | $messages = array(); 368 | if ( empty( $mapping ) ) { 369 | $did_action = 'add'; 370 | check_admin_referer( 'mercator-add-' . $id ); 371 | } 372 | else { 373 | $did_action = 'edit'; 374 | check_admin_referer( 'mercator-edit-' . $mapping->get_id() ); 375 | } 376 | 377 | // Check that the parameters are correct first 378 | $params = validate_alias_parameters( wp_unslash( $_POST ) ); 379 | if ( is_wp_error( $params ) ) { 380 | $messages[] = $params->get_error_message(); 381 | 382 | if ( $params->get_error_code() === 'mercator.params.domain_invalid_chars' ) { 383 | $messages[] = __( 'Note: for internationalized domain names, use the ASCII form (e.g, xn--bcher-kva.example)', 'mercator' ); 384 | } 385 | 386 | return $messages; 387 | } 388 | if ( empty( $mapping ) ) { 389 | // Create the actual mapping 390 | $result = $mapping = Mapping::create( $params['site'], $params['domain'], $params['active'] ); 391 | } 392 | else { 393 | // Update our existing 394 | $result = $mapping->update( $params ); 395 | } 396 | 397 | if ( is_wp_error( $result ) ) { 398 | $messages[] = $result->get_error_message(); 399 | 400 | return $messages; 401 | } 402 | 403 | // Success, redirect to alias page 404 | $location = add_query_arg( 405 | array( 406 | 'action' => 'mercator-aliases', 407 | 'id' => $id, 408 | 'did_action' => $did_action, 409 | 'mappings' => $mapping->get_id(), 410 | 'processed' => 1, 411 | '_wpnonce' => wp_create_nonce( 'mercator-alias-added-' . $mapping->get_id() ), 412 | ), 413 | network_admin_url( 'admin.php' ) 414 | ); 415 | wp_safe_redirect( $location ); 416 | exit; 417 | } 418 | 419 | /** 420 | * Output alias editing page 421 | */ 422 | function output_edit_page() { 423 | 424 | $id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; 425 | 426 | if ( ! $id ) 427 | wp_die( __('Invalid site ID.') ); 428 | 429 | $id = absint( $id ); 430 | 431 | $details = get_blog_details( $id ); 432 | if ( ! can_edit_network( $details->site_id ) || (int) $details->blog_id !== $id ) 433 | wp_die( __( 'You do not have permission to access this page.' ) ); 434 | 435 | // Are we editing? 436 | $mapping = null; 437 | $form_action = network_admin_url( 'admin.php?action=mercator-add' ); 438 | if ( ! empty( $_REQUEST['mapping'] ) ) { 439 | $mapping_id = absint( $_REQUEST['mapping'] ); 440 | $mapping = Mapping::get( $mapping_id ); 441 | if ( is_wp_error( $mapping ) || empty( $mapping ) ) { 442 | wp_die( __( 'Invalid alias ID.', 'mercator' ) ); 443 | } 444 | 445 | $form_action = network_admin_url( 'admin.php?action=mercator-edit' ); 446 | } 447 | 448 | // Handle form submission 449 | $messages = array(); 450 | if ( ! empty( $_POST['submit'] ) ) { 451 | $messages = handle_edit_page_submit( $id, $mapping ); 452 | } 453 | 454 | output_page_header( $id, $messages ); 455 | 456 | if ( empty( $mapping ) || ! empty( $_POST['_wpnonce'] ) ) { 457 | $domain = empty( $_POST['domain'] ) ? '' : wp_unslash( $_POST['domain'] ); 458 | $active = ! empty( $_POST['active'] ); 459 | } 460 | else { 461 | $domain = $mapping->get_domain(); 462 | $active = $mapping->is_active(); 463 | } 464 | 465 | ?> 466 |
467 | 468 | 469 | 472 | 477 | 478 | 479 | 482 | 490 | 491 |
470 | 471 | 473 | 476 |
480 | 481 | 483 | 489 |
492 | 493 | 494 | get_id() ) . '" />'; 502 | wp_nonce_field( 'mercator-edit-' . $mapping->get_id() ); 503 | submit_button( __( 'Save Alias', 'mercator' ) ); 504 | } 505 | 506 | ?> 507 |
508 | ' . sprintf( 528 | __( 'Enhanced by Mercator', 'mercator' ), 529 | 'https://github.com/humanmade/Mercator', 530 | sprintf( 531 | __( 'Version %s', 'mercator' ), 532 | \Mercator\VERSION 533 | ) 534 | ) . ''; 535 | array_unshift( $meta, $note ); 536 | return $meta; 537 | } 538 | --------------------------------------------------------------------------------