Commit d6b8d7ae authored by Hans-Christoph Steiner's avatar Hans-Christoph Steiner

Merge branch 'debian-buster-update' into 'master'

Debian/buster update

Closes #52

See merge request !49
parents 920a12bd 54869ce5
Pipeline #127169654 passed with stage
in 2 minutes and 44 seconds
image: ruby:2.3
stages:
- test
test:
stage: test
ruby_latest:
image: ruby
script:
- ruby -v
- for f in `find * -name \*.rb`; do printf "$f\t"; ruby -c $f; done
- apt-get update
- apt-get install -y bundler
- bundle install --path vendor
- bundle config set path 'vendor'
- bundle install
- bundle exec rubocop lib spec
- bundle exec rspec
debian_buster:
image: debian:buster
variables:
DEBIAN_FRONTEND: noninteractive
LANG: C.UTF-8
script:
- echo 'quiet "1";' \
'APT::Install-Recommends "0";'
'APT::Install-Suggests "0";'
'APT::Acquire::Retries "20";'
'APT::Get::Assume-Yes "true";'
'Dpkg::Use-Pty "0";'
> /etc/apt/apt.conf.d/99gitlab
- echo "deb http://deb.debian.org/debian/ testing main" >> /etc/apt/sources.list
- printf "Package\x3a *\nPin\x3a release a=testing\nPin-Priority\x3a 100\n\nPackage\x3a ruby-jekyll-include-cache ruby-jekyll-paginate-v2\nPin\x3a release a=testing\nPin-Priority\x3a 500\n" > /etc/apt/preferences.d/debian-testing.pref
- apt-get update
- apt-get dist-upgrade
- apt-get install
rubocop
ruby-jekyll-include-cache
ruby-jekyll-paginate-v2
ruby-json
ruby-loofah
ruby-rspec
ruby-zip
- rubocop lib spec
- rspec
......@@ -4,3 +4,6 @@ AllCops:
Layout:
Enabled: true
Layout/LineLength:
Max: 600
\ No newline at end of file
---
liberapay: F-Droid-Data
github:
- eighthave
tidelift: rubygems/jekyll-fdroid
custom:
- https://f-droid.org/about/
- https://www.hellotux.com/f-droid
- https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E2FCXCT6837GL
......@@ -28,26 +28,6 @@ use the following tags in your page:
{% fdroid_show_last_updated_packages %}
```
To add a search input which lets the user search all packages, use the following tags in your page.
The first renders a default list item for each search result:
```
{{ fdroid_search_autocomplete }}
```
And this allows for a custom [Mustache.js](https://github.com/janl/mustache.js) template for each search result:
```
{{ fdroid_search_autocomplete_with_template }}
Mustache.js template goes in here, and has access
to the following tags:
* {{ name }}
* {{ summary }}
* {{ icon }}
* {{ packageName }}
{{ endfdroid_search_autocomplete_with_template }}
```
## Running Tests
To run the test suite, you must first have installed the releveant dependencies:
......
---
layout: default
---
{% assign strings = site.data.strings.package %}
<article class="package">
{% if page.feature_graphic %}
<img
class="feature-graphic"
src="{{ site.fdroid-repo}}/{{ page.package_name }}/{{ page.feature_graphic }}" />
{% endif %}
<header class="package-header">
<img class="package-icon" src="{{ site.fdroid-repo }}/{{ page.icon }}" />
<div class="package-title">
<h3 class="package-name">
{{ page.title }}
</h3>
<div class="package-summary">
{{ page.summary }}
</div>
</div>
</header>
<div class="package-description">
{{ page.description }}
</div>
<ul class="package-links">
<li class="package-link">
{{ strings.license }}:
<a href="{{ page.license|get_license_url }}">{{ page.license|get_license_name }}</a>
</li>
{% if page.website != "" and page.website != nil %}
<li class="package-link">
<a href="{{ page.website }}">{{ strings.website }}</a>
</li>
{% endif %}
{% if page.issue_tracker != "" and page.issue_tracker != nil %}
<li class="package-link">
<a href="{{ page.issue_tracker }}">{{ strings.issue_tracker }}</a>
</li>
{% endif %}
{% if page.source_code != "" and page.source_code != nil %}
<li class="package-link">
<a href="{{ page.source_code }}">{{ strings.source_code }}</a>
</li>
{% endif %}
{% if page.changelog != "" and page.changelog != nil %}
<li class="package-link">
<a href="{{ page.changelog }}">{{ strings.changelog }}</a>
</li>
{% endif %}
{% if page.donate != "" and page.donate != nil %}
<li class="package-link">
<a href="{{ page.donate }}">{{ strings.donate }}</a>
</li>
{% endif %}
{% if page.flattrID != "" and page.flattrID != nil %}
<li class="package-link">
<a href="https://flattr/thing/{{ page.flattrID }}">{{ strings.flattr }}</a>
</li>
{% endif %}
<li class="package-link">
<a href="https://f-droid.org/wiki/page/{{page.package}}">{{ strings.technical_info }}</a>
</li>
</ul>
{% if page.phone_screenshots %}
<div id="screenshots" class="screenshots">
<ul class="gallery">
{% for screenshot in page.phone_screenshots %}
<li class="screenshot"><img src="{{ site.fdroid-repo}}/{{ page.package_name }}/{{ screenshot }}" /></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="package-versions">
<h3>{{ strings.packages }}</h3>
<p>
{{ strings.suggest_downloading_via_fdroid }}
</p>
<a href="https://f-droid.org/FDroid.apk" class="material-button">{{ site.data.strings.index.download_fdroid }}</a>
<ul class="package-versions-list">
{% for package in page.packages %}
<li class="package-version">
<div class="package-version-header">
{% comment %}
Use YYYY-MM-DD format, because it is more internationalizable than versions which include English month names.
{% endcomment %}
{% assign package_added_date = package.added | date: '%F' %}
<a name="{{ package.version_name }}"></a>
<a name="{{ package.version_code }}"></a>
{{ strings.version_info | replace: '[package_version]', package.version_name | replace: '[package_version_code]', package.version_code | replace: '[package_added_date]', package_added_date }}
</div>
<p class="package-version-requirement">
{% assign android_version = package.min_sdk_version | android_sdk_level_to_version %}
{{ strings.minimum_android_version | replace: '[android_version]', android_version }}
</p>
<p class="package-version-download">
<a href="{{ site.fdroid-repo }}/{{ package.apk_name }}">{{ strings.download_apk }}</a> {{ package.size|file_size_human_readable }}
<a href="{{ site.fdroid-repo }}/{{ package.apk_name }}.asc">{{ strings.gpg_signature }}</a>
</p>
<div class="package-version-permissions">
<h4>{{ strings.permissions }}</h4>
<ul class="package-version-permissions-list">
{% for permission in package.uses_permission %}
<li>
{{ permission.permission }}
{% if permission.min_sdk %}
({{ permission.min_sdk | android_sdk_level_to_version }})
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</li>
{% endfor %}
</ul>
</div>
</article>
<script src="{{ site.baseurl }}/js/lunr.js"></script>
<script src="{{ site.baseurl }}/js/mustache.min.js"></script>
<script src="{{ site.baseurl }}/js/awesomplete.min.js"></script>
<script src="{{ site.baseurl }}/js/fdroid-search-autocomplete.js"></script>
<link href="{{ site.baseurl }}/js/awesomplete.css" rel="stylesheet" type="text/css" />
<script id="search-result-template-{{ search_id }}" type="x-tmpl-mustache">
{{ result_item_template }}
</script>
<div
class="search-input-wrapper"
data-search-id="{{ search_id }}"
data-baseurl="{{ site.baseurl }}"
data-fdroid-repo="{{ site.fdroid-repo }}"
data-repo-timestamp="{{ repo_timestamp }}"></div>
<script src="{{ site.baseurl }}/js/fdroid-search-autocomplete-init.js"></script>
<script src="{{ site.baseurl }}/js/lunr.js"></script>
<script src="{{ site.baseurl }}/js/mustache.min.js"></script>
<script src="{{ site.baseurl }}/js/awesomplete.min.js"></script>
<script src="{{ site.baseurl }}/js/fdroid-search-autocomplete.js"></script>
<script src="{{ site.baseurl }}/js/register-listener.js"></script>
<link href="{{ site.baseurl }}/js/awesomplete.css" rel="stylesheet" type="text/css" />
<script id="search-result-template-{{ search_id }}" type="x-tmpl-mustache">
{{ result_item_template }}
</script>
<div id="search-input-{{ search_id }}" class="search-widget"></div>
......@@ -13,8 +13,6 @@ pagination:
title: ':title'
---
{% fdroid_search_full full-package-list %}
<div id="full-package-list">
{% for package in paginator.posts %}
{% include package-list-item.html package=package %}
......
//@license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3
(function() {
var elements = document.getElementsByClassName('search-input-wrapper');
for (var i = 0; i < elements.length; i ++) {
var element = elements[i];
var searchId = element.getAttribute('data-search-id');
var baseurl = element.getAttribute('data-baseurl');
var fdroidRepo = element.getAttribute('data-fdroid-repo');
var repoTimestamp = element.getAttribute('data-repo-timestamp');
FDroid.Search.addAutocomplete(
element,
document.getElementById('search-result-template-' + searchId),
baseurl,
fdroidRepo,
repoTimestamp
);
}
})();
// @license-end
//@license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3
(function() {
/**
* Loads an index.json file built by jekyll-fdroid via AJAX. Once loaded, it is passed to buildIndex()
* which will construct the search index and also add the search widget to the DOM.
*
* Note that both the autocomplete sidebar and also the full search widget both require index
* data to operate. This caches the results in the `indexData` variable, and ensures that the
* actual network call is only performed once.
*/
function loadIndex(config) {
var existingData = window.FDroid.Search.Net.indexData;
if (existingData !== null) {
buildIndex(config, existingData.docs, existingData.index);
return;
}
window.FDroid.Search.Net.indexLoadedCallbacks.push(function(docs, index) {
buildIndex(config, docs, index);
});
if (window.FDroid.Search.Net.isLoadingIndex) {
return;
}
window.FDroid.Search.Net.isLoadingIndex = true;
var http = new XMLHttpRequest();
http.onreadystatechange = function () {
if (http.readyState === XMLHttpRequest.DONE && http.status === 200) {
var data = JSON.parse(http.responseText);
for (var i = 0; i < window.FDroid.Search.Net.indexLoadedCallbacks.length; i ++) {
window.FDroid.Search.Net.indexLoadedCallbacks[i](data.docs, data.index);
}
window.FDroid.Search.Net.indexData = data;
window.FDroid.Search.Net.isLoadingIndex = false;
}
};
http.open('GET', config.baseurl + '/js/index.json?timestamp=' + config.repoTimestamp, true);
http.send();
}
/**
* Iterate over the packages provided, and for each ensure the name + summary is indexed. Theoretically we could
* of course index the description too, but given the rate at which this would increase the index size, it is to
* be avoided. Note that lunr.js allows us to prebuild the index to save time, but this may not be a good idea
* because:
* * We still need the original documents, and so we will end up making two web requests (one for documents
* and one for the index).
*/
function buildIndex(config, packages, index) {
for (var packageId in packages) {
if (packages.hasOwnProperty(packageId)) {
var pkg = packages[packageId];
pkg.icon_url = config.fdroidRepo + '/' + pkg.icon;
}
}
index = lunr.Index.load(index);
config.onLoad(config, packages, index)
}
/**
* Anything after the "#q=" in the URL is used for the intial search terms when showing a full search.
* The reason for #q= is to be forwards compatible with an arbitrary number of arguments in the future,
* such as "#q=F-Droid&page=2"
*/
function getInitialSearchTerms() {
return window.location.hash.substring(3)
}
/**
* Helper method to ensure that the search input for both full search and autocomplete are similar.
*/
function createSearchInput() {
var searchInput = document.createElement('input');
searchInput.type = "text";
searchInput.className = "search-input";
return searchInput
}
function handleFullSearchResults(config, packages, index) {
var searchInput = createSearchInput();
searchInput.value = getInitialSearchTerms();
config.element.appendChild(searchInput);
var resultsContainer = document.createElement('ul')
resultsContainer.className = "results";
config.element.appendChild(resultsContainer);
var emptySearchElement = config.emptySearchElement;
var defaultEmptySearchElementDisplay = emptySearchElement == null ? null : emptySearchElement.style.display;
var showResults = function() {
// Use loop instead of innerHTML = '' for performance (https://stackoverflow.com/a/3955238)
while (resultsContainer.firstChild) {
resultsContainer.removeChild(resultsContainer.firstChild);
}
var results = performSearch(index, packages, searchInput.value);
if (results == null) {
if (emptySearchElement != null) {
emptySearchElement.style.display = defaultEmptySearchElementDisplay;
}
} else {
emptySearchElement.style.display = 'none';
results.forEach(function(item) {
var package = packages[item.ref];
var node = document.createElement('li');
node.innerHTML = Mustache.render(config.template, package);
resultsContainer.appendChild(node);
})
}
}
// "Debounce" search results so that rapid key presses in succession don't cause a DOM
// rebuild immediately. Rather, we will wait 300ms of no typing before we go ahead and
// rebuild the DOM (which has a non-zero cost).
searchInput.addEventListener('input', debounce(function(event) {
// Will trigger "window.onhashchange", which will in turn cause a search to be run.
window.location.hash = (searchInput.value.length == 0) ? "" : "q=" + searchInput.value;
}, 300));
// If the search results are prepopulated, show relevant results
if (searchInput.value != null && searchInput.value.length > 0) {
showResults()
}
// The sidebar can trigger the has to change if it is used on the full search page.
// Under these circumstances, we should show the relevant results.
window.onhashchange = function(event) {
searchInput.value = getInitialSearchTerms();
showResults();
};
}
function viewPackagePage(config, packageName) {
document.location = config.baseurl + '/packages/' + packageName + '/';
}
/**
* Construct the Awesomplete based on the a dynamically created input element. It is configured to render the Mustache
* template available in the #search-result-template script element.
* @returns {Awesomplete}
*/
function setupAutocompleteSearch(config) {
var searchInput = createSearchInput();
config.element.appendChild(searchInput);
var autocomplete = new Awesomplete(searchInput, {
maxItems: 10,
filter: function() { return true; }, // Don't filter, this is done by lunr.js already.
item: function(item) {
var node = document.createElement('li');
node.innerHTML = Mustache.render(config.template, item.value);
return node;
},
replace: function(item) {
// This isn't actualy required, because we redirect the browser once the user selects an option.
// However without it, there will be a brief period while loading the next page that it looks weird.
this.input.value = item.value.name;
}
});
searchInput.addEventListener('awesomplete-selectcomplete', function(event) {
viewPackagePage(config, event.text.value.packageName);
});
searchInput.onkeypress = function(event) {
if (event.keyCode == 13) {
var path = config.baseurl + '/packages'
if (location.pathname.substr(0, path.length) == path) {
location.hash = "q=" + searchInput.value;
autocomplete.close();
} else {
document.location = path + '#q=' + searchInput.value;
}
}
};
return autocomplete;
}
function handleAutocompleteResults(config, packages, index) {
var autocomplete = setupAutocompleteSearch(config);
autocomplete.input.oninput = function() {
var results = performSearch(index, packages, autocomplete.input.value);
if (results !== null) {
autocomplete.list = results.map(function(item) {
return packages[item.ref];
});
}
};
}
/**
* Executed each time the user enteres some new text in the search input.
* Uses the lunr.js index to perform a search. The search which is performed is a wildcard search.
* @param {lunr.Index} index
* @param {Object[]} packages
* @param {string} terms
* @return {Object[]|null} Will return null if a the search terms is less than three characters, otherwise an array of results.
*/
function performSearch(index, packages, terms) {
// For performance reasons, don't try and search with less than three characters. There is a noticibly longer
// search time when searching for 1 or 2 string terms.
if (terms == null || terms.length < 3) {
return null;
}
return index.search(terms + "*")
}
// http://beeker.io/jquery-document-ready-equivalent-vanilla-javascript
var domReady = function(callback) {
document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
};
// MIT licensed debounce from https://github.com/jgarber623/javascript-debounce.
// Used to prevent every single keystroke from a fast typer requiring us to rebuild
// the DOM of the search results. Instead, it can happen after a pause in typing.
var debounce = function(callback, delay) {
var timeout;
return function() {
var context = this,
args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
callback.apply(context, args);
}, delay);
};
};
window.FDroid = window.FDroid || {};
/* The `Net` object is to keep track of the network request for index data,