├── README.md ├── admin ├── class-tux-su-license-rules-list.php ├── class-tux-su-licenses-list.php ├── class-tux-su-tracking-list.php ├── css │ ├── images │ │ ├── ui-icons_444444_256x240.png │ │ ├── ui-icons_555555_256x240.png │ │ ├── ui-icons_777620_256x240.png │ │ ├── ui-icons_777777_256x240.png │ │ ├── ui-icons_cc0000_256x240.png │ │ └── ui-icons_ffffff_256x240.png │ └── jquery-ui.min.css ├── tuxedo-su-admin-license-rules.php ├── tuxedo-su-admin-licenses.php ├── tuxedo-su-admin-settings.php ├── tuxedo-su-admin-tools.php ├── tuxedo-su-admin-tracking.php ├── tuxedo-su-admin-update-key.php └── tuxedo-su-admin.php ├── tuxedo-software-updater.php ├── tuxedo-su-database.php ├── tuxedo-su-edd.php ├── tuxedo-su-rest-api.php ├── tuxedo-su-update-key.php └── tuxedo-su-woo.php /README.md: -------------------------------------------------------------------------------- 1 | # Tuxedo Software Update Licensing 2 | 3 | TSUL is a WordPress plugin for managing software update licenses. It works as a standalone plugin and also has WooCommerce and Easy Digital Downloads integration. An updater plugin generator is included to create a client side update plugin for updating WordPress themes and plugins. 4 | 5 | While this is a 1.0 release, there is still much to be completed. Tracking is limited, and at the moment only collects and displays the raw data. It would be nice to compile this tracking data into more usable metrics. 6 | 7 | Licenses are generated per user per order, but the system will use the first available and active license it can find. Both acitvation limits and expirations are supported, if using WooCommerce or Easy Digital Downloads these can be adjusted per variable product. 8 | 9 | TSUL identifies users with an *update key*. This key is automatically generated for the user when they access their licensing information on the front-end for either WooCommerce or Easy Digital Downloads. WooCommerce update licensing information is available to customers under their *My Account* page. Easy Digital Downloads update licensing information can be placed on any page using the shortcode [update_licenses]. Additional update key options are provided to adiministrators under the edit user page in the WordPress admin. 10 | 11 | All updates are managed by creating a license rule. These rules tell the system how to handle an update request, such as if a license is required and where the update file is located. 12 | 13 | The *Automatic Update* option signals to the client WordPress install that this update should be done automatically. 14 | 15 | The *Open Update* option controls whether the update requires a license or not. If selected then the update information can be retrieved without an update key and no licensing check will be performed. When used with an e-Commerce solution this will disable automatic license generation for the product. 16 | 17 | The *Product ID* option is a numeric ID used to identify the update, and when using an e-Commerce solution this ID should match the ID of the product in that system. This will allow TSUL to identify a product through WooCommerce or Easy Digital Downloads and automatically generate a license for it at the time of purchase. 18 | 19 | If the product is a *variable product* (known as *child products* in TSUL), editing the license rule after creating or saving will display any variable product information. Different *activation limits* and *expiries* can be set for these. 20 | 21 | ### Update Plugin Generator 22 | 23 | Included under the *Tools* menu is an update plugin creator, which will generate a plugin for your customers that will handle plugin and theme updates. The *Header ID* option is used to identify products (using numerical IDs) in headers of themes and plugins. An example would be "Tuxedo Update ID". Say you have created an update rule with a *Product ID* of 999. To identify this to the update plugin your theme or plugin header information should include "Tuxedo Update ID: 999". 24 | 25 | Example for a plugin would look like: 26 | 27 | 'rule', 27 | 'plural' => 'rules', 28 | 'ajax' => false, 29 | ) ); 30 | 31 | } 32 | 33 | /** 34 | * No items output. 35 | * 36 | * @since 1.0.0 37 | */ 38 | public function no_items() { 39 | 40 | esc_html_e( 'No license rules found.', 'tuxedo-software-updater' ); 41 | 42 | } 43 | 44 | /** 45 | * Get columns for list table. 46 | * 47 | * @since 1.0.0 48 | * 49 | * @return array Columns. 50 | */ 51 | public function get_columns() { 52 | 53 | return array( 54 | 'cb' => '', 55 | 'product_id' => __( 'Product ID', 'tuxedo-software-updater' ), 56 | 'file_url' => __( 'File URL', 'tuxedo-software-updater' ), 57 | 'version' => __( 'Version', 'tuxedo-software-updater' ), 58 | 'open_update' => __( 'Open Update', 'tuxedo-software-updater' ), 59 | 'activation_limit' => __( 'Activation Limit', 'tuxedo-software-updater' ), 60 | 'expiry' => __( 'Expiry', 'tuxedo-software-updater' ), 61 | 'created' => __( 'Created', 'tuxedo-software-updater' ), 62 | 'modified' => __( 'Modified', 'tuxedo-software-updater' ), 63 | ); 64 | 65 | } 66 | 67 | /** 68 | * Get record count from database. 69 | * 70 | * @since 1.0.0 71 | * 72 | * @return null|string Count. 73 | */ 74 | public static function record_count() { 75 | 76 | if ( ! empty( $_GET['s'] ) ) { 77 | 78 | return tux_su_get_db( array( 79 | 'product_id' => $_GET['s'], 80 | 'info' => $_GET['s'], 81 | 'type' => 'rule', 82 | 'count' => true, 83 | 'search' => true, 84 | ) ); 85 | 86 | } else { 87 | 88 | return tux_su_get_db( array( 89 | 'type' => 'rule', 90 | 'count' => true, 91 | ) ); 92 | 93 | } 94 | } 95 | 96 | /** 97 | * Default column output. 98 | * 99 | * @since 1.0.0 100 | * 101 | * @param array $item List table item. 102 | * @param string $column_name Column name. 103 | * 104 | * @return string Output. 105 | */ 106 | public function column_default( $item, $column_name ) { 107 | 108 | return $item[ $column_name ]; 109 | 110 | } 111 | 112 | /** 113 | * Check box column output. 114 | * 115 | * @since 1.0.0 116 | * 117 | * @param array $item List table item. 118 | * 119 | * @return string Output. 120 | */ 121 | public function column_cb( $item ) { 122 | 123 | return sprintf( '', $item['id'] ); 124 | 125 | } 126 | 127 | /** 128 | * Product id column output. 129 | * 130 | * @since 1.0.0 131 | * 132 | * @param array $item List table item. 133 | * 134 | * @return string Output. 135 | */ 136 | public function column_product_id( $item ) { 137 | 138 | $delete_nonce = wp_create_nonce( 'tux_delete_license_rule' ); 139 | 140 | $title = '' . $item['product_id'] . ' - ' . $item['product_name']; 141 | 142 | $actions = array( 143 | 'id' => 'ID: ' . $item['id'], 144 | 'edit' => sprintf( '' . __( 'Edit', 'tuxedo-software-updater' ) . '', esc_attr( $_REQUEST['page'] ), 'edit', absint( $item['id'] ) ), 145 | 'delete' => sprintf( '' . __( 'Delete', 'tuxedo-software-updater' ) . '', esc_attr( $_REQUEST['page'] ), 'delete', absint( $item['id'] ), $delete_nonce, esc_attr__( 'Delete this license rule?', 'tuxedo-software-updater' ) ), 146 | 'licenses' => sprintf( '' . __( 'Licenses' ) . '', $item['product_id'] ), 147 | ); 148 | 149 | return $title . $this->row_actions( $actions ); 150 | 151 | } 152 | 153 | /** 154 | * Get sortable columns. 155 | * 156 | * @since 1.0.0 157 | * 158 | * @return array Columns. 159 | */ 160 | public function get_sortable_columns() { 161 | 162 | return array( 163 | 'product_id' => array( 'product_id', false ), 164 | 'created' => array( 'created', false ), 165 | 'modified' => array( 'modified', true ), 166 | ); 167 | 168 | } 169 | 170 | /** 171 | * Get bulk actions. 172 | * 173 | * @since 1.0.0 174 | * 175 | * @return array Bulk actions. 176 | */ 177 | public function get_bulk_actions() { 178 | 179 | return array( 180 | 'bulk-delete' => __( 'Delete', 'tuxedo-software-updater' ), 181 | ); 182 | 183 | } 184 | 185 | /** 186 | * Prepare items for display. 187 | * 188 | * @since 1.0.0 189 | */ 190 | public function prepare_items() { 191 | 192 | $this->_column_headers = array( 193 | $this->get_columns(), 194 | array(), 195 | $this->get_sortable_columns(), 196 | 'product_id', 197 | ); 198 | 199 | $this->set_pagination_args( array( 200 | 'total_items' => Tux_SU_License_Rules_List::record_count(), 201 | 'per_page' => 20, 202 | ) ); 203 | 204 | if ( isset( $_GET['orderby'] ) && ( in_array( $_GET['orderby'], array( 'created', 'modified', 'product_id', ) ) ) ) { 205 | 206 | $order = $_GET['orderby']; 207 | 208 | } else { 209 | 210 | $order = 'modified'; 211 | 212 | } 213 | 214 | if ( isset( $_GET['order'] ) && ( 'asc' === $_GET['order'] || 'desc' === $_GET['order'] ) ) { 215 | 216 | $order .= ' ' . $_GET['order']; 217 | 218 | } else { 219 | 220 | $order .= ' desc'; 221 | 222 | } 223 | 224 | if ( ! empty( $_GET['s'] ) ) { 225 | 226 | $items = tux_su_get_db( array( 227 | 'product_id' => $_GET['s'], 228 | 'info' => $_GET['s'], 229 | 'type' => 'rule', 230 | 'order' => $order, 231 | 'limit' => 20, 232 | 'offset' => ( $this->get_pagenum() - 1 ) * 20, 233 | 'search' => true, 234 | ) ); 235 | 236 | } else { 237 | 238 | if ( isset( $_GET['product_id'] ) ) { 239 | 240 | $items = tux_su_get_db( array( 241 | 'product_id' => $_GET['product_id'], 242 | 'type' => 'rule', 243 | 'order' => $order, 244 | 'limit' => 20, 245 | 'offset' => ( $this->get_pagenum() - 1 ) * 20, 246 | ) ); 247 | 248 | 249 | } else { 250 | 251 | $items = tux_su_get_db( array( 252 | 'type' => 'rule', 253 | 'order' => $order, 254 | 'limit' => 20, 255 | 'offset' => ( $this->get_pagenum() - 1 ) * 20, 256 | ) ); 257 | 258 | } 259 | } 260 | 261 | foreach ( $items as $item ) { 262 | 263 | $info = array(); 264 | 265 | if ( isset( $item['info'] ) ) { 266 | 267 | $info = maybe_unserialize( $item['info'] ); 268 | 269 | } 270 | 271 | $this->items[] = array( 272 | 'id' => isset( $item['id'] ) ? $item['id'] : '', 273 | 'product_name' => isset( $info['product_name'] ) ? $info['product_name'] : '', 274 | 'product_id' => isset( $item['product_id'] ) ? $item['product_id'] : '', 275 | 'file_url' => isset( $info['file_url'] ) ? $info['file_url'] : '', 276 | 'version' => isset( $info['version'] ) ? $info['version'] : '', 277 | 'open_update' => isset( $info['open_update'] ) ? ( 1 === $info['open_update'] ? '' . __( 'open', 'tuxedo-software-updater' ) . '' : __( 'closed', 'tuxedo-software-updater' ) ) : __( 'closed', 'tuxedo-software-updater' ), 278 | 'activation_limit' => isset( $info['activation_limit'] ) ? ( empty( $info['activation_limit'] ) ? __( 'unlimited', 'tuxedo-software-updater' ) : $info['activation_limit'] ) : '', 279 | 'expiry' => isset( $info['expiry'] ) ? ( empty( $info['expiry'] ) ? __( 'never', 'tuxedo-software-updater' ) : $info['expiry'] . ' ' . __( 'days', 'tuxedo-software-updater' ) ) : '', 280 | 'created' => isset( $item['created'] ) ? date( 'M j, Y', strtotime( $item['created'] ) ) : '', 281 | 'modified' => isset( $item['modified'] ) ? date( 'M j, Y', strtotime( $item['modified'] ) ) . '
' . date( 'g:i a', strtotime( $item['modified'] ) ) : '', 282 | ); 283 | 284 | } 285 | 286 | } 287 | 288 | } 289 | -------------------------------------------------------------------------------- /admin/class-tux-su-licenses-list.php: -------------------------------------------------------------------------------- 1 | 'license', 27 | 'plural' => 'licenses', 28 | 'ajax' => false, 29 | ) ); 30 | 31 | } 32 | 33 | /** 34 | * No items output. 35 | * 36 | * @since 1.0.0 37 | */ 38 | public function no_items() { 39 | 40 | esc_html_e( 'No licenses found.', 'tuxedo-software-updater' ); 41 | 42 | } 43 | 44 | /** 45 | * Get columns for list table. 46 | * 47 | * @since 1.0.0 48 | * 49 | * @return array Columns. 50 | */ 51 | public function get_columns() { 52 | 53 | return array( 54 | 'cb' => '', 55 | 'product_id' => __( 'Product ID', 'tuxedo-software-updater' ), 56 | 'user_id' => __( 'User ID', 'tuxedo-software-updater' ), 57 | 'order_id' => __( 'Order ID', 'tuxedo-software-updater' ), 58 | 'activations' => __( 'Activations', 'tuxedo-software-updater' ), 59 | 'expires' => __( 'Expires', 'tuxedo-software-updater' ), 60 | 'created' => __( 'Created', 'tuxedo-software-updater' ), 61 | 'modified' => __( 'Modified', 'tuxedo-software-updater' ), 62 | ); 63 | 64 | } 65 | 66 | /** 67 | * Get record count from database. 68 | * 69 | * @since 1.0.0 70 | * 71 | * @return null|string Count. 72 | */ 73 | public static function record_count() { 74 | 75 | if ( ! empty( $_GET['s'] ) ) { 76 | 77 | return tux_su_get_db( array( 78 | 'user_id' => $_GET['s'], 79 | 'product_id' => $_GET['s'], 80 | 'info' => $_GET['s'], 81 | 'type' => 'license', 82 | 'count' => true, 83 | 'search' => true, 84 | ) ); 85 | 86 | } else { 87 | 88 | return tux_su_get_db( array( 89 | 'type' => 'license', 90 | 'count' => true, 91 | ) ); 92 | 93 | } 94 | } 95 | 96 | /** 97 | * Default column output. 98 | * 99 | * @since 1.0.0 100 | * 101 | * @param array $item List table item. 102 | * @param string $column_name Column name. 103 | * 104 | * @return string Output. 105 | */ 106 | public function column_default( $item, $column_name ) { 107 | 108 | return $item[ $column_name ]; 109 | 110 | } 111 | 112 | /** 113 | * Check box column output. 114 | * 115 | * @since 1.0.0 116 | * 117 | * @param array $item List table item. 118 | * 119 | * @return string Output. 120 | */ 121 | public function column_cb( $item ) { 122 | 123 | return sprintf( '', $item['id'] ); 124 | 125 | } 126 | 127 | /** 128 | * Product id column output. 129 | * 130 | * @since 1.0.0 131 | * 132 | * @param array $item List table item. 133 | * 134 | * @return string Output. 135 | */ 136 | public function column_product_id( $item ) { 137 | 138 | $delete_nonce = wp_create_nonce( 'tux_delete_license' ); 139 | 140 | $title = '' . $item['product_id'] . ' - ' . $item['product_name']; 141 | 142 | $actions = array( 143 | 'id' => 'ID: ' . $item['id'], 144 | 'edit' => sprintf( '' . __( 'Edit', 'tuxedo-software-updater' ) . '', esc_attr( $_REQUEST['page'] ), 'edit', absint( $item['id'] ) ), 145 | 'delete' => sprintf( '' . __( 'Delete', 'tuxedo-software-updater' ) . '', esc_attr( $_REQUEST['page'] ), 'delete', absint( $item['id'] ), $delete_nonce, esc_attr__( 'Delete this license?', 'tuxedo-software-updater' ) ), 146 | 'license_rule' => sprintf( '' . __( 'License Rules' ) . '', $item['product_id'] ), 147 | ); 148 | 149 | return $title . $this->row_actions( $actions ); 150 | 151 | } 152 | 153 | /** 154 | * User id column output. 155 | * 156 | * @since 1.0.0 157 | * 158 | * @param array $item List table item. 159 | * 160 | * @return string Output. 161 | */ 162 | public function column_user_id( $item ) { 163 | 164 | return '' . $item['user_id'] . ' - ' . $item['user_name']; 165 | 166 | } 167 | 168 | /** 169 | * Get sortable columns. 170 | * 171 | * @since 1.0.0 172 | * 173 | * @return array Columns. 174 | */ 175 | public function get_sortable_columns() { 176 | 177 | return array( 178 | 'product_id' => array( 'product_id', false ), 179 | 'user_id' => array( 'user_id', false ), 180 | 'created' => array( 'created', false ), 181 | 'modified' => array( 'modified', true ), 182 | ); 183 | 184 | } 185 | 186 | /** 187 | * Get bulk actions. 188 | * 189 | * @since 1.0.0 190 | * 191 | * @return array Bulk actions. 192 | */ 193 | public function get_bulk_actions() { 194 | 195 | return array( 196 | 'bulk-delete' => __( 'Delete', 'tuxedo-software-updater' ), 197 | ); 198 | 199 | } 200 | 201 | /** 202 | * Prepare items for display. 203 | * 204 | * @since 1.0.0 205 | */ 206 | public function prepare_items() { 207 | 208 | $this->_column_headers = array( 209 | $this->get_columns(), 210 | array(), 211 | $this->get_sortable_columns(), 212 | 'product_id', 213 | ); 214 | 215 | $this->set_pagination_args( array( 216 | 'total_items' => Tux_SU_Licenses_List::record_count(), 217 | 'per_page' => 20, 218 | ) ); 219 | 220 | if ( isset( $_GET['orderby'] ) && ( in_array( $_GET['orderby'], array( 'created', 'modified', 'user_id', 'product_id', ) ) ) ) { 221 | 222 | $order = $_GET['orderby']; 223 | 224 | } else { 225 | 226 | $order = 'modified'; 227 | 228 | } 229 | 230 | if ( isset( $_GET['order'] ) && ( 'asc' === $_GET['order'] || 'desc' === $_GET['order'] ) ) { 231 | 232 | $order .= ' ' . $_GET['order']; 233 | 234 | } else { 235 | 236 | $order .= ' desc'; 237 | 238 | } 239 | 240 | if ( ! empty( $_GET['s'] ) ) { 241 | 242 | $items = tux_su_get_db( array( 243 | 'user_id' => $_GET['s'], 244 | 'product_id' => $_GET['s'], 245 | 'info' => $_GET['s'], 246 | 'type' => 'license', 247 | 'order' => $order, 248 | 'limit' => 20, 249 | 'offset' => ( $this->get_pagenum() - 1 ) * 20, 250 | 'search' => true, 251 | ) ); 252 | 253 | } else { 254 | 255 | if ( isset( $_GET['product_id'] ) ) { 256 | 257 | $items = tux_su_get_db( array( 258 | 'product_id' => $_GET['product_id'], 259 | 'type' => 'license', 260 | 'order' => $order, 261 | 'limit' => 20, 262 | 'offset' => ( $this->get_pagenum() - 1 ) * 20, 263 | ) ); 264 | 265 | 266 | } else { 267 | 268 | $items = tux_su_get_db( array( 269 | 'type' => 'license', 270 | 'order' => $order, 271 | 'limit' => 20, 272 | 'offset' => ( $this->get_pagenum() - 1 ) * 20, 273 | ) ); 274 | 275 | } 276 | } // End if(). 277 | 278 | $product_ids = array(); 279 | 280 | foreach ( $items as $item ) { 281 | 282 | if ( ! empty( $item['product_id'] ) ) { 283 | 284 | $product_ids[] = $item['product_id']; 285 | 286 | } 287 | } 288 | 289 | $license_rules = array(); 290 | 291 | if ( ! empty( $product_ids ) ) { 292 | 293 | $license_rules = tux_su_get_db( array( 294 | 'product_id' => $product_ids, 295 | 'type' => 'rule', 296 | ) ); 297 | 298 | } 299 | 300 | foreach ( $items as $item ) { 301 | 302 | $info = array(); 303 | 304 | if ( isset( $item['info'] ) ) { 305 | 306 | $info = maybe_unserialize( $item['info'] ); 307 | 308 | } 309 | 310 | $product_name = ''; 311 | $activation_limit = 0; 312 | $expiry = 0; 313 | 314 | foreach ( $license_rules as $license_rule ) { 315 | 316 | if ( $license_rule['product_id'] === $item['product_id'] ) { 317 | 318 | $license_rule['info'] = maybe_unserialize( $license_rule['info'] ); 319 | 320 | if ( isset( $license_rule['info']['product_name'] ) ) { 321 | 322 | $product_name = $license_rule['info']['product_name']; 323 | 324 | } 325 | 326 | if ( ! empty( $info['child_id'] ) ) { 327 | 328 | if ( ! empty( $license_rule['info']['children'][ $info['child_id'] ]['name'] ) ) { 329 | 330 | $product_name .= ' ' . $license_rule['info']['children'][ $info['child_id'] ]['name']; 331 | 332 | } 333 | 334 | if ( isset( $license_rule['info']['children'][ $info['child_id'] ]['activation_limit'] ) ) { 335 | 336 | $activation_limit = $license_rule['info']['children'][ $info['child_id'] ]['activation_limit']; 337 | 338 | } 339 | 340 | if ( isset( $license_rule['info']['children'][ $info['child_id'] ]['expiry'] ) ) { 341 | 342 | $expiry = $license_rule['info']['children'][ $info['child_id'] ]['expiry']; 343 | 344 | } 345 | } else { 346 | 347 | if ( isset( $license_rule['info']['activation_limit'] ) ) { 348 | 349 | $activation_limit = $license_rule['info']['activation_limit']; 350 | 351 | } 352 | 353 | if ( isset( $license_rule['info']['expiry'] ) ) { 354 | 355 | $expiry = $license_rule['info']['expiry']; 356 | 357 | } 358 | } 359 | 360 | break; 361 | 362 | } // End if(). 363 | } // End foreach(). 364 | 365 | $this->items[] = array( 366 | 'id' => isset( $item['id'] ) ? $item['id'] : '', 367 | 'user_id' => isset( $item['user_id'] ) ? $item['user_id'] : '', 368 | 'user_name' => isset( $info['user_name'] ) ? $info['user_name'] : '', 369 | 'product_name' => $product_name, 370 | 'product_id' => isset( $item['product_id'] ) ? $item['product_id'] : '', 371 | 'order_id' => isset( $info['order_id'] ) ? $info['order_id'] : '', 372 | 'activations' => empty( $activation_limit ) ? __( 'unlimited', 'tuxedo-software-updater' ) : count( isset( $info['activations'] ) ? $info['activations'] : array() ) . ' / ' . $activation_limit, 373 | 'expires' => isset( $item['created'] ) ? ( empty( $expiry ) ? __( 'never', 'tuxedo-software-updater' ) : date( 'M j, Y', strtotime( $item['created'] . ' + ' . $expiry . ' days' ) ) ) : __( 'error', 'tuxedo-software-updater' ), 374 | 'created' => isset( $item['created'] ) ? date( 'M j, Y', strtotime( $item['created'] ) ) : '', 375 | 'modified' => isset( $item['modified'] ) ? date( 'M j, Y', strtotime( $item['modified'] ) ) . '
' . date( 'g:i a', strtotime( $item['modified'] ) ) : '', 376 | ); 377 | 378 | } 379 | 380 | } 381 | 382 | } 383 | -------------------------------------------------------------------------------- /admin/class-tux-su-tracking-list.php: -------------------------------------------------------------------------------- 1 | 'tracking', 27 | 'plural' => 'trackings', 28 | 'ajax' => false, 29 | ) ); 30 | 31 | } 32 | 33 | /** 34 | * No items output. 35 | * 36 | * @since 1.0.0 37 | */ 38 | public function no_items() { 39 | 40 | esc_html_e( 'No tracking found.', 'tuxedo-software-updater' ); 41 | 42 | } 43 | 44 | /** 45 | * Get columns for list table. 46 | * 47 | * @since 1.0.0 48 | * 49 | * @return array Columns. 50 | */ 51 | public function get_columns() { 52 | 53 | return array( 54 | 'cb' => '', 55 | 'user_id' => __( 'User ID', 'tuxedo-software-updater' ), 56 | 'ip_id' => __( 'IP / ID', 'tuxedo-software-updater' ), 57 | 'user_agent' => __( 'User Agent', 'tuxedo-software-updater' ), 58 | 'request_response' => __( 'Request / Response', 'tuxedo-software-updater' ), 59 | 'created' => __( 'Created', 'tuxedo-software-updater' ), 60 | ); 61 | 62 | } 63 | 64 | /** 65 | * Get record count from database. 66 | * 67 | * @since 1.0.0 68 | * 69 | * @return null|string Count. 70 | */ 71 | public static function record_count() { 72 | 73 | if ( ! empty( $_GET['s'] ) ) { 74 | 75 | return tux_su_get_db( array( 76 | 'user_id' => $_GET['s'], 77 | 'info' => $_GET['s'], 78 | 'count' => true, 79 | 'search' => true, 80 | ), 'tracking' ); 81 | 82 | } else { 83 | 84 | return tux_su_get_db( array( 85 | 'count' => true, 86 | ), 'tracking' ); 87 | 88 | } 89 | } 90 | 91 | /** 92 | * Default column output. 93 | * 94 | * @since 1.0.0 95 | * 96 | * @param array $item List table item. 97 | * @param string $column_name Column name. 98 | * 99 | * @return string Output. 100 | */ 101 | public function column_default( $item, $column_name ) { 102 | 103 | return $item[ $column_name ]; 104 | 105 | } 106 | 107 | /** 108 | * Check box column output. 109 | * 110 | * @since 1.0.0 111 | * 112 | * @param array $item List table item. 113 | * 114 | * @return string Output. 115 | */ 116 | public function column_cb( $item ) { 117 | 118 | return sprintf( '', $item['id'] ); 119 | 120 | } 121 | 122 | /** 123 | * User id column output. 124 | * 125 | * @since 1.0.0 126 | * 127 | * @param array $item List table item. 128 | * 129 | * @return string Output. 130 | */ 131 | public function column_user_id( $item ) { 132 | 133 | $delete_nonce = wp_create_nonce( 'tux_delete_tracking' ); 134 | 135 | $title = '' . $item['user_id'] . ' - ' . $item['user_name']; 136 | 137 | $actions = array( 138 | 'id' => 'ID: ' . $item['id'], 139 | 'delete' => sprintf( '' . __( 'Delete', 'tuxedo-software-updater' ) . '', esc_attr( $_REQUEST['page'] ), 'delete', absint( $item['id'] ), $delete_nonce, esc_attr__( 'Delete this tracking data?', 'tuxedo-software-updater' ) ), 140 | ); 141 | 142 | return $title . $this->row_actions( $actions ); 143 | 144 | } 145 | 146 | /** 147 | * Get sortable columns. 148 | * 149 | * @since 1.0.0 150 | * 151 | * @return array Columns. 152 | */ 153 | public function get_sortable_columns() { 154 | 155 | return array( 156 | 'user_id' => array( 'user_id', false ), 157 | 'created' => array( 'created', false ), 158 | ); 159 | 160 | } 161 | 162 | /** 163 | * Get bulk actions. 164 | * 165 | * @since 1.0.0 166 | * 167 | * @return array Bulk actions. 168 | */ 169 | public function get_bulk_actions() { 170 | 171 | return array( 172 | 'bulk-delete' => __( 'Delete', 'tuxedo-software-updater' ), 173 | ); 174 | 175 | } 176 | 177 | /** 178 | * Prepare items for display. 179 | * 180 | * @since 1.0.0 181 | */ 182 | public function prepare_items() { 183 | 184 | $this->_column_headers = array( 185 | $this->get_columns(), 186 | array(), 187 | $this->get_sortable_columns(), 188 | 'user_id', 189 | ); 190 | 191 | $this->set_pagination_args( array( 192 | 'total_items' => Tux_SU_Tracking_List::record_count(), 193 | 'per_page' => 20, 194 | ) ); 195 | 196 | if ( isset( $_GET['orderby'] ) && ( in_array( $_GET['orderby'], array( 'created', 'user_id' ) ) ) ) { 197 | 198 | $order = $_GET['orderby']; 199 | 200 | } else { 201 | 202 | $order = 'created'; 203 | 204 | } 205 | 206 | if ( isset( $_GET['order'] ) && ( 'asc' === $_GET['order'] || 'desc' === $_GET['order'] ) ) { 207 | 208 | $order .= ' ' . $_GET['order']; 209 | 210 | } else { 211 | 212 | $order .= ' desc'; 213 | 214 | } 215 | 216 | if ( ! empty( $_GET['s'] ) ) { 217 | 218 | $items = tux_su_get_db( array( 219 | 'user_id' => $_GET['s'], 220 | 'info' => $_GET['s'], 221 | 'order' => $order, 222 | 'limit' => 20, 223 | 'offset' => ($this->get_pagenum() - 1) * 20, 224 | 'search' => true, 225 | ), 'tracking' ); 226 | 227 | } else { 228 | 229 | $items = tux_su_get_db( array( 230 | 'order' => $order, 231 | 'limit' => 20, 232 | 'offset' => ($this->get_pagenum() - 1) * 20, 233 | ), 'tracking' ); 234 | 235 | } 236 | 237 | $user_ids = array(); 238 | 239 | foreach ( $items as $item ) { 240 | 241 | $user_ids[] = $item['user_id']; 242 | 243 | } 244 | 245 | $user_query = new WP_User_Query( array( 'include' => $user_ids, 'fields' => array( 'ID', 'user_login' ) ) ); 246 | 247 | $users = $user_query->get_results(); 248 | 249 | foreach ( $items as $item ) { 250 | 251 | $info = array(); 252 | 253 | if ( isset( $item['info'] ) ) { 254 | 255 | $info = maybe_unserialize( $item['info'] ); 256 | 257 | } 258 | 259 | $user_name = ''; 260 | 261 | foreach ( $users as $user ) { 262 | 263 | if ( absint( $user->ID ) === absint( $item['user_id'] ) ) { 264 | 265 | $user_name = $user->user_login; 266 | 267 | } 268 | } 269 | 270 | $this->items[] = array( 271 | 'id' => isset( $item['id'] ) ? $item['id'] : '', 272 | 'user_id' => isset( $item['user_id'] ) ? $item['user_id'] : '', 273 | 'user_name' => $user_name, 274 | 'ip_id' => ( isset( $info['ip'] ) ? $info['ip'] : '' ) . ( isset( $info['request']['activation_id'] ) ? '
' . $info['request']['activation_id'] : '' ), 275 | 'user_agent' => isset( $info['user_agent'] ) ? $info['user_agent'] : '', 276 | 'request_response' => '' . __( 'View', 'tuxedo-software-updater' ) . '', 278 | 'created' => isset( $item['created'] ) ? date( 'M j, Y', strtotime( $item['created'] ) ) . '
' . date( 'g:i a', strtotime( $item['created'] ) ) : '', 279 | ); 280 | 281 | } 282 | 283 | } 284 | 285 | } 286 | -------------------------------------------------------------------------------- /admin/css/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andtrev/Tuxedo-Software-Updater/746d0fb7fb4944ca169d916642bd1e0229ff6310/admin/css/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /admin/css/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andtrev/Tuxedo-Software-Updater/746d0fb7fb4944ca169d916642bd1e0229ff6310/admin/css/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /admin/css/images/ui-icons_777620_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andtrev/Tuxedo-Software-Updater/746d0fb7fb4944ca169d916642bd1e0229ff6310/admin/css/images/ui-icons_777620_256x240.png -------------------------------------------------------------------------------- /admin/css/images/ui-icons_777777_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andtrev/Tuxedo-Software-Updater/746d0fb7fb4944ca169d916642bd1e0229ff6310/admin/css/images/ui-icons_777777_256x240.png -------------------------------------------------------------------------------- /admin/css/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andtrev/Tuxedo-Software-Updater/746d0fb7fb4944ca169d916642bd1e0229ff6310/admin/css/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /admin/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andtrev/Tuxedo-Software-Updater/746d0fb7fb4944ca169d916642bd1e0229ff6310/admin/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /admin/css/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2017-06-18 2 | * http://jqueryui.com 3 | * Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif 5 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 6 | 7 | .ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} -------------------------------------------------------------------------------- /admin/tuxedo-su-admin-license-rules.php: -------------------------------------------------------------------------------- 1 | absint( $_GET['edit_id'] ), 31 | ) ); 32 | 33 | $license_rule = reset( $license_rule ); 34 | 35 | if ( isset( $license_rule['info'] ) ) { 36 | 37 | $license_rule['info'] = maybe_unserialize( $license_rule['info'] ); 38 | 39 | } 40 | } 41 | 42 | $list_table = new Tux_SU_License_Rules_List(); 43 | ?> 44 |
45 | 46 |

47 | 48 |

49 | 50 |

51 | 52 |

53 | 54 | 55 | 56 | 57 |
58 | 59 |

' . esc_html__( 'Error editing license rule.', 'tuxedo-software-updater' ) . '

'; 65 | echo ''; 66 | 67 | return; 68 | 69 | } 70 | } 71 | 72 | if ( isset( $_GET['updated'] ) && 'success' === $_GET['updated'] ) { 73 | 74 | echo '

' . esc_html__( 'Successfully updated license rule.', 'tuxedo-software-updater' ) . '

'; 75 | 76 | } 77 | 78 | if ( isset( $_GET['updated'] ) && 'error' === $_GET['updated'] ) { 79 | 80 | echo '

' . esc_html__( 'Error updating license rule.', 'tuxedo-software-updater' ) . '

'; 81 | 82 | } 83 | 84 | if ( isset( $_GET['deleted'] ) && 'success' === $_GET['deleted'] ) { 85 | 86 | echo '

' . esc_html__( 'Successfully deleted license rule(s).', 'tuxedo-software-updater' ) . '

'; 87 | 88 | } 89 | 90 | if ( isset( $_GET['deleted'] ) && 'error' === $_GET['deleted'] ) { 91 | 92 | echo '

' . esc_html__( 'Error deleting license rule(s).', 'tuxedo-software-updater' ) . '

'; 93 | 94 | } 95 | 96 | $_SERVER['REQUEST_URI'] = remove_query_arg( array( 97 | 'updated', 98 | 'deleted', 99 | '_wpnonce', 100 | '_wp_http_referer', 101 | ), $_SERVER['REQUEST_URI'] ); 102 | ?> 103 |
104 |
style="display:none;"> 105 |
106 |

107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |

115 |
116 |
118 |

119 |
122 |
123 | 126 |

127 |

128 |
131 | 132 |

133 |

134 |
137 | 138 |

139 |

140 | 141 | 145 |

146 |

147 | 148 |
152 | 153 | 154 | 155 |

156 |

157 |
160 | 161 |

162 |

163 |
166 |
167 | 168 | 169 | 170 |

171 |

172 |
175 |
176 |

177 |

178 |
181 |
182 | 183 | 184 | 185 |

186 |

187 |
190 |
191 | 192 | 193 | 194 |

195 | $license_rule['product_id'], 209 | 'type' => 'variation', 210 | ) ); 211 | 212 | $parent_product_name = get_the_title( $license_rule['product_id'] ); 213 | 214 | foreach ( $products as $product ) { 215 | 216 | $child_products[ $product->get_id() ]['name'] = str_replace( $parent_product_name, '', $product->get_name() ); 217 | $child_products[ $product->get_id() ]['found'] = true; 218 | 219 | } 220 | } elseif ( class_exists( 'Easy_Digital_Downloads' ) ) { 221 | 222 | $products = get_post_meta( $license_rule['product_id'], 'edd_variable_prices', true ); 223 | 224 | if ( ! empty( $products ) ) { 225 | 226 | foreach ( $products as $index => $product ) { 227 | 228 | $child_products[ $index ]['name'] = '- ' . $product['name']; 229 | $child_products[ $index ]['found'] = true; 230 | 231 | } 232 | } 233 | } 234 | } 235 | 236 | if ( ! empty( $child_products ) ) : ?> 237 |
238 |

239 | $child_product ) : ?> 240 |

241 | 242 | 243 | 244 |

245 | 246 |

247 | 248 |
249 | 250 | 254 |

255 | 256 |

257 |
260 |
261 | 262 | 263 | 264 |

265 |

266 |
269 |
270 | 271 | 272 | 273 |

274 |
275 | 276 | 277 |

278 | 279 |
280 | 281 | 282 | 283 |   284 | 285 | 286 | 287 | 288 | 289 |   290 | 293 | 294 |

295 |
296 | 318 |
319 |
320 |
321 | 322 | 323 |
324 | 325 | prepare_items(); 327 | $list_table->search_box( __( 'Search', 'tuxedo-software-updater' ), 'tux-su' ); 328 | $list_table->display(); 329 | ?> 330 |
331 | 332 |
333 | 334 | isset( $_POST[ 'tux_child_name_' . $child_id ] ) ? sanitize_text_field( $_POST[ 'tux_child_name_' . $child_id ] ) : '', 365 | 'activation_limit' => isset( $_POST[ 'tux_activation_limit_' . $child_id ] ) ? absint( $_POST[ 'tux_activation_limit_' . $child_id ] ) : 0, 366 | 'expiry' => isset( $_POST[ 'tux_expiry_' . $child_id ] ) ? absint( $_POST[ 'tux_expiry_' . $child_id ] ) : 0, 367 | ); 368 | 369 | } 370 | } 371 | } 372 | 373 | $tux_insert = tux_su_update_db( array( 374 | 'id' => isset( $_POST['tux_id'] ) ? absint( $_POST['tux_id'] ) : 0, 375 | 'product_id' => absint( $_POST['tux_product_id'] ), 376 | 'type' => 'rule', 377 | 'info' => array( 378 | 'product_name' => wp_strip_all_tags( sanitize_text_field( $_POST['tux_product_name'] ) ), 379 | 'product_url' => esc_url_raw( $_POST['tux_product_url'] ), 380 | 'file_url' => esc_url_raw( $_POST['tux_file_url'] ), 381 | 'version' => sanitize_text_field( $_POST['tux_version'] ), 382 | 'compatible' => sanitize_text_field( $_POST['tux_compatible'] ), 383 | 'autoupdate' => absint( $_POST['tux_autoupdate'] ), 384 | 'open_update' => absint( $_POST['tux_open_update'] ), 385 | 'activation_limit' => absint( $_POST['tux_activation_limit'] ), 386 | 'expiry' => absint( $_POST['tux_expiry'] ), 387 | 'children' => $child_products, 388 | ), 389 | ) ); 390 | 391 | $sendback = admin_url( 'admin.php?page=tuxedo-su-license-rules' ); 392 | 393 | if ( false === $tux_insert ) { 394 | 395 | $sendback = add_query_arg( 'updated', 'error', $sendback ); 396 | 397 | } else { 398 | 399 | $sendback = add_query_arg( 'updated', 'success', $sendback ); 400 | 401 | } 402 | 403 | wp_redirect( $sendback ); 404 | exit(); 405 | 406 | } // End if(). 407 | } 408 | 409 | /** 410 | * Add styles output function to admin_print_styles action. 411 | * 412 | * @since 1.0.0 413 | */ 414 | function tux_su_admin_styles_add_action_license_rules() { 415 | 416 | add_action( 'admin_print_styles', 'tux_su_admin_styles_license_rules' ); 417 | 418 | } 419 | 420 | /** 421 | * Output css styles. 422 | * 423 | * @since 1.0.0 424 | */ 425 | function tux_su_admin_styles_license_rules() { 426 | 427 | ?> 428 | 444 | absint( $_GET['edit_id'] ), 31 | ) ); 32 | 33 | $license = reset( $license ); 34 | 35 | if ( isset( $license['info'] ) ) { 36 | 37 | $license['info'] = maybe_unserialize( $license['info'] ); 38 | 39 | } 40 | 41 | $license['info']['product_name'] = ''; 42 | 43 | if ( isset( $license['product_id'] ) ) { 44 | 45 | $license_rule = tux_su_get_db( array( 46 | 'product_id' => $license['product_id'], 47 | 'type' => 'rule', 48 | ) ); 49 | 50 | $license_rule = reset( $license_rule ); 51 | 52 | if ( isset( $license_rule['info'] ) ) { 53 | 54 | $license_rule['info'] = maybe_unserialize( $license_rule['info'] ); 55 | 56 | if ( isset( $license_rule['info']['product_name'] ) ) { 57 | 58 | $license['info']['product_name'] = $license_rule['info']['product_name']; 59 | 60 | } 61 | } 62 | 63 | unset( $license_rule ); 64 | 65 | } 66 | } // End if(). 67 | 68 | $list_table = new Tux_SU_Licenses_List(); 69 | ?> 70 |
71 | 72 |

73 | 74 |

75 | 76 |

77 | 78 |

79 | 80 | 81 | 82 | 83 |
84 | 85 |

' . esc_html__( 'Error editing license.', 'tuxedo-software-updater' ) . '

'; 91 | echo ''; 92 | 93 | return; 94 | 95 | } 96 | } 97 | 98 | if ( isset( $_GET['updated'] ) && 'success' === $_GET['updated'] ) { 99 | 100 | echo '

' . esc_html__( 'Successfully updated license.', 'tuxedo-software-updater' ) . '

'; 101 | 102 | } 103 | 104 | if ( isset( $_GET['updated'] ) && 'error' === $_GET['updated'] ) { 105 | 106 | echo '

' . esc_html__( 'Error updating license.', 'tuxedo-software-updater' ) . '

'; 107 | 108 | } 109 | 110 | if ( isset( $_GET['deleted'] ) && 'success' === $_GET['deleted'] ) { 111 | 112 | echo '

' . esc_html__( 'Successfully deleted license(s).', 'tuxedo-software-updater' ) . '

'; 113 | 114 | } 115 | 116 | if ( isset( $_GET['deleted'] ) && 'error' === $_GET['deleted'] ) { 117 | 118 | echo '

' . esc_html__( 'Error deleting license(s).', 'tuxedo-software-updater' ) . '

'; 119 | 120 | } 121 | 122 | $_SERVER['REQUEST_URI'] = remove_query_arg( array( 123 | 'updated', 124 | 'deleted', 125 | '_wpnonce', 126 | '_wp_http_referer', 127 | ), $_SERVER['REQUEST_URI'] ); 128 | ?> 129 |
130 |
style="display:none;"> 131 |
132 |

133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 |
142 |
143 |

144 |
147 | 148 |

149 |

150 |
153 | 154 |

155 |

156 |
159 | 160 |

161 |

162 |
165 | 166 |

167 |

168 | 179 |
182 | 183 | 184 |
185 | 186 | 187 | 188 |

189 | 0 ) : ?> 190 |
191 |

192 | 193 |

194 |

195 | 196 | 197 | 198 | 201 | 204 | 207 | 208 | 209 | 210 | $activation ) : ?> 211 | 212 | 215 | 218 | 221 | 222 | 223 | 224 |
199 | 200 | 202 | 203 | 205 | 206 |
213 | 214 | 216 | 217 | 219 | 220 |
225 |

226 | 227 |

228 | 229 |
230 | 231 | 232 | 233 |   234 | 235 | 236 | 237 | 238 | 239 |   240 | 243 | 244 |

245 |
246 |
247 |
248 |
249 | 250 | 251 |
252 | 253 | prepare_items(); 255 | $list_table->search_box( __( 'Search', 'tuxedo-software-updater' ), 'tux-su' ); 256 | $list_table->display(); 257 | ?> 258 |
259 | 260 |
261 | 262 | user_login; 286 | 287 | } 288 | 289 | if ( isset( $_POST['tux_created_year'], $_POST['tux_created_month'], $_POST['tux_created_day'] ) ) { 290 | 291 | $year = absint( $_POST['tux_created_year'] ); 292 | $month = absint( $_POST['tux_created_month'] ); 293 | $day = absint( $_POST['tux_created_day'] ); 294 | 295 | } else { 296 | 297 | $year = current_time( 'Y' ); 298 | $month = current_time( 'm' ); 299 | $day = current_time( 'd' ); 300 | 301 | } 302 | 303 | $activations = array(); 304 | 305 | if ( isset( $_POST['tux_id'] ) ) { 306 | 307 | $license = tux_su_get_db( array( 308 | 'id' => $_POST['tux_id'], 309 | ) ); 310 | 311 | $license = reset( $license ); 312 | 313 | if ( isset( $license['info'] ) ) { 314 | 315 | $license['info'] = maybe_unserialize( $license['info'] ); 316 | 317 | } 318 | 319 | if ( isset( $license['info']['activations'] ) ) { 320 | 321 | $activations = $license['info']['activations']; 322 | 323 | } 324 | } 325 | 326 | if ( isset( $_POST['activation_delete'] ) ) { 327 | 328 | foreach ( $_POST['activation_delete'] as $activation_delete ) { 329 | 330 | unset( $activations[ $activation_delete ] ); 331 | 332 | } 333 | } 334 | 335 | $tux_insert = tux_su_update_db( array( 336 | 'id' => isset( $_POST['tux_id'] ) ? $_POST['tux_id'] : 0, 337 | 'user_id' => $user_id, 338 | 'product_id' => $_POST['tux_product_id'], 339 | 'type' => 'license', 340 | 'info' => array( 341 | 'child_id' => ! empty( $_POST['tux_child_id'] ) ? absint( $_POST['tux_child_id'] ) : 0, 342 | 'order_id' => isset( $_POST['tux_order_id'] ) ? absint( $_POST['tux_order_id'] ) : 0, 343 | 'user_name' => wp_strip_all_tags( $user_name ), 344 | 'activations' => $activations, 345 | ), 346 | 'created' => date( 'Y-m-d', strtotime( "{$year}-{$month}-{$day}" ) ), 347 | ) ); 348 | 349 | $sendback = admin_url( 'admin.php?page=tuxedo-su-licenses' ); 350 | 351 | if ( false === $tux_insert ) { 352 | 353 | $sendback = add_query_arg( 'updated', 'error', $sendback ); 354 | 355 | } else { 356 | 357 | $sendback = add_query_arg( 'updated', 'success', $sendback ); 358 | 359 | } 360 | 361 | wp_redirect( $sendback ); 362 | exit(); 363 | 364 | } 365 | 366 | /** 367 | * Add styles output function to admin_print_styles action. 368 | * 369 | * @since 1.0.0 370 | */ 371 | function tux_su_admin_styles_add_action_licenses() { 372 | 373 | add_action( 'admin_print_styles', 'tux_su_admin_styles_licenses' ); 374 | 375 | } 376 | 377 | /** 378 | * Output css styles. 379 | * 380 | * @since 1.0.0 381 | */ 382 | function tux_su_admin_styles_licenses() { 383 | 384 | ?> 385 | 400 | 23 |
24 |

25 |
26 |
27 | 32 |
33 |
34 | 'tux_ecommerce', 65 | ) 66 | ); 67 | 68 | add_settings_section( 69 | 'tux_tracking_settings', 70 | __( 'Tracking', 'tuxedo-software-updater' ), 71 | 'tux_su_tracking_settings', 72 | 'tuxedo-su-settings' 73 | ); 74 | 75 | add_settings_field( 76 | 'tux_tracking', 77 | __( 'Track API hits', 'tuxedo-software-updater' ), 78 | 'tux_su_tracking_field', 79 | 'tuxedo-su-settings', 80 | 'tux_tracking_settings', 81 | array( 82 | 'label_for' => 'tux_tracking', 83 | ) 84 | ); 85 | 86 | add_settings_field( 87 | 'tux_tracking_cleanup', 88 | __( 'Remove tracking data after X days', 'tuxedo-software-updater' ), 89 | 'tux_su_tracking_cleanup_field', 90 | 'tuxedo-su-settings', 91 | 'tux_tracking_settings', 92 | array( 93 | 'label_for' => 'tux_tracking_cleanup', 94 | ) 95 | ); 96 | 97 | } 98 | 99 | add_action( 'admin_init', 'tux_su_settings_fields' ); 100 | 101 | /** 102 | * Validate setting options. 103 | * 104 | * @since 1.0.0 105 | * 106 | * @param array $input Input. 107 | * 108 | * @return array Validated input. 109 | */ 110 | function tux_su_validate_settings_options( $input ) { 111 | 112 | return array( 113 | 'ecommerce' => empty( $input['ecommerce'] ) ? 0 : 1, 114 | 'tracking' => empty( $input['tracking'] ) ? 0 : 1, 115 | 'tracking_cleanup' => empty( $input['tracking_cleanup'] ) ? 0 : absint( $input['tracking_cleanup'] ), 116 | ); 117 | 118 | } 119 | 120 | /** 121 | * E-Commerce section output. 122 | * 123 | * @since 1.0.0 124 | * 125 | * @param array $args Display arguments. 126 | */ 127 | function tux_su_ecommerce_settings( $args ) { 128 | 129 | if ( class_exists( 'WooCommerce' ) ) { 130 | 131 | ?> 132 | 133 | 134 | 135 | 138 | 141 | 142 | 143 |
136 | 137 | 139 | 140 |
144 | 149 | 150 | 151 | 152 | 155 | 158 | 159 | 160 |
153 | 154 | 156 | 157 |
161 | 166 | 167 | 168 | 169 | 172 | 175 | 176 | 177 |
170 | 171 | 173 | 174 |
178 | 192 | 193 | /> 194 | 219 | 220 | /> 221 | 234 | 235 |
236 | 237 | 23 |
24 |

25 | 26 |

27 |
28 |
29 |

30 |

31 | 32 |

33 |
34 |

35 | 36 |

37 |
38 | 138 | 170 |
171 | 1088 | array( 1115 | 'code' => 'ACCESS_DENIED', 1116 | ), 1117 | ) 1118 | ); 1119 | 1120 | exit(); 1121 | 1122 | } 1123 | 1124 | if ( ! isset( $_POST['tux_su_tools_nonce'], $_POST['tux_su_page'], $_POST['tux_su_start_date'], $_POST['tux_su_end_date'] ) || ! wp_verify_nonce( $_POST['tux_su_tools_nonce'], 'tux_su_tools_ajax' ) || absint( $_POST['tux_su_page'] ) < 1 ) { 1125 | 1126 | echo wp_json_encode( array( 1127 | 'error' => array( 1128 | 'code' => 'PARAMETER_INCORRECT', 1129 | ), 1130 | ) 1131 | ); 1132 | 1133 | exit(); 1134 | 1135 | } 1136 | 1137 | if ( class_exists( 'WooCommerce' ) ) { 1138 | 1139 | $post_type = 'shop_order'; 1140 | $post_status = 'wc-completed'; 1141 | 1142 | } elseif ( class_exists( 'Easy_Digital_Downloads' ) ) { 1143 | 1144 | $post_type = 'edd_payment'; 1145 | $post_status = 'publish'; 1146 | 1147 | } 1148 | 1149 | if ( empty( $post_type ) || empty( $post_status ) ) { 1150 | 1151 | echo wp_json_encode( array( 1152 | 'error' => array( 1153 | 'code' => 'PARAMETER_INCORRECT', 1154 | ), 1155 | ) 1156 | ); 1157 | 1158 | exit(); 1159 | 1160 | } 1161 | 1162 | $orders = new WP_Query( array( 1163 | 'posts_per_page' => 10, 1164 | 'paged' => absint( $_POST['tux_su_page'] ), 1165 | 'post_type' => $post_type, 1166 | 'post_status' => $post_status, 1167 | 'date_query' => array( 1168 | array( 1169 | 'after' => empty( $_POST['tux_su_start_date'] ) ? '0' : sanitize_text_field( $_POST['tux_su_start_date'] ), 1170 | 'before' => empty( $_POST['tux_su_end_date'] ) ? current_time( 'mysql' ) : sanitize_text_field( $_POST['tux_su_end_date'] ) . ' 23:59:59', 1171 | 'inclusive' => true, 1172 | ), 1173 | ), 1174 | 'fields' => 'ids', 1175 | ) ); 1176 | 1177 | if ( $orders->post_count < 1 ) { 1178 | 1179 | echo wp_json_encode( array( 1180 | 'error' => array( 1181 | 'code' => 'NO_DATA', 1182 | ), 1183 | ) 1184 | ); 1185 | 1186 | exit(); 1187 | 1188 | } 1189 | 1190 | foreach ( $orders->posts as $order_id ) { 1191 | 1192 | if ( class_exists( 'WooCommerce' ) ) { 1193 | 1194 | tux_su_woo_create_license_on_order_completed( $order_id ); 1195 | 1196 | } elseif ( class_exists( 'Easy_Digital_Downloads' ) ) { 1197 | 1198 | tux_su_edd_create_license_on_order_completed( $order_id ); 1199 | 1200 | } 1201 | } 1202 | 1203 | echo wp_json_encode( array( 1204 | 'total' => $orders->found_posts, 1205 | 'processed' => $orders->post_count, 1206 | 'page' => absint( $_POST['tux_su_page'] ), 1207 | ) ); 1208 | exit(); 1209 | 1210 | } 1211 | 1212 | add_action( 'wp_ajax_tux_su_process_orders', 'tux_su_tools_process_orders' ); 1213 | -------------------------------------------------------------------------------- /admin/tuxedo-su-admin-tracking.php: -------------------------------------------------------------------------------- 1 | 29 |
30 |

31 | 32 |

33 |
34 | 35 |

' . esc_html__( 'Successfully deleted tracking data.', 'tuxedo-software-updater' ) . '

'; 39 | 40 | } 41 | 42 | if ( isset( $_GET['deleted'] ) && 'error' === $_GET['deleted'] ) { 43 | 44 | echo '

' . esc_html__( 'Error deleting tracking data.', 'tuxedo-software-updater' ) . '

'; 45 | 46 | } 47 | 48 | $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'updated', 'deleted', '_wpnonce', '_wp_http_referer' ), $_SERVER['REQUEST_URI'] ); 49 | ?> 50 |
51 | 52 |
53 | 54 | prepare_items(); 56 | $list_table->search_box( __( 'Search', 'tuxedo-software-updater' ), 'tux-su' ); 57 | $list_table->display(); 58 | ?> 59 |
60 |
61 | 62 | 86 | 97 | 109 | 122 | ID ) ) { 21 | 22 | return; 23 | 24 | } 25 | 26 | $update_key = get_user_meta( $user->ID, '_tux_su_update_key', true ); 27 | $disabled = 0; 28 | 29 | if ( empty( $update_key ) ) { 30 | 31 | $update_key = tux_su_generate_update_key( $user->ID ); 32 | 33 | } 34 | 35 | if ( strpos( $update_key, '-disabled' ) !== false ) { 36 | 37 | $update_key = str_replace( '-disabled', '     [ ' . __( 'DISABLED', 'tuxedo-software-updater' ) . ' ]', $update_key ); 38 | $disabled = 1; 39 | 40 | } 41 | 42 | ?> 43 |

44 | 45 | 46 | 49 | 52 | 53 | 54 | 57 | 61 | 62 | 63 | 66 | 70 | 71 |
47 | 48 | 50 | 51 |
55 | 56 | 58 | 60 |
64 | 65 | 67 | 69 |
72 | 1, 129 | 'tracking' => 0, 130 | 'tracking_cleanup' => 0, 131 | ) ); 132 | 133 | } 134 | 135 | add_rewrite_endpoint( 'update-licenses', EP_ROOT | EP_PAGES ); 136 | flush_rewrite_rules(); 137 | 138 | $wpdb->hide_errors(); 139 | 140 | $collate = ''; 141 | if ( $wpdb->has_cap( 'collation' ) ) { 142 | 143 | if ( ! empty( $wpdb->charset ) ) { 144 | 145 | $collate .= " DEFAULT CHARACTER SET $wpdb->charset"; 146 | 147 | } 148 | 149 | if ( ! empty( $wpdb->collate ) ) { 150 | 151 | $collate .= " COLLATE $wpdb->collate"; 152 | 153 | } 154 | } 155 | 156 | /** Included for dbDelta */ 157 | require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 158 | 159 | $sql = 160 | "CREATE TABLE {$wpdb->tux_su_licensing} ( 161 | id bigint(20) unsigned NOT NULL auto_increment, 162 | user_id bigint(20) unsigned NOT NULL default '0', 163 | product_id bigint(20) unsigned NOT NULL default '0', 164 | type int(10) unsigned NOT NULL default '0', 165 | info longtext, 166 | created datetime NOT NULL default '0000-00-00 00:00:00', 167 | modified datetime NOT NULL default '0000-00-00 00:00:00', 168 | PRIMARY KEY (id), 169 | KEY user_id (user_id), 170 | KEY product_id (product_id) 171 | ){$collate};"; 172 | 173 | dbDelta( $sql ); 174 | 175 | $sql = 176 | "CREATE TABLE {$wpdb->tux_su_tracking} ( 177 | id bigint(20) unsigned NOT NULL auto_increment, 178 | user_id bigint(20) unsigned NOT NULL default '0', 179 | info longtext, 180 | created datetime NOT NULL default '0000-00-00 00:00:00', 181 | PRIMARY KEY (id), 182 | KEY user_id (user_id), 183 | KEY created (created) 184 | ){$collate};"; 185 | 186 | dbDelta( $sql ); 187 | 188 | } 189 | 190 | /** 191 | * Plugin deactivation. 192 | * 193 | * @since 1.0.0 194 | */ 195 | function tux_su_deactivation() { 196 | 197 | if ( wp_next_scheduled( 'tux_su_tracking_cleanup' ) ) { 198 | 199 | wp_unschedule_event( wp_next_scheduled( 'tux_su_tracking_cleanup' ), 'tux_su_tracking_cleanup' ); 200 | 201 | } 202 | 203 | flush_rewrite_rules(); 204 | 205 | } 206 | -------------------------------------------------------------------------------- /tuxedo-su-database.php: -------------------------------------------------------------------------------- 1 | tux_su_licensing = $GLOBALS['wpdb']->prefix . 'tux_su_licensing'; 13 | $GLOBALS['wpdb']->tux_su_tracking = $GLOBALS['wpdb']->prefix . 'tux_su_tracking'; 14 | 15 | /** 16 | * Get rows from the database. 17 | * 18 | * @since 1.0.0 19 | * 20 | * @param array $args { 21 | * Arguments for database retrieval. 22 | * 23 | * @type array|int $id Database row id. 24 | * @type array|int $user_id User id. 25 | * @type array|int $product_id Product id. 26 | * @type string $type Type of row to retrieve ('license' or 'rule'). 27 | * @type array|string $info Blind info search, info is stored as serialized data. 28 | * @type array|string $info_value Info search for exact serialized value. 29 | * @type string $group Group by clause. 30 | * @type string $order Order by clause. 31 | * @type int $limit Limit of rows to return. 32 | * @type int $offset Offset of rows to return. 33 | * @type bool $count Return number of found rows. 34 | * @type bool $search Request is a search. 35 | * } 36 | * @param string $table Optional. Table to retrieve from ('licensing' or 'tracking'). Default licensing. 37 | * 38 | * @return array|null|string Null on failure, string for count, array for returned rows. 39 | */ 40 | function tux_su_get_db( $args = array(), $table = 'licensing' ) { 41 | 42 | global $wpdb; 43 | 44 | if ( 'licensing' === $table ) { 45 | 46 | $table = $wpdb->tux_su_licensing; 47 | 48 | } elseif ( 'tracking' === $table ) { 49 | 50 | $table = $wpdb->tux_su_tracking; 51 | 52 | } else { 53 | 54 | return null; 55 | 56 | } 57 | 58 | $where = array(); 59 | $group = ''; 60 | $order = ''; 61 | $limit = ''; 62 | $offset = ''; 63 | $count = false; 64 | $search = false; 65 | $operator = ' AND '; 66 | 67 | foreach ( $args as $key => $arg ) { 68 | 69 | switch ( $key ) { 70 | 71 | case 'id': 72 | case 'user_id': 73 | case 'product_id': 74 | case 'type': 75 | 76 | if ( 'type' === $key ) { 77 | 78 | if ( 'rule' === $arg ) { 79 | 80 | $arg = 1; 81 | 82 | } else { 83 | 84 | $arg = 2; 85 | 86 | } 87 | 88 | if ( is_array( $arg ) ) { 89 | 90 | array_unshift( $where, $key . ' IN (' . implode( ',', array_map( 'absint', $arg ) ) . ')' ); 91 | 92 | } else { 93 | 94 | array_unshift( $where, $key . ' = ' . absint( $arg ) ); 95 | 96 | } 97 | 98 | break; 99 | 100 | } 101 | 102 | if ( is_array( $arg ) ) { 103 | 104 | $where[] = $key . ' IN (' . implode( ',', array_map( 'absint', $arg ) ) . ')'; 105 | 106 | } else { 107 | 108 | $where[] = $key . ' = ' . absint( $arg ); 109 | 110 | } 111 | 112 | break; 113 | 114 | case 'info': 115 | 116 | if ( is_array( $arg ) ) { 117 | 118 | foreach ( $arg as $info ) { 119 | 120 | $where[] = $key . ' LIKE \'%' . esc_sql( $info ) . '%\''; 121 | 122 | } 123 | } else { 124 | 125 | $where[] = $key . ' LIKE \'%' . esc_sql( $arg ) . '%\''; 126 | 127 | } 128 | 129 | break; 130 | 131 | case 'info_value': 132 | 133 | if ( is_array( $arg ) ) { 134 | 135 | foreach ( $arg as $info ) { 136 | 137 | $where[] = 'info LIKE \':"%' . esc_sql( $info ) . '%";\''; 138 | 139 | } 140 | } else { 141 | 142 | $where[] = 'info LIKE \':"%' . esc_sql( $arg ) . '%";\''; 143 | 144 | } 145 | 146 | break; 147 | 148 | case 'group': 149 | 150 | $group = ' GROUP BY ' . $arg; 151 | 152 | break; 153 | 154 | case 'order': 155 | 156 | $order = ' ORDER BY ' . $arg; 157 | 158 | break; 159 | 160 | case 'limit': 161 | 162 | $limit = ' LIMIT ' . absint( $arg ); 163 | 164 | break; 165 | 166 | case 'offset': 167 | 168 | $offset = ' OFFSET ' . absint( $arg ); 169 | 170 | break; 171 | 172 | case 'count': 173 | 174 | if ( true === $arg ) { 175 | 176 | $count = true; 177 | 178 | } 179 | 180 | break; 181 | 182 | case 'search': 183 | 184 | if ( true === $arg ) { 185 | 186 | $search = true; 187 | $operator = ' OR '; 188 | 189 | } 190 | 191 | break; 192 | 193 | } // End switch(). 194 | } // End foreach(). 195 | 196 | $where = implode( $operator, $where ); 197 | 198 | if ( $search ) { 199 | 200 | if ( strpos( $where, 'type = 1 OR' ) !== false || strpos( $where, 'type = 2 OR' ) !== false ) { 201 | 202 | $where = str_replace( array( 'type = 1 OR', 'type = 2 OR' ), array( '(type = 1) AND (', '(type = 2) AND (' ), $where ) . ')'; 203 | 204 | } 205 | } 206 | 207 | if ( ! empty( $where ) ) { 208 | 209 | $where = ' WHERE ' . $where; 210 | 211 | } 212 | 213 | if ( $count ) { 214 | 215 | return $wpdb->get_var( "SELECT COUNT(*) FROM {$table}{$where}" ); 216 | 217 | } 218 | 219 | return $wpdb->get_results( "SELECT * FROM {$table}{$where}{$group}{$order}{$limit}{$offset}", ARRAY_A ); 220 | 221 | } 222 | 223 | /** 224 | * Update or add a row to the database. 225 | * 226 | * @since 1.0.0 227 | * 228 | * @param array $args { 229 | * Arguments for database updating. 230 | * 231 | * @type int $id Optional. Database row id, a new row will be added if empty. 232 | * @type int $user_id User id. 233 | * @type int $product_id Product id. 234 | * @type string $type Type of row ('license' or 'rule'). 235 | * @type array $info Optional. Info is stored as serialized data. 236 | * @type string $created Optional. Date-time string. 237 | * @type string $modified Date-time string. 238 | * } 239 | * @param string $table Optional. Table to update or add to ('licensing' or 'tracking'). Default licensing. 240 | * 241 | * @return false|int False on failure, int of updated or added row on success. 242 | */ 243 | function tux_su_update_db( $args = array(), $table = 'licensing' ) { 244 | 245 | global $wpdb; 246 | 247 | $defaults = array( 248 | 'id' => 0, 249 | 'user_id' => 0, 250 | 'product_id' => 0, 251 | 'type' => '', 252 | 'info' => '', 253 | 'created' => '', 254 | 'modified' => current_time( 'mysql' ), 255 | ); 256 | 257 | $args = wp_parse_args( $args, $defaults ); 258 | $columns = array( 259 | 'user_id' => absint( $args['user_id'] ), 260 | 'product_id' => absint( $args['product_id'] ), 261 | 'type' => 'rule' === $args['type'] ? 1 : 2, 262 | 'info' => maybe_serialize( $args['info'] ), 263 | 'created' => empty( $args['created'] ) ? $args['modified'] : $args['created'], 264 | 'modified' => $args['modified'], 265 | ); 266 | 267 | if ( 'licensing' === $table ) { 268 | 269 | $table = $wpdb->tux_su_licensing; 270 | 271 | if ( empty( $args['created'] ) && ! empty( $args['id'] ) ) { 272 | 273 | unset( $columns['created'] ); 274 | $format = array( '%d', '%d', '%d', '%s', '%s' ); 275 | 276 | } else { 277 | 278 | $format = array( '%d', '%d', '%d', '%s', '%s', '%s' ); 279 | 280 | } 281 | } elseif ( 'tracking' === $table ) { 282 | 283 | $table = $wpdb->tux_su_tracking; 284 | 285 | unset( $columns['product_id'], $columns['type'], $columns['modified'] ); 286 | $format = array( '%d', '%s', '%s' ); 287 | 288 | } else { 289 | 290 | return false; 291 | 292 | } 293 | 294 | if ( empty( $args['id'] ) ) { 295 | 296 | return $wpdb->insert( $table, $columns, $format ); 297 | 298 | } 299 | 300 | return $wpdb->update( $table, $columns, array( 'id' => absint( $args['id'] ) ), $format, '%d' ); 301 | 302 | } 303 | 304 | /** 305 | * Delete from the licensing database table. 306 | * 307 | * @since 1.0.0 308 | * 309 | * @param array $id Array of row ids. 310 | * @param string $table Optional. Table to delete from ('licensing' or 'tracking'). Default licensing. 311 | * 312 | * @return false|int False on failure, number of rows deleted on success. 313 | */ 314 | function tux_su_delete_db( $id = array(), $table = 'licensing' ) { 315 | 316 | global $wpdb; 317 | 318 | if ( 'licensing' === $table ) { 319 | 320 | $table = $wpdb->tux_su_licensing; 321 | 322 | } elseif ( 'tracking' === $table ) { 323 | 324 | $table = $wpdb->tux_su_tracking; 325 | 326 | } else { 327 | 328 | return false; 329 | 330 | } 331 | 332 | if ( empty( $id ) || ! is_array( $id ) ) { 333 | 334 | return false; 335 | 336 | } 337 | 338 | return $wpdb->query( "DELETE FROM {$table} WHERE id IN (" . implode( ',', array_map( 'absint', $id ) ) . ")" ); 339 | 340 | } 341 | 342 | /** 343 | * Tracking cleanup. 344 | * Remove old tracking data from the tracking table. 345 | * 346 | * @aince 1.0.0 347 | */ 348 | function tux_su_tracking_cleanup() { 349 | 350 | $tux_su_settings = get_option( 'tux_su_settings' ); 351 | 352 | if ( empty( $tux_su_settings['tracking_cleanup'] ) ) { 353 | 354 | return; 355 | 356 | } 357 | 358 | global $wpdb; 359 | 360 | $removal_cutoff = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) - ( absint( $tux_su_settings['tracking_cleanup'] ) * DAY_IN_SECONDS ) ); 361 | 362 | $wpdb->query( "DELETE FROM {$wpdb->tux_su_tracking} WHERE created < '{$removal_cutoff}'" ); 363 | 364 | } 365 | 366 | add_action( 'tux_su_tracking_cleanup', 'tux_su_tracking_cleanup' ); 367 | -------------------------------------------------------------------------------- /tuxedo-su-edd.php: -------------------------------------------------------------------------------- 1 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 |
48 | 49 |
55 | 56 |
60 | get_current_user_id(), 64 | 'type' => 'license', 65 | ) ); 66 | 67 | if ( empty( $licenses ) ) { 68 | 69 | return; 70 | 71 | } 72 | 73 | $product_ids = array(); 74 | 75 | foreach ( $licenses as $license ) { 76 | 77 | $product_ids[] = absint( $license['product_id'] ); 78 | 79 | } 80 | 81 | $license_rules = array(); 82 | 83 | if ( count( $product_ids ) > 0 ) { 84 | 85 | $license_rules = tux_su_get_db( array( 86 | 'product_id' => $product_ids, 87 | 'type' => 'rule', 88 | ) ); 89 | 90 | } 91 | 92 | $delete_ids = array(); 93 | 94 | foreach ( $licenses as $license ) { 95 | 96 | ?> 97 | 98 | 99 | strtotime( $license['created'] . ' + ' . $expiry . ' days' ) ) { 163 | 164 | $delete_ids[] = $license['id']; 165 | continue; 166 | 167 | } 168 | 169 | if ( isset( $_GET['delnonce'], $_GET['action'], $_GET['license_id'], $_GET['activation_id'], $license['info']['activations'] ) && 'delete' === $_GET['action'] && absint( $license['id'] ) === absint( $_GET['license_id'] ) && wp_verify_nonce( $_GET['delnonce'], 'tux_delete_activation' ) ) { 170 | 171 | unset( $license['info']['activations'][ sanitize_text_field( $_GET['activation_id'] ) ] ); 172 | 173 | tux_su_update_db( array( 174 | 'id' => $license['id'], 175 | 'user_id' => $license['user_id'], 176 | 'product_id' => $license['product_id'], 177 | 'type' => 'license', 178 | 'info' => $license['info'], 179 | ) ); 180 | 181 | } 182 | 183 | ?> 184 | 185 | 188 | 189 | 190 | 191 | 192 | 195 | 198 | 199 | 200 | 203 | 206 | 207 | $activation ) : ?> 209 | 210 | 213 | 218 | 219 |
186 | 187 |
193 | 194 | 196 | 197 |
201 | 202 | 204 | 205 |
211 | . 212 | 214 | 215 | 216 | 217 |
'; 223 | 224 | } // End foreach(). 225 | 226 | if ( ! empty( $delete_ids ) ) { 227 | 228 | tux_su_delete_db( $delete_ids ); 229 | 230 | } 231 | 232 | return ob_get_clean(); 233 | 234 | } 235 | 236 | add_shortcode( 'update_licenses', 'tux_su_edd_licensing_shortcode' ); 237 | 238 | /** 239 | * Create a license when an order is completed. 240 | * 241 | * @since 1.0.0 242 | * 243 | * @param int $payment_id Payment ID. 244 | */ 245 | function tux_su_edd_create_license_on_order_completed( $payment_id ) { 246 | 247 | $order = new EDD_Payment( $payment_id ); 248 | $user_id = $order->customer_id; 249 | $order_items = $order->cart_details; 250 | $product_ids = array(); 251 | $order_note = ''; 252 | $date_completed = $order->completed_date; 253 | 254 | if ( empty( $date_completed ) ) { 255 | 256 | return; 257 | 258 | } 259 | 260 | $date_completed = date( 'Y-m-d', strtotime( $date_completed ) ); 261 | 262 | $all_bundled_items = array(); 263 | 264 | foreach ( $order_items as $item_index => $order_item ) { 265 | 266 | $item_type = edd_get_download_type( $order_item['id'] ); 267 | 268 | if ( 'bundle' === $item_type ) { 269 | 270 | $get_bundled_items = edd_get_bundled_products( $order_item['id'] ); 271 | $bundled_items = array(); 272 | 273 | foreach ( $get_bundled_items as $bundled_item ) { 274 | 275 | $bundled_item_id = explode( '_', $bundled_item ); 276 | 277 | if ( isset( $bundled_item_id[1] ) ) { 278 | 279 | $bundled_item_id[1] = array( 280 | 'item_number' => array( 281 | 'options' => array( 282 | 'price_id' => $bundled_item_id[1], 283 | ), 284 | ), 285 | ); 286 | 287 | } else { 288 | 289 | $bundled_item_id[1] = array(); 290 | 291 | } 292 | 293 | $bundled_items[] = array_merge( array( 294 | 'id' => $bundled_item_id[0], 295 | 'name' => get_the_title( $bundled_item_id[0] ), 296 | ), $bundled_item_id[1] ); 297 | 298 | $product_ids[] = $bundled_item_id[0]; 299 | 300 | } 301 | 302 | $all_bundled_items = array_merge( $all_bundled_items, $bundled_items ); 303 | 304 | } else { 305 | 306 | $product_ids[] = $order_item['id']; 307 | 308 | } // End if(). 309 | } // End foreach(). 310 | 311 | $order_items = array_merge( $order_items, $all_bundled_items ); 312 | 313 | $licenses = tux_su_get_db( array( 314 | 'user_id' => array( 0, $user_id ), 315 | 'product_id' => $product_ids, 316 | 'order' => 'type ASC', 317 | ) ); 318 | 319 | foreach ( $order_items as $order_item ) { 320 | 321 | $license_rule = array(); 322 | $license_exists = array(); 323 | $license_update_id = false; 324 | 325 | foreach ( $licenses as $license ) { 326 | 327 | if ( (int) $order_item['id'] === (int) $license['product_id'] ) { 328 | 329 | // Is rule. 330 | if ( 1 === (int) $license['type'] && empty( $license_rule ) ) { 331 | 332 | $license['info'] = maybe_unserialize( $license['info'] ); 333 | $license_rule = $license; 334 | continue; 335 | 336 | } 337 | 338 | // Is license. 339 | if ( 2 === (int) $license['type'] && empty( $license_exists ) ) { 340 | 341 | $license['info'] = maybe_unserialize( $license['info'] ); 342 | 343 | if ( isset( $license['info']['order_id'] ) && (int) $license['info']['order_id'] === (int) $payment_id ) { 344 | 345 | $license_exists = $license; 346 | break; 347 | 348 | } 349 | } 350 | } 351 | } 352 | 353 | if ( ! empty( $license_rule['info']['open_update'] ) ) { 354 | 355 | continue; 356 | 357 | } 358 | 359 | if ( ! empty( $license_rule ) && empty( $license_exists ) ) { 360 | 361 | $user = get_userdata( $user_id ); 362 | $user_name = $user->user_login; 363 | 364 | $license_update_id = tux_su_update_db( array( 365 | 'user_id' => $user_id, 366 | 'product_id' => $order_item['id'], 367 | 'type' => 'license', 368 | 'info' => array( 369 | 'child_id' => isset( $order_item['item_number']['options']['price_id'] ) ? (int) $order_item['item_number']['options']['price_id'] : 0, 370 | 'order_id' => absint( $payment_id ), 371 | 'user_name' => wp_strip_all_tags( $user_name ), 372 | 'activations' => array(), 373 | ), 374 | 'created' => $date_completed, 375 | ) ); 376 | 377 | } 378 | 379 | if ( false !== $license_update_id ) { 380 | 381 | if ( empty( $order_note ) ) { 382 | 383 | $order_note = __( 'Tuxedo Software Update Licensing', 'tuxedo-software-updater' ) . "\n"; 384 | 385 | } 386 | 387 | /* translators: %s: order item name */ 388 | $order_note .= '* ' . sprintf( __( 'Updated license for %s.', 'tuxedo-software-updater' ), wp_strip_all_tags( $order_item['name'] ) ) . "\n"; 389 | 390 | } elseif ( ! empty( $license_rule ) && empty( $license_exists ) ) { 391 | 392 | if ( empty( $order_note ) ) { 393 | 394 | $order_note = __( 'Tuxedo Software Update Licensing', 'tuxedo-software-updater' ) . "\n"; 395 | 396 | } 397 | 398 | /* translators: %s: order item name */ 399 | $order_note .= '* ' . sprintf( __( 'Error updating license for %s.', 'tuxedo-software-updater' ), wp_strip_all_tags( $order_item['name'] ) ) . "\n"; 400 | 401 | } 402 | } // End foreach(). 403 | 404 | if ( ! empty( $order_note ) ) { 405 | 406 | edd_insert_payment_note( $payment_id, $order_note ); 407 | 408 | } 409 | 410 | } 411 | 412 | add_action( 'edd_complete_purchase', 'tux_su_edd_create_license_on_order_completed' ); 413 | -------------------------------------------------------------------------------- /tuxedo-su-rest-api.php: -------------------------------------------------------------------------------- 1 | 'GET,POST', 20 | 'callback' => 'tux_su_get_updates', 21 | 'args' => array( 22 | 'update_key' => array( 23 | 'required' => false, 24 | 'description' => __( 'Update key.', 'tuxedo-software-updater' ), 25 | 'type' => 'string', 26 | ), 27 | 'ids' => array( 28 | 'required' => true, 29 | 'description' => __( 'Comma separated list of product IDs.', 'tuxedo-software-updater' ), 30 | 'type' => 'string', 31 | ), 32 | 'versions' => array( 33 | 'required' => true, 34 | 'description' => __( 'Comma separated list of current versions requesting updates, per product ID.', 'tuxedo-software-updater' ), 35 | 'type' => 'string', 36 | ), 37 | 'activation_id' => array( 38 | 'required' => false, 39 | 'description' => __( 'Human readable ID for the current activation.', 'tuxedo-software-updater' ), 40 | 'type' => 'string', 41 | ), 42 | ), 43 | ) ); 44 | 45 | } 46 | 47 | add_action( 'rest_api_init', 'tux_su_register_rest_api_routes' ); 48 | 49 | /** 50 | * Process REST API get updates request. 51 | * 52 | * @since 1.0.0 53 | * 54 | * @param WP_REST_Request $request Request class. 55 | * 56 | * @return array { 57 | * Response from update server, update key and error info. 58 | * 59 | * @type array $id { 60 | * Update product info, array key is the product id. 61 | * 62 | * @type string $package Download file url. 63 | * @type string $url Update info url. 64 | * @type string $new_version Update version. 65 | * @type bool $autoupdate Should product be updated automatically? 66 | * @type int $expires Amount of days the license will expire in, -1 for never. 67 | * } 68 | * @type array $update_key { 69 | * Update key and error info. 70 | * 71 | * @type bool $found If update key is found. 72 | * @type bool $disabled If update key is disabled. 73 | * @type array $error { 74 | * Error info. 75 | * 76 | * @type string $code Error code (INVALID_UPDATE_KEY_FORMAT). 77 | * @type string $message Human readable error message. 78 | * } 79 | * } 80 | * } 81 | */ 82 | function tux_su_get_updates( WP_REST_Request $request ) { 83 | 84 | $user = false; 85 | $product_ids = explode( ',', $request['ids'] ); 86 | $product_versions = explode( ',', $request['versions'] ); 87 | $updates = array(); 88 | $delete_ids = array(); 89 | $activation_id = empty( $request['activation_id'] ) ? '' : $request['activation_id']; 90 | $activation_ip = filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ); 91 | 92 | if ( false === $activation_ip && isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 93 | 94 | $activation_ip = filter_var( $_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 ); 95 | 96 | } 97 | 98 | if ( false === $activation_ip && isset( $_SERVER['HTTP_CLIENT_IP'] ) ) { 99 | 100 | $activation_ip = filter_var( $_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 ); 101 | 102 | } 103 | 104 | if ( false === $activation_ip ) { 105 | 106 | $activation_ip = $_SERVER['REMOTE_ADDR']; 107 | 108 | } 109 | 110 | $activation_ip_hash = hash( 'crc32', $activation_ip ); 111 | $current_time = current_time( 'timestamp' ); 112 | $tux_su_settings = get_option( 'tux_su_settings' ); 113 | 114 | $update_key = str_replace( '-disabled', '', empty( $request['update_key'] ) ? '' : $request['update_key'] ); 115 | 116 | if ( tux_su_validate_update_key( $update_key ) ) { 117 | 118 | $user = tux_su_get_user_by_update_key( $update_key ); 119 | 120 | } else { 121 | 122 | $updates['update_key']['error'] = array( 123 | 'code' => 'INVALID_UPDATE_KEY_FORMAT', 124 | 'message' => esc_html__( 'Invalid update key format.', 'tuxedo-software-updater' ), 125 | ); 126 | 127 | } 128 | 129 | if ( false === $user ) { 130 | 131 | $updates['update_key']['found'] = false; 132 | 133 | } else { 134 | 135 | $updates['update_key']['found'] = true; 136 | $updates['update_key']['disabled'] = $user['disabled']; 137 | 138 | } 139 | 140 | if ( count( $product_ids ) === count( $product_versions ) ) { 141 | 142 | if ( false === $user || ! isset( $user['disabled'] ) || true === $user['disabled'] ) { 143 | 144 | $licenses = tux_su_get_db( array( 145 | 'product_id' => $product_ids, 146 | 'user_id' => 0, 147 | 'type' => 'rule', 148 | ) ); 149 | 150 | } else { 151 | 152 | $licenses = tux_su_get_db( array( 153 | 'product_id' => $product_ids, 154 | 'user_id' => array( 0, $user['id'] ), 155 | 'order' => 'product_id, type ASC', 156 | ) ); 157 | 158 | } 159 | 160 | foreach ( $licenses as $license ) { 161 | 162 | if ( 1 === (int) $license['type'] ) { // If is rule. 163 | 164 | $license_rule = $license; 165 | $license_rule['info'] = maybe_unserialize( $license_rule['info'] ); 166 | 167 | if ( 1 === (int) $license_rule['info']['open_update'] ) { 168 | 169 | $updates[ $license_rule['product_id'] ]['package'] = $license_rule['info']['file_url']; 170 | $updates[ $license_rule['product_id'] ]['url'] = $license_rule['info']['product_url']; 171 | $updates[ $license_rule['product_id'] ]['new_version'] = $license_rule['info']['version']; 172 | $updates[ $license_rule['product_id'] ]['autoupdate'] = 1 === (int) $license_rule['info']['autoupdate'] ? true : false; 173 | $updates[ $license_rule['product_id'] ]['tested'] = $license_rule['info']['compatible']; 174 | $updates[ $license_rule['product_id'] ]['expires'] = - 1; 175 | 176 | } 177 | 178 | continue; 179 | 180 | } 181 | 182 | if ( empty( $license_rule['product_id'] ) ) { 183 | 184 | continue; 185 | 186 | } 187 | 188 | $license['info'] = maybe_unserialize( $license['info'] ); 189 | 190 | if ( ! isset( $updates[ $license_rule['product_id'] ] ) ) { 191 | 192 | $activation_limit = (int) $license_rule['info']['activation_limit']; 193 | $expiry = (int) $license_rule['info']['expiry']; 194 | 195 | if ( ! empty( $license['info']['child_id'] ) ) { 196 | 197 | if ( isset( $license_rule['info']['children'][ $license['info']['child_id'] ]['activation_limit'] ) ) { 198 | 199 | $activation_limit = (int) $license_rule['info']['children'][ $license['info']['child_id'] ]['activation_limit']; 200 | 201 | } 202 | 203 | if ( isset( $license_rule['info']['children'][ $license['info']['child_id'] ]['expiry'] ) ) { 204 | 205 | $expiry = (int) $license_rule['info']['children'][ $license['info']['child_id'] ]['expiry']; 206 | 207 | } 208 | } 209 | 210 | if ( $expiry < 1 || $current_time < strtotime( $license['created'] . ' + ' . $expiry . ' days' ) ) { 211 | 212 | if ( $activation_limit < 1 || count( $license['info']['activations'] ) < $activation_limit || isset( $license['info']['activations'][ $activation_ip_hash ] ) ) { 213 | 214 | if ( $activation_limit > 0 && ( ! isset( $license['info']['activations'][ $activation_ip_hash ]['id'] ) || $license['info']['activations'][ $activation_ip_hash ]['id'] !== $activation_id ) ) { 215 | 216 | $license['info']['activations'][ $activation_ip_hash ]['ip'] = sanitize_text_field( $activation_ip ); 217 | $license['info']['activations'][ $activation_ip_hash ]['id'] = sanitize_text_field( $activation_id ); 218 | 219 | tux_su_update_db( array( 220 | 'id' => $license['id'], 221 | 'user_id' => $license['user_id'], 222 | 'product_id' => $license['product_id'], 223 | 'type' => 'license', 224 | 'info' => $license['info'], 225 | ) ); 226 | 227 | } 228 | 229 | $updates[ $license_rule['product_id'] ]['package'] = $license_rule['info']['file_url']; 230 | $updates[ $license_rule['product_id'] ]['url'] = $license_rule['info']['product_url']; 231 | $updates[ $license_rule['product_id'] ]['new_version'] = $license_rule['info']['version']; 232 | $updates[ $license_rule['product_id'] ]['autoupdate'] = 1 === (int) $license_rule['info']['autoupdate'] ? true : false; 233 | $updates[ $license_rule['product_id'] ]['tested'] = $license_rule['info']['compatible']; 234 | 235 | if ( $expiry < 1 ) { 236 | 237 | $updates[ $license_rule['product_id'] ]['expires'] = -1; 238 | 239 | } else { 240 | 241 | $updates[ $license_rule['product_id'] ]['expires'] = ceil( ( strtotime( $license['created'] . ' + ' . $expiry . ' days' ) - $current_time ) / DAY_IN_SECONDS ); 242 | 243 | } 244 | } 245 | } else { 246 | 247 | $delete_ids[] = $license['id']; 248 | 249 | } // End if(). 250 | } // End if(). 251 | } // End foreach(). 252 | 253 | if ( ! empty( $delete_ids ) ) { 254 | 255 | tux_su_delete_db( $delete_ids ); 256 | 257 | } 258 | } else { 259 | 260 | $updates = new WP_Error( 'ID_VERSION_MISMATCH', esc_html( 'Count of product ids does not match count of versions.', 'tuxedo-software-updater' ), array( 'status' => 200 ) ); 261 | 262 | } // End if(). 263 | 264 | if ( ! empty( $tux_su_settings['tracking'] ) ) { 265 | 266 | tux_su_update_db( array( 267 | 'user_id' => empty( $user['id'] ) ? 0 : $user['id'], 268 | 'info' => array( 269 | 'ip' => sanitize_text_field( $activation_ip ), 270 | 'request' => array( 271 | 'update_key' => sanitize_text_field( $update_key ), 272 | 'ids' => implode( ',', $product_ids ), 273 | 'versions' => implode( ',', $product_versions ), 274 | 'activation_id' => $activation_id, 275 | ), 276 | 'user_agent' => sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ), 277 | 'response' => $updates, 278 | ), 279 | ), 'tracking' ); 280 | 281 | } 282 | 283 | return $updates; 284 | 285 | } 286 | -------------------------------------------------------------------------------- /tuxedo-su-update-key.php: -------------------------------------------------------------------------------- 1 | get_results( $wpdb->prepare( "SELECT user_id AS id, meta_value AS update_key FROM {$wpdb->usermeta} WHERE meta_key = '_tux_su_update_key' AND (meta_value = '%s' OR meta_value = '%s')", $key, $key . '-disabled' ), ARRAY_A ); 51 | 52 | if ( is_array( $key_info ) ) { 53 | 54 | $key_info = reset( $key_info ); 55 | 56 | } 57 | 58 | if ( empty( $key_info['id'] ) ) { 59 | 60 | return false; 61 | 62 | } 63 | 64 | $key_info['disabled'] = strpos( $key_info['update_key'], '-disable' ) !== false ? true : false; 65 | 66 | unset( $key_info['update_key'] ); 67 | 68 | return $key_info; 69 | 70 | } 71 | 72 | /** 73 | * Generate update key for user. 74 | * 75 | * Key will be updated in user meta as well. 76 | * 77 | * @param int $user_id User id. 78 | * 79 | * @return string Update key. 80 | */ 81 | function tux_su_generate_update_key( $user_id = 0 ) { 82 | 83 | global $wpdb; 84 | 85 | $key_is_unique = false; 86 | 87 | while ( ! $key_is_unique ) { 88 | 89 | $key = sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', 90 | mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), 91 | mt_rand( 0, 0xffff ), 92 | mt_rand( 0, 0x0fff ) | 0x4000, 93 | mt_rand( 0, 0x3fff ) | 0x8000, 94 | mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) 95 | ); 96 | 97 | $key_query = $wpdb->get_var( $wpdb->prepare( "SELECT umeta_id FROM {$wpdb->usermeta} WHERE meta_key = '_tux_su_update_key' AND meta_value = '%s'", $key ) ); 98 | 99 | if ( empty( $key_query ) ) { 100 | 101 | $key_is_unique = true; 102 | 103 | } 104 | } 105 | 106 | if ( ! empty( $user_id ) ) { 107 | 108 | update_user_meta( $user_id, '_tux_su_update_key', $key ); 109 | 110 | } 111 | 112 | return $key; 113 | 114 | } 115 | -------------------------------------------------------------------------------- /tuxedo-su-woo.php: -------------------------------------------------------------------------------- 1 | __( 'Update Licenses', 'tuxedo-software-updater' ), ); 27 | $array += array_slice( $items, $position, count( $items ) - $position, true ); 28 | 29 | return $array; 30 | 31 | } 32 | 33 | add_filter( 'woocommerce_account_menu_items', 'tux_su_woo_my_account_tabs' ); 34 | 35 | /** 36 | * Add update-license endpoint. 37 | * 38 | * @since 1.0.0 39 | */ 40 | function tux_su_woo_add_endpoints() { 41 | 42 | add_rewrite_endpoint( 'update-licenses', EP_ROOT | EP_PAGES ); 43 | 44 | } 45 | 46 | add_action( 'init', 'tux_su_woo_add_endpoints' ); 47 | 48 | /** 49 | * Add update-license query var. 50 | * 51 | * @since 1.0.0 52 | * 53 | * @param array $vars Query vars. 54 | * 55 | * @return array 56 | */ 57 | function tux_su_woo_add_query_vars( $vars ) { 58 | 59 | $vars[] = 'update-licenses'; 60 | 61 | return $vars; 62 | 63 | } 64 | 65 | add_filter( 'query_vars', 'tux_su_woo_add_query_vars', 0 ); 66 | 67 | /** 68 | * Add 'Update Licenses' title to endpoint. 69 | * 70 | * @since 1.0.0 71 | * 72 | * @param string $title Title. 73 | * 74 | * @return string|void 75 | */ 76 | function tux_su_woo_endpoint_title( $title ) { 77 | 78 | global $wp_query; 79 | 80 | $is_endpoint = isset( $wp_query->query_vars['update-licenses'] ); 81 | 82 | if ( $is_endpoint && ! is_admin() && is_main_query() && in_the_loop() && is_account_page() ) { 83 | 84 | $title = __( 'Update Licenses', 'tuxedo-software-updater' ); 85 | 86 | remove_filter( 'the_title', 'tux_su_woo_endpoint_title' ); 87 | 88 | } 89 | 90 | return $title; 91 | 92 | } 93 | 94 | add_filter( 'the_title', 'tux_su_woo_endpoint_title' ); 95 | 96 | /** 97 | * WooCommerce update licensing account page. 98 | * 99 | * @since 1.0.0 100 | */ 101 | function tux_su_woo_endpoint_content() { 102 | 103 | if ( ! is_user_logged_in() ) { 104 | 105 | return; 106 | 107 | } 108 | 109 | $update_key = get_user_meta( get_current_user_id(), '_tux_su_update_key', true ); 110 | 111 | if ( empty( $update_key ) ) { 112 | 113 | $update_key = tux_su_generate_update_key( get_current_user_id() ); 114 | 115 | } 116 | 117 | if ( strpos( $update_key, '-disabled' ) !== false ) { 118 | 119 | esc_html_e( 'You\'re update key has been disabled. Please contact us with any questions or for more information.', 'tuxedo-software-updater' ); 120 | 121 | return; 122 | 123 | } 124 | 125 | ?> 126 | 127 | 128 | 129 | 132 | 133 | 134 | 135 | 136 | 139 | 140 | 141 |
130 | 131 |
137 | 138 |
142 | get_current_user_id(), 146 | 'type' => 'license', 147 | ) ); 148 | 149 | if ( empty( $licenses ) ) { 150 | 151 | return; 152 | 153 | } 154 | 155 | $product_ids = array(); 156 | 157 | foreach ( $licenses as $license ) { 158 | 159 | $product_ids[] = absint( $license['product_id'] ); 160 | 161 | } 162 | 163 | $license_rules = array(); 164 | 165 | if ( count( $product_ids ) > 0 ) { 166 | 167 | $license_rules = tux_su_get_db( array( 168 | 'product_id' => $product_ids, 169 | 'type' => 'rule', 170 | ) ); 171 | 172 | } 173 | 174 | $delete_ids = array(); 175 | 176 | foreach ( $licenses as $license ) { 177 | 178 | ?> 179 | 180 | 181 | strtotime( $license['created'] . ' + ' . $expiry . ' days' ) ) { 245 | 246 | $delete_ids[] = $license['id']; 247 | continue; 248 | 249 | } 250 | 251 | if ( isset( $_GET['delnonce'], $_GET['action'], $_GET['license_id'], $_GET['activation_id'], $license['info']['activations'] ) && 'delete' === $_GET['action'] && absint( $license['id'] ) === absint( $_GET['license_id'] ) && wp_verify_nonce( $_GET['delnonce'], 'tux_delete_activation' ) ) { 252 | 253 | unset( $license['info']['activations'][ sanitize_text_field( $_GET['activation_id'] ) ] ); 254 | 255 | tux_su_update_db( array( 256 | 'id' => $license['id'], 257 | 'user_id' => $license['user_id'], 258 | 'product_id' => $license['product_id'], 259 | 'type' => 'license', 260 | 'info' => $license['info'], 261 | ) ); 262 | 263 | } 264 | 265 | ?> 266 | 267 | 270 | 271 | 272 | 273 | 274 | 277 | 280 | 281 | 282 | 285 | 288 | 289 | $activation ) : ?> 291 | 292 | 295 | 300 | 301 |
268 | 269 |
275 | 276 | 278 | 279 |
283 | 284 | 286 | 287 |
293 | . 294 | 296 | 297 | 298 | 299 |
'; 305 | 306 | } // End foreach(). 307 | 308 | if ( ! empty( $delete_ids ) ) { 309 | 310 | tux_su_delete_db( $delete_ids ); 311 | 312 | } 313 | 314 | } 315 | 316 | add_action( 'woocommerce_account_update-licenses_endpoint', 'tux_su_woo_endpoint_content' ); 317 | 318 | /** 319 | * Create a license when an order is completed. 320 | * 321 | * @since 1.0.0 322 | * 323 | * @param int $order_id Order ID. 324 | */ 325 | function tux_su_woo_create_license_on_order_completed( $order_id ) { 326 | 327 | $order = new WC_Order( $order_id ); 328 | $user_id = $order->get_customer_id(); 329 | $order_items = $order->get_items(); 330 | $product_ids = array(); 331 | $order_note = ''; 332 | $date_completed = $order->get_date_completed(); 333 | 334 | if ( empty( $date_completed ) ) { 335 | 336 | return; 337 | 338 | } 339 | 340 | if ( null !== $date_completed ) { 341 | 342 | $date_completed = $date_completed->date( 'Y-m-d' ); 343 | 344 | } 345 | 346 | foreach ( $order_items as $order_item ) { 347 | 348 | $product_ids[] = $order_item['product_id']; 349 | 350 | } 351 | 352 | $licenses = tux_su_get_db( array( 353 | 'user_id' => array( 0, $user_id ), 354 | 'product_id' => $product_ids, 355 | 'order' => 'type ASC', 356 | ) ); 357 | 358 | foreach ( $order_items as $order_item ) { 359 | 360 | $license_rule = array(); 361 | $license_exists = array(); 362 | $license_update_id = false; 363 | 364 | foreach ( $licenses as $license ) { 365 | 366 | if ( (int) $order_item['product_id'] === (int) $license['product_id'] ) { 367 | 368 | // Is rule. 369 | if ( 1 === (int) $license['type'] && empty( $license_rule ) ) { 370 | 371 | $license['info'] = maybe_unserialize( $license['info'] ); 372 | $license_rule = $license; 373 | continue; 374 | 375 | } 376 | 377 | // Is license. 378 | if ( 2 === (int) $license['type'] && empty( $license_exists ) ) { 379 | 380 | $license['info'] = maybe_unserialize( $license['info'] ); 381 | 382 | if ( isset( $license['info']['order_id'] ) && (int) $license['info']['order_id'] === (int) $order_id ) { 383 | 384 | $license_exists = $license; 385 | break; 386 | 387 | } 388 | } 389 | } 390 | } 391 | 392 | if ( ! empty( $license_rule['info']['open_update'] ) ) { 393 | 394 | continue; 395 | 396 | } 397 | 398 | if ( ! empty( $license_rule ) && empty( $license_exists ) ) { 399 | 400 | $user = get_userdata( $user_id ); 401 | $user_name = $user->user_login; 402 | 403 | $license_update_id = tux_su_update_db( array( 404 | 'user_id' => $user_id, 405 | 'product_id' => $order_item['product_id'], 406 | 'type' => 'license', 407 | 'info' => array( 408 | 'child_id' => isset( $order_item['variation_id'] ) ? (int) $order_item['variation_id'] : 0, 409 | 'order_id' => absint( $order_id ), 410 | 'user_name' => wp_strip_all_tags( $user_name ), 411 | 'activations' => array(), 412 | ), 413 | 'created' => $date_completed, 414 | ) ); 415 | 416 | } 417 | 418 | if ( false !== $license_update_id ) { 419 | 420 | if ( empty( $order_note ) ) { 421 | 422 | $order_note = __( 'Tuxedo Software Update Licensing', 'tuxedo-software-updater' ) . "\n"; 423 | 424 | } 425 | 426 | /* translators: %s: order item name */ 427 | $order_note .= '* ' . sprintf( __( 'Updated license for %s.', 'tuxedo-software-updater' ), wp_strip_all_tags( $order_item['name'] ) ) . "\n"; 428 | 429 | } elseif ( ! empty( $license_rule ) && empty( $license_exists ) ) { 430 | 431 | if ( empty( $order_note ) ) { 432 | 433 | $order_note = __( 'Tuxedo Software Update Licensing', 'tuxedo-software-updater' ) . "\n"; 434 | 435 | } 436 | 437 | /* translators: %s: order item name */ 438 | $order_note .= '* ' . sprintf( __( 'Error updating license for %s.', 'tuxedo-software-updater' ), wp_strip_all_tags( $order_item['name'] ) ) . "\n"; 439 | 440 | } 441 | } // End foreach(). 442 | 443 | if ( ! empty( $order_note ) ) { 444 | 445 | $order->add_order_note( $order_note ); 446 | 447 | } 448 | } 449 | 450 | add_action( 'woocommerce_order_status_completed', 'tux_su_woo_create_license_on_order_completed' ); 451 | --------------------------------------------------------------------------------