You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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