diff --git a/.gitignore b/.gitignore index 13d1490..d33cb5b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ __pycache__/ *.py[cod] *$py.class +output/ +Output/ + # C extensions *.so diff --git a/content/firstbatch.md b/content/firstbatch.md new file mode 100644 index 0000000..254e09c --- /dev/null +++ b/content/firstbatch.md @@ -0,0 +1,19 @@ +Title: First batch +Date: 2019-05-14 +Category: News +Summary: On the 5th of April 2019 we brewed the first Multilaterale [brew]({filename}redale.md) at the [Noord-Hollandse Bierbrouwerij](http://www.dnhbb.nl/dnhbb/). + +On the 5th of April 2019 we brewed the first Multilaterale [brew]({filename}redale.md) at the [Noord-Hollandse Bierbrouwerij](http://www.dnhbb.nl/dnhbb/). + +![]({attach}images/bottling.jpeg) + +We bottled 3528 bottles on the 14th of May. + +![]({attach}images/delivery.jpeg) + +We received this on the 29th of May. + +The first batch also featured a special design for Goethe Institut Niederlande. + +![]({attach}images/redale.jpg) +![]({attach}images/goethe.jpeg) \ No newline at end of file diff --git a/content/goethecollab.md b/content/goethecollab.md new file mode 100644 index 0000000..a5fbe5d --- /dev/null +++ b/content/goethecollab.md @@ -0,0 +1,16 @@ +Title: Ongoing specials for Goethe-Institut NL +Date: 2020-02-11 +Category: News +Summary: This is already our second special edition made for the Goethe-Institut Netherlands. + +For every batch that we brew we ask our stakeholders if they are interested to have their own label for the upcoming batch. This is already the second time we worked together with Goethe-Institut Netherlands to design and produce their own label!. + +## GI Pale Ale +![]({attach}/images/goethe_paleale.jpeg) + +## GI Red Ale +![]({attach}/images/goethe.jpeg) + +## Interested? + +If you are intersted in doing the same, get in touch! diff --git a/content/goldendawn.md b/content/goldendawn.md new file mode 100644 index 0000000..274f84a --- /dev/null +++ b/content/goldendawn.md @@ -0,0 +1,17 @@ +Title: Golden Dawn +Date: 2020-11-03 +Category: Brews +Image: images/web96_igfm_1.jpg +Summary: Golden Dawn for Murder Capital + +A Golden Ale made for [Intergalactic.fm](https://intergalactic.fm). Fresh with a moderate bitterness. Hopped with Magnum and Ariana. + +![]({static}images/web96_igfm_1.jpg) + +###Data + +* IBU: 21 +* EBC: 9 +* ABV: 4.1% +* TOP fermentation + diff --git a/content/grooscollab.md b/content/grooscollab.md new file mode 100644 index 0000000..6f127ea --- /dev/null +++ b/content/grooscollab.md @@ -0,0 +1,18 @@ +Title: Irish Stout +Date: 2020-07-29 +Category: Brews +Summary: Irish Stout in collaboration with Brouwerij Groos +Image: images/Stout.jpg + +This Irish Stout came to be in collaboration with [Brouwerij Groos](https://untappd.com/BrouwerijGroos). It has a souple body, roasty notes of coffee and chocolate. Already sold out and here mostly for archival purposes. + +![]({static}images/Stout.jpg) + + +###Data + +* IBU: 24 +* EBC: 75 +* ABV: 4.5% +* TOP fermentation + diff --git a/content/images/MC.Beer.jpg b/content/images/MC.Beer.jpg new file mode 100644 index 0000000..daeb8f7 Binary files /dev/null and b/content/images/MC.Beer.jpg differ diff --git a/content/images/MC.kit.jpg b/content/images/MC.kit.jpg new file mode 100644 index 0000000..6cc3a48 Binary files /dev/null and b/content/images/MC.kit.jpg differ diff --git a/content/images/Multilaterale_Pale_3.jpg b/content/images/Multilaterale_Pale_3.jpg new file mode 100644 index 0000000..4891aa1 Binary files /dev/null and b/content/images/Multilaterale_Pale_3.jpg differ diff --git a/content/images/Stout.jpg b/content/images/Stout.jpg new file mode 100644 index 0000000..1babf0d Binary files /dev/null and b/content/images/Stout.jpg differ diff --git a/content/images/bottling.jpeg b/content/images/bottling.jpeg new file mode 100644 index 0000000..61c2a1b Binary files /dev/null and b/content/images/bottling.jpeg differ diff --git a/content/images/cheerswference.jpg b/content/images/cheerswference.jpg new file mode 100644 index 0000000..dd48aa5 Binary files /dev/null and b/content/images/cheerswference.jpg differ diff --git a/content/images/delivery.jpeg b/content/images/delivery.jpeg new file mode 100644 index 0000000..a158948 Binary files /dev/null and b/content/images/delivery.jpeg differ diff --git a/content/images/goethe.jpeg b/content/images/goethe.jpeg new file mode 100644 index 0000000..c1033b7 Binary files /dev/null and b/content/images/goethe.jpeg differ diff --git a/content/images/goethe_paleale.jpeg b/content/images/goethe_paleale.jpeg new file mode 100644 index 0000000..cfd7e66 Binary files /dev/null and b/content/images/goethe_paleale.jpeg differ diff --git a/content/images/paleale.jpg b/content/images/paleale.jpg new file mode 100644 index 0000000..2aace38 Binary files /dev/null and b/content/images/paleale.jpg differ diff --git a/content/images/redale.jpg b/content/images/redale.jpg new file mode 100644 index 0000000..3e69774 Binary files /dev/null and b/content/images/redale.jpg differ diff --git a/content/images/skiddaw.jpg b/content/images/skiddaw.jpg new file mode 100644 index 0000000..e98ce77 Binary files /dev/null and b/content/images/skiddaw.jpg differ diff --git a/content/images/web96_igfm_1.jpg b/content/images/web96_igfm_1.jpg new file mode 100644 index 0000000..d3159a4 Binary files /dev/null and b/content/images/web96_igfm_1.jpg differ diff --git a/content/images/web96_session_pale.jpg b/content/images/web96_session_pale.jpg new file mode 100644 index 0000000..333af7e Binary files /dev/null and b/content/images/web96_session_pale.jpg differ diff --git a/content/pages/about.md b/content/pages/about.md new file mode 100644 index 0000000..2609622 --- /dev/null +++ b/content/pages/about.md @@ -0,0 +1,19 @@ +Title: About +Date: 2019-01-01 + +### About + +Multilaterale is a brewing initiative by and for the independent art spaces in Rotterdam.The idea is that the Multilaterale Group can brew high quality beers of styles otherwise not commonly found, while the spaces in the Multilaterale network can use the sales to finance their activities. With this model we hope to sustain both independent spaces and community brewing initiatives. + +### Support + +Rather than purchasing the brews after production, the spaces in the network order in advance. This minimizes financial risks for the brewers, which allows for more creativity on the one hand and guarantees good wholesale prices for the network on the other hand. This way Multilaterale is also an invitation to the network to participate in and think about the brewing processes, logistics, design and mutual support. + +### Who? + +Multilaterale is an initiative by Henning Rosenbrock and Roel Roscam Abbing who are also founding members of the [Brouwvereniging Rotterdam](https://brouwvereniging.nl), the home brewing association of Rotterdam. + + +### Contact + +You can reach us via cheers[at]multilaterale.group! diff --git a/content/pages/network.md b/content/pages/network.md new file mode 100644 index 0000000..06a1bdf --- /dev/null +++ b/content/pages/network.md @@ -0,0 +1,62 @@ +Title: Network +Date: 2019-01-01 + +Currently the Multilaterale network is composed of the following spaces. These are also the locations where you can purchase Multilaterale. You can also contact us to purchase directly. + +###Multilaterale Group +https://multilaterale.group + +Get in touch if you have a party, an opening, a presentation or in need of a gift. Drop us a mail at cheers[at]multilaterale.group + +### varia +Gouwstraat 3 Rotterdam | + +varia is a space for developing collective approaches to everyday technology. + +Multilaterale beers are available during the events. + +### DE PLAYER +Hillelaan 49d Rotterdam | + + +DE PLAYER is a polymorhpic production platform in Rotterdam (nl), which has been putting together programs on the cutting edge of performance art, experimental music and the visual arts. + +Multilaterale was available during Gardena, we don't know wheter they still have stock. + +### Stichting AppendiX Grafiek + + +AppendiX is a Rotterdam based platform for contemporary print, graphics and fine arts. + +Multilaterale beers are available during the events. + + + +### Goethe Institut Niederlande +Westersingel 9 Rotterdam en Herengracht 470 Amsterdam | + +Multilaterale beers were available during the events, but we don't know whether they still have stock. + +### ook_huis +Brielselaan 198 Rotterdam | + +ook_huis can also be used freely by neighbors and acquaintances. +By programming various activities, ook_ wants to show a communal presence: just meeting eachother. +It is a home for also_artistic activity, in whatever way. + +Sometimes there is also Multilaterale. + +###Shimmer +Waalhaven Oostzijde 1 Rotterdam | + +Shimmer is a Rotterdam based exhibition space that operates with an ever-changing studio-like mentality where knowledge arises through participation and experimentation. + +Multilaterale beers are available during openings. + +### LAG +1e Schinkelstraat 14-16 Amsterdam | + +LAG is: +onsidering hacking a political practice. We give workshops and learn how to have difficulties finding where people to challenging environment where people to challenging environment away from one is free to create and we want to be more their technology an + +They drank all the Multilaterale and we are currently waiting to hear if they want more! \ No newline at end of file diff --git a/content/paleale.md b/content/paleale.md new file mode 100644 index 0000000..40e99f6 --- /dev/null +++ b/content/paleale.md @@ -0,0 +1,39 @@ +Title: Pale Ale +Date: 2020-02-11 +Category: Brews +Image: images/paleale.jpg + +A new brew has arrived! A hoppy, fruity Pale Ale this time! + +Brewed with Cascade, Hersbrucker and Aurora hops this refreshing light-colored beer will leave you wanting for more! Since this is a smaller batch, make sure to get it while it lasts! + +The perfect beer for all those who ask you 'Do you also have something special?'. + +This ale was brewed in collaboration with our friends at Groos brewery. + +![]({static}images/paleale.jpg) + +###Data + +* IBU: 23 +* EBC: 12 +* ABV: 5.3% +* TOP fermentation + +### Malts + +* Barley +* Oats + +### Hops + +* Cascade +* Hersbrucker +* Aurora + +
+ +![]({attach}images/Multilaterale_Pale_3.jpg) + +Right off the bottling line! + diff --git a/content/panamaracingclub.md b/content/panamaracingclub.md new file mode 100644 index 0000000..8d7c94e --- /dev/null +++ b/content/panamaracingclub.md @@ -0,0 +1,19 @@ +Title: Special Editions for Murder Capital +Date: 2020-07-29 +Category: News +Summary: Panama Racing Club special editions. + +Our Red Ale and Irish Stout have been labelled in a Panama Racing Club theme for Murder Capital label. The Panama Red and Night Rider. Check them out in the IFM webshop: https://www.intergalactic.fm/shop/main/murdercapital-beer/?v=7516fd43adaa + +## Here they are +![]({attach}/images/MC.Beer.jpg) + +![]({attach}/images/MC.kit.jpg) + +## Cheers + +![]({attach}/images/cheerswference.jpg) + +## Interested? + +If you are intersted in doing the same, get in touch! diff --git a/content/redale.md b/content/redale.md new file mode 100644 index 0000000..44debef --- /dev/null +++ b/content/redale.md @@ -0,0 +1,37 @@ +Title: Red Ale +Date: 2019-05-14 +Category: Brews +Image: images/redale.jpg + +Our first Multilaterale Group beer is a Red Ale. Inspired by Irish Red Ales. + +It has a vibrant copper red colour. Smooth and malty flavour with tender hoppiness and a slight hint of sour in the after taste. + +The perfect beer for all those who ask you 'Do you also have normal beer?'. + +![]({static}images/redale.jpg) + +###Data + +* IBU: 18 +* EBC: 22 +* ABV: 4.7% +* TOP fermentation + +### Malts + +* 65% Pale Ale +* 24% Cara Red +* 12% Cara 120 + +### Hops + +* Magnum +* Fuggle +* Bramling Cross + +
+ +![]({attach}images/skiddaw.jpg) + +We have evidence of an Irishman approving of our Red Ale. This obviously bespeaks of the quality of this beer. \ No newline at end of file diff --git a/content/sessionpale.md b/content/sessionpale.md new file mode 100644 index 0000000..3be134a --- /dev/null +++ b/content/sessionpale.md @@ -0,0 +1,17 @@ +Title: Session Pale +Date: 2020-11-03 +Category: Brews +Image: images/web96_session_pale.jpg +Summary: Session Pale Ale + +New Session Pale Ale from yours truly. Fruity, hoppy with a grapefruit-like bitterness. Hopped with Magnum and Cascade. + +![]({static}images/web96_session_pale.jpg) + +###Data + +* IBU: 30 +* EBC: 10 +* ABV: 4.1% +* TOP fermentation + diff --git a/pelicanconf.py b/pelicanconf.py new file mode 100644 index 0000000..7ec74e4 --- /dev/null +++ b/pelicanconf.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- # +from __future__ import unicode_literals + +AUTHOR = 'the group' +SITENAME = 'MULTILATERALE' +SITEURL = 'https://multilaterale.group' + +PATH = 'content' + +TIMEZONE = 'Europe/Paris' + +DEFAULT_LANG = 'en' +DEFAULT_DATE = 'fs' + +THEME = "themes/multilaterale/" + +PLUGIN_PATHS = ['plugins'] +PLUGINS = ["representative_image"] + +# Feed generation is usually not desired when developing +FEED_ALL_ATOM = None +CATEGORY_FEED_ATOM = None +TRANSLATION_FEED_ATOM = None +AUTHOR_FEED_ATOM = None +AUTHOR_FEED_RSS = None + +# Blogroll +LINKS = (('Pelican', 'http://getpelican.com/'), + ('Python.org', 'http://python.org/'), + ('Jinja2', 'http://jinja.pocoo.org/'), + ('You can modify those links in your config file', '#'),) + +# Social widget +SOCIAL = (('You can add links in your config file', '#'), + ('Another social link', '#'),) + +DEFAULT_PAGINATION = False + +# Uncomment following line if you want document-relative URLs when developing +#RELATIVE_URLS = True \ No newline at end of file diff --git a/plugins/representative_image/__init__.py b/plugins/representative_image/__init__.py new file mode 100644 index 0000000..364773c --- /dev/null +++ b/plugins/representative_image/__init__.py @@ -0,0 +1 @@ +from .representative_image import * \ No newline at end of file diff --git a/plugins/representative_image/representative_image.py b/plugins/representative_image/representative_image.py new file mode 100644 index 0000000..64d68ce --- /dev/null +++ b/plugins/representative_image/representative_image.py @@ -0,0 +1,50 @@ +import six +from pelican import signals +from pelican.contents import Article, Page +from pelican.generators import ArticlesGenerator +from bs4 import BeautifulSoup + + +def images_extraction(instance): + representativeImage = None + if type(instance) in (Article, Page): + if 'image' in instance.metadata: + representativeImage = instance.metadata['image'] + + # Process Summary: + # If summary contains images, extract one to be the representativeImage and remove images from summary + soup = BeautifulSoup(instance.summary, 'html.parser') + images = soup.find_all('img') + for i in images: + if not representativeImage: + representativeImage = i['src'] + i.extract() + if len(images) > 0: + # set _summary field which is based on metadata. summary field is only based on article's content and not settable + instance._summary = six.text_type(soup) + + # If there are no image in summary, look for it in the content body + if not representativeImage: + soup = BeautifulSoup(instance._content, 'html.parser') + imageTag = soup.find('img') + if imageTag: + representativeImage = imageTag['src'] + + # Set the attribute to content instance + instance.featured_image = representativeImage + + +def run_plugin(generators): + for generator in generators: + if isinstance(generator, ArticlesGenerator): + for article in generator.articles: + images_extraction(article) + + +def register(): + try: + signals.all_generators_finalized.connect(run_plugin) + except AttributeError: + # NOTE: This results in #314 so shouldn't really be relied on + # https://github.com/getpelican/pelican-plugins/issues/314 + signals.content_object_init.connect(images_extraction) diff --git a/plugins/thumbnailer/Readme.md b/plugins/thumbnailer/Readme.md new file mode 100644 index 0000000..c3db910 --- /dev/null +++ b/plugins/thumbnailer/Readme.md @@ -0,0 +1,33 @@ +Thumbnail Creation of images +============================ + +This plugin creates thumbnails for all of the images found under a specific directory, in various thumbnail sizes. +It requires `PIL` to function properly since `PIL` is used to resize the images, and the thumbnail will only be re-built +if it doesn't already exist (to save processing time). + +Installation +------------- + +Set up like any other plugin, making sure to set `PLUGIN_PATH` and add `thumbnailer` to the `PLUGINS` list. + +[PIL](http://www.pythonware.com/products/pil/) or [Pillow](http://pillow.readthedocs.org/en/latest/installation.html#) +is required. If you choose Pillow, you need to install some additional +[external libraries](http://www.pythonware.com/products/pil/) to add support for image types like `jpg`. + +Configuration +------------- + +* `IMAGE_PATH` is the path to the image directory. It should reside inside your content directory, and defaults to "pictures". +* `THUMBNAIL_DIR` is the path to the output sub-directory where the thumbnails are generated. +* `THUMBNAIL_SIZES` is a dictionary mapping size name to size specifications. + The generated filename will be `originalname_thumbnailname.ext` unless `THUMBNAIL_KEEP_NAME` is set. +* `THUMBNAIL_KEEP_NAME` is a Boolean that, if set, puts the file with the original name in a thumbnailname folder, named like the key in `THUMBNAIL_SIZES`. +* `THUMBNAIL_KEEP_TREE` is a Boolean that, if set, saves the image directory tree. +* `THUMBNAIL_INCLUDE_REGEX` is an optional string that is used as regular expression to restrict thumbnailing to matching files. By default all files not starting with a dot are respected. + +Sizes can be specified using any of the following formats: + +* wxh will resize to exactly wxh cropping as necessary to get that size +* wx? will resize so that the width is the specified size, and the height will scale to retain aspect ratio +* ?xh same as wx? but will height being a set size +* s is a shorthand for wxh where w=h diff --git a/plugins/thumbnailer/__init__.py b/plugins/thumbnailer/__init__.py new file mode 100644 index 0000000..20797b1 --- /dev/null +++ b/plugins/thumbnailer/__init__.py @@ -0,0 +1 @@ +from .thumbnailer import * \ No newline at end of file diff --git a/plugins/thumbnailer/test_data/expected_exact.jpg b/plugins/thumbnailer/test_data/expected_exact.jpg new file mode 100644 index 0000000..5819792 Binary files /dev/null and b/plugins/thumbnailer/test_data/expected_exact.jpg differ diff --git a/plugins/thumbnailer/test_data/expected_height.jpg b/plugins/thumbnailer/test_data/expected_height.jpg new file mode 100644 index 0000000..6459410 Binary files /dev/null and b/plugins/thumbnailer/test_data/expected_height.jpg differ diff --git a/plugins/thumbnailer/test_data/expected_square.jpg b/plugins/thumbnailer/test_data/expected_square.jpg new file mode 100644 index 0000000..de99e5b Binary files /dev/null and b/plugins/thumbnailer/test_data/expected_square.jpg differ diff --git a/plugins/thumbnailer/test_data/expected_width.jpg b/plugins/thumbnailer/test_data/expected_width.jpg new file mode 100644 index 0000000..9c2efc6 Binary files /dev/null and b/plugins/thumbnailer/test_data/expected_width.jpg differ diff --git a/plugins/thumbnailer/test_data/sample_image.jpg b/plugins/thumbnailer/test_data/sample_image.jpg new file mode 100644 index 0000000..cc83880 Binary files /dev/null and b/plugins/thumbnailer/test_data/sample_image.jpg differ diff --git a/plugins/thumbnailer/test_data/subdir/sample_image.jpg b/plugins/thumbnailer/test_data/subdir/sample_image.jpg new file mode 100644 index 0000000..cc83880 Binary files /dev/null and b/plugins/thumbnailer/test_data/subdir/sample_image.jpg differ diff --git a/plugins/thumbnailer/test_thumbnails.py b/plugins/thumbnailer/test_thumbnails.py new file mode 100644 index 0000000..fdb0c34 --- /dev/null +++ b/plugins/thumbnailer/test_thumbnails.py @@ -0,0 +1,63 @@ +from thumbnailer import _resizer +from unittest import TestCase, main +import os +from PIL import Image + +class ThumbnailerTests(TestCase): + + def path(self, filename): + return os.path.join(self.img_path, filename) + + def setUp(self): + self.img_path = os.path.join(os.path.dirname(__file__), "test_data") + self.img = Image.open(self.path("sample_image.jpg")) + + def testSquare(self): + r = _resizer('square', '100', self.img_path) + output = r.resize(self.img) + self.assertEqual((100, 100), output.size) + + def testExact(self): + r = _resizer('exact', '250x100', self.img_path) + output = r.resize(self.img) + self.assertEqual((250, 100), output.size) + + def testWidth(self): + r = _resizer('aspect', '250x?', self.img_path) + output = r.resize(self.img) + self.assertEqual((250, 166), output.size) + + def testHeight(self): + r = _resizer('aspect', '?x250', self.img_path) + output = r.resize(self.img) + self.assertEqual((375, 250), output.size) + +class ThumbnailerFilenameTest(TestCase): + + def path(self, *parts): + return os.path.join(self.img_path, *parts) + + def setUp(self): + self.img_path = os.path.join(os.path.dirname(__file__), "test_data") + + def testRoot(self): + """Test a file that is in the root of img_path.""" + + r = _resizer('square', '100', self.img_path) + new_name = r.get_thumbnail_name(self.path('sample_image.jpg')) + self.assertEqual('sample_image_square.jpg', new_name) + + def testRootWithSlash(self): + r = _resizer('square', '100', self.img_path + '/') + new_name = r.get_thumbnail_name(self.path('sample_image.jpg')) + self.assertEqual('sample_image_square.jpg', new_name) + + def testSubdir(self): + """Test a file that is in a sub-directory of img_path.""" + + r = _resizer('square', '100', self.img_path) + new_name = r.get_thumbnail_name(self.path('subdir', 'sample_image.jpg')) + self.assertEqual('subdir/sample_image_square.jpg', new_name) + +if __name__=="__main__": + main() diff --git a/plugins/thumbnailer/thumbnailer.py b/plugins/thumbnailer/thumbnailer.py new file mode 100644 index 0000000..f95f264 --- /dev/null +++ b/plugins/thumbnailer/thumbnailer.py @@ -0,0 +1,210 @@ +import os +import os.path as path +import re +from pelican import signals + +import logging +logger = logging.getLogger(__name__) + +try: + from PIL import Image, ImageOps + enabled = True +except ImportError: + logging.warning("Unable to load PIL, disabling thumbnailer") + enabled = False + +DEFAULT_IMAGE_DIR = "pictures" +DEFAULT_THUMBNAIL_DIR = "thumbnails" +DEFAULT_THUMBNAIL_SIZES = { + 'thumbnail_square': '150', + 'thumbnail_wide': '150x?', + 'thumbnail_tall': '?x150', +} +DEFAULT_TEMPLATE = """{filename}""" +DEFAULT_GALLERY_THUMB = "thumbnail_square" + +class _resizer(object): + """ Resizes based on a text specification, see readme """ + + REGEX = re.compile(r'(\d+|\?)x(\d+|\?)') + + def __init__(self, name, spec, root): + self._name = name + self._spec = spec + # The location of input images from _image_path. + self._root = root + + def _null_resize(self, w, h, image): + return image + + def _exact_resize(self, w, h, image): + retval = ImageOps.fit(image, (w,h), Image.BICUBIC) + return retval + + def _aspect_resize(self, w, h, image): + retval = image.copy() + retval.thumbnail((w, h), Image.ANTIALIAS) + + return retval + + def resize(self, image): + resizer = self._null_resize + + # Square resize and crop + if 'x' not in self._spec: + resizer = self._exact_resize + targetw = int(self._spec) + targeth = targetw + else: + matches = self.REGEX.search(self._spec) + tmpw = matches.group(1) + tmph = matches.group(2) + + # Full Size + if tmpw == '?' and tmph == '?': + targetw = image.size[0] + targeth = image.size[1] + resizer = self._null_resize + + # Set Height Size + if tmpw == '?': + targetw = image.size[0] + targeth = int(tmph) + resizer = self._aspect_resize + + # Set Width Size + elif tmph == '?': + targetw = int(tmpw) + targeth = image.size[1] + resizer = self._aspect_resize + + # Scale and Crop + else: + targetw = int(tmpw) + targeth = int(tmph) + resizer = self._exact_resize + + logging.debug("Using resizer {0}".format(resizer.__name__)) + return resizer(targetw, targeth, image) + + def get_thumbnail_name(self, in_path): + # Find the partial path + filename beyond the input image directory. + prefix = path.commonprefix([in_path, self._root]) + new_filename = in_path[len(prefix) + 1:] + + # Generate the new filename. + (basename, ext) = path.splitext(new_filename) + return "{0}_{1}{2}".format(basename, self._name, ext) + + def resize_file_to(self, in_path, out_path, keep_filename=False): + """ Given a filename, resize and save the image per the specification into out_path + + :param in_path: path to image file to save. Must be supported by PIL + :param out_path: path to the directory root for the outputted thumbnails to be stored + :return: None + """ + + if keep_filename: + filename = path.join(out_path, path.basename(in_path)) + else: + filename = path.join(out_path, self.get_thumbnail_name(in_path)) + out_path = path.dirname(filename) + if not path.exists(out_path): + os.makedirs(out_path) + if not path.exists(filename): + try: + image = Image.open(in_path) + thumbnail = self.resize(image) + logger.debug('test') + thumbnail.save(filename) + logger.info("Generated Thumbnail {0}".format(path.basename(filename))) + except IOError: + logger.info("Generating Thumbnail for {0} skipped".format(path.basename(filename))) + + +def resize_thumbnails(pelican): + """ Resize a directory tree full of images into thumbnails + + :param pelican: The pelican instance + :return: None + """ + global enabled + if not enabled: + return + + in_path = _image_path(pelican) + + include_regex = pelican.settings.get('THUMBNAIL_INCLUDE_REGEX') + if include_regex: + pattern = re.compile(include_regex) + is_included = lambda name: pattern.match(name) + else: + is_included = lambda name: not name.startswith('.') + + sizes = pelican.settings.get('THUMBNAIL_SIZES', DEFAULT_THUMBNAIL_SIZES) + resizers = dict((k, _resizer(k, v, in_path)) for k,v in sizes.items()) + logger.debug("Thumbnailer Started") + for dirpath, _, filenames in os.walk(in_path): + for filename in filenames: + if is_included(filename): + for name, resizer in resizers.items(): + in_filename = path.join(dirpath, filename) + out_path = get_out_path(pelican, in_path, in_filename, name) + resizer.resize_file_to( + in_filename, + out_path, pelican.settings.get('THUMBNAIL_KEEP_NAME')) + + +def get_out_path(pelican, in_path, in_filename, name): + base_out_path = path.join(pelican.settings['OUTPUT_PATH'], + pelican.settings.get('THUMBNAIL_DIR', DEFAULT_THUMBNAIL_DIR)) + logger.debug("Processing thumbnail {0}=>{1}".format(in_filename, name)) + if pelican.settings.get('THUMBNAIL_KEEP_NAME', False): + if pelican.settings.get('THUMBNAIL_KEEP_TREE', False): + return path.join(base_out_path, name, path.dirname(path.relpath(in_filename, in_path))) + else: + return path.join(base_out_path, name) + else: + return base_out_path + + +def _image_path(pelican): + return path.join(pelican.settings['PATH'], + pelican.settings.get("IMAGE_PATH", DEFAULT_IMAGE_DIR)).rstrip('/') + + +def expand_gallery(generator, metadata): + """ Expand a gallery tag to include all of the files in a specific directory under IMAGE_PATH + + :param pelican: The pelican instance + :return: None + """ + if "gallery" not in metadata or metadata['gallery'] is None: + return # If no gallery specified, we do nothing + + lines = [ ] + base_path = _image_path(generator) + in_path = path.join(base_path, metadata['gallery']) + template = generator.settings.get('GALLERY_TEMPLATE', DEFAULT_TEMPLATE) + thumbnail_name = generator.settings.get("GALLERY_THUMBNAIL", DEFAULT_GALLERY_THUMB) + thumbnail_prefix = generator.settings.get("") + resizer = _resizer(thumbnail_name, '?x?', base_path) + for dirpath, _, filenames in os.walk(in_path): + for filename in filenames: + if not filename.startswith('.'): + url = path.join(dirpath, filename).replace(base_path, "")[1:] + url = path.join('/static', generator.settings.get('IMAGE_PATH', DEFAULT_IMAGE_DIR), url).replace('\\', '/') + logger.debug("GALLERY: {0}".format(url)) + thumbnail = resizer.get_thumbnail_name(filename) + thumbnail = path.join('/', generator.settings.get('THUMBNAIL_DIR', DEFAULT_THUMBNAIL_DIR), thumbnail).replace('\\', '/') + lines.append(template.format( + filename=filename, + url=url, + thumbnail=thumbnail, + )) + metadata['gallery_content'] = "\n".join(lines) + + +def register(): + signals.finalized.connect(resize_thumbnails) + signals.article_generator_context.connect(expand_gallery) diff --git a/publishconf.py b/publishconf.py new file mode 100644 index 0000000..e8ab6f6 --- /dev/null +++ b/publishconf.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- # +from __future__ import unicode_literals + +# This file is only used if you use `make publish` or +# explicitly specify it as your config file. + +import os +import sys +sys.path.append(os.curdir) +from pelicanconf import * + +# If your site is available via HTTPS, make sure SITEURL begins with https:// +SITEURL = 'https://multilaterale.group' +RELATIVE_URLS = False + +FEED_ALL_ATOM = 'feeds/all.atom.xml' +CATEGORY_FEED_ATOM = 'feeds/{slug}.atom.xml' + +DELETE_OUTPUT_DIRECTORY = True + +# Following items are often useful when publishing + +#DISQUS_SITENAME = "" +#GOOGLE_ANALYTICS = "" \ No newline at end of file diff --git a/themes/multilaterale/static/multilaterale_logo.svg b/themes/multilaterale/static/multilaterale_logo.svg new file mode 100644 index 0000000..1e2149a --- /dev/null +++ b/themes/multilaterale/static/multilaterale_logo.svg @@ -0,0 +1,63 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/themes/multilaterale/static/multilaterale_logo_opacity.svg b/themes/multilaterale/static/multilaterale_logo_opacity.svg new file mode 100644 index 0000000..0ace54a --- /dev/null +++ b/themes/multilaterale/static/multilaterale_logo_opacity.svg @@ -0,0 +1,64 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/themes/multilaterale/static/multilaterale_logo_pink.svg b/themes/multilaterale/static/multilaterale_logo_pink.svg new file mode 100644 index 0000000..7f75a94 --- /dev/null +++ b/themes/multilaterale/static/multilaterale_logo_pink.svg @@ -0,0 +1,64 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/themes/multilaterale/static/multilaterale_logo_watermerk.svg b/themes/multilaterale/static/multilaterale_logo_watermerk.svg new file mode 100644 index 0000000..2bfa929 --- /dev/null +++ b/themes/multilaterale/static/multilaterale_logo_watermerk.svg @@ -0,0 +1,64 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/themes/multilaterale/static/multilaterale_text.svg b/themes/multilaterale/static/multilaterale_text.svg new file mode 100644 index 0000000..9ad64e7 --- /dev/null +++ b/themes/multilaterale/static/multilaterale_text.svg @@ -0,0 +1,134 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/themes/multilaterale/static/style.css b/themes/multilaterale/static/style.css new file mode 100644 index 0000000..9db6202 --- /dev/null +++ b/themes/multilaterale/static/style.css @@ -0,0 +1,176 @@ +html { +background-color: rgb(240,240,240); +background-image:url('/theme/multilaterale_logo_opacity.svg'); +background-size:200vw; +background-position: center top; +background-repeat: no-repeat; +} + +body { + font-size:22px; + font-family: 'Kotta One', serif; + max-width:50%; + margin:0 auto; +} + +a { + color:deeppink; + text-decoration-style: wavy; +} + +h1,h2,h3{ + text-transform: uppercase; + /*font-family: 'Amatic SC', cursive;*/ +} +h3{ + margin:0; + margin-top:2em; +} + +h3 + p { + margin:0; +} + +p { + margin-bottom:0.2em; +} + +#banner{ + margin-top:2em; + margin-bottom:0em; +} + +#banner_img{ + width:100%; +} + +#menu ul li{ + display:inline; +} + +#menu ul { + padding-left:0px; + font-size: 2em; + text-align: center; + margin-top:0px; + margin-bottom:1.5em; +} + +#menu a { + color:black; + text-decoration-style: none; +} + +#menu a:hover { + color:black; + text-decoration-style: wavy; + text-decoration-color: deeppink; +} + +#menu .active a{ + + text-decoration-style: wavy; + text-decoration-color: deeppink; +} + +.featured_image{ + display:inline-block; + max-width: 40%; +} + +.featured_image img { + max-width: 100%; +} + +.summary{ + display:inline-block; + max-width: 50%; + vertical-align: top; + margin-left: 1em; +} + +.summary h2 { + text-transform: uppercase; + margin-bottom: 0; + /*border-bottom: 6px solid black;*/ +} + +.entry-content p img { + max-width: 100%; +} + +#data-left, #data-right{ + max-width: 49%; + display:inline-block; + vertical-align: top; +} + +#data-left img{ + height:100%; +} + +#data h3 { + margin:0.3em; +} + +#data ul { + margin:0.5em; +} + +@media only screen and (max-width: 600px) { + body { + font-size:24px; + max-width: 92% + } + #menu ul { + font-size:1.5em; + } + #banner{ + margin-top:1em; +} + +#data-left, #data-right{ + max-width: 100%; + display:inline-block; +} + +} + +@media only screen and (max-width: 300px) { + body { + font-size:18px; + max-width: 92% + } + #menu ul { + font-size:1.2em; + } + #banner{ + margin-top:1em; +} +} + +@media only screen and (orientation: portrait) { + body { + font-size:48px; + max-width: 92% + } + + #menu ul { + font-size:1.5em; + } + #banner{ + margin-top:1em; +} +.featured_image{ + max-width: 100%; +} + +.featured_image img { + max-width: 100%; +} + +.summary{ + max-width: 100%; +} + +} \ No newline at end of file diff --git a/themes/multilaterale/templates/archives.html b/themes/multilaterale/templates/archives.html new file mode 100644 index 0000000..cd12950 --- /dev/null +++ b/themes/multilaterale/templates/archives.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Archives{% endblock %} + +{% block content %} +

Archives for {{ SITENAME }}

+ +
+{% for article in dates %} +
{{ article.locale_date }}
+
{{ article.title }}
+{% endfor %} +
+{% endblock %} diff --git a/themes/multilaterale/templates/article.html b/themes/multilaterale/templates/article.html new file mode 100644 index 0000000..a9ff2e6 --- /dev/null +++ b/themes/multilaterale/templates/article.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% block html_lang %}{{ article.lang }}{% endblock %} + +{% block title %}{{ SITENAME }} - {{ article.title }}{% endblock %} + +{% block head %} + {{ super() }} + + {% import 'translations.html' as translations with context %} + {% if translations.entry_hreflang(article) %} + {{ translations.entry_hreflang(article) }} + {% endif %} + {% if article.description %} + + {% endif %} + {% for tag in article.tags %} + + {% endfor %} + +{% endblock %} + + +{% block content %} +
+
+

{{article.title}}

+ {{ article.content }} +
+
+{% endblock %} diff --git a/themes/multilaterale/templates/author.html b/themes/multilaterale/templates/author.html new file mode 100644 index 0000000..a190194 --- /dev/null +++ b/themes/multilaterale/templates/author.html @@ -0,0 +1,8 @@ +{% extends "index.html" %} + +{% block title %}{{ SITENAME }} - Articles by {{ author }}{% endblock %} + +{% block content_title %} +

Articles by {{ author }}

+{% endblock %} + diff --git a/themes/multilaterale/templates/authors.html b/themes/multilaterale/templates/authors.html new file mode 100644 index 0000000..9aee5db --- /dev/null +++ b/themes/multilaterale/templates/authors.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Authors{% endblock %} + +{% block content %} +

Authors on {{ SITENAME }}

+
    + {% for author, articles in authors|sort %} +
  • {{ author }} ({{ articles|count }})
  • + {% endfor %} +
+{% endblock %} diff --git a/themes/multilaterale/templates/base.html b/themes/multilaterale/templates/base.html new file mode 100644 index 0000000..38b6971 --- /dev/null +++ b/themes/multilaterale/templates/base.html @@ -0,0 +1,48 @@ + + + + {% block head %} + {% block title %}{{ SITENAME }}{% endblock title %} + + + + + + {% block rss %} + {% endblock rss %} + {% endblock head %} + + + + {% block menu %} + + + {% endblock menu %} + {% block content %} + {% endblock %} +
+
+
+
+ + diff --git a/themes/multilaterale/templates/categories.html b/themes/multilaterale/templates/categories.html new file mode 100644 index 0000000..7999de4 --- /dev/null +++ b/themes/multilaterale/templates/categories.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Categories{% endblock %} + +{% block content %} +

Categories on {{ SITENAME }}

+
    + {% for category, articles in categories|sort %} +
  • {{ category }} ({{ articles|count }})
  • + {% endfor %} +
+{% endblock %} diff --git a/themes/multilaterale/templates/category.html b/themes/multilaterale/templates/category.html new file mode 100644 index 0000000..f03d2a3 --- /dev/null +++ b/themes/multilaterale/templates/category.html @@ -0,0 +1,35 @@ +{% extends "index.html" %} + +{% block title %}{{ SITENAME }} - {{ category }} category{% endblock %} + +{% block content_title %} +

Articles in the {{ category }} category

+{% endblock %} + +{% block content %} +
+ +
+{% for article in articles_page.object_list %} + + +{% endfor %} +
+ +{% if articles_page.has_other_pages() %} + {% include 'pagination.html' %} +{% endif %} +
+{% endblock content %} \ No newline at end of file diff --git a/themes/multilaterale/templates/gosquared.html b/themes/multilaterale/templates/gosquared.html new file mode 100644 index 0000000..49ccbbe --- /dev/null +++ b/themes/multilaterale/templates/gosquared.html @@ -0,0 +1,14 @@ +{% if GOSQUARED_SITENAME %} + +{% endif %} diff --git a/themes/multilaterale/templates/index.html b/themes/multilaterale/templates/index.html new file mode 100644 index 0000000..24e6ca3 --- /dev/null +++ b/themes/multilaterale/templates/index.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block content %} +
+{% block content_title %} + +{% endblock %} + +
+{% for article in articles_page.object_list %} + {% if article.category != 'News' %} + + {% endif %} +{% endfor %} +

UPDATES

+{% for article in articles_page.object_list %} + {% if article.category != 'Brews' %} +
+
+
+

{{ article.title }}

+ {{article.date | strftime('%d %B %Y')}} + {{article.summary}} +
+
+
+ {% endif %} +{% endfor %} +
+ +{% if articles_page.has_other_pages() %} + {% include 'pagination.html' %} +{% endif %} +
+{% endblock content %} diff --git a/themes/multilaterale/templates/page.html b/themes/multilaterale/templates/page.html new file mode 100644 index 0000000..5222e53 --- /dev/null +++ b/themes/multilaterale/templates/page.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% block html_lang %}{{ page.lang }}{% endblock %} + +{% block title %}{{ SITENAME }} - {{ page.title }}{%endblock%} + +{% block head %} + {{ super() }} + + {% import 'translations.html' as translations with context %} + {% if translations.entry_hreflang(page) %} + {{ translations.entry_hreflang(page) }} + {% endif %} +{% endblock %} + +{% block content %} + {% import 'translations.html' as translations with context %} + {{ translations.translations_for(page) }} + + {{ page.content }} + + {% if page.modified %} +

+ Last updated: {{ page.locale_modified }} +

+ {% endif %} +{% endblock %} diff --git a/themes/multilaterale/templates/pagination.html b/themes/multilaterale/templates/pagination.html new file mode 100644 index 0000000..4219a5c --- /dev/null +++ b/themes/multilaterale/templates/pagination.html @@ -0,0 +1,11 @@ +{% if DEFAULT_PAGINATION %} +

+ {% if articles_page.has_previous() %} + « + {% endif %} + Page {{ articles_page.number }} / {{ articles_paginator.num_pages }} + {% if articles_page.has_next() %} + » + {% endif %} +

+{% endif %} diff --git a/themes/multilaterale/templates/period_archives.html b/themes/multilaterale/templates/period_archives.html new file mode 100644 index 0000000..e1ddf62 --- /dev/null +++ b/themes/multilaterale/templates/period_archives.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - {{ period | reverse | join(' ') }} archives{% endblock %} + +{% block content %} +

Archives for {{ period | reverse | join(' ') }}

+ +
+{% for article in dates %} +
{{ article.locale_date }}
+
{{ article.title }}
+{% endfor %} +
+{% endblock %} diff --git a/themes/multilaterale/templates/rss.html b/themes/multilaterale/templates/rss.html new file mode 100644 index 0000000..9fe9a77 --- /dev/null +++ b/themes/multilaterale/templates/rss.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + {%block rss%} + {% if FEED_ALL_ATOM %} + + {% endif %} + {% if FEED_ALL_RSS %} + + {% endif %} + {% if FEED_ATOM %} + + {% endif %} + {% if FEED_RSS %} + + {% endif %} + {% if CATEGORY_FEED_ATOM and category %} + + {% endif %} + {% if CATEGORY_FEED_RSS and category %} + + {% endif %} + {% if TAG_FEED_ATOM and tag %} + + {% endif %} + {% if TAG_FEED_RSS and tag %} + + {% endif %} + {%endblock rss%} \ No newline at end of file diff --git a/themes/multilaterale/templates/tag.html b/themes/multilaterale/templates/tag.html new file mode 100644 index 0000000..9c95803 --- /dev/null +++ b/themes/multilaterale/templates/tag.html @@ -0,0 +1,7 @@ +{% extends "index.html" %} + +{% block title %}{{ SITENAME }} - {{ tag }} tag{% endblock %} + +{% block content_title %} +

Articles tagged with {{ tag }}

+{% endblock %} diff --git a/themes/multilaterale/templates/tags.html b/themes/multilaterale/templates/tags.html new file mode 100644 index 0000000..b90b0ac --- /dev/null +++ b/themes/multilaterale/templates/tags.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Tags{% endblock %} + +{% block content %} +

Tags for {{ SITENAME }}

+
    + {% for tag, articles in tags|sort %} +
  • {{ tag }} ({{ articles|count }})
  • + {% endfor %} +
+{% endblock %} diff --git a/themes/multilaterale/templates/translations.html b/themes/multilaterale/templates/translations.html new file mode 100644 index 0000000..f0e2478 --- /dev/null +++ b/themes/multilaterale/templates/translations.html @@ -0,0 +1,16 @@ +{% macro translations_for(article) %} +{% if article.translations %} +Translations: +{% for translation in article.translations %} +{{ translation.lang }} +{% endfor %} +{% endif %} +{% endmacro %} + +{% macro entry_hreflang(entry) %} +{% if entry.translations %} + {% for translation in entry.translations %} + + {% endfor %} +{% endif %} +{% endmacro %}