Compare commits
319 Commits
wip-redesi
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e7886261bd | ||
81c5687f02 | |||
![]() |
6b835e74f1 | ||
b6127c736d | |||
7280f1dbc1 | |||
28ee78ec02 | |||
96a54695af | |||
cbdcd04423 | |||
c618e6cf17 | |||
b8defe329e | |||
cf887d8f5f | |||
11effcb580 | |||
35ba45445f | |||
6c720b7b08 | |||
344a66f0eb | |||
941ed4a0e0 | |||
acfffce48d | |||
b6b097483a | |||
b76be2a7ba | |||
c168c09293 | |||
db74c89e6f | |||
23d7e50df2 | |||
a8afccba00 | |||
24118b6777 | |||
76825fda39 | |||
d49f69ecbd | |||
c91a52046d | |||
577bf8b964 | |||
049d71dc77 | |||
2075c8a790 | |||
2d6b5d4b67 | |||
5f497fc645 | |||
a321d3501a | |||
d2d4a52846 | |||
ae176cbbdf | |||
d5f5b63b3f | |||
274766d6f4 | |||
e74d573063 | |||
f514cc4176 | |||
3d567ff6f8 | |||
ba69dd46a0 | |||
cfbb3d7e5a | |||
c9bbf26a71 | |||
35675866ee | |||
d813935f43 | |||
947dab3185 | |||
c0cb80ceec | |||
48c8f79371 | |||
53b22641f2 | |||
4f9699c7ae | |||
a04e62e3e9 | |||
5df18b670a | |||
a5cd12ad87 | |||
2c51407196 | |||
0e0716449a | |||
f0c4f1576c | |||
42edd9486b | |||
8534cdbaeb | |||
7209a3c525 | |||
81a564a9d9 | |||
1c47197fd2 | |||
ea95e7b2b2 | |||
be28b2d13d | |||
ea9da2acdb | |||
c66592a5ae | |||
1e3041d997 | |||
f5db3c8da2 | |||
224e4dc1e0 | |||
c1e52ae320 | |||
b8be19c729 | |||
51d081971c | |||
b438c319b0 | |||
87bc2b5378 | |||
c3261ed83a | |||
468fc85751 | |||
049fdf3b63 | |||
ad31b6338f | |||
61b1ab0c20 | |||
08de073464 | |||
a756fb5f6e | |||
2050f4b7d8 | |||
51e22eb414 | |||
cabfce12c0 | |||
b6d9039e82 | |||
4a6e971e71 | |||
eeab0a407e | |||
eafc1e981f | |||
dfbd03e448 | |||
12c64f13a2 | |||
b62f500b2e | |||
6d47946b1b | |||
b4ecf93485 | |||
3d6b2452d6 | |||
4afe23e284 | |||
479153af9b | |||
aee369cc5a | |||
8f0670d017 | |||
858bed66f4 | |||
7a02f86a5b | |||
37667424ab | |||
b4739521ed | |||
d125a6ac55 | |||
4473d56379 | |||
c971f799ef | |||
36f31caf04 | |||
d54d5ec157 | |||
4a180e3784 | |||
6ac75c6a14 | |||
4b7cc3f58e | |||
cbbaf90002 | |||
c5154240ca | |||
1e62aff62c | |||
b015cc8fa4 | |||
3be54da5c3 | |||
5c1b94544a | |||
1ed2a3937e | |||
80b69438ed | |||
3f3112f272 | |||
1d7cbd8ba5 | |||
a53dc51680 | |||
29ff9609e7 | |||
bd07a63acd | |||
9bf6c0fcb3 | |||
142dd36e3c | |||
e03cf2f5da | |||
79b409c83a | |||
a67af63459 | |||
362694f4b6 | |||
364fa76956 | |||
144dcf7a76 | |||
f8995fb657 | |||
95762acf14 | |||
35a9986290 | |||
e8c878d0f3 | |||
fdf05d16de | |||
49bffd108d | |||
87c1eae1a6 | |||
163aee6bbc | |||
5f01b0d6ba | |||
da955ce4af | |||
ad1a55c5d5 | |||
db976a4d50 | |||
42215d2b02 | |||
9cf3a7147e | |||
fb016c3e3b | |||
039ab1ce70 | |||
1bd1c6e5fd | |||
353660c7ba | |||
b77f3aaa49 | |||
50c0842e72 | |||
5d2ba7fc96 | |||
6972e1662d | |||
af6022c997 | |||
2fc1738e99 | |||
149013d64c | |||
e13a1bff1b | |||
9ac60fe922 | |||
2f78f36f61 | |||
5edcf931e9 | |||
b417f25811 | |||
691c1411bc | |||
9f3946ba9e | |||
269daa0d43 | |||
39516e63cc | |||
84881743ef | |||
e975492869 | |||
78e1c728fa | |||
be18dfb985 | |||
8797b18754 | |||
727ba3fd58 | |||
a369c04b38 | |||
fad5f803e8 | |||
143cd27c55 | |||
2f854ebeee | |||
6fa3af50cf | |||
1fd2443bd7 | |||
c5f8add5f5 | |||
a8e5d593ac | |||
fc986b0ab6 | |||
b7b6543043 | |||
d32c44e50c | |||
369161e29f | |||
3cd55e2a83 | |||
1071915f27 | |||
5f9406edd2 | |||
5bf1693d5b | |||
27caff7e6e | |||
63d25d1dca | |||
2950a4347a | |||
76a707e5bf | |||
5218bd17e3 | |||
37fe235d47 | |||
1a49b24f8e | |||
0b2a3c99ce | |||
a674de4db5 | |||
d2815acd80 | |||
670c600382 | |||
f2207bc4d4 | |||
a9848c3fad | |||
c81711de53 | |||
2a8a109d83 | |||
35b1106ccc | |||
1f326b2728 | |||
ccb3187c17 | |||
734a8db145 | |||
82c6c30a0a | |||
594af19b2b | |||
3de73ac35e | |||
97c549de08 | |||
b5ff89f4ca | |||
ef8bd8d22b | |||
4ff52a8af0 | |||
218ba3831c | |||
a753f29ccc | |||
1a7be4b565 | |||
765f36261a | |||
bce054d47d | |||
89ea34724b | |||
7983a7b038 | |||
7ba8ff7580 | |||
fd1db5d2e0 | |||
205e34289f | |||
e7190f09dc | |||
7e61d218b9 | |||
12936c80ea | |||
dcac1317bf | |||
afa0c96156 | |||
012eaaef11 | |||
54e4e76945 | |||
6cbd5ca369 | |||
690b35bab1 | |||
356e4705b3 | |||
748190d15b | |||
05ff27a12d | |||
74e18bb500 | |||
c460359b31 | |||
14daead15d | |||
8e9d63df2b | |||
10addb1521 | |||
735e6400e3 | |||
a1d84196cd | |||
678a03dbf1 | |||
811dc4d65b | |||
265794d4b7 | |||
ece0ba4ae7 | |||
2395bd8090 | |||
fef7d5feac | |||
f0f96bf2f1 | |||
e05a0c0e04 | |||
dbba955afe | |||
17240f5793 | |||
8ff8975dbb | |||
0a144ec12d | |||
6d9fa89d90 | |||
06e7ea53bb | |||
00cd29befc | |||
a5c7ec285d | |||
7fff47c5c5 | |||
534e212802 | |||
1fac97e3f8 | |||
0556c5ae9a | |||
bb2c351460 | |||
a65d771bd6 | |||
b50a3e1fb3 | |||
6f88de3b20 | |||
6569e22fa8 | |||
c773145bd6 | |||
ae907719d0 | |||
88f936772d | |||
0f1088702d | |||
40f6ebd99c | |||
fca2b0f44f | |||
08b1b03802 | |||
23bf27ca75 | |||
15264877e6 | |||
2eb969f7ee | |||
1196f178e8 | |||
aaeecc1429 | |||
df33a1803e | |||
dc59bb53de | |||
8fdd54eaad | |||
11f44560bb | |||
1015254d93 | |||
dfa0c14bb0 | |||
bbb643e371 | |||
7d3c24d712 | |||
e1433c3c2a | |||
90d6685add | |||
9fd233a8dc | |||
a40eb5d6e4 | |||
fab0d412fa | |||
c5287da78c | |||
37726bee0f | |||
10f15185e0 | |||
c90cd41e23 | |||
c8261e5df6 | |||
34ae8e55c3 | |||
f9368c0729 | |||
b4c51007ab | |||
a17253c482 | |||
e348b003b1 | |||
23fbb68cfc | |||
4a4e75ee59 | |||
9aae856ac8 | |||
94c2c6e550 | |||
0b1f295480 | |||
a64d3902fd | |||
8dd1de1018 | |||
3fdbb92b93 | |||
cf98883633 | |||
7b32b97203 | |||
7f58be4568 | |||
099984f97c | |||
8bfb40ce54 | |||
d60a65c9f0 | |||
9cd2853e49 | |||
3d5554d9ce | |||
764ccfa78e | |||
0b8ebecfea |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,10 +3,11 @@
|
|||||||
.coverage
|
.coverage
|
||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
|
*.js.map
|
||||||
|
*.css.map
|
||||||
|
|
||||||
/cloud/templates/
|
/cloud/templates/
|
||||||
/cloud/static/assets/css/
|
/cloud/static/assets/
|
||||||
/cloud/static/assets/js/bootstrap.min.js
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
/config_local.py
|
/config_local.py
|
||||||
@@ -20,6 +21,7 @@ node_modules/
|
|||||||
/docker/2_buildpy/python/
|
/docker/2_buildpy/python/
|
||||||
/docker/4_run/wheelhouse/
|
/docker/4_run/wheelhouse/
|
||||||
/docker/4_run/deploy/
|
/docker/4_run/deploy/
|
||||||
|
/docker/4_run/staging/
|
||||||
/celerybeat-schedule.bak
|
/celerybeat-schedule.bak
|
||||||
/celerybeat-schedule.dat
|
/celerybeat-schedule.dat
|
||||||
/celerybeat-schedule.dir
|
/celerybeat-schedule.dir
|
||||||
|
30
README.md
30
README.md
@@ -27,14 +27,19 @@ git clone git://git.blender.org/blender-cloud.git
|
|||||||
|
|
||||||
### Initial setup and configuration
|
### Initial setup and configuration
|
||||||
|
|
||||||
Create a virtualenv for the project and install the requirements:
|
Create a virtualenv for the project and install the requirements. Dependencies are managed via
|
||||||
|
[Poetry](https://poetry.eustace.io/). Install it using `pip install -U --user poetry`.
|
||||||
|
|
||||||
```
|
```
|
||||||
cd blender-cloud
|
cd blender-cloud
|
||||||
mkvirtualenv blender-cloud -p python3.6
|
pip install --user -U poetry
|
||||||
pip install -r requirements-dev.txt
|
poetry install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE: After a dependency changed its own dependencies (say a new library was added as dependency of
|
||||||
|
Pillar), you need to run `poetry update`. This will take the new dependencies into account and write
|
||||||
|
them to the `poetry.lock` file.
|
||||||
|
|
||||||
Build assets and templates for all Blender Cloud dependencies using Gulp.
|
Build assets and templates for all Blender Cloud dependencies using Gulp.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -51,7 +56,7 @@ cp config_local.example.py config_local.py
|
|||||||
Setup the database with the initial collections and the admin user.
|
Setup the database with the initial collections and the admin user.
|
||||||
|
|
||||||
```
|
```
|
||||||
./manage.py setup setup_db your_email
|
poetry run ./manage.py setup setup_db your_email
|
||||||
```
|
```
|
||||||
|
|
||||||
The command will return the following message:
|
The command will return the following message:
|
||||||
@@ -65,7 +70,7 @@ Copy the value of `<project_id>` and assign it as value for `MAIN_PROJECT_ID`.
|
|||||||
Run the application:
|
Run the application:
|
||||||
|
|
||||||
```
|
```
|
||||||
./manage.py runserver
|
poetry run ./manage.py runserver
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -102,7 +107,7 @@ git stash # if you still have local stuff.
|
|||||||
|
|
||||||
# pull from master, run unittests, push your changes to master.
|
# pull from master, run unittests, push your changes to master.
|
||||||
git pull
|
git pull
|
||||||
py.test
|
poetry run py.test
|
||||||
git push
|
git push
|
||||||
|
|
||||||
# Switch to production branch, and investigate the situation.
|
# Switch to production branch, and investigate the situation.
|
||||||
@@ -112,7 +117,7 @@ git prod
|
|||||||
git ff master
|
git ff master
|
||||||
|
|
||||||
# Run tests again
|
# Run tests again
|
||||||
py.test
|
poetry run py.test
|
||||||
|
|
||||||
# Push the production branch.
|
# Push the production branch.
|
||||||
git push
|
git push
|
||||||
@@ -120,13 +125,4 @@ git push
|
|||||||
|
|
||||||
## Deploying to production server
|
## Deploying to production server
|
||||||
|
|
||||||
```
|
See [deploy/README.md](deploy/README.md).
|
||||||
workon blender-cloud # activate your virtualenv
|
|
||||||
cd $projectdir/deploy
|
|
||||||
./2docker.sh
|
|
||||||
./build-all.sh # or ./build-quick.sh
|
|
||||||
./2server.sh servername
|
|
||||||
```
|
|
||||||
|
|
||||||
To deploy another branch than `production`, do `export DEPLOY_BRANCH=otherbranch` before starting
|
|
||||||
the above commands.
|
|
||||||
|
@@ -3,6 +3,8 @@ import logging
|
|||||||
import flask
|
import flask
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
|
|
||||||
|
import pillarsdk
|
||||||
|
import pillar.auth
|
||||||
from pillar.api.utils import authorization
|
from pillar.api.utils import authorization
|
||||||
from pillar.extension import PillarExtension
|
from pillar.extension import PillarExtension
|
||||||
|
|
||||||
@@ -41,6 +43,9 @@ class CloudExtension(PillarExtension):
|
|||||||
'EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER': 'https://store.blender.org/api/',
|
'EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER': 'https://store.blender.org/api/',
|
||||||
'EXTERNAL_SUBSCRIPTIONS_TIMEOUT_SECS': 10,
|
'EXTERNAL_SUBSCRIPTIONS_TIMEOUT_SECS': 10,
|
||||||
'BLENDER_ID_WEBHOOK_USER_CHANGED_SECRET': 'oos9wah1Zoa0Yau6ahThohleiChephoi',
|
'BLENDER_ID_WEBHOOK_USER_CHANGED_SECRET': 'oos9wah1Zoa0Yau6ahThohleiChephoi',
|
||||||
|
'NODE_TAGS': ['animation', 'modeling', 'rigging', 'sculpting', 'shading', 'texturing', 'lighting',
|
||||||
|
'character-pipeline', 'effects', 'video-editing', 'digital-painting', 'production-design',
|
||||||
|
'walk-through'],
|
||||||
}
|
}
|
||||||
|
|
||||||
def eve_settings(self):
|
def eve_settings(self):
|
||||||
@@ -84,6 +89,54 @@ class CloudExtension(PillarExtension):
|
|||||||
'current_user_is_subscriber': authorization.user_has_cap('subscriber')
|
'current_user_is_subscriber': authorization.user_has_cap('subscriber')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def is_cloud_project(self, project):
|
||||||
|
"""Returns whether the project is set up for Blender Cloud.
|
||||||
|
|
||||||
|
Requires the presence of the 'cloud' key in extension_props
|
||||||
|
"""
|
||||||
|
|
||||||
|
if project.extension_props is None:
|
||||||
|
# There are no extension_props on this project
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
pprops = project.extension_props[EXTENSION_NAME]
|
||||||
|
except AttributeError:
|
||||||
|
self._log.warning("is_cloud_project: Project url=%r doesn't have any "
|
||||||
|
"extension properties.", project['url'])
|
||||||
|
if self._log.isEnabledFor(logging.DEBUG):
|
||||||
|
import pprint
|
||||||
|
self._log.debug('Project: %s', pprint.pformat(project.to_dict()))
|
||||||
|
return False
|
||||||
|
except KeyError:
|
||||||
|
# Not set up for Blender Cloud
|
||||||
|
return False
|
||||||
|
|
||||||
|
if pprops is None:
|
||||||
|
self._log.debug("is_cloud_project: Project url=%r doesn't have Blender Cloud"
|
||||||
|
" extension properties.", project['url'])
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_project_settings(self) -> bool:
|
||||||
|
# Only available for admins
|
||||||
|
return pillar.auth.current_user.has_cap('admin')
|
||||||
|
|
||||||
|
def project_settings(self, project: pillarsdk.Project, **template_args: dict) -> flask.Response:
|
||||||
|
"""Renders the project settings page for this extension.
|
||||||
|
|
||||||
|
Set YourExtension.has_project_settings = True and Pillar will call this function.
|
||||||
|
|
||||||
|
:param project: the project for which to render the settings.
|
||||||
|
:param template_args: additional template arguments.
|
||||||
|
:returns: a Flask HTTP response
|
||||||
|
"""
|
||||||
|
|
||||||
|
from cloud.routes import project_settings
|
||||||
|
|
||||||
|
return project_settings(project, **template_args)
|
||||||
|
|
||||||
def setup_app(self, app):
|
def setup_app(self, app):
|
||||||
from . import routes, webhooks, eve_hooks, email
|
from . import routes, webhooks, eve_hooks, email
|
||||||
|
|
||||||
|
10
cloud/cli.py
10
cloud/cli.py
@@ -9,6 +9,8 @@ import requests
|
|||||||
|
|
||||||
from pillar.cli import manager
|
from pillar.cli import manager
|
||||||
from pillar.api import service
|
from pillar.api import service
|
||||||
|
from pillar.api.utils import authentication
|
||||||
|
import cloud.setup
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -126,4 +128,12 @@ def reconcile_subscribers():
|
|||||||
log.info(' skipped : %d', count_skipped)
|
log.info(' skipped : %d', count_skipped)
|
||||||
|
|
||||||
|
|
||||||
|
@manager_cloud.command
|
||||||
|
def setup_for_film(project_url):
|
||||||
|
"""Adds Blender Cloud film custom properties to a project."""
|
||||||
|
|
||||||
|
authentication.force_cli_user()
|
||||||
|
cloud.setup.setup_for_film(project_url)
|
||||||
|
|
||||||
|
|
||||||
manager.add_command("cloud", manager_cloud)
|
manager.add_command("cloud", manager_cloud)
|
||||||
|
15
cloud/forms.py
Normal file
15
cloud/forms.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import BooleanField, StringField
|
||||||
|
from wtforms.fields.html5 import URLField
|
||||||
|
from wtforms.validators import URL
|
||||||
|
|
||||||
|
from pillar.web.utils.forms import FileSelectField
|
||||||
|
|
||||||
|
|
||||||
|
class FilmProjectForm(FlaskForm):
|
||||||
|
video_url = URLField(validators=[URL()])
|
||||||
|
poster = FileSelectField('Poster Image', file_format='image')
|
||||||
|
logo = FileSelectField('Logo', file_format='image')
|
||||||
|
is_in_production = BooleanField('In Production')
|
||||||
|
is_featured = BooleanField('Featured')
|
||||||
|
theme_color = StringField('Theme Color')
|
346
cloud/routes.py
346
cloud/routes.py
@@ -3,23 +3,32 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from flask_login import current_user, login_required
|
import bson
|
||||||
|
from flask_login import login_required
|
||||||
import flask
|
import flask
|
||||||
from flask import Blueprint, render_template, redirect, session, url_for, abort, flash
|
import werkzeug.exceptions as wz_exceptions
|
||||||
|
from flask import Blueprint, render_template, redirect, session, url_for, abort, flash, request
|
||||||
from pillarsdk import Node, Project, User, exceptions as sdk_exceptions, Group
|
from pillarsdk import Node, Project, User, exceptions as sdk_exceptions, Group
|
||||||
from pillarsdk.exceptions import ResourceNotFound
|
from pillarsdk.exceptions import ResourceNotFound
|
||||||
|
|
||||||
|
import pillar
|
||||||
|
import pillarsdk
|
||||||
from pillar import current_app
|
from pillar import current_app
|
||||||
import pillar.api
|
from pillar.api.utils import authorization
|
||||||
|
from pillar.auth import current_user
|
||||||
from pillar.web.users import forms
|
from pillar.web.users import forms
|
||||||
from pillar.web.utils import system_util, get_file, current_user_is_authenticated
|
from pillar.web.utils import system_util, get_file, current_user_is_authenticated
|
||||||
from pillar.web.utils import attach_project_pictures
|
from pillar.web.utils import attach_project_pictures
|
||||||
from pillar.web.settings import blueprint as blueprint_settings
|
from pillar.web.settings import blueprint as blueprint_settings
|
||||||
from pillar.web.nodes.routes import url_for_node
|
from pillar.web.nodes.routes import url_for_node
|
||||||
from pillar.web.nodes.custom.comments import render_comments_for_node
|
|
||||||
from pillar.web.projects.routes import render_project
|
from pillar.web.projects.routes import render_project
|
||||||
from pillar.web.projects.routes import find_project_or_404
|
from pillar.web.projects.routes import find_project_or_404
|
||||||
|
from pillar.web.projects.routes import project_view
|
||||||
|
from pillar.web.projects.routes import project_navigation_links
|
||||||
|
|
||||||
|
from cloud import current_cloud
|
||||||
|
from cloud.forms import FilmProjectForm
|
||||||
|
from . import EXTENSION_NAME
|
||||||
|
|
||||||
blueprint = Blueprint('cloud', __name__)
|
blueprint = Blueprint('cloud', __name__)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -42,35 +51,6 @@ def _homepage_context() -> dict:
|
|||||||
|
|
||||||
# Get latest blog posts
|
# Get latest blog posts
|
||||||
api = system_util.pillar_api()
|
api = system_util.pillar_api()
|
||||||
latest_posts = Node.all({
|
|
||||||
'projection': {
|
|
||||||
'name': 1,
|
|
||||||
'project': 1,
|
|
||||||
'node_type': 1,
|
|
||||||
'picture': 1,
|
|
||||||
'properties.url': 1,
|
|
||||||
'properties.content': 1,
|
|
||||||
'properties.attachments': 1
|
|
||||||
},
|
|
||||||
|
|
||||||
'where': {'node_type': 'post', 'properties.status': 'published'},
|
|
||||||
'embedded': {'project': 1},
|
|
||||||
'sort': '-_created',
|
|
||||||
'max_results': '3'
|
|
||||||
}, api=api)
|
|
||||||
|
|
||||||
# Append picture Files to last_posts
|
|
||||||
for post in latest_posts._items:
|
|
||||||
post.picture = get_file(post.picture, api=api)
|
|
||||||
post.url = url_for_node(node=post)
|
|
||||||
|
|
||||||
# Get latest assets added to any project
|
|
||||||
latest_assets = Node.latest('assets', api=api)
|
|
||||||
|
|
||||||
# Append picture Files to latest_assets
|
|
||||||
for asset in latest_assets._items:
|
|
||||||
asset.picture = get_file(asset.picture, api=api)
|
|
||||||
asset.url = url_for_node(node=asset)
|
|
||||||
|
|
||||||
# Get latest comments to any node
|
# Get latest comments to any node
|
||||||
latest_comments = Node.latest('comments', api=api)
|
latest_comments = Node.latest('comments', api=api)
|
||||||
@@ -89,6 +69,7 @@ def _homepage_context() -> dict:
|
|||||||
'name': 1,
|
'name': 1,
|
||||||
'node_type': 1,
|
'node_type': 1,
|
||||||
'project': 1,
|
'project': 1,
|
||||||
|
'parent': 1,
|
||||||
'properties.url': 1,
|
'properties.url': 1,
|
||||||
}},
|
}},
|
||||||
api=api)
|
api=api)
|
||||||
@@ -115,23 +96,24 @@ def _homepage_context() -> dict:
|
|||||||
main_project = Project.find(current_app.config['MAIN_PROJECT_ID'], api=api)
|
main_project = Project.find(current_app.config['MAIN_PROJECT_ID'], api=api)
|
||||||
main_project.picture_header = get_file(main_project.picture_header, api=api)
|
main_project.picture_header = get_file(main_project.picture_header, api=api)
|
||||||
|
|
||||||
# Merge latest assets and comments into one activity stream.
|
|
||||||
def sort_key(item):
|
|
||||||
return item._created
|
|
||||||
|
|
||||||
activity_stream = sorted(latest_assets._items, key=sort_key, reverse=True)
|
|
||||||
|
|
||||||
for node in activity_stream:
|
|
||||||
node.url = url_for_node(node=node)
|
|
||||||
|
|
||||||
return dict(
|
return dict(
|
||||||
main_project=main_project,
|
main_project=main_project,
|
||||||
latest_posts=latest_posts._items,
|
|
||||||
latest_comments=latest_comments._items,
|
latest_comments=latest_comments._items,
|
||||||
activity_stream=activity_stream,
|
|
||||||
random_featured=random_featured)
|
random_featured=random_featured)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/design-system')
|
||||||
|
def design_system():
|
||||||
|
"""Display the design system page.
|
||||||
|
|
||||||
|
This endpoing is intended for development only, and returns a
|
||||||
|
rendered template only if the app is running in debug mode.
|
||||||
|
"""
|
||||||
|
if not current_app.config['DEBUG']:
|
||||||
|
abort(404)
|
||||||
|
return render_template('design_system.html')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/login')
|
@blueprint.route('/login')
|
||||||
def login():
|
def login():
|
||||||
from flask import request
|
from flask import request
|
||||||
@@ -169,6 +151,16 @@ def services():
|
|||||||
return render_template('services.html')
|
return render_template('services.html')
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/learn')
|
||||||
|
def learn():
|
||||||
|
return render_template('learn.html')
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/libraries')
|
||||||
|
def libraries():
|
||||||
|
return render_template('libraries.html')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/stats')
|
@blueprint.route('/stats')
|
||||||
def stats():
|
def stats():
|
||||||
return render_template('stats.html')
|
return render_template('stats.html')
|
||||||
@@ -219,10 +211,35 @@ def courses():
|
|||||||
def open_projects():
|
def open_projects():
|
||||||
@current_app.cache.cached(timeout=3600, unless=current_user_is_authenticated)
|
@current_app.cache.cached(timeout=3600, unless=current_user_is_authenticated)
|
||||||
def render_page():
|
def render_page():
|
||||||
projects = get_projects('film')
|
api = system_util.pillar_api()
|
||||||
|
projects = Project.all({
|
||||||
|
'where': {
|
||||||
|
'category': 'film',
|
||||||
|
'is_private': False
|
||||||
|
},
|
||||||
|
'sort': '-_created',
|
||||||
|
}, api=api)
|
||||||
|
for project in projects._items:
|
||||||
|
# Attach poster file (ensure the extension_props.cloud.poster
|
||||||
|
# attributes exists)
|
||||||
|
try:
|
||||||
|
# If the attribute exists, but is None, continue
|
||||||
|
if not project['extension_props'][EXTENSION_NAME]['poster']:
|
||||||
|
continue
|
||||||
|
# Fetch the file and embed it in the document
|
||||||
|
project.extension_props.cloud.poster = get_file(
|
||||||
|
project.extension_props.cloud.poster, api=api)
|
||||||
|
# Add convenience attribute that specifies the presence of the
|
||||||
|
# poster file
|
||||||
|
project.has_poster = True
|
||||||
|
# If there was a key error because one of the nested attributes is
|
||||||
|
# missing,
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'projects_index_collection.html',
|
'films.html',
|
||||||
title='open-projects',
|
title='films',
|
||||||
projects=projects._items,
|
projects=projects._items,
|
||||||
api=system_util.pillar_api())
|
api=system_util.pillar_api())
|
||||||
|
|
||||||
@@ -261,12 +278,13 @@ def get_random_featured_nodes() -> typing.List[dict]:
|
|||||||
'summary': True,
|
'summary': True,
|
||||||
'picture_square': True}},
|
'picture_square': True}},
|
||||||
{'$unwind': {'path': '$nodes_featured'}},
|
{'$unwind': {'path': '$nodes_featured'}},
|
||||||
{'$sample': {'size': 3}},
|
{'$sample': {'size': 6}},
|
||||||
{'$lookup': {'from': 'nodes',
|
{'$lookup': {'from': 'nodes',
|
||||||
'localField': 'nodes_featured',
|
'localField': 'nodes_featured',
|
||||||
'foreignField': '_id',
|
'foreignField': '_id',
|
||||||
'as': 'node'}},
|
'as': 'node'}},
|
||||||
{'$unwind': {'path': '$node'}},
|
{'$unwind': {'path': '$node'}},
|
||||||
|
{'$match': {'node._deleted': {'$ne': True}}},
|
||||||
{'$project': {'url': True,
|
{'$project': {'url': True,
|
||||||
'name': True,
|
'name': True,
|
||||||
'summary': True,
|
'summary': True,
|
||||||
@@ -276,7 +294,11 @@ def get_random_featured_nodes() -> typing.List[dict]:
|
|||||||
'node.permissions': True,
|
'node.permissions': True,
|
||||||
'node.picture': True,
|
'node.picture': True,
|
||||||
'node.properties.content_type': True,
|
'node.properties.content_type': True,
|
||||||
'node.properties.url': True}},
|
'node.properties.duration_seconds': True,
|
||||||
|
'node.properties.url': True,
|
||||||
|
'node._created': True,
|
||||||
|
}
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
featured_node_documents = []
|
featured_node_documents = []
|
||||||
@@ -285,11 +307,10 @@ def get_random_featured_nodes() -> typing.List[dict]:
|
|||||||
# Turn the project-with-node doc into a node-with-project doc.
|
# Turn the project-with-node doc into a node-with-project doc.
|
||||||
node_document = node_info.pop('node')
|
node_document = node_info.pop('node')
|
||||||
node_document['project'] = node_info
|
node_document['project'] = node_info
|
||||||
|
node_document['_id'] = str(node_document['_id'])
|
||||||
|
|
||||||
node = Node(node_document)
|
node = Node(node_document)
|
||||||
node.picture = get_file(node.picture, api=api)
|
node.picture = get_file(node.picture, api=api)
|
||||||
node.url = url_for_node(node=node)
|
|
||||||
node.project.url = url_for('projects.view', project_url=node.project.url)
|
|
||||||
node.project.picture_square = get_file(node.project.picture_square, api=api)
|
node.project.picture_square = get_file(node.project.picture_square, api=api)
|
||||||
featured_node_documents.append(node)
|
featured_node_documents.append(node)
|
||||||
|
|
||||||
@@ -391,6 +412,13 @@ def privacy():
|
|||||||
return render_template('privacy.html')
|
return render_template('privacy.html')
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/production')
|
||||||
|
def production():
|
||||||
|
return render_template(
|
||||||
|
'production.html',
|
||||||
|
title='production')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/emails/welcome.send')
|
@blueprint.route('/emails/welcome.send')
|
||||||
@login_required
|
@login_required
|
||||||
def emails_welcome_send():
|
def emails_welcome_send():
|
||||||
@@ -416,32 +444,27 @@ def emails_welcome_txt():
|
|||||||
return flask.Response(txt, content_type='text/plain; charset=utf-8')
|
return flask.Response(txt, content_type='text/plain; charset=utf-8')
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/nodes/<string(length=24):node_id>/comments')
|
@blueprint.route('/p/<project_url>')
|
||||||
def comments_for_node(node_id):
|
def project_landing(project_url):
|
||||||
"""Overrides the default render_comments_for_node.
|
"""Override Pillar project_view endpoint completely.
|
||||||
|
|
||||||
This is done in order to extend can_post_comments by requiring the
|
The first part of the function is identical to the one in Pillar, but the
|
||||||
subscriber capability.
|
second part (starting with 'Load custom project properties') extends the
|
||||||
|
behaviour to support film project landing pages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
template_name = None
|
||||||
|
if request.args.get('format') == 'jstree':
|
||||||
|
log.warning('projects.view(%r) endpoint called with format=jstree, '
|
||||||
|
'redirecting to proper endpoint. URL is %s; referrer is %s',
|
||||||
|
project_url, request.url, request.referrer)
|
||||||
|
return redirect(url_for('projects.jstree', project_url=project_url))
|
||||||
|
|
||||||
api = system_util.pillar_api()
|
api = system_util.pillar_api()
|
||||||
|
project = find_project_or_404(project_url,
|
||||||
node = Node.find(node_id, api=api)
|
|
||||||
project = Project({'_id': node.project})
|
|
||||||
can_post_comments = project.node_type_has_method('comment', 'POST', api=api)
|
|
||||||
can_comment_override = flask.request.args.get('can_comment', 'True') == 'True'
|
|
||||||
can_post_comments = can_post_comments and can_comment_override and current_user.has_cap(
|
|
||||||
'subscriber')
|
|
||||||
|
|
||||||
return render_comments_for_node(node_id, can_post_comments=can_post_comments)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/p/hero')
|
|
||||||
def project_hero():
|
|
||||||
api = system_util.pillar_api()
|
|
||||||
project = find_project_or_404('hero',
|
|
||||||
embedded={'header_node': 1},
|
embedded={'header_node': 1},
|
||||||
api=api)
|
api=api)
|
||||||
|
|
||||||
# Load the header video file, if there is any.
|
# Load the header video file, if there is any.
|
||||||
header_video_file = None
|
header_video_file = None
|
||||||
header_video_node = None
|
header_video_node = None
|
||||||
@@ -451,15 +474,182 @@ def project_hero():
|
|||||||
header_video_file = get_file(project.header_node.properties.file)
|
header_video_file = get_file(project.header_node.properties.file)
|
||||||
header_video_node.picture = get_file(header_video_node.picture)
|
header_video_node.picture = get_file(header_video_node.picture)
|
||||||
|
|
||||||
pages = Node.all({
|
extra_context = {
|
||||||
'where': {'project': project._id, 'node_type': 'page'},
|
'header_video_file': header_video_file,
|
||||||
'projection': {'name': 1}}, api=api)
|
'header_video_node': header_video_node}
|
||||||
|
|
||||||
|
# Load custom project properties. If the project has a 'cloud' extension prop,
|
||||||
|
# render it using the projects/landing.html template and try to attach a
|
||||||
|
# number of additional attributes (pages, images, etc.).
|
||||||
|
if 'extension_props' in project and EXTENSION_NAME in project['extension_props']:
|
||||||
|
extension_props = project['extension_props'][EXTENSION_NAME]
|
||||||
|
extension_props['logo'] = get_file(extension_props['logo'])
|
||||||
|
|
||||||
|
pages = Node.all({
|
||||||
|
'where': {
|
||||||
|
'project': project._id,
|
||||||
|
'node_type': 'page',
|
||||||
|
'_deleted': {'$ne': True}},
|
||||||
|
'projection': {'name': 1}}, api=api)
|
||||||
|
|
||||||
|
extra_context.update({'pages': pages._items})
|
||||||
|
template_name = 'projects/landing.html'
|
||||||
|
|
||||||
return render_project(project, api,
|
return render_project(project, api,
|
||||||
extra_context={'header_video_file': header_video_file,
|
extra_context=extra_context,
|
||||||
'header_video_node': header_video_node,
|
template_name=template_name)
|
||||||
'pages': pages._items,},
|
|
||||||
template_name='projects/landing.html')
|
|
||||||
|
@blueprint.route('/p/<project_url>/browse')
|
||||||
|
@project_view()
|
||||||
|
def project_browse(project: pillarsdk.Project):
|
||||||
|
"""Project view displaying all top-level nodes.
|
||||||
|
|
||||||
|
We render a regular project view, but we introduce an additional template
|
||||||
|
variable: browse. By doing that we prevent the regular project view
|
||||||
|
from loading and fetch via AJAX a "group" node-like view instead (see
|
||||||
|
project_browse_view_nodes).
|
||||||
|
"""
|
||||||
|
return render_template(
|
||||||
|
'projects/view.html',
|
||||||
|
api=system_util.pillar_api(),
|
||||||
|
project=project,
|
||||||
|
node=None,
|
||||||
|
show_project=True,
|
||||||
|
browse=True,
|
||||||
|
og_picture=None,
|
||||||
|
navigation_links=project_navigation_links(project, system_util.pillar_api()),
|
||||||
|
extension_sidebar_links=current_app.extension_sidebar_links(project))
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/p/<project_url>/browse/nodes')
|
||||||
|
@project_view()
|
||||||
|
def project_browse_view_nodes(project: pillarsdk.Project):
|
||||||
|
"""Display top-level nodes for a Project.
|
||||||
|
|
||||||
|
This view is always meant to be served embedded, as part of project_browse.
|
||||||
|
"""
|
||||||
|
api = system_util.pillar_api()
|
||||||
|
# Get top level nodes
|
||||||
|
projection = {
|
||||||
|
'project': 1,
|
||||||
|
'name': 1,
|
||||||
|
'picture': 1,
|
||||||
|
'node_type': 1,
|
||||||
|
'properties.order': 1,
|
||||||
|
'properties.status': 1,
|
||||||
|
'user': 1,
|
||||||
|
'properties.content_type': 1,
|
||||||
|
'permissions.world': 1}
|
||||||
|
where = {
|
||||||
|
'project': project['_id'],
|
||||||
|
'parent': {'$exists': False},
|
||||||
|
'properties.status': 'published',
|
||||||
|
'_deleted': {'$ne': True},
|
||||||
|
'node_type': {'$in': ['group', 'asset']},
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
nodes = Node.all({
|
||||||
|
'projection': projection,
|
||||||
|
'where': where,
|
||||||
|
'sort': [('properties.order', 1), ('name', 1)]}, api=api)
|
||||||
|
except pillarsdk.exceptions.ForbiddenAccess:
|
||||||
|
return render_template('errors/403_embed.html')
|
||||||
|
nodes = nodes._items
|
||||||
|
|
||||||
|
for child in nodes:
|
||||||
|
child.picture = get_file(child.picture, api=api)
|
||||||
|
return render_template(
|
||||||
|
'projects/browse_embed.html',
|
||||||
|
nodes=nodes)
|
||||||
|
|
||||||
|
|
||||||
|
def project_settings(project: pillarsdk.Project, **template_args: dict):
|
||||||
|
"""Renders the project settings page for Blender Cloud projects.
|
||||||
|
|
||||||
|
If the project has been setup for Blender Cloud, check for the cloud.category
|
||||||
|
property, to render the proper form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Based on the project state, we can render a different template.
|
||||||
|
if not current_cloud.is_cloud_project(project):
|
||||||
|
return render_template('project_settings/offer_setup.html',
|
||||||
|
project=project, **template_args)
|
||||||
|
|
||||||
|
cloud_props = project['extension_props'][EXTENSION_NAME]
|
||||||
|
|
||||||
|
category = cloud_props['category']
|
||||||
|
if category != 'film':
|
||||||
|
log.error('No interface available to edit %s projects, yet' % category)
|
||||||
|
|
||||||
|
form = FilmProjectForm()
|
||||||
|
|
||||||
|
# Iterate over the form fields and set the data if exists in the project document
|
||||||
|
for field_name in form.data:
|
||||||
|
if field_name not in cloud_props:
|
||||||
|
continue
|
||||||
|
# Skip csrf_token field
|
||||||
|
if field_name == 'csrf_token':
|
||||||
|
continue
|
||||||
|
form_field = getattr(form, field_name)
|
||||||
|
form_field.data = cloud_props[field_name]
|
||||||
|
|
||||||
|
return render_template('project_settings/settings.html',
|
||||||
|
project=project,
|
||||||
|
form=form,
|
||||||
|
**template_args)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/<project_url>/settings/film', methods=['POST'])
|
||||||
|
@authorization.require_login(require_cap='admin')
|
||||||
|
@project_view()
|
||||||
|
def save_film_settings(project: pillarsdk.Project):
|
||||||
|
# Ensure that the project is setup for Cloud (see @attract_project_view for example)
|
||||||
|
form = FilmProjectForm()
|
||||||
|
if not form.validate_on_submit():
|
||||||
|
log.debug('Form submission failed')
|
||||||
|
# Return list of validation errors
|
||||||
|
|
||||||
|
updated_extension_props = {}
|
||||||
|
for field_name in form.data:
|
||||||
|
# Skip csrf_token field
|
||||||
|
if field_name == 'csrf_token':
|
||||||
|
continue
|
||||||
|
form_field = getattr(form, field_name)
|
||||||
|
# TODO(fsiddi) if form_field type is FileSelectField, convert it to ObjectId
|
||||||
|
# Currently this raises TypeError: Object of type 'ObjectId' is not JSON serializable
|
||||||
|
|
||||||
|
if form_field.data == '':
|
||||||
|
form_field.data = None
|
||||||
|
updated_extension_props[field_name] = form_field.data
|
||||||
|
|
||||||
|
# Update extension props and save project
|
||||||
|
extension_props = project['extension_props'][EXTENSION_NAME]
|
||||||
|
# Project is a Resource, so we update properties iteratively
|
||||||
|
for k, v in updated_extension_props.items():
|
||||||
|
extension_props[k] = v
|
||||||
|
project.update(api=system_util.pillar_api())
|
||||||
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/<project_url>/setup-for-film', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@project_view()
|
||||||
|
def setup_for_film(project: pillarsdk.Project):
|
||||||
|
import cloud.setup
|
||||||
|
|
||||||
|
project_id = project._id
|
||||||
|
|
||||||
|
if not project.has_method('PUT'):
|
||||||
|
log.warning('User %s tries to set up project %s for Blender Cloud, but has no PUT rights.',
|
||||||
|
current_user, project_id)
|
||||||
|
raise wz_exceptions.Forbidden()
|
||||||
|
|
||||||
|
log.info('User %s sets up project %s for Blender Cloud', current_user, project_id)
|
||||||
|
cloud.setup.setup_for_film(project.url)
|
||||||
|
|
||||||
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
def setup_app(app):
|
def setup_app(app):
|
||||||
|
54
cloud/setup.py
Normal file
54
cloud/setup.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
"""Setting up projects for Blender Cloud."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from bson import ObjectId
|
||||||
|
from eve.methods.put import put_internal
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from pillar.api.utils import remove_private_keys
|
||||||
|
from . import EXTENSION_NAME
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_for_film(project_url):
|
||||||
|
"""Add Blender Cloud extension_props specific for film projects.
|
||||||
|
|
||||||
|
Returns the updated project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
projects_collection = current_app.data.driver.db['projects']
|
||||||
|
|
||||||
|
# Find the project in the database.
|
||||||
|
project = projects_collection.find_one({'url': project_url})
|
||||||
|
if not project:
|
||||||
|
raise RuntimeError('Project %s does not exist.' % project_url)
|
||||||
|
|
||||||
|
# Set default extension properties. Be careful not to overwrite any properties that
|
||||||
|
# are already there.
|
||||||
|
all_extension_props = project.setdefault('extension_props', {})
|
||||||
|
cloud_extension_props = {
|
||||||
|
'category': 'film',
|
||||||
|
'theme_css': '',
|
||||||
|
# The accent color (can be 'blue' or '#FFBBAA' or 'rgba(1, 1, 1, 1)
|
||||||
|
'theme_color': '',
|
||||||
|
'is_in_production': False,
|
||||||
|
'video_url': '', # Oembeddable url
|
||||||
|
'poster': None, # File ObjectId
|
||||||
|
'logo': None, # File ObjectId
|
||||||
|
# TODO(fsiddi) when we introduce other setup_for_* in Blender Cloud, make available
|
||||||
|
# at a higher scope
|
||||||
|
'is_featured': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
all_extension_props.setdefault(EXTENSION_NAME, cloud_extension_props)
|
||||||
|
|
||||||
|
project_id = ObjectId(project['_id'])
|
||||||
|
project = remove_private_keys(project)
|
||||||
|
result, _, _, status_code = put_internal('projects', project, _id=project_id)
|
||||||
|
|
||||||
|
if status_code != 200:
|
||||||
|
raise RuntimeError("Can't update project %s, issues: %s", project_id, result)
|
||||||
|
|
||||||
|
log.info('Project %s was updated for Blender Cloud.', project_url)
|
3
cloud/tagged/__init__.py
Normal file
3
cloud/tagged/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""Routes for fetching tagged assets."""
|
||||||
|
|
||||||
|
|
16
cloud/tagged/routes.py
Normal file
16
cloud/tagged/routes.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from flask import Blueprint, jsonify
|
||||||
|
|
||||||
|
blueprint = Blueprint('cloud.tagged', __name__, url_prefix='/tagged')
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/')
|
||||||
|
def index():
|
||||||
|
"""Return all tagged assets as JSON, grouped by tag."""
|
||||||
|
|
||||||
|
|
@@ -7,8 +7,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from flask_login import request
|
from flask import Blueprint, request
|
||||||
from flask import Blueprint
|
|
||||||
import werkzeug.exceptions as wz_exceptions
|
import werkzeug.exceptions as wz_exceptions
|
||||||
|
|
||||||
from pillar import current_app
|
from pillar import current_app
|
||||||
@@ -102,8 +101,8 @@ def insert_or_fetch_user(wh_payload: dict) -> typing.Optional[dict]:
|
|||||||
{'auth.provider': 'blender-id', 'auth.user_id': bid_str},
|
{'auth.provider': 'blender-id', 'auth.user_id': bid_str},
|
||||||
{'email': {'$in': [wh_payload['old_email'], email]}},
|
{'email': {'$in': [wh_payload['old_email'], email]}},
|
||||||
]}
|
]}
|
||||||
db_users = users_coll.find(query)
|
db_users = list(users_coll.find(query))
|
||||||
user_count = db_users.count()
|
user_count = len(db_users)
|
||||||
if user_count > 1:
|
if user_count > 1:
|
||||||
# Now we have to pay the price for finding users in one query; we
|
# Now we have to pay the price for finding users in one query; we
|
||||||
# have to prioritise them and return the one we think is most reliable.
|
# have to prioritise them and return the one we think is most reliable.
|
||||||
@@ -118,6 +117,10 @@ def insert_or_fetch_user(wh_payload: dict) -> typing.Optional[dict]:
|
|||||||
my_log.debug('found user %s', db_user['email'])
|
my_log.debug('found user %s', db_user['email'])
|
||||||
return db_user
|
return db_user
|
||||||
|
|
||||||
|
if wh_payload.get('date_deletion_requested'):
|
||||||
|
my_log.info('Received update for a deleted user %s, not creating', bid_str)
|
||||||
|
return None
|
||||||
|
|
||||||
# Pretend to create the user, so that we can inspect the resulting
|
# Pretend to create the user, so that we can inspect the resulting
|
||||||
# capabilities. This is more future-proof than looking at the list
|
# capabilities. This is more future-proof than looking at the list
|
||||||
# of roles in the webhook payload.
|
# of roles in the webhook payload.
|
||||||
@@ -165,6 +168,7 @@ def user_modified():
|
|||||||
'old_email': 'old@example.com',
|
'old_email': 'old@example.com',
|
||||||
'full_name': 'Harry',
|
'full_name': 'Harry',
|
||||||
'email': 'new@example'com,
|
'email': 'new@example'com,
|
||||||
|
'avatar_changed': True,
|
||||||
'roles': ['role1', 'role2', …]}
|
'roles': ['role1', 'role2', …]}
|
||||||
"""
|
"""
|
||||||
my_log = log.getChild('user_modified')
|
my_log = log.getChild('user_modified')
|
||||||
@@ -181,6 +185,10 @@ def user_modified():
|
|||||||
my_log.info('Received update for unknown user %r', payload['old_email'])
|
my_log.info('Received update for unknown user %r', payload['old_email'])
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
if payload.get('date_deletion_requested'):
|
||||||
|
delete_user(db_user, payload)
|
||||||
|
return '', 204
|
||||||
|
|
||||||
# Use direct database updates to change the email and full name.
|
# Use direct database updates to change the email and full name.
|
||||||
# Also updates the db_user dict so that local_user below will have
|
# Also updates the db_user dict so that local_user below will have
|
||||||
# the updated information.
|
# the updated information.
|
||||||
@@ -200,6 +208,11 @@ def user_modified():
|
|||||||
updates['full_name'] = db_user['username']
|
updates['full_name'] = db_user['username']
|
||||||
db_user['full_name'] = updates['full_name']
|
db_user['full_name'] = updates['full_name']
|
||||||
|
|
||||||
|
if payload.get('avatar_changed'):
|
||||||
|
import pillar.celery.avatar
|
||||||
|
my_log.info('User %s changed avatar, scheduling download', db_user['_id'])
|
||||||
|
pillar.celery.avatar.sync_avatar_for_user.delay(str(db_user['_id']))
|
||||||
|
|
||||||
if updates:
|
if updates:
|
||||||
users_coll = current_app.db('users')
|
users_coll = current_app.db('users')
|
||||||
update_res = users_coll.update_one({'_id': db_user['_id']},
|
update_res = users_coll.update_one({'_id': db_user['_id']},
|
||||||
@@ -214,3 +227,37 @@ def user_modified():
|
|||||||
subscription.do_update_subscription(local_user, payload)
|
subscription.do_update_subscription(local_user, payload)
|
||||||
|
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
|
def delete_user(db_user, payload):
|
||||||
|
"""Handle deletion request coming from BID."""
|
||||||
|
my_log = log.getChild('delete_user')
|
||||||
|
date_deletion_requested = payload['date_deletion_requested']
|
||||||
|
bid_str = str(payload['id'])
|
||||||
|
local_id = db_user['_id']
|
||||||
|
my_log.info(
|
||||||
|
'User %s with BID=%s requested deletion on %s, soft-deleting the user',
|
||||||
|
local_id, bid_str, date_deletion_requested,
|
||||||
|
)
|
||||||
|
# Delete all session tokens linked to this user
|
||||||
|
token_coll = current_app.db('tokens')
|
||||||
|
delete_res = token_coll.delete_many({'user': local_id})
|
||||||
|
my_log.info('Deleted %s session tokens of user %s', delete_res.deleted_count, local_id)
|
||||||
|
|
||||||
|
# Soft-delete the user and clear their PII
|
||||||
|
users_coll = current_app.db('users')
|
||||||
|
updates = {
|
||||||
|
'_deleted': True,
|
||||||
|
'email': None,
|
||||||
|
'full_name': None,
|
||||||
|
'username': None,
|
||||||
|
'auth': [],
|
||||||
|
}
|
||||||
|
update_res = users_coll.update_one({'_id': local_id}, {'$set': updates})
|
||||||
|
if update_res.matched_count != 1:
|
||||||
|
my_log.error(
|
||||||
|
'Soft-deleted %s users %s with BID=%s',
|
||||||
|
update_res.matched_count, local_id, bid_str,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
my_log.warning('Soft-deleted user %s with BID=%s', local_id, bid_str)
|
||||||
|
@@ -2,7 +2,7 @@ import os
|
|||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
BLENDER_ID_ENDPOINT = 'http://id.local:8000'
|
BLENDER_ID_ENDPOINT = 'http://id.local:8000/'
|
||||||
|
|
||||||
SERVER_NAME = 'cloud.local:5001'
|
SERVER_NAME = 'cloud.local:5001'
|
||||||
SCHEME = 'http'
|
SCHEME = 'http'
|
||||||
@@ -30,3 +30,14 @@ URLER_SERVICE_AUTH_TOKEN = '##DEFINE##'
|
|||||||
ZENCODER_API_KEY = '##DEFINE##'
|
ZENCODER_API_KEY = '##DEFINE##'
|
||||||
ZENCODER_NOTIFICATIONS_SECRET = '##DEFINE##'
|
ZENCODER_NOTIFICATIONS_SECRET = '##DEFINE##'
|
||||||
ZENCODER_NOTIFICATIONS_URL = 'http://zencoderfetcher/'
|
ZENCODER_NOTIFICATIONS_URL = 'http://zencoderfetcher/'
|
||||||
|
|
||||||
|
# Special announcement on top of every page, for non-subscribers.
|
||||||
|
# category: 'string', can be 'info', 'warning', 'danger', or 'success'.
|
||||||
|
# message: 'string', any text, it gets markdowned.
|
||||||
|
# icon: 'string', any icon in font-pillar. e.g. 'pi-heart-filled'
|
||||||
|
UI_ANNOUNCEMENT_NON_SUBSCRIBERS = {
|
||||||
|
'category': 'danger',
|
||||||
|
'message': 'Spring will swing away the gray clouds, until then, '
|
||||||
|
'[take cover under Blender Cloud](https://cloud.blender.org)!',
|
||||||
|
'icon': 'pi-heart-filled',
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
DEPLOY_BRANCH=${DEPLOY_BRANCH:-production}
|
STAGING_BRANCH=${STAGING_BRANCH:-production}
|
||||||
|
|
||||||
# macOS does not support readlink -f, so we use greadlink instead
|
# macOS does not support readlink -f, so we use greadlink instead
|
||||||
if [[ `uname` == 'Darwin' ]]; then
|
if [[ `uname` == 'Darwin' ]]; then
|
||||||
@@ -11,34 +11,34 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
ROOT="$(dirname "$(dirname "$($readlink -f "$0")")")"
|
ROOT="$(dirname "$(dirname "$($readlink -f "$0")")")"
|
||||||
DEPLOYDIR="$ROOT/docker/4_run/deploy"
|
STAGINGDIR="$ROOT/docker/4_run/staging"
|
||||||
PROJECT_NAME="$(basename $ROOT)"
|
PROJECT_NAME="$(basename $ROOT)"
|
||||||
|
|
||||||
if [ -e $DEPLOYDIR ]; then
|
if [ -e $STAGINGDIR ]; then
|
||||||
echo "$DEPLOYDIR already exists, press [ENTER] to DESTROY AND DEPLOY, Ctrl+C to abort."
|
echo "$STAGINGDIR already exists, press [ENTER] to destroy and re-install, Ctrl+C to abort."
|
||||||
read dummy
|
read dummy
|
||||||
rm -rf $DEPLOYDIR
|
rm -rf $STAGINGDIR
|
||||||
else
|
else
|
||||||
echo -n "Deploying to $DEPLOYDIR… "
|
echo -n "Installing into $STAGINGDIR… "
|
||||||
echo "press [ENTER] to continue, Ctrl+C to abort."
|
echo "press [ENTER] to continue, Ctrl+C to abort."
|
||||||
read dummy
|
read dummy
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd ${ROOT}
|
cd ${ROOT}
|
||||||
mkdir -p $DEPLOYDIR
|
mkdir -p $STAGINGDIR
|
||||||
REMOTE_ROOT="$DEPLOYDIR/$PROJECT_NAME"
|
REMOTE_ROOT="$STAGINGDIR/$PROJECT_NAME"
|
||||||
|
|
||||||
if [ -z "$SKIP_BRANCH_CHECK" ]; then
|
if [ -z "$SKIP_BRANCH_CHECK" ]; then
|
||||||
# Check that we're on production branch.
|
# Check that we're on production branch.
|
||||||
if [ $(git rev-parse --abbrev-ref HEAD) != "$DEPLOY_BRANCH" ]; then
|
if [ $(git rev-parse --abbrev-ref HEAD) != "$STAGING_BRANCH" ]; then
|
||||||
echo "You are NOT on the $DEPLOY_BRANCH branch, refusing to deploy." >&2
|
echo "You are NOT on the $STAGING_BRANCH branch, refusing to stage." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check that production branch has been pushed.
|
# Check that production branch has been pushed.
|
||||||
if [ -n "$(git log origin/$DEPLOY_BRANCH..$DEPLOY_BRANCH --oneline)" ]; then
|
if [ -n "$(git log origin/$STAGING_BRANCH..$STAGING_BRANCH --oneline)" ]; then
|
||||||
echo "WARNING: not all changes to the $DEPLOY_BRANCH branch have been pushed."
|
echo "WARNING: not all changes to the $STAGING_BRANCH branch have been pushed."
|
||||||
echo "Press [ENTER] to continue deploying current origin/$DEPLOY_BRANCH, CTRL+C to abort."
|
echo "Press [ENTER] to continue staging current origin/$STAGING_BRANCH, CTRL+C to abort."
|
||||||
read dummy
|
read dummy
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -88,15 +88,21 @@ function git_clone() {
|
|||||||
echo "==================================================================="
|
echo "==================================================================="
|
||||||
echo "CLONING REPO ON $PROJECT_NAME @$BRANCH"
|
echo "CLONING REPO ON $PROJECT_NAME @$BRANCH"
|
||||||
URL=$(git -C $LOCAL_ROOT remote get-url origin)
|
URL=$(git -C $LOCAL_ROOT remote get-url origin)
|
||||||
git -C $DEPLOYDIR clone --depth 1 --branch $BRANCH $URL $PROJECT_NAME
|
git -C $STAGINGDIR clone --depth 1 --branch $BRANCH $URL $PROJECT_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
git_clone pillar-python-sdk master $SDK_DIR
|
if [ "$STAGING_BRANCH" == "production" ]; then
|
||||||
git_clone pillar $DEPLOY_BRANCH $PILLAR_DIR
|
SDK_STAGING_BRANCH=master # SDK doesn't have a production branch
|
||||||
git_clone attract $DEPLOY_BRANCH $ATTRACT_DIR
|
else
|
||||||
git_clone flamenco $DEPLOY_BRANCH $FLAMENCO_DIR
|
SDK_STAGING_BRANCH=$STAGING_BRANCH
|
||||||
git_clone pillar-svnman $DEPLOY_BRANCH $SVNMAN_DIR
|
fi
|
||||||
git_clone blender-cloud $DEPLOY_BRANCH $ROOT
|
|
||||||
|
git_clone pillar-python-sdk $SDK_STAGING_BRANCH $SDK_DIR
|
||||||
|
git_clone pillar $STAGING_BRANCH $PILLAR_DIR
|
||||||
|
git_clone attract $STAGING_BRANCH $ATTRACT_DIR
|
||||||
|
git_clone flamenco $STAGING_BRANCH $FLAMENCO_DIR
|
||||||
|
git_clone pillar-svnman $STAGING_BRANCH $SVNMAN_DIR
|
||||||
|
git_clone blender-cloud $STAGING_BRANCH $ROOT
|
||||||
|
|
||||||
# Gulp everywhere
|
# Gulp everywhere
|
||||||
GULP=$ROOT/node_modules/.bin/gulp
|
GULP=$ROOT/node_modules/.bin/gulp
|
||||||
@@ -104,13 +110,21 @@ if [ ! -e $GULP -o gulpfile.js -nt $GULP ]; then
|
|||||||
npm install
|
npm install
|
||||||
touch $GULP # installer doesn't always touch this after a build, so we do.
|
touch $GULP # installer doesn't always touch this after a build, so we do.
|
||||||
fi
|
fi
|
||||||
$GULP --cwd $DEPLOYDIR/pillar --production
|
|
||||||
$GULP --cwd $DEPLOYDIR/attract --production
|
# List of projects
|
||||||
$GULP --cwd $DEPLOYDIR/flamenco --production
|
PROJECTS="pillar attract flamenco pillar-svnman blender-cloud"
|
||||||
$GULP --cwd $DEPLOYDIR/pillar-svnman --production
|
|
||||||
$GULP --cwd $DEPLOYDIR/blender-cloud --production
|
# Run ./gulp for every project
|
||||||
|
for PROJECT in $PROJECTS; do
|
||||||
|
pushd $STAGINGDIR/$PROJECT; ./gulp --production; popd;
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove node_modules (only after all projects with interdependencies have been built)
|
||||||
|
for PROJECT in $PROJECTS; do
|
||||||
|
pushd $STAGINGDIR/$PROJECT; rm -r node_modules; popd;
|
||||||
|
done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==================================================================="
|
echo "==================================================================="
|
||||||
echo "Deploy of ${PROJECT_NAME} is ready for dockerisation."
|
echo "Staging of ${PROJECT_NAME} is ready for dockerisation."
|
||||||
echo "==================================================================="
|
echo "==================================================================="
|
||||||
|
@@ -9,7 +9,6 @@ else
|
|||||||
fi
|
fi
|
||||||
ROOT="$(dirname "$(dirname "$($readlink -f "$0")")")"
|
ROOT="$(dirname "$(dirname "$($readlink -f "$0")")")"
|
||||||
PROJECT_NAME="$(basename $ROOT)"
|
PROJECT_NAME="$(basename $ROOT)"
|
||||||
DOCKER_DEPLOYDIR="$ROOT/docker/4_run/deploy"
|
|
||||||
DOCKER_IMAGE="armadillica/blender_cloud:latest"
|
DOCKER_IMAGE="armadillica/blender_cloud:latest"
|
||||||
REMOTE_SECRET_CONFIG_DIR="/data/config"
|
REMOTE_SECRET_CONFIG_DIR="/data/config"
|
||||||
REMOTE_DOCKER_COMPOSE_DIR="/root/docker"
|
REMOTE_DOCKER_COMPOSE_DIR="/root/docker"
|
||||||
|
39
deploy/README.md
Normal file
39
deploy/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Deploying to Production
|
||||||
|
|
||||||
|
```
|
||||||
|
workon blender-cloud # activate your virtualenv
|
||||||
|
cd $projectdir/deploy
|
||||||
|
./full-pull.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Details
|
||||||
|
|
||||||
|
Deployment consists of a few steps:
|
||||||
|
|
||||||
|
1. Populate a staging directory with the files from the production branches of the various projects.
|
||||||
|
2. Create Docker images.
|
||||||
|
3. Push the docker images to Docker Hub.
|
||||||
|
4. Pull the docker images on the production server and rebuild+restart the containers.
|
||||||
|
|
||||||
|
The scripts involved are:
|
||||||
|
|
||||||
|
- `2docker.sh`: performs step 1. above.
|
||||||
|
- `build-{xxx}.sh`: performs steps 2. and 3. above.
|
||||||
|
- `2server.sh`: performs step 4. above.
|
||||||
|
|
||||||
|
The `full-{xxx}.sh` scripts perform all the steps, and call into `build-{xxx}.sh`.
|
||||||
|
|
||||||
|
For `xxx` there are:
|
||||||
|
|
||||||
|
- `all`: Rebuild all Docker images from scratch. This is good for getting the latest updates to the
|
||||||
|
base image.
|
||||||
|
- `pull`: Pull the base and intermediate images from Docker Hub so that they are the same as the
|
||||||
|
last time someone pushed to production, then rebuilds the final Docker image.
|
||||||
|
- `quick`: Just rebuild the final Docker image. Only use this if the last time a deployment to
|
||||||
|
the production server was done was by you, on the machine you're working on now.
|
||||||
|
|
||||||
|
|
||||||
|
## Hacking Stuff
|
||||||
|
|
||||||
|
To deploy another branch than `production`, do `export STAGING_BRANCH=otherbranch` before starting
|
||||||
|
the above commands.
|
@@ -1 +0,0 @@
|
|||||||
build-quick.sh
|
|
43
deploy/build-all.sh
Executable file
43
deploy/build-all.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# macOS does not support readlink -f, so we use greadlink instead
|
||||||
|
if [[ `uname` == 'Darwin' ]]; then
|
||||||
|
command -v greadlink 2>/dev/null 2>&1 || { echo >&2 "Install greadlink using brew."; exit 1; }
|
||||||
|
readlink='greadlink'
|
||||||
|
else
|
||||||
|
readlink='readlink'
|
||||||
|
fi
|
||||||
|
ROOT="$(dirname "$(dirname "$($readlink -f "$0")")")"
|
||||||
|
|
||||||
|
case "$(basename "$0")" in
|
||||||
|
build-pull.sh)
|
||||||
|
docker pull armadillica/pillar_py:3.6
|
||||||
|
docker pull armadillica/pillar_wheelbuilder:latest
|
||||||
|
pushd "$ROOT/docker/3_buildwheels"
|
||||||
|
./build.sh
|
||||||
|
popd
|
||||||
|
pushd "$ROOT/docker/4_run"
|
||||||
|
./build.sh
|
||||||
|
;;
|
||||||
|
build-quick.sh)
|
||||||
|
pushd "$ROOT/docker/4_run"
|
||||||
|
./build.sh
|
||||||
|
;;
|
||||||
|
build-all.sh)
|
||||||
|
pushd "$ROOT/docker"
|
||||||
|
./full_rebuild.sh
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown script $0, aborting" >&2
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
popd
|
||||||
|
echo
|
||||||
|
echo "Press [ENTER] to push the new Docker images."
|
||||||
|
read dummy
|
||||||
|
docker push armadillica/pillar_py:3.6
|
||||||
|
docker push armadillica/pillar_wheelbuilder:latest
|
||||||
|
docker push armadillica/blender_cloud:latest
|
||||||
|
echo
|
||||||
|
echo "Build is done, ready to update the server."
|
1
deploy/build-pull.sh
Symbolic link
1
deploy/build-pull.sh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
build-quick.sh
|
@@ -1,34 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
# macOS does not support readlink -f, so we use greadlink instead
|
|
||||||
if [[ `uname` == 'Darwin' ]]; then
|
|
||||||
command -v greadlink 2>/dev/null 2>&1 || { echo >&2 "Install greadlink using brew."; exit 1; }
|
|
||||||
readlink='greadlink'
|
|
||||||
else
|
|
||||||
readlink='readlink'
|
|
||||||
fi
|
|
||||||
ROOT="$(dirname "$(dirname "$($readlink -f "$0")")")"
|
|
||||||
DOCKERDIR="$ROOT/docker/4_run"
|
|
||||||
|
|
||||||
case "$(basename "$0")" in
|
|
||||||
build-quick.sh)
|
|
||||||
pushd "$ROOT/docker/4_run"
|
|
||||||
./build.sh
|
|
||||||
;;
|
|
||||||
build-all.sh)
|
|
||||||
pushd "$ROOT/docker"
|
|
||||||
./full_rebuild.sh
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown script $0, aborting" >&2
|
|
||||||
exit 1
|
|
||||||
esac
|
|
||||||
|
|
||||||
popd
|
|
||||||
echo
|
|
||||||
echo "Press [ENTER] to push the new Docker image."
|
|
||||||
read dummy
|
|
||||||
docker push armadillica/blender_cloud:latest
|
|
||||||
echo
|
|
||||||
echo "Build is done, ready to update the server."
|
|
||||||
|
|
1
deploy/build-quick.sh
Symbolic link
1
deploy/build-quick.sh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
build-all.sh
|
9
deploy/full-all.sh
Executable file
9
deploy/full-all.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
NAME="$(basename "$0")"
|
||||||
|
|
||||||
|
./2docker.sh
|
||||||
|
./${NAME/full-/build-}
|
||||||
|
./2server.sh cloud2
|
1
deploy/full-pull.sh
Symbolic link
1
deploy/full-pull.sh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
full-all.sh
|
1
deploy/full-quick.sh
Symbolic link
1
deploy/full-quick.sh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
full-all.sh
|
@@ -1,6 +1,10 @@
|
|||||||
FROM ubuntu:17.10
|
FROM ubuntu:18.04
|
||||||
MAINTAINER Francesco Siddi <francesco@blender.org>
|
LABEL maintainer="Sybren A. Stüvel <sybren@blender.studio>"
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -qyy \
|
RUN set -ex; \
|
||||||
-o APT::Install-Recommends=false -o APT::Install-Suggests=false \
|
apt-get update; \
|
||||||
openssl ca-certificates
|
DEBIAN_FRONTEND=noninteractive apt-get install \
|
||||||
|
-qyy -o APT::Install-Recommends=false -o APT::Install-Suggests=false \
|
||||||
|
tzdata openssl ca-certificates locales; \
|
||||||
|
locale-gen en_US.UTF-8 en_GB.UTF-8 nl_NL.UTF-8
|
||||||
|
ENV LANG en_US.UTF-8
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
1325134dd525b4a2c3272a1a0214dd54 Python-3.6.4.tar.xz
|
|
1
docker/2_buildpy/Python-3.6.6.tar.xz.md5
Normal file
1
docker/2_buildpy/Python-3.6.6.tar.xz.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
c3f30a0aff425dda77d19e02f420d6ba Python-3.6.6.tar.xz
|
@@ -34,6 +34,9 @@ make -j8 install
|
|||||||
# Make sure we can run Python
|
# Make sure we can run Python
|
||||||
ldconfig
|
ldconfig
|
||||||
|
|
||||||
|
# Upgrade pip
|
||||||
|
/opt/python/bin/python3 -m pip install -U pip
|
||||||
|
|
||||||
# Build mod-wsgi-py3 for Python 3.6
|
# Build mod-wsgi-py3 for Python 3.6
|
||||||
cd /dpkg/mod-wsgi-*
|
cd /dpkg/mod-wsgi-*
|
||||||
./configure --with-python=/opt/python/bin/python3
|
./configure --with-python=/opt/python/bin/python3
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
FROM pillar_base
|
FROM pillar_base
|
||||||
LABEL maintainer Sybren A. Stüvel <sybren@blender.studio>
|
LABEL maintainer="Sybren A. Stüvel <sybren@blender.studio>"
|
||||||
|
|
||||||
RUN sed -i 's/^# deb-src/deb-src/' /etc/apt/sources.list && \
|
RUN sed -i 's/^# deb-src/deb-src/' /etc/apt/sources.list && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -qy \
|
DEBIAN_FRONTEND=noninteractive apt-get install -qy \
|
||||||
build-essential \
|
build-essential \
|
||||||
apache2-dev \
|
apache2-dev \
|
||||||
checkinstall \
|
checkinstall \
|
||||||
@@ -11,13 +11,13 @@ RUN sed -i 's/^# deb-src/deb-src/' /etc/apt/sources.list && \
|
|||||||
|
|
||||||
RUN apt-get build-dep -y python3.6
|
RUN apt-get build-dep -y python3.6
|
||||||
|
|
||||||
ADD Python-3.6.4.tar.xz.md5 /Python-3.6.4.tar.xz.md5
|
ADD Python-3.6.6.tar.xz.md5 /Python-3.6.6.tar.xz.md5
|
||||||
|
|
||||||
# Install Python sources
|
# Install Python sources
|
||||||
RUN curl -O https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz && \
|
RUN curl -O https://www.python.org/ftp/python/3.6.6/Python-3.6.6.tar.xz && \
|
||||||
md5sum -c Python-3.6.4.tar.xz.md5 && \
|
md5sum -c Python-3.6.6.tar.xz.md5 && \
|
||||||
tar xf Python-3.6.4.tar.xz && \
|
tar xf Python-3.6.6.tar.xz && \
|
||||||
rm -v Python-3.6.4.tar.xz
|
rm -v Python-3.6.6.tar.xz
|
||||||
|
|
||||||
# Install mod-wsgi sources
|
# Install mod-wsgi sources
|
||||||
RUN mkdir -p /dpkg && cd /dpkg && apt-get source libapache2-mod-wsgi-py3
|
RUN mkdir -p /dpkg && cd /dpkg && apt-get source libapache2-mod-wsgi-py3
|
||||||
@@ -32,4 +32,4 @@ RUN echo /opt/python/lib > /etc/ld.so.conf.d/python.conf
|
|||||||
RUN ldconfig
|
RUN ldconfig
|
||||||
ENV PATH=/opt/python/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
ENV PATH=/opt/python/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
|
||||||
ENV PYTHONSOURCE=/Python-3.6.4
|
ENV PYTHONSOURCE=/Python-3.6.6
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
FROM pillar_base
|
FROM pillar_base
|
||||||
LABEL maintainer Sybren A. Stüvel <sybren@blender.studio>
|
LABEL maintainer="Sybren A. Stüvel <sybren@blender.studio>"
|
||||||
|
|
||||||
ADD python /opt/python
|
ADD python /opt/python
|
||||||
|
|
||||||
@@ -10,5 +10,4 @@ RUN echo Python is installed in /opt/python/ > README.python
|
|||||||
ENV PATH=/opt/python/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
ENV PATH=/opt/python/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
|
||||||
RUN cd /opt/python/bin && \
|
RUN cd /opt/python/bin && \
|
||||||
ln -s python3 python && \
|
ln -s python3 python
|
||||||
ln -s pip3 pip
|
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
FROM armadillica/pillar_py:3.6
|
FROM armadillica/pillar_py:3.6
|
||||||
LABEL maintainer Sybren A. Stüvel <sybren@blender.studio>
|
LABEL maintainer="Sybren A. Stüvel <sybren@blender.studio>"
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -qy \
|
RUN set -ex; \
|
||||||
|
apt-get update; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -qy \
|
||||||
git \
|
git \
|
||||||
build-essential \
|
build-essential \
|
||||||
checkinstall \
|
checkinstall \
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
DOCKER_IMAGE_NAME=armadillica/pillar_wheelbuilder
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# macOS does not support readlink -f, so we use greadlink instead
|
# macOS does not support readlink -f, so we use greadlink instead
|
||||||
@@ -21,25 +23,40 @@ fi
|
|||||||
|
|
||||||
echo "Wheelhouse is $WHEELHOUSE"
|
echo "Wheelhouse is $WHEELHOUSE"
|
||||||
mkdir -p "$WHEELHOUSE"
|
mkdir -p "$WHEELHOUSE"
|
||||||
|
rm -f "$WHEELHOUSE"/*
|
||||||
|
|
||||||
docker build -t pillar_wheelbuilder .
|
docker build -t $DOCKER_IMAGE_NAME:latest .
|
||||||
|
|
||||||
GID=$(id -g)
|
GID=$(id -g)
|
||||||
docker run --rm -i \
|
docker run --rm -i \
|
||||||
-v "$WHEELHOUSE:/data/wheelhouse" \
|
-v "$WHEELHOUSE:/data/wheelhouse" \
|
||||||
-v "$TOPDEVDIR:/data/topdev" \
|
-v "$TOPDEVDIR:/data/topdev" \
|
||||||
pillar_wheelbuilder <<EOT
|
$DOCKER_IMAGE_NAME <<EOT
|
||||||
set -e
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Globally upgrade Pip, so that we can get a compatible version of the cryptography package.
|
||||||
|
# See https://github.com/pyca/cryptography/issues/5771
|
||||||
|
pip3 install --upgrade pip setuptools wheel
|
||||||
|
|
||||||
|
# Pin poetry to 1.0, as more recent version to not support nested filesystem package
|
||||||
|
# dependencies.
|
||||||
|
pip3 install wheel poetry==1.0 cryptography==2.7
|
||||||
|
|
||||||
# Build wheels for all dependencies.
|
# Build wheels for all dependencies.
|
||||||
cd /data/topdev/blender-cloud
|
cd /data/topdev/blender-cloud
|
||||||
pip3 install wheel
|
|
||||||
pip3 wheel --wheel-dir=/data/wheelhouse -r requirements.txt
|
|
||||||
chown -R $UID:$GID /data/wheelhouse
|
|
||||||
|
|
||||||
# Install the dependencies so that we can get a full freeze.
|
poetry install --no-dev
|
||||||
pip3 install --no-index --find-links=/data/wheelhouse -r requirements.txt
|
|
||||||
pip3 freeze | grep -v '^-[ef] ' > /data/wheelhouse/requirements.txt
|
# Apparently pip doesn't like projects without setup.py, so it think we have 'pillar-svnman' as
|
||||||
|
# requirement (because that's the name of the directory). We have to grep that out.
|
||||||
|
poetry run pip3 freeze | grep -v '\(pillar\)\|\(^-[ef] \)' > \$WHEELHOUSE/requirements.txt
|
||||||
|
|
||||||
|
pip3 wheel --wheel-dir=\$WHEELHOUSE -r \$WHEELHOUSE/requirements.txt
|
||||||
|
chown -R $UID:$GID \$WHEELHOUSE
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
# Remove our own projects, they shouldn't be installed as wheel (for now).
|
# Remove our own projects, they shouldn't be installed as wheel (for now).
|
||||||
rm -f $WHEELHOUSE/{attract,flamenco,pillar,pillarsdk}*.whl
|
rm -f $WHEELHOUSE/{attract,flamenco,pillar,pillarsdk}*.whl
|
||||||
|
|
||||||
|
echo "Build of $DOCKER_IMAGE_NAME:latest is done."
|
||||||
|
@@ -1,17 +1,19 @@
|
|||||||
FROM armadillica/pillar_py:3.6
|
FROM armadillica/pillar_py:3.6
|
||||||
LABEL maintainer Sybren A. Stüvel <sybren@blender.studio>
|
LABEL maintainer="Sybren A. Stüvel <sybren@blender.studio>"
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -qyy \
|
RUN set -ex; \
|
||||||
-o APT::Install-Recommends=false -o APT::Install-Suggests=false \
|
apt-get update; \
|
||||||
git \
|
DEBIAN_FRONTEND=noninteractive apt-get install -qy \
|
||||||
apache2 \
|
-o APT::Install-Recommends=false -o APT::Install-Suggests=false \
|
||||||
libapache2-mod-xsendfile \
|
git \
|
||||||
libjpeg8 \
|
apache2 \
|
||||||
libtiff5 \
|
libapache2-mod-xsendfile \
|
||||||
ffmpeg \
|
libjpeg8 \
|
||||||
rsyslog logrotate \
|
libtiff5 \
|
||||||
nano vim-tiny curl \
|
ffmpeg \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
rsyslog logrotate \
|
||||||
|
nano vim-tiny curl; \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN ln -s /usr/bin/vim.tiny /usr/bin/vim
|
RUN ln -s /usr/bin/vim.tiny /usr/bin/vim
|
||||||
|
|
||||||
@@ -36,8 +38,9 @@ ENV USE_X_SENDFILE True
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
|
ADD apache/remoteip.conf /etc/apache2/mods-available/
|
||||||
ADD apache/wsgi-py36.* /etc/apache2/mods-available/
|
ADD apache/wsgi-py36.* /etc/apache2/mods-available/
|
||||||
RUN a2enmod rewrite && a2enmod wsgi-py36
|
RUN a2enmod remoteip && a2enmod rewrite && a2enmod wsgi-py36
|
||||||
|
|
||||||
ADD apache/apache2.conf /etc/apache2/apache2.conf
|
ADD apache/apache2.conf /etc/apache2/apache2.conf
|
||||||
ADD apache/000-default.conf /etc/apache2/sites-available/000-default.conf
|
ADD apache/000-default.conf /etc/apache2/sites-available/000-default.conf
|
||||||
@@ -55,7 +58,7 @@ ENTRYPOINT /docker-entrypoint.sh
|
|||||||
|
|
||||||
# Add the most-changing files as last step for faster rebuilds.
|
# Add the most-changing files as last step for faster rebuilds.
|
||||||
ADD config_local.py /data/git/blender-cloud/
|
ADD config_local.py /data/git/blender-cloud/
|
||||||
ADD deploy /data/git
|
ADD staging /data/git
|
||||||
RUN python3 -c "import re, secrets; \
|
RUN python3 -c "import re, secrets; \
|
||||||
f = open('/data/git/blender-cloud/config_local.py', 'a'); \
|
f = open('/data/git/blender-cloud/config_local.py', 'a'); \
|
||||||
h = re.sub(r'[_.~-]', '', secrets.token_urlsafe())[:8]; \
|
h = re.sub(r'[_.~-]', '', secrets.token_urlsafe())[:8]; \
|
||||||
|
@@ -49,6 +49,8 @@
|
|||||||
RewriteRule "^/training/?$" "/courses" [R=301,L]
|
RewriteRule "^/training/?$" "/courses" [R=301,L]
|
||||||
RewriteRule "^/spring/?$" "/p/spring" [R=301,L]
|
RewriteRule "^/spring/?$" "/p/spring" [R=301,L]
|
||||||
RewriteRule "^/hero/?$" "/p/hero" [R=301,L]
|
RewriteRule "^/hero/?$" "/p/hero" [R=301,L]
|
||||||
|
RewriteRule "^/coffee-run/?$" "/p/coffee-run" [R=301,L]
|
||||||
|
RewriteRule "^/settlers/?$" "/p/settlers" [R=301,L]
|
||||||
# Waking the forest was moved from the art gallery to its own workshop
|
# Waking the forest was moved from the art gallery to its own workshop
|
||||||
RewriteRule "^/p/gallery/58cfec4f88ac8f1440aeb309/?$" "/p/waking-the-forest" [R=301,L]
|
RewriteRule "^/p/gallery/58cfec4f88ac8f1440aeb309/?$" "/p/waking-the-forest" [R=301,L]
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
@@ -133,9 +133,9 @@ AccessFileName .htaccess
|
|||||||
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
|
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
|
||||||
# Use mod_remoteip instead.
|
# Use mod_remoteip instead.
|
||||||
#
|
#
|
||||||
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
|
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
|
||||||
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||||
LogFormat "%h %l %u %t \"%r\" %>s %O" common
|
LogFormat "%a %l %u %t \"%r\" %>s %O" common
|
||||||
LogFormat "%{Referer}i -> %U" referer
|
LogFormat "%{Referer}i -> %U" referer
|
||||||
LogFormat "%{User-agent}i" agent
|
LogFormat "%{User-agent}i" agent
|
||||||
|
|
||||||
|
2
docker/4_run/apache/remoteip.conf
Normal file
2
docker/4_run/apache/remoteip.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RemoteIPHeader X-Forwarded-For
|
||||||
|
RemoteIPInternalProxy 172.16.0.0/12
|
@@ -28,7 +28,7 @@ CACHE_REDIS_URL = 'redis://redis:6379'
|
|||||||
|
|
||||||
PILLAR_SERVER_ENDPOINT = 'https://cloud.blender.org/api/'
|
PILLAR_SERVER_ENDPOINT = 'https://cloud.blender.org/api/'
|
||||||
|
|
||||||
BLENDER_ID_ENDPOINT = 'https://www.blender.org/id'
|
BLENDER_ID_ENDPOINT = 'https://www.blender.org/id/'
|
||||||
|
|
||||||
GCLOUD_APP_CREDENTIALS = '/data/config/google_app.json'
|
GCLOUD_APP_CREDENTIALS = '/data/config/google_app.json'
|
||||||
GCLOUD_PROJECT = 'blender-cloud'
|
GCLOUD_PROJECT = 'blender-cloud'
|
||||||
@@ -83,21 +83,22 @@ LOGGING = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Latest version of the add-on.
|
||||||
|
BLENDER_CLOUD_ADDON_VERSION = '1.9.0'
|
||||||
|
|
||||||
REDIRECTS = {
|
REDIRECTS = {
|
||||||
# For old links, refer to the services page (hopefully it refreshes then)
|
# For old links, refer to the services page (hopefully it refreshes then)
|
||||||
'downloads/blender_cloud-latest-bundle.zip': 'https://cloud.blender.org/services#blender-addon',
|
'downloads/blender_cloud-latest-bundle.zip': 'https://cloud.blender.org/services#blender-addon',
|
||||||
|
|
||||||
# Latest Blender Cloud add-on; remember to update BLENDER_CLOUD_ADDON_VERSION.
|
# Latest Blender Cloud add-on.
|
||||||
'downloads/blender_cloud-latest-addon.zip':
|
'downloads/blender_cloud-latest-addon.zip':
|
||||||
'https://storage.googleapis.com/institute-storage/addons/blender_cloud-1.8.0.addon.zip',
|
f'https://storage.googleapis.com/institute-storage/addons/'
|
||||||
|
f'blender_cloud-{BLENDER_CLOUD_ADDON_VERSION}.addon.zip',
|
||||||
|
|
||||||
# Redirect old Grafista endpoint to /stats
|
# Redirect old Grafista endpoint to /stats
|
||||||
'/stats/': '/stats',
|
'/stats/': '/stats',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Latest version of the add-on; remember to update REDIRECTS.
|
|
||||||
BLENDER_CLOUD_ADDON_VERSION = '1.8.0'
|
|
||||||
|
|
||||||
UTM_LINKS = {
|
UTM_LINKS = {
|
||||||
'cartoon_brew': {
|
'cartoon_brew': {
|
||||||
'image': 'https://imgur.com/13nQTi3.png',
|
'image': 'https://imgur.com/13nQTi3.png',
|
||||||
@@ -105,15 +106,6 @@ UTM_LINKS = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Disabled until we have regenerated the majority of the links.
|
|
||||||
CELERY_BEAT_SCHEDULE = {
|
|
||||||
'regenerate-expired-links': {
|
|
||||||
'task': 'pillar.celery.file_link_tasks.regenerate_all_expired_links',
|
|
||||||
'schedule': 600, # every N seconds
|
|
||||||
'args': ('gcs', 500)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
SVNMAN_REPO_URL = 'https://svn.blender.cloud/repo/'
|
SVNMAN_REPO_URL = 'https://svn.blender.cloud/repo/'
|
||||||
SVNMAN_API_URL = 'https://svn.blender.cloud/api/'
|
SVNMAN_API_URL = 'https://svn.blender.cloud/api/'
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
if [ -f /installed ]; then
|
if [ -f /installed ]; then
|
||||||
return
|
return
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
version: '3.4'
|
version: '3.4'
|
||||||
services:
|
services:
|
||||||
mongo:
|
mongo:
|
||||||
image: mongo:3.4.2
|
image: mongo:3.4
|
||||||
container_name: mongo
|
container_name: mongo
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
@@ -15,8 +15,12 @@ services:
|
|||||||
max-size: "200k"
|
max-size: "200k"
|
||||||
max-file: "20"
|
max-file: "20"
|
||||||
|
|
||||||
|
# Databases in use:
|
||||||
|
# 0: Flask Cache
|
||||||
|
# 1: Celery (backend)
|
||||||
|
# 2: Celery (broker)
|
||||||
redis:
|
redis:
|
||||||
image: redis:3.2.8
|
image: redis:5.0
|
||||||
container_name: redis
|
container_name: redis
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
@@ -27,19 +31,8 @@ services:
|
|||||||
max-size: "200k"
|
max-size: "200k"
|
||||||
max-file: "20"
|
max-file: "20"
|
||||||
|
|
||||||
rabbit:
|
|
||||||
image: rabbitmq:3.6.10
|
|
||||||
container_name: rabbit
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:5672:5672"
|
|
||||||
logging:
|
|
||||||
driver: "json-file"
|
|
||||||
options:
|
|
||||||
max-size: "200k"
|
|
||||||
max-file: "20"
|
|
||||||
|
|
||||||
elastic:
|
elastic:
|
||||||
|
# This image is defined in blender-cloud/docker/elastic
|
||||||
image: armadillica/elasticsearch:6.1.1
|
image: armadillica/elasticsearch:6.1.1
|
||||||
container_name: elastic
|
container_name: elastic
|
||||||
restart: always
|
restart: always
|
||||||
@@ -70,6 +63,7 @@ services:
|
|||||||
max-file: "20"
|
max-file: "20"
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
|
# This image is defined in blender-cloud/docker/elastic
|
||||||
image: armadillica/kibana:6.1.1
|
image: armadillica/kibana:6.1.1
|
||||||
container_name: kibana
|
container_name: kibana
|
||||||
restart: always
|
restart: always
|
||||||
@@ -109,7 +103,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
- redis
|
- redis
|
||||||
- rabbit
|
|
||||||
|
|
||||||
celery_worker:
|
celery_worker:
|
||||||
image: armadillica/blender_cloud:latest
|
image: armadillica/blender_cloud:latest
|
||||||
@@ -126,7 +119,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
- redis
|
- redis
|
||||||
- rabbit
|
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
options:
|
options:
|
||||||
@@ -148,7 +140,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
- redis
|
- redis
|
||||||
- rabbit
|
|
||||||
- celery_worker
|
- celery_worker
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
@@ -169,7 +160,8 @@ services:
|
|||||||
- /data/letsencrypt:/data/letsencrypt
|
- /data/letsencrypt:/data/letsencrypt
|
||||||
|
|
||||||
haproxy:
|
haproxy:
|
||||||
image: dockercloud/haproxy:1.5.3
|
# This image is defined in blender-cloud/docker/haproxy
|
||||||
|
image: armadillica/haproxy:1.6.7
|
||||||
container_name: haproxy
|
container_name: haproxy
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
5
docker/haproxy/Dockerfile
Normal file
5
docker/haproxy/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM dockercloud/haproxy:1.6.7
|
||||||
|
LABEL maintainer="Sybren A. Stüvel <sybren@blender.studio>"
|
||||||
|
|
||||||
|
# Fix https://talosintelligence.com/vulnerability_reports/TALOS-2019-0782
|
||||||
|
RUN sed 's/root::/root:!:/' -i /etc/shadow
|
10
docker/haproxy/build.sh
Executable file
10
docker/haproxy/build.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# When updating this, also update the version in Dockerfile
|
||||||
|
VERSION=1.6.7
|
||||||
|
|
||||||
|
docker build -t armadillica/haproxy:${VERSION} .
|
||||||
|
docker tag armadillica/haproxy:${VERSION} armadillica/haproxy:latest
|
||||||
|
|
||||||
|
echo "Done, built armadillica/haproxy:${VERSION}"
|
||||||
|
echo "Also tagged as armadillica/haproxy:latest"
|
6
gulp
6
gulp
@@ -15,9 +15,8 @@ if [ "$1" == "watch" ]; then
|
|||||||
# Treat "gulp watch" as "gulp && gulp watch"
|
# Treat "gulp watch" as "gulp && gulp watch"
|
||||||
$GULP
|
$GULP
|
||||||
elif [ "$1" == "all" ]; then
|
elif [ "$1" == "all" ]; then
|
||||||
|
pushd .
|
||||||
# This is useful when building the Blender Cloud project for the first time.
|
# This is useful when building the Blender Cloud project for the first time.
|
||||||
# Run "gulp" once inside the repo
|
|
||||||
$GULP
|
|
||||||
# Run ./gulp in all depending projects (pillar, attract, flamenco, pillar-svnman)
|
# Run ./gulp in all depending projects (pillar, attract, flamenco, pillar-svnman)
|
||||||
declare -a repos=("pillar" "attract" "flamenco" "pillar-svnman")
|
declare -a repos=("pillar" "attract" "flamenco" "pillar-svnman")
|
||||||
for r in "${repos[@]}"
|
for r in "${repos[@]}"
|
||||||
@@ -25,6 +24,9 @@ elif [ "$1" == "all" ]; then
|
|||||||
cd ../$r
|
cd ../$r
|
||||||
./gulp
|
./gulp
|
||||||
done
|
done
|
||||||
|
popd
|
||||||
|
# Run "gulp" once inside the repo
|
||||||
|
$GULP
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
144
gulpfile.js
144
gulpfile.js
@@ -1,22 +1,21 @@
|
|||||||
var argv = require('minimist')(process.argv.slice(2));
|
let argv = require('minimist')(process.argv.slice(2));
|
||||||
var autoprefixer = require('gulp-autoprefixer');
|
let autoprefixer = require('gulp-autoprefixer');
|
||||||
var cache = require('gulp-cached');
|
let cache = require('gulp-cached');
|
||||||
var chmod = require('gulp-chmod');
|
let chmod = require('gulp-chmod');
|
||||||
var concat = require('gulp-concat');
|
let concat = require('gulp-concat');
|
||||||
var git = require('gulp-git');
|
let git = require('gulp-git');
|
||||||
var gulp = require('gulp');
|
let gulp = require('gulp');
|
||||||
var gulpif = require('gulp-if');
|
let gulpif = require('gulp-if');
|
||||||
var pug = require('gulp-pug');
|
let pug = require('gulp-pug');
|
||||||
var livereload = require('gulp-livereload');
|
let plumber = require('gulp-plumber');
|
||||||
var plumber = require('gulp-plumber');
|
let rename = require('gulp-rename');
|
||||||
var rename = require('gulp-rename');
|
let sass = require('gulp-sass');
|
||||||
var sass = require('gulp-sass');
|
let sourcemaps = require('gulp-sourcemaps');
|
||||||
var sourcemaps = require('gulp-sourcemaps');
|
let uglify = require('gulp-uglify-es').default;
|
||||||
var uglify = require('gulp-uglify-es').default;
|
|
||||||
|
|
||||||
var enabled = {
|
let enabled = {
|
||||||
uglify: argv.production,
|
uglify: argv.production,
|
||||||
maps: argv.production,
|
maps: !argv.production,
|
||||||
failCheck: !argv.production,
|
failCheck: !argv.production,
|
||||||
prettyPug: !argv.production,
|
prettyPug: !argv.production,
|
||||||
cachify: !argv.production,
|
cachify: !argv.production,
|
||||||
@@ -24,21 +23,19 @@ var enabled = {
|
|||||||
chmod: argv.production,
|
chmod: argv.production,
|
||||||
};
|
};
|
||||||
|
|
||||||
var destination = {
|
let destination = {
|
||||||
css: 'cloud/static/assets/css',
|
css: 'cloud/static/assets/css',
|
||||||
pug: 'cloud/templates',
|
pug: 'cloud/templates',
|
||||||
js: 'cloud/static/assets/js',
|
js: 'cloud/static/assets/js',
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = {
|
let source = {
|
||||||
pillar: '../pillar/',
|
pillar: '../pillar/'
|
||||||
bootstrap: 'node_modules/bootstrap/',
|
|
||||||
popper: 'node_modules/popper.js/'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* CSS */
|
/* CSS */
|
||||||
gulp.task('styles', function() {
|
gulp.task('styles', function(done) {
|
||||||
gulp.src('src/styles/**/*.sass')
|
gulp.src('src/styles/**/*.sass')
|
||||||
.pipe(gulpif(enabled.failCheck, plumber()))
|
.pipe(gulpif(enabled.failCheck, plumber()))
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.init()))
|
.pipe(gulpif(enabled.maps, sourcemaps.init()))
|
||||||
@@ -47,32 +44,34 @@ gulp.task('styles', function() {
|
|||||||
))
|
))
|
||||||
.pipe(autoprefixer("last 3 versions"))
|
.pipe(autoprefixer("last 3 versions"))
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
||||||
.pipe(gulp.dest(destination.css))
|
.pipe(gulp.dest(destination.css));
|
||||||
.pipe(gulpif(argv.livereload, livereload()));
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* Templates - Pug */
|
/* Templates - Pug */
|
||||||
gulp.task('templates', function() {
|
gulp.task('templates', function(done) {
|
||||||
gulp.src('src/templates/**/*.pug')
|
gulp.src('src/templates/**/*.pug')
|
||||||
.pipe(gulpif(enabled.failCheck, plumber()))
|
.pipe(gulpif(enabled.failCheck, plumber()))
|
||||||
.pipe(gulpif(enabled.cachify, cache('templating')))
|
.pipe(gulpif(enabled.cachify, cache('templating')))
|
||||||
.pipe(pug({
|
.pipe(pug({
|
||||||
pretty: enabled.prettyPug
|
pretty: enabled.prettyPug
|
||||||
}))
|
}))
|
||||||
.pipe(gulp.dest(destination.pug))
|
.pipe(gulp.dest(destination.pug));
|
||||||
.pipe(gulpif(argv.livereload, livereload()));
|
|
||||||
// TODO(venomgfx): please check why 'gulp watch' doesn't pick up on .txt changes.
|
// TODO(venomgfx): please check why 'gulp watch' doesn't pick up on .txt changes.
|
||||||
gulp.src('src/templates/**/*.txt')
|
gulp.src('src/templates/**/*.txt')
|
||||||
.pipe(gulpif(enabled.failCheck, plumber()))
|
.pipe(gulpif(enabled.failCheck, plumber()))
|
||||||
.pipe(gulpif(enabled.cachify, cache('templating')))
|
.pipe(gulpif(enabled.cachify, cache('templating')))
|
||||||
.pipe(gulp.dest(destination.pug))
|
.pipe(gulp.dest(destination.pug));
|
||||||
.pipe(gulpif(argv.livereload, livereload()));
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/* Tutti gets built by Pillar. See gulpfile.js in pillar.*/
|
||||||
|
|
||||||
|
|
||||||
/* Individual Uglified Scripts */
|
/* Individual Uglified Scripts */
|
||||||
gulp.task('scripts', function() {
|
gulp.task('scripts', function(done) {
|
||||||
gulp.src('src/scripts/*.js')
|
gulp.src('src/scripts/*.js')
|
||||||
.pipe(gulpif(enabled.failCheck, plumber()))
|
.pipe(gulpif(enabled.failCheck, plumber()))
|
||||||
.pipe(gulpif(enabled.cachify, cache('scripting')))
|
.pipe(gulpif(enabled.cachify, cache('scripting')))
|
||||||
@@ -80,68 +79,39 @@ gulp.task('scripts', function() {
|
|||||||
.pipe(gulpif(enabled.uglify, uglify()))
|
.pipe(gulpif(enabled.uglify, uglify()))
|
||||||
.pipe(rename({suffix: '.min'}))
|
.pipe(rename({suffix: '.min'}))
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
||||||
.pipe(gulpif(enabled.chmod, chmod(644)))
|
.pipe(gulpif(enabled.chmod, chmod(0o644)))
|
||||||
.pipe(gulp.dest(destination.js))
|
.pipe(gulp.dest(destination.js));
|
||||||
.pipe(gulpif(argv.livereload, livereload()));
|
done();
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/* Collection of scripts in src/scripts/tutti/ to merge into tutti.min.js */
|
|
||||||
/* Since it's always loaded, it's only for functions that we want site-wide */
|
|
||||||
gulp.task('scripts_concat_tutti', function() {
|
|
||||||
gulp.src('src/scripts/tutti/**/*.js')
|
|
||||||
.pipe(gulpif(enabled.failCheck, plumber()))
|
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.init()))
|
|
||||||
.pipe(concat("tutti.min.js"))
|
|
||||||
.pipe(gulpif(enabled.uglify, uglify()))
|
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
|
||||||
.pipe(gulpif(enabled.chmod, chmod(644)))
|
|
||||||
.pipe(gulp.dest(destination.js))
|
|
||||||
.pipe(gulpif(argv.livereload, livereload()));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Combine all needed Bootstrap JavaScript into a single file.
|
|
||||||
gulp.task('scripts_concat_bootstrap', function() {
|
|
||||||
|
|
||||||
toUglify = [
|
|
||||||
source.popper + 'dist/umd/popper.min.js',
|
|
||||||
source.bootstrap + 'js/dist/index.js',
|
|
||||||
source.bootstrap + 'js/dist/util.js',
|
|
||||||
source.bootstrap + 'js/dist/tooltip.js',
|
|
||||||
source.bootstrap + 'js/dist/dropdown.js',
|
|
||||||
];
|
|
||||||
|
|
||||||
gulp.src(toUglify)
|
|
||||||
.pipe(gulpif(enabled.failCheck, plumber()))
|
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.init()))
|
|
||||||
.pipe(concat("bootstrap.min.js"))
|
|
||||||
.pipe(gulpif(enabled.uglify, uglify()))
|
|
||||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
|
||||||
.pipe(gulpif(enabled.chmod, chmod(644)))
|
|
||||||
.pipe(gulp.dest(destination.js))
|
|
||||||
.pipe(gulpif(argv.livereload, livereload()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// While developing, run 'gulp watch'
|
// While developing, run 'gulp watch'
|
||||||
gulp.task('watch',function() {
|
gulp.task('watch',function(done) {
|
||||||
// Only listen for live reloads if ran with --livereload
|
let watchStyles = [
|
||||||
if (argv.livereload){
|
'src/styles/**/*.sass',
|
||||||
livereload.listen();
|
source.pillar + 'src/styles/**/*.sass',
|
||||||
}
|
];
|
||||||
|
|
||||||
gulp.watch('src/styles/**/*.sass',['styles']);
|
let watchScripts = [
|
||||||
gulp.watch(source.pillar + 'src/styles/**/*.sass',['styles']);
|
'src/scripts/**/*.js',
|
||||||
|
source.pillar + 'src/scripts/**/*.js',
|
||||||
|
];
|
||||||
|
|
||||||
gulp.watch('src/templates/**/*.pug',['templates']);
|
let watchTemplates = [
|
||||||
gulp.watch('src/scripts/*.js',['scripts']);
|
'src/templates/**/*.pug',
|
||||||
gulp.watch('src/scripts/tutti/**/*.js',['scripts_concat_tutti']);
|
source.pillar + 'src/templates/**/*.pug',
|
||||||
|
];
|
||||||
|
|
||||||
|
gulp.watch(watchStyles, gulp.series('styles'));
|
||||||
|
gulp.watch(watchScripts, gulp.series('scripts'));
|
||||||
|
gulp.watch(watchTemplates, gulp.series('templates'));
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Erases all generated files in output directories.
|
// Erases all generated files in output directories.
|
||||||
gulp.task('cleanup', function() {
|
gulp.task('cleanup', function(done) {
|
||||||
var paths = [];
|
let paths = [];
|
||||||
for (attr in destination) {
|
for (attr in destination) {
|
||||||
paths.push(destination[attr]);
|
paths.push(destination[attr]);
|
||||||
}
|
}
|
||||||
@@ -149,12 +119,12 @@ gulp.task('cleanup', function() {
|
|||||||
git.clean({ args: '-f -X ' + paths.join(' ') }, function (err) {
|
git.clean({ args: '-f -X ' + paths.join(' ') }, function (err) {
|
||||||
if(err) throw err;
|
if(err) throw err;
|
||||||
});
|
});
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Run 'gulp' to build everything at once
|
// Run 'gulp' to build everything at once
|
||||||
var tasks = [];
|
let tasks = [];
|
||||||
if (enabled.cleanup) tasks.push('cleanup');
|
if (enabled.cleanup) tasks.push('cleanup');
|
||||||
|
|
||||||
gulp.task('default', tasks.concat(['styles', 'templates', 'scripts', 'scripts_concat_tutti', 'scripts_concat_bootstrap']));
|
gulp.task('default', gulp.parallel(tasks.concat(['styles', 'templates', 'scripts'])));
|
||||||
|
6
jwtkeys/README.md
Normal file
6
jwtkeys/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Flamenco Server JWT keys
|
||||||
|
|
||||||
|
To generate a keypair for `ES256`:
|
||||||
|
|
||||||
|
openssl ecparam -genkey -name prime256v1 -noout -out es256-private.pem
|
||||||
|
openssl ec -in es256-private.pem -pubout -out es256-public.pem
|
6028
package-lock.json
generated
6028
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,18 +7,17 @@
|
|||||||
"url": "git://git.blender.org/blender-cloud.git"
|
"url": "git://git.blender.org/blender-cloud.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gulp": "~3.9.1",
|
"gulp": "~4.0",
|
||||||
"gulp-autoprefixer": "~6.0.0",
|
"gulp-autoprefixer": "~6.0.0",
|
||||||
"gulp-cached": "~1.1.1",
|
"gulp-cached": "~1.1.1",
|
||||||
"gulp-chmod": "~2.0.0",
|
"gulp-chmod": "~2.0.0",
|
||||||
"gulp-concat": "~2.6.1",
|
"gulp-concat": "~2.6.1",
|
||||||
"gulp-if": "^2.0.2",
|
"gulp-if": "^2.0.2",
|
||||||
"gulp-git": "~2.8.0",
|
"gulp-git": "~2.8.0",
|
||||||
"gulp-livereload": "~4.0.0",
|
|
||||||
"gulp-plumber": "~1.2.0",
|
"gulp-plumber": "~1.2.0",
|
||||||
"gulp-pug": "~4.0.1",
|
"gulp-pug": "~4.0.1",
|
||||||
"gulp-rename": "~1.4.0",
|
"gulp-rename": "~1.4.0",
|
||||||
"gulp-sass": "~4.0.1",
|
"gulp-sass": "~4.1.0",
|
||||||
"gulp-sourcemaps": "~2.6.4",
|
"gulp-sourcemaps": "~2.6.4",
|
||||||
"gulp-uglify-es": "^1.0.4",
|
"gulp-uglify-es": "^1.0.4",
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
@@ -26,6 +25,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^4.1.3",
|
"bootstrap": "^4.1.3",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"popper.js": "^1.14.4"
|
"natives": "^1.1.6",
|
||||||
|
"popper.js": "^1.14.4",
|
||||||
|
"video.js": "^7.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1917
poetry.lock
generated
Normal file
1917
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
poetry.toml
Normal file
2
poetry.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[virtualenvs]
|
||||||
|
in-project = false
|
75
pyproject.toml
Normal file
75
pyproject.toml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "blender-cloud"
|
||||||
|
version = "1.0"
|
||||||
|
description = ""
|
||||||
|
authors = [
|
||||||
|
"Francesco Siddi <francesco@blender.org>",
|
||||||
|
"Pablo Vazquez <pablo@blender.studio>",
|
||||||
|
"Sybren Stüvel <sybren@blender.studio>",
|
||||||
|
]
|
||||||
|
include = ["README.md", "LICENSE.txt"]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "~3.6"
|
||||||
|
pillar = {path = "../pillar"}
|
||||||
|
attract = {path = "../attract"}
|
||||||
|
flamenco = {path = "../flamenco"}
|
||||||
|
svnman = {path = "../pillar-svnman"}
|
||||||
|
amqp = "2.5.0"
|
||||||
|
asn1crypto = "0.24.0"
|
||||||
|
attrs = "19.1.0"
|
||||||
|
babel = "2.7.0"
|
||||||
|
bcrypt = "3.1.6"
|
||||||
|
billiard = "3.6.0.0"
|
||||||
|
bleach = "3.1.0"
|
||||||
|
celery = "4.3.0"
|
||||||
|
cerberus = "1.3.1"
|
||||||
|
certifi = "2019.3.9"
|
||||||
|
cffi = "1.12.3"
|
||||||
|
chardet = "3.0.4"
|
||||||
|
click = "7.0"
|
||||||
|
commonmark = "0.9.0"
|
||||||
|
cryptography = "2.7"
|
||||||
|
eve = "0.9.1"
|
||||||
|
fasteners = "0.15"
|
||||||
|
flask = "1.0.3"
|
||||||
|
flask-wtf = "0.14.2"
|
||||||
|
future = "0.17.1"
|
||||||
|
google-apitools = "0.5.28"
|
||||||
|
googleapis-common-protos = "1.6.0"
|
||||||
|
grpcio = "1.21.1"
|
||||||
|
httplib2 = "0.12.3"
|
||||||
|
ipaddress = "1.0.22"
|
||||||
|
jinja2 = "2.10.1"
|
||||||
|
kombu = "4.6.0"
|
||||||
|
protobuf = "3.8.0"
|
||||||
|
pyasn1 = "0.4.5"
|
||||||
|
pyasn1-modules = "0.2.5"
|
||||||
|
pycparser = "2.19"
|
||||||
|
pymongo = "3.8.0"
|
||||||
|
pyopenssl = "19.0.0"
|
||||||
|
pytz = "2019.1"
|
||||||
|
requests = "2.22.0"
|
||||||
|
rsa = "4.0"
|
||||||
|
setuptools = "51.0.0"
|
||||||
|
shortcodes = "2.5.0"
|
||||||
|
simplejson = "3.16.0"
|
||||||
|
six = "1.12.0"
|
||||||
|
wheel = "0.35.1"
|
||||||
|
wtforms = "2.2.1"
|
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies]
|
||||||
|
pillar-devdeps = {path = "../pillar/devdeps"}
|
||||||
|
responses = "0.10.6"
|
||||||
|
zipp = "0.5.1"
|
||||||
|
py = "1.8.0"
|
||||||
|
colorama = "0.4.1"
|
||||||
|
importlib-metadata = "0.17"
|
||||||
|
more-itertools = "7.0.0"
|
||||||
|
coverage = "4.5.3"
|
||||||
|
pluggy = "0.12.0"
|
||||||
|
atomicwrites = "1.3.0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry==1.0","cryptography==2.7","setuptools==51.0.0","wheel==0.35.1"]
|
||||||
|
build-backend = "poetry.masonry.api"
|
@@ -1,11 +0,0 @@
|
|||||||
-r ../pillar-python-sdk/requirements-dev.txt
|
|
||||||
-r ../pillar/requirements-dev.txt
|
|
||||||
-r ../attract/requirements-dev.txt
|
|
||||||
-r ../flamenco/requirements-dev.txt
|
|
||||||
-r ../pillar-svnman/requirements-dev.txt
|
|
||||||
|
|
||||||
-e ../pillar-python-sdk
|
|
||||||
-e ../pillar
|
|
||||||
-e ../attract
|
|
||||||
-e ../flamenco
|
|
||||||
-e ../pillar-svnman
|
|
@@ -1,4 +0,0 @@
|
|||||||
-r ../pillar/requirements.txt
|
|
||||||
-r ../attract/requirements.txt
|
|
||||||
-r ../flamenco/requirements.txt
|
|
||||||
-r ../pillar-svnman/requirements.txt
|
|
18
setup.py
18
setup.py
@@ -1,18 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""Setup file for testing, not for packaging/distribution."""
|
|
||||||
|
|
||||||
import setuptools
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
name='blender-cloud',
|
|
||||||
version='1.0',
|
|
||||||
packages=setuptools.find_packages('.', exclude=['tests']),
|
|
||||||
tests_require=[
|
|
||||||
'pytest>=2.9.1',
|
|
||||||
'responses>=0.5.1',
|
|
||||||
'pytest-cov>=2.2.1',
|
|
||||||
'mock>=2.0.0',
|
|
||||||
],
|
|
||||||
zip_safe=False,
|
|
||||||
)
|
|
29
src/scripts/tagged_assets.js
Normal file
29
src/scripts/tagged_assets.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Support for fetching & rendering assets by tags.
|
||||||
|
*/
|
||||||
|
(function($) {
|
||||||
|
$.fn.loadTaggedAssets = function(load_initial_count, load_next_count, has_subscription) {
|
||||||
|
mark_if_public = !has_subscription;
|
||||||
|
this.each(function(index, each) {
|
||||||
|
let $card_deck_element = $(each)
|
||||||
|
$card_deck_element.trigger('pillar:workStart');
|
||||||
|
$.get('/api/nodes/tagged/' + $card_deck_element.data('assetTag'))
|
||||||
|
.fail(function(error) {
|
||||||
|
let msg = xhrErrorResponseMessage(error);
|
||||||
|
$card_deck_element
|
||||||
|
.append(
|
||||||
|
$('<p>').addClass('bg-danger').text(msg)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.done(function(resp) {
|
||||||
|
// 'resp' is a list of node documents.
|
||||||
|
$card_deck_element.append(
|
||||||
|
pillar.templates.Nodes.createListOf$nodeItems(resp, load_initial_count, load_next_count)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.always(function() {
|
||||||
|
$card_deck_element.trigger('pillar:workStop');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}(jQuery));
|
@@ -1,502 +1,60 @@
|
|||||||
|
.random-featured
|
||||||
|
// Hide irrelevant info from cards.
|
||||||
|
li
|
||||||
|
&.item-type,
|
||||||
|
&.item-date
|
||||||
|
@extend .d-none
|
||||||
|
|
||||||
|
// Fit 3 cards per row.
|
||||||
|
&.card-deck.card-deck-responsive
|
||||||
|
.card
|
||||||
|
+media-md
|
||||||
|
flex: 1 0 30%
|
||||||
|
max-width: 30%
|
||||||
|
flex: 1 0 31%
|
||||||
|
max-width: 31%
|
||||||
|
|
||||||
.title-underline
|
.homepage
|
||||||
padding-bottom: 5px
|
.timeline
|
||||||
position: relative
|
.card-text,
|
||||||
margin-bottom: 20px
|
.card-title
|
||||||
|
margin-bottom: 0 !important
|
||||||
&:before
|
padding: 0 !important
|
||||||
background-color: $primary
|
|
||||||
content: ' '
|
|
||||||
display: block
|
|
||||||
height: 2px
|
|
||||||
top: 125%
|
|
||||||
position: absolute
|
|
||||||
width: 50px
|
|
||||||
|
|
||||||
|
|
||||||
nav#nav-tabs,
|
|
||||||
nav#sub-nav-tabs
|
|
||||||
ul#nav-tabs__list,
|
|
||||||
ul#sub-nav-tabs__list
|
|
||||||
margin: 0
|
|
||||||
padding: 0
|
|
||||||
list-style: none
|
|
||||||
border-bottom: thin solid $color-background
|
|
||||||
+clearfix
|
|
||||||
|
|
||||||
li.nav-tabs__list-tab
|
|
||||||
float: left
|
|
||||||
border: none
|
|
||||||
border-bottom: 3px solid transparent
|
|
||||||
color: $color-text-dark-primary
|
|
||||||
user-select: none
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
border-color: rgba($color-secondary, .3)
|
|
||||||
cursor: pointer
|
|
||||||
color: $color-text-dark
|
|
||||||
a
|
|
||||||
color: $color-text-dark
|
|
||||||
|
|
||||||
|
// Hide project name, it's already in the timeline.
|
||||||
a
|
a
|
||||||
display: block
|
|
||||||
text-decoration: none
|
|
||||||
padding: 10px 15px 5px
|
|
||||||
color: $color-text-dark-primary
|
|
||||||
|
|
||||||
i
|
|
||||||
margin-right: 5px
|
|
||||||
color: $color-text-dark-secondary
|
|
||||||
font-size: .9em
|
|
||||||
|
|
||||||
&.pi-blender
|
|
||||||
margin-right: 10px
|
|
||||||
|
|
||||||
span
|
|
||||||
color: $color-text-dark-hint
|
|
||||||
margin-left: 5px
|
|
||||||
|
|
||||||
&.active
|
|
||||||
border-color: $color-secondary
|
|
||||||
color: $color-secondary-dark
|
|
||||||
a, i
|
|
||||||
color: $color-secondary-dark
|
|
||||||
|
|
||||||
&.disabled
|
|
||||||
border-color: $color-background-light
|
|
||||||
color: $color-text-dark-hint
|
|
||||||
cursor: default
|
|
||||||
a, i
|
|
||||||
color: $color-text-dark-hint
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
border-color: $color-background-light
|
|
||||||
pointer-events: none
|
|
||||||
|
|
||||||
.dashboard-container
|
|
||||||
word-break: break-word
|
|
||||||
|
|
||||||
section.stream
|
|
||||||
ul.activity-stream__list
|
|
||||||
$activity-stream-thumbnail-size: 110px
|
|
||||||
|
|
||||||
> li
|
|
||||||
position: relative
|
|
||||||
display: flex
|
|
||||||
padding: 10px 0
|
|
||||||
overflow: hidden
|
|
||||||
border-top: thin solid $color-background-dark
|
|
||||||
|
|
||||||
&:first-child
|
|
||||||
border: none
|
|
||||||
|
|
||||||
&.active .activity-stream__list-details .title
|
|
||||||
color: $color-primary
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
.title
|
|
||||||
text-decoration: underline
|
|
||||||
&.video
|
|
||||||
a.image
|
|
||||||
&:hover
|
|
||||||
i
|
|
||||||
font-size: 3.5em
|
|
||||||
img
|
|
||||||
opacity: .9
|
|
||||||
img
|
|
||||||
opacity: .7
|
|
||||||
z-index: 0
|
|
||||||
transition: opacity 150ms ease-in-out
|
|
||||||
|
|
||||||
i
|
|
||||||
+position-center-translate
|
|
||||||
z-index: 1
|
|
||||||
color: rgba(white, .6)
|
|
||||||
font-size: 3em
|
|
||||||
transition: font-size 100ms ease-in-out
|
|
||||||
|
|
||||||
&.comment
|
|
||||||
.activity-stream__list-details
|
|
||||||
padding: 0
|
|
||||||
.title
|
|
||||||
color: $color-text-dark
|
|
||||||
padding: 7px 10px 2px 10px
|
|
||||||
font-size: 1em
|
|
||||||
margin: 0
|
|
||||||
|
|
||||||
ul.meta
|
|
||||||
+list-meta
|
|
||||||
font-size: .9em
|
|
||||||
padding: 0 10px 7px 10px
|
|
||||||
|
|
||||||
li
|
|
||||||
&.where-parent:before
|
|
||||||
content: '\e83a'
|
|
||||||
font-family: 'pillar-font'
|
|
||||||
|
|
||||||
&.what:before
|
|
||||||
display: none
|
|
||||||
|
|
||||||
&.post
|
|
||||||
.activity-stream__list-thumbnail
|
|
||||||
border-color: $node-type-post
|
|
||||||
background-color: $node-type-post
|
|
||||||
.activity-stream__list-details .title
|
|
||||||
color: darken($node-type-post, 15%)
|
|
||||||
font:
|
|
||||||
size: 1.3em
|
|
||||||
weight: 500
|
|
||||||
|
|
||||||
&.asset, &.comment, &.post
|
|
||||||
&:hover
|
|
||||||
cursor: pointer
|
|
||||||
&.empty
|
|
||||||
display: none
|
display: none
|
||||||
color: $color-text-dark-primary
|
|
||||||
padding: 20px
|
// On blog posts, center text and title.
|
||||||
|
.h1.text-uppercase
|
||||||
|
+media-md
|
||||||
text-align: center
|
text-align: center
|
||||||
span
|
+media-xl
|
||||||
color: $color-primary
|
text-align: left
|
||||||
&:hover
|
|
||||||
text-decoration: underline
|
.node-details-description
|
||||||
cursor: pointer
|
+media-md
|
||||||
|
margin-left: auto
|
||||||
&.with-picture
|
margin-right: auto
|
||||||
min-height: $activity-stream-thumbnail-size
|
+media-xl
|
||||||
|
margin-left: 0
|
||||||
.activity-stream__list-thumbnail
|
margin-right: auto
|
||||||
background-color: black
|
|
||||||
width: $activity-stream-thumbnail-size * 1.69
|
.featured-projects
|
||||||
min-width: $activity-stream-thumbnail-size * 1.69
|
+media-sm
|
||||||
|
padding-left: $spacer
|
||||||
.activity-stream__list-thumbnail-icon
|
padding-right: $spacer
|
||||||
position: absolute
|
|
||||||
top: 0
|
.featured-project-card
|
||||||
left: 0
|
+media-xl
|
||||||
right: 0
|
.card-thumbnail
|
||||||
bottom: 0
|
height: 100%
|
||||||
font-size: 1.3em
|
border-radius: $border-radius
|
||||||
text-shadow: 1px 1px 0 rgba(black, .2)
|
|
||||||
background-image: linear-gradient(10deg, rgba(black, .5) 0%, transparent 40%)
|
.card-body
|
||||||
|
padding-left: 15px !important
|
||||||
i
|
padding-top: 0 !important
|
||||||
position: absolute
|
|
||||||
bottom: -8px
|
.homepage
|
||||||
left: 20px
|
.title-underline
|
||||||
top: initial
|
padding-bottom: 2px
|
||||||
right: initial
|
|
||||||
color: white
|
|
||||||
|
|
||||||
.activity-stream__list-thumbnail
|
|
||||||
position: relative
|
|
||||||
display: flex
|
|
||||||
justify-content: center
|
|
||||||
align-items: center
|
|
||||||
overflow: hidden
|
|
||||||
width: 35px
|
|
||||||
height: auto
|
|
||||||
min-width: 35px
|
|
||||||
min-height: auto
|
|
||||||
|
|
||||||
+media-xs
|
|
||||||
display: none
|
|
||||||
|
|
||||||
|
|
||||||
&.image i
|
|
||||||
color: $node-type-asset_image
|
|
||||||
&.file i
|
|
||||||
color: $node-type-asset_file
|
|
||||||
&.video i
|
|
||||||
color: $node-type-asset_video
|
|
||||||
|
|
||||||
i
|
|
||||||
+position-center-translate
|
|
||||||
left: 23px
|
|
||||||
top: 21px
|
|
||||||
font-size: 1.1em
|
|
||||||
|
|
||||||
img
|
|
||||||
max-height: $activity-stream-thumbnail-size
|
|
||||||
+position-center-translate
|
|
||||||
|
|
||||||
.activity-stream__list-details
|
|
||||||
display: flex
|
|
||||||
flex-direction: column
|
|
||||||
justify-content: space-around
|
|
||||||
flex: 1
|
|
||||||
overflow: hidden
|
|
||||||
position: relative
|
|
||||||
max-width: 100%
|
|
||||||
padding: 10px 0
|
|
||||||
|
|
||||||
+media-xs
|
|
||||||
margin-left: 0
|
|
||||||
|
|
||||||
.ribbon
|
|
||||||
+ribbon
|
|
||||||
right: -47px
|
|
||||||
top: 5px
|
|
||||||
font-size: 12px
|
|
||||||
|
|
||||||
span
|
|
||||||
padding: 1px 50px
|
|
||||||
|
|
||||||
.title
|
|
||||||
padding: 0 10px
|
|
||||||
color: $color-text-dark
|
|
||||||
|
|
||||||
span
|
|
||||||
@include badge(hsl(hue($color-success), 60%, 45%), 3px)
|
|
||||||
font-size: .7em
|
|
||||||
padding: 1px 5px
|
|
||||||
margin-right: 5px
|
|
||||||
|
|
||||||
section.comments
|
|
||||||
padding: 0 15px 5px
|
|
||||||
|
|
||||||
ul
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
> ul
|
|
||||||
list-style-type: none
|
|
||||||
margin: 10px 0 0
|
|
||||||
|
|
||||||
> li
|
|
||||||
+text-overflow-ellipsis
|
|
||||||
border-top: thin solid $color-background-dark
|
|
||||||
padding: 10px 0
|
|
||||||
|
|
||||||
&:first-child
|
|
||||||
border: none
|
|
||||||
|
|
||||||
> a
|
|
||||||
+text-overflow-ellipsis
|
|
||||||
color: $color-text
|
|
||||||
display: block
|
|
||||||
padding-bottom: 5px
|
|
||||||
|
|
||||||
section.random-asset
|
|
||||||
border-bottom: thin solid $color-background-dark
|
|
||||||
|
|
||||||
ul.random-asset__list
|
|
||||||
list-style: none
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
> li
|
|
||||||
align-items: center
|
|
||||||
border-top: thin solid $color-background
|
|
||||||
display: flex
|
|
||||||
padding: 7px 0
|
|
||||||
position: relative
|
|
||||||
overflow: hidden
|
|
||||||
|
|
||||||
&:first-child
|
|
||||||
border-top: none
|
|
||||||
|
|
||||||
.ribbon
|
|
||||||
+ribbon
|
|
||||||
right: -47px
|
|
||||||
top: 5px
|
|
||||||
font:
|
|
||||||
size: 12px
|
|
||||||
weight: 500
|
|
||||||
|
|
||||||
z-index: 1
|
|
||||||
|
|
||||||
span
|
|
||||||
padding: 1px 50px
|
|
||||||
|
|
||||||
.random-asset__list-thumbnail
|
|
||||||
background-color: $color-background
|
|
||||||
display: block
|
|
||||||
height: 50px
|
|
||||||
margin-right: 15px
|
|
||||||
min-height: 50px
|
|
||||||
min-width: 50px
|
|
||||||
overflow: hidden
|
|
||||||
position: relative
|
|
||||||
width: 50px
|
|
||||||
|
|
||||||
img
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
i
|
|
||||||
+position-center-translate
|
|
||||||
font-size: 1.6em
|
|
||||||
color: $color-text-light
|
|
||||||
|
|
||||||
&.image
|
|
||||||
background-color: $node-type-asset_image
|
|
||||||
&.file
|
|
||||||
background-color: $node-type-asset_file
|
|
||||||
font-size: .8em
|
|
||||||
&.video
|
|
||||||
background-color: $node-type-asset_video
|
|
||||||
font-size: .8em
|
|
||||||
&.None
|
|
||||||
background-color: $node-type-group
|
|
||||||
|
|
||||||
.random-asset__list-details
|
|
||||||
.title
|
|
||||||
display: block
|
|
||||||
font-size: 1em
|
|
||||||
color: $color-text-dark-primary
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
color: $color-primary
|
|
||||||
|
|
||||||
ul.meta
|
|
||||||
+list-meta
|
|
||||||
padding-top: 5px
|
|
||||||
font-size: .9em
|
|
||||||
|
|
||||||
li
|
|
||||||
&:before
|
|
||||||
left: -5px
|
|
||||||
&.what
|
|
||||||
text-transform: capitalize
|
|
||||||
|
|
||||||
&.featured
|
|
||||||
align-items: flex-start
|
|
||||||
flex-direction: column
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
a.title
|
|
||||||
font-size: 1.1em
|
|
||||||
padding: 10px 0 5px
|
|
||||||
display: block
|
|
||||||
color: $color-text
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
color: $color-primary
|
|
||||||
|
|
||||||
a.random-asset__thumbnail
|
|
||||||
display: block
|
|
||||||
position: relative
|
|
||||||
|
|
||||||
&.video
|
|
||||||
background-color: black
|
|
||||||
img
|
|
||||||
opacity: .7
|
|
||||||
|
|
||||||
img
|
|
||||||
transition: opacity 150ms ease-in-out
|
|
||||||
width: 100%
|
|
||||||
max-width: 100%
|
|
||||||
|
|
||||||
i
|
|
||||||
+position-center-translate
|
|
||||||
color: white
|
|
||||||
font-size: 3em
|
|
||||||
text-shadow: 0 0 25px black
|
|
||||||
transition: font-size 150ms ease-in-out
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
i
|
|
||||||
font-size: 3.5em
|
|
||||||
img
|
|
||||||
opacity: .85
|
|
||||||
|
|
||||||
ul.meta
|
|
||||||
+list-meta
|
|
||||||
padding-bottom: 10px
|
|
||||||
|
|
||||||
|
|
||||||
section.announcement
|
|
||||||
+container-box
|
|
||||||
margin-left: 15px
|
|
||||||
margin-right: 15px
|
|
||||||
|
|
||||||
.header-icons
|
|
||||||
display: flex
|
|
||||||
align-items: center
|
|
||||||
justify-content: center
|
|
||||||
padding: 20px 0 5px 0
|
|
||||||
|
|
||||||
i
|
|
||||||
font-size: 2.5em
|
|
||||||
color: $color-info
|
|
||||||
|
|
||||||
&.pi-heart-filled
|
|
||||||
color: $color-danger
|
|
||||||
margin-left: 5px
|
|
||||||
|
|
||||||
img.header
|
|
||||||
width: 100%
|
|
||||||
margin: 0 auto
|
|
||||||
border-top-left-radius: 3px
|
|
||||||
border-top-right-radius: 3px
|
|
||||||
|
|
||||||
iframe
|
|
||||||
width: 100%
|
|
||||||
position: relative
|
|
||||||
left: 15px
|
|
||||||
margin: 25px auto
|
|
||||||
|
|
||||||
+media-sm
|
|
||||||
height: 500px
|
|
||||||
+media-md
|
|
||||||
height: 520px
|
|
||||||
+media-lg
|
|
||||||
height: 580px
|
|
||||||
|
|
||||||
.text
|
|
||||||
padding: 15px
|
|
||||||
|
|
||||||
.title
|
|
||||||
padding-bottom: 10px
|
|
||||||
|
|
||||||
+media-xs
|
|
||||||
font-size: 1.4em
|
|
||||||
|
|
||||||
strong
|
|
||||||
color: $color-primary-dark
|
|
||||||
|
|
||||||
a
|
|
||||||
color: $color-text-dark-primary
|
|
||||||
|
|
||||||
.lead
|
|
||||||
font-size: 1em
|
|
||||||
+list-bullets
|
|
||||||
|
|
||||||
ul
|
|
||||||
margin-top: 10px
|
|
||||||
padding-left: 10px
|
|
||||||
|
|
||||||
hr
|
|
||||||
border: none
|
|
||||||
height: 1px
|
|
||||||
width: 100%
|
|
||||||
margin: 10px 0
|
|
||||||
background-color: $color-background
|
|
||||||
clear: both
|
|
||||||
|
|
||||||
+media-xs
|
|
||||||
padding-left: 10px
|
|
||||||
|
|
||||||
.buttons
|
|
||||||
margin: 15px auto 0 auto
|
|
||||||
display: flex
|
|
||||||
align-items: center
|
|
||||||
justify-content: space-around
|
|
||||||
flex-wrap: wrap
|
|
||||||
|
|
||||||
|
|
||||||
body.homepage
|
|
||||||
.blog
|
|
||||||
// Custom tweak to Bootstrap grid for the only case when
|
|
||||||
// the post is inside a column (it's usually centered in the page).
|
|
||||||
.col-md-9
|
|
||||||
flex: 1
|
|
||||||
max-width: 100%
|
|
||||||
|
|
||||||
.jumbotron
|
|
||||||
padding-top: 6em
|
|
||||||
padding-bottom: 6em
|
|
||||||
|
|
||||||
*
|
|
||||||
font-size: $h1-font-size
|
|
||||||
|
|
||||||
.lead
|
|
||||||
font-size: $font-size-base
|
|
||||||
|
37
src/styles/_list_films.sass
Normal file
37
src/styles/_list_films.sass
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
body.films
|
||||||
|
.page-content
|
||||||
|
@extend .text-white
|
||||||
|
background-color: $color-bg-dark-pages
|
||||||
|
|
||||||
|
h1
|
||||||
|
@extend .text-white
|
||||||
|
|
||||||
|
hr
|
||||||
|
background-color: lighten($color-bg-dark-pages, 20%)
|
||||||
|
|
||||||
|
|
||||||
|
.films-item
|
||||||
|
animation: fade-in-down .33s ease-out
|
||||||
|
animation-fill-mode: both
|
||||||
|
|
||||||
|
img
|
||||||
|
box-shadow: 0 10px 25px rgba($black, .5)
|
||||||
|
transition: box-shadow 800ms ease-in-out, transform 200ms ease-in-out
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
box-shadow: 0 0 25px rgba($primary, .1), 0 0 50px rgba(white, .1)
|
||||||
|
transform: scale(1.05)
|
||||||
|
|
||||||
|
|
||||||
|
@for $i from 1 through 20
|
||||||
|
.films-item:nth-child(#{$i}n)
|
||||||
|
animation-delay: #{$i * 0.125}s
|
||||||
|
|
||||||
|
@keyframes fade-in-down
|
||||||
|
0%
|
||||||
|
opacity: 0
|
||||||
|
transform: translateY(-25px)
|
||||||
|
80%
|
||||||
|
opacity: 1
|
||||||
|
100%
|
||||||
|
transform: translateY(0)
|
85
src/styles/_project-landing.sass
Normal file
85
src/styles/_project-landing.sass
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
.landing-home
|
||||||
|
.page-content
|
||||||
|
@extend .text-white
|
||||||
|
background-color: $color-bg-dark-pages
|
||||||
|
|
||||||
|
.jumbotron
|
||||||
|
+media-xs
|
||||||
|
background-position: top right
|
||||||
|
background-position: top center
|
||||||
|
padding-bottom: 16em
|
||||||
|
padding-top: 8em
|
||||||
|
|
||||||
|
.card
|
||||||
|
@extend .bg-transparent
|
||||||
|
@extend .text-white
|
||||||
|
|
||||||
|
.card-text
|
||||||
|
@extend .text-secondary
|
||||||
|
|
||||||
|
.node-details-description
|
||||||
|
@extend .mx-auto
|
||||||
|
color: #ddd
|
||||||
|
font-size: 1.3em
|
||||||
|
|
||||||
|
a
|
||||||
|
color: $color-primary
|
||||||
|
|
||||||
|
.btn-outline-primary
|
||||||
|
border-color: $color-text-light
|
||||||
|
color: $color-text-light
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
border-color: transparent
|
||||||
|
|
||||||
|
.gallery
|
||||||
|
max-width: 1024px
|
||||||
|
|
||||||
|
.thumbnail
|
||||||
|
float: left
|
||||||
|
position: relative
|
||||||
|
width: 23%
|
||||||
|
padding-bottom: 23%
|
||||||
|
margin: 0.83%
|
||||||
|
overflow: hidden
|
||||||
|
transition: box-shadow 150ms ease-in-out
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
box-shadow: 2px 6px 50px 0 rgba(black, .2)
|
||||||
|
|
||||||
|
.img-container
|
||||||
|
position: absolute
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
img
|
||||||
|
width: 300%
|
||||||
|
transform: translate(-20%,-10%)
|
||||||
|
|
||||||
|
@media screen and (max-width: 992px)
|
||||||
|
.thumbnail
|
||||||
|
width: 22%
|
||||||
|
padding-bottom: 22%
|
||||||
|
margin: 1.5%
|
||||||
|
|
||||||
|
@media screen and (max-width: 720px)
|
||||||
|
.thumbnail
|
||||||
|
width: 29%
|
||||||
|
padding-bottom: 29%
|
||||||
|
margin: 2.16%
|
||||||
|
|
||||||
|
@media screen and (max-width: 470px)
|
||||||
|
.thumbnail
|
||||||
|
width: 44%
|
||||||
|
padding-bottom: 44%
|
||||||
|
margin: 3%
|
||||||
|
|
||||||
|
.jumbotron
|
||||||
|
&.jumbotron-overlay-gradient-fade-to-gray
|
||||||
|
*
|
||||||
|
z-index: 1
|
||||||
|
&:after
|
||||||
|
background-color: transparent
|
||||||
|
background-image: linear-gradient(transparent 60%, $color-bg-dark-pages 100%)
|
||||||
|
display: block
|
||||||
|
visibility: visible
|
4
src/styles/_utils.sass
Normal file
4
src/styles/_utils.sass
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.list-first-new
|
||||||
|
li:first-child
|
||||||
|
span
|
||||||
|
@extend .new
|
5
src/styles/_variables.sass
Normal file
5
src/styles/_variables.sass
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
$color-bg-dark-pages: #151515
|
||||||
|
|
||||||
|
// Alias for Blender Cloud logo used in project edit.
|
||||||
|
.pi-cloud
|
||||||
|
@extend .pi-blender-cloud
|
@@ -312,7 +312,7 @@ section.pricing
|
|||||||
transform: scale(1)
|
transform: scale(1)
|
||||||
|
|
||||||
a.sign-up-now
|
a.sign-up-now
|
||||||
+button($color-primary, 3px, true)
|
+button($color-primary, $btn-border-radius, true)
|
||||||
|
|
||||||
h3
|
h3
|
||||||
font-size: 1.8em
|
font-size: 1.8em
|
||||||
@@ -361,7 +361,7 @@ section.pricing
|
|||||||
transform: translateX(-50%)
|
transform: translateX(-50%)
|
||||||
font-size: 1.2em
|
font-size: 1.2em
|
||||||
|
|
||||||
+button($color-primary, 3px)
|
+button($color-primary, $btn-border-radius)
|
||||||
padding: 5px 25px
|
padding: 5px 25px
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
text-align: center
|
text-align: center
|
||||||
|
@@ -1,51 +1,53 @@
|
|||||||
// Bootstrap variables and utilities.
|
// Bootstrap variables and utilities.
|
||||||
@import "../../node_modules/bootstrap/scss/functions"
|
@import "../../../pillar/node_modules/bootstrap/scss/functions"
|
||||||
@import "../../node_modules/bootstrap/scss/variables"
|
@import "../../../pillar/node_modules/bootstrap/scss/variables"
|
||||||
@import "../../node_modules/bootstrap/scss/mixins"
|
@import "../../../pillar/node_modules/bootstrap/scss/mixins"
|
||||||
|
|
||||||
// Pillar variables and utilities.
|
// Pillar variables and utilities.
|
||||||
@import "../../../pillar/src/styles/config"
|
@import "../../../pillar/src/styles/config"
|
||||||
@import "../../../pillar/src/styles/utils"
|
@import "../../../pillar/src/styles/utils"
|
||||||
|
|
||||||
|
$pillar-font-path: "../../../../static/pillar/assets/font"
|
||||||
|
@import "../../../pillar/src/styles/font-pillar"
|
||||||
|
|
||||||
// Bootstrap components.
|
// Bootstrap components.
|
||||||
@import "../../node_modules/bootstrap/scss/root"
|
@import "../../../pillar/node_modules/bootstrap/scss/root"
|
||||||
@import "../../node_modules/bootstrap/scss/reboot"
|
@import "../../../pillar/node_modules/bootstrap/scss/reboot"
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/type"
|
@import "../../../pillar/node_modules/bootstrap/scss/type"
|
||||||
@import "../../node_modules/bootstrap/scss/images"
|
@import "../../../pillar/node_modules/bootstrap/scss/images"
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/code"
|
@import "../../../pillar/node_modules/bootstrap/scss/code"
|
||||||
@import "../../node_modules/bootstrap/scss/grid"
|
@import "../../../pillar/node_modules/bootstrap/scss/grid"
|
||||||
@import "../../node_modules/bootstrap/scss/tables"
|
@import "../../../pillar/node_modules/bootstrap/scss/tables"
|
||||||
@import "../../node_modules/bootstrap/scss/forms"
|
@import "../../../pillar/node_modules/bootstrap/scss/forms"
|
||||||
@import "../../node_modules/bootstrap/scss/buttons"
|
@import "../../../pillar/node_modules/bootstrap/scss/buttons"
|
||||||
@import "../../node_modules/bootstrap/scss/transitions"
|
@import "../../../pillar/node_modules/bootstrap/scss/transitions"
|
||||||
@import "../../node_modules/bootstrap/scss/dropdown"
|
@import "../../../pillar/node_modules/bootstrap/scss/dropdown"
|
||||||
@import "../../node_modules/bootstrap/scss/button-group"
|
@import "../../../pillar/node_modules/bootstrap/scss/button-group"
|
||||||
@import "../../node_modules/bootstrap/scss/input-group"
|
@import "../../../pillar/node_modules/bootstrap/scss/input-group"
|
||||||
@import "../../node_modules/bootstrap/scss/custom-forms"
|
@import "../../../pillar/node_modules/bootstrap/scss/custom-forms"
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/nav"
|
@import "../../../pillar/node_modules/bootstrap/scss/nav"
|
||||||
@import "../../node_modules/bootstrap/scss/navbar"
|
@import "../../../pillar/node_modules/bootstrap/scss/navbar"
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/card"
|
@import "../../../pillar/node_modules/bootstrap/scss/card"
|
||||||
@import "../../node_modules/bootstrap/scss/breadcrumb"
|
@import "../../../pillar/node_modules/bootstrap/scss/breadcrumb"
|
||||||
@import "../../node_modules/bootstrap/scss/pagination"
|
@import "../../../pillar/node_modules/bootstrap/scss/pagination"
|
||||||
@import "../../node_modules/bootstrap/scss/badge"
|
@import "../../../pillar/node_modules/bootstrap/scss/badge"
|
||||||
@import "../../node_modules/bootstrap/scss/jumbotron"
|
@import "../../../pillar/node_modules/bootstrap/scss/jumbotron"
|
||||||
@import "../../node_modules/bootstrap/scss/alert"
|
@import "../../../pillar/node_modules/bootstrap/scss/alert"
|
||||||
@import "../../node_modules/bootstrap/scss/progress"
|
@import "../../../pillar/node_modules/bootstrap/scss/progress"
|
||||||
@import "../../node_modules/bootstrap/scss/media"
|
@import "../../../pillar/node_modules/bootstrap/scss/media"
|
||||||
@import "../../node_modules/bootstrap/scss/list-group"
|
@import "../../../pillar/node_modules/bootstrap/scss/list-group"
|
||||||
@import "../../node_modules/bootstrap/scss/close"
|
@import "../../../pillar/node_modules/bootstrap/scss/close"
|
||||||
@import "../../node_modules/bootstrap/scss/modal"
|
@import "../../../pillar/node_modules/bootstrap/scss/modal"
|
||||||
@import "../../node_modules/bootstrap/scss/tooltip"
|
@import "../../../pillar/node_modules/bootstrap/scss/tooltip"
|
||||||
@import "../../node_modules/bootstrap/scss/popover"
|
@import "../../../pillar/node_modules/bootstrap/scss/popover"
|
||||||
@import "../../node_modules/bootstrap/scss/carousel"
|
@import "../../../pillar/node_modules/bootstrap/scss/carousel"
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/utilities"
|
@import "../../../pillar/node_modules/bootstrap/scss/utilities"
|
||||||
@import "../../node_modules/bootstrap/scss/print"
|
@import "../../../pillar/node_modules/bootstrap/scss/print"
|
||||||
|
|
||||||
|
|
||||||
// Pillar components.
|
// Pillar components.
|
||||||
@@ -72,6 +74,8 @@
|
|||||||
@import "../../../pillar/src/styles/components/checkbox"
|
@import "../../../pillar/src/styles/components/checkbox"
|
||||||
@import "../../../pillar/src/styles/components/overlay"
|
@import "../../../pillar/src/styles/components/overlay"
|
||||||
@import "../../../pillar/src/styles/components/card"
|
@import "../../../pillar/src/styles/components/card"
|
||||||
|
@import "../../../pillar/src/styles/components/placeholder"
|
||||||
|
@import "../../../pillar/src/styles/components/timeline"
|
||||||
|
|
||||||
@import "../../../pillar/src/styles/comments"
|
@import "../../../pillar/src/styles/comments"
|
||||||
@import "../../../pillar/src/styles/notifications"
|
@import "../../../pillar/src/styles/notifications"
|
||||||
@@ -82,10 +86,6 @@
|
|||||||
@import "../../../pillar/src/styles/_project-dashboard"
|
@import "../../../pillar/src/styles/_project-dashboard"
|
||||||
@import "../../../pillar/src/styles/_user"
|
@import "../../../pillar/src/styles/_user"
|
||||||
|
|
||||||
@import _welcome
|
|
||||||
@import _homepage
|
|
||||||
@import _services
|
|
||||||
@import _about
|
|
||||||
@import "../../../pillar/src/styles/_search"
|
@import "../../../pillar/src/styles/_search"
|
||||||
@import "../../../pillar/src/styles/_organizations"
|
@import "../../../pillar/src/styles/_organizations"
|
||||||
|
|
||||||
@@ -97,3 +97,12 @@
|
|||||||
@import "../../../pillar/src/styles/plugins/_js_select2"
|
@import "../../../pillar/src/styles/plugins/_js_select2"
|
||||||
|
|
||||||
/* CSS for pillar-font comes from fontello.com using static/assets/font/config.json */
|
/* CSS for pillar-font comes from fontello.com using static/assets/font/config.json */
|
||||||
|
|
||||||
|
@import variables
|
||||||
|
@import utils
|
||||||
|
|
||||||
|
@import welcome
|
||||||
|
@import services
|
||||||
|
@import about
|
||||||
|
@import homepage
|
||||||
|
@import list_films
|
||||||
|
@@ -1,384 +0,0 @@
|
|||||||
// Bootstrap variables and utilities.
|
|
||||||
@import "../../node_modules/bootstrap/scss/functions"
|
|
||||||
@import "../../node_modules/bootstrap/scss/variables"
|
|
||||||
@import "../../node_modules/bootstrap/scss/mixins"
|
|
||||||
|
|
||||||
// Pillar variables and utilities.
|
|
||||||
@import "../../../pillar/src/styles/config"
|
|
||||||
@import "../../../pillar/src/styles/utils"
|
|
||||||
|
|
||||||
// Bootstrap components.
|
|
||||||
@import "../../node_modules/bootstrap/scss/root"
|
|
||||||
@import "../../node_modules/bootstrap/scss/reboot"
|
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/type"
|
|
||||||
@import "../../node_modules/bootstrap/scss/images"
|
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/code"
|
|
||||||
@import "../../node_modules/bootstrap/scss/grid"
|
|
||||||
@import "../../node_modules/bootstrap/scss/tables"
|
|
||||||
@import "../../node_modules/bootstrap/scss/forms"
|
|
||||||
@import "../../node_modules/bootstrap/scss/buttons"
|
|
||||||
@import "../../node_modules/bootstrap/scss/transitions"
|
|
||||||
@import "../../node_modules/bootstrap/scss/dropdown"
|
|
||||||
@import "../../node_modules/bootstrap/scss/button-group"
|
|
||||||
@import "../../node_modules/bootstrap/scss/input-group"
|
|
||||||
@import "../../node_modules/bootstrap/scss/custom-forms"
|
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/nav"
|
|
||||||
@import "../../node_modules/bootstrap/scss/navbar"
|
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/card"
|
|
||||||
@import "../../node_modules/bootstrap/scss/breadcrumb"
|
|
||||||
@import "../../node_modules/bootstrap/scss/pagination"
|
|
||||||
@import "../../node_modules/bootstrap/scss/badge"
|
|
||||||
@import "../../node_modules/bootstrap/scss/jumbotron"
|
|
||||||
@import "../../node_modules/bootstrap/scss/alert"
|
|
||||||
@import "../../node_modules/bootstrap/scss/progress"
|
|
||||||
@import "../../node_modules/bootstrap/scss/media"
|
|
||||||
@import "../../node_modules/bootstrap/scss/list-group"
|
|
||||||
@import "../../node_modules/bootstrap/scss/close"
|
|
||||||
@import "../../node_modules/bootstrap/scss/modal"
|
|
||||||
@import "../../node_modules/bootstrap/scss/tooltip"
|
|
||||||
@import "../../node_modules/bootstrap/scss/popover"
|
|
||||||
@import "../../node_modules/bootstrap/scss/carousel"
|
|
||||||
|
|
||||||
@import "../../node_modules/bootstrap/scss/utilities"
|
|
||||||
@import "../../node_modules/bootstrap/scss/print"
|
|
||||||
|
|
||||||
|
|
||||||
// Pillar components.
|
|
||||||
@import "../../../pillar/src/styles/apps_base"
|
|
||||||
@import "../../../pillar/src/styles/error"
|
|
||||||
|
|
||||||
@import "../../../pillar/src/styles/components/base"
|
|
||||||
|
|
||||||
@import "../../../pillar/src/styles/components/jumbotron"
|
|
||||||
@import "../../../pillar/src/styles/components/alerts"
|
|
||||||
@import "../../../pillar/src/styles/components/navbar"
|
|
||||||
@import "../../../pillar/src/styles/components/dropdown"
|
|
||||||
@import "../../../pillar/src/styles/components/footer"
|
|
||||||
@import "../../../pillar/src/styles/components/shortcode"
|
|
||||||
@import "../../../pillar/src/styles/components/statusbar"
|
|
||||||
@import "../../../pillar/src/styles/components/search"
|
|
||||||
|
|
||||||
@import "../../../pillar/src/styles/components/flyout"
|
|
||||||
@import "../../../pillar/src/styles/components/inputs"
|
|
||||||
@import "../../../pillar/src/styles/components/buttons"
|
|
||||||
@import "../../../pillar/src/styles/components/popover"
|
|
||||||
@import "../../../pillar/src/styles/components/tooltip"
|
|
||||||
@import "../../../pillar/src/styles/components/checkbox"
|
|
||||||
@import "../../../pillar/src/styles/components/overlay"
|
|
||||||
@import "../../../pillar/src/styles/components/card"
|
|
||||||
|
|
||||||
@import "../../../pillar/src/styles/notifications"
|
|
||||||
|
|
||||||
@import "../../../pillar/src/styles/_search"
|
|
||||||
|
|
||||||
$node-latest-thumbnail-size: 160px
|
|
||||||
$node-latest-gallery-thumbnail-size: 200px
|
|
||||||
|
|
||||||
nav.navbar
|
|
||||||
.navbar-header
|
|
||||||
+media-xs
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
.navbar-toggle
|
|
||||||
border: none
|
|
||||||
color: $color-text
|
|
||||||
position: absolute
|
|
||||||
right: 10px
|
|
||||||
|
|
||||||
.navbar-nav
|
|
||||||
+media-xs
|
|
||||||
padding: 10px
|
|
||||||
|
|
||||||
.search-input
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.node-details-container
|
|
||||||
max-width: 620px
|
|
||||||
font-size: 1.3em
|
|
||||||
line-height: 1.5em
|
|
||||||
margin: 0 auto 40px auto
|
|
||||||
padding-bottom: 40px
|
|
||||||
|
|
||||||
+media-xs
|
|
||||||
padding-left: 10px
|
|
||||||
padding-right: 10px
|
|
||||||
|
|
||||||
p
|
|
||||||
margin-bottom: 1.3em
|
|
||||||
|
|
||||||
header
|
|
||||||
display: flex
|
|
||||||
flex-direction: column /* stack flex items vertically */
|
|
||||||
position: relative
|
|
||||||
img.header
|
|
||||||
width: 100%
|
|
||||||
flex-direction: column /* stack flex items vertically */
|
|
||||||
position: relative
|
|
||||||
a.page-card-cta
|
|
||||||
position: absolute
|
|
||||||
left: 76%
|
|
||||||
top: 50%
|
|
||||||
transform: translate(-50%, -50%)
|
|
||||||
color: white
|
|
||||||
font-weight: bold
|
|
||||||
background: #ff4970
|
|
||||||
border-radius: 3px
|
|
||||||
border: none
|
|
||||||
box-shadow: 1px 1px 0 rgba(black, .2)
|
|
||||||
padding: 7px 20px
|
|
||||||
text-decoration: none
|
|
||||||
text-shadow: none
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background: lighten(#ff4970, 5%)
|
|
||||||
|
|
||||||
.landing
|
|
||||||
h2
|
|
||||||
text-align: center
|
|
||||||
margin-bottom: 40px
|
|
||||||
|
|
||||||
section
|
|
||||||
max-width: 1024px
|
|
||||||
padding-top: 20px
|
|
||||||
border-top: thin solid $color-background
|
|
||||||
margin: 0 auto
|
|
||||||
|
|
||||||
.navbar-secondary
|
|
||||||
max-width: 620px
|
|
||||||
margin: 0 auto
|
|
||||||
|
|
||||||
.navbar-collapse
|
|
||||||
padding-left: 0
|
|
||||||
|
|
||||||
li
|
|
||||||
a
|
|
||||||
padding-left: 20px
|
|
||||||
padding-right: 20px
|
|
||||||
color: $color-text
|
|
||||||
&:hover
|
|
||||||
&.active
|
|
||||||
background: none
|
|
||||||
color: black
|
|
||||||
box-shadow: 0px 2px 0 rgba(red, .8)
|
|
||||||
|
|
||||||
.node-extra
|
|
||||||
display: flex
|
|
||||||
flex-direction: column
|
|
||||||
|
|
||||||
//padding: 0 20px
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
|
|
||||||
.node-updates
|
|
||||||
flex: 1
|
|
||||||
font-size: 1.1em
|
|
||||||
|
|
||||||
ul
|
|
||||||
padding: 0
|
|
||||||
margin: 0 0 15px 0
|
|
||||||
display: flex
|
|
||||||
flex-direction: row
|
|
||||||
flex-wrap: wrap
|
|
||||||
|
|
||||||
li
|
|
||||||
display: flex
|
|
||||||
flex-direction: column
|
|
||||||
list-style: none
|
|
||||||
padding: 5px
|
|
||||||
cursor: pointer
|
|
||||||
width: 33.3333%
|
|
||||||
|
|
||||||
+media-xs
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
&.texture, &.group_texture
|
|
||||||
width: 25%
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
img
|
|
||||||
opacity: .9
|
|
||||||
a.title
|
|
||||||
//color: $color-primary
|
|
||||||
text-decoration: underline
|
|
||||||
|
|
||||||
&.post
|
|
||||||
.info .title
|
|
||||||
//color: $node-type-post
|
|
||||||
font-size: 1.1em
|
|
||||||
a.image
|
|
||||||
border: none
|
|
||||||
//border-color: $node-type-post
|
|
||||||
background-color: hsl(hue($node-type-post), 20%, 55%)
|
|
||||||
|
|
||||||
&.asset.image a.image
|
|
||||||
border-color: $node-type-asset_image
|
|
||||||
background-color: hsl(hue($node-type-asset_image), 20%, 55%)
|
|
||||||
&.asset.file a.image
|
|
||||||
border-color: $node-type-asset_file
|
|
||||||
background-color: hsl(hue($node-type-asset_file), 20%, 55%)
|
|
||||||
&.asset.video a.image
|
|
||||||
border-color: $node-type-asset_video
|
|
||||||
background-color: hsl(hue($node-type-asset_video), 20%, 55%)
|
|
||||||
|
|
||||||
.image
|
|
||||||
width: 100%
|
|
||||||
height: $node-latest-thumbnail-size
|
|
||||||
min-height: $node-latest-thumbnail-size
|
|
||||||
max-height: $node-latest-thumbnail-size
|
|
||||||
background-color: $color-background
|
|
||||||
margin: 5px auto 10px auto
|
|
||||||
position: relative
|
|
||||||
overflow: hidden
|
|
||||||
border-radius: 0
|
|
||||||
|
|
||||||
img
|
|
||||||
max-height: $node-latest-thumbnail-size
|
|
||||||
+position-center-translate
|
|
||||||
|
|
||||||
i
|
|
||||||
color: rgba(white, .9)
|
|
||||||
font-size: 1.8em
|
|
||||||
position: absolute
|
|
||||||
bottom: 3px
|
|
||||||
left: 5px
|
|
||||||
text-shadow: 1px 1px 0 rgba(black, .2)
|
|
||||||
|
|
||||||
&.pi-file-archive
|
|
||||||
font-size: 1.5em
|
|
||||||
bottom: 5px
|
|
||||||
&.pi-newspaper
|
|
||||||
font-size: 1.6em
|
|
||||||
left: 7px
|
|
||||||
|
|
||||||
.ribbon
|
|
||||||
+ribbon
|
|
||||||
|
|
||||||
.info
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
display: flex
|
|
||||||
flex-direction: column
|
|
||||||
justify-content: space-between
|
|
||||||
word-break: break-word
|
|
||||||
|
|
||||||
.description
|
|
||||||
font-size: 1em
|
|
||||||
line-height: 1.8em
|
|
||||||
padding-top: 8px
|
|
||||||
color: $color-text-dark-primary
|
|
||||||
|
|
||||||
.title
|
|
||||||
display: block
|
|
||||||
font-size: 1.3em
|
|
||||||
color: $color-text-dark
|
|
||||||
font-weight: 600
|
|
||||||
+clearfix
|
|
||||||
+text-overflow-ellipsis
|
|
||||||
|
|
||||||
span.details
|
|
||||||
width: 100%
|
|
||||||
display: block
|
|
||||||
font-size: 1em
|
|
||||||
line-height: 1.2em
|
|
||||||
padding: 5px 0
|
|
||||||
color: $color-text-dark-secondary
|
|
||||||
+clearfix
|
|
||||||
|
|
||||||
.who
|
|
||||||
margin-left: 3px
|
|
||||||
.what
|
|
||||||
text-transform: capitalize
|
|
||||||
|
|
||||||
|
|
||||||
$bg-color: #444
|
|
||||||
$bg-color2: #666
|
|
||||||
$yellow: rgb(249,229,89)
|
|
||||||
$almost-white: rgb(255,255,255)
|
|
||||||
$btn-transparent-color: rgba(249,229,89,1)
|
|
||||||
$btn-transparent-bg: rgba(249,229,89,0)
|
|
||||||
|
|
||||||
|
|
||||||
section.gallery
|
|
||||||
max-width: 1024px
|
|
||||||
margin: 60px auto 0 auto
|
|
||||||
text-align: center
|
|
||||||
padding-bottom: 40px
|
|
||||||
|
|
||||||
p
|
|
||||||
color: $almost-white
|
|
||||||
padding: 0 40px
|
|
||||||
|
|
||||||
|
|
||||||
.thumbnail
|
|
||||||
float: left
|
|
||||||
position: relative
|
|
||||||
width: 23%
|
|
||||||
padding-bottom: 23%
|
|
||||||
margin: 0.83%
|
|
||||||
overflow: hidden
|
|
||||||
&:hover
|
|
||||||
box-shadow: 2px 2px 50px 0 rgba(0,0,0,0.3)
|
|
||||||
|
|
||||||
.img-container
|
|
||||||
position: absolute
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
img
|
|
||||||
width: 300%
|
|
||||||
transform: translate(-20%,-10%)
|
|
||||||
|
|
||||||
&:hover .img-caption
|
|
||||||
top: 0
|
|
||||||
left: 0
|
|
||||||
.btn-trans
|
|
||||||
background: rgba(255,255,255,0.4)
|
|
||||||
|
|
||||||
.img-caption
|
|
||||||
position: absolute
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
background: rgba(0, 0, 0, 0.3)
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
.table
|
|
||||||
display: table
|
|
||||||
.table-cell
|
|
||||||
display: table-cell
|
|
||||||
vertical-align: bottom
|
|
||||||
border: none
|
|
||||||
|
|
||||||
@media screen and (max-width: 992px)
|
|
||||||
.thumbnail
|
|
||||||
width: 22%
|
|
||||||
padding-bottom: 22%
|
|
||||||
margin: 1.5%
|
|
||||||
|
|
||||||
.img-container:hover .img-caption
|
|
||||||
top: 0
|
|
||||||
left: 0
|
|
||||||
|
|
||||||
.img-caption
|
|
||||||
position: absolute
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
background: rgba(0, 0, 0, .7)
|
|
||||||
text-align: center
|
|
||||||
a
|
|
||||||
color: $yellow
|
|
||||||
|
|
||||||
@media screen and (max-width: 720px)
|
|
||||||
.thumbnail
|
|
||||||
width: 29%
|
|
||||||
padding-bottom: 29%
|
|
||||||
margin: 2.16%
|
|
||||||
|
|
||||||
@media screen and (max-width: 470px)
|
|
||||||
.thumbnail
|
|
||||||
width: 44%
|
|
||||||
padding-bottom: 44%
|
|
||||||
margin: 3%
|
|
93
src/styles/project-main.sass
Normal file
93
src/styles/project-main.sass
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Bootstrap variables and utilities.
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/functions"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/variables"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/mixins"
|
||||||
|
|
||||||
|
// Pillar variables and utilities.
|
||||||
|
@import "../../../pillar/src/styles/_config"
|
||||||
|
@import "../../../pillar/src/styles/_utils"
|
||||||
|
|
||||||
|
$pillar-font-path: "../../../../static/pillar/assets/font"
|
||||||
|
@import "../../../pillar/src/styles/font-pillar"
|
||||||
|
|
||||||
|
// Bootstrap components.
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/root"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/reboot"
|
||||||
|
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/type"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/images"
|
||||||
|
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/code"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/grid"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/tables"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/forms"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/buttons"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/transitions"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/dropdown"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/button-group"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/input-group"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/custom-forms"
|
||||||
|
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/nav"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/navbar"
|
||||||
|
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/card"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/breadcrumb"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/pagination"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/badge"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/jumbotron"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/alert"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/progress"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/media"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/list-group"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/close"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/modal"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/tooltip"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/popover"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/carousel"
|
||||||
|
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/utilities"
|
||||||
|
@import "../../../pillar/node_modules/bootstrap/scss/print"
|
||||||
|
|
||||||
|
// Pillar components.
|
||||||
|
@import "../../../pillar/src/styles/apps_base"
|
||||||
|
@import "../../../pillar/src/styles/components/base"
|
||||||
|
|
||||||
|
@import "../../../pillar/src/styles/components/jumbotron"
|
||||||
|
@import "../../../pillar/src/styles/components/alerts"
|
||||||
|
@import "../../../pillar/src/styles/components/navbar"
|
||||||
|
@import "../../../pillar/src/styles/components/dropdown"
|
||||||
|
@import "../../../pillar/src/styles/components/footer"
|
||||||
|
@import "../../../pillar/src/styles/components/shortcode"
|
||||||
|
@import "../../../pillar/src/styles/components/statusbar"
|
||||||
|
@import "../../../pillar/src/styles/components/search"
|
||||||
|
@import "../../../pillar/src/styles/components/placeholder"
|
||||||
|
@import "../../../pillar/src/styles/components/timeline"
|
||||||
|
|
||||||
|
@import "../../../pillar/src/styles/components/flyout"
|
||||||
|
@import "../../../pillar/src/styles/components/forms"
|
||||||
|
@import "../../../pillar/src/styles/components/inputs"
|
||||||
|
@import "../../../pillar/src/styles/components/buttons"
|
||||||
|
@import "../../../pillar/src/styles/components/popover"
|
||||||
|
@import "../../../pillar/src/styles/components/tooltip"
|
||||||
|
@import "../../../pillar/src/styles/components/checkbox"
|
||||||
|
@import "../../../pillar/src/styles/components/overlay"
|
||||||
|
@import "../../../pillar/src/styles/components/card"
|
||||||
|
@import "../../../pillar/src/styles/components/breadcrumbs"
|
||||||
|
|
||||||
|
@import "../../../pillar/src/styles/_notifications"
|
||||||
|
@import "../../../pillar/src/styles/_comments"
|
||||||
|
|
||||||
|
@import "../../../pillar/src/styles/_project"
|
||||||
|
@import "../../../pillar/src/styles/_project-sharing"
|
||||||
|
@import "../../../pillar/src/styles/_project-dashboard"
|
||||||
|
@import "../../../pillar/src/styles/_error"
|
||||||
|
|
||||||
|
@import "../../../pillar/src/styles/_search"
|
||||||
|
|
||||||
|
@import "../../../pillar/src/styles/plugins/_jstree"
|
||||||
|
@import "../../../pillar/src/styles/plugins/_js_select2"
|
||||||
|
|
||||||
|
// Cloud components.
|
||||||
|
@import variables
|
||||||
|
@import "_project-landing"
|
124
src/templates/_footer.pug
Normal file
124
src/templates/_footer.pug
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
.footer-wrapper
|
||||||
|
| {% block footer_navigation %}
|
||||||
|
.footer-navigation
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col-md-4.col-xs-6
|
||||||
|
h4
|
||||||
|
a(href="{{ url_for('main.homepage') }}")
|
||||||
|
i.pi-blender-cloud-logo
|
||||||
|
|
||||||
|
p.pl-2.
|
||||||
|
Blender Cloud is the creative hub for your projects,
|
||||||
|
powered by Free and Open Source Software.
|
||||||
|
|
||||||
|
h5.d-flex
|
||||||
|
a.px-2(href="https://www.youtube.com/BlenderCloudOfficial",
|
||||||
|
title="Blender Cloud YouTube Channel")
|
||||||
|
i.pi-social-youtube
|
||||||
|
|
||||||
|
a.px-2(href="https://twitter.com/Blender_Cloud",
|
||||||
|
title="Follow us on Twitter")
|
||||||
|
i.pi-social-twitter
|
||||||
|
|
||||||
|
a.px-2(href="https://www.facebook.com/BlenderCloudOfficial/",
|
||||||
|
title="Follow us on Facebook")
|
||||||
|
i.pi-social-facebook
|
||||||
|
|
||||||
|
.col-md-2.col-xs-6
|
||||||
|
h7.font-weight-bold
|
||||||
|
a.d-block.pb-2(href="{{ url_for('cloud.learn') }}")
|
||||||
|
| TRAINING
|
||||||
|
|
||||||
|
ul.list-unstyled.mb-3
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('cloud.courses') }}")
|
||||||
|
| Courses
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('cloud.workshops') }}")
|
||||||
|
| Workshops
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('cloud.production') }}")
|
||||||
|
span.new Production Lessons
|
||||||
|
|
||||||
|
h7.font-weight-bold
|
||||||
|
a.d-block.pb-2(href="{{ url_for('cloud.open_projects') }}")
|
||||||
|
| FILMS
|
||||||
|
|
||||||
|
.col-md-2.col-xs-6
|
||||||
|
h7.font-weight-bold
|
||||||
|
a.d-block.pb-2(href="{{ url_for('cloud.libraries') }}")
|
||||||
|
| LIBRARIES
|
||||||
|
|
||||||
|
ul.list-unstyled
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('projects.view', project_url='hdri') }}",
|
||||||
|
title="HDRI Library")
|
||||||
|
| HDRIs
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('projects.view', project_url='textures') }}",
|
||||||
|
title="Texture Library")
|
||||||
|
| Textures
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('projects.view', project_url='characters') }}",
|
||||||
|
title="Characters")
|
||||||
|
| Characters
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('projects.view', project_url='gallery') }}")
|
||||||
|
| Art Gallery
|
||||||
|
|
||||||
|
.col-md-2.col-xs-6
|
||||||
|
h7.font-weight-bold
|
||||||
|
a.d-block.pb-2(href="{{ url_for('cloud.services') }}")
|
||||||
|
| SERVICES
|
||||||
|
|
||||||
|
ul.list-unstyled
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('cloud.services') }}#blender-cloud-add-on",
|
||||||
|
title="Blender Cloud add-on")
|
||||||
|
| Add-on
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('projects.home_project') }}",
|
||||||
|
title="Your synced Blender settings")
|
||||||
|
| Blender Sync
|
||||||
|
li
|
||||||
|
a(href="/attract",
|
||||||
|
title="Production management")
|
||||||
|
| Attract
|
||||||
|
li
|
||||||
|
a(href="/flamenco",
|
||||||
|
title="Render management")
|
||||||
|
| Flamenco
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('projects.home_project_shared_images')}}",
|
||||||
|
title="Share your images from within Blender")
|
||||||
|
| Image Sharing
|
||||||
|
|
||||||
|
.col-md-2.col-xs-6
|
||||||
|
h7.font-weight-bold
|
||||||
|
a.d-block.pb-2(href="{{ url_for('main.homepage') }}")
|
||||||
|
| CLOUD
|
||||||
|
|
||||||
|
ul.list-unstyled
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('main.main_blog') }}",
|
||||||
|
title="Blender Cloud Blog")
|
||||||
|
| Blog
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('cloud.terms_and_conditions') }}",
|
||||||
|
title="Terms and Conditions")
|
||||||
|
| Terms and Conditions
|
||||||
|
li
|
||||||
|
a(href="{{ url_for('cloud.privacy') }}",
|
||||||
|
title="Privacy")
|
||||||
|
| Privacy Policy
|
||||||
|
li.dropdown-divider
|
||||||
|
li
|
||||||
|
a(href="https://www.blender.org",
|
||||||
|
title="Home of Blender, the Free and Open Source creative suite")
|
||||||
|
| blender.org
|
||||||
|
|
||||||
|
| {% endblock footer_navigation %}
|
||||||
|
|
||||||
|
#hop(title="Be awesome in space")
|
||||||
|
i.pi-angle-up
|
@@ -1,26 +1,174 @@
|
|||||||
include ../mixins/components
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% macro navigation_tabs(title) %}
|
| {#
|
||||||
+nav-secondary()
|
| Secondary Navigation Bars.
|
||||||
|
| #}
|
||||||
|
|
||||||
|
| {% macro navigation_homepage(title) %}
|
||||||
|
button.navbar-toggler(
|
||||||
|
type="button",
|
||||||
|
data-toggle="collapse",
|
||||||
|
data-target="#navigationLinks",
|
||||||
|
aria-controls="navigationLinks",
|
||||||
|
aria-expanded="false",
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
)
|
||||||
|
i.pi-blender-cloud
|
||||||
|
i.pi-angle-down
|
||||||
|
+nav-secondary(class="collapse navbar-collapse")#navigationLinks
|
||||||
+nav-secondary-link(
|
+nav-secondary-link(
|
||||||
class="{% if title == 'homepage' %}active{% endif %}",
|
href="{{ url_for('main.homepage') }}")
|
||||||
href="{{ url_for('main.homepage') }}")
|
i.pi-blender-cloud-logo
|
||||||
| Activity
|
|
||||||
|
|
||||||
+nav-secondary-link(
|
+nav-secondary-link(
|
||||||
class="{% if title == 'home' %}active{% endif %}",
|
href="{{ url_for('cloud.open_projects') }}",
|
||||||
href="{{ url_for('projects.home_project') }}")
|
class="{% if title == 'films' %}active{% endif %}")
|
||||||
| Home
|
span Films
|
||||||
|
|
||||||
+nav-secondary-link(
|
+nav-secondary-link(
|
||||||
class="{% if title == 'dashboard' %}active{% endif %}",
|
href="{{ url_for('cloud.learn') }}",
|
||||||
href="{{ url_for('projects.index') }}")
|
class="{% if title in ('learn', 'courses', 'workshops') %}active{% endif %}")
|
||||||
| My Projects
|
span Training
|
||||||
|
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('cloud.libraries') }}",
|
||||||
|
class="{% if title == 'libraries' %}active{% endif %}")
|
||||||
|
span Libraries
|
||||||
|
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('cloud.services') }}",
|
||||||
|
class="{% if title == 'services' %}active{% endif %}")
|
||||||
|
span Services
|
||||||
|
| {% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
| {% macro navigation_home_project(title) %}
|
||||||
|
button.navbar-toggler(
|
||||||
|
type="button",
|
||||||
|
data-toggle="collapse",
|
||||||
|
data-target="#navigationLinks",
|
||||||
|
aria-controls="navigationLinks",
|
||||||
|
aria-expanded="false",
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
)
|
||||||
|
i.pi-blender-cloud
|
||||||
|
i.pi-angle-down
|
||||||
|
+nav-secondary(class="collapse navbar-collapse")#navigationLinks
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('main.homepage') }}")
|
||||||
|
i.pi-blender-cloud
|
||||||
|
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('projects.index') }}",
|
||||||
|
class="{% if title == 'dashboard' %}active{% endif %}")
|
||||||
|
i.pi-star.pr-2
|
||||||
|
span My Projects
|
||||||
|
|
||||||
| {% if current_user.has_organizations() %}
|
| {% if current_user.has_organizations() %}
|
||||||
+nav-secondary-link(
|
+nav-secondary-link(
|
||||||
class="{% if title == 'organizations' %}active{% endif %}",
|
href="{{ url_for('pillar.web.organizations.index') }}",
|
||||||
href="{{ url_for('pillar.web.organizations.index') }}")
|
class="{% if title == 'organizations' %}active{% endif %}")
|
||||||
| My Organizations
|
i.pi-users.pr-2
|
||||||
|
span My Organizations
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('projects.home_project_shared_images')}}",
|
||||||
|
class="{% if title == 'images' %}active{% endif %}")
|
||||||
|
i.pi-picture.pr-2
|
||||||
|
span Image Sharing
|
||||||
|
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('projects.home_project') }}",
|
||||||
|
class="{% if title == 'blender-sync' %}active{% endif %}")
|
||||||
|
i.pi-blender.pr-2
|
||||||
|
span Blender Sync
|
||||||
|
| {% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
| {% macro navigation_project(project, navigation_links, extension_sidebar_links, title) %}
|
||||||
|
|
||||||
|
| {% if project.category == 'course' %}
|
||||||
|
| {% set category_url = url_for('cloud.courses') %}
|
||||||
|
| {% set category_title = 'Courses' %}
|
||||||
|
|
||||||
|
| {% elif project.category == 'workshop' %}
|
||||||
|
| {% set category_url = url_for('cloud.workshops') %}
|
||||||
|
| {% set category_title = 'Workshops' %}
|
||||||
|
|
||||||
|
| {% elif project.category == 'film' %}
|
||||||
|
| {% set category_url = url_for('cloud.open_projects') %}
|
||||||
|
| {% set category_title = 'Films' %}
|
||||||
|
|
||||||
|
| {% elif project.category == 'assets' %}
|
||||||
|
| {% set category_url = url_for('cloud.libraries') %}
|
||||||
|
| {% set category_title = 'Libraries' %}
|
||||||
|
|
||||||
|
| {% else %}
|
||||||
|
| {% set category_url = url_for('main.homepage') %}
|
||||||
|
| {% set category_title = project.category %}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
button.navbar-toggler(
|
||||||
|
type="button",
|
||||||
|
data-toggle="collapse",
|
||||||
|
data-target="#navigationLinks",
|
||||||
|
aria-controls="navigationLinks",
|
||||||
|
aria-expanded="false",
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
)
|
||||||
|
i.pi-blender-cloud
|
||||||
|
i.pi-angle-down
|
||||||
|
|
||||||
|
+nav-secondary(class="collapse navbar-collapse")#navigationLinks
|
||||||
|
|
||||||
|
//- Blender Cloud logo.
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('main.homepage') }}")
|
||||||
|
i.pi-blender-cloud
|
||||||
|
|
||||||
|
//- Category (Films, Courses, etc).
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ category_url }}",
|
||||||
|
class="px-0")
|
||||||
|
span {{ category_title }}
|
||||||
|
li(class="nav-item px-1")
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
//- Project Name.
|
||||||
|
| {% if project.url != 'blender-cloud' %}
|
||||||
|
+nav-secondary-link(
|
||||||
|
class="font-weight-bold{% if title == 'default' %} active{% endif %} px-0",
|
||||||
|
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
||||||
|
span {{ project.name }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
//- Pages (Blog, Team, Awards, etc).
|
||||||
|
| {% for link in navigation_links %}
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ link['url'] }}",
|
||||||
|
class="{% if link['slug'] == title %}active{% endif %}")
|
||||||
|
span {{ link['label'] }}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('cloud.project_browse', project_url=project.url) }}",
|
||||||
|
title="Browse {{ project.name }}",
|
||||||
|
class="{% if title == 'project' %}active{% endif %}")
|
||||||
|
span Browse
|
||||||
|
|
||||||
|
//- Link to Production Tools (Attract, Flamenco, SVN, etc).
|
||||||
|
| {% if extension_sidebar_links %}
|
||||||
|
+nav-secondary()
|
||||||
|
li.nav-item.dropdown
|
||||||
|
a.nav-link.dropdown-toggle(
|
||||||
|
class="{% if title == 'production-tools' %}active{% endif %}"
|
||||||
|
href="#"
|
||||||
|
data-toggle="dropdown")
|
||||||
|
span Production Tools
|
||||||
|
i.pi-angle-down
|
||||||
|
|
||||||
|
ul.dropdown-menu
|
||||||
|
| {{ extension_sidebar_links }}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
| {% endmacro %}
|
| {% endmacro %}
|
||||||
|
17
src/templates/_macros/_opengraph.pug
Normal file
17
src/templates/_macros/_opengraph.pug
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//- Opengraph/Twitter cards for social media.
|
||||||
|
|
||||||
|
| {% macro opengraph(title, description, url_image, url_page) %}
|
||||||
|
meta(property="og:type", content="website")
|
||||||
|
meta(property="og:url", content="{{ url_page }}")
|
||||||
|
|
||||||
|
meta(property="og:title", content="{{ title }} on Blender Cloud")
|
||||||
|
meta(name="twitter:title", content="{{ title }} on Blender Cloud")
|
||||||
|
|
||||||
|
meta(property="og:description", content="{{ description }}")
|
||||||
|
meta(name="twitter:description", content="{{ description }}")
|
||||||
|
|
||||||
|
| {% if url_image %}
|
||||||
|
meta(property="og:image", content="{{ url_image }}")
|
||||||
|
meta(name="twitter:image", content="{{ url_image }}")
|
||||||
|
| {% endif %}
|
||||||
|
| {% endmacro %}
|
@@ -193,16 +193,16 @@ style.
|
|||||||
small October 30th, 2015
|
small October 30th, 2015
|
||||||
.page-card-summary
|
.page-card-summary
|
||||||
| Introducing integrated blogs in Blender Cloud projects. Glass Half is the first project fully developed on the new Blender Cloud. It's also the first and only project to have share its
|
| Introducing integrated blogs in Blender Cloud projects. Glass Half is the first project fully developed on the new Blender Cloud. It's also the first and only project to have share its
|
||||||
a(href='https://cloud.blender.org/p/glass-half/5627bb22f0e7220061109c9f') animation dailies
|
a(href='/p/glass-half/5627bb22f0e7220061109c9f') animation dailies
|
||||||
| ! But the biggest outcome from Glass Half was definitely
|
| ! But the biggest outcome from Glass Half was definitely
|
||||||
a(href='https://cloud.blender.org/p/glass-half/569d6044c379cf445461293e') Flexirig
|
a(href='/p/glass-half/569d6044c379cf445461293e') Flexirig
|
||||||
| .
|
| .
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/p/glass-half/blog/glass-half-premiere')
|
a(href='/p/glass-half/blog/glass-half-premiere')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_10_30_glass.jpg') }}", alt="Glass Half")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_10_30_glass.jpg') }}", alt="Glass Half")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/new-art-gallery-with-gleb-alexandrov')
|
a(href='/blog/new-art-gallery-with-gleb-alexandrov')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_11_19_art.jpg') }}", alt="Art Gallery")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_11_19_art.jpg') }}", alt="Art Gallery")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -218,11 +218,11 @@ style.
|
|||||||
.page-card-summary
|
.page-card-summary
|
||||||
| With so much going on in the Cloud at at the studio. The Blender Institute Podcast was born! Sharing our daily studio work, Blender community news, and interacting with the awesome Blender Cloud subscribers.
|
| With so much going on in the Cloud at at the studio. The Blender Institute Podcast was born! Sharing our daily studio work, Blender community news, and interacting with the awesome Blender Cloud subscribers.
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-blender-institute-podcast')
|
a(href='/blog/introducing-blender-institute-podcast')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_11_24_bip.jpg') }}", alt="Blender Institute Podcast")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_11_24_bip.jpg') }}", alt="Blender Institute Podcast")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/p/blenrig/blog/welcome-to-the-blenrig-project')
|
a(href='/p/blenrig/blog/welcome-to-the-blenrig-project')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_12_01_blenrig.jpg') }}", alt="Blenrig")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_12_01_blenrig.jpg') }}", alt="Blenrig")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -238,11 +238,11 @@ style.
|
|||||||
.page-card-summary
|
.page-card-summary
|
||||||
| The biggest source for CC0/Public Domain textures on the interwebs goes live. First as beta, as a quick gift right before Xmas 2015!
|
| The biggest source for CC0/Public Domain textures on the interwebs goes live. First as beta, as a quick gift right before Xmas 2015!
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/new-texture-library')
|
a(href='/blog/new-texture-library')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_12_23_textures.jpg') }}", alt="Texture Library")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2015_12_23_textures.jpg') }}", alt="Texture Library")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/nraryew-the-character-lib')
|
a(href='/blog/nraryew-the-character-lib')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_01_05_charlib.jpg') }}", alt="Character Library")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_01_05_charlib.jpg') }}", alt="Character Library")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -262,11 +262,11 @@ style.
|
|||||||
a(href='https://www.youtube.com/watch?v=kQH897V9bDg&list=PLI2TkLMzCSr_H6ppmzDtU0ut0RwxGvXjv') nicely edited Weekly video reports
|
a(href='https://www.youtube.com/watch?v=kQH897V9bDg&list=PLI2TkLMzCSr_H6ppmzDtU0ut0RwxGvXjv') nicely edited Weekly video reports
|
||||||
| .
|
| .
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/p/caminandes-3/blog/caminandes-llamigos')
|
a(href='/p/caminandes-3/blog/caminandes-llamigos')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_01_30_llamigos.jpg') }}", alt="Caminandes: Llamigos")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_01_30_llamigos.jpg') }}", alt="Caminandes: Llamigos")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/welcome-sybren')
|
a(href='/blog/welcome-sybren')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_03_01_sybren.jpg') }}", alt="Dr. Sybren!")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_03_01_sybren.jpg') }}", alt="Dr. Sybren!")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -282,11 +282,11 @@ style.
|
|||||||
.page-card-summary
|
.page-card-summary
|
||||||
| Create your own private projects on Blender Cloud.
|
| Create your own private projects on Blender Cloud.
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/welcome-sybren')
|
a(href='/blog/welcome-sybren')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_03_projects.jpg') }}", alt="Projects")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_03_projects.jpg') }}", alt="Projects")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-project-sharing')
|
a(href='/blog/introducing-project-sharing')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_09_projectsharing.jpg') }}", alt="Sharing")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_09_projectsharing.jpg') }}", alt="Sharing")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -302,11 +302,11 @@ style.
|
|||||||
.page-card-summary
|
.page-card-summary
|
||||||
| Browse the textures from within Blender!
|
| Browse the textures from within Blender!
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-project-sharing')
|
a(href='/blog/introducing-project-sharing')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_11_addon.jpg') }}", alt="Blender Cloud Add-on")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_11_addon.jpg') }}", alt="Blender Cloud Add-on")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-private-texture-libraries')
|
a(href='/blog/introducing-private-texture-libraries')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_23_privtextures.jpg') }}", alt="Texture Libraries")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_05_23_privtextures.jpg') }}", alt="Texture Libraries")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -322,11 +322,11 @@ style.
|
|||||||
.page-card-summary
|
.page-card-summary
|
||||||
| Sync your Blender preferences across multiple devices.
|
| Sync your Blender preferences across multiple devices.
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-blender-sync')
|
a(href='/blog/introducing-blender-sync')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_06_30_sync.jpg') }}", alt="Blender Sync")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_06_30_sync.jpg') }}", alt="Blender Sync")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-image-sharing')
|
a(href='/blog/introducing-image-sharing')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_07_14_image.jpg') }}", alt="Image Sharing")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_07_14_image.jpg') }}", alt="Image Sharing")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
@@ -337,21 +337,21 @@ style.
|
|||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
a(href='https://cloud.blender.org/blog/introducing-the-hdri-library')
|
a(href='/blog/introducing-the-hdri-library')
|
||||||
| HDRI Library
|
| HDRI Library
|
||||||
small July 27th, 2016
|
small July 27th, 2016
|
||||||
.page-card-summary
|
.page-card-summary
|
||||||
| High-dynamic range images are now available on Blender Cloud! With their own special viewer. Also available via the Blender Cloud add-on.
|
| High-dynamic range images are now available on Blender Cloud! With their own special viewer. Also available via the Blender Cloud add-on.
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-the-hdri-library')
|
a(href='/blog/introducing-the-hdri-library')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_07_27_hdri.jpg') }}", alt="HDRI Library")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_07_27_hdri.jpg') }}", alt="HDRI Library")
|
||||||
section.page-card
|
section.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/blog/introducing-the-hdri-library')
|
a(href='/blog/introducing-the-hdri-library')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_12_06_toon.jpg') }}", alt="Hdri Library")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2016_12_06_toon.jpg') }}", alt="Hdri Library")
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
a(href='https://cloud.blender.org/blog/new-training-toon-character-workflow')
|
a(href='/blog/new-training-toon-character-workflow')
|
||||||
| Toon Character Workflow
|
| Toon Character Workflow
|
||||||
small December 6th, 2016
|
small December 6th, 2016
|
||||||
.page-card-summary
|
.page-card-summary
|
||||||
@@ -366,7 +366,7 @@ style.
|
|||||||
| to all resources and training produced so far!
|
| to all resources and training produced so far!
|
||||||
a.page-card-cta(href='https://store.blender.org/product/membership/') Subscribe
|
a.page-card-cta(href='https://store.blender.org/product/membership/') Subscribe
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a(href='https://cloud.blender.org/p/agent-327')
|
a(href='/p/agent-327')
|
||||||
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2017_03_10_agent.jpg') }}", alt="Agent 327")
|
img.img-responsive(src="{{ url_for('static_cloud', filename='img/2017_03_10_agent.jpg') }}", alt="Agent 327")
|
||||||
|
|
||||||
|
|
||||||
|
7
src/templates/design_system.pug
Normal file
7
src/templates/design_system.pug
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
| {% extends 'layout.html' %}
|
||||||
|
|
||||||
|
| {% block body %}
|
||||||
|
.row.py-2
|
||||||
|
.col.text-center
|
||||||
|
h1 Design System goes here
|
||||||
|
| {% endblock %}
|
@@ -11,7 +11,7 @@ section
|
|||||||
our team to create more Open Projects, training, services and of course to make Blender the best
|
our team to create more Open Projects, training, services and of course to make Blender the best
|
||||||
CG pipeline in the world. You rock!
|
CG pipeline in the world. You rock!
|
||||||
p.buttons
|
p.buttons
|
||||||
a.button(href="{{ abs_url('cloud.login', next='/') }}", target='_blank') Explore Now >
|
a.button(href="{{ abs_url('cloud.login', next='/') }}", target='_blank') Browse Now >
|
||||||
|
|
||||||
p.
|
p.
|
||||||
Here is a quick guide to help you get started with Blender Cloud.
|
Here is a quick guide to help you get started with Blender Cloud.
|
||||||
|
61
src/templates/films.pug
Normal file
61
src/templates/films.pug
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
| {% extends 'layout.html' %}
|
||||||
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
include mixins/components
|
||||||
|
|
||||||
|
| {% set page_title = 'Films' %}
|
||||||
|
| {% set page_description = 'The iconic Blender Open Movies. Featuring all the production files, assets, artwork, and never-seen-before content.' %}
|
||||||
|
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_agent327_01.jpg', _external=true) %}
|
||||||
|
|
||||||
|
| {% block page_title %}{{ page_title }}{% endblock %}
|
||||||
|
|
||||||
|
| {% block og %}
|
||||||
|
| {{ opengraph(page_title, page_description, page_header_image, request.url) }}
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_homepage(title) }}
|
||||||
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
|
| {% block body %}
|
||||||
|
.container.py-4
|
||||||
|
+category_list_header('{{ page_title }}', '{{ page_description }}')
|
||||||
|
|
||||||
|
.row.films-list
|
||||||
|
| {% for project in projects %}
|
||||||
|
|
||||||
|
| {% if (project.status == 'published') or (project.status == 'pending' and current_user.is_authenticated) and project._id != config.MAIN_PROJECT_ID %}
|
||||||
|
| {% set project_url = url_for('projects.view', project_url=project.url) %}
|
||||||
|
|
||||||
|
.films-item.col-md-4.col-sm-6.col-lg-3.my-5
|
||||||
|
.d-flex.flex-column.h-100
|
||||||
|
| {% if project.has_poster %} {# Check convenience attribute set in open_projects() #}
|
||||||
|
a.mx-auto(
|
||||||
|
href="{{ project_url }}",
|
||||||
|
tabindex='{{ loop.index }}')
|
||||||
|
img.rounded.w-100(
|
||||||
|
alt="{{ project.name }}",
|
||||||
|
src="{{ project.extension_props.cloud.poster.thumbnail('l', api=api) }}")
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
h4.pt-5.pb-3.text-center
|
||||||
|
a.text-white(href="{{ project_url }}")
|
||||||
|
| {{ project.name }}
|
||||||
|
|
||||||
|
| {% if project.summary %}
|
||||||
|
a.lead.text-secondary(href="{{ project_url }}")
|
||||||
|
small {{ project | markdowned('summary') }}
|
||||||
|
|
||||||
|
.d-flex.align-items-center.mt-auto
|
||||||
|
a.btn-link.mr-auto.my-3(href="{{ project_url }}")
|
||||||
|
| Browse #[i.pi-angle-right]
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
| {% if project.status == 'pending' and current_user.is_authenticated and current_user.has_role('admin') %}
|
||||||
|
p.text-danger
|
||||||
|
small Project Not Published
|
||||||
|
| {% endif %}
|
||||||
|
| {% endif %}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
|
| {% endblock body %}
|
@@ -1,12 +1,15 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
|
| {% from '_macros/_asset_list_item.html' import asset_list_item %}
|
||||||
| {% from 'nodes/custom/blog/_macros.html' import render_blog_post %}
|
| {% from 'nodes/custom/blog/_macros.html' import render_blog_post %}
|
||||||
|
|
||||||
|
include ../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% set title = 'homepage' %}
|
| {% set title = 'homepage' %}
|
||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:type", content="website")
|
meta(property="og:type", content="website")
|
||||||
meta(property="og:url", content="https://cloud.blender.org/")
|
meta(property="og:url", content="{{ request.url }}")
|
||||||
|
|
||||||
meta(property="og:title", content="Blender Cloud")
|
meta(property="og:title", content="Blender Cloud")
|
||||||
meta(name="twitter:title", content="Blender Cloud")
|
meta(name="twitter:title", content="Blender Cloud")
|
||||||
@@ -19,228 +22,124 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
|
|||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
| {{ navigation_tabs(title) }}
|
| {{ navigation_homepage(title) }}
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
|
mixin featured_project_card(title, description, url, image)
|
||||||
|
a.featured-project-card.card.asset.my-2(href=url)
|
||||||
|
img.card-thumbnail(alt=title, src=image)
|
||||||
|
.card-body.py-2()
|
||||||
|
.card-title.mb-1.font-weight-bold
|
||||||
|
=title
|
||||||
|
.card-text
|
||||||
|
=description
|
||||||
|
|
||||||
|
.btn-link
|
||||||
|
| See more
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
mixin featured_projects()
|
||||||
|
section.py-2.mb-3
|
||||||
|
h6.title-underline
|
||||||
|
| Featured Projects
|
||||||
|
|
||||||
|
.featured-projects
|
||||||
|
+card-deck(3)&attributes(attributes)
|
||||||
|
+featured_project_card(
|
||||||
|
"MASTER SHADING NODES",
|
||||||
|
"Dive into a sea of nodes with this training by Simon Thommes.",
|
||||||
|
"/p/procedural-shading",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/training_procedural_shading_01.jpg')}}")
|
||||||
|
|
||||||
|
+featured_project_card(
|
||||||
|
"IMPROVE YOUR RIGS",
|
||||||
|
"The ultimate guide to learn about this crucial step while rigging.",
|
||||||
|
"/p/weight-painting",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/training_weight_painting_01.jpg')}}")
|
||||||
|
|
||||||
|
+featured_project_card(
|
||||||
|
"COFFEE RUN",
|
||||||
|
"This 2d-scroller-inspired short film will take you on the journey of a lifetime.",
|
||||||
|
"/p/coffee-run",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/coffee_run_02.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
.container-fluid.dashboard-container.imgs-fluid
|
.container-fluid.dashboard-container.imgs-fluid
|
||||||
.row
|
.row.mt-3
|
||||||
.col-md-8
|
.col-md-10.col-lg-9.col-xl-8.mx-auto
|
||||||
section.blog
|
.d-xl-none
|
||||||
ul.list-unstyled
|
+featured_projects()
|
||||||
| {% if latest_posts %}
|
|
||||||
| {% for node in latest_posts %}
|
+timeline()
|
||||||
| {{ render_blog_post(node) }}
|
|
||||||
|
.d-block.text-center
|
||||||
|
a.d-inline-block.p-3.text-muted(href="{{ url_for('main.main_blog') }}")
|
||||||
|
| See All Blog Posts
|
||||||
|
|
||||||
|
a.d-inline-block.p-3.text-muted(
|
||||||
|
href="{{ url_for('main.feeds_blogs') }}",
|
||||||
|
title="Blogs Feed",
|
||||||
|
data-toggle="tooltip",
|
||||||
|
data-placement="left")
|
||||||
|
i.pi-rss
|
||||||
|
| RSS Feed
|
||||||
|
|
||||||
|
.col-md-10.col-lg-9.col-xl-4.mx-auto
|
||||||
|
.d-lg-none.d-xl-block
|
||||||
|
+featured_projects()(class="card-deck-vertical border-bottom pb-3")
|
||||||
|
|
||||||
|
section.py-2.border-bottom.mb-3
|
||||||
|
h6.title-underline
|
||||||
|
a.text-muted(href="{{ url_for('main.nodes_search_index') }}")
|
||||||
|
| Random Awesome
|
||||||
|
|
||||||
|
| {% if random_featured %}
|
||||||
|
+card-deck()(class='pl-3 random-featured')
|
||||||
|
| {% for child in random_featured %}
|
||||||
|
| {% if child.node_type not in ['comment'] %}
|
||||||
|
| {{ asset_list_item(child, current_user) }}
|
||||||
|
| {% endif %}
|
||||||
| {% endfor %}
|
| {% endfor %}
|
||||||
|
| {% else %}
|
||||||
|
.card
|
||||||
|
.card-body
|
||||||
|
h6.card-title
|
||||||
|
| No random featured.
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
section.py-3
|
||||||
|
h6.title-underline Latest Comments
|
||||||
|
|
||||||
|
ul.list-unstyled.pt-2
|
||||||
|
| {% if latest_comments %}
|
||||||
|
| {% for n in latest_comments %}
|
||||||
|
li.pb-2.mb-2.border-bottom.text-truncate
|
||||||
|
|
||||||
|
a.js-comment-content.text-muted(href="{{ n.url }}")
|
||||||
|
| {{ n.properties.content | striptags | truncate(200) }}
|
||||||
|
|
||||||
|
| {% if n.attached_to %}
|
||||||
|
.d-flex.align-items-baseline
|
||||||
|
a.text-muted.text-truncate(href="{{ n.attached_to.url }}")
|
||||||
|
small.pr-2.font-weight-bold {{ n.project.name }}
|
||||||
|
small {{ n.attached_to.name }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
.d-flex.align-items-baseline
|
||||||
|
small.pr-2.font-weight-bold {{ n.user.full_name }}
|
||||||
|
|
||||||
|
a.text-muted(href="{{ n.url }}", title="{{ n._created }}")
|
||||||
|
small {{ n._created | pretty_date }}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
| {% else %}
|
| {% else %}
|
||||||
li
|
span
|
||||||
| No blog entries... yet!
|
| No comments... yet!
|
||||||
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
.d-block.text-center
|
|
||||||
a.d-inline-block.p-3.text-muted(href="{{ url_for('main.main_blog') }}")
|
|
||||||
| See All Blog Posts
|
|
||||||
|
|
||||||
a.d-inline-block.p-3.text-muted(
|
|
||||||
href="{{ url_for('main.feeds_blogs') }}",
|
|
||||||
title="Blogs Feed",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-rss
|
|
||||||
| RSS Feed
|
|
||||||
|
|
||||||
.col-md-4
|
|
||||||
.dashboard-sidebar
|
|
||||||
section.pt-3
|
|
||||||
h6.title-underline In Production
|
|
||||||
a(href="/p/spring/")
|
|
||||||
img(src="{{ url_for('static', filename='assets/img/projects/spring_450x150.jpg')}}")
|
|
||||||
|
|
||||||
p.text-muted.pt-2.
|
|
||||||
A poetic short film about a mountain spirit and her wise little dog. #[a(href="/p/spring/") Check it out].
|
|
||||||
|
|
||||||
section.stream.py-3
|
|
||||||
h6.title-underline Latest Assets
|
|
||||||
|
|
||||||
ul.activity-stream__list.list-unstyled
|
|
||||||
| {% for n in activity_stream %}
|
|
||||||
li(
|
|
||||||
class="{{ n.node_type }} {{ n.properties.content_type }} {% if n.picture %}with-picture{% endif %}",
|
|
||||||
data-url="{{ n.url }}")
|
|
||||||
a.activity-stream__list-thumbnail(
|
|
||||||
class="{{ n.properties.content_type }}",
|
|
||||||
href="{{ n.url }}")
|
|
||||||
| {% if n.picture %}
|
|
||||||
img(src="{{ n.picture.thumbnail('m', api=api) }}")
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
.activity-stream__list-thumbnail-icon
|
|
||||||
| {% if n.node_type == 'asset' %}
|
|
||||||
| {% if n.properties.content_type == 'video' %}
|
|
||||||
i.pi-play
|
|
||||||
| {% elif n.properties.content_type == 'image' %}
|
|
||||||
i.pi-picture
|
|
||||||
| {% elif n.properties.content_type == 'file' %}
|
|
||||||
i.pi-file-archive
|
|
||||||
| {% else %}
|
|
||||||
i.pi-folder
|
|
||||||
| {% endif %}
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
.activity-stream__list-details
|
|
||||||
a.title(href="{{ n.url }}")
|
|
||||||
| {{ n.name }}
|
|
||||||
|
|
||||||
| {% if n.permissions.world %}
|
|
||||||
.ribbon
|
|
||||||
span free
|
|
||||||
| {% endif %}
|
|
||||||
ul.list-unstyled.d-flex.text-muted
|
|
||||||
| {% if not n.picture %}
|
|
||||||
li.when
|
|
||||||
a(href="{{ n.url }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
|
|
||||||
li.who {{ n.user.full_name }}
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% if n.attached_to %}
|
|
||||||
li.where-parent
|
|
||||||
a(href="{{ n.attached_to.url }}") {{ n.attached_to.name }}
|
|
||||||
| {% endif %}
|
|
||||||
li.where-project
|
|
||||||
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
|
|
||||||
li.what
|
|
||||||
| {% if n.node_type == 'asset' %}
|
|
||||||
| {{ n.properties.content_type | undertitle }}
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% if n.picture %}
|
|
||||||
ul.list-unstyled.d-flex.text-muted.extra
|
|
||||||
li.when
|
|
||||||
a(href="{{ n.url }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
|
|
||||||
li.who {{ n.user.full_name }}
|
|
||||||
| {% endif %}
|
|
||||||
| {% endfor %}
|
|
||||||
|
|
||||||
li.activity-stream__list-item.empty#activity-stream__empty
|
|
||||||
| No items to list.
|
|
||||||
|
|
||||||
|
|
||||||
section.random-asset.py-3
|
|
||||||
h6.title-underline
|
|
||||||
a(href="/search") Explore the Cloud
|
|
||||||
.pb-3.text-muted Random selection of the best assets & tutorials
|
|
||||||
|
|
||||||
ul.random-asset__list.list-unstyled
|
|
||||||
| {% for n in random_featured %}
|
|
||||||
| {% if n.picture and loop.first %}
|
|
||||||
li.random-asset__list-item.project
|
|
||||||
| {% if n.project.picture_square %}
|
|
||||||
a.random-asset__list-thumbnail(
|
|
||||||
href="{{ n.project.url }}")
|
|
||||||
img.image(src="{{ n.project.picture_square.thumbnail('s', api=api) }}")
|
|
||||||
| {% endif %}
|
|
||||||
.random-asset__list-details
|
|
||||||
a.title(href="{{ n.project.url }}") {{ n.project.name }}
|
|
||||||
| {% if n.project.summary %}
|
|
||||||
ul.list-unstyled.d-flex.text-muted
|
|
||||||
li.what
|
|
||||||
a(href="{{ n.project.url }}") {{ n.project.summary }}
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
li.random-asset__list-item.featured
|
|
||||||
| {% if n.permissions.world %}
|
|
||||||
.ribbon
|
|
||||||
span free
|
|
||||||
| {% endif %}
|
|
||||||
a.random-asset__thumbnail(
|
|
||||||
href="{{ n.url }}",
|
|
||||||
class="{{ n.properties.content_type }}")
|
|
||||||
| {% if n.picture %}
|
|
||||||
img(src="{{ n.picture.thumbnail('l', api=api) }}")
|
|
||||||
|
|
||||||
| {% if n.properties.content_type == 'video' %}
|
|
||||||
i.pi-play
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
a.title(href="{{ n.url }}")
|
|
||||||
| {{ n.name }}
|
|
||||||
ul.list-unstyled.d-flex.text-muted
|
|
||||||
li.what
|
|
||||||
a(href="{{ n.url }}")
|
|
||||||
| {% if n.properties.content_type %}{{ n.properties.content_type | undertitle }}{% else %}Folder{% endif %}
|
|
||||||
li.where
|
|
||||||
a(href="{{ n.project.url }}")
|
|
||||||
| {{ n.project.name }}
|
|
||||||
| {% else %}
|
|
||||||
|
|
||||||
li
|
|
||||||
| {% if n.permissions.world %}
|
|
||||||
.ribbon
|
|
||||||
span free
|
|
||||||
| {% endif %}
|
|
||||||
a.random-asset__list-thumbnail(
|
|
||||||
href="{{ n.url }}",
|
|
||||||
class="{{ n.properties.content_type }}")
|
|
||||||
| {% if n.picture %}
|
|
||||||
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
|
|
||||||
| {% else %}
|
|
||||||
| {% if n.properties.content_type == 'video' %}
|
|
||||||
i.pi-film-thick
|
|
||||||
| {% elif n.properties.content_type == 'image' %}
|
|
||||||
i.pi-picture
|
|
||||||
| {% elif n.properties.content_type == 'file' %}
|
|
||||||
i.pi-file-archive
|
|
||||||
| {% else %}
|
|
||||||
i.pi-folder
|
|
||||||
| {% endif %}
|
|
||||||
| {% endif %}
|
|
||||||
.random-asset__list-details
|
|
||||||
a.title(href="{{ n.url }}") {{ n.name }}
|
|
||||||
ul.list-unstyled.d-flex.text-muted
|
|
||||||
li.what
|
|
||||||
a(href="{{ n.url }}")
|
|
||||||
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
|
|
||||||
li.where
|
|
||||||
a(href="{{ n.project.url }}") {{ n.project.name }}
|
|
||||||
|
|
||||||
| {% endif %}
|
|
||||||
| {% endfor %}
|
|
||||||
|
|
||||||
|
|
||||||
section.comments.py-3
|
|
||||||
h6.title-underline Latest Comments
|
|
||||||
|
|
||||||
ul.list-unstyled
|
|
||||||
| {% if latest_comments %}
|
|
||||||
| {% for n in latest_comments %}
|
|
||||||
li(
|
|
||||||
class="{{ n.node_type }}",
|
|
||||||
data-url="{{ n.url }}")
|
|
||||||
|
|
||||||
a.comment-content(href="{{ n.url }}")
|
|
||||||
| {{ n.properties.content | striptags | truncate(200) }}
|
|
||||||
|
|
||||||
ul.list-unstyled.d-flex.text-muted
|
|
||||||
li.who {{ n.user.full_name }}
|
|
||||||
| {% if n.attached_to %}
|
|
||||||
|
|
||||||
li.where-parent
|
|
||||||
a(href="{{ n.attached_to.url }}") {{ n.attached_to.name }}
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
li.when
|
|
||||||
a(href="{{ n.url }}", title="{{ n._created }}")
|
|
||||||
| {{ n._created | pretty_date_time }}
|
|
||||||
| {% endfor %}
|
|
||||||
|
|
||||||
| {% else %}
|
|
||||||
li.activity-stream__list-item.empty#activity-stream__empty
|
|
||||||
| No comments... yet!
|
|
||||||
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
@@ -250,16 +149,10 @@ script.
|
|||||||
$(function () {
|
$(function () {
|
||||||
|
|
||||||
/* cleanup mentions in comments */
|
/* cleanup mentions in comments */
|
||||||
$('.comment-content').each(function(){
|
$('.js-comment-content').each(function(){
|
||||||
$(this).text($(this).text().replace(/\*|\@|\<(.*?)\>/g, ''));
|
$(this).text($(this).text().replace(/\*|\@|\<(.*?)\>/g, ''));
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Click on the whole asset/comment row to go */
|
|
||||||
$('.activity-stream__list li, .comments ul li').click(function(e){
|
|
||||||
window.location.href = $(this).data('url');
|
|
||||||
$(this).addClass('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
hopToTop(); // Display jump to top button
|
hopToTop(); // Display jump to top button
|
||||||
});
|
});
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
include ../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
doctype
|
doctype
|
||||||
html(lang="en")
|
html(lang="en")
|
||||||
head
|
head
|
||||||
@@ -19,7 +21,7 @@ html(lang="en")
|
|||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:title", content="Blender Cloud")
|
meta(property="og:title", content="Blender Cloud")
|
||||||
meta(property="og:url", content="https://cloud.blender.org")
|
meta(property="og:url", content="{{ request.url }}")
|
||||||
meta(property="og:type", content="website")
|
meta(property="og:type", content="website")
|
||||||
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_gleb_locomotive.jpg')}}")
|
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_gleb_locomotive.jpg')}}")
|
||||||
meta(property="og:description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
|
meta(property="og:description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
|
||||||
@@ -29,27 +31,22 @@ html(lang="en")
|
|||||||
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_gleb_locomotive.jpg')}}")
|
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_gleb_locomotive.jpg')}}")
|
||||||
| {% endblock og %}
|
| {% endblock og %}
|
||||||
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery-3.1.0.min.js')}}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/tutti.min.js') }}")
|
||||||
|
script.
|
||||||
|
pillar.utils.initCurrentUser({{ current_user | json | safe }});
|
||||||
|
script(src="{{ url_for('static_pillar', filename='assets/js/timeline.min.js') }}")
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.typeahead-0.11.1.min.js')}}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.typeahead-0.11.1.min.js')}}")
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/js.cookie-2.0.3.min.js')}}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/js.cookie-2.0.3.min.js')}}")
|
||||||
| {% if current_user.is_authenticated %}
|
| {% if current_user.is_authenticated %}
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/clipboard.min.js')}}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/clipboard.min.js')}}")
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% if current_user.has_cap('subscriber') %}
|
|
||||||
| {# Only load if we can comment (for converting markdown as-we-type) #}
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/markdown.min.js') }}")
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/tutti.min.js') }}")
|
|
||||||
|
|
||||||
link(href="{{ url_for('static', filename='assets/img/favicon.png') }}", rel="shortcut icon")
|
link(href="{{ url_for('static', filename='assets/img/favicon.png') }}", rel="shortcut icon")
|
||||||
link(href="{{ url_for('static', filename='assets/img/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192")
|
link(href="{{ url_for('static', filename='assets/img/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192")
|
||||||
|
|
||||||
| {% block head %}{% endblock %}
|
| {% block head %}{% endblock %}
|
||||||
|
|
||||||
| {% block css %}
|
| {% block css %}
|
||||||
link(href="{{ url_for('static_pillar', filename='assets/css/font-pillar.css') }}", rel="stylesheet")
|
|
||||||
| {% if title == 'blog' %}
|
| {% if title == 'blog' %}
|
||||||
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet")
|
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet")
|
||||||
| {% else %}
|
| {% else %}
|
||||||
@@ -58,11 +55,23 @@ html(lang="en")
|
|||||||
| {% endblock css %}
|
| {% endblock css %}
|
||||||
|
|
||||||
| {% if not title %}{% set title="default" %}{% endif %}
|
| {% if not title %}{% set title="default" %}{% endif %}
|
||||||
body(class="{{ title }}")
|
body(class="{{ title }} {{'project' if project and project.url != 'blender-cloud'}} {% block bodyclasses %}{% endblock %}"
|
||||||
|
"{% block bodyattrs %}{% endblock %}"
|
||||||
|
)
|
||||||
| {% with messages = get_flashed_messages(with_categories=True) %}
|
| {% with messages = get_flashed_messages(with_categories=True) %}
|
||||||
| {% if messages %}
|
| {% if messages or (config.UI_ANNOUNCEMENT_NON_SUBSCRIBERS and not current_user.has_cap('subscriber')) %}
|
||||||
|
| {% if config.UI_ANNOUNCEMENT_NON_SUBSCRIBERS %}
|
||||||
|
.alert.d-flex.justify-content-center(
|
||||||
|
role="alert",
|
||||||
|
class="alert-{{ config.UI_ANNOUNCEMENT_NON_SUBSCRIBERS['category'] }}")
|
||||||
|
i.pr-2(class="{{ config.UI_ANNOUNCEMENT_NON_SUBSCRIBERS['icon'] }}")
|
||||||
|
| {{ config.UI_ANNOUNCEMENT_NON_SUBSCRIBERS['message'] | markdown }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
| {% for (category, message) in messages %}
|
| {% for (category, message) in messages %}
|
||||||
.alert(role="alert", class="alert-{{ category }}")
|
.alert.d-flex.justify-content-center(
|
||||||
|
role="alert",
|
||||||
|
class="alert-{{ category }}")
|
||||||
i.alert-icon(class="{{ category }}")
|
i.alert-icon(class="{{ category }}")
|
||||||
span {{ message }}
|
span {{ message }}
|
||||||
button.close(type="button", data-dismiss="alert")
|
button.close(type="button", data-dismiss="alert")
|
||||||
@@ -72,214 +81,45 @@ html(lang="en")
|
|||||||
| {% endwith %}
|
| {% endwith %}
|
||||||
|
|
||||||
nav.navbar.navbar-expand-md.fixed-top.bg-white
|
nav.navbar.navbar-expand-md.fixed-top.bg-white
|
||||||
a.navbar-brand(
|
|
||||||
href="{{ url_for('main.homepage') }}",
|
|
||||||
title="Blender Cloud")
|
|
||||||
span.app-logo
|
|
||||||
i.pi-blender-cloud
|
|
||||||
|
|
||||||
button.navbar-toggler.text-light(
|
|
||||||
data-target=".navbar-collapse",
|
|
||||||
data-toggle="collapse",
|
|
||||||
type="button")
|
|
||||||
span.sr-only Toggle navigation
|
|
||||||
span.navbar-toggler-icon.d-flex.align-items-center
|
|
||||||
i.pi-menu
|
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
|
+nav-secondary(class="collapse navbar-collapse")#navigationLinks
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('main.homepage') }}")
|
||||||
|
i.pi-blender-cloud-logo
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
+nav-secondary()(class="m-auto keep-when-overlay")
|
||||||
|
div.nav-item.quick-search.qs-input#qs-input
|
||||||
|
|
||||||
| {% block navigation_search %}
|
+nav-secondary()(class="ml-auto")
|
||||||
// TODO (pablo) - bring it back asap
|
| {% if node and node.properties and node.properties.category %}
|
||||||
.search-input
|
| {% set category = node.properties.category %}
|
||||||
input#cloud-search(
|
| {% else %}
|
||||||
type="text",
|
| {% set category = title %}
|
||||||
placeholder="Search assets, tutorials...")
|
| {% endif %}
|
||||||
i.search-icon.pi-search
|
li.nav-item.quick-search.cursor-pointer.px-3.pi-search#qs-toggle
|
||||||
| {% endblock navigation_search %}
|
|
||||||
|
|
||||||
.collapse.navbar-collapse
|
| {% block navigation_sections %}
|
||||||
ul.navbar-nav.ml-auto
|
| {% endblock navigation_sections %}
|
||||||
| {% if node and node.properties and node.properties.category %}
|
|
||||||
| {% set category = node.properties.category %}
|
|
||||||
| {% else %}
|
|
||||||
| {% set category = title %}
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% block navigation_sections %}
|
| {% block navigation_user %}
|
||||||
li
|
| {% include 'menus/notifications.html' %}
|
||||||
a.navbar-item(
|
| {% include 'menus/user.html' %}
|
||||||
href="{{ url_for('main.main_blog') }}",
|
| {% endblock navigation_user %}
|
||||||
title="Blender Cloud Blog",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="bottom",
|
|
||||||
class="{% if category == 'blog' %}active{% endif %}")
|
|
||||||
span Blog
|
|
||||||
|
|
||||||
li.dropdown
|
| {% if current_user.is_anonymous %}
|
||||||
a.navbar-item.dropdown-toggle(
|
li
|
||||||
href="",
|
a.btn.btn-sm.btn-primary.px-4.mx-1(
|
||||||
data-toggle="dropdown",
|
href="https://store.blender.org/product/membership/",
|
||||||
title="Libraries")
|
title="Sign up") Sign up
|
||||||
span Libraries
|
| {% endif %}
|
||||||
i.pi-angle-down
|
|
||||||
|
|
||||||
ul.dropdown-menu.p-0
|
.loading-bar
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('projects.view', project_url='hdri') }}",
|
|
||||||
title="HDRI Library",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-globe
|
|
||||||
| HDRI
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('projects.view', project_url='textures') }}",
|
|
||||||
title="Textures Library",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-folder-texture
|
|
||||||
| Textures
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('projects.view', project_url='characters') }}",
|
|
||||||
title="Character Library",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-character
|
|
||||||
| Characters
|
|
||||||
|
|
||||||
|
|
||||||
li(class="dropdown")
|
|
||||||
a.navbar-item.dropdown-toggle(
|
|
||||||
href="{{ url_for('cloud.workshops') }}"
|
|
||||||
data-toggle="dropdown",
|
|
||||||
title="Training")
|
|
||||||
span Training
|
|
||||||
i.pi-angle-down
|
|
||||||
|
|
||||||
ul.dropdown-menu
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('cloud.courses') }}",
|
|
||||||
title="Courses",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-graduation-cap
|
|
||||||
| Courses
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('cloud.workshops') }}",
|
|
||||||
title="Workshops",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-lightbulb
|
|
||||||
| Workshops
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('projects.view', project_url='gallery') }}",
|
|
||||||
title="Curated artwork collection",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-image
|
|
||||||
| Art Gallery
|
|
||||||
|
|
||||||
li(class="dropdown")
|
|
||||||
a.navbar-item.dropdown-toggle(
|
|
||||||
href="{{ url_for('cloud.open_projects') }}",
|
|
||||||
title="Browse all the Open Projects",
|
|
||||||
data-toggle="dropdown",
|
|
||||||
class="{% if category in ['open-projects', 'film'] %}active{% endif %}")
|
|
||||||
span Open Projects
|
|
||||||
i.pi-angle-down
|
|
||||||
|
|
||||||
ul.dropdown-menu
|
|
||||||
li
|
|
||||||
a.navbar-item(href="/p/spring")
|
|
||||||
span.px-2 Spring
|
|
||||||
li
|
|
||||||
a.navbar-item(href="/p/hero")
|
|
||||||
span.px-2 Hero
|
|
||||||
li
|
|
||||||
a.navbar-item(href="/p/dailydweebs")
|
|
||||||
span.px-2 The Daily Dweebs
|
|
||||||
li
|
|
||||||
a.navbar-item(href="/p/agent-327")
|
|
||||||
span.px-2 Agent 327
|
|
||||||
li
|
|
||||||
a.navbar-item(href="/p/caminandes-3")
|
|
||||||
span.px-2 Caminandes: Llamigos
|
|
||||||
|
|
||||||
li.dropdown-divider
|
|
||||||
|
|
||||||
li
|
|
||||||
a.navbar-item(href="{{ url_for('cloud.open_projects') }}")
|
|
||||||
span.pl-2 All Open Projects
|
|
||||||
|
|
||||||
li(class="dropdown")
|
|
||||||
a.navbar-item.dropdown-toggle(
|
|
||||||
href="{{ url_for('cloud.services') }}",
|
|
||||||
title="Blender Cloud Services",
|
|
||||||
data-toggle="dropdown",
|
|
||||||
class="{% if category == 'services' %}active{% endif %}")
|
|
||||||
span Services
|
|
||||||
i.pi-angle-down
|
|
||||||
|
|
||||||
ul.dropdown-menu
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="/attract",
|
|
||||||
title="Production Management",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-attract
|
|
||||||
| Attract
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="/flamenco",
|
|
||||||
title="Render Management",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-flamenco
|
|
||||||
| Flamenco
|
|
||||||
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="/services#blender-cloud-add-on",
|
|
||||||
title="Blender Sync, Texture Browser and more",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-blender
|
|
||||||
| Blender Cloud Add-on
|
|
||||||
|
|
||||||
li.dropdown-divider
|
|
||||||
|
|
||||||
li
|
|
||||||
a.navbar-item(
|
|
||||||
href="{{ url_for('cloud.services') }}",
|
|
||||||
title="All Blender Cloud services",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="left")
|
|
||||||
i.pi-list
|
|
||||||
| All Services
|
|
||||||
|
|
||||||
| {% endblock navigation_sections %}
|
|
||||||
|
|
||||||
| {% block navigation_user %}
|
|
||||||
| {% include 'menus/notifications.html' %}
|
|
||||||
| {% include 'menus/user.html' %}
|
|
||||||
| {% endblock navigation_user %}
|
|
||||||
|
|
||||||
| {% if current_user.is_anonymous %}
|
|
||||||
li.pt-1
|
|
||||||
a.btn.btn-sm.btn-primary.px-3.mx-1(
|
|
||||||
href="https://store.blender.org/product/membership/",
|
|
||||||
title="Sign up") Sign up
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
.page-content
|
.page-content
|
||||||
#search-overlay
|
.quick-search.container-fluid.m-auto.p-5#search-overlay
|
||||||
|
ul.qs-loading.text-center
|
||||||
|
i.h1.pi-spin.spinner
|
||||||
|
h2 Loading
|
||||||
| {% block page_overlay %}
|
| {% block page_overlay %}
|
||||||
#page-overlay
|
#page-overlay
|
||||||
| {% endblock page_overlay %}
|
| {% endblock page_overlay %}
|
||||||
@@ -287,114 +127,7 @@ html(lang="en")
|
|||||||
| {% block body %}{% endblock %}
|
| {% block body %}{% endblock %}
|
||||||
|
|
||||||
| {% block footer_container %}
|
| {% block footer_container %}
|
||||||
.footer-wrapper
|
| {% include '_footer.html' %}
|
||||||
| {% block footer_navigation %}
|
|
||||||
.footer-navigation
|
|
||||||
.container
|
|
||||||
.row
|
|
||||||
.col-md-4.col-xs-6
|
|
||||||
h4
|
|
||||||
a(href="{{ url_for('main.homepage') }}")
|
|
||||||
i.pi-blender-cloud-logo
|
|
||||||
|
|
||||||
p.pl-2.
|
|
||||||
Blender Cloud is the creative hub for your projects,
|
|
||||||
powered by Free and Open Source Software.
|
|
||||||
|
|
||||||
h5.d-flex
|
|
||||||
a.px-2(href="https://twitter.com/Blender_Cloud",
|
|
||||||
title="Follow us on Twitter")
|
|
||||||
i.pi-social-youtube
|
|
||||||
|
|
||||||
a.px-2(href="https://twitter.com/Blender_Cloud",
|
|
||||||
title="Follow us on Twitter")
|
|
||||||
i.pi-social-twitter
|
|
||||||
|
|
||||||
a.px-2(href="https://www.facebook.com/BlenderCloudOfficial/",
|
|
||||||
title="Follow us on Facebook")
|
|
||||||
i.pi-social-facebook
|
|
||||||
|
|
||||||
.col-md-2.col-xs-6
|
|
||||||
h7.font-weight-bold
|
|
||||||
| TRAINING
|
|
||||||
|
|
||||||
ul.list-unstyled
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.courses') }}")
|
|
||||||
| Courses
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.workshops') }}")
|
|
||||||
| Workshops
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('projects.view', project_url='gallery') }}")
|
|
||||||
| Art Gallery
|
|
||||||
|
|
||||||
.col-md-2.col-xs-6
|
|
||||||
h7.font-weight-bold
|
|
||||||
| LIBRARIES
|
|
||||||
|
|
||||||
ul.list-unstyled
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('main.main_blog') }}",
|
|
||||||
title="Blender Cloud Blog")
|
|
||||||
| HDRIs
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.services') }}",
|
|
||||||
title="Blender Cloud Services")
|
|
||||||
| Textures
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.about') }}",
|
|
||||||
title="About Blender Cloud")
|
|
||||||
| Characters
|
|
||||||
|
|
||||||
.col-md-2.col-xs-6
|
|
||||||
h7.font-weight-bold
|
|
||||||
a(href="{{ url_for('cloud.services') }}")
|
|
||||||
| SERVICES
|
|
||||||
|
|
||||||
ul.list-unstyled
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('main.main_blog') }}",
|
|
||||||
title="Blender Cloud Blog")
|
|
||||||
| Add-on
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('main.main_blog') }}",
|
|
||||||
title="Blender Cloud Blog")
|
|
||||||
| Blender Sync
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.services') }}",
|
|
||||||
title="Blender Cloud Services")
|
|
||||||
| Attract
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.about') }}",
|
|
||||||
title="About Blender Cloud")
|
|
||||||
| Flamenco
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.about') }}",
|
|
||||||
title="About Blender Cloud")
|
|
||||||
| Image Sharing
|
|
||||||
|
|
||||||
.col-md-2.col-xs-6
|
|
||||||
h7.font-weight-bold
|
|
||||||
| BLENDER
|
|
||||||
ul.list-unstyled
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('main.main_blog') }}",
|
|
||||||
title="Blender Cloud Blog")
|
|
||||||
| blender.org
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.terms_and_conditions') }}",
|
|
||||||
title="Terms and Conditions")
|
|
||||||
| Terms and Conditions
|
|
||||||
li
|
|
||||||
a(href="{{ url_for('cloud.privacy') }}",
|
|
||||||
title="Privacy")
|
|
||||||
| Privacy
|
|
||||||
| {% endblock footer_navigation %}
|
|
||||||
|
|
||||||
#hop(title="Be awesome in space")
|
|
||||||
i.pi-angle-up
|
|
||||||
|
|
||||||
| {% endblock footer_container %}
|
| {% endblock footer_container %}
|
||||||
|
|
||||||
#notification-pop(data-url="", data-read-toggle="")
|
#notification-pop(data-url="", data-read-toggle="")
|
||||||
@@ -406,9 +139,6 @@ html(lang="en")
|
|||||||
.nc-text
|
.nc-text
|
||||||
span.nc-date
|
span.nc-date
|
||||||
a(href="")
|
a(href="")
|
||||||
|
|
||||||
script(src="{{ url_for('static_cloud', filename='assets/js/bootstrap.min.js') }}")
|
|
||||||
|
|
||||||
| {% if current_user.is_authenticated %}
|
| {% if current_user.is_authenticated %}
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.typewatch-3.0.0.min.js') }}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.typewatch-3.0.0.min.js') }}")
|
||||||
script.
|
script.
|
||||||
@@ -433,13 +163,43 @@ html(lang="en")
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable all tooltips.
|
||||||
if (typeof $().tooltip != 'undefined'){
|
if (typeof $().tooltip != 'undefined'){
|
||||||
$('[data-toggle="tooltip"]').tooltip({'delay' : {'show': 0, 'hide': 0}});
|
$('[data-toggle="tooltip"]').tooltip({'delay' : {'show': 0, 'hide': 0}});
|
||||||
}
|
}
|
||||||
if(typeof($.fn.popover) != 'undefined'){
|
|
||||||
$('[data-toggle="popover"]').popover();
|
// Enable Quick Search
|
||||||
|
let searches = {
|
||||||
|
{% if project and not project.is_private %}
|
||||||
|
project: {
|
||||||
|
name: 'Project',
|
||||||
|
uiUrl: '{{ url_for("projects.search", project_url=project.url)}}',
|
||||||
|
apiUrl: '/api/newsearch/multisearch',
|
||||||
|
searchParams: [
|
||||||
|
{name: 'Assets', params: {project: '{{ project._id }}', node_type: 'asset'}},
|
||||||
|
{name: 'Blog', params: {project: '{{ project._id }}', node_type: 'post'}},
|
||||||
|
{name: 'Groups', params: {project: '{{ project._id }}', node_type: 'group'}},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
cloud: {
|
||||||
|
name: 'Cloud',
|
||||||
|
uiUrl: '/search',
|
||||||
|
apiUrl: '/api/newsearch/multisearch',
|
||||||
|
searchParams: [
|
||||||
|
{name: 'Assets', params: {node_type: 'asset'}},
|
||||||
|
{name: 'Blog', params: {node_type: 'post'}},
|
||||||
|
{name: 'Groups', params: {node_type: 'group'}},
|
||||||
|
]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#qs-toggle').quickSearch({
|
||||||
|
resultTarget: '#search-overlay',
|
||||||
|
inputTarget: '#qs-input',
|
||||||
|
searches: searches,
|
||||||
|
});
|
||||||
|
|
||||||
| {% block footer_scripts_pre %}{% endblock %}
|
| {% block footer_scripts_pre %}{% endblock %}
|
||||||
| {% block footer_scripts %}{% endblock %}
|
| {% block footer_scripts %}{% endblock %}
|
||||||
|
|
||||||
|
103
src/templates/learn.pug
Normal file
103
src/templates/learn.pug
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
| {% extends 'layout.html' %}
|
||||||
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
include mixins/components
|
||||||
|
|
||||||
|
| {% set title = 'learn' %}
|
||||||
|
|
||||||
|
| {% set page_title = 'Learn' %}
|
||||||
|
| {% set page_description = 'Production quality training by Blender professionals.' %}
|
||||||
|
| {% set page_header_image = url_for('static', filename='assets/img/features/training_minecraft_animation.jpg', _external=true) %}
|
||||||
|
|
||||||
|
| {% block page_title %}{{ page_title }}{% endblock %}
|
||||||
|
|
||||||
|
| {% block og %}
|
||||||
|
| {{ opengraph(page_title, page_description, page_header_image, request.url) }}
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_homepage(title) }}
|
||||||
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
|
| {% block body %}
|
||||||
|
.container.py-4
|
||||||
|
+category_list_header('{{ page_title }}', '{{ page_description }}')
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'COURSES',
|
||||||
|
'In-depth training for mastering every corner in Blender.',
|
||||||
|
"{{ url_for('cloud.courses') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/training_animation_fundamentals_01.jpg')}}",
|
||||||
|
'/p/animation-fundamentals')
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2.list-first-new
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Procedural Shading': '/p/procedural-shading',
|
||||||
|
'Weight Painting':'/p/weight-painting',
|
||||||
|
'Animation Fundamentals':'/p/animation-fundamentals',
|
||||||
|
'Stylized Character Workflow':'/p/stylized-character-workflow',
|
||||||
|
'Scripting for Artists':'/p/scripting-for-artists'
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('cloud.courses') }}")
|
||||||
|
| See all Courses
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
hr.mb-4
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'WORKSHOPS',
|
||||||
|
'Enter the artist workshop and learn by example.',
|
||||||
|
"{{ url_for('cloud.workshops') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/training_anglerfish_01.jpg')}}",
|
||||||
|
'/p/anglerfish')
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Anglerfish':'/p/anglerfish',
|
||||||
|
'Speed Sculpting':'/p/speed-sculpting',
|
||||||
|
'Minecraft Animation':'/p/minecraft-animation-workshop',
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('cloud.workshops') }}")
|
||||||
|
| See all Workshops
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
hr.mb-4
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'PRODUCTION LESSONS',
|
||||||
|
'Tips and tricks by the Blender Open Movies crew.',
|
||||||
|
"{{ url_for('cloud.production') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/open_movies_spring_03.jpg')}}")
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Walk-throughs':'/production#walk-through',
|
||||||
|
'Animation Tips':'/production#animation',
|
||||||
|
'Character Pipeline':'/production#character-pipeline'
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('cloud.production') }}")
|
||||||
|
| See all Production Lessons
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
| {% endblock body %}
|
128
src/templates/libraries.pug
Normal file
128
src/templates/libraries.pug
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
| {% extends 'layout.html' %}
|
||||||
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
include mixins/components
|
||||||
|
|
||||||
|
| {% set title = 'libraries' %}
|
||||||
|
|
||||||
|
| {% set page_title = 'Libraries' %}
|
||||||
|
| {% set page_description = 'Download 1000s of files and assets.' %}
|
||||||
|
| {% set page_header_image = url_for('static', filename='assets/img/features/characters_01.jpg', _external=true) %}
|
||||||
|
|
||||||
|
| {% block page_title %}{{ page_title }}{% endblock %}
|
||||||
|
|
||||||
|
| {% block og %}
|
||||||
|
| {{ opengraph(page_title, page_description, page_header_image, request.url) }}
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_homepage(title) }}
|
||||||
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
|
| {% block body %}
|
||||||
|
.container.py-4
|
||||||
|
+category_list_header('LIBRARIES', null)
|
||||||
|
.lead
|
||||||
|
| Browse Textures and HDRI within Blender with our #[a(href="/services#blender-cloud-add-on") Blender Cloud add-on!]
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'HDR IMAGES',
|
||||||
|
'Up to 16K and 24 EVs HDRI to enhance your renders.',
|
||||||
|
"{{ url_for('projects.view', project_url='hdri') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/hdri_02.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Myanmar' : '/p/hdri/58d824e588ac8f2107b314e1',
|
||||||
|
'Indoor' : '/p/hdri/57976801c379cf39de54cc5d',
|
||||||
|
'Skies' : '/p/hdri/585ab5521f47427f2f7b3604'
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('projects.view', project_url='hdri') }}")
|
||||||
|
| See all HDRIs
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
hr.mb-4
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'TEXTURES',
|
||||||
|
'More than 1500 public-domain textures.',
|
||||||
|
"{{ url_for('projects.view', project_url='textures') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/textures_02.jpg')}}")
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Metal' : '/p/textures/5677e2f4c379cf0007b31fe1',
|
||||||
|
'Wood' : '/p/textures/567800d4c379cf211051a439',
|
||||||
|
'Bricks': '/p/textures/5672df9fc379cf0007b3198b'
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('projects.view', project_url='textures') }}")
|
||||||
|
| See all Textures
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
hr.mb-4
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'ART GALLERY',
|
||||||
|
'Dive into the most interesting .blend files from the community.',
|
||||||
|
"{{ url_for('projects.view', project_url='gallery') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/gallery_01.jpg')}}",
|
||||||
|
'/p/gallery/564a15bec379cf089a7ad514')
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Grease Pencil Files' : '/p/gallery/5b642e25bf419c1042056fc6',
|
||||||
|
'Gleb Alexandrov' : '/p/gallery/57907fb8c379cf33d47a098d',
|
||||||
|
'Midge "Mantissa" Sinnaeve' : '/p/gallery/5800d64ee5e20f084523a059'
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('projects.view', project_url='gallery') }}")
|
||||||
|
| Visit the Gallery
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
hr.mb-4
|
||||||
|
|
||||||
|
+category_list_item(
|
||||||
|
'CHARACTERS',
|
||||||
|
'Production quality characters ready to animate and render.',
|
||||||
|
"{{ url_for('projects.view', project_url='characters') }}",
|
||||||
|
"{{ url_for('static', filename='assets/img/features/characters_01.jpg')}}")
|
||||||
|
|
||||||
|
ul.list-unstyled.mt-3.mb-0.column-count-2
|
||||||
|
-
|
||||||
|
var projects = {
|
||||||
|
'Vincent' : '/p/characters/5718a967c379cf04929a4247',
|
||||||
|
'Big Buck Bunny' : '/p/characters/56cb2785c379cf0079716c19',
|
||||||
|
'Min (Glass Half)' : '/p/characters/5672d39bc379cf0007b31911'
|
||||||
|
};
|
||||||
|
|
||||||
|
each url, title in projects
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href=url)
|
||||||
|
span=title
|
||||||
|
li
|
||||||
|
a.d-block.py-1.text-primary(href="{{ url_for('projects.view', project_url='characters') }}")
|
||||||
|
| See all Characters
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
|
| {% endblock body %}
|
@@ -9,11 +9,8 @@
|
|||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% block menu_avatar %}
|
| {% block menu_avatar %}
|
||||||
a.navbar-item.dropdown-toggle(href="#", data-toggle="dropdown", title="{{ current_user.email }}")
|
a.navbar-item.dropdown-toggle(href="{{ url_for('settings.profile') }}", data-toggle="dropdown")
|
||||||
img.gravatar(
|
current-user-avatar
|
||||||
src="{{ current_user.gravatar }}",
|
|
||||||
class="{{ subscription }}",
|
|
||||||
alt="Avatar")
|
|
||||||
.special(class="{{ subscription }}")
|
.special(class="{{ subscription }}")
|
||||||
| {% if subscription == 'subscriber' %}
|
| {% if subscription == 'subscriber' %}
|
||||||
i.pi-check
|
i.pi-check
|
||||||
@@ -22,32 +19,34 @@ a.navbar-item.dropdown-toggle(href="#", data-toggle="dropdown", title="{{ curren
|
|||||||
| {% else %}
|
| {% else %}
|
||||||
i.pi-attention
|
i.pi-attention
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
script.
|
||||||
|
new Vue({el: 'current-user-avatar'})
|
||||||
| {% endblock menu_avatar %}
|
| {% endblock menu_avatar %}
|
||||||
|
|
||||||
|
|
||||||
| {% block menu_list %}
|
| {% block menu_list %}
|
||||||
li.subscription-status(class="{{ subscription }}")
|
li.subscription-status(class="{{ subscription }}")
|
||||||
| {% if subscription == 'subscriber' %}
|
| {% if subscription == 'subscriber' %}
|
||||||
a.navbar-item(
|
a.navbar-item.pt-2.pl-2.pr-3(
|
||||||
href="{{url_for('settings.billing')}}"
|
href="{{ url_for('settings.billing') }}"
|
||||||
title="View subscription info")
|
title="View subscription info")
|
||||||
i.pi-grin
|
i.pi-grin
|
||||||
span Your subscription is active!
|
span.subitem Your subscription is active!
|
||||||
| {% elif subscription == 'demo' %}
|
| {% elif subscription == 'demo' %}
|
||||||
a.navbar-item(
|
a.navbar-item.pt-2.pl-2.pr-3(
|
||||||
href="{{url_for('settings.billing')}}"
|
href="{{url_for('settings.billing')}}"
|
||||||
title="View subscription info")
|
title="View subscription info")
|
||||||
i.pi-heart-filled
|
i.pi-heart-filled
|
||||||
span You have a free account.
|
span.subitem You have a free account.
|
||||||
| {% elif current_user.has_cap('can-renew-subscription') %}
|
| {% elif current_user.has_cap('can-renew-subscription') %}
|
||||||
a.navbar-item(target='_blank', href="/renew", title="Renew subscription")
|
a.navbar-item.pt-2.pl-2.pr-3(target='_blank', href="/renew", title="Renew subscription")
|
||||||
i.pi-heart
|
i.pi-heart
|
||||||
span.info Your subscription is not active.
|
span.info Your subscription is not active.
|
||||||
span.renew Click here to renew.
|
span.renew Click here to renew.
|
||||||
| {% else %}
|
| {% else %}
|
||||||
a.navbar-item(
|
a.navbar-item(
|
||||||
href="https://store.blender.org/product/membership/"
|
href="https://store.blender.org/product/membership/"
|
||||||
title="Renew subscription")
|
title="Renew subscription")
|
||||||
i.pi-unhappy
|
i.pi-unhappy
|
||||||
span.info Your subscription is not active.
|
span.info Your subscription is not active.
|
||||||
span.renew Click here to renew.
|
span.renew Click here to renew.
|
||||||
@@ -56,9 +55,9 @@ li.subscription-status(class="{{ subscription }}")
|
|||||||
| {{ super() }}
|
| {{ super() }}
|
||||||
|
|
||||||
li
|
li
|
||||||
a.navbar-item(
|
a.navbar-item.px-2(
|
||||||
href="{{ url_for('settings.billing') }}"
|
href="{{ url_for('settings.billing') }}"
|
||||||
title="Billing")
|
title="Billing")
|
||||||
i.pi-credit-card
|
i.pi-credit-card
|
||||||
| Subscription
|
| Subscription
|
||||||
| {% endblock menu_list %}
|
| {% endblock menu_list %}
|
||||||
|
@@ -1,69 +1,33 @@
|
|||||||
// {#
|
//- Category listing (Learn, Libraries, etc).
|
||||||
// Header of landing pages. title or text can be skipped:
|
//- Header
|
||||||
// +jumbotron("{{ page_title }}", null, "{{ page_header_image }}")
|
mixin category_list_header(title, text)
|
||||||
// Any extra attributes added (in a separate group) will be passed as is:
|
.row.pt-2.pb-3.mb-4.border-bottom&attributes(attributes)
|
||||||
// +jumbotron("{{ page_title }}", null, "{{ page_header_image }}")(data-node-id='{{ node._id }}')
|
.col-md-9
|
||||||
// #}
|
h1.py-2.font-weight-bold
|
||||||
mixin jumbotron(title, text, image, url)
|
|
||||||
if url
|
|
||||||
a.jumbotron.jumbotron-overlay.text-white(
|
|
||||||
style='background-image: url(' + image + ');',
|
|
||||||
href=url)&attributes(attributes)
|
|
||||||
.container
|
|
||||||
.row
|
|
||||||
.col-md-9
|
|
||||||
if title
|
|
||||||
.display-4.text-uppercase.font-weight-bold
|
|
||||||
=title
|
|
||||||
if text
|
|
||||||
.lead
|
|
||||||
=text
|
|
||||||
else
|
|
||||||
.jumbotron.jumbotron-overlay.text-white(style='background-image: url(' + image + ');')&attributes(attributes)
|
|
||||||
.container
|
|
||||||
.row
|
|
||||||
.col-md-9
|
|
||||||
if title
|
|
||||||
.display-4.text-uppercase.font-weight-bold
|
|
||||||
=title
|
|
||||||
if text
|
|
||||||
.lead
|
|
||||||
=text
|
|
||||||
|
|
||||||
// {# Secondary navigation.
|
|
||||||
// e.g. Workshops, Courses. #}
|
|
||||||
mixin nav-secondary(title)
|
|
||||||
ul.nav.nav-secondary&attributes(attributes)
|
|
||||||
if title
|
|
||||||
li.font-weight-bold.px-2
|
|
||||||
=title
|
=title
|
||||||
|
|
||||||
if block
|
.lead
|
||||||
block
|
=text
|
||||||
else
|
|
||||||
p No items defined.
|
|
||||||
|
|
||||||
mixin nav-secondary-link()
|
if block
|
||||||
li.nav-item
|
block
|
||||||
a.nav-link&attributes(attributes)
|
|
||||||
block
|
|
||||||
|
|
||||||
// {# Takes as argument the number of columns to use in this deck. 1-6 #}
|
//- List Item
|
||||||
mixin card-deck(columns)
|
mixin category_list_item(title, text, url, image, image_link_url)
|
||||||
.card-deck.card-padless(class='card-' + columns + '-columns')
|
.row.pb-2.my-2&attributes(attributes)
|
||||||
if block
|
.col-md-8
|
||||||
block
|
a(href=url, title=title)
|
||||||
else
|
h3.font-weight-bold.text-muted
|
||||||
p No cards defined.
|
=title
|
||||||
|
.lead
|
||||||
|
=text
|
||||||
|
|
||||||
// {#
|
if block
|
||||||
// Passes all attributes to the card.
|
block
|
||||||
// You can do fun stuff in a loop even like:
|
|
||||||
// +card(data-url="{{ url_for('projects.view', project_url=project.url) }}", tabindex='{{ loop.index }}')
|
.col-md-4
|
||||||
// #}
|
if image_link_url
|
||||||
mixin card()
|
- var url = image_link_url
|
||||||
.card.card-fade.cursor-pointer.mb-4.js-project-go&attributes(attributes)
|
|
||||||
if block
|
a(href=url, title=title)
|
||||||
block
|
img.img-fluid.rounded(alt=title, src=image)
|
||||||
else
|
|
||||||
p No card content defined.
|
|
||||||
|
@@ -1,21 +1,25 @@
|
|||||||
include ../../../mixins/components
|
include ../../../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% import 'projects/_macros.html' as projectmacros %}
|
|
||||||
| {% macro render_blog_post(node, project=None, pages=None) %}
|
| {% macro render_blog_post(node, project=None, pages=None) %}
|
||||||
|
|
||||||
.expand-image-links.imgs-fluid
|
.expand-image-links.imgs-fluid
|
||||||
| {% if node.picture %}
|
| {% if node.picture %}
|
||||||
+jumbotron(
|
+jumbotron(
|
||||||
"{{ node.name }}",
|
"{{ node.name }}",
|
||||||
"{{ node._created | pretty_date }}",
|
"{{ node._created | pretty_date }}{% if node.user.full_name %} · {{ node.user.full_name }}{% endif %}{% if node.properties.status != 'published' %} · {{ node.properties.status }}{% endif %}",
|
||||||
"{{ node.picture.thumbnail('h', api=api) }}",
|
"{{ node.picture.thumbnail('h', api=api) }}",
|
||||||
"{{ node.url }}")(class="row")
|
"{{ node.url }}")(
|
||||||
|
class="jumbotron-overlay")
|
||||||
| {% else %}
|
| {% else %}
|
||||||
.pt-3.text-center.text-muted
|
.pt-5.text-center.text-muted
|
||||||
h2
|
h2.pb-2
|
||||||
a.text-muted(href="{{ node.url }}")
|
a.text-muted(href="{{ node.url }}")
|
||||||
| {{ node.name }}
|
| {{ node.name }}
|
||||||
ul.d-flex.list-unstyled.justify-content-center
|
ul.d-flex.list-unstyled.justify-content-center
|
||||||
|
| {% if node.properties.status != 'published' %}
|
||||||
|
li.mr-3(title="Status {{ node.properties.status }}")
|
||||||
|
span.badge.badge-danger {{ node.properties.status | undertitle }}
|
||||||
|
| {% endif %}
|
||||||
| {% if node.project.name %}
|
| {% if node.project.name %}
|
||||||
li.pr-2 {{ node.project.name }}
|
li.pr-2 {{ node.project.name }}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
@@ -30,87 +34,89 @@ include ../../../mixins/components
|
|||||||
li
|
li
|
||||||
a.px-2(href="{{ node.url }}#comments")
|
a.px-2(href="{{ node.url }}#comments")
|
||||||
| Leave a comment
|
| Leave a comment
|
||||||
|
|
||||||
| {% if node.has_method('PUT') %}
|
|
||||||
li
|
|
||||||
a.px-2(href="{{url_for('nodes.edit', node_id=node._id)}}")
|
|
||||||
i.pi-edit
|
|
||||||
| Edit Post
|
|
||||||
| {% endif %}
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% if project and project._id != config.MAIN_PROJECT_ID %}
|
.node-details-description.mx-auto.py-5
|
||||||
| {{ projectmacros.render_secondary_navigation(project, pages=pages) }}
|
| {{ node.properties | markdowned('content') }}
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
.row
|
hr.my-4
|
||||||
.col-md-9.mx-auto
|
|
||||||
|
|
||||||
.item-content.pt-4
|
comments-tree#comments-embed.justify-content-center.mx-auto(
|
||||||
| {{ node.properties | markdowned('content') }}
|
parent-id="{{ node._id }}"
|
||||||
|
read-only=false
|
||||||
hr.my-4
|
)
|
||||||
| {% endmacro %}
|
| {% endmacro %}
|
||||||
|
|
||||||
//- ******************************************************* -//
|
//- ******************************************************* -//
|
||||||
| {% macro render_blog_list_item(node) %}
|
| {% macro render_blog_list_item(node) %}
|
||||||
.row.position-relative.py-2
|
a.card.asset.card-image-fade(
|
||||||
.col-md-1
|
href="{{ node.url }}")
|
||||||
|
.card-thumbnail
|
||||||
| {% if node.picture %}
|
| {% if node.picture %}
|
||||||
a.imgs-fluid(href="{{ node.url }}")
|
img.card-img-top(src="{{ node.picture.thumbnail('m', api=api) }}", alt="{{ node.name }}")
|
||||||
img(src="{{ node.picture.thumbnail('s', api=api) }}")
|
|
||||||
| {% else %}
|
| {% else %}
|
||||||
.bg-primary.rounded.h-100
|
.card-img-top
|
||||||
a.d-flex.align-items-center.justify-content-center.h-100.text-white(href="{{ node.url }}")
|
i.pi-document-text
|
||||||
i.pi-document-text
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
.col-md-11
|
.card-body.py-2.d-flex.flex-column
|
||||||
h5
|
.card-title.mb-1.font-weight-bold
|
||||||
a.text-muted(href="{{ node.url }}") {{node.name}}
|
| {{ node.name }}
|
||||||
|
|
||||||
.text-muted.
|
ul.card-text.list-unstyled.d-flex.text-black-50.mt-auto
|
||||||
#[span(title="{{node._created}}") {{node._created | pretty_date }}]
|
li.pr-2 {{ node.user.full_name }}
|
||||||
{% if node._created != node._updated %}
|
li {{ node._created | pretty_date }}
|
||||||
#[span(title="{{node._updated}}") (updated {{node._updated | pretty_date }})]
|
| {% if node.properties.status != 'published' %}
|
||||||
{% endif %}
|
li.text-info.font-weight-bold {{ node.properties.status}}
|
||||||
{% if node.properties.category %} · {{node.properties.category}}{% endif %}
|
| {% endif %}
|
||||||
· {{node.user.full_name}}
|
|
||||||
{% if node.properties.status != 'published' %} · {{ node.properties.status}} {% endif %}
|
|
||||||
|
|
||||||
| {% endmacro %}
|
| {% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
//- ******************************************************* -//
|
//- ******************************************************* -//
|
||||||
| {% macro render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=None) %}
|
| {% macro render_blog_index(current_post, project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=None) %}
|
||||||
| {% if can_create_blog_posts %}
|
|
||||||
+nav-secondary
|
| {% if can_create_blog_posts or (current_post and current_post.has_method('PUT')) %}
|
||||||
|
+nav-secondary(class="bg-light border-bottom")
|
||||||
|
| {% if can_create_blog_posts %}
|
||||||
+nav-secondary-link(href="{{url_for('nodes.posts_create', project_id=project._id)}}")
|
+nav-secondary-link(href="{{url_for('nodes.posts_create', project_id=project._id)}}")
|
||||||
span.text-success
|
i.pi-plus.pr-2
|
||||||
i.pi-plus
|
span Create New Blog Post
|
||||||
| Create New Blog Post
|
| {% endif %}
|
||||||
|
| {% if (current_post and current_post.has_method('PUT')) %}
|
||||||
|
+nav-secondary-link(href="{{url_for('nodes.edit', node_id=current_post._id)}}")
|
||||||
|
i.pi-edit.pr-2
|
||||||
|
span Edit Post
|
||||||
|
| {% endif %}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% if posts %}
|
| {% if posts %}
|
||||||
| {{ render_blog_post(posts[0], project=project, pages=pages) }}
|
| {{ render_blog_post(current_post, project=project, pages=pages) }}
|
||||||
|
|
||||||
.container
|
.container
|
||||||
.row
|
.pt-4.text-center
|
||||||
.col-md-9.mx-auto
|
h5
|
||||||
| {% for node in posts[1:] %}
|
|
||||||
| {% if loop.first %}
|
|
||||||
h5.text-muted.text-center Blasts from the past
|
|
||||||
| {% endif %}
|
|
||||||
| {{ render_blog_list_item(node) }}
|
|
||||||
| {% endfor %}
|
|
||||||
|
|
||||||
| {% if more_posts_available %}
|
| {% if more_posts_available %}
|
||||||
.blog-archive-navigation
|
a.text-muted.py-3.d-block(href="{{ project.blog_archive_url }}")
|
||||||
a(href="{{ project.blog_archive_url }}")
|
| More from {{ project.name }} blog
|
||||||
| {{posts_meta.total - posts|length}} more blog posts over here
|
| {% else %}
|
||||||
i.pi-angle-right
|
| More from {{ project.name }} blog
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
|
+card-deck(class="px-2")
|
||||||
|
| {% for node in posts %}
|
||||||
|
| {# Skip listing the current post #}
|
||||||
|
| {% if node._id != current_post._id %}
|
||||||
|
| {{ render_blog_list_item(node) }}
|
||||||
|
| {% endif %}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
|
| {% if more_posts_available %}
|
||||||
|
a.d-block.pb-4.text-center(href="{{ project.blog_archive_url }}")
|
||||||
|
| {{posts_meta.total - posts|length}} more blog posts over here
|
||||||
|
i.pi-angle-right
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
| {% else %}
|
| {% else %}
|
||||||
|
|
||||||
.text-center
|
.text-center
|
||||||
@@ -122,29 +128,29 @@ include ../../../mixins/components
|
|||||||
|
|
||||||
//- Macro for rendering the navigation buttons for prev/next pages -//
|
//- Macro for rendering the navigation buttons for prev/next pages -//
|
||||||
| {% macro render_archive_pagination(project) %}
|
| {% macro render_archive_pagination(project) %}
|
||||||
.blog-archive-navigation
|
.d-flex.justify-content-center
|
||||||
| {% if project.blog_archive_prev %}
|
| {% if project.blog_archive_prev %}
|
||||||
a.archive-nav-button(
|
a.px-5.py-3(
|
||||||
href="{{ project.blog_archive_prev }}", rel="prev")
|
href="{{ project.blog_archive_prev }}", rel="prev")
|
||||||
i.pi-angle-left
|
i.pi-angle-left
|
||||||
| Previous page
|
| Previous page
|
||||||
| {% else %}
|
| {% else %}
|
||||||
span.archive-nav-button
|
span.px-5.py-3.text-black-50
|
||||||
i.pi-angle-left
|
i.pi-angle-left
|
||||||
| Previous page
|
| Previous page
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
a.archive-nav-button(
|
a.px-5.py-3(
|
||||||
href="{{ url_for('main.project_blog', project_url=project.url) }}")
|
href="{{ url_for('main.project_blog', project_url=project.url) }}")
|
||||||
| Blog Index
|
| Blog Index
|
||||||
|
|
||||||
| {% if project.blog_archive_next %}
|
| {% if project.blog_archive_next %}
|
||||||
a.archive-nav-button(
|
a.px-5.py-3(
|
||||||
href="{{ project.blog_archive_next }}", rel="next")
|
href="{{ project.blog_archive_next }}", rel="next")
|
||||||
| Next page
|
| Next page
|
||||||
i.pi-angle-right
|
i.pi-angle-right
|
||||||
| {% else %}
|
| {% else %}
|
||||||
span.archive-nav-button
|
span.px-5.py-3.text-black-50
|
||||||
| Next page
|
| Next page
|
||||||
i.pi-angle-right
|
i.pi-angle-right
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
@@ -155,9 +161,10 @@ include ../../../mixins/components
|
|||||||
|
|
||||||
| {{ render_archive_pagination(project) }}
|
| {{ render_archive_pagination(project) }}
|
||||||
|
|
||||||
| {% for node in posts %}
|
+card-deck(class="px-2")
|
||||||
| {{ render_blog_list_item(node) }}
|
| {% for node in posts %}
|
||||||
| {% endfor %}
|
| {{ render_blog_list_item(node) }}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
| {{ render_archive_pagination(project) }}
|
| {{ render_archive_pagination(project) }}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
| {% from '_macros/_navigation.html' import navigation_home_project %}
|
||||||
include ../mixins/components
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% set title = 'organizations' %}
|
| {% set title = 'organizations' %}
|
||||||
| {% block page_title %}Organizations{% endblock %}
|
| {% block page_title %}Organizations{% endblock %}
|
||||||
@@ -16,9 +16,8 @@ meta(property="og:image", content="{{ url_for('static', filename='assets/img/bac
|
|||||||
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/cloud_services_oti.jpg')}}")
|
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/cloud_services_oti.jpg')}}")
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
| {{ navigation_tabs(title) }}
|
| {{ navigation_home_project(title) }}
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
@@ -27,8 +26,8 @@ meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/ba
|
|||||||
+nav-secondary-link(
|
+nav-secondary-link(
|
||||||
class="create",
|
class="create",
|
||||||
onclick='createNewOrganization(this)')
|
onclick='createNewOrganization(this)')
|
||||||
|
i.pi-plus.text-success
|
||||||
span.text-success
|
span.text-success
|
||||||
i.pi-plus
|
|
||||||
| Create Organization
|
| Create Organization
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/ba
|
|||||||
ul.meta
|
ul.meta
|
||||||
li(title="Members")
|
li(title="Members")
|
||||||
| {{ organization.members|hide_none|count }} Member{{ organization.members|hide_none|count|pluralize }}
|
| {{ organization.members|hide_none|count }} Member{{ organization.members|hide_none|count|pluralize }}
|
||||||
| {% if (organization.unknown_members|count) != 0 %}
|
| {% if (organization.unknown_members|hide_none|count) != 0 %}
|
||||||
| ({{ organization.unknown_members|hide_none|count }} pending)
|
| ({{ organization.unknown_members|hide_none|count }} pending)
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
li(title="Seats")
|
li(title="Seats")
|
||||||
|
@@ -15,7 +15,7 @@ style.
|
|||||||
This Application collects some Personal Data from its Users.
|
This Application collects some Personal Data from its Users.
|
||||||
h3 Data Controller and Owner
|
h3 Data Controller and Owner
|
||||||
p.
|
p.
|
||||||
Blender Institute B.V. - Entrepotdok 57A - 1018 AD Amsterdam - the Netherlands,
|
Blender Institute B.V. - Buikslotermeerplein 161 - 1025 ET Amsterdam - the Netherlands,
|
||||||
institute@blender.org
|
institute@blender.org
|
||||||
p.
|
p.
|
||||||
Blender Institute has been authorised by Stichting Blender Foundation to conduct these
|
Blender Institute has been authorised by Stichting Blender Foundation to conduct these
|
||||||
@@ -121,7 +121,8 @@ style.
|
|||||||
Data Controller to erase the Personal Data. Unless stated otherwise, the then-current privacy
|
Data Controller to erase the Personal Data. Unless stated otherwise, the then-current privacy
|
||||||
policy applies to all Personal Data the Data Controller has about Users.
|
policy applies to all Personal Data the Data Controller has about Users.
|
||||||
h4 Definitions and legal references
|
h4 Definitions and legal references
|
||||||
p Latest update: February 27, 2014
|
p Original issue: February 27, 2014
|
||||||
|
p Latest update: June 10, 2019 (Updated Blender Institute address)
|
||||||
|
|
||||||
|
|
||||||
| {% endblock body%}
|
| {% endblock body%}
|
||||||
|
64
src/templates/production.pug
Normal file
64
src/templates/production.pug
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
| {% extends 'layout.html' %}
|
||||||
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
include mixins/components
|
||||||
|
include ../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
|
mixin group(title, tag)
|
||||||
|
.row(id=tag)
|
||||||
|
section.py-4.my-3.border-bottom.col-12
|
||||||
|
|
||||||
|
h4.title-underline.mt-2.mb-4
|
||||||
|
a.text-muted(href="#" + tag)= title
|
||||||
|
+card-deck(data-asset-tag=tag, class="js-asset-list p-3")
|
||||||
|
|
||||||
|
| {% set title = 'learn' %}
|
||||||
|
|
||||||
|
| {% set page_title = 'Production Lessons' %}
|
||||||
|
| {% set page_description = 'Tips and tricks by the Blender Open Movies crew.' %}
|
||||||
|
| {% set page_header_image = url_for('static', filename='assets/img/features/open_movies_02.jpg', _external=true) %}
|
||||||
|
|
||||||
|
| {% block page_title %}{{ page_title }}{% endblock %}
|
||||||
|
|
||||||
|
| {% block og %}
|
||||||
|
| {{ opengraph(page_title, page_description, page_header_image, request.url) }}
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_homepage(title) }}
|
||||||
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
|
| {% block head %}
|
||||||
|
script(src="{{ url_for('static_cloud', filename='assets/js/tagged_assets.min.js') }}")
|
||||||
|
|
||||||
|
script.
|
||||||
|
$(function() {
|
||||||
|
let is_subscriber = {{ current_user.has_cap('subscriber')|string|lower }};
|
||||||
|
$('.js-asset-list').loadTaggedAssets(8, 8, is_subscriber);
|
||||||
|
});
|
||||||
|
| {% endblock %}
|
||||||
|
| {% block body %}
|
||||||
|
.container.py-4
|
||||||
|
+category_list_header('{{ page_title }}', '{{ page_description }}')
|
||||||
|
.row
|
||||||
|
.col-12
|
||||||
|
+group('Walk-through', 'walk-through')
|
||||||
|
+group('Modeling', 'modeling')
|
||||||
|
+group('Sculpting', 'sculpting')
|
||||||
|
+group('Animation', 'animation')
|
||||||
|
+group('Shading', 'shading')
|
||||||
|
+group('Texturing', 'texturing')
|
||||||
|
+group('Character Pipeline', 'character-pipeline')
|
||||||
|
+group('Rigging', 'rigging')
|
||||||
|
+group('Lighting & Rendering', 'lighting')
|
||||||
|
+group('Simulation & Effects', 'effects')
|
||||||
|
+group('Video Editing', 'video-editing')
|
||||||
|
+group('Digital Painting', 'digital-painting')
|
||||||
|
+group('Production Design', 'production-design')
|
||||||
|
|
||||||
|
a.d-block.py-5.text-center.text-muted(
|
||||||
|
href="{{ url_for('main.nodes_search_index') }}")
|
||||||
|
| Search Blender Cloud to find even more content
|
||||||
|
i.pi-angle-right.pl-1
|
||||||
|
|
||||||
|
| {% endblock body%}
|
21
src/templates/project_settings/cloud_layout.pug
Normal file
21
src/templates/project_settings/cloud_layout.pug
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
| {% extends 'projects/edit_layout.html' %}
|
||||||
|
| {% set title = 'cloud' %}
|
||||||
|
| {% block page_title %}Blender Cloud settings for {{ project.name }}{% endblock %}
|
||||||
|
|
||||||
|
| {% block head %}
|
||||||
|
script(src="{{ url_for('static_attract', filename='assets/js/generated/tutti.min.js') }}")
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block project_context %}
|
||||||
|
.container-fluid
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
h5.pt-3 {{ self.page_title() }}
|
||||||
|
|
||||||
|
hr
|
||||||
|
|
||||||
|
#node-edit-container
|
||||||
|
| {% block cloud_container %}
|
||||||
|
| {% endblock cloud_container %}
|
||||||
|
|
||||||
|
| {% endblock project_context %}
|
28
src/templates/project_settings/offer_setup.pug
Normal file
28
src/templates/project_settings/offer_setup.pug
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
| {% extends 'project_settings/cloud_layout.html' %}
|
||||||
|
|
||||||
|
| {% block cloud_container %}
|
||||||
|
#node-edit-form
|
||||||
|
p This project is not setup for Blender Cloud #[span.text-muted (yet!)]
|
||||||
|
p
|
||||||
|
button.btn.btn-outline-primary.px-3(onclick='setupForFilm()')
|
||||||
|
i.pr-2.pi-blender-cloud
|
||||||
|
| Setup Project for Film
|
||||||
|
|
||||||
|
| {% endblock cloud_container %}
|
||||||
|
|
||||||
|
| {% block footer_scripts %}
|
||||||
|
script.
|
||||||
|
function setupForFilm() {
|
||||||
|
$.ajax({
|
||||||
|
url: '{{ url_for( "cloud.setup_for_film", project_url=project.url) }}',
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.done(function() {
|
||||||
|
window.location.reload();
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
var err_elt = xhrErrorResponseElement(err, 'Error setting up your project: ');
|
||||||
|
toastr.error(err_elt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
| {% endblock %}
|
66
src/templates/project_settings/settings.pug
Normal file
66
src/templates/project_settings/settings.pug
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
| {% extends 'project_settings/cloud_layout.html' %}
|
||||||
|
|
||||||
|
| {% block cloud_container %}
|
||||||
|
#node-edit-form
|
||||||
|
form(onsubmit="save(this, '{{ url_for('cloud.save_film_settings', project_url=project['url']) }}'); return false;")
|
||||||
|
| {% for field in form %}
|
||||||
|
|
||||||
|
| {% if field.name == 'csrf_token' %}
|
||||||
|
| {{ field }}
|
||||||
|
| {% else %}
|
||||||
|
| {% if field.type == 'HiddenField' %}
|
||||||
|
| {{ field }}
|
||||||
|
| {% else %}
|
||||||
|
|
||||||
|
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
|
||||||
|
| {{ field.label }}
|
||||||
|
| {% if field.name == 'picture' %}
|
||||||
|
| {% if post.picture %}
|
||||||
|
img.node-preview-thumbnail(src="{{ post.picture.thumbnail('m', api=api) }}")
|
||||||
|
a(href="#", class="file_delete", data-field-name="picture", data-file_id="{{post.picture._id}}") Delete
|
||||||
|
| {% endif %}
|
||||||
|
| {% endif %}
|
||||||
|
| {{ field(class='form-control') }}
|
||||||
|
|
||||||
|
| {% if field.description %}
|
||||||
|
small.form-text.text-muted
|
||||||
|
| {{ field.description }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
| {% if field.errors %}
|
||||||
|
ul.error
|
||||||
|
| {% for error in field.errors %}
|
||||||
|
li {{ error }}
|
||||||
|
| {% endfor %}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
| {% endif %}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
| {% endfor %}
|
||||||
|
button.btn.btn-outline-success.btn-block(type='submit')
|
||||||
|
i.pi-check
|
||||||
|
| Save
|
||||||
|
|
||||||
|
|
||||||
|
| {% endblock cloud_container %}
|
||||||
|
| {% block footer_scripts %}
|
||||||
|
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.ui.widget.min.js') }}")
|
||||||
|
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
|
||||||
|
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.fileupload.min.js') }}")
|
||||||
|
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/file_upload.min.js') }}")
|
||||||
|
|
||||||
|
script.
|
||||||
|
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: true, nodeId: ''});
|
||||||
|
|
||||||
|
function save(form, url) {
|
||||||
|
let serializedData = $(form).serializeArray()
|
||||||
|
$.post(url, serializedData)
|
||||||
|
.done(function(xhr) {
|
||||||
|
toastr.success('Properties saved');
|
||||||
|
})
|
||||||
|
.fail(function(err) {
|
||||||
|
toastr.error(xhrErrorResponseElement(err, 'Error saving properties: '));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
| {% endblock %}
|
23
src/templates/projects/browse_embed.pug
Normal file
23
src/templates/projects/browse_embed.pug
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
| {% from '_macros/_asset_list_item.html' import asset_list_item %}
|
||||||
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
|
| {% block body %}
|
||||||
|
#node-container
|
||||||
|
section.d-flex
|
||||||
|
h4.p-4 Browse
|
||||||
|
|
||||||
|
section.container-fluid
|
||||||
|
| {% if nodes %}
|
||||||
|
+card-deck(id="asset_list_explore", class="pl-4")
|
||||||
|
| {% for node in nodes %}
|
||||||
|
| {{ asset_list_item(node, current_user) }}
|
||||||
|
| {% endfor %}
|
||||||
|
| {% else %}
|
||||||
|
.list-node-children-container
|
||||||
|
.list-node-children-empty No items... yet!
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
script.
|
||||||
|
// Generate GA pageview
|
||||||
|
ga('send', 'pageview', location.pathname);
|
||||||
|
| {% endblock %}
|
@@ -1,4 +1,6 @@
|
|||||||
| {% extends 'projects/home_layout.html' %}
|
| {% extends 'projects/home_layout.html' %}
|
||||||
|
| {% set title = 'blender-sync' %}
|
||||||
|
|
||||||
| {% set subtab = 'blender_sync' %}
|
| {% set subtab = 'blender_sync' %}
|
||||||
| {% set learn_more_btn_url = '/blog/introducing-blender-sync' %}
|
| {% set learn_more_btn_url = '/blog/introducing-blender-sync' %}
|
||||||
| {% block currenttab %}
|
| {% block currenttab %}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
| {% from '_macros/_navigation.html' import navigation_home_project %}
|
||||||
include ../mixins/components
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% set title = 'home' %}
|
|
||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:type", content="website")
|
meta(property="og:type", content="website")
|
||||||
@@ -20,20 +18,13 @@ meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/ba
|
|||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
| {{ navigation_tabs(title) }}
|
| {{ navigation_home_project(title) }}
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
.dashboard-container
|
.dashboard-container
|
||||||
|
|
||||||
section#projects.bg-white
|
section#projects.bg-white
|
||||||
+nav-secondary()(id='sub-nav-tabs__list')
|
|
||||||
+nav-secondary-link(id="subtab-blender_sync", data-tab-url="{{ url_for('projects.home_project')}}")
|
|
||||||
| Blender Sync
|
|
||||||
|
|
||||||
+nav-secondary-link(id="subtab-images", data-tab-url="{{ url_for('projects.home_project_shared_images')}}")
|
|
||||||
| Images
|
|
||||||
|
|
||||||
| {% block currenttab %}{% endblock %}
|
| {% block currenttab %}{% endblock %}
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
| {% from '_macros/_navigation.html' import navigation_home_project %}
|
||||||
include ../mixins/components
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% set title = 'dashboard' %}
|
| {% set title = 'dashboard' %}
|
||||||
|
|
||||||
@@ -39,42 +39,46 @@ style.
|
|||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
| {{ navigation_tabs(title) }}
|
| {{ navigation_home_project(title) }}
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
.dashboard-container
|
.dashboard-container
|
||||||
section.dashboard-main
|
section.dashboard-main
|
||||||
section#projects.bg-white
|
section#projects.bg-white
|
||||||
+nav-secondary()(id='sub-nav-tabs__list')
|
.d-flex
|
||||||
+nav-secondary-link(data-tab-toggle='own_projects', class="active")
|
+nav-secondary()(id='sub-nav-tabs__list')
|
||||||
| Own Projects
|
+nav-secondary-link(data-tab-toggle='own_projects', class="active")
|
||||||
| {% if projects_user|length != 0 %}
|
span
|
||||||
span ({{ projects_user|length }})
|
| Own Projects
|
||||||
| {% endif %}
|
| {% if projects_user | length != 0 %}
|
||||||
|
.d-inline.text-muted.pl-1 ({{ projects_user|length }})
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
+nav-secondary-link(data-tab-toggle='shared')
|
+nav-secondary-link(data-tab-toggle='shared')
|
||||||
| Shared with me
|
span
|
||||||
| {% if projects_shared|length != 0 %}
|
| Shared with me
|
||||||
span ({{ projects_shared|length }})
|
| {% if projects_shared | length != 0 %}
|
||||||
| {% endif %}
|
.d-inline.text-muted.pl-1 ({{ projects_shared|length }})
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
| {% if current_user.has_cap('subscriber') %}
|
+nav-secondary()()
|
||||||
+nav-secondary-link(
|
| {% if current_user.has_cap('subscriber') %}
|
||||||
id="project-create",
|
+nav-secondary-link(
|
||||||
data-url="{{ url_for('projects.create') }}",
|
id="project-create",
|
||||||
href="{{ url_for('projects.create') }}")
|
data-url="{{ url_for('projects.create') }}",
|
||||||
span.text-success
|
href="{{ url_for('projects.create') }}")
|
||||||
| #[i.pi-plus] Create New Project
|
span.text-success Create New Project...
|
||||||
| {% elif current_user.has_cap('can-renew-subscription') %}
|
|
||||||
+nav-secondary-link(
|
| {% elif current_user.has_cap('can-renew-subscription') %}
|
||||||
id="project-create",
|
+nav-secondary-link(
|
||||||
data-url="{{ url_for('projects.create') }}",
|
id="project-create",
|
||||||
href="/renew",
|
data-url="{{ url_for('projects.create') }}",
|
||||||
target="_blank")
|
href="/renew",
|
||||||
| #[i.pi-heart-filled.text-danger] Resubscribe to Create a Project
|
target="_blank")
|
||||||
| {% endif %}
|
i.pi-heart-filled.text-danger.pr-1
|
||||||
|
span Resubscribe to Create a Project
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
nav.nav-tabs__tab.active#own_projects
|
nav.nav-tabs__tab.active#own_projects
|
||||||
.deleted-projects-toggle
|
.deleted-projects-toggle
|
||||||
|
@@ -1,68 +1,25 @@
|
|||||||
| {% import 'projects/_macros.html' as projectmacros %}
|
|
||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
|
| {% from '_macros/_navigation.html' import navigation_project %}
|
||||||
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
|
||||||
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
|
| {% block bodyclasses %}{{ super() }} landing-home{% endblock %}
|
||||||
|
|
||||||
|
| {% from '_macros/_asset_list_item.html' import asset_list_item %}
|
||||||
|
|
||||||
| {% block page_title %}{{ project.name }}{% endblock%}
|
| {% block page_title %}{{ project.name }}{% endblock%}
|
||||||
|
|
||||||
| {% block og %}
|
|
||||||
meta(property="og:type", content="website")
|
|
||||||
|
|
||||||
| {% if og_picture %}
|
| {% if og_picture %}
|
||||||
meta(property="og:image", content="{{ og_picture.thumbnail('l', api=api) }}")
|
| {% set og_picture_url = og_picture.thumbnail('l', api=api) %}
|
||||||
meta(name="twitter:image", content="{{ og_picture.thumbnail('l', api=api) }}")
|
|
||||||
| {% elif node and node.picture %}
|
|
||||||
meta(property="og:image", content="{{ node.picture.thumbnail('l', api=api) }}")
|
|
||||||
meta(name="twitter:image", content="{{ node.picture.thumbnail('l', api=api) }}")
|
|
||||||
| {% elif project.picture_header %}
|
|
||||||
meta(property="og:image", content="{{ project.picture_header.thumbnail('l', api=api) }}")
|
|
||||||
meta(name="twitter:image", content="{{ project.picture_header.thumbnail('l', api=api) }}")
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% if show_project %}
|
|
||||||
meta(property="og:title", content="{{ project.name }} - Blender Cloud")
|
|
||||||
meta(name="twitter:title", content="{{ project.name }} - Blender Cloud")
|
|
||||||
meta(property="og:description", content="{{ project.summary }}")
|
|
||||||
meta(name="twitter:description", content="{{ project.summary }}")
|
|
||||||
meta(property="og:url", content="{{ url_for('projects.view', project_url=project.url, _external=True) }}")
|
|
||||||
| {% else %}
|
| {% else %}
|
||||||
|
| {% set og_picture_url = None %}
|
||||||
| {% if node %}
|
|
||||||
meta(property="og:title", content="{{ node.name }} - Blender Cloud")
|
|
||||||
meta(name="twitter:title", content="{{ node.name }} on Blender Cloud")
|
|
||||||
|
|
||||||
| {% if node.node_type == 'post' %}
|
|
||||||
|
|
||||||
| {% if node.properties.content %}
|
|
||||||
meta(property="og:description", content="{{ node.properties.content | truncate(180) }}")
|
|
||||||
meta(name="twitter:description", content="{{ node.properties.content | truncate(180) }}")
|
|
||||||
| {% else %}
|
|
||||||
meta(property="og:description", content="Blender Cloud, your source for open content and training")
|
|
||||||
meta(name="twitter:description", content="Blender Cloud, your source for open content and training")
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% else %}
|
| {% block og %}
|
||||||
|
| {{ opengraph(project.name, project.summary, og_picture_url, url_for('cloud.project_landing', project_url=project.url, _external=True)) }}
|
||||||
|
| {% endblock %}
|
||||||
|
|
||||||
| {% if node.description %}
|
|
||||||
meta(property="og:description", content="{{ node.description | truncate(180) }}")
|
|
||||||
meta(name="twitter:description", content="{{ node.description | truncate(180) }}")
|
|
||||||
| {% else %}
|
|
||||||
meta(property="og:description", content="Blender Cloud, your source for open content and training")
|
|
||||||
meta(name="twitter:description", content="Blender Cloud, your source for open content and training")
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
meta(property="og:url", content="{{url_for('projects.view_node', project_url=project.url, node_id=node._id)}}")
|
|
||||||
| {% else %}
|
|
||||||
meta(property="og:title", content="{{ project.name }} Blog on Blender Cloud")
|
|
||||||
meta(name="twitter:title", content="{{ project.name }} Blog on Blender Cloud")
|
|
||||||
meta(property="og:description", content="{{ project.summary }}")
|
|
||||||
meta(name="twitter:description", content="{{ project.summary }}")
|
|
||||||
|
|
||||||
meta(property="og:url", content="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% endif %}
|
|
||||||
| {% endblock og %}
|
|
||||||
|
|
||||||
| {% block page_overlay %}
|
| {% block page_overlay %}
|
||||||
#page-overlay.video
|
#page-overlay.video
|
||||||
@@ -70,107 +27,156 @@ meta(property="og:url", content="{{url_for('projects.view', project_url=project.
|
|||||||
#others
|
#others
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block head %}
|
|
||||||
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-6.2.8.min.js') }}")
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-ga-0.4.2.min.js') }}")
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-hotkeys-0.2.20.min.js') }}")
|
|
||||||
| {% endblock %}
|
|
||||||
|
|
||||||
| {% block css %}
|
| {% block css %}
|
||||||
link(href="{{ url_for('static_cloud', filename='assets/css/project-landing.css') }}", rel="stylesheet")
|
link(href="{{ url_for('static_cloud', filename='assets/css/project-main.css') }}", rel="stylesheet")
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_project(project, navigation_links, extension_sidebar_links, title) }}
|
||||||
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
header
|
|
||||||
//a(href="{{ url_for( 'projects.view', project_url=project.url) }}")
|
|
||||||
img.header(src="{{ project.picture_header.thumbnail('h', api=api) }}")
|
|
||||||
|
|
||||||
| {% block navbar_secondary %}
|
| {% if project.has_method('PUT') %}
|
||||||
| {{ projectmacros.render_secondary_navigation(project, pages=pages) }}
|
+nav-secondary
|
||||||
|
+nav-secondary-link(
|
||||||
|
href="{{ url_for('projects.edit', project_url=project.url) }}",
|
||||||
|
class="text-white")
|
||||||
|
i.pi-edit.pr-2
|
||||||
|
span Edit Project
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
| {% endblock navbar_secondary %}
|
| {% if project.picture_header %}
|
||||||
#container.landing
|
| {% set project_header = project.picture_header.thumbnail('h', api=api) %}
|
||||||
section.node-details-container.project
|
| {% endif %}
|
||||||
.node-details-title
|
|
||||||
h1 {{ project.name }}
|
|
||||||
|
|
||||||
| {% if project.description %}
|
| {% set project_browse_url = url_for('cloud.project_browse', project_url=project.url) %}
|
||||||
.node-details-description
|
|
||||||
| {{ project | markdowned('description') }}
|
.jumbotron.text-white.jumbotron-overlay-gradient-fade-to-gray(
|
||||||
| {% endif %}
|
style="background-image: url(\'{{ project_header }}\');")
|
||||||
|
.container
|
||||||
|
.row
|
||||||
|
.col-md-6
|
||||||
|
.display-4.text-uppercase.font-weight-bold
|
||||||
|
| {% if project.extension_props.cloud.logo %}
|
||||||
|
a(href="{{ project_browse_url }}")
|
||||||
|
img.img-fluid(
|
||||||
|
alt="{{ project.name }}",
|
||||||
|
src="{{ project.extension_props.cloud.logo.thumbnail('m', api=api) }}")
|
||||||
|
| {% else %}
|
||||||
|
a.text-white(href="{{ project_browse_url }}")
|
||||||
|
| {{ project.name }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
.lead.pt-3
|
||||||
|
| {% if project.summary %}
|
||||||
|
| {{ project | markdowned('summary') }}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
.d-flex.pt-4
|
||||||
|
| {% if project.extension_props.cloud.video_url %}
|
||||||
|
a.btn.btn-primary.px-5(
|
||||||
|
class="js-open-overlay-video",
|
||||||
|
href="{{ project.extension_props.cloud.video_url }}",
|
||||||
|
target="_blank")
|
||||||
|
i.pi-play.pr-2
|
||||||
|
| WATCH
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
|
a.btn.btn-link.px-4.text-white(href="{{ project_browse_url }}")
|
||||||
|
| Browse
|
||||||
|
i.pi-angle-right.pl-2
|
||||||
|
|
||||||
|
|
||||||
section.gallery
|
.container-fluid.landing
|
||||||
h2 Gallery
|
.row
|
||||||
| {% for n in activity_stream %}
|
.col-md-8.mx-auto.mt-5
|
||||||
| {% if n.node_type not in ['comment', 'post'] and n.picture %}
|
.node-details-description
|
||||||
.thumbnail.expand-image-links
|
| {% if project.description %}
|
||||||
.img-container
|
| {{ project | markdowned('description') }}
|
||||||
a(href="{{ n.picture.thumbnail('l', api=api) }}", data-node_id="{{ n._id }}")
|
|
||||||
img(src="{{ n.picture.thumbnail('l', api=api) }}", alt="{{ n.name }}")
|
|
||||||
.img-caption.table
|
|
||||||
| {# Not using for the moment
|
|
||||||
span.table-cell {{ n.name }}
|
|
||||||
| #}
|
|
||||||
| {% endif %}
|
|
||||||
| {% endfor %}
|
|
||||||
div(class="clearfix")
|
|
||||||
| {% if project.nodes_featured %}
|
|
||||||
| {# In some cases featured_nodes might might be embedded #}
|
|
||||||
| {% if '_id' in project.nodes_featured[0] %}
|
|
||||||
| {% set featured_node_id=project.nodes_featured[0]._id %}
|
|
||||||
| {% else %}
|
|
||||||
| {% set featured_node_id=project.nodes_featured[0] %}
|
|
||||||
| {% endif %}
|
|
||||||
a.btn(href="{{ url_for('projects.view_node', project_url=project.url, node_id=featured_node_id) }}") See more
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
section.node-extra
|
|
||||||
h2 Latest Updates
|
|
||||||
|
|
||||||
| {% if activity_stream %}
|
|
||||||
.node-updates
|
|
||||||
ul.node-updates-list
|
|
||||||
| {% for n in activity_stream %}
|
|
||||||
| {% if n.node_type == 'post' %}
|
|
||||||
li.node-updates-list-item(
|
|
||||||
data-node_id="{{ n._id }}",
|
|
||||||
class="{{ n.node_type }} {{ n.properties.content_type | hide_none }}")
|
|
||||||
a.image(href="{{ url_for_node(node=n) }}")
|
|
||||||
| {% if n.picture %}
|
|
||||||
img(src="{{ n.picture.thumbnail('l', api=api) }}")
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
.info
|
|
||||||
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
|
|
||||||
p.description(href="{{ url_for_node(node=n) }}")
|
|
||||||
| {% if n.node_type == 'post' %}
|
|
||||||
| {{ n.properties | markdowned('content') | striptags | truncate(140, end="... <small>read more</small>") | safe | hide_none }}
|
|
||||||
| {% else %}
|
|
||||||
| {{ n | markdowned('description') | striptags | truncate(140, end="... <small>read more</small>") | safe | hide_none }}
|
|
||||||
| {% endif %}
|
|
||||||
//span.details
|
|
||||||
// span.what {% if n.properties.content_type %}{{ n.properties.content_type | undertitle }}{% else %}{{ n.node_type | undertitle }}{% endif %} ·
|
|
||||||
// span.when {{ n._updated | pretty_date }} by
|
|
||||||
// span.who {{ n.user.full_name }}
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
| {% endfor %}
|
|
||||||
| {% endif %}
|
.row
|
||||||
a.btn(href="{{ url_for('main.project_blog', project_url=project.url) }}") See all updates
|
.col-md-10.mx-auto
|
||||||
|
section.py-5
|
||||||
|
.gallery.mx-auto
|
||||||
|
| {% for n in activity_stream %}
|
||||||
|
| {% if n.node_type not in ['comment', 'post'] and n.picture %}
|
||||||
|
.thumbnail.expand-image-links
|
||||||
|
.img-container
|
||||||
|
a.js-open-overlay-image(
|
||||||
|
title="{{ n.name }}",
|
||||||
|
href="{{ n.picture.thumbnail('l', api=api) }}")
|
||||||
|
img(
|
||||||
|
alt="{{ n.name }}",
|
||||||
|
src="{{ n.picture.thumbnail('l', api=api) }}")
|
||||||
|
| {% endif %}
|
||||||
|
| {% endfor %}
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
.text-center.mx-auto.py-3
|
||||||
|
a.btn.btn-outline-primary.px-5(
|
||||||
|
href="{{ project_browse_url }}")
|
||||||
|
| See More Artwork
|
||||||
|
|
||||||
|
|
||||||
|
.row.mt-5
|
||||||
|
.col-md-10.mx-auto
|
||||||
|
h2.pb-3 Project Timeline
|
||||||
|
.timeline-dark
|
||||||
|
+timeline("{{ project._id }}")
|
||||||
|
|
||||||
| {% endblock body %}
|
| {% endblock body %}
|
||||||
|
|
||||||
|
|
||||||
| {% block footer_scripts %}
|
| {% block footer_scripts %}
|
||||||
script.
|
script.
|
||||||
// Click anywhere in the page to hide the overlay
|
function showOverlay(html_content) {
|
||||||
function hideOverlay() {
|
$('#page-overlay')
|
||||||
$('#page-overlay.video').removeClass('active');
|
.addClass('active')
|
||||||
$('#page-overlay.video .video-embed').html('');
|
.html(html_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hideOverlay() {
|
||||||
|
$('#page-overlay')
|
||||||
|
.removeClass('active')
|
||||||
|
.html('');
|
||||||
|
}
|
||||||
|
|
||||||
|
$("a.js-open-overlay-image").on( "click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var url = $(this).attr('href');
|
||||||
|
showOverlay('<img src="' + url + '"/>');
|
||||||
|
});
|
||||||
|
|
||||||
|
{% if project.extension_props.cloud.video_url %}
|
||||||
|
//- By isherwood - http://jsfiddle.net/isherwood/cH6e8/
|
||||||
|
function getYoutubeId(url) {
|
||||||
|
var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
|
||||||
|
var match = url.match(regExp);
|
||||||
|
|
||||||
|
if (match && match[2].length == 11) {
|
||||||
|
return match[2];
|
||||||
|
} else {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var videoId = getYoutubeId('{{ project.extension_props.cloud.video_url }}');
|
||||||
|
var iframeMarkup = '<iframe width="960" height="540" src="//www.youtube.com/embed/'
|
||||||
|
+ videoId + '" frameborder="0" allowfullscreen></iframe>';
|
||||||
|
|
||||||
|
$("a.js-open-overlay-video").on( "click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
showOverlay(iframeMarkup);
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
//- Click anywhere on the page or hit Escape to hide the overlay.
|
||||||
$(document).click(function () {
|
$(document).click(function () {
|
||||||
hideOverlay();
|
hideOverlay();
|
||||||
});
|
});
|
||||||
@@ -180,16 +186,4 @@ script.
|
|||||||
hideOverlay();
|
hideOverlay();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("a[data-node_id]").on( "click", function(e) {
|
|
||||||
// var nodeId = $(this).data('node_id');
|
|
||||||
// displayNode(nodeId);
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
$('#page-overlay').addClass('active');
|
|
||||||
var url = $(this).attr('href');
|
|
||||||
$('#page-overlay').html('<img src="' + url + '"/>')
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
| {% from '_macros/_add_new_menu.html' import add_new_menu %}
|
| {% from '_macros/_add_new_menu.html' import add_new_menu %}
|
||||||
include ../mixins/components
|
| {% from '_macros/_navigation.html' import navigation_project %}
|
||||||
|
|
||||||
|
include ../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% block page_title %}{{ project.name }}{% endblock%}
|
| {% block page_title %}{{ project.name }}{% endblock%}
|
||||||
|
|
||||||
|
| {% if title is not defined %}
|
||||||
| {% set title = 'project' %}
|
| {% set title = 'project' %}
|
||||||
|
| {% endif %}
|
||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:type", content="website")
|
meta(property="og:type", content="website")
|
||||||
@@ -73,220 +78,153 @@ link(href="{{ url_for('static_pillar', filename='assets/jstree/themes/default/st
|
|||||||
link(rel="amphtml", href="{{ url_for('nodes.view', node_id=node._id, _external=True, format='amp') }}")
|
link(rel="amphtml", href="{{ url_for('nodes.view', node_id=node._id, _external=True, format='amp') }}")
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-6.2.8.min.js') }}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/video.min.js') }}")
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-ga-0.4.2.min.js') }}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-ga-0.4.2.min.js') }}")
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-hotkeys-0.2.20.min.js') }}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/videojs-hotkeys-0.2.20.min.js') }}")
|
||||||
script(src="{{ url_for('static_pillar', filename='assets/js/video_plugins.min.js') }}")
|
script(src="{{ url_for('static_pillar', filename='assets/js/video_plugins.min.js') }}")
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block css %}
|
| {% block css %}
|
||||||
link(href="{{ url_for('static_pillar', filename='assets/css/font-pillar.css') }}", rel="stylesheet")
|
link(href="{{ url_for('static_cloud', filename='assets/css/project-main.css') }}", rel="stylesheet")
|
||||||
link(href="{{ url_for('static_pillar', filename='assets/css/project-main.css') }}", rel="stylesheet")
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
+nav-secondary()(class="bg-white")
|
| {{ navigation_project(project, navigation_links, extension_sidebar_links, title) }}
|
||||||
| {% if project.category == 'course' %}
|
|
||||||
li.text-capitalize
|
|
||||||
a.nav-link.text-muted.px-0(href="{{ url_for('cloud.courses') }}")
|
|
||||||
| Courses
|
|
||||||
| {% elif project.category == 'workshop' %}
|
|
||||||
li.text-capitalize
|
|
||||||
a.nav-link.text-muted.px-0(href="{{ url_for('cloud.workshops') }}")
|
|
||||||
| Workshops
|
|
||||||
li.px-1
|
|
||||||
i.pi-angle-right
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
+nav-secondary-link(
|
|
||||||
class="px-0",
|
|
||||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| {{ project.name }}
|
|
||||||
|
|
||||||
| {% if project.category == "open_project" %}
|
|
||||||
+nav-secondary-link(
|
|
||||||
class="active",
|
|
||||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| Explore
|
|
||||||
+nav-secondary-link(
|
|
||||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| Blog
|
|
||||||
+nav-secondary-link(
|
|
||||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| About
|
|
||||||
+nav-secondary-link(
|
|
||||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| Team
|
|
||||||
+nav-secondary-link(
|
|
||||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
| Awards
|
|
||||||
| {% endif %}
|
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
#project-container
|
#project-container.is-sidebar-visible
|
||||||
#project-side-container
|
#project-side-container.bg-light
|
||||||
#project_sidebar.bg-white
|
|
||||||
ul.project-tabs.p-0
|
|
||||||
//- li.tabs-thumbnail(class="{% if project.picture_square %}image{% endif %}")
|
|
||||||
//- a(href="{{url_for('projects.view', project_url=project.url)}}")
|
|
||||||
//- #project-loading
|
|
||||||
//- i.pi-spin
|
|
||||||
//- | {% if project.picture_square %}
|
|
||||||
//- img(src="{{ project.picture_square.thumbnail('b', api=api) }}")
|
|
||||||
//- | {% else %}
|
|
||||||
//- i.pi-home
|
|
||||||
//- | {% endif %}
|
|
||||||
|
|
||||||
li.tabs-browse(
|
|
||||||
title="Browse",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="right",
|
|
||||||
class="active")
|
|
||||||
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
i.pi-folder
|
|
||||||
|
|
||||||
| {% if not project.is_private %}
|
|
||||||
| {% if current_user_is_subscriber %}
|
|
||||||
li.tabs-search(
|
|
||||||
title="Search",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="right")
|
|
||||||
a(href="{{ url_for('projects.search', project_url=project.url, _external=True)}} ")
|
|
||||||
i.pi-search
|
|
||||||
| {% else %}
|
|
||||||
li.tabs-search(
|
|
||||||
title="Search (subscribers only)",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="right")
|
|
||||||
a(href="{{ url_for('cloud.join') }}")
|
|
||||||
i.pi-search
|
|
||||||
| {% endif %}
|
|
||||||
| {% endif %}
|
|
||||||
| {{ extension_sidebar_links }}
|
|
||||||
|
|
||||||
| {% if project.has_method('PUT') %}
|
|
||||||
li(
|
|
||||||
title="Edit Project",
|
|
||||||
data-toggle="tooltip",
|
|
||||||
data-placement="right")
|
|
||||||
a(href="{{ url_for('projects.edit', project_url=project.url) }}")
|
|
||||||
i.pi-cog
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
#project_nav(class="{{ title }}")
|
#project_nav(class="{{ title }}")
|
||||||
#project_nav-container
|
#project_nav-container
|
||||||
| {% if title != 'about' %}
|
| {% if title != 'about' %}
|
||||||
//- +nav-secondary(class="bg-white")
|
button.project-sidebar-toggle.btn.btn-sm.btn-link.px-1.rounded-0.bg-light.text-muted.position-absolute(
|
||||||
//- +nav-secondary-link(
|
type="button",
|
||||||
//- class="active",
|
class="js-project-sidebar-toggle")
|
||||||
//- href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
i.pi-angle-double-left
|
||||||
//- | {{ project.name }}
|
|
||||||
//- #project_nav-header.bg-white
|
|
||||||
|
|
||||||
//- a.project-title.p-2.font-weight-bold.text-dark(
|
|
||||||
//- href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
|
||||||
//- | {{ project.name }}
|
|
||||||
|
|
||||||
| {% block project_tree %}
|
| {% block project_tree %}
|
||||||
#project_tree.bg-white
|
#project_tree.bg-light.p-1
|
||||||
| {% endblock project_tree %}
|
| {% endblock project_tree %}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
|
#project_context-container
|
||||||
|
.breadcrumbs-container.bg-dark.fixed-top
|
||||||
|
button.project-sidebar-toggle.btn.btn-sm.btn-link.px-1.bg-dark.rounded-0.text-muted(
|
||||||
|
type="button",
|
||||||
|
class="js-project-sidebar-toggle")
|
||||||
|
i.pi-menu
|
||||||
|
|
||||||
|
node-breadcrumbs(node-id="{{ node._id }}", @navigate="(nodeId)=>{displayNode(nodeId)}")
|
||||||
|
script.
|
||||||
|
new Vue({el:'node-breadcrumbs'});
|
||||||
|
|
||||||
#project_context-container.border-left
|
|
||||||
| {% if project.has_method('PUT') %}
|
| {% if project.has_method('PUT') %}
|
||||||
#project_context-header.bg-white
|
#project_context-header.position-absolute
|
||||||
span#status-bar
|
ul.project-edit-tools.disabled.d-flex.list-unstyled.py-2.mb-0
|
||||||
|
li.dropdown(
|
||||||
ul.project-edit-tools.disabled
|
title="Create...",
|
||||||
li.dropdown
|
data-toggle="tooltip",
|
||||||
button#item_add.project-mode-view.btn.btn-sm.btn-outline-secondary.dropdown-toggle(
|
data-placement="left")
|
||||||
|
button.dropdown-toggle.btn.btn-sm.btn-outline-secondary(
|
||||||
|
id="item_add",
|
||||||
|
class="project-mode-view",
|
||||||
type="button",
|
type="button",
|
||||||
data-toggle="dropdown",
|
data-toggle="dropdown",
|
||||||
aria-haspopup="true",
|
aria-haspopup="true",
|
||||||
aria-expanded="false")
|
aria-expanded="false")
|
||||||
i.button-add-icon.pi-collection-plus
|
i.pi-collection-plus
|
||||||
| New...
|
|
||||||
|
|
||||||
ul.dropdown-menu.add_new-menu
|
ul.dropdown-menu.dropdown-menu-right(
|
||||||
|
class="add_new-menu")
|
||||||
| {{ add_new_menu(project.node_types) }}
|
| {{ add_new_menu(project.node_types) }}
|
||||||
|
|
||||||
li.button-edit
|
li.button-edit
|
||||||
a#item_edit.project-mode-view.btn.btn-sm.btn-outline-secondary.ml-2(
|
a.btn.btn-sm.btn-outline-secondary.ml-2.px-2(
|
||||||
|
id="item_edit",
|
||||||
|
class="project-mode-view",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Edit",
|
title="Edit",
|
||||||
data-project_id="{{project._id}}")
|
data-project_id="{{project._id}}",
|
||||||
|
data-toggle="tooltip",
|
||||||
|
data-placement="top")
|
||||||
i.button-edit-icon.pi-edit
|
i.button-edit-icon.pi-edit
|
||||||
| Edit Project
|
|
||||||
|
|
||||||
li.dropdown
|
li.dropdown
|
||||||
button.dropdown-toggle.project-mode-view.btn.btn-sm.btn-outline-secondary.mx-2(
|
button.dropdown-toggle.btn.btn-sm.btn-outline-secondary.mx-2(
|
||||||
|
class="project-mode-view",
|
||||||
type="button",
|
type="button",
|
||||||
data-toggle="dropdown",
|
data-toggle="dropdown",
|
||||||
aria-haspopup="true",
|
aria-haspopup="true",
|
||||||
aria-expanded="false")
|
aria-expanded="false")
|
||||||
i.pi-more-vertical.p-0
|
i.pi-more-vertical.p-0
|
||||||
|
|
||||||
ul.dropdown-menu
|
ul.dropdown-menu.dropdown-menu-right
|
||||||
| {% if current_user.has_cap('admin') %}
|
| {% if current_user.has_cap('admin') %}
|
||||||
li.dropdown-item
|
li
|
||||||
a#item_featured(
|
a.dropdown-item(
|
||||||
|
id="item_featured",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Feature on project's homepage",
|
title="Feature on project's homepage",
|
||||||
data-toggle="tooltip",
|
data-toggle="tooltip",
|
||||||
data-placement="left")
|
data-placement="left")
|
||||||
i.pi-star
|
i.pi-star.pr-2
|
||||||
| Toggle Featured
|
| Toggle Featured
|
||||||
|
|
||||||
li.dropdown-item
|
li
|
||||||
a#item_toggle_public(
|
a.dropdown-item(
|
||||||
|
id="item_toggle_public",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Make it accessible to anyone",
|
title="Make it accessible to anyone",
|
||||||
data-toggle="tooltip",
|
data-toggle="tooltip",
|
||||||
data-placement="left")
|
data-placement="left")
|
||||||
i.pi-lock-open
|
i.pi-lock-open.pr-2
|
||||||
| Toggle Public
|
| Toggle Public
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
li.dropdown-item
|
li
|
||||||
a#item_toggle_projheader(
|
a.dropdown-item(
|
||||||
|
id="item_toggle_projheader",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Feature as project's header",
|
title="Feature as project's header",
|
||||||
data-toggle="tooltip",
|
data-toggle="tooltip",
|
||||||
data-placement="left")
|
data-placement="left")
|
||||||
i.pi-star
|
i.pi-star.pr-2
|
||||||
| Toggle Project Header video
|
| Toggle Project Header video
|
||||||
|
|
||||||
li.dropdown-item.button-move
|
li.button-move
|
||||||
a#item_move(
|
a.dropdown-item(
|
||||||
|
id="item_move",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Move into a folder...",
|
title="Move into a folder...",
|
||||||
data-toggle="tooltip",
|
data-toggle="tooltip",
|
||||||
data-placement="left")
|
data-placement="left")
|
||||||
i.button-move-icon.pi-move
|
i.button-move-icon.pi-move.pr-2
|
||||||
| Move
|
| Move
|
||||||
|
|
||||||
li.dropdown-item.button-delete
|
li.button-delete
|
||||||
a#item_delete(
|
a.dropdown-item(
|
||||||
|
id="item_delete",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Can be undone within a month",
|
title="Can be undone within a month",
|
||||||
data-toggle="tooltip",
|
data-toggle="tooltip",
|
||||||
data-placement="left")
|
data-placement="left")
|
||||||
i.pi-trash
|
i.pi-trash.pr-2
|
||||||
| Delete Project
|
| Delete Project
|
||||||
|
|
||||||
// Edit Mode
|
// Edit Mode
|
||||||
li.button-cancel
|
li.button-cancel
|
||||||
a#item_cancel.project-mode-edit.btn.btn-outline-secondary(
|
a.btn.btn-outline-secondary(
|
||||||
|
id="item_cancel",
|
||||||
|
class="project-mode-edit",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Cancel changes")
|
title="Cancel changes")
|
||||||
i.button-cancel-icon.pi-cancel
|
i.button-cancel-icon.pi-cancel
|
||||||
| Cancel
|
| Cancel
|
||||||
|
|
||||||
li.button-save
|
li.button-save
|
||||||
a#item_save.project-mode-edit.btn.btn-outline-success.mx-2(
|
a.btn.btn-outline-success.mx-2(
|
||||||
|
id="item_save",
|
||||||
|
class="project-mode-edit",
|
||||||
href="javascript:void(0);",
|
href="javascript:void(0);",
|
||||||
title="Save changes")
|
title="Save changes")
|
||||||
i.button-save-icon.pi-check
|
i.button-save-icon.pi-check
|
||||||
@@ -302,7 +240,8 @@ link(href="{{ url_for('static_pillar', filename='assets/css/project-main.css') }
|
|||||||
| {% endif %}
|
| {% endif %}
|
||||||
#project_context
|
#project_context
|
||||||
| {% block project_context %}
|
| {% block project_context %}
|
||||||
| {% if show_project %}
|
| {% if show_project and not browse %}
|
||||||
|
| {# Embed the project view only if we are not exploring it. #}
|
||||||
| {% include "projects/view_embed.html" %}
|
| {% include "projects/view_embed.html" %}
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
| {% endblock project_context %}
|
| {% endblock project_context %}
|
||||||
@@ -331,6 +270,13 @@ script(type="text/javascript", src="{{ url_for('static_pillar', filename='assets
|
|||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
script.
|
script.
|
||||||
|
loadProjectSidebar();
|
||||||
|
|
||||||
|
$('body').on('click', '.js-project-sidebar-toggle', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
toggleProjectSidebar();
|
||||||
|
});
|
||||||
|
|
||||||
function updateToggleProjHeaderMenuItem() {
|
function updateToggleProjHeaderMenuItem() {
|
||||||
var $toggle_projheader = $('#item_toggle_projheader');
|
var $toggle_projheader = $('#item_toggle_projheader');
|
||||||
|
|
||||||
@@ -388,7 +334,7 @@ script.
|
|||||||
|
|
||||||
// TODO: Maybe remove this, now it's also in loadNodeContent(), but double-check
|
// TODO: Maybe remove this, now it's also in loadNodeContent(), but double-check
|
||||||
// it's done like that in all users of updateUi().
|
// it's done like that in all users of updateUi().
|
||||||
$('#project-loading').removeAttr('class');
|
loadingBarHide();
|
||||||
}
|
}
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
@@ -416,31 +362,36 @@ script.
|
|||||||
|
|
||||||
|
|
||||||
function loadNodeContent(url, nodeId) {
|
function loadNodeContent(url, nodeId) {
|
||||||
$('#project-loading').addClass('active');
|
|
||||||
|
var $projectContext = $('#project_context')
|
||||||
|
$projectContext.trigger('pillar:workStart')
|
||||||
|
|
||||||
$.get(url, function(dataHtml) {
|
$.get(url, function(dataHtml) {
|
||||||
// Update the DOM injecting the generate HTML into the page
|
// Update the DOM injecting the generate HTML into the page
|
||||||
$('#project_context').html(dataHtml);
|
$projectContext.html(dataHtml);
|
||||||
})
|
})
|
||||||
.done(function(){
|
.done(function(){
|
||||||
|
pillar.events.Nodes.triggerLoaded(nodeId);
|
||||||
updateUi(nodeId, 'view');
|
updateUi(nodeId, 'view');
|
||||||
})
|
})
|
||||||
.fail(function(dataResponse) {
|
.fail(function(dataResponse) {
|
||||||
$('#project_context').html($('<iframe id="server_error"/>'));
|
$projectContext.html($('<iframe id="server_error"/>'));
|
||||||
$('#server_error').attr('src', url);
|
$('#server_error').attr('src', url);
|
||||||
})
|
})
|
||||||
.always(function(){
|
.always(function(){
|
||||||
$('#project-loading').removeAttr('class');
|
$projectContext.trigger('pillar:workStop')
|
||||||
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
|
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function loadProjectContent(url) {
|
function loadProjectContent(url) {
|
||||||
$('#project-loading').addClass('active');
|
var $projectContext = $('#project_context')
|
||||||
|
$projectContext.trigger('pillar:workStart')
|
||||||
|
|
||||||
$.get(url, function(dataHtml) {
|
$.get(url, function(dataHtml) {
|
||||||
// Update the DOM injecting the generated HTML into the page
|
// Update the DOM injecting the generated HTML into the page
|
||||||
$('#project_context').html(dataHtml);
|
$projectContext.html(dataHtml);
|
||||||
})
|
})
|
||||||
.done(function() {
|
.done(function() {
|
||||||
updateUi('', 'view');
|
updateUi('', 'view');
|
||||||
@@ -448,11 +399,11 @@ script.
|
|||||||
addMenuDisable(['texture']);
|
addMenuDisable(['texture']);
|
||||||
})
|
})
|
||||||
.fail(function(dataResponse) {
|
.fail(function(dataResponse) {
|
||||||
$('#project_context').html($('<iframe id="server_error"/>'));
|
$projectContext.html($('<iframe id="server_error"/>'));
|
||||||
$('#server_error').attr('src', url);
|
$('#server_error').attr('src', url);
|
||||||
})
|
})
|
||||||
.always(function(){
|
.always(function(){
|
||||||
$('#project-loading').removeAttr('class');
|
$projectContext.trigger('pillar:workStop')
|
||||||
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
|
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -546,6 +497,11 @@ script.
|
|||||||
}
|
}
|
||||||
$('.project-mode-view').displayAs('inline-block');
|
$('.project-mode-view').displayAs('inline-block');
|
||||||
$('.project-mode-edit').hide();
|
$('.project-mode-edit').hide();
|
||||||
|
|
||||||
|
{% if browse %}
|
||||||
|
let url = "{{url_for('cloud.project_browse_view_nodes', project_url=project.url)}}";
|
||||||
|
loadProjectContent(url);
|
||||||
|
{% endif %}
|
||||||
} else {
|
} else {
|
||||||
displayNode(nodeId, false);
|
displayNode(nodeId, false);
|
||||||
}
|
}
|
||||||
@@ -593,7 +549,7 @@ script.
|
|||||||
"image" : {"icon": "pi-image", "max_children": 0},
|
"image" : {"icon": "pi-image", "max_children": 0},
|
||||||
"hdri" : {"icon": "pi-globe", "max_children": 0},
|
"hdri" : {"icon": "pi-globe", "max_children": 0},
|
||||||
"texture" : {"icon": "pi-texture", "max_children": 0},
|
"texture" : {"icon": "pi-texture", "max_children": 0},
|
||||||
"video" : {"icon": "pi-play", "max_children": 0},
|
"video" : {"icon": "pi-film-thick", "max_children": 0},
|
||||||
"blog" : {"icon": "pi-newspaper", "max_children": 0},
|
"blog" : {"icon": "pi-newspaper", "max_children": 0},
|
||||||
"page" : {"icon": "pi-document-text", "max_children": 0},
|
"page" : {"icon": "pi-document-text", "max_children": 0},
|
||||||
"default" : {"icon": "pi-document"}
|
"default" : {"icon": "pi-document"}
|
||||||
@@ -673,10 +629,6 @@ script.
|
|||||||
/* UI Stuff */
|
/* UI Stuff */
|
||||||
$(window).on("load resize",function(){
|
$(window).on("load resize",function(){
|
||||||
containerResizeY($(window).height());
|
containerResizeY($(window).height());
|
||||||
|
|
||||||
if ($(window).width() > 480) {
|
|
||||||
project_container.style.height = (window.innerHeight - project_container.offsetTop) + "px";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
{% if current_user_is_subscriber %}
|
{% if current_user_is_subscriber %}
|
||||||
|
@@ -1,39 +1,23 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
|
||||||
|
include ../../../pillar/src/templates/mixins/components
|
||||||
include mixins/components
|
include mixins/components
|
||||||
|
|
||||||
| {# Default case is Open Projects #}
|
| {# Default collection is 'Courses' #}
|
||||||
| {% set page_title = 'Open Projects' %}
|
| {% set page_title = 'Courses' %}
|
||||||
| {% set page_description = 'Full production data and tutorials from all open movies, for you to use freely' %}
|
| {% set page_description = 'In-depth training on character modeling, 3D printing, rigging, VFX and more.' %}
|
||||||
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_agent327_01.jpg') %}
|
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_agent327_04.jpg', _external=True) %}
|
||||||
| {% set page_header_text = 'The iconic Blender Institute Open Movies. Featuring all the production files, assets, artwork, and never-seen-before content.' %}
|
|
||||||
|
|
||||||
| {% if title == 'courses' %}
|
| {% if title == 'workshops' %}
|
||||||
| {% set page_title = 'Courses' %}
|
|
||||||
| {% set page_description = 'Production quality training by 3D professionals' %}
|
|
||||||
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_agent327_04.jpg') %}
|
|
||||||
| {% set page_header_text = 'Character modeling, 3D printing, VFX, rigging and more.' %}
|
|
||||||
|
|
||||||
| {% elif title == 'workshops' %}
|
|
||||||
| {% set page_title = 'Workshops' %}
|
| {% set page_title = 'Workshops' %}
|
||||||
| {% set page_description = 'Production quality training by 3D professionals' %}
|
| {% set page_description = 'Enter the artist workshop and learn by example.' %}
|
||||||
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_agent327_04.jpg') %}
|
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_agent327_04.jpg', _external=True) %}
|
||||||
| {% set page_header_text = 'Enter the artist workshop and learn by example.' %}
|
|
||||||
|
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:type", content="website")
|
| {{ opengraph(page_title, page_description, page_header_image, request.url) }}
|
||||||
meta(property="og:url", content="https://cloud.blender.org")
|
|
||||||
|
|
||||||
meta(property="og:title", content="{{ page_title }} on Blender Cloud")
|
|
||||||
meta(name="twitter:title", content="{{ page_title }} on Blender Cloud")
|
|
||||||
|
|
||||||
meta(property="og:description", content="{{ page_description }}")
|
|
||||||
meta(name="twitter:description", content="{{ page_description }}")
|
|
||||||
|
|
||||||
meta(property="og:image", content="{{ page_header_image }}")
|
|
||||||
meta(name="twitter:image", content="{{ page_header_image }}")
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block page_title %}
|
| {% block page_title %}
|
||||||
@@ -41,47 +25,28 @@ meta(name="twitter:image", content="{{ page_header_image }}")
|
|||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block navigation_tabs %}
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_homepage(title) }}
|
||||||
| {% if title in ['courses', 'workshops'] %}
|
|
||||||
+nav-secondary
|
|
||||||
+nav-secondary-link(
|
|
||||||
class="{% if title == 'workshops' %}active{% endif %}",
|
|
||||||
href="{{ url_for('cloud.workshops') }}")
|
|
||||||
| Workshops
|
|
||||||
|
|
||||||
+nav-secondary-link(
|
|
||||||
class="{% if title == 'courses' %}active{% endif %}",
|
|
||||||
href="{{ url_for('cloud.courses') }}")
|
|
||||||
| Courses
|
|
||||||
|
|
||||||
+nav-secondary-link(
|
|
||||||
class="{% if title == 'gallery' %}active{% endif %}",
|
|
||||||
href="{{ url_for('projects.view', project_url='gallery') }}")
|
|
||||||
| Art Gallery
|
|
||||||
| {% endif %}
|
|
||||||
|
|
||||||
| {% endblock navigation_tabs %}
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
|
.container.py-4
|
||||||
|
+category_list_header('{{ page_title }}', '{{ page_description }}', '{{ request.url }}')
|
||||||
|
|
||||||
.container.pb-5
|
+card-deck()
|
||||||
.pt-4
|
|
||||||
h2.text-uppercase.font-weight-bold
|
|
||||||
| {{ page_title }}
|
|
||||||
.lead
|
|
||||||
| {{ page_header_text }}
|
|
||||||
|
|
||||||
hr.pb-2
|
|
||||||
|
|
||||||
+card-deck(3)
|
|
||||||
| {% for project in projects %}
|
| {% for project in projects %}
|
||||||
|
|
||||||
| {% if (project.status == 'published') or (project.status == 'pending' and current_user.is_authenticated) and project._id != config.MAIN_PROJECT_ID %}
|
| {% if (project.status == 'published') or (project.status == 'pending' and current_user.is_authenticated) and project._id != config.MAIN_PROJECT_ID %}
|
||||||
+card(data-url="{{ url_for('projects.view', project_url=project.url) }}", tabindex='{{ loop.index }}')
|
+card(
|
||||||
| {% if project.picture_header %}
|
class='js-project-go card-fade cursor-pointer mb-4',
|
||||||
a(href="{{ url_for('projects.view', project_url=project.url) }}")
|
style="min-width: 30%",
|
||||||
|
data-url="{{ url_for('projects.view', project_url=project.url) }}",
|
||||||
|
tabindex='{{ loop.index }}')
|
||||||
|
|
||||||
|
| {% if project.picture_16_9 %}
|
||||||
|
a.card-thumbnail(href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||||
img.card-img-top(
|
img.card-img-top(
|
||||||
src="{{ project.picture_header.thumbnail('l', api=api) }}", alt="{{ project.name }}")
|
alt="{{ project.name }}",
|
||||||
|
src="{{ project.picture_16_9.thumbnail('l', api=api) }}")
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
.card-body
|
.card-body
|
||||||
|
@@ -1,28 +1,33 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
| {% block page_title %}Services{% endblock %}
|
| {% from '_macros/_navigation.html' import navigation_homepage %}
|
||||||
| {% set title = 'services' %}
|
| {% from '_macros/_opengraph.html' import opengraph %}
|
||||||
|
include ../../../pillar/src/templates/mixins/components
|
||||||
include mixins/components
|
include mixins/components
|
||||||
|
|
||||||
| {% block og %}
|
| {% set title = 'services' %}
|
||||||
meta(property="og:type", content="website")
|
|
||||||
meta(property="og:url", content="{{ url_for('cloud.services') }}")
|
|
||||||
|
|
||||||
meta(property="og:title", content="Services - Blender Cloud")
|
| {% set page_title = 'Services' %}
|
||||||
meta(name="twitter:title", content="Services - Blender Cloud")
|
| {% set page_description = 'On Blender Cloud you can create and share personal projects, access our texture and HDRI library (or create your own), keep track of your production, manage your renders and much more!' %}
|
||||||
meta(property="og:description", content="Personal Projects · Blender Integration · Texture Browsing · Production Management")
|
| {% set page_header_image = url_for('static', filename='assets/img/backgrounds/background_services_16_9.jpg', _external=true) %}
|
||||||
meta(name="twitter:description", content="Personal Projects · Blender Integration · Texture Browsing · Production Management")
|
|
||||||
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_services.jpg')}}")
|
| {% block page_title %}{{ page_title }}{% endblock %}
|
||||||
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_services.jpg')}}")
|
|
||||||
|
| {% block og %}
|
||||||
|
| {{ opengraph(page_title, page_description, page_header_image, request.url) }}
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
|
| {% block navigation_tabs %}
|
||||||
|
| {{ navigation_homepage(title) }}
|
||||||
|
| {% endblock navigation_tabs %}
|
||||||
|
|
||||||
| {% block page_overlay %}
|
| {% block page_overlay %}
|
||||||
#page-overlay.video
|
#page-overlay.video
|
||||||
.video-embed
|
.video-embed
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
- var header_text = "On Blender Cloud you can create and share personal projects, access our texture and HDRI library (or create your own), keep track of your production, manage your renders and much more!";
|
.container.py-4
|
||||||
+jumbotron("Services", header_text, "{{ url_for('static', filename='assets/img/backgrounds/services_projects.jpg')}}")
|
+category_list_header('{{ page_title }}', '{{ page_description }}', '{{ request.url }}')
|
||||||
|
|
||||||
- var addon_text = 'Available through the <a href="{{ url_for(\'cloud.services\') }}#blender-cloud-add-on">Blender Cloud add-on</a>';
|
- var addon_text = 'Available through the <a href="{{ url_for(\'cloud.services\') }}#blender-cloud-add-on">Blender Cloud add-on</a>';
|
||||||
section#blender-cloud-add-on.page-card
|
section#blender-cloud-add-on.page-card
|
||||||
@@ -39,16 +44,19 @@ section#blender-cloud-add-on.page-card
|
|||||||
|
|
||||||
small Blender Cloud add-on requires Blender 2.78 or newer
|
small Blender Cloud add-on requires Blender 2.78 or newer
|
||||||
|
|
||||||
a.btn.btn-outline-success(
|
a.btn.btn-primary(
|
||||||
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-addon.zip")
|
href="/r/downloads/blender_cloud-latest-addon.zip",
|
||||||
|
title="Download Blender Cloud add-on")
|
||||||
i.pi-download
|
i.pi-download
|
||||||
| Download add-on <small>v</small> {{ config.BLENDER_CLOUD_ADDON_VERSION }}
|
| Download add-on <small>v</small> {{ config.BLENDER_CLOUD_ADDON_VERSION }}
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
href="/r/downloads/blender_cloud-latest-addon.zip",
|
||||||
|
title="Download Blender Cloud add-on")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/blender_cloud_addon_thumbnail.png')}}")
|
src="{{ url_for('static', filename='assets/img/features/blender_cloud_addon_thumbnail.png')}}")
|
||||||
|
|
||||||
section#blender-sync.page-card.right
|
section#blender-sync.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title Blender Sync
|
h2.page-card-title Blender Sync
|
||||||
.page-card-summary
|
.page-card-summary
|
||||||
@@ -61,39 +69,53 @@ section#blender-sync.page-card.right
|
|||||||
|
|
||||||
.tip !{addon_text}
|
.tip !{addon_text}
|
||||||
|
|
||||||
|
a.btn.btn-outline-primary(
|
||||||
|
href="/r/downloads/blender_cloud-latest-addon.zip",
|
||||||
|
title="Download Blender Cloud add-on")
|
||||||
|
i.pi-download
|
||||||
|
| Download add-on <small>v</small> {{ config.BLENDER_CLOUD_ADDON_VERSION }}
|
||||||
|
|
||||||
a.page-card-cta(
|
a.btn.btn-link(
|
||||||
href="https://cloud.blender.org/blog/introducing-blender-sync")
|
href="/blog/introducing-blender-sync",
|
||||||
|
title="Learn more about Blender Sync")
|
||||||
| Learn More
|
| Learn More
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
href="/blog/introducing-blender-sync",
|
||||||
|
title="Learn more about Blender Sync")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/sync_thumbnail.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/features/sync_thumbnail.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
section#texture-browser.page-card.right
|
section#texture-browser.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title Texture & HDRI Browser
|
h2.page-card-title Texture & HDRI Browser
|
||||||
.page-card-summary
|
.page-card-summary
|
||||||
p.
|
p.
|
||||||
Access the <a href="https://cloud.blender.org/p/textures/">Blender Cloud Textures</a>
|
Access the <a href="/p/textures/">Blender Cloud Textures</a>
|
||||||
library from within Blender using our exclusive add-on.
|
library from within Blender using our exclusive add-on.
|
||||||
Create, manage and share <em>your own</em> texture libraries!
|
Create, manage and share <em>your own</em> texture libraries!
|
||||||
|
|
||||||
.tip !{addon_text}
|
.tip !{addon_text}
|
||||||
|
|
||||||
a.btn.btn-outline-success.js-watch-video(
|
a.btn.btn-outline-primary.js-watch-video(
|
||||||
href="https://www.youtube.com/watch?v=-srXYv2Osjw",
|
href="https://www.youtube.com/watch?v=-srXYv2Osjw",
|
||||||
data-youtube-id="-srXYv2Osjw")
|
data-youtube-id="-srXYv2Osjw",
|
||||||
|
title="Watch video about Texture and HDRI Browser")
|
||||||
i.pi-play
|
i.pi-play
|
||||||
| Watch Video
|
| Watch Video
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
class="js-watch-video",
|
||||||
|
href="https://www.youtube.com/watch?v=-srXYv2Osjw",
|
||||||
|
data-youtube-id="-srXYv2Osjw",
|
||||||
|
title="Watch video about Texture and HDRI Browser")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/tex_library_thumbnail.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/features/tex_library_thumbnail.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
section#image-sharing.page-card.right
|
section#image-sharing.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title Image Sharing
|
h2.page-card-title Image Sharing
|
||||||
.page-card-summary
|
.page-card-summary
|
||||||
@@ -102,79 +124,96 @@ section#image-sharing.page-card.right
|
|||||||
|
|
||||||
.tip !{addon_text}
|
.tip !{addon_text}
|
||||||
|
|
||||||
a.btn.btn-outline-success.js-watch-video(
|
a.btn.btn-outline-primary.js-watch-video(
|
||||||
href="https://www.youtube.com/watch?v=yvtqeMBOAyk",
|
href="https://www.youtube.com/watch?v=yvtqeMBOAyk",
|
||||||
data-youtube-id="yvtqeMBOAyk")
|
data-youtube-id="yvtqeMBOAyk",
|
||||||
|
title="Watch video about Image Sharing")
|
||||||
i.pi-play
|
i.pi-play
|
||||||
| Watch Video
|
| Watch Video
|
||||||
|
|
||||||
a.page-card-cta.outline(
|
a.btn.btn-link(
|
||||||
href="https://cloud.blender.org/blog/introducing-image-sharing")
|
href="/blog/introducing-image-sharing",
|
||||||
|
title="Learn more about Image Sharing")
|
||||||
| Learn More
|
| Learn More
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
class="js-watch-video",
|
||||||
|
href="https://www.youtube.com/watch?v=yvtqeMBOAyk",
|
||||||
|
data-youtube-id="yvtqeMBOAyk",
|
||||||
|
title="Watch video about Image Sharing")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/image_sharing_thumbnail.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/features/image_sharing_thumbnail.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
section#projects.page-card.right
|
section#projects.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title Private Projects
|
h2.page-card-title Private Projects
|
||||||
.page-card-summary.
|
.page-card-summary.
|
||||||
Create and manage your own personal projects.
|
Create and manage your own personal projects.
|
||||||
Upload assets and collaborate with other Blender Cloud members.
|
Upload assets and collaborate with other Blender Cloud members.
|
||||||
|
|
||||||
a.page-card-cta(
|
a.btn.btn-link(
|
||||||
href="https://cloud.blender.org/blog/introducing-private-projects")
|
href="/blog/introducing-private-projects",
|
||||||
|
title="Learn more about Private Projects")
|
||||||
| Learn More
|
| Learn More
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
href="/blog/introducing-private-projects",
|
||||||
|
title="Learn more about Private Projects")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/projects_thumbnail.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/features/projects_thumbnail.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
section#attract.page-card.right
|
section#attract.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
| Attract
|
| Attract
|
||||||
.page-card-summary.
|
.page-card-summary.
|
||||||
Production-management software for your film, game, or commercial projects.
|
Production-management software for your film, game, or commercial projects.
|
||||||
|
|
||||||
a.btn.btn-outline-success.js-watch-video(
|
a.btn.btn-outline-primary.js-watch-video(
|
||||||
href="https://www.youtube.com/watch?v=b9x1rlyyt_o",
|
href="https://www.youtube.com/watch?v=b9x1rlyyt_o",
|
||||||
data-youtube-id="b9x1rlyyt_o")
|
data-youtube-id="b9x1rlyyt_o")
|
||||||
i.pi-play
|
i.pi-play
|
||||||
| Watch Video
|
| Watch Video
|
||||||
|
|
||||||
a.page-card-cta(
|
a.btn.btn-link(
|
||||||
href="https://cloud.blender.org/blog/attract-and-flamenco-public-beta",
|
href="/blog/attract-and-flamenco-public-beta",
|
||||||
title="Learn more about Attract")
|
title="Learn more about Attract")
|
||||||
| Learn More
|
| Learn More
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
href="/blog/attract-and-flamenco-public-beta",
|
||||||
|
title="Learn more about Attract")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/attract_thumbnail.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/features/attract_thumbnail.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
section#flamenco.page-card.right
|
section#flamenco.page-card
|
||||||
.page-card-side
|
.page-card-side
|
||||||
h2.page-card-title
|
h2.page-card-title
|
||||||
| Flamenco
|
| Flamenco
|
||||||
.page-card-summary.
|
.page-card-summary.
|
||||||
Take control of your computing infrastructure and get things done.
|
Take control of your computing infrastructure and get things done.
|
||||||
|
|
||||||
a.btn.btn-outline-success.js-watch-video(
|
a.btn.btn-outline-primary.js-watch-video(
|
||||||
href="https://www.youtube.com/watch?v=7cnFKhsM67Q",
|
href="https://www.youtube.com/watch?v=7cnFKhsM67Q",
|
||||||
data-youtube-id="7cnFKhsM67Q")
|
data-youtube-id="7cnFKhsM67Q")
|
||||||
i.pi-play
|
i.pi-play
|
||||||
| Watch Video
|
| Watch Video
|
||||||
|
|
||||||
a.page-card-cta(
|
a.btn.btn-link(
|
||||||
href="https://flamenco.io",
|
href="https://flamenco.io",
|
||||||
title="Learn more about Flamenco")
|
title="Learn more about Flamenco")
|
||||||
| Learn More
|
| Learn More
|
||||||
|
i.pi-angle-right
|
||||||
|
|
||||||
.page-card-side
|
a.page-card-side(
|
||||||
|
href="https://flamenco.io",
|
||||||
|
title="Learn more about Flamenco")
|
||||||
img(
|
img(
|
||||||
src="{{ url_for('static', filename='assets/img/features/flamenco_thumbnail.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/features/flamenco_thumbnail.jpg')}}")
|
||||||
|
|
||||||
@@ -190,7 +229,8 @@ section.page-card(
|
|||||||
.page-card-summary.text-white
|
.page-card-summary.text-white
|
||||||
| Join us for only $9.90/month!
|
| Join us for only $9.90/month!
|
||||||
|
|
||||||
a.page-card-cta(href="https://store.blender.org/product/membership/")
|
a.btn.btn-outline-light.px-3(href="https://store.blender.org/product/membership/")
|
||||||
|
i.pi-heart.mr-2
|
||||||
| Subscribe Now
|
| Subscribe Now
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:title", content="Blender Cloud Statistics")
|
meta(property="og:title", content="Blender Cloud Statistics")
|
||||||
meta(property="og:url", content="https://cloud.blender.org/stats")
|
meta(property="og:url", content="{{ request.url }}")
|
||||||
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_andy_hdribot_01.jpg')}}")
|
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_andy_hdribot_01.jpg')}}")
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
|
||||||
|
@@ -19,8 +19,8 @@ style.
|
|||||||
and the Blender Cloud. If you do not wish to be bound by this Agreement, do not use the
|
and the Blender Cloud. If you do not wish to be bound by this Agreement, do not use the
|
||||||
Blender Cloud Service.
|
Blender Cloud Service.
|
||||||
p.
|
p.
|
||||||
Blender Cloud is an activity of Blender Institute B.V. - Entrepotdok 57A - 1018 AD Amsterdam
|
Blender Cloud is an activity of Blender Institute B.V. - Buikslotermeerplein 161 -
|
||||||
- the Netherlands, contact: institute@blender.org.
|
1025 ET Amsterdam - the Netherlands, contact: institute@blender.org.
|
||||||
p.
|
p.
|
||||||
Blender Institute has been authorised by Stichting Blender Foundation to conduct these
|
Blender Institute has been authorised by Stichting Blender Foundation to conduct these
|
||||||
services on blender.org. Blender Institute is committed to comply to and support the goals of
|
services on blender.org. Blender Institute is committed to comply to and support the goals of
|
||||||
@@ -57,10 +57,10 @@ style.
|
|||||||
h2 Blender Cloud Membership fee
|
h2 Blender Cloud Membership fee
|
||||||
p.
|
p.
|
||||||
To register and activate a membership an additional charge will apply, including a minimum of
|
To register and activate a membership an additional charge will apply, including a minimum of
|
||||||
3 months of membership fees. Fees are: (Feb 23, 2014)
|
1 month of membership fees. Fees are: (Apr 09, 2020)
|
||||||
p.
|
p.
|
||||||
45 euro (59 USD), membership registration, which includes 3 months Cloud membership.
|
9.90 EUR/month for subscription with automatic renewal and 14.90 EUR/month for subscriptions
|
||||||
After that, 10 euro (13.50 USD), monthly membership fee
|
with manual renewal.
|
||||||
|
|
||||||
h2 Refund policy
|
h2 Refund policy
|
||||||
p.
|
p.
|
||||||
|
@@ -12,7 +12,7 @@ style(type='text/css').
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
| {% if user_cls == 'demo' %}
|
| {% if user_cls == 'demo' %}
|
||||||
h3.subscription-demo
|
h4.text-info.py-3
|
||||||
i.pi-heart-filled
|
i.pi-heart-filled
|
||||||
| You have a free account
|
| You have a free account
|
||||||
hr
|
hr
|
||||||
@@ -23,7 +23,7 @@ p.
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
| {% elif user_cls == 'outsider' %}
|
| {% elif user_cls == 'outsider' %}
|
||||||
h3.subscription-missing
|
h4.text-info.py-3
|
||||||
i.pi-info
|
i.pi-info
|
||||||
| You do not have an active subscription.
|
| You do not have an active subscription.
|
||||||
hr
|
hr
|
||||||
@@ -33,23 +33,23 @@ h3
|
|||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
| {% elif user_cls == 'subscriber-expired' %}
|
| {% elif user_cls == 'subscriber-expired' %}
|
||||||
| {% set renew_url = url_for('cloud.renew_subscription') %}
|
| {% set renew_url = url_for('cloud.renew_subscription') %}
|
||||||
h3.subscription-missing
|
h4.text-info.py-3
|
||||||
i.pi-info
|
i.pi-info
|
||||||
a(href="{{renew_url}}") Your subscription can be renewed
|
a(href="{{renew_url}}") Your subscription can be renewed
|
||||||
hr
|
hr
|
||||||
p.text-danger Subscription expired on: <strong>{{ expiration_date }}</strong>
|
p.text-danger Subscription expired on: <strong>{{ expiration_date }}</strong>
|
||||||
p
|
p
|
||||||
a.btn.btn-success(href="{{renew_url}}") Renew now
|
a.btn.btn-success.px-5(href="{{renew_url}}") Renew now
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
| {% elif current_user.has_cap('subscriber') %}
|
| {% elif current_user.has_cap('subscriber') %}
|
||||||
h3.subscription-active
|
h4.text-success.py-3
|
||||||
i.pi-check
|
i.pi-heart-filled.text-danger.pr-2
|
||||||
| Your subscription is active
|
| Your subscription is active
|
||||||
|
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
| {% if user_cls == 'subscriber' %}
|
| {% if user_cls == 'subscriber' %}
|
||||||
h4 Thank you for supporting us!
|
h5 Thank you for supporting Blender!
|
||||||
hr
|
hr
|
||||||
p Subscription expires on: <strong>{{ expiration_date }}</strong>
|
p Subscription expires on: <strong>{{ expiration_date }}</strong>
|
||||||
p
|
p
|
||||||
@@ -64,10 +64,9 @@ p Your organisation provides you with your subscription.
|
|||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
hr
|
hr
|
||||||
p
|
|
||||||
button#recheck_subscription.btn.btn-outline-secondary(onclick="javascript:recheck_subscription(this)") Re-check my subscription
|
|
||||||
|
|
||||||
hr
|
button#recheck_subscription.btn.btn-sm.btn-outline-secondary.px-5(onclick="javascript:recheck_subscription(this)")
|
||||||
|
| Re-check my Subscription
|
||||||
|
|
||||||
script.
|
script.
|
||||||
function recheck_subscription(button) {
|
function recheck_subscription(button) {
|
||||||
|
@@ -10,8 +10,9 @@
|
|||||||
{{ subfield.label }}
|
{{ subfield.label }}
|
||||||
| {% endfor %}
|
| {% endfor %}
|
||||||
|
|
||||||
.buttons
|
.py-3
|
||||||
button.btn.btn-outline-success.button-submit(type='submit')
|
button.btn.btn-outline-success.px-5.button-submit(type='submit')
|
||||||
i.pi-check
|
i.pi-check.pr-2
|
||||||
| Save Changes
|
| {{ _("Save Changes") }}
|
||||||
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
| {% extends 'users/settings/base.html' %}
|
| {% extends 'users/settings/base.html' %}
|
||||||
|
include ../../../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% block settings_sidebar_menu %}
|
| {% block settings_sidebar_menu %}
|
||||||
| {{ super() }}
|
| {{ super() }}
|
||||||
a(class="{% if title == 'emails' %}active{% endif %}",
|
+nav-secondary-link(
|
||||||
href="{{ url_for('settings.emails') }}")
|
class="{% if title == 'emails' %}active{% endif %}",
|
||||||
li
|
href="{{ url_for('settings.emails') }}")
|
||||||
i.pi-email
|
i.pr-3.pi-email
|
||||||
| Emails
|
span Emails
|
||||||
a(class="{% if title == 'billing' %}active{% endif %}",
|
+nav-secondary-link(
|
||||||
href="{{ url_for('settings.billing') }}")
|
class="{% if title == 'billing' %}active{% endif %}",
|
||||||
li
|
href="{{ url_for('settings.billing') }}")
|
||||||
i.pi-credit-card
|
i.pr-3.pi-credit-card
|
||||||
| Subscription
|
span Subscription
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
| {% extends 'layout.html' %}
|
| {% extends 'layout.html' %}
|
||||||
|
include ../../../pillar/src/templates/mixins/components
|
||||||
|
|
||||||
| {% block page_title %}Welcome{% endblock %}
|
| {% block page_title %}Welcome{% endblock %}
|
||||||
|
|
||||||
| {% set title = 'join' %}
|
| {% set title = 'join' %}
|
||||||
@@ -8,7 +10,7 @@
|
|||||||
|
|
||||||
| {% block og %}
|
| {% block og %}
|
||||||
meta(property="og:title", content="Blender Cloud - Open Content Production Platform")
|
meta(property="og:title", content="Blender Cloud - Open Content Production Platform")
|
||||||
meta(property="og:url", content="https://cloud.blender.org/")
|
meta(property="og:url", content="{{ request.url }}")
|
||||||
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_dweebs_01.jpg')}}")
|
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_dweebs_01.jpg')}}")
|
||||||
| {% endblock og %}
|
| {% endblock og %}
|
||||||
|
|
||||||
@@ -19,65 +21,123 @@ meta(property="og:image", content="{{ url_for('static', filename='assets/img/bac
|
|||||||
|
|
||||||
| {% block navigation_search %}{% endblock %}
|
| {% block navigation_search %}{% endblock %}
|
||||||
| {% block navigation_sections %}
|
| {% block navigation_sections %}
|
||||||
li
|
+nav-secondary-link(href="#pricing")
|
||||||
a.navbar-item(href="{{ url_for('main.main_blog') }}")
|
span Pricing
|
||||||
span Blog
|
|
||||||
li
|
|
||||||
a.navbar-item(href="#pricing")
|
|
||||||
span Pricing
|
|
||||||
| {% endblock navigation_sections %}
|
| {% endblock navigation_sections %}
|
||||||
|
|
||||||
| {% block navigation_user %}
|
| {% block navigation_user %}
|
||||||
li.pt-1.pr-1
|
li.pr-1
|
||||||
| {% if current_user.is_anonymous %}
|
| {% if current_user.is_anonymous %}
|
||||||
a.btn.btn-outline-success(href="{{ url_for('users.login', next='/') }}")
|
a.btn.btn-sm.btn-outline-primary.px-3(href="{{ url_for('users.login', next='/') }}")
|
||||||
| Log in and Explore
|
| Log in & Browse
|
||||||
| {% else %}
|
| {% else %}
|
||||||
a.btn.btn-outline-success(href="{{ url_for('main.homepage') }}")
|
a.btn.btn-sm.btn-outline-primary.px-3(href="{{ url_for('main.homepage') }}")
|
||||||
| Explore
|
| Browse
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
| {% endblock navigation_user %}
|
| {% endblock navigation_user %}
|
||||||
|
|
||||||
|
|
||||||
| {% block body %}
|
| {% block body %}
|
||||||
#page-container.join
|
#page-container.join
|
||||||
#page-header(
|
#page-header(
|
||||||
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_dweebs_01.jpg')}})")
|
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_spring_02.jpg')}})")
|
||||||
.container.wide-on-sm
|
.container.wide-on-sm
|
||||||
.row
|
.row
|
||||||
.col-sm-8.col-md-6.col-lg-6
|
.col-sm-8.col-md-6.col-lg-6
|
||||||
.page-title
|
.page-title
|
||||||
| Open content #[br] creation platform
|
| Blender Cloud
|
||||||
|
|
||||||
.page-title-summary.
|
.page-title-summary.
|
||||||
Support a world-class team of artists and developers,
|
Join the production platform as used daily by a world-class
|
||||||
access training, assets and use the same animation
|
team of artists and developers.
|
||||||
production pipeline used to make shorts like Agent 327 or Cosmos Laundromat
|
|
||||||
for your own projects.
|
|
||||||
|
|
||||||
#page-content
|
#page-content
|
||||||
section.page-card.intro
|
|
||||||
|
|
||||||
.page-card-side
|
|
||||||
h2.page-card-title A Different Kind of Cloud
|
|
||||||
.page-card-summary
|
|
||||||
p.
|
|
||||||
Blender Cloud is a fresh take on computer animation. We are an international group
|
|
||||||
of artists and developers #[strong creating short films with Blender].
|
|
||||||
We share what we make - assets, trainings and the finished films - as Open Content.
|
|
||||||
p.
|
|
||||||
With every project, we push Blender beyond its limits and contribute to
|
|
||||||
improve it for everyone's benefit.
|
|
||||||
|
|
||||||
.page-card-side
|
|
||||||
a.page-card-image(href="https://cloud.blender.org/p/caminandes-3/56bdacccc379cf00797160b0", target="_blank")
|
|
||||||
video(autoplay, loop)
|
|
||||||
source(src="{{ url_for('static', filename='assets/img/features/animation_review_01.mp4')}}")
|
|
||||||
|
|
||||||
|
|
||||||
section.page-card-header
|
section.page-card-header
|
||||||
a(href="{{ subscribe_url }}")
|
a(href="{{ url_for('cloud.courses') }}")
|
||||||
h2 Subscribe to Get
|
h2 Featured Content
|
||||||
|
|
||||||
|
.page-triplet-container.homepage
|
||||||
|
.row
|
||||||
|
.col-md-4
|
||||||
|
.triplet-card(data-url="/p/spring/")
|
||||||
|
.triplet-card-thumbnail
|
||||||
|
img(
|
||||||
|
alt="Spring Open Movie",
|
||||||
|
src="{{ url_for('static', filename='assets/img/features/open_movies_spring_02.jpg')}}")
|
||||||
|
.triplet-card-info
|
||||||
|
h3 Spring Open Movie
|
||||||
|
p.
|
||||||
|
Explore the production assets of the latest Blender Animation Studio short film.
|
||||||
|
a.triplet-cta(href="/p/spring/")
|
||||||
|
| LEARN MORE
|
||||||
|
|
||||||
|
.col-md-4
|
||||||
|
.triplet-card(data-url="/p/speed-sculpting/")
|
||||||
|
.triplet-card-thumbnail
|
||||||
|
img(
|
||||||
|
alt="Speed Sculpting Workshop",
|
||||||
|
src="{{ url_for('static', filename='assets/img/features/training_speed_sculpting.jpg')}}")
|
||||||
|
.triplet-card-info
|
||||||
|
h3 Speed Sculpting
|
||||||
|
p.
|
||||||
|
Learn speed sculpting in Blender 2.8 with Julien Kaspar.
|
||||||
|
a.triplet-cta(href="/p/speed-sculpting/")
|
||||||
|
| LEARN MORE
|
||||||
|
|
||||||
|
.col-md-4
|
||||||
|
.triplet-card(data-url="/p/grease-pencil-fundamentals/")
|
||||||
|
.triplet-card-thumbnail
|
||||||
|
img(
|
||||||
|
alt="Grease Pencil Fundamentals",
|
||||||
|
src="{{ url_for('static', filename='assets/img/features/training_grease_pencil.jpg')}}")
|
||||||
|
.triplet-card-info
|
||||||
|
h3 Grease Pencil
|
||||||
|
p.
|
||||||
|
Become proficient with the latest Blender 2.8 Grease Pencil.
|
||||||
|
a.triplet-cta(href="/p/grease-pencil-fundamentals/")
|
||||||
|
| LEARN MORE
|
||||||
|
|
||||||
|
.row.training-other
|
||||||
|
.col-md-10.col-md-offset-1
|
||||||
|
p.
|
||||||
|
Other training:
|
||||||
|
#[a(href="/p/toon-character-workflow/") Toon Character Workflow],
|
||||||
|
#[a(href="/p/3d-printing/") Blender for 3D Printing],
|
||||||
|
#[a(href="/p/game-asset-creation/") Game Asset Creation],
|
||||||
|
#[a(href="/p/blenderella/") Character Modeling],
|
||||||
|
#[a(href="/p/character-animation/") Character Animation],
|
||||||
|
#[a(href="/p/humane-rigging/") Introduction] and
|
||||||
|
#[a(href="/p/blenrig/") Advanced Rigging],
|
||||||
|
#[a(href="/p/track-match-2/") VFX Workflow],
|
||||||
|
#[a(href="/p/creature-factory-2/") Creature] and
|
||||||
|
#[a(href="/p/venoms-lab-2/") Cartoon Character creation],
|
||||||
|
#[a(href="/p/chaos-evolution/") Advanced]
|
||||||
|
#[a(href="/p/blend-and-paint/") Digital Painting] and
|
||||||
|
#[a(href="{{ url_for('cloud.courses') }}") much more]!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//section.page-card.intro
|
||||||
|
//
|
||||||
|
// .page-card-side
|
||||||
|
// h2.page-card-title A Different Kind of Cloud
|
||||||
|
// .page-card-summary
|
||||||
|
// p.
|
||||||
|
// Blender Cloud is a fresh take on computer animation. We are an international group
|
||||||
|
// of artists and developers #[strong creating short films with Blender].
|
||||||
|
// We share what we make - assets, trainings and the finished films - as Open Content.
|
||||||
|
// p.
|
||||||
|
// With every project, we push Blender beyond its limits and contribute to
|
||||||
|
// improve it for everyone's benefit.
|
||||||
|
//
|
||||||
|
// .page-card-side
|
||||||
|
// a.page-card-image(href="/p/caminandes-3/56bdacccc379cf00797160b0", target="_blank")
|
||||||
|
// video(autoplay, loop)
|
||||||
|
// source(src="{{ url_for('static', filename='assets/img/features/animation_review_01.mp4')}}")
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//section.page-card-header
|
||||||
|
// a(href="{{ subscribe_url }}")
|
||||||
|
// h2 Subscribe to Get
|
||||||
|
|
||||||
|
|
||||||
section.page-card.training.right.light(
|
section.page-card.training.right.light(
|
||||||
@@ -92,7 +152,7 @@ li.pt-1.pr-1
|
|||||||
Access high quality content, organized in
|
Access high quality content, organized in
|
||||||
#[a(href="{{ url_for('cloud.courses') }}") classes],
|
#[a(href="{{ url_for('cloud.courses') }}") classes],
|
||||||
#[a(href="{{ url_for('cloud.workshops') }}") workshop] and the
|
#[a(href="{{ url_for('cloud.workshops') }}") workshop] and the
|
||||||
#[a(href="https://cloud.blender.org/p/gallery") art gallery],
|
#[a(href="/p/gallery") art gallery],
|
||||||
a curated artwork selection, where you can open a
|
a curated artwork selection, where you can open a
|
||||||
.blend file and see how it was made.
|
.blend file and see how it was made.
|
||||||
|
|
||||||
@@ -108,75 +168,12 @@ li.pt-1.pr-1
|
|||||||
| See #[a.learn(href="{{ url_for('cloud.courses') }}") Courses] & #[a.learn(href="{{ url_for('cloud.courses') }}") Workshops]
|
| See #[a.learn(href="{{ url_for('cloud.courses') }}") Courses] & #[a.learn(href="{{ url_for('cloud.courses') }}") Workshops]
|
||||||
|
|
||||||
.page-card-side
|
.page-card-side
|
||||||
a.page-card-image(href="https://cloud.blender.org/p/scripting-for-artists/")
|
a.page-card-image(href="/p/scripting-for-artists/")
|
||||||
img(
|
img(
|
||||||
alt="Sybren teaches Python Scripting with Blender",
|
alt="Sybren teaches Python Scripting with Blender",
|
||||||
src="{{ url_for('static', filename='assets/img/backgrounds/background_sybren_01.jpg')}}")
|
src="{{ url_for('static', filename='assets/img/backgrounds/background_sybren_01.jpg')}}")
|
||||||
|
|
||||||
|
|
||||||
section.page-card-header
|
|
||||||
a(href="{{ url_for('cloud.courses') }}")
|
|
||||||
h2 Featured Content
|
|
||||||
|
|
||||||
.page-triplet-container.homepage
|
|
||||||
.row
|
|
||||||
.col-md-4
|
|
||||||
.triplet-card(data-url="https://cloud.blender.org/p/minecraft-animation-workshop/")
|
|
||||||
.triplet-card-thumbnail
|
|
||||||
img(
|
|
||||||
alt="Textures",
|
|
||||||
src="{{ url_for('static', filename='assets/img/features/training_minecraft_animation.jpg')}}")
|
|
||||||
.triplet-card-info
|
|
||||||
h3 Minecraft Animation
|
|
||||||
p.
|
|
||||||
Learn how to make animations with this workshop by Dillon Gu.
|
|
||||||
a.triplet-cta(href="https://cloud.blender.org/p/minecraft-animation-workshop/")
|
|
||||||
| LEARN MORE
|
|
||||||
|
|
||||||
.col-md-4
|
|
||||||
.triplet-card(data-url="https://cloud.blender.org/p/motion-graphics/")
|
|
||||||
.triplet-card-thumbnail
|
|
||||||
img(
|
|
||||||
alt="HDRI",
|
|
||||||
src="{{ url_for('static', filename='assets/img/features/training_motion_graphics.jpg')}}")
|
|
||||||
.triplet-card-info
|
|
||||||
h3 Motion Graphics
|
|
||||||
p.
|
|
||||||
A comprehensive guide to motion graphics techniques using Blender.
|
|
||||||
a.triplet-cta(href="https://cloud.blender.org/p/motion-graphics/")
|
|
||||||
| LEARN MORE
|
|
||||||
|
|
||||||
.col-md-4
|
|
||||||
.triplet-card(data-url="https://cloud.blender.org/p/gallery")
|
|
||||||
.triplet-card-thumbnail
|
|
||||||
img(
|
|
||||||
alt="Characters",
|
|
||||||
src="{{ url_for('static', filename='assets/img/features/training_bob_forest.jpg')}}")
|
|
||||||
.triplet-card-info
|
|
||||||
h3 Art Walk-throughs
|
|
||||||
p.
|
|
||||||
Follow the creative process and techniques behind stunning artwork.
|
|
||||||
a.triplet-cta(href="https://cloud.blender.org/p/gallery")
|
|
||||||
| LEARN MORE
|
|
||||||
|
|
||||||
.row.training-other
|
|
||||||
.col-md-10.col-md-offset-1
|
|
||||||
p.
|
|
||||||
Other training:
|
|
||||||
#[a(href="https://cloud.blender.org/p/toon-character-workflow/") Toon Character Workflow],
|
|
||||||
#[a(href="https://cloud.blender.org/p/3d-printing/") Blender for 3D Printing],
|
|
||||||
#[a(href="https://cloud.blender.org/p/game-asset-creation/") Game Asset Creation],
|
|
||||||
#[a(href="https://cloud.blender.org/p/blenderella/") Character Modeling],
|
|
||||||
#[a(href="https://cloud.blender.org/p/character-animation/") Character Animation],
|
|
||||||
#[a(href="https://cloud.blender.org/p/humane-rigging/") Introduction] and
|
|
||||||
#[a(href="https://cloud.blender.org/p/blenrig/") Advanced Rigging],
|
|
||||||
#[a(href="https://cloud.blender.org/p/track-match-2/") VFX Workflow],
|
|
||||||
#[a(href="https://cloud.blender.org/p/creature-factory-2/") Creature] and
|
|
||||||
#[a(href="https://cloud.blender.org/p/venoms-lab-2/") Cartoon Character creation],
|
|
||||||
#[a(href="https://cloud.blender.org/p/chaos-evolution/") Advanced]
|
|
||||||
#[a(href="https://cloud.blender.org/p/blend-and-paint/") Digital Painting] and
|
|
||||||
#[a(href="{{ url_for('cloud.courses') }}") much more]!
|
|
||||||
|
|
||||||
|
|
||||||
section.page-card.open-movies.light(
|
section.page-card.open-movies.light(
|
||||||
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_blue_01.jpg')}})")
|
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_blue_01.jpg')}})")
|
||||||
@@ -215,7 +212,7 @@ li.pt-1.pr-1
|
|||||||
.page-triplet-container.homepage
|
.page-triplet-container.homepage
|
||||||
.row
|
.row
|
||||||
.col-md-4
|
.col-md-4
|
||||||
.triplet-card(data-url="https://cloud.blender.org/p/hero/")
|
.triplet-card(data-url="/p/hero/")
|
||||||
.triplet-card-thumbnail
|
.triplet-card-thumbnail
|
||||||
img(
|
img(
|
||||||
alt="Hero",
|
alt="Hero",
|
||||||
@@ -224,11 +221,11 @@ li.pt-1.pr-1
|
|||||||
h3 Hero
|
h3 Hero
|
||||||
p.
|
p.
|
||||||
The first ever Grease Pencil open movie made with Blender 2.8
|
The first ever Grease Pencil open movie made with Blender 2.8
|
||||||
a.triplet-cta(href="https://cloud.blender.org/p/hero/")
|
a.triplet-cta(href="/p/hero/")
|
||||||
| LEARN MORE
|
| LEARN MORE
|
||||||
|
|
||||||
.col-md-4
|
.col-md-4
|
||||||
.triplet-card(data-url="https://cloud.blender.org/p/spring/")
|
.triplet-card(data-url="/p/spring/")
|
||||||
.triplet-card-thumbnail
|
.triplet-card-thumbnail
|
||||||
img(
|
img(
|
||||||
alt="Spring",
|
alt="Spring",
|
||||||
@@ -237,11 +234,11 @@ li.pt-1.pr-1
|
|||||||
h3 Spring
|
h3 Spring
|
||||||
p.
|
p.
|
||||||
A poetic fantasy film. #[br] A stunning visual journey.
|
A poetic fantasy film. #[br] A stunning visual journey.
|
||||||
a.triplet-cta(href="https://cloud.blender.org/p/spring/")
|
a.triplet-cta(href="/p/spring/")
|
||||||
| LEARN MORE
|
| LEARN MORE
|
||||||
|
|
||||||
.col-md-4
|
.col-md-4
|
||||||
.triplet-card(data-url="https://cloud.blender.org/p/caminandes-3/")
|
.triplet-card(data-url="/p/caminandes-3/")
|
||||||
.triplet-card-thumbnail
|
.triplet-card-thumbnail
|
||||||
img(
|
img(
|
||||||
alt="Caminandes",
|
alt="Caminandes",
|
||||||
@@ -250,21 +247,21 @@ li.pt-1.pr-1
|
|||||||
h3 Caminandes
|
h3 Caminandes
|
||||||
p.
|
p.
|
||||||
Follow the adventures of Koro through the Patagonian pampas.
|
Follow the adventures of Koro through the Patagonian pampas.
|
||||||
a.triplet-cta(href="https://cloud.blender.org/p/caminandes-3/")
|
a.triplet-cta(href="/p/caminandes-3/")
|
||||||
| LEARN MORE
|
| LEARN MORE
|
||||||
|
|
||||||
.row.training-other
|
.row.training-other
|
||||||
.col-md-10.col-md-offset-1
|
.col-md-10.col-md-offset-1
|
||||||
p.
|
p.
|
||||||
Other open movies:
|
Other open movies:
|
||||||
#[a(href="https://cloud.blender.org/p/elephants-dream/") Elephants Dream],
|
#[a(href="/p/elephants-dream/") Elephants Dream],
|
||||||
#[a(href="https://cloud.blender.org/p/big-buck-bunny/") Big Buck Bunny],
|
#[a(href="/p/big-buck-bunny/") Big Buck Bunny],
|
||||||
#[a(href="https://cloud.blender.org/p/sintel/") Sintel],
|
#[a(href="/p/sintel/") Sintel],
|
||||||
#[a(href="https://cloud.blender.org/p/tears-of-steel/") Tears of Steel],
|
#[a(href="/p/tears-of-steel/") Tears of Steel],
|
||||||
#[a(href="https://cloud.blender.org/p/cosmos-laundromat/") Cosmos Laundromat],
|
#[a(href="/p/cosmos-laundromat/") Cosmos Laundromat],
|
||||||
#[a(href="https://cloud.blender.org/p/glass-half/") Glass Half],
|
#[a(href="/p/glass-half/") Glass Half],
|
||||||
#[a(href="https://cloud.blender.org/p/dailydweebs/") The Daily Dweebs],
|
#[a(href="/p/dailydweebs/") The Daily Dweebs],
|
||||||
#[a(href="https://cloud.blender.org/p/agent-327/") Agent 327]
|
#[a(href="/p/agent-327/") Agent 327]
|
||||||
and #[a(href="{{ url_for('cloud.open_projects') }}") more]
|
and #[a(href="{{ url_for('cloud.open_projects') }}") more]
|
||||||
|
|
||||||
|
|
||||||
|
BIN
static/assets/img/backgrounds/background_services_16_9.jpg
Normal file
BIN
static/assets/img/backgrounds/background_services_16_9.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
BIN
static/assets/img/backgrounds/background_spring_01.jpg
Normal file
BIN
static/assets/img/backgrounds/background_spring_01.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 601 KiB |
BIN
static/assets/img/backgrounds/background_spring_02.jpg
Normal file
BIN
static/assets/img/backgrounds/background_spring_02.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 138 KiB |
BIN
static/assets/img/features/coffee_run_01.jpg
Normal file
BIN
static/assets/img/features/coffee_run_01.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
static/assets/img/features/coffee_run_02.jpg
Normal file
BIN
static/assets/img/features/coffee_run_02.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
static/assets/img/features/gallery_01.jpg
Normal file
BIN
static/assets/img/features/gallery_01.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user