active-list 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ // Default style for ActiveList
2
+ @import "compass/css3";
3
+ @import "compass/typography/text/replacement";
4
+
5
+ // Because Compass sprites do not seems to work well in gems
6
+ @mixin active-list-sprite($name, $size: 16px) {
7
+ background-image: image-url("active-list.png");
8
+ background-repeat: no-repeat;
9
+ height: $size;
10
+ width: $size;
11
+ $i: 0;
12
+ @each $icon in menu check columns export pages first-page previous-page previous-line next-line next-page last-page sort sort-down sort-up true false {
13
+ @if $icon == $name {
14
+ background-position: (-$i * $size) 0;
15
+ }
16
+ $i: $i + 1;
17
+ }
18
+ }
19
+
20
+
21
+ @mixin active-list-theme($theme-color: #777777) {
22
+ $default-border-color: mix($theme-color, #FFF, 30%);
23
+ table.list {
24
+ border-collapse: collapse;
25
+ width: 100%;
26
+ border: 1px solid $default-border-color;
27
+ z-index: 2;
28
+ @include box-shadow(0 0 7px rgba(0, 0, 0, 0.15));
29
+ thead {
30
+ th {
31
+ border: 1px solid $default-border-color;
32
+ padding: 2px 3px;
33
+ @include background($theme-color linear-gradient(top, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.8)));
34
+ &[data-list-column-sort] {
35
+ .icon {
36
+ @include active-list-sprite(sort);
37
+ @include inline-block;
38
+ margin: 0 2px;
39
+ }
40
+ &.sor[data-list-column-sort="desc"] .icon {
41
+ @include active-list-sprite(sort-down); }
42
+ &.sor[data-list-column-sort="asc"] .icon {
43
+ @include active-list-sprite(sort-up); }
44
+ }
45
+ html[dir="ltr"] & { text-align: left; }
46
+ html[dir="rtl"] & { text-align: right; }
47
+ &.spe {
48
+ width: 16px;
49
+ height: 16px;
50
+ .list-menu {
51
+ position: relative;
52
+ background: none;
53
+ .list-menu-start {
54
+ padding: 2px;
55
+ width: 16px;
56
+ height: 16px;
57
+ .icon { @include active-list-sprite(menu); }
58
+ .text { display: none; }
59
+ }
60
+ &:hover {
61
+ .list-menu-start {
62
+ background-color: white;
63
+ z-index:5000;
64
+ }
65
+ }
66
+ ul {
67
+ border: 1px solid $default-border-color;
68
+ background: #FFF;
69
+ @include box-shadow(0 0 5px rgba(0, 0, 0, 0.3));
70
+ li {
71
+ $menu-width: 290px;
72
+ width: $menu-width;
73
+ ul {
74
+ html[dir="ltr"] & { right: $menu-width; }
75
+ html[dir="rtl"] & { left: $menu-width; }
76
+ }
77
+ }
78
+ }
79
+ a, li {
80
+ display: block;
81
+ padding: 2px;
82
+ .icon {
83
+ @include inline-block;
84
+ height:16px;
85
+ width: 16px;
86
+ html[dir="ltr"] & { margin-right: 4px; }
87
+ html[dir="rtl"] & { margin-left: 4px; }
88
+ }
89
+ &.pages .icon { @include active-list-sprite(pages); }
90
+ &.export .icon { @include active-list-sprite(export); }
91
+ &.check .icon { @include active-list-sprite(check); }
92
+ &.columns .icon { @include active-list-sprite(columns); }
93
+ &.checked .icon { @include active-list-sprite(true); }
94
+ &.unchecked .icon { @include active-list-sprite(false); }
95
+ }
96
+ a:hover, li:hover {
97
+ text-decoration: none;
98
+ background: mix($theme-color, #FFF, 10%);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ tbody {
105
+ tr {
106
+ td {
107
+ padding: 2px 3px;
108
+ border-top: 1px solid mix($theme-color, #FFF, 10%);
109
+ border-bottom: none;
110
+ border-left: none;
111
+ border-right: none;
112
+ &.chk,&.act, &.dld, &.bln, &.dat, &.web { text-align:center }
113
+ &.dec, &.flt, &.int {
114
+ html[dir="ltr"] & { text-align: right; }
115
+ html[dir="rtl"] & { text-align: left; }
116
+ }
117
+ &.country { white-space: nowrap; }
118
+ &.color { color: white; text-shadow: 0 0 2px #000; width: 6ex; text-align: center; }
119
+ }
120
+ &:first-child td {
121
+ border-top: none;
122
+ }
123
+ }
124
+ }
125
+
126
+ @include list-colors(#FFFFFF);
127
+ }
128
+ .extras {
129
+ z-index: 0;
130
+ @include background(transparentize(mix($theme-color, #FFF, 40%), 0.7));
131
+ .pagination {
132
+ html[dir="ltr"] & { text-align: left; }
133
+ html[dir="rtl"] & { text-align: right; }
134
+ & > * {
135
+ margin: 0 2px;
136
+ }
137
+ .first-page, .previous-page, .next-page, .last-page {
138
+ @include inline-block;
139
+ @include squish-text;
140
+ }
141
+ html[dir="rtl"] & .last-page, html[dir="ltr"] & .first-page {
142
+ @include active-list-sprite(first-page);
143
+ }
144
+ html[dir="rtl"] & .first-page, html[dir="ltr"] & .last-page {
145
+ @include active-list-sprite(last-page);
146
+ }
147
+ html[dir="rtl"] & .next-page, html[dir="ltr"] & .previous-page {
148
+ @include active-list-sprite(previous-page);
149
+ }
150
+ html[dir="rtl"] & .previous-page, html[dir="ltr"] & .next-page {
151
+ @include active-list-sprite(next-page);
152
+ }
153
+ .separator {
154
+ @include inline-block;
155
+ width: 2px;
156
+ height: 1.2em;
157
+ background: transparentize($theme-color, 0.7);
158
+ }
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,103 @@
1
+ module ActiveList
2
+
3
+ class Table
4
+ attr_reader :name, :model, :options, :id, :columns, :parameters
5
+
6
+ @@current_id = 0
7
+
8
+ def initialize(name, model = nil, options = {})
9
+ @name = name
10
+ @model = model || name.to_s.classify.constantize
11
+ @options = options
12
+ @paginate = !(@options[:pagination]==:none || @options[:paginate].is_a?(FalseClass))
13
+ @options[:renderer] ||= :simple_renderer
14
+ @options[:per_page] = 20 if @options[:per_page].to_i <= 0
15
+ @options[:page] = 1 if @options[:page].to_i <= 0
16
+ @columns = []
17
+ @current_id = 0
18
+ @id = @@current_id.to_s(36).to_sym
19
+ @@current_id += 1
20
+ @parameters = {:sort=>:to_s, :dir=>:to_s}
21
+ @parameters.merge!(:page=>:to_i, :per_page=>:to_i) if self.paginate?
22
+ end
23
+
24
+ def new_id
25
+ id = @current_id.to_s(36).to_sym
26
+ @current_id += 1
27
+ return id
28
+ end
29
+
30
+ def model_columns
31
+ @model.columns
32
+ end
33
+
34
+ def sortable_columns
35
+ @columns.select{|c| c.sortable?}
36
+ end
37
+
38
+ def exportable_columns
39
+ @columns.select{|c| c.exportable?}
40
+ end
41
+
42
+ def paginate?
43
+ @paginate
44
+ end
45
+
46
+ def load_default_columns
47
+ for column in self.model_columns
48
+ reflections = @model.reflections.values.select{|r| r.macro == :belongs_to and r.foreign_key.to_s == column.name.to_s}
49
+ if reflections.size == 1
50
+ reflection = reflections.first
51
+ columns = reflection.class_name.constantize.columns.collect{ |c| c.name.to_s }
52
+ self.column([:label, :name, :code, :number].detect{ |l| columns.include?(l.to_s) }, :through => reflection.name, :url => true)
53
+ else
54
+ self.column(column.name)
55
+ end
56
+ end
57
+ return true
58
+ end
59
+
60
+ end
61
+
62
+
63
+ class Column
64
+ attr_accessor :name, :options, :table
65
+ attr_reader :id
66
+
67
+ def initialize(table, name, options={})
68
+ @table = table
69
+ @name = name
70
+ @options = options
71
+ @column = @table.model.columns.detect{|c| c.name.to_s == @name.to_s }
72
+ @id = @table.new_id
73
+ end
74
+
75
+ def header_code
76
+ raise NotImplementedError.new("#{self.class.name}#header_code is not implemented.")
77
+ end
78
+
79
+ def sortable?
80
+ false
81
+ end
82
+
83
+ def exportable?
84
+ false
85
+ end
86
+
87
+ # Unique identifier of the column in the application
88
+ def unique_id
89
+ "#{@table.name}-#{@id}"
90
+ end
91
+
92
+ # Uncommon but simple identifier for CSS class uses
93
+ def simple_id
94
+ "_#{@table.id}_#{@id}"
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ require "active-list/columns/data_column"
102
+ require "active-list/columns/action_column"
103
+ require "active-list/columns/field_column"
@@ -0,0 +1,71 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ module ActiveList
4
+
5
+ mattr_reader :exporters
6
+ @@exporters = {}
7
+
8
+ def self.register_exporter(name, exporter)
9
+ raise ArgumentError.new("ActiveList::Exporter expected (got #{exporter.name}/#{exporter.ancestors.inspect})") unless exporter.ancestors.include? ActiveList::Exporter
10
+ @@exporters[name] = exporter.new(name)
11
+ end
12
+
13
+ class Exporter
14
+ attr_reader :name
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ end
19
+
20
+ def file_extension
21
+ "txt"
22
+ end
23
+
24
+ def mime_type
25
+ Mime::TEXT
26
+ end
27
+
28
+ # Not used
29
+ # def condition
30
+ # "not request.xhr? and params[:format] == '#{name}'"
31
+ # end
32
+
33
+ def send_data_code(table)
34
+ raise NotImplementedError.new("#{self.class.name}#format_data_code is not implemented.")
35
+ end
36
+
37
+ def columns_headers(table, options={})
38
+ headers, columns = [], table.exportable_columns
39
+ for column in columns
40
+ datum = column.header_code
41
+ headers << (options[:iconv] ? "#{options[:iconv]}.iconv("+datum+".to_s)" : datum)
42
+ end
43
+ return headers
44
+ end
45
+
46
+ def columns_to_array(table, nature, options={})
47
+ columns = table.exportable_columns
48
+
49
+ array = []
50
+ record = options[:record]||'rekord'
51
+ for column in columns
52
+ if column.is_a? ActiveList::Column
53
+ if nature==:header
54
+ datum = column.header_code
55
+ else
56
+ datum = column.exporting_datum_code(record)
57
+ end
58
+ array << (options[:iconv] ? "#{options[:iconv]}.iconv("+datum+".to_s)" : datum)
59
+ end
60
+ end
61
+ return array
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+
69
+ require "active-list/exporters/open_document_spreadsheet_exporter"
70
+ require "active-list/exporters/csv_exporter"
71
+ require "active-list/exporters/excel_csv_exporter"
@@ -0,0 +1,30 @@
1
+ module ActiveList
2
+
3
+ class CsvExporter < ActiveList::Exporter
4
+
5
+ def file_extension
6
+ "csv"
7
+ end
8
+
9
+ def mime_type
10
+ Mime::CSV
11
+ end
12
+
13
+ def send_data_code(table)
14
+ record = "r"
15
+ code = table.select_data_code(:paginate => false)
16
+ code += "data = ActiveList::CSV.generate do |csv|\n"
17
+ code += " csv << [#{columns_to_array(table, :header).join(', ')}]\n"
18
+ code += " for #{record} in #{table.records_variable_name}\n"
19
+ code += " csv << [#{columns_to_array(table, :body, :record=>record).join(', ')}]\n"
20
+ code += " end\n"
21
+ code += "end\n"
22
+ code += "send_data(data, :type=>#{self.mime_type.to_s.inspect}, :disposition=>'inline', :filename=>#{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i,'_')+'.#{self.file_extension}')\n"
23
+ return code
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ ActiveList.register_exporter(:csv, ActiveList::CsvExporter)
@@ -0,0 +1,36 @@
1
+ # encoding: UTF-8
2
+
3
+ # Register XCSV format unless is already set
4
+ Mime::Type.register("text/csv", :xcsv) unless defined? Mime::XCSV
5
+
6
+ module ActiveList
7
+
8
+ class ExcelCsvExporter < ActiveList::CsvExporter
9
+
10
+ def file_extension
11
+ "csv"
12
+ end
13
+
14
+ def mime_type
15
+ Mime::XCSV
16
+ end
17
+
18
+ def send_data_code(table)
19
+ record = "r"
20
+ code = table.select_data_code(:paginate => false)
21
+ code += "ic = Iconv.new('cp1252', 'utf-8')\n"
22
+ code += "data = ActiveList::CSV.generate(:col_sep=>';') do |csv|\n"
23
+ code += " csv << [#{columns_to_array(table, :header, :iconv=>'ic').join(', ')}]\n"
24
+ code += " for #{record} in #{table.records_variable_name}\n"
25
+ code += " csv << [#{columns_to_array(table, :body, :record=>record, :iconv=>'ic').join(', ')}]\n"
26
+ code += " end\n"
27
+ code += "end\n"
28
+ code += "send_data(data, :type=>#{self.mime_type.to_s.inspect}, :disposition=>'inline', :filename=>#{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i,'_')+'.#{self.file_extension}')\n"
29
+ return code
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ ActiveList.register_exporter(:xcsv, ActiveList::ExcelCsvExporter)
@@ -0,0 +1,81 @@
1
+ # encoding: UTF-8
2
+
3
+ # Register ODS format unless is already set
4
+ Mime::Type.register("application/vnd.oasis.opendocument.spreadsheet", :ods) unless defined? Mime::ODS
5
+
6
+ module ActiveList
7
+
8
+ class OpenDocumentSpreadsheetExporter < ActiveList::Exporter
9
+
10
+ DATE_ELEMENTS = {
11
+ "m" => "<number:month number:style=\"long\"/>",
12
+ "d" => "<number:day number:style=\"long\"/>",
13
+ "Y" => "<number:year/>"
14
+ }
15
+
16
+
17
+ def file_extension
18
+ "ods"
19
+ end
20
+
21
+ def mime_type
22
+ Mime::ODS
23
+ end
24
+
25
+ def send_data_code(table)
26
+ xml_escape = "to_s.gsub('&', '&amp;').gsub('\\'', '&apos;').gsub('<', '&lt;').gsub('>', '&gt;')"
27
+ xml_escape << ".force_encoding('US-ASCII')" if xml_escape.respond_to?(:force_encoding)
28
+ record = "r"
29
+ code = table.select_data_code(:paginate => false)
30
+ code << "name = #{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i,'_')\n"
31
+ code << "temporary_dir = ::Rails.root.join('tmp', 'active-list')\n"
32
+ code << "FileUtils.mkdir_p(temporary_dir)\n"
33
+ code << "file = temporary_dir.join(name+rand.to_s+'.#{self.file_extension}')\n"
34
+ code << "Zip::ZipOutputStream.open(file) do |zile|\n"
35
+ # MimeType in first place
36
+ code << " zile.put_next_entry('mimetype', nil, nil, Zip::ZipEntry::STORED)\n"
37
+ code << " zile << '#{self.mime_type}'\n"
38
+
39
+ # Manifest
40
+ code << " zile.put_next_entry('META-INF/manifest.xml')\n"
41
+ code << " zile << ('<?xml version=\"1.0\" encoding=\"UTF-8\"?><manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\"><manifest:file-entry manifest:media-type=\"#{self.mime_type}\" manifest:full-path=\"/\"/><manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/></manifest:manifest>')\n"
42
+ code << " zile.put_next_entry('content.xml')\n"
43
+
44
+ code << " zile << ('<?xml version=\"1.0\" encoding=\"UTF-8\"?><office:document-content xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\" xmlns:math=\"http://www.w3.org/1998/Math/MathML\" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\" xmlns:ooo=\"http://openoffice.org/2004/office\" xmlns:ooow=\"http://openoffice.org/2004/writer\" xmlns:oooc=\"http://openoffice.org/2004/calc\" xmlns:dom=\"http://www.w3.org/2001/xml-events\" xmlns:xforms=\"http://www.w3.org/2002/xforms\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:field=\"urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0\" office:version=\"1.1\"><office:scripts/>')\n"
45
+ # Styles
46
+ code << " zile << ('<office:automatic-styles>"+
47
+ "<style:style style:name=\"co1\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:use-optimal-column-width=\"true\"/></style:style>"+
48
+ "<style:style style:name=\"header\" style:family=\"table-cell\"><style:text-properties fo:font-weight=\"bold\" style:font-weight-asian=\"bold\" style:font-weight-complex=\"bold\"/></style:style>"+
49
+ "<number:date-style style:name=\"K4D\" number:automatic-order=\"true\"><number:text>"+::I18n.translate("date.formats.default").gsub(/\%./){|x| "</number:text>"+DATE_ELEMENTS[x[1..1]]+"<number:text>"} +"</number:text></number:date-style><style:style style:name=\"ce1\" style:family=\"table-cell\" style:data-style-name=\"K4D\"/>"+
50
+ "</office:automatic-styles>')\n"
51
+
52
+ # Tables
53
+ code << " zile << ('<office:body><office:spreadsheet><table:table table:name=\"'+#{table.model.name}.model_name.human.#{xml_escape}+'\">')\n"
54
+ code << " zile << ('<table:table-column table:number-columns-repeated=\"#{table.exportable_columns.size}\"/>')\n"
55
+ code << " zile << ('<table:table-header-rows><table:table-row>"+columns_headers(table).collect{|h| "<table:table-cell table:style-name=\"header\" office:value-type=\"string\"><text:p>'+(#{h}).#{xml_escape}+'</text:p></table:table-cell>"}.join+"</table:table-row></table:table-header-rows>')\n"
56
+ code << " for #{record} in #{table.records_variable_name}\n"
57
+ code << " zile << ('<table:table-row>"+table.exportable_columns.collect do |column|
58
+ "<table:table-cell"+(if column.numeric? or column.datatype==:decimal
59
+ " office:value-type=\"float\" office:value=\"'+(#{column.datum_code(record)}).#{xml_escape}+'\""
60
+ elsif column.datatype==:boolean
61
+ " office:value-type=\"boolean\" office:boolean-value=\"'+(#{column.datum_code(record)}).#{xml_escape}+'\""
62
+ elsif column.datatype==:date
63
+ " office:value-type=\"date\" table:style-name=\"ce1\" office:date-value=\"'+(#{column.datum_code(record)}).#{xml_escape}+'\""
64
+ else
65
+ " office:value-type=\"string\""
66
+ end)+"><text:p>'+("+column.exporting_datum_code(record, true)+").#{xml_escape}+'</text:p></table:table-cell>"
67
+ end.join+"</table:table-row>')\n"
68
+ code << " end\n"
69
+ code << " zile << ('</table:table></office:spreadsheet></office:body></office:document-content>')\n"
70
+ code << "end\n"
71
+ code << "send_file(file, :stream=>false, :type=>#{self.mime_type.to_s.inspect}, :disposition=>'inline', :filename=>name+'.#{self.file_extension}')\n"
72
+ code << "File.delete(file)\n" # Removes tmp files before they explode the disk
73
+ # raise code
74
+ return code
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ ActiveList.register_exporter(:ods, ActiveList::OpenDocumentSpreadsheetExporter)