Parent Directory
|
Revision Log
|
Revision Graph
cleanup based on coder - still some issues related to code copy/pasted from cck/views which I'm going to ignore
| 1 | <?php |
| 2 | //$Id: market.module,v 1.13 2008/03/10 00:48:24 greggles Exp $ |
| 3 | |
| 4 | // Trade states |
| 5 | define('MARKET_STATE_TRADE_OPEN', 2); |
| 6 | define('MARKET_STATE_TRADE_EXECUTED', 4); |
| 7 | define('MARKET_STATE_TRADE_INVALID', 5); |
| 8 | define('MARKET_STATE_TRADE_PARTIALLY_EXECUTED', 6); |
| 9 | define('MARKET_STATE_TRADE_CANCELLED', 3); |
| 10 | |
| 11 | // Portfolio states |
| 12 | define('MARKET_STATE_PORTFOLIO_OPEN', 8); |
| 13 | define('MARKET_STATE_PORTFOLIO_CLOSED', 9); |
| 14 | |
| 15 | // Item states |
| 16 | define('MARKET_STATE_ITEM_OPEN', 11); |
| 17 | define('MARKET_STATE_ITEM_PENDING_DECISION', 14); |
| 18 | define('MARKET_STATE_ITEM_CLOSED_SUCCESS', 12); |
| 19 | define('MARKET_STATE_ITEM_CLOSED_FAIL', 13); |
| 20 | |
| 21 | // Other random defines |
| 22 | define('MARKET_ROBOT_UID', 2); |
| 23 | |
| 24 | /** |
| 25 | * Implementation of hook_help(). |
| 26 | */ |
| 27 | function market_help($section) { |
| 28 | switch ($section) { |
| 29 | case 'admin/settings/market': |
| 30 | $output = t('Configure userpoints moderation and branding translation'); |
| 31 | break; |
| 32 | case 'admin/help#market': |
| 33 | $output = t('Users earn !points as they post nodes, comments, and vote on nodes'); |
| 34 | } |
| 35 | return $output; |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * Implementation of hook_menu(). |
| 40 | */ |
| 41 | function market_menu($may_cache) { |
| 42 | |
| 43 | $items = array(); |
| 44 | |
| 45 | if ($may_cache) { |
| 46 | $items[] = array( |
| 47 | 'path' => 'admin/settings/market', |
| 48 | 'callback' => 'drupal_get_form', |
| 49 | 'callback arguments' => array('market_admin_settings'), |
| 50 | 'title' => t('Market settings'), |
| 51 | 'description' => t('Configure market settings'), |
| 52 | 'access' => user_access('administer markets'), |
| 53 | 'type' => MENU_NORMAL_ITEM, |
| 54 | ); |
| 55 | } |
| 56 | |
| 57 | return $items; |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Implementation of hook_perm(). |
| 62 | */ |
| 63 | function market_perm() { |
| 64 | return array('administer markets'); |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * menu callback for settings form. |
| 69 | */ |
| 70 | function market_admin_settings() { |
| 71 | $form = array(); |
| 72 | $group = 'workflow'; |
| 73 | $form[$group] = array( |
| 74 | '#type' => 'fieldset', |
| 75 | '#title' => t('Workflow States'), |
| 76 | '#collapsible' => TRUE, |
| 77 | '#collapsed' => TRUE, |
| 78 | '#weight' => -1, |
| 79 | '#description' => t('Configure the state IDs for the states of the workflows you have defined. <br> See the INSTALL.txt for more details'), |
| 80 | ); |
| 81 | |
| 82 | // Trade States |
| 83 | $form[$group]['trade'] = array( |
| 84 | '#type' => 'fieldset', |
| 85 | '#title' => t('Workflow States for Trades'), |
| 86 | '#collapsible' => FALSE, |
| 87 | '#collapsed' => FALSE, |
| 88 | '#weight' => -1, |
| 89 | ); |
| 90 | |
| 91 | $form[$group]['trade']['market_state_trade_open'] = array( |
| 92 | '#type' => 'textfield', |
| 93 | '#title' => t('Open State ID'), |
| 94 | '#default_value' => variable_get('market_state_trade_open', MARKET_STATE_TRADE_OPEN), |
| 95 | '#size' => 20, |
| 96 | '#maxlength' => '20', |
| 97 | '#required' => TRUE, |
| 98 | ); |
| 99 | |
| 100 | $form[$group]['trade']['market_state_trade_executed'] = array( |
| 101 | '#type' => 'textfield', |
| 102 | '#title' => t('Executed State ID'), |
| 103 | '#default_value' => variable_get('market_state_trade_executed', MARKET_STATE_TRADE_EXECUTED), |
| 104 | '#size' => 20, |
| 105 | '#maxlength' => '20', |
| 106 | '#required' => TRUE, |
| 107 | ); |
| 108 | |
| 109 | $form[$group]['trade']['market_state_trade_invalid'] = array( |
| 110 | '#type' => 'textfield', |
| 111 | '#title' => t('Invalid State ID'), |
| 112 | '#default_value' => variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), |
| 113 | '#size' => 20, |
| 114 | '#maxlength' => '20', |
| 115 | '#required' => TRUE, |
| 116 | ); |
| 117 | |
| 118 | $form[$group]['trade']['market_state_trade_partially_executed'] = array( |
| 119 | '#type' => 'textfield', |
| 120 | '#title' => t('Partially Executed State ID'), |
| 121 | '#default_value' => variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED), |
| 122 | '#size' => 20, |
| 123 | '#maxlength' => '20', |
| 124 | '#required' => TRUE, |
| 125 | ); |
| 126 | |
| 127 | $form[$group]['trade']['market_state_trade_cancelled'] = array( |
| 128 | '#type' => 'textfield', |
| 129 | '#title' => t('Cancelled State ID'), |
| 130 | '#default_value' => variable_get('market_state_trade_cancelled', MARKET_STATE_TRADE_CANCELLED), |
| 131 | '#size' => 20, |
| 132 | '#maxlength' => '20', |
| 133 | '#required' => TRUE, |
| 134 | ); |
| 135 | |
| 136 | // Portfolio states |
| 137 | $form[$group]['portfolio'] = array( |
| 138 | '#type' => 'fieldset', |
| 139 | '#title' => t('Workflow States for Portfolios'), |
| 140 | '#collapsible' => FALSE, |
| 141 | '#collapsed' => FALSE, |
| 142 | '#weight' => -1, |
| 143 | ); |
| 144 | |
| 145 | $form[$group]['portfolio']['market_state_portfolio_open'] = array( |
| 146 | '#type' => 'textfield', |
| 147 | '#title' => t('Portfolio Open State ID'), |
| 148 | '#default_value' => variable_get('market_state_portfolio_open', MARKET_STATE_PORTFOLIO_OPEN), |
| 149 | '#size' => 20, |
| 150 | '#maxlength' => '20', |
| 151 | '#required' => TRUE, |
| 152 | ); |
| 153 | $form[$group]['portfolio']['market_state_portfolio_closed'] = array( |
| 154 | '#type' => 'textfield', |
| 155 | '#title' => t('Portfolio Open State ID'), |
| 156 | '#default_value' => variable_get('market_state_portfolio_closed', MARKET_STATE_PORTFOLIO_CLOSED), |
| 157 | '#size' => 20, |
| 158 | '#maxlength' => '20', |
| 159 | '#required' => TRUE, |
| 160 | ); |
| 161 | |
| 162 | |
| 163 | // Item (contract, stock, option, etc.) states |
| 164 | $form[$group]['item'] = array( |
| 165 | '#type' => 'fieldset', |
| 166 | '#title' => t('Workflow States for Items'), |
| 167 | '#collapsible' => FALSE, |
| 168 | '#collapsed' => FALSE, |
| 169 | '#weight' => -1, |
| 170 | ); |
| 171 | $form[$group]['item']['market_state_item_open'] = array( |
| 172 | '#type' => 'textfield', |
| 173 | '#title' => t('Item Open State ID'), |
| 174 | '#default_value' => variable_get('market_state_item_open', MARKET_STATE_ITEM_OPEN), |
| 175 | '#size' => 20, |
| 176 | '#maxlength' => '20', |
| 177 | '#required' => TRUE, |
| 178 | ); |
| 179 | $form[$group]['item']['market_state_item_pending_decision'] = array( |
| 180 | '#type' => 'textfield', |
| 181 | '#title' => t('Item Pending Decision State ID'), |
| 182 | '#default_value' => variable_get('market_state_item_pending_decision', MARKET_STATE_ITEM_PENDING_DECISION), |
| 183 | '#size' => 20, |
| 184 | '#maxlength' => '20', |
| 185 | '#required' => TRUE, |
| 186 | ); |
| 187 | $form[$group]['item']['market_state_item_closed_success'] = array( |
| 188 | '#type' => 'textfield', |
| 189 | '#title' => t('Item Closed Success State ID'), |
| 190 | '#default_value' => variable_get('market_state_item_closed_success', MARKET_STATE_ITEM_CLOSED_SUCCESS), |
| 191 | '#size' => 20, |
| 192 | '#maxlength' => '20', |
| 193 | '#required' => TRUE, |
| 194 | ); |
| 195 | $form[$group]['item']['market_state_item_closed_fail'] = array( |
| 196 | '#type' => 'textfield', |
| 197 | '#title' => t('Item Closed Fail State ID'), |
| 198 | '#default_value' => variable_get('market_state_item_closed_fail', MARKET_STATE_ITEM_CLOSED_FAIL), |
| 199 | '#size' => 20, |
| 200 | '#maxlength' => '20', |
| 201 | '#required' => TRUE, |
| 202 | ); |
| 203 | |
| 204 | // General Settings |
| 205 | $group = 'general'; |
| 206 | $form[$group] = array( |
| 207 | '#type' => 'fieldset', |
| 208 | '#title' => t('General Settings'), |
| 209 | '#collapsible' => TRUE, |
| 210 | '#collapsed' => TRUE, |
| 211 | '#weight' => -1, |
| 212 | ); |
| 213 | |
| 214 | $form[$group]['market_robot_uid'] = array( |
| 215 | '#type' => 'textfield', |
| 216 | '#title' => t('User ID for the Market State Robot'), |
| 217 | '#default_value' => variable_get('market_robot_uid', MARKET_ROBOT_UID), |
| 218 | '#weight' => -1, |
| 219 | '#size' => 20, |
| 220 | '#maxlength' => '20', |
| 221 | '#required' => TRUE, |
| 222 | '#description' => t('The User ID for the user who will perform state changes. This user should have an advanced role (i.e. administrator) which is allowed to perform the state changes in your trade workflows. See INSTALL.txt for more details'), |
| 223 | ); |
| 224 | |
| 225 | return system_settings_form($form); |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Implementation of hook_block(). |
| 230 | */ |
| 231 | function market_block($op = 'list', $delta = 0) { |
| 232 | if ($op == 'list') { |
| 233 | $blocks[0]['info'] = t('Market: Item Price (bid/ask)'); |
| 234 | $blocks[1]['info'] = t('Market: Items in This Portfolio'); |
| 235 | return $blocks; |
| 236 | } |
| 237 | else if ($op == 'view') { |
| 238 | switch ($delta) { |
| 239 | case 0: |
| 240 | if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) != 'edit') { |
| 241 | $node = node_load(arg(1)); |
| 242 | if ($node->type == 'item') { |
| 243 | $block['subject'] = t('Price (may be delayed)'); |
| 244 | $block['content'] = _market_block_bid_ask(arg(1)); |
| 245 | } |
| 246 | } |
| 247 | break; |
| 248 | case 1: |
| 249 | if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) != 'edit') { |
| 250 | $node = node_load(arg(1)); |
| 251 | if ($node->type == 'portfolio') { |
| 252 | $block['subject'] = t('Items in this portfolio'); |
| 253 | $block['content'] = _market_block_portfolio_contents(arg(1)); |
| 254 | } |
| 255 | } |
| 256 | break; |
| 257 | } |
| 258 | return $block; |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | |
| 263 | function _market_block_bid_ask($item_nid) { |
| 264 | // Max bid price (buy orders) |
| 265 | $max_bid = db_result(db_query("SELECT max(ctt.field_price_value) FROM {content_type_trade} ctt INNER JOIN {workflow_node} wn ON ctt.nid = wn.nid WHERE field_item_nid = %d AND field_action_value = 'buy' AND wn.sid in (%d, %d)", |
| 266 | $item_nid, variable_get('market_state_trade_open', MARKET_STATE_TRADE_OPEN), variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED))); |
| 267 | if ($max_bid > 0) { |
| 268 | $items[] = t('Highest buy order: %price.', array('%price' => $max_bid)); |
| 269 | } |
| 270 | else { |
| 271 | $items[] = t('There are no open buy orders.'); |
| 272 | } |
| 273 | // Minimum ask (sell orders) |
| 274 | $min_ask = db_result(db_query("SELECT min(ctt.field_price_value) FROM {content_type_trade} ctt INNER JOIN {workflow_node} wn ON ctt.nid = wn.nid WHERE field_item_nid = %d AND field_action_value = 'sell' AND wn.sid in (%d, %d)", |
| 275 | $item_nid, variable_get('market_state_trade_open', MARKET_STATE_TRADE_OPEN), variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED))); |
| 276 | if ($min_ask > 0) { |
| 277 | $items[] = t('Lowest sell order: %price.', array('%price' => $min_ask)); |
| 278 | } |
| 279 | else { |
| 280 | $items[] = t('There are no open sell orders.'); |
| 281 | } |
| 282 | |
| 283 | // Last order |
| 284 | $last_nid = db_result(db_query("SELECT max(ctt.nid) FROM {content_type_trade} ctt INNER JOIN {workflow_node} wn ON ctt.nid = wn.nid WHERE field_item_nid = %d AND wn.sid in (%d, %d)", |
| 285 | $item_nid, variable_get('market_state_trade_executed', MARKET_STATE_TRADE_EXECUTED), variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED))); |
| 286 | if ($last_nid) { |
| 287 | $last_price = db_result(db_query("SELECT ctt.field_price_value FROM {content_type_trade} ctt WHERE ctt.nid = %d", |
| 288 | $last_nid)); |
| 289 | $items[] = t('Last price: %price.', array('%price' => $last_price)); |
| 290 | } |
| 291 | else { |
| 292 | $items[] = t('No transactions yet.'); |
| 293 | } |
| 294 | |
| 295 | return theme('item_list', $items); |
| 296 | } |
| 297 | |
| 298 | function _market_block_portfolio_contents($portfolio_nid) { |
| 299 | $results = db_query("SELECT n.nid, n.title, cti.field_price_estimate_value FROM node n INNER JOIN content_type_item cti ON n.vid = cti.vid WHERE field_portfolio_nid = %d", $portfolio_nid); |
| 300 | while ($result = db_fetch_array($results)) { |
| 301 | $output = l(t('@title: last price @price', array('@title' => $result['title'], '@price' => $result['field_price_estimate_value'])), 'node/'. $result['nid']); |
| 302 | if (user_access('create trade content')) { |
| 303 | $output .= ' '. l('buy', 'node/add/trade', NULL, 'edit[field_item][nids]='. $result['nid'] .'&edit[field_action][key]=buy'); |
| 304 | $output .= ' '. l('sell', 'node/add/trade', NULL, 'edit[field_item][nids]='. $result['nid'] .'&edit[field_action][key]=sell'); |
| 305 | } |
| 306 | $items[] = $output; |
| 307 | } |
| 308 | // TODO get the bid/ask for these - maybe slightly intensive so skipped for now. |
| 309 | return theme('item_list', $items); |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | * Implementation of hook_cron |
| 314 | */ |
| 315 | function market_cron() { |
| 316 | // TODO some stuff |
| 317 | // TODO close trades on time based items |
| 318 | // TODO If it hasn't been done yet, crunch the numbers on yesterday's high/low/average/volume for all items |
| 319 | |
| 320 | } |
| 321 | |
| 322 | /** |
| 323 | * Implementation of hook_exit |
| 324 | */ |
| 325 | function market_exit() { |
| 326 | // Save the state change for nodes if someone else set a nid for us |
| 327 | global $_market_state; |
| 328 | if (is_array($_market_state) && count($_market_state)) { |
| 329 | foreach ($_market_state as $key => $state) { |
| 330 | _market_state_change($state['nid'], $state['state'], $state['comment']); |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | // Save the remaining change for nodes if someone else set a nid for us |
| 335 | // We do this at the end instead of along the way in case a single trade gets updated multiple times |
| 336 | global $_market_remaining; |
| 337 | if (is_array($_market_remaining) && count($_market_remaining)) { |
| 338 | foreach ($_market_remaining as $nid => $remaining) { |
| 339 | _market_remaining_change($nid, $remaining); |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | |
| 345 | function _market_state_change($nid, $state, $comment) { |
| 346 | // Sneaky robot stuff to make sure we can change the state to executed |
| 347 | global $user; |
| 348 | $orig_user = $user; |
| 349 | $user = user_load(array('uid' => variable_get('market_robot_uid', MARKET_ROBOT_UID))); |
| 350 | // Actually change the state - copied from workflow module |
| 351 | $node = node_load($nid, NULL, TRUE); |
| 352 | $node->workflow = $state; |
| 353 | $node->workflow_comment = $comment; |
| 354 | $node->revision = 1; |
| 355 | node_save($node); |
| 356 | |
| 357 | // Set back the sneaky robot business |
| 358 | $user = $orig_user; |
| 359 | } |
| 360 | |
| 361 | function _market_remaining_change($nid, $remaining) { |
| 362 | $node = node_load($nid, NULL, TRUE); |
| 363 | $node->field_remaining[0]['value'] = (string)$remaining; |
| 364 | $node->revision = 1; |
| 365 | node_save($node); |
| 366 | } |
| 367 | |
| 368 | /* |
| 369 | * Implementation of hook_workflow |
| 370 | */ |
| 371 | function market_workflow($op, $old_sid, $sid, $node) { |
| 372 | if ($op == 'transition post' && $node->type == 'item') { |
| 373 | if ($sid == variable_get('market_state_item_closed_success', MARKET_STATE_ITEM_CLOSED_SUCCESS)) { |
| 374 | // This is a transition post situation, the node is an "item" and it was closed to success |
| 375 | // So, we give the owners of this item 100 points for each contract they own |
| 376 | $users = db_query('SELECT n.uid, ctic.field_item_count_value, ctic.field_owned_items_nid FROM node n INNER JOIN content_type_item_count ctic ON n.vid = ctic.vid WHERE field_owned_items_nid = %d', $node->nid); |
| 377 | while ($user = db_fetch_array($users)) { |
| 378 | $params = array( |
| 379 | 'uid' => $user['uid'], |
| 380 | 'points' => $user['field_item_count_value'] * 100, // TODO make the 100 variable per site (or better - per portfolio) |
| 381 | 'description' => t('Success for contract number @contract_nid. Congrats!', array('@contract_nid' => $user['field_item_count_value'])), |
| 382 | 'event' => 'trade', |
| 383 | 'reference' => $trade_nid, |
| 384 | ); |
| 385 | $status = userpoints_userpointsapi($params); |
| 386 | |
| 387 | // MAYBEDO create a workflow where each item_count is moved from a workflow state of OPEN to CREDITED so we can do this in cron runs as well |
| 388 | } |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | } |
| 393 | |
| 394 | /* |
| 395 | * Implementation of hook_nodeapi |
| 396 | */ |
| 397 | function market_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { |
| 398 | // TODO for items: |
| 399 | // TODO Validate the order prior to submit to make sure they can currently enter it |
| 400 | // buy orders need to have enough money |
| 401 | // sell orders need to have the shares |
| 402 | // TODO state the upside/downside of the position prior to submission |
| 403 | |
| 404 | // TODO for portfolios: |
| 405 | // TODO some ability to open/close trading on a portfolio |
| 406 | // TODO if it's a portfolio buy/sell set the price to 100 and warn the user |
| 407 | |
| 408 | // TODO for items: |
| 409 | // TODO On load pull up last price |
| 410 | |
| 411 | switch ($op) { |
| 412 | case 'validate': |
| 413 | if ($node->type == 'trade' && !isset($node->nid)) { |
| 414 | // Set the remaining to the quantity |
| 415 | |
| 416 | |
| 417 | // Buying - have enough points? |
| 418 | if ($node->field_action[0]['value'] == 'buy') { |
| 419 | if (($node->field_price[0]['value'] * $node->field_quantity[0]['value']) > userpoints_get_current_points($node->uid)) { |
| 420 | form_set_error('field_quantity', t('Insufficient points to purchase this many portfolios. Consider getting more points or reducing the quantity/price.')); |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | $item = node_load($node->field_item[0]['nid']); |
| 425 | if ($item->type == 'portfolio') { |
| 426 | if ($node->field_price[0]['value'] != 100) { |
| 427 | form_set_error('field_price', t('Bids on whole portfolios must have a price of 100.')); |
| 428 | } |
| 429 | // Selling - have enough items? |
| 430 | if ($node->field_action[0]['value'] == 'sell') { |
| 431 | // Get the list of items in this portfolio |
| 432 | // Do they own all the items in this portfolio? |
| 433 | // If so, do they own enough of them? |
| 434 | |
| 435 | $fewest = market_portfolio_minimum_ownership($node->uid, $node->field_item[0]['nid']); |
| 436 | // return array('items' => $fewest_items, 'item_nid' => $fewest_nid); |
| 437 | |
| 438 | if ($fewest === 0) { |
| 439 | form_set_error('field_quantity', t('You are trying to sell a portfolio where you do not own any of the items in the portfolio.')); |
| 440 | } |
| 441 | else if ($fewest['items'] < $node->field_quantity[0]['value']) { |
| 442 | form_set_error('field_quantity', t('You are trying to sell more items than you own. You can only sell %fewest or fewer items.', array('%fewest' => $fewest['items']))); |
| 443 | } |
| 444 | } |
| 445 | } |
| 446 | if ($item->type == 'item') { |
| 447 | // Make sure that the item is open for trading |
| 448 | if ($item->_workflow != variable_get('market_state_item_open', MARKET_STATE_ITEM_OPEN)) { |
| 449 | form_set_error('field_item_nids', t('You are trying to transact in an item which is not available for sale')); |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | break; |
| 454 | case 'insert': |
| 455 | if ($node->type == 'trade') { |
| 456 | $item = node_load($node->field_item[0]['nid']); |
| 457 | // TODO: instead of loading the node, just do one quick query on the node table to get title and type which is all we need |
| 458 | if ($item->type == 'portfolio' || $item->type == 'item') { |
| 459 | $userpoints_display_message = variable_get('userpoints_display_message', '1'); |
| 460 | variable_set('userpoints_display_message', '0'); |
| 461 | |
| 462 | // This actually does all the work of changing points and recording item counts |
| 463 | market_transaction($node->field_action[0]['value'], $node->uid, $node->field_price[0]['value'], $node->field_quantity[0]['value'], $node->nid, $item->nid, $item->title, $item->type); |
| 464 | |
| 465 | variable_set('userpoints_display_message', $userpoints_display_message); |
| 466 | } |
| 467 | else { |
| 468 | // Else: what do we do if it's a trade that's not a portfolio or item |
| 469 | watchdog('market', t('Something is pretty wrong. In <a href="!uri">this trade</a> a user traded an item which can not be traded', array('!uri' => url('node/'. $node->nid)))); |
| 470 | } |
| 471 | } |
| 472 | break; |
| 473 | default: |
| 474 | break; |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | /** |
| 479 | * Implementation of hook_form_alter |
| 480 | */ |
| 481 | function market_form_alter($form_id, &$form) { |
| 482 | // TODO Remove the remaining quantity from the input form |
| 483 | if ($form_id == 'trade_node_form') { |
| 484 | $form['field_remaining'][0]['value']['#type'] = 'hidden'; |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | /** |
| 489 | * Perform the actual transaction inside of a database transaction |
| 490 | * WORKHERE |
| 491 | */ |
| 492 | function market_transaction($action, $uid, $price, $quantity, $trade_nid, $item_nid, $title, $type) { |
| 493 | global $_market_state; |
| 494 | global $_market_remaining; |
| 495 | $_market_remaining[$trade_nid] = $quantity; // Default this guy |
| 496 | |
| 497 | $txn = new pressflow_transaction(); |
| 498 | |
| 499 | // Determine if the quantity should be positive or negative - buy vs. sell |
| 500 | if ($action == 'buy') { |
| 501 | $match_action = 'sell'; |
| 502 | $match_sort = ' ASC '; |
| 503 | $match_compare_order = ' <= '; |
| 504 | } |
| 505 | else { |
| 506 | $match_action = 'buy'; |
| 507 | $match_sort = ' DESC '; |
| 508 | $match_compare_order = ' >= '; |
| 509 | } |
| 510 | |
| 511 | // We try to execute the portfolios immediately |
| 512 | if ($type == 'portfolio') { |
| 513 | if ($action != 'buy') { // TODO we are now doing this buy thing 3 times - stop that! |
| 514 | $quantity = -$quantity; |
| 515 | } |
| 516 | |
| 517 | // Try to transact the points |
| 518 | $status = _market_point_transaction($action, $uid, $price, $quantity, $trade_nid, $title); |
| 519 | |
| 520 | // Only try to transact the trade if the points transferred properly |
| 521 | if ($status) { |
| 522 | |
| 523 | $status = _market_portfolio_transaction($item_nid, $quantity, $uid); |
| 524 | if ($status) { |
| 525 | $_market_state[] = array('nid' => $trade_nid, 'state' => variable_get('market_state_trade_executed', MARKET_STATE_TRADE_EXECUTED), 'comment' => t('Trade executed')); |
| 526 | $_market_remaining[$trade_nid] = '0'; |
| 527 | } |
| 528 | else { |
| 529 | $_market_state[] = array('nid' => $trade_nid, 'state' => variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), 'comment' => t('Trade invalid - accounting problem.')); |
| 530 | $_market_remaining[$trade_nid] = abs($quantity); |
| 531 | } |
| 532 | } |
| 533 | else { |
| 534 | $_market_state[] = array('nid' => $trade_nid, 'state' => variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), 'comment' => t('Trade invalid - points problem.')); |
| 535 | $_market_remaining[$trade_nid] = abs($quantity); |
| 536 | } |
| 537 | } |
| 538 | else { |
| 539 | // Individual items outside a portfolio are checked against the book. |
| 540 | // If they match the book - execute now. If not, store for later |
| 541 | // MAYBEDO: some locking mechanism? |
| 542 | |
| 543 | $match = db_fetch_array(db_query_range("SELECT n.nid, n.uid, ctt.field_price_value, COALESCE(ctt.field_remaining_value, ctt.field_quantity_value) quantity, wn.sid state_id FROM {content_type_trade} ctt INNER JOIN {node} n ON ctt.vid = n.vid INNER JOIN {workflow_node} wn ON ctt.nid = wn.nid WHERE field_item_nid = %d AND field_action_value = '%s' AND field_price_value $match_compare_order %d AND wn.sid in (%d, %d) ORDER BY field_price_value %s, nid ASC", |
| 544 | $item_nid, $match_action, $price, variable_get('market_state_trade_open', MARKET_STATE_TRADE_OPEN), variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED), $match_sort, 0, 1)); |
| 545 | // Coalesce is probably unnecessary, but just in case... |
| 546 | drupal_set_message(print_r($match, TRUE)); |
| 547 | if ($match['nid']) { |
| 548 | |
| 549 | // Set the quantity to the right number for this situation |
| 550 | $exact_match = FALSE; |
| 551 | $more_in_new = FALSE; |
| 552 | $more_in_existing = FALSE; |
| 553 | |
| 554 | if (abs($quantity) == abs($match['quantity'])) { |
| 555 | $exact_match = TRUE; |
| 556 | $trade_quantity = $quantity; |
| 557 | } |
| 558 | else if (abs($quantity) > abs($match['quantity'])) { |
| 559 | $more_in_new = TRUE; |
| 560 | $trade_quantity = $match['quantity']; |
| 561 | } |
| 562 | else { |
| 563 | $more_in_existing = TRUE; |
| 564 | $trade_quantity = $quantity; |
| 565 | } |
| 566 | // Now we flip the bits for buy/sell |
| 567 | if ($action == 'buy') { |
| 568 | $trade_quantity = abs($trade_quantity); |
| 569 | $match_quantity = -abs($trade_quantity); |
| 570 | } |
| 571 | else { |
| 572 | $trade_quantity = -abs($trade_quantity); |
| 573 | $match_quantity = abs($trade_quantity); |
| 574 | } |
| 575 | |
| 576 | // Great, we got a match, begin the transaction |
| 577 | // Points for the match first (this tests that their order is still valid as well) |
| 578 | $status = _market_point_transaction($match_action, $match['uid'], $match['field_price_value'], -$trade_quantity, $trade_nid, $title); |
| 579 | // And now the initiator of the transaction |
| 580 | if ($status) { |
| 581 | // Then the initator |
| 582 | $status = _market_point_transaction($action, $uid, $match['field_price_value'], $trade_quantity, $trade_nid, $title); |
| 583 | } |
| 584 | else { |
| 585 | // That transaction went bad, mark the trade for workflow changes |
| 586 | _market_state_change($match['nid'], variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), t('Trade found to be invalid during a transaction due to points problems.')); |
| 587 | // We do this twice - if it's already in the state, the changer will bail because the workflow doesn't allow that transition? |
| 588 | // If the transaction got rolled back then this exit change will still happen. Kinda weird, may need to rethink this. |
| 589 | $_market_state[] = array('nid' => $match['nid'], 'state' => variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), |
| 590 | 'comment' => t('Trade found to be invalid during a transaction due to points problems.')); |
| 591 | } |
| 592 | |
| 593 | // And now the items |
| 594 | if (!$status) { |
| 595 | // That transaction went bad, mark the trade for workflow changes |
| 596 | _market_state_change($trade_nid, variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), t('Trade found to be invalid during a transaction due to points problems.')); |
| 597 | // We do this twice - if it's already in the state, the changer will bail because the workflow doesn't allow that transition? |
| 598 | // If the transaction got rolled back then this exit change will still happen. Kinda weird, may need to rethink this. |
| 599 | $_market_state[] = array('nid' => $match['nid'], 'state' => variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), |
| 600 | 'comment' => t('Trade found to be invalid during a transaction due to points problems.')); |
| 601 | } |
| 602 | else { |
| 603 | $status = _market_item_transaction($item_nid, $match_quantity, $match['uid']); |
| 604 | } |
| 605 | |
| 606 | // If that failed, mark the item as invalid and bail |
| 607 | if (!$status) { |
| 608 | // asdf |
| 609 | } |
| 610 | else { // First accounting worked, mark it, and try the second one |
| 611 | if ($exact_match || $more_in_new) { |
| 612 | // That worked and the amount for the existing was fully filled |
| 613 | _market_state_change($match['nid'], variable_get('market_state_trade_executed', MARKET_STATE_TRADE_EXECUTED), t('Trade completed.')); |
| 614 | $_market_remaining[$match['nid']] = '0'; |
| 615 | } |
| 616 | else { |
| 617 | // Partial fill, set state and remaining shares |
| 618 | _market_state_change($match['nid'], variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED), t('Trade completed.')); |
| 619 | $_market_remaining[$match['nid']] = $match['quantity'] - abs($trade_quantity); |
| 620 | } |
| 621 | // Try the new trade |
| 622 | $status = _market_item_transaction($item_nid, $trade_quantity, $uid); |
| 623 | if (!$status) { |
| 624 | // That transaction went bad, mark the trade for workflow changes |
| 625 | // TODO make sure we break out of the trade - if the original is broken then we don't want to do anything else |
| 626 | $_market_state[] = array('nid' => $trade_nid, 'state' => variable_get('market_state_trade_invalid', MARKET_STATE_TRADE_INVALID), 'comment' => t('Trade found to be invalid during a transaction.')); |
| 627 | // TODO break; or something like that? |
| 628 | } |
| 629 | else { // Succeded, account, recurse if necessary |
| 630 | // Update the price on the item |
| 631 | drupal_set_message('updating price to '. $match['field_price_value']); |
| 632 | $item_node = node_load($item_nid); |
| 633 | $item_node->field_price_estimate[0]['value'] = $match['field_price_value']; |
| 634 | node_save($item_node); |
| 635 | |
| 636 | if ($exact_match || $more_in_existing) { |
| 637 | $_market_state[] = array('nid' => $trade_nid, 'state' => variable_get('market_state_trade_executed', MARKET_STATE_TRADE_EXECUTED), 'comment' => t('Trade completed.')); |
| 638 | $_market_remaining[$trade_nid] = 0; |
| 639 | } |
| 640 | else { |
| 641 | $_market_state[] = array('nid' => $trade_nid, 'state' => variable_get('market_state_trade_partially_executed', MARKET_STATE_TRADE_PARTIALLY_EXECUTED), 'comment' => t('Trade partially completed.')); |
| 642 | $_market_remaining[$trade_nid] = abs($trade_quantity) - abs($quantity); |
| 643 | // TODO If there are more item on this order unsatisfied then 1)mark this as partial executed 2) recurse on this function again, baby |
| 644 | market_transaction($action, $uid, $price, abs($_market_remaining[$trade_nid]), $trade_nid, $item_nid, $title, $type); // Subtle but important difference in quantity - use the updated one |
| 645 | } |
| 646 | } |
| 647 | } |
| 648 | // TODO be sure we set the $_market_state for success |
| 649 | // TODO be sure we set the $_market_state for ANY failures |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | // Rollback or end transaction |
| 654 | if ($status) { |
| 655 | // Commit (pressflow transaction things aren't actually committed, you just don't roll them back) |
| 656 | } |
| 657 | else { |
| 658 | unset($_market_remaining); // Stuff went bad so let's not change the remaining share numbers... |
| 659 | $txn->rollback(); |
| 660 | drupal_set_message(t('Trade was not executed (maybe never will be).')); |
| 661 | } |
| 662 | return $status; |
| 663 | } |
| 664 | |
| 665 | /** |
| 666 | * Helper function to make userpoints transactions |
| 667 | * We have to do this here instead of hook_nodeapi |
| 668 | */ |
| 669 | function _market_point_transaction($action, $uid, $price, $quantity, $trade_nid, $title) { |
| 670 | $status = TRUE; |
| 671 | |
| 672 | // Try to debit/credit the account |
| 673 | // Build the userpoints parameters |
| 674 | $params = array( |
| 675 | 'uid' => $uid, |
| 676 | 'points' => $price * $quantity * -1, // -1 because buying a quantity should decrement the points |
| 677 | 'description' => t('Trade to @action @quantity items of @title at @price in trade id @nid', |
| 678 | array('@action' => $action, '@quantity' => $quantity, '@title' => $title, '@nid' => $trade_nid, '@price' => $price)), |
| 679 | 'event' => 'trade', |
| 680 | 'reference' => $trade_nid, |
| 681 | ); |
| 682 | $status = userpoints_userpointsapi($params); |
| 683 | if (!$status['status']) { |
| 684 | watchdog('market', t('Problem with points: %reason', array('%reason' => $status['reason']))); |
| 685 | } |
| 686 | return $status['status']; |
| 687 | } |
| 688 | |
| 689 | /** |
| 690 | * Quite a simple function, really. Maintained for API consistency between portfolios and items and in case we need to do more here later. |
| 691 | */ |
| 692 | function _market_item_transaction($item_nid, $quantity, $uid) { |
| 693 | // Do some stuff? |
| 694 | $status = TRUE; // Default to TRUE, if we fail along the way it will get flipped |
| 695 | // Finds all of the item for a portfolio |
| 696 | $status = _market_account_items($item_nid, $quantity, $uid); |
| 697 | return $status; |
| 698 | } |
| 699 | |
| 700 | /** |
| 701 | * Market portfolio transaction - |
| 702 | * $portfolio_nid is the nid of the portfolio |
| 703 | * $quanitity - positive to buy, negative to sell |
| 704 | * $uid is the username of the person making the trade |
| 705 | * |
| 706 | * returns TRUE if the transaction was processed, |
| 707 | * FALSE if something prevented it |
| 708 | */ |
| 709 | function _market_portfolio_transaction($portfolio_nid, $quantity, $uid) { |
| 710 | $status = TRUE; // Default to TRUE, if we fail along the way it will get flipped |
| 711 | |
| 712 | // Finds all of the item for a portfolio |
| 713 | $results = db_query("SELECT nid FROM {content_type_item} WHERE field_portfolio_nid = %d", $portfolio_nid); |
| 714 | while (($result = db_fetch_array($results)) && $status) { |
| 715 | $status = _market_account_items($result['nid'], $quantity, $uid); |
| 716 | } |
| 717 | return $status; |
| 718 | } |
| 719 | |
| 720 | function _market_account_items($item_nid, $quantity, $uid) { |
| 721 | $status = TRUE; |
| 722 | $accounting_nid = FALSE; |
| 723 | $accounting_nid = db_result(db_query("SELECT n.nid FROM {content_type_item_count} ct INNER JOIN {node} n ON ct.vid = n.vid WHERE field_owned_items_nid = %d AND n.uid = %d", $item_nid, $uid)); //TODO confirm that this works with revisions and everything - maybe we need more join conditions? |
| 724 | |
| 725 | if ($accounting_nid) { |
| 726 | // Update it |
| 727 | $accounting_node = node_load($accounting_nid, NULL, TRUE); |
| 728 | $accounting_node->field_item_count[0][value] += $quantity; |
| 729 | $accounting_node->revision = 1; |
| 730 | |
| 731 | if ($accounting_node->field_item_count[0][value] >= 0) { |
| 732 | node_save($accounting_node); |
| 733 | } |
| 734 | else { |
| 735 | $status = FALSE; |
| 736 | $message = t('Transaction failed, it would sell more items than are currently owned.'); |
| 737 | drupal_set_message($message, 'error'); |
| 738 | watchdog('market', $message); |
| 739 | } |
| 740 | } |
| 741 | else if ($quantity > 0) { |
| 742 | // They don't currently have a item_count - as long as they are buying, create a new node for this item |
| 743 | $new_node->type = 'item_count'; |
| 744 | $values = array(); |
| 745 | $trading_user = user_load(array('uid' => $uid)); |
| 746 | $values['name'] = $trading_user->name; |
| 747 | $values['type'] = 'item_count'; |
| 748 | $values['title'] = 'auto generated title'; // TODO better titles |
| 749 | |
| 750 | $values['status'] = 1; |
| 751 | $values['promote'] = 0; |
| 752 | $values['sticky'] = 0; |
| 753 | $values['revision'] = 1; |
| 754 | $values['comment'] = 2; |
| 755 | $values['field_credit'][0]['value'] = strip_tags($row->byline); |
| 756 | |
| 757 | $values['field_owned_items'] = array('nids' => $item_nid); // Caution: if we change the style of the item field to a different widget this might need to be changed as well |
| 758 | $values['field_item_count'][0]['value'] = $quantity; |
| 759 | $return = drupal_execute('item_count_node_form', $values, $new_node); |
| 760 | $errors = form_get_errors(); |
| 761 | if (count($errors)) { |
| 762 | watchdog('market', print_r($errors, TRUE), 'error'); |
| 763 | $status = FALSE; |
| 764 | watchdog('market', 'Something went wrong in creating a new item count for this trade'); |
| 765 | } |
| 766 | } |
| 767 | // This should really never happen, but in case it does, at least we block and set the stage for a roll back. |
| 768 | else { |
| 769 | $status = FALSE; |
| 770 | drupal_set_message(t('You tried to sell a something that you do not entirely own: item %item_nid, quantity %quantity, user id %uid, ', array('%item_nid' => $item_nid, '%quantity' => $quantity, '%uid' => $uid)), 'error'); |
| 771 | } |
| 772 | return $status; |
| 773 | } |
| 774 | |
| 775 | |
| 776 | /** |
| 777 | * Checks to see which item a user has the fewest of out of a portfolio |
| 778 | * Returns an array with the number of items and nid of the item |
| 779 | * If no items are owned, returns 0; |
| 780 | */ |
| 781 | function market_portfolio_minimum_ownership($uid, $portfolio_nid) { |
| 782 | // MAYBEDO - 2 queries instead of N queries, 1 for items, 1 for owns, then merge |
| 783 | $itemss = db_query("SELECT nid FROM {content_type_item} WHERE field_portfolio_nid = %d", $portfolio_nid); |
| 784 | $owns = array(); |
| 785 | while ($item = db_fetch_array($itemss)) { |
| 786 | $count = db_result(db_query("SELECT field_item_count_value FROM {content_type_item_count} ctcc INNER JOIN {node} n ON ctcc.vid = n.vid WHERE n.uid = %d AND field_owned_items_nid = %d", $uid, $item['nid'])); |
| 787 | $owns[$item['nid']] = $count ? $count : 0; |
| 788 | if (!isset($fewest_items) || $fewest_items > $owns[$item['nid']]) { |
| 789 | $fewest_items = $owns[$item['nid']]; |
| 790 | $fewest_nid = $item['nid']; |
| 791 | } |
| 792 | } |
| 793 | if (isset($fewest_items)) { |
| 794 | return array('items' => $fewest_items, 'item_nid' => $fewest_nid); |
| 795 | } |
| 796 | else { |
| 797 | return 0; |
| 798 | } |
| 799 | |
| 800 | } |
| 801 | |
| 802 | /** |
| 803 | * Some default views |
| 804 | */ |
| 805 | |
| 806 | function market_views_default_views() { |
| 807 | // Item count (i.e. ownership of contracts) |
| 808 | $view = new stdClass(); |
| 809 | $view->name = 'item_counts'; |
| 810 | $view->description = 'counts items - arg for uid owning items'; |
| 811 | $view->access = array (); |
| 812 | $view->view_args_php = ''; |
| 813 | $view->page = TRUE; |
| 814 | $view->page_title = 'Item Counts'; |
| 815 | $view->page_header = ''; |
| 816 | $view->page_header_format = '1'; |
| 817 | $view->page_footer = ''; |
| 818 | $view->page_footer_format = '1'; |
| 819 | $view->page_empty = ''; |
| 820 | $view->page_empty_format = '1'; |
| 821 | $view->page_type = 'table'; |
| 822 | $view->url = 'item_count'; |
| 823 | $view->use_pager = TRUE; |
| 824 | $view->nodes_per_page = '100'; |
| 825 | $view->sort = array (); |
| 826 | $view->argument = array ( |
| 827 | array ( |
| 828 | 'type' => 'uid', |
| 829 | 'argdefault' => '2', |
| 830 | 'title' => '', |
| 831 | 'options' => '', |
| 832 | 'wildcard' => '', |
| 833 | 'wildcard_substitution' => '', |
| 834 | ), |
| 835 | ); |
| 836 | $view->field = array ( |
| 837 | array ( |
| 838 | 'tablename' => 'node_data_field_owned_items', |
| 839 | 'field' => 'field_owned_items_nid', |
| 840 | 'label' => 'Item', |
| 841 | 'handler' => 'content_views_field_handler_group', |
| 842 | 'options' => 'default', |
| 843 | ), |
| 844 | array ( |
| 845 | 'tablename' => 'node_data_field_item_count', |
| 846 | 'field' => 'field_item_count_value', |
| 847 | 'label' => 'Count', |
| 848 | 'handler' => 'content_views_field_handler_group', |
| 849 | 'options' => 'default', |
| 850 | ), |
| 851 | array ( |
| 852 | 'tablename' => 'node', |
| 853 | 'field' => 'view', |
| 854 | 'label' => 'View', |
| 855 | ), |
| 856 | array ( |
| 857 | 'tablename' => 'node', |
| 858 | 'field' => 'edit', |
| 859 | 'label' => 'Edit', |
| 860 | 'handler' => 'views_handler_node_edit_destination', |
| 861 | ), |
| 862 | array ( |
| 863 | 'tablename' => 'users', |
| 864 | 'field' => 'name', |
| 865 | 'label' => 'Owner', |
| 866 | ), |
| 867 | array ( |
| 868 | 'tablename' => 'node', |
| 869 | 'field' => 'nid', |
| 870 | 'label' => 'NID of Item Count', |
| 871 | ), |
| 872 | ); |
| 873 | $view->filter = array ( |
| 874 | array ( |
| 875 | 'tablename' => 'node', |
| 876 | 'field' => 'type', |
| 877 | 'operator' => 'OR', |
| 878 | 'options' => '', |
| 879 | 'value' => array ( |
| 880 | 0 => 'item_count', |
| 881 | ), |
| 882 | ), |
| 883 | ); |
| 884 | $view->exposed_filter = array ( |
| 885 | array ( |
| 886 | 'tablename' => 'users', |
| 887 | 'field' => 'uid', |
| 888 | 'label' => 'Counts by user:', |
| 889 | 'optional' => '1', |
| 890 | 'is_default' => '0', |
| 891 | 'operator' => '1', |
| 892 | 'single' => '1', |
| 893 | ), |
| 894 | ); |
| 895 | $view->requires = array(node_data_field_owned_items, node_data_field_item_count, node, users); |
| 896 | $views[$view->name] = $view; |
| 897 | |
| 898 | // Trades |
| 899 | $view = new stdClass(); |
| 900 | $view->name = 'trades'; |
| 901 | $view->description = 'trades'; |
| 902 | $view->access = array ( |
| 903 | ); |
| 904 | $view->view_args_php = ''; |
| 905 | $view->page = TRUE; |
| 906 | $view->page_title = 'Trades'; |
| 907 | $view->page_header = ''; |
| 908 | $view->page_header_format = '1'; |
| 909 | $view->page_footer = ''; |
| 910 | $view->page_footer_format = '1'; |
| 911 | $view->page_empty = 'No trades match your filter (or none exist)'; |
| 912 | $view->page_empty_format = '1'; |
| 913 | $view->page_type = 'table'; |
| 914 | $view->url = 'trades'; |
| 915 | $view->use_pager = TRUE; |
| 916 | $view->nodes_per_page = '100'; |
| 917 | $view->sort = array ( |
| 918 | ); |
| 919 | $view->argument = array ( |
| 920 | ); |
| 921 | $view->field = array ( |
| 922 | array ( |
| 923 | 'tablename' => 'node', |
| 924 | 'field' => 'view', |
| 925 | 'label' => '', |
| 926 | ), |
| 927 | array ( |
| 928 | 'tablename' => 'node', |
| 929 | 'field' => 'edit', |
| 930 | 'label' => 'Edit', |
| 931 | 'handler' => 'views_handler_node_edit_destination', |
| 932 | ), |
| 933 | array ( |
| 934 | 'tablename' => 'node', |
| 935 | 'field' => 'delete', |
| 936 | 'label' => '', |
| 937 | 'handler' => 'views_handler_node_delete_destination', |
| 938 | ), |
| 939 | array ( |
| 940 | 'tablename' => 'node_data_field_item', |
| 941 | 'field' => 'field_item_nid', |
| 942 | 'label' => 'Item to trade', |
| 943 | 'handler' => 'content_views_field_handler_group', |
| 944 | 'options' => 'default', |
| 945 | ), |
| 946 | array ( |
| 947 | 'tablename' => 'node_data_field_price', |
| 948 | 'field' => 'field_price_value', |
| 949 | 'label' => 'price', |
| 950 | 'handler' => 'content_views_field_handler_group', |
| 951 | 'options' => 'default', |
| 952 | ), |
| 953 | array ( |
| 954 | 'tablename' => 'node_data_field_quantity', |
| 955 | 'field' => 'field_quantity_value', |
| 956 | 'label' => 'original quantity', |
| 957 | 'handler' => 'content_views_field_handler_group', |
| 958 | 'options' => 'default', |
| 959 | ), |
| 960 | array ( |
| 961 | 'tablename' => 'node_data_field_remaining', |
| 962 | 'field' => 'field_remaining_value', |
| 963 | 'label' => 'Remaining Quantity', |
| 964 | 'handler' => 'content_views_field_handler_group', |
| 965 | 'options' => 'default', |
| 966 | ), |
| 967 | array ( |
| 968 | 'tablename' => 'workflow_states', |
| 969 | 'field' => 'state', |
| 970 | 'label' => 'state', |
| 971 | ), |
| 972 | array ( |
| 973 | 'tablename' => 'users', |
| 974 | 'field' => 'name', |
| 975 | 'label' => 'trader', |
| 976 | ), |
| 977 | array ( |
| 978 | 'tablename' => 'node_data_field_action', |
| 979 | 'field' => 'field_action_value', |
| 980 | 'label' => 'action', |
| 981 | 'handler' => 'content_views_field_handler_group', |
| 982 | 'options' => 'default', |
| 983 | ), |
| 984 | ); |
| 985 | $view->filter = array ( |
| 986 | array ( |
| 987 | 'tablename' => 'node', |
| 988 | 'field' => 'type', |
| 989 | 'operator' => 'OR', |
| 990 | 'options' => '', |
| 991 | 'value' => array ( |
| 992 | 0 => 'trade', |
| 993 | ), |
| 994 | ), |
| 995 | array ( |
| 996 | 'tablename' => 'workflow_node', |
| 997 | 'field' => 'sid', |
| 998 | 'operator' => 'OR', |
| 999 | 'options' => '', |
| 1000 | 'value' => array ( |
| 1001 | 0 => '2', |
| 1002 | ), |
| 1003 | ), |
| 1004 | ); |
| 1005 | $view->exposed_filter = array ( |
| 1006 | array ( |
| 1007 | 'tablename' => 'workflow_node', |
| 1008 | 'field' => 'sid', |
| 1009 | 'label' => 'State', |
| 1010 | 'optional' => '1', |
| 1011 | 'is_default' => '1', |
| 1012 | 'operator' => '1', |
| 1013 | 'single' => '0', |
| 1014 | ), |
| 1015 | ); |
| 1016 | $view->requires = array(node, node_data_field_item, node_data_field_price, node_data_field_quantity, node_data_field_remaining, workflow_states, users, node_data_field_action, workflow_node); |
| 1017 | $views[$view->name] = $view; |
| 1018 | |
| 1019 | // Items that are available to trade |
| 1020 | $view = new stdClass(); |
| 1021 | $view->name = 'available_items'; |
| 1022 | $view->description = ''; |
| 1023 | $view->access = array ( |
| 1024 | ); |
| 1025 | $view->view_args_php = ''; |
| 1026 | $view->page = TRUE; |
| 1027 | $view->page_title = ''; |
| 1028 | $view->page_header = ''; |
| 1029 | $view->page_header_format = '1'; |
| 1030 | $view->page_footer = ''; |
| 1031 | $view->page_footer_format = '1'; |
| 1032 | $view->page_empty = ''; |
| 1033 | $view->page_empty_format = '1'; |
| 1034 | $view->page_type = 'table'; |
| 1035 | $view->url = 'available_items'; |
| 1036 | $view->use_pager = FALSE; |
| 1037 | $view->nodes_per_page = '50'; |
| 1038 | $view->sort = array ( |
| 1039 | array ( |
| 1040 | 'tablename' => 'node', |
| 1041 | 'field' => 'created', |
| 1042 | 'sortorder' => 'DESC', |
| 1043 | 'options' => 'normal', |
| 1044 | ), |
| 1045 | ); |
| 1046 | $view->argument = array ( |
| 1047 | ); |
| 1048 | $view->field = array ( |
| 1049 | array ( |
| 1050 | 'tablename' => 'node', |
| 1051 | 'field' => 'title', |
| 1052 | 'label' => '', |
| 1053 | 'handler' => 'views_handler_field_nodelink', |
| 1054 | 'options' => 'link', |
| 1055 | ), |
| 1056 | ); |
| 1057 | $view->filter = array ( |
| 1058 | array ( |
| 1059 | 'tablename' => 'workflow_node', |
| 1060 | 'field' => 'sid', |
| 1061 | 'operator' => 'OR', |
| 1062 | 'options' => '', |
| 1063 | 'value' => array ( |
| 1064 | 0 => '11', |
| 1065 | 1 => '8', |
| 1066 | ), |
| 1067 | ), |
| 1068 | array ( |
| 1069 | 'tablename' => 'node', |
| 1070 | 'field' => 'type', |
| 1071 | 'operator' => 'OR', |
| 1072 | 'options' => '', |
| 1073 | 'value' => array ( |
| 1074 | 0 => 'item', |
| 1075 | 1 => 'portfolio', |
| 1076 | ), |
| 1077 | ), |
| 1078 | array ( |
| 1079 | 'tablename' => 'node', |
| 1080 | 'field' => 'status', |
| 1081 | 'operator' => '=', |
| 1082 | 'options' => '', |
| 1083 | 'value' => '1', |
| 1084 | ), |
| 1085 | ); |
| 1086 | $view->exposed_filter = array ( |
| 1087 | ); |
| 1088 | $view->requires = array(node, workflow_node); |
| 1089 | $views[$view->name] = $view; |
| 1090 | |
| 1091 | return $views; |
| 1092 | } |
| 1093 | |
| 1094 | // TODO _later_ |
| 1095 | |
| 1096 | /* |
| 1097 | + Keep track of the original price and the clearing price on the trade nodes (since these can be different) |
| 1098 | |