Compare commits
14 Commits
tmp-video-
...
wip-tags-v
Author | SHA1 | Date | |
---|---|---|---|
263d68071e | |||
0f7f7d5a66 | |||
6b29c70212 | |||
07670dce96 | |||
fe288b1cc2 | |||
2e9555e160 | |||
b0311af6b5 | |||
35a22cab4b | |||
0055633732 | |||
78b186c8e4 | |||
232321cc2c | |||
a6d662b690 | |||
32c7ffbc99 | |||
cfcc629b61 |
2176
package-lock.json
generated
2176
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -94,13 +94,31 @@ def share_node(node_id):
|
||||
@blueprint.route('/tagged/<tag>')
|
||||
def tagged(tag=''):
|
||||
"""Return all tagged nodes of public projects as JSON."""
|
||||
from pillar.auth import current_user
|
||||
|
||||
# We explicitly register the tagless endpoint to raise a 404, otherwise the PATCH
|
||||
# handler on /api/nodes/<node_id> will return a 405 Method Not Allowed.
|
||||
if not tag:
|
||||
raise wz_exceptions.NotFound()
|
||||
|
||||
return _tagged(tag)
|
||||
# Build the (cached) list of tagged nodes
|
||||
agg_list = _tagged(tag)
|
||||
|
||||
# If the user is anonymous, no more information is needed and we return
|
||||
if current_user.is_anonymous:
|
||||
return jsonify(agg_list)
|
||||
|
||||
# If the user is authenticated, attach view_progress for video assets
|
||||
view_progress = current_user.nodes['view_progress']
|
||||
for node in agg_list:
|
||||
node_id = str(node['_id'])
|
||||
# View progress should be added only for nodes of type 'asset' and
|
||||
# with content_type 'video', only if the video was already in the watched
|
||||
# list for the current user.
|
||||
if node_id in view_progress:
|
||||
node['view_progress'] = view_progress[node_id]
|
||||
|
||||
return jsonify(agg_list)
|
||||
|
||||
|
||||
def _tagged(tag: str):
|
||||
@@ -129,7 +147,8 @@ def _tagged(tag: str):
|
||||
|
||||
{'$sort': {'_created': -1}}
|
||||
])
|
||||
return jsonify(list(agg))
|
||||
|
||||
return list(agg)
|
||||
|
||||
|
||||
def generate_and_store_short_code(node):
|
||||
|
@@ -61,16 +61,10 @@ def posts_view(project_id=None, project_url=None, url=None, *, archive=False, pa
|
||||
post.picture = get_file(post.picture, api=api)
|
||||
post.url = url_for_node(node=post)
|
||||
|
||||
# Use the *_main_project.html template for the main blog
|
||||
is_main_project = project_id == current_app.config['MAIN_PROJECT_ID']
|
||||
main_project_template = '_main_project' if is_main_project else ''
|
||||
main_project_template = '_main_project'
|
||||
index_arch = 'archive' if archive else 'index'
|
||||
template_path = f'nodes/custom/blog/{index_arch}{main_project_template}.html',
|
||||
template_path = f'nodes/custom/blog/{index_arch}.html',
|
||||
|
||||
if url:
|
||||
template_path = f'nodes/custom/post/view{main_project_template}.html',
|
||||
|
||||
post = Node.find_one({
|
||||
'where': {'parent': blog._id, 'properties.url': url},
|
||||
'embedded': {'node_type': 1, 'user': 1},
|
||||
@@ -95,6 +89,7 @@ def posts_view(project_id=None, project_url=None, url=None, *, archive=False, pa
|
||||
can_create_blog_posts = project.node_type_has_method('post', 'POST', api=api)
|
||||
|
||||
# Use functools.partial so we can later pass page=X.
|
||||
is_main_project = project_id == current_app.config['MAIN_PROJECT_ID']
|
||||
if is_main_project:
|
||||
url_func = functools.partial(url_for, 'main.main_blog_archive')
|
||||
else:
|
||||
@@ -121,7 +116,7 @@ def posts_view(project_id=None, project_url=None, url=None, *, archive=False, pa
|
||||
return render_template(
|
||||
template_path,
|
||||
blog=blog,
|
||||
node=post,
|
||||
node=post, # node is used by the generic comments rendering (see custom/_scripts.pug)
|
||||
posts=posts._items,
|
||||
posts_meta=pmeta,
|
||||
more_posts_available=pmeta['total'] > pmeta['max_results'],
|
||||
|
@@ -403,7 +403,6 @@ nav.sidebar
|
||||
$loader-bar-width: 100px
|
||||
$loader-bar-height: 2px
|
||||
.loader-bar
|
||||
background-color: $color-background
|
||||
bottom: 0
|
||||
content: ''
|
||||
display: none
|
||||
@@ -412,10 +411,12 @@ $loader-bar-height: 2px
|
||||
position: absolute
|
||||
visibility: hidden
|
||||
width: 100%
|
||||
z-index: 20
|
||||
|
||||
&:before
|
||||
animation: none
|
||||
background-color: $primary
|
||||
background-image: linear-gradient(to right, $primary-accent, $primary)
|
||||
content: ''
|
||||
display: block
|
||||
height: $loader-bar-height
|
||||
@@ -453,3 +454,4 @@ $loader-bar-height: 2px
|
||||
|
||||
.progress-bar
|
||||
background-color: $primary
|
||||
background-image: linear-gradient(to right, $primary-accent, $primary)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
$comments-width-max: 710px
|
||||
|
||||
.comments-container
|
||||
max-width: $comments-width-max
|
||||
position: relative
|
||||
|
||||
#comments-reload
|
||||
@@ -314,9 +315,6 @@ $comments-width-max: 710px
|
||||
color: $color-success
|
||||
|
||||
.comment-reply
|
||||
&-container
|
||||
background-color: $color-background
|
||||
|
||||
/* Little gravatar icon on the left */
|
||||
&-avatar
|
||||
img
|
||||
@@ -333,7 +331,7 @@ $comments-width-max: 710px
|
||||
width: 100%
|
||||
|
||||
&-field
|
||||
background-color: $color-background-dark
|
||||
background-color: $color-background-light
|
||||
border-radius: 3px
|
||||
box-shadow: inset 0 0 2px 0 rgba(darken($color-background-dark, 20%), .5)
|
||||
display: flex
|
||||
@@ -342,6 +340,7 @@ $comments-width-max: 710px
|
||||
|
||||
textarea
|
||||
+node-details-description
|
||||
background-color: $color-background-light
|
||||
border-bottom-right-radius: 0
|
||||
border-top-right-radius: 0
|
||||
border: none
|
||||
@@ -376,7 +375,6 @@ $comments-width-max: 710px
|
||||
|
||||
&.filled
|
||||
textarea
|
||||
background-color: $color-background-light
|
||||
border-bottom: thin solid $color-background
|
||||
|
||||
&:focus
|
||||
|
@@ -30,6 +30,7 @@ $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
|
||||
$primary-accent: #0bd
|
||||
|
||||
$color-secondary: #f42942 !default
|
||||
$color-secondary-light: hsl(hue($color-secondary), 30%, 90%) !default
|
||||
@@ -156,3 +157,5 @@ $tooltip-max-width: auto
|
||||
$tooltip-opacity: 1
|
||||
|
||||
$nav-link-height: 37px
|
||||
$navbar-padding-x: 0
|
||||
$navbar-padding-y: 0
|
@@ -24,13 +24,16 @@
|
||||
color: $color-secondary
|
||||
|
||||
#notifications-toggle
|
||||
color: $color-text
|
||||
cursor: pointer
|
||||
font-size: 1.5em
|
||||
position: relative
|
||||
user-select: none
|
||||
|
||||
> i:before
|
||||
content: '\e815'
|
||||
font-size: 1.3em
|
||||
position: relative
|
||||
top: 2px
|
||||
|
||||
&.has-notifications
|
||||
> i:before
|
||||
|
@@ -77,6 +77,8 @@ body.workshops
|
||||
|
||||
a
|
||||
color: $primary
|
||||
i
|
||||
+active-gradient
|
||||
|
||||
a
|
||||
align-items: center
|
||||
|
@@ -95,7 +95,7 @@ $search-hit-width_grid: 100px
|
||||
.search-list
|
||||
width: 30%
|
||||
|
||||
.card-deck.card-deck-horizontal
|
||||
.card-deck.card-deck-vertical
|
||||
.card .embed-responsive
|
||||
max-width: 80px
|
||||
|
||||
|
@@ -67,131 +67,6 @@
|
||||
&:hover
|
||||
background-color: lighten($provider-color-google, 7%)
|
||||
|
||||
#settings
|
||||
#settings-sidebar
|
||||
+media-xs
|
||||
width: 100%
|
||||
|
||||
+container-box
|
||||
background-color: $color-background-light
|
||||
color: $color-text
|
||||
margin-right: 15px
|
||||
width: 30%
|
||||
|
||||
.settings-content
|
||||
padding: 0
|
||||
|
||||
ul
|
||||
list-style: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
a
|
||||
&:hover
|
||||
text-decoration: none
|
||||
|
||||
li
|
||||
background-color: lighten($color-background, 5%)
|
||||
|
||||
li
|
||||
border-bottom: thin solid $color-background
|
||||
border-left: thick solid transparent
|
||||
margin: 0
|
||||
padding: 25px
|
||||
transition: all 100ms ease-in-out
|
||||
|
||||
i
|
||||
font-size: 1.1em
|
||||
padding-right: 15px
|
||||
|
||||
.active
|
||||
li
|
||||
background-color: lighten($color-background, 5%)
|
||||
border-left: thick solid $color-info
|
||||
|
||||
|
||||
#settings-container
|
||||
+media-xs
|
||||
width: 100%
|
||||
|
||||
+container-box
|
||||
background-color: $color-background-light
|
||||
width: 70%
|
||||
|
||||
.settings-header
|
||||
background-color: $color-background
|
||||
border-top-left-radius: 3px
|
||||
border-top-right-radius: 3px
|
||||
|
||||
.settings-title
|
||||
font:
|
||||
size: 1.5em
|
||||
weight: 300
|
||||
padding: 10px 15px 10px 25px
|
||||
|
||||
.settings-content
|
||||
padding: 25px
|
||||
|
||||
.settings-billing-info
|
||||
font-size: 1.2em
|
||||
|
||||
.subscription-active
|
||||
color: $color-success
|
||||
padding-bottom: 20px
|
||||
.subscription-demo
|
||||
color: $color-info
|
||||
margin-top: 0
|
||||
.subscription-missing
|
||||
color: $color-danger
|
||||
margin-top: 0
|
||||
|
||||
.button-submit
|
||||
clear: both
|
||||
display: block
|
||||
min-width: 200px
|
||||
margin: 0 auto
|
||||
+button($color-primary, 3px, true)
|
||||
|
||||
|
||||
#settings-container
|
||||
#settings-form
|
||||
width: 100%
|
||||
|
||||
|
||||
.settings-form
|
||||
align-items: center
|
||||
display: flex
|
||||
justify-content: center
|
||||
|
||||
.left, .right
|
||||
padding: 25px 0
|
||||
|
||||
.left
|
||||
width: 60%
|
||||
float: left
|
||||
|
||||
.right
|
||||
width: 40%
|
||||
float: right
|
||||
text-align: center
|
||||
|
||||
label
|
||||
color: $color-text
|
||||
display: block
|
||||
|
||||
.settings-avatar
|
||||
img
|
||||
border-radius: 3px
|
||||
|
||||
span
|
||||
display: block
|
||||
padding: 15px 0
|
||||
font:
|
||||
size: .9em
|
||||
|
||||
.settings-password
|
||||
color: $color-text-dark-primary
|
||||
|
||||
|
||||
#user-edit-container
|
||||
padding: 15px
|
||||
|
@@ -659,6 +659,9 @@
|
||||
.user-select-none
|
||||
user-select: none
|
||||
|
||||
.pointer-events-none
|
||||
pointer-events: 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.
|
||||
@@ -669,3 +672,15 @@
|
||||
|
||||
.overflow-hidden
|
||||
overflow: hidden
|
||||
|
||||
=text-gradient($color_from, $color_to)
|
||||
background: linear-gradient(to right, $color_from, $color_to)
|
||||
background-clip: text
|
||||
-webkit-background-clip: text
|
||||
-webkit-text-fill-color: transparent
|
||||
|
||||
=active-gradient
|
||||
+text-gradient($primary-accent, $primary)
|
||||
|
||||
&:before
|
||||
+text-gradient($primary-accent, $primary)
|
||||
|
@@ -15,77 +15,37 @@
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
@import _notifications
|
||||
|
||||
#blog_container
|
||||
+media-xs
|
||||
@@ -522,29 +482,3 @@
|
||||
|
||||
.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
|
||||
|
@@ -21,7 +21,7 @@
|
||||
flex: 1 0 20%
|
||||
max-width: 20%
|
||||
|
||||
&.card-deck-horizontal
|
||||
&.card-deck-vertical
|
||||
@extend .flex-column
|
||||
flex-wrap: initial
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
i
|
||||
opacity: .2
|
||||
|
||||
/* Tiny label for cards. e.g. 'WATCHED' on videos. */
|
||||
.card-label
|
||||
background-color: rgba($black, .5)
|
||||
border-radius: 3px
|
||||
@@ -105,7 +106,7 @@
|
||||
display: block
|
||||
font-size: $font-size-xxs
|
||||
left: 5px
|
||||
top: -25px
|
||||
top: -27px // enough to be above the progress-bar
|
||||
position: absolute
|
||||
padding: 1px 5px
|
||||
z-index: 1
|
||||
|
@@ -24,3 +24,21 @@ ul.dropdown-menu
|
||||
nav .dropdown:hover
|
||||
ul.dropdown-menu
|
||||
display: block
|
||||
|
||||
nav .dropdown.large:hover
|
||||
.dropdown-menu
|
||||
@extend .d-flex
|
||||
|
||||
.dropdown.large.show
|
||||
@extend .d-flex
|
||||
|
||||
.dropdown-menu.show
|
||||
@extend .d-flex
|
||||
|
||||
.dropdown-menu-tab
|
||||
display: none
|
||||
min-width: 100px
|
||||
|
||||
|
||||
&.show // .dropdown-menu-tab.show
|
||||
@extend .d-flex
|
||||
|
@@ -2,8 +2,7 @@
|
||||
.navbar
|
||||
box-shadow: inset 0 -2px $color-background
|
||||
|
||||
.navbar,
|
||||
nav.sidebar
|
||||
.nav
|
||||
border: none
|
||||
color: $color-text-dark-secondary
|
||||
padding: 0
|
||||
@@ -19,29 +18,6 @@ nav.sidebar
|
||||
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
|
||||
@@ -80,49 +56,74 @@ nav.sidebar
|
||||
i
|
||||
+position-center-translate
|
||||
|
||||
.dropdown
|
||||
min-width: 50px // navbar avatar size
|
||||
.dropdown
|
||||
min-width: 50px // navbar avatar size
|
||||
|
||||
span.fa-stack
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
.navbar-item
|
||||
&:hover
|
||||
box-shadow: none // Remove the blue underline usually on navbar, from dropdown items.
|
||||
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a
|
||||
white-space: nowrap
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a
|
||||
white-space: nowrap
|
||||
|
||||
&:hover
|
||||
box-shadow: none // removes underline
|
||||
.subitem // e.g. "Not Sintel? Log out"
|
||||
font-size: .8em
|
||||
text-transform: initial
|
||||
|
||||
.subitem // e.g. "Not Sintel? Log out"
|
||||
font-size: .8em
|
||||
text-transform: initial
|
||||
i
|
||||
width: 30px
|
||||
|
||||
i
|
||||
width: 30px
|
||||
&.subscription-status
|
||||
a, a:hover
|
||||
color: $white
|
||||
|
||||
&.subscription-status
|
||||
a, a:hover
|
||||
color: $white
|
||||
&.none
|
||||
background-color: $color-danger
|
||||
|
||||
&.none
|
||||
background-color: $color-danger
|
||||
&.subscriber
|
||||
background-color: $color-success
|
||||
|
||||
&.subscriber
|
||||
background-color: $color-success
|
||||
&.demo
|
||||
background-color: $color-info
|
||||
|
||||
&.demo
|
||||
background-color: $color-info
|
||||
span.info
|
||||
display: block
|
||||
|
||||
span.info
|
||||
span.renew
|
||||
display: block
|
||||
font-size: .9em
|
||||
|
||||
span.renew
|
||||
display: block
|
||||
font-size: .9em
|
||||
|
||||
.nav-link
|
||||
@extend .d-flex
|
||||
|
||||
.nav-title
|
||||
white-space: nowrap
|
||||
|
||||
.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
|
||||
|
||||
|
||||
/* Secondary navigation. */
|
||||
@@ -132,19 +133,36 @@ $nav-secondary-bar-size: -2px
|
||||
box-shadow: inset 0 $nav-secondary-bar-size 0 0 $color-background
|
||||
|
||||
.nav-link
|
||||
box-shadow: inset 0 $nav-secondary-bar-size 0 0 $color-background
|
||||
color: $color-text
|
||||
cursor: pointer
|
||||
transition: box-shadow 150ms ease-in-out
|
||||
transition: color 150ms ease-in-out
|
||||
|
||||
&:after
|
||||
background-color: transparent
|
||||
bottom: 0
|
||||
content: ''
|
||||
height: 2px
|
||||
position: absolute
|
||||
right: 0
|
||||
left: 0
|
||||
width: 0
|
||||
transition: width 150ms ease-in-out
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active,
|
||||
.nav-item.dropdown.show .nav-link
|
||||
.nav-item.dropdown.show > .nav-link
|
||||
// Blue bar on the bottom.
|
||||
box-shadow: inset 0 $nav-secondary-bar-size 0 0 $primary
|
||||
&:after
|
||||
background-color: $primary-accent
|
||||
background-image: linear-gradient(to right, $primary-accent 70%, $primary)
|
||||
height: 2px
|
||||
width: 100%
|
||||
|
||||
span
|
||||
+active-gradient
|
||||
|
||||
i
|
||||
color: $primary
|
||||
color: $primary-accent
|
||||
|
||||
&.nav-secondary-vertical
|
||||
align-items: flex-start
|
||||
@@ -156,19 +174,31 @@ $nav-secondary-bar-size: -2px
|
||||
|
||||
// Blue bar on the side.
|
||||
.nav-link
|
||||
box-shadow: inset 0 -1px 0 0 $color-background, inset -1px 0 0 0 $color-background
|
||||
|
||||
&:hover,
|
||||
&.active
|
||||
box-shadow: inset 0 -1px 0 0 $color-background, inset ($nav-secondary-bar-size * 1.5) 0 0 0 $primary
|
||||
color: $primary
|
||||
@extend .bg-white
|
||||
|
||||
&.nav-main
|
||||
&:after
|
||||
background-image: linear-gradient($primary-accent 70%, $primary)
|
||||
height: 100%
|
||||
left: initial
|
||||
top: 0
|
||||
width: 3px
|
||||
|
||||
// Big navigation dropdown.
|
||||
.nav-main
|
||||
.nav-secondary
|
||||
.nav-link
|
||||
color: $color-text-dark-secondary
|
||||
@extend .pr-5
|
||||
box-shadow: none
|
||||
|
||||
&:hover
|
||||
color: $body-color
|
||||
&.nav-see-more
|
||||
color: $primary
|
||||
font-size: $font-size-xxs
|
||||
|
||||
i, span
|
||||
+active-gradient
|
||||
|
||||
.navbar-overlay
|
||||
+media-lg
|
||||
@@ -188,15 +218,6 @@ $nav-secondary-bar-size: -2px
|
||||
background-color: $color-background-nav
|
||||
text-shadow: none
|
||||
|
||||
.navbar-brand
|
||||
color: inherit
|
||||
padding: 0 0 0 3px
|
||||
position: relative
|
||||
top: -2px
|
||||
|
||||
&:hover
|
||||
color: $primary
|
||||
|
||||
nav.navbar
|
||||
.navbar-collapse
|
||||
> ul > li > .navbar-item
|
||||
|
@@ -1,82 +0,0 @@
|
||||
// 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
|
||||
@import _error
|
||||
|
||||
@import _search
|
||||
|
||||
@import plugins/_jstree
|
||||
@import plugins/_js_select2
|
@@ -43,7 +43,6 @@ html(lang="en")
|
||||
|
||||
| {% 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")
|
||||
| {% if title == 'blog' %}
|
||||
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet")
|
||||
| {% else %}
|
||||
|
@@ -1,10 +1,11 @@
|
||||
| {% if current_user.is_authenticated %}
|
||||
|
||||
li.nav-notifications
|
||||
a.navbar-item#notifications-toggle.px-0(
|
||||
title="Notifications",
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom")
|
||||
li.nav-notifications.nav-item
|
||||
a.nav-link.px-2(
|
||||
id="notifications-toggle",
|
||||
title="Notifications",
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom")
|
||||
i.pi-notifications-none.nav-notifications-icon
|
||||
span#notifications-count
|
||||
span
|
||||
|
@@ -12,25 +12,6 @@ li.dropdown
|
||||
ul.dropdown-menu.dropdown-menu-right
|
||||
| {% if not current_user.has_role('protected') %}
|
||||
| {% block menu_list %}
|
||||
li
|
||||
a.navbar-item.px-2(
|
||||
href="{{ url_for('projects.home_project') }}"
|
||||
title="Home")
|
||||
| #[i.pi-home] Home
|
||||
|
||||
li
|
||||
a.navbar-item.px-2(
|
||||
href="{{ url_for('projects.index') }}"
|
||||
title="My Projects")
|
||||
| #[i.pi-star] My Projects
|
||||
|
||||
| {% if current_user.has_organizations() %}
|
||||
li
|
||||
a.navbar-item.px-2(
|
||||
href="{{ url_for('pillar.web.organizations.index') }}"
|
||||
title="My Organizations")
|
||||
| #[i.pi-users] My Organizations
|
||||
| {% endif %}
|
||||
|
||||
li
|
||||
a.navbar-item.px-2(
|
||||
|
@@ -35,8 +35,8 @@ mixin jumbotron(title, text, image, url)
|
||||
mixin nav-secondary(title)
|
||||
ul.nav.nav-secondary&attributes(attributes)
|
||||
if title
|
||||
li.font-weight-bold.px-2
|
||||
=title
|
||||
li.nav-item
|
||||
span.nav-title.nav-link.font-weight-bold.pointer-events-none= title
|
||||
|
||||
if block
|
||||
block
|
||||
|
@@ -1,8 +1,11 @@
|
||||
| {% extends 'nodes/custom/blog/index.html' %}
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
|
||||
| {% block project_context %}
|
||||
#blog_container
|
||||
#blog_index-container.expand-image-links
|
||||
| {{ blogmacros.render_archive(project, posts, posts_meta) }}
|
||||
| {% endblock project_context%}
|
||||
| {% block body %}
|
||||
.container
|
||||
.pt-4
|
||||
h2.text-uppercase.font-weight-bold
|
||||
| Blog Archive
|
||||
|
||||
| {{ blogmacros.render_archive(project, posts, posts_meta) }}
|
||||
| {% endblock body %}
|
||||
|
@@ -1,9 +0,0 @@
|
||||
| {% extends 'nodes/custom/blog/index_main_project.html' %}
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
|
||||
| {% block body %}
|
||||
.container
|
||||
h3 Blog Archive
|
||||
|
||||
| {{ blogmacros.render_archive(project, posts, posts_meta) }}
|
||||
| {% endblock body %}
|
@@ -1,55 +1,44 @@
|
||||
| {% extends 'projects/view.html' %}
|
||||
| {% extends 'layout.html' %}
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
| {% from 'projects/_macros.html' import render_secondary_navigation %}
|
||||
|
||||
| {% set title = 'blog' %}
|
||||
|
||||
| {% block page_title %}Blog{% endblock%}
|
||||
|
||||
| {% block css %}
|
||||
| {{ super() }}
|
||||
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet")
|
||||
| {% endblock %}
|
||||
| {% block navigation_tabs %}
|
||||
| {{ render_secondary_navigation(project, navigation_links, title) }}
|
||||
| {% endblock navigation_tabs %}
|
||||
|
||||
| {% block project_context %}
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta) }}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block project_tree %}
|
||||
#project_tree.jstree.jstree-default.blog
|
||||
ul.jstree-container-ul.jstree-children
|
||||
li.jstree-node(data-node-type="page")
|
||||
a.jstree-anchor(
|
||||
href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
| Browse Project
|
||||
|
||||
li.jstree-node(data-node-type="page")
|
||||
a.jstree-anchor.jstree-clicked(
|
||||
href="{{ url_for('main.project_blog', project_url=project.url) }}") Blog
|
||||
|
||||
| {% for post in posts %}
|
||||
li.jstree-node
|
||||
a.jstree-anchor.tree-item.post(
|
||||
href="{{ node.url }}")
|
||||
.tree-item-thumbnail
|
||||
| {% if post.picture %}
|
||||
img(src="{{ post.picture.thumbnail('s', api=api) }}")
|
||||
| {% else %}
|
||||
i.pi-document-text
|
||||
| {% endif %}
|
||||
span.tree-item-title {{ post.name }}
|
||||
span.tree-item-info {{ post._created | pretty_date }}
|
||||
| {% endfor %}
|
||||
| {% block body %}
|
||||
| {% if node %}
|
||||
| {{ blogmacros.render_blog_post(node, project=project) }}
|
||||
| {% else %}
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=pages) }}
|
||||
| {% endif %}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
|
||||
include ../_scripts
|
||||
script.
|
||||
/* UI Stuff */
|
||||
var project_container = document.getElementById('project-container');
|
||||
hopToTop(); // Display jump to top button
|
||||
|
||||
$(window).on("load resize",function(){
|
||||
containerResizeY($(window).height());
|
||||
/* 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');
|
||||
$('.item-content a img').on('click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
if ($(window).width() > 480) {
|
||||
project_container.style.height = (window.innerHeight - project_container.offsetTop) + "px";
|
||||
var href = $(this).parent().attr('href');
|
||||
var src = $(this).attr('src');
|
||||
|
||||
if (href.match("jpg$") || href.match("png$") || href.match("gif$")) {
|
||||
$(page_overlay)
|
||||
.addClass('active')
|
||||
.html('<img src="' + src + '"/>');
|
||||
} else {
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -1,40 +0,0 @@
|
||||
| {% extends 'layout.html' %}
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
| {% set title = 'blog' %}
|
||||
|
||||
| {% block page_title %}Blog{% endblock%}
|
||||
|
||||
| {% block css %}
|
||||
| {{ super() }}
|
||||
link(href="{{ url_for('static_cloud', filename='assets/css/project-landing.css') }}", rel="stylesheet")
|
||||
| {% endblock css %}
|
||||
|
||||
| {% block body %}
|
||||
| {{ blogmacros.render_blog_index(project, posts, can_create_blog_posts, api, more_posts_available, posts_meta, pages=pages) }}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
|
||||
include ../_scripts
|
||||
script.
|
||||
hopToTop(); // Display jump to top button
|
||||
|
||||
/* 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');
|
||||
$('.item-content a img').on('click', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
var href = $(this).parent().attr('href');
|
||||
var src = $(this).attr('src');
|
||||
|
||||
if (href.match("jpg$") || href.match("png$") || href.match("gif$")) {
|
||||
$(page_overlay)
|
||||
.addClass('active')
|
||||
.html('<img src="' + src + '"/>');
|
||||
} else {
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
|
||||
| {% endblock %}
|
@@ -23,60 +23,8 @@ include ../../../mixins/components
|
||||
i.pi-list
|
||||
|
||||
+card-deck(class="px-2")
|
||||
| {% for child in children %}
|
||||
| {# Browse type: List #}
|
||||
a(
|
||||
href="{{ url_for_node(node=child) }}",
|
||||
data-node_id="{{ child._id }}",
|
||||
class="js-item-open list-node-children-item browse-list")
|
||||
.list-node-children-item-thumbnail
|
||||
|
||||
| {% if child.picture %}
|
||||
img(
|
||||
src="{{ child.picture.thumbnail('t', api=api)}} ")
|
||||
| {% else %}
|
||||
.cloud-logo
|
||||
i.pi-blender-cloud
|
||||
| {% endif %}
|
||||
|
||||
| {% if child.permissions.world %}
|
||||
.list-node-children-item-ribbon
|
||||
span free
|
||||
| {% endif %}
|
||||
|
||||
.list-node-children-item-thumbnail-icon
|
||||
| {% if child.properties.content_type and child.properties.content_type == 'video' %}
|
||||
i.pi-play
|
||||
| {% elif child.properties.content_type and child.properties.content_type == 'image' %}
|
||||
i.pi-image
|
||||
| {% elif child.properties.content_type and child.properties.content_type == 'file' %}
|
||||
i.pi-file-archive
|
||||
| {% else %}
|
||||
i.pi-folder
|
||||
| {% endif %}
|
||||
|
||||
.list-node-children-item-name {{ child.name }}
|
||||
|
||||
.list-node-children-item-meta
|
||||
| {% if child.properties.status != 'published' %}
|
||||
span.status {{ child.properties.status }}
|
||||
| {% endif %}
|
||||
|
||||
span.type
|
||||
| {% if child.properties.content_type %}
|
||||
| {{ child.properties.content_type | undertitle }} ·
|
||||
| {% elif child.node_type == 'group' %}
|
||||
| Folder ·
|
||||
| {% else %}
|
||||
| {{ child.node_type | undertitle }} ·
|
||||
| {% endif %}
|
||||
|
||||
span(title="Created on {{ child._created }}") {{ child._created | pretty_date }}
|
||||
|
||||
| {# Browse type: Icon #}
|
||||
|
||||
| {% for child in children %}
|
||||
| {{ asset_list_item(child, current_user) }}
|
||||
|
||||
| {% endfor %}
|
||||
| {% else %}
|
||||
.list-node-children-container
|
||||
@@ -115,14 +63,12 @@ include ../../../mixins/components
|
||||
|
||||
// Browse type: icon or list
|
||||
function projectBrowseTypeIcon() {
|
||||
$(".list-node-children-item.browse-list").hide();
|
||||
$(".list-node-children-item.browse-icon").show();
|
||||
$(".card-deck").removeClass('card-deck-vertical');
|
||||
$(".js-btn-browsetoggle").html('<i class="pi-list"></i> List View');
|
||||
};
|
||||
|
||||
function projectBrowseTypeList() {
|
||||
$(".list-node-children-item.browse-list").show();
|
||||
$(".list-node-children-item.browse-icon").hide();
|
||||
$(".card-deck").addClass('card-deck-vertical');
|
||||
$(".js-btn-browsetoggle").html('<i class="pi-layout"></i> Grid View');
|
||||
};
|
||||
|
||||
|
@@ -5,9 +5,7 @@
|
||||
header
|
||||
img.header(src="{{ node.picture.thumbnail('h', api=api) }}")
|
||||
| {% endif %}
|
||||
| {% block navbar_secondary %}
|
||||
| {{ super() }}
|
||||
| {% endblock navbar_secondary %}
|
||||
|
||||
#node-container
|
||||
#node-overlay
|
||||
|
||||
|
@@ -1,73 +0,0 @@
|
||||
| {% extends 'projects/view.html' %}
|
||||
| {% set title = 'blog' %}
|
||||
|
||||
| {% block og %}
|
||||
meta(property="og:title", content="{{ node.name }}")
|
||||
meta(property="og:url", content="{{ url_for('main.project_blog', project_url=project.url, url=node.properties.url, _external=True)}}")
|
||||
meta(property="og:type", content="website")
|
||||
| {% if node.picture %}
|
||||
meta(property="og:image", content="{{ node.picture.thumbnail('l', api=api) }}")
|
||||
| {% endif %}
|
||||
meta(property="og:description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
|
||||
|
||||
meta(name="twitter:title", content="{{ node.name }}")
|
||||
meta(name="twitter:description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
|
||||
| {% if node.picture %}
|
||||
meta(property="og:image", content="{{ node.picture.thumbnail('l', api=api) }}")
|
||||
| {% endif %}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block page_title %}{{node.name}} - Blog{% endblock%}
|
||||
|
||||
| {% block css %}
|
||||
| {{ super() }}
|
||||
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css') }}", rel="stylesheet")
|
||||
| {% endblock %}
|
||||
|
||||
| {% block project_context %}
|
||||
| {% include 'nodes/custom/post/view_embed.html' %}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block project_tree %}
|
||||
#project_tree.jstree.jstree-default.blog
|
||||
ul.jstree-container-ul.jstree-children
|
||||
li.jstree-node(data-node-type="page")
|
||||
a.jstree-anchor(
|
||||
href="{{ url_for('projects.view', project_url=project.url) }}")
|
||||
| Browse Project
|
||||
|
||||
li.jstree-node(data-node-type="page")
|
||||
a.jstree-anchor(
|
||||
href="{{ url_for('main.project_blog', project_url=project.url) }}") Blog
|
||||
|
||||
| {% for post in posts %}
|
||||
li.jstree-node
|
||||
a.jstree-anchor.tree-item.post(
|
||||
href="{{ url_for_node(node=post) }}",
|
||||
class="{% if post._id == node._id %}jstree-clicked{% endif %}")
|
||||
.tree-item-thumbnail
|
||||
| {% if post.picture %}
|
||||
img(src="{{ post.picture.thumbnail('s', api=api) }}")
|
||||
| {% else %}
|
||||
i.pi-document-text
|
||||
| {% endif %}
|
||||
span.tree-item-title {{ post.name }}
|
||||
span.tree-item-info {{ post._created | pretty_date }}
|
||||
| {% endfor %}
|
||||
| {% endblock %}
|
||||
|
||||
| {% block footer_scripts %}
|
||||
script.
|
||||
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: false, nodeId: '{{node._id}}'});
|
||||
|
||||
/* UI Stuff */
|
||||
var project_container = document.getElementById('project-container');
|
||||
$(window).on("load resize",function(){
|
||||
containerResizeY($(window).height());
|
||||
|
||||
if ($(window).width() > 480) {
|
||||
project_container.style.height = (window.innerHeight - project_container.offsetTop) + "px";
|
||||
}
|
||||
});
|
||||
|
||||
| {% endblock footer_scripts %}
|
@@ -1,9 +0,0 @@
|
||||
| {% import 'nodes/custom/blog/_macros.html' as blogmacros %}
|
||||
|
||||
| {{ blogmacros.render_blog_post(node, project=project) }}
|
||||
|
||||
#comments-embed.comments-compact
|
||||
.comments-list-loading
|
||||
i.pi-spin
|
||||
|
||||
include ../_scripts
|
@@ -18,11 +18,6 @@ meta(property="og:image", content="{{ node.picture.thumbnail('l', api=api) }}")
|
||||
|
||||
| {% block page_title %}{{node.name}} - Blog{% endblock%}
|
||||
|
||||
| {% block css %}
|
||||
| {{ super() }}
|
||||
link(href="{{ url_for('static_cloud', filename='assets/css/project-landing.css') }}", rel="stylesheet")
|
||||
| {% endblock css %}
|
||||
|
||||
| {% set title = 'blog' %}
|
||||
|
||||
| {% block body %}
|
||||
|
@@ -68,7 +68,7 @@ script.
|
||||
|
||||
#stats.search-list-stats
|
||||
|
||||
+card-deck()(id='hits', class="h-100 m-0 pt-3 pr-2 card-deck-horizontal")
|
||||
+card-deck()(id='hits', class="h-100 m-0 pt-3 pr-2 card-deck-vertical")
|
||||
|
||||
#search-details.border-left.search-details
|
||||
#search-error
|
||||
|
@@ -1,41 +1,48 @@
|
||||
| {% macro render_secondary_navigation(project, pages=None) %}
|
||||
nav.navbar-secondary
|
||||
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 %}
|
||||
include ../mixins/components
|
||||
|
||||
| {% macro render_secondary_navigation(project, navigation_links, title) %}
|
||||
|
||||
| {% if project.category == 'course' %}
|
||||
| {% set category_url = url_for('cloud.courses') %}
|
||||
| {% elif project.category == 'workshop' %}
|
||||
| {% set category_url = url_for('cloud.workshops') %}
|
||||
| {% elif project.category == 'film' %}
|
||||
| {% set category_url = url_for('cloud.open_projects') %}
|
||||
| {% else %}
|
||||
| {% set category_url = url_for('main.homepage') %}
|
||||
| {% endif %}
|
||||
|
||||
+nav-secondary()
|
||||
| {% if project.url != 'blender-cloud' %}
|
||||
li.text-capitalize
|
||||
a.nav-link.text-muted.px-0(href="{{ category_url }}")
|
||||
span {{ project.category }}
|
||||
li.px-1
|
||||
i.pi-angle-right
|
||||
|
||||
+nav-secondary-link(
|
||||
class="px-1 font-weight-bold",
|
||||
href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
|
||||
span {{ project.name }}
|
||||
| {% endif %}
|
||||
|
||||
| {% if project.nodes_featured and (title !='project') %}
|
||||
| {# 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 %}
|
||||
+nav-secondary-link(
|
||||
href="{{ url_for('projects.view_node', project_url=project.url, node_id=featured_node_id) }}",
|
||||
title="Explore {{ project.name }}",
|
||||
class="{% if title == 'project' %}active{% endif %}")
|
||||
span Explore
|
||||
| {% endif %}
|
||||
|
||||
| {% for link in navigation_links %}
|
||||
+nav-secondary-link(href="{{ link['url'] }}")
|
||||
| {{ link['label'] }}
|
||||
| {% endfor %}
|
||||
|
||||
| {% endmacro %}
|
||||
|
@@ -1,36 +1,34 @@
|
||||
| {% extends 'layout.html' %}
|
||||
include ../../mixins/components
|
||||
|
||||
//- Don't extend this base file directly. Instead, extend page.html so that Pillar extensions
|
||||
//- can provide overrides.
|
||||
| {% block body %}
|
||||
.container
|
||||
#settings.d-flex.py-4.flex-xs-column
|
||||
#settings-sidebar
|
||||
.container.py-4
|
||||
.row
|
||||
.col-md-3
|
||||
| {% block settings_sidebar %}
|
||||
.settings-header
|
||||
.settings-title Settings
|
||||
.settings-content
|
||||
ul
|
||||
| {% block settings_sidebar_menu %}
|
||||
a(class="{% if title == 'profile' %}active{% endif %}",
|
||||
href="{{ url_for('settings.profile') }}")
|
||||
li
|
||||
i.pi-vcard
|
||||
| Profile
|
||||
| {% endblock settings_sidebar_menu %}
|
||||
| {% block settings_sidebar_menu_bottom %}
|
||||
a(class="{% if title == 'roles' %}active{% endif %}",
|
||||
href="{{ url_for('settings.roles') }}")
|
||||
li
|
||||
i.pi-cog
|
||||
| Roles & Capabilities
|
||||
| {% endblock settings_sidebar_menu_bottom %}
|
||||
+nav-secondary('Settings')(class="nav-secondary-vertical bg-light")
|
||||
| {% block settings_sidebar_menu %}
|
||||
+nav-secondary-link(
|
||||
class="{% if title == 'profile' %}active{% endif %} border-top",
|
||||
href="{{ url_for('settings.profile') }}")
|
||||
i.pr-3.pi-vcard
|
||||
span Profile
|
||||
| {% endblock settings_sidebar_menu %}
|
||||
|
||||
| {% block settings_sidebar_menu_bottom %}
|
||||
+nav-secondary-link(
|
||||
class="{% if title == 'roles' %}active{% endif %}",
|
||||
href="{{ url_for('settings.roles') }}")
|
||||
i.pr-3.pi-cog
|
||||
span Roles & Capabilities
|
||||
| {% endblock settings_sidebar_menu_bottom %}
|
||||
| {% endblock %}
|
||||
|
||||
#settings-container
|
||||
.settings-header
|
||||
.settings-title {% block settings_page_title %}{{ _("Title not set") }}{% endblock %}
|
||||
.col-md-9
|
||||
h3.py-1 {% block settings_page_title %}{{ _("Title not set") }}{% endblock %}
|
||||
|
||||
.settings-content
|
||||
| {% block settings_page_content %}No content set, update your template.{% endblock %}
|
||||
| {% block settings_page_content %}No content set, update your template.{% endblock %}
|
||||
|
||||
| {% endblock %}
|
||||
|
@@ -21,7 +21,7 @@ style.
|
||||
| {% block settings_page_content %}
|
||||
.settings-form
|
||||
form#settings-form(method='POST', action="{{url_for('settings.profile')}}")
|
||||
.left
|
||||
.pb-3
|
||||
.form-group
|
||||
| {{ form.username.label }}
|
||||
| {{ form.username(size=20, class='form-control') }}
|
||||
@@ -45,14 +45,13 @@ style.
|
||||
| {{ current_user.badges_html|safe }}
|
||||
p.hint-text Note that updates to these badges may take a few minutes to be visible here.
|
||||
| {% endif %}
|
||||
.right
|
||||
.settings-avatar
|
||||
a(href="https://gravatar.com/")
|
||||
img(src="{{ current_user.gravatar }}")
|
||||
span {{ _("Change Gravatar") }}
|
||||
.py-3
|
||||
a(href="https://gravatar.com/")
|
||||
img.rounded-circle(src="{{ current_user.gravatar }}")
|
||||
span.p-3 {{ _("Change Gravatar") }}
|
||||
|
||||
.buttons
|
||||
button.btn.btn-outline-success.button-submit(type='submit')
|
||||
i.pi-check
|
||||
.py-3
|
||||
button.btn.btn-outline-success.px-5.button-submit(type='submit')
|
||||
i.pi-check.pr-2
|
||||
| {{ _("Save Changes") }}
|
||||
| {% endblock %}
|
||||
|
@@ -479,61 +479,65 @@ class TextureSortFilesTest(AbstractPillarTest):
|
||||
|
||||
|
||||
class TaggedNodesTest(AbstractPillarTest):
|
||||
def test_tagged_nodes_api(self):
|
||||
def setUp(self, **kwargs):
|
||||
super().setUp(**kwargs)
|
||||
|
||||
self.pid, _ = self.ensure_project_exists()
|
||||
self.file_id, _ = self.ensure_file_exists()
|
||||
self.uid = self.create_user()
|
||||
|
||||
from pillar.api.utils import utcnow
|
||||
self.fake_now = utcnow()
|
||||
|
||||
def test_tagged_nodes_api(self):
|
||||
from datetime import timedelta
|
||||
|
||||
pid, _ = self.ensure_project_exists()
|
||||
file_id, _ = self.ensure_file_exists()
|
||||
uid = self.create_user()
|
||||
|
||||
now = utcnow()
|
||||
base_node = {
|
||||
'name': 'Just a node name',
|
||||
'project': pid,
|
||||
'project': self.pid,
|
||||
'description': '',
|
||||
'node_type': 'asset',
|
||||
'user': uid,
|
||||
'user': self.uid,
|
||||
}
|
||||
base_props = {'status': 'published',
|
||||
'file': file_id,
|
||||
'file': self.file_id,
|
||||
'content_type': 'video',
|
||||
'order': 0}
|
||||
# No tags, should never be returned.
|
||||
self.create_node({
|
||||
'_created': now,
|
||||
'_created': self.fake_now,
|
||||
'properties': base_props,
|
||||
**base_node})
|
||||
# Empty tag list, should never be returned.
|
||||
self.create_node({
|
||||
'_created': now + timedelta(seconds=1),
|
||||
'_created': self.fake_now + timedelta(seconds=1),
|
||||
'properties': {'tags': [], **base_props},
|
||||
**base_node})
|
||||
# Empty string as tag, should never be returned.
|
||||
self.create_node({
|
||||
'_created': now + timedelta(seconds=1),
|
||||
'_created': self.fake_now + timedelta(seconds=1),
|
||||
'properties': {'tags': [''], **base_props},
|
||||
**base_node})
|
||||
nid_single_tag = self.create_node({
|
||||
'_created': now + timedelta(seconds=2),
|
||||
'_created': self.fake_now + timedelta(seconds=2),
|
||||
# 'एनिमेशन' is 'animation' in Hindi.
|
||||
'properties': {'tags': ['एनिमेशन'], **base_props},
|
||||
**base_node,
|
||||
})
|
||||
nid_double_tag = self.create_node({
|
||||
'_created': now + timedelta(hours=3),
|
||||
'_created': self.fake_now + timedelta(hours=3),
|
||||
'properties': {'tags': ['एनिमेशन', 'rigging'], **base_props},
|
||||
**base_node,
|
||||
})
|
||||
nid_other_tag = self.create_node({
|
||||
'_deleted': False,
|
||||
'_created': now + timedelta(days=4),
|
||||
'_created': self.fake_now + timedelta(days=4),
|
||||
'properties': {'tags': ['producción'], **base_props},
|
||||
**base_node,
|
||||
})
|
||||
# Matching tag but deleted node, should never be returned.
|
||||
self.create_node({
|
||||
'_created': now + timedelta(seconds=1),
|
||||
'_created': self.fake_now + timedelta(seconds=1),
|
||||
'_deleted': True,
|
||||
'properties': {'tags': ['एनिमेशन'], **base_props},
|
||||
**base_node})
|
||||
@@ -556,3 +560,76 @@ class TaggedNodesTest(AbstractPillarTest):
|
||||
with self.app.app_context():
|
||||
invalid_url = flask.url_for('nodes_api.tagged', tag='')
|
||||
self.get(invalid_url, expected_status=404)
|
||||
|
||||
def test_tagged_nodes_asset_video_with_progress_api(self):
|
||||
from datetime import timedelta
|
||||
from pillar.auth import current_user
|
||||
|
||||
base_node = {
|
||||
'name': 'Spring hair rig setup',
|
||||
'project': self.pid,
|
||||
'description': '',
|
||||
'node_type': 'asset',
|
||||
'user': self.uid,
|
||||
}
|
||||
base_props = {'status': 'published',
|
||||
'file': self.file_id,
|
||||
'content_type': 'video',
|
||||
'order': 0}
|
||||
|
||||
# Create one node of type asset video
|
||||
nid_single_tag = self.create_node({
|
||||
'_created': self.fake_now + timedelta(seconds=2),
|
||||
# 'एनिमेशन' is 'animation' in Hindi.
|
||||
'properties': {'tags': ['एनिमेशन'], **base_props},
|
||||
**base_node,
|
||||
})
|
||||
|
||||
# Create another node
|
||||
self.create_node({
|
||||
'_created': self.fake_now + timedelta(seconds=2),
|
||||
# 'एनिमेशन' is 'animation' in Hindi.
|
||||
'properties': {'tags': ['एनिमेशन'], **base_props},
|
||||
**base_node,
|
||||
})
|
||||
|
||||
# Add video watch progress for the self.uid user
|
||||
with self.app.app_context():
|
||||
users_coll = self.app.db('users')
|
||||
# Define video progress
|
||||
progress_in_sec = 333
|
||||
video_progress = {
|
||||
'progress_in_sec': progress_in_sec,
|
||||
'progress_in_percent': 70,
|
||||
'done': False,
|
||||
'last_watched': self.fake_now + timedelta(seconds=2),
|
||||
}
|
||||
users_coll.update_one(
|
||||
{'_id': self.uid},
|
||||
{'$set': {f'nodes.view_progress.{nid_single_tag}': video_progress}})
|
||||
|
||||
# Utility to fetch tagged nodes and return them as JSON list
|
||||
def do_query():
|
||||
animation_tags_url = flask.url_for('nodes_api.tagged', tag='एनिमेशन')
|
||||
return self.get(animation_tags_url).json
|
||||
|
||||
# Ensure that anonymous users get videos with no view_progress info
|
||||
with self.app.app_context():
|
||||
resp = do_query()
|
||||
for node in resp:
|
||||
self.assertNotIn('view_progress', node)
|
||||
|
||||
# Ensure that an authenticated user gets view_progress info if the video was watched
|
||||
with self.login_as(self.uid):
|
||||
resp = do_query()
|
||||
for node in resp:
|
||||
if node['_id'] in current_user.nodes['view_progress']:
|
||||
self.assertIn('view_progress', node)
|
||||
self.assertEqual(progress_in_sec, node['view_progress']['progress_in_sec'])
|
||||
|
||||
# Ensure that another user with no view progress does not get any view progress info
|
||||
other_user = self.create_user(user_id=ObjectId())
|
||||
with self.login_as(other_user):
|
||||
resp = do_query()
|
||||
for node in resp:
|
||||
self.assertNotIn('view_progress', node)
|
||||
|
Reference in New Issue
Block a user