Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

423 řádky
12 KiB

  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. wn.views.get_listview = function(doctype, parent) {
  4. if(wn.doclistviews[doctype]) {
  5. var listview = new wn.doclistviews[doctype](parent);
  6. } else {
  7. var listview = new wn.views.ListView(parent, doctype);
  8. }
  9. return listview;
  10. }
  11. wn.provide("wn.listview_settings");
  12. wn.views.ListView = Class.extend({
  13. init: function(doclistview, doctype) {
  14. this.doclistview = doclistview;
  15. this.doctype = doctype;
  16. this.settings = wn.listview_settings[this.doctype] || {};
  17. this.set_fields();
  18. this.set_columns();
  19. this.id_list = [];
  20. if(this.settings.group_by)
  21. this.group_by = this.settings.group_by;
  22. var me = this;
  23. this.doclistview.onreset = function() {
  24. me.id_list = [];
  25. }
  26. },
  27. set_fields: function() {
  28. var me = this;
  29. var t = "`tab"+this.doctype+"`.";
  30. this.fields = [t + 'name', t + 'owner', t + 'docstatus',
  31. t + '_user_tags', t + '_comments', t + 'modified', t + 'modified_by'];
  32. this.stats = ['_user_tags'];
  33. // add workflow field (as priority)
  34. this.workflow_state_fieldname = wn.workflow.get_state_fieldname(this.doctype);
  35. if(this.workflow_state_fieldname) {
  36. this.fields.push(t + this.workflow_state_fieldname);
  37. this.stats.push(this.workflow_state_fieldname);
  38. }
  39. $.each(wn.model.get("DocField", {"parent":this.doctype, "in_list_view":1}), function(i,d) {
  40. if(wn.perm.has_perm(me.doctype, d.permlevel, "read")) {
  41. if(d.fieldtype=="Image" && d.options) {
  42. me.fields.push(t + "`" + d.options + "`");
  43. } else {
  44. me.fields.push(t + "`" + d.fieldname + "`");
  45. }
  46. if(d.fieldtype=="Select") {
  47. me.stats.push(d.fieldname);
  48. }
  49. // currency field for symbol (multi-currency)
  50. if(d.fieldtype=="Currency" && d.options) {
  51. if(d.options.indexOf(":")!=-1) {
  52. me.fields.push(t + "`" + d.options.split(":")[1] + "`");
  53. } else {
  54. me.fields.push(t + "`" + d.options + "`");
  55. };
  56. }
  57. }
  58. });
  59. // additional fields
  60. if(this.settings.add_fields) {
  61. $.each(this.settings.add_fields, function(i, d) {
  62. if(me.fields.indexOf(d)==-1)
  63. me.fields.push(d);
  64. });
  65. }
  66. },
  67. set_columns: function() {
  68. this.columns = [];
  69. var me = this;
  70. if(this.workflow_state_fieldname) {
  71. this.columns.push({
  72. colspan: 3,
  73. content: this.workflow_state_fieldname,
  74. type:"select"
  75. });
  76. }
  77. // overridden
  78. var overridden = $.map(this.settings.add_columns || [], function(d) {
  79. return d.content;
  80. });
  81. var docfields_in_list_view = wn.model.get("DocField", {"parent":this.doctype,
  82. "in_list_view":1}).sort(function(a, b) { return a.idx - b.idx })
  83. $.each(docfields_in_list_view, function(i,d) {
  84. if(in_list(overridden, d.fieldname)) {
  85. return;
  86. }
  87. // field width
  88. var colspan = "3";
  89. if(in_list(["Int", "Percent", "Select"], d.fieldtype)) {
  90. colspan = "2";
  91. } else if(d.fieldtype=="Check") {
  92. colspan = "1";
  93. } else if(in_list(["name", "subject", "title"], d.fieldname)) { // subjects are longer
  94. colspan = "4";
  95. } else if(d.fieldtype=="Text Editor" || d.fieldtype=="Text") {
  96. colspan = "4";
  97. }
  98. me.columns.push({colspan: colspan, content: d.fieldname,
  99. type:d.fieldtype, df:d, title:wn._(d.label) });
  100. });
  101. // additional columns
  102. if(this.settings.add_columns) {
  103. $.each(this.settings.add_columns, function(i, d) {
  104. me.columns.push(d);
  105. });
  106. }
  107. },
  108. render: function(row, data) {
  109. this.prepare_data(data);
  110. //$(row).removeClass("list-row");
  111. // maintain id_list to avoid duplication incase
  112. // of filtering by child table
  113. if(in_list(this.id_list, data.name))
  114. return;
  115. else
  116. this.id_list.push(data.name);
  117. var body = $('<div class="doclist-row row">\
  118. <div class="list-row-id-area col-sm-4" style="white-space: nowrap;\
  119. text-overflow: ellipsis; max-height: 30px"></div>\
  120. <div class="list-row-content-area col-sm-8"></div>\
  121. </div>').appendTo($(row).css({"position":"relative"})),
  122. colspans = 0,
  123. me = this;
  124. me.render_avatar_and_id(data, body.find(".list-row-id-area"))
  125. // make table
  126. $.each(this.columns, function(i, v) {
  127. var colspan = v.colspan || 3;
  128. colspans = colspans + flt(colspan)
  129. if(colspans <= 12) {
  130. var col = me.make_column(body.find(".list-row-content-area"), colspan);
  131. me.render_column(data, col, v);
  132. }
  133. });
  134. var comments = data._comments ? JSON.parse(data._comments) : [];
  135. var tags = $.map((data._user_tags || "").split(","), function(v) { return v ? v : null; });
  136. var timestamp_and_comment =
  137. $('<div class="list-timestamp">')
  138. .appendTo(row)
  139. .html(""
  140. + (tags.length ? (
  141. '<span style="margin-right: 10px;" class="list-tag-preview">' + tags.join(", ") + '</span>'
  142. ): "")
  143. + (comments.length ?
  144. ('<a style="margin-right: 10px;" href="#Form/'+
  145. this.doctype + '/' + data.name
  146. +'" title="'+
  147. comments[comments.length-1].comment
  148. +'"><i class="icon-comments"></i> '
  149. + comments.length + " " + (
  150. comments.length===1 ? wn._("comment") : wn._("comments")) + '</a>')
  151. : "")
  152. + comment_when(data.modified));
  153. // row #2
  154. var row2 = $('<div class="row tag-row" style="margin-bottom: 5px;">\
  155. <div class="col-xs-12">\
  156. <div class="col-xs-3"></div>\
  157. <div class="col-xs-7">\
  158. <div class="list-tag xs-hidden"></div>\
  159. <div class="list-last-modified text-muted xs-visible"></div>\
  160. </div>\
  161. </div>\
  162. </div>').appendTo(row);
  163. // modified
  164. body.find(".list-last-modified").html(wn._("Last updated by") + ": " + wn.user_info(data.modified_by).fullname);
  165. if(!me.doclistview.tags_shown) {
  166. row2.addClass("hide");
  167. }
  168. // add tags
  169. var tag_editor = new wn.ui.TagEditor({
  170. parent: row2.find(".list-tag"),
  171. frm: {
  172. doctype: this.doctype,
  173. docname: data.name
  174. },
  175. user_tags: data._user_tags
  176. });
  177. tag_editor.$w.on("click", ".tagit-label", function() {
  178. me.doclistview.set_filter("_user_tags",
  179. $(this).text());
  180. });
  181. },
  182. make_column: function(body, colspan) {
  183. var col = $("<div class='col'>")
  184. .appendTo(body)
  185. .addClass("col-sm-" + cint(colspan))
  186. .css({
  187. "white-space": "nowrap",
  188. "text-overflow": "ellipsis",
  189. "height": "30px",
  190. "padding-top":"3px"
  191. })
  192. return col;
  193. },
  194. render_avatar_and_id: function(data, parent) {
  195. if((wn.model.can_delete(this.doctype) || this.settings.selectable) && !this.no_delete) {
  196. $('<input class="list-delete" type="checkbox">')
  197. .data('name', data.name)
  198. .data('data', data)
  199. .css({"margin-right": "5px"})
  200. .appendTo(parent)
  201. }
  202. var $avatar = $(wn.avatar(data.modified_by, false, wn._("Modified by")+": "
  203. + wn.user_info(data.modified_by).fullname))
  204. .appendTo(parent)
  205. .css({"max-width": "100%"})
  206. if(wn.model.is_submittable(this.doctype)) {
  207. $(parent).append(repl('<span class="docstatus" style="margin-right: 3px;"> \
  208. <i class="%(docstatus_icon)s" \
  209. title="%(docstatus_title)s"></i></span>', data));
  210. }
  211. $("<a>")
  212. .attr("href", "#Form/" + data.doctype + "/" + encodeURIComponent(data.name))
  213. .html(data.name)
  214. .appendTo(parent.css({"overflow":"hidden"}));
  215. },
  216. render_column: function(data, parent, opts) {
  217. var me = this;
  218. if(opts.type) opts.type= opts.type.toLowerCase();
  219. // style
  220. if(opts.css) {
  221. $.each(opts.css, function(k, v) { $(parent).css(k, v)});
  222. }
  223. // multiple content
  224. if(opts.content.indexOf && opts.content.indexOf('+')!=-1) {
  225. $.map(opts.content.split('+'), function(v) {
  226. me.render_column(data, parent, {content:v, title: opts.title});
  227. });
  228. return;
  229. }
  230. // content
  231. if(typeof opts.content=='function') {
  232. opts.content(parent, data, me);
  233. }
  234. else if(opts.content=='check') {
  235. }
  236. else if(opts.type=='bar-graph' || opts.type=="percent") {
  237. this.render_bar_graph(parent, data, opts.content, opts.label);
  238. }
  239. else if(opts.template) {
  240. $(parent).append(repl(opts.template, data));
  241. }
  242. else if(opts.type=="date" && data[opts.content]) {
  243. $("<span>")
  244. .html(wn.datetime.str_to_user(data[opts.content]))
  245. .css({"color":"#888"})
  246. .appendTo(parent);
  247. }
  248. else if(opts.type=="image") {
  249. data[opts.content] = data[opts.df.options];
  250. if(data[opts.content])
  251. $("<img>")
  252. .attr("src", wn.utils.get_file_link(data[opts.content]))
  253. .css({
  254. "max-width": "100%",
  255. "max-height": "30px"
  256. })
  257. .appendTo(parent);
  258. }
  259. else if(opts.type=="select" && data[opts.content]) {
  260. var label_class = "label-default";
  261. var style = wn.utils.guess_style(data[opts.content]);
  262. if(style) label_class = "label-" + style;
  263. $("<span>"
  264. + data[opts.content] + "</span>")
  265. .css({"cursor":"pointer"})
  266. .addClass("label")
  267. .addClass(label_class)
  268. .attr("data-fieldname", opts.content)
  269. .click(function() {
  270. me.doclistview.set_filter($(this).attr("data-fieldname"),
  271. $(this).text());
  272. })
  273. .appendTo(parent.css({"overflow":"hidden"}));
  274. }
  275. else if(data[opts.content]) {
  276. $("<span>")
  277. .html(wn.format(data[opts.content], opts.df, null, data))
  278. .appendTo(parent.css({"overflow":"hidden"}))
  279. }
  280. // finally
  281. if(!$(parent).html()) {
  282. $("<span>-</span>").css({color:"#ccc"}).appendTo(parent);
  283. }
  284. // title
  285. if(!in_list(["avatar", "_user_tags", "check"], opts.content)) {
  286. if($(parent).attr("title")==undefined) {
  287. $(parent).attr("title", (opts.title || opts.content) + ": "
  288. + (data[opts.content] || "Not Set"))
  289. }
  290. $(parent).tooltip();
  291. }
  292. },
  293. show_hide_check_column: function() {
  294. if(!this.doclistview.can_delete) {
  295. this.columns = $.map(this.columns, function(v, i) { if(v.content!='check') return v });
  296. }
  297. },
  298. prepare_data: function(data) {
  299. if(data.modified)
  300. this.prepare_when(data, data.modified);
  301. // docstatus
  302. if(data.docstatus==0 || data.docstatus==null) {
  303. data.docstatus_icon = 'icon-check-empty';
  304. data.docstatus_title = wn._('Editable');
  305. } else if(data.docstatus==1) {
  306. data.docstatus_icon = 'icon-lock';
  307. data.docstatus_title = wn._('Submitted');
  308. } else if(data.docstatus==2) {
  309. data.docstatus_icon = 'icon-remove';
  310. data.docstatus_title = wn._('Cancelled');
  311. }
  312. // nulls as strings
  313. for(key in data) {
  314. if(data[key]==null) {
  315. data[key]='';
  316. }
  317. }
  318. // prepare data in settings
  319. if(this.settings.prepare_data)
  320. this.settings.prepare_data(data);
  321. },
  322. prepare_when: function(data, date_str) {
  323. if (!date_str) date_str = data.modified;
  324. // when
  325. data.when = (dateutil.str_to_user(date_str)).split(' ')[0];
  326. var diff = dateutil.get_diff(dateutil.get_today(), date_str.split(' ')[0]);
  327. if(diff==0) {
  328. data.when = dateutil.comment_when(date_str);
  329. }
  330. if(diff == 1) {
  331. data.when = wn._('Yesterday')
  332. }
  333. if(diff == 2) {
  334. data.when = wn._('2 days ago')
  335. }
  336. },
  337. render_bar_graph: function(parent, data, field, label) {
  338. var args = {
  339. percent: data[field],
  340. label: label
  341. }
  342. $(parent).append(repl('<span class="progress" style="width: 100%; float: left; margin: 5px 0px;"> \
  343. <span class="progress-bar" title="%(percent)s% %(label)s" \
  344. style="width: %(percent)s%;"></span>\
  345. </span>', args));
  346. },
  347. render_icon: function(parent, icon_class, label) {
  348. var icon_html = "<i class='%(icon_class)s' title='%(label)s'></i>";
  349. $(parent).append(repl(icon_html, {icon_class: icon_class, label: label || ''}));
  350. }
  351. });
  352. // embeddable
  353. wn.provide('wn.views.RecordListView');
  354. wn.views.RecordListView = wn.views.DocListView.extend({
  355. init: function(doctype, wrapper, ListView) {
  356. this.doctype = doctype;
  357. this.wrapper = wrapper;
  358. this.listview = new ListView(this, doctype);
  359. this.listview.parent = this;
  360. this.setup();
  361. },
  362. setup: function() {
  363. var me = this;
  364. me.page_length = 10;
  365. $(me.wrapper).empty();
  366. me.init_list();
  367. },
  368. get_args: function() {
  369. var args = this._super();
  370. $.each((this.default_filters || []), function(i, f) {
  371. args.filters.push(f);
  372. });
  373. args.docstatus = args.docstatus.concat((this.default_docstatus || []));
  374. return args;
  375. },
  376. });