PageRenderTime 48ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/core/Mage/XmlConnect/Helper/Android.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 733 lines | 521 code | 39 blank | 173 comment | 36 complexity | fb5632078ae2dc49870e64ef93b94873 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_XmlConnect
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * XmlConnect device helper for Android
  28. *
  29. * @category Mage
  30. * @package Mage_XmlConnect
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_XmlConnect_Helper_Android extends Mage_Core_Helper_Abstract
  34. {
  35. /**
  36. * Submission title length
  37. *
  38. * @var int
  39. */
  40. const SUBMISSION_TITLE_LENGTH = 30;
  41. /**
  42. * Submission description length
  43. *
  44. * @var int
  45. */
  46. const SUBMISSION_DESCRIPTION_LENGTH = 4000;
  47. /**
  48. * Android preview banner widht
  49. *
  50. * @var int
  51. */
  52. const PREVIEW_BANNER_WIDTH = 320;
  53. /**
  54. * Android preview banner image height
  55. *
  56. * @var int
  57. */
  58. const PREVIEW_BANNER_HEIGHT = 258;
  59. /**
  60. * Tags identifier for title bar
  61. *
  62. * @var int
  63. */
  64. const TAGS_ID_FOR_TITLE_BAR = 1;
  65. /**
  66. * Tags identifier for options menu
  67. *
  68. * @var int
  69. */
  70. const TAGS_ID_FOR_OPTION_MENU = 2;
  71. /**
  72. * Country renderer for submission
  73. *
  74. * @var string
  75. */
  76. const SUBMISSION_COUNTRY_RENDERER = 'androidmarket';
  77. /**
  78. * Country columns for submission
  79. *
  80. * @var int
  81. */
  82. const SUBMISSION_COUNTRY_COLUMNS = 2;
  83. /**
  84. * Submit images that are stored in "params" field of history table
  85. *
  86. * @var array
  87. */
  88. protected $_imageIds = array('icon', 'android_loader_image', 'android_logo', 'big_logo');
  89. /**
  90. * Country field renderer
  91. *
  92. * @var Mage_XmlConnect_Block_Adminhtml_Mobile_Submission_Renderer_Country_Androidmarket
  93. */
  94. protected $_countryRenderer = null;
  95. /**
  96. * Get submit images that are required for application submit
  97. *
  98. * @return array
  99. */
  100. public function getSubmitImages()
  101. {
  102. return $this->_imageIds;
  103. }
  104. /**
  105. * List of coutries that allowed in Ituens by Apple Store
  106. *
  107. * array(
  108. * 'country name' => 'country id at directory model'
  109. * )
  110. *
  111. * @var array
  112. */
  113. protected $_allowedCountries = array(
  114. 'Argentina' => 'AR',
  115. 'Australia' => 'AU',
  116. 'Austria' => 'AT',
  117. 'Belgium' => 'BE',
  118. 'Brazil' =>'BR',
  119. 'Canada' => 'CA',
  120. 'Denmark' => 'DK',
  121. 'Finland' => 'FI',
  122. 'France' => 'FR',
  123. 'Germany' => 'DE',
  124. 'Hong Kong SAR China' => 'HK',
  125. 'Ireland' => 'IE',
  126. 'Israel' => 'IL',
  127. 'Italy' => 'IT',
  128. 'Japan' => 'JP',
  129. 'Mexico' => 'MX',
  130. 'Netherlands' => 'NL',
  131. 'New Zealand' => 'NZ',
  132. 'Norway' => 'NO',
  133. 'Portugal' => 'PT',
  134. 'Russia' => 'RU',
  135. 'Singapore' => 'SG',
  136. 'Spain' => 'ES',
  137. 'South Korea' => 'KR',
  138. 'Sweden' => 'SE',
  139. 'Switzerland' => 'CH',
  140. 'Taiwan' => 'TW',
  141. 'United Kingdom' => 'GB',
  142. 'United States' => 'US',
  143. );
  144. /**
  145. * Get default application tabs
  146. *
  147. * @param string
  148. * @return array
  149. */
  150. public function getDefaultDesignTabs()
  151. {
  152. if (!isset($this->_tabs)) {
  153. $this->_tabs = array(
  154. array(
  155. 'label' => Mage::helper('xmlconnect')->__('Home'),
  156. 'image' => 'tab_home_android.png',
  157. 'action' => 'Home',
  158. 'menu' => self::TAGS_ID_FOR_TITLE_BAR,
  159. ),
  160. array(
  161. 'label' => Mage::helper('xmlconnect')->__('Search'),
  162. 'image' => 'tab_search_android.png',
  163. 'action' => 'Search',
  164. 'menu' => self::TAGS_ID_FOR_TITLE_BAR,
  165. ),
  166. array(
  167. 'label' => Mage::helper('xmlconnect')->__('Account'),
  168. 'image' => 'tab_account_android.png',
  169. 'action' => 'Account',
  170. 'menu' => self::TAGS_ID_FOR_TITLE_BAR,
  171. ),
  172. array(
  173. 'label' => Mage::helper('xmlconnect')->__('Shop'),
  174. 'image' => 'tab_shop_android.png',
  175. 'action' => 'Shop',
  176. 'menu' => self::TAGS_ID_FOR_OPTION_MENU,
  177. ),
  178. array(
  179. 'label' => Mage::helper('xmlconnect')->__('Cart'),
  180. 'image' => 'tab_cart_android.png',
  181. 'action' => 'Cart',
  182. 'menu' => self::TAGS_ID_FOR_OPTION_MENU,
  183. ),
  184. array(
  185. 'label' => Mage::helper('xmlconnect')->__('More Info'),
  186. 'image' => 'tab_info_android.png',
  187. 'action' => 'AboutUs',
  188. 'menu' => self::TAGS_ID_FOR_OPTION_MENU,
  189. ),
  190. );
  191. }
  192. return $this->_tabs;
  193. }
  194. /**
  195. * Default application configuration
  196. *
  197. * @return array
  198. */
  199. public function getDefaultConfiguration()
  200. {
  201. return array(
  202. 'native' => array(
  203. 'body' => array(
  204. 'backgroundColor' => '#ABABAB',
  205. 'scrollBackgroundColor' => '#EDEDED',
  206. ),
  207. 'itemActions' => array(
  208. 'relatedProductBackgroundColor' => '#404040',
  209. ),
  210. 'fonts' => array(
  211. 'Title1' => array(
  212. 'name' => 'HelveticaNeue-Bold',
  213. 'size' => '14',
  214. 'color' => '#FEFEFE',
  215. ),
  216. 'Title2' => array(
  217. 'name' => 'HelveticaNeue-Bold',
  218. 'size' => '12',
  219. 'color' => '#222222',
  220. ),
  221. 'Title3' => array(
  222. 'name' => 'HelveticaNeue',
  223. 'size' => '13',
  224. 'color' => '#000000',
  225. ),
  226. 'Title4' => array(
  227. 'name' => 'HelveticaNeue',
  228. 'size' => '12',
  229. 'color' => '#FFFFFF',
  230. ),
  231. 'Title5' => array(
  232. 'name' => 'HelveticaNeue-Bold',
  233. 'size' => '13',
  234. 'color' => '#dc5f02',
  235. ),
  236. 'Title6' => array(
  237. 'name' => 'HelveticaNeue-Bold',
  238. 'size' => '16',
  239. 'color' => '#222222',
  240. ),
  241. 'Title7' => array(
  242. 'name' => 'HelveticaNeue-Bold',
  243. 'size' => '13',
  244. 'color' => '#000000',
  245. ),
  246. 'Title8' => array(
  247. 'name' => 'HelveticaNeue-Bold',
  248. 'size' => '11',
  249. 'color' => '#FFFFFF',
  250. ),
  251. 'Title9' => array(
  252. 'name' => 'HelveticaNeue-Bold',
  253. 'size' => '12',
  254. 'color' => '#FFFFFF',
  255. ),
  256. 'Text1' => array(
  257. 'name' => 'HelveticaNeue-Bold',
  258. 'size' => '12',
  259. 'color' => '#777777',
  260. ),
  261. 'Text2' => array(
  262. 'name' => 'HelveticaNeue',
  263. 'size' => '10',
  264. 'color' => '#555555',
  265. ),
  266. ),
  267. ),
  268. );
  269. }
  270. /**
  271. * List of allowed fonts for Android application
  272. *
  273. * @return array
  274. */
  275. public function getFontList()
  276. {
  277. return array(
  278. array(
  279. 'value' => 'HiraKakuProN-W3',
  280. 'label' => 'HiraKakuProN-W3',
  281. ),
  282. array(
  283. 'value' => 'Courier',
  284. 'label' => 'Courier',
  285. ),
  286. array(
  287. 'value' => 'Courier-BoldOblique',
  288. 'label' => 'Courier-BoldOblique',
  289. ),
  290. array(
  291. 'value' => 'Courier-Oblique',
  292. 'label' => 'Courier-Oblique',
  293. ),
  294. array(
  295. 'value' => 'Courier-Bold',
  296. 'label' => 'Courier-Bold',
  297. ),
  298. array(
  299. 'value' => 'ArialMT',
  300. 'label' => 'ArialMT',
  301. ),
  302. array(
  303. 'value' => 'Arial-BoldMT',
  304. 'label' => 'Arial-BoldMT',
  305. ),
  306. array(
  307. 'value' => 'Arial-BoldItalicMT',
  308. 'label' => 'Arial-BoldItalicMT',
  309. ),
  310. array(
  311. 'value' => 'Arial-ItalicMT',
  312. 'label' => 'Arial-ItalicMT',
  313. ),
  314. array(
  315. 'value' => 'STHeitiTC-Light',
  316. 'label' => 'STHeitiTC-Light',
  317. ),
  318. array(
  319. 'value' => 'STHeitiTC-Medium',
  320. 'label' => 'STHeitiTC-Medium',
  321. ),
  322. array(
  323. 'value' => 'AppleGothic',
  324. 'label' => 'AppleGothic',
  325. ),
  326. array(
  327. 'value' => 'CourierNewPS-BoldMT',
  328. 'label' => 'CourierNewPS-BoldMT',
  329. ),
  330. array(
  331. 'value' => 'CourierNewPS-ItalicMT',
  332. 'label' => 'CourierNewPS-ItalicMT',
  333. ),
  334. array(
  335. 'value' => 'CourierNewPS-BoldItalicMT',
  336. 'label' => 'CourierNewPS-BoldItalicMT',
  337. ),
  338. array(
  339. 'value' => 'CourierNewPSMT',
  340. 'label' => 'CourierNewPSMT',
  341. ),
  342. array(
  343. 'value' => 'Zapfino',
  344. 'label' => 'Zapfino',
  345. ),
  346. array(
  347. 'value' => 'HiraKakuProN-W6',
  348. 'label' => 'HiraKakuProN-W6',
  349. ),
  350. array(
  351. 'value' => 'ArialUnicodeMS',
  352. 'label' => 'ArialUnicodeMS',
  353. ),
  354. array(
  355. 'value' => 'STHeitiSC-Medium',
  356. 'label' => 'STHeitiSC-Medium',
  357. ),
  358. array(
  359. 'value' => 'STHeitiSC-Light',
  360. 'label' => 'STHeitiSC-Light',
  361. ),
  362. array(
  363. 'value' => 'AmericanTypewriter',
  364. 'label' => 'AmericanTypewriter',
  365. ),
  366. array(
  367. 'value' => 'AmericanTypewriter-Bold',
  368. 'label' => 'AmericanTypewriter-Bold',
  369. ),
  370. array(
  371. 'value' => 'Helvetica-Oblique',
  372. 'label' => 'Helvetica-Oblique',
  373. ),
  374. array(
  375. 'value' => 'Helvetica-BoldOblique',
  376. 'label' => 'Helvetica-BoldOblique',
  377. ),
  378. array(
  379. 'value' => 'Helvetica',
  380. 'label' => 'Helvetica',
  381. ),
  382. array(
  383. 'value' => 'Helvetica-Bold',
  384. 'label' => 'Helvetica-Bold',
  385. ),
  386. array(
  387. 'value' => 'MarkerFelt-Thin',
  388. 'label' => 'MarkerFelt-Thin',
  389. ),
  390. array(
  391. 'value' => 'HelveticaNeue',
  392. 'label' => 'HelveticaNeue',
  393. ),
  394. array(
  395. 'value' => 'HelveticaNeue-Bold',
  396. 'label' => 'HelveticaNeue-Bold',
  397. ),
  398. array(
  399. 'value' => 'DBLCDTempBlack',
  400. 'label' => 'DBLCDTempBlack',
  401. ),
  402. array(
  403. 'value' => 'Verdana-Bold',
  404. 'label' => 'Verdana-Bold',
  405. ),
  406. array(
  407. 'value' => 'Verdana-BoldItalic',
  408. 'label' => 'Verdana-BoldItalic',
  409. ),
  410. array(
  411. 'value' => 'Verdana',
  412. 'label' => 'Verdana',
  413. ),
  414. array(
  415. 'value' => 'Verdana-Italic',
  416. 'label' => 'Verdana-Italic',
  417. ),
  418. array(
  419. 'value' => 'TimesNewRomanPSMT',
  420. 'label' => 'TimesNewRomanPSMT',
  421. ),
  422. array(
  423. 'value' => 'TimesNewRomanPS-BoldMT',
  424. 'label' => 'TimesNewRomanPS-BoldMT',
  425. ),
  426. array(
  427. 'value' => 'TimesNewRomanPS-BoldItalicMT',
  428. 'label' => 'TimesNewRomanPS-BoldItalicMT',
  429. ),
  430. array(
  431. 'value' => 'TimesNewRomanPS-ItalicMT',
  432. 'label' => 'TimesNewRomanPS-ItalicMT',
  433. ),
  434. array(
  435. 'value' => 'Georgia-Bold',
  436. 'label' => 'Georgia-Bold',
  437. ),
  438. array(
  439. 'value' => 'Georgia',
  440. 'label' => 'Georgia',
  441. ),
  442. array(
  443. 'value' => 'Georgia-BoldItalic',
  444. 'label' => 'Georgia-BoldItalic',
  445. ),
  446. array(
  447. 'value' => 'Georgia-Italic',
  448. 'label' => 'Georgia-Italic',
  449. ),
  450. array(
  451. 'value' => 'STHeitiJ-Medium',
  452. 'label' => 'STHeitiJ-Medium',
  453. ),
  454. array(
  455. 'value' => 'STHeitiJ-Light',
  456. 'label' => 'STHeitiJ-Light',
  457. ),
  458. array(
  459. 'value' => 'ArialRoundedMTBold',
  460. 'label' => 'ArialRoundedMTBold',
  461. ),
  462. array(
  463. 'value' => 'TrebuchetMS-Italic',
  464. 'label' => 'TrebuchetMS-Italic',
  465. ),
  466. array(
  467. 'value' => 'TrebuchetMS',
  468. 'label' => 'TrebuchetMS',
  469. ),
  470. array(
  471. 'value' => 'Trebuchet-BoldItalic',
  472. 'label' => 'Trebuchet-BoldItalic',
  473. ),
  474. array(
  475. 'value' => 'TrebuchetMS-Bold',
  476. 'label' => 'TrebuchetMS-Bold',
  477. ),
  478. array(
  479. 'value' => 'STHeitiK-Medium',
  480. 'label' => 'STHeitiK-Medium',
  481. ),
  482. array(
  483. 'value' => 'STHeitiK-Light',
  484. 'label' => 'STHeitiK-Light',
  485. ),
  486. );
  487. }
  488. /**
  489. * List of allowed font sizes for Android application
  490. *
  491. * @return array
  492. */
  493. public function getFontSizes()
  494. {
  495. $result = array( );
  496. for ($i = 6; $i < 32; $i++) {
  497. $result[] = array(
  498. 'value' => $i,
  499. 'label' => $i . ' pt',
  500. );
  501. }
  502. return $result;
  503. }
  504. /**
  505. * Validate submit application data
  506. *
  507. * @param array $params
  508. * @return array
  509. */
  510. public function validateSubmit($params)
  511. {
  512. $errors = array();
  513. if (!Zend_Validate::is(isset($params['title']) ? $params['title'] : null, 'NotEmpty')) {
  514. $errors[] = Mage::helper('xmlconnect')->__('Please enter the Title.');
  515. }
  516. if (isset($params['title'])) {
  517. $titleLength = self::SUBMISSION_TITLE_LENGTH;
  518. $strRules = array('min' => '1', 'max' => $titleLength);
  519. if (!Zend_Validate::is($params['title'], 'StringLength', $strRules)) {
  520. $errors[] = Mage::helper('xmlconnect')->__('"Title" is more than %d characters long', $strRules['max']);
  521. }
  522. }
  523. if (!Zend_Validate::is(isset($params['description']) ? $params['description'] : null, 'NotEmpty')) {
  524. $errors[] = Mage::helper('xmlconnect')->__('Please enter the Description.');
  525. }
  526. if (isset($params['description'])) {
  527. $descriptionLength = self::SUBMISSION_DESCRIPTION_LENGTH;
  528. $strRules = array('min' => '1', 'max' => $descriptionLength);
  529. if (!Zend_Validate::is($params['title'], 'StringLength', $strRules)) {
  530. $errors[] = Mage::helper('xmlconnect')->__('"Description" is more than %d characters long', $strRules['max']);
  531. }
  532. }
  533. if (!Zend_Validate::is(isset($params['copyright']) ? $params['copyright'] : null, 'NotEmpty')) {
  534. $errors[] = Mage::helper('xmlconnect')->__('Please enter the Copyright.');
  535. }
  536. if (empty($params['price_free'])) {
  537. if (!Zend_Validate::is(isset($params['price']) ? $params['price'] : null, 'NotEmpty')) {
  538. $errors[] = Mage::helper('xmlconnect')->__('Please enter the Price.');
  539. }
  540. }
  541. if (!Zend_Validate::is(isset($params['country']) ? $params['country'] : null, 'NotEmpty')) {
  542. $errors[] = Mage::helper('xmlconnect')->__('Please select at least one country.');
  543. }
  544. $keyLenght = Mage_XmlConnect_Model_Application::APP_MAX_KEY_LENGTH;
  545. if (Mage::helper('xmlconnect')->getApplication()->getIsResubmitAction()) {
  546. if (isset($params['resubmission_activation_key'])) {
  547. $resubmissionKey = $params['resubmission_activation_key'];
  548. } else {
  549. $resubmissionKey = null;
  550. }
  551. if (!Zend_Validate::is($resubmissionKey, 'NotEmpty')) {
  552. $errors[] = Mage::helper('xmlconnect')->__('Please enter the Resubmission Key.');
  553. } else if (!Zend_Validate::is($resubmissionKey, 'StringLength', array(1, $keyLenght))) {
  554. $errors[] = Mage::helper('xmlconnect')->__('Submit App failure. Invalid activation key provided');
  555. }
  556. } else {
  557. $key = isset($params['key']) ? $params['key'] : null;
  558. if (!Zend_Validate::is($key, 'NotEmpty')) {
  559. $errors[] = Mage::helper('xmlconnect')->__('Please enter the Activation Key.');
  560. } else if (!Zend_Validate::is($key, 'StringLength', array(1, $keyLenght))) {
  561. $errors[] = Mage::helper('xmlconnect')->__('Submit App failure. Invalid activation key provided');
  562. }
  563. }
  564. return $errors;
  565. }
  566. /**
  567. * Check config for valid values
  568. *
  569. * @param array $native
  570. * @return array
  571. */
  572. public function validateConfig($native)
  573. {
  574. $errors = array();
  575. if ( ($native === false)
  576. || (!isset($native['navigationBar']) || !is_array($native['navigationBar'])
  577. || !isset($native['navigationBar']['icon'])
  578. || !Zend_Validate::is($native['navigationBar']['icon'], 'NotEmpty'))) {
  579. $errors[] = Mage::helper('xmlconnect')->__('Please upload an image for "Logo in Header" field from Design Tab.');
  580. }
  581. if (!Mage::helper('xmlconnect')->validateConfFieldNotEmpty('bannerAndroidImage', $native)) {
  582. $errors[] = Mage::helper('xmlconnect')->__('Please upload an image for "Banner on Home Screen" field from Design Tab.');
  583. }
  584. return $errors;
  585. }
  586. /**
  587. * Get renderer for submission country
  588. *
  589. * @return Mage_XmlConnect_Block_Adminhtml_Mobile_Submission_Renderer_Country_Androidmarket
  590. */
  591. public function getCountryRenderer()
  592. {
  593. if (empty($this->_countryRenderer)) {
  594. $renderer = 'xmlconnect/adminhtml_mobile_submission_renderer_country_'
  595. . self::SUBMISSION_COUNTRY_RENDERER;
  596. $this->_countryRenderer = Mage::app()->getLayout()->createBlock($renderer);
  597. }
  598. return $this->_countryRenderer;
  599. }
  600. /**
  601. * Get label for submission country
  602. *
  603. * @return string
  604. */
  605. public function getCountryLabel()
  606. {
  607. return Mage::helper('xmlconnect')->__('Locations');
  608. }
  609. /**
  610. * Get columns for submission country
  611. *
  612. * @return int
  613. */
  614. public function getCountryColumns()
  615. {
  616. return self::SUBMISSION_COUNTRY_COLUMNS;
  617. }
  618. /**
  619. * Get placement of Country Names for submission country
  620. *
  621. * @return bool
  622. */
  623. public function isCountryNamePlaceLeft()
  624. {
  625. return false;
  626. }
  627. /**
  628. * Get class name for submission country
  629. *
  630. * @return string
  631. */
  632. public function getCountryClass()
  633. {
  634. return self::SUBMISSION_COUNTRY_RENDERER;
  635. }
  636. /**
  637. * Get list of countries that allowed by Magento Inc. for Android
  638. *
  639. * @return array
  640. */
  641. public function getAndroidMarketCountriesArray()
  642. {
  643. return $this->_allowedCountries;
  644. }
  645. /**
  646. * Check image fields
  647. *
  648. * We set empty value for image field if file was missed in some reason
  649. *
  650. * @param array $data
  651. * @return array
  652. */
  653. public function checkImages(array $data)
  654. {
  655. if (isset($data['conf']['native']['navigationBar']['icon']) &&
  656. !file_exists($data['conf']['native']['navigationBar']['icon'])
  657. ) {
  658. $data['conf']['native']['navigationBar']['icon'] = '';
  659. }
  660. if (isset($data['conf']['native']['body']['bannerAndroidImage']) &&
  661. !file_exists($data['conf']['native']['body']['bannerAndroidImage'])
  662. ) {
  663. $data['conf']['native']['body']['bannerAndroidImage'] = '';
  664. }
  665. return $data;
  666. }
  667. /**
  668. * Check required fields of a config for a front-end
  669. *
  670. * @throws Mage_Core_Exception
  671. * @param array $data
  672. * @return void
  673. */
  674. public function checkRequiredConfigFields($data)
  675. {
  676. if (!is_array($data)) {
  677. return;
  678. }
  679. if (isset($data['navigationBar']['icon'])
  680. && empty($data['navigationBar']['icon'])
  681. ) {
  682. Mage::throwException(
  683. Mage::helper('xmlconnect')->__('Logo in Header image missing.')
  684. );
  685. }
  686. if (isset($data['body']['bannerAndroidImage'])
  687. && empty($data['body']['bannerAndroidImage'])
  688. ) {
  689. Mage::throwException(
  690. Mage::helper('xmlconnect')->__('Banner on Home Screen image missing.')
  691. );
  692. }
  693. }
  694. }