├── LICENSE ├── README.md ├── lib ├── admin.php ├── css │ └── rkv.repo.admin.css ├── js │ ├── jquery.autosize.js │ ├── jquery.autosize.min.js │ ├── jquery.datepick.min.js │ └── rkv.repo.admin.js ├── markdown.php ├── parse.php └── postmeta.php ├── reaktiv-remote-repo.php └── sample-plugin ├── RKV_Remote_Updater.php └── sample-plugin.php /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Reaktiv Remote Repo 2 | =================== 3 | 4 | A plugin to provide a self-hosted WP plugin repository 5 | 6 | 7 | ## General Setup ## 8 | 9 | To properly set this up, you must add some code to your plugin, with data from the site you have set up Reaktiv Remote Repo on. 10 | All of this should reside in your root file. Note: all of this code resides in your individual plugin. The actual updater class 11 | does not get modified in any way. 12 | 13 | 14 | You first add your repository update URL as a constant. It should be the domain of your site, with `update` on the end. 15 | 16 | ```php 17 | if ( ! defined( 'YOUR_REPO_URL' ) ) { 18 | define( 'YOUR_REPO_URL', 'http://yourdomain.com/update/' ); 19 | } 20 | ``` 21 | 22 | You then need to add the unique key as a constant. This key is generated in your individual item in the repo. 23 | 24 | ```php 25 | if ( ! defined( 'YOUR_PLUGIN_UNIQUE' ) ) { 26 | define( 'YOUR_PLUGIN_UNIQUE', 'XXXXXXXXXX' ); 27 | } 28 | ``` 29 | 30 | Include a version number, which is what the update will check against. 31 | 32 | ```php 33 | if ( ! defined( 'YOUR_PLUGIN_VER' ) ) { 34 | define( 'YOUR_PLUGIN_VER', '0.0.1' ); 35 | } 36 | ``` 37 | 38 | Include the updater class file in your plugin, and load it. 39 | 40 | ```php 41 | add_action( 'plugins_loaded', 'rkv_load_updater' ); 42 | 43 | function rkv_load_updater() { 44 | 45 | if ( ! class_exists( 'RKV_Remote_Updater' ) ) { 46 | include( 'lib/RKV_Remote_Updater.php' ); 47 | } 48 | } 49 | ``` 50 | 51 | And then add the update function in your plugin 52 | 53 | ```php 54 | add_action( 'admin_init', 'rkv_remote_update' ); 55 | 56 | function rkv_remote_update() { 57 | 58 | // ensure the class exists before running 59 | if ( ! class_exists( 'RKV_Remote_Updater' ) ) { 60 | return; 61 | } 62 | 63 | $updater = new RKV_Remote_Updater( YOUR_REPO_URL, __FILE__, array( 64 | 'unique' => YOUR_PLUGIN_UNIQUE, 65 | 'version' => YOUR_PLUGIN_VER, 66 | ) 67 | ); 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /lib/admin.php: -------------------------------------------------------------------------------- 1 | \n"; 107 | $rules .= "Order Allow,Deny\n"; 108 | $rules .= "Allow from all\n"; 109 | $rules .= "\n"; 110 | 111 | return $rules; 112 | } 113 | 114 | /** 115 | * Creates blank index.php and .htaccess files 116 | * 117 | * This function runs approximately once per month in order to ensure all folders 118 | * have their necessary protection files 119 | * 120 | * @return void 121 | */ 122 | public function secure_upload_dir() { 123 | 124 | // if ( false === get_transient( 'rkv_check_protection_files' ) ) { 125 | 126 | $upload_path = self::get_upload_dir(); 127 | 128 | // Make sure the /edd folder is created 129 | wp_mkdir_p( $upload_path ); 130 | 131 | // Top level .htaccess file 132 | $rules = self::get_htaccess_rules(); 133 | if ( self::htaccess_exists() ) { 134 | $contents = @file_get_contents( $upload_path . '/.htaccess' ); 135 | if ( $contents !== $rules || ! $contents ) { 136 | // Update the .htaccess rules if they don't match 137 | @file_put_contents( $upload_path . '/.htaccess', $rules ); 138 | } 139 | } elseif( wp_is_writable( $upload_path ) ) { 140 | // Create the file if it doesn't exist 141 | @file_put_contents( $upload_path . '/.htaccess', $rules ); 142 | } 143 | 144 | // Top level blank index.php 145 | if ( ! file_exists( $upload_path . '/index.php' ) && wp_is_writable( $upload_path ) ) { 146 | @file_put_contents( $upload_path . '/index.php', 'post_type == 'repo-items' ) : 165 | 166 | global $post; 167 | 168 | wp_enqueue_style( 'rkv-repo', plugins_url( '/css/rkv.repo.admin.css', __FILE__ ), array(), null, 'all' ); 169 | 170 | wp_enqueue_media( array( 'post' => $post->ID ) ); 171 | wp_enqueue_script( 'datepick', plugins_url( '/js/jquery.datepick.min.js', __FILE__ ), array('jquery'), null, true ); 172 | wp_enqueue_script( 'rkv-repo', plugins_url( '/js/rkv.repo.admin.js', __FILE__ ) , array( 'jquery', 'jquery-ui-sortable', 'media-upload' ), null, true ); 173 | wp_localize_script( 'rkv-repo', 'rkvAsset', array( 174 | 'icon' => '', 175 | 'uptitle' => 'Upload or select a file', 176 | 'upbutton' => 'Add This File', 177 | 'upcheck' => '', 178 | )); 179 | 180 | endif; 181 | 182 | } 183 | 184 | /** 185 | * reorder menu 186 | * 187 | * @return 188 | */ 189 | 190 | public function menu_order( $menu_ord ) { 191 | 192 | if ( ! $menu_ord ) 193 | return true; 194 | 195 | return array( 196 | 'index.php', // this represents the dashboard link 197 | 'edit.php?post_type=repo-items', // custom post type 198 | 'edit.php', // this is the default POST admin menu 199 | ); 200 | } 201 | 202 | /** 203 | * adjust admin display columns 204 | * 205 | * @return 206 | */ 207 | 208 | public function repo_columns( $columns ) { 209 | 210 | // remove stuff 211 | unset( $columns['date'] ); 212 | 213 | // now add the custom stuff 214 | $columns['title'] = __( 'Item Name', '' ); 215 | $columns['package'] = __( 'Package File', '' ); 216 | $columns['version'] = __( 'Version', '' ); 217 | $columns['added'] = __( 'Added', '' ); 218 | $columns['updated'] = __( 'Updated', '' ); 219 | $columns['readme'] = __( 'Readme', '' ); 220 | 221 | $columns = apply_filters( 'rkv_remote_repo_admin_columns', $columns ); 222 | 223 | return $columns; 224 | 225 | } 226 | 227 | 228 | /** 229 | * Column mods 230 | * 231 | * @return 232 | */ 233 | 234 | public function display_columns( $column, $post_id ) { 235 | 236 | $meta = get_post_meta( $post_id, '_rkv_repo_data', true ); 237 | $none = '('.__( 'none entered', '' ).')'; 238 | 239 | switch ( $column ) { 240 | 241 | case 'package': 242 | $data = !empty( $meta['package'] ) ? ''.__( 'Download', '' ).'' : $none; 243 | 244 | echo $data; 245 | 246 | break; 247 | 248 | case 'version': 249 | $data = !empty( $meta['version'] ) ? $meta['version'] : $none; 250 | 251 | echo $data; 252 | 253 | break; 254 | 255 | case 'added': 256 | $data = !empty( $meta['added'] ) ? date( 'Y-m-d', floatval( $meta['added'] ) ) : $none; 257 | 258 | echo $data; 259 | 260 | break; 261 | 262 | case 'updated': 263 | $data = !empty( $meta['last_updated'] ) ? date( 'Y-m-d', floatval( $meta['last_updated'] ) ) : $none; 264 | 265 | echo $data; 266 | 267 | break; 268 | 269 | case 'readme': 270 | $file = get_post_meta( $post_id, '_rkv_repo_readme_file', true ); 271 | $data = ! $file ? 'dashicons-no meta-column-no' : 'dashicons-yes meta-column-yes'; 272 | 273 | echo ''; 274 | 275 | break; 276 | 277 | // end all case breaks 278 | } 279 | 280 | } 281 | 282 | 283 | /** 284 | * build out post type 285 | * 286 | * @return 287 | */ 288 | 289 | public function _register_types() { 290 | 291 | $labels = array( 292 | 'name' => __( 'Repository', '' ), 293 | 'menu_name' => __( 'Repository', '' ), 294 | 'all_items' => __( 'Items', '' ), 295 | 'singular_name' => __( 'Item', '' ), 296 | 'add_new' => __( 'Add New Item', '' ), 297 | 'add_new_item' => __( 'Add New Item', '' ), 298 | 'edit' => __( 'Edit Item', '' ), 299 | 'edit_item' => __( 'Edit Item', '' ), 300 | 'new_item' => __( 'New Item', '' ), 301 | 'view' => __( 'View Item', '' ), 302 | 'view_item' => __( 'View Item', '' ), 303 | 'search_items' => __( 'Search Repository', '' ), 304 | 'not_found' => __( 'No Items found', '' ), 305 | 'not_found_in_trash' => __( 'No Items found in Trash', '' ), 306 | ); 307 | 308 | $cpt_args = array( 309 | 'labels' => $labels, 310 | 'public' => true, 311 | 'show_in_menu' => true, 312 | 'show_in_nav_menus' => false, 313 | 'show_ui' => true, 314 | 'publicly_queryable' => true, 315 | 'exclude_from_search' => true, 316 | 'hierarchical' => false, 317 | 'menu_position' => null, 318 | 'capability_type' => 'post', 319 | 'query_var' => true, 320 | 'menu_icon' => 'dashicons-share-alt', 321 | 'rewrite' => false, 322 | 'has_archive' => false, 323 | 'supports' => array( 'title' ), 324 | ); 325 | 326 | $cpt_args = apply_filters( 'rkv_remote_repo_type_args', $cpt_args ); 327 | 328 | register_post_type( 'repo-items', $cpt_args ); 329 | 330 | } 331 | 332 | /// end class 333 | } 334 | 335 | 336 | // Instantiate our class 337 | new RKV_Remote_Repo_Admin(); 338 | -------------------------------------------------------------------------------- /lib/css/rkv.repo.admin.css: -------------------------------------------------------------------------------- 1 | /* post table */ 2 | 3 | th.column-readme, 4 | td.column-readme { 5 | width: 100px; 6 | text-align: center; 7 | } 8 | 9 | span.meta-column-item { 10 | display: block; 11 | width: 100%; 12 | font-size: 24px; 13 | } 14 | 15 | span.meta-column-yes { 16 | color: #458b00; 17 | } 18 | 19 | span.meta-column-no { 20 | color: #cc0000; 21 | } 22 | 23 | /* post editor */ 24 | 25 | /* unique ID box */ 26 | #repo-unique-id { 27 | background: #fff; 28 | padding: 10px; 29 | margin: 0 0 10px 0; 30 | border: 1px solid #E5E5E5; 31 | } 32 | 33 | #repo-unique-id p { 34 | display: inline-block; 35 | vertical-align: top; 36 | margin: 0; 37 | font-size: 16px; 38 | line-height: 20px; 39 | } 40 | 41 | #repo-unique-id p.code-item { 42 | margin: 0 5px 0 0; 43 | } 44 | 45 | #repo-unique-id p.code-item code { 46 | font-size: 18px; 47 | line-height: 20px; 48 | color: #fff; 49 | background: #333; 50 | font-weight: 700; 51 | letter-spacing: 1px; 52 | padding: 3px 10px 2px; 53 | cursor: text; 54 | } 55 | 56 | #repo-unique-id p.code-label { 57 | font-style: italic; 58 | font-size: 14px; 59 | } 60 | 61 | /* file details */ 62 | input.repo-item-file-text { 63 | width: 75%; 64 | margin-right: 10px; 65 | } 66 | 67 | textarea.repo-item-textarea { 68 | width: 100%; 69 | min-height: 50px; 70 | overflow: auto; 71 | vertical-align: top; 72 | resize: vertical; 73 | } 74 | 75 | input.repo-item-num-text { 76 | width: 40%; 77 | } 78 | 79 | label.repo-item-label { 80 | width: 60%; 81 | } 82 | 83 | p.repo-cal-field { 84 | position: relative; 85 | } 86 | 87 | p.repo-cal-field .rkv-cal-icon { 88 | position: absolute; 89 | right: 152px; 90 | top: 8px; 91 | font-size: 14px; 92 | cursor: pointer; 93 | } 94 | 95 | tr.repo-screenshots-field p.uploader-info { 96 | cursor: pointer; 97 | margin: 0 0 1em; 98 | } 99 | 100 | tr.repo-screenshots-field span.rkv-screenshot-uploader { 101 | cursor: pointer; 102 | margin: 0 3px 0 0; 103 | } 104 | 105 | tr.repo-screenshots-field span.screenshot-spinner { 106 | visibility: hidden; 107 | display: inline-block; 108 | vertical-align: top; 109 | float: none; 110 | position: relative; 111 | left: 20px; 112 | top: -1px; 113 | } 114 | 115 | div.repo-screenshot-gallery div.screenshot-item { 116 | display: inline-block; 117 | vertical-align: top; 118 | position: relative; 119 | width: 92px; 120 | } 121 | 122 | div.repo-screenshot-gallery div.screenshot-item img.screenshot-image { 123 | width: 80px; 124 | height: 80px; 125 | padding: 2px; 126 | border: 1px solid #ccc; 127 | margin: 0 10px 0 0; 128 | } 129 | 130 | div.repo-screenshot-gallery div.screenshot-item span.screenshot-remove { 131 | position: absolute; 132 | bottom: 0; 133 | right: 0; 134 | cursor: pointer; 135 | padding: 2px; 136 | background: #efefef; 137 | border: 1px solid #ccc; 138 | color: #ff0000; 139 | border-radius: 50%; 140 | font-size: 14px; 141 | height: 14px; 142 | width: 14px; 143 | } 144 | 145 | 146 | tr.repo-readme-notice h4.readme-info-text { 147 | margin: 0; 148 | font-weight: 400; 149 | } 150 | 151 | tr.repo-upgrade-notice-field span.repo-unotice-vers-title, 152 | tr.repo-upgrade-notice-field span.repo-unotice-text-title { 153 | font-size: 12px; 154 | font-weight: 700; 155 | } 156 | 157 | tr.repo-upgrade-notice-field span.repo-unotice-vers-title, 158 | tr.repo-upgrade-notice-field input.repo-unotice-vers { 159 | display: inline-block; 160 | vertical-align: top; 161 | width: 5%; 162 | margin-right: 1%; 163 | } 164 | tr.repo-upgrade-notice-field span.repo-unotice-text-title, 165 | tr.repo-upgrade-notice-field input.repo-unotice-text { 166 | display: inline-block; 167 | vertical-align: top; 168 | width: 80%; 169 | } 170 | 171 | /* Default styling for jQuery Datepicker v4.0.6. */ 172 | .datepick { 173 | background-color: #fff; 174 | color: #000; 175 | border: 1px solid #444; 176 | -moz-border-radius: 0.25em; 177 | -webkit-border-radius: 0.25em; 178 | border-radius: 0.25em; 179 | font-family: Arial,Helvetica,Sans-serif; 180 | font-size: 90%; 181 | } 182 | .datepick-rtl { 183 | direction: rtl; 184 | } 185 | .datepick-popup { 186 | z-index: 1000; 187 | } 188 | .datepick-disable { 189 | position: absolute; 190 | z-index: 100; 191 | background-color: white; 192 | opacity: 0.5; 193 | filter: alpha(opacity=50); 194 | } 195 | .datepick a { 196 | color: #fff; 197 | text-decoration: none; 198 | } 199 | .datepick a.datepick-disabled { 200 | color: #888; 201 | cursor: auto; 202 | } 203 | .datepick button { 204 | margin: 0.25em; 205 | padding: 0.125em 0em; 206 | background-color: #fcc; 207 | border: none; 208 | -moz-border-radius: 0.25em; 209 | -webkit-border-radius: 0.25em; 210 | border-radius: 0.25em; 211 | font-weight: bold; 212 | } 213 | .datepick-nav, .datepick-ctrl { 214 | float: left; 215 | width: 100%; 216 | background-color: #000; 217 | color: #fff; 218 | font-size: 90%; 219 | font-weight: bold; 220 | } 221 | .datepick-ctrl { 222 | background-color: #600; 223 | } 224 | .datepick-cmd { 225 | width: 30%; 226 | } 227 | .datepick-cmd:hover { 228 | background-color: #777; 229 | } 230 | .datepick-ctrl .datepick-cmd:hover { 231 | background-color: #f08080; 232 | } 233 | .datepick-cmd-prevJump, .datepick-cmd-nextJump { 234 | width: 8%; 235 | } 236 | a.datepick-cmd { 237 | height: 1.5em; 238 | } 239 | button.datepick-cmd { 240 | text-align: center; 241 | } 242 | .datepick-cmd-prev, .datepick-cmd-prevJump, .datepick-cmd-clear { 243 | float: left; 244 | padding-left: 2%; 245 | } 246 | .datepick-cmd-current, .datepick-cmd-today { 247 | float: left; 248 | width: 35%; 249 | text-align: center; 250 | } 251 | .datepick-cmd-next, .datepick-cmd-nextJump, .datepick-cmd-close { 252 | float: right; 253 | padding-right: 2%; 254 | text-align: right; 255 | } 256 | .datepick-rtl .datepick-cmd-prev, .datepick-rtl .datepick-cmd-prevJump, 257 | .datepick-rtl .datepick-cmd-clear { 258 | float: right; 259 | padding-left: 0%; 260 | padding-right: 2%; 261 | text-align: right; 262 | } 263 | .datepick-rtl .datepick-cmd-current, .datepick-rtl .datepick-cmd-today { 264 | float: right; 265 | } 266 | .datepick-rtl .datepick-cmd-next, .datepick-rtl .datepick-cmd-nextJump, 267 | .datepick-rtl .datepick-cmd-close { 268 | float: left; 269 | padding-left: 2%; 270 | padding-right: 0%; 271 | text-align: left; 272 | } 273 | .datepick-month-nav { 274 | float: left; 275 | background-color: #777; 276 | text-align: center; 277 | } 278 | .datepick-month-nav div { 279 | float: left; 280 | width: 12.5%; 281 | margin: 1%; 282 | padding: 1%; 283 | } 284 | .datepick-month-nav span { 285 | color: #888; 286 | } 287 | .datepick-month-row { 288 | clear: left; 289 | } 290 | .datepick-month { 291 | float: left; 292 | width: 15em; 293 | border: 1px solid #444; 294 | text-align: center; 295 | } 296 | .datepick-month-header, .datepick-month-header select, .datepick-month-header input { 297 | height: 1.5em; 298 | background-color: #444; 299 | color: #fff; 300 | font-weight: bold; 301 | } 302 | .datepick-month-header select, .datepick-month-header input { 303 | height: 1.4em; 304 | border: none; 305 | } 306 | .datepick-month-header input { 307 | position: absolute; 308 | display: none; 309 | } 310 | .datepick-month table { 311 | width: 100%; 312 | border-collapse: collapse; 313 | } 314 | .datepick-month thead { 315 | border-bottom: 1px solid #aaa; 316 | } 317 | .datepick-month th, .datepick-month td { 318 | margin: 0em; 319 | padding: 0em; 320 | font-weight: normal; 321 | text-align: center; 322 | } 323 | .datepick-month th { 324 | border: 1px solid #777; 325 | } 326 | .datepick-month th, .datepick-month th a { 327 | background-color: #777; 328 | color: #fff; 329 | } 330 | .datepick-month td { 331 | background-color: #eee; 332 | border: 1px solid #aaa; 333 | } 334 | .datepick-month td.datepick-week { 335 | border: 1px solid #777; 336 | } 337 | .datepick-month td.datepick-week * { 338 | background-color: #777; 339 | color: #fff; 340 | border: none; 341 | } 342 | .datepick-month a { 343 | display: block; 344 | width: 100%; 345 | padding: 0.125em 0em; 346 | background-color: #eee; 347 | color: #000; 348 | text-decoration: none; 349 | } 350 | .datepick-month span { 351 | display: block; 352 | width: 100%; 353 | padding: 0.125em 0em; 354 | } 355 | .datepick-month td span { 356 | color: #888; 357 | } 358 | .datepick-month td .datepick-other-month { 359 | background-color: #fff; 360 | } 361 | .datepick-month td .datepick-weekend { 362 | background-color: #ddd; 363 | } 364 | .datepick-month td .datepick-today { 365 | background-color: #f0c0c0; 366 | } 367 | .datepick-month td .datepick-highlight { 368 | background-color: #f08080; 369 | } 370 | .datepick-month td .datepick-selected { 371 | background-color: #777; 372 | color: #fff; 373 | } 374 | .datepick-month th.datepick-week { 375 | background-color: #777; 376 | color: #fff; 377 | } 378 | .datepick-status { 379 | clear: both; 380 | background-color: #ddd; 381 | text-align: center; 382 | } 383 | .datepick-clear-fix { 384 | clear: both; 385 | } 386 | .datepick-cover { 387 | display: none; 388 | display/**/: block; 389 | position: absolute; 390 | z-index: -1; 391 | filter: mask(); 392 | top: -1px; 393 | left: -1px; 394 | width: 100px; 395 | height: 100px; 396 | } 397 | -------------------------------------------------------------------------------- /lib/js/jquery.autosize.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Autosize v1.18.4 - 2014-01-11 3 | Automatically adjust textarea height based on user input. 4 | (c) 2014 Jack Moore - http://www.jacklmoore.com/autosize 5 | license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | (function ($) { 8 | var 9 | defaults = { 10 | className: 'autosizejs', 11 | append: '', 12 | callback: false, 13 | resizeDelay: 10, 14 | placeholder: true 15 | }, 16 | 17 | // border:0 is unnecessary, but avoids a bug in Firefox on OSX 18 | copy = ''; 319 | echo ''; 320 | echo '

'; 321 | 322 | do_action( 'rkv_remote_repo_after_author_meta', $post, $data ); 323 | 324 | } 325 | 326 | /** 327 | * [rating_details description] 328 | * @param [type] $post [description] 329 | * @return [type] [description] 330 | */ 331 | public function rating_details( $post ) { 332 | 333 | $data = get_post_meta( $post->ID, '_rkv_repo_data', true ); 334 | 335 | $rating = isset( $data['rating'] ) && ! empty( $data['rating'] ) ? $data['rating'] : ''; 336 | $rcount = isset( $data['num_ratings'] ) && ! empty( $data['num_ratings'] ) ? $data['num_ratings'] : ''; 337 | $dcount = isset( $data['downloaded'] ) && ! empty( $data['downloaded'] ) ? $data['downloaded'] : ''; 338 | 339 | do_action( 'rkv_remote_repo_before_rating_meta', $post, $data ); 340 | 341 | echo '

'; 342 | echo ''; 343 | echo ' '; 344 | echo '

'; 345 | 346 | echo '

'; 347 | echo ''; 348 | echo ' '; 349 | echo '

'; 350 | 351 | echo '

'; 352 | echo ''; 353 | echo ' '; 354 | echo '

'; 355 | 356 | do_action( 'rkv_remote_repo_after_rating_meta', $post, $data ); 357 | 358 | } 359 | 360 | 361 | 362 | /** 363 | * [save_repo_item_meta description] 364 | * @param [type] $post_id [description] 365 | * @return [type] [description] 366 | */ 367 | public function save_repo_item_meta( $post_id ) { 368 | 369 | // run various checks to make sure we aren't doing anything weird 370 | if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 371 | return $post_id; 372 | 373 | if ( ! isset( $_POST['rkv_repo_meta_nonce'] ) || ! wp_verify_nonce( $_POST['rkv_repo_meta_nonce'], 'rkv_repo_meta_nonce' ) ) 374 | return $post_id; 375 | 376 | if ( 'repo-items' !== $_POST['post_type'] ) 377 | return $post_id; 378 | 379 | if ( !current_user_can( 'edit_post', $post_id ) ) 380 | return $post_id; 381 | 382 | // get data via $_POST and store it 383 | $data = $_POST['repo-meta']; 384 | 385 | 386 | // fetch the unique ID to make sure we have one 387 | $unique = isset( $data['unique-id'] ) && ! empty( $data['unique-id'] ) ? esc_attr( $data['unique-id'] ) : wp_generate_password( 16, false, false ); 388 | 389 | update_post_meta( $post_id, '_rkv_repo_unique_id', $unique ); 390 | 391 | // trim my upgrade notices 392 | if ( isset( $data['upgrade_notice'] ) ) { 393 | $filter_array = array_map( 'array_filter', $data['upgrade_notice'] ); 394 | $data['upgrade_notice'] = array_filter( $filter_array ); 395 | } 396 | 397 | 398 | if ( isset( $data ) && ! empty( $data ) ) 399 | update_post_meta( $post_id, '_rkv_repo_data', $data ); 400 | 401 | if ( ! isset( $data ) || isset( $data ) && empty( $data ) ) 402 | delete_post_meta( $post_id, '_rkv_repo_data' ); 403 | 404 | // do a quick check for a readme and set a flag 405 | if ( isset( $data['readme'] ) && ! empty( $data['readme'] ) ) 406 | update_post_meta( $post_id, '_rkv_repo_readme_file', 1 ); 407 | 408 | if ( ! isset( $data['readme'] ) || isset( $data['readme'] ) && empty( $data['readme'] ) ) 409 | delete_post_meta( $post_id, '_rkv_repo_readme_file' ); 410 | 411 | 412 | } 413 | 414 | /** 415 | * [repo_editor_load description] 416 | * @param [type] $key [description] 417 | * @return [type] [description] 418 | */ 419 | static function repo_editor_load( $key ) { 420 | 421 | $args = array( 422 | 'media_buttons' => false, 423 | 'textarea_name' => 'repo-meta['.$key.']', 424 | 'textarea_rows' => 6, 425 | 'teeny' => true 426 | ); 427 | 428 | $args = apply_filters( 'rkv_remote_repo_editor_args', $args, $key ); 429 | 430 | return $args; 431 | 432 | } 433 | 434 | /** 435 | * [readme_notice description] 436 | * @return [type] [description] 437 | */ 438 | static function readme_notice() { 439 | 440 | if ( false === apply_filters( 'rkv_remote_repo_readme_notice', true ) ) 441 | return; 442 | 443 | $show = ''; 444 | 445 | $show .= ''; 446 | $show .= ' '; 447 | $show .= ''; 448 | $show .= '

'.__( 'Note: If you have uploaded a markdown-formatted readme file, that will take priority over anything in this section', '' ).'

'; 449 | $show .= ''; 450 | $show .= ''; 451 | 452 | return $show; 453 | 454 | } 455 | 456 | /// end class 457 | } 458 | 459 | 460 | // Instantiate our class 461 | new RKV_Remote_Repo_PostMeta(); 462 | -------------------------------------------------------------------------------- /reaktiv-remote-repo.php: -------------------------------------------------------------------------------- 1 | get_col( $wpdb->prepare( 135 | " 136 | SELECT post_id 137 | FROM $wpdb->postmeta 138 | WHERE meta_key = %s 139 | AND meta_value = %s 140 | ", 141 | $meta_key, 142 | $unique 143 | ) ); 144 | 145 | if ( ! $products ) { 146 | return false; 147 | } 148 | 149 | $product_id = $products[0]; 150 | 151 | // set_transient( 'rkv_repo_search_'.$unique, $product_id, 0 ); 152 | 153 | // endif; 154 | 155 | // $product_id = get_transient( 'rkv_repo_search_'.$unique ); 156 | 157 | return $product_id; 158 | 159 | } 160 | 161 | /** 162 | * run various checks on the purchase request 163 | * @param array $wp_query API query being passed 164 | * @return bool 165 | */ 166 | public function validate_request( $wp_query ) { 167 | 168 | do_action( 'rkv_remote_repo_before_validation', $wp_query ); 169 | 170 | // check if action isnt one of our allowed 171 | if ( ! in_array( $wp_query->query_vars['action'], array( 'plugin_latest_version', 'plugin_information', 'update_counts' ) ) ) : 172 | 173 | $response = array( 174 | 'success' => false, 175 | 'error_code' => 'ACTION_INCORRECT', 176 | 'message' => 'The declared action was not understood.' 177 | ); 178 | 179 | $this->output( $response ); 180 | return false; 181 | 182 | endif; 183 | 184 | // check for missing slug 185 | if ( ! isset( $wp_query->query_vars['slug'] ) ) : 186 | 187 | $response = array( 188 | 'success' => false, 189 | 'error_code' => 'SLUG_MISSING', 190 | 'message' => 'The required slug was not provided.' 191 | ); 192 | 193 | $this->output( $response ); 194 | return false; 195 | 196 | endif; 197 | 198 | // check for missing product name 199 | if ( ! isset( $wp_query->query_vars['name'] ) ) : 200 | 201 | $response = array( 202 | 'success' => false, 203 | 'error_code' => 'ITEM_NAME_MISSING', 204 | 'message' => 'No item name has been supplied.' 205 | ); 206 | 207 | $this->output( $response ); 208 | return false; 209 | 210 | endif; 211 | 212 | // check for missing product unique 213 | if ( ! isset( $wp_query->query_vars['unique'] ) ) : 214 | 215 | $response = array( 216 | 'success' => false, 217 | 'error_code' => 'ITEM_UNIQUE_MISSING', 218 | 'message' => 'No unique ID has been supplied.' 219 | ); 220 | 221 | $this->output( $response ); 222 | return false; 223 | 224 | endif; 225 | 226 | // check for missing product version 227 | if ( ! isset( $wp_query->query_vars['version'] ) ) : 228 | 229 | $response = array( 230 | 'success' => false, 231 | 'error_code' => 'ITEM_VERSION_MISSING', 232 | 'message' => 'No item version has been supplied.' 233 | ); 234 | 235 | $this->output( $response ); 236 | return false; 237 | 238 | endif; 239 | 240 | // check if the product exists 241 | $product_id = self::get_product_id( $wp_query->query_vars['unique'] ); 242 | 243 | if ( ! $product_id ) : 244 | 245 | $response = array( 246 | 'success' => false, 247 | 'error_code' => 'NOT_VALID_ITEM', 248 | 'message' => 'The provided name does not match a valid item.' 249 | ); 250 | 251 | $this->output( $response ); 252 | return false; 253 | 254 | endif; 255 | 256 | // run my comparison 257 | $unique_id = self::fetch_product_unique( $product_id ); 258 | 259 | if ( $wp_query->query_vars['unique'] != $unique_id ) : 260 | 261 | $response = array( 262 | 'success' => false, 263 | 'error_code' => 'UNIQUE_MISMATCH', 264 | 'message' => 'The unique key provided does not match the requested item.' 265 | ); 266 | 267 | $this->output( $response ); 268 | return false; 269 | 270 | endif; 271 | 272 | // fetch product data for the rest 273 | $product_data = $this->get_product_data( absint( $product_id ) ); 274 | 275 | // check if the product has a version 276 | if ( ! $product_data['version'] ) : 277 | 278 | $response = array( 279 | 'success' => false, 280 | 'error_code' => 'NO_PRODUCT_VERSION', 281 | 'message' => 'The provided product has no available version.' 282 | ); 283 | 284 | $this->output( $response ); 285 | return false; 286 | 287 | endif; 288 | 289 | // check if the product file exists 290 | if ( ! $product_data['package'] ) : 291 | 292 | $response = array( 293 | 'success' => false, 294 | 'error_code' => 'NO_PRODUCT_FILE', 295 | 'message' => 'The provided product has no available file.' 296 | ); 297 | 298 | $this->output( $response ); 299 | return false; 300 | 301 | endif; 302 | 303 | /* TODO add custom validation methods */ 304 | do_action( 'rkv_remote_repo_after_validation', $wp_query ); 305 | 306 | // add some shit to the array 307 | $product_data['item_id'] = $product_id; 308 | $product_data['name'] = get_the_title( $product_id ); 309 | 310 | // all checks passed, return product file 311 | return $product_data; 312 | 313 | } 314 | 315 | /** 316 | * [screenshot_layout description] 317 | * @param [type] $data [description] 318 | * @return [type] [description] 319 | */ 320 | static function screenshot_data( $data ) { 321 | 322 | if ( ! isset( $data['screenshots'] ) || isset( $data['screenshots'] ) && empty( $data['screenshots'] ) ) 323 | return; 324 | 325 | // make sure we have an array first 326 | $screenshots = (array) $data['screenshots']; 327 | 328 | // set the image size to return via filter 329 | $image_size = apply_filters( 'rkv_remote_repo_screenshot_size', 'medium' ); 330 | 331 | foreach ( $screenshots as $image_id ) : 332 | $file_data = wp_get_attachment_image_src( $image_id, $image_size ); 333 | $file_name = get_the_title( $image_id ); 334 | 335 | $image_data[] = array( 336 | 'url' => $file_data[0], 337 | 'name' => $file_name 338 | ); 339 | endforeach; 340 | 341 | 342 | $display = ''; 343 | 344 | $display .= '
    '; 345 | foreach ( $image_data as $image ) : 346 | $display .= '
  1. '; 347 | $display .= ''; 348 | $display .= '

    '.esc_attr( $image['name'] ).'

    '; 349 | $display .= '
  2. '; 350 | endforeach; 351 | $display .= '
'; 352 | 353 | return $display; 354 | 355 | } 356 | 357 | 358 | /** 359 | * Sanitize Plugin Sections Data just to make sure it's proper data. 360 | * This is a helper function to sanitize plugin sections data to send to user site. 361 | * @since 0.1.0 362 | * @return string of sanitized section data 363 | */ 364 | static function sanitize_section_data( $section ) { 365 | 366 | /* allowed tags */ 367 | $allowed = array( 368 | 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ), 369 | 'abbr' => array( 'title' => array() ), 'acronym' => array( 'title' => array() ), 370 | 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), 371 | 'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), 372 | 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), 373 | 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ) 374 | ); 375 | 376 | $allowed = apply_filters( 'rkv_remote_repo_allowed_tags', $allowed ); 377 | 378 | $content = wp_kses( $section, $allowed ); 379 | 380 | return $content; 381 | } 382 | 383 | /** 384 | * [get_parse_data description] 385 | * @param [type] $section [description] 386 | * @param [type] $data [description] 387 | * @return [type] [description] 388 | */ 389 | static function get_parse_data( $file, $key ) { 390 | 391 | // load the WP Readme Parser and parse 392 | $parser = new WordPress_Readme_Parser(); 393 | $readme = $parser->parse_readme( $file ); 394 | 395 | if ( ! $readme ) 396 | return; 397 | 398 | if ( ! isset( $readme[ $key ] ) || isset( $readme[ $key ] ) && empty( $readme[ $key ] ) ) 399 | return; 400 | 401 | return $readme[ $key ]; 402 | 403 | } 404 | 405 | /** 406 | * [get_section_data description] 407 | * @param [type] $product_id [description] 408 | * @param [type] $data [description] 409 | * @param [type] $key [description] 410 | * @return [type] [description] 411 | */ 412 | static function get_section_data( $product_id, $data, $key ) { 413 | 414 | // set an initial fallback for the item being requested 415 | $item = isset( $data[ $key ] ) && ! empty( $data[ $key ] ) ? $data[ $key ] : ''; 416 | 417 | // run a check for the readme file 418 | $check = get_post_meta( $product_id, '_rkv_repo_readme_file', true ); 419 | $file = isset( $data['readme'] ) && ! empty( $data['readme'] ) ? esc_url( $data['readme'] ) : ''; 420 | 421 | // if no file, return the backup data 422 | if ( ! $check || empty ( $file ) ) 423 | return $item; 424 | 425 | // check to make sure the readme parse is there 426 | if ( ! class_exists( 'WordPress_Readme_Parser' ) ) 427 | return $item; 428 | 429 | $sections = self::get_parse_data( $file, 'sections' ); 430 | 431 | // bail if we don't have any sections 432 | if ( ! isset( $sections ) ) 433 | return $item; 434 | 435 | // bail if we don't have the section we want 436 | if ( ! isset( $sections[ $key ] ) ) 437 | return $item; 438 | 439 | // run the section content through the filter 440 | $content = self::sanitize_section_data( $sections[ $key ] ); 441 | 442 | // return the section being requested 443 | return $content; 444 | 445 | } 446 | 447 | /** 448 | * [get_lineitem_data description] 449 | * @param [type] $product_id [description] 450 | * @param [type] $data [description] 451 | * @param [type] $key [description] 452 | * @return [type] [description] 453 | */ 454 | static function get_lineitem_data( $product_id, $data, $key ) { 455 | 456 | // set an initial fallback for the item being requested 457 | $item = isset( $data[ $key ] ) && ! empty( $data[ $key ] ) ? $data[ $key ] : ''; 458 | 459 | // run a check for the readme file 460 | $check = get_post_meta( $product_id, '_rkv_repo_readme_file', true ); 461 | $file = isset( $data['readme'] ) && ! empty( $data['readme'] ) ? esc_url( $data['readme'] ) : ''; 462 | 463 | // if no file, return the backup data 464 | if ( ! $check || empty ( $file ) ) 465 | return $item; 466 | 467 | // check to make sure the readme parse is there 468 | if ( ! class_exists( 'WordPress_Readme_Parser' ) ) 469 | return $item; 470 | 471 | // do some key swapping 472 | $rpkey = array( 'requires', 'tested' ); 473 | $rdkey = array( 'requires_at_least', 'tested_up_to' ); 474 | 475 | $key = str_replace( $rpkey, $rdkey, $key ); 476 | 477 | $data = self::get_parse_data( $file, $key ); 478 | 479 | // bail if we don't have anything 480 | if ( ! isset( $data ) ) 481 | return $item; 482 | 483 | return $data; 484 | 485 | } 486 | 487 | /** 488 | * [fetch_product_unique description] 489 | * @param [type] $product_id [description] 490 | * @return [type] [description] 491 | */ 492 | static function fetch_product_unique( $product_id, $link = false ) { 493 | 494 | $unique = get_post_meta( $product_id, '_rkv_repo_unique_id', true ); 495 | 496 | if ( ! $unique ) 497 | return false; 498 | 499 | // send back the key if we don't need a link 500 | if ( ! $link ) 501 | return $unique; 502 | 503 | // send back the constructed URL 504 | return add_query_arg( array( 'download_key' => $unique ), home_url( '/' ) ); 505 | 506 | } 507 | 508 | /** 509 | * fetch the product data 510 | * @param int $product_id product ID 511 | * @return array 512 | */ 513 | public function get_product_data( $product_id, $meta = false ) { 514 | 515 | // get the data array 516 | $data = get_post_meta( $product_id, '_rkv_repo_data', true ); 517 | 518 | // get the readme parsed items 519 | $description = self::get_section_data( $product_id, $data, 'description' ); 520 | $faq = self::get_section_data( $product_id, $data, 'frequently_asked_questions' ); 521 | $changelog = self::get_section_data( $product_id, $data, 'changelog' ); 522 | $installation = self::get_section_data( $product_id, $data, 'installation' ); 523 | $requires = self::get_lineitem_data( $product_id, $data, 'requires' ); 524 | $tested = self::get_lineitem_data( $product_id, $data, 'tested' ); 525 | 526 | // pull items from post meta 527 | $package = self::fetch_product_unique( $product_id, true ); 528 | $item_url = isset( $data['package'] ) ? esc_url( $data['package'] ) : ''; 529 | $homepage = isset( $data['homepage'] ) ? esc_url( $data['homepage'] ) : ''; 530 | $version = isset( $data['version'] ) ? esc_html( $data['version'] ) : ''; 531 | $author = isset( $data['author'] ) ? esc_html( $data['author'] ) : ''; 532 | $profile = isset( $data['author_profile'] ) ? esc_url( $data['author_profile'] ) : ''; 533 | 534 | $rating = isset( $data['rating'] ) ? $data['rating'] : ''; 535 | $rcount = isset( $data['num_ratings'] ) ? $data['num_ratings'] : ''; 536 | $dcount = isset( $data['downloaded'] ) ? $data['downloaded'] : ''; 537 | 538 | // some fancy contributor stuff 539 | $contributors = isset( $data['contributors'] ) ? esc_html( $data['contributors'] ) : ''; 540 | if ( ! empty( $contributors ) ) 541 | $contributors = explode( ',', $contributors ); 542 | 543 | // do some fancy timestamp stuff 544 | $add_stamp = isset( $data['added'] ) ? $data['added'] : ''; 545 | $upd_stamp = isset( $data['last_updated'] ) ? $data['last_updated'] : ''; 546 | 547 | $add_show = ! empty( $add_stamp ) ? date( 'Y-m-d', floatval( $add_stamp ) ) : ''; 548 | $upd_show = ! empty( $upd_stamp ) ? date( 'Y-m-d', floatval( $upd_stamp ) ) : ''; 549 | 550 | // fetch our screenshots 551 | $screenshots = self::screenshot_data( $data ); 552 | 553 | // build out the big array 554 | $product_data = array( 555 | 'product_id' => $product_id, 556 | 'item-url' => $item_url, 557 | 'package' => $package, 558 | 'homepage' => $homepage, 559 | 'description' => $description, 560 | 'faq' => $faq, 561 | 'installation' => $installation, 562 | 'screenshots' => $screenshots, 563 | 'changelog' => $changelog, 564 | 'version' => $version, 565 | 'tested' => $tested, 566 | 'requires' => $requires, 567 | 'added' => $add_show, 568 | 'updated' => $upd_show, 569 | 'author' => $author, 570 | 'profile' => $profile, 571 | 'contributors' => $contributors, 572 | 'rating' => $rating, 573 | 'num_ratings' => $rcount, 574 | 'downloaded' => $dcount, 575 | ); 576 | 577 | $product_data = apply_filters( 'rkv_remote_repo_product_data', $product_data, $product_id ); 578 | 579 | // return single bit of info if requested 580 | if ( $meta && isset( $product_data[ $meta ] ) ) 581 | return $product_data[ $meta ]; 582 | 583 | return $product_data; 584 | 585 | } 586 | 587 | /** 588 | * [get_display_sections description] 589 | * @param [type] $data [description] 590 | * @return [type] [description] 591 | */ 592 | static function get_display_sections( $data ) { 593 | 594 | // build sections 595 | $sections = array( 596 | 'description' => $data['description'], 597 | 'installation' => $data['installation'], 598 | 'screenshots' => $data['screenshots'], 599 | 'changelog' => $data['changelog'], 600 | 'faq' => $data['faq'], 601 | ); 602 | 603 | $sections = apply_filters( 'rkv_remote_repo_display_sections', $sections, $data ); 604 | 605 | return $sections; 606 | 607 | } 608 | 609 | 610 | /** 611 | * [process_plugin_version description] 612 | * @param [type] $data [description] 613 | * @param [type] $slug [description] 614 | * @return [type] [description] 615 | */ 616 | public function process_plugin_version( $data, $slug ) { 617 | 618 | //fetch_product_unique 619 | 620 | $fields = array( 621 | 'slug' => $slug, 622 | 'new_version' => $data['version'], 623 | 'url' => $data['homepage'], 624 | 'package' => $data['package'], 625 | ); 626 | 627 | $fields = apply_filters( 'rkv_remote_repo_plugin_version', $fields, $slug ); 628 | 629 | return array( 630 | 'success' => true, 631 | 'fields' => $fields 632 | ); 633 | 634 | } 635 | 636 | /** 637 | * [process_plugin_details description] 638 | * @param [type] $data [description] 639 | * @param [type] $slug [description] 640 | * @return [type] [description] 641 | */ 642 | public function process_plugin_details( $data, $slug ) { 643 | 644 | $sections = self::get_display_sections( $data ); 645 | 646 | $fields = array( 647 | 'name' => $data['name'], 648 | 'slug' => $slug, 649 | 'version' => $data['version'], 650 | 'author' => $data['author'], 651 | 'author_profile' => $data['profile'], 652 | 'contributors' => $data['contributors'], 653 | 'requires' => $data['requires'], 654 | 'tested' => $data['tested'], 655 | 'rating' => $data['rating'], 656 | 'num_ratings' => $data['num_ratings'], 657 | 'downloaded' => $data['downloaded'], 658 | 'added' => $data['added'], 659 | 'last_updated' => $data['updated'], 660 | 'homepage' => $data['homepage'], 661 | 'download_link' => $data['package'], 662 | 'sections' => $sections, 663 | ); 664 | 665 | 666 | $fields = apply_filters( 'rkv_remote_repo_plugin_details', $fields, $slug ); 667 | 668 | return array( 669 | 'success' => true, 670 | 'fields' => $fields 671 | ); 672 | 673 | } 674 | 675 | /** 676 | * [process_plugin_counts description] 677 | * @param [type] $data [description] 678 | * @return [type] [description] 679 | */ 680 | public function process_plugin_counts( $data ) { 681 | 682 | // check our bypass filter first 683 | if ( false === apply_filters( 'rkv_remote_repo_plugin_counts', true ) ) 684 | return false; 685 | 686 | // pull the current item meta 687 | $meta = get_post_meta( $data['item_id'], '_rkv_repo_data', true ); 688 | 689 | // get our count (or set to zero) 690 | $current = isset( $meta['downloaded'] ) ? absint( $meta['downloaded'] ) : absint( 0 ); 691 | 692 | // increase it by one 693 | $update = $current + 1; 694 | 695 | // add it back into the array 696 | $meta['downloaded'] = absint( $update ); 697 | 698 | // update item with new count 699 | update_post_meta( $data['item_id'], '_rkv_repo_data', $meta ); 700 | 701 | // get out 702 | return true; 703 | 704 | } 705 | 706 | /** 707 | * Listens for the API and then processes the API requests 708 | * 709 | * @access public 710 | * @author Andrew Norcross 711 | * @global $wp_query 712 | * @since 0.0.1 713 | * @return void 714 | */ 715 | public function process_query() { 716 | 717 | global $wp_query; 718 | 719 | // Check for update var. Get out if not present 720 | if ( ! isset( $wp_query->query_vars['update'] ) ) 721 | return false; 722 | 723 | // bail if no action is set 724 | if ( ! isset( $wp_query->query_vars['action'] ) ) 725 | return false; 726 | 727 | // run my validation checks 728 | $data = $this->validate_request( $wp_query ); 729 | if ( ! $data ) 730 | return false; 731 | 732 | $process = false; 733 | 734 | if ( $wp_query->query_vars['action'] == 'plugin_latest_version' ) 735 | $process = $this->process_plugin_version( $data, $wp_query->query_vars['slug'] ); 736 | 737 | if ( $wp_query->query_vars['action'] == 'plugin_information' ) 738 | $process = $this->process_plugin_details( $data, $wp_query->query_vars['slug'] ); 739 | 740 | if ( $wp_query->query_vars['action'] == 'update_counts' ) 741 | $process = $this->process_plugin_counts( $data ); 742 | 743 | if ( ! $process ) 744 | return false; 745 | 746 | // Send out data to the output function 747 | $this->output( $process ); 748 | 749 | } 750 | 751 | /** 752 | * Output data in JSON 753 | * 754 | * @author Andrew Norcross 755 | * @since 0.0.1 756 | * @global $wp_query 757 | * 758 | * @param int $process 759 | */ 760 | 761 | public function output( $process ) { 762 | 763 | header( 'HTTP/1.1 200' ); 764 | header( 'Content-type: application/json; charset=utf-8' ); 765 | echo json_encode( $process ); 766 | 767 | die(); 768 | 769 | } 770 | 771 | public function process_download() { 772 | 773 | $args = apply_filters( 'rkv_remote_repo_process_download_args', array( 774 | 'download_key' => ( isset( $_GET['download_key'] ) ) ? $_GET['download_key'] : false, 775 | ) ); 776 | 777 | if ( ! isset( $_GET['download_key'] ) ) { 778 | return false; 779 | } 780 | 781 | extract( $args ); 782 | 783 | // Verify that the download key exists 784 | $product_id = self::get_product_id( $download_key ); 785 | 786 | if ( $product_id ) { 787 | 788 | $product = get_post( $product_id ); 789 | 790 | if ( ! $product ) { 791 | wp_die( 'No download exists.' ); 792 | } 793 | 794 | $package = $this->get_product_data( $product_id, 'item-url' ); 795 | 796 | $file_extension = $this->get_file_extension( $package ); 797 | $ctype = $this->get_file_ctype( $file_extension ); 798 | 799 | @session_write_close(); 800 | if( function_exists( 'apache_setenv' ) ) @apache_setenv('no-gzip', 1); 801 | @ini_set( 'zlib.output_compression', 'Off' ); 802 | 803 | nocache_headers(); 804 | header("Robots: none"); 805 | header("Content-Type: " . $ctype . ""); 806 | header("Content-Description: File Transfer"); 807 | header("Content-Disposition: attachment; filename=\"" . apply_filters( 'rkv_remote_repo_requested_file_name', basename( $package ) ) . "\";"); 808 | header("Content-Transfer-Encoding: binary"); 809 | 810 | header("Location: " . $package); 811 | 812 | exit(); 813 | 814 | } else { 815 | wp_die( 'No download exists.' ); 816 | } 817 | 818 | exit(); 819 | 820 | } 821 | 822 | /** 823 | * Get File Extension 824 | * 825 | * Returns the file extension of a filename. 826 | * 827 | * (From EDD 1.9.8) 828 | * 829 | * @since 1.0 830 | * 831 | * @param unknown $str File name 832 | * 833 | * @return mixed File extension 834 | */ 835 | private function get_file_extension( $str ) { 836 | $parts = explode( '.', $str ); 837 | return end( $parts ); 838 | } 839 | 840 | /** 841 | * Get the file content type 842 | * 843 | * (From EDD 1.9.8) 844 | * 845 | * @access public 846 | * @param string file extension 847 | * @return string 848 | */ 849 | function get_file_ctype( $extension ) { 850 | switch( $extension ): 851 | case 'ac' : $ctype = "application/pkix-attr-cert"; break; 852 | case 'adp' : $ctype = "audio/adpcm"; break; 853 | case 'ai' : $ctype = "application/postscript"; break; 854 | case 'aif' : $ctype = "audio/x-aiff"; break; 855 | case 'aifc' : $ctype = "audio/x-aiff"; break; 856 | case 'aiff' : $ctype = "audio/x-aiff"; break; 857 | case 'air' : $ctype = "application/vnd.adobe.air-application-installer-package+zip"; break; 858 | case 'apk' : $ctype = "application/vnd.android.package-archive"; break; 859 | case 'asc' : $ctype = "application/pgp-signature"; break; 860 | case 'atom' : $ctype = "application/atom+xml"; break; 861 | case 'atomcat' : $ctype = "application/atomcat+xml"; break; 862 | case 'atomsvc' : $ctype = "application/atomsvc+xml"; break; 863 | case 'au' : $ctype = "audio/basic"; break; 864 | case 'aw' : $ctype = "application/applixware"; break; 865 | case 'avi' : $ctype = "video/x-msvideo"; break; 866 | case 'bcpio' : $ctype = "application/x-bcpio"; break; 867 | case 'bin' : $ctype = "application/octet-stream"; break; 868 | case 'bmp' : $ctype = "image/bmp"; break; 869 | case 'boz' : $ctype = "application/x-bzip2"; break; 870 | case 'bpk' : $ctype = "application/octet-stream"; break; 871 | case 'bz' : $ctype = "application/x-bzip"; break; 872 | case 'bz2' : $ctype = "application/x-bzip2"; break; 873 | case 'ccxml' : $ctype = "application/ccxml+xml"; break; 874 | case 'cdmia' : $ctype = "application/cdmi-capability"; break; 875 | case 'cdmic' : $ctype = "application/cdmi-container"; break; 876 | case 'cdmid' : $ctype = "application/cdmi-domain"; break; 877 | case 'cdmio' : $ctype = "application/cdmi-object"; break; 878 | case 'cdmiq' : $ctype = "application/cdmi-queue"; break; 879 | case 'cdf' : $ctype = "application/x-netcdf"; break; 880 | case 'cer' : $ctype = "application/pkix-cert"; break; 881 | case 'cgm' : $ctype = "image/cgm"; break; 882 | case 'class' : $ctype = "application/octet-stream"; break; 883 | case 'cpio' : $ctype = "application/x-cpio"; break; 884 | case 'cpt' : $ctype = "application/mac-compactpro"; break; 885 | case 'crl' : $ctype = "application/pkix-crl"; break; 886 | case 'csh' : $ctype = "application/x-csh"; break; 887 | case 'css' : $ctype = "text/css"; break; 888 | case 'cu' : $ctype = "application/cu-seeme"; break; 889 | case 'davmount' : $ctype = "application/davmount+xml"; break; 890 | case 'dbk' : $ctype = "application/docbook+xml"; break; 891 | case 'dcr' : $ctype = "application/x-director"; break; 892 | case 'deploy' : $ctype = "application/octet-stream"; break; 893 | case 'dif' : $ctype = "video/x-dv"; break; 894 | case 'dir' : $ctype = "application/x-director"; break; 895 | case 'dist' : $ctype = "application/octet-stream"; break; 896 | case 'distz' : $ctype = "application/octet-stream"; break; 897 | case 'djv' : $ctype = "image/vnd.djvu"; break; 898 | case 'djvu' : $ctype = "image/vnd.djvu"; break; 899 | case 'dll' : $ctype = "application/octet-stream"; break; 900 | case 'dmg' : $ctype = "application/octet-stream"; break; 901 | case 'dms' : $ctype = "application/octet-stream"; break; 902 | case 'doc' : $ctype = "application/msword"; break; 903 | case 'docx' : $ctype = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; break; 904 | case 'dotx' : $ctype = "application/vnd.openxmlformats-officedocument.wordprocessingml.template"; break; 905 | case 'dssc' : $ctype = "application/dssc+der"; break; 906 | case 'dtd' : $ctype = "application/xml-dtd"; break; 907 | case 'dump' : $ctype = "application/octet-stream"; break; 908 | case 'dv' : $ctype = "video/x-dv"; break; 909 | case 'dvi' : $ctype = "application/x-dvi"; break; 910 | case 'dxr' : $ctype = "application/x-director"; break; 911 | case 'ecma' : $ctype = "application/ecmascript"; break; 912 | case 'elc' : $ctype = "application/octet-stream"; break; 913 | case 'emma' : $ctype = "application/emma+xml"; break; 914 | case 'eps' : $ctype = "application/postscript"; break; 915 | case 'epub' : $ctype = "application/epub+zip"; break; 916 | case 'etx' : $ctype = "text/x-setext"; break; 917 | case 'exe' : $ctype = "application/octet-stream"; break; 918 | case 'exi' : $ctype = "application/exi"; break; 919 | case 'ez' : $ctype = "application/andrew-inset"; break; 920 | case 'f4v' : $ctype = "video/x-f4v"; break; 921 | case 'fli' : $ctype = "video/x-fli"; break; 922 | case 'flv' : $ctype = "video/x-flv"; break; 923 | case 'gif' : $ctype = "image/gif"; break; 924 | case 'gml' : $ctype = "application/srgs"; break; 925 | case 'gpx' : $ctype = "application/gml+xml"; break; 926 | case 'gram' : $ctype = "application/gpx+xml"; break; 927 | case 'grxml' : $ctype = "application/srgs+xml"; break; 928 | case 'gtar' : $ctype = "application/x-gtar"; break; 929 | case 'gxf' : $ctype = "application/gxf"; break; 930 | case 'hdf' : $ctype = "application/x-hdf"; break; 931 | case 'hqx' : $ctype = "application/mac-binhex40"; break; 932 | case 'htm' : $ctype = "text/html"; break; 933 | case 'html' : $ctype = "text/html"; break; 934 | case 'ice' : $ctype = "x-conference/x-cooltalk"; break; 935 | case 'ico' : $ctype = "image/x-icon"; break; 936 | case 'ics' : $ctype = "text/calendar"; break; 937 | case 'ief' : $ctype = "image/ief"; break; 938 | case 'ifb' : $ctype = "text/calendar"; break; 939 | case 'iges' : $ctype = "model/iges"; break; 940 | case 'igs' : $ctype = "model/iges"; break; 941 | case 'ink' : $ctype = "application/inkml+xml"; break; 942 | case 'inkml' : $ctype = "application/inkml+xml"; break; 943 | case 'ipfix' : $ctype = "application/ipfix"; break; 944 | case 'jar' : $ctype = "application/java-archive"; break; 945 | case 'jnlp' : $ctype = "application/x-java-jnlp-file"; break; 946 | case 'jp2' : $ctype = "image/jp2"; break; 947 | case 'jpe' : $ctype = "image/jpeg"; break; 948 | case 'jpeg' : $ctype = "image/jpeg"; break; 949 | case 'jpg' : $ctype = "image/jpeg"; break; 950 | case 'js' : $ctype = "application/javascript"; break; 951 | case 'json' : $ctype = "application/json"; break; 952 | case 'jsonml' : $ctype = "application/jsonml+json"; break; 953 | case 'kar' : $ctype = "audio/midi"; break; 954 | case 'latex' : $ctype = "application/x-latex"; break; 955 | case 'lha' : $ctype = "application/octet-stream"; break; 956 | case 'lrf' : $ctype = "application/octet-stream"; break; 957 | case 'lzh' : $ctype = "application/octet-stream"; break; 958 | case 'lostxml' : $ctype = "application/lost+xml"; break; 959 | case 'm3u' : $ctype = "audio/x-mpegurl"; break; 960 | case 'm4a' : $ctype = "audio/mp4a-latm"; break; 961 | case 'm4b' : $ctype = "audio/mp4a-latm"; break; 962 | case 'm4p' : $ctype = "audio/mp4a-latm"; break; 963 | case 'm4u' : $ctype = "video/vnd.mpegurl"; break; 964 | case 'm4v' : $ctype = "video/x-m4v"; break; 965 | case 'm21' : $ctype = "application/mp21"; break; 966 | case 'ma' : $ctype = "application/mathematica"; break; 967 | case 'mac' : $ctype = "image/x-macpaint"; break; 968 | case 'mads' : $ctype = "application/mads+xml"; break; 969 | case 'man' : $ctype = "application/x-troff-man"; break; 970 | case 'mar' : $ctype = "application/octet-stream"; break; 971 | case 'mathml' : $ctype = "application/mathml+xml"; break; 972 | case 'mbox' : $ctype = "application/mbox"; break; 973 | case 'me' : $ctype = "application/x-troff-me"; break; 974 | case 'mesh' : $ctype = "model/mesh"; break; 975 | case 'metalink' : $ctype = "application/metalink+xml"; break; 976 | case 'meta4' : $ctype = "application/metalink4+xml"; break; 977 | case 'mets' : $ctype = "application/mets+xml"; break; 978 | case 'mid' : $ctype = "audio/midi"; break; 979 | case 'midi' : $ctype = "audio/midi"; break; 980 | case 'mif' : $ctype = "application/vnd.mif"; break; 981 | case 'mods' : $ctype = "application/mods+xml"; break; 982 | case 'mov' : $ctype = "video/quicktime"; break; 983 | case 'movie' : $ctype = "video/x-sgi-movie"; break; 984 | case 'm1v' : $ctype = "video/mpeg"; break; 985 | case 'm2v' : $ctype = "video/mpeg"; break; 986 | case 'mp2' : $ctype = "audio/mpeg"; break; 987 | case 'mp2a' : $ctype = "audio/mpeg"; break; 988 | case 'mp21' : $ctype = "application/mp21"; break; 989 | case 'mp3' : $ctype = "audio/mpeg"; break; 990 | case 'mp3a' : $ctype = "audio/mpeg"; break; 991 | case 'mp4' : $ctype = "video/mp4"; break; 992 | case 'mp4s' : $ctype = "application/mp4"; break; 993 | case 'mpe' : $ctype = "video/mpeg"; break; 994 | case 'mpeg' : $ctype = "video/mpeg"; break; 995 | case 'mpg' : $ctype = "video/mpeg"; break; 996 | case 'mpg4' : $ctype = "video/mpeg"; break; 997 | case 'mpga' : $ctype = "audio/mpeg"; break; 998 | case 'mrc' : $ctype = "application/marc"; break; 999 | case 'mrcx' : $ctype = "application/marcxml+xml"; break; 1000 | case 'ms' : $ctype = "application/x-troff-ms"; break; 1001 | case 'mscml' : $ctype = "application/mediaservercontrol+xml"; break; 1002 | case 'msh' : $ctype = "model/mesh"; break; 1003 | case 'mxf' : $ctype = "application/mxf"; break; 1004 | case 'mxu' : $ctype = "video/vnd.mpegurl"; break; 1005 | case 'nc' : $ctype = "application/x-netcdf"; break; 1006 | case 'oda' : $ctype = "application/oda"; break; 1007 | case 'oga' : $ctype = "application/ogg"; break; 1008 | case 'ogg' : $ctype = "application/ogg"; break; 1009 | case 'ogx' : $ctype = "application/ogg"; break; 1010 | case 'omdoc' : $ctype = "application/omdoc+xml"; break; 1011 | case 'onetoc' : $ctype = "application/onenote"; break; 1012 | case 'onetoc2' : $ctype = "application/onenote"; break; 1013 | case 'onetmp' : $ctype = "application/onenote"; break; 1014 | case 'onepkg' : $ctype = "application/onenote"; break; 1015 | case 'opf' : $ctype = "application/oebps-package+xml"; break; 1016 | case 'oxps' : $ctype = "application/oxps"; break; 1017 | case 'p7c' : $ctype = "application/pkcs7-mime"; break; 1018 | case 'p7m' : $ctype = "application/pkcs7-mime"; break; 1019 | case 'p7s' : $ctype = "application/pkcs7-signature"; break; 1020 | case 'p8' : $ctype = "application/pkcs8"; break; 1021 | case 'p10' : $ctype = "application/pkcs10"; break; 1022 | case 'pbm' : $ctype = "image/x-portable-bitmap"; break; 1023 | case 'pct' : $ctype = "image/pict"; break; 1024 | case 'pdb' : $ctype = "chemical/x-pdb"; break; 1025 | case 'pdf' : $ctype = "application/pdf"; break; 1026 | case 'pki' : $ctype = "application/pkixcmp"; break; 1027 | case 'pkipath' : $ctype = "application/pkix-pkipath"; break; 1028 | case 'pfr' : $ctype = "application/font-tdpfr"; break; 1029 | case 'pgm' : $ctype = "image/x-portable-graymap"; break; 1030 | case 'pgn' : $ctype = "application/x-chess-pgn"; break; 1031 | case 'pgp' : $ctype = "application/pgp-encrypted"; break; 1032 | case 'pic' : $ctype = "image/pict"; break; 1033 | case 'pict' : $ctype = "image/pict"; break; 1034 | case 'pkg' : $ctype = "application/octet-stream"; break; 1035 | case 'png' : $ctype = "image/png"; break; 1036 | case 'pnm' : $ctype = "image/x-portable-anymap"; break; 1037 | case 'pnt' : $ctype = "image/x-macpaint"; break; 1038 | case 'pntg' : $ctype = "image/x-macpaint"; break; 1039 | case 'pot' : $ctype = "application/vnd.ms-powerpoint"; break; 1040 | case 'potx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.template"; break; 1041 | case 'ppm' : $ctype = "image/x-portable-pixmap"; break; 1042 | case 'pps' : $ctype = "application/vnd.ms-powerpoint"; break; 1043 | case 'ppsx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.slideshow"; break; 1044 | case 'ppt' : $ctype = "application/vnd.ms-powerpoint"; break; 1045 | case 'pptx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; break; 1046 | case 'prf' : $ctype = "application/pics-rules"; break; 1047 | case 'ps' : $ctype = "application/postscript"; break; 1048 | case 'psd' : $ctype = "image/photoshop"; break; 1049 | case 'qt' : $ctype = "video/quicktime"; break; 1050 | case 'qti' : $ctype = "image/x-quicktime"; break; 1051 | case 'qtif' : $ctype = "image/x-quicktime"; break; 1052 | case 'ra' : $ctype = "audio/x-pn-realaudio"; break; 1053 | case 'ram' : $ctype = "audio/x-pn-realaudio"; break; 1054 | case 'ras' : $ctype = "image/x-cmu-raster"; break; 1055 | case 'rdf' : $ctype = "application/rdf+xml"; break; 1056 | case 'rgb' : $ctype = "image/x-rgb"; break; 1057 | case 'rm' : $ctype = "application/vnd.rn-realmedia"; break; 1058 | case 'rmi' : $ctype = "audio/midi"; break; 1059 | case 'roff' : $ctype = "application/x-troff"; break; 1060 | case 'rss' : $ctype = "application/rss+xml"; break; 1061 | case 'rtf' : $ctype = "text/rtf"; break; 1062 | case 'rtx' : $ctype = "text/richtext"; break; 1063 | case 'sgm' : $ctype = "text/sgml"; break; 1064 | case 'sgml' : $ctype = "text/sgml"; break; 1065 | case 'sh' : $ctype = "application/x-sh"; break; 1066 | case 'shar' : $ctype = "application/x-shar"; break; 1067 | case 'sig' : $ctype = "application/pgp-signature"; break; 1068 | case 'silo' : $ctype = "model/mesh"; break; 1069 | case 'sit' : $ctype = "application/x-stuffit"; break; 1070 | case 'skd' : $ctype = "application/x-koan"; break; 1071 | case 'skm' : $ctype = "application/x-koan"; break; 1072 | case 'skp' : $ctype = "application/x-koan"; break; 1073 | case 'skt' : $ctype = "application/x-koan"; break; 1074 | case 'sldx' : $ctype = "application/vnd.openxmlformats-officedocument.presentationml.slide"; break; 1075 | case 'smi' : $ctype = "application/smil"; break; 1076 | case 'smil' : $ctype = "application/smil"; break; 1077 | case 'snd' : $ctype = "audio/basic"; break; 1078 | case 'so' : $ctype = "application/octet-stream"; break; 1079 | case 'spl' : $ctype = "application/x-futuresplash"; break; 1080 | case 'spx' : $ctype = "audio/ogg"; break; 1081 | case 'src' : $ctype = "application/x-wais-source"; break; 1082 | case 'stk' : $ctype = "application/hyperstudio"; break; 1083 | case 'sv4cpio' : $ctype = "application/x-sv4cpio"; break; 1084 | case 'sv4crc' : $ctype = "application/x-sv4crc"; break; 1085 | case 'svg' : $ctype = "image/svg+xml"; break; 1086 | case 'swf' : $ctype = "application/x-shockwave-flash"; break; 1087 | case 't' : $ctype = "application/x-troff"; break; 1088 | case 'tar' : $ctype = "application/x-tar"; break; 1089 | case 'tcl' : $ctype = "application/x-tcl"; break; 1090 | case 'tex' : $ctype = "application/x-tex"; break; 1091 | case 'texi' : $ctype = "application/x-texinfo"; break; 1092 | case 'texinfo' : $ctype = "application/x-texinfo"; break; 1093 | case 'tif' : $ctype = "image/tiff"; break; 1094 | case 'tiff' : $ctype = "image/tiff"; break; 1095 | case 'torrent' : $ctype = "application/x-bittorrent"; break; 1096 | case 'tr' : $ctype = "application/x-troff"; break; 1097 | case 'tsv' : $ctype = "text/tab-separated-values"; break; 1098 | case 'txt' : $ctype = "text/plain"; break; 1099 | case 'ustar' : $ctype = "application/x-ustar"; break; 1100 | case 'vcd' : $ctype = "application/x-cdlink"; break; 1101 | case 'vrml' : $ctype = "model/vrml"; break; 1102 | case 'vsd' : $ctype = "application/vnd.visio"; break; 1103 | case 'vss' : $ctype = "application/vnd.visio"; break; 1104 | case 'vst' : $ctype = "application/vnd.visio"; break; 1105 | case 'vsw' : $ctype = "application/vnd.visio"; break; 1106 | case 'vxml' : $ctype = "application/voicexml+xml"; break; 1107 | case 'wav' : $ctype = "audio/x-wav"; break; 1108 | case 'wbmp' : $ctype = "image/vnd.wap.wbmp"; break; 1109 | case 'wbmxl' : $ctype = "application/vnd.wap.wbxml"; break; 1110 | case 'wm' : $ctype = "video/x-ms-wm"; break; 1111 | case 'wml' : $ctype = "text/vnd.wap.wml"; break; 1112 | case 'wmlc' : $ctype = "application/vnd.wap.wmlc"; break; 1113 | case 'wmls' : $ctype = "text/vnd.wap.wmlscript"; break; 1114 | case 'wmlsc' : $ctype = "application/vnd.wap.wmlscriptc"; break; 1115 | case 'wmv' : $ctype = "video/x-ms-wmv"; break; 1116 | case 'wmx' : $ctype = "video/x-ms-wmx"; break; 1117 | case 'wrl' : $ctype = "model/vrml"; break; 1118 | case 'xbm' : $ctype = "image/x-xbitmap"; break; 1119 | case 'xdssc' : $ctype = "application/dssc+xml"; break; 1120 | case 'xer' : $ctype = "application/patch-ops-error+xml"; break; 1121 | case 'xht' : $ctype = "application/xhtml+xml"; break; 1122 | case 'xhtml' : $ctype = "application/xhtml+xml"; break; 1123 | case 'xla' : $ctype = "application/vnd.ms-excel"; break; 1124 | case 'xlam' : $ctype = "application/vnd.ms-excel.addin.macroEnabled.12"; break; 1125 | case 'xlc' : $ctype = "application/vnd.ms-excel"; break; 1126 | case 'xlm' : $ctype = "application/vnd.ms-excel"; break; 1127 | case 'xls' : $ctype = "application/vnd.ms-excel"; break; 1128 | case 'xlsx' : $ctype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; break; 1129 | case 'xlsb' : $ctype = "application/vnd.ms-excel.sheet.binary.macroEnabled.12"; break; 1130 | case 'xlt' : $ctype = "application/vnd.ms-excel"; break; 1131 | case 'xltx' : $ctype = "application/vnd.openxmlformats-officedocument.spreadsheetml.template"; break; 1132 | case 'xlw' : $ctype = "application/vnd.ms-excel"; break; 1133 | case 'xml' : $ctype = "application/xml"; break; 1134 | case 'xpm' : $ctype = "image/x-xpixmap"; break; 1135 | case 'xsl' : $ctype = "application/xml"; break; 1136 | case 'xslt' : $ctype = "application/xslt+xml"; break; 1137 | case 'xul' : $ctype = "application/vnd.mozilla.xul+xml"; break; 1138 | case 'xwd' : $ctype = "image/x-xwindowdump"; break; 1139 | case 'xyz' : $ctype = "chemical/x-xyz"; break; 1140 | case 'zip' : $ctype = "application/zip"; break; 1141 | default : $ctype = "application/force-download"; 1142 | endswitch; 1143 | 1144 | return apply_filters( 'rkv_remote_repo_file_ctype', $ctype ); 1145 | } 1146 | 1147 | } 1148 | 1149 | new Reaktiv_Remote_Repo(); -------------------------------------------------------------------------------- /sample-plugin/RKV_Remote_Updater.php: -------------------------------------------------------------------------------- 1 | api_url = trailingslashit( $_api_url ); 36 | $this->api_data = urlencode_deep( $_api_data ); 37 | $this->name = plugin_basename( $_plugin_file ); 38 | $this->slug = basename( $_plugin_file, '.php'); 39 | $this->version = $_api_data['version']; 40 | $this->unique = $_api_data['unique']; 41 | 42 | // Set up hooks. 43 | $this->hook(); 44 | 45 | } 46 | 47 | /** 48 | * Set up Wordpress filters to hook into WP's update process. 49 | * 50 | * @uses add_filter() 51 | * 52 | * @return void 53 | */ 54 | 55 | private function hook() { 56 | 57 | add_filter ( 'pre_set_site_transient_update_plugins', array( $this, 'api_check' ) ); 58 | add_filter ( 'plugins_api', array( $this, 'api_data' ), 10, 3 ); 59 | add_filter ( 'upgrader_post_install', array( $this, 'run_remote_count' ), 10, 3 ); 60 | add_filter ( 'http_request_args', array( $this, 'disable_wporg' ), 5, 2 ); 61 | 62 | register_activation_hook ( __FILE__, array( $this, 'clear_transient' ) ); 63 | 64 | } 65 | 66 | /** 67 | * delete the transient check on initial install 68 | * @return void 69 | */ 70 | public function clear_transient() { 71 | 72 | delete_transient( 'update_plugins' ); 73 | } 74 | 75 | /** 76 | * run transient check 77 | * @param [type] $transient [description] 78 | * @return [type] [description] 79 | */ 80 | 81 | public function api_check( $_transient_data ) { 82 | 83 | if( empty( $_transient_data ) ) 84 | return $_transient_data; 85 | 86 | $to_send = array( 'slug' => $this->slug ); 87 | 88 | $api_response = $this->api_request( 'plugin_latest_version', $to_send ); 89 | 90 | if( false !== $api_response && is_object( $api_response ) ) { 91 | 92 | if( version_compare( $this->version, $api_response->new_version, '<' ) ) { 93 | $_transient_data->response[$this->name] = $api_response; 94 | 95 | // add our update row piece 96 | $name = $this->name; 97 | add_action( "after_plugin_row_$name", 'wp_plugin_update_row', 10, 2 ); 98 | } 99 | 100 | } 101 | 102 | return $_transient_data; 103 | 104 | } 105 | 106 | /** 107 | * Updates information on the "View version x.x details" page with custom data. 108 | * 109 | * @uses api_request() 110 | * 111 | * @param mixed $_data 112 | * @param string $_action 113 | * @param object $_args 114 | * @return object $_data 115 | */ 116 | function api_data( $_data, $_action = '', $_args = null ) { 117 | 118 | if ( ( $_action != 'plugin_information' ) || !isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) 119 | return $_data; 120 | 121 | $to_send = array( 'slug' => $this->slug ); 122 | 123 | $api_response = $this->api_request( 'plugin_information', $to_send ); 124 | 125 | if ( false !== $api_response ) 126 | $_data = $api_response; 127 | 128 | return $_data; 129 | } 130 | 131 | /** 132 | * Calls the API and, if successfull, returns the object delivered by the API. 133 | * 134 | * @uses wp_remote_post() 135 | * @uses is_wp_error() 136 | * 137 | * @param string $_action The requested action. 138 | * @param array $_data Parameters for the API action. 139 | * @return false||object 140 | */ 141 | private function api_request( $_action, $_data ) { 142 | 143 | $data = array_merge( $this->api_data, $_data ); 144 | 145 | // make sure we're checking the right thing 146 | if( ! isset( $data['slug'] ) || $data['slug'] != $this->slug ) 147 | return; 148 | 149 | // check for array elements, bail if missing 150 | if ( ! isset( $data['unique'] ) || ! isset( $data['version'] ) ) 151 | return; 152 | 153 | // build array 154 | $api_args = array( 155 | 'method' => 'POST', 156 | 'timeout' => 15, 157 | 'sslverify' => false, 158 | 'body' => array( 159 | 'action' => $_action, 160 | 'unique' => $data['unique'], 161 | 'version' => $data['version'], 162 | 'slug' => $data['slug'], 163 | ), 164 | ); 165 | 166 | // send request 167 | $request = wp_remote_post( $this->api_url, $api_args ); 168 | 169 | // bail if my request errors out 170 | if ( is_wp_error( $request ) ) 171 | return false; 172 | 173 | // bail if my request can't connect 174 | if ( ! isset( $request['response'] ) || $request['response']['code'] != 200 ) 175 | return false; 176 | 177 | // decode the JSON I sent back and make sure it's an array instead of object 178 | $response = json_decode( wp_remote_retrieve_body( $request ), true ); 179 | 180 | // bail if I don't have an array, or if its empty 181 | if ( ! is_array( $response ) || is_array( $response ) && empty( $response ) ) 182 | return false; 183 | 184 | // bail if the success is false 185 | if ( ! isset( $response['success'] ) || ! $response['success'] ) 186 | return false; 187 | 188 | // bail if the success is false 189 | if ( ! isset( $response['fields'] ) || isset( $response['fields'] ) && empty( $response['fields'] ) ) 190 | return false; 191 | 192 | // now run the update return based on request 193 | $updates = false; 194 | 195 | // build and return the basic info 196 | if ( $_action == 'plugin_latest_version' ) 197 | $updates = self::get_version_response( $response['fields'] ); 198 | 199 | // build the larger return for updates 200 | if ( $_action == 'plugin_information' ) 201 | $updates = self::get_information_response( $response['fields'] ); 202 | 203 | // bail if we have nothing 204 | if ( ! $updates ) 205 | return false; 206 | 207 | // send it back 208 | return $updates; 209 | 210 | } 211 | 212 | /** 213 | * [get_version_response description] 214 | * @param [type] $response [description] 215 | * @return [type] [description] 216 | */ 217 | static function get_version_response( $response ) { 218 | 219 | // Create response data object 220 | $updates = new stdClass; 221 | 222 | $updates->slug = $response['slug']; 223 | $updates->new_version = $response['new_version']; 224 | $updates->url = $response['url']; 225 | $updates->package = $response['package']; 226 | 227 | return $updates; 228 | 229 | } 230 | 231 | /** 232 | * [get_information_response description] 233 | * @param [type] $response [description] 234 | * @return [type] [description] 235 | */ 236 | static function get_information_response( $response ) { 237 | 238 | // Create response data object 239 | $updates = new stdClass; 240 | 241 | // build out our huge goddamn array 242 | $updates->name = $response['name']; 243 | $updates->slug = $response['slug']; 244 | $updates->version = $response['version']; 245 | $updates->author = $response['author']; 246 | $updates->author_profile = $response['author_profile']; 247 | $updates->contributors = $response['contributors']; 248 | $updates->requires = $response['requires']; 249 | $updates->tested = $response['tested']; 250 | $updates->rating = $response['rating']; 251 | $updates->num_ratings = $response['num_ratings']; 252 | $updates->downloaded = $response['downloaded']; 253 | $updates->last_updated = $response['last_updated']; 254 | $updates->added = $response['added']; 255 | $updates->homepage = $response['homepage']; 256 | $updates->download_link = $response['download_link']; 257 | $updates->sections = $response['sections']; 258 | 259 | return $updates; 260 | 261 | } 262 | 263 | /** 264 | * [update_remote_count description] 265 | * @return [type] [description] 266 | */ 267 | public function update_remote_count( $data ) { 268 | 269 | // build array 270 | $api_args = array( 271 | 'method' => 'POST', 272 | 'timeout' => 15, 273 | 'sslverify' => false, 274 | 'body' => array( 275 | 'action' => 'update_counts', 276 | 'unique' => $data['unique'], 277 | 'version' => $data['version'], 278 | 'slug' => $data['slug'], 279 | ), 280 | ); 281 | 282 | // send request 283 | $request = wp_remote_post( $this->api_url, $api_args ); 284 | 285 | // bail now, because we don't really care what the outcome was 286 | return; 287 | 288 | } 289 | 290 | /** 291 | * [run_remote_count description] 292 | * @param [type] $true [description] 293 | * @param [type] $hook_extra [description] 294 | * @param [type] $result [description] 295 | * @return [type] [description] 296 | */ 297 | public function run_remote_count( $true, $hook_extra, $result ) { 298 | 299 | // first check our bypass filter 300 | if ( false === apply_filters( 'rkv_remote_repo_update_count', true ) ) 301 | return $result; 302 | 303 | // make sure there's data to check 304 | if ( ! isset( $hook_extra ) ) 305 | return $result; 306 | 307 | // make sure we're dealing with our plugin 308 | if ( ! isset( $hook_extra['plugin'] ) || $hook_extra['plugin'] != $this->name ) 309 | return $result; 310 | 311 | // build our data array 312 | $data = array( 313 | 'unique' => $this->unique, 314 | 'version' => $this->version, 315 | 'slug' => $this->slug, 316 | ); 317 | 318 | // run our callback counter 319 | $this->update_remote_count( $data ); 320 | 321 | return $result; 322 | 323 | } 324 | 325 | /** 326 | * Disable request to wp.org plugin repository 327 | * this function is to remove update request data of this plugin to wp.org 328 | * so wordpress would not do update check for this plugin. 329 | * 330 | * @link http://markjaquith.wordpress.com/2009/12/14/excluding-your-plugin-or-theme-from-update-checks/ 331 | * @since 0.1.2 332 | */ 333 | public function disable_wporg( $r, $url ){ 334 | 335 | /* WP.org plugin update check URL */ 336 | $wp_url_string = 'api.wordpress.org/plugins/update-check'; 337 | 338 | /* If it's not a plugin update check request, bail early */ 339 | if ( false === strpos( $url, $wp_url_string ) ) 340 | return $r; 341 | 342 | 343 | /* Get this plugin slug */ 344 | $plugin_slug = dirname( $this->slug ); 345 | 346 | /* Get response body (json/serialize data) */ 347 | $r_body = wp_remote_retrieve_body( $r ); 348 | 349 | /* Get plugins request */ 350 | $r_plugins = ''; 351 | $r_plugins_json = false; 352 | if( isset( $r_body['plugins'] ) ) { 353 | 354 | /* Check if data can be serialized */ 355 | if ( is_serialized( $r_body['plugins'] ) ) { 356 | 357 | /* unserialize data ( PRE WP 3.7 ) */ 358 | $r_plugins = @unserialize( $r_body['plugins'] ); 359 | $r_plugins = (array) $r_plugins; // convert object to array 360 | } 361 | 362 | /* if unserialize didn't work ( POST WP.3.7 using json ) */ 363 | else { 364 | /* use json decode to make body request to array */ 365 | $r_plugins = json_decode( $r_body['plugins'], true ); 366 | $r_plugins_json = true; 367 | } 368 | } 369 | 370 | /* this plugin */ 371 | $to_disable = ''; 372 | 373 | /* check if plugins request is not empty */ 374 | if ( !empty( $r_plugins ) ) { 375 | 376 | /* All plugins */ 377 | $all_plugins = $r_plugins['plugins']; 378 | 379 | /* Loop all plugins */ 380 | foreach ( $all_plugins as $plugin_base => $plugin_data ){ 381 | 382 | /* Only if the plugin have the same folder, because plugins can have different main file. */ 383 | if ( dirname( $plugin_base ) == $plugin_slug ){ 384 | 385 | /* get plugin to disable */ 386 | $to_disable = $plugin_base; 387 | } 388 | } 389 | 390 | /* Unset this plugin only */ 391 | if ( !empty( $to_disable ) ){ 392 | unset( $all_plugins[ $to_disable ] ); 393 | } 394 | 395 | /* Merge plugins request back to request */ 396 | if ( true === $r_plugins_json ){ // json encode data 397 | $r_plugins['plugins'] = $all_plugins; 398 | $r['body']['plugins'] = json_encode( $r_plugins ); 399 | } 400 | else{ // serialize data 401 | $r_plugins['plugins'] = $all_plugins; 402 | $r_plugins_object = (object) $r_plugins; 403 | $r['body']['plugins'] = serialize( $r_plugins_object ); 404 | } 405 | } 406 | 407 | /* return the request */ 408 | return $r; 409 | } 410 | 411 | } -------------------------------------------------------------------------------- /sample-plugin/sample-plugin.php: -------------------------------------------------------------------------------- 1 | RKV_ITEM, 32 | 'version' => RKV_VERS, 33 | ) 34 | ); 35 | } --------------------------------------------------------------------------------