├── .gitignore ├── LICENSE ├── README.md ├── api.php ├── composer.json ├── config.ini └── utilities └── import.php /.gitignore: -------------------------------------------------------------------------------- 1 | test.php 2 | config.local.ini 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andrew Gioia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EchoMTG PHP Library 2 | Basic wrapper for the [EchoMTG](https://www.echomtg.com) REST API to manage Magic: the Gathering card collections, prices, and values. See more information on API developments at [https://www.echomtg.com/api](https://www.echomtg.com/api). 3 | 4 | This PHP library is currently in development/beta testing and provided as-is. 5 | 6 | ## Installation 7 | 8 | Make sure you have an EchoMTG account (you can register for free). The API does not currently support OAuth so all authentication is done via encrypted posts. 9 | 10 | Copy the `config.ini` file and rename it `config.local.ini`, then set the account email and password there. Make sure the `api.php` library file is in the same folder as the .ini file in your project and instantiate a new object: 11 | 12 | require 'api.php'; 13 | 14 | $echomtg = new EchoPHP(); 15 | $echomtg->initSession(); 16 | 17 | Note: `initSession()` will check to see if your auth token has been saved to your current session; if it isn't, it posts to `/user/auth` to sign you in. 18 | 19 | ## Usage 20 | 21 | This wrapper class is in development, check below for the currently supported API calls. 22 | 23 | ### Managing Inventory 24 | 25 | #### Adding cards to inventory 26 | 27 | To add a card individually, use the `addCard()` method: 28 | 29 | $echomtg->addCard( 4797, 1, 1.50, '08-20-2015', 0 ); 30 | 31 | The only required parameter is the first one, the card's [Mutiverse ID](http://gatherer.wizards.com), i.e., it's ID in Gatherer. There are two ways to get this: 32 | 33 | 1. The preferred method is EchoMTG's card reference call, accessible here via the `cardReference()` method. This reference is important because Promo cards **do not** have a Multiverse ID but they are assigned them by EchoMTG in this reference, allowing you to add promo cards programmatically. This method takes a string for the card name and, optionally, set code to search and returns the ID: 34 | 35 | $echomtg->cardReference( 'Verdant Force', 'tmp' ); 36 | 37 | 2. Alternatively you can get Multiverse ID's by hand by searching for the card/printing at Gatherer or using the [MTGJson](http://mtgjson.com) API (or similar services). 38 | 39 | The other parameters are quantity, your purchase price, the date of your purchase in mm-dd-yyyy format, and whether the card is foil (1) or not (0). 40 | 41 | #### Removing cards from the inventory 42 | 43 | To remove a card by the inventory ID, use `removeCard()`. It only takes the EchoMTG inventory ID ("EID") of the card in your inventory. 44 | 45 | #### Adjusting the acquisition price 46 | 47 | To adjust the acquisition price of a card in inventory, call `adjustAcquiredPrice()`, passing in the card's EID in your inventory and the price you want to set. 48 | 49 | #### Toggling foil status 50 | 51 | Set a card in inventory as a foil (1) or not foil (0) by calling `toggleFoil()`, passing in the card's EID and the foil boolean. 52 | 53 | #### Adjusting the acquisition date 54 | 55 | To adjust the acquisition date of a card in inventory, call `adjustAcquiredDate()`, passing in the card's EID and the date you want to set in `MM-DD-YYYY` format (`m-d-Y` in PHP `date()` lingo). 56 | 57 | ### Viewing Inventory 58 | 59 | #### Getting the inventory 60 | 61 | Use the `getInventory()` method to return the user's inventory. By default the method will return the most recently acquired 10 cards. 62 | 63 | You can pass in parameters for start and end values to limit the results, the attribute to **sort** on (`price, cmc, foil_price, date_aquired, set`), sort **order** (`desc, asc`), a **card name** to search, **color** (`Colorless, Multicolor, White, Blue, Black, Red, Green, Land`), **card type** (`Planeswalker, Sorcery, Instant, Creature, Artifact, Enchantment, Legendary, Land`), and **set code**. 64 | 65 | E.g., the following returns the most recent 10 cards that are green legends, sorted by price descending: 66 | 67 | $echomtg->getInventory( 0, 9, 'price', 'desc', null, 'Green', 'Legendary' ); 68 | 69 | #### Getting inventory statistics 70 | 71 | Call the `getStats()` method to return the user's inventory stats. This takes no parameters and sends an authenticated GET. 72 | 73 | ### Lists 74 | 75 | #### Get a specific list 76 | 77 | With the list ID you can get a specific list and its attributes/cards by calling the `getList()` method and passing the list ID in. There is an optional second parameter that will return the list and cards formatted in HTML when you pass `true` in. 78 | 79 | #### Return all user's lists 80 | 81 | To get the full list of lists for a user call the `getAllLists()` method. This only takes one parameter for sort order, which can be `created`, `alpha_desc`, `alpha_asc`, or `last_edited`. The default is last_edited. 82 | 83 | #### Create a list 84 | 85 | Call the `createList()` method to create a new list. This requires a name for the list as the first parameter, with an optional description as the second parameter. 86 | 87 | This method returns the list ID of the newly created list, if it was successful. 88 | 89 | #### Edit a list 90 | 91 | You can edit a list if you have the list ID by calling the `editList()` method. It requires the list ID as the first parameter, then the name and description as the next two if you want to edit them. 92 | 93 | #### Toggle a list as activated or deactivated 94 | 95 | To toggle a list's status as activated or deactivated, call the `toggleListStatus()` method with the list ID as the first parameter. Pass in a `1` to flag it as active or a `0` to flag it as deactivated. 96 | 97 | ## Debugging 98 | 99 | To enable debugging mode, set the class property `$debug_mode` to true in `api.php`. This adds all auth, session, and requests/responses to the `$debug` property. Output that to view the current debugging log: 100 | 101 | print_r( $echomtg->debug ); 102 | -------------------------------------------------------------------------------- /api.php: -------------------------------------------------------------------------------- 1 | config = parse_ini_file( 'config.local.ini' ); 36 | $this->email = $this->config[ 'email' ]; 37 | $this->password = $this->config[ 'password' ]; 38 | } 39 | 40 | /** 41 | * Get the authentication token from the session, 42 | * or create the session and authenticate 43 | * 44 | * @return boolean 45 | */ 46 | public function initSession() 47 | { 48 | $session = session_id(); 49 | if ( empty( $session ) ) 50 | { 51 | session_start(); 52 | } 53 | 54 | if ( ! isset( $_SESSION[ 'echophp_session' ] ) || $_SESSION[ 'echophp_session' ] == '' ) 55 | { 56 | $token = $this->authenticate(); 57 | if ( $token ) 58 | { 59 | $this->auth_token = $token; 60 | $_SESSION[ 'echophp_session' ] = $token; 61 | $this->debugInfo( [ 'session' => [ 'login', $_SESSION[ 'echophp_session' ] ] ] ); 62 | return true; 63 | } 64 | else 65 | { 66 | $this->postError( [ 67 | 'status' => 'error', 68 | 'message' => 'Unable to authentication, incorrect email or password' ] ); 69 | $this->debugInfo( [ 'session' => [ 'error', $_SESSION[ 'echophp_session' ] ] ] ); 70 | return false; 71 | } 72 | } 73 | else 74 | { 75 | $this->auth_token = $_SESSION[ 'echophp_session' ]; 76 | $this->debugInfo( [ 'session' => [ 'saved', $_SESSION[ 'echophp_session' ] ] ] ); 77 | return true; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | /** 84 | * Destroy the authentication token and "log out" 85 | * 86 | * @return void 87 | */ 88 | public function logout() 89 | { 90 | session_start(); 91 | unset( $_SESSION[ 'echophp_session' ] ); 92 | session_destroy(); 93 | $this->debugInfo( [ 'session' => $_SESSION[ 'echophp_session' ] ] ); 94 | } 95 | 96 | /** 97 | * MANAGE INVENTORY */ 98 | 99 | /** 100 | * Add X of one card to Inventory 101 | * @param int $m_id (card's multiverse ID) 102 | * @param int $quantity (amount to add) 103 | * @param float $acquired_price (set the purchase price) 104 | * @param string $acquired_date (set the date of purchase, MM-DD-YYYY 105 | * @param boolean $foil (flag for whether the card is foil) 106 | * @return array (response) 107 | */ 108 | public function addCard( 109 | $mid, 110 | $quantity = 1, 111 | $acquired_price = false, 112 | $acquired_date = false, 113 | $foil = 0 ) 114 | { 115 | // check that the multiverse ID is correct 116 | if ( is_numeric( $mid ) && strlen( $mid ) > 0 ) 117 | { 118 | $card[ 'mid' ] = $mid; 119 | } 120 | else 121 | { 122 | $this->postError( [ 123 | 'status' => 'error', 124 | 'message' => 'Invalid card multiverse ID' ] ); 125 | } 126 | 127 | // add the remaining fields 128 | $card[ 'quantity' ] = $quantity; 129 | $card[ 'foil' ] = $foil; 130 | if ( $acquired_price ) $card[ 'acquired_price' ] = $acquired_price; 131 | if ( $acquired_date ) $card[ 'acquired_date' ] = $acquired_date; 132 | 133 | // attempt to add the card 134 | $response = $this->sendPost( 135 | 'inventory/add/', 136 | $card, 137 | true, 138 | 'post' ); 139 | 140 | // set some debug logging 141 | $this->debugInfo( [ 'add_card' => $response ] ); 142 | 143 | return $response; 144 | } 145 | 146 | /** 147 | * Remove card from inventory 148 | * 149 | * @param int $eid (echomtg inventory ID for card) 150 | * @return boolean 151 | */ 152 | public function removeCard( $eid ) 153 | { 154 | // check that we have an integer first 155 | $this->checkInventoryID( $eid ); 156 | 157 | // attempt to remove the card 158 | $response = $this->sendPost( 159 | 'inventory/remove/', 160 | [ 'inventory_id' => $eid ], 161 | true, 162 | 'post' ); 163 | 164 | // set some debug logging 165 | $this->debugInfo( [ 'remove_card' => $response ] ); 166 | 167 | return $response; 168 | } 169 | 170 | /** 171 | * Adjust acquisition price of a card in inventory 172 | * NM = Near Mint 173 | * LP = Lightly Played 174 | * MP = Moderately Played 175 | * HP = Heavily Played 176 | * D = Damaged 177 | * ALT = Altered 178 | * SGN = Signed 179 | * 180 | * @param int @eid (echo inventory ID for the card) 181 | * @param float $condition (adjusted condition) 182 | * @return array 183 | */ 184 | public function adjustCondition( $eid, $condition ) 185 | { 186 | // check that we have an integer first 187 | $this->checkInventoryID( $eid ); 188 | // set the parameters 189 | $request = [ 190 | 'id' => $eid, 191 | 'value' => $condition ]; 192 | // attempt to remove the card 193 | $response = $this->sendPost( 194 | 'inventory/change_condition/', 195 | $request, 196 | true, 197 | 'post' ); 198 | // set some debug logging 199 | $this->debugInfo( [ 'adjust_condition' => $response ] ); 200 | return $response; 201 | } 202 | 203 | /** 204 | * Adjust acquisition price of a card in inventory 205 | * 206 | * @param int @eid (echo inventory ID for the card) 207 | * @param float $price (adjusted acquisition price) 208 | * @return array 209 | */ 210 | public function adjustAcquiredPrice( $eid, $price ) 211 | { 212 | // check that we have an integer first 213 | $this->checkInventoryID( $eid ); 214 | 215 | // set the parameters 216 | $request = [ 217 | 'id' => $eid, 218 | 'adjusted_price' => $price ]; 219 | 220 | // attempt to remove the card 221 | $response = $this->sendPost( 222 | 'inventory/adjust/', 223 | $request, 224 | true, 225 | 'post' ); 226 | 227 | // set some debug logging 228 | $this->debugInfo( [ 'adjust_price' => $response ] ); 229 | 230 | return $response; 231 | } 232 | 233 | /** 234 | * Toggle the foil status of a card in inventory 235 | * 236 | * @param int @eid (echo inventory ID for the card) 237 | * @param boolean $foil (0 for nonfoil, 1 for foil) 238 | * @return array 239 | */ 240 | public function toggleFoil( $eid, $foil = 1 ) 241 | { 242 | // check that we have an integer first 243 | $this->checkInventoryID( $eid ); 244 | 245 | // make sure it's a 0 or 1 246 | $foil = ( $foil == 0 ) ? $foil : 1; 247 | 248 | // set the parameters 249 | $request = [ 250 | 'id' => $eid, 251 | 'foil' => $foil ]; 252 | 253 | // attempt to remove the card 254 | $response = $this->sendPost( 255 | 'inventory/toggle_foil/', 256 | $request, 257 | true, 258 | 'post' ); 259 | 260 | // set some debug logging 261 | $this->debugInfo( [ 'toggle_foil' => $response ] ); 262 | 263 | return $response; 264 | } 265 | 266 | /** 267 | * Adjust acquisition price of a card in inventory 268 | * 269 | * @param int @eid (echo inventory ID for the card) 270 | * @param string $date (date of card acquisition, in MM-DD-YYYY) 271 | * @return array 272 | */ 273 | public function adjustAcquiredDate( $eid, $date ) 274 | { 275 | // check that we have an integer first 276 | $this->checkInventoryID( $eid ); 277 | 278 | // format the date correctly 279 | $date = date( 'm-d-Y', strtotime( $date ) ); 280 | 281 | // set the parameters 282 | $request = [ 283 | 'id' => $eid, 284 | 'value' => $date ]; 285 | 286 | // attempt to remove the card 287 | $response = $this->sendPost( 288 | 'inventory/adjust_date/', 289 | $request, 290 | true, 291 | 'post' ); 292 | 293 | // set some debug logging 294 | $this->debugInfo( [ 'adjust_date' => $response ] ); 295 | 296 | return $response; 297 | } 298 | 299 | 300 | /** 301 | * INVENTORY */ 302 | 303 | /** 304 | * Get user's inventory 305 | * 306 | * @param int $start 307 | * @param int $end 308 | * @param string $sort (price, cmc, foil_price, date_aquired, set) 309 | * @param string $order (asc, desc) 310 | * @param string $search (card name to search) 311 | * @param string $color (Colorless, Multicolor, White, Blue, Black, Red, Green, Land) 312 | * @param string $type (Planeswalker, Sorcery, Instant, Creature, Artifact, 313 | * Enchantment, Legendary, Land) 314 | * @param string $set_code (if showing only from a set) 315 | * @return array 316 | */ 317 | public function getInventory( 318 | $start = 0, 319 | $end = 9, 320 | $sort = 'date_acquired', 321 | $order = 'desc', 322 | $search = false, 323 | $color = false, 324 | $type = false, 325 | $set_code = false ) 326 | { 327 | // check that start and end are integers 328 | if ( ! is_int( $start ) || ! is_int( $end ) ) 329 | { 330 | $this->postError( [ 331 | 'status' => 'error', 332 | 'message' => 'Invalid card multiverse ID' ] ); 333 | } 334 | 335 | // set the base data fields 336 | $query[ 'start' ] = $start; 337 | $query[ 'limit' ] = $end; 338 | $query[ 'sort' ] = $sort; 339 | $query[ 'order' ] = $order; 340 | 341 | // searching by card name 342 | if ( $search ) $query[ 'search' ] = $search; 343 | 344 | // filtering by a color 345 | $color_options = [ 346 | 'Colorless', 'Multicolor', 'White', 'Blue', 'Black', 'Red', 347 | 'Green', 'Land' ]; 348 | if ( $color ) 349 | { 350 | if ( in_array( $color, $color_options ) ) 351 | { 352 | $query[ 'color' ] = $color; 353 | } 354 | } 355 | 356 | // filtering by type 357 | $type_options = [ 358 | 'Planeswalker', 'Sorcery', 'Instant', 'Creature', 'Artifact', 359 | 'Enchantment', 'Legendary', 'Land' ]; 360 | if ( $type ) 361 | { 362 | if ( in_array( $type, $type_options ) ) 363 | { 364 | $query[ 'type' ] = $type; 365 | } 366 | } 367 | 368 | // filtering by set code 369 | if ( $set_code ) $query[ 'set_code' ] = $set_code; 370 | 371 | // attempt to add the card 372 | $response = $this->sendPost( 373 | 'inventory/view/', 374 | $query, 375 | true, 376 | 'get' ); 377 | 378 | // set some debug logging 379 | $this->debugInfo( [ 'view_inventory' => $response ] ); 380 | 381 | return $response; 382 | } 383 | 384 | /** 385 | * Return the user's inventory statistics 386 | * 387 | * @return array 388 | */ 389 | public function getStats() 390 | { 391 | // make the request 392 | $response = $this->sendPost( 393 | 'inventory/stats/', 394 | [], 395 | true, 396 | 'get' ); 397 | 398 | // set some debug logging 399 | $this->debugInfo( [ 'inventory_stats' => $response ] ); 400 | 401 | return $response; 402 | } 403 | 404 | 405 | /** 406 | * CARD REFERENCE */ 407 | 408 | /** 409 | * Search EchoMTG's card list to return the appropriate ID 410 | * 411 | * @param string $cardname (card name to search) 412 | * @param string $setcode (optional set code, otherwise first match returns) 413 | * @return int $mid 414 | */ 415 | public function cardReference( $cardname = '', $setcode = false ) 416 | { 417 | // make sure a card name was passed in 418 | if ( trim( $cardname ) == '' || strlen( trim( $cardname ) ) == 0 ) 419 | { 420 | $this->postError( [ 421 | 'status' => 'error', 422 | 'message' => 'You need to pass a card name to search' ] ); 423 | } 424 | 425 | // set the request fields 426 | $request[ 'type' ] = 'json'; 427 | $request[ 'name' ] = $cardname; // does not do anything 428 | 429 | // pull in the master list 430 | $cards = $this->sendPost( 431 | 'data/card_reference/', 432 | $request, 433 | true, 434 | 'get' ); 435 | 436 | // currently we have to iterate over this object to search 437 | // the api call needs to take a parameter to search in the future 438 | $results = []; 439 | foreach ( $cards->cards as $mid => $card ) 440 | { 441 | if ( $card->name == $cardname ) 442 | { 443 | $results[ $mid ] = $card; 444 | } 445 | } 446 | 447 | // if we have a set code passed in, return just that row; 448 | // otherwise return the earliest printing (lowest ID) 449 | if ( count( $results ) > 0 ) 450 | { 451 | if ( $setcode ) 452 | { 453 | foreach ( $results as $id => $result ) 454 | { 455 | if ( strtolower( $result->set_code ) == strtolower( $setcode ) ) 456 | { 457 | return $id; 458 | } 459 | } 460 | $this->postError( [ 461 | 'status' => 'error', 462 | 'message' => 'No cards matched your search.' ] ); 463 | } 464 | else 465 | { 466 | ksort( $results ); 467 | return array_keys( $results )[ 0 ]; 468 | } 469 | } 470 | else 471 | { 472 | $this->postError( [ 473 | 'status' => 'error', 474 | 'message' => 'No cards matched your search.' ] ); 475 | } 476 | } 477 | 478 | 479 | /** 480 | * LISTS */ 481 | 482 | /** 483 | * Get a specific list 484 | * 485 | * @param int $lid (list ID) 486 | * @param $html (flag to return preformatted HTML view) 487 | * @return array (list of lists) 488 | */ 489 | public function getList( $lid = false, $html = false ) 490 | { 491 | // validate the list id 492 | $this->checkListID( $lid ); 493 | 494 | // set the fields 495 | $fields = [ 496 | 'list' => $lid, 497 | 'view' => ( $html ) ? 'true' : 'false' ]; 498 | 499 | // make the request 500 | $response = $this->sendPost( 501 | 'lists/get/', 502 | $fields, 503 | true, 504 | 'get' ); 505 | 506 | // set some debug logging 507 | $this->debugInfo( [ 'get_list' => $response ] ); 508 | 509 | return $response; 510 | } 511 | 512 | /** 513 | * Get all of the user's lists 514 | * 515 | * @param string $order (optional sort order) 516 | * @return array 517 | */ 518 | public function getAllLists( $order = 'last_edited' ) 519 | { 520 | // set the optional sort order 521 | $order_options = [ 'created', 'alpha_desc', 'alpha_asc', 'last_edited' ]; 522 | $fields[ 'order' ] = ( ! in_array( $order, $order_options ) ) 523 | ? 'last_edited' 524 | : $order; 525 | 526 | // send the request 527 | $response = $this->sendPost( 528 | 'lists/all/', 529 | $fields, 530 | true, 531 | 'get' ); 532 | 533 | // set some debug logging 534 | $this->debugInfo( [ 'all_lists' => $response ] ); 535 | 536 | // cast as array and return just the lists 537 | return (array)$response->lists; 538 | } 539 | 540 | /** 541 | * Create a new list 542 | * 543 | * @param string $name (name of the list) 544 | * @param string $description (description of the list) 545 | * @return int $id (of newly created list) 546 | */ 547 | public function createList( $name = '', $description = '' ) 548 | { 549 | // make sure we have a name 550 | if ( trim( $name ) == '' || strlen( trim( $name ) ) == 0 ) 551 | { 552 | $this->postError( [ 553 | 'status' => 'error', 554 | 'message' => 'You need to supply a name for the list' ] ); 555 | } 556 | 557 | // set the request fields 558 | $fields[ 'name' ] = $name; 559 | $fields[ 'description' ] = $description; 560 | 561 | // send the request 562 | $response = $this->sendPost( 563 | 'lists/create/', 564 | $fields, 565 | true, 566 | 'post' ); 567 | 568 | // set some debug logging 569 | $this->debugInfo( [ 'create_list' => $response ] ); 570 | 571 | // if we have a successful new list, get the ID 572 | if ( isset( $response->status ) && $response->status == 'success' ) 573 | { 574 | $all_lists = $this->getAllLists(); 575 | rsort( $all_lists ); 576 | if ( is_array( $all_lists ) && count( $all_lists ) > 0 ) 577 | { 578 | $newest_list = array_values( $all_lists )[ 0 ]; 579 | return ( isset( $newest_list->id ) ) 580 | ? $newest_list->id 581 | : false; 582 | } 583 | else 584 | { 585 | $this->postError( [ 586 | 'status' => 'error', 587 | 'message' => 'Error retreiving new list id; list was created.' ] ); 588 | } 589 | } 590 | else 591 | { 592 | $this->postError( [ 593 | 'status' => 'error', 594 | 'message' => 'Error creating the new list' ] ); 595 | } 596 | } 597 | 598 | /** 599 | * Edit the name or description of a list 600 | * 601 | * @param int $lid (ID of the list to edit) 602 | * @param string $name (name of the list) 603 | * @param string $description (description of the list) 604 | * @return boolean 605 | */ 606 | public function editList( $lid, $name = false, $description = false ) 607 | { 608 | // validate the list id 609 | $this->checkListID( $lid ); 610 | 611 | // set the list ID 612 | $fields[ 'list' ] = $lid; 613 | 614 | // if a name is set, make sure it isn't blank 615 | if ( $name ) 616 | { 617 | if ( trim( $name ) == '' || strlen( trim( $name ) ) == 0 ) 618 | { 619 | $this->postError( [ 620 | 'status' => 'error', 621 | 'message' => 'You need to supply a name for the list' ] ); 622 | } 623 | else 624 | { 625 | $fields[ 'name' ] = $name; 626 | } 627 | } 628 | 629 | // no need to validate the description, just pass it if it exists 630 | if ( $description ) 631 | { 632 | $fields[ 'description' ] = $description; 633 | } 634 | 635 | // send the request 636 | $response = $this->sendPost( 637 | 'lists/edit/', 638 | $fields, 639 | true, 640 | 'post' ); 641 | 642 | // set some debug logging 643 | $this->debugInfo( [ 'edit_list' => $response ] ); 644 | 645 | // return true on success, otherwise false 646 | return $this->returnStatus( $response ); 647 | } 648 | 649 | /** 650 | * Toggle a list's activated status (active or deactivated) 651 | * 652 | * @param int $lid (the ID of the list to toggle) 653 | * @param boolean $status (0 for deactivated, 1 for active) 654 | * @return boolean (true for success, false for failure) 655 | */ 656 | public function toggleListStatus( $lid, $status = 1 ) 657 | { 658 | // validate the list id 659 | $this->checkListID( $lid ); 660 | 661 | // create the field array 662 | $fields[ 'list' ] = $lid; 663 | $fields[ 'status' ] = ( in_array( $status, [ 0, 1 ] ) ) 664 | ? $status 665 | : 1; 666 | 667 | // send the request 668 | $response = $this->sendPost( 669 | 'lists/toggle_status/', 670 | $fields, 671 | true, 672 | 'post' ); 673 | 674 | // set some debug logging 675 | $this->debugInfo( [ 'toggle_list_status' => $response ] ); 676 | 677 | // return true on success, otherwise false 678 | return $this->returnStatus( $response ); 679 | } 680 | 681 | 682 | /** 683 | * UTILITIES */ 684 | 685 | /** 686 | * Send an authentication request 687 | * 688 | * @return string (response token) 689 | */ 690 | private function authenticate() 691 | { 692 | // attempt to login 693 | $response = $this->sendPost( 694 | 'user/auth/', 695 | [ 'email' => $this->email, 'password' => $this->password ], 696 | false, 697 | 'post' ); 698 | 699 | // set some debug logging 700 | $this->debugInfo( [ 'auth' => $response ] ); 701 | 702 | // return the token if we have it 703 | if ( isset( $response->status ) && $response->status == 'success' ) 704 | { 705 | return $response->token; 706 | } 707 | else 708 | { 709 | return false; 710 | } 711 | } 712 | 713 | /** 714 | * Post to an EchoMTG API endpoint 715 | * 716 | * @param $endpoint (path to api call) 717 | * @param $fields (array of data fields to send in the POST) 718 | * @param $auth (flag for whether this request is authenticated) 719 | * @param $post (flag for sending as a post, otherwise get) 720 | * @return array (json response) 721 | */ 722 | private function sendPost( 723 | $endpoint = false, 724 | $fields = array(), 725 | $auth = true, 726 | $method = 'post' ) 727 | { 728 | // build the query from the data fields 729 | if ( $auth ) { $fields[ 'auth' ] = $this->auth_token; } 730 | $data = http_build_query( $fields ); 731 | 732 | // check for the method 733 | $method = ( ! in_array( $method, [ 'post', 'put', 'get' ] ) ) 734 | ? 'post' 735 | : $method; 736 | 737 | // get the url to post to 738 | $uri = ( $endpoint ) 739 | ? $this->api_host.$endpoint 740 | : $this->api_host; 741 | 742 | // set some debug logging 743 | $this->debugInfo( [ $method => [ $uri, $data ] ] ); 744 | 745 | // create the full request based on post/get/put status 746 | $request = ( $method == 'get' ) 747 | ? $uri.$data 748 | : $uri; 749 | 750 | // create a new cURL resource 751 | $ch = curl_init(); 752 | 753 | // set URL and other appropriate options 754 | curl_setopt( $ch, CURLOPT_URL, $request ); 755 | 756 | // using SSL 757 | curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); 758 | curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false ); 759 | 760 | // post headers 761 | if ( $method == 'post' ) { 762 | curl_setopt( $ch, CURLOPT_POST, 1 ); 763 | curl_setopt( $ch, CURLOPT_POSTFIELDS, $data ); 764 | } 765 | // put headers 766 | else if ( $method == 'put' ) 767 | { 768 | curl_setopt( $ch, CURLOPT_PUT, 1 ); 769 | curl_setopt( $ch, CURLOPT_POSTFIELDS, $data ); 770 | } 771 | 772 | // query and header options 773 | curl_setopt( $ch, CURLOPT_HEADER, false ) ; 774 | curl_setopt( $ch, CURLINFO_HEADER_OUT, true ); 775 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); 776 | 777 | // get it 778 | $response = trim( curl_exec( $ch ) ); 779 | $response = json_decode( $response ); 780 | 781 | // close the connection 782 | curl_close( $ch ); 783 | 784 | return $response; 785 | } 786 | 787 | /** 788 | * Save to the error property 789 | * 790 | * @param array $object (error array with status and message) 791 | * @return void 792 | */ 793 | private function postError( $object ) 794 | { 795 | $this->error = json_encode( $object ); 796 | } 797 | 798 | /** 799 | * Save to the debug property 800 | * 801 | * @param array $object (debug array with status and message) 802 | * @return void 803 | */ 804 | private function debugInfo( $object ) 805 | { 806 | if ( $this->config[ 'debug_mode' ] ) 807 | { 808 | $this->debug[] = $object; 809 | } 810 | } 811 | 812 | private function checkInventoryID( $eid ) 813 | { 814 | if ( ! is_int( $eid ) || $eid < 1 ) 815 | { 816 | $this->postError( [ 817 | 'status' => 'error', 818 | 'message' => 'The card ID is not an integer.' ] ); 819 | return false; 820 | } 821 | } 822 | 823 | private function checkListID( $lid ) 824 | { 825 | if ( ! is_int( $lid ) || $lid < 1 ) 826 | { 827 | $this->postError( [ 828 | 'status' => 'error', 829 | 'message' => 'The list ID is not an integer.' ] ); 830 | return false; 831 | } 832 | } 833 | 834 | private function returnStatus( $response ) 835 | { 836 | if ( is_object( $response ) && isset( $response->status ) ) 837 | { 838 | return ( $response->status == 'success' ) 839 | ? true 840 | : false; 841 | } 842 | else 843 | { 844 | return false; 845 | } 846 | } 847 | } 848 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ardeay/echo-mtg-php-wrapper", 3 | "description": "Basic wrapper for the EchoMTG.com REST API", 4 | "require": { 5 | "php": ">=5.3.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | ; Default configuration file 2 | ; Copy this to the api.php directory and rename it config.local.ini, 3 | ; then save your account email and password below 4 | 5 | [user authentication] 6 | email= 7 | password= 8 | 9 | [utilities] 10 | path= 11 | 12 | [debugging] 13 | debug_mode=true 14 | -------------------------------------------------------------------------------- /utilities/import.php: -------------------------------------------------------------------------------- 1 | config->debug_mode ) 11 | { 12 | session_start(); 13 | unset( $_SESSION[ 'echophp_session' ] ); 14 | session_destroy(); 15 | } 16 | $echo->initSession(); 17 | 18 | /** 19 | * Handle the .csv file upload and add cards individually 20 | */ 21 | if ( ! empty( $_FILES ) ) 22 | { 23 | // validate the file upload 24 | try 25 | { 26 | // if $_FILES is undefined/missing/corrupt, treat it as invalid 27 | if ( ! isset( $_FILES[ 'file' ] ) ) 28 | { 29 | throw new \RuntimeException( 'Something is wrong with the file.' ); 30 | } 31 | else if ( ! isset( $_FILES[ 'file' ][ 'error' ] ) 32 | || is_array( $_FILES[ 'file' ][ 'error' ] ) ) 33 | { 34 | throw new \RuntimeException( 'Something is wrong with the file.' ); 35 | } 36 | else 37 | { 38 | $file = $_FILES[ 'file' ]; 39 | } 40 | 41 | // check for any error with the upload 42 | switch ( $file[ 'error' ] ) { 43 | case UPLOAD_ERR_OK: 44 | break; 45 | case UPLOAD_ERR_NO_FILE: 46 | throw new \RuntimeException( 'Please select a file to upload!' ); 47 | case UPLOAD_ERR_INI_SIZE: 48 | case UPLOAD_ERR_FORM_SIZE: 49 | throw new \RuntimeException( 50 | 'Oops, that file was too big to handle.' ); 51 | default: 52 | throw new \RuntimeException( 53 | 'Sorry, it looks like there was a problem with the upload.' ); 54 | } 55 | 56 | // check for filesize I guess 57 | if ( $file[ 'size' ] > 20000000 ) { 58 | throw new \RuntimeException( 59 | 'Oops, that file is too big. Please upload one less than 20MB.'); 60 | } 61 | 62 | // check/set the mime type ourselves 63 | $finfo = new \finfo( FILEINFO_MIME_TYPE ); 64 | $ext = in_array( 65 | $finfo->file( $file[ 'tmp_name' ] ), 66 | [ 'text/csv', 'text/plain', 'application/vnd.ms-excel', 'application/csv' ] ); 67 | if ( $ext === false ) { 68 | throw new \RuntimeException( 69 | 'It looks like you didn\'t upload a CSV file.'); 70 | } 71 | 72 | // rename and move the file! 73 | $new_file_name = sha1_file( $file[ 'tmp_name' ] ).".csv"; 74 | $new_file_path = $echo->config[ 'path' ].$new_file_name; 75 | 76 | if ( ! move_uploaded_file( $file[ 'tmp_name' ], $new_file_path ) ) { 77 | throw new \RuntimeException( 'There was a problem copying the file.' ); 78 | } 79 | 80 | } catch ( \RuntimeException $e ) { 81 | 82 | return $e; 83 | 84 | } 85 | 86 | // check if we're just outputting the MIDs 87 | $ids_only = false; 88 | if ( isset( $_POST[ 'ids_only' ] ) && $_POST[ 'ids_only' ] == '1' ) 89 | { 90 | $ids_only = true; 91 | } 92 | 93 | // get the file contents 94 | $contents = file_get_contents( $new_file_path ); 95 | 96 | // make sure it's UTF-8 encoded 97 | if ( ! mb_check_encoding( $contents, 'UTF-8' ) 98 | || ! ( $contents === mb_convert_encoding( mb_convert_encoding( 99 | $contents, 'UTF-32', 'UTF-8' ), 'UTF-8', 'UTF-32' ) ) ) { 100 | $contents = mb_convert_encoding( $contents, 'UTF-8' ); 101 | } 102 | 103 | // break out each line of the file 104 | $rows = str_getcsv( $contents, PHP_EOL ); 105 | $cards = []; 106 | 107 | // iterate over the rows and submit the cards 108 | foreach ( $rows as $row ) 109 | { 110 | // parse out the card values 111 | $card = str_getcsv( $row, ',' ); 112 | $name = $card[ 0 ]; 113 | $set = $card[ 1 ]; 114 | $quantity = $card [ 2 ]; 115 | $price = $card[ 3 ]; 116 | $date = $card[ 4 ]; 117 | 118 | // get the multiverse ID 119 | $mid = $echo->cardReference( $name, $set ); 120 | 121 | // if we're good, add the card or build the print to screen 122 | if ( $mid ) 123 | { 124 | if ( ! $ids_only ) 125 | { 126 | $response = $echo->addCard( $mid, $quantity, $price, $date ); 127 | if ( ! $response ) 128 | { 129 | $errors[ 'adds'] = [ $mid, $name, $set, $quantity, $price, $date ]; 130 | } 131 | else 132 | { 133 | $success[] = [ $mid, $name, $set, $quantity, $price, $date ]; 134 | } 135 | } 136 | else 137 | { 138 | $csv[] = [ $mid, $name, $set, $quantity, $price, $date ]; 139 | } 140 | } 141 | else 142 | { 143 | $errors[ 'mids' ] = $card; 144 | } 145 | } 146 | 147 | // show the csv list if we have that set 148 | if ( $ids_only ) 149 | { 150 | echo "
"; 156 | } 157 | 158 | } 159 | 160 | ?> 161 | 162 | 163 | 164 | 165 |id,name,set,quantity,price,date\n"; 151 | foreach ( $csv as $i => $card ) 152 | { 153 | echo implode( ',', $card ).'
'; 154 | } 155 | echo "
184 | Select your .csv file below then click the "Import" button. Make sure your .csv is formatted the same way as the template file. 185 |
186 | 201 | 202 | 203 | --------------------------------------------------------------------------------