Compare commits
48 Commits
wip-open-p
...
wip-redesi
Author | SHA1 | Date | |
---|---|---|---|
0baf5b38c3 | |||
858a75af8d | |||
6b1a5e24e8 | |||
1500e20291 | |||
d347534fea | |||
4546469d37 | |||
b0d8da821f | |||
1821bb6b7d | |||
278eebd235 | |||
2777c37085 | |||
9c2ded79dd | |||
b4acfb89fa | |||
33bd2c5880 | |||
76338b4568 | |||
7405e198eb | |||
2332bc0960 | |||
ac3a599bb6 | |||
814275fc95 | |||
46b0d6d663 | |||
40f79af49d | |||
84608500b9 | |||
819300f954 | |||
b569829343 | |||
c35fb6202b | |||
d0ff519980 | |||
6ff4ee8fa1 | |||
b5535a8773 | |||
2ded541955 | |||
3965061bde | |||
5238e2c26d | |||
466adabbb0 | |||
5fb40eb32b | |||
9f380751f5 | |||
49075cbc60 | |||
81848c2c44 | |||
9ee7b742ab | |||
58c33074c3 | |||
756427b34e | |||
7e06212cd5 | |||
ef3912b647 | |||
151484dee3 | |||
bec1f209ba | |||
0e14bdd09f | |||
ce6df542cc | |||
530302b74f | |||
1bfb6cd2f6 | |||
53b6210531 | |||
aeaa03ed80 |
37
gulpfile.js
37
gulpfile.js
@@ -12,7 +12,7 @@ var pug = require('gulp-pug');
|
||||
var rename = require('gulp-rename');
|
||||
var sass = require('gulp-sass');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var uglify = require('gulp-uglify');
|
||||
var uglify = require('gulp-uglify-es').default;
|
||||
|
||||
var enabled = {
|
||||
uglify: argv.production,
|
||||
@@ -21,6 +21,7 @@ var enabled = {
|
||||
prettyPug: !argv.production,
|
||||
cachify: !argv.production,
|
||||
cleanup: argv.production,
|
||||
chmod: argv.production,
|
||||
};
|
||||
|
||||
var destination = {
|
||||
@@ -29,6 +30,10 @@ var destination = {
|
||||
js: 'pillar/web/static/assets/js',
|
||||
}
|
||||
|
||||
var source = {
|
||||
bootstrap: 'node_modules/bootstrap/',
|
||||
popper: 'node_modules/popper.js/'
|
||||
}
|
||||
|
||||
/* CSS */
|
||||
gulp.task('styles', function() {
|
||||
@@ -67,7 +72,7 @@ gulp.task('scripts', function() {
|
||||
.pipe(gulpif(enabled.uglify, uglify()))
|
||||
.pipe(rename({suffix: '.min'}))
|
||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
||||
.pipe(chmod(644))
|
||||
.pipe(gulpif(enabled.chmod, chmod(644)))
|
||||
.pipe(gulp.dest(destination.js))
|
||||
.pipe(gulpif(argv.livereload, livereload()));
|
||||
});
|
||||
@@ -82,7 +87,7 @@ gulp.task('scripts_concat_tutti', function() {
|
||||
.pipe(concat("tutti.min.js"))
|
||||
.pipe(gulpif(enabled.uglify, uglify()))
|
||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
||||
.pipe(chmod(644))
|
||||
.pipe(gulpif(enabled.chmod, chmod(644)))
|
||||
.pipe(gulp.dest(destination.js))
|
||||
.pipe(gulpif(argv.livereload, livereload()));
|
||||
});
|
||||
@@ -94,7 +99,30 @@ gulp.task('scripts_concat_markdown', function() {
|
||||
.pipe(concat("markdown.min.js"))
|
||||
.pipe(gulpif(enabled.uglify, uglify()))
|
||||
.pipe(gulpif(enabled.maps, sourcemaps.write(".")))
|
||||
.pipe(chmod(644))
|
||||
.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()));
|
||||
});
|
||||
@@ -137,4 +165,5 @@ gulp.task('default', tasks.concat([
|
||||
'scripts',
|
||||
'scripts_concat_tutti',
|
||||
'scripts_concat_markdown',
|
||||
'scripts_concat_bootstrap',
|
||||
]));
|
||||
|
5452
package-lock.json
generated
Normal file
5452
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -4,23 +4,28 @@
|
||||
"author": "Blender Institute",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/armadillica/pillar.git"
|
||||
"url": "git://git.blender.org/pillar.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "~3.9.1",
|
||||
"gulp-autoprefixer": "~2.3.1",
|
||||
"gulp-cached": "~1.1.0",
|
||||
"gulp-chmod": "~1.3.0",
|
||||
"gulp-concat": "~2.6.0",
|
||||
"gulp-if": "^2.0.1",
|
||||
"gulp-git": "~2.4.2",
|
||||
"gulp-livereload": "~3.8.1",
|
||||
"gulp-plumber": "~1.1.0",
|
||||
"gulp-pug": "~3.2.0",
|
||||
"gulp-rename": "~1.2.2",
|
||||
"gulp-sass": "~2.3.1",
|
||||
"gulp-sourcemaps": "~1.6.0",
|
||||
"gulp-uglify": "~1.5.3",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-autoprefixer": "^6.0.0",
|
||||
"gulp-cached": "^1.1.1",
|
||||
"gulp-chmod": "^2.0.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-git": "^2.8.0",
|
||||
"gulp-livereload": "^4.0.0",
|
||||
"gulp-plumber": "^1.2.0",
|
||||
"gulp-pug": "^4.0.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-sass": "^4.0.1",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-uglify-es": "^1.0.4",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^4.1.3",
|
||||
"jquery": "^3.3.1",
|
||||
"popper.js": "^1.14.4"
|
||||
}
|
||||
}
|
||||
|
@@ -184,7 +184,6 @@ class PillarServer(BlinkerCompatibleEve):
|
||||
|
||||
if not self.config.get('STATIC_FILE_HASH'):
|
||||
self.log.warning('STATIC_FILE_HASH is empty, generating random one')
|
||||
f = open('/data/git/blender-cloud/config_local.py', 'a')
|
||||
h = re.sub(r'[_.~-]', '', secrets.token_urlsafe())[:8]
|
||||
self.config['STATIC_FILE_HASH'] = h
|
||||
|
||||
|
@@ -47,13 +47,6 @@ def store_subclient_token():
|
||||
'subclient_user_id': str(db_user['_id'])}), status
|
||||
|
||||
|
||||
def blender_id_endpoint():
|
||||
"""Gets the endpoint for the authentication API. If the env variable
|
||||
is defined, it's possible to override the (default) production address.
|
||||
"""
|
||||
return current_app.config['BLENDER_ID_ENDPOINT'].rstrip('/')
|
||||
|
||||
|
||||
def validate_create_user(blender_id_user_id, token, oauth_subclient_id):
|
||||
"""Validates a user against Blender ID, creating the user in our database.
|
||||
|
||||
@@ -121,13 +114,13 @@ def validate_token(user_id, token, oauth_subclient_id):
|
||||
# We only want to accept Blender Cloud tokens.
|
||||
payload['client_id'] = current_app.config['OAUTH_CREDENTIALS']['blender-id']['id']
|
||||
|
||||
url = '{0}/u/validate_token'.format(blender_id_endpoint())
|
||||
url = '{0}/u/validate_token'.format(current_app.config['BLENDER_ID_ENDPOINT'])
|
||||
log.debug('POSTing to %r', url)
|
||||
|
||||
# Retry a few times when POSTing to BlenderID fails.
|
||||
# Source: http://stackoverflow.com/a/15431343/875379
|
||||
s = requests.Session()
|
||||
s.mount(blender_id_endpoint(), HTTPAdapter(max_retries=5))
|
||||
s.mount(current_app.config['BLENDER_ID_ENDPOINT'], HTTPAdapter(max_retries=5))
|
||||
|
||||
# POST to Blender ID, handling errors as negative verification results.
|
||||
try:
|
||||
@@ -225,7 +218,7 @@ def fetch_blenderid_user() -> dict:
|
||||
|
||||
my_log = log.getChild('fetch_blenderid_user')
|
||||
|
||||
bid_url = '%s/api/user' % blender_id_endpoint()
|
||||
bid_url = '%s/api/user' % current_app.config['BLENDER_ID_ENDPOINT']
|
||||
my_log.debug('Fetching user info from %s', bid_url)
|
||||
|
||||
credentials = current_app.config['OAUTH_CREDENTIALS']['blender-id']
|
||||
@@ -270,7 +263,7 @@ def setup_app(app, url_prefix):
|
||||
def switch_user_url(next_url: str) -> str:
|
||||
from urllib.parse import quote
|
||||
|
||||
base_url = '%s/switch' % blender_id_endpoint()
|
||||
base_url = '%s/switch' % current_app.config['BLENDER_ID_ENDPOINT']
|
||||
if next_url:
|
||||
return '%s?next=%s' % (base_url, quote(next_url))
|
||||
return base_url
|
||||
|
@@ -40,6 +40,51 @@ attachments_embedded_schema = {
|
||||
},
|
||||
}
|
||||
|
||||
# TODO (fsiddi) reference this schema in all node_types that allow ratings
|
||||
ratings_embedded_schema = {
|
||||
'type': 'dict',
|
||||
# Total count of positive ratings (updated at every rating action)
|
||||
'schema': {
|
||||
'positive': {
|
||||
'type': 'integer',
|
||||
},
|
||||
# Total count of negative ratings (updated at every rating action)
|
||||
'negative': {
|
||||
'type': 'integer',
|
||||
},
|
||||
# Collection of ratings, keyed by user
|
||||
'ratings': {
|
||||
'type': 'list',
|
||||
'schema': {
|
||||
'type': 'dict',
|
||||
'schema': {
|
||||
'user': {
|
||||
'type': 'objectid',
|
||||
'data_relation': {
|
||||
'resource': 'users',
|
||||
'field': '_id',
|
||||
'embeddable': False
|
||||
}
|
||||
},
|
||||
'is_positive': {
|
||||
'type': 'boolean'
|
||||
},
|
||||
# Weight of the rating based on user rep and the context.
|
||||
# Currently we have the following weights:
|
||||
# - 1 auto null
|
||||
# - 2 manual null
|
||||
# - 3 auto valid
|
||||
# - 4 manual valid
|
||||
'weight': {
|
||||
'type': 'integer'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'hot': {'type': 'float'},
|
||||
},
|
||||
}
|
||||
|
||||
# Import after defining the common embedded schemas, to prevent dependency cycles.
|
||||
from pillar.api.node_types.asset import node_type_asset
|
||||
from pillar.api.node_types.blog import node_type_blog
|
||||
|
87
pillar/api/utils/rating.py
Normal file
87
pillar/api/utils/rating.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# These functions come from Reddit
|
||||
# https://github.com/reddit/reddit/blob/master/r2/r2/lib/db/_sorts.pyx
|
||||
|
||||
# Additional resources
|
||||
# http://www.redditblog.com/2009/10/reddits-new-comment-sorting-system.html
|
||||
# http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
|
||||
# http://amix.dk/blog/post/19588
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from math import log
|
||||
from math import sqrt
|
||||
|
||||
epoch = datetime(1970, 1, 1, 0, 0, 0, 0, timezone.utc)
|
||||
|
||||
|
||||
def epoch_seconds(date):
|
||||
"""Returns the number of seconds from the epoch to date."""
|
||||
td = date - epoch
|
||||
return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)
|
||||
|
||||
|
||||
def score(ups, downs):
|
||||
return ups - downs
|
||||
|
||||
|
||||
def hot(ups, downs, date):
|
||||
"""The hot formula. Reddit's hot ranking uses the logarithm function to
|
||||
weight the first votes higher than the rest.
|
||||
The first 10 upvotes have the same weight as the next 100 upvotes which
|
||||
have the same weight as the next 1000, etc.
|
||||
|
||||
Dillo authors: we modified the formula to give more weight to negative
|
||||
votes when an entry is controversial.
|
||||
|
||||
TODO: make this function more dynamic so that different defaults can be
|
||||
specified depending on the item that is being rated.
|
||||
"""
|
||||
|
||||
s = score(ups, downs)
|
||||
order = log(max(abs(s), 1), 10)
|
||||
sign = 1 if s > 0 else -1 if s < 0 else 0
|
||||
seconds = epoch_seconds(date) - 1134028003
|
||||
base_hot = round(sign * order + seconds / 45000, 7)
|
||||
|
||||
if downs > 1:
|
||||
rating_delta = 100 * (downs - ups) / downs
|
||||
if rating_delta < 25:
|
||||
# The post is controversial
|
||||
return base_hot
|
||||
base_hot = base_hot - (downs * 6)
|
||||
|
||||
return base_hot
|
||||
|
||||
|
||||
def _confidence(ups, downs):
|
||||
n = ups + downs
|
||||
|
||||
if n == 0:
|
||||
return 0
|
||||
|
||||
z = 1.0 #1.0 = 85%, 1.6 = 95%
|
||||
phat = float(ups) / n
|
||||
return sqrt(phat+z*z/(2*n)-z*((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
|
||||
|
||||
|
||||
def confidence(ups, downs):
|
||||
if ups + downs == 0:
|
||||
return 0
|
||||
else:
|
||||
return _confidence(ups, downs)
|
||||
|
||||
|
||||
def update_hot(document):
|
||||
"""Update the hotness of a document given its current ratings.
|
||||
|
||||
We expect the document to implement the ratings_embedded_schema in
|
||||
a 'ratings' property.
|
||||
"""
|
||||
|
||||
dt = document['_created']
|
||||
dt = dt.replace(tzinfo=timezone.utc)
|
||||
|
||||
document['properties']['ratings']['hot'] = hot(
|
||||
document['properties']['ratings']['positive'],
|
||||
document['properties']['ratings']['negative'],
|
||||
dt,
|
||||
)
|
@@ -131,16 +131,15 @@ class BlenderIdSignIn(OAuthSignIn):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
base_url = current_app.config['OAUTH_CREDENTIALS']['blender-id'].get(
|
||||
'base_url', 'https://www.blender.org/id/')
|
||||
base_url = current_app.config['BLENDER_ID_ENDPOINT']
|
||||
|
||||
self.service = OAuth2Service(
|
||||
name='blender-id',
|
||||
client_id=self.consumer_id,
|
||||
client_secret=self.consumer_secret,
|
||||
authorize_url='%soauth/authorize' % base_url,
|
||||
access_token_url='%soauth/token' % base_url,
|
||||
base_url='%sapi/' % base_url
|
||||
authorize_url='%s/oauth/authorize' % base_url,
|
||||
access_token_url='%s/oauth/token' % base_url,
|
||||
base_url='%s/api/' % base_url
|
||||
)
|
||||
|
||||
def authorize(self):
|
||||
|
@@ -32,7 +32,7 @@ SECRET_KEY = ''
|
||||
AUTH_TOKEN_HMAC_KEY = b''
|
||||
|
||||
# Authentication settings
|
||||
BLENDER_ID_ENDPOINT = 'http://blender-id:8000/'
|
||||
BLENDER_ID_ENDPOINT = 'http://id.local:8000'
|
||||
|
||||
CDN_USE_URL_SIGNING = True
|
||||
CDN_SERVICE_DOMAIN_PROTOCOL = 'https'
|
||||
@@ -124,9 +124,8 @@ BLENDER_ID_USER_INFO_TOKEN = '-set-in-config-local-'
|
||||
# Example entry:
|
||||
# OAUTH_CREDENTIALS = {
|
||||
# 'blender-id': {
|
||||
# 'id': 'CLOUD-OF-SNOWFLAKES-43',
|
||||
# 'id': 'CLOUD-OF-SNOWFLAKES-42',
|
||||
# 'secret': 'thesecret',
|
||||
# 'base_url': 'http://blender-id:8000/'
|
||||
# }
|
||||
# }
|
||||
# OAuth providers are defined in pillar.auth.oauth
|
||||
|
@@ -45,11 +45,15 @@ ALLOWED_STYLES = [
|
||||
def markdown(s: str) -> str:
|
||||
commented_shortcodes = shortcodes.comment_shortcodes(s)
|
||||
tainted_html = CommonMark.commonmark(commented_shortcodes)
|
||||
safe_html = bleach.clean(tainted_html,
|
||||
tags=ALLOWED_TAGS,
|
||||
|
||||
# Create a Cleaner that supports parsing of bare links (see filters).
|
||||
cleaner = bleach.Cleaner(tags=ALLOWED_TAGS,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
styles=ALLOWED_STYLES,
|
||||
strip_comments=False)
|
||||
strip_comments=False,
|
||||
filters=[bleach.linkifier.LinkifyFilter])
|
||||
|
||||
safe_html = cleaner.clean(tainted_html)
|
||||
return safe_html
|
||||
|
||||
|
||||
|
@@ -33,18 +33,57 @@ log = logging.getLogger(__name__)
|
||||
def shortcode(name: str):
|
||||
"""Class decorator for shortcodes."""
|
||||
|
||||
def decorator(cls):
|
||||
assert hasattr(cls, '__call__'), '@shortcode should be used on callables.'
|
||||
if isinstance(cls, type):
|
||||
instance = cls()
|
||||
def decorator(decorated):
|
||||
assert hasattr(decorated, '__call__'), '@shortcode should be used on callables.'
|
||||
if isinstance(decorated, type):
|
||||
as_callable = decorated()
|
||||
else:
|
||||
instance = cls
|
||||
shortcodes.register(name)(instance)
|
||||
return cls
|
||||
as_callable = decorated
|
||||
shortcodes.register(name)(as_callable)
|
||||
return decorated
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class capcheck:
|
||||
"""Decorator for shortcodes.
|
||||
|
||||
On call, check for capabilities before calling the function. If the user does not
|
||||
have a capability, display a message insdead of the content.
|
||||
|
||||
kwargs:
|
||||
- 'cap': Capability required for viewing.
|
||||
- 'nocap': Optional, text shown when the user does not have this capability.
|
||||
- others: Passed to the decorated shortcode.
|
||||
"""
|
||||
|
||||
def __init__(self, decorated):
|
||||
assert hasattr(decorated, '__call__'), '@capcheck should be used on callables.'
|
||||
if isinstance(decorated, type):
|
||||
as_callable = decorated()
|
||||
else:
|
||||
as_callable = decorated
|
||||
self.decorated = as_callable
|
||||
|
||||
def __call__(self,
|
||||
context: typing.Any,
|
||||
content: str,
|
||||
pargs: typing.List[str],
|
||||
kwargs: typing.Dict[str, str]) -> str:
|
||||
from pillar.auth import current_user
|
||||
|
||||
cap = kwargs.pop('cap', '')
|
||||
if cap:
|
||||
nocap = kwargs.pop('nocap', '')
|
||||
if not current_user.has_cap(cap):
|
||||
if not nocap:
|
||||
return ''
|
||||
html = html_module.escape(nocap)
|
||||
return f'<p class="shortcode nocap">{html}</p>'
|
||||
|
||||
return self.decorated(context, content, pargs, kwargs)
|
||||
|
||||
|
||||
@shortcode('test')
|
||||
class Test:
|
||||
def __call__(self,
|
||||
@@ -68,6 +107,7 @@ class Test:
|
||||
|
||||
|
||||
@shortcode('youtube')
|
||||
@capcheck
|
||||
class YouTube:
|
||||
log = log.getChild('YouTube')
|
||||
|
||||
@@ -129,6 +169,7 @@ class YouTube:
|
||||
|
||||
|
||||
@shortcode('iframe')
|
||||
@capcheck
|
||||
def iframe(context: typing.Any,
|
||||
content: str,
|
||||
pargs: typing.List[str],
|
||||
@@ -140,16 +181,6 @@ def iframe(context: typing.Any,
|
||||
- others: Turned into attributes for the iframe element.
|
||||
"""
|
||||
import xml.etree.ElementTree as ET
|
||||
from pillar.auth import current_user
|
||||
|
||||
cap = kwargs.pop('cap', '')
|
||||
if cap:
|
||||
nocap = kwargs.pop('nocap', '')
|
||||
if not current_user.has_cap(cap):
|
||||
if not nocap:
|
||||
return ''
|
||||
html = html_module.escape(nocap)
|
||||
return f'<p class="shortcode nocap">{html}</p>'
|
||||
|
||||
kwargs['class'] = f'shortcode {kwargs.get("class", "")}'.strip()
|
||||
element = ET.Element('iframe', kwargs)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Flask configuration file for unit testing."""
|
||||
|
||||
BLENDER_ID_ENDPOINT = 'http://127.0.0.1:8001' # nonexistant server, no trailing slash!
|
||||
BLENDER_ID_ENDPOINT = 'http://id.local:8001' # Non existant server
|
||||
|
||||
SERVER_NAME = 'localhost'
|
||||
PILLAR_SERVER_ENDPOINT = 'http://localhost/api/'
|
||||
@@ -26,7 +26,6 @@ OAUTH_CREDENTIALS = {
|
||||
'blender-id': {
|
||||
'id': 'blender-id-app-id',
|
||||
'secret': 'blender-id–secret',
|
||||
'base_url': 'http://blender-id:8000/'
|
||||
},
|
||||
'facebook': {
|
||||
'id': 'fb-app-id',
|
||||
|
@@ -4,7 +4,7 @@ from datetime import datetime
|
||||
from datetime import date
|
||||
import pillarsdk
|
||||
from flask import current_app
|
||||
from flask_wtf import Form
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField
|
||||
from wtforms import DateField
|
||||
from wtforms import SelectField
|
||||
@@ -110,7 +110,7 @@ def get_node_form(node_type):
|
||||
:param node_type: Describes the node type via dyn_schema, form_schema and
|
||||
parent
|
||||
"""
|
||||
class ProceduralForm(Form):
|
||||
class ProceduralForm(FlaskForm):
|
||||
pass
|
||||
|
||||
parent_prop = node_type['parent']
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from flask_wtf import Form
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField
|
||||
from wtforms import BooleanField
|
||||
from wtforms import HiddenField
|
||||
@@ -12,7 +12,7 @@ from pillar.web import system_util
|
||||
from pillar.web.utils.forms import FileSelectField, JSONRequired
|
||||
|
||||
|
||||
class ProjectForm(Form):
|
||||
class ProjectForm(FlaskForm):
|
||||
project_id = HiddenField('project_id', validators=[DataRequired()])
|
||||
name = StringField('Name', validators=[DataRequired()])
|
||||
url = StringField('Url', validators=[DataRequired()])
|
||||
@@ -32,7 +32,7 @@ class ProjectForm(Form):
|
||||
picture_square = FileSelectField('Picture square', file_format='image')
|
||||
|
||||
def validate(self):
|
||||
rv = Form.validate(self)
|
||||
rv = FlaskForm.validate(self)
|
||||
if not rv:
|
||||
return False
|
||||
|
||||
@@ -54,7 +54,7 @@ class ProjectForm(Form):
|
||||
return True
|
||||
|
||||
|
||||
class NodeTypeForm(Form):
|
||||
class NodeTypeForm(FlaskForm):
|
||||
project_id = HiddenField('project_id', validators=[DataRequired()])
|
||||
name = StringField('Name', validators=[DataRequired()])
|
||||
parent = StringField('Parent')
|
||||
|
@@ -12,14 +12,6 @@ from pillar.sdk import FlaskInternalApi
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def blender_id_endpoint():
|
||||
"""Gets the endpoint for the authentication API. If the env variable
|
||||
is defined, it's possible to override the (default) production address.
|
||||
"""
|
||||
return os.environ.get('BLENDER_ID_ENDPOINT',
|
||||
"https://www.blender.org/id").rstrip('/')
|
||||
|
||||
|
||||
def pillar_server_endpoint():
|
||||
"""Gets the endpoint for the authentication API. If the env variable
|
||||
is defined, we will use the one from the config object.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
from flask_login import current_user
|
||||
from flask_wtf import Form
|
||||
from flask_wtf import FlaskForm
|
||||
from pillar.web import system_util
|
||||
from pillarsdk.users import User
|
||||
|
||||
@@ -14,7 +14,7 @@ from wtforms.validators import Regexp
|
||||
import wtforms.validators as wtvalid
|
||||
|
||||
|
||||
class UserLoginForm(Form):
|
||||
class UserLoginForm(FlaskForm):
|
||||
username = StringField('Username', validators=[DataRequired()])
|
||||
password = PasswordField('Password', validators=[DataRequired()])
|
||||
remember_me = BooleanField('Remember Me')
|
||||
@@ -23,7 +23,7 @@ class UserLoginForm(Form):
|
||||
super(UserLoginForm, self).__init__(csrf_enabled=False, *args, **kwargs)
|
||||
|
||||
|
||||
class UserProfileForm(Form):
|
||||
class UserProfileForm(FlaskForm):
|
||||
username = StringField('Username', validators=[DataRequired(), Length(
|
||||
min=3, max=128, message="Min. 3, max. 128 chars please"), Regexp(
|
||||
r'^[\w.@+-]+$', message="Please do not use spaces")])
|
||||
@@ -52,7 +52,7 @@ class UserProfileForm(Form):
|
||||
return True
|
||||
|
||||
|
||||
class UserSettingsEmailsForm(Form):
|
||||
class UserSettingsEmailsForm(FlaskForm):
|
||||
choices = [
|
||||
(1, 'Keep me updated with Blender Cloud news.'),
|
||||
(0, 'Do not mail me news update.')]
|
||||
@@ -74,7 +74,7 @@ class RolesField(SelectMultipleField):
|
||||
return current_app.user_roles
|
||||
|
||||
|
||||
class UserEditForm(Form):
|
||||
class UserEditForm(FlaskForm):
|
||||
roles = RolesField('Roles')
|
||||
email = StringField(
|
||||
validators=[wtvalid.DataRequired(), wtvalid.Email()],
|
||||
|
@@ -5,7 +5,7 @@ attrs==16.2.0
|
||||
algoliasearch==1.12.0
|
||||
bcrypt==3.1.3
|
||||
blinker==1.4
|
||||
bleach==1.4.3
|
||||
bleach==2.1.3
|
||||
celery[redis]==4.0.2
|
||||
CommonMark==0.7.2
|
||||
elasticsearch==6.1.1
|
||||
@@ -40,7 +40,7 @@ Flask-PyMongo==0.4.1
|
||||
-e git+https://github.com/armadillica/cerberus.git@sybren-0.9#egg=Cerberus
|
||||
Events==0.2.2
|
||||
future==0.15.2
|
||||
html5lib==0.9999999
|
||||
html5lib==0.99999999
|
||||
googleapis-common-protos==1.1.0
|
||||
itsdangerous==0.24
|
||||
Jinja2==2.9.6
|
||||
|
@@ -64,4 +64,13 @@
|
||||
return this;
|
||||
};
|
||||
|
||||
// jQuery's show() sets display as 'inline', this utility sets it to whatever we want.
|
||||
// Useful for buttons or links that need 'inline-block' or flex for correct padding and alignment.
|
||||
$.fn.displayAs = function(display_type) {
|
||||
if (typeof(display_type) === 'undefined') {
|
||||
display_type = 'block';
|
||||
}
|
||||
|
||||
this.css('display', display_type);
|
||||
}
|
||||
}(jQuery));
|
||||
|
@@ -453,12 +453,17 @@ $comments-width-max: 710px
|
||||
transition: background-color 150ms ease-in-out, color 150ms ease-in-out
|
||||
width: 100px
|
||||
|
||||
// The actual button for submitting the comment.
|
||||
button.comment-action-submit
|
||||
align-items: center
|
||||
background: transparent
|
||||
border: none
|
||||
border-top-left-radius: 0
|
||||
border-bottom-left-radius: 0
|
||||
color: $color-success
|
||||
cursor: pointer
|
||||
display: flex
|
||||
justify-content: center
|
||||
flex-direction: column
|
||||
height: 100%
|
||||
position: relative
|
||||
@@ -466,8 +471,12 @@ $comments-width-max: 710px
|
||||
white-space: nowrap
|
||||
width: 100%
|
||||
|
||||
&:hover
|
||||
background: rgba($color-success, .1)
|
||||
|
||||
&:focus
|
||||
background: lighten($color-success, 10%)
|
||||
color: $white
|
||||
|
||||
&.submitting
|
||||
color: $color-info
|
||||
|
@@ -25,7 +25,7 @@ $color-text-light-primary: rgba($color-text-light, .87) !default
|
||||
$color-text-light-secondary: rgba($color-text-light, .54) !default
|
||||
$color-text-light-hint: rgba($color-text-light, .38) !default
|
||||
|
||||
$color-primary: #68B3C8 !default
|
||||
$color-primary: #009eff !default
|
||||
$color-primary-light: hsl(hue($color-primary), 30%, 90%) !default
|
||||
$color-primary-dark: hsl(hue($color-primary), 80%, 30%) !default
|
||||
$color-primary-accent: hsl(hue($color-primary), 100%, 50%) !default
|
||||
@@ -100,13 +100,10 @@ $sidebar-width: 50px !default
|
||||
|
||||
/* Project specifics */
|
||||
$project_nav-width: 250px !default
|
||||
$project-sidebar-width: 50px !default
|
||||
$project_header-height: 50px !default
|
||||
$project-sidebar-width: 40px !default
|
||||
$project_header-height: 37px !default
|
||||
$project_footer-height: 30px !default
|
||||
|
||||
$navbar-height: 50px !default
|
||||
$navbar-backdrop-height: 600px !default
|
||||
|
||||
$node-type-asset_image: #e87d86 !default
|
||||
$node-type-asset_file: #CC91C7 !default
|
||||
$node-type-asset_video: #7dc5e8 !default
|
||||
@@ -125,3 +122,29 @@ $z-index-base: 13 !default
|
||||
|
||||
@media (min-width: $screen-lg-min)
|
||||
width: 1270px
|
||||
|
||||
|
||||
// Bootstrap overrides.
|
||||
$enable-caret: false
|
||||
|
||||
$border-radius: .2rem
|
||||
$btn-border-radius: $border-radius
|
||||
|
||||
$primary: $color-primary
|
||||
|
||||
$body-bg: $white
|
||||
$body-color: $color-text
|
||||
|
||||
$color-background-nav: #fff
|
||||
$link-color: $primary
|
||||
|
||||
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"
|
||||
$font-size-base: .9rem
|
||||
|
||||
$dropdown-border-width: 0
|
||||
$dropdown-box-shadow: 0 10px 25px rgba($black, .1)
|
||||
|
||||
// Tooltips.
|
||||
$tooltip-font-size: 0.83rem
|
||||
$tooltip-max-width: auto
|
||||
$tooltip-opacity: 1
|
||||
|
@@ -60,14 +60,13 @@
|
||||
|
||||
#node-overlay
|
||||
#error-container
|
||||
position: fixed
|
||||
top: $navbar-height
|
||||
align-items: flex-start
|
||||
position: fixed
|
||||
top: $nav-link-height
|
||||
|
||||
#error-box
|
||||
box-shadow: 0 0 25px rgba(black, .1), 0 0 50px rgba(black, .1)
|
||||
width: auto
|
||||
border-top-left-radius: 0
|
||||
border-top-right-radius: 0
|
||||
box-shadow: 0 0 25px rgba(black, .1), 0 0 50px rgba(black, .1)
|
||||
position: relative
|
||||
width: 100%
|
||||
|
@@ -9,7 +9,6 @@
|
||||
color: $color-primary
|
||||
cursor: pointer
|
||||
float: right
|
||||
font-family: $font-body
|
||||
height: initial
|
||||
margin: 0
|
||||
padding: 8px 10px 0 10px
|
||||
|
@@ -1,20 +1,4 @@
|
||||
body.organizations
|
||||
ul#sub-nav-tabs__list
|
||||
align-items: center
|
||||
display: flex
|
||||
|
||||
li.result
|
||||
padding: 10px 20px
|
||||
li.create
|
||||
margin-left: auto
|
||||
|
||||
|
||||
.dashboard-secondary
|
||||
.box
|
||||
+container-box
|
||||
padding: 10px 20px
|
||||
margin: 0
|
||||
|
||||
#item-details
|
||||
.organization
|
||||
label
|
||||
|
@@ -409,7 +409,6 @@ a.page-card-cta
|
||||
display: block
|
||||
+position-center-translate
|
||||
|
||||
|
||||
+media-xs
|
||||
display: none
|
||||
+media-sm
|
||||
@@ -419,9 +418,6 @@ a.page-card-cta
|
||||
+media-lg
|
||||
width: 100%
|
||||
|
||||
.services.navbar-backdrop-overlay
|
||||
background: rgba(black, .5)
|
||||
|
||||
.services
|
||||
.page-card-side
|
||||
max-width: 500px
|
||||
|
@@ -1,22 +1,16 @@
|
||||
|
||||
.dashboard-container
|
||||
section#home,
|
||||
section#projects
|
||||
background-color: $color-background
|
||||
border-bottom-left-radius: 3px
|
||||
border-bottom-right-radius: 3px
|
||||
|
||||
nav#sub-nav-tabs.home,
|
||||
nav#sub-nav-tabs.projects
|
||||
background-color: white
|
||||
border-bottom: thin solid $color-background-dark
|
||||
|
||||
li.nav-tabs__list-tab
|
||||
padding: 15px 20px 10px 20px
|
||||
|
||||
section#home
|
||||
background-color: $color-background-dark
|
||||
|
||||
nav.nav-tabs__tab
|
||||
display: none
|
||||
background-color: $color-background-light
|
||||
@@ -287,9 +281,8 @@
|
||||
flex-direction: column
|
||||
|
||||
.title
|
||||
font-size: 1.2em
|
||||
padding-bottom: 2px
|
||||
color: $color-text-dark-primary
|
||||
padding-bottom: 2px
|
||||
|
||||
ul.meta
|
||||
font-size: .9em
|
||||
|
@@ -92,10 +92,6 @@ ul.sharing-users-list
|
||||
&:hover
|
||||
color: lighten($color-danger, 10%)
|
||||
|
||||
.sharing-users-intro,
|
||||
.sharing-users-info
|
||||
h4
|
||||
font-family: $font-body
|
||||
|
||||
.sharing-users-info
|
||||
padding-left: 15px
|
||||
|
@@ -1,4 +1,3 @@
|
||||
$project-sidebar-background: lighten($color-background, 5%)
|
||||
$node-latest-thumbnail-size: 160px
|
||||
|
||||
body.open-projects,
|
||||
@@ -41,10 +40,6 @@ body.blog
|
||||
width: 100%
|
||||
left: 0
|
||||
|
||||
#project_context-header
|
||||
span#status-bar
|
||||
text-align: left
|
||||
|
||||
#project_nav-container
|
||||
+media-lg
|
||||
width: $project_nav-width * 1.33
|
||||
@@ -63,16 +58,12 @@ body.blog
|
||||
#project_sidebar
|
||||
width: $project-sidebar-width
|
||||
z-index: $z-index-base + 6
|
||||
box-shadow: inset -1px 0 0 0 $color-background
|
||||
|
||||
+media-xs
|
||||
width: 100%
|
||||
|
||||
ul.project-tabs
|
||||
background-color: $color-background-nav
|
||||
margin: 0
|
||||
padding: 0
|
||||
list-style: none
|
||||
|
||||
position: fixed
|
||||
width: $project-sidebar-width
|
||||
top: $project_header-height
|
||||
@@ -85,16 +76,9 @@ body.blog
|
||||
width: 100%
|
||||
|
||||
li
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
width: $project-sidebar-width
|
||||
height: $project-sidebar-width
|
||||
position: relative
|
||||
color: white
|
||||
background-color: $color-background-nav
|
||||
border-left: 2px solid transparent
|
||||
transition: all 100ms ease-in-out
|
||||
|
||||
+media-xs
|
||||
border-bottom: 2px solid transparent
|
||||
@@ -108,78 +92,33 @@ body.blog
|
||||
&:first-child
|
||||
border-top: thin solid transparent
|
||||
|
||||
&:hover
|
||||
background-color: $color-background-nav-light
|
||||
&:hover,
|
||||
&.active
|
||||
cursor: pointer
|
||||
|
||||
+media-xs
|
||||
border-bottom: 2px solid $color-background-nav-light
|
||||
+media-sm
|
||||
border-bottom: 2px solid $color-background-nav-light
|
||||
+media-md
|
||||
border-left: 2px solid $color-background-nav-light
|
||||
+media-lg
|
||||
border-left: 2px solid $color-background-nav-light
|
||||
a
|
||||
color: $primary
|
||||
|
||||
a
|
||||
width: $project-sidebar-width
|
||||
height: $project-sidebar-width
|
||||
display: flex
|
||||
align-items: center
|
||||
color: $color-text
|
||||
display: flex
|
||||
justify-content: center
|
||||
color: $color-text-light-primary
|
||||
|
||||
i
|
||||
font-size: 1.1em
|
||||
|
||||
&.active
|
||||
background-color: $color-background-nav-light
|
||||
|
||||
// The tiny triangle
|
||||
&:after
|
||||
+media-xs
|
||||
border-top-color: $project-sidebar-background
|
||||
border-right-color: transparent
|
||||
left: 50%
|
||||
right: initial
|
||||
transform: translateX(-50%)
|
||||
top: 0
|
||||
|
||||
border: 7px solid transparent
|
||||
border-right-color: $project-sidebar-background
|
||||
content: ''
|
||||
display: block
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
|
||||
a
|
||||
color: white
|
||||
height: $project-sidebar-width
|
||||
width: $project-sidebar-width
|
||||
|
||||
&.tabs-thumbnail
|
||||
height: $project-sidebar-width
|
||||
border-bottom: 2px solid $color-background-nav-light
|
||||
|
||||
img
|
||||
width: $project-sidebar-width
|
||||
height: $project-sidebar-width
|
||||
|
||||
&.image
|
||||
border-left: none
|
||||
width: $project-sidebar-width
|
||||
|
||||
#project-loading
|
||||
position: absolute
|
||||
width: $project-sidebar-width
|
||||
height: $project-sidebar-width
|
||||
display: flex
|
||||
align-items: center
|
||||
display: flex
|
||||
height: $project-sidebar-width
|
||||
justify-content: center
|
||||
i
|
||||
position: relative
|
||||
top: -1px
|
||||
font-size: 1em
|
||||
color: white
|
||||
width: $project-sidebar-width
|
||||
|
||||
|
||||
#search-container #project_sidebar ul.project-tabs li.tabs-thumbnail
|
||||
background-color: $color-background-nav-dark
|
||||
@@ -199,7 +138,6 @@ body.blog
|
||||
+media-xs
|
||||
width: initial
|
||||
|
||||
background-color: $project-sidebar-background
|
||||
display: block
|
||||
left: 0
|
||||
position: relative
|
||||
@@ -221,53 +159,34 @@ body.blog
|
||||
|
||||
#project_context-header
|
||||
align-items: center
|
||||
background-color: white
|
||||
background-color: $color-background-light
|
||||
border-bottom: thin solid lighten($color-text-dark-hint, 10%)
|
||||
box-shadow: none
|
||||
color: $color-text-dark-secondary
|
||||
display: flex
|
||||
height: $project_header-height
|
||||
min-height: $project_header-height
|
||||
position: fixed
|
||||
top: $project_header-height
|
||||
top: $project_header-height + 1
|
||||
transition: box-shadow 250ms ease-in-out
|
||||
width: auto
|
||||
z-index: $z-index-base + 3
|
||||
|
||||
&.is-offset
|
||||
box-shadow: 0 0 25px rgba(black, .2)
|
||||
|
||||
span#status-bar
|
||||
text-align: left
|
||||
|
||||
#project_nav-header
|
||||
z-index: $z-index-base + 3
|
||||
left: 0
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
height: $project_header-height
|
||||
min-height: $project_header-height
|
||||
width: 100%
|
||||
background-color: $color-background-light
|
||||
border-bottom: thin solid $color-background-dark
|
||||
z-index: $z-index-base + 3
|
||||
|
||||
/* Name of the project */
|
||||
.project-title
|
||||
width: 100%
|
||||
max-width: 100%
|
||||
+text-overflow-ellipsis
|
||||
height: 100%
|
||||
transition: background-color 150ms ease-in-out
|
||||
|
||||
a
|
||||
display: block
|
||||
width: 100%
|
||||
color: $color-text-dark
|
||||
padding: 15px
|
||||
font-size: 1.1em
|
||||
+text-overflow-ellipsis
|
||||
|
||||
&:hover, &:active
|
||||
color: $color-primary
|
||||
text-decoration: none
|
||||
outline: none
|
||||
max-width: 100%
|
||||
width: 100%
|
||||
|
||||
span#status-bar
|
||||
position: absolute
|
||||
@@ -278,7 +197,6 @@ span#status-bar
|
||||
color: $color-text-dark
|
||||
opacity: 0
|
||||
z-index: 1
|
||||
background-color: $color-background-light
|
||||
font-weight: 400
|
||||
white-space: nowrap
|
||||
transition: all 250ms ease-in-out
|
||||
@@ -293,7 +211,7 @@ span#status-bar
|
||||
&.info
|
||||
color: $color-info
|
||||
&.error
|
||||
color: $color-danger
|
||||
color: $danger
|
||||
&.warning
|
||||
color: $color-warning
|
||||
&.success
|
||||
@@ -320,71 +238,29 @@ span#project-edit-title
|
||||
display: none
|
||||
|
||||
ul.project-edit-tools
|
||||
display: flex
|
||||
align-items: center
|
||||
height: 100%
|
||||
padding: 0
|
||||
margin:
|
||||
top: 0
|
||||
left: auto
|
||||
right: 5px
|
||||
bottom: 0
|
||||
color: $color-text-dark-hint
|
||||
|
||||
display: flex
|
||||
list-style-type: none
|
||||
font-size: .9em
|
||||
margin: 0 0 0 auto
|
||||
padding: 0
|
||||
|
||||
+media-xs
|
||||
width: 100%
|
||||
margin: 0 auto
|
||||
justify-content: space-around
|
||||
|
||||
/* Bottons at the end of a form, like Save Changes */
|
||||
&.bottom
|
||||
+clearfix
|
||||
padding: 20px 0 25px 0
|
||||
border-top: thin solid $color-text-dark-hint
|
||||
justify-content: flex-end
|
||||
width: 100%
|
||||
height: initial
|
||||
|
||||
li
|
||||
margin: 0
|
||||
padding: 0
|
||||
position: relative
|
||||
float: left
|
||||
user-select: none
|
||||
a, button
|
||||
padding: $dropdown-item-padding-y ($dropdown-item-padding-x / 2)
|
||||
|
||||
a
|
||||
min-width: 110px
|
||||
user-select: none
|
||||
|
||||
&:focus, &:active
|
||||
text-decoration: none
|
||||
outline: none
|
||||
i
|
||||
padding-right: 10px
|
||||
|
||||
&.button-save
|
||||
|
||||
&.disabled
|
||||
border-bottom: none
|
||||
|
||||
a
|
||||
color: white
|
||||
border-color: darken($color-success, 10%)
|
||||
|
||||
a
|
||||
border-color: $color-success
|
||||
background-color: $color-success
|
||||
color: white
|
||||
margin-right: 10px
|
||||
|
||||
&:hover
|
||||
background-color: lighten($color-success, 5%)
|
||||
|
||||
&.field-error
|
||||
a
|
||||
background-color: $color-danger
|
||||
border-color: $color-danger
|
||||
background-color: $danger
|
||||
border-color: $danger
|
||||
color: white
|
||||
|
||||
&.saving
|
||||
@@ -411,31 +287,6 @@ ul.project-edit-tools
|
||||
&.button-edit
|
||||
min-width: 80px
|
||||
|
||||
|
||||
&.button-dropdown
|
||||
min-width: 50px
|
||||
cursor: pointer
|
||||
i
|
||||
margin: auto
|
||||
padding: 0
|
||||
|
||||
&.button-add-icon
|
||||
margin-right: 5px
|
||||
|
||||
a.dropdown-toggle
|
||||
display: inline-block
|
||||
min-width: auto
|
||||
|
||||
&.open
|
||||
a.dropdown-toggle
|
||||
color: $color-text-light
|
||||
background-color: $color-primary
|
||||
border-color: darken($color-primary, 5%)
|
||||
|
||||
li.button-delete:hover
|
||||
a
|
||||
color: $color-danger
|
||||
|
||||
&.featured
|
||||
a
|
||||
color: $color-warning
|
||||
@@ -445,132 +296,44 @@ ul.project-edit-tools
|
||||
background-color: rgba($color-warning, .1)
|
||||
|
||||
&.disabled
|
||||
cursor: not-allowed
|
||||
border-bottom: thin solid $color-text-dark-hint
|
||||
+disabled-stripes
|
||||
|
||||
a
|
||||
pointer-events: none
|
||||
background: repeating-linear-gradient(-45deg, lighten($color-text-dark-hint, 15%), lighten($color-text-dark-hint, 15%) 10px, lighten($color-text-dark-hint, 5%) 10px, lighten($color-text-dark-hint, 5%) 20px)
|
||||
border-color: darken($color-text-dark-hint, 5%)
|
||||
opacity: .6
|
||||
color: $color-text-dark
|
||||
+disabled-stripes
|
||||
|
||||
&.button-dropdown.disabled
|
||||
border-bottom: none
|
||||
|
||||
a
|
||||
margin: 5px
|
||||
padding: 5px 15px
|
||||
color: $color-text-dark-primary
|
||||
border-radius: 3px
|
||||
border: thin solid $color-text-dark-primary
|
||||
text-align: center
|
||||
text-transform: uppercase
|
||||
transition: background-color 150ms ease-in-out
|
||||
background-color: $color-background-light
|
||||
|
||||
i
|
||||
margin-right: 5px
|
||||
|
||||
&:hover
|
||||
background-color: $color-background-active-dark
|
||||
border-color: darken($color-background-active-dark, 5%)
|
||||
color: $color-text-light
|
||||
text-decoration: none
|
||||
|
||||
&:focus, &:active
|
||||
text-decoration: none
|
||||
outline: none
|
||||
|
||||
|
||||
&#item_save,
|
||||
&#item_cancel
|
||||
i
|
||||
font-size: 1em
|
||||
|
||||
&#item_add,
|
||||
&#item_edit,
|
||||
&#item_move,
|
||||
&#item_featured
|
||||
&.dropdown
|
||||
li
|
||||
i
|
||||
margin-right: 10px
|
||||
padding: 0
|
||||
|
||||
&#item_delete
|
||||
li i
|
||||
margin-right: 0
|
||||
// &:not(:last-of-type)
|
||||
// border-right: thin solid darken($color-background, 50%)
|
||||
a
|
||||
color: $body-color
|
||||
display: block
|
||||
padding: $dropdown-item-padding-y $dropdown-item-padding-x
|
||||
padding-left: 15px
|
||||
user-select: none
|
||||
|
||||
/* ul.project-edit-tools */
|
||||
&:hover
|
||||
color: $primary
|
||||
text-decoration: none
|
||||
|
||||
/* Extra asset tools in dropdown */
|
||||
ul.dropdown-menu
|
||||
width: auto
|
||||
min-width: 180px
|
||||
padding: 0
|
||||
margin: 0
|
||||
top: 44px
|
||||
left: initial
|
||||
right: 10px
|
||||
bottom: initial
|
||||
border: thin solid $color-text-dark-hint
|
||||
border-bottom-left-radius: 3px
|
||||
border-bottom-right-radius: 3px
|
||||
border-top-left-radius: 0
|
||||
border-top-right-radius: 0
|
||||
background-color: white
|
||||
color: $color-text-dark-primary
|
||||
box-shadow: 1px 1px 25px rgba(black, .2)
|
||||
|
||||
li
|
||||
padding: 0
|
||||
clear: both
|
||||
display: flex
|
||||
align-items: center
|
||||
width: 100%
|
||||
|
||||
a
|
||||
margin: 0
|
||||
padding: 10px 15px
|
||||
width: 100%
|
||||
border: 0
|
||||
font-size: .9em
|
||||
width: 100%
|
||||
text-align: left
|
||||
|
||||
&:hover
|
||||
color: $color-primary
|
||||
background-color: transparent
|
||||
&:active, &:focus
|
||||
color: $color-primary
|
||||
background-color: transparent
|
||||
|
||||
i
|
||||
display: inline-block
|
||||
margin: 0 15px 0 0
|
||||
|
||||
&.icon-group:after
|
||||
content: '\e80d'
|
||||
&.icon-group_texture:after,
|
||||
&.icon-group_hdri:after
|
||||
content: '\e80b'
|
||||
font-size: 1.1em
|
||||
&.icon-asset:after
|
||||
content: '\e825'
|
||||
&.icon-page:after
|
||||
content: '\e824'
|
||||
&.icon-texture:after
|
||||
content: '\e80a'
|
||||
&.icon-hdri:after
|
||||
content: '\f019'
|
||||
/* Icons per asset type. */
|
||||
i
|
||||
&.icon-group:after
|
||||
content: '\e80d'
|
||||
&.icon-group_texture:after,
|
||||
&.icon-group_hdri:after
|
||||
content: '\e80b'
|
||||
font-size: 1.1em
|
||||
&.icon-asset:after
|
||||
content: '\e825'
|
||||
&.icon-page:after
|
||||
content: '\e824'
|
||||
&.icon-texture:after
|
||||
content: '\e80a'
|
||||
&.icon-hdri:after
|
||||
content: '\f019'
|
||||
|
||||
/* // Extra asset tools in dropdown */
|
||||
|
||||
&.open
|
||||
button
|
||||
box-shadow: none
|
||||
|
||||
/* // Edit Asset buttons */
|
||||
|
||||
#project-loading
|
||||
@@ -607,9 +370,6 @@ ul.project_nav-edit-list
|
||||
li
|
||||
background-color: $color-background
|
||||
border-bottom: 1px solid $color-background-dark
|
||||
font:
|
||||
weight: 400
|
||||
family: $font-body
|
||||
color: $color-text-dark
|
||||
position: relative
|
||||
|
||||
@@ -663,9 +423,8 @@ ul.project_nav-edit-list
|
||||
padding-top: $project_header-height
|
||||
|
||||
#node-container
|
||||
flex: 1
|
||||
color: $color-text-dark
|
||||
background-color: white
|
||||
flex: 1
|
||||
|
||||
/* For error messages (403) and other overlaid text*/
|
||||
#node-overlay
|
||||
@@ -687,8 +446,6 @@ ul.project_nav-edit-list
|
||||
/* Project context on the right of the navigation */
|
||||
/* Contains #project_context */
|
||||
#project_context-container
|
||||
background-color: $color-background-nav
|
||||
|
||||
iframe#server_error
|
||||
width: 100%
|
||||
min-height: 800px
|
||||
@@ -700,11 +457,9 @@ ul.project_nav-edit-list
|
||||
+media-xs
|
||||
margin-top: 0
|
||||
|
||||
background-color: $project-sidebar-background
|
||||
border-right: thin solid $color-background
|
||||
margin-top: $project_header-height
|
||||
overflow-y: auto
|
||||
padding: 0 0 5px 0// some padding on top/bottom of jstree
|
||||
// margin-top: $project_header-height //so it's right below the project title.
|
||||
overflow-y: auto // show vertical scrollbars when needed.
|
||||
padding: 0 0 5px 0 // some padding on top/bottom of jstree.
|
||||
position: relative
|
||||
|
||||
&.edit
|
||||
@@ -713,9 +468,7 @@ ul.project_nav-edit-list
|
||||
|
||||
/* Node Context */
|
||||
=project-node-title
|
||||
font:
|
||||
family: $font-body
|
||||
size: 1.5em
|
||||
font-size: 1.5em
|
||||
color: $color-text-dark-primary
|
||||
|
||||
$node-preview-max-height-sm: 300px
|
||||
@@ -825,7 +578,7 @@ $node-preview-max-height-lg: 700px
|
||||
margin-left: auto
|
||||
|
||||
&.pending
|
||||
color: $color-danger
|
||||
color: $danger
|
||||
|
||||
section.node-preview.texture
|
||||
overflow: hidden
|
||||
@@ -897,7 +650,6 @@ $node-preview-max-height-lg: 700px
|
||||
.node-title
|
||||
color: $color-text-dark-primary
|
||||
font:
|
||||
family: $font-body
|
||||
weight: 500
|
||||
size: 1.5em
|
||||
+text-overflow-ellipsis
|
||||
@@ -1054,6 +806,7 @@ section.node-preview-forbidden
|
||||
justify-content: center
|
||||
min-height: 400px
|
||||
position: relative
|
||||
overflow: hidden
|
||||
|
||||
img
|
||||
height: 130%
|
||||
@@ -1075,13 +828,6 @@ section.node-preview-forbidden
|
||||
span
|
||||
display: block
|
||||
|
||||
a
|
||||
color: $color-text-light
|
||||
|
||||
&.btn
|
||||
border-color: white
|
||||
color: white
|
||||
|
||||
hr
|
||||
opacity: .5
|
||||
|
||||
@@ -1128,9 +874,6 @@ section.node-preview.group
|
||||
padding: 0
|
||||
margin: 0
|
||||
|
||||
&.project
|
||||
.node-details-title
|
||||
padding: 10px 20px 0 20px
|
||||
|
||||
.node-details-description
|
||||
+node-details-description
|
||||
@@ -1164,7 +907,7 @@ section.node-preview.group
|
||||
padding-left: 0
|
||||
|
||||
&.status-pending
|
||||
color: $color-danger
|
||||
color: $danger
|
||||
|
||||
&.public
|
||||
color: $color-success
|
||||
@@ -1354,7 +1097,7 @@ section.node-details-container
|
||||
opacity: 1
|
||||
|
||||
.error-node-type-not-found
|
||||
color: $color-danger
|
||||
color: $danger
|
||||
clear: both
|
||||
|
||||
a.learn-more
|
||||
@@ -1491,8 +1234,6 @@ a.learn-more
|
||||
|
||||
|
||||
section.node-children
|
||||
background-color: white
|
||||
|
||||
&.group, &.storage
|
||||
flex: 1
|
||||
padding: 10px 0 25px 20px
|
||||
@@ -1911,7 +1652,6 @@ section.node-children
|
||||
|
||||
.node_index-collection-name
|
||||
font:
|
||||
family: $font-body
|
||||
size: 4em
|
||||
weight: 600
|
||||
margin-bottom: -5px
|
||||
@@ -2034,7 +1774,6 @@ section.node-children
|
||||
color: $color-text-dark
|
||||
font:
|
||||
size: 1.5em
|
||||
family: $font-body
|
||||
weight: 500
|
||||
text-decoration: none
|
||||
|
||||
@@ -2101,15 +1840,15 @@ section.node-children
|
||||
text-transform: capitalize
|
||||
|
||||
&.error
|
||||
color: $color-danger
|
||||
color: $danger
|
||||
background-color: $color-background-light
|
||||
padding: 10px 15px
|
||||
border: thin solid lighten($color-danger, 10%)
|
||||
border-top: 2px solid $color-danger
|
||||
border: thin solid lighten($danger, 10%)
|
||||
border-top: 2px solid $danger
|
||||
border-bottom-left-radius: 3px
|
||||
border-bottom-right-radius: 3px
|
||||
label
|
||||
color: $color-danger
|
||||
color: $danger
|
||||
font-weight: 500
|
||||
|
||||
&.file
|
||||
@@ -2176,7 +1915,7 @@ section.node-children
|
||||
outline: none
|
||||
|
||||
&.field-error
|
||||
border-color: $color-danger
|
||||
border-color: $danger
|
||||
|
||||
.md-preview-loading
|
||||
position: absolute
|
||||
@@ -2423,7 +2162,7 @@ section.node-children
|
||||
.cancel
|
||||
+button($color-warning, 3px)
|
||||
.delete
|
||||
+button($color-danger, 3px)
|
||||
+button($danger, 3px)
|
||||
.toggle
|
||||
margin: 0 20px
|
||||
|
||||
@@ -2434,7 +2173,7 @@ section.node-children
|
||||
&.cancel
|
||||
+button($color-warning, 3px)
|
||||
&.delete
|
||||
+button($color-danger, 3px)
|
||||
+button($danger, 3px)
|
||||
|
||||
&.create
|
||||
+button($color-success, 3px)
|
||||
|
@@ -29,7 +29,6 @@ $search-hit-width_grid: 100px
|
||||
font:
|
||||
size: .9em
|
||||
weight: 400
|
||||
family: $font-body
|
||||
style: initial
|
||||
width: 100%
|
||||
+text-overflow-ellipsis
|
||||
@@ -416,9 +415,7 @@ $search-hit-width_grid: 100px
|
||||
|
||||
&.texture
|
||||
.texture-title
|
||||
font:
|
||||
size: 2em
|
||||
family: $font-body
|
||||
font-size: 2em
|
||||
padding: 15px 10px 10px 15px
|
||||
.node-row
|
||||
background: white
|
||||
|
@@ -68,13 +68,6 @@
|
||||
background-color: lighten($provider-color-google, 7%)
|
||||
|
||||
#settings
|
||||
+media-xs
|
||||
flex-direction: column
|
||||
|
||||
align-items: stretch
|
||||
display: flex
|
||||
margin: 25px auto
|
||||
|
||||
#settings-sidebar
|
||||
+media-xs
|
||||
width: 100%
|
||||
|
@@ -26,7 +26,6 @@
|
||||
display: inline-flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
font-family: $font-body
|
||||
padding: 5px 12px
|
||||
border-radius: $roundness
|
||||
|
||||
@@ -83,6 +82,15 @@
|
||||
text-shadow: none
|
||||
|
||||
|
||||
=disabled-stripes
|
||||
color: $color-text-dark
|
||||
cursor: not-allowed
|
||||
background: repeating-linear-gradient(-45deg, lighten($color-text-dark-hint, 15%), lighten($color-text-dark-hint, 15%) 10px, lighten($color-text-dark-hint, 5%) 10px, lighten($color-text-dark-hint, 5%) 20px)
|
||||
border-color: darken($color-text-dark-hint, 5%)
|
||||
pointer-events: none
|
||||
opacity: .6
|
||||
|
||||
|
||||
@mixin overlay($from-color, $from-percentage, $to-color, $to-percentage)
|
||||
position: absolute
|
||||
top: 0
|
||||
@@ -122,24 +130,17 @@
|
||||
transform: translate(-50%, -50%)
|
||||
|
||||
=input-generic
|
||||
padding: 5px 5px 5px 0
|
||||
// padding: 5px 5px 5px 0
|
||||
color: $color-text-dark
|
||||
box-shadow: none
|
||||
font-family: $font-body
|
||||
border: thin solid transparent
|
||||
border-radius: 0
|
||||
border-bottom-color: $color-background-dark
|
||||
background-color: transparent
|
||||
transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out
|
||||
|
||||
&:hover
|
||||
border-bottom-color: $color-background
|
||||
|
||||
&:focus
|
||||
outline: 0
|
||||
border: thin solid transparent
|
||||
border-bottom-color: $color-primary
|
||||
box-shadow: 0 1px 0 0 $color-primary
|
||||
border-color: $primary
|
||||
box-shadow: none
|
||||
|
||||
=label-generic
|
||||
color: $color-text-dark-primary
|
||||
@@ -354,7 +355,6 @@
|
||||
+clearfix
|
||||
color: darken($color-text-dark, 5%)
|
||||
font:
|
||||
family: $font-body
|
||||
weight: 300
|
||||
size: 1.2em
|
||||
|
||||
@@ -405,6 +405,10 @@
|
||||
bottom: 25px
|
||||
top: 25px
|
||||
|
||||
&.emoji
|
||||
display: inline-block
|
||||
padding: initial
|
||||
|
||||
h2
|
||||
margin-bottom: 15px
|
||||
|
||||
@@ -443,11 +447,12 @@
|
||||
|
||||
li
|
||||
margin-bottom: 7px
|
||||
|
||||
img
|
||||
display: block
|
||||
padding:
|
||||
top: 25px
|
||||
bottom: 10px
|
||||
top: 25px
|
||||
|
||||
ul, ul li ul
|
||||
margin-top: 15px
|
||||
@@ -455,10 +460,13 @@
|
||||
|
||||
code, kbd, pre, samp
|
||||
background-color: rgba($color-primary, .05)
|
||||
color: $color-primary
|
||||
color: darken($color-primary, 15%)
|
||||
font-size: inherit
|
||||
white-space: pre-line
|
||||
|
||||
code
|
||||
background-color: transparent
|
||||
|
||||
kbd
|
||||
border:
|
||||
color: rgba($color-primary, .33)
|
||||
@@ -652,3 +660,17 @@
|
||||
transition: all 1s ease-out
|
||||
img
|
||||
transition: all 1s ease-out
|
||||
|
||||
.cursor-pointer
|
||||
cursor: pointer
|
||||
|
||||
.user-select-none
|
||||
user-select: none
|
||||
|
||||
// Bootstrap has .img-fluid, a class to limit the width of an image to 100%.
|
||||
// .imgs-fluid below is to be applied on a parent container when we can't add
|
||||
// classes to the images themselves. e.g. the blog.
|
||||
.imgs-fluid
|
||||
img
|
||||
// Just re-use Bootstrap's mixin here.
|
||||
+img-fluid
|
||||
|
1290
src/styles/base.sass
1290
src/styles/base.sass
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,96 @@
|
||||
@import _normalize
|
||||
// Bootstrap variables and utilities.
|
||||
@import "../../node_modules/bootstrap/scss/functions"
|
||||
@import "../../node_modules/bootstrap/scss/variables"
|
||||
@import "../../node_modules/bootstrap/scss/mixins"
|
||||
|
||||
@import _config
|
||||
@import _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 "apps_base"
|
||||
@import "components/base"
|
||||
|
||||
@import "components/jumbotron"
|
||||
@import "components/alerts"
|
||||
@import "components/navbar"
|
||||
@import "components/dropdown"
|
||||
@import "components/footer"
|
||||
@import "components/shortcode"
|
||||
@import "components/statusbar"
|
||||
@import "components/search"
|
||||
@import "components/flyout"
|
||||
@import "components/forms"
|
||||
@import "components/inputs"
|
||||
@import "components/buttons"
|
||||
@import "components/popover"
|
||||
@import "components/tooltip"
|
||||
@import "components/checkbox"
|
||||
@import "components/overlay"
|
||||
@import "components/card"
|
||||
|
||||
@import _comments
|
||||
@import _error
|
||||
@import _search
|
||||
|
||||
.container-fluid.blog
|
||||
padding: 0
|
||||
@import components/base
|
||||
@import components/alerts
|
||||
@import components/navbar
|
||||
@import components/footer
|
||||
@import components/shortcode
|
||||
@import components/statusbar
|
||||
@import components/search
|
||||
@import components/flyout
|
||||
@import components/forms
|
||||
@import components/inputs
|
||||
@import components/buttons
|
||||
@import components/popover
|
||||
@import components/tooltip
|
||||
@import components/checkbox
|
||||
@import components/overlay
|
||||
|
||||
#blog_container
|
||||
+media-xs
|
||||
flex-direction: column
|
||||
padding-top: 0
|
||||
display: flex
|
||||
padding:
|
||||
bottom: 15px
|
||||
|
||||
video
|
||||
max-width: 100%
|
||||
@@ -26,7 +101,6 @@
|
||||
.form-group
|
||||
position: relative
|
||||
margin: 0 auto 30px auto
|
||||
font-family: $font-body
|
||||
|
||||
input, textarea, select
|
||||
+input-generic
|
||||
@@ -167,6 +241,56 @@
|
||||
#blog_post-edit-container
|
||||
padding: 25px
|
||||
|
||||
.blog_index-item
|
||||
.item-picture
|
||||
position: relative
|
||||
width: 100%
|
||||
max-height: 350px
|
||||
min-height: 200px
|
||||
height: auto
|
||||
overflow: hidden
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
+clearfix
|
||||
|
||||
img
|
||||
+position-center-translate
|
||||
width: 100%
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
|
||||
+media-xs
|
||||
min-height: 150px
|
||||
+media-sm
|
||||
min-height: 150px
|
||||
+media-md
|
||||
min-height: 250px
|
||||
+media-lg
|
||||
min-height: 250px
|
||||
|
||||
.item-content
|
||||
+node-details-description
|
||||
|
||||
+media-xs
|
||||
padding:
|
||||
left: 0
|
||||
right: 0
|
||||
|
||||
img
|
||||
display: block
|
||||
margin: 0 auto
|
||||
|
||||
.item-meta
|
||||
color: $color-text-dark-secondary
|
||||
padding:
|
||||
left: 25px
|
||||
right: 25px
|
||||
|
||||
+media-xs
|
||||
padding:
|
||||
left: 10px
|
||||
right: 10px
|
||||
|
||||
#blog_index-container,
|
||||
#blog_post-create-container,
|
||||
#blog_post-edit-container
|
||||
@@ -185,133 +309,11 @@
|
||||
+media-lg
|
||||
width: 100%
|
||||
|
||||
.blog_index-header
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
display: block
|
||||
overflow: hidden
|
||||
position: relative
|
||||
text-align: center
|
||||
width: 100%
|
||||
|
||||
img
|
||||
width: 100%
|
||||
|
||||
.blog_index-item
|
||||
+media-lg
|
||||
max-width: 780px
|
||||
+media-md
|
||||
max-width: 780px
|
||||
+media-sm
|
||||
max-width: 780px
|
||||
|
||||
margin: 15px auto
|
||||
|
||||
&:hover
|
||||
.item-info a
|
||||
color: $color-primary
|
||||
|
||||
.item-picture
|
||||
position: relative
|
||||
width: 100%
|
||||
max-height: 350px
|
||||
min-height: 200px
|
||||
height: auto
|
||||
overflow: hidden
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
+clearfix
|
||||
|
||||
img
|
||||
+position-center-translate
|
||||
width: 100%
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
|
||||
+media-xs
|
||||
min-height: 150px
|
||||
+media-sm
|
||||
min-height: 150px
|
||||
+media-md
|
||||
min-height: 250px
|
||||
+media-lg
|
||||
min-height: 250px
|
||||
|
||||
.item-title
|
||||
color: $color-text-dark
|
||||
display: block
|
||||
font:
|
||||
family: $font-body
|
||||
size: 1.8em
|
||||
|
||||
padding: 10px 25px 10px
|
||||
|
||||
ul.meta
|
||||
+list-meta
|
||||
font-size: .9em
|
||||
padding: 0px 25px 5px
|
||||
|
||||
.item-content
|
||||
+node-details-description
|
||||
font-size: 1.3em
|
||||
padding: 15px 25px 25px
|
||||
|
||||
+media-xs
|
||||
padding:
|
||||
left: 0
|
||||
right: 0
|
||||
|
||||
img
|
||||
display: block
|
||||
margin: 0 auto
|
||||
|
||||
.item-meta
|
||||
color: $color-text-dark-secondary
|
||||
padding:
|
||||
left: 25px
|
||||
right: 25px
|
||||
|
||||
+media-xs
|
||||
padding:
|
||||
left: 10px
|
||||
right: 10px
|
||||
|
||||
.button-create,
|
||||
.button-edit
|
||||
+button($color-success, 3px, true)
|
||||
|
||||
.item-picture+.button-back+.button-edit
|
||||
right: 20px
|
||||
top: 20px
|
||||
|
||||
.comments-container
|
||||
padding:
|
||||
left: 20px
|
||||
right: 20px
|
||||
max-width: 680px
|
||||
margin: 0 auto
|
||||
|
||||
+media-lg
|
||||
padding:
|
||||
left: 0
|
||||
right: 0
|
||||
|
||||
.comment-reply-container
|
||||
background-color: transparent
|
||||
|
||||
.comment-reply-field
|
||||
textarea, .comment-reply-meta
|
||||
background-color: $color-background-light
|
||||
|
||||
&.filled
|
||||
.comment-reply-meta
|
||||
background-color: $color-success
|
||||
|
||||
.comment-reply-form
|
||||
+media-xs
|
||||
padding:
|
||||
left: 0
|
||||
|
||||
#blog_post-edit-form
|
||||
padding: 0
|
||||
|
||||
@@ -371,12 +373,6 @@
|
||||
+media-lg
|
||||
width: 25%
|
||||
|
||||
.button-create
|
||||
display: block
|
||||
width: 100%
|
||||
+button($color-success, 6px)
|
||||
margin: 0
|
||||
|
||||
.button-back
|
||||
+button($color-info, 6px, true)
|
||||
display: block
|
||||
@@ -475,105 +471,6 @@
|
||||
text-decoration: none
|
||||
color: $color-primary
|
||||
|
||||
|
||||
#blog_container
|
||||
&.cloud-blog
|
||||
#blog_index-container,
|
||||
#blog_post-create-container,
|
||||
#blog_post-edit-container
|
||||
width: 100%
|
||||
padding: 25px 30px 20px 30px
|
||||
|
||||
#blog_index-container+#blog_index-sidebar
|
||||
display: none
|
||||
|
||||
#blog_index-container,
|
||||
&.cloud-blog #blog_index-container
|
||||
+media-sm
|
||||
width: 100%
|
||||
|
||||
+media-xs
|
||||
width: 100%
|
||||
|
||||
padding: 0 0 50px 0
|
||||
margin: 0 auto
|
||||
|
||||
.blog_index-item
|
||||
+media-xs
|
||||
width: 100%
|
||||
padding: 10px 25px
|
||||
|
||||
&.list
|
||||
margin: 0 auto
|
||||
padding: 15px 0
|
||||
margin: 0 auto
|
||||
border-bottom: thin solid $color-background
|
||||
|
||||
&:last-child
|
||||
border-bottom: none
|
||||
|
||||
+media-xs
|
||||
width: 100%
|
||||
padding: 15px 10px
|
||||
margin: 0
|
||||
|
||||
a.item-title
|
||||
padding:
|
||||
top: 0
|
||||
bottom: 5px
|
||||
font:
|
||||
size: 1.6em
|
||||
weight: 400
|
||||
family: $font-body
|
||||
|
||||
.item-info
|
||||
color: $color-text-dark-secondary
|
||||
font-size: .9em
|
||||
padding:
|
||||
left: 25px
|
||||
right: 25px
|
||||
|
||||
.item-header
|
||||
width: 50px
|
||||
height: 50px
|
||||
position: absolute
|
||||
top: 20px
|
||||
border-radius: 3px
|
||||
background-color: $color-background
|
||||
overflow: hidden
|
||||
|
||||
img
|
||||
+position-center-translate
|
||||
width: 100%
|
||||
|
||||
i
|
||||
+position-center-translate
|
||||
font-size: 1.2em
|
||||
color: $color-text-dark-hint
|
||||
|
||||
&.nothumb
|
||||
border-radius: 50%
|
||||
|
||||
a.item-title, .item-info
|
||||
padding-left: 70px
|
||||
|
||||
#blog_index-container
|
||||
.blog_index-item
|
||||
position: relative
|
||||
|
||||
+media-xs
|
||||
padding: 25px 0 20px 0
|
||||
|
||||
&.list
|
||||
padding: 15px 10px
|
||||
margin: 0
|
||||
|
||||
+media-xs
|
||||
width: 100%
|
||||
padding: 15px 10px
|
||||
margin: 0
|
||||
|
||||
|
||||
.blog-archive-navigation
|
||||
+media-xs
|
||||
font-size: 1em
|
||||
@@ -603,16 +500,7 @@
|
||||
color: $color-text-dark-secondary
|
||||
pointer-events: none
|
||||
|
||||
.blog-action
|
||||
display: flex
|
||||
padding: 10px
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
z-index: 1
|
||||
|
||||
|
||||
// Specific tweaks for blogs in the context of a project
|
||||
// Specific tweaks for blogs in the context of a project.
|
||||
#project_context
|
||||
.blog_index-item
|
||||
+media-xs
|
||||
@@ -637,3 +525,29 @@
|
||||
|
||||
.blog-archive-navigation
|
||||
margin-left: 35px
|
||||
|
||||
// Used on the blog.
|
||||
.comments-compact
|
||||
.comments-list
|
||||
border: none
|
||||
padding: 0 0 15px 0
|
||||
|
||||
.comments-container
|
||||
max-width: 680px
|
||||
margin: 0 auto
|
||||
|
||||
.comment-reply-container
|
||||
background-color: transparent
|
||||
|
||||
.comment-reply-field
|
||||
textarea, .comment-reply-meta
|
||||
background-color: $color-background-light
|
||||
|
||||
&.filled
|
||||
.comment-reply-meta
|
||||
background-color: $color-success
|
||||
|
||||
.comment-reply-form
|
||||
+media-xs
|
||||
padding:
|
||||
left: 0
|
||||
|
72
src/styles/components/_alerts.sass
Normal file
72
src/styles/components/_alerts.sass
Normal file
@@ -0,0 +1,72 @@
|
||||
.alert
|
||||
margin-bottom: 0
|
||||
text-align: center
|
||||
padding: 10px 20px
|
||||
z-index: 16
|
||||
|
||||
// overriden by alert types
|
||||
color: $color-text-dark
|
||||
background-color: $color-background
|
||||
|
||||
&.alert-danger,
|
||||
&.alert-error
|
||||
background-color: lighten($color-danger, 35%)
|
||||
color: $color-danger
|
||||
.alert-icon, .close
|
||||
color: $color-danger
|
||||
|
||||
&.alert-warning
|
||||
background-color: lighten($color-warning, 20%)
|
||||
color: darken($color-warning, 20%)
|
||||
.alert-icon, .close
|
||||
color: darken($color-warning, 20%)
|
||||
|
||||
&.alert-success
|
||||
background-color: lighten($color-success, 45%)
|
||||
color: $color-success
|
||||
|
||||
.alert-icon, .close
|
||||
color: $color-success
|
||||
|
||||
&.alert-info
|
||||
background-color: lighten($color-info, 30%)
|
||||
color: darken($color-info, 10%)
|
||||
.alert-icon, .close
|
||||
color: darken($color-info, 10%)
|
||||
|
||||
button.close
|
||||
position: absolute
|
||||
right: 10px
|
||||
|
||||
i
|
||||
font-size: .8em
|
||||
|
||||
i.alert-icon
|
||||
&:before
|
||||
font-family: "pillar-font"
|
||||
padding-right: 10px
|
||||
|
||||
&.success:before
|
||||
content: '\e801'
|
||||
&.info:before
|
||||
content: "\e80c"
|
||||
&.warning:before
|
||||
content: "\e80b"
|
||||
&.danger:before,
|
||||
&.error:before
|
||||
content: "\e83d"
|
||||
|
||||
|
||||
/* When there's an alert, disable the fixed top */
|
||||
.alert+.navbar-fixed-top
|
||||
position: relative
|
||||
margin-bottom: 0
|
||||
|
||||
&+.container
|
||||
padding-top: 0
|
||||
|
||||
.alert+.navbar
|
||||
position: relative
|
||||
|
||||
.alert+.navbar+.page-content
|
||||
padding-top: 0
|
29
src/styles/components/_base.sass
Normal file
29
src/styles/components/_base.sass
Normal file
@@ -0,0 +1,29 @@
|
||||
body
|
||||
height: 100%
|
||||
|
||||
+media-sm
|
||||
width: 100%
|
||||
max-width: 100%
|
||||
min-width: auto
|
||||
|
||||
+media-xs
|
||||
width: 100%
|
||||
max-width: 100%
|
||||
min-width: auto
|
||||
|
||||
.container
|
||||
+media-xs
|
||||
max-width: 100%
|
||||
min-width: auto
|
||||
padding:
|
||||
left: 0
|
||||
right: 0
|
||||
|
||||
&.box
|
||||
+container-box
|
||||
|
||||
.page-content
|
||||
background-color: $white
|
||||
|
||||
.container-box
|
||||
+container-box
|
14
src/styles/components/_buttons.sass
Normal file
14
src/styles/components/_buttons.sass
Normal file
@@ -0,0 +1,14 @@
|
||||
.btn-outline
|
||||
background-color: transparent
|
||||
border-width: 1px
|
||||
transition: background-color .1s
|
||||
|
||||
&:focus, &:active
|
||||
box-shadow: none
|
||||
|
||||
.btn-empty
|
||||
background-color: transparent
|
||||
border-color: transparent
|
||||
|
||||
&:focus, &:active
|
||||
box-shadow: none
|
23
src/styles/components/_card.sass
Normal file
23
src/styles/components/_card.sass
Normal file
@@ -0,0 +1,23 @@
|
||||
.card-deck
|
||||
// Custom, as of bootstrap 4.1.3 there is no way to do this.
|
||||
&.card-3-columns
|
||||
.card
|
||||
min-width: 30%
|
||||
max-width: 30%
|
||||
|
||||
|
||||
.card-padless
|
||||
.card
|
||||
border: none
|
||||
|
||||
.card-body
|
||||
padding: 15px 0
|
||||
|
||||
.card-fade
|
||||
img
|
||||
opacity: .8
|
||||
transition: opacity ease-in-out 150ms
|
||||
|
||||
&:hover
|
||||
img
|
||||
opacity: 1
|
8
src/styles/components/_checkbox.sass
Normal file
8
src/styles/components/_checkbox.sass
Normal file
@@ -0,0 +1,8 @@
|
||||
.checkbox label label
|
||||
padding-left: 0
|
||||
|
||||
.checkbox label input[type=checkbox] + label
|
||||
transition: color 100ms ease-in-out
|
||||
|
||||
.checkbox label input[type=checkbox]:checked + label
|
||||
color: $color-success !important
|
13
src/styles/components/_dropdown.sass
Normal file
13
src/styles/components/_dropdown.sass
Normal file
@@ -0,0 +1,13 @@
|
||||
// Global, we want all menus to look like this.
|
||||
.dropdown-menu
|
||||
box-shadow: $dropdown-box-shadow
|
||||
top: 95% // So there is less gap between the dropdown and the item.
|
||||
|
||||
> li
|
||||
> a
|
||||
padding: $dropdown-item-padding-y
|
||||
|
||||
// Open dropdown on mouse hover dropdowns in the navbar.
|
||||
nav .dropdown:hover
|
||||
ul.dropdown-menu
|
||||
display: block
|
25
src/styles/components/_flyout.sass
Normal file
25
src/styles/components/_flyout.sass
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Flyouts (only used on notifications for now) */
|
||||
.flyout
|
||||
background-color: $color-background
|
||||
border-radius: 3px
|
||||
border: thin solid darken($color-background, 3%)
|
||||
box-shadow: 1px 2px 2px rgba(black, .2)
|
||||
display: block
|
||||
font-size: .9em
|
||||
|
||||
& .flyout-title
|
||||
cursor: default
|
||||
display: block
|
||||
float: left
|
||||
font-size: 1.1em
|
||||
font-weight: 600
|
||||
padding: 8px 10px 5px 10px
|
||||
|
||||
&.notifications
|
||||
max-height: 1000%
|
||||
overflow-x: hidden
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 60px
|
||||
width: 420px
|
||||
z-index: 9999
|
117
src/styles/components/_footer.sass
Normal file
117
src/styles/components/_footer.sass
Normal file
@@ -0,0 +1,117 @@
|
||||
/* FOOTER */
|
||||
.footer-wrapper
|
||||
background-color: $color-background
|
||||
position: relative
|
||||
|
||||
&:after
|
||||
background-color: $color-background
|
||||
bottom: 0
|
||||
content: ''
|
||||
position: fixed
|
||||
left: 0
|
||||
right: 0
|
||||
top: 0
|
||||
pointer-events: none
|
||||
z-index: -1
|
||||
|
||||
/* Footer Navigation */
|
||||
footer
|
||||
font-size: .75em
|
||||
padding: 0 0 10px 0
|
||||
|
||||
a
|
||||
color: $color-text-dark-primary
|
||||
|
||||
&:hover
|
||||
color: $color-primary
|
||||
|
||||
ul.links
|
||||
float: left
|
||||
padding: 0
|
||||
margin: 0
|
||||
list-style-type: none
|
||||
|
||||
li
|
||||
padding: 0 15px 0 0
|
||||
margin: 0
|
||||
float: left
|
||||
|
||||
#hop
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
visibility: hidden
|
||||
position: fixed
|
||||
right: 25px
|
||||
bottom: 25px
|
||||
z-index: 999
|
||||
cursor: pointer
|
||||
opacity: 0
|
||||
background: $color-background-light
|
||||
width: 32px
|
||||
height: 32px
|
||||
border-radius: 50%
|
||||
color: $color-text-dark-secondary
|
||||
font-size: 2em
|
||||
box-shadow: 0 0 15px rgba(black, .2)
|
||||
transform: scale(0.5)
|
||||
transition: all 150ms ease-in-out
|
||||
|
||||
&:hover
|
||||
transform: scale(1.2)
|
||||
background-color: $color-background-nav
|
||||
|
||||
&.active
|
||||
visibility: visible
|
||||
opacity: 1
|
||||
transform: scale(1)
|
||||
|
||||
|
||||
.footer-navigation
|
||||
font-size: .85em
|
||||
margin-bottom: 5px
|
||||
color: lighten($color-text, 30%)
|
||||
border-top: thick solid lighten($color-text, 60%)
|
||||
padding:
|
||||
top: 15px
|
||||
bottom: 15px
|
||||
|
||||
a
|
||||
color: lighten($color-text, 35%)
|
||||
|
||||
&:hover
|
||||
color: $color-primary
|
||||
|
||||
.footer-links
|
||||
i
|
||||
font-size: 80%
|
||||
position: absolute
|
||||
left: -14px
|
||||
top: 20%
|
||||
|
||||
.special
|
||||
padding:
|
||||
top: 10px
|
||||
bottom: 15px
|
||||
font-size: .9em
|
||||
border-left: thin solid darken($color-background, 20%)
|
||||
|
||||
|
||||
img
|
||||
max-width: 100%
|
||||
opacity: .6
|
||||
|
||||
ul.footer-social
|
||||
width: 100%
|
||||
text-align:center
|
||||
margin: 0 auto
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: space-around
|
||||
|
||||
li
|
||||
display: inline-block
|
||||
padding: 30px 0
|
||||
|
||||
i
|
||||
font-size: 3em
|
132
src/styles/components/_forms.sass
Normal file
132
src/styles/components/_forms.sass
Normal file
@@ -0,0 +1,132 @@
|
||||
/* File Upload forms */
|
||||
.fieldlist
|
||||
list-style: none
|
||||
padding: 0
|
||||
margin: 10px 0 0 0
|
||||
|
||||
li.fieldlist-item
|
||||
background-color: $color-background-light
|
||||
border: thin solid $color-background
|
||||
border-left: 3px solid $color-primary
|
||||
border-top-right-radius: 3px
|
||||
border-bottom-right-radius: 3px
|
||||
|
||||
margin-bottom: 10px
|
||||
padding: 10px
|
||||
+clearfix
|
||||
|
||||
.form-group
|
||||
margin-bottom: 0 !important // override bs
|
||||
width: 100%
|
||||
|
||||
input.form-control
|
||||
background-color: white !important
|
||||
padding: 0 10px !important
|
||||
border: thin solid $color-background-dark !important
|
||||
|
||||
div[class$="slug"]
|
||||
width: 50%
|
||||
float: left
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
label
|
||||
margin-right: 10px
|
||||
|
||||
.fieldlist-action-button
|
||||
+button($color-success, 3px)
|
||||
margin: 0 0 0 10px
|
||||
padding: 5px 10px
|
||||
text-transform: initial
|
||||
|
||||
.form-upload-file
|
||||
margin-bottom: 10px
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
.form-upload-progress
|
||||
margin-top: 10px
|
||||
|
||||
.form-upload-progress-bar
|
||||
margin-top: 5px
|
||||
background-color: $color-success
|
||||
height: 5px
|
||||
min-width: 0
|
||||
border-radius: 3px
|
||||
|
||||
&.progress-uploading
|
||||
background-color: hsl(hue($color-success), 80%, 65%) !important
|
||||
|
||||
&.progress-processing
|
||||
+stripes($color-success, lighten($color-success, 15%), -45deg, 25px)
|
||||
+stripes-animate
|
||||
animation-duration: 1s
|
||||
|
||||
&.progress-error
|
||||
background-color: $color-danger !important
|
||||
|
||||
.preview-thumbnail
|
||||
width: 50px
|
||||
height: 50px
|
||||
min-width: 50px
|
||||
min-height: 50px
|
||||
margin-right: 10px
|
||||
margin-top: 5px
|
||||
border-radius: 3px
|
||||
background-color: $color-background
|
||||
|
||||
.form-upload-file-meta-container
|
||||
display: flex
|
||||
|
||||
.form-upload-file-meta
|
||||
list-style: none
|
||||
padding: 0
|
||||
margin: 0
|
||||
width: 100%
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
flex: 1
|
||||
|
||||
li
|
||||
display: inline-block
|
||||
padding: 5px 10px
|
||||
|
||||
&:first-child
|
||||
padding-left: 0
|
||||
|
||||
&.dimensions, &.size
|
||||
color: $color-text-dark-secondary
|
||||
|
||||
&.delete
|
||||
margin-left: auto
|
||||
|
||||
&.name
|
||||
+text-overflow-ellipsis
|
||||
|
||||
.file_delete
|
||||
color: $color-danger
|
||||
|
||||
.form-upload-file-actions
|
||||
list-style: none
|
||||
padding: 0
|
||||
margin: 0
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
li
|
||||
display: inline-block
|
||||
padding: 5px 10px
|
||||
|
||||
.file_delete
|
||||
color: $color-danger
|
||||
|
||||
.form-group
|
||||
&.error
|
||||
.form-control, input
|
||||
border-color: $color-danger !important
|
||||
|
||||
ul.error
|
||||
padding: 5px 0 0 0
|
||||
margin: 0
|
||||
color: $color-danger
|
||||
list-style-type: none
|
38
src/styles/components/_inputs.sass
Normal file
38
src/styles/components/_inputs.sass
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Inputs */
|
||||
input, input.form-control,
|
||||
textarea, textarea.form-control,
|
||||
select, select.form-control
|
||||
+input-generic
|
||||
|
||||
label, label.control-label
|
||||
+label-generic
|
||||
|
||||
select, select.form-control
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
background-color: $color-background-light
|
||||
|
||||
option
|
||||
background-color: white
|
||||
|
||||
input.fileupload
|
||||
background-color: transparent
|
||||
display: block
|
||||
margin-top: 10px
|
||||
|
||||
textarea
|
||||
resize: vertical
|
||||
|
||||
button, .btn
|
||||
&.disabled
|
||||
opacity: .5 !important
|
||||
pointer-events: none !important
|
||||
text-shadow: none !important
|
||||
user-select: none !important
|
||||
|
||||
.input-group-flex
|
||||
display: flex
|
||||
|
||||
.input-group-separator
|
||||
margin: 10px 0
|
||||
border-top: thin solid $color-background
|
28
src/styles/components/_jumbotron.sass
Normal file
28
src/styles/components/_jumbotron.sass
Normal file
@@ -0,0 +1,28 @@
|
||||
// Mainly overrides bootstrap jumbotron settings
|
||||
.jumbotron
|
||||
background-size: cover
|
||||
border-radius: 0
|
||||
padding-top: 10em
|
||||
padding-bottom: 10em
|
||||
|
||||
// Black-transparent gradient from left to right to better read the overlay text.
|
||||
&.jumbotron-overlay
|
||||
position: relative
|
||||
|
||||
&:after
|
||||
background-image: linear-gradient(45deg, rgba(black, .5) 25%, transparent 50%)
|
||||
bottom: 0
|
||||
content: ''
|
||||
left: 0
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
|
||||
*
|
||||
z-index: 1
|
||||
|
||||
h2, p
|
||||
text-shadow: 1px 1px rgba(black, .2), 1px 1px 25px rgba(black, .5)
|
||||
|
||||
&:hover
|
||||
text-decoration: none
|
201
src/styles/components/_navbar.sass
Normal file
201
src/styles/components/_navbar.sass
Normal file
@@ -0,0 +1,201 @@
|
||||
// Navigation.
|
||||
|
||||
.navbar
|
||||
box-shadow: inset 0 -2px $color-background
|
||||
|
||||
.navbar,
|
||||
nav.sidebar
|
||||
border: none
|
||||
color: $color-text-dark-secondary
|
||||
padding: 0
|
||||
z-index: $z-index-base + 5 /* Flowplayer seems to take up to 11, project container is 12 */
|
||||
|
||||
nav
|
||||
margin-left: auto
|
||||
margin-right: 0
|
||||
|
||||
.navbar-nav
|
||||
margin-right: 0
|
||||
+media-xs
|
||||
margin: 0
|
||||
width: 100%
|
||||
|
||||
.navbar-item
|
||||
align-items: center
|
||||
display: flex
|
||||
user-select: none
|
||||
color: inherit
|
||||
|
||||
+media-sm
|
||||
padding-left: 10px
|
||||
padding-right: 10px
|
||||
|
||||
&:hover, &:focus
|
||||
color: $primary
|
||||
background-color: transparent
|
||||
box-shadow: inset 0 -3px 0 $primary
|
||||
text-decoration: none
|
||||
|
||||
&:focus
|
||||
box-shadow: inset 0 -3px 0 $primary
|
||||
|
||||
&.active
|
||||
color: $primary
|
||||
box-shadow: inset 0 -3px 0 $primary
|
||||
|
||||
li
|
||||
user-select: none
|
||||
position: relative
|
||||
|
||||
img.gravatar
|
||||
border-radius: 999em
|
||||
height: 32px
|
||||
width: 32px
|
||||
box-shadow: 1px 1px 0 rgba(black, .2)
|
||||
position: relative
|
||||
|
||||
.special
|
||||
width: 18px
|
||||
height: 18px
|
||||
border-radius: 999em
|
||||
position: absolute
|
||||
background-color: white
|
||||
z-index: 2
|
||||
display: inline-block
|
||||
top: 10px
|
||||
left: 38px
|
||||
font-size: 1.2em
|
||||
box-shadow: 1px 1px 1px rgba(black, .2)
|
||||
|
||||
&.subscriber
|
||||
background-color: $color-success
|
||||
color: white
|
||||
font-size: .6em
|
||||
|
||||
&.demo
|
||||
background-color: $color-info
|
||||
color: white
|
||||
font-size: .6em
|
||||
|
||||
&.none
|
||||
color: $color-danger
|
||||
|
||||
i
|
||||
+position-center-translate
|
||||
|
||||
.dropdown
|
||||
min-width: 60px // navbar avatar size
|
||||
|
||||
span.fa-stack
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a
|
||||
white-space: nowrap
|
||||
|
||||
&:hover
|
||||
box-shadow: none // removes underline
|
||||
|
||||
&.subitem // e.g. "Not Sintel? Log out"
|
||||
font-size: .8em
|
||||
padding-top: 0
|
||||
text-transform: initial
|
||||
|
||||
i
|
||||
width: 30px
|
||||
|
||||
&.subscription-status
|
||||
&.none a
|
||||
color: $color-danger
|
||||
|
||||
&.subscriber a
|
||||
color: $color-success
|
||||
|
||||
&.demo a
|
||||
color: $color-info
|
||||
|
||||
span.info
|
||||
display: block
|
||||
|
||||
span.renew
|
||||
display: block
|
||||
color: $color-text-dark-primary
|
||||
font-size: .9em
|
||||
|
||||
|
||||
// Secondary navigation for
|
||||
.nav-secondary
|
||||
align-items: center
|
||||
box-shadow: inset 0 -2px 0 0 $color-background
|
||||
|
||||
.nav-link
|
||||
color: $color-text
|
||||
cursor: pointer
|
||||
transition: box-shadow 150ms ease-in-out
|
||||
|
||||
&:hover,
|
||||
&.active
|
||||
box-shadow: inset 0 -2px 0 0 $primary
|
||||
|
||||
|
||||
.navbar-overlay
|
||||
+media-lg
|
||||
display: block
|
||||
bottom: 0
|
||||
display: none
|
||||
left: 0
|
||||
height: 100%
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
transition: background-color 350ms ease-in-out
|
||||
width: 100%
|
||||
z-index: 0
|
||||
|
||||
&.is-active
|
||||
background-color: $color-background-nav
|
||||
text-shadow: none
|
||||
|
||||
.navbar-brand
|
||||
color: inherit
|
||||
padding-left: 4px
|
||||
|
||||
&:hover
|
||||
color: $primary
|
||||
|
||||
nav.navbar
|
||||
.navbar-collapse
|
||||
> ul > li > .navbar-item
|
||||
padding: $navbar-nav-link-padding-x
|
||||
height: $nav-link-height
|
||||
|
||||
|
||||
.navbar-backdrop-container
|
||||
width: 100%
|
||||
height: 100%
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
|
||||
img
|
||||
display: none
|
||||
position: fixed
|
||||
width: 100%
|
||||
align-self: flex-start
|
||||
+media-md
|
||||
display: block
|
||||
+media-lg
|
||||
display: block
|
||||
|
||||
|
||||
.nav-tabs .dropdown-menu, .nav-pills .dropdown-menu
|
||||
margin-top: 0
|
||||
|
||||
.navbar+.page-content
|
||||
padding-top: $nav-link-height
|
75
src/styles/components/_overlay.sass
Normal file
75
src/styles/components/_overlay.sass
Normal file
@@ -0,0 +1,75 @@
|
||||
#page-overlay
|
||||
background-color: rgba(black, .8)
|
||||
position: fixed
|
||||
top: 0
|
||||
bottom: 0
|
||||
right: 0
|
||||
left: 0
|
||||
z-index: $z-index-base + 15
|
||||
visibility: hidden
|
||||
opacity: 0
|
||||
transition: opacity 150ms ease-in-out
|
||||
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
|
||||
img
|
||||
user-select: none
|
||||
display: block
|
||||
max-height: 96%
|
||||
max-width: 96%
|
||||
z-index: 0
|
||||
|
||||
box-shadow: 0 0 15px rgba(black, .2), 0 0 100px rgba(black, .5)
|
||||
p.caption
|
||||
position: absolute
|
||||
bottom: 1%
|
||||
|
||||
&.active
|
||||
visibility: visible
|
||||
opacity: 1
|
||||
|
||||
.no-preview
|
||||
user-select: none
|
||||
z-index: 0
|
||||
color: $color-text-light-secondary
|
||||
|
||||
.nav-prev, .nav-next
|
||||
display: block
|
||||
font:
|
||||
family: 'pillar-font'
|
||||
size: 2em
|
||||
height: 80%
|
||||
width: 50px
|
||||
cursor: pointer
|
||||
color: $color-text-light-secondary
|
||||
z-index: 1
|
||||
+position-center-translate
|
||||
|
||||
&:hover
|
||||
color: $color-text-light
|
||||
|
||||
&:before, &:after
|
||||
+position-center-translate
|
||||
|
||||
.nav-prev
|
||||
left: 50px
|
||||
&:before
|
||||
content: '\e839'
|
||||
|
||||
.nav-next
|
||||
left: initial
|
||||
right: 0
|
||||
&:before
|
||||
content: '\e83a'
|
||||
|
||||
|
||||
&.video
|
||||
.video-embed
|
||||
+position-center-translate
|
||||
position: fixed
|
||||
|
||||
iframe
|
||||
width: 853px
|
||||
height: 480px
|
26
src/styles/components/_popover.sass
Normal file
26
src/styles/components/_popover.sass
Normal file
@@ -0,0 +1,26 @@
|
||||
.popover
|
||||
background-color: lighten($color-background-nav, 5%)
|
||||
border-radius: 3px
|
||||
box-shadow: 1px 1px 2px rgba(black, .2)
|
||||
border: thin solid lighten($color-background-nav, 10%)
|
||||
|
||||
&.in
|
||||
opacity: 1
|
||||
|
||||
.popover-title
|
||||
background-color: lighten($color-background-nav, 10%)
|
||||
border-bottom: thin solid lighten($color-background-nav, 3%)
|
||||
color: $color-text-light-primary
|
||||
|
||||
.popover-content
|
||||
color: $color-text-light
|
||||
font-size: .9em
|
||||
|
||||
&.top .arrow:after
|
||||
border-top-color: lighten($color-background-nav, 5%)
|
||||
&.bottom .arrow:after
|
||||
border-bottom-color: lighten($color-background-nav, 5%)
|
||||
&.left .arrow:after
|
||||
border-left-color: lighten($color-background-nav, 5%)
|
||||
&.right .arrow:after
|
||||
border-right-color: lighten($color-background-nav, 5%)
|
87
src/styles/components/_search.sass
Normal file
87
src/styles/components/_search.sass
Normal file
@@ -0,0 +1,87 @@
|
||||
#search-overlay
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
width: 100%
|
||||
height: 100%
|
||||
pointer-events: none
|
||||
visibility: hidden
|
||||
opacity: 0
|
||||
z-index: $z-index-base + 4
|
||||
transition: opacity 150ms ease-in-out
|
||||
|
||||
&.active
|
||||
opacity: 1
|
||||
visibility: visible
|
||||
background-color: rgba($color-background-nav, .7)
|
||||
|
||||
.search-input
|
||||
+media-lg
|
||||
max-width: 350px
|
||||
+media-md
|
||||
max-width: 350px
|
||||
+media-sm
|
||||
max-width: 120px
|
||||
+media-xs
|
||||
display: block
|
||||
margin: 0 10px
|
||||
position: absolute
|
||||
z-index: $z-index-base
|
||||
right: 5px
|
||||
position: relative
|
||||
float: left
|
||||
padding: 0
|
||||
margin: 0
|
||||
|
||||
.search-icon
|
||||
position: absolute
|
||||
top: 4px
|
||||
left: 10px
|
||||
cursor: pointer
|
||||
|
||||
&:after
|
||||
@extend .tooltip-inner
|
||||
|
||||
content: 'Use advanced search...'
|
||||
font-size: .85em
|
||||
font-style: normal
|
||||
left: -10px
|
||||
opacity: 0
|
||||
pointer-events: none
|
||||
position: absolute
|
||||
top: 30px
|
||||
transition: top 150ms ease-in-out, opacity 150ms ease-in-out
|
||||
width: 150px
|
||||
|
||||
&:hover
|
||||
&:after
|
||||
opacity: 1
|
||||
top: 35px
|
||||
|
||||
|
||||
#cloud-search, .tt-hint
|
||||
+text-overflow-ellipsis
|
||||
border: thin solid $color-background
|
||||
border-radius: 3px
|
||||
font:
|
||||
size: 1em
|
||||
weight: 400
|
||||
margin: 0
|
||||
min-height: 32px
|
||||
outline: none
|
||||
padding: 0 20px 0 40px
|
||||
transition: border 100ms ease-in-out
|
||||
|
||||
&:focus
|
||||
box-shadow: none
|
||||
border: none
|
||||
|
||||
&::placeholder
|
||||
color: rgba($color-text, .5)
|
||||
transition: color 150ms ease-in-out
|
||||
|
||||
&:hover
|
||||
&::placeholder
|
||||
color: rgba($color-text, .6)
|
6
src/styles/components/_shortcode.sass
Normal file
6
src/styles/components/_shortcode.sass
Normal file
@@ -0,0 +1,6 @@
|
||||
p.shortcode.nocap
|
||||
padding: 0.6em 3em
|
||||
font-size: .8em
|
||||
color: $color-text-dark-primary
|
||||
background-color: $color-background-dark
|
||||
border-radius: 3px
|
21
src/styles/components/_statusbar.sass
Normal file
21
src/styles/components/_statusbar.sass
Normal file
@@ -0,0 +1,21 @@
|
||||
#status-bar
|
||||
opacity: 0
|
||||
transition: all 250ms ease-in-out
|
||||
|
||||
i
|
||||
margin-right: 5px
|
||||
|
||||
&.info
|
||||
color: $color-info
|
||||
&.error
|
||||
color: $color-danger
|
||||
&.warning
|
||||
color: $color-warning
|
||||
&.success
|
||||
color: $color-success
|
||||
&.default
|
||||
color: $color-text-light
|
||||
|
||||
&.active
|
||||
opacity: 1
|
||||
|
5
src/styles/components/_tooltip.sass
Normal file
5
src/styles/components/_tooltip.sass
Normal file
@@ -0,0 +1,5 @@
|
||||
.tooltip
|
||||
transition: none
|
||||
|
||||
.tooltip-inner
|
||||
white-space: nowrap
|
@@ -1,7 +1,74 @@
|
||||
/* SCROLL TO READ ABOUT UPDATING THIS FILE FROM FONTELLO */
|
||||
|
||||
/* Makes it possible to override the path before importing font-pillar.sass */
|
||||
$pillar-font-path: "../font" !default
|
||||
|
||||
/* Font aliases */
|
||||
.pi /* blank item with the right spacing */
|
||||
&:after
|
||||
content: ''
|
||||
font-family: "pillar-font"
|
||||
font-style: normal
|
||||
font-weight: normal
|
||||
speak: none
|
||||
display: inline-block
|
||||
text-decoration: inherit
|
||||
width: 1em
|
||||
margin-right: .2em
|
||||
text-align: center
|
||||
font-variant: normal
|
||||
text-transform: none
|
||||
line-height: 1em
|
||||
margin-left: .2em
|
||||
-webkit-font-smoothing: antialiased
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
position: relative
|
||||
|
||||
&:before
|
||||
position: relative
|
||||
|
||||
.pi-license-cc-zero:before
|
||||
content: '\e85a'
|
||||
.pi-license-cc-sa:before
|
||||
content: '\e858'
|
||||
.pi-license-cc-nd:before
|
||||
content: '\e859'
|
||||
.pi-license-cc-nc:before
|
||||
content: '\e857'
|
||||
|
||||
.pi-license-cc-0
|
||||
@extend .pi-license-cc-zero
|
||||
position: relative
|
||||
top: 1px
|
||||
.pi-license-cc-by-sa
|
||||
@extend .pi-license-cc-sa
|
||||
.pi-license-cc-by-nd
|
||||
@extend .pi-license-cc-nd
|
||||
.pi-license-cc-by-nc
|
||||
@extend .pi-license-cc-nc
|
||||
|
||||
.pi-license-cc-by-sa,
|
||||
.pi-license-cc-by-nd,
|
||||
.pi-license-cc-by-nc
|
||||
@extend .pi
|
||||
|
||||
&:after
|
||||
content: '\e807'
|
||||
left: -27px
|
||||
|
||||
&:before
|
||||
left: 27px
|
||||
|
||||
/*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Here begins the CSS code generated by fontello.com, converted to *
|
||||
* Sass and replaced the path with our variable $pillar-font-path. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
|
||||
@font-face
|
||||
font-family: 'pillar-font'
|
||||
src: url('../font/pillar-font.eot?55726379')
|
||||
src: url('../font/pillar-font.eot?55726379#iefix') format("embedded-opentype"), url('../font/pillar-font.woff2?55726379') format("woff2"), url('../font/pillar-font.woff?55726379') format("woff")
|
||||
src: url('#{$pillar-font-path}/pillar-font.woff?55726379') format("woff"), url('#{$pillar-font-path}/pillar-font.woff2?55726379') format("woff2")
|
||||
font-weight: normal
|
||||
font-style: normal
|
||||
|
||||
|
@@ -1,15 +1,84 @@
|
||||
@import _normalize
|
||||
// Bootstrap variables and utilities.
|
||||
@import "../../node_modules/bootstrap/scss/functions"
|
||||
@import "../../node_modules/bootstrap/scss/variables"
|
||||
@import "../../node_modules/bootstrap/scss/mixins"
|
||||
|
||||
@import _config
|
||||
@import _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 "apps_base"
|
||||
@import "components/base"
|
||||
|
||||
@import "components/jumbotron"
|
||||
@import "components/alerts"
|
||||
@import "components/navbar"
|
||||
@import "components/dropdown"
|
||||
@import "components/footer"
|
||||
@import "components/shortcode"
|
||||
@import "components/statusbar"
|
||||
@import "components/search"
|
||||
|
||||
@import "components/flyout"
|
||||
@import "components/forms"
|
||||
@import "components/inputs"
|
||||
@import "components/buttons"
|
||||
@import "components/popover"
|
||||
@import "components/tooltip"
|
||||
@import "components/checkbox"
|
||||
@import "components/overlay"
|
||||
@import "components/card"
|
||||
|
||||
|
||||
/* Generic styles (comments, notifications, etc) come from base.css */
|
||||
@import _notifications
|
||||
@import _comments
|
||||
|
||||
@import _project
|
||||
@import _project-sharing
|
||||
@import _project-dashboard
|
||||
@import _user
|
||||
@import _search
|
||||
@import _organizations
|
||||
@import _search
|
||||
|
||||
/* services, about, etc */
|
||||
@import _pages
|
||||
|
@@ -1,9 +1,8 @@
|
||||
/* jsTree overrides */
|
||||
|
||||
$tree-color-text: $color-text-dark-primary
|
||||
$tree-color-highlight: hsl(hue($color-background-active), 40%, 50%)
|
||||
$tree-color-highlight-background: hsl(hue($color-background-active), 40%, 50%)
|
||||
$tree-color-highlight-background-text: white
|
||||
$tree-color-highlight: $color-primary-accent
|
||||
$tree-color-highlight-background: $white
|
||||
$tree-color-highlight-background-text: $primary
|
||||
|
||||
.jstree-default
|
||||
/* list item */
|
||||
@@ -63,49 +62,48 @@ $tree-color-highlight-background-text: white
|
||||
&.jstree-open
|
||||
/* Text of children for an open tree (like a folder) */
|
||||
.jstree-children > .jstree-node
|
||||
padding-left: 15px !important
|
||||
padding-left: 16px !important
|
||||
|
||||
.jstree-icon:empty
|
||||
left: 20px !important
|
||||
|
||||
// Tweaks for specific icons
|
||||
&.pi-file-archive
|
||||
left: 22px !important
|
||||
left: 25px !important
|
||||
&.pi-folder
|
||||
left: 21px !important
|
||||
left: 20px !important
|
||||
font-size: .9em !important
|
||||
&.pi-film-thick
|
||||
left: 22px !important
|
||||
&.pi-splay
|
||||
left: 20px !important
|
||||
font-size: .85em !important
|
||||
|
||||
.jstree-anchor
|
||||
box-shadow: inset 1px 0 0 0 rgba($tree-color-text, .2)
|
||||
// box-shadow: inset 1px 0 0 0 $color-background
|
||||
|
||||
/* Closed Folder */
|
||||
// &.jstree-closed
|
||||
|
||||
&.jstree-open .jstree-icon.jstree-ocl,
|
||||
&.jstree-closed .jstree-icon.jstree-ocl
|
||||
float: left
|
||||
min-width: 30px
|
||||
opacity: 0
|
||||
position: absolute
|
||||
z-index: 1
|
||||
opacity: 0
|
||||
min-width: 30px
|
||||
float: left
|
||||
|
||||
/* The text of the last level item */
|
||||
.jstree-anchor
|
||||
+media-xs
|
||||
width: 98%
|
||||
padding: 0 !important
|
||||
width: 98%
|
||||
border: none
|
||||
font-size: 13px
|
||||
height: inherit
|
||||
line-height: 26px
|
||||
line-height: 24px
|
||||
overflow: hidden
|
||||
padding-left: 28px
|
||||
padding-right: 10px
|
||||
text-overflow: ellipsis
|
||||
transition: none
|
||||
transition: color 50ms ease-in-out, background-color 100ms ease-in-out
|
||||
white-space: nowrap
|
||||
width: 100%
|
||||
|
||||
@@ -113,7 +111,7 @@ $tree-color-highlight-background-text: white
|
||||
&:after
|
||||
content: '\e83a' !important
|
||||
font-family: 'pillar-font'
|
||||
color: white
|
||||
color: $tree-color-highlight-background-text
|
||||
display: none
|
||||
position: absolute
|
||||
right: 7px
|
||||
@@ -121,31 +119,31 @@ $tree-color-highlight-background-text: white
|
||||
|
||||
// Icon, not selected
|
||||
.jstree-icon
|
||||
color: $color-text-dark-secondary
|
||||
font-size: 95% !important
|
||||
color: $tree-color-text
|
||||
margin: 0 !important
|
||||
|
||||
/* Selected item */
|
||||
&.jstree-clicked
|
||||
background-color: $tree-color-highlight-background !important
|
||||
color: white !important
|
||||
color: $tree-color-highlight-background-text !important
|
||||
font-weight: bold
|
||||
|
||||
&:after
|
||||
display: block
|
||||
color: white !important
|
||||
color: $tree-color-highlight-background-text !important
|
||||
|
||||
.jstree-ocl,
|
||||
.jstree-icon
|
||||
color: white
|
||||
color: $tree-color-highlight-background-text
|
||||
|
||||
/* hover an active item */
|
||||
&.jstree-hovered
|
||||
background-color: lighten($tree-color-highlight-background, 10%) !important
|
||||
box-shadow: none
|
||||
color: white !important
|
||||
color: $tree-color-highlight-background-text !important
|
||||
|
||||
&.jstree-hovered .jstree-icon
|
||||
color: white !important
|
||||
color: $tree-color-highlight-background-text !important
|
||||
|
||||
.jstree-hovered
|
||||
background-color: rgba($tree-color-highlight, .1) !important
|
||||
@@ -184,8 +182,8 @@ $tree-color-highlight-background-text: white
|
||||
position: absolute
|
||||
|
||||
&:empty
|
||||
line-height: 26px
|
||||
left: 5px
|
||||
line-height: 24px
|
||||
left: 3px
|
||||
|
||||
&.is_subscriber
|
||||
.jstree-node
|
||||
@@ -269,7 +267,7 @@ $tree-color-highlight-background-text: white
|
||||
|
||||
.jstree-default .jstree-node.jstree-closed .jstree-icon.jstree-ocl + .jstree-anchor,
|
||||
.jstree-default .jstree-node.jstree-open .jstree-icon.jstree-ocl + .jstree-anchor
|
||||
padding-left: 28px !important
|
||||
padding-left: 24px !important
|
||||
|
||||
/* hovered text */
|
||||
.jstree-default .jstree-hovered,
|
||||
@@ -280,11 +278,11 @@ $tree-color-highlight-background-text: white
|
||||
|
||||
a.jstree-anchor.jstree-clicked+ul li a.jstree-anchor.jstree-clicked
|
||||
background-color: rgba($tree-color-highlight-background, .8) !important
|
||||
color: white !important
|
||||
color: $tree-color-highlight-background-text !important
|
||||
|
||||
a.jstree-anchor.jstree-clicked+ul li a.jstree-anchor.jstree-clicked+ul li a.jstree-anchor.jstree-clicked
|
||||
background-color: rgba($tree-color-highlight-background, .8) !important
|
||||
color: white !important
|
||||
color: $tree-color-highlight-background-text !important
|
||||
|
||||
i.jstree-icon.jstree-ocl
|
||||
color: rgba($tree-color-text, .5) !important
|
||||
|
@@ -1,5 +1,5 @@
|
||||
$videoplayer-controls-color: white
|
||||
$videoplayer-background-color: $color-background-nav
|
||||
$videoplayer-background-color: $black
|
||||
|
||||
.video-js
|
||||
.vjs-big-play-button:before, .vjs-control:before, .vjs-modal-dialog
|
||||
|
@@ -1,8 +1,76 @@
|
||||
@import _normalize
|
||||
// Bootstrap variables and utilities.
|
||||
@import "../../node_modules/bootstrap/scss/functions"
|
||||
@import "../../node_modules/bootstrap/scss/variables"
|
||||
@import "../../node_modules/bootstrap/scss/mixins"
|
||||
|
||||
@import _config
|
||||
@import _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 "apps_base"
|
||||
@import "components/base"
|
||||
|
||||
@import "components/jumbotron"
|
||||
@import "components/alerts"
|
||||
@import "components/navbar"
|
||||
@import "components/dropdown"
|
||||
@import "components/footer"
|
||||
@import "components/shortcode"
|
||||
@import "components/statusbar"
|
||||
@import "components/search"
|
||||
|
||||
@import "components/flyout"
|
||||
@import "components/forms"
|
||||
@import "components/inputs"
|
||||
@import "components/buttons"
|
||||
@import "components/popover"
|
||||
@import "components/tooltip"
|
||||
@import "components/checkbox"
|
||||
@import "components/overlay"
|
||||
@import "components/card"
|
||||
|
||||
@import _notifications
|
||||
@import _comments
|
||||
|
||||
@import _project
|
||||
@import _project-sharing
|
||||
@import _project-dashboard
|
||||
|
@@ -7,8 +7,7 @@ $color-theatre-background-dark: darken($color-theatre-background, 5%)
|
||||
|
||||
$theatre-width: 350px
|
||||
|
||||
body.theatre,
|
||||
body.theatre .container-page
|
||||
body.theatre
|
||||
background-color: $color-theatre-background
|
||||
nav.navbar
|
||||
+media-lg
|
||||
@@ -26,6 +25,7 @@ body.theatre .container-page
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
|
||||
.page-body
|
||||
height: 100%
|
||||
width: 100%
|
||||
|
@@ -6,20 +6,21 @@
|
||||
| {% if node_type_name == 'group' %}
|
||||
| {% set node_type_name = 'folder' %}
|
||||
| {% endif %}
|
||||
li(class="button-{{ node_type['name'] }}")
|
||||
li.dropdown-item(class="button-{{ node_type['name'] }}")
|
||||
a.item_add_node(
|
||||
href="#",
|
||||
title="{{ node_type['description'] }}",
|
||||
data-node-type-name="{{ node_type['name'] }}",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
href="#",
|
||||
title="{{ node_type['description'] }}",
|
||||
data-node-type-name="{{ node_type['name'] }}",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
i.pi(class="icon-{{ node_type['name'] }}")
|
||||
| {% if node_type_name == 'group_texture' %}
|
||||
| Texture Folder
|
||||
| {% elif node_type_name == 'group_hdri' %}
|
||||
| HDRi Folder
|
||||
| {% else %}
|
||||
| {{ node_type_name }}
|
||||
span.text-capitalize
|
||||
|{{ node_type_name }}
|
||||
| {% endif %}
|
||||
| {% endif %}
|
||||
| {% endfor %}
|
||||
|
@@ -28,15 +28,15 @@
|
||||
span Add files...
|
||||
input(type='file', name='file', multiple='')
|
||||
|
||||
button.btn.btn-primary.start(type='submit')
|
||||
button.btn.btn-outline-primary.start(type='submit')
|
||||
i.pi-upload
|
||||
span Start upload
|
||||
span Start Upload
|
||||
|
||||
button.btn.btn-warning.cancel(type='reset')
|
||||
button.btn.btn-outline-warning.cancel(type='reset')
|
||||
i.pi-cancel
|
||||
span Cancel upload
|
||||
span Cancel Upload
|
||||
|
||||
button.btn.btn-danger.delete(type='button')
|
||||
button.btn.btn-outline-danger.delete(type='button')
|
||||
i.pi-trash
|
||||
span Delete
|
||||
|
||||
|
@@ -23,7 +23,7 @@ script#template-upload(type="text/x-tmpl").
|
||||
</button>
|
||||
{% } %}
|
||||
{% if (!i) { %}
|
||||
<button class="btn btn-warning cancel">
|
||||
<button class="btn btn-outline-secondary cancel">
|
||||
<i class="ion-close-round"></i>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
@@ -61,7 +61,7 @@ script#template-download(type="text/x-tmpl").
|
||||
</td>
|
||||
<td>
|
||||
{% if (file.deleteUrl) { %}
|
||||
<button class="btn btn-danger delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields='{"withCredentials":true}'{% } %}>
|
||||
<button class="btn btn-outline-danger delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields='{"withCredentials":true}'{% } %}>
|
||||
<i class="ion-trash-b"></i>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
@@ -71,7 +71,7 @@ script#template-download(type="text/x-tmpl").
|
||||
Create
|
||||
</div>
|
||||
{% } else { %}
|
||||
<button class="btn btn-warning cancel">
|
||||
<button class="btn btn-outline-secondary cancel">
|
||||
<i class="ion-close-round"></i>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
|
@@ -1,26 +0,0 @@
|
||||
| {% macro navigation_tabs(title) %}
|
||||
|
||||
nav#nav-tabs
|
||||
ul#nav-tabs__list
|
||||
li.nav-tabs__list-tab(
|
||||
class="{% if title == 'homepage' %}active{% endif %}")
|
||||
a(href="{{ url_for('main.homepage') }}") Activity
|
||||
|
||||
li.nav-tabs__list-tab(
|
||||
class="{% if title == 'home' %}active{% endif %}")
|
||||
a(href="{{ url_for('projects.home_project') }}") Home
|
||||
|
||||
li.nav-tabs__list-tab(
|
||||
class="{% if title == 'dashboard' %}active{% endif %}")
|
||||
a(href="{{ url_for('projects.index') }}") My Projects
|
||||
|
||||
| {% if current_user.has_organizations() %}
|
||||
li.nav-tabs__list-tab(
|
||||
class="{% if title == 'organizations' %}active{% endif %}")
|
||||
a(
|
||||
href="{{ url_for('pillar.web.organizations.index') }}",
|
||||
title="My Organizations")
|
||||
| My Organizations
|
||||
| {% endif %}
|
||||
|
||||
| {% endmacro %}
|
@@ -9,5 +9,5 @@
|
||||
.modal-body
|
||||
| ...
|
||||
.modal-footer
|
||||
button.btn.btn-default(type='button', data-dismiss='modal') Close
|
||||
button.btn.btn-outline-secondary(type='button', data-dismiss='modal') Close
|
||||
button.btn.btn-primary(type='button') Save changes
|
||||
|
@@ -66,10 +66,9 @@ html(lang="en")
|
||||
| {% if not title %}{% set title="default" %}{% endif %}
|
||||
|
||||
body(class="{{ title }}")
|
||||
.container-page
|
||||
.page-content
|
||||
.page-body
|
||||
| {% block body %}{% endblock %}
|
||||
.page-content
|
||||
.page-body
|
||||
| {% block body %}{% endblock %}
|
||||
|
||||
| {% block footer_container %}
|
||||
#footer-container
|
||||
|
@@ -1,7 +1,7 @@
|
||||
| {% block menu_body %}
|
||||
| {% if current_user.is_authenticated %}
|
||||
|
||||
li(class="dropdown")
|
||||
li.dropdown
|
||||
| {% block menu_avatar %}
|
||||
a.navbar-item.dropdown-toggle(href="#", data-toggle="dropdown", title="{{ current_user.email }}")
|
||||
img.gravatar(
|
||||
@@ -9,42 +9,38 @@ li(class="dropdown")
|
||||
alt="Avatar")
|
||||
| {% endblock menu_avatar %}
|
||||
|
||||
ul.dropdown-menu
|
||||
ul.dropdown-menu.dropdown-menu-right
|
||||
| {% if not current_user.has_role('protected') %}
|
||||
| {% block menu_list %}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.home_project') }}"
|
||||
title="Home")
|
||||
i.pi-home
|
||||
| Home
|
||||
| #[i.pi-home] Home
|
||||
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.index') }}"
|
||||
title="My Projects")
|
||||
i.pi-star
|
||||
| My Projects
|
||||
| #[i.pi-star] My Projects
|
||||
|
||||
| {% if current_user.has_organizations() %}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('pillar.web.organizations.index') }}"
|
||||
title="My Organizations")
|
||||
i.pi-users
|
||||
| My Organizations
|
||||
| #[i.pi-users] My Organizations
|
||||
| {% endif %}
|
||||
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('settings.profile') }}"
|
||||
title="Settings")
|
||||
i.pi-cog
|
||||
| Settings
|
||||
| #[i.pi-cog] Settings
|
||||
|
||||
| {% endblock menu_list %}
|
||||
|
||||
li.divider(role="separator")
|
||||
li.dropdown-divider(role="separator")
|
||||
| {% endif %}
|
||||
|
||||
li
|
||||
@@ -59,8 +55,9 @@ li(class="dropdown")
|
||||
|
||||
| {% else %}
|
||||
|
||||
li.nav-item-sign-in
|
||||
a.navbar-item(href="{{ url_for('users.login') }}")
|
||||
| Log in
|
||||
li.pt-1.pr-1
|
||||
a.btn.btn-sm.btn-outline-primary.px-3(
|
||||
href="{{ url_for('users.login') }}")
|
||||
| Log In
|
||||
| {% endif %}
|
||||
| {% endblock menu_body %}
|
||||
|
@@ -5,21 +5,21 @@ section.node-preview-forbidden
|
||||
|
||||
div
|
||||
p Available to Blender Cloud subscribers
|
||||
hr
|
||||
hr.bg-white
|
||||
| {% if current_user.has_cap('can-renew-subscription') %}
|
||||
p
|
||||
small You have a subscription, it just needs to be renewed.
|
||||
a.btn(href="/renew")
|
||||
a.btn.btn-light(href="/renew")
|
||||
| #[i.pi-heart] Renew Subscription
|
||||
| {% else %}
|
||||
p
|
||||
small Support Blender and get awesome stuff!
|
||||
a.btn(href="{{ url_for('cloud.join') }}")
|
||||
a.btn.btn-light(href="{{ url_for('cloud.join') }}")
|
||||
| #[i.pi-heart] Get a Subscription
|
||||
| {% endif %}
|
||||
|
||||
| {% if current_user.is_anonymous %}
|
||||
p(style="margin-top: 15px")
|
||||
small
|
||||
a(href="{{ url_for('users.login') }}") Already a subscriber? Log in
|
||||
a.text-white(href="{{ url_for('users.login') }}") Already a subscriber? Log in
|
||||
| {% endif %}
|
||||
|
@@ -61,7 +61,7 @@ script(type="text/javascript").
|
||||
}
|
||||
|
||||
{% if node.has_method('PUT') %}
|
||||
$('.project-mode-view').show();
|
||||
$('.project-mode-view').displayAs('inline-block');
|
||||
{% else %}
|
||||
$('.project-mode-view').hide();
|
||||
{% endif %}
|
||||
|
@@ -23,7 +23,7 @@ section.node-preview.video
|
||||
|
||||
| {% block node_download %}
|
||||
| {% if node.file_variations %}
|
||||
button.btn.btn-default.dropdown-toggle(
|
||||
button.btn.btn-outline-secondary.dropdown-toggle(
|
||||
type="button",
|
||||
data-toggle="dropdown",
|
||||
aria-haspopup="true",
|
||||
|
@@ -1,142 +0,0 @@
|
||||
//- ******************************************************* -//
|
||||
| {% import 'projects/_macros.html' as projectmacros %}
|
||||
| {% macro render_blog_post(node, project=None, pages=None) %}
|
||||
| {% if node.picture %}
|
||||
a.blog_index-header(href="{{ node.url }}")
|
||||
img(src="{{ node.picture.thumbnail('l', api=api) }}")
|
||||
| {% endif %}
|
||||
| {% if project and project._id != config.MAIN_PROJECT_ID %}
|
||||
| {{ projectmacros.render_secondary_navigation(project, pages=pages) }}
|
||||
| {% endif %}
|
||||
.blog_index-item
|
||||
a.item-title(
|
||||
href="{{ node.url }}")
|
||||
| {{ node.name }}
|
||||
ul.meta
|
||||
| {% if node.project.name %}
|
||||
li {{ node.project.name }}
|
||||
| {% endif %}
|
||||
| {% if node.user.full_name%}
|
||||
li.who
|
||||
| by {{ node.user.full_name }}
|
||||
| {% endif %}
|
||||
li.when
|
||||
a(href="{{ node.url }}",
|
||||
title="Updated {{ node._updated | pretty_date }}")
|
||||
| {{ node._created | pretty_date }}
|
||||
li
|
||||
a(href="{{ node.url }}#comments")
|
||||
| comment
|
||||
|
||||
|
||||
.item-content
|
||||
| {{ node.properties | markdowned('content') }}
|
||||
|
||||
| {% endmacro %}
|
||||
|
||||
//- ******************************************************* -//
|
||||
| {% macro render_blog_list_item(node) %}
|
||||
.blog_index-item.list
|
||||
|
||||
| {% if node.picture %}
|
||||
a.item-header(href="{{ node.url }}")
|
||||
img.image(src="{{ node.picture.thumbnail('s', api=api) }}")
|
||||
| {% else %}
|
||||
a.item-header.nothumb(href="{{ node.url }}")
|
||||
i.pi-document-text
|
||||
| {% endif %}
|
||||
|
||||
a.item-title(
|
||||
href="{{ node.url }}")
|
||||
| {{node.name}}
|
||||
|
||||
.item-info.
|
||||
#[span(title="{{node._created}}") {{node._created | pretty_date }}]
|
||||
{% if node._created != node._updated %}
|
||||
#[span(title="{{node._updated}}") (updated {{node._updated | pretty_date }})]
|
||||
{% endif %}
|
||||
{% if node.properties.category %} · {{node.properties.category}}{% endif %}
|
||||
· {{node.user.full_name}}
|
||||
{% if node.properties.status != 'published' %} · {{ node.properties.status}} {% endif %}
|
||||
|
||||
| {% endmacro %}
|
||||
|
||||
|
||||
//- ******************************************************* -//
|
||||
| {% macro render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=None) %}
|
||||
| {% if can_create_blog_posts %}
|
||||
.blog-action
|
||||
a.btn.btn-default.button-create(href="{{url_for('nodes.posts_create', project_id=project._id)}}")
|
||||
i.pi-plus
|
||||
| Create New Post
|
||||
| {% endif %}
|
||||
|
||||
| {% if posts %}
|
||||
| {{ render_blog_post(posts[0], project=project, pages=pages) }}
|
||||
|
||||
| {% for node in posts[1:] %}
|
||||
| {% if loop.first %}
|
||||
.blog-archive-navigation
|
||||
span Blasts from the past
|
||||
| {% endif %}
|
||||
| {{ render_blog_list_item(node) }}
|
||||
| {% endfor %}
|
||||
|
||||
| {% if more_posts_available %}
|
||||
.blog-archive-navigation
|
||||
a(href="{{ project.blog_archive_url }}")
|
||||
| {{posts_meta.total - posts|length}} more blog posts over here
|
||||
i.pi-angle-right
|
||||
| {% endif %}
|
||||
|
||||
| {% else %}
|
||||
|
||||
.blog_index-item
|
||||
.item-content No posts... yet!
|
||||
|
||||
| {% endif %} {# posts #}
|
||||
| {% endmacro %}
|
||||
|
||||
|
||||
//- Macro for rendering the navigation buttons for prev/next pages -//
|
||||
| {% macro render_archive_pagination(project) %}
|
||||
.blog-archive-navigation
|
||||
| {% if project.blog_archive_prev %}
|
||||
a.archive-nav-button(
|
||||
href="{{ project.blog_archive_prev }}", rel="prev")
|
||||
i.pi-angle-left
|
||||
| Previous page
|
||||
| {% else %}
|
||||
span.archive-nav-button
|
||||
i.pi-angle-left
|
||||
| Previous page
|
||||
| {% endif %}
|
||||
|
||||
a.archive-nav-button(
|
||||
href="{{ url_for('main.project_blog', project_url=project.url) }}")
|
||||
| Blog Index
|
||||
|
||||
| {% if project.blog_archive_next %}
|
||||
a.archive-nav-button(
|
||||
href="{{ project.blog_archive_next }}", rel="next")
|
||||
| Next page
|
||||
i.pi-angle-right
|
||||
| {% else %}
|
||||
span.archive-nav-button
|
||||
| Next page
|
||||
i.pi-angle-right
|
||||
| {% endif %}
|
||||
|
||||
| {% endmacro %}
|
||||
|
||||
| {% macro render_archive(project, posts, posts_meta) %}
|
||||
|
||||
| {{ render_archive_pagination(project) }}
|
||||
|
||||
| {% for node in posts %}
|
||||
| {{ render_blog_list_item(node) }}
|
||||
| {% endfor %}
|
||||
|
||||
| {{ render_archive_pagination(project) }}
|
||||
|
||||
| {% endmacro %}
|
@@ -2,11 +2,8 @@
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
|
||||
| {% block body %}
|
||||
.container-fluid
|
||||
#blog_container.cloud-blog
|
||||
#blog_index-container
|
||||
.blog_index-header
|
||||
h3 Blog Archive
|
||||
.container
|
||||
h3 Blog Archive
|
||||
|
||||
| {{ blogmacros.render_archive(project, posts, posts_meta) }}
|
||||
| {{ blogmacros.render_archive(project, posts, posts_meta) }}
|
||||
| {% endblock body %}
|
||||
|
@@ -9,9 +9,7 @@ link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel=
|
||||
| {% endblock %}
|
||||
|
||||
| {% block project_context %}
|
||||
#blog_container
|
||||
#blog_index-container.expand-image-links
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta) }}
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta) }}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block project_tree %}
|
||||
|
@@ -10,10 +10,7 @@ link(href="{{ url_for('static_cloud', filename='assets/css/project-landing.css')
|
||||
| {% endblock css %}
|
||||
|
||||
| {% block body %}
|
||||
.container-fluid.blog
|
||||
#blog_container.cloud-blog
|
||||
#blog_index-container.expand-image-links
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=pages) }}
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=pages) }}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
@@ -25,7 +22,7 @@ script.
|
||||
/* Expand images when their link points to a jpg/png/gif */
|
||||
/* TODO: De-duplicate code from view post */
|
||||
var page_overlay = document.getElementById('page-overlay');
|
||||
$('.blog_index-item .item-content a img').on('click', function(e){
|
||||
$('.item-content a img').on('click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
var href = $(this).parent().attr('href');
|
||||
|
@@ -32,14 +32,13 @@
|
||||
.comment-reply-preview-md
|
||||
.comment-reply-info
|
||||
.comment-action-cancel(
|
||||
type="button",
|
||||
title="{{ _('cancel') }}")
|
||||
span {{ _('cancel') }}
|
||||
|
||||
a(
|
||||
title="{{ _('Handy guide of Markdown syntax') }}",
|
||||
target="_blank",
|
||||
href="https://guides.github.com/features/mastering-markdown/")
|
||||
href="http://commonmark.org/help/")
|
||||
span {{ _('markdown cheatsheet') }}
|
||||
| {% endblock can_post_comment %}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ li.node-details-meta-list-item
|
||||
|
||||
| {% block node_download %}
|
||||
| {% if node.properties.files %}
|
||||
button.btn.btn-default.dropdown-toggle(
|
||||
button.btn.btn-outline-secondary.dropdown-toggle(
|
||||
title="Download HDRI",
|
||||
type="button",
|
||||
data-toggle="dropdown",
|
||||
|
@@ -3,7 +3,7 @@
|
||||
| {% block body %}
|
||||
| {% if node.picture %}
|
||||
header
|
||||
img.header(src="{{ node.picture.thumbnail('l', api=api) }}")
|
||||
img.header(src="{{ node.picture.thumbnail('h', api=api) }}")
|
||||
| {% endif %}
|
||||
| {% block navbar_secondary %}
|
||||
| {{ super() }}
|
||||
|
@@ -68,9 +68,9 @@
|
||||
| {% endif %}
|
||||
| {% endfor %}
|
||||
|
||||
input.btn.btn-default.button-create(type='submit', value='Create {{ node_type.name }}')
|
||||
input.btn.btn-outline-secondary(type='submit', value='Create {{ node_type.name }}')
|
||||
|
||||
a.btn.btn-default.button-back(href="{{ url_for('projects.view', project_url=project.url) }}blog")
|
||||
a.btn.btn-link.button-back(href="{{ url_for('projects.view', project_url=project.url) }}blog")
|
||||
| Back to Blog
|
||||
|
||||
#blog_post-create-container
|
||||
|
@@ -1,19 +1,9 @@
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
|
||||
#blog_container(class="{% if project and project._id == config.MAIN_PROJECT_ID %}cloud-blog{% endif %}")
|
||||
| {{ blogmacros.render_blog_post(node, project=project) }}
|
||||
|
||||
#blog_index-container.expand-image-links
|
||||
.blog-action
|
||||
| {% if node.has_method('PUT') %}
|
||||
a.btn.btn-default.button-edit(href="{{url_for('nodes.edit', node_id=node._id)}}")
|
||||
i.pi-edit
|
||||
| Edit Post
|
||||
| {% endif %}
|
||||
|
||||
| {{ blogmacros.render_blog_post(node, project=project) }}
|
||||
|
||||
#comments-embed
|
||||
.comments-list-loading
|
||||
i.pi-spin
|
||||
#comments-embed.comments-compact
|
||||
.comments-list-loading
|
||||
i.pi-spin
|
||||
|
||||
include ../_scripts
|
||||
|
@@ -26,8 +26,7 @@ link(href="{{ url_for('static_cloud', filename='assets/css/project-landing.css')
|
||||
| {% set title = 'blog' %}
|
||||
|
||||
| {% block body %}
|
||||
.container-fluid.blog
|
||||
| {% include 'nodes/custom/post/view_embed.html' %}
|
||||
| {% include 'nodes/custom/post/view_embed.html' %}
|
||||
| {% endblock %}
|
||||
|
||||
|
||||
|
@@ -91,11 +91,11 @@
|
||||
a(href="{{ f.file.link }}",,
|
||||
title="Download texture",
|
||||
download="{{ f.file.filename }}")
|
||||
button.btn.btn-default(type="button")
|
||||
button.btn.btn-outline-secondary(type="button")
|
||||
i.pi-download
|
||||
| Download
|
||||
| {% else %}
|
||||
button.btn.btn-default.disabled.sorry(type="button")
|
||||
button.btn.btn-outline-secondary.disabled.sorry(type="button")
|
||||
i.pi-lock
|
||||
| Download
|
||||
| {% endif %}
|
||||
|
@@ -38,18 +38,21 @@
|
||||
|
||||
| {% elif field.type == 'HiddenField' %}
|
||||
| {{ field }}
|
||||
|
||||
| {% elif field.name == 'attachments' %}
|
||||
hr
|
||||
#attachments-actions
|
||||
.btn.btn-info#attachments-action-add
|
||||
i.pi-plus
|
||||
| Add New Attachment
|
||||
|
||||
p.text-muted
|
||||
p.text-muted.mt-3
|
||||
| Attachments can be included in any MarkDown field by using the #[code {attachment slug}] shortcode
|
||||
| (#[a(href='https://pillarframework.org/shortcodes/#attachments', target='_blank') help]).
|
||||
| This shortcode is placed on your copy-paste buffer by clicking "Copy to clipboard".
|
||||
|
||||
| {{ render_field(field, field.name) }}
|
||||
hr
|
||||
|
||||
| {% elif field.name == 'files' %}
|
||||
#files-actions
|
||||
@@ -66,20 +69,23 @@
|
||||
|
||||
| {% endfor %}
|
||||
|
||||
ul.project-edit-tools.bottom
|
||||
hr
|
||||
|
||||
ul.project-edit-tools.justify-content-end.h-auto
|
||||
li.button-cancel
|
||||
a#item_cancel.item-cancel.project-mode-edit(
|
||||
a#item_cancel.item-cancel.project-mode-edit.btn.btn-outline-secondary(
|
||||
href="javascript:void(0);",
|
||||
title="Cancel changes")
|
||||
i.button-cancel-icon.pi-cancel
|
||||
| Cancel
|
||||
|
||||
li.button-save
|
||||
a#item_save.item-save.project-mode-edit(
|
||||
a#item_save.item-save.project-mode-edit.btn.btn-outline-success.ml-2(
|
||||
href="javascript:void(0);",
|
||||
title="Save changes")
|
||||
i.button-save-icon.pi-check
|
||||
| Save Changes
|
||||
|
||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.ui.widget.min.js') }}")
|
||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
|
||||
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.fileupload.min.js') }}")
|
||||
|
@@ -1,210 +1,10 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
||||
|
||||
| {% set title = 'organizations' %}
|
||||
| {% block page_title %}Organizations{% endblock %}
|
||||
|
||||
| {% block og %}
|
||||
meta(property="og:title", content="Dashboard")
|
||||
meta(name="twitter:title", content="Blender Cloud")
|
||||
|
||||
meta(property="og:url", content="https://cloud.blender.org/{{ request.path }}")
|
||||
meta(property="og:type", content="website")
|
||||
|
||||
meta(property="og: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 %}
|
||||
|
||||
| {% block body %}
|
||||
.dashboard-container
|
||||
section.dashboard-main
|
||||
| {{ navigation_tabs(title) }}
|
||||
|
||||
section#projects
|
||||
|
||||
| {% if can_create_organization %}
|
||||
nav#sub-nav-tabs.projects
|
||||
ul#sub-nav-tabs__list
|
||||
li.result#create_organization_result_panel
|
||||
li.create
|
||||
button.btn.btn-success(onclick='createNewOrganization(this)')
|
||||
i.pi-plus
|
||||
| Create Organization
|
||||
| {% endif %}
|
||||
|
||||
nav.nav-tabs__tab.active#own_projects
|
||||
ul.projects__list
|
||||
| {% if organizations %}
|
||||
| {% for organization in organizations['_items'] %}
|
||||
| {% set link_url = url_for('pillar.web.organizations.view_embed', organization_id=organization._id) %}
|
||||
li.projects__list-item(
|
||||
data-url="{{ link_url }}",
|
||||
id="organization-{{ organization._id }}")
|
||||
a.projects__list-thumbnail(
|
||||
href="{{ link_url }}")
|
||||
i.pi-users
|
||||
.projects__list-details
|
||||
a.title(href="{{ link_url }}")
|
||||
| {{ organization.name }}
|
||||
|
||||
ul.meta
|
||||
li(title="Members")
|
||||
| {{ organization.members|hide_none|count }} Member{{ organization.members|hide_none|count|pluralize }}
|
||||
| {% if (organization.unknown_members|count) != 0 %}
|
||||
| ({{ organization.unknown_members|hide_none|count }} pending)
|
||||
| {% endif %}
|
||||
li(title="Seats")
|
||||
| {{ organization.seat_count }} Seat{{ organization.seat_count|pluralize }}
|
||||
|
||||
| {% endfor %}
|
||||
| {% else %}
|
||||
li.projects__list-item
|
||||
a.projects__list-thumbnail
|
||||
i.pi-blender-cloud
|
||||
.projects__list-details
|
||||
span Create an Organization to get started!
|
||||
| {% endif %}
|
||||
|
||||
section.dashboard-secondary
|
||||
section.box
|
||||
#item-details
|
||||
|
||||
| {% endblock %}
|
||||
|
||||
|
||||
| {% block footer_scripts %}
|
||||
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.autocomplete-0.22.0.min.js') }}", async=true)
|
||||
|
||||
script.
|
||||
|
||||
/* Returns a more-or-less reasonable message given an error response object. */
|
||||
function xhrErrorResponseMessage(err) {
|
||||
if (typeof err.responseJSON == 'undefined')
|
||||
return err.statusText;
|
||||
|
||||
if (typeof err.responseJSON._error != 'undefined' && typeof err.responseJSON._error.message != 'undefined')
|
||||
return err.responseJSON._error.message;
|
||||
|
||||
if (typeof err.responseJSON._message != 'undefined')
|
||||
return err.responseJSON._message
|
||||
|
||||
return err.statusText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an organization in the #item-details div.
|
||||
*/
|
||||
function item_open(item_id, pushState)
|
||||
{
|
||||
if (item_id === undefined ) {
|
||||
throw new ReferenceError("item_open(" + item_id + ") called.");
|
||||
}
|
||||
|
||||
// Style elements starting with item_type and dash, e.g. "#job-uuid"
|
||||
var clean_classes = 'active processing';
|
||||
var current_item = $('#organization-' + item_id);
|
||||
|
||||
$('[id^="organization-"]').removeClass(clean_classes);
|
||||
current_item
|
||||
.removeClass(clean_classes)
|
||||
.addClass('processing');
|
||||
|
||||
var item_url = '/o/' + item_id;
|
||||
|
||||
$.get(item_url, function(item_data) {
|
||||
$('#item-details').html(item_data);
|
||||
|
||||
current_item
|
||||
.removeClass(clean_classes)
|
||||
.addClass('active');
|
||||
|
||||
}).fail(function(xhr) {
|
||||
if (console) {
|
||||
console.log('Error fetching organization', item_id, 'from', item_url);
|
||||
console.log('XHR:', xhr);
|
||||
}
|
||||
|
||||
current_item.removeClass(clean_classes);
|
||||
toastr.error('Failed to open organization');
|
||||
|
||||
if (xhr.status) {
|
||||
$('#item-details').html(xhr.responseText);
|
||||
} else {
|
||||
$('#item-details').html('<p class="text-danger">Opening ' + item_type + ' failed. There possibly was ' +
|
||||
'an error connecting to the server. Please check your network connection and ' +
|
||||
'try again.</p>');
|
||||
}
|
||||
});
|
||||
|
||||
// Determine whether we should push the new state or not.
|
||||
pushState = (typeof pushState !== 'undefined') ? pushState : true;
|
||||
if (!pushState) return;
|
||||
|
||||
// Push the correct URL onto the history.
|
||||
var push_state = {itemId: item_id};
|
||||
|
||||
window.history.pushState(
|
||||
push_state,
|
||||
'Organization: ' + item_id,
|
||||
item_url
|
||||
);
|
||||
}
|
||||
|
||||
$('li.projects__list-item').click(function(e){
|
||||
url = $(this).data('url');
|
||||
if (typeof url === 'undefined') return;
|
||||
|
||||
window.location.href = url;
|
||||
if (console) console.log(url);
|
||||
|
||||
$(this).addClass('active');
|
||||
$(this).find('.projects__list-thumbnail i')
|
||||
.removeAttr('class')
|
||||
.addClass('pi-spin spin');
|
||||
});
|
||||
|
||||
|
||||
{% if open_organization_id %}
|
||||
$(function() { item_open('{{ open_organization_id }}', false); });
|
||||
{% endif %}
|
||||
|
||||
{% if can_create_organization %}
|
||||
function createNewOrganization(button) {
|
||||
$(button)
|
||||
.attr('disabled', 'disabled')
|
||||
.fadeTo(200, 0.1);
|
||||
$('#create_organization_result_panel').html('');
|
||||
|
||||
// TODO: create a form to get the initial info from the user.
|
||||
$.post(
|
||||
'{{ url_for('pillar.web.organizations.create_new') }}',
|
||||
{
|
||||
name: 'New Organization',
|
||||
seat_count: 1,
|
||||
}
|
||||
)
|
||||
.done(function(result) {
|
||||
var $p = $('<p>').text('organization created, reloading list.')
|
||||
$('#create_organization_result_panel').html($p);
|
||||
|
||||
window.location.href = result.location;
|
||||
})
|
||||
.fail(function(err) {
|
||||
var msg = xhrErrorResponseMessage(err);
|
||||
$('#create_organization_result_panel').html('Error creating organization: ' + msg);
|
||||
|
||||
$(button)
|
||||
.fadeTo(1000, 1.0)
|
||||
.queue(function() {
|
||||
$(this)
|
||||
.removeAttr('disabled')
|
||||
.dequeue()
|
||||
;
|
||||
})
|
||||
})
|
||||
;
|
||||
return false;
|
||||
}
|
||||
{% endif %}
|
||||
.p-5.text-center
|
||||
h2 Organizations Index
|
||||
.lead.
|
||||
See Blender Cloud template for reference.
|
||||
| {% endblock %}
|
||||
|
@@ -75,7 +75,7 @@
|
||||
script $('#admin-picker').hide();
|
||||
|
||||
.input-group
|
||||
button#item-save.btn.btn-success.btn-block(type='submit')
|
||||
button#item-save.btn.btn-outline-success.btn-block(type='submit')
|
||||
i.pi-check
|
||||
| Save Changes
|
||||
| {% else %}
|
||||
|
@@ -1,36 +1,41 @@
|
||||
| {% macro render_secondary_navigation(project, pages=None) %}
|
||||
nav.navbar-secondary
|
||||
.navbar-container
|
||||
nav.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.view', project_url=project.url) }}",
|
||||
title="{{ project.name }} Homepage")
|
||||
span
|
||||
b {{ project.name }}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('main.project_blog', project_url=project.url) }}",
|
||||
title="Project Blog",
|
||||
class="{% if category == 'blog' %}active{% endif %}")
|
||||
span Blog
|
||||
| {% if pages %}
|
||||
| {% for p in pages %}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.view_node', project_url=project.url, node_id=p._id) }}",
|
||||
title="{{ p.name }}",
|
||||
class="{% if category == 'page' %}active{% endif %}")
|
||||
span {{ p.name }}
|
||||
| {% endfor %}
|
||||
| {% endif %}
|
||||
| {% if project.nodes_featured %}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.view_node', project_url=project.url, node_id=project.nodes_featured[0]) }}",
|
||||
title="Explore {{ project.name }}",
|
||||
class="{% if category == 'blog' %}active{% endif %}")
|
||||
span Explore
|
||||
| {% endif %}
|
||||
nav.collapse.navbar-collapse
|
||||
ul.navbar-nav.navbar-right
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.view', project_url=project.url) }}",
|
||||
title="{{ project.name }} Homepage")
|
||||
span
|
||||
b {{ project.name }}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('main.project_blog', project_url=project.url) }}",
|
||||
title="Project Blog",
|
||||
class="{% if category == 'blog' %}active{% endif %}")
|
||||
span Blog
|
||||
| {% if pages %}
|
||||
| {% for p in pages %}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.view_node', project_url=project.url, node_id=p._id) }}",
|
||||
title="{{ p.name }}",
|
||||
class="{% if category == 'page' %}active{% endif %}")
|
||||
span {{ p.name }}
|
||||
| {% endfor %}
|
||||
| {% endif %}
|
||||
| {% 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 %}
|
||||
li
|
||||
a.navbar-item(
|
||||
href="{{ url_for('projects.view_node', project_url=project.url, node_id=featured_node_id) }}",
|
||||
title="Explore {{ project.name }}",
|
||||
class="{% if category == 'blog' %}active{% endif %}")
|
||||
span Explore
|
||||
| {% endif %}
|
||||
| {% endmacro %}
|
||||
|
@@ -7,17 +7,16 @@ span#project-edit-title
|
||||
| Edit Project
|
||||
|
||||
ul.project-edit-tools
|
||||
|
||||
// Edit Mode
|
||||
li.button-cancel
|
||||
a#item_cancel.project-mode-edit(
|
||||
a#item_cancel.project-mode-edit.btn.btn-sm.btn-link(
|
||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}",
|
||||
title="Cancel changes")
|
||||
i.button-cancel-icon.pi-back
|
||||
i.button-cancel-icon.pi-angle-left
|
||||
| Go to Project
|
||||
|
||||
li.button-save
|
||||
a#item_save.project-mode-edit(
|
||||
a#item_save.project-mode-edit.btn.btn-sm.btn-outline-success.mx-2(
|
||||
href="#",
|
||||
title="Save changes")
|
||||
i.button-save-icon.pi-check
|
||||
@@ -80,16 +79,18 @@ ul.project-edit-tools
|
||||
|
||||
| {% endfor %}
|
||||
|
||||
ul.project-edit-tools.bottom
|
||||
hr
|
||||
|
||||
ul.project-edit-tools.justify-content-end.h-auto
|
||||
li.button-cancel
|
||||
a#item_cancel.project-mode-edit(
|
||||
a#item_cancel.project-mode-edit.btn.btn-link(
|
||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}",
|
||||
title="Cancel changes")
|
||||
i.button-cancel-icon.pi-back
|
||||
i.button-cancel-icon.pi-angle-left
|
||||
| Go to Project
|
||||
|
||||
li.button-save
|
||||
a#item_save.project-mode-edit(
|
||||
a#item_save.project-mode-edit.btn.btn-outline-success.ml-2(
|
||||
href="#",
|
||||
title="Save changes")
|
||||
i.button-save-icon.pi-check
|
||||
@@ -97,6 +98,8 @@ ul.project-edit-tools
|
||||
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_container %}{% endblock %}
|
||||
|
||||
| {% 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') }}")
|
||||
@@ -105,7 +108,8 @@ script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets
|
||||
|
||||
script(type="text/javascript").
|
||||
|
||||
$('.project-mode-edit').show();
|
||||
// Show edit mode buttons (save, cancel).
|
||||
$('.project-mode-edit').displayAs('inline-block');
|
||||
|
||||
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: true, nodeId: ''});
|
||||
var convert = new Markdown.getSanitizingConverter().makeHtml;
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#project-container
|
||||
#project-side-container
|
||||
#project_sidebar
|
||||
ul.project-tabs
|
||||
ul.project-tabs.p-0
|
||||
li.tabs-thumbnail(
|
||||
title="About",
|
||||
data-toggle="tooltip",
|
||||
@@ -51,13 +51,13 @@
|
||||
|
||||
#project_nav
|
||||
#project_nav-container
|
||||
#project_nav-header
|
||||
.project-title
|
||||
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
||||
| {{ 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 }}
|
||||
|
||||
// TODO - make list a macro
|
||||
#project_tree.edit
|
||||
#project_tree.edit.bg-white
|
||||
ul.project_nav-edit-list
|
||||
li(class="{% if title == 'edit' %}active{% endif %}")
|
||||
a(href="{{ url_for('projects.edit', project_url=project.url) }}")
|
||||
@@ -77,10 +77,9 @@
|
||||
i(class="pi-{{ext.icon}}")
|
||||
| {{ext.name | title}}
|
||||
| {% endfor %}
|
||||
.project_split(title="Toggle Navigation [T]")
|
||||
|
||||
#project_context-container
|
||||
#project_context-header
|
||||
#project_context-header.bg-white
|
||||
span#status-bar
|
||||
| {% block project_context_header %}
|
||||
| {% endblock %}
|
||||
|
@@ -19,7 +19,7 @@ form(
|
||||
.col-md-9
|
||||
h3 Editing: {{ node_type['name'] }}
|
||||
.col-md-3
|
||||
button.js-form-save.btn.btn-success.pull-right(style="margin-top: 15px;")
|
||||
button.js-form-save.btn.btn-outline-success.pull-right(style="margin-top: 15px;")
|
||||
| Save Changes
|
||||
|
||||
.row
|
||||
@@ -46,7 +46,7 @@ form(
|
||||
| {% endif %}
|
||||
| {% endfor %}
|
||||
|
||||
button.js-form-save.btn.btn-success.pull-right
|
||||
button.js-form-save.btn.btn-outline-success.pull-right
|
||||
| Save Changes
|
||||
|
||||
|
||||
|
@@ -80,7 +80,7 @@ section.nav-tabs__tab.active#tab-images
|
||||
Image Sharing requires a Blender Cloud subscription.
|
||||
|
||||
.buttons
|
||||
a.btn.btn-default.btn-outline.green(href="https://store.blender.org/product/membership/")
|
||||
a.btn.btn-outline-primary(href="https://store.blender.org/product/membership/")
|
||||
| Join Now
|
||||
| {% endif %}
|
||||
| {% endblock %}
|
||||
|
@@ -1,43 +1,8 @@
|
||||
| {% extends 'projects/home_layout.html' %}
|
||||
| {% set subtab = 'blender_sync' %}
|
||||
| {% set learn_more_btn_url = '/blog/introducing-blender-sync' %}
|
||||
| {% block currenttab %}
|
||||
section.nav-tabs__tab.active#tab-blender_sync
|
||||
.tab_header-container
|
||||
.tab_header-intro(
|
||||
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/pattern_01.jpg')}})")
|
||||
.tab_header-intro_text
|
||||
h2 Connect Blender with the Cloud
|
||||
p
|
||||
| Save your Blender preferences once, load them anywhere.
|
||||
<br/>
|
||||
| Use the
|
||||
=' '
|
||||
a(href='https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip') Blender Cloud add-on
|
||||
=' '
|
||||
| to synchronise your settings from within Blender.
|
||||
.tab_header-intro_icons
|
||||
i.pi-blender
|
||||
i.pi-heart-filled
|
||||
i.pi-blender-cloud
|
||||
|
||||
| {% for version in synced_versions %}
|
||||
.blender_sync-main
|
||||
.blender_sync-main-header
|
||||
h2.blender_sync-main-title
|
||||
i.pi-blender
|
||||
| Blender {{ version.version }}
|
||||
.blender_sync-main-last
|
||||
| Last synced on: {{ version.date|pretty_date }}
|
||||
| {% else %}
|
||||
.blender_sync-main.empty
|
||||
.blender_sync-main-header
|
||||
span.blender_sync-main-title
|
||||
| No settings synced yet
|
||||
<hr/>
|
||||
a.download(
|
||||
href='https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip')
|
||||
| Download add-on
|
||||
| {% endfor %}
|
||||
.p-5.text-center
|
||||
h2 Home Index
|
||||
p.lead.
|
||||
See Blender Cloud for reference.
|
||||
| {% endblock %}
|
||||
|
||||
|
@@ -1,77 +1,15 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
||||
|
||||
| {% set title = 'home' %}
|
||||
|
||||
| {% block og %}
|
||||
meta(property="og:type", content="website")
|
||||
meta(property="og:url", content="https://cloud.blender.org{{ request.path }}")
|
||||
|
||||
meta(property="og:title", content="Blender Cloud - Home")
|
||||
meta(name="twitter:title", content="Blender Cloud")
|
||||
|
||||
meta(property="og: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 %}
|
||||
|
||||
| {% block page_title %}
|
||||
| {{current_user.full_name}}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block body %}
|
||||
.dashboard-container
|
||||
section.dashboard-main
|
||||
| {{ navigation_tabs(title) }}
|
||||
.p-5.text-center
|
||||
h2 Home Layout
|
||||
p.lead.
|
||||
See Blender Cloud for reference.
|
||||
|
||||
section#projects
|
||||
|
||||
nav#sub-nav-tabs.home
|
||||
ul#sub-nav-tabs__list
|
||||
li.nav-tabs__list-tab#subtab-blender_sync(data-tab-url="{{ url_for('projects.home_project')}}")
|
||||
i.pi-blender
|
||||
| Blender Sync
|
||||
|
||||
li.nav-tabs__list-tab#subtab-images(data-tab-url="{{ url_for('projects.home_project_shared_images')}}")
|
||||
i.pi-picture
|
||||
| Images
|
||||
| {% block currenttab %}{% endblock %}
|
||||
|
||||
section.dashboard-secondary
|
||||
section.announcement
|
||||
img.header(
|
||||
src="{{ url_for('static', filename='assets/img/blender_sync_header.jpg') }}")
|
||||
.text
|
||||
| {% block side_announcement %}
|
||||
.title
|
||||
a(href="https://cloud.blender.org/blog/introducing-blender-sync") Blender Sync
|
||||
|
||||
.lead
|
||||
span.
|
||||
Save your settings once. Use them anywhere.
|
||||
Carry your Blender configuration with you, use our free add-on to sync your keymaps and preferences.
|
||||
<hr/>
|
||||
Syncing is free for everyone. No subscription required.
|
||||
| {% endblock %}
|
||||
| {% if show_addon_download_buttons %}
|
||||
.buttons
|
||||
a.btn.btn-default.btn-outline.orange(
|
||||
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
|
||||
i.pi-download
|
||||
| Download <small>v</small>{{ config.BLENDER_CLOUD_ADDON_VERSION }}
|
||||
a.btn.btn-default.btn-outline.blue(
|
||||
href="{{ learn_more_btn_url }}")
|
||||
| Learn More
|
||||
| {% endif %}
|
||||
| {% endblock %}
|
||||
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script.
|
||||
$(document).ready(function () {
|
||||
$('#subtab-{{ subtab }}').addClass('active');
|
||||
var $nav_tabs = $('#sub-nav-tabs__list').find('li.nav-tabs__list-tab');
|
||||
$nav_tabs.on('click', function (e) {
|
||||
window.location = $(this).attr('data-tab-url');
|
||||
});
|
||||
});
|
||||
| {% block currenttab %}{% endblock currenttab %}
|
||||
| {% endblock %}
|
||||
|
@@ -1,347 +1,16 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% from '_macros/_navigation.html' import navigation_tabs %}
|
||||
|
||||
| {% set title = 'dashboard' %}
|
||||
|
||||
| {% block og %}
|
||||
meta(property="og:title", content="Dashboard")
|
||||
meta(name="twitter:title", content="Blender Cloud")
|
||||
|
||||
meta(property="og:url", content="https://cloud.blender.org/{{ request.path }}")
|
||||
meta(property="og:type", content="website")
|
||||
|
||||
meta(property="og: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 %}
|
||||
|
||||
| {% block page_title %}
|
||||
| {{current_user.full_name}}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block css %}
|
||||
| {{ super() }}
|
||||
style.
|
||||
.deleted-projects-toggle {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
font-size: 20px;
|
||||
padding: 3px;
|
||||
text-shadow: 0 0 2px white;
|
||||
}
|
||||
.deleted-projects-toggle .show-deleted {
|
||||
color: #aaa;
|
||||
}
|
||||
.deleted-projects-toggle .hide-deleted {
|
||||
color: #bbb;
|
||||
}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block body %}
|
||||
.dashboard-container
|
||||
section.dashboard-main
|
||||
| {{ navigation_tabs(title) }}
|
||||
|
||||
section#projects
|
||||
|
||||
nav#sub-nav-tabs.projects
|
||||
ul#sub-nav-tabs__list
|
||||
li.nav-tabs__list-tab.active(data-tab-toggle='own_projects')
|
||||
| Own Projects
|
||||
| {% if projects_user|length != 0 %}
|
||||
span ({{ projects_user|length }})
|
||||
| {% endif %}
|
||||
|
||||
li.nav-tabs__list-tab(data-tab-toggle='shared')
|
||||
| Shared with me
|
||||
| {% if projects_shared|length != 0 %}
|
||||
span ({{ projects_shared|length }})
|
||||
| {% endif %}
|
||||
|
||||
| {% if current_user.has_cap('subscriber') %}
|
||||
li.create#project-create(
|
||||
data-url="{{ url_for('projects.create') }}")
|
||||
a.btn.btn-success(
|
||||
href="{{ url_for('projects.create') }}")
|
||||
i.pi-plus
|
||||
| Create Project
|
||||
| {% elif current_user.has_cap('can-renew-subscription') %}
|
||||
li.create
|
||||
a.btn(href="/renew", target="_blank")
|
||||
i.pi-heart
|
||||
| Renew subscription to create a project
|
||||
| {% endif %}
|
||||
|
||||
nav.nav-tabs__tab.active#own_projects
|
||||
.deleted-projects-toggle
|
||||
| {% if show_deleted_projects %}
|
||||
a.hide-deleted(href="{{ request.base_url }}", title='Hide deleted projects')
|
||||
i.pi-trash
|
||||
| {% else %}
|
||||
a.show-deleted(href="{{ request.base_url }}?deleted=1", title='Show deleted projects')
|
||||
i.pi-trash
|
||||
| {% endif %}
|
||||
|
||||
ul.projects__list
|
||||
| {% for project in projects_deleted %}
|
||||
li.projects__list-item.deleted
|
||||
span.projects__list-thumbnail
|
||||
| {% if project.picture_square %}
|
||||
img(src="{{ project.picture_square.thumbnail('s', api=api) }}")
|
||||
| {% else %}
|
||||
i.pi-blender-cloud
|
||||
| {% endif %}
|
||||
.projects__list-details
|
||||
span.title {{ project.name }}
|
||||
ul.meta
|
||||
li.status.deleted Deleted
|
||||
li.edit
|
||||
a(href="javascript:undelete_project('{{ project._id }}')") Restore project
|
||||
| {% else %}
|
||||
| {% if show_deleted_projects %}
|
||||
li.projects__list-item.deleted You have no recenly deleted projects. Deleted projects can be restored within a month after deletion.
|
||||
| {% endif %}
|
||||
| {% endfor %}
|
||||
|
||||
| {% for project in projects_user %}
|
||||
li.projects__list-item(
|
||||
data-url="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
a.projects__list-thumbnail(
|
||||
href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
| {% if project.picture_square %}
|
||||
img(src="{{ project.picture_square.thumbnail('s', api=api) }}")
|
||||
| {% else %}
|
||||
i.pi-blender-cloud
|
||||
| {% endif %}
|
||||
.projects__list-details
|
||||
a.title(href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
| {{ project.name }}
|
||||
|
||||
ul.meta
|
||||
li.status(
|
||||
class="{{ project.is_private | yesno('private,public,') }}",
|
||||
title="{{ project.is_private | yesno('Private Project,Public Project,') }}")
|
||||
| {{ project.is_private | yesno('Private,Public,') }}
|
||||
li.when(title="{{ project._created }}") {{ project._created | pretty_date }}
|
||||
li.edit
|
||||
a(href="{{ url_for('projects.edit', project_url=project.url) }}") Edit
|
||||
| {% if project.status == 'pending' and current_user.has_cap('view-pending-nodes') %}
|
||||
li.pending Not Published
|
||||
| {% endif %}
|
||||
| {% else %}
|
||||
| {% if current_user.has_cap('subscriber') %}
|
||||
li.projects__list-item(data-url="{{ url_for('projects.create') }}")
|
||||
a.projects__list-thumbnail
|
||||
i.pi-plus
|
||||
.projects__list-details
|
||||
a.title(href="{{ url_for('projects.create') }}")
|
||||
| Create a project to get started!
|
||||
| {% elif current_user.has_cap('can-renew-subscription') %}
|
||||
li.projects__list-item(data-url="https://store.blender.org/renew-my-subscription.php")
|
||||
a.projects__list-thumbnail
|
||||
i.pi-plus
|
||||
.projects__list-details
|
||||
a.title(href="https://store.blender.org/renew-my-subscription.php")
|
||||
| Renew your Blender Cloud subscription to create your own projects!
|
||||
| {% else %}
|
||||
li.projects__list-item(data-url="/join")
|
||||
a.projects__list-thumbnail
|
||||
i.pi-plus
|
||||
.projects__list-details
|
||||
a.title(href="/join")
|
||||
| Join Blender Cloud to create your own projects!
|
||||
| {% endif %}
|
||||
| {% endfor %}
|
||||
|
||||
section.nav-tabs__tab#shared(style='display: none')
|
||||
ul.projects__list
|
||||
| {% if projects_shared %}
|
||||
| {% for project in projects_shared %}
|
||||
li.projects__list-item(
|
||||
data-url="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
a.projects__list-thumbnail(
|
||||
href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
| {% if project.picture_square %}
|
||||
img(src="{{ project.picture_square.thumbnail('s', api=api) }}")
|
||||
| {% else %}
|
||||
i.pi-blender-cloud
|
||||
| {% endif %}
|
||||
.projects__list-details
|
||||
a.title(href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
| {{ project.name }}
|
||||
|
||||
ul.meta
|
||||
li.status(
|
||||
class="{{ project.is_private | yesno('private,public,') }}",
|
||||
title="{{ project.is_private | yesno('Private Project,Public Project,') }}")
|
||||
| {{ project.is_private | yesno('Private,Public,') }}
|
||||
li.when {{ project._created | pretty_date }}
|
||||
li.who by {{ project.user.full_name }}
|
||||
li.edit
|
||||
a(href="{{ url_for('projects.edit', project_url=project.url) }}") Edit
|
||||
| {% if project.status == 'pending' and current_user.has_cap('view-pending-nodes') %}
|
||||
li.pending Not Published
|
||||
| {% endif %}
|
||||
|
||||
li.leave
|
||||
span.user-remove-prompt
|
||||
| Leave Project
|
||||
|
||||
span.user-remove
|
||||
| Are you sure?
|
||||
span.user-remove-confirm(
|
||||
user-id="{{ current_user.objectid }}",
|
||||
project-url="{{url_for('projects.sharing', project_url=project.url)}}")
|
||||
i.pi-check
|
||||
| Yes, leave
|
||||
span.user-remove-cancel
|
||||
i.pi-cancel
|
||||
| No, cancel
|
||||
|
||||
| {% endfor %}
|
||||
| {% else %}
|
||||
li.projects__list-item
|
||||
a.projects__list-thumbnail
|
||||
i.pi-heart-broken
|
||||
.projects__list-details
|
||||
.title
|
||||
| No projects shared with you... yet!
|
||||
| {% endif %}
|
||||
|
||||
section.dashboard-secondary
|
||||
section.announcement
|
||||
img.header(
|
||||
src="{{ url_for('static', filename='assets/img/backgrounds/services_projects.jpg')}}")
|
||||
.text
|
||||
.title Projects
|
||||
.lead
|
||||
span.
|
||||
Create and manage your own personal projects.
|
||||
Upload assets and collaborate with other Blender Cloud members.
|
||||
.buttons
|
||||
a.btn.btn-default.btn-outline.blue(
|
||||
href="https://cloud.blender.org/blog/introducing-private-projects")
|
||||
| Learn More
|
||||
|
||||
section.announcement
|
||||
a(href="https://cloud.blender.org/blog/introducing-blender-sync")
|
||||
img.header(
|
||||
src="{{ url_for('static', filename='assets/img/blender_sync_header.jpg') }}")
|
||||
.text
|
||||
.title
|
||||
a(href="https://cloud.blender.org/blog/introducing-blender-sync") Textures Browser & Settings Sync
|
||||
|
||||
.lead
|
||||
span.
|
||||
Get the official Blender Cloud add-on:
|
||||
ul
|
||||
li Save your Blender settings online, use them anywhere
|
||||
li Browse over 800 textures & HDRIs within Blender
|
||||
li Share Screenshots & Renders directly to Blender Cloud
|
||||
|
||||
.buttons
|
||||
a.btn.btn-default.btn-outline.orange(
|
||||
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
|
||||
i.pi-download
|
||||
| Download Add-on <small>v</small> {{ config.BLENDER_CLOUD_ADDON_VERSION }}
|
||||
a.btn.btn-default.btn-outline.blue(
|
||||
href="https://cloud.blender.org/blog/introducing-blender-sync")
|
||||
| Learn More
|
||||
|
||||
.p-5.text-center
|
||||
h2 Index Dashboard
|
||||
p.lead.
|
||||
See Blender Cloud template for reference.
|
||||
| {% endblock %}
|
||||
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script.
|
||||
$(document).ready(function() {
|
||||
|
||||
$('li.projects__list-item').click(function(e){
|
||||
url = $(this).data('url');
|
||||
if (typeof url === 'undefined') return;
|
||||
|
||||
window.location.href = url;
|
||||
if (console) console.log(url);
|
||||
|
||||
$(this).addClass('active');
|
||||
$(this).find('.projects__list-thumbnail i')
|
||||
.removeAttr('class')
|
||||
.addClass('pi-spin spin');
|
||||
});
|
||||
|
||||
// Tabs behavior
|
||||
var $nav_tabs_list = $('#sub-nav-tabs__list');
|
||||
var $nav_tabs = $nav_tabs_list.find('li.nav-tabs__list-tab');
|
||||
$nav_tabs.on('click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
$nav_tabs.removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
$('.nav-tabs__tab').hide();
|
||||
$('#' + $(this).attr('data-tab-toggle')).show();
|
||||
});
|
||||
|
||||
// Create project
|
||||
$('#project-create').on('click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
$(this).addClass('disabled');
|
||||
$('a', this).html('<i class="pi-spin spin"></i> Creating project...');
|
||||
|
||||
window.location.href = $(this).data('url');
|
||||
});
|
||||
|
||||
// Leave project
|
||||
var $projects_list = $('ul.projects__list');
|
||||
$projects_list.find('span.user-remove-prompt').on('click', function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
$(this).next().show();
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
$projects_list.find('span.user-remove-cancel').on('click', function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
$(this).parent().prev().show();
|
||||
$(this).parent().hide();
|
||||
});
|
||||
|
||||
$projects_list.find('span.user-remove-confirm').on('click', function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
var parent = $(this).closest('.projects__list-item');
|
||||
|
||||
function removeUser(userId, projectUrl){
|
||||
$.post(projectUrl, {user_id: userId, action: 'remove'})
|
||||
.done(function (data) {
|
||||
parent.remove();
|
||||
});
|
||||
}
|
||||
|
||||
removeUser($(this).attr('user-id'), $(this).attr('project-url'));
|
||||
});
|
||||
|
||||
hopToTop(); // Display jump to top button
|
||||
});
|
||||
|
||||
|
||||
var patch_url = '{{ url_for('projects.patch.patch_project', project_id='PROJECTID') }}';
|
||||
function undelete_project(project_id) {
|
||||
console.log('undeleting project', project_id);
|
||||
$.ajax({
|
||||
url: patch_url.replace('PROJECTID', project_id),
|
||||
method: 'PATCH',
|
||||
data: JSON.stringify({'op': 'undelete'}),
|
||||
contentType: 'application/json'
|
||||
})
|
||||
.done(function(data, textStatus, jqXHR) {
|
||||
location.href = jqXHR.getResponseHeader('Location');
|
||||
})
|
||||
.fail(function(err) {
|
||||
toastr.error(xhrErrorResponseMessage(err), 'Undeletion failed');
|
||||
})
|
||||
}
|
||||
| {% endblock %}
|
||||
|
@@ -1,641 +1,11 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% from '_macros/_add_new_menu.html' import add_new_menu %}
|
||||
|
||||
| {% block page_title %}{{ project.name }}{% endblock%}
|
||||
|
||||
| {% block og %}
|
||||
meta(property="og:type", content="website")
|
||||
|
||||
| {% if og_picture %}
|
||||
meta(property="og:image", content="{{ 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 %}
|
||||
|
||||
| {% 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 %}
|
||||
|
||||
| {% else %}
|
||||
|
||||
| {% 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 %}
|
||||
|
||||
| {% block head %}
|
||||
link(href="{{ url_for('static_pillar', filename='assets/jstree/themes/default/style.min.css') }}", rel="stylesheet")
|
||||
| {% if node %}
|
||||
link(rel="amphtml", href="{{ url_for('nodes.view', node_id=node._id, _external=True, format='amp') }}")
|
||||
| {% 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/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 %}
|
||||
link(href="{{ url_for('static_pillar', filename='assets/css/font-pillar.css') }}", rel="stylesheet")
|
||||
link(href="{{ url_for('static_pillar', filename='assets/css/base.css') }}", rel="stylesheet")
|
||||
link(href="{{ url_for('static_pillar', filename='assets/css/project-main.css') }}", rel="stylesheet")
|
||||
| {% endblock %}
|
||||
| {% set title = 'project' %}
|
||||
|
||||
| {% block body %}
|
||||
#project-container
|
||||
#project-side-container
|
||||
#project_sidebar
|
||||
ul.project-tabs
|
||||
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-container
|
||||
| {% if title != 'about' %}
|
||||
#project_nav-header
|
||||
.project-title
|
||||
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
||||
| {{ project.name }}
|
||||
|
||||
| {% block project_tree %}
|
||||
#project_tree
|
||||
| {% endblock project_tree %}
|
||||
| {% endif %}
|
||||
|
||||
|
||||
#project_context-container
|
||||
| {% if project.has_method('PUT') %}
|
||||
#project_context-header
|
||||
span#status-bar
|
||||
|
||||
ul.project-edit-tools.disabled
|
||||
li.button-dropdown
|
||||
a#item_add.dropdown-toggle.project-mode-view(
|
||||
type="button",
|
||||
data-toggle="dropdown",
|
||||
aria-haspopup="true",
|
||||
aria-expanded="false")
|
||||
i.button-add-icon.pi-collection-plus
|
||||
| New...
|
||||
|
||||
ul.dropdown-menu.add_new-menu
|
||||
| {{ add_new_menu(project.node_types) }}
|
||||
|
||||
li.button-edit
|
||||
a#item_edit.project-mode-view(
|
||||
href="javascript:void(0);",
|
||||
title="Edit",
|
||||
data-project_id="{{project._id}}")
|
||||
i.button-edit-icon.pi-edit
|
||||
| Edit Project
|
||||
|
||||
li.button-dropdown
|
||||
a.dropdown-toggle.project-mode-view(
|
||||
type="button",
|
||||
data-toggle="dropdown",
|
||||
aria-haspopup="true",
|
||||
aria-expanded="false")
|
||||
i.pi-more-vertical
|
||||
|
||||
ul.dropdown-menu
|
||||
| {% if current_user.has_cap('admin') %}
|
||||
li.button-featured
|
||||
a#item_featured(
|
||||
href="javascript:void(0);",
|
||||
title="Feature on project's homepage",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
i.button-featured-icon.pi-star
|
||||
| Toggle Featured
|
||||
|
||||
li.button-toggle-public
|
||||
a#item_toggle_public(
|
||||
href="javascript:void(0);",
|
||||
title="Make it accessible to anyone",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
i.pi-lock-open
|
||||
| Toggle public
|
||||
| {% endif %}
|
||||
|
||||
li.button-toggle-projheader
|
||||
a#item_toggle_projheader(
|
||||
href="javascript:void(0);",
|
||||
title="Feature as project's header",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
i.button-featured-icon.pi-star
|
||||
| Toggle Project Header video
|
||||
|
||||
li.button-move
|
||||
a#item_move(
|
||||
href="javascript:void(0);",
|
||||
title="Move into a folder...",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
i.button-move-icon.pi-move
|
||||
| Move
|
||||
|
||||
li.button-delete
|
||||
a#item_delete(
|
||||
href="javascript:void(0);",
|
||||
title="Can be undone within a month",
|
||||
data-toggle="tooltip",
|
||||
data-placement="left")
|
||||
i.pi-trash
|
||||
| Delete Project
|
||||
|
||||
// Edit Mode
|
||||
li.button-cancel
|
||||
a#item_cancel.project-mode-edit(
|
||||
href="javascript:void(0);",
|
||||
title="Cancel changes")
|
||||
i.button-cancel-icon.pi-cancel
|
||||
| Cancel
|
||||
|
||||
li.button-save
|
||||
a#item_save.project-mode-edit(
|
||||
href="javascript:void(0);",
|
||||
title="Save changes")
|
||||
i.button-save-icon.pi-check
|
||||
| Save Changes
|
||||
|
||||
| {% endif %}
|
||||
|
||||
| {% set utm_source = request.args.get('utm_source') %}
|
||||
| {% if config.UTM_LINKS and utm_source in config.UTM_LINKS %}
|
||||
#utm_container
|
||||
a(href="{{config.UTM_LINKS[utm_source]['link']}}")
|
||||
img(src="{{config.UTM_LINKS[utm_source]['image']}}", alt="gift", class="img-responsive")
|
||||
| {% endif %}
|
||||
#project_context
|
||||
| {% block project_context %}
|
||||
| {% if show_project %}
|
||||
| {% include "projects/view_embed.html" %}
|
||||
| {% endif %}
|
||||
| {% endblock project_context %}
|
||||
|
||||
#overlay-mode-move-container
|
||||
.overlay-container
|
||||
.title
|
||||
i.pi-angle-left
|
||||
| Select the <strong>folder</strong> where you want to move it
|
||||
.buttons
|
||||
button#item_move_accept.move.disabled
|
||||
| Select a Folder
|
||||
button#item_move_cancel.cancel
|
||||
i.pi-cancel
|
||||
| Cancel
|
||||
|
||||
.p-5.text-center
|
||||
h2 Project View
|
||||
.lead.
|
||||
See Blender Cloud for reference.
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_navigation %}{% endblock %}
|
||||
| {% block footer %}{% endblock %}
|
||||
|
||||
|
||||
| {% block footer_scripts_pre %}
|
||||
|
||||
| {% if project.has_method('PUT') %}
|
||||
| {# JS containing the Edit, Add, Featured, and Move functions #}
|
||||
script(type="text/javascript", src="{{ url_for('static_pillar', filename='assets/js/project-edit.min.js') }}")
|
||||
| {% endif %}
|
||||
|
||||
script.
|
||||
function updateToggleProjHeaderMenuItem() {
|
||||
var $toggle_projheader = $('#item_toggle_projheader');
|
||||
|
||||
if (ProjectUtils.isProject()) {
|
||||
$toggle_projheader.hide();
|
||||
return;
|
||||
}
|
||||
if (ProjectUtils.nodeType() == 'asset') {
|
||||
$toggle_projheader.show();
|
||||
} else {
|
||||
$toggle_projheader.hide();
|
||||
}
|
||||
}
|
||||
$(updateToggleProjHeaderMenuItem);
|
||||
|
||||
// Function to update the interface on loadNodeContent, and edit/saving assets
|
||||
function updateUi(nodeId, mode) {
|
||||
|
||||
if (mode === 'view') {
|
||||
$('.project-mode-view').show();
|
||||
$('.project-mode-edit').hide();
|
||||
|
||||
$("#node-edit-form").unbind("submit");
|
||||
$("#item_save").unbind("click");
|
||||
$("#item_cancel").unbind("click");
|
||||
} else if (mode === 'edit') {
|
||||
$('.project-mode-view').hide();
|
||||
$('.project-mode-edit').show();
|
||||
} else {
|
||||
if (console) console.log('Invalid mode:', mode);
|
||||
}
|
||||
|
||||
// Prevent flicker by scrolling to top
|
||||
$("#project_context-container").scrollTop(0);
|
||||
|
||||
// Enable specific items under the Add New dropdown
|
||||
if (ProjectUtils.nodeType() === 'group') {
|
||||
addMenuEnable(['asset', 'group']);
|
||||
|
||||
} else if (ProjectUtils.nodeType() === 'group_texture') {
|
||||
addMenuEnable(['group_texture', 'texture']);
|
||||
|
||||
} else if (ProjectUtils.nodeType() === 'group_hdri') {
|
||||
addMenuEnable(['group_hdri', 'hdri']);
|
||||
|
||||
} else if (!ProjectUtils.isProject()) {
|
||||
addMenuEnable(false);
|
||||
}
|
||||
|
||||
updateToggleProjHeaderMenuItem();
|
||||
|
||||
// Set the page title on the document
|
||||
var page_title = $('#node-title').text() + " - {{ project.name }} — Blender Cloud";
|
||||
DocumentTitleAPI.set_page_title(page_title);
|
||||
|
||||
// TODO: Maybe remove this, now it's also in loadNodeContent(), but double-check
|
||||
// it's done like that in all users of updateUi().
|
||||
$('#project-loading').removeAttr('class');
|
||||
}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script(src="{{ url_for('static_pillar', filename='assets/jstree/jstree.min.js') }}")
|
||||
|
||||
script.
|
||||
{% if show_project %}
|
||||
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: true, nodeId: ''});
|
||||
{% else %}
|
||||
{% if node %}
|
||||
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: false, nodeId: '{{node._id}}'});
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
var projectTree = document.getElementById('project_tree');
|
||||
|
||||
var urlNodeMove = "{{url_for('projects.move_node')}}";
|
||||
var urlNodeFeature = "{{url_for('projects.add_featured_node')}}";
|
||||
var urlNodeDelete = "{{url_for('projects.delete_node')}}";
|
||||
var urlNodeTogglePublic = "{{url_for('projects.toggle_node_public')}}";
|
||||
var urlNodeToggleProjHeader = "{{url_for('projects.toggle_node_project_header')}}";
|
||||
var urlProjectDelete = "{{url_for('projects.delete')}}";
|
||||
var urlProjectEdit = "{{url_for('projects.edit', project_url=project.url)}}";
|
||||
|
||||
|
||||
function loadNodeContent(url, nodeId) {
|
||||
$('#project-loading').addClass('active');
|
||||
$.get(url, function(dataHtml) {
|
||||
// Update the DOM injecting the generate HTML into the page
|
||||
$('#project_context').html(dataHtml);
|
||||
})
|
||||
.done(function(){
|
||||
updateUi(nodeId, 'view');
|
||||
})
|
||||
.fail(function(dataResponse) {
|
||||
$('#project_context').html($('<iframe id="server_error"/>'));
|
||||
$('#server_error').attr('src', url);
|
||||
})
|
||||
.always(function(){
|
||||
$('#project-loading').removeAttr('class');
|
||||
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadProjectContent(url) {
|
||||
$('#project-loading').addClass('active');
|
||||
|
||||
$.get(url, function(dataHtml) {
|
||||
// Update the DOM injecting the generated HTML into the page
|
||||
$('#project_context').html(dataHtml);
|
||||
})
|
||||
.done(function() {
|
||||
updateUi('', 'view');
|
||||
addMenuEnable();
|
||||
addMenuDisable(['texture']);
|
||||
})
|
||||
.fail(function(dataResponse) {
|
||||
$('#project_context').html($('<iframe id="server_error"/>'));
|
||||
$('#server_error').attr('src', url);
|
||||
})
|
||||
.always(function(){
|
||||
$('#project-loading').removeAttr('class');
|
||||
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function displayStorage(storageNodeId, path) {
|
||||
var url = '/nodes/' + storageNodeId + '/view?path=' + path;
|
||||
loadNodeContent(url);
|
||||
}
|
||||
|
||||
|
||||
function displayNode(nodeId, pushState) {
|
||||
// Remove the 'n_' suffix from the id
|
||||
if (nodeId.substring(0, 2) == 'n_') {
|
||||
nodeId = nodeId.substr(2);
|
||||
}
|
||||
|
||||
var url = '/nodes/' + nodeId + '/view';
|
||||
loadNodeContent(url, nodeId);
|
||||
|
||||
// Determine whether we should push the new state or not.
|
||||
pushState = (typeof pushState !== 'undefined') ? pushState : true;
|
||||
if (!pushState) return;
|
||||
|
||||
// Push the correct URL onto the history.
|
||||
var push_state = {nodeId: nodeId, url: url};
|
||||
var push_url = '{{url_for("projects.view", project_url=project.url)}}' + nodeId;
|
||||
// console.log('Pushing state ', push_state, ' with URL ', push_url);
|
||||
window.history.pushState(
|
||||
push_state,
|
||||
'Node ' + nodeId, // TODO: use sensible title
|
||||
push_url
|
||||
);
|
||||
}
|
||||
|
||||
function redirectToNode(nodeId) {
|
||||
var generic_url = '{{ url_for("projects.view_node", project_url=project.url, node_id="theNodeId") }}';
|
||||
var node_url = generic_url.replace('theNodeId', nodeId);
|
||||
|
||||
// This makes the user skip the current page when using the 'back' button,
|
||||
// i.e. it works as a proper redirect.
|
||||
location.replace(node_url);
|
||||
}
|
||||
|
||||
window.onpopstate = function(event) {
|
||||
var state = event.state;
|
||||
// console.log('State popped. location:', document.location, 'state:', state);
|
||||
|
||||
// Deselect any selected node. We'll select the visited node (if any) later on.
|
||||
var jstreeAPI = $(projectTree).jstree(true);
|
||||
jstreeAPI.deselect_all(true);
|
||||
|
||||
if (state == null) {
|
||||
// Went back to the project.
|
||||
displayProject();
|
||||
return;
|
||||
}
|
||||
|
||||
// Went back to a node.
|
||||
loadNodeContent(state.url, state.nodeId);
|
||||
|
||||
// Annoying hack because jstreeAPI.select_node() can only suppress the
|
||||
// changed.jstree event, and NOT the selected_node.jstree event.
|
||||
projectTree.dataset.ignoreSelectNode = true;
|
||||
jstreeAPI.select_node('n_' + state.nodeId, true);
|
||||
delete projectTree.dataset.ignoreSelectNode;
|
||||
};
|
||||
|
||||
function displayProject() {
|
||||
var url = "{{url_for('projects.view', project_url=project.url, embed=1)}}";
|
||||
loadProjectContent(url);
|
||||
}
|
||||
|
||||
|
||||
function getHashId() {
|
||||
if (console)
|
||||
console.log('getHashId() should not be used any more!');
|
||||
}
|
||||
|
||||
/* Loaded once, on page load */
|
||||
function loadContent() {
|
||||
|
||||
var nodeId = ProjectUtils.nodeId();
|
||||
var isProject = ProjectUtils.isProject();
|
||||
if (isProject) {
|
||||
// No need to asynchronously load the project, as it's embedded by Jinja.
|
||||
// displayProject() is still needed, though, when people use 'back' to go there.
|
||||
if (location.hash) {
|
||||
// Handle old-style /p/{url}/#node-ID links, and redirect them to the correct spot.
|
||||
redirectToNode(location.hash.substr(1));
|
||||
}
|
||||
$('.project-mode-view').show();
|
||||
$('.project-mode-edit').hide();
|
||||
} else {
|
||||
displayNode(nodeId, false);
|
||||
}
|
||||
|
||||
$(projectTree).jstree({
|
||||
'core': {
|
||||
'data': function (obj, callback) {
|
||||
if(obj.id === '#') { //tree root
|
||||
if (isProject) {
|
||||
$.getJSON("{{url_for('projects.jstree', project_url=project.url)}}", function (jsonObject) {
|
||||
callback.call(this, jsonObject['items']);
|
||||
});
|
||||
} else {
|
||||
$.getJSON('/nodes/' + nodeId + '/jstree', function(jsonObject) {
|
||||
callback.call(this, jsonObject['items']);
|
||||
});
|
||||
}
|
||||
} else { //normal node
|
||||
var childNodeId;
|
||||
if (obj.original.type == 'group_storage') {
|
||||
childNodeId = obj.original.storage_node;
|
||||
$.getJSON('/nodes/' + childNodeId + '/jstree?children=1&path=' + obj.original.path, function(jsonObject) {
|
||||
callback.call(this, jsonObject.children);
|
||||
});
|
||||
} else {
|
||||
// Remove the 'n_' suffix from the id
|
||||
childNodeId = obj.id.substring(2);
|
||||
$.getJSON('/nodes/' + childNodeId + '/jstree?children=1', function(jsonObject) {
|
||||
callback.call(this, jsonObject.children);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"types" : {
|
||||
"#": {"valid_children": ["collection"]},
|
||||
"chapter" : {"icon": "pi-folder"},
|
||||
"group" : {"icon": "pi-folder"},
|
||||
"group_texture" : {"icon": "pi-folder-texture"},
|
||||
"group_hdri" : {"icon": "pi-folder-texture", "max_children": 0},
|
||||
"group_storage" : {"icon": "pi-folder"},
|
||||
"filesystem_node" : {"icon": "pi-folder"},
|
||||
"file" : {"icon": "pi-file-archive", "max_children": 0},
|
||||
"filesystem_file" : {"icon": "pi-document", "max_children": 0},
|
||||
"image" : {"icon": "pi-image", "max_children": 0},
|
||||
"hdri" : {"icon": "pi-globe", "max_children": 0},
|
||||
"texture" : {"icon": "pi-texture", "max_children": 0},
|
||||
"video" : {"icon": "pi-film-thick", "max_children": 0},
|
||||
"blog" : {"icon": "pi-newspaper", "max_children": 0},
|
||||
"page" : {"icon": "pi-document-text", "max_children": 0},
|
||||
"default" : {"icon": "pi-document"}
|
||||
},
|
||||
"plugins": ["types",] //, "state", "sort"
|
||||
});
|
||||
|
||||
|
||||
var jstreeAPI = $(projectTree).jstree(true);
|
||||
|
||||
$(projectTree).on("select_node.jstree", function (e, data) {
|
||||
var selectedNodeId = data.node.id.substr(2);
|
||||
|
||||
// Ignore events that can't be suppressed otherwise.
|
||||
// This can be removed if jstreeAPI.select_node() allows suppressing
|
||||
// the select_node.jstree event.
|
||||
if (e.target.dataset.ignoreSelectNode === 'true') return;
|
||||
|
||||
if (typeof(data.node.original.path) === 'undefined') {
|
||||
var movingMode = Cookies.getJSON('bcloud_moving_node');
|
||||
|
||||
// Check if we are in the process of moving a node
|
||||
if (movingMode) {
|
||||
// Allow moving nodes only inside of node_type group
|
||||
if (data.node.original.type != 'group' || movingMode.node_id === selectedNodeId || movingMode.node_id === ProjectUtils.parentNodeId()) {
|
||||
|
||||
if (movingMode.node_type === 'texture') {
|
||||
|
||||
if (data.node.original.type === 'group_texture') {
|
||||
$('#item_move_accept').html('<i class="pi-check"></i>Move Here').removeClass('disabled');
|
||||
} else {
|
||||
$('#item_move_accept').html('Select a Texture Folder').addClass('disabled');
|
||||
}
|
||||
|
||||
} else if (movingMode.node_type === 'hdri') {
|
||||
|
||||
if (data.node.original.type === 'group_hdri') {
|
||||
$('#item_move_accept').html('<i class="pi-check"></i>Move Here').removeClass('disabled');
|
||||
} else {
|
||||
$('#item_move_accept').html('Select an HDRi Folder').addClass('disabled');
|
||||
}
|
||||
|
||||
} else {
|
||||
$('#item_move_accept').html('Select a Folder').addClass('disabled');
|
||||
}
|
||||
|
||||
} else {
|
||||
$('#item_move_accept').html('<i class="pi-check"></i>Move Here').removeClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
// Check the type of node and act accordingly
|
||||
if (data.node.original.custom_view) {
|
||||
window.location = data.node.a_attr.href;
|
||||
} else {
|
||||
var currentNodeId = ProjectUtils.nodeId();
|
||||
if (currentNodeId != selectedNodeId) {
|
||||
displayNode(selectedNodeId);
|
||||
}
|
||||
|
||||
jstreeAPI.open_node(data.node);
|
||||
}
|
||||
} else {
|
||||
displayStorage(data.node.original.storage_node, data.node.original.path);
|
||||
jstreeAPI.toggle_node(data.node);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
{% if is_embedded_edit is not defined or is_embedded_edit %}
|
||||
// Initialize the page if we are not directly editing a node (most of the time)
|
||||
loadContent();
|
||||
{% endif %}
|
||||
|
||||
var project_container = document.getElementById('project-container');
|
||||
|
||||
/* UI Stuff */
|
||||
$(window).on("load resize",function(){
|
||||
containerResizeY($(window).height());
|
||||
|
||||
if ($(window).width() > 480) {
|
||||
project_container.style.height = (window.innerHeight - project_container.offsetTop) + "px";
|
||||
}
|
||||
});
|
||||
|
||||
{% if current_user_is_subscriber %}
|
||||
$(projectTree).addClass('is_subscriber');
|
||||
{% endif %}
|
||||
|
||||
| {% endblock %}
|
||||
|
||||
| {% block comment_scripts %} {% endblock%}
|
||||
|
@@ -22,9 +22,8 @@
|
||||
| {% endif %}
|
||||
|
||||
section.node-details-container.project
|
||||
.node-details-title
|
||||
h1
|
||||
a(href="{{ url_for( 'projects.view', project_url=project.url) }}") {{ project.name }}
|
||||
h2.px-3.pt-3
|
||||
a.text-muted(href="{{ url_for( 'projects.view', project_url=project.url) }}") {{ project.name }}
|
||||
|
||||
| {% if project.description %}
|
||||
.node-details-description
|
||||
@@ -127,5 +126,4 @@ script.
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
| {% endblock %}
|
||||
|
@@ -49,7 +49,7 @@
|
||||
h3.
|
||||
Now only $10 per month
|
||||
|
||||
.btn.btn-default
|
||||
.btn.btn-outline-primary
|
||||
| Join the Cloud
|
||||
|
||||
hr
|
||||
|
@@ -72,9 +72,9 @@
|
||||
| none
|
||||
| {% endif %}
|
||||
|
||||
a.btn.btn-default(href="javascript:update_from_bid()") Update from Blender ID
|
||||
a.btn.btn-outline-secondary(href="javascript:update_from_bid()") Update from Blender ID
|
||||
|
||||
input#submit_edit_user.btn.btn-default(
|
||||
input#submit_edit_user.btn.btn-outline-success(
|
||||
data-user-id="{{user.user_id}}",
|
||||
type="submit" value="Submit")
|
||||
|
||||
|
@@ -18,11 +18,11 @@
|
||||
.buttons
|
||||
.login-button-container
|
||||
//a.forgot(href="https://blender.org/id/reset") forgot your password?
|
||||
button.btn.btn-success.btn-block.button-login(type="submit")
|
||||
button.btn.btn-outline-success.btn-block.button-login(type="submit")
|
||||
i.pi-log-in
|
||||
| Login
|
||||
|
||||
//a.btn.btn-default.button-register(href="https://blender.org/id/register", target="_blank")
|
||||
//a.btn.btn-outline-secondary.button-register(href="https://blender.org/id/register", target="_blank")
|
||||
// i.pi-star-outline
|
||||
// | Create Account
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
//- can provide overrides.
|
||||
| {% block body %}
|
||||
.container
|
||||
#settings
|
||||
#settings.d-flex.py-4.flex-xs-column
|
||||
#settings-sidebar
|
||||
| {% block settings_sidebar %}
|
||||
.settings-header
|
||||
|
@@ -28,7 +28,7 @@
|
||||
span {{ _("Change Gravatar") }}
|
||||
|
||||
.buttons
|
||||
button.btn.btn-default.button-submit(type='submit')
|
||||
button.btn.btn-outline-success.button-submit(type='submit')
|
||||
i.pi-check
|
||||
| {{ _("Save Changes") }}
|
||||
| {% endblock %}
|
||||
|
@@ -137,7 +137,7 @@ script().
|
||||
$('td', row).eq(4).html(render_timing(data.timing));
|
||||
$('td', row).eq(5).html(render_status_label(data, data.name));
|
||||
|
||||
var view_tag = '<span class="btn btn-default btn-xs load-shot-view" shot-view-url="' + data.url_edit + '"><i class="glyphicon glyphicon-edit"></i> View</span>';
|
||||
var view_tag = '<span class="btn btn-outline-secondary btn-xs load-shot-view" shot-view-url="' + data.url_edit + '"><i class="glyphicon glyphicon-edit"></i> View</span>';
|
||||
$('td', row).eq(6).html(view_tag);
|
||||
}
|
||||
});
|
||||
|
199
tests/test_api/test_cerberus.py
Normal file
199
tests/test_api/test_cerberus.py
Normal file
@@ -0,0 +1,199 @@
|
||||
"""Test that what we feed to Cerberus actually works.
|
||||
|
||||
This'll help us upgrade to new versions of Cerberus.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from pillar.tests import AbstractPillarTest
|
||||
|
||||
from bson import ObjectId
|
||||
|
||||
|
||||
class CerberusCanaryTest(unittest.TestCase):
|
||||
|
||||
def _canary_test(self, validator):
|
||||
groups_schema = {'name': {'type': 'string', 'required': True}}
|
||||
|
||||
# On error, validate_schema() raises ValidationError
|
||||
validator.validate_schema(groups_schema)
|
||||
|
||||
# On error, validate() returns False
|
||||
self.assertTrue(validator.validate({'name': 'je moeder'}, groups_schema))
|
||||
self.assertFalse(validator.validate({'je moeder': 'op je hoofd'}, groups_schema))
|
||||
|
||||
def test_canary(self):
|
||||
import cerberus
|
||||
|
||||
validator = cerberus.Validator()
|
||||
self._canary_test(validator)
|
||||
|
||||
def test_our_validator_simple(self):
|
||||
from pillar.api import custom_field_validation
|
||||
|
||||
validator = custom_field_validation.ValidateCustomFields()
|
||||
self._canary_test(validator)
|
||||
|
||||
|
||||
class ValidationTest(AbstractPillarTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
from pillar.api import custom_field_validation
|
||||
|
||||
self.validator = custom_field_validation.ValidateCustomFields()
|
||||
self.user_id = ObjectId(8 * 'abc')
|
||||
self.ensure_user_exists(self.user_id, 'Tést Üsâh')
|
||||
|
||||
def assertValid(self, document, schema):
|
||||
with self.app.app_context():
|
||||
is_valid = self.validator.validate(document, schema)
|
||||
self.assertTrue(is_valid, f'errors: {self.validator.errors}')
|
||||
|
||||
def assertInvalid(self, document, schema):
|
||||
with self.app.app_context():
|
||||
is_valid = self.validator.validate(document, schema)
|
||||
self.assertFalse(is_valid)
|
||||
|
||||
|
||||
class ProjectValidationTest(ValidationTest):
|
||||
|
||||
def test_empty(self):
|
||||
from pillar.api.eve_settings import projects_schema
|
||||
self.assertInvalid({}, projects_schema)
|
||||
|
||||
def test_simple_project(self):
|
||||
from pillar.api.eve_settings import projects_schema
|
||||
|
||||
project = {
|
||||
'name': 'Té Ærhüs',
|
||||
'user': self.user_id,
|
||||
'category': 'assets',
|
||||
'is_private': False,
|
||||
'status': 'published',
|
||||
}
|
||||
|
||||
self.assertValid(project, projects_schema)
|
||||
|
||||
def test_with_node_types(self):
|
||||
from pillar.api.eve_settings import projects_schema
|
||||
from pillar.api import node_types
|
||||
|
||||
project = {
|
||||
'name': 'Té Ærhüs',
|
||||
'user': self.user_id,
|
||||
'category': 'assets',
|
||||
'is_private': False,
|
||||
'status': 'published',
|
||||
'node_types': [node_types.node_type_asset,
|
||||
node_types.node_type_comment]
|
||||
}
|
||||
|
||||
self.assertValid(project, projects_schema)
|
||||
|
||||
|
||||
class NodeValidationTest(ValidationTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.pid, self.project = self.ensure_project_exists()
|
||||
|
||||
def test_empty(self):
|
||||
from pillar.api.eve_settings import nodes_schema
|
||||
self.assertInvalid({}, nodes_schema)
|
||||
|
||||
def test_asset(self):
|
||||
from pillar.api.eve_settings import nodes_schema
|
||||
|
||||
file_id, _ = self.ensure_file_exists()
|
||||
|
||||
node = {
|
||||
'name': '"The Harmless Prototype™"',
|
||||
'project': self.pid,
|
||||
'node_type': 'asset',
|
||||
'properties': {
|
||||
'status': 'published',
|
||||
'content_type': 'image',
|
||||
'file': file_id,
|
||||
},
|
||||
'user': self.user_id,
|
||||
'short_code': 'ABC333',
|
||||
}
|
||||
self.assertValid(node, nodes_schema)
|
||||
|
||||
def test_asset_invalid_properties(self):
|
||||
from pillar.api.eve_settings import nodes_schema
|
||||
|
||||
file_id, _ = self.ensure_file_exists()
|
||||
|
||||
node = {
|
||||
'name': '"The Harmless Prototype™"',
|
||||
'project': self.pid,
|
||||
'node_type': 'asset',
|
||||
'properties': {
|
||||
'status': 'invalid-status',
|
||||
'content_type': 'image',
|
||||
'file': file_id,
|
||||
},
|
||||
'user': self.user_id,
|
||||
'short_code': 'ABC333',
|
||||
}
|
||||
self.assertInvalid(node, nodes_schema)
|
||||
|
||||
def test_comment(self):
|
||||
from pillar.api.eve_settings import nodes_schema
|
||||
|
||||
file_id, _ = self.ensure_file_exists()
|
||||
|
||||
node = {
|
||||
'name': '"The Harmless Prototype™"',
|
||||
'project': self.pid,
|
||||
'node_type': 'asset',
|
||||
'properties': {
|
||||
'status': 'published',
|
||||
'content_type': 'image',
|
||||
'file': file_id,
|
||||
},
|
||||
'user': self.user_id,
|
||||
'short_code': 'ABC333',
|
||||
}
|
||||
node_id = self.create_node(node)
|
||||
|
||||
comment = {
|
||||
'name': 'comment on some node',
|
||||
'project': self.pid,
|
||||
'node_type': 'comment',
|
||||
'properties': {
|
||||
'content': 'this is a comment',
|
||||
'status': 'published',
|
||||
},
|
||||
'parent': node_id,
|
||||
}
|
||||
self.assertValid(comment, nodes_schema)
|
||||
|
||||
|
||||
class IPRangeValidatorTest(ValidationTest):
|
||||
schema = {'iprange': {'type': 'iprange', 'required': True}}
|
||||
|
||||
def assertValid(self, document, schema=None):
|
||||
return super().assertValid(document, schema or self.schema)
|
||||
|
||||
def assertInvalid(self, document, schema=None):
|
||||
return super().assertInvalid(document, schema or self.schema)
|
||||
|
||||
def test_ipv6(self):
|
||||
self.assertValid({'iprange': '2a03:b0c0:0:1010::8fe:6ef1'})
|
||||
self.assertValid({'iprange': '0:0:0:0:0:ffff:102:304'})
|
||||
self.assertValid({'iprange': '2a03:b0c0:0:1010::8fe:6ef1/120'})
|
||||
self.assertValid({'iprange': 'ff06::/8'})
|
||||
self.assertValid({'iprange': '::/8'})
|
||||
self.assertValid({'iprange': '::/1'})
|
||||
self.assertValid({'iprange': '::1/128'})
|
||||
self.assertValid({'iprange': '::'})
|
||||
self.assertInvalid({'iprange': '::/0'})
|
||||
self.assertInvalid({'iprange': 'barbled'})
|
||||
|
||||
def test_ipv4(self):
|
||||
self.assertValid({'iprange': '1.2.3.4'})
|
||||
self.assertValid({'iprange': '1.2.3.4/24'})
|
||||
self.assertValid({'iprange': '127.0.0.0/8'})
|
||||
self.assertInvalid({'iprange': '127.0.0.0/0'})
|
||||
self.assertInvalid({'iprange': 'garbled'})
|
@@ -12,7 +12,7 @@ class OAuthTests(AbstractPillarTest):
|
||||
|
||||
oauth_provider = OAuthSignIn.get_provider('blender-id')
|
||||
self.assertIsInstance(oauth_provider, BlenderIdSignIn)
|
||||
self.assertEqual(oauth_provider.service.base_url, 'http://blender-id:8000/api/')
|
||||
self.assertEqual(oauth_provider.service.base_url, 'http://id.local:8001/api/')
|
||||
|
||||
def test_provider_not_implemented(self):
|
||||
from pillar.auth.oauth import OAuthSignIn, ProviderNotImplemented
|
||||
@@ -46,11 +46,11 @@ class OAuthTests(AbstractPillarTest):
|
||||
def test_provider_callback_happy(self):
|
||||
from pillar.auth.oauth import OAuthSignIn
|
||||
|
||||
responses.add(responses.POST, 'http://blender-id:8000/oauth/token',
|
||||
responses.add(responses.POST, 'http://id.local:8001/oauth/token',
|
||||
json={'access_token': 'successful-token'},
|
||||
status=200)
|
||||
|
||||
responses.add(responses.GET, 'http://blender-id:8000/api/user',
|
||||
responses.add(responses.GET, 'http://id.local:8001/api/user',
|
||||
json={'id': '7',
|
||||
'email': 'harry@blender.org'},
|
||||
status=200)
|
||||
|
@@ -184,3 +184,31 @@ class NodeSetattrTest(unittest.TestCase):
|
||||
|
||||
node_setattr(node, 'b.complex', {None: 5})
|
||||
self.assertEqual({'b': {'complex': {None: 5}}}, node)
|
||||
|
||||
|
||||
class TestRating(unittest.TestCase):
|
||||
def test_hotness(self):
|
||||
"""We expect the sorted values to reflect the original order in the
|
||||
list.
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
from pillar.api.utils.rating import hot
|
||||
t = datetime(2017, 2, 11, 0, 0, 0, 0, timezone.utc)
|
||||
y = datetime(2017, 2, 10, 0, 0, 0, 0, timezone.utc)
|
||||
w = datetime(2017, 2, 5, 0, 0, 0, 0, timezone.utc)
|
||||
cases = [
|
||||
(hot(1, 8, t), 'today super bad'),
|
||||
(hot(0, 3, t), 'today slightly worse'),
|
||||
(hot(0, 2, y), 'yesterday bad'),
|
||||
(hot(0, 2, t), 'today bad'),
|
||||
(hot(4, 4, w), 'last week controversial'),
|
||||
(hot(7, 1, w), 'last week very good'),
|
||||
(hot(5, 1, y), 'yesterday medium'),
|
||||
(hot(5, 0, y), 'yesterday good'),
|
||||
(hot(7, 1, y), 'yesterday very good'),
|
||||
(hot(4, 4, t), 'today controversial'),
|
||||
(hot(7, 1, t), 'today very good'),
|
||||
]
|
||||
sorted_by_hot = sorted(cases, key=lambda tup: tup[0])
|
||||
for idx, t in enumerate(sorted_by_hot):
|
||||
self.assertEqual(cases[idx][0], t[0])
|
||||
|
@@ -40,7 +40,7 @@ class DemoTest(unittest.TestCase):
|
||||
self.assertEqual('<dl><dt>test</dt><dt>ü</dt><dd>é</dd></dl>', render('{test ü="é"}'))
|
||||
|
||||
|
||||
class YouTubeTest(unittest.TestCase):
|
||||
class YouTubeTest(AbstractPillarTest):
|
||||
def test_missing(self):
|
||||
from pillar.shortcodes import render
|
||||
|
||||
@@ -104,6 +104,19 @@ class YouTubeTest(unittest.TestCase):
|
||||
render('{youtube "https://www.youtube.com/watch?v=NwVGvcIrNWA" width=5 height="3"}')
|
||||
)
|
||||
|
||||
def test_user_no_cap(self):
|
||||
from pillar.shortcodes import render
|
||||
|
||||
with self.app.app_context():
|
||||
# Anonymous user, so no subscriber capability.
|
||||
self.assertEqual('', render('{youtube ABCDEF cap=subscriber}'))
|
||||
self.assertEqual('', render('{youtube ABCDEF cap="subscriber"}'))
|
||||
self.assertEqual(
|
||||
'<p class="shortcode nocap">Aðeins áskrifendur hafa aðgang að þessu efni.</p>',
|
||||
render('{youtube ABCDEF'
|
||||
' cap="subscriber"'
|
||||
' nocap="Aðeins áskrifendur hafa aðgang að þessu efni."}'))
|
||||
|
||||
|
||||
class IFrameTest(AbstractPillarTest):
|
||||
def test_missing_cap(self):
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user