<!-- 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> {{if .Views}} <small class="has-text-grey is-size-7 ml-2"> <i class="fa fa-eye"></i> {{.Views}} </small> {{end}} </div> <div class="mt-2"> {{if .Pinned}} <span class="tag is-success is-light"> <span class="icon"><i class="fa fa-thumbtack"></i></span> <span>Pinned</span> </span> {{end}} {{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"}} <label class="card-footer-item checkbox"> <input type="checkbox" class="nonshy-edit-photo-id" name="id" value="{{.ID}}"> </label> <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}} <!-- Main content template --> {{define "content"}} {{if not .IsSiteGallery}} <style type="text/css"> {{template "profile-theme-hero-style" .User}} </style> {{end}} <div class="container"> <section class="hero is-link is-bold"> <div class="hero-body"> <div class="container"> <div class="level"> <div class="level-left"> <h1 class="title"> <span class="icon mr-4"><i class="fa fa-image"></i></span> <span>{{template "title" .}}</span> </h1> </div> {{if or .IsOwnPhotos .IsSiteGallery}} <div class="level-right"> <div> <a href="/photo/upload" class="button"> <span class="icon"><i class="fa fa-upload"></i></span> <span>Upload Photo</span> </a> </div> </div> {{end}} </div> </div> </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> <li> <a href="/u/{{.User.Username}}/notes"> <span class="icon is-small"> <i class="fa fa-pen-to-square"></i> </span> <span> Notes {{if .NoteCount}}<span class="tag is-link is-light ml-1">{{.NoteCount}}</span>{{end}} </span> </a> </li> <li> <a href="/u/{{.User.Username}}/friends"> <span class="icon is-small"> <i class="fa fa-user-group"></i> </span> <span> Friends {{if .FriendCount}}<span class="tag is-link is-light ml-1">{{.FriendCount}}</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"> <!-- Notes: to get the image to always scale and fit on screen, it is made as a background image in CSS on the detailImg div; but we don't have the image's minimum size here, so the hidden <img> inside provides the size pushing to make it visible on screen, otherwise the divs are 0x0 pixels and nothing would be visible. --> <div id="detailImg"> <img style="visibility: hidden"> <!-- Alt Text button for accessibility --> <button class="button is-small alt-text py-1 px-2"> <strong>ALT</strong> </button> </div> </div> <button class="modal-close is-large" aria-label="close"></button> </div> <!-- Shy User alert banner (Site Gallery) --> {{if and .IsSiteGallery .IsShyUser}} <div class="notification is-danger is-light"> <i class="fa fa-exclamation-triangle"></i> You have a <strong>Shy Account</strong> so you will only see pictures of you and your friends here. <a href="/faq#shy-faqs">Learn more <small class="fa fa-external-link"></small></a> </div> {{end}} <!-- Shy User alert banner (User Gallery - IsShyFrom) --> {{if .IsShyFrom}} <div class="notification is-danger is-light"> <i class="fa fa-exclamation-triangle"></i> You have a <strong>Shy Account</strong> and you are not friends with this person so can not see their gallery. <a href="/faq#shy-faqs">Learn more <small class="fa fa-external-link"></small></a> </div> {{end}} <!-- Notice if the current user can not see the user's default profile picture --> {{if .ProfilePictureHiddenVisibility}} <div class="block"> <i class="fa fa-info-circle mr-1"></i> <strong>Notice:</strong> @{{.User.Username}}'s default profile picture is set to {{if eq .ProfilePictureHiddenVisibility "friends"}} <img src="/static/img/shy-friends.png" width="16" height="16"> <strong class="has-text-warning">Friends only</strong> {{else}} <img src="/static/img/shy-private.png" width="16" height="16"> <strong class="has-text-private">Private</strong> {{end}} visibility and can not be seen by you. <a href="/faq#private-avatar" target="_blank">Learn more <i class="fa fa-external-link"></i></a> </div> {{end}} <div class="block"> <div class="level mb-2"> <div class="level-left"> <div class="level-item"> {{if .Pager.Total}} <span> Found <strong>{{FormatNumberCommas .Pager.Total}}</strong> photo{{Pluralize64 .Pager.Total}} (page {{.Pager.Page}} of {{.Pager.Pages}}). {{if .ExplicitCount}} {{.ExplicitCount}} explicit photo{{Pluralize64 .ExplicitCount}} hidden per your <a href="/settings#prefs">settings</a>. {{end}} </span> {{else if .ExplicitCount}} <!-- No pager, but still show explicit hint, e.g. in case user filters by Private but all privates are explicit --> <span> {{.ExplicitCount}} explicit photo{{Pluralize64 .ExplicitCount}} hidden per your <a href="/settings#prefs">settings</a>. </span> {{end}} </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}}?{{QueryPlus "view" "cards"}}">Cards</a> </li> <li{{if eq .ViewStyle "full"}} class="is-active"{{end}}> <a href="{{.Request.URL.Path}}?{{QueryPlus "view" "full"}}">Full</a> </li> </ul> </div> </div> </div> </div> <!-- Show an "Unsubscribe to this user's new photo notifications" if you are Friends. --> {{if .AreFriends}} <p class="block"> <a href="/comments/subscription?table_name=friend.photos&table_id={{.User.Username}}&next={{UrlEncode .Request.URL.String}}&subscribe={{if .AreNotificationsMuted}}true{{else}}false{{end}}" class="{{if .AreNotificationsMuted}}has-text-success{{else}}{{end}}"> <span class="icon"><i class="fa fa-bell{{if not .AreNotificationsMuted}}-slash{{end}}"></i></span> <span> {{if .AreNotificationsMuted}} Enable notifications about <strong>{{.User.Username}}</strong>'s new photos {{else}} Mute notifications about <strong>{{.User.Username}}</strong>'s new photos {{end}} </span> </a> </p> {{end}} <!-- If viewing our own profile, and we don't have a profile picture set, offer advice. --> {{if and (not .IsSiteGallery) (eq .CurrentUser.ProfilePhoto.ID 0) (eq .CurrentUser.ID .User.ID)}} <div class="notification is-success is-light content"> <p> <i class="fa-regular fa-id-badge mr-1"></i> <strong>Your default profile picture is not set</strong> <p> Your default profile picture is currently not set to anything, and appears to other members as the default blue <img src="/static/img/shy.png" width="16" height="16"> placeholder image. </p> <ul> <li> To upload a <strong>new</strong> profile picture, <a href="/photo/upload?intent=profile_pic">click here</a>. </li> <li> To use one of your <strong>existing</strong> photos as your profile picture: <ol class="my-2"> <li> Click on the "Edit" button beneath one of your photos below. </li> <li> On the edit page, below the picture, click on the button to "Set this as my profile photo (crop image)" and select the square shape you want for your profile pic. </li> <li> Click on "Save Changes" when done! </li> </ol> </li> </ul> <p> Having a profile picture set, along with an approved <a href="/photo/certification">certification photo</a>, is required to access the social features on {{PrettyTitle}} such as the chat room, forums and member directory. </p> </p> </div> {{end}} <!-- Indicator if friends-only is selected. --> {{if eq .FilterWho "friends"}} <div class="notification is-success is-light"> Showing you all recent photos from <strong>yourself & your friends.</strong> <a href="{{.Request.URL.Path}}?who=everybody">See all certified members' gallery photos?</a> </div> {{else if eq .FilterWho "friends+private"}} <div class="notification is-success is-light"> Showing you all recent photos from <strong>yourself & your friends</strong> as well as any <strong>private photos shared with you</strong> by others on the site (if they are marked to appear in the Site Gallery). </div> {{else if eq .FilterWho "likes"}} <div class="notification is-success is-light"> Showing you photos that you have <i class="fa fa-heart"></i> <strong>Liked.</strong> </div> {{end}} <!-- Filters --> <div class="block"> <form action="{{.Request.URL.Path}}" method="GET"> <div class="card nonshy-collapsible-mobile"> <header class="card-header has-background-link-light"> <p class="card-header-title has-text-dark"> Search Filters </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"> <!-- Site Gallery: friends-only filter --> {{if .IsSiteGallery}} <div class="column"> <div class="field"> <label class="label" for="who">Whose photos:</label> <div class="select is-fullwidth"> <select id="who" name="who"> <option value="friends"{{if eq .FilterWho "friends"}} selected{{end}}>Myself & friends only</option> <option value="friends+private"{{if eq .FilterWho "friends+private"}} selected{{end}}>Myself, friends, & private photo grants</option> <option value="likes"{{if eq .FilterWho "likes"}} selected{{end}}>Photos I have 'liked'</option> <option value="everybody"{{if eq .FilterWho "everybody"}} selected{{end}}>All certified members</option> {{if .CurrentUser.HasAdminScope "social.moderator.photo"}} <option value="uncertified"{{if eq .FilterWho "uncertified"}} selected{{end}}>☮ Non-certified members</option> {{end}} </select> </div> </div> </div> {{end}} {{if or .CurrentUser.Explicit .IsOwnPhotos}} <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> <!-- Friends & Private: always show on Site Gallery, show if available on User Gallery --> {{if or .IsSiteGallery .AreFriends .IsOwnPhotos}} <option value="friends"{{if eq .FilterVisibility "friends"}} selected{{end}}>Friends only</option> {{end}} {{if or .IsSiteGallery .AreWeGrantedPrivate .IsOwnPhotos}} <option value="private"{{if eq .FilterVisibility "private"}} selected{{end}}>Private only</option> {{end}} </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"> {{if not .IsSiteGallery}} <option value="pinned desc nulls last, updated_at desc"{{if eq .Sort "pinned desc nulls last, updated_at desc"}} selected{{end}}> Pinned, recently updated </option> {{end}} <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> <option value="like_count desc"{{if eq .Sort "like_count desc"}} selected{{end}}>Most likes</option> <option value="comment_count desc"{{if eq .Sort "comment_count desc"}} selected{{end}}>Most comments</option> <option value="views desc"{{if eq .Sort "views desc"}} selected{{end}}>Most views</option> </select> </div> </div> </div> {{if and .IsSiteGallery (.CurrentUser.HasAdminScope "social.moderator.photo")}} <div class="column"> <div class="field"> <label class="label has-text-danger" for="admin_view">Admin view:</label> <div class="select is-fullwidth"> <select id="admin_view" name="admin_view"> <option value="">Default (disabled)</option> <option value="true"{{if .AdminView}} selected{{end}}>Show all photos</option> </select> </div> </div> </div> {{end}} </div> <div class="has-text-centered"> <a href="{{.Request.URL.Path}}" class="button">Reset</a> <button type="submit" class="button is-success"> Apply Filters </button> </div> </div> </div> <!-- Retain cards vs. full parameter --> <input type="hidden" name="view" value="{{.ViewStyle}}"> </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> </a> </div> {{else if not .IsSiteGallery}} <!-- Private photo unlock/status prompt for this other user. --> {{if .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> </div> {{else if .ShowPrivateUnlockPrompt}} <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> </a> </div> {{end}} {{end}} {{if .AreWeGrantedPrivate}} <div class="block mt-0"> <span class="icon"><i class="fa fa-eye has-text-private"></i></span> <strong>{{.User.Username}}</strong> has <span class="has-text-private">granted</span> you access to see their private photos. </div> {{end}} {{SimplePager .Pager}} <!-- Form to wrap the gallery, e.g. for batch edits on user views. --> <form action="/photo/batch-edit"> <!-- "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 mr-2"> {{template "avatar-24x24" $Owner}} </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 has-text-centered is-clipped"> <!-- GIF video? --> {{if HasSuffix .Filename ".mp4"}} <video loop controls controlsList="nodownload" playsinline class="js-modal-trigger" data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}" {{if .AltText}}title="{{.AltText}}"{{end}} {{if BlurExplicit .}}class="blurred-explicit" {{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay {{end}}> <source src="{{PhotoURL .Filename}}" type="video/mp4"> </video> {{else}} <a href="/photo/view?id={{.ID}}" data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}" target="_blank" class="js-modal-trigger"> <img src="{{PhotoURL .Filename}}" loading="lazy" {{if BlurExplicit .}}class="blurred-explicit"{{end}} {{if .AltText}}alt="{{.AltText}}" title="{{.AltText}}"{{end}}> </a> {{end}} </div> <div class="card-content"> {{if .Caption}} {{.Caption}} {{else}}<em>No caption</em>{{end}} {{template "card-body" .}} <!-- Quick mark photo as explicit --> {{if and (not .Explicit) (ne .UserID $Root.CurrentUser.ID) (not .HasAdminLabelNonExplicit)}} <div class="mt-2"> <a href="#" class="has-text-danger is-size-7 nonshy-mark-explicit" data-photo-id="{{.ID}}" data-photo-url="{{PhotoURL .Filename}}"> <i class="fa fa-fire mr-1"></i> Should this photo be marked Explicit? </a> </div> {{end}} <!-- Like & Comments buttons --> {{if not $Root.AdminView}} <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> {{end}} </div> <footer class="card-footer"> {{if or $Root.IsOwnPhotos ($Root.CurrentUser.HasAdminScope "social.moderator.photo")}} {{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 mr-2"> {{template "avatar-24x24" $Owner}} </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 has-text-centered is-clipped"> <!-- GIF video? --> {{if HasSuffix .Filename ".mp4"}} <video loop controls controlsList="nodownload" playsinline class="js-modal-trigger" data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}" {{if .AltText}}title="{{.AltText}}"{{end}} {{if BlurExplicit .}}class="blurred-explicit" {{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay {{end}}> <source src="{{PhotoURL .Filename}}" type="video/mp4"> </video> {{else}} <a href="/photo/view?id={{.ID}}" data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}" target="_blank" class="js-modal-trigger"> <img src="{{PhotoURL .Filename}}" loading="lazy" {{if BlurExplicit .}}class="blurred-explicit"{{end}} {{if .AltText}}alt="{{.AltText}}" title="{{.AltText}}"{{end}}> </a> {{end}} </div> <div class="card-content"> {{if .Caption}} {{.Caption}} {{else}}<em>No caption</em>{{end}} {{template "card-body" .}} <!-- Quick mark photo as explicit --> {{if and (not .Explicit) (ne .UserID $Root.CurrentUser.ID) (not .HasAdminLabelNonExplicit)}} <div class="mt-2"> <a href="#" class="has-text-danger is-size-7 nonshy-mark-explicit" data-photo-id="{{.ID}}" data-photo-url="{{PhotoURL .Filename}}"> <i class="fa fa-fire mr-1"></i> Should this photo be marked Explicit? </a> </div> {{end}} <!-- Like & Comments buttons --> {{if not $Root.AdminView}} <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> {{end}} </div> <footer class="card-footer"> {{if or $Root.IsOwnPhotos ($Root.CurrentUser.HasAdminScope "social.moderator.photo")}} {{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 --> {{SimplePager .Pager}} <!-- Bulk user actions to their photos --> {{if or .IsOwnPhotos (.CurrentUser.HasAdminScope "social.moderator.photo")}} <hr> <div class="columns is-multiline is-mobile my-4"> <div class="column is-narrow"> <div class="buttons has-addons"> <button type="button" class="button" id="nonshy-select-all"> <i class="fa fa-square-check"></i> </button> <button type="button" class="button" id="nonshy-select-none"> <i class="fa fa-square"></i> </button> </div> </div> <div class="column" id="nonshy-edit-buttons"> <button type="submit" class="button is-small is-danger is-outlined" name="intent" value="delete"> <i class="fa fa-trash mr-2"></i> Delete </button> <button type="submit" class="button mx-1 is-small is-info is-outlined" name="intent" value="visibility"> <i class="fa fa-eye mr-2"></i> Edit Visibility </button> <span id="nonshy-count-selected" class="is-size-7 ml-2"></span> </div> </div> {{end}} </form><!-- end gallery form for batch edits --> </div> <!-- Admin change log link --> {{if .CurrentUser.HasAdminScope "admin.changelog"}} <div class="block"> <a href="/admin/changelog?table_name=photos{{if .User}}&about_user_id={{.User.ID}}{{end}}" class="button is-small has-text-warning"> <span class="icon"><i class="fa fa-peace mr-1"></i></span> <span>{{if .User}}User{{else}}Site{{end}} Gallery change log</span> </a> </div> {{end}} </div> </div> <script type="text/javascript"> {{if or .IsOwnPhotos (.CurrentUser.HasAdminScope "social.moderator.photo")}} // Batch edit controls document.addEventListener("DOMContentLoaded", () => { const checkboxes = document.getElementsByClassName("nonshy-edit-photo-id"), $checkAll = document.querySelector("#nonshy-select-all"), $checkNone = document.querySelector("#nonshy-select-none"), $countSelected = document.querySelector("#nonshy-count-selected"), $submitButtons = document.querySelector("#nonshy-edit-buttons"); $submitButtons.style.display = "none"; const setAllChecked = (v) => { for (let box of checkboxes) { box.checked = v; } }; const areAnyChecked = () => { let any = false, count = 0; for (let box of checkboxes) { if (box.checked) { any = true; count++; } } // update the selected count $countSelected.innerHTML = count > 0 ? `${count} selected.` : ""; $countSelected.style.display = count > 0 ? "" : "none"; return any; }; const showHideButtons = () => { $submitButtons.style.display = areAnyChecked() ? "" : "none"; }; showHideButtons(); // Check/Uncheck All buttons. $checkAll.addEventListener("click", (e) => { setAllChecked(true); showHideButtons(); }); $checkNone.addEventListener("click", (e) => { setAllChecked(false); showHideButtons(); }); // When checkboxes are toggled. for (let box of checkboxes) { box.addEventListener("change", (e) => { showHideButtons(); }); } }); {{end}} document.addEventListener("DOMContentLoaded", () => { // Get our modal to trigger it on click of a detail img. let $modal = document.querySelector("#detail-modal"), $altText = $modal.getElementsByTagName("button")[0]; function setModalImage(url, altText) { let $modalImg = document.querySelector("#detailImg"), $img = $modalImg.getElementsByTagName("img")[0]; $img.src = url; $modalImg.style.backgroundImage = `url(${url})`; // Alt text? $modalImg.title = altText; $altText.style.display = altText ? "block" : "none"; $altText.onclick = (e) => { window.alert(altText); e.preventDefault(); e.stopPropagation(); return false; } return false; } function markImageViewed(photoID) { fetch(`/v1/photo/${photoID}/view`, { method: "POST", mode: "same-origin", cache: "no-cache", credentials: "same-origin", headers: { "Content-Type": "application/json", }, }).then(response => response.json()) .then(data => { if (data.StatusCode !== 200) { console.error("When marking photo %d as viewed: status code %d: %s", photoID, data.StatusCode, data.data.error); return; } }).catch(window.alert); } document.querySelectorAll(".js-modal-trigger").forEach(node => { let $img = node.getElementsByTagName("img"), $video = node.tagName === 'VIDEO' ? node : null, photoID = node.dataset.photoId, altText = $img[0] != undefined ? $img[0].alt : ''; // Video (animated GIF) handlers. if ($video !== null) { // Log this video viewed if the user interacts with it in any way. // Note: because videos don't open in the lightbox modal. ['pause', 'mouseover'].forEach(event => { $video.addEventListener(event, (e) => { // Log a view of this video. markImageViewed(photoID); }); }); return; } // Images: open in the lightbox modal. node.addEventListener("click", (e) => { e.preventDefault(); setModalImage(node.dataset.url, altText); $modal.classList.add("is-active"); // Log a view of this photo. markImageViewed(photoID); }); // Images: count a mouseover as a view to be on par with videos, otherwise // videos climb to the top of the most viewed list too quickly. node.addEventListener("mouseover", (e) => { markImageViewed(photoID); }); }); }); </script> <!-- Mark Explicit modal --> {{template "mark-explicit-modal" .}} {{end}}