uc_order.module 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435
  1. <?php
  2. /**
  3. * @file
  4. * Handles all things concerning Ubercart orders.
  5. *
  6. * The order system allows for backend order creation, editing, and management.
  7. * Hooks allow for third party module integration, automated fulfillment, and
  8. * more. This module also governs the order review options and invoices
  9. * displayed to customers.
  10. */
  11. require_once dirname(__FILE__) . '/uc_order.order_pane.inc';
  12. require_once dirname(__FILE__) . '/uc_order.line_item.inc';
  13. /**
  14. * Defines an order object.
  15. */
  16. class UcOrder {
  17. public $order_id = 0;
  18. public $uid = 0;
  19. public $order_status = '';
  20. public $order_total = 0;
  21. public $primary_email = '';
  22. public $delivery_first_name = '';
  23. public $delivery_last_name = '';
  24. public $delivery_phone = '';
  25. public $delivery_company = '';
  26. public $delivery_street1 = '';
  27. public $delivery_street2 = '';
  28. public $delivery_city = '';
  29. public $delivery_zone = 0;
  30. public $delivery_postal_code = '';
  31. public $delivery_country = 0;
  32. public $billing_first_name = '';
  33. public $billing_last_name = '';
  34. public $billing_phone = '';
  35. public $billing_company = '';
  36. public $billing_street1 = '';
  37. public $billing_street2 = '';
  38. public $billing_city = '';
  39. public $billing_zone = 0;
  40. public $billing_postal_code = '';
  41. public $billing_country = 0;
  42. public $products = array();
  43. public $line_items = array();
  44. public $payment_method = '';
  45. public $data = array();
  46. public $created = 0;
  47. public $modified = 0;
  48. public $currency = '';
  49. /**
  50. * Order object constructor.
  51. *
  52. * @param $uid
  53. * The user ID that owns the order, or a cart ID. Cart IDs are integer
  54. * user IDs for authenticated users, or are strings of 22 characters
  55. * or more for anonymous users.
  56. * @param $state
  57. * The initial order state.
  58. */
  59. public function __construct($uid = 0, $state = 'in_checkout') {
  60. if (strlen($uid) < 22 && $uid > 0) {
  61. $this->uid = $uid;
  62. if ($account = user_load($uid)) {
  63. $this->primary_email = $account->mail;
  64. }
  65. }
  66. $this->order_status = uc_order_state_default($state);
  67. $this->currency = variable_get('uc_currency_code', 'USD');
  68. $this->billing_country = variable_get('uc_store_country', 840);
  69. $this->delivery_country = variable_get('uc_store_country', 840);
  70. $this->created = REQUEST_TIME;
  71. $this->modified = REQUEST_TIME;
  72. }
  73. }
  74. /**
  75. * Implements hook_help().
  76. */
  77. function uc_order_help($path, $arg) {
  78. switch ($path) {
  79. case 'admin/store/settings/orders/products/fields':
  80. return '<p>' . t('Attach fields to order product entities. These entities represent the product line items that appear in orders. Fields attached here are not visible in the current version of Ubercart, but they are available in Views. A ')
  81. . l(t('Computed Field'), 'http://drupal.org/project/computed_field')
  82. . t(' is useful for attaching custom data to each product as it is ordered.') . '</p>';
  83. }
  84. }
  85. /**
  86. * Implements hook_menu().
  87. */
  88. function uc_order_menu() {
  89. global $user;
  90. $items = array();
  91. $items['admin/store/customers'] = array(
  92. 'title' => 'Customers',
  93. 'description' => 'View and search customer information.',
  94. 'page callback' => 'uc_order_customers',
  95. 'access arguments' => array('view customers'),
  96. 'weight' => -6,
  97. 'position' => 'left',
  98. 'file' => 'uc_order.admin.inc',
  99. );
  100. // admin/store/customers/view is provided by Views.
  101. // admin/store/customers/orders/% is provided by Views.
  102. $items['admin/store/settings/orders'] = array(
  103. 'title' => 'Orders',
  104. 'description' => 'Configure the display and workflow for orders.',
  105. 'page callback' => 'drupal_get_form',
  106. 'page arguments' => array('uc_order_settings_form'),
  107. 'access arguments' => array('administer store'),
  108. 'file' => 'uc_order.admin.inc',
  109. );
  110. $items['admin/store/settings/orders/settings'] = array(
  111. 'title' => 'Settings',
  112. 'description' => 'Edit the basic order settings.',
  113. 'type' => MENU_DEFAULT_LOCAL_TASK,
  114. 'weight' => -10,
  115. );
  116. $items['admin/store/settings/orders/workflow'] = array(
  117. 'title' => 'Workflow',
  118. 'description' => 'Modify and configure order states and statuses.',
  119. 'page callback' => 'drupal_get_form',
  120. 'page arguments' => array('uc_order_workflow_form'),
  121. 'access arguments' => array('administer order workflow'),
  122. 'type' => MENU_LOCAL_TASK,
  123. 'weight' => -8,
  124. 'file' => 'uc_order.admin.inc',
  125. );
  126. $items['admin/store/settings/orders/workflow/create'] = array(
  127. 'title' => 'Create custom order status',
  128. 'description' => 'Create a custom order status for your store.',
  129. 'page callback' => 'drupal_get_form',
  130. 'page arguments' => array('uc_order_status_create_form'),
  131. 'access arguments' => array('administer order workflow'),
  132. 'type' => MENU_LOCAL_ACTION,
  133. 'file' => 'uc_order.admin.inc',
  134. );
  135. $items['admin/store/orders'] = array(
  136. 'title' => 'Orders',
  137. 'description' => 'View and process orders.',
  138. 'page callback' => 'uc_order_orders',
  139. 'access arguments' => array('view all orders'),
  140. 'weight' => -10,
  141. 'position' => 'left',
  142. 'file' => 'uc_order.admin.inc',
  143. );
  144. // admin/store/orders/view is provided by Views.
  145. $items['admin/store/orders/create'] = array(
  146. 'title' => 'Create order',
  147. 'description' => 'Create an empty new order.',
  148. 'page callback' => 'drupal_get_form',
  149. 'page arguments' => array('uc_order_create_form'),
  150. 'access arguments' => array('create orders'),
  151. 'weight' => -5,
  152. 'file' => 'uc_order.admin.inc',
  153. );
  154. $items['admin/store/orders/create/%user'] = array(
  155. 'title' => 'Create order for this customer',
  156. 'description' => 'Create an empty new order.',
  157. 'page callback' => 'uc_order_create_for_user',
  158. 'page arguments' => array(4),
  159. 'access arguments' => array('create orders'),
  160. 'file' => 'uc_order.admin.inc',
  161. );
  162. // admin/store/orders/search is provided by Views.
  163. $items['admin/store/orders/address_book'] = array(
  164. 'title' => 'Select address',
  165. 'page callback' => 'uc_order_address_book',
  166. 'access arguments' => array('edit orders'),
  167. 'type' => MENU_CALLBACK,
  168. 'file' => 'uc_order.admin.inc',
  169. );
  170. $items['admin/store/orders/customer'] = array(
  171. 'title' => 'Select customer',
  172. 'page callback' => 'uc_order_select_customer',
  173. 'page arguments' => array(NULL),
  174. 'access arguments' => array('edit orders'),
  175. 'type' => MENU_CALLBACK,
  176. 'file' => 'uc_order.admin.inc',
  177. );
  178. // user/%/orders is provided by Views.
  179. $items['user/%user/orders/%uc_order'] = array(
  180. 'title callback' => 'uc_order_page_title',
  181. 'title arguments' => array(3),
  182. 'description' => 'View order.',
  183. 'page callback' => 'uc_order_view',
  184. 'page arguments' => array(3, 'customer'),
  185. 'access callback' => 'uc_order_can_view_order',
  186. 'access arguments' => array(1, 3),
  187. );
  188. $items['user/%user/orders/%uc_order/invoice'] = array(
  189. 'title' => 'View invoice',
  190. 'description' => 'View order invoice.',
  191. 'page callback' => 'uc_order_view_invoice',
  192. 'page arguments' => array(3),
  193. 'access callback' => 'uc_order_can_view_order',
  194. 'access arguments' => array(1, 3, TRUE),
  195. );
  196. $items['user/%user/orders/%uc_order/print'] = array(
  197. 'title' => 'Print invoice',
  198. 'description' => 'Print order invoice.',
  199. 'page callback' => 'uc_order_view_invoice',
  200. 'page arguments' => array(3, TRUE),
  201. 'access callback' => 'uc_order_can_view_order',
  202. 'access arguments' => array(1, 3, TRUE),
  203. );
  204. $items['admin/store/orders/%uc_order'] = array(
  205. 'title callback' => 'uc_order_page_title',
  206. 'title arguments' => array(3),
  207. 'description' => 'View order',
  208. 'page callback' => 'uc_order_view',
  209. 'page arguments' => array(3, 'view'),
  210. 'access arguments' => array('view all orders'),
  211. 'file' => 'uc_order.admin.inc',
  212. );
  213. $items['admin/store/orders/%uc_order/view'] = array(
  214. 'title' => 'View',
  215. 'type' => MENU_DEFAULT_LOCAL_TASK,
  216. 'weight' => -10,
  217. );
  218. $items['admin/store/orders/%uc_order/edit'] = array(
  219. 'title' => 'Edit',
  220. 'page callback' => 'drupal_get_form',
  221. 'page arguments' => array('uc_order_edit_form', 3),
  222. 'access arguments' => array('edit orders'),
  223. 'type' => MENU_LOCAL_TASK,
  224. 'weight' => 1,
  225. 'file' => 'uc_order.admin.inc',
  226. );
  227. $items['admin/store/orders/%uc_order/add_line_item/%'] = array(
  228. 'title' => 'Add a line item',
  229. 'page callback' => 'drupal_get_form',
  230. 'page arguments' => array('uc_order_add_line_item_form', 3, 5),
  231. 'access arguments' => array('edit orders'),
  232. 'type' => MENU_CALLBACK,
  233. 'file' => 'uc_order.admin.inc',
  234. );
  235. $items['admin/store/orders/%uc_order/invoice'] = array(
  236. 'title' => 'Invoice',
  237. 'page callback' => 'uc_order_view_invoice',
  238. 'page arguments' => array(3),
  239. 'access arguments' => array('view all orders'),
  240. 'type' => MENU_LOCAL_TASK,
  241. 'weight' => 3,
  242. );
  243. $items['admin/store/orders/%uc_order/invoice/view'] = array(
  244. 'title' => 'View invoice',
  245. 'type' => MENU_DEFAULT_LOCAL_TASK,
  246. 'weight' => -10,
  247. );
  248. $items['admin/store/orders/%uc_order/invoice/print'] = array(
  249. 'title' => 'Printable invoice',
  250. 'page arguments' => array(3, TRUE),
  251. 'access arguments' => array('view all orders'),
  252. 'type' => MENU_LOCAL_TASK,
  253. 'weight' => -5,
  254. );
  255. $items['admin/store/orders/%uc_order/invoice/mail'] = array(
  256. 'title' => 'Mail invoice',
  257. 'page callback' => 'drupal_get_form',
  258. 'page arguments' => array('uc_order_mail_invoice_form', 3),
  259. 'access arguments' => array('view all orders'),
  260. 'type' => MENU_LOCAL_TASK,
  261. 'weight' => 0,
  262. 'file' => 'uc_order.admin.inc',
  263. );
  264. $items['admin/store/orders/%uc_order/log'] = array(
  265. 'title' => 'Log',
  266. 'page callback' => 'uc_order_log',
  267. 'page arguments' => array(3),
  268. 'access arguments' => array('view all orders'),
  269. 'type' => MENU_LOCAL_TASK,
  270. 'weight' => 10,
  271. 'file' => 'uc_order.admin.inc',
  272. );
  273. $items['admin/store/orders/%uc_order/delete'] = array(
  274. 'title' => 'Delete an order',
  275. 'page callback' => 'drupal_get_form',
  276. 'page arguments' => array('uc_order_delete_confirm_form', 3),
  277. 'access callback' => 'uc_order_can_delete',
  278. 'access arguments' => array(3),
  279. 'file' => 'uc_order.admin.inc',
  280. );
  281. return $items;
  282. }
  283. /**
  284. * Implements hook_menu_alter().
  285. */
  286. function uc_order_menu_alter(&$items) {
  287. // Adjust the Field UI tabs on admin/store/settings/orders.
  288. $items['admin/store/settings/orders/products/fields']['title'] = 'Ordered product fields';
  289. $items['admin/store/settings/orders/products/fields']['weight'] = 3;
  290. // Disable field display settings for ordered products,
  291. // as we don't use this anywhere (yet).
  292. $items['admin/store/settings/orders/products/display']['access callback'] = FALSE;
  293. }
  294. /**
  295. * Implements hook_menu_local_tasks_alter().
  296. */
  297. function uc_order_menu_local_tasks_alter(&$data, $router_item, $root_path) {
  298. if ($root_path == 'admin/store/customers/orders/%') {
  299. $uid = $router_item['page_arguments'][0];
  300. $item = menu_get_item('admin/store/orders/create/' . $uid);
  301. if ($item['access']) {
  302. $data['actions']['output'][] = array(
  303. '#theme' => 'menu_local_action',
  304. '#link' => $item,
  305. );
  306. }
  307. }
  308. }
  309. /**
  310. * Title callback for admin/store/orders/%uc_order.
  311. */
  312. function uc_order_page_title($order) {
  313. return t('Order @order_id', array('@order_id' => $order->order_id));
  314. }
  315. /**
  316. * Implements hook_admin_paths().
  317. */
  318. function uc_order_admin_paths() {
  319. return array(
  320. // Don't show invoices with the admin theme, overlay, etc.
  321. 'admin/store/orders/*/invoice*' => FALSE,
  322. );
  323. }
  324. /**
  325. * Implements hook_init().
  326. */
  327. function uc_order_init() {
  328. // Load uc_order.js on all order and customer admin pages.
  329. if (arg(0) == 'admin' && arg(1) == 'store' && (arg(2) == 'orders' || arg(2) == 'customers')) {
  330. drupal_add_js(array(
  331. 'ucURL' => array(
  332. 'adminOrders' => url('admin/store/orders/'),
  333. ),
  334. ), 'setting');
  335. drupal_add_js(drupal_get_path('module', 'uc_order') . '/uc_order.js');
  336. }
  337. }
  338. /**
  339. * Implements hook_theme().
  340. */
  341. function uc_order_theme($existing, $type, $theme, $path) {
  342. $theme_hooks = array(
  343. 'uc_order' => array(
  344. 'template' => 'uc-order',
  345. 'path' => $path . '/templates',
  346. 'variables' => array(
  347. 'order' => NULL,
  348. 'op' => 'view',
  349. 'template' => 'customer',
  350. 'thank_you_message' => FALSE,
  351. 'help_text' => FALSE,
  352. 'email_text' => FALSE,
  353. 'store_footer' => FALSE,
  354. 'business_header' => FALSE,
  355. 'shipping_method' => FALSE,
  356. ),
  357. ),
  358. 'uc_order_invoice_page' => array(
  359. 'variables' => array('content' => NULL),
  360. 'template' => 'uc_order-invoice-page',
  361. ),
  362. 'uc_order_state_table' => array(
  363. 'render element' => 'form',
  364. 'file' => 'uc_order.admin.inc',
  365. ),
  366. 'uc_order_status_table' => array(
  367. 'render element' => 'form',
  368. 'file' => 'uc_order.admin.inc',
  369. ),
  370. 'uc_order_edit_form' => array(
  371. 'render element' => 'form',
  372. 'file' => 'uc_order.admin.inc',
  373. ),
  374. 'uc_order_pane_line_items' => array(
  375. 'render element' => 'form',
  376. 'file' => 'uc_order.order_pane.inc',
  377. ),
  378. );
  379. $theme_hooks += array(
  380. 'uc_order__customer' => array(
  381. 'template' => 'uc-order--customer',
  382. 'path' => $path . '/templates',
  383. 'variables' => $theme_hooks['uc_order']['variables'],
  384. ),
  385. 'uc_order__admin' => array(
  386. 'template' => 'uc-order--admin',
  387. 'path' => $path . '/templates',
  388. 'variables' => $theme_hooks['uc_order']['variables'],
  389. ),
  390. );
  391. return $theme_hooks;
  392. }
  393. /**
  394. * Implements hook_permission().
  395. */
  396. function uc_order_permission() {
  397. return array(
  398. 'administer order workflow' => array(
  399. 'title' => t('Administer order workflow'),
  400. ),
  401. 'view customers' => array(
  402. 'title' => t('View customers'),
  403. ),
  404. 'view own orders' => array(
  405. 'title' => t('View own orders'),
  406. ),
  407. 'view own invoices' => array(
  408. 'title' => t('View own invoices'),
  409. ),
  410. 'view all orders' => array(
  411. 'title' => t('View all orders'),
  412. ),
  413. 'create orders' => array(
  414. 'title' => t('Create orders'),
  415. ),
  416. 'edit orders' => array(
  417. 'title' => t('Edit orders'),
  418. ),
  419. 'delete orders' => array(
  420. 'title' => t('Delete orders'),
  421. ),
  422. 'unconditionally delete orders' => array(
  423. 'title' => t('Unconditionally delete orders'),
  424. ),
  425. );
  426. }
  427. /**
  428. * Access callback for user/%user/orders*.
  429. */
  430. function uc_order_can_view_order($account, $order, $view_invoice = FALSE) {
  431. global $user;
  432. // Users with 'view all orders' are straightforward.
  433. $access = user_access('view all orders');
  434. // If the user is the current user and is not anonymous,
  435. // also allow the "own" permissions.
  436. if ($user->uid && $user->uid == $account->uid) {
  437. if ($view_invoice) {
  438. $access = $access || user_access('view own invoices');
  439. }
  440. else {
  441. $access = $access || user_access('view own orders');
  442. }
  443. }
  444. // The order must also be owned by the user specified in the URL.
  445. $access = $access && $account->uid == $order->uid;
  446. return $access;
  447. }
  448. /**
  449. * Implements hook_query_TAG_alter().
  450. */
  451. function uc_order_query_uc_order_access_alter(QueryAlterableInterface $query) {
  452. global $user;
  453. // Read metadata from query, if provided.
  454. if (!$account = $query->getMetaData('account')) {
  455. $account = $user;
  456. }
  457. // If account can view all orders, we don't need to alter the query.
  458. if (user_access('view all orders', $account)) {
  459. return;
  460. }
  461. if (user_access('view own orders', $account)) {
  462. // Only allow the user to see their own orders.
  463. foreach ($query->getTables() as $table) {
  464. if ($table['table'] === 'uc_orders') {
  465. $query->condition($table['alias'] . '.uid', $account->uid);
  466. }
  467. }
  468. }
  469. else {
  470. // Deny access to everything.
  471. $query->where('1 = 0');
  472. }
  473. }
  474. /**
  475. * Implements hook_entity_info().
  476. */
  477. function uc_order_entity_info() {
  478. return array(
  479. 'uc_order' => array(
  480. 'label' => t('Order'),
  481. 'controller class' => 'UcOrderController',
  482. 'base table' => 'uc_orders',
  483. 'fieldable' => TRUE,
  484. 'entity keys' => array(
  485. 'id' => 'order_id',
  486. ),
  487. 'bundles' => array(
  488. 'uc_order' => array(
  489. 'label' => t('Order'),
  490. 'admin' => array(
  491. 'path' => 'admin/store/settings/orders',
  492. 'access arguments' => array('administer store'),
  493. ),
  494. ),
  495. ),
  496. 'view modes' => array(
  497. 'view' => array(
  498. 'label' => t('Admin view'),
  499. ),
  500. 'customer' => array(
  501. 'label' => t('Customer view'),
  502. ),
  503. ),
  504. 'uri callback' => 'uc_order_uri',
  505. // Entity API callbacks.
  506. 'access callback' => 'uc_order_order_entity_access',
  507. 'creation callback' => 'uc_order_create',
  508. 'save callback' => 'uc_order_save',
  509. 'deletion callback' => 'uc_order_delete',
  510. ),
  511. 'uc_order_product' => array(
  512. 'label' => t('Order product'),
  513. 'base table' => 'uc_order_products',
  514. 'controller class' => 'UcOrderProductController',
  515. 'metadata controller class' => 'UcOrderProductMetadataController',
  516. 'fieldable' => TRUE,
  517. 'module' => 'uc_order',
  518. 'entity keys' => array(
  519. 'id' => 'order_product_id',
  520. ),
  521. 'bundles' => array(
  522. 'uc_order_product' => array(
  523. 'label' => t('Order product'),
  524. 'admin' => array(
  525. 'path' => 'admin/store/settings/orders/products',
  526. 'access arguments' => array('administer store'),
  527. ),
  528. ),
  529. ),
  530. 'view modes' => array(
  531. 'full' => array(
  532. 'label' => t('Normal view'),
  533. ),
  534. 'cart' => array(
  535. 'label' => t('Cart view'),
  536. ),
  537. ),
  538. // Entity API callbacks.
  539. 'access callback' => 'uc_order_order_product_access',
  540. ),
  541. );
  542. }
  543. /**
  544. * Implements hook_field_extra_fields().
  545. */
  546. function uc_order_field_extra_fields() {
  547. $panes = module_invoke_all('uc_order_pane');
  548. $extra = array();
  549. foreach ($panes as $id => $pane) {
  550. $extra_field = array(
  551. 'label' => $pane['title'],
  552. 'description' => $pane['desc'],
  553. 'weight' => $pane['weight'],
  554. );
  555. if (in_array('edit', $pane['show'])) {
  556. $extra['uc_order']['uc_order']['form'][$id] = $extra_field;
  557. }
  558. if (in_array('view', $pane['show']) || in_array('customer', $pane['show'])) {
  559. $extra['uc_order']['uc_order']['display'][$id] = $extra_field;
  560. }
  561. }
  562. return $extra;
  563. }
  564. /**
  565. * Implements hook_user_view().
  566. */
  567. function uc_order_user_view($account, $view_mode) {
  568. global $user;
  569. if ($user->uid && (($user->uid == $account->uid && user_access('view own orders')) || user_access('view all orders'))) {
  570. $account->content['summary']['orders'] = array(
  571. '#type' => 'user_profile_item',
  572. '#title' => t('Orders'),
  573. '#markup' => l(t('Click here to view your order history.'), 'user/' . $account->uid . '/orders'),
  574. );
  575. }
  576. }
  577. /**
  578. * Implements hook_mail().
  579. */
  580. function uc_order_mail($key, &$message, $params) {
  581. $langcode = isset($message['language']) ? $message['language']->language : NULL;
  582. // Build the appropriate message parameters based on the e-mail key.
  583. switch ($key) {
  584. // Setup an e-mailed invoice.
  585. case 'invoice':
  586. $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed';
  587. $message['subject'] = t('Your Order Invoice', array(), array('langcode' => $langcode));
  588. $message['from'] = uc_store_email_from();
  589. $message['body'][] = theme('uc_order', array('order' => $params['order'], 'op' => 'admin-mail', 'template' => variable_get('uc_cust_order_invoice_template', 'customer')));
  590. break;
  591. // Setup a custom e-mail defined by an action on a predicate.
  592. case 'action-mail':
  593. // Assemble an email message from the conditional actions settings.
  594. $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed';
  595. $message['from'] = $params['from'];
  596. // Perform token replacement on the subject and body.
  597. $subject = token_replace($params['subject'], $params['replacements'], $langcode ? array('language' => $message['language']) : array());
  598. $body = token_replace($params['message'], $params['replacements'], $langcode ? array('language' => $message['language']) : array());
  599. // Strip newline characters from e-mail subjects.
  600. // @todo: Maybe drupal_mail_send() should do this?
  601. $message['subject'] = str_replace(array("\r\n", "\r", "\n"), ' ', $subject);
  602. // Apply an input format to the message body if specified.
  603. if (isset($params['format'])) {
  604. $message['body'] = explode("\n", check_markup($body, $params['format'], $langcode));
  605. }
  606. else {
  607. $message['body'] = explode("\n", $body);
  608. }
  609. break;
  610. }
  611. }
  612. /**
  613. * Implements hook_views_api().
  614. */
  615. function uc_order_views_api() {
  616. return array(
  617. 'api' => '2.0',
  618. 'path' => drupal_get_path('module', 'uc_order') . '/views',
  619. );
  620. }
  621. /**
  622. * Implements hook_form_FORM_ID_alter() for views_exposed_form().
  623. */
  624. function uc_order_form_views_exposed_form_alter(&$form, &$form_state) {
  625. if (substr($form['#id'], 0, 29) == 'views-exposed-form-uc-orders-') {
  626. $form['#submit'][] = 'uc_order_form_views_exposed_form_submit';
  627. if ($form['#id'] == 'views-exposed-form-uc-orders-search' && !module_exists('date_views')) {
  628. drupal_set_message(t('Download the <a href="@url">Date module</a> and enable Date Views to allow searching orders by date.', array('@url' => 'http://drupal.org/project/date')), 'warning');
  629. }
  630. }
  631. }
  632. /**
  633. * Redirects if an order ID was entered in the exposed field.
  634. */
  635. function uc_order_form_views_exposed_form_submit($form, &$form_state) {
  636. if (!empty($form_state['values']['order_id']) && uc_order_exists($form_state['values']['order_id'])) {
  637. $form_state['redirect'] = 'admin/store/orders/' . $form_state['values']['order_id'];
  638. $form_state['no_redirect'] = FALSE;
  639. }
  640. }
  641. /**
  642. * Implements hook_form_FORM_ID_alter() for uc_store_settings_form().
  643. */
  644. function uc_order_form_uc_store_settings_form_alter(&$form, &$form_state) {
  645. $form['display']['uc_order_capitalize_addresses'] = array(
  646. '#type' => 'checkbox',
  647. '#title' => t('Capitalize address on order screens'),
  648. '#default_value' => variable_get('uc_order_capitalize_addresses', TRUE),
  649. );
  650. }
  651. /**
  652. * Implements hook_uc_order_pane().
  653. */
  654. function uc_order_uc_order_pane() {
  655. $panes['print_button'] = array(
  656. 'callback' => 'uc_order_pane_print_button',
  657. 'title' => t('Print button'),
  658. 'display title' => '',
  659. 'desc' => t("Button to open a printable invoice."),
  660. 'class' => 'abs-left',
  661. 'weight' => -10,
  662. 'show' => array('customer'),
  663. );
  664. $panes['ship_to'] = array(
  665. 'callback' => 'uc_order_pane_ship_to',
  666. 'title' => t('Ship to'),
  667. 'desc' => t("Manage the order's shipping address and contact information."),
  668. 'class' => 'pos-left',
  669. 'weight' => 1,
  670. 'show' => array('view', 'edit', 'customer'),
  671. );
  672. $panes['bill_to'] = array(
  673. 'callback' => 'uc_order_pane_bill_to',
  674. 'title' => t('Bill to'),
  675. 'desc' => t("Manage the order's billing address and contact information."),
  676. 'class' => 'pos-left',
  677. 'weight' => 2,
  678. 'show' => array('view', 'edit', 'customer'),
  679. );
  680. $panes['customer'] = array(
  681. 'callback' => 'uc_order_pane_customer',
  682. 'title' => t('Customer info'),
  683. 'desc' => t("Manage the information for the customer's user account."),
  684. 'class' => 'pos-left',
  685. 'weight' => 3,
  686. 'show' => array('view', 'edit'),
  687. );
  688. $panes['products'] = array(
  689. 'callback' => 'uc_order_pane_products',
  690. 'title' => t('Products'),
  691. 'desc' => t('Manage the products an order contains.'),
  692. 'class' => 'abs-left',
  693. 'weight' => 5,
  694. 'show' => array('view', 'edit', 'customer'),
  695. );
  696. $panes['line_items'] = array(
  697. 'callback' => 'uc_order_pane_line_items',
  698. 'title' => t('Line items'),
  699. 'display title' => '',
  700. 'desc' => t("View and modify an order's line items."),
  701. 'class' => 'abs-left',
  702. 'weight' => 6,
  703. 'show' => array('view', 'edit', 'customer'),
  704. );
  705. $panes['order_comments'] = array(
  706. 'callback' => 'uc_order_pane_order_comments',
  707. 'title' => t('Order comments'),
  708. 'desc' => t('View the order comments, used for communicating with customers.'),
  709. 'class' => 'abs-left',
  710. 'weight' => 8,
  711. 'show' => array('view', 'customer'),
  712. );
  713. $panes['admin_comments'] = array(
  714. 'callback' => 'uc_order_pane_admin_comments',
  715. 'title' => t('Admin comments'),
  716. 'desc' => t('View the admin comments, used for administrative notes and instructions.'),
  717. 'class' => 'abs-left',
  718. 'weight' => 9,
  719. 'show' => array('view', 'edit'),
  720. );
  721. $panes['update'] = array(
  722. 'callback' => 'uc_order_pane_update',
  723. 'title' => t('Update order'),
  724. 'desc' => t("Update an order's status or add comments to an order."),
  725. 'class' => 'abs-left',
  726. 'weight' => 10,
  727. 'show' => array('view'),
  728. );
  729. return $panes;
  730. }
  731. /**
  732. * Implements hook_uc_order_state().
  733. */
  734. function uc_order_uc_order_state() {
  735. $states['canceled'] = array(
  736. 'title' => t('Canceled'),
  737. 'weight' => -20,
  738. 'scope' => 'specific',
  739. );
  740. $states['in_checkout'] = array(
  741. 'title' => t('In checkout'),
  742. 'weight' => -10,
  743. 'scope' => 'specific',
  744. );
  745. $states['post_checkout'] = array(
  746. 'title' => t('Post checkout'),
  747. 'weight' => 0,
  748. 'scope' => 'general',
  749. );
  750. $states['completed'] = array(
  751. 'title' => t('Completed'),
  752. 'weight' => 20,
  753. 'scope' => 'general',
  754. );
  755. return $states;
  756. }
  757. /**
  758. * Implements hook_uc_line_item().
  759. */
  760. function uc_order_uc_line_item() {
  761. $items['subtotal'] = array(
  762. 'title' => t('Subtotal'),
  763. 'weight' => 0,
  764. 'stored' => FALSE,
  765. 'calculated' => FALSE,
  766. 'callback' => 'uc_line_item_subtotal',
  767. );
  768. $items['generic'] = array(
  769. 'title' => t('Empty line'),
  770. 'weight' => 2,
  771. 'stored' => TRUE,
  772. 'add_list' => TRUE,
  773. 'calculated' => TRUE,
  774. 'callback' => 'uc_line_item_generic',
  775. );
  776. $items['total'] = array(
  777. 'title' => t('Total'),
  778. 'weight' => 15,
  779. 'stored' => FALSE,
  780. 'calculated' => FALSE,
  781. 'display_only' => TRUE,
  782. 'callback' => 'uc_line_item_total',
  783. );
  784. return $items;
  785. }
  786. /**
  787. * Implements hook_uc_message().
  788. */
  789. function uc_order_uc_message() {
  790. $messages['order_update_email'] = t("[order:first-name] [order:last-name],\n\nYour order number [order:link] at [store:name] has been updated.\n\nOrder status: [order:order-status]\n\nOrder comment:\n[order:last-comment]\n\nBrowse to the following page to login to your account and view your order details:\n[site:login-link]\n\n\nThanks again,\n\n[store:name]\n[site:slogan]");
  791. return $messages;
  792. }
  793. /**
  794. * Implements hook_uc_invoice_templates().
  795. */
  796. function uc_order_uc_invoice_templates() {
  797. return array('admin', 'customer');
  798. }
  799. /**
  800. * Entity API "uri callback" for uc_order entity.
  801. *
  802. * @param $order
  803. * The order to return the URI for.
  804. */
  805. function uc_order_uri($order) {
  806. return array(
  807. 'path' => 'admin/store/orders/' . $order->order_id,
  808. );
  809. }
  810. /**
  811. * Entity API "access callback" for uc_order entity.
  812. *
  813. * Checks order access for various operations.
  814. *
  815. * @param $op
  816. * The operation being performed. One of 'view', 'update', 'create' or
  817. * 'delete'.
  818. * @param $order
  819. * (optional) An order to check access for.
  820. * @param $account
  821. * (optional) The account to check, or current user if not given.
  822. */
  823. function uc_order_order_entity_access($op, $order = NULL, $account = NULL) {
  824. global $user;
  825. if (!isset($account)) {
  826. $account = $user;
  827. }
  828. if ($op == 'delete') {
  829. if (!empty($order)) {
  830. return uc_order_can_delete($order, $account);
  831. }
  832. else {
  833. return FALSE;
  834. }
  835. }
  836. if ($op == 'update') {
  837. return user_access('edit orders', $account);
  838. }
  839. if ($op == 'create') {
  840. return user_access('create orders', $account);
  841. }
  842. if ($op == 'view') {
  843. if (user_access('view all orders', $account)) {
  844. return TRUE;
  845. }
  846. return (!empty($order) && $order->uid == $account->uid && user_access('view own orders', $account));
  847. }
  848. }
  849. /**
  850. * Generates and saves a new order.
  851. *
  852. * @param $uid
  853. * The user ID to assign the order to.
  854. * @param $state
  855. * The initial state of the order.
  856. */
  857. function uc_order_new($uid = 0, $state = 'in_checkout') {
  858. $order = new UcOrder($uid, $state);
  859. module_invoke_all('entity_presave', $order, 'uc_order');
  860. drupal_write_record('uc_orders', $order);
  861. uc_order_module_invoke('new', $order, NULL);
  862. module_invoke_all('entity_insert', $order, 'uc_order');
  863. return $order;
  864. }
  865. /**
  866. * Entity API "creation callback" for uc_order entity.
  867. *
  868. * @param $values
  869. * A set of default values for the order.
  870. */
  871. function uc_order_create($values, $entity_type) {
  872. $order = new UcOrder();
  873. foreach ($values as $key => $value) {
  874. $order->$key = $value;
  875. }
  876. module_invoke_all('entity_presave', $order, 'uc_order');
  877. drupal_write_record('uc_orders', $order);
  878. uc_order_module_invoke('new', $order, NULL);
  879. module_invoke_all('entity_insert', $order, 'uc_order');
  880. return $order;
  881. }
  882. /**
  883. * Entity API "save callback" for uc_order entity.
  884. *
  885. * Saves an order to the database.
  886. */
  887. function uc_order_save($order) {
  888. $transaction = db_transaction();
  889. try {
  890. if (is_null($order->order_id) || intval($order->order_id) == 0) {
  891. return FALSE;
  892. }
  893. field_attach_presave('uc_order', $order);
  894. $order->order_total = uc_order_get_total($order);
  895. $order->product_count = uc_order_get_product_count($order);
  896. if (is_null($order->delivery_country) || $order->delivery_country == 0) {
  897. $order->delivery_country = variable_get('uc_store_country', 840);
  898. }
  899. if (is_null($order->billing_country) || $order->billing_country == 0) {
  900. $order->billing_country = variable_get('uc_store_country', 840);
  901. }
  902. $order->host = ip_address();
  903. $order->modified = REQUEST_TIME;
  904. uc_order_module_invoke('presave', $order, NULL);
  905. module_invoke_all('entity_presave', $order, 'uc_order');
  906. drupal_write_record('uc_orders', $order, 'order_id');
  907. if (is_array($order->products)) {
  908. foreach ($order->products as $product) {
  909. drupal_alter('uc_order_product', $product, $order);
  910. uc_order_product_save($order->order_id, $product);
  911. }
  912. }
  913. field_attach_update('uc_order', $order);
  914. uc_order_module_invoke('save', $order, NULL);
  915. module_invoke_all('entity_update', $order, 'uc_order');
  916. $order->order_total = uc_order_get_total($order);
  917. }
  918. catch (Exception $e) {
  919. $transaction->rollback('uc_order');
  920. watchdog_exception('uc_order', $e);
  921. throw $e;
  922. }
  923. }
  924. /**
  925. * Entity API "deletion callback" for uc_order entity.
  926. *
  927. * Deletes an order and tells other modules to do the same.
  928. *
  929. * @param $order_id
  930. * The ID of the order you wish to delete.
  931. */
  932. function uc_order_delete($order_id) {
  933. global $user;
  934. $order = uc_order_load($order_id);
  935. // Perform the operations if we're deleting a valid order.
  936. if ($order !== FALSE) {
  937. rules_invoke_event('uc_order_delete', $order);
  938. uc_order_module_invoke('delete', $order, NULL);
  939. module_invoke_all('entity_delete', $order, 'uc_order');
  940. // Delete data from the appropriate Ubercart order tables.
  941. db_delete('uc_orders')
  942. ->condition('order_id', $order_id)
  943. ->execute();
  944. $efq = new EntityFieldQuery();
  945. $result = $efq->entityCondition('entity_type', 'uc_order_product')
  946. ->propertyCondition('order_id', $order->order_id)
  947. ->execute();
  948. if (!empty($result['uc_order_product'])) {
  949. $product_ids = array_keys($result['uc_order_product']);
  950. uc_order_product_delete_multiple($product_ids);
  951. }
  952. db_delete('uc_order_comments')
  953. ->condition('order_id', $order_id)
  954. ->execute();
  955. db_delete('uc_order_admin_comments')
  956. ->condition('order_id', $order_id)
  957. ->execute();
  958. db_delete('uc_order_log')
  959. ->condition('order_id', $order_id)
  960. ->execute();
  961. // Delete line items for the order.
  962. uc_order_delete_line_item($order_id, TRUE);
  963. // Delete attached field values.
  964. field_attach_delete('uc_order', $order);
  965. // Log the action in the database.
  966. watchdog('uc_order', 'Order @order_id deleted by user @uid.', array('@order_id' => $order_id, '@uid' => $user->uid));
  967. }
  968. }
  969. /**
  970. * Entity API "access callback" for uc_order_product entity.
  971. *
  972. * Checks order product access for various operations.
  973. *
  974. * @param $op
  975. * The operation being performed. One of 'view', 'update', 'create' or
  976. * 'delete'.
  977. * @param $order
  978. * Optionally an order to check access for.
  979. * @param $account
  980. * The user to check for. Leave it to NULL to check for the current user.
  981. */
  982. function uc_order_order_product_access($op, $product = NULL, $account = NULL) {
  983. if (isset($product) && $product->order_id) {
  984. $order = uc_order_load($product->order_id);
  985. return uc_order_order_entity_access($op, $order, $account);
  986. }
  987. return FALSE;
  988. }
  989. /**
  990. * Save a product to an order.
  991. */
  992. function uc_order_product_save($order_id, $product) {
  993. $product->order_id = $order_id;
  994. return entity_save('uc_order_product', $product);
  995. }
  996. /**
  997. * API wrapper for uc_order_product_save().
  998. *
  999. * @todo: Change the signature for uc_order_product_save() to this one.
  1000. */
  1001. function uc_order_product_entity_save($product) {
  1002. return uc_order_product_save($product->order_id, $product);
  1003. }
  1004. /**
  1005. * Remove a single product line from an order.
  1006. *
  1007. * @param int $order_product_id
  1008. * Ordered product ID of product to remove.
  1009. */
  1010. function uc_order_product_delete($order_product_id) {
  1011. return uc_order_product_delete_multiple(array($order_product_id));
  1012. }
  1013. /**
  1014. * Remove multiple product lines from an order.
  1015. *
  1016. * @param array $order_product_ids
  1017. * List of ordered product IDs of products to remove.
  1018. */
  1019. function uc_order_product_delete_multiple($order_product_ids) {
  1020. return entity_delete_multiple('uc_order_product', $order_product_ids);
  1021. }
  1022. /**
  1023. * Load a single ordered product entity.
  1024. *
  1025. * @param int $opid
  1026. * The ID of the ordered product entity.
  1027. * @param bool $reset
  1028. * Whether to reset the internal cache for ordered product IDs. Defaults to
  1029. * FALSE.
  1030. *
  1031. * @return object
  1032. * The ordered product entity matching $opid.
  1033. */
  1034. function uc_order_product_load($opid, $reset = FALSE) {
  1035. $products = uc_order_product_load_multiple(array($opid), $reset);
  1036. return reset($products);
  1037. }
  1038. /**
  1039. * Load multiple ordered product entities.
  1040. *
  1041. * @param array $opids
  1042. * Array of ordered product IDs of products to load. Default is the empty
  1043. * array, which loads all ordered products.
  1044. * @param bool $reset
  1045. * Whether to reset the internal cache for ordered product IDs. Defaults to
  1046. * FALSE.
  1047. *
  1048. * @return array
  1049. * An array of ordered product entities matching $opids, indexed by
  1050. * ID.
  1051. */
  1052. function uc_order_product_load_multiple($opids = array(), $reset = FALSE) {
  1053. return entity_load('uc_order_product', $opids, array(), $reset);
  1054. }
  1055. /**
  1056. * Generate an array for rendering the multiple order products.
  1057. *
  1058. * Order products being viewed are generally expected to be fully-loaded entity
  1059. * objects, thus have their name or id key set. However, it is possible to
  1060. * view a single entity without any id, e.g. for generating a preview during
  1061. * creation.
  1062. *
  1063. * @param array $order_products
  1064. * The array of order product to render.
  1065. * @param $view_mode
  1066. * A view mode as used by this entity type, e.g. 'full', 'teaser'...
  1067. * @param $langcode
  1068. * (optional) A language code to use for rendering. Defaults to the global
  1069. * content language of the current request.
  1070. * @param $page
  1071. * (optional) If set will control if the entity is rendered: if TRUE
  1072. * the entity will be rendered without its title, so that it can be embedded
  1073. * in another context. If FALSE the entity will be displayed with its title
  1074. * in a mode suitable for lists.
  1075. * If unset, the page mode will be enabled if the current path is the URI
  1076. * of the entity, as returned by entity_uri().
  1077. *
  1078. * @return
  1079. * The renderable array, keyed by the entity type and by entity identifiers,
  1080. * for which the entity name is used if existing - see entity_id(). If there
  1081. * is no information on how to view an entity, FALSE is returned.
  1082. */
  1083. function uc_order_product_view_multiple($order_products, $view_mode = 'full', $langcode = NULL, $page = NULL) {
  1084. return entity_view('uc_order_product', $order_products, $view_mode, $langcode, $page);
  1085. }
  1086. /**
  1087. * Merge fields from their associated nodes into a set of order products.
  1088. *
  1089. * @param $products
  1090. * An array of order_products.
  1091. * @param $published
  1092. * TRUE to load only published nodes, FALSE to load all nodes.
  1093. *
  1094. * @return
  1095. * TRUE if the merge was successful for all products in the set. FALSE
  1096. * otherwise (i.e. if the associated product node no longer exists).
  1097. */
  1098. function uc_order_product_revive($products, $published = TRUE) {
  1099. // Allow invocation for a single product.
  1100. if (!is_array($products)) {
  1101. $products = array($products);
  1102. }
  1103. // Load the nodes associated with each order product.
  1104. $nids = array();
  1105. foreach ($products as $product) {
  1106. $nids[] = $product->nid;
  1107. }
  1108. $nodes = node_load_multiple($nids);
  1109. // Merge in fields from any nodes that still exist (but don't override order
  1110. // product fields that are already set).
  1111. $return = TRUE;
  1112. foreach ($products as &$product) {
  1113. if (!empty($nodes[$product->nid]) && (!$published || $nodes[$product->nid]->status == NODE_PUBLISHED)) {
  1114. foreach ($nodes[$product->nid] as $key => $value) {
  1115. if (!isset($product->$key)) {
  1116. $product->$key = $value;
  1117. }
  1118. }
  1119. // Order products are always variants.
  1120. $product->variant = TRUE;
  1121. }
  1122. else {
  1123. $return = FALSE;
  1124. }
  1125. }
  1126. return $return;
  1127. }
  1128. /**
  1129. * Displays the order view screen, constructed via hook_uc_order_pane().
  1130. */
  1131. function uc_order_view($order, $view_mode = 'full') {
  1132. $order->content['#view_mode'] = $view_mode;
  1133. // Build fields content.
  1134. // In case of a multiple view, node_view_multiple() already ran the
  1135. // 'prepare_view' step. An internal flag prevents the operation from running
  1136. // twice.
  1137. field_attach_prepare_view('uc_order', array($order->order_id => $order), $view_mode);
  1138. entity_prepare_view('uc_order', array($order->order_id => $order));
  1139. $order->content += field_attach_view('uc_order', $order, $view_mode);
  1140. $panes = _uc_order_pane_list($view_mode);
  1141. foreach ($panes as $pane) {
  1142. if (in_array($view_mode, $pane['show'])) {
  1143. $func = $pane['callback'];
  1144. if (function_exists($func) && ($contents = $func($view_mode, $order)) != NULL) {
  1145. $title = isset($pane['display title']) ? $pane['display title'] : $pane['title'];
  1146. if ($title) {
  1147. $title = array(
  1148. '#markup' => $pane['title'] . ':',
  1149. '#prefix' => '<div class="order-pane-title">',
  1150. '#suffix' => '</div>',
  1151. );
  1152. }
  1153. else {
  1154. $title = array();
  1155. }
  1156. $order->content[$pane['id']] = array(
  1157. '#prefix' => '<div class="order-pane ' . $pane['class'] . '" id="order-pane-' . $pane['id'] . '">',
  1158. '#suffix' => '</div>',
  1159. );
  1160. $order->content[$pane['id']]['title'] = $title;
  1161. $order->content[$pane['id']]['pane'] = $contents;
  1162. }
  1163. }
  1164. }
  1165. return $order->content;
  1166. }
  1167. /**
  1168. * Displays an order invoice.
  1169. */
  1170. function uc_order_view_invoice($order, $print = FALSE) {
  1171. $build = array(
  1172. '#theme' => 'uc_order',
  1173. '#order' => $order,
  1174. '#op' => $print ? 'print' : 'view',
  1175. '#template' => variable_get('uc_cust_order_invoice_template', 'customer'),
  1176. );
  1177. if ($print) {
  1178. drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
  1179. print theme('uc_order_invoice_page', array('content' => drupal_render($build)));
  1180. exit();
  1181. }
  1182. return $build;
  1183. }
  1184. /**
  1185. * Loads one order entity from the database.
  1186. */
  1187. function uc_order_load($order_id, $reset = FALSE) {
  1188. if (is_null($order_id) || $order_id < 1) {
  1189. return FALSE;
  1190. }
  1191. $orders = uc_order_load_multiple(array($order_id), array(), $reset);
  1192. return $orders ? reset($orders) : FALSE;
  1193. }
  1194. /**
  1195. * Loads one or more order entities from the database.
  1196. *
  1197. * @param $ids
  1198. * An array of order IDs.
  1199. * @param array $conditions
  1200. * An array of conditions on the {uc_orders} table in the form
  1201. * 'field' => $value.
  1202. *
  1203. * @return array
  1204. * An array of order objects indexed by order_id.
  1205. */
  1206. function uc_order_load_multiple($ids, $conditions = array(), $reset = FALSE) {
  1207. return entity_load('uc_order', $ids, $conditions, $reset);
  1208. }
  1209. /**
  1210. * Returns an array of comments or admin comments for an order.
  1211. */
  1212. function uc_order_comments_load($order_id, $admin = FALSE) {
  1213. $table = $admin ? 'uc_order_admin_comments' : 'uc_order_comments';
  1214. $query = db_select($table, 'oc')
  1215. ->fields('oc')
  1216. ->condition('order_id', $order_id)
  1217. ->orderBy('oc.created')
  1218. ->orderBy('oc.comment_id');
  1219. if (!$admin) {
  1220. $query->leftJoin('uc_order_statuses', 'os', 'oc.order_status = os.order_status_id');
  1221. $query->fields('os');
  1222. }
  1223. $comments = $query->execute()->fetchAll();
  1224. return $comments;
  1225. }
  1226. /**
  1227. * Inserts a comment, $type being either 'order' or 'admin'.
  1228. */
  1229. function uc_order_comment_save($order_id, $uid, $message, $type = 'admin', $status = 'pending', $notify = FALSE) {
  1230. if ($type == 'admin') {
  1231. db_insert('uc_order_admin_comments')
  1232. ->fields(array(
  1233. 'order_id' => $order_id,
  1234. 'uid' => $uid,
  1235. 'message' => $message,
  1236. 'created' => REQUEST_TIME,
  1237. ))
  1238. ->execute();
  1239. }
  1240. elseif ($type == 'order') {
  1241. db_insert('uc_order_comments')
  1242. ->fields(array(
  1243. 'order_id' => $order_id,
  1244. 'uid' => $uid,
  1245. 'message' => $message,
  1246. 'order_status' => $status,
  1247. 'notified' => $notify ? 1 : 0,
  1248. 'created' => REQUEST_TIME,
  1249. ))
  1250. ->execute();
  1251. }
  1252. }
  1253. /**
  1254. * Returns an array containing an order's line items ordered by weight.
  1255. *
  1256. * @param $order
  1257. * An order object whose line items are to be loaded.
  1258. * @param $stored
  1259. * Boolean flag. If TRUE, only line items stored in the database are loaded.
  1260. * If FALSE, only line items not stored in the database are loaded.
  1261. * This distinction is made because the non-stored line items may depend on
  1262. * the amounts of all of the stored line items.
  1263. *
  1264. * @return
  1265. * An array of line items, which are arrays containing the following keys:
  1266. * - line_item_id: The line item id.
  1267. * - type: The line item type.
  1268. * - title: The line item title.
  1269. * - amount: The line item amount.
  1270. * - weight: The line item weight.
  1271. */
  1272. function uc_order_load_line_items($order) {
  1273. $items = array();
  1274. $result = db_query("SELECT * FROM {uc_order_line_items} WHERE order_id = :id", array(':id' => $order->order_id));
  1275. foreach ($result as $row) {
  1276. $item = array(
  1277. 'line_item_id' => $row->line_item_id,
  1278. 'type' => $row->type,
  1279. 'title' => $row->title,
  1280. 'amount' => $row->amount,
  1281. 'weight' => $row->weight,
  1282. 'data' => unserialize($row->data),
  1283. );
  1284. drupal_alter('uc_line_item', $item, $order);
  1285. $items[] = $item;
  1286. }
  1287. // Set stored line items so hook_uc_line_item_alter() can access them.
  1288. $order->line_items = $items;
  1289. foreach (_uc_line_item_list() as $type) {
  1290. if ($type['stored'] == FALSE && empty($type['display_only']) && !empty($type['callback']) && function_exists($type['callback'])) {
  1291. $result = $type['callback']('load', $order);
  1292. if ($result !== FALSE && is_array($result)) {
  1293. foreach ($result as $line) {
  1294. $item = array(
  1295. 'line_item_id' => $line['id'],
  1296. 'type' => $type['id'],
  1297. 'title' => $line['title'],
  1298. 'amount' => $line['amount'],
  1299. 'weight' => isset($line['weight']) ? $line['weight'] : $type['weight'],
  1300. 'data' => isset($line['data']) ? $line['data'] : array(),
  1301. );
  1302. drupal_alter('uc_line_item', $item, $order);
  1303. $items[] = $item;
  1304. }
  1305. }
  1306. }
  1307. }
  1308. usort($items, 'uc_weight_sort');
  1309. return $items;
  1310. }
  1311. /**
  1312. * Returns an order's line items ordered by weight, prepared for display.
  1313. *
  1314. * @param $order
  1315. * An order object whose line items are to be loaded.
  1316. *
  1317. * @return
  1318. * An array of line items, which are arrays containing the following keys:
  1319. * - type: The line item type.
  1320. * - title: The line item title.
  1321. * - amount: The line item amount.
  1322. * - weight: The line item weight.
  1323. */
  1324. function uc_order_load_line_items_display($order) {
  1325. $temp = clone $order;
  1326. $line_items = uc_order_load_line_items($order);
  1327. $temp->line_items = &$line_items;
  1328. $items = _uc_line_item_list();
  1329. foreach ($items as $item) {
  1330. if (!empty($item['display_only'])) {
  1331. $result = $item['callback']('display', $temp);
  1332. if (is_array($result)) {
  1333. foreach ($result as $line) {
  1334. $line_items[] = array(
  1335. 'type' => $item['id'],
  1336. 'title' => $line['title'],
  1337. 'amount' => $line['amount'],
  1338. 'weight' => isset($line['weight']) ? $line['weight'] : $item['weight'],
  1339. 'data' => isset($line['data']) ? $line['data'] : array(),
  1340. );
  1341. }
  1342. }
  1343. }
  1344. }
  1345. foreach ($line_items as &$item) {
  1346. $item['title'] = check_plain($item['title']);
  1347. $item['formatted_amount'] = uc_currency_format($item['amount']);
  1348. }
  1349. usort($line_items, 'uc_weight_sort');
  1350. return $line_items;
  1351. }
  1352. /**
  1353. * Updates an order's status as long as no one objects.
  1354. *
  1355. * @param $order_id
  1356. * The ID of the order to be updated.
  1357. * @param $status
  1358. * The new status ID we want to move the order to.
  1359. *
  1360. * @return
  1361. * TRUE or FALSE depending on the success of the update.
  1362. */
  1363. function uc_order_update_status($order_id, $status) {
  1364. // Return FALSE if an invalid $status is specified.
  1365. if (uc_order_status_data($status, 'id') == NULL) {
  1366. return FALSE;
  1367. }
  1368. $order = uc_order_load($order_id);
  1369. // Attempt the update if the order exists.
  1370. if ($order !== FALSE) {
  1371. // Return TRUE if the order status is already set.
  1372. if ($order->order_status == $status) {
  1373. return TRUE;
  1374. }
  1375. // Return FALSE if any module says the update is not good to go.
  1376. foreach (module_implements('uc_order') as $module) {
  1377. $function = $module . '_uc_order';
  1378. // $order must be passed by reference.
  1379. if (function_exists($function) && $function('can_update', $order, $status) === FALSE) {
  1380. return FALSE;
  1381. }
  1382. }
  1383. // Otherwise perform the update and log the changes.
  1384. db_update('uc_orders')
  1385. ->fields(array(
  1386. 'order_status' => $status,
  1387. 'modified' => REQUEST_TIME,
  1388. ))
  1389. ->condition('order_id', $order_id)
  1390. ->execute();
  1391. uc_order_module_invoke('update', $order, $status);
  1392. $change = array(t('Order status') => array('old' => uc_order_status_data($order->order_status, 'title'), 'new' => uc_order_status_data($status, 'title')));
  1393. uc_order_log_changes($order->order_id, $change);
  1394. $updated = uc_order_load($order_id, TRUE);
  1395. rules_invoke_event('uc_order_status_update', $order, $updated);
  1396. return TRUE;
  1397. }
  1398. // Return FALSE if the order didn't exist.
  1399. return FALSE;
  1400. }
  1401. /**
  1402. * Logs changes made to an order.
  1403. *
  1404. * @param $order_id
  1405. * The ID of the order that was changed.
  1406. * @param $changes
  1407. * An array of changes. Two formats are allowed:
  1408. * - keys: Keys being the name of the field changed and the values being
  1409. * associative arrays with the keys 'old' and 'new' to represent the old
  1410. * and new values of the field. These will be converted into a changed
  1411. * message.
  1412. * - string: A pre-formatted string describing the change. This is useful for
  1413. * logging details like payments.
  1414. *
  1415. * @return
  1416. * TRUE or FALSE depending on whether or not changes were logged.
  1417. */
  1418. function uc_order_log_changes($order_id, $changes) {
  1419. global $user;
  1420. if (count($changes) == 0) {
  1421. return FALSE;
  1422. }
  1423. foreach ($changes as $key => $value) {
  1424. if (is_array($value)) {
  1425. $items[] = t('@key changed from %old to %new.', array('@key' => $key, '%old' => $value['old'], '%new' => $value['new']));
  1426. }
  1427. elseif (is_string($value)) {
  1428. $items[] = $value;
  1429. }
  1430. }
  1431. db_insert('uc_order_log')
  1432. ->fields(array(
  1433. 'order_id' => $order_id,
  1434. 'uid' => $user->uid,
  1435. 'changes' => theme('item_list', array('items' => $items)),
  1436. 'created' => REQUEST_TIME,
  1437. ))
  1438. ->execute();
  1439. return TRUE;
  1440. }
  1441. /**
  1442. * Returns an address from an order object.
  1443. *
  1444. * @param $order
  1445. * An order object.
  1446. * @param $type
  1447. * Either 'delivery' or 'billing'.
  1448. */
  1449. function uc_order_address($order, $type) {
  1450. $name = $order->{$type . '_first_name'} . ' ' . $order->{$type . '_last_name'};
  1451. $address = uc_address_format(
  1452. $order->{$type . '_first_name'},
  1453. $order->{$type . '_last_name'},
  1454. $order->{$type . '_company'},
  1455. $order->{$type . '_street1'},
  1456. $order->{$type . '_street2'},
  1457. $order->{$type . '_city'},
  1458. $order->{$type . '_zone'},
  1459. $order->{$type . '_postal_code'},
  1460. $order->{$type . '_country'}
  1461. );
  1462. if (variable_get('uc_order_capitalize_addresses', TRUE)) {
  1463. $address = drupal_strtoupper($address);
  1464. }
  1465. return $address;
  1466. }
  1467. /**
  1468. * Invokes hook_uc_order() in every module.
  1469. *
  1470. * We cannot use module_invoke() for this, because the arguments need to
  1471. * be passed by reference.
  1472. */
  1473. function uc_order_module_invoke($op, &$order, $edit) {
  1474. foreach (module_implements('uc_order') as $module) {
  1475. $function = $module . '_uc_order';
  1476. if (function_exists($function)) {
  1477. $function($op, $order, $edit);
  1478. }
  1479. }
  1480. }
  1481. /**
  1482. * Returns TRUE if an order exists.
  1483. */
  1484. function uc_order_exists($order_id) {
  1485. if (intval($order_id) <= 0) {
  1486. return FALSE;
  1487. }
  1488. $order = db_query("SELECT order_id FROM {uc_orders} WHERE order_id = :id", array(':id' => $order_id))->fetchField();
  1489. if ($order) {
  1490. return TRUE;
  1491. }
  1492. return FALSE;
  1493. }
  1494. /**
  1495. * Calculates an order's total.
  1496. */
  1497. function uc_order_get_total($order, $products_only = FALSE) {
  1498. $total = 0;
  1499. if ($order === FALSE) {
  1500. return $total;
  1501. }
  1502. if (is_array($order->products)) {
  1503. foreach ($order->products as $product) {
  1504. $qty = $product->qty ? $product->qty : 1;
  1505. $total += $product->price * $qty;
  1506. }
  1507. }
  1508. if ($products_only) {
  1509. return $total;
  1510. }
  1511. $total += uc_line_items_calculate($order);
  1512. foreach (module_implements('uc_order') as $module) {
  1513. $function = $module . '_uc_order';
  1514. // $order must be passed by reference.
  1515. if (function_exists($function) && ($value = $function('total', $order, NULL)) && is_numeric($value)) {
  1516. $total += $value;
  1517. }
  1518. }
  1519. return $total;
  1520. }
  1521. /**
  1522. * Calculates an order's product count.
  1523. */
  1524. function uc_order_get_product_count($order) {
  1525. $count = 0;
  1526. if (is_array($order->products)) {
  1527. foreach ($order->products as $product) {
  1528. $count += $product->qty;
  1529. }
  1530. }
  1531. return $count;
  1532. }
  1533. /**
  1534. * Determines whether a product is shippable or not.
  1535. */
  1536. function uc_order_product_is_shippable($product) {
  1537. // Return FALSE if the product form specifies this as not shippable.
  1538. if (empty($product->data['shippable'])) {
  1539. return FALSE;
  1540. }
  1541. // See if any other modules have a say in the matter...
  1542. $result = module_invoke_all('uc_order_product_can_ship', $product);
  1543. // Return TRUE by default.
  1544. if (empty($result) || in_array(TRUE, $result)) {
  1545. return TRUE;
  1546. }
  1547. return FALSE;
  1548. }
  1549. /**
  1550. * Entity API getter for the list of order products.
  1551. */
  1552. function uc_order_get_product_list($order) {
  1553. return array_keys($order->products);
  1554. }
  1555. /**
  1556. * Determines if an order is shippable.
  1557. *
  1558. * An order can be shipped if any of its products can be shipped.
  1559. */
  1560. function uc_order_is_shippable($order) {
  1561. if (!is_array($order->products) || empty($order->products)) {
  1562. return FALSE;
  1563. }
  1564. foreach ($order->products as $product) {
  1565. if (uc_order_product_is_shippable($product)) {
  1566. return TRUE;
  1567. }
  1568. }
  1569. return FALSE;
  1570. }
  1571. /**
  1572. * Preprocesses a formatted invoice with an order's data.
  1573. *
  1574. * @see uc_order--admin.tpl.php
  1575. * @see uc_order--customer.tpl.php
  1576. */
  1577. function template_preprocess_uc_order(&$variables) {
  1578. $order = &$variables['order'];
  1579. switch ($variables['op']) {
  1580. case 'checkout-mail':
  1581. $variables['thank_you_message'] = TRUE;
  1582. case 'admin-mail':
  1583. $variables['help_text'] = TRUE;
  1584. $variables['email_text'] = TRUE;
  1585. $variables['store_footer'] = TRUE;
  1586. case 'view':
  1587. case 'print':
  1588. $variables['business_header'] = TRUE;
  1589. $variables['shipping_method'] = TRUE;
  1590. break;
  1591. }
  1592. $variables['shippable'] = uc_order_is_shippable($order);
  1593. $variables['products'] = $order->products;
  1594. $display = uc_order_product_view_multiple($order->products);
  1595. foreach ($variables['products'] as &$product) {
  1596. $product->title = check_plain($product->title);
  1597. $product->model = check_plain($product->model);
  1598. $product->total_price = render($display['uc_order_product'][$product->order_product_id]['total']);
  1599. if ($product->qty > 1) {
  1600. $product->individual_price = t('(!price each)', array('!price' => uc_currency_format($display['uc_order_product'][$product->order_product_id]['price']['#price'])));
  1601. }
  1602. else {
  1603. $product->individual_price = '';
  1604. }
  1605. $product->details = '';
  1606. if (!empty($product->data['attributes'])) {
  1607. $attributes = array();
  1608. foreach ($product->data['attributes'] as $attribute => $option) {
  1609. $attributes[] = t('@attribute: @options', array('@attribute' => $attribute, '@options' => implode(', ', (array) $option)));
  1610. }
  1611. $product->details .= theme('item_list', array('items' => $attributes));
  1612. }
  1613. }
  1614. $variables['line_items'] = uc_order_load_line_items_display($variables['order']);
  1615. $order->line_items = $variables['line_items'];
  1616. // Generate tokens to use as template variables.
  1617. $types = array(
  1618. 'uc_order' => $order,
  1619. );
  1620. $token_info = token_info();
  1621. $replacements = array();
  1622. foreach (array('site', 'store', 'uc_order') as $type) {
  1623. $replacements[$type] = token_generate($type, drupal_map_assoc(array_keys($token_info['tokens'][$type])), $types);
  1624. }
  1625. foreach ($replacements as $type => $tokens) {
  1626. foreach ($tokens as $token => $value) {
  1627. $key = str_replace('-', '_', $type . '_' . $token);
  1628. $key = str_replace('uc_', '', $key);
  1629. $variables[$key] = $value;
  1630. }
  1631. }
  1632. // Add hook suggestions, default to customer template.
  1633. $variables['theme_hook_suggestions'] = array(
  1634. 'uc_order__customer',
  1635. 'uc_order__' . $variables['template'],
  1636. );
  1637. }
  1638. /**
  1639. * Preprocesses a printable invoice page.
  1640. *
  1641. * @see uc_order-invoice-page.tpl.php
  1642. */
  1643. function template_preprocess_uc_order_invoice_page(&$variables) {
  1644. // Construct page title.
  1645. if (drupal_get_title()) {
  1646. $head_title = array(
  1647. 'title' => strip_tags(drupal_get_title()),
  1648. 'name' => variable_get('site_name', 'Drupal'),
  1649. );
  1650. }
  1651. else {
  1652. $head_title = array('name' => variable_get('site_name', 'Drupal'));
  1653. if (variable_get('site_slogan', '')) {
  1654. $head_title['slogan'] = variable_get('site_slogan', '');
  1655. }
  1656. }
  1657. // Add favicon.
  1658. if (theme_get_setting('toggle_favicon')) {
  1659. $favicon = theme_get_setting('favicon');
  1660. $type = theme_get_setting('favicon_mimetype');
  1661. drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
  1662. }
  1663. $variables['head'] = drupal_get_html_head();
  1664. // Set the default language if necessary.
  1665. $language = isset($GLOBALS['language']) ? $GLOBALS['language'] : language_default();
  1666. $variables['head_title'] = implode(' | ', $head_title);
  1667. $variables['base_path'] = base_path();
  1668. $variables['language'] = $language;
  1669. $variables['language']->dir = $language->direction ? 'rtl' : 'ltr';
  1670. }
  1671. /**
  1672. * Returns an array of invoice templates found in ubercart/uc_order/templates.
  1673. */
  1674. function uc_invoice_template_list() {
  1675. $templates = drupal_map_assoc(module_invoke_all('uc_invoice_templates'));
  1676. // Sort the template names alphabetically.
  1677. sort($templates);
  1678. return $templates;
  1679. }
  1680. /**
  1681. * Returns a list of options for a template select box.
  1682. */
  1683. function uc_order_template_options($custom = FALSE) {
  1684. $templates = drupal_map_assoc(uc_invoice_template_list());
  1685. if ($custom) {
  1686. $templates[0] = t('Custom template');
  1687. }
  1688. return $templates;
  1689. }
  1690. /**
  1691. * Returns a sorted list of the order states defined in the various modules.
  1692. *
  1693. * @param $scope
  1694. * Specify the scope for the order states you want listed - all, general, or
  1695. * specific. States with a general scope are used on general lists and pages.
  1696. * @param $sql
  1697. * Pass this parameter as TRUE to alter the return value for a SQL query.
  1698. *
  1699. * @return
  1700. * Either an array of state arrays or a string containing an array of state
  1701. * ids for use in a SQL query.
  1702. */
  1703. function uc_order_state_list($scope = 'all', $sql = FALSE) {
  1704. $states = array();
  1705. foreach (module_invoke_all('uc_order_state') as $id => $state) {
  1706. // Preserve backward compatibility for states with no key specified.
  1707. if (is_numeric($id)) {
  1708. $id = $state['id'];
  1709. }
  1710. else {
  1711. $state['id'] = $id;
  1712. }
  1713. if ($scope == 'all' || $state['scope'] == $scope) {
  1714. $states[$id] = $state;
  1715. }
  1716. }
  1717. uasort($states, 'uc_weight_sort');
  1718. return $sql ? array_keys($states) : $states;
  1719. }
  1720. /**
  1721. * Returns a bit of data from a state array based on the state ID and array key.
  1722. *
  1723. * @param $state_id
  1724. * The ID of the order state you want to get data from.
  1725. * @param $key
  1726. * The key in the state array whose value you want: id, title, weight, scope.
  1727. *
  1728. * @return
  1729. * The value of the key you specify.
  1730. */
  1731. function uc_order_state_data($state_id, $key) {
  1732. static $states;
  1733. if (empty($states)) {
  1734. $states = uc_order_state_list();
  1735. }
  1736. return $states[$state_id][$key];
  1737. }
  1738. /**
  1739. * Returns the default order status for a particular order state.
  1740. *
  1741. * @param $state_id
  1742. * The ID of the order state whose default status you want to find.
  1743. *
  1744. * @return
  1745. * A string containing the default order status ID for the specified state.
  1746. */
  1747. function uc_order_state_default($state_id) {
  1748. static $default;
  1749. // Return the default value if it exists.
  1750. if (isset($default[$state_id])) {
  1751. return $default[$state_id];
  1752. }
  1753. // Attempt to get the default state from the form.
  1754. $default[$state_id] = variable_get('uc_state_' . $state_id . '_default', NULL);
  1755. // If it is not found, pick the lightest status for this state.
  1756. if (empty($default[$state_id])) {
  1757. $statuses = uc_order_status_list($state_id);
  1758. $default[$state_id] = $statuses[0]['id'];
  1759. }
  1760. return $default[$state_id];
  1761. }
  1762. /**
  1763. * Returns a sorted list of order statuses, sortable by order state/scope.
  1764. *
  1765. * @param $scope
  1766. * Specify the scope for the order statuses you want listed - all, general,
  1767. * specific, or any order state id. Defaults to all.
  1768. * @param $sql
  1769. * Pass this parameter as TRUE to alter the return value for a SQL query.
  1770. * @param $action
  1771. * Empty by default. Set to rebuild to load the order statuses from scratch,
  1772. * disregarding the current cached value for the specified $scope.
  1773. *
  1774. * @return
  1775. * Either an array of status arrays or a string containing an array of status
  1776. * ids for use in a SQL query.
  1777. */
  1778. function uc_order_status_list($scope = 'all', $sql = FALSE, $action = '') {
  1779. static $statuses;
  1780. if (!isset($statuses[$scope]) || $action == 'rebuild') {
  1781. switch ($scope) {
  1782. case 'all':
  1783. $result = db_query("SELECT * FROM {uc_order_statuses}");
  1784. break;
  1785. case 'general':
  1786. case 'specific':
  1787. $result = db_query("SELECT * FROM {uc_order_statuses} WHERE state IN (:states)", array(':states' => uc_order_state_list($scope, TRUE)));
  1788. break;
  1789. default:
  1790. $result = db_query("SELECT * FROM {uc_order_statuses} WHERE state = :scope", array(':scope' => $scope));
  1791. break;
  1792. }
  1793. $statuses[$scope] = array();
  1794. while ($status = $result->fetchAssoc()) {
  1795. $status['id'] = $status['order_status_id'];
  1796. unset($status['order_status_id']);
  1797. if (function_exists('i18n_string')) {
  1798. $status['title'] = i18n_string('uc_order_status:status:' . $status['id'] . ':title', $status['title']);
  1799. }
  1800. $statuses[$scope][] = $status;
  1801. }
  1802. usort($statuses[$scope], 'uc_weight_sort');
  1803. }
  1804. if ($sql) {
  1805. $ids = array();
  1806. foreach ($statuses[$scope] as $status) {
  1807. $ids[] = $status['id'];
  1808. }
  1809. return $ids;
  1810. }
  1811. return $statuses[$scope];
  1812. }
  1813. /**
  1814. * Returns a bit of data from a status array based on status ID and array key.
  1815. *
  1816. * @param $status_id
  1817. * The ID of the order status you want to get data from.
  1818. * @param $key
  1819. * The key in the status array whose value you want: id, title, state, weight.
  1820. *
  1821. * @return
  1822. * The value of the key you specify.
  1823. */
  1824. function uc_order_status_data($status_id, $key) {
  1825. static $statuses;
  1826. if (empty($statuses)) {
  1827. $data = uc_order_status_list();
  1828. foreach ($data as $status) {
  1829. $statuses[$status['id']] = $status;
  1830. }
  1831. }
  1832. return $statuses[$status_id][$key];
  1833. }
  1834. /**
  1835. * Returns an option list of order statuses.
  1836. */
  1837. function uc_order_status_options_list() {
  1838. $options = array();
  1839. foreach (uc_order_status_list() as $status) {
  1840. $options[$status['id']] = $status['title'];
  1841. }
  1842. return $options;
  1843. }
  1844. /**
  1845. * Returns the actions a user may perform on an order.
  1846. *
  1847. * @param $icon_html
  1848. * Specify whether or not to return the result as an HTML string with the
  1849. * order action icon links.
  1850. *
  1851. * @return
  1852. * Valid actions for an order; returned according to the $icon_html parameter.
  1853. */
  1854. function uc_order_actions($order, $icon_html = FALSE) {
  1855. global $user;
  1856. $state = uc_order_status_data($order->order_status, 'state');
  1857. $order_id = array('@order_id' => $order->order_id);
  1858. $actions = array();
  1859. if (user_access('view all orders')) {
  1860. $alt = t('View order @order_id.', $order_id);
  1861. $actions[] = array(
  1862. 'name' => t('View'),
  1863. 'url' => 'admin/store/orders/' . $order->order_id,
  1864. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_store') . '/images/order_view.gif', 'alt' => $alt)),
  1865. 'title' => $alt,
  1866. );
  1867. $alt = t('Print order @order_id.', $order_id);
  1868. $actions[] = array(
  1869. 'name' => t('Print'),
  1870. 'url' => 'admin/store/orders/' . $order->order_id . '/invoice/print',
  1871. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_store') . '/images/print.gif', 'alt' => $alt)),
  1872. 'title' => $alt,
  1873. );
  1874. }
  1875. elseif (user_access('view own orders') && $order->uid == $user->uid) {
  1876. $alt = t('View order @order_id.', $order_id);
  1877. $actions[] = array(
  1878. 'name' => t('View'),
  1879. 'url' => 'user/' . $user->uid . '/orders/' . $order->order_id,
  1880. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_store') . '/images/order_view.gif', 'alt' => $alt)),
  1881. 'title' => $alt,
  1882. );
  1883. if (user_access('view own invoices')) {
  1884. $alt = t('Print order @order_id.', $order_id);
  1885. $actions[] = array(
  1886. 'name' => t('Print'),
  1887. 'url' => 'user/' . $user->uid . '/orders/' . $order->order_id . '/print',
  1888. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_store') . '/images/print.gif', 'alt' => $alt)),
  1889. 'title' => $alt,
  1890. );
  1891. }
  1892. }
  1893. if (user_access('edit orders')) {
  1894. $alt = t('Edit order @order_id.', $order_id);
  1895. $actions[] = array(
  1896. 'name' => t('Edit'),
  1897. 'url' => 'admin/store/orders/' . $order->order_id . '/edit',
  1898. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_store') . '/images/order_edit.gif', 'alt' => $alt)),
  1899. 'title' => $alt,
  1900. );
  1901. }
  1902. if (uc_order_can_delete($order)) {
  1903. $alt = t('Delete order @order_id.', $order_id);
  1904. $actions[] = array(
  1905. 'name' => t('Delete'),
  1906. 'url' => 'admin/store/orders/' . $order->order_id . '/delete',
  1907. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_store') . '/images/order_delete.gif', 'alt' => $alt)),
  1908. 'title' => $alt,
  1909. );
  1910. }
  1911. $extra = module_invoke_all('uc_order_actions', $order);
  1912. if (count($extra)) {
  1913. $actions = array_merge($actions, $extra);
  1914. }
  1915. drupal_alter('uc_order_actions', $actions, $order);
  1916. if ($icon_html) {
  1917. $output = '';
  1918. foreach ($actions as $action) {
  1919. $action['classes'][] = 'uc-order-action';
  1920. if (empty($action['attributes'])) {
  1921. $action['attributes'] = array();
  1922. }
  1923. $action['attributes']['title'] = $action['title'];
  1924. $action['attributes']['class'] = implode(' ', $action['classes']);
  1925. $output .= l($action['icon'], $action['url'], array(
  1926. 'attributes' => $action['attributes'],
  1927. 'html' => TRUE,
  1928. ));
  1929. }
  1930. return $output;
  1931. }
  1932. else {
  1933. return $actions;
  1934. }
  1935. }
  1936. /**
  1937. * Access callback for admin/store/orders/%uc_order/delete.
  1938. *
  1939. * Returns TRUE if an order can be deleted by the current user.
  1940. */
  1941. function uc_order_can_delete($order, $account = NULL) {
  1942. if (user_access('unconditionally delete orders', $account)) {
  1943. // Unconditional deletion perms are always TRUE.
  1944. return TRUE;
  1945. }
  1946. elseif (user_access('delete orders', $account)) {
  1947. // Only users with unconditional deletion perms can delete completed orders.
  1948. $state = uc_order_status_data($order->order_status, 'state');
  1949. if ($state == 'completed') {
  1950. return FALSE;
  1951. }
  1952. else {
  1953. $can_delete = TRUE;
  1954. // See if any modules have a say in this order's eligibility for deletion.
  1955. foreach (module_implements('uc_order') as $module) {
  1956. $function = $module . '_uc_order';
  1957. if (function_exists($function) && $function('can_delete', $order, NULL) === FALSE) {
  1958. $can_delete = FALSE;
  1959. break;
  1960. }
  1961. }
  1962. return $can_delete;
  1963. }
  1964. }
  1965. else {
  1966. return FALSE;
  1967. }
  1968. }
  1969. /**
  1970. * Entity metadata callback for getting order properties.
  1971. *
  1972. * @see uc_order_entity_property_info()
  1973. */
  1974. function uc_order_uc_order_get_properties($order, array $options, $name, $entity_type) {
  1975. switch ($name) {
  1976. case 'customer':
  1977. return $order->uid;
  1978. }
  1979. }
  1980. /**
  1981. * Entity metadata callback for setting order properties.
  1982. *
  1983. * @see uc_order_entity_property_info()
  1984. */
  1985. function uc_order_uc_order_set_properties($order, $name, $value) {
  1986. if ($name == 'customer') {
  1987. $order->uid = $value;
  1988. }
  1989. }
  1990. /**
  1991. * Entity metadata callback: get the delivery or billing address of an order.
  1992. */
  1993. function uc_order_address_property_get($order, array $options, $name, $entity_type) {
  1994. switch ($name) {
  1995. case 'delivery_address':
  1996. $type = 'delivery_';
  1997. break;
  1998. case 'billing_address':
  1999. $type = 'billing_';
  2000. break;
  2001. default:
  2002. return NULL;
  2003. }
  2004. $address = new UcAddress();
  2005. foreach ($address as $field => $value) {
  2006. if ($field != 'email') {
  2007. $address->{$field} = $order->{$type . $field};
  2008. }
  2009. }
  2010. return $address;
  2011. }
  2012. /**
  2013. * Entity metadata callback: set the delivery or billing address of an order.
  2014. */
  2015. function uc_order_address_property_set($order, $name, $address) {
  2016. switch ($name) {
  2017. case 'delivery_address':
  2018. $type = 'delivery_';
  2019. break;
  2020. case 'billing_address':
  2021. $type = 'billing_';
  2022. break;
  2023. default:
  2024. return;
  2025. }
  2026. foreach ($address as $field => $value) {
  2027. if ($field != 'email') {
  2028. $order->{$type . $field} = $value;
  2029. }
  2030. }
  2031. }
  2032. /**
  2033. * Entity metadata callback: get the full product node for an order product.
  2034. */
  2035. function uc_order_product_node_property_get($product, array $options, $name, $entity_type) {
  2036. return node_load($product->nid);
  2037. }
  2038. /**
  2039. * Entity metadata callback: set the nid for an order product.
  2040. */
  2041. function uc_order_product_node_property_set($product, $name, $node) {
  2042. $product->nid = $node->nid;
  2043. }
  2044. /**
  2045. * Implements hook_date_views_tables().
  2046. */
  2047. function uc_order_date_views_tables() {
  2048. return array('uc_orders');
  2049. }
  2050. /**
  2051. * Implements hook_date_views_fields().
  2052. *
  2053. * All modules that create custom fields that use the
  2054. * 'views_handler_field_date' handler can provide
  2055. * additional information here about the type of
  2056. * date they create so the date can be used by
  2057. * the Date API views date argument and date filter.
  2058. */
  2059. function uc_order_date_views_fields($field) {
  2060. $values = array(
  2061. // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
  2062. 'sql_type' => DATE_UNIX,
  2063. // Timezone handling options: 'none', 'site', 'date', 'utc' .
  2064. 'tz_handling' => 'site',
  2065. // Needed only for dates that use 'date' tz_handling.
  2066. 'timezone_field' => '',
  2067. // Needed only for dates that use 'date' tz_handling.
  2068. 'offset_field' => '',
  2069. // Array of "table.field" values for related fields that should be
  2070. // loaded automatically in the Views SQL.
  2071. 'related_fields' => array(),
  2072. // Granularity of this date field's db data.
  2073. 'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
  2074. );
  2075. switch ($field) {
  2076. case 'uc_orders.created':
  2077. case 'uc_orders.modified':
  2078. return $values;
  2079. }
  2080. }
  2081. /**
  2082. * Implements hook_action_info().
  2083. */
  2084. function uc_order_action_info() {
  2085. return array(
  2086. 'uc_order_action_set_status' => array(
  2087. 'label' => t('Set order status'),
  2088. 'type' => 'uc_order',
  2089. 'configurable' => TRUE,
  2090. 'triggers' => array('any'),
  2091. ),
  2092. 'uc_order_action_print' => array(
  2093. 'label' => t('Print invoice'),
  2094. 'type' => 'uc_order',
  2095. 'aggregate' => TRUE,
  2096. 'configurable' => FALSE,
  2097. 'triggers' => array('any'),
  2098. ),
  2099. 'uc_order_action_delete' => array(
  2100. 'label' => t('Delete order'),
  2101. 'type' => 'uc_order',
  2102. 'configurable' => FALSE,
  2103. 'behavior' => array('deletes_property'),
  2104. 'triggers' => array('any'),
  2105. ),
  2106. );
  2107. }
  2108. /**
  2109. * Action implementation: sets the status of an order.
  2110. */
  2111. function uc_order_action_set_status($order, $context = array()) {
  2112. $update = uc_order_update_status($order->order_id, $context['status']);
  2113. if ($update && $context['notify']) {
  2114. // Update order object, as uc_order_update_status() cannot.
  2115. $order->order_status = $context['status'];
  2116. rules_invoke_event('uc_order_status_email_update', $order);
  2117. }
  2118. }
  2119. /**
  2120. * Action form: selects the order status to be used.
  2121. */
  2122. function uc_order_action_set_status_form($context) {
  2123. $form['status'] = array(
  2124. '#type' => 'select',
  2125. '#title' => t('Order status'),
  2126. '#default_value' => @$context['status'],
  2127. '#options' => uc_order_status_options_list(),
  2128. );
  2129. $form['notify'] = array(
  2130. '#type' => 'checkbox',
  2131. '#title' => t('Send e-mail notification on update.'),
  2132. '#default_value' => @$context['notify'],
  2133. );
  2134. return $form;
  2135. }
  2136. /**
  2137. * Submit callback: selects the order status to be used.
  2138. */
  2139. function uc_order_action_set_status_submit($form, $form_state) {
  2140. return array(
  2141. 'status' => $form_state['values']['status'],
  2142. 'notify' => $form_state['values']['notify'],
  2143. );
  2144. }
  2145. /**
  2146. * Action implementation: prints multiple invoices.
  2147. */
  2148. function uc_order_action_print($orders, $context = array()) {
  2149. $output = '';
  2150. foreach ($orders as $order) {
  2151. $output .= '<div style="page-break-after: always;">';
  2152. $output .= theme('uc_order', array(
  2153. 'order' => $order,
  2154. 'op' => 'print',
  2155. 'template' => variable_get('uc_cust_order_invoice_template', 'customer'),
  2156. ));
  2157. $output .= '</div>';
  2158. }
  2159. print '<html><head><title>Invoice</title></head>';
  2160. print '<body onload="print();">';
  2161. print $output;
  2162. print '</body></html>';
  2163. exit;
  2164. }
  2165. /**
  2166. * Action implementation: delete an order.
  2167. */
  2168. function uc_order_action_delete($order) {
  2169. uc_order_delete($order->order_id);
  2170. }