Editable.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2013 2amigOS! Consulting Group LLC
  4. * @link http://2amigos.us
  5. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  6. */
  7. namespace dosamigos\editable;
  8. use dosamigos\editable\EditableAddressAsset;
  9. use dosamigos\editable\EditableBootstrapAsset;
  10. use dosamigos\editable\EditableComboDateAsset;
  11. use dosamigos\editable\EditableDatePickerAsset;
  12. use dosamigos\editable\EditableDateTimePickerAsset;
  13. use dosamigos\editable\EditableSelect2Asset;
  14. use dosamigos\editable\EditableWysiHtml5Asset;
  15. use yii\base\InvalidConfigException;
  16. use yii\helpers\ArrayHelper;
  17. use yii\helpers\Html;
  18. use yii\helpers\Json;
  19. use yii\helpers\Url;
  20. use yii\widgets\InputWidget;
  21. /**
  22. * Editable renders the amazing x-editable js plugin from vitalets. For more information please visit the
  23. * [plugin site](http://vitalets.github.io/x-editable/index.html).
  24. *
  25. * @author Antonio Ramirez <amigo.cobos@gmail.com>
  26. * @link http://www.ramirezcobos.com/
  27. * @link http://www.2amigos.us/
  28. * @package dosamigos\editable
  29. */
  30. class Editable extends InputWidget
  31. {
  32. /**
  33. * @var string the type of input. Type of input.
  34. */
  35. public $type = 'text';
  36. /**
  37. * @var string the Mode of editable, can be popup or inline.
  38. */
  39. public $mode = 'inline';
  40. /**
  41. * @var string|array Url for submit, e.g. '/post'. If function - it will be called instead of ajax. Function should
  42. * return deferred object to run fail/done callbacks.
  43. *
  44. * ```
  45. * url: function(params) {
  46. * var d = new $.Deferred;
  47. * if(params.value === 'abc') {
  48. * return d.reject('error message'); //returning error via deferred object
  49. * } else {
  50. * //async saving data in js model
  51. * someModel.asyncSaveMethod({
  52. * ...,
  53. * success: function(){
  54. * d.resolve();
  55. * }
  56. * });
  57. * return d.promise();
  58. * }
  59. * }
  60. * ```
  61. */
  62. public $url;
  63. /**
  64. * @var array the options for the X-editable.js plugin.
  65. * Please refer to the X-editable.js plugin web page for possible options.
  66. * @see http://vitalets.github.io/x-editable/docs.html#editable
  67. */
  68. public $clientOptions = [];
  69. /**
  70. * @var array the event handlers for the X-editable.js plugin.
  71. * Please refer to the X-editable.js plugin web page for possible options.
  72. * @see http://vitalets.github.io/x-editable/docs.html#editable
  73. */
  74. public $clientEvents = [];
  75. /**
  76. * Initializes the widget.
  77. * This method will register the bootstrap asset bundle. If you override this method,
  78. * make sure you call the parent implementation first.
  79. */
  80. public function init()
  81. {
  82. if ($this->url === null) {
  83. throw new InvalidConfigException("'Url' property must be specified.");
  84. }
  85. if (!isset($this->options['id'])) {
  86. $this->options['id'] = $this->hasModel()
  87. ? Html::getInputId($this->model, $this->attribute)
  88. : $this->getId();
  89. }
  90. parent::init();
  91. }
  92. /**
  93. * @inheritdoc
  94. */
  95. public function run()
  96. {
  97. $value = $this->value;
  98. if ($this->hasModel()) {
  99. $model = $this->model;
  100. if ($value !== null) {
  101. if (is_string($value)) {
  102. $show = ArrayHelper::getValue($model, $value);
  103. } else {
  104. $show = call_user_func($value, $model);
  105. }
  106. } else {
  107. $show = ArrayHelper::getValue($model, $this->attribute);
  108. }
  109. } else {
  110. $show = $value;
  111. }
  112. echo Html::a($show, null, $this->options);
  113. $this->registerClientScript();
  114. }
  115. /**
  116. * Registers X-Editable plugin and the related events
  117. */
  118. protected function registerClientScript()
  119. {
  120. $view = $this->getView();
  121. $language = ArrayHelper::getValue($this->clientOptions, 'language');
  122. switch ($this->type) {
  123. case 'address':
  124. EditableAddressAsset::register($view);
  125. break;
  126. case 'combodate':
  127. EditableComboDateAsset::register($view);
  128. break;
  129. case 'date':
  130. if ($language) {
  131. EditableDatePickerAsset::register(
  132. $view
  133. )->js[] = 'vendor/js/locales/bootstrap-datetimepicker.' . $language . '.js';
  134. } else {
  135. EditableDatePickerAsset::register($view);
  136. }
  137. break;
  138. case 'datetime':
  139. if ($language) {
  140. EditableDateTimePickerAsset::register(
  141. $view
  142. )->js[] = 'vendor/js/locales/bootstrap-datetimepicker.' . $language . '.js';
  143. } else {
  144. EditableDateTimePickerAsset::register($view);
  145. }
  146. break;
  147. case 'select2':
  148. EditableSelect2Asset::register($view);
  149. break;
  150. case 'wysihtml5':
  151. $language = $language ? : 'en-US';
  152. EditableWysiHtml5Asset::register(
  153. $view
  154. )->js[] = 'vendor/locales/bootstrap-wysihtml5.' . $language . '.js';
  155. break;
  156. default:
  157. EditableBootstrapAsset::register($view);
  158. }
  159. $id = ArrayHelper::remove($this->clientOptions, 'selector', '#' . $this->options['id']);
  160. // Escape meta-characters in element Id
  161. // http://api.jquery.com/category/selectors/
  162. // This actually only needs to be done for dots, since Html::getInputId
  163. // will enforce word-only characters.
  164. $id = preg_replace('/([.])/', '\\\\\\\$1', $id);
  165. $this->clientOptions['url'] = Url::toRoute($this->url);
  166. $this->clientOptions['type'] = $this->type;
  167. $this->clientOptions['mode'] = $this->mode;
  168. $this->clientOptions['name'] = $this->attribute ? : $this->name;
  169. $pk = ArrayHelper::getValue(
  170. $this->clientOptions,
  171. 'pk',
  172. $this->hasModel() ? $this->model->getPrimaryKey() : null
  173. );
  174. $this->clientOptions['pk'] = base64_encode(serialize($pk));
  175. if ($this->hasModel() && $this->model->isNewRecord) {
  176. $this->clientOptions['send'] = 'always'; // send to server without pk
  177. }
  178. $options = Json::encode($this->clientOptions);
  179. $js = "jQuery('$id').editable($options);";
  180. $view->registerJs($js);
  181. if (!empty($this->clientEvents)) {
  182. $js = [];
  183. foreach ($this->clientEvents as $event => $handler) {
  184. $js[] = "jQuery('$id').on('$event', $handler);";
  185. }
  186. $view->registerJs(implode("\n", $js));
  187. }
  188. }
  189. }