Noah
de9ba94dd9
* Add ability to unlock your private photos for others. * Link to unlock is on another user's Photos page. * You can also access your "Manage Private Photos" page in the User Menu or Dashboard and add a user by username. * See who you have granted access, and see users who have granted you access to their private photos. * Private photos of users who unlocked them for you may appear on the Site Gallery if the photo allows. * Add new filters to the Site Gallery: you can choose to filter by Explicit, limit to a specific Visibility, and order by Newest or Oldest. Non-explicit users do not get a filter to see explicit content - they must opt-in in their settings. * Bugfix: Site Gallery was not correctly filtering Explicit photos away from users who did not opt-in for explicit!
537 lines
25 KiB
HTML
537 lines
25 KiB
HTML
<!--
|
|
Photo Gallery Template, shared by Site Photos + User Photos.
|
|
|
|
When Site Gallery: .IsSiteGallery is defined and true.
|
|
When User Gallery: .User is defined, .IsOwnPhotos may be.
|
|
-->
|
|
{{define "title"}}
|
|
{{if .IsSiteGallery}}
|
|
Member Gallery
|
|
{{else}}
|
|
Photos of {{.User.Username}}
|
|
{{if eq .User.Visibility "private"}}<sup class="fa fa-mask ml-2 is-size-6" title="Private Profile"></sup>{{end}}
|
|
{{end}}
|
|
{{end}}
|
|
|
|
<!-- Reusable card body -->
|
|
{{define "card-body"}}
|
|
<div>
|
|
<small class="has-text-grey">Uploaded {{.CreatedAt.Format "Jan _2 2006 15:04:05"}}</small>
|
|
</div>
|
|
<div class="mt-2">
|
|
{{if .Explicit}}
|
|
<span class="tag is-danger is-light">
|
|
<span class="icon"><i class="fa fa-fire"></i></span>
|
|
<span>Explicit</span>
|
|
</span>
|
|
{{end}}
|
|
|
|
{{if eq .Visibility "public"}}
|
|
<span class="tag is-info is-light">
|
|
<span class="icon"><i class="fa fa-eye"></i></span>
|
|
<span>
|
|
Public
|
|
</span>
|
|
</span>
|
|
{{else if eq .Visibility "friends"}}
|
|
<span class="tag is-warning is-light">
|
|
<span class="icon"><i class="fa fa-user-group"></i></span>
|
|
<span>
|
|
Friends
|
|
</span>
|
|
</span>
|
|
{{else}}
|
|
<span class="tag is-private is-light">
|
|
<span class="icon"><i class="fa fa-lock"></i></span>
|
|
<span>
|
|
Private
|
|
</span>
|
|
</span>
|
|
{{end}}
|
|
|
|
{{if .Gallery}}
|
|
<span class="tag is-success is-light">
|
|
<span class="icon"><i class="fa fa-image"></i></span>
|
|
<span>Gallery</span>
|
|
</span>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Reusable card footer -->
|
|
{{define "card-footer"}}
|
|
<a class="card-footer-item" href="/photo/edit?id={{.ID}}">
|
|
<span class="icon"><i class="fa fa-edit"></i></span>
|
|
<span>Edit</span>
|
|
</a>
|
|
<a class="card-footer-item has-text-danger" href="/photo/delete?id={{.ID}}">
|
|
<span class="icon"><i class="fa fa-trash"></i></span>
|
|
<span>Delete</span>
|
|
</a>
|
|
{{end}}
|
|
|
|
<!-- Reusable pager -->
|
|
{{define "pager"}}
|
|
<nav class="pagination" role="navigation" aria-label="pagination">
|
|
<a class="pagination-previous{{if not .Pager.HasPrevious}} is-disabled{{end}}" title="Previous"
|
|
href="{{.Request.URL.Path}}?view={{.ViewStyle}}&page={{.Pager.Previous}}">Previous</a>
|
|
<a class="pagination-next{{if not .Pager.HasNext}} is-disabled{{end}}" title="Next"
|
|
href="{{.Request.URL.Path}}?view={{.ViewStyle}}&page={{.Pager.Next}}">Next page</a>
|
|
<ul class="pagination-list">
|
|
{{$Root := .}}
|
|
{{range .Pager.Iter}}
|
|
<li>
|
|
<a class="pagination-link{{if .IsCurrent}} is-current{{end}}"
|
|
aria-label="Page {{.Page}}"
|
|
href="{{$Root.Request.URL.Path}}?view={{$Root.ViewStyle}}&page={{.Page}}">
|
|
{{.Page}}
|
|
</a>
|
|
</li>
|
|
{{end}}
|
|
</ul>
|
|
</nav>
|
|
{{end}}
|
|
|
|
<!-- Main content template -->
|
|
{{define "content"}}
|
|
<div class="container">
|
|
<section class="hero is-info is-bold">
|
|
<div class="hero-body">
|
|
{{if .IsSiteGallery}}
|
|
<h1 class="title">
|
|
{{template "title" .}}
|
|
</h1>
|
|
{{else}}
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<h1 class="title">
|
|
{{template "title" .}}
|
|
</h1>
|
|
</div>
|
|
{{if .IsOwnPhotos}}
|
|
<div class="level-right">
|
|
<div>
|
|
<a href="/photo/upload" class="button">
|
|
<span class="icon"><i class="fa fa-upload"></i></span>
|
|
<span>Upload Photos</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ugly hack.. needed by the card-footers later below. -->
|
|
{{$Root := .}}
|
|
|
|
<div class="block p-4">
|
|
<!-- Profile Tab for user view -->
|
|
{{if not .IsSiteGallery}}
|
|
<div class="tabs is-boxed">
|
|
<ul>
|
|
<li>
|
|
<a href="/u/{{.User.Username}}">
|
|
<span class="icon is-small">
|
|
<i class="fa fa-user"></i>
|
|
</span>
|
|
<span>Profile</span>
|
|
</a>
|
|
</li>
|
|
<li class="is-active">
|
|
<a>
|
|
<span class="icon is-small">
|
|
<i class="fa fa-image"></i>
|
|
</span>
|
|
<span>
|
|
Photos
|
|
{{if .PhotoCount}}<span class="tag is-link is-light ml-1">{{.PhotoCount}}</span>{{end}}
|
|
</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Photo Detail Modal -->
|
|
<div class="modal" id="detail-modal">
|
|
<div class="modal-background"></div>
|
|
<div class="modal-content photo-modal">
|
|
<div class="image is-fullwidth">
|
|
<img id="detailImg">
|
|
</div>
|
|
</div>
|
|
<button class="modal-close is-large" aria-label="close"></button>
|
|
</div>
|
|
|
|
<div class="block">
|
|
<div class="level{{if .IsOwnPhotos}}mb-0{{end}}">
|
|
<div class="level-left">
|
|
<div class="level-item">
|
|
<span>
|
|
Found <strong>{{.Pager.Total}}</strong> photo{{Pluralize64 .Pager.Total}} (page {{.Pager.Page}} of {{.Pager.Pages}}).
|
|
{{if .ExplicitCount}}
|
|
{{.ExplicitCount}} explicit photo{{Pluralize64 .ExplicitCount}} hidden per your settings.
|
|
{{end}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="level-right">
|
|
<div class="level-item">
|
|
<div class="tabs is-toggle is-small is-hidden-mobile">
|
|
<ul>
|
|
<li{{if eq .ViewStyle "cards"}} class="is-active"{{end}}>
|
|
<a href="{{.Request.URL.Path}}?view=cards">Cards</a>
|
|
</li>
|
|
<li{{if eq .ViewStyle "full"}} class="is-active"{{end}}>
|
|
<a href="{{.Request.URL.Path}}?view=full">Full</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="block">
|
|
<form action="/photo/gallery" method="GET">
|
|
|
|
<div class="card nonshy-collapsible-mobile">
|
|
<header class="card-header has-background-link-light">
|
|
<p class="card-header-title">
|
|
Search Filters <span class="tag is-success ml-2">NEW</span>
|
|
</p>
|
|
<button class="card-header-icon" type="button">
|
|
<span class="icon">
|
|
<i class="fa fa-angle-up"></i>
|
|
</span>
|
|
</button>
|
|
</header>
|
|
<div class="card-content">
|
|
<div class="columns is-multiline mb-0">
|
|
|
|
{{if .CurrentUser.Explicit}}
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label" for="explicit">Explicit:</label>
|
|
<div class="select is-fullwidth">
|
|
<select id="explicit" name="explicit">
|
|
<option value="">Show all</option>
|
|
<option value="true"{{if eq .FilterExplicit "true"}} selected{{end}}>Only explicit</option>
|
|
<option value="false"{{if eq .FilterExplicit "false"}} selected{{end}}>Hide explicit</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label" for="visibility">Visibility:</label>
|
|
<div class="select is-fullwidth">
|
|
<select id="visibility" name="visibility">
|
|
<option value="">All photos</option>
|
|
<option value="public"{{if eq .FilterVisibility "public"}} selected{{end}}>Public only</option>
|
|
<option value="friends"{{if eq .FilterVisibility "friends"}} selected{{end}}>Friends only</option>
|
|
<option value="private"{{if eq .FilterVisibility "private"}} selected{{end}}>Private only</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label" for="sort">Sort by:</label>
|
|
<div class="select is-fullwidth">
|
|
<select id="sort" name="sort">
|
|
<option value="created_at desc"{{if eq .Sort "created_at desc"}} selected{{end}}>Most recent</option>
|
|
<option value="created_at asc"{{if eq .Sort "created_at asc"}} selected{{end}}>Oldest first</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="has-text-centered">
|
|
<a href="/photo/gallery" class="button">Reset</a>
|
|
<button type="submit" class="button is-success">
|
|
Apply Filters
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
|
|
{{if .IsOwnPhotos}}
|
|
<div class="block">
|
|
<a href="/photo/private" class="has-text-private">
|
|
<span class="icon"><i class="fa fa-lock"></i></span>
|
|
<span>Manage who can see <strong>my</strong> private photos</span>
|
|
<span class="tag is-success">NEW</span>
|
|
</a>
|
|
</div>
|
|
{{else if and (not .IsSiteGallery) (not .IsMyPrivateUnlockedFor)}}
|
|
<div class="block">
|
|
<a href="/photo/private/share?to={{.User.Username}}" class="has-text-private">
|
|
<span class="icon"><i class="fa fa-unlock"></i></span>
|
|
<span>Grant <strong>{{.User.Username}}</strong> access to see <strong>my</strong> private photos</span>
|
|
<span class="tag is-success">NEW</span>
|
|
</a>
|
|
</div>
|
|
{{else if and (not .IsSiteGallery) .IsMyPrivateUnlockedFor}}
|
|
<div class="block">
|
|
<span class="icon"><i class="fa fa-unlock has-text-private"></i></span>
|
|
<span>You had granted <strong>{{.User.Username}}</strong> access to see <strong>your</strong> private photos.</span>
|
|
<a href="/photo/private">Manage that here.</a>
|
|
<span class="tag is-success">NEW</span>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{template "pager" .}}
|
|
|
|
<!-- "Full" view style? (blog style) -->
|
|
{{if eq .ViewStyle "full"}}
|
|
{{range .Photos}}
|
|
<div class="card block">
|
|
<header class="card-header {{if .Explicit}}has-background-danger{{else}}has-background-link{{end}}">
|
|
<!-- Site Gallery header -->
|
|
{{if $Root.IsSiteGallery}}
|
|
<div class="card-header-title has-text-light">
|
|
{{if $Root.UserMap.Has .UserID}}
|
|
{{$Owner := $Root.UserMap.Get .UserID}}
|
|
<div class="columns is-mobile is-gapless nonshy-fullwidth">
|
|
<div class="column is-narrow">
|
|
<figure class="image is-24x24 mr-2">
|
|
{{if gt $Owner.ProfilePhoto.ID 0}}
|
|
<img src="{{PhotoURL $Owner.ProfilePhoto.CroppedFilename}}" class="is-rounded">
|
|
{{else}}
|
|
<img src="/static/img/shy.png" class="is-rounded has-background-warning">
|
|
{{end}}
|
|
</figure>
|
|
</div>
|
|
<div class="column">
|
|
<a href="/u/{{$Owner.Username}}" class="has-text-light">
|
|
{{$Owner.Username}}
|
|
<i class="fa fa-external-link ml-2"></i>
|
|
</a>
|
|
</div>
|
|
<div class="column is-narrow">
|
|
<span class="icon">
|
|
{{if eq .Visibility "friends"}}
|
|
<i class="fa fa-user-group has-text-warning" title="Friends"></i>
|
|
{{else if eq .Visibility "private"}}
|
|
<i class="fa fa-lock has-text-private-light" title="Private"></i>
|
|
{{else}}
|
|
<i class="fa fa-eye has-text-link-light" title="Public"></i>
|
|
{{end}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{{else}}
|
|
<span class="fa fa-user mr-2"></span>
|
|
<span>[deleted]</span>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<!-- User Gallery Full Header -->
|
|
<p class="card-header-title has-text-light">
|
|
<span class="icon">
|
|
<i class="fa fa-image"></i>
|
|
</span>
|
|
{{or .Caption "Photo"}}
|
|
</p>
|
|
{{end}}
|
|
</header>
|
|
|
|
<div class="card-image">
|
|
<figure class="image">
|
|
<img src="{{PhotoURL .Filename}}">
|
|
</figure>
|
|
</div>
|
|
|
|
<div class="card-content">
|
|
{{if .Caption}}
|
|
{{.Caption}}
|
|
{{else}}<em>No caption</em>{{end}}
|
|
|
|
{{template "card-body" .}}
|
|
|
|
<!-- Like & Comments buttons -->
|
|
<div class="mt-4 columns is-centered is-mobile is-gapless">
|
|
<div class="column is-narrow mr-1">
|
|
{{$Like := $Root.LikeMap.Get .ID}}
|
|
<button type="button" class="button is-small nonshy-like-button"
|
|
data-table-name="photos" data-table-id="{{.ID}}"
|
|
title="Like">
|
|
<span class="icon{{if $Like.UserLikes}} has-text-danger{{end}}"><i class="fa fa-heart"></i></span>
|
|
<span class="nonshy-likes">
|
|
Like
|
|
{{if gt $Like.Count 0}}
|
|
({{$Like.Count}})
|
|
{{end}}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div class="column is-narrow">
|
|
{{$Comments := $Root.CommentMap.Get .ID}}
|
|
<a href="/photo/view?id={{.ID}}#comments" class="button is-small">
|
|
<span class="icon"><i class="fa fa-comment"></i></span>
|
|
<span>{{$Comments}} Comment{{Pluralize64 $Comments}}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="card-footer">
|
|
{{if or $Root.IsOwnPhotos $Root.CurrentUser.IsAdmin}}
|
|
{{template "card-footer" .}}
|
|
{{end}}
|
|
|
|
{{if not $Root.IsOwnPhotos}}
|
|
<a class="card-footer-item has-text-danger" href="/contact?intent=report&subject=report.photo&id={{.ID}}">
|
|
<span class="icon"><i class="fa fa-flag"></i></span>
|
|
<span>Report</span>
|
|
</a>
|
|
{{end}}
|
|
</footer>
|
|
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- "Cards" style (default) -->
|
|
{{else}}
|
|
<div class="columns is-multiline">
|
|
{{range .Photos}}
|
|
<div class="column is-one-quarter-desktop is-half-tablet">
|
|
<div class="card">
|
|
<!-- Header only on Site Gallery version -->
|
|
{{if $Root.IsSiteGallery}}
|
|
<header class="card-header {{if .Explicit}}has-background-danger{{else}}has-background-link{{end}}">
|
|
<div class="card-header-title has-text-light">
|
|
{{if $Root.UserMap.Has .UserID}}
|
|
{{$Owner := $Root.UserMap.Get .UserID}}
|
|
<div class="columns is-mobile is-gapless nonshy-fullwidth">
|
|
<div class="column is-narrow">
|
|
<figure class="image is-24x24 mr-2">
|
|
{{if gt $Owner.ProfilePhoto.ID 0}}
|
|
<img src="{{PhotoURL $Owner.ProfilePhoto.CroppedFilename}}" class="is-rounded">
|
|
{{else}}
|
|
<img src="/static/img/shy.png" class="is-rounded has-background-warning">
|
|
{{end}}
|
|
</figure>
|
|
</div>
|
|
<div class="column">
|
|
<a href="/u/{{$Owner.Username}}" class="has-text-light">
|
|
{{$Owner.Username}}
|
|
<i class="fa fa-external-link ml-2"></i>
|
|
</a>
|
|
</div>
|
|
<div class="column is-narrow">
|
|
<span class="icon">
|
|
{{if eq .Visibility "friends"}}
|
|
<i class="fa fa-user-group has-text-warning" title="Friends"></i>
|
|
{{else if eq .Visibility "private"}}
|
|
<i class="fa fa-lock has-text-private-light" title="Private"></i>
|
|
{{else}}
|
|
<i class="fa fa-eye has-text-link-light" title="Public"></i>
|
|
{{end}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{{else}}
|
|
<span class="fa fa-user mr-2"></span>
|
|
<span>[deleted]</span>
|
|
{{end}}
|
|
</div>
|
|
</header>
|
|
{{end}}
|
|
|
|
<div class="card-image">
|
|
<figure class="image">
|
|
<a href="{{PhotoURL .Filename}}" target="_blank"
|
|
class="js-modal-trigger" data-target="detail-modal"
|
|
onclick="setModalImage(this.href)">
|
|
<img src="{{PhotoURL .Filename}}">
|
|
</a>
|
|
</figure>
|
|
</div>
|
|
<div class="card-content">
|
|
{{if .Caption}}
|
|
{{.Caption}}
|
|
{{else}}<em>No caption</em>{{end}}
|
|
|
|
{{template "card-body" .}}
|
|
|
|
<!-- Like & Comments buttons -->
|
|
<div class="mt-4 columns is-centered is-mobile is-gapless">
|
|
<div class="column is-narrow mr-1">
|
|
{{$Like := $Root.LikeMap.Get .ID}}
|
|
<button type="button" class="button is-small nonshy-like-button"
|
|
data-table-name="photos" data-table-id="{{.ID}}"
|
|
title="Like">
|
|
<span class="icon{{if $Like.UserLikes}} has-text-danger{{end}}"><i class="fa fa-heart"></i></span>
|
|
<span class="nonshy-likes">
|
|
Like
|
|
{{if gt $Like.Count 0}}
|
|
({{$Like.Count}})
|
|
{{end}}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div class="column is-narrow">
|
|
{{$Comments := $Root.CommentMap.Get .ID}}
|
|
<a href="/photo/view?id={{.ID}}#comments" class="button is-small">
|
|
<span class="icon"><i class="fa fa-comment"></i></span>
|
|
<span>{{$Comments}} Comment{{Pluralize64 $Comments}}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="card-footer">
|
|
{{if or $Root.IsOwnPhotos $Root.CurrentUser.IsAdmin}}
|
|
{{template "card-footer" .}}
|
|
{{end}}
|
|
|
|
{{if not $Root.IsOwnPhotos}}
|
|
<a class="card-footer-item has-text-danger" href="/contact?intent=report&subject=report.photo&id={{.ID}}">
|
|
<span class="icon"><i class="fa fa-flag"></i></span>
|
|
<span class="is-hidden-desktop">Report</span>
|
|
</a>
|
|
{{end}}
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}<!-- ViewStyle -->
|
|
|
|
{{template "pager" .}}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script type="text/javascript">
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
// Get our modal to trigger it on click of a detail img.
|
|
let $modal = document.querySelector("#detail-modal");
|
|
document.querySelectorAll(".js-modal-trigger").forEach(node => {
|
|
node.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
setModalImage(node.href);
|
|
$modal.classList.add("is-active");
|
|
})
|
|
});
|
|
});
|
|
function setModalImage(url) {
|
|
let $modalImg = document.querySelector("#detailImg");
|
|
$modalImg.src = url;
|
|
return false;
|
|
}
|
|
</script>
|
|
{{end}} |