jekyll-fdroid 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4b78b9f00daf8c96e99905e33a38149b2ea0f2e
4
- data.tar.gz: 841d3465e9c14da0bd3bda315672d6d0c2b5f7c4
3
+ metadata.gz: 5223ec6f44fa3c3131928d083cee93e06c28045b
4
+ data.tar.gz: 1acae1bbdda2af1529df4d8fc955e27d6e428514
5
5
  SHA512:
6
- metadata.gz: 4cb43683d09abcf6a5cf4fb0b4168bcb61582017f632aa2d7b92d509bb9ac16bc215c2eaf93c55f0d312468d1a8a6343827e8542786f064293b26f2ae87ce73f
7
- data.tar.gz: bffd6df0de24f340df08d420e3b38aa0372f6476a2de610cd5fde5bcf8bbfeec47dfee23edf0e6d12559dcbc8dc695578e909b5706ff0639bcd6b91fd8db8141
6
+ metadata.gz: 52dd3848f495b9cf4a2c973faf5f3324e90bdacacbd21545e63a93a455771c4d1522739aba09968727e8e48f4d7bc496320142138ec9425cc7ce420fcad98bf6
7
+ data.tar.gz: c6227570b89a1c4edad316e55678bca5bd1278f28511d3474f41f58d6eda57bcd625c760626bebde750f80a960b6dd8fb828a08c87ca82ca1bc62e10d6469e78
@@ -0,0 +1,248 @@
1
+ # F-Droid's Jekyll Plugin
2
+ #
3
+ # Copyright (C) 2017 Nico Alt
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require_relative './Package'
19
+
20
+ module FDroid
21
+ class App
22
+ def initialize(app, packages, locale)
23
+ # Sort packages in reverse-chronological order
24
+ @packages = packages.map { |p| Package.new(p) }
25
+ @app = app
26
+ @locale = locale
27
+ @available_locales = app.key?('localized') ? App.available_locales(locale, app['localized']) : nil
28
+ end
29
+
30
+ def package_name
31
+ field 'packageName'
32
+ end
33
+
34
+ def to_s
35
+ package_name
36
+ end
37
+
38
+ def icon
39
+ localized = App.localized_graphic_path(@available_locales, @app['localized'], 'icon')
40
+ if localized
41
+ "#{package_name}/#{localized}"
42
+ else
43
+ "icons-640/#{field('icon')}"
44
+ end
45
+ end
46
+
47
+ def name
48
+ App.localized(@available_locales, @app['localized'], 'name') || field('name')
49
+ end
50
+
51
+ def summary
52
+ App.localized(@available_locales, @app['localized'], 'summary') || field('summary')
53
+ end
54
+
55
+ def description
56
+ desc = App.localized(@available_locales, @app['localized'], 'description') || field('description')
57
+
58
+ if desc != nil
59
+ desc = App.process_app_description(desc)
60
+ end
61
+
62
+ return desc
63
+ end
64
+
65
+ def suggested_version_code
66
+ code = field('suggestedVersionCode')
67
+ if code != nil
68
+ code = Integer(code)
69
+ end
70
+ return code
71
+ end
72
+
73
+
74
+ # Generates a hash of dumb strings to be used in templates.
75
+ # If a specific value is not present, then it will have a nil value.
76
+ # If a value can be localized, then it will choose the most appropriate
77
+ # translation based on @available_locales and @locale.
78
+ # The 'packages' key is an array of Package.to_data hashes.
79
+ # @return [Hash]
80
+ def to_data
81
+ {
82
+ # These fields are taken as is from the metadata. If not present, they are
83
+ 'package_name' => package_name,
84
+ 'author_email' => field('authorEmail'),
85
+ 'author_name' => field('authorName'),
86
+ 'author_website' => field('authorWebSite'),
87
+ 'bitcoin' => field('bitcoin'),
88
+ 'donate' => field('donate'),
89
+ 'flattr' => field('flattr'),
90
+ 'categories' => field('categories'),
91
+ 'anti_features' => field('anti_features'),
92
+ 'suggested_version_code' => suggested_version_code,
93
+ 'suggested_version_name' => @packages.detect { |p| p.version_code == suggested_version_code }&.version_name,
94
+ 'issue_tracker' => field('issueTracker'),
95
+ 'changelog' => field('changelog'),
96
+ 'license' => field('license'),
97
+ 'source_code' => field('sourceCode'),
98
+ 'website' => field('webSite'),
99
+ 'added' => field('added'),
100
+ 'last_updated' => field('lastUpdated'),
101
+ 'whats_new' => App.process_app_description(App.localized(@available_locales, @app['localized'], 'whatsNew')),
102
+
103
+ 'icon' => icon,
104
+ 'title' => name,
105
+ 'summary' => summary,
106
+
107
+ 'description' => description,
108
+ 'feature_graphic' => App.localized_graphic_path(@available_locales, @app['localized'], 'featureGraphic'),
109
+ 'phone_screenshots' => App.localized_graphic_list_paths(@available_locales, @app['localized'], 'phoneScreenshots'),
110
+ 'seven_inch_screenshots' => App.localized_graphic_list_paths(@available_locales, @app['localized'], 'sevenInchScreenshots'),
111
+
112
+ 'packages' => @packages.sort.reverse.map { |p| p.to_data },
113
+
114
+ 'beautiful_url' => "/packages/#{package_name}"
115
+ }
116
+ end
117
+
118
+ # Any transformations which are required to turn the "description" into something which is
119
+ # displayable via HTML is done here (e.g. replacing "fdroid.app:" schemes, formatting new lines,
120
+ # etc.
121
+ def self.process_app_description(string)
122
+ if string == nil
123
+ return nil
124
+ end
125
+
126
+ string = self.replace_fdroid_app_links(string)
127
+ self.format_description_to_html(string)
128
+ end
129
+
130
+ # Finds all "fdroid.app:" schemes in a particular string, and replaces with "/packages/".
131
+ # @param [string] string
132
+ # @return [string]
133
+ def self.replace_fdroid_app_links(string)
134
+ string.gsub /fdroid\.app:([\w._]*)/, '/packages/\1'
135
+ end
136
+
137
+ # Ensure double newlines "\n\n" are converted to "<br />" tags.
138
+ def self.format_description_to_html(string)
139
+ string
140
+ .gsub("\n\n", '<br />')
141
+ .gsub(/\r?\n/, ' ')
142
+ end
143
+
144
+ # @param [string] available_locales
145
+ # @param [string] localized
146
+ # @param [string] field
147
+ # @return [string]
148
+ def self.localized(available_locales, localized, field)
149
+ return nil unless available_locales != nil
150
+
151
+ available_locales.each do |l|
152
+ if localized[l].key?(field)
153
+ return localized[l][field]
154
+ end
155
+ end
156
+
157
+ return nil
158
+ end
159
+
160
+ # Prefixes the result with "chosen_locale/" before returning.
161
+ # @see localized
162
+ def self.localized_graphic_path(available_locales, localized, field)
163
+ return nil unless available_locales != nil
164
+
165
+ available_locales.each do |l|
166
+ if localized[l].key?(field)
167
+ return "#{l}/#{localized[l][field]}"
168
+ end
169
+ end
170
+
171
+ return nil
172
+ end
173
+
174
+ # Similar to localized_graphic_path, but prefixes each item in the resulting array
175
+ # with "chosen_locale/field/".
176
+ # @see localized
177
+ # @see localized_graphic_path
178
+ def self.localized_graphic_list_paths(available_locales, localized, field)
179
+ return nil unless available_locales != nil
180
+
181
+ available_locales.each do |l|
182
+ if localized[l].key?(field)
183
+ return localized[l][field].map { |val| "#{l}/#{field}/#{val}" }
184
+ end
185
+ end
186
+
187
+ return nil
188
+ end
189
+
190
+ # Given the desired_locale, searches through the list of localized_data entries
191
+ # and finds those with keys which match either:
192
+ # * The desired locale exactly
193
+ # * The same language as the desired locale (but different region)
194
+ # * Any English language (so if the desired language is not there it will suffice)
195
+ #
196
+ # These will be sorted in order of preference:
197
+ # * Exact matches (language and region)
198
+ # * Language portion matches but region is absent/doesn't match.
199
+ # * en-US
200
+ # * en
201
+ # * en-*
202
+ #
203
+ # It is intentionally liberal in searching for either "_" or "-" to separate language
204
+ # and region, because they both mean (in different context) to split langugae on the
205
+ # left, and region on the right, and it is cheap to do so.
206
+ #
207
+ # @param [string] desired_locale
208
+ # @param [Hash] localized_data
209
+ # @return [Array]
210
+ def self.available_locales(desired_locale, localized_data)
211
+ parts = desired_locale.split(/[_-]/)
212
+ desired_lang = parts[0]
213
+
214
+ locales = localized_data.keys.select do |available_locale|
215
+ parts = available_locale.split(/[_-]/)
216
+ available_lang = parts[0]
217
+ available_lang == desired_lang || available_lang == 'en'
218
+ end
219
+
220
+ measure_locale_goodness = lambda do |locale|
221
+ parts = locale.split(/[_-]/)
222
+ lang = parts[0]
223
+ region = parts.length > 1 ? parts[1] : nil
224
+ if locale == desired_locale
225
+ return 1
226
+ elsif lang == desired_lang
227
+ return 2
228
+ elsif locale == 'en-US'
229
+ return 3
230
+ elsif lang == 'en' && region.nil?
231
+ return 4
232
+ elsif lang == 'en'
233
+ return 5
234
+ end
235
+ end
236
+
237
+ locales.sort do |a, b|
238
+ measure_locale_goodness.call(a) <=> measure_locale_goodness.call(b)
239
+ end
240
+ end
241
+
242
+ private
243
+
244
+ def field(name)
245
+ @app.key?(name) ? @app[name] : nil
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,75 @@
1
+ # F-Droid's Jekyll Plugin
2
+ #
3
+ # Copyright (C) 2017 Nico Alt
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'tmpdir'
19
+ require 'open-uri'
20
+ require 'net/http'
21
+ require 'json'
22
+ require 'zip'
23
+ require_relative './App'
24
+ require_relative './Repo'
25
+
26
+ module FDroid
27
+ class IndexV1
28
+ attr_reader :apps, :repo
29
+
30
+ @@downloaded_repos = {}
31
+
32
+ # Download and parse an index, returning a new instance of IndexV1.
33
+ # @param [string] repo
34
+ # @param [string] locale
35
+ # @return [FDroid::IndexV1]
36
+ def self.download(repo, locale)
37
+ repo = URI.parse "#{repo}/index-v1.jar"
38
+ index = download_index repo
39
+ IndexV1.new(JSON.parse(index), locale)
40
+ end
41
+
42
+ # Make a network request, download the index-v1.jar file from the repo, unzip and get the contents
43
+ # of the index-v1.json file.
44
+ # @param [string] repo
45
+ # @return [Hash]
46
+ def self.download_index(repo)
47
+ if @@downloaded_repos.has_key? repo
48
+ return @@downloaded_repos[repo]
49
+ end
50
+
51
+ Dir.mktmpdir do |dir|
52
+ jar = File.join dir, 'index-v1.jar'
53
+ open(jar, 'wb') do |file|
54
+ file.write(Net::HTTP.get(repo))
55
+ end
56
+
57
+ Zip::File.open(jar) do |zip_file|
58
+ entry = zip_file.glob('index-v1.json').first
59
+ @@downloaded_repos[repo] = entry.get_input_stream.read
60
+ next @@downloaded_repos[repo]
61
+ end
62
+ end
63
+ end
64
+
65
+ def initialize(index, locale)
66
+ @apps = index['apps'].map do |app_json|
67
+ packages_json = index['packages'][app_json['packageName']]
68
+ App.new(app_json, packages_json, locale)
69
+ end
70
+
71
+ @repo = Repo.new(index['repo'])
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,75 @@
1
+ # F-Droid's Jekyll Plugin
2
+ #
3
+ # Copyright (C) 2017 Nico Alt
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require_relative './Permission'
19
+
20
+ module FDroid
21
+ class Package
22
+ def initialize(package)
23
+ @package = package
24
+ end
25
+
26
+ def <=> (other)
27
+ self.version_code <=> other.version_code
28
+ end
29
+
30
+ def version_code
31
+ @package['versionCode']
32
+ end
33
+
34
+ def version_name
35
+ @package['versionName']
36
+ end
37
+
38
+ def to_data
39
+ added = nil
40
+ if @package['added'] != nil then
41
+ added = Date.strptime("#{@package['added'] / 1000}", '%s')
42
+ end
43
+
44
+ {
45
+ 'version_name' => version_name,
46
+ 'version_code' => version_code,
47
+ 'added' => added,
48
+ 'apk_name' => @package['apkName'],
49
+ 'hash' => @package['hash'],
50
+ 'hash_type' => @package['hashType'],
51
+ 'min_sdk_version' => @package['minSdkVersion'],
52
+ 'max_sdk_version' => @package['maxSdkVersion'],
53
+ 'target_sdk_version' => @package['targetSdkVersion'],
54
+ 'native_code' => @package['nativecode'],
55
+ 'sig' => @package['sig'],
56
+ 'size' => @package['size'],
57
+ 'uses_permission' => permission,
58
+ }
59
+ end
60
+
61
+ def permission
62
+ if @package['uses-permission'] == nil then
63
+ []
64
+ else
65
+ @package['uses-permission'].map {|perm| Permission.new(perm).to_data }
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def field(name)
72
+ @app.key?(name) ? name : nil
73
+ end
74
+ end
75
+ end
@@ -15,12 +15,18 @@
15
15
  # You should have received a copy of the GNU Affero General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
- require 'nokogiri'
19
- require 'open-uri'
18
+ module FDroid
19
+ class Permission
20
+ def initialize(permission)
21
+ @permission = permission[0]
22
+ @min_sdk = permission[1]
23
+ end
20
24
 
21
- class FDroidIndex
22
- def getIndex(repo)
23
- index = open(URI.parse(repo + '/index.xml')).read
24
- Nokogiri::XML(index).xpath('fdroid').xpath('application')
25
- end
26
- end
25
+ def to_data
26
+ {
27
+ 'permission' => @permission,
28
+ 'min_sdk' => @min_sdk,
29
+ }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,45 @@
1
+ # F-Droid's Jekyll Plugin
2
+ #
3
+ # Copyright (C) 2017 Peter Serwylo
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module FDroid
19
+ class Repo
20
+ def initialize(repo)
21
+ @repo = repo
22
+ end
23
+
24
+ def name
25
+ @repo['name']
26
+ end
27
+
28
+ def address
29
+ @repo['address']
30
+ end
31
+
32
+ def icon_url
33
+ "#{self.address}/icons/#{@repo['icon']}"
34
+ end
35
+
36
+ def description
37
+ @repo['description']
38
+ end
39
+
40
+ def date
41
+ added = Date.strptime("#{@repo['timestamp'] / 1000}", '%s')
42
+ end
43
+
44
+ end
45
+ end
@@ -15,7 +15,7 @@
15
15
  # You should have received a copy of the GNU Affero General Public License
16
16
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
- require "fdroid/FDroidIndex"
18
+ require "fdroid/IndexV1"
19
19
  require "jekyll/ReadYamlPage"
20
20
  require "jekyll/FDroidBrowsingPage"
21
21
  require "jekyll/FDroidFilters"
@@ -24,6 +24,7 @@ require "jekyll/FDroidLatestPackagesTag"
24
24
  require "jekyll/FDroidPackageDetailGenerator"
25
25
  require "jekyll/FDroidPackageDetailPage"
26
26
  require "jekyll/FDroidSearchAutocompleteTag"
27
+ require "jekyll/FDroidRepoInfoTag"
27
28
  require "lunr/LunrIndexer"
28
29
  require "lunr/SearchIndexFile"
29
30
  require "lunr/Javascript"
@@ -44,13 +44,13 @@ module Jekyll
44
44
  end
45
45
  site.config["pagination"]["enabled"] = true
46
46
 
47
- packages = FDroidIndex.new.getIndex(site.config["fdroid-repo"])
47
+ index = FDroid::IndexV1.download(site.config["fdroid-repo"], site.active_lang || 'en_US')
48
48
 
49
- Jekyll::LunrJsSearch::Indexer.new.generate(site, packages)
49
+ Jekyll::LunrJsSearch::Indexer.new.generate(site, index.apps)
50
50
 
51
51
  # Generate detail page for every package
52
52
  site.collections["packages"] = Collection.new(site, "packages")
53
- packages.each do |package|
53
+ index.apps.each do |package|
54
54
  # This page needs to be created twice, once for site.pages, and once for site.collections.
55
55
  # If not, then the i18n code in jekyll-polyglot will end up processing the page twice, as
56
56
  # it iterates over all pages and all packages. The end result is a double prefix for "/en/en"
@@ -17,112 +17,29 @@
17
17
 
18
18
  module Jekyll
19
19
 
20
- class FDroidPackageDetailPage < ReadYamlPage
21
- def initialize(site, base, package)
22
- $package = package
23
- @site = site
24
- @base = base
25
- @dir = "packages"
26
- @name = $package.at_xpath('id').content + "/index.html"
27
-
28
- self.process(@name)
29
- self.read_yaml(getLayoutDir(), 'package.html')
30
-
31
- getGeneralFrontMatterData
32
- getPackagesFrontMatterData
33
- end
34
-
35
- def getLayoutDir()
36
- layout_dir_override = File.join(site.source, "_layouts")
37
- if File.exists? File.join(layout_dir_override, "package.html")
38
- return layout_dir_override
39
- else
40
- return File.expand_path "../../_layouts", File.dirname(__FILE__)
41
- end
42
- end
43
-
44
- def getGeneralFrontMatterData
45
- # Hash with relation between Jekyll and XML variable name
46
- assignments = {
47
- "added" => "added",
48
- "antifeatures" => "antifeatures",
49
- "bitcoin" => "bitcoin",
50
- "categories" => "categories",
51
- "changelog" => "changelog",
52
- "description" => "desc",
53
- "donate" => "donate",
54
- "flattr" => "flattr",
55
- "icon" => "icon",
56
- "issueTracker" => "tracker",
57
- "lastUpdated" => "lastupdated",
58
- "license" => "license",
59
- "suggestedVersionCode" => "marketvercode",
60
- "suggestedVersion" => "marketversion",
61
- "package" => "id",
62
- "sourceCode" => "source",
63
- "summary" => "summary",
64
- "title" => "name",
65
- "webSite" => "web"
66
- }
67
- # Add information from XML to front matter
68
- assignments.each do |jekyll, xml|
69
- addGeneralFrontMatterData(jekyll, xml)
70
- end
71
- self.data["description"] = processFdroidAppLinks(self.data["description"])
72
- self.data["beautifulURL"] = "/packages/" + self.data["package"]
73
- end
74
-
75
- def processFdroidAppLinks(string)
76
- string.gsub(/fdroid\.app:([\w._]*)/, '/packages/\1')
77
- end
78
-
79
- def addGeneralFrontMatterData(jekyll, xml)
80
- xmlData = $package.at_xpath(xml)
81
- if xmlData != nil
82
- self.data[jekyll] = xmlData.content
83
- end
84
- end
85
-
86
- # Hash with relation between Jekyll and XML variable names for the package metadata
87
- @@jekyllToXmlPackageAssignments = {
88
- "added" => "added",
89
- "apkName" => "apkname",
90
- "hash" => "hash",
91
- "nativeCode" => "nativecode",
92
- "maxSDKVersion" => "maxsdkver",
93
- "permissions" => "permissions",
94
- "sdkVersion" => "sdkver",
95
- "sig" => "sig",
96
- "size" => "size",
97
- "srcName" => "srcname",
98
- "targetSdkVersion" => "targetSdkVersion",
99
- "version" => "version",
100
- "versionCode" => "versioncode",
101
- }
102
-
103
- def getPackagesFrontMatterData
104
- self.data["packages"] = $package.xpath('package').map { |package| getPackageFromXml(package) }
105
- end
106
-
107
- def getPackageFromXml(packageXml)
108
- packageInformation = Hash.new
109
-
110
- # Add information from XML to front matter
111
- @@jekyllToXmlPackageAssignments.each do |jekyll, xml|
112
- xmlData = packageXml.at_xpath(xml)
113
- if xmlData == nil
114
- next
115
- end
116
-
117
- # nativeCode and permissions can be comma separated arrays
118
- if ["nativeCode", "permissions"].include? jekyll
119
- packageInformation[jekyll] = xmlData.content.split(",")
120
- else
121
- packageInformation[jekyll] = xmlData.content
122
- end
123
- end
124
-
125
- return packageInformation
126
- end
127
- end
20
+ class FDroidPackageDetailPage < ReadYamlPage
21
+
22
+ # @param [Jekyll::Site] site
23
+ # @param [string] base
24
+ # @param [FDfroid::App] package
25
+ def initialize(site, base, package)
26
+ @site = site
27
+ @base = base
28
+ @dir = 'packages'
29
+ @name = "#{package.package_name}/index.html"
30
+
31
+ self.process(@name)
32
+ self.read_yaml(get_layout_dir, 'package.html')
33
+ self.data.update(package.to_data)
34
+ end
35
+
36
+ def get_layout_dir()
37
+ layout_dir_override = File.join(site.source, '_layouts')
38
+ if File.exists? File.join(layout_dir_override, 'package.html')
39
+ layout_dir_override
40
+ else
41
+ File.expand_path '../../_layouts', File.dirname(__FILE__)
42
+ end
43
+ end
44
+ end
128
45
  end
@@ -0,0 +1,39 @@
1
+ # F-Droid's Jekyll Plugin
2
+ #
3
+ # Copyright (C) 2017 Peter Serwylo
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require_relative '../fdroid/IndexV1'
19
+
20
+ module Jekyll
21
+
22
+ # Used to output the repo name/timestamp used to generate this F-Droid site.
23
+ class FDroidRepoInfoTag < Liquid::Tag
24
+
25
+ def initialize(tag_name, text, tokens)
26
+ super
27
+ end
28
+
29
+ def render(context)
30
+ site = context.registers[:site]
31
+ url = site.config['fdroid-repo']
32
+ index = FDroid::IndexV1.download(url, 'en')
33
+
34
+ "#{index.repo.name} #{index.repo.date}"
35
+ end
36
+ end
37
+ end
38
+
39
+ Liquid::Template.register_tag('fdroid_repo_info', Jekyll::FDroidRepoInfoTag)
@@ -17,29 +17,76 @@
17
17
 
18
18
  module Jekyll
19
19
 
20
- class FDroidSearchTemplateableAutocompleteBlock < Liquid::Block
21
- def self.render_template(context, template)
22
- context['result_item_template'] = template
20
+ class SearchForm
21
+ def self.render_form(context, search_form_template_path, result_item_template_contents)
22
+ context['result_item_template'] = result_item_template_contents
23
23
  context['search_id'] = rand(1000000)
24
24
 
25
- path = "../../_layouts/search-autocomplete.html"
26
- template = Liquid::Template.parse(IO.read((File.expand_path path, File.dirname(__FILE__))))
25
+ template = Liquid::Template.parse(IO.read((File.expand_path( search_form_template_path, File.dirname(__FILE__)))))
27
26
  template.render(context)
28
27
  end
28
+ end
29
+
30
+ # As the user types, a list of results is shown below the text input (floating above other content).
31
+ # When an item is selected, it will navigate to that packages page.
32
+ # Designed to be used in a sidebar widget.
33
+
34
+ class DropDownWithTemplate < Liquid::Block
35
+ def render(context)
36
+ search_form_template_path = "../../_layouts/search-autocomplete.html"
37
+ SearchForm.render_form(context, search_form_template_path, super.to_s)
38
+ end
39
+ end
40
+
41
+ class DefaultDropDown < Liquid::Tag
42
+ def render(context)
43
+ search_form_template_path = "../../_layouts/search-autocomplete.html"
44
+
45
+ result_item_template_path = "../../_includes/search-autocomplete-default-result-template.html"
46
+ result_item_template = IO.read((File.expand_path(result_item_template_path, File.dirname(__FILE__))))
47
+
48
+ SearchForm.render_form(context, search_form_template_path, result_item_template)
49
+ end
50
+ end
51
+
52
+ # As the user types, a div is populated with search results.
53
+ # Differs from DropDownAutocomplete in that once you move focus away from the text input, the results
54
+ # are still displayed.
55
+ # Designed for a fully fledged search form on its own page.
29
56
 
57
+ # For each result result, this will render the template found between
58
+ # the {% fdroid_search_full_with_template %}{% endfdroid_search_full_with_template %} tags.
59
+ class FullSearchWithTemplate < Liquid::Block
30
60
  def render(context)
31
- FDroidSearchTemplateableAutocompleteBlock.render_template(context, super.to_s)
61
+ search_form_template_path = "../../_layouts/search-full.html"
62
+ SearchForm.render_form(context, search_form_template_path, super.to_s)
63
+ end
64
+ end
65
+
66
+ # For each search result, this will render the contents of
67
+ # "_includes/search-full-default-result-template.html" from this plugin.
68
+ class DefaultFullSearch < Liquid::Tag
69
+ def initialize(tag_name, argument, tokens)
70
+ super
71
+ @empty_search_id = argument.strip
32
72
  end
33
- end
34
73
 
35
- class FDroidSearchAutocompleteTag < Liquid::Tag
36
74
  def render(context)
37
- path = "../../_includes/search-autocomplete-default-result-template.html"
38
- result_item_template = IO.read((File.expand_path path, File.dirname(__FILE__)))
39
- FDroidSearchTemplateableAutocompleteBlock.render_template(context, result_item_template)
75
+ search_form_template_path = "../../_layouts/search-full.html"
76
+
77
+ result_item_template_path = "../../_includes/search-full-default-result-template.html"
78
+ result_item_template = IO.read((File.expand_path(result_item_template_path, File.dirname(__FILE__))))
79
+
80
+ context['empty_search_id'] = @empty_search_id
81
+ SearchForm.render_form(context, search_form_template_path, result_item_template)
40
82
  end
41
83
  end
42
84
  end
43
85
 
44
- Liquid::Template.register_tag('fdroid_search_autocomplete', Jekyll::FDroidSearchAutocompleteTag)
45
- Liquid::Template.register_tag('fdroid_search_autocomplete_with_template', Jekyll::FDroidSearchTemplateableAutocompleteBlock)
86
+ Liquid::Template.register_tag('fdroid_search_autocomplete', Jekyll::DefaultDropDown)
87
+ Liquid::Template.register_tag('fdroid_search_autocomplete_with_template', Jekyll::DropDownWithTemplate)
88
+
89
+ # You can optionally specify the ID of a div where the results are to be rendered as the argument to these tags.
90
+ # Note that if you do so, it will hide all elements from this div when rendering it.
91
+ Liquid::Template.register_tag('fdroid_search_full', Jekyll::DefaultFullSearch)
92
+ Liquid::Template.register_tag('fdroid_search_full_with_template', Jekyll::FullSearchWithTemplate)
@@ -13,97 +13,90 @@ require 'uri'
13
13
  require 'v8'
14
14
 
15
15
  module Jekyll
16
- module LunrJsSearch
17
- class Indexer
18
- def generate(site, packages)
19
- @js_dir = 'js'
20
-
21
- ctx = V8::Context.new
22
- ctx.load(Indexer.path_to_asset('bower_components/lunr.js/lunr.js'))
23
-
24
- ctx['indexer'] = proc do |this|
25
- this.ref('id')
26
- this.field('name')
27
- this.field('summary')
28
- end
29
-
30
- ctx.eval('builder = new lunr.Builder')
31
- ctx.eval('builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter)')
32
- ctx.eval('indexer.call(builder, builder)')
33
-
34
- @lunr_version = ctx.eval('lunr.version')
35
- @docs = {}
36
-
37
- Jekyll.logger.info "Lunr:", "Creating search index (lunr.js version #{@lunr_version})..."
38
-
39
- @site = site
40
-
41
- packages.each_with_index do |package, i|
42
- package_name = package['id']
43
- name = Indexer.content_from_xml(package, 'name')
44
- icon = Indexer.content_from_xml(package, 'icon')
45
- summary = Indexer.content_from_xml(package, 'summary')
46
-
47
- doc = {
48
- 'id' => i,
49
- 'packageName' => package_name,
50
- 'icon' => icon,
51
- 'name' => name,
52
- 'summary' => summary
53
- }
54
-
55
- ctx['builder'].add(doc)
56
- @docs[i] = doc
57
-
58
- Jekyll.logger.debug "Lunr:", package_name
59
- end
60
-
61
- @index = ctx.eval('builder.build()')
62
-
63
- FileUtils.mkdir_p(File.join(site.dest, @js_dir))
64
- filename = File.join(@js_dir, 'index.json')
65
-
66
- total = {
67
- "docs" => @docs,
68
- "index" => @index.to_hash
69
- }
70
-
71
- filepath = File.join(site.dest, filename)
72
- File.open(filepath, "w") { |f| f.write(JSON.dump(total)) }
73
- Jekyll.logger.info "Lunr:", "Index ready (lunr.js v#{@lunr_version})"
74
- added_files = [filename]
75
-
76
- site_js = File.join(site.dest, @js_dir)
77
- extras = [
78
- 'assets/fdroid-search-autocomplete.js',
79
- 'bower_components/lunr.js/lunr.js',
80
- 'bower_components/mustache.js/mustache.min.js',
81
- 'bower_components/awesomplete/awesomplete.min.js',
82
- 'bower_components/awesomplete/awesomplete.css'
83
- ]
84
- Jekyll.logger.info "Lunr:", "Added required assets to #{@js_dir}"
85
- extras.each do |path|
86
- src = Indexer.path_to_asset(path)
87
- Jekyll.logger.debug "Lunr:", "Copying asset from #{src} to #{site_js}"
88
- FileUtils.cp(src, site_js)
89
- added_files.push(File.join(@js_dir, File.basename(src)))
90
- end
91
-
92
- # Keep the written files from being cleaned by Jekyll
93
- added_files.each do |filename|
94
- site.static_files << SearchIndexFile.new(site, site.dest, "/", filename)
95
- end
96
- end
97
-
98
- def self.path_to_asset(path)
99
- return File.join(File.dirname(__FILE__), "../../#{path}")
100
- end
101
-
102
- def self.content_from_xml(xml_node, element_name)
103
- xml_data = xml_node.at_xpath(element_name)
104
- return xml_data == nil ? nil : xml_data.content
105
- end
106
-
107
- end
108
- end
16
+ module LunrJsSearch
17
+ class Indexer
18
+
19
+ # @param [Jekyll::Site] site
20
+ # @param [Array<App>] packages
21
+ # @return [Object]
22
+ def generate(site, packages)
23
+ @js_dir = 'js'
24
+
25
+ ctx = V8::Context.new
26
+ ctx.load(Indexer.path_to_asset('bower_components/lunr.js/lunr.js'))
27
+
28
+ ctx['indexer'] = proc do |this|
29
+ this.ref('id')
30
+ this.field('name')
31
+ this.field('summary')
32
+ end
33
+
34
+ ctx.eval('builder = new lunr.Builder')
35
+ ctx.eval('builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter)')
36
+ ctx.eval('indexer.call(builder, builder)')
37
+
38
+ @lunr_version = ctx.eval('lunr.version')
39
+ @docs = {}
40
+
41
+ Jekyll.logger.info "Lunr:", "Creating search index (lunr.js version #{@lunr_version})..."
42
+
43
+ @site = site
44
+
45
+ packages.each_with_index do |package, i|
46
+ doc = {
47
+ 'id' => i,
48
+ 'packageName' => package.package_name,
49
+ 'icon' => package.icon,
50
+ 'name' => package.name,
51
+ 'summary' => package.summary
52
+ }
53
+
54
+ ctx['builder'].add(doc)
55
+ @docs[i] = doc
56
+
57
+ Jekyll.logger.debug "Lunr:", package.package_name
58
+ end
59
+
60
+ @index = ctx.eval('builder.build()')
61
+
62
+ FileUtils.mkdir_p(File.join(site.dest, @js_dir))
63
+ filename = File.join(@js_dir, 'index.json')
64
+
65
+ total = {
66
+ "docs" => @docs,
67
+ "index" => @index.to_hash
68
+ }
69
+
70
+ filepath = File.join(site.dest, filename)
71
+ File.open(filepath, "w") {|f| f.write(JSON.dump(total))}
72
+ Jekyll.logger.info "Lunr:", "Index ready (lunr.js v#{@lunr_version})"
73
+ added_files = [filename]
74
+
75
+ site_js = File.join(site.dest, @js_dir)
76
+ extras = [
77
+ 'assets/fdroid-search-autocomplete.js',
78
+ 'bower_components/lunr.js/lunr.js',
79
+ 'bower_components/mustache.js/mustache.min.js',
80
+ 'bower_components/awesomplete/awesomplete.min.js',
81
+ 'bower_components/awesomplete/awesomplete.css'
82
+ ]
83
+ Jekyll.logger.info "Lunr:", "Added required assets to #{@js_dir}"
84
+ extras.each do |path|
85
+ src = Indexer.path_to_asset(path)
86
+ Jekyll.logger.debug "Lunr:", "Copying asset from #{src} to #{site_js}"
87
+ FileUtils.cp(src, site_js)
88
+ added_files.push(File.join(@js_dir, File.basename(src)))
89
+ end
90
+
91
+ # Keep the written files from being cleaned by Jekyll
92
+ added_files.each do |filename|
93
+ site.static_files << SearchIndexFile.new(site, site.dest, "/", filename)
94
+ end
95
+ end
96
+
97
+ def self.path_to_asset(path)
98
+ return File.join(File.dirname(__FILE__), "../../#{path}")
99
+ end
100
+ end
101
+ end
109
102
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-fdroid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nico Alt
@@ -11,7 +11,7 @@ cert_chain: []
11
11
  date: 2017-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: nokogiri
14
+ name: jekyll-include-cache
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,35 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: jekyll-include-cache
28
+ name: jekyll-paginate-v2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "<="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.7.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "<="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.7.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: therubyracer
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.12'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubyzip
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - ">="
@@ -39,40 +67,44 @@ dependencies:
39
67
  - !ruby/object:Gem::Version
40
68
  version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
- name: jekyll-paginate-v2
70
+ name: json
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
- - - "<="
73
+ - - ">="
46
74
  - !ruby/object:Gem::Version
47
- version: 1.7.3
75
+ version: 1.8.5
48
76
  type: :runtime
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
- - - "<="
80
+ - - ">="
53
81
  - !ruby/object:Gem::Version
54
- version: 1.7.3
82
+ version: 1.8.5
55
83
  - !ruby/object:Gem::Dependency
56
- name: therubyracer
84
+ name: rspec
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
- - - "~>"
87
+ - - ">="
60
88
  - !ruby/object:Gem::Version
61
- version: '0.12'
62
- type: :runtime
89
+ version: '0'
90
+ type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
- - - "~>"
94
+ - - ">="
67
95
  - !ruby/object:Gem::Version
68
- version: '0.12'
96
+ version: '0'
69
97
  description: Browse packages of a F-Droid repository.
70
98
  email: nicoalt@posteo.org
71
99
  executables: []
72
100
  extensions: []
73
101
  extra_rdoc_files: []
74
102
  files:
75
- - lib/fdroid/FDroidIndex.rb
103
+ - lib/fdroid/App.rb
104
+ - lib/fdroid/IndexV1.rb
105
+ - lib/fdroid/Package.rb
106
+ - lib/fdroid/Permission.rb
107
+ - lib/fdroid/Repo.rb
76
108
  - lib/jekyll-fdroid.rb
77
109
  - lib/jekyll/FDroidBrowsingPage.rb
78
110
  - lib/jekyll/FDroidFilters.rb
@@ -80,6 +112,7 @@ files:
80
112
  - lib/jekyll/FDroidLatestPackagesTag.rb
81
113
  - lib/jekyll/FDroidPackageDetailGenerator.rb
82
114
  - lib/jekyll/FDroidPackageDetailPage.rb
115
+ - lib/jekyll/FDroidRepoInfoTag.rb
83
116
  - lib/jekyll/FDroidSearchAutocompleteTag.rb
84
117
  - lib/jekyll/ReadYamlPage.rb
85
118
  - lib/lunr/Javascript.rb