├── LICENSE
├── README.md
├── bandwagon-album
├── checkout.html
├── javascript
│ └── playlist.js
├── oembed.html
├── stylesheet.html
├── template.hjson
├── unpublish.html
├── upload.html
└── view.html
├── bandwagon-common
└── template.hjson
├── bandwagon-event
├── edit.html
├── stylesheet.html
├── template.hjson
└── view.html
├── bandwagon-news
├── edit.html
├── template.hjson
└── view.html
├── bandwagon-outbox
├── albums.html
├── coming-soon.html
├── events.html
├── header.html
├── news.html
├── outbox.html
├── stylesheet.html
├── template.hjson
├── wizard-1.html
├── wizard-2.html
└── wizard-3.html
├── bandwagon-search-albums
├── json.html
├── template.hjson
├── view-results.html
├── view-sidebar.html
└── view-tags.html
├── bandwagon-search-events
├── embed-test.html
├── embed.html
├── template.hjson
├── view-results.html
└── view-sidebar.html
├── bandwagon-search-news
├── template.hjson
├── view-results.html
└── view-sidebar.html
├── bandwagon-search-tracks
├── json.html
├── template.hjson
├── view-results.html
├── view-sidebar.html
└── view-tags.html
├── bandwagon-search-users
├── template.hjson
├── view-results.html
└── view-sidebar.html
├── bandwagon-search
├── hyperscript
│ ├── listbox._hs
│ ├── popUp._hs
│ ├── searchSuggestion._hs
│ ├── searchTag._hs
│ ├── searchWidget._hs
│ └── utils._hs
├── javascript
│ └── parseTags.js
├── search-related.html
├── template.hjson
├── view-results.html
├── view-suggestions.html
├── view-widget.html
└── view.html
└── bandwagon-song
├── oembed.html
├── template.hjson
└── view.html
/README.md:
--------------------------------------------------------------------------------
1 | # Bandwagon - Social Web for Musicians and Bands
2 | Bandwagon is a social music sharing service for the Fediverse. Built for bands, Bandwagon is a customizable home page for all your songs, show dates, photos, and announcements.
3 |
4 | ## Build with Emissary
5 | Bandwagon is a small social app build on [Emissary, the social web toolkit](https://emissary.dev). You must [install Emissary](https://emissary.dev/installation) on a server first, before using Bandwagon.
6 |
7 | ### Installing
8 | To install Bandwagon on your own Emissary server directly from this repository, just open your Emissary setup console, navigate to Server Settings > Packages, and add a new Git adapter that points to: https://github.com/EmissarySocial/app-bandwagon.git
9 |
10 | ### Customizing
11 | Bandwagon consists of a new custom profile page (or, outbox) and templates for a handful of new content types that each band can post: albums, show dates, photos, and announcements. After installing Bandwagon, each musician can customize the colors, images, and labels used in their own profile.
12 |
--------------------------------------------------------------------------------
/bandwagon-album/checkout.html:
--------------------------------------------------------------------------------
1 | {{- $userID := .AttributedTo.UserID.Hex -}}
2 | {{- $permalink := .Permalink -}}
3 | {{- $checkoutLabel := first (.Data "checkout label") "Buy Now" -}}
4 |
{{$checkoutLabel}}
5 |
6 |
17 |
18 |
19 | Close Window
20 |
--------------------------------------------------------------------------------
/bandwagon-album/javascript/playlist.js:
--------------------------------------------------------------------------------
1 | var playlist
2 | var playlistIndex = -1
3 |
4 | // Play loads and plays the song at the current playlistIndex
5 | function Play() {
6 |
7 | if (playlistIndex < 0) {
8 | return
9 | }
10 |
11 | if (playlistIndex >= playlist.length) {
12 | return
13 | }
14 |
15 | var audio = htmx.find("audio")
16 | var controls = htmx.find("#media-controls")
17 | var song = playlist[playlistIndex]
18 |
19 | htmx.find("#source-mp3").src = song.url + ".mp3?bitrate=128&v=2"
20 | htmx.find("#source-ogg").src = song.url + ".opus?bitrate=128&v=2"
21 |
22 | audio.load()
23 | audio.play()
24 |
25 | htmx.addClass(controls, "PLAYING")
26 | htmx.takeClass(htmx.find("#track-" + playlistIndex), "PLAYING")
27 | }
28 |
29 | // Pause pauses the audio control
30 | function Pause() {
31 | var controls = htmx.find("#media-controls")
32 | var audio = htmx.find("audio")
33 | var track = htmx.find("#track-" + playlistIndex)
34 |
35 | audio.pause()
36 | htmx.removeClass(controls, "PLAYING")
37 | htmx.removeClass(track, "PLAYING")
38 | }
39 |
40 | // PlayFirst jumps to the first song in the playlist and plays it
41 | function PlayFirst() {
42 | if (playlist.length > 0) {
43 | playlistIndex = 0
44 | Play()
45 | }
46 | }
47 |
48 | // PlayPrevious plays the previous song in the playlist and plays it
49 | function PlayPrevious() {
50 | if (playlist.length > 0) {
51 | playlistIndex--
52 | if (playlistIndex < 0) {
53 | playlistIndex = playlist.length - 1
54 | }
55 | Play()
56 | }
57 | }
58 |
59 | // PlayNext plays the next song in the playlist and plays it
60 | function PlayNext(loop) {
61 | if (playlist.length > 0 ) {
62 | playlistIndex++
63 | if (playlistIndex >= playlist.length) {
64 |
65 | if (loop == true) {
66 | playlistIndex = 0
67 | } else {
68 | return
69 | }
70 | }
71 | Play()
72 | }
73 | }
74 |
75 | function Toggle(trackNumber) {
76 |
77 | var audio = htmx.find("audio")
78 |
79 | // Do not overflow the playlist.
80 | if (trackNumber >= playlist.length) {
81 | return
82 | }
83 |
84 | // If the audio is already paused, or is playing a different track, then play this track
85 | if ((audio.paused) || (playlistIndex != trackNumber)) {
86 | playlistIndex = trackNumber
87 | Play()
88 | } else {
89 | Pause()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/bandwagon-album/oembed.html:
--------------------------------------------------------------------------------
1 | {{- $albumColors := .Data "color" -}}
2 | {{- $bodyColor := first $albumColors.body "#eeeeee" -}}
3 | {{- $pageColor := parseColor (first $albumColors.page "#ffffff") -}}
4 | {{- $buttonColor := parseColor (first $albumColors.button "#0f62fe") -}}
5 | {{- $titleColor := $pageColor.Text -}}
6 | {{- $textColor := $pageColor.TextExt -}}
7 |
8 |
9 |
10 |
11 |
12 | {{- if ne "" .IconURL -}}
13 |
14 |
15 |
16 | {{- end -}}
17 |
18 | {{.Label}}
19 |
28 |
54 |
55 | {{- if ne "" .Summary -}}
56 |
57 | {{.Summary}}
58 |
59 | {{- end -}}
60 |
61 |
62 | {{- $license := .DataString "license"}}
63 | {{- if eq "COPYRIGHT" $license -}}
64 | ©
65 | Copyright. All Rights Reserved.
66 | {{- else if ne "" $license -}}
67 | {{- $licenses := .Dataset "bandwagon-album.licenses" -}}
68 | {{- $licenseInfo := $licenses.Value $license -}}
69 | {{icon $licenseInfo.Icon}}
70 | {{$licenseInfo.Label}}
71 | {{- end -}}
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/bandwagon-album/stylesheet.html:
--------------------------------------------------------------------------------
1 | {{- $artist := .ParentOutbox "view" -}}
2 | {{- $albumColors := .Data "color" -}}
3 |
4 | {{- $bodyColor := first $albumColors.body ($artist.Data "color-body") -}}
5 | {{- $pageColor := first $albumColors.page ($artist.Data "color-page") -}}
6 | {{- $buttonColor := first $albumColors.button ($artist.Data "color-button") -}}
7 |
8 | body {
9 |
10 | {{ if ne nil $bodyColor -}}
11 | --body-background: {{$bodyColor}};
12 | {{- end }}
13 |
14 | {{ if ne nil $pageColor -}}
15 | {{- $pageColorParsed := parseColor $pageColor -}}
16 | --page-background: {{$pageColorParsed}};
17 | --page-border: {{$pageColorParsed.Darken 10}};
18 | --heading-color: {{$pageColorParsed.Text}};
19 | --text-color: {{$pageColorParsed.TextExt}};
20 |
21 | {{if $pageColorParsed.IsLight }}
22 | --white: rgb(255, 255, 255);
23 | --gray00: rgb(255, 255, 255);
24 | --gray01: rgba(0, 0, 0, 0.01);
25 | --gray02: rgba(0, 0, 0, 0.02);
26 | --gray03: rgba(0, 0, 0, 0.03);
27 | --gray04: rgba(0, 0, 0, 0.04);
28 | --gray05: rgba(0, 0, 0, 0.05);
29 | --gray10: rgba(0, 0, 0, 0.10);
30 | --gray15: rgba(0, 0, 0, 0.15);
31 | --gray20: rgba(0, 0, 0, 0.20);
32 | --gray30: rgba(0, 0, 0, 0.30);
33 | --gray40: rgba(0, 0, 0, 0.40);
34 | --gray50: rgba(0, 0, 0, 0.50);
35 | --gray60: rgba(0, 0, 0, 0.60);
36 | --gray70: rgba(0, 0, 0, 0.70);
37 | --gray80: rgba(0, 0, 0, 0.80);
38 | --gray90: rgba(0, 0, 0, 0.90);
39 | --gray100: rgb(0, 0, 0);
40 | --black: rgb(0, 0, 0);
41 | {{- else }}
42 | --white: rgb(0, 0, 0) ;
43 | --gray00: rgb(0, 0, 0) ;
44 | --gray01: rgba(255, 255, 255, 0.01);
45 | --gray02: rgba(255, 255, 255, 0.02);
46 | --gray03: rgba(255, 255, 255, 0.03);
47 | --gray04: rgba(255, 255, 255, 0.04);
48 | --gray05: rgba(255, 255, 255, 0.05);
49 | --gray10: rgba(255, 255, 255, 0.10);
50 | --gray15: rgba(255, 255, 255, 0.15);
51 | --gray20: rgba(255, 255, 255, 0.20);
52 | --gray30: rgba(255, 255, 255, 0.30);
53 | --gray40: rgba(255, 255, 255, 0.40);
54 | --gray50: rgba(255, 255, 255, 0.50);
55 | --gray60: rgba(255, 255, 255, 0.60);
56 | --gray70: rgba(255, 255, 255, 0.70);
57 | --gray80: rgba(255, 255, 255, 0.80);
58 | --gray90: rgba(255, 255, 255, 0.90);
59 | --gray100: rgb(255, 255, 255);
60 | --black: rgb(255, 255, 255);
61 | {{- end -}}
62 |
63 | {{- end }}
64 |
65 | {{ if ne nil $buttonColor -}}
66 | {{- $buttonColorParsed := parseColor $buttonColor -}}
67 | {{- $buttonText := $buttonColorParsed.Text }}
68 | {{- $buttonColorHover := ($buttonColorParsed.Lighten 10) -}}
69 | {{- $buttonTextHover := $buttonColorHover.Text }}
70 |
71 | --button-primary-background: {{$buttonColor}};
72 | --button-primary-background-hover: {{$buttonColorHover}};
73 | --button-primary-color: {{$buttonText}};
74 | --button-primary-color-hover: {{$buttonTextHover}};
75 | --link-color: {{$buttonColor}};
76 | --link-color-hover: {{$buttonColorHover}};
77 | {{- end }}
78 |
79 | }
80 |
81 | #media-controls:not(.PLAYING) > .playing-show,
82 | #media-controls.PLAYING > .playing-hide,
83 | .track:not(.PLAYING) .playing-show,
84 | .track.PLAYING .playing-hide,
85 | .track:not(.PLAYING) .playing-hide.hover-hide
86 | .track:not(.PLAYING):hover .playing-hide.hover-show
87 | {
88 | display: none;
89 | }
90 |
91 | #media-controls.PLAYING > .playing-show,
92 | #media-controls:not(.PLAYING) > .playing-hide,
93 | .track.PLAYING playing-show,
94 | .track:not(.PLAYING) .playing-hide,
95 | .track:not(.PLAYING) .playing-hide.hover-hide
96 | .track:not(.PLAYING):hover .playing-hide.hover-show,
97 | {
98 | display: initial;
99 | }
100 |
101 | .track.PLAYING {
102 | background-color: var(--gray10);
103 | }
104 |
105 | {{$artist.Data "stylesheet"}}
--------------------------------------------------------------------------------
/bandwagon-album/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-album
3 | templateRole: album
4 | model: Stream
5 | containedBy: ["outbox"]
6 | extends: ["base-intent", "bandwagon-common"]
7 | icon: album
8 | label: Album (Default)
9 | description: Contains one or more songs
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string", required: true}
14 | summary: {type:"string", format:"html", maxLength:2048}
15 | iconUrl: {type:"string", format:"url"}
16 | syndication: {type:"array", items:{type:"string"}}
17 | data: {
18 | type:object
19 | properties: {
20 | checkoutLabel: {type:"string", maxLength:255}
21 | license: {type:"string"}
22 | imageFilename: {type:"string", maxLength:255}
23 | tags: {type:"string"}
24 | releaseDate:{type:"string", format:"date"}
25 | links: {type:"object", wildcard: {type:"string", format:"uri"}}
26 | color: {type:"object", properties: {
27 | body: {type:"string", format:"color"},
28 | page: {type:"string", format:"color"},
29 | button: {type:"string", format:"color"}
30 | }}
31 | }
32 | }
33 | }
34 | }
35 |
36 | accessRoles: {
37 | buyer: {
38 | label: Buyer
39 | description: Buyers have purchased this album and have lifetime access to it.
40 | subscription: true
41 | }
42 | subscriber: {
43 | label: Subscriber
44 | description: Subscribers have full access so long as their subscription is valid.
45 | subscription: true
46 | }
47 | renter: {
48 | label: Renter
49 | description: IDK, man. I'm just testing subscription stuff.
50 | subscription: true
51 | }
52 | }
53 |
54 | socialRole: Album
55 | socialRules: [
56 |
57 | {target:"@context", append:"https://funkwhale.audio/ns"}
58 | {target:"content", expression:"{{ markdown .Summary }}"}
59 | {target:"license", path:"data.license"}
60 | {target:"links", forEach:"data.links", rules:[
61 | {target:"label", path:"key"}
62 | {target:"rel", value:"alternate"}
63 | {target:"href", path:"value"}
64 | ]}
65 | {target:"library", expression:"{{.URL}}/pub/children"}
66 | {target:"artists", value:[]}
67 | {target:"artists", append:{type:"Artist"}}
68 | {target:"artists.0.id", path:"attributedTo.profileUrl"}
69 | {target:"artists.0.name", path:"attributedTo.name"}
70 | {target:"tracks", expression:"{{.URL}}/pub/children"}
71 | ]
72 |
73 | tagPaths: ["data.tags"]
74 |
75 | search: {
76 | type: "Album"
77 | iconUrl:""
78 | summary:""
79 | text: "{{.Label}} {{.AttributedTo.Name}}"
80 | tags: "LIC:{{index .Data `license`}}"
81 | }
82 |
83 | states: {
84 | unpublished: {
85 | label:"Default State"
86 | description:"Visible only to Authors and Owners"
87 | }
88 | published: {
89 | label:"Published"
90 | description:"Visible to all people with permissions"
91 | }
92 | }
93 | bundles: {
94 | javascript: {contentType: "text/javascript"}
95 | }
96 |
97 | actions: {
98 | create: {
99 | roles:["author"]
100 | steps: [
101 | {do:"as-modal", steps: [
102 | {do:"set-data", from-url:["isFeatured"]}
103 | {
104 | do: edit
105 | form: {
106 | label: + Add an Album
107 | type:layout-tabs
108 | children: [
109 | {
110 | type:layout-vertical
111 | label: General
112 | children: [
113 | {type:"text", path:"label", label:"Album Name"}
114 | {type:"select", path:"data.license", label:"License", options:{required:true, provider:"bandwagon-album.licenses"}}
115 | {type:"date", path:"data.releaseDate", label:"Release Date"}
116 | {type:"upload", path:"iconUrl", label:"Album Art", options:{accept:"image/*", filename:"data.imageFilename", delete:"/{{.StreamID}}/delete-icon", rules:{width:800, height:800, types:["webp"]}}}
117 | {type:"toggle", path:"isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured?"}}
118 | ]
119 | }
120 | {
121 | type:layout-vertical
122 | label: Metadata
123 | children: [
124 | {type:"textarea", path:"summary", label:"Sidebar Notes", description:"Notes appear on the side of the album page. Markdown is allowed.", options:{rows:8, showLimit:true}}
125 | {type:"textarea", path:"data.tags", label:"Tags", description:"Enter #Hashtags separated by spaces."}
126 | ]
127 | }
128 | {
129 | type:layout-vertical
130 | label: Links
131 | children: [
132 | {type:"text", path:"data.links.AMAZON", options:{placeholder:"Amazon Music"}}
133 | {type:"text", path:"data.links.APPLE", options:{placeholder:"Apple Music"}}
134 | {type:"text", path:"data.links.BANDCAMP", options:{placeholder:"Bandcamp"}}
135 | {type:"text", path:"data.links.GOOGLE", options:{placeholder:"Google Play"}}
136 | {type:"text", path:"data.links.SOUNDCLOUD", options:{placeholder:"Soundcloud"}}
137 | {type:"text", path:"data.links.SPOTIFY", options:{placeholder:"Spotify"}}
138 | {type:"text", path:"data.links.TIDAL", options:{placeholder:"Tidal"}}
139 | {type:"text", path:"data.links.YOUTUBE", options:{placeholder:"YouTube Music"}}
140 | {type:"text", path:"data.links.OTHER1", options:{placeholder:"Other"}}
141 | {type:"text", path:"data.links.OTHER2", options:{placeholder:"Other"}}
142 | {type:"text", path:"data.links.OTHER3", options:{placeholder:"Other"}}
143 | ]
144 | }
145 | {
146 | type:layout-vertical
147 | label: Colors
148 | children: [
149 | {type:"colorpicker", path:"data.color.body", label:"Window Background"}
150 | {type:"colorpicker", path:"data.color.page", label:"Page Background"}
151 | {type:"colorpicker", path:"data.color.button", label:"Links and Buttons"}
152 | ]
153 | }
154 | ]
155 | }
156 | }
157 | ]}
158 | {do:"upload-attachments", category:"image", fieldname:"iconUrl", download-path:"iconUrl", accept-type:"image/*", maximum:1, rules:{width:800, height:800, types:["webp"]}}
159 | {do:"process-tags", paths:"data.tags"}
160 | {do:"save"}
161 | {do:"forward-to", url: "/{{.StreamID}}"}
162 | ]
163 | }
164 | view: {
165 | roles: ["editor", "owner"]
166 | stateRoles: {
167 | published: ["anonymous"]
168 | }
169 | do: "view-html"
170 | }
171 | edit: {
172 | roles: ["author"]
173 | steps: [
174 | {do:"as-modal", steps: [
175 | {
176 | do: edit
177 | options: ["delete:/{{.StreamID}}/delete"]
178 | form: {
179 | label: Edit Album
180 | type:layout-tabs
181 | children: [
182 | {
183 | type:layout-vertical
184 | label: General
185 | children: [
186 | {type:"text", path:"label", label:"Album Name"}
187 | {type:"select", path:"data.license", label:"License", options:{required:true, provider:"bandwagon-album.licenses"}}
188 | {type:"date", path:"data.releaseDate", label:"Release Date"}
189 | {type:"upload", path:"iconUrl", label:"Album Art", options:{accept:"image/*", filename:"data.imageFilename", delete:"/{{.StreamID}}/delete-icon", rules:{width:800, height:800, types:["webp"]}}}
190 | {type:"toggle", path:"isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured?"}}
191 | ]
192 | }
193 | {
194 | type:layout-vertical
195 | label: Metadata
196 | children: [
197 | {type:"textarea", path:"summary", label:"Sidebar Notes", description:"Notes appear on the side of the album page. Markdown is allowed.", options:{rows:8, showLimit:true}}
198 | {type:"textarea", path:"data.tags", label:"Tags", description:"Enter #Hashtags separated by spaces"}
199 | ]
200 | }
201 | {
202 | type:layout-vertical
203 | label: Links
204 | children: [
205 | {type:"text", path:"data.links.AMAZON", options:{placeholder:"Amazon Music"}}
206 | {type:"text", path:"data.links.APPLE", options:{placeholder:"Apple Music"}}
207 | {type:"text", path:"data.links.BANDCAMP", options:{placeholder:"Bandcamp"}}
208 | {type:"text", path:"data.links.GOOGLE", options:{placeholder:"Google Play"}}
209 | {type:"text", path:"data.links.SOUNDCLOUD", options:{placeholder:"Soundcloud"}}
210 | {type:"text", path:"data.links.SPOTIFY", options:{placeholder:"Spotify"}}
211 | {type:"text", path:"data.links.TIDAL", options:{placeholder:"Tidal"}}
212 | {type:"text", path:"data.links.YOUTUBE", options:{placeholder:"YouTube Music"}}
213 | {type:"text", path:"data.links.OTHER1", options:{placeholder:"Other"}}
214 | {type:"text", path:"data.links.OTHER2", options:{placeholder:"Other"}}
215 | {type:"text", path:"data.links.OTHER3", options:{placeholder:"Other"}}
216 | ]
217 | }
218 | {
219 | type:layout-vertical
220 | label: Colors
221 | children: [
222 | {type:"colorpicker", path:"data.color.body", label:"Window Background"}
223 | {type:"colorpicker", path:"data.color.page", label:"Page Background"}
224 | {type:"colorpicker", path:"data.color.button", label:"Links and Buttons"}
225 | ]
226 | }
227 | {
228 | type:"layout-vertical"
229 | label:"Distribution"
230 | children:[
231 | {type:"multiselect", path:"syndication", label:"Distribute to these streaming services", description:"These services will be notified when this album is published.", options:{provider:"syndication-targets"}}
232 | ]
233 | }
234 | ]
235 | }
236 | }
237 | ]}
238 | {do:"upload-attachments", category:"image", fieldname:"iconUrl", download-path:"iconUrl", accept-type:"image/*", maximum:1}
239 | {do:"process-tags", paths:"data.tags"}
240 | {do:"search-index"}
241 |
242 | {do:"if", condition: "{{.IsPublished}}",
243 | then: [
244 | {do:"save-and-publish"}
245 | ],
246 | else: [
247 | {do:"save"}
248 | ]
249 | }
250 | {do:"refresh-page"}
251 | ]
252 | }
253 |
254 | checkout: {
255 | roles: ["anonymous"]
256 | steps: [
257 | {do:"if", condition:"{{eq 1 .Products.Count }}", then:[
258 | {do:"redirect-to", url:"/.checkout?productId={{.Products.Slice.First.ProductID.Hex}}&return={{.Permalink}}"}
259 | ], else:[
260 | {do:"as-modal", background:"view", steps:[
261 | {do:"view-html"}
262 | ]}
263 | ]}
264 | ]
265 | }
266 |
267 | /*
268 | edit-products: {
269 | roles: ["owner"]
270 | steps: [
271 | {do:"as-modal", steps: [
272 | {do:"set-products", title:"Purchase Options"}
273 | {do:"save", message:"Subscription settings updated by {{.Author}}"}
274 | ]}
275 | ]
276 | }
277 | */
278 |
279 | delete: {
280 | roles: ["author"]
281 | steps: [
282 | {do:"delete"}
283 | {do:"search-index"}
284 | {do:"forward-to", url: "/@{{.AttributedTo.UserID.Hex}}"}
285 | ]
286 | }
287 |
288 | delete-icon: {
289 | roles: ["author"]
290 | steps: [
291 | {do:"delete-attachments"}
292 | {do:"set-data", values:{"iconUrl": ""}}
293 | {do:"save"}
294 | ]
295 | }
296 |
297 | add-song: {
298 | roles: ["author"]
299 | steps: [
300 | {
301 | do: add-stream
302 | style: inline
303 | location: child
304 | title: + Add a Song
305 | type:bandwagon-song
306 | with-data:{
307 | "data.artist":"{{.AttributedTo.Name}}"
308 | "data.composer":"{{.AttributedTo.Name}}"
309 | "data.tags":"{{.Data `tags`}}"
310 | "data.license": "{{.Data `license`}}"
311 | }
312 | }
313 | {do:"refresh-page"}
314 | ]
315 | }
316 |
317 | sort-tracks: {
318 | roles: ["author"]
319 | steps: [
320 | {do:"sort", model:"Stream", keys:"_id", values:"rank"}
321 | {do:"refresh-page"}
322 | ]
323 | }
324 |
325 | links: {
326 | steps:[
327 | {do:"as-modal", steps:[
328 | {do:"view-html"}
329 | ]}
330 | ]
331 | }
332 |
333 | stylesheet: {
334 | steps: [
335 | {do:"cache-url"}
336 | {do:"view-css"}
337 | ]
338 | }
339 |
340 | upload: {
341 | steps: [
342 | {do:"as-modal", steps:[
343 | {do:"view-html"}
344 | {do:"edit", form:{
345 | type:"layout-vertical"
346 | children:[
347 | {type:"text", path:"attachmentLabel", label:"Label"}
348 | {type:"textarea", path:"attachmentDescription", label:"Description", description:"A few words to show underneath the filename."}
349 | {type:"upload", path:"file", label:"Attachment"}
350 | ]
351 | }, options:["submit-label:Upload Attachment"]}
352 | {do:"upload-attachments", category:"other", maximum:8, fieldname:"file", label-fieldname:"attachmentLabel", description-fieldname:"attachmentDescription"}
353 | {do:"refresh-page"}
354 | ]}
355 | ]
356 | }
357 |
358 | edit-upload: {
359 | steps: [
360 | {do:"as-modal", steps:[
361 | {do:"with-attachment", steps:[
362 | {
363 | do:"edit",
364 | options:[
365 | "endpoint:/{{.ObjectID}}/edit-upload?attachmentId={{.AttachmentID}}",
366 | "delete:/{{.ObjectID}}/delete-upload?attachmentId={{.AttachmentID}}"
367 | ]
368 | form:{
369 | type:"layout-vertical"
370 | label:"Edit Attachment",
371 | children:[
372 | {type:"text", path:"label", label:"Label"}
373 | {type:"textarea", path:"description", label:"Description"}
374 | ]
375 | }
376 | }
377 | {do:"save"}
378 | {do:"refresh-page"}
379 | ]}
380 | ]}
381 | ]
382 | }
383 |
384 | delete-upload: {
385 | steps: [
386 | {do:"as-confirmation", title:"Delete this Attachment?", message:"Are you sure you want to delete this attachment? There is NO UNDO.", submit:"DELETE"}
387 | {do:"delete-attachments"}
388 | {do:"refresh-page"}
389 | ]
390 | }
391 |
392 | publish: {
393 | roles: ["author"]
394 | steps: [
395 | {do:"as-modal", steps:[
396 | {do:"if", condition:"{{eq `true` (.QueryParam `republish`)}}",
397 | then:[
398 | {
399 | do: edit
400 | form: {
401 | type: layout-vertical
402 | label: Re-Publish this Album?
403 | description: "If you've made changes to this album, you can send updates to all distribution channels.",
404 | children:[
405 | {type:"select", path:"data.license", label:"License", options:{required:true, provider:"bandwagon-album.licenses"}}
406 | {type:"select", path:"isFeatured", label:"Feature on Home Page", options:{enum:[{value:"false", label:"NO. Do not show on my home page"}, {value:"true", label:"YES. Show on my home page"}]}}
407 | {type:"multiselect", path:"syndication", label:"Distribute to Streaming Stations", description:"Share this album with indie streaming services", options:{provider:"syndication-targets"}}
408 | ]
409 | },
410 | options:["submit-label:Publish Album"]
411 | }
412 | ],
413 | else:[
414 | {
415 | do: edit
416 | form: {
417 | type: layout-vertical
418 | label: Publish this Album?
419 | children:[
420 | {type:"select", path:"data.license", label:"License", options:{required:true, provider:"bandwagon-album.licenses"}}
421 | {type:"select", path:"isFeatured", label:"Feature on Home Page", options:{enum:[{value:"false", label:"NO. Do not show on my home page"}, {value:"true", label:"YES. Show on my home page"}]}}
422 | {type:"multiselect", path:"syndication", label:"Distribute to Streaming Stations", description:"Share this album with indie streaming services.", options:{provider:"syndication-targets"}}
423 | ]
424 | },
425 | options:["submit-label:Publish Album"]
426 | }
427 | ]}
428 | {do:"set-state", state:"published"}
429 | {do:"save-and-publish", outbox:"true", republish:true}
430 | {do:"search-index"}
431 | {do:"make-archive", token:"FULL-ALBUM", depth:1, json:true, attachments:true, metadata: [
432 |
433 | // metadata for album attachments
434 | [],
435 |
436 | // metadata for song attachments
437 | [
438 | {target:"cover", path:"parent.iconUrl"},
439 | {target:"comment", path:"parent.attributedTo.profileUrl"}
440 | {target:"title", path:"stream.label"},
441 | {target:"author", path:"parent.attributedTo.name"}
442 | {target:"artist", path:"parent.attributedTo.name"}
443 | {target:"album_artist", path:"parent.attributedTo.name"},
444 | {target:"album", path:"parent.label"},
445 | {target:"composer", path:"stream.data.composer"},
446 | {target:"producer", path:"stream.data.producer"},
447 | {target:"publisher", path:"stream.data.publisher"},
448 | {target:"year", path:"parent.data.year"},
449 | {target:"track", path:"stream.rank"},
450 | {target:"genre", path:"stream.data.genre"}
451 | {target:"copyright", path:"parent.data.license"},
452 | {target:"lyrics", path:"stream.data.lyrics"},
453 | {target:"TISRC", path:"stream.data.isrc"},
454 | ]
455 | ]}
456 | {do:"refresh-page"}
457 | ]}
458 | ]
459 | }
460 |
461 | unpublish: {
462 | roles: ["author"]
463 | steps: [
464 | {do:"as-modal", steps:[
465 | {do:"view-html"}
466 | /*{do:"as-confirmation", icon:"check", title:"Published to Internet", message:"This album has been published and is visible to everyone online. You can un-publish it if you want to hide it. You'll still be able to edit and make changes to it, but others will not see it.", submit:"Un-Publish Album"} */
467 | {do:"set-state", state:"unpublished"}
468 | {do:"unpublish", outbox:"true"}
469 | {do:"search-index"}
470 | {do:"delete-archive", token:"FULL-ALBUM"}
471 | {do:"refresh-page"}
472 | ]}
473 | ]
474 | }
475 |
476 | icon: {
477 | steps: [
478 | {do:"redirect-to", url:"{{.IconURL}}"}
479 | ]
480 | }
481 |
482 | zip9348103752: {
483 | steps: [
484 | {do:"get-archive", token:"FULL-ALBUM", depth:1, json:true, attachments:true, metadata: [
485 |
486 | // metadata for album attachments
487 | [],
488 |
489 | // metadata for song attachments
490 | [
491 | {target:"cover", path:"parent.iconUrl"},
492 | {target:"comment", path:"parent.attributedTo.profileUrl"}
493 | {target:"title", path:"stream.label"},
494 | {target:"author", path:"parent.attributedTo.name"}
495 | {target:"artist", path:"parent.attributedTo.name"}
496 | {target:"album_artist", path:"parent.attributedTo.name"},
497 | {target:"album", path:"parent.label"},
498 | {target:"composer", path:"stream.data.composer"},
499 | {target:"producer", path:"stream.data.producer"},
500 | {target:"publisher", path:"stream.data.publisher"},
501 | {target:"year", path:"parent.data.year"},
502 | {target:"track", path:"stream.rank"},
503 | {target:"genre", path:"stream.data.genre"}
504 | {target:"copyright", path:"parent.data.license"},
505 | {target:"lyrics", path:"stream.data.lyrics"},
506 | {target:"TISRC", path:"stream.data.isrc"},
507 | ]
508 | ]}
509 | ]
510 | }
511 | }
512 | }
--------------------------------------------------------------------------------
/bandwagon-album/unpublish.html:
--------------------------------------------------------------------------------
1 | {{icon "check"}} Published Album
2 |
3 |
4 | This album has been published and is visible to everyone online.
5 |
6 |
7 | If you've made changes, you can re-publish to streaming partners to send those updates to them.
8 |
9 |
10 | You can un-publish it if you want to hide it. You'll still be able to edit and make changes to it, but others will not see it.
11 |
12 |
13 | Un-Publish Album
14 | Cancel
--------------------------------------------------------------------------------
/bandwagon-album/upload.html:
--------------------------------------------------------------------------------
1 |
2 |
{{icon "upload"}} Attach a File
3 |
Attach files to this album, like liner notes or album artwork . Attached files are public, and downloadable by anyone online.
4 |
--------------------------------------------------------------------------------
/bandwagon-album/view.html:
--------------------------------------------------------------------------------
1 | {{- $streamID := .StreamID -}}
2 | {{- $tags := .Tags -}}
3 | {{- $songs := .Children.Top60.ByRank.Slice -}}
4 | {{- $canEdit := .UserCan "edit" -}}
5 | {{- $isPlayable := false -}}
6 | {{- $links := .Data "links" -}}
7 | {{- range $index, $song := $songs -}}
8 | {{- if ne nil $song.Data.attachmentId -}}
9 | {{- $isPlayable = true -}}
10 | {{- end -}}
11 | {{- end -}}
12 | {{- $checkoutLabel := first (.Data "checkout label") "Buy Now" -}}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{- $year := .Data "year" -}}
34 | {{- range $index, $song := $songs -}}
35 |
36 |
37 | {{- end -}}
38 |
39 | {{- if not .IsPublished -}}
40 | {{- if $songs.NotEmpty -}}
41 |
42 | {{icon "check-circle"}}
43 |
44 | This album IS READY TO PUBLISH on the social web.
45 | Click here to publish it now
46 |
47 | {{- end -}}
48 | {{- end -}}
49 |
50 |
51 |
52 |
53 |
54 | {{- if ne "" .IconURL -}}
55 |
56 | {{- else if $canEdit -}}
57 |
58 | Click here to add album art
59 |
60 | {{- end -}}
61 |
62 |
63 |
64 |
65 | Album Name:
66 | {{.Label}}
67 |
68 |
73 |
74 | {{- if .IsPublished -}}
75 |
76 |
77 |
78 |
95 |
96 |
97 | {{icon "share"}} Share
98 | {{icon "thumbs-up"}} Like
99 | {{icon "more-horizontal"}}
100 |
101 | {{- end -}}
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | {{- if $songs.NotEmpty -}}
112 |
113 |
179 |
180 | {{- else if $canEdit -}}
181 |
182 |
183 |
184 |
{{icon "music"}}
185 |
186 |
187 | Add the first song to this album
188 |
189 |
You'll need to add at least one song to this album before you can publish it.
190 |
191 |
192 |
193 | {{- end -}}
194 |
195 |
196 |
197 |
198 |
199 |
200 | {{- if $tags.NotEmpty -}}
201 |
202 | {{- range $index, $tag := $tags -}}
203 | {{- if eq "Hashtag" $tag.Type}}
204 |
#{{$tag.Name}}
205 | {{- end -}}
206 | {{- end -}}
207 |
208 | {{- end -}}
209 |
210 | {{- if ne "" .Summary -}}
211 |
212 |
Album Notes
213 | {{.Summary | markdown}}
214 |
215 | {{- end -}}
216 |
217 | {{- if $links.NotEmpty -}}
218 |
219 |
Stream Online
220 | {{- range $key, $value := $links -}}
221 |
222 | {{- if ne "" $value -}}
223 | {{- $icon := "music-note-beamed" -}}
224 | {{- $label := $value -}}
225 |
226 | {{- if eq "AMAZON" $key -}}
227 | {{- $label = "Amazon Music" -}}
228 | {{- $icon = "amazon" -}}
229 |
230 | {{- else if eq "APPLE" $key -}}
231 | {{- $icon = "apple" -}}
232 | {{- $label = "Apple Music" -}}
233 |
234 | {{- else if eq "BANDCAMP" $key -}}
235 | {{- $label = "Bandcamp" -}}
236 |
237 | {{- else if eq "GOOGLE" $key -}}
238 | {{- $label = "Google Play" -}}
239 | {{- $icon = "google" -}}
240 |
241 | {{- else if eq "IHEARTRADIO" $key -}}
242 | {{- $label = "iHeartRadio" -}}
243 |
244 | {{- else if eq "PANDORA" $key -}}
245 | {{- $label = "Pandora" -}}
246 |
247 | {{- else if eq "SOUNDCLOUD" $key -}}
248 | {{- $label = "SoundCloud" -}}
249 | {{- $icon = "cloud" -}}
250 |
251 | {{- else if eq "SPOTIFY" $key -}}
252 | {{- $label = "Spotify" -}}
253 | {{- $icon = "spotify" -}}
254 |
255 | {{- else if eq "TIDAL" $key -}}
256 | {{- $label = "Tidal" -}}
257 |
258 | {{- else if eq "YOUTUBE" $key -}}
259 | {{- $label = "YouTube Music" -}}
260 | {{- $icon = "youtube" -}}
261 |
262 | {{- else -}}
263 | {{- $label = domainOnly $value -}}
264 |
265 | {{- end -}}
266 |
267 |
268 | {{ $label }}
269 |
270 | {{- end -}}
271 |
272 | {{- end -}}
273 |
274 | {{- end -}}
275 |
276 | {{- $uploads := .AttachmentsByCategory "other" -}}
277 | {{- if or $uploads.NotEmpty $canEdit -}}
278 |
279 |
280 |
Other Files
281 |
282 |
283 | {{- if $canEdit -}}
284 |
285 |
Attachments like liner notes or album art that anyone can access.
286 |
{{icon "upload"}} Attach a File
287 |
288 | {{- end -}}
289 |
290 | {{- if $uploads.NotEmpty -}}
291 |
292 | {{- range $index, $upload := $uploads -}}
293 |
294 |
298 | {{- if $canEdit -}}
299 |
300 | {{icon "more-horizontal"}}
301 |
302 | {{- end -}}
303 |
304 | {{- end -}}
305 |
306 | {{- end -}}
307 |
308 | {{- end -}}
309 |
310 |
311 | {{- $license := .DataString "license"}}
312 | {{- if eq "COPYRIGHT" $license -}}
313 |
License
314 |
315 | © Copyright
316 | {{- if ne nil (.Data "year") }}
317 | {{.Data "year"}}.
318 | {{- else -}}
319 | .
320 | {{- end }}
321 | All Rights Reserved.
322 |
323 | {{- else if ne "" $license -}}
324 | {{- $licenses := .Dataset "bandwagon-album.licenses" -}}
325 | {{- $licenseInfo := $licenses.Value $license -}}
326 |
License
327 |
336 | {{- end -}}
337 |
338 |
339 |
340 |
341 |
342 |
343 | {{- if $songs.NotEmpty -}}
344 |
345 |
346 |
347 |
348 |
349 |
350 |
362 |
363 | {{- if $canEdit -}}
364 |
365 |
366 |
367 |
374 |
375 |
386 | {{- end -}}
387 |
388 | {{- end -}}
389 |
390 |
391 | {{- if $canEdit -}}
392 |
Edit Album
393 |
396 |
397 | {{- if .IsPublished -}}
398 |
{{icon "check"}} Published
399 | {{- else if $songs.NotEmpty -}}
400 |
Publish
401 | {{- end -}}
402 | {{- end -}}
403 |
404 | {{- if .UserCan "zip" -}}
405 |
Download
406 | {{- end -}}
407 |
408 |
409 |
410 |
411 | {{- if $canEdit -}}
412 |
413 |
414 |
415 |
416 |
419 |
420 | {{- end -}}
421 |
--------------------------------------------------------------------------------
/bandwagon-common/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-common
3 | templateRole: album
4 | containedBy: []
5 | extends: []
6 | label: Bandwagon Common
7 | description: Common elements for Bandwagon apps
8 |
9 | datasets: {
10 | "licenses": [
11 | {value:"COPYRIGHT", label:"All Rights Reserved", icon:"copyright"},
12 | {value:"CC-BY", label:"Creative Commons - Attribution", href:"https://creativecommons.org/licenses/by/4.0/", icon:"cc-by"}
13 | {value:"CC-BY-NC", label:"Creative Commons - Attribution - Non Commercial", href:"https://creativecommons.org/licenses/by-nc/4.0/", icon:"cc-by-nc"}
14 | {value:"CC-BY-NC-ND", label:"Creative Commons - Attribution - Non Commercial - No Derivatives", href:"https://creativecommons.org/licenses/by-nc-nd/4.0/", icon:"cc-by-nc-nd"}
15 | {value:"CC-BY-NC-SA", label:"Creative Commons - Attribution - Non Commercial - Share Alike", href:"https://creativecommons.org/licenses/by-nc-sa/4.0/", icon:"cc-by-nc-sa"}
16 | {value:"CC-BY-ND", label:"Creative Commons - Attribution - No Derivatives ", href:"https://creativecommons.org/licenses/by-nd/4.0/", icon:"cc-by-nd"}
17 | {value:"CC-BY-SA", label:"Creative Commons - Attribution - Share Alike", href:"https://creativecommons.org/licenses/by-sa/4.0/", icon:"cc-by-sa"}
18 | {value:"CC0", label:"Creative Commons Zero - No Rights Reserved", href:"https://creativecommons.org/public-domain/cc0/", icon:"cc0"},
19 | {value:"PUBLIC-DOMAIN", label:"Public Domain", href:"https://creativecommons.org/publicdomain/mark/1.0/", icon:"public-domain"}
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/bandwagon-event/edit.html:
--------------------------------------------------------------------------------
1 | {{icon "calendar"}} {{.Label}}
--------------------------------------------------------------------------------
/bandwagon-event/stylesheet.html:
--------------------------------------------------------------------------------
1 | {{- $artist := .ParentOutbox "view" -}}
2 | {{- $eventColors := .Data "color" -}}
3 |
4 | {{- $bodyColor := $eventColors.body -}}
5 | {{- $pageColor := $eventColors.page -}}
6 | {{- $buttonColor := $eventColors.button -}}
7 |
8 | body {
9 |
10 | {{ if ne nil $bodyColor -}}
11 | --body-background: {{$bodyColor}};
12 | {{- end }}
13 |
14 | {{ if ne nil $pageColor -}}
15 | {{- $pageColorParsed := parseColor $pageColor -}}
16 | --page-background: {{$pageColorParsed}};
17 | --page-border: {{$pageColorParsed.Darken 10}};
18 | --heading-color: {{$pageColorParsed.Text}};
19 | --text-color: {{$pageColorParsed.TextExt}};
20 |
21 | {{if $pageColorParsed.IsLight }}
22 | --white: rgb(255, 255, 255);
23 | --gray00: rgb(255, 255, 255);
24 | --gray01: rgba(0, 0, 0, 0.01);
25 | --gray02: rgba(0, 0, 0, 0.02);
26 | --gray03: rgba(0, 0, 0, 0.03);
27 | --gray04: rgba(0, 0, 0, 0.04);
28 | --gray05: rgba(0, 0, 0, 0.05);
29 | --gray10: rgba(0, 0, 0, 0.10);
30 | --gray15: rgba(0, 0, 0, 0.15);
31 | --gray20: rgba(0, 0, 0, 0.20);
32 | --gray30: rgba(0, 0, 0, 0.30);
33 | --gray40: rgba(0, 0, 0, 0.40);
34 | --gray50: rgba(0, 0, 0, 0.50);
35 | --gray60: rgba(0, 0, 0, 0.60);
36 | --gray70: rgba(0, 0, 0, 0.70);
37 | --gray80: rgba(0, 0, 0, 0.80);
38 | --gray90: rgba(0, 0, 0, 0.90);
39 | --gray100: rgb(0, 0, 0);
40 | --black: rgb(0, 0, 0);
41 | {{- else }}
42 | --white: rgb(0, 0, 0) ;
43 | --gray00: rgb(0, 0, 0) ;
44 | --gray01: rgba(255, 255, 255, 0.01);
45 | --gray02: rgba(255, 255, 255, 0.02);
46 | --gray03: rgba(255, 255, 255, 0.03);
47 | --gray04: rgba(255, 255, 255, 0.04);
48 | --gray05: rgba(255, 255, 255, 0.05);
49 | --gray10: rgba(255, 255, 255, 0.10);
50 | --gray15: rgba(255, 255, 255, 0.15);
51 | --gray20: rgba(255, 255, 255, 0.20);
52 | --gray30: rgba(255, 255, 255, 0.30);
53 | --gray40: rgba(255, 255, 255, 0.40);
54 | --gray50: rgba(255, 255, 255, 0.50);
55 | --gray60: rgba(255, 255, 255, 0.60);
56 | --gray70: rgba(255, 255, 255, 0.70);
57 | --gray80: rgba(255, 255, 255, 0.80);
58 | --gray90: rgba(255, 255, 255, 0.90);
59 | --gray100: rgb(255, 255, 255);
60 | --black: rgb(255, 255, 255);
61 | {{- end -}}
62 |
63 | {{- end }}
64 |
65 | {{ if ne nil $buttonColor -}}
66 | {{- $buttonColorParsed := parseColor $buttonColor -}}
67 | {{- $buttonText := $buttonColorParsed.Text }}
68 | {{- $buttonColorHover := ($buttonColorParsed.Lighten 10) -}}
69 | {{- $buttonTextHover := $buttonColorHover.Text }}
70 |
71 | --button-primary-background: {{$buttonColor}};
72 | --button-primary-background-hover: {{$buttonColorHover}};
73 | --button-primary-color: {{$buttonText}};
74 | --button-primary-color-hover: {{$buttonTextHover}};
75 | --link-color: {{$buttonColor}};
76 | --link-color-hover: {{$buttonColorHover}};
77 | {{- end }}
78 |
79 | }
80 |
81 | {{ $artist.Data "stylesheet"}}
--------------------------------------------------------------------------------
/bandwagon-event/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-event
3 | templateRole: event
4 | model: Stream
5 | containedBy: ["outbox"]
6 | extends: ["base-intent"]
7 | icon: calendar
8 | label: Event
9 | description: Contains event details
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string", required:true}
14 | summary: {type:"string"}
15 | iconUrl: {type:"string", format:"url"}
16 | startDate: {type:"object", properties: {
17 | date: {type:"string", format:"date"}
18 | time: {type:"string", format:"time"}
19 | }}
20 | places: {
21 | type:"array",
22 | items: {
23 | type:"object"
24 | properties: {
25 | "name": {type:"string"}
26 | "fullAddress": {type:"string"}
27 | "latitude": {type:"number"}
28 | "longitude": {type:"number"}
29 | }
30 | }
31 | }
32 | data: {
33 | type:object
34 | properties: {
35 | tags: {type:"string"}
36 | bannerUrl: {type:"string", format:"url"}
37 | flyerUrl: {type:"string", format:"url"}
38 | city: {type:"string"}
39 | venue: {type:"string"}
40 | websiteLabel: {type:"string"}
41 | website: {type:"string", format:"url"}
42 | color: {type:"object", properties: {
43 | body: {type:"string", format:"color"},
44 | page: {type:"string", format:"color"},
45 | button: {type:"string", format:"color"}
46 | }}
47 | }
48 | }
49 | }
50 | }
51 |
52 | socialRole: Event
53 | socialRules: [
54 | {target:"type", value:["Event","Article"]}
55 | {target:"name", expression:"{{.Data.city}} {{.Data.date}}"},
56 | {target:"summary", expression:"{{.Data.venue}}"},
57 | ]
58 |
59 | search: {
60 | type: "Event"
61 | iconUrl:"{{.IconURL}}"
62 | text: "{{.Label}} {{.AttributedTo.Name}} {{index .Data `year`}}"
63 | }
64 |
65 | tagPaths: ["data.tags"]
66 |
67 | actions: {
68 | create: {
69 | roles:["author"]
70 | steps: [
71 | {do:"as-modal", steps: [
72 | {do:"set-data", from-url:["isFeatured"]}
73 | {
74 | do:edit
75 | form: {
76 | label:"+ Add a Show"
77 | type:layout-tabs
78 | children: [
79 | {
80 | label: Show Date
81 | type:layout-vertical
82 | children: [
83 | {type:"text", path:"label", label:"Name"}
84 | {type:"text", path:"data.tags", label:"Tags", description:"Enter #Hashtags separated by spaces."}
85 | {type:"date", path:"startDate.date", label:"Date"}
86 | {type:"time", path:"startDate.time", label:"Start Time"}
87 | {type:"toggle", path:"isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured"}}
88 | ]
89 | }
90 | {
91 | label: Description
92 | type:layout-vertical
93 | children: [
94 | {type:"textarea", path:"summary", description:"Shows on event details. Markdown is okay.", options:{rows:10}}
95 | ]
96 | }
97 | {
98 | label: Venue
99 | type:layout-vertical
100 | children: [
101 | {type:"text", path:"place.0.name", label:"Venue Name", description:"Just a label. Shows on event cards"}
102 | {type:"text", path:"data.city", label:"City/State", description:"Just a label. Shows on event cards"}
103 | {type:"textarea", path:"places.0.fullAddress", label:"Physical Address", description:"Use an accurate address. Used to generate maps."}
104 | ]
105 | }
106 | {
107 | label: Tickets
108 | type:layout-vertical
109 | children: [
110 | {type:"text", path:"data.website", label:"Ticketing Website", description: "Website where fans can buy tickets"}
111 | {type:"text", path:"data.websiteLabel", label:"Button Text", description:"The label to display on your website", options:{placeholder:"Get Tickets"}}
112 | ]
113 | }
114 | {
115 | type:layout-vertical
116 | label: Colors
117 | children: [
118 | {type:"colorpicker", path:"data.color.body", label:"Window Background"}
119 | {type:"colorpicker", path:"data.color.page", label:"Page Background"}
120 | {type:"colorpicker", path:"data.color.button", label:"Links and Buttons"}
121 | ]
122 | }
123 | {
124 | label: Uploads
125 | type:layout-vertical
126 | children: [
127 | {type:"upload", path:"iconUrl", label:"Card Image", description:"Shows on event cards. Square recommended.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-icon"}}
128 | {type:"upload", path:"data.bannerUrl", label:"Banner Image", description:"Shows on top of event page. Optional.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-image"}}
129 | {type:"upload", path:"data.flyerUrl", label:"Flyer", options:{delete:"/{{.StreamID}}/delete-flyer"}}
130 | ]
131 | }
132 | ]
133 | }
134 | }
135 | ]}
136 |
137 | {do:"upload-attachments", category:"icon", fieldname:"iconUrl", download-path:"iconUrl", rules:{width:800, height:800, types:["webp"]}}
138 | {do:"upload-attachments", category:"image", fieldname:"data.bannerUrl", download-path:"data.bannerUrl", rules:{}}
139 | {do:"upload-attachments", category:"flyer", fieldname:"data.flyerUrl", download-path:"data.flyerUrl", rules:{}}
140 |
141 | {do:"process-tags", paths:"data.tags"}
142 | {do:"save", outbox:"true"}
143 | {do:"forward-to", url:"/{{.StreamID}}"}
144 | ]
145 | }
146 | view: {do:"view-html"}
147 | edit: {
148 | roles: ["author"]
149 | steps: [
150 | {do:"as-modal", steps: [
151 | {do:"view-html"}
152 | {
153 | do:edit
154 | options:["delete:/{{.StreamID}}/delete"]
155 | form: {
156 | type:layout-tabs
157 | children: [
158 | {
159 | label: Show Date
160 | type:layout-vertical
161 | children: [
162 | {type:"text", path:"label", label:"Name"}
163 | {type:"text", path:"data.tags", label:"Tags", description:"Enter #Hashtags separated by spaces."}
164 | {type:"date", path:"startDate.date", label:"Date"}
165 | {type:"time", path:"startDate.time", label:"Start Time"}
166 | {type:"toggle", path:"isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured"}}
167 | ]
168 | }
169 | {
170 | label: Description
171 | type:layout-vertical
172 | children: [
173 | {type:"textarea", path:"summary", description:"Shows on event details. Markdown is okay.", options:{rows:10}}
174 | ]
175 | }
176 | {
177 | label: Venue
178 | type:layout-vertical
179 | children: [
180 | {type:"text", path:"places.0.name", label:"Venue Name", description:"Just a label. Shows on event cards"}
181 | {type:"text", path:"data.city", label:"City/State", description:"Just a label. Shows on event cards"}
182 | {type:"textarea", path:"places.0.fullAddress", label:"Physical Address", description:"Use an accurate address. Used to generate maps."}
183 | ]
184 | }
185 | {
186 | label: Tickets
187 | type:layout-vertical
188 | children: [
189 | {type:"text", path:"data.website", label:"Ticketing Website", description: "Website where fans can buy tickets"}
190 | {type:"text", path:"data.websiteLabel", label:"Button Text", description:"The label to display on your website", options:{placeholder:"Get Tickets"}}
191 | ]
192 | }
193 | {
194 | type:layout-vertical
195 | label: Colors
196 | children: [
197 | {type:"colorpicker", path:"data.color.body", label:"Window Background"}
198 | {type:"colorpicker", path:"data.color.page", label:"Page Background"}
199 | {type:"colorpicker", path:"data.color.button", label:"Links and Buttons"}
200 | ]
201 | }
202 | {
203 | label: Uploads
204 | type:layout-vertical
205 | children: [
206 | {type:"upload", path:"iconUrl", label:"Card Image", description:"Shows on event cards. Square recommended.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-icon"}}
207 | {type:"upload", path:"data.bannerUrl", label:"Banner Image", description:"Shows on top of event page. Optional.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-image"}}
208 | {type:"upload", path:"data.flyerUrl", label:"Flyer", options:{delete:"/{{.StreamID}}/delete-flyer"}}
209 | ]
210 | }
211 | ]
212 | }
213 | }
214 | ]}
215 |
216 | {do:"upload-attachments", category:"icon", fieldname:"iconUrl", download-path:"iconUrl", rules:{width:800, height:800, types:["webp"]}}
217 | {do:"upload-attachments", category:"image", fieldname:"data.bannerUrl", download-path:"data.bannerUrl", rules:{}}
218 | {do:"upload-attachments", category:"flyer", fieldname:"data.flyerUrl", download-path:"data.flyerUrl", rules:{}}
219 |
220 | {do:"process-tags", paths:"data.tags"}
221 | {do:"if", condition: "{{.IsPublished}}",
222 | then: [
223 | {do:"save-and-publish"}
224 | {do:"search-index"}
225 | ],
226 | else: [
227 | {do:"save"}
228 | ]
229 | }
230 | {do:"refresh-page"}
231 | ]
232 | }
233 |
234 | publish: {
235 | roles: ["author"]
236 | steps: [
237 | {do:"as-modal", steps:[
238 | {
239 | do: edit
240 | form: {
241 | type: layout-vertical
242 | label: Publish this Show?
243 | children:[
244 | {type:"toggle", path:"isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured"}}
245 | ]
246 | },
247 | options:["submit-label:Publish Show"]
248 | }
249 | {do:"set-state", state:"published"}
250 | {do:"save-and-publish", outbox:"true"}
251 | {do:"search-index"}
252 | {do:"refresh-page"}
253 | ]}
254 | ]
255 | }
256 |
257 | unpublish: {
258 | roles: ["author"]
259 | steps: [
260 | {do:"as-confirmation", title:"Un-Publish this Show?", message:"Un-Publishing this show will hide it everyone online. You'll still be able to edit and make changes to it, but others will not see it.", submit:"Un-Publish from Website"}
261 | {do:"set-state", state:"unpublished"}
262 | {do:"unpublish", outbox:"true"}
263 | {do:"search-index"}
264 | {do:"refresh-page"}
265 | ]
266 | }
267 |
268 | stylesheet: {
269 | steps:[
270 | {do:"view-css"}
271 | ]
272 | }
273 | delete: {
274 | roles: ["author"]
275 | steps: [
276 | {do:"delete"}
277 | {do:"unpublish", outbox:"true"}
278 | {do:"refresh-page"}
279 | ]
280 | }
281 |
282 | delete-icon: {
283 | roles: ["author"]
284 | steps: [
285 | {do:"delete-attachments", fieldname:"iconUrl"}
286 | {do:"save"}
287 | ]
288 | }
289 | delete-banner: {
290 | roles: ["author"]
291 | steps: [
292 | {do:"delete-attachments", fieldname:"data.bannerUrl"}
293 | {do:"save"}
294 | ]
295 | }
296 | delete-flyer: {
297 | roles: ["author"]
298 | steps: [
299 | {do:"delete-attachments", fieldname:"data.flyerUrl"}
300 | {do:"save"}
301 | ]
302 | }
303 | }
304 | }
--------------------------------------------------------------------------------
/bandwagon-event/view.html:
--------------------------------------------------------------------------------
1 | {{- $canEdit := .UserCan "edit" -}}
2 | {{- $ticketURL := .Data "website" -}}
3 | {{- $bannerURL := .Data "bannerUrl" -}}
4 | {{- $flyerURL := .Data "flyerUrl" -}}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {{- if not .IsPublished -}}
19 |
20 | {{icon "alert-fill"}}
21 | This show HAS NOT BEEN published yet and won't be seen by others.
22 | Click here to publish it
23 |
24 | {{- end -}}
25 |
26 | {{- if ne "" $bannerURL -}}
27 |
28 | {{- end -}}
29 |
30 |
31 |
32 |
33 | {{- if ne "" .IconURL -}}
34 |
35 |
36 |
37 | {{- end -}}
38 |
39 |
40 |
41 |
42 |
43 | {{- .StartDate | longDate -}}
44 | at {{ .StartDate | shortTime -}}
45 |
46 |
47 |
48 |
49 | {{- if ne "" $ticketURL -}}
50 | {{- $ticketLabel := .Data "websiteLabel" -}}
51 | {{- $ticketLabel := first $ticketLabel "Get Tickets" -}}
52 |
{{ $ticketLabel }}
53 | {{- end -}}
54 |
55 |
{{icon "share"}} Share
56 |
57 | {{- if ne nil $flyerURL -}}
58 |
{{icon "download"}} Download
59 | {{- end -}}
60 |
61 |
62 |
63 | {{- if ne "" .Summary -}}
64 |
65 |
{{.Summary | markdown}}
66 | {{- end -}}
67 |
68 | {{- if .Tags.NotEmpty -}}
69 |
70 |
75 | {{- end -}}
76 |
77 |
78 |
79 | {{- if .Places.NotEmpty -}}
80 |
81 | {{- $venue := .Places.First -}}
82 |
83 | {{- if $venue.NotEmpty -}}
84 |
85 | {{- $hasGeocode := $venue.HasGeocode -}}
86 |
87 |
88 |
89 |
90 |
91 | {{if $hasGeocode}}
92 |
93 |
94 | Maps ©
OpenStreetMap .
95 | Icon by
Icons8
96 |
97 |
98 | {{- end -}}
99 |
100 |
101 |
102 |
103 |
104 |
105 |
{{- $venue.FullAddress | text -}}
106 |
107 |
108 | {{if $hasGeocode}}
109 |
Open With:
110 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
157 |
158 |
159 |
160 | {{- end -}}
161 |
162 |
163 |
164 | {{- end -}}
165 |
166 | {{- end -}}
167 |
168 | {{- if $canEdit -}}
169 |
170 | Edit Show
171 | {{- if .IsPublished -}}
172 | Un-Publish
173 | {{- else -}}
174 | Publish
175 | {{- end -}}
176 |
177 |
178 |
181 | {{- end -}}
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/bandwagon-news/edit.html:
--------------------------------------------------------------------------------
1 | {{.Label}}
2 |
3 | Published: {{.PublishDate | shortDate}}
--------------------------------------------------------------------------------
/bandwagon-news/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-news
3 | model: Stream
4 | templateRole: Note
5 | label: News Item
6 | extends: ["base-intent"]
7 | containedBy: ["outbox"]
8 | schema: {
9 | type:object
10 | properties: {
11 | title: {type:"string"}
12 | summary: {type:"string"}
13 | content: {type:"object", properties:{
14 | "type": {type:"string"}
15 | "raw": {type:"string"}
16 | "html": {type:"string"}
17 | }}
18 | data: {type:"object", properties:{
19 | linkText: {type:"string"}
20 | bannerUrl: {type:"string", format:"url"}
21 | }}
22 | }
23 | }
24 |
25 | socialRole: Article
26 | socialRules: [
27 | {target:"content", expression:"{{.SummaryOrContent}}"}
28 | ]
29 |
30 | search: {
31 | type:Article
32 | iconUrl:""
33 | summary:""
34 | text: "{{.Label}} {{.AttributedTo.Name}} {{.Summary}} {{.Content.HTML}}"
35 | }
36 |
37 | tagPaths: ["summary", "content.html"]
38 |
39 | actions: {
40 |
41 | create: {
42 | roles: ["author"]
43 | steps: [
44 | {do:"as-modal", options:["class:large"], steps:[
45 | {do:"set-data", from-url:["isFeatured"]}
46 | {
47 | do:edit
48 | form: {
49 | type:layout-tabs
50 | label:"+ Add News"
51 | children: [
52 | {
53 | type:layout-vertical
54 | label: News
55 | children: [
56 | {type:"text", path: "label", label: "Title"}
57 | {type:"textarea", path: "summary", label: "Summary", description:"Shows on list pages.", options:{rows:5}}
58 | {type:"text", path:"data.linkText", label:"Link Text", description:"Text for the link to the full article.", options:{placeholder:"Read More"}}
59 | {type:"toggle", path: "isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured"}}
60 | ]
61 | }
62 | {
63 | type:layout-vertical
64 | label:"Content"
65 | children:[
66 | {type:"textarea", path: "content.raw", label: "Content", description:"Shows on news article page only. Markdown is okay.", options:{rows:12}}
67 | ]
68 | }
69 | {
70 | type:layout-vertical
71 | label:"Uploads"
72 | children:[
73 | {type:"upload", path:"iconUrl", label:"Card Image", description:"Shows on event cards. Square recommended.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-icon"}}
74 | {type:"upload", path:"data.bannerUrl", label:"Banner Image", description:"Shows on top of event page. Optional.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-image"}}
75 | ]
76 | }
77 | ]
78 | }
79 | }
80 |
81 | {do:"upload-attachments", category:"icon", fieldname:"iconUrl", download-path:"iconUrl", rules:{width:800, height:800, types:["webp"]}}
82 | {do:"upload-attachments", category:"image", fieldname:"data.bannerUrl", download-path:"data.bannerUrl", rules:{}}
83 |
84 | {do:"edit-content", field:"content.raw", format:"MARKDOWN"}
85 | {do:"process-tags", paths:"data.tags"}
86 | {do:"save"}
87 | {do:"search-index"}
88 | {do:"forward-to", url:"/{{.StreamID}}"}
89 | ]}
90 | ]
91 | }
92 |
93 | edit: {
94 | roles: ["author"]
95 | steps: [
96 | {do:"as-modal", options:["class:large"], steps:[
97 | {do:"view-html"}
98 | {
99 | do:"edit"
100 | options: ["delete:/{{.StreamID}}/delete"]
101 | form: {
102 | type:layout-tabs
103 | children: [
104 | {
105 | type:layout-vertical
106 | label: News
107 | children: [
108 | {type:"text", path: "label", label: "Title"}
109 | {type:"textarea", path: "summary", label: "Summary", description:"Shows on list pages.", options:{rows:5}}
110 | {type:"text", path:"data.linkText", label:"Link Text", description:"Text for the link to the full article.", options:{placeholder:"Read More"}}
111 | {type:"toggle", path: "isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured"}}
112 | ]
113 | }
114 | {
115 | type:layout-vertical
116 | label:"Content"
117 | children:[
118 | {type:"textarea", path: "content.raw", label: "Content", description:"Shows on news article page only. Markdown is okay.", options:{rows:12}}
119 | ]
120 | }
121 | {
122 | type:layout-vertical
123 | label:"Uploads"
124 | children:[
125 | {type:"upload", path:"iconUrl", label:"Card Image", description:"Shows on event cards. Square recommended.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-icon"}}
126 | {type:"upload", path:"data.bannerUrl", label:"Banner Image", description:"Shows on top of event page. Optional.", options:{accept:"image/*", delete:"/{{.StreamID}}/delete-image"}}
127 | ]
128 | }
129 | ]
130 | }
131 | }
132 |
133 | {do:"upload-attachments", category:"icon", fieldname:"iconUrl", download-path:"iconUrl", rules:{width:800, height:800, types:["webp"]}}
134 | {do:"upload-attachments", category:"image", fieldname:"data.bannerUrl", download-path:"data.bannerUrl", rules:{}}
135 |
136 | {do:"edit-content", field:"content.raw", format:"MARKDOWN"}
137 | {do:"process-tags", paths:"data.tags"}
138 | {do:"if", condition: "{{.IsPublished}}",
139 | then: [
140 | {do:"save-and-publish"}
141 | {do:"search-index"}
142 | ],
143 | else: [
144 | {do:"save"}
145 | ]
146 | }
147 | {do:"refresh-page"}
148 | ]}
149 | ]
150 | }
151 |
152 | view: {
153 | steps:[
154 | {do:"view-html"}
155 | ]
156 | }
157 |
158 | delete: {
159 | roles: ["author"]
160 | steps: [
161 | {do:"delete"}
162 | {do:"unpublish", outbox:"true"}
163 | {do:"refresh-page"}
164 | ]
165 | }
166 |
167 | publish: {
168 | roles: ["author"]
169 | steps: [
170 | {do:"as-modal", steps:[
171 | {
172 | do: edit
173 | form: {
174 | type: layout-vertical
175 | label: Publish this News?
176 | children:[
177 | {type:"toggle", path:"isFeatured", options:{true-text:"Featured (shows on home page)", false-text:"Featured"}}
178 | ]
179 | },
180 | options:["submit-label:Publish"]
181 | }
182 | {do:"set-state", state:"published"}
183 | {do:"save-and-publish", outbox:"true"}
184 | {do:"search-index"}
185 | {do:"refresh-page"}
186 | ]}
187 | ]
188 | }
189 |
190 | unpublish: {
191 | roles: ["author"]
192 | steps: [
193 | {do:"as-confirmation", title:"Un-Publish this News?", message:"Un-Publishing this news will hide it everyone online. You'll still be able to edit and make changes to it, but others will not see it.", submit:"Un-Publish from Website"}
194 | {do:"set-state", state:"unpublished"}
195 | {do:"unpublish", outbox:"true"}
196 | {do:"search-index"}
197 | {do:"refresh-page"}
198 | ]
199 | }
200 |
201 | }
202 | }
--------------------------------------------------------------------------------
/bandwagon-news/view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{- if not .IsPublished -}}
14 |
15 | {{icon "alert-fill"}}
16 | This news HAS NOT BEEN published yet and won't be seen by others.
17 | Click here to publish it
18 |
19 | {{- end -}}
20 |
21 |
22 |
23 | {{- $bannerURL := .Data "bannerUrl" -}}
24 | {{- if ne "" $bannerURL -}}
25 |
26 | {{- end -}}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
{{.AttributedTo.Name}}
34 |
35 |
{{.PublishDate | shortDate}}
36 |
37 |
38 |
39 | {{icon "share"}} Share
40 | {{icon "thumbs-up"}} Like
41 |
42 |
{{.ContentHTML}}
43 |
44 |
45 | {{- if .UserCan "edit" -}}
46 |
47 | Edit News
48 | {{- if .IsPublished -}}
49 | Un-Publish
50 | {{- else -}}
51 | Publish
52 | {{- end -}}
53 |
54 |
55 |
58 | {{- end -}}
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/bandwagon-outbox/albums.html:
--------------------------------------------------------------------------------
1 | {{- $albums := (.Outbox.Where "templateId" "bandwagon-album").ByRankAlt.Slice -}}
2 | {{- $isMyself := .IsMyself -}}
3 | {{- $label := first (.Data "label-albums") "Albums" -}}
4 | {{- $description := .Data "description-albums" -}}
5 |
6 |
7 |
8 |
9 | {{- template "header" . -}}
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{.DisplayName}}
17 |
18 |
· {{$label}}
19 |
20 | {{- if $isMyself -}}
21 |
22 | {{icon "add"}} Album
23 | Edit
24 |
25 | {{- end -}}
26 |
27 |
28 |
29 | {{- if ne "" $description -}}
30 |
{{$description | markdown}}
31 | {{- end -}}
32 |
33 | {{- if $isMyself -}}
34 |
73 |
74 |
75 |
76 |
86 |
87 |
107 |
108 | {{- end -}}
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/bandwagon-outbox/coming-soon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Coming Soon
4 | {{.DisplayName}}
5 |
6 | This page is under construction. Please check back in the future.
7 |
8 |
--------------------------------------------------------------------------------
/bandwagon-outbox/events.html:
--------------------------------------------------------------------------------
1 | {{- $events := .Outbox.ByStartDate -}}
2 | {{- $events := $events.Where "templateId" "bandwagon-event" -}}
3 | {{- $showCurrent := ne "past" (.QueryParam "view") -}}
4 |
5 | {{- if $showCurrent -}}
6 | {{- $events = $events.WhereGT "startDate" today -}}
7 | {{- else -}}
8 | {{- $events = $events.WhereLT "startDate" today -}}
9 | {{- end -}}
10 |
11 | {{- $events := $events.Slice -}}
12 | {{- $root := . -}}
13 | {{- $isMyself := .IsMyself -}}
14 | {{- $label := first (.Data "label-events") "Shows" -}}
15 | {{- $description := .Data "description-events" -}}
16 |
17 |
18 |
19 | {{- template "header" . -}}
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{.DisplayName}}
27 |
28 |
· {{$label}}
29 |
30 | {{- if $isMyself -}}
31 |
32 | {{icon "add"}} Show
33 | Edit
34 |
35 | {{- end -}}
36 |
37 |
38 |
39 | {{- if ne "" $description -}}
40 |
{{$description | markdown}}
41 | {{- end -}}
42 |
43 |
44 | {{- if $showCurrent -}}
45 |
Upcoming Shows
46 |
Past
47 | {{- else -}}
48 |
Upcoming
49 |
Past Shows
50 | {{- end -}}
51 |
52 |
53 |
54 |
55 | {{- range $events -}}
56 |
57 | {{- $backgroundColor := .Data.color.page | string | parseColor -}}
58 | {{- $textColor := $backgroundColor.TextExt -}}
59 | {{- $venue := .Places.First -}}
60 |
61 |
89 |
90 |
91 | {{- if $isMyself -}}
92 |
93 | Edit Show
94 |
95 | {{- end -}}
96 |
97 |
98 | {{- end -}}
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/bandwagon-outbox/header.html:
--------------------------------------------------------------------------------
1 | {{- $isMyself := .IsMyself -}}
2 | {{- $showAlbums := eq "true" (.Data "show-albums") -}}
3 | {{- $showEvents := eq "true" (.Data "show-events") -}}
4 | {{- $showNews := eq "true" (.Data "show-news") -}}
5 | {{- $showShop := ne "" (.Data "shop-url") -}}
6 | {{- $hasBannerImage := ne "" .ImageURL -}}
7 | {{- $hasBannerColor := ne "" (.Data "color-banner") -}}
8 |
9 |
10 |
11 |
12 | {{- if or $hasBannerImage $hasBannerColor $isMyself -}}
13 |
14 | {{- if $isMyself -}}
15 |
16 |
17 | Edit Banner
18 |
19 | {{- end -}}
20 |
21 | {{- end -}}
22 |
23 |
--------------------------------------------------------------------------------
/bandwagon-outbox/news.html:
--------------------------------------------------------------------------------
1 | {{- $posts := .Outbox.Top60.ByPublishDate.Reverse -}}
2 | {{- $posts := $posts.Where "templateId" "bandwagon-news" -}}
3 | {{- $posts := $posts.Slice -}}
4 |
5 | {{- $isMyself := .IsMyself -}}
6 | {{- $label := first (.Data "label-news") "News" -}}
7 | {{- $description := .Data "description-news" -}}
8 |
9 |
10 |
11 | {{- template "header" . -}}
12 |
13 |
14 |
15 |
16 |
17 |
18 | {{.DisplayName}}
19 |
20 |
· {{$label}}
21 |
22 | {{- if $isMyself -}}
23 |
24 | {{icon "add"}} News
25 | Edit
26 |
27 | {{- end -}}
28 |
29 |
30 |
31 | {{- if ne "" $description -}}
32 |
{{$description | markdown}}
33 | {{- end -}}
34 |
35 | {{- if $posts.NotEmpty -}}
36 |
37 | {{- range $posts -}}
38 |
55 | {{- end -}}
56 |
57 |
58 | {{- end -}}
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/bandwagon-outbox/outbox.html:
--------------------------------------------------------------------------------
1 | {{- $isMyself := .IsMyself -}}
2 | {{- $username := .Username -}}
3 |
4 | {{- if eq .StateID "LIVE" -}}
5 |
6 | {{- $albums := .Outbox.Featured.Where "templateId" "bandwagon-album" -}}
7 | {{- $albums := $albums.By "data.releaseDate" -}}
8 | {{- $albums := $albums.Reverse.Slice -}}
9 |
12 |
13 |
14 |
15 |
16 |
{{.DisplayName}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {{- if not .IsPublic -}}
43 | {{- if $albums.NotEmpty -}}
44 |
45 | {{icon "check-circle"}}
46 | Your profile IS READY TO PUBLISH on the social web.
47 | Click here to publish it now
48 |
49 | {{- end -}}
50 | {{- end -}}
51 |
52 | {{- template "header" . -}}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
{{.DisplayName}}
64 | {{- if $isMyself -}}
65 |
66 | Edit Profile
67 | Edit Links
68 | Sign Out
69 |
70 | {{- end -}}
71 |
72 |
73 |
74 | @{{.Username}}@{{.Hostname}}
75 | {{- if ne "" .Location }}
76 | {{icon "location"}} {{.Location}}
77 | {{- end -}}
78 |
79 |
80 |
90 |
91 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | {{- if or $albums.NotEmpty $isMyself -}}
124 |
125 | {{- $albumLabel := first (.Data "label-albums") "Albums" -}}
126 |
127 |
145 |
146 |
147 | {{- if $albums.IsEmpty -}}
148 |
149 | Get started by uploading music to your bandwagon profile. Featured albums appear here.
150 |
151 | {{- end -}}
152 |
153 |
154 | {{- range $albums -}}
155 |
156 | {{- if or .IsPublished $isMyself -}}
157 |
158 |
182 | {{- end -}}
183 | {{- end -}}
184 |
185 | {{- if $albums.IsEmpty -}}
186 |
187 |
{{icon "cassette"}}
188 |
Next Step:
189 |
Add an Album
190 |
You can publish your profile after you've uploaded at least one album
191 |
192 | {{- end -}}
193 |
194 | {{- end -}}
195 |
196 |
197 |
198 | {{- $events := .Outbox.Featured.Where "templateId" "bandwagon-event" -}}
199 | {{- $events := $events.WhereGT "startDate" yesterday -}}
200 | {{- $events := $events.ByStartDate.Slice -}}
201 |
202 | {{- if or $events.NotEmpty $isMyself -}}
203 |
204 | {{- $eventLabel := first (.Data "label-events") "Shows" -}}
205 |
206 |
220 |
221 |
222 | {{- range $index, $event := $events -}}
223 |
259 | {{- end -}}
260 |
261 |
262 |
263 | {{- if $events.IsEmpty -}}
264 |
265 | Publish showtimes, locations, and ticketing websites on your event calendar. Featured events appear here.
266 |
267 | {{- end -}}
268 |
269 | {{- end -}}
270 |
271 |
272 |
273 | {{- $news := .Outbox.Featured.Where "templateId" "bandwagon-news" -}}
274 | {{- $news := $news.ByPublishDate.Reverse.Slice -}}
275 |
276 | {{- if or $news.NotEmpty $isMyself -}}
277 |
278 | {{- $newsLabel := first (.Data "label-news") "News" -}}
279 |
280 |
295 |
296 |
297 | {{- range $news -}}
298 | {{- if or .IsPublished $isMyself -}}
299 |
300 |
326 | {{- end -}}
327 | {{- end -}}
328 |
329 |
330 | {{- end -}}
331 |
332 | {{- if $news.IsEmpty -}}
333 |
334 | Publish updates to your followers. Featured items appear here.
335 |
336 | {{- end -}}
337 |
338 |
339 |
340 |
341 |
342 |
343 |
About {{.DisplayName}}
344 | {{- if $isMyself -}}
345 | Edit
346 | {{- end -}}
347 |
348 |
{{.StatusMessage | markdown}}
349 |
350 |
351 |
352 |
353 |
354 |
362 |
363 |
364 |
365 | {{- if $isMyself -}}
366 |
373 | {{- end -}}
374 |
375 |
376 |
377 |
378 |
379 | {{- else if not $isMyself -}}
380 | {{- template "coming-soon" . -}}
381 | {{- else if eq .StateID "WIZARD-STEP-3" -}}
382 | {{- template "wizard-3" . -}}
383 | {{- else if eq .StateID "WIZARD-STEP-2" -}}
384 | {{- template "wizard-2" . -}}
385 | {{- else -}}
386 | {{- template "wizard-1" . -}}
387 | {{- end -}}
--------------------------------------------------------------------------------
/bandwagon-outbox/stylesheet.html:
--------------------------------------------------------------------------------
1 | body {
2 |
3 | {{ if ne "" (.Data "background-body" )}}
4 | background-image:url("{{.Data "background-body"}}");
5 | background-repeat: no-repeat;
6 | background-size: cover;
7 | {{ end }}
8 |
9 | {{ if ne "" (.Data "color-body") -}}
10 | {{- $bodyColor := parseColor (.Data "color-body") -}}
11 | --body-background: {{.Data "color-body"}};
12 | --page-border: {{($bodyColor.Darken 10)}};
13 | {{- end }}
14 |
15 | {{ if ne "" (.Data "image-body" )}}
16 |
17 | {{ end }}
18 |
19 | {{ if ne "" (.Data "color-menu" )}}
20 | {{$menu := parseColor (.Data "color-menu")}}
21 | {{$menuHover := $menu.Contrast 15}}
22 | {{$menuSelected := $menu.Contrast 30}}
23 |
24 | --menu-background: {{$menu}};
25 | --menu-text: {{$menu.TextExt}};
26 | --menu-hover-background: {{$menuHover}};
27 | --menu-hover-text: {{$menuHover.Text}};
28 | --menu-selected-background: {{$menuSelected}};
29 | --menu-selected-text: {{$menuSelected.Text}};
30 | {{- end -}}
31 |
32 | {{ if ne "" (.Data "color-page") -}}
33 | {{- $pageColor := parseColor (.Data "color-page") -}}
34 | {{- $textColor := $pageColor.Text -}}
35 | --page-background: {{.Data "color-page"}};
36 | --heading-color: {{$pageColor.Text}};
37 | --text-color: {{$pageColor.TextExt}};
38 |
39 | {{if $pageColor.IsLight }}
40 | --white: #ffffff;
41 | --gray00: #ffffff;
42 | --gray01: #fefefe;
43 | --gray02: #fdfdfd;
44 | --gray03: #fcfcfc;
45 | --gray04: #fbfbfb;
46 | --gray05: #fafafa;
47 | --gray10: #f4f4f4;
48 | --gray15: #eaeaea;
49 | --gray20: #e0e0e0;
50 | --gray30: #c6c6c6;
51 | --gray40: #a8a8a8;
52 | --gray50: #8d8d8d;
53 | --gray60: #6f6f6f;
54 | --gray70: #525252;
55 | --gray80: #393939;
56 | --gray90: #262626;
57 | --gray100: #000000;
58 | --black: #000000;
59 | {{- else }}
60 | --black: #ffffff;
61 | --gray100: #ffffff;
62 | --gray90: #f7f3f2;
63 | --gray80: #e5e0df;
64 | --gray70: #cac5c4;
65 | --gray60: #a8a8a8;
66 | --gray50: #8f8b8b;
67 | --gray40: #726e6e;
68 | --gray30: #565151;
69 | --gray20: #3c3838;
70 | --gray15: #312e2e;
71 | --gray10: #272525;
72 | --gray05: #1f1c1c;
73 | --gray04: #1e1e1b;
74 | --gray03: #1c1c19;
75 | --gray02: #1a1a17;
76 | --gray01: #181815;
77 | --gray00: #171714;
78 | --white: #000000;
79 | {{- end -}}
80 |
81 | {{- end }}
82 |
83 | {{ if ne "" (.Data "color-button") -}}
84 | {{- $buttonColor := parseColor (.Data "color-button") -}}
85 | {{- $buttonText := $buttonColor.Text }}
86 | {{- $buttonColorHover := ($buttonColor.Lighten 10) -}}
87 | {{- $buttonTextHover := $buttonColorHover.Text }}
88 | {{- $buttonColor := .Data "color-button" -}}
89 |
90 | --button-primary-background: {{$buttonColor}};
91 | --button-primary-background-hover: {{$buttonColorHover}};
92 | --button-primary-color: {{$buttonText}};
93 | --button-primary-color-hover: {{$buttonTextHover}};
94 | --link-color: {{$buttonColor}};
95 | --link-color-hover: {{$buttonColorHover}};
96 | {{- end }}
97 |
98 | }
99 |
100 | {{- $bannerHex := first (.Data "color-banner") "#a0a0a0" -}}
101 | {{- $bannerColor := parseColor $bannerHex -}}
102 |
103 | #profile-banner {
104 | background-color: {{$bannerColor}};
105 | height:256px;
106 | background-image:url('{{.ImageURL}}');
107 | background-size:cover;
108 | background-position:center;
109 | }
110 |
111 | #profile-menu {
112 | background-color: var(--menu-background);
113 | font-size: 1.25em;
114 | text-align: center;
115 | }
116 |
117 | #profile-menu > [role=menuitem] {
118 | display: inline-block;
119 | padding: var(--rhythm) calc(var(--rhythm) * 1.5) ;
120 | color: var(--menu-text);
121 | }
122 |
123 | #profile-menu > [role=menuitem]:hover {
124 | background-color: var(--menu-hover-background);
125 | color: var(--menu-hover-text);
126 | }
127 |
128 | #profile-menu > [role=menuitem][aria-selected=true],
129 | #profile-menu > [role=menuitem][aria-selected=true]:hover {
130 | background-color: var(--menu-selected-background);
131 | color: var(--menu-selected-text);
132 | }
133 |
134 |
135 | {{.Data "stylesheet" | css }}
136 |
--------------------------------------------------------------------------------
/bandwagon-outbox/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-outbox
3 | templateRole: user-outbox
4 | model: User
5 | label: Bandwagon Outbox
6 | extends: ["user-outbox"]
7 | schema: {
8 | type: object
9 | properties: {
10 | statusMessage: {type:"string"}
11 | location: {type:"string"}
12 | data: {
13 | type: object
14 | wildcard: string
15 | properties: {
16 | tags: {type:"string"}
17 | show-albums: {type:"string"}
18 | show-events: {type:"string"}
19 | show-news: {type:"string"}
20 | shop-url: {type:"string", format:"url"}
21 | label-albums: {type:"string", default:"Albums"}
22 | label-events: {type:"string", default:"Events"}
23 | label-news: {type:"string", default:"News"}
24 | label-shop: {type:"string", default:"Shop"}
25 | description-albums: {type:"string"}
26 | description-events: {type:"string"}
27 | description-news: {type:"string"}
28 | background-body: {type:"string", format:"uri"}
29 | stylesheet: {type:"string"}
30 | color-body: {type:"string", format:"color"}
31 | color-banner: {type:"string", format:"color"}
32 | color-menu: {type:"string", format:"color"}
33 | color-page: {type:"string", format:"color"}
34 | color-button: {type:"string", format:"color"}
35 | }
36 | }
37 | }
38 | }
39 |
40 | tagPaths: ["data.tags"]
41 |
42 | actions: {
43 |
44 | create: {
45 | steps:[
46 | {
47 | do:"set-data",
48 | values:{
49 | "data.label-albums":"Albums"
50 | "data.label-events":"Events"
51 | "data.label-news":"News"
52 | "data.label-shop":"Shop"
53 | }
54 | }
55 | ]
56 | }
57 |
58 | view: {do:"view-html", file:"outbox"}
59 | albums: {do:"view-html"}
60 | events: {do:"view-html"}
61 | news: {do:"view-html"}
62 |
63 | add-album: {
64 | roles: ["self"]
65 | steps: [
66 | {
67 | do: add-stream
68 | style: inline
69 | location: outbox
70 | template: bandwagon-album
71 | label: + Add Album
72 | set-permissions: {copy:true, editor:["self"]}
73 | with-data:{data.tags:"{{.Data `tags`}}"}
74 | }
75 | {do:"set-data", values:{"data.show-albums":"true"}}
76 | {do:"save"}
77 | {do:"refresh-page"}
78 | ]
79 | }
80 |
81 | add-event: {
82 | roles: ["self"]
83 | steps: [
84 | {
85 | do: add-stream
86 | style: inline
87 | location: outbox
88 | template: bandwagon-event
89 | title: + Add a Song
90 | set-permissions: {copy:true, editor:["self"]}
91 | with-data:{data.tags:"{{.Data `tags`}}"}
92 | }
93 | {do:"set-data", values:{"data.show-events":"true"}}
94 | {do: "save"}
95 | {do: "refresh-page"}
96 | ]
97 | }
98 |
99 | add-news: {
100 | roles: ["self"]
101 | steps: [
102 | {
103 | do: add-stream
104 | style: inline
105 | location: outbox
106 | template: bandwagon-news
107 | title: + Add News
108 | set-permissions: {copy:true, editor:["self"]}
109 | }
110 | {do:"set-data", values:{"data.show-news":"true"}}
111 | {do: "save"}
112 | {do: "refresh-page"}
113 | ]
114 | }
115 |
116 | edit: {
117 | roles: ["self"]
118 | steps: [
119 | {do: "as-modal", background:"view", steps:[
120 | {
121 | do: "edit"
122 | form:{
123 | label: Edit Profile
124 | type:layout-tabs
125 | children: [
126 | {
127 | type: layout-vertical
128 | label: Basics
129 | children: [
130 | {type:"text", path:"displayName", label:"Name", description:"Displayed wherever someone sees your profile. (PUBLIC)"}
131 | {type:"textarea", path:"statusMessage", label:"About Me", description:"Displayed on your profile page. Markdown is allowed. (PUBLIC)", options:{rows:4, showLimit:true}}
132 | {type:"text", path:"location", label:"Location", description:"City, State, or whatever you want. (PUBLIC)"}
133 | {type:"text", path:"data.tags", label:"Hashtags", description:"Enter #Hashtags separated by spaces. (PUBLIC)"}
134 | ]
135 | }
136 | {
137 | type: layout-vertical
138 | label: Images
139 | children: [
140 | {type:"upload", path:"iconUrl", label:"Avatar Image", description:"Displayed next to your name as an icon. (PUBLIC)", options:{accept:"image/*", delete:"/@me/delete-icon"}}
141 | {type:"upload", path:"imageUrl", label:"Banner Image", description:"Displays at the top of your profile page. (PUBLIC)", options:{accept:"image/*", delete:"/@me/delete-image"}}
142 | {type:"upload", path:"data.background-body", label:"Window Background Image", description:"Displays instead of the window's background color (PUBLIC)", options:{accept:"image/*", delete:"/@me/delete-background"}}
143 | ]
144 | }
145 | {
146 | label: Colors
147 | description: "Change the colors of your profile page to match your brand."
148 | type:layout-vertical
149 | children: [
150 | {type:"colorpicker", path:"data.color-body", label:"Window Background"}
151 | {type:"colorpicker", path:"data.color-banner", label:"Banner Background"}
152 | {type:"colorpicker", path:"data.color-menu", label:"Menu Background"}
153 | {type:"colorpicker", path:"data.color-page", label:"Page Background"}
154 | {type:"colorpicker", path:"data.color-button", label:"Links and Buttons"}
155 | ]
156 | }
157 | {
158 | label: Style
159 | type:layout-vertical
160 | children: [
161 | {type:"textarea", path:"data.stylesheet", label:"Custom CSS Stylesheet", description: "Experts only! Read the CSS Guide → ", options:{rows:12, style:"font-family:monospace; font-size:14px;"}}
162 | ]
163 | }
164 | {
165 | type:"layout-vertical"
166 | label: Security
167 | children: [
168 | {type:"text", path:"username", label:"Username", description:"How others will identify you online. (PUBLIC)", options:{autocomplete:"off", validator:"/.validate/username"}}
169 | {type:"text", path:"emailAddress", label:"Email Address", description:"Used to sign in and support your account. Not shared. (PRIVATE)"}
170 | {type:"password", path:"new_password", label:"Change Password", description:"At least 8 characters. Please use a Password Manager. (PRIVATE)"}
171 | {type:"toggle", path:"isPublic", options:{text:"Public (visible to people)"}}
172 | {type:"toggle", path:"isIndexable", options:{text:"Indexable (request inclusion in search engines)"}}
173 | {type:"html", description:"Delete my account
"}
174 | ]
175 | }
176 | ]
177 | }
178 | }
179 | {do:"upload-attachments", category:"icon", fieldname:"iconUrl", attachment-path:"iconId", rules:{width:400, height:400, types:["webp"]}}
180 | {do:"upload-attachments", category:"image", fieldname:"imageUrl", attachment-path:"imageId", rules:{types:["webp"]}}
181 | {do:"upload-attachments", category:"body-background", fieldname:"data.background-body", download-path:"data.background-body", rules:{types:["webp"]}}
182 | {do:"process-tags", paths:["data.tags"]}
183 | {do:"save"}
184 | {do:"set-password"}
185 | {do:"search-index"}
186 | {do:"refresh-page"}
187 | ]}
188 | ]
189 | }
190 |
191 | edit-status: {
192 | roles: ["self"]
193 | steps: [
194 | {do: "as-modal", background:"view", steps:[
195 | {
196 | do: "edit"
197 | form:{
198 | label: Edit Status Message
199 | type: layout-vertical
200 | children: [
201 | {type:"textarea", path:"statusMessage", description:"Displayed on your profile page. Markdown is allowed. (PUBLIC)", options:{rows:8, showLimit:true}}
202 | ]
203 | }
204 | }
205 | {do:"save"}
206 | {do:"search-index"}
207 | {do:"refresh-page"}
208 | ]}
209 | ]
210 | }
211 |
212 | edit-banner: {
213 | roles: ["self"]
214 | steps: [
215 | {do: "as-modal", background:"view", steps:[
216 | {
217 | do: "edit"
218 | form:{
219 | label: Edit Banner
220 | description: Choose a color or an image to display a banner on your profile. Leave empty and it will be hidden to visitors.
221 | type:layout-vertical
222 | children: [
223 | {type:"colorpicker", path:"data.color-banner", label:"Background Color", description:"Displays underneath your banner image."}
224 | {type:"upload", path:"imageUrl", label:"Banner Image", description:"Displays at the top of your profile page.", options:{accept:"image/*", delete:"/@me/delete-image"}}
225 | ]
226 | }
227 | }
228 | {do:"upload-attachments", category:"image", fieldname:"imageUrl", attachment-path:"imageId", rules:{types:["webp"]}}
229 | {do:"save"}
230 | {do:"search-index", if:"{{.IsIndexable}}"}
231 | {do:"refresh-page"}
232 | ]}
233 | ]
234 | }
235 |
236 | edit-albums: {
237 | roles: ["self"]
238 | steps: [
239 | {do: "as-modal", background:"view", steps:[
240 | {
241 | do: "edit"
242 | form:{
243 | type: layout-vertical
244 | label: Edit Albums
245 | children: [
246 | {type:"text", path:"data.label-albums", label:"Title Text", description:"Used on profile, and at top of 'Albums' page.", options:{placeholder:"Albums"}}
247 | {type:"textarea", path:"data.description-albums", label:"Top of Page Text", description:"Text to show at the top of this page. Markdown is allowed.", options:{rows:8}}
248 | ]
249 | }
250 | }
251 | {do:"save"}
252 | {do:"refresh-page"}
253 | ]}
254 | ]
255 | }
256 |
257 |
258 | edit-events: {
259 | roles: ["self"]
260 | steps: [
261 | {do: "as-modal", background:"view", steps:[
262 | {
263 | do: "edit"
264 | form:{
265 | label: Edit Shows
266 | type: layout-vertical
267 | children: [
268 | {type:"text", path:"data.label-events", label:"Title Text", description:"Used on profile and at the top of 'Shows' page.", options:{placeholder:"Shows"}}
269 | {type:"textarea", path:"data.description-events", label:"Top of Page Text", description:"Text to show at the top of this page. Markdown is allowed.", options:{rows:8}}
270 | ]
271 | }
272 | }
273 | {do:"save"}
274 | {do:"refresh-page"}
275 | ]}
276 | ]
277 | }
278 |
279 |
280 | edit-news: {
281 | roles: ["self"]
282 | steps: [
283 | {do: "as-modal", background:"view", steps:[
284 | {
285 | do: "edit"
286 | form:{
287 | label: Edit News
288 | type: layout-vertical
289 | children: [
290 | {type:"text", path:"data.label-news", label:"Title Text", description:"Used on profile and at the top of 'News' page.", options:{placeholder:"News"}}
291 | {type:"textarea", path:"data.description-news", label:"Top of Page Text", description:"Text to show at the top of the 'News' section. Markdown is allowed.", options:{rows:8}}
292 | ]
293 | }
294 | }
295 | {do:"save"}
296 | {do:"refresh-page"}
297 | ]}
298 | ]
299 | }
300 |
301 | edit-menu: {
302 | roles: ["self"]
303 | steps: [
304 | {do: "as-modal", background:"view", steps:[
305 | {
306 | do: "edit"
307 | form:{
308 | label: Edit Menu
309 | type:layout-tabs
310 | children: [
311 | {
312 | type: layout-vertical
313 | label: Albums
314 | children: [
315 | {type:"toggle", path:"data.show-albums", options:{text:"Display 'Albums' in Menu Bar"}}
316 | {type:"text", path:"data.label-albums", label:"Menu Bar Text", options:{placeholder:"Albums"}}
317 | {type:"textarea", path:"data.description-albums", label:"Top of Page Text", description:"Text to show at the top of the 'Albums' section. Markdown is allowed.", options:{rows:8}}
318 | ]
319 | }
320 | {
321 | type: layout-vertical
322 | label: Shows
323 | children: [
324 | {type:"toggle", path:"data.show-events", options:{text:"Display 'Shows' in Menu Bar"}}
325 | {type:"text", path:"data.label-events", label:"Menu Bar Text", options:{placeholder:"Shows"}}
326 | {type:"textarea", path:"data.description-events", label:"Top of Page Text", description:"Text to show at the top of the 'Events' section. Markdown is allowed.", options:{rows:8}}
327 | ]
328 | }
329 | {
330 | type: layout-vertical
331 | label: News
332 | children: [
333 | {type:"toggle", path:"data.show-news", options:{text:"Display 'News' in Menu Bar"}}
334 | {type:"text", path:"data.label-news", label:"Menu Bar Text", options:{placeholder:"News"}}
335 | {type:"textarea", path:"data.description-news", label:"Top of Page Text", description:"Text to show at the top of the 'News' section. Markdown is allowed.", options:{rows:8}}
336 | ]
337 | }
338 | {
339 | type: layout-vertical
340 | label: Shop Link
341 | children: [
342 | {type:"label", label:"The 'Shop' link is displayed automatically when you enter the shop URL below"}
343 | {type:"text", path:"data.label-shop", label:"Menu Bar Text", options:{placeholder:"Shop"}}
344 | {type:"text", path:"data.shop-url", label:"Shop URL"}
345 | ]
346 | }
347 | {
348 | type: layout-vertical
349 | label: Colors
350 | children: [
351 | {type:"colorpicker", path:"data.color-menu", label:"Background Color"}
352 | ]
353 | }
354 | ]
355 | }
356 | }
357 | {do:"save"}
358 | {do:"refresh-page"}
359 | ]}
360 | ]
361 | }
362 | sort-featured: {
363 | roles: ["self"]
364 | steps: [
365 | {do:"sort", model:"Stream", keys:"_id", values:"rank"}
366 | {do:"set-header", name:"Hx-Push-Url", value:"false"}
367 | ]
368 | }
369 |
370 | sort-children: {
371 | roles: ["self"]
372 | steps: [
373 | {do:"sort", model:"Stream", keys:"_id", values:"rankAlt"}
374 | ]
375 | }
376 |
377 | header: {
378 | steps: [
379 | {do:"view-html", file:"header"}
380 | ]
381 | }
382 |
383 | stylesheet: {
384 | steps: [
385 | {do:"view-css"}
386 | ]
387 | }
388 |
389 | wizard-1: {
390 | roles: ["self"]
391 | steps: [
392 | {
393 | do:edit
394 | form: {
395 | type:layout-vertical
396 | children: [
397 | {type:"text", path:"displayName", label:"Artist/Band Name", description:"Displayed publicly. The name you want to be known by.", options:{autocomplete:"name", required:true}}
398 | {type:"text", path:"username", label:"Username", description:"Displayed publicly. How others will find you online.", options:{autocomplete:"username", required:true}}
399 | {type:"text", path:"emailAddress", label:"Email Address", description:"Used to sign in, and support your account. Not shared. Not displayed publicly.", options:{autocomplete:"email", required:true}}
400 | {type:"password", path:"new_password", label:"Choose Your Password", description:"At least 8 characters. Please use a Password Manager.", options:{autocomplete:"new-password", minLength:8, required:true}}
401 | ]
402 | }
403 | options: ["endpoint:/@{{.UserID}}/wizard-1", "submit-label:Continue >>", "cancel-button:hide"]
404 | },
405 | {do:"set-state", state:"WIZARD-STEP-2"}
406 | {do:"save"}
407 | {do:"set-password"}
408 | {do:"set-header", name:"HX-Refresh", value:"true"}
409 | ]
410 | }
411 |
412 | wizard-2: {
413 | roles: ["self"]
414 | steps: [
415 | {
416 | do:edit
417 | form: {
418 | type: layout-vertical
419 | children: [
420 | {type:"textarea", path:"statusMessage", label:"About You", description:"A brief description of you and your music. Markdown is okay."}
421 | {type:"textarea", path:"data.tags", label:"Tags", description:"Enter #Hashtags separated by spaces."}
422 | {type:"text", path:"location", label:"Location", description:"City, State, or Country where you are based."}
423 | {type:"upload", path:"iconUrl", label:"Avatar Image", description:"Displayed publicly next to your name, often on other websites, too.", options:{accept:"image/*"}}
424 | {type:"upload", path:"imageUrl", label:"Banner", description:"Displays at the top of your profile page.", options:{accept:"image/*"}}
425 | ]
426 | }
427 | options: ["endpoint:/@{{.UserID}}/wizard-2", "submit-label:Continue >>", "cancel-button:hide"]
428 | },
429 | {do:"upload-attachments", category:"icon", fieldname:"iconUrl", attachment-path:"iconId", rules:{width:400, height:400, types:["webp"]}}
430 | {do:"upload-attachments", category:"image", fieldname:"imageUrl", attachment-path:"imageId", rules:{types:["webp"]}}
431 | {do:"set-state", state:"WIZARD-STEP-3"}
432 | {do:"save"}
433 | {do:"set-header", name:"Hx-Refresh", value:"true"}
434 | ]
435 | }
436 |
437 | wizard-3: {
438 | roles: ["self"]
439 | steps: [
440 | {
441 | do:edit
442 | form: {
443 | type:layout-vertical
444 | children: [
445 | {type:"colorpicker", path:"data.color-body", label:"Window Background"}
446 | {type:"colorpicker", path:"data.color-menu", label:"Menu Background"}
447 | {type:"colorpicker", path:"data.color-page", label:"Page Background"}
448 | {type:"colorpicker", path:"data.color-button", label:"Links and Buttons"}
449 |
450 | ]
451 | }
452 | options: ["endpoint:/@{{.UserID}}/wizard-3", "submit-label:Continue to Album Upload", "cancel-button:hide"]
453 | },
454 | {do:"set-state", state:"LIVE"}
455 | {do:"save"}
456 | {do:"search-index", if:"{{.IsIndexable}}"}
457 | {do:"set-header", name:"Hx-Refresh", value:"true"}
458 | ]
459 | }
460 |
461 | publish: {
462 | roles: ["self"]
463 | steps: [
464 | {do:"as-modal", steps:[
465 | {do:"edit", form:{
466 | type:"layout-vertical"
467 | label:"Publish Your Profile"
468 | description:"This determines who can see your profile and interact with you online."
469 | children:[
470 | {type:"toggle", path:"isPublic", options:{text:"Make My Profile Public (visible to people)"}}
471 | {type:"toggle", path:"isIndexable", options:{text:"Make My Profile Indexable (include in search results)"}}
472 | ]}
473 | }
474 | ]}
475 | {do:"save"}
476 | {do:"search-index", if:"{{.IsIndexable}}"}
477 | {do:"refresh-page"}
478 | ]
479 | }
480 |
481 | delete-icon: {
482 | roles: ["self"]
483 | steps: [
484 | {do:"delete-attachments", field:"iconId"}
485 | {do:"save"}
486 | {do:"search-index", if:"{{.IsIndexable}}"}
487 | ]
488 | }
489 |
490 | delete-image: {
491 | roles: ["self"]
492 | steps: [
493 | {do:"delete-attachments", field:"imageId"}
494 | {do:"save"}
495 | {do:"search-index", if:"{{.IsIndexable}}"}
496 | ]
497 | }
498 |
499 | delete-background: {
500 | roles: ["self"]
501 | steps: [
502 | {do:"set-data", values:{"data.background-body":""}}
503 | {do:"delete-attachments", category:"body-background"}
504 | {do:"save"}
505 | ]
506 | }
507 | }
508 | }
--------------------------------------------------------------------------------
/bandwagon-outbox/wizard-1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Getting Started: Step 1 of 3
6 |
Welcome to Bandwagon!
7 |
8 |
Please fill out a few quick questions to set up your profile..
9 |
10 | {{.View "wizard-1" }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/bandwagon-outbox/wizard-2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Getting Started: Step 2 of 3
6 |
A Bit About You
7 |
8 |
9 | This information will show on your public profile.
10 | Don't worry if you don't have it perfect, just yet. You can always change it later.
11 |
12 |
13 | {{.View "wizard-2" }}
14 |
15 |
16 |
--------------------------------------------------------------------------------
/bandwagon-outbox/wizard-3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Getting Started: Step 3 of 3
6 |
Your Brand Colors
7 |
8 | Default colors are boring. Customize the color scheme of your profile page, to fit your image and style.
9 | If you're not ready, no worries. You can change these settings later.
10 |
11 |
12 | {{.View "wizard-3" }}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/bandwagon-search-albums/json.html:
--------------------------------------------------------------------------------
1 | {{- $host := .Host -}}
2 | {{- $token := .Token -}}
3 | {{- $syndication := .QueryParam "syndication" -}}
4 | {{- $syndicationSlice := array $syndication "ALL-PARTNERS" -}}
5 |
6 | {{- $streams := .Streams.Top600.ByPublishDate.Reverse -}}
7 | {{- $streams := $streams.Where "templateId" "bandwagon-album" -}}
8 | {{- $streams := $streams.WhereIN "syndication.values" $syndicationSlice -}}
9 | {{- $streams := $streams.Slice -}}
10 |
11 | {
12 | "syndication": "{{$syndication}}",
13 | "orderedItems": [
14 | {{- range $index, $stream := $streams -}}
15 | {{- if gt $index 0 -}}
16 | ,
17 | {{- end -}}
18 | {
19 | "url": "{{$host}}/{{$stream.StreamID}}",
20 | "export": "{{$host}}/{{$stream.StreamID}}/zip9348103752",
21 | "name": {{$stream.Label | json}},
22 | "icon": {{$stream.IconURL | json}},
23 | "artist": {{$stream.AttributedTo.ProfileURL | json}},
24 | "publishDate": {{$stream.PublishDate}}
25 | }
26 | {{- end -}}
27 | ]
28 | }
--------------------------------------------------------------------------------
/bandwagon-search-albums/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-search-albums
3 | templateRole: search
4 | extends: ["bandwagon-search", "bandwagon-common"]
5 | model: Stream
6 | containedBy: ["top"]
7 | icon: book
8 | label: Bandwagon Album Search
9 | description: Search Engine for Albums
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string"}
14 | summary: {type:"string"}
15 | data: {
16 | type:object
17 | properties: {
18 | searchText: {type:"string"}
19 | content: {type:"string"}
20 | }
21 | }
22 | }
23 | }
24 |
25 | actions: {
26 | view-albums: {
27 | steps: [
28 | {do:"view-html"}
29 | ]
30 | }
31 |
32 | feed: {
33 | steps: [
34 | {do:"view-feed", search-types:["Album"]}
35 | ]
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/bandwagon-search-albums/view-results.html:
--------------------------------------------------------------------------------
1 | {{- $token := .Token -}}
2 | {{- $query := .QueryParam "q" -}}
3 | {{- $tags := .QueryParam "tags" -}}
4 | {{- $empty := and (eq "" $query) (eq "" $tags) -}}
5 |
6 |
7 | {{- $results := .Search.Top60.ByShuffle.WhereType "Album" -}}
8 | {{- $after := .QueryParam "after" | int64 -}}
9 |
10 | {{- if ne 0 $after -}}
11 | {{- $results = $results.AfterShuffle $after -}}
12 | {{- end -}}
13 |
14 | {{- $results := $results.Slice -}}
15 | {{- $last := $results.Last -}}
16 | {{- $results = $results.Shuffle -}}
17 |
18 | {{- if $results.NotEmpty -}}
19 |
20 | {{- if eq 0 $after -}}
21 |
45 |
46 |
56 |
57 | {{- end -}}
58 |
59 |
76 |
77 | {{- else if eq 0 $after -}}
78 |
79 | {{- $tags := .FeaturedSearchTags.Slice -}}
80 |
81 |
82 | There are no albums that match
83 | {{if ne "" $query}}
84 | "{{$query}}" .
85 | {{- else -}}
86 | your search.
87 | {{- end -}}
88 |
89 | Please try some of these search terms instead...
90 |
91 |
92 |
101 |
102 | {{- end -}}
103 |
104 |
--------------------------------------------------------------------------------
/bandwagon-search-albums/view-sidebar.html:
--------------------------------------------------------------------------------
1 | {{- $licenses := .Dataset "bandwagon-search-albums.licenses" -}}
2 | {{- $tags := .QueryParam "tags" -}}
3 |
4 | {{- $query := .QueryParam "q" -}}
5 | {{- $escapedQuery := queryEscape $query -}}
6 |
7 |
14 |
15 |
16 |
17 |
18 | LICENSE
19 |
20 | (All)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/bandwagon-search-albums/view-tags.html:
--------------------------------------------------------------------------------
1 | {{- $token := .Token -}}
2 | {{- $tags := .FeaturedSearchTags.ByRank.Slice -}}
3 |
4 |
5 | {{- range $index, $tag := $tags -}}
6 |
7 | {{- $background00 := $tag.Colors.At 0 | parseColor -}}
8 | {{- $background01 := $tag.Colors.At 1 | parseColor -}}
9 | {{- $backgroundImage := concat "linear-gradient(120deg, " $background00.RGBA ", " $background01.RGBA ")" -}}
10 | {{- $textColor := $background00.Text -}}
11 | {{- $shadowColor := $textColor.Text -}}
12 |
13 |
14 |
15 | {{- if ne "" $tag.ImageURL }}
16 |
17 | {{- end -}}
18 |
{{$tag.Name}}
19 |
20 |
21 | {{- end -}}
22 |
23 |
24 |
35 |
--------------------------------------------------------------------------------
/bandwagon-search-events/embed-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Before
7 |
8 |
9 |
10 |
11 | After
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/bandwagon-search-events/embed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 |
16 | {{- .View "view-results" -}}
17 |
18 |
21 |
22 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/bandwagon-search-events/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-search-events
3 | templateRole: search
4 | extends: ["bandwagon-search"]
5 | model: Stream
6 | containedBy: ["top"]
7 | icon: home
8 | label: Bandwagon Event Search
9 | description: Search Engine for Events
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string"}
14 | summary: {type:"string"}
15 | data: {
16 | type:object
17 | properties: {
18 | searchText: {type:"string"}
19 | content: {type:"string"}
20 | }
21 | }
22 | }
23 | }
24 |
25 | actions: {
26 | embed: {
27 | steps: [
28 | {do:"view-html", as-full-page:true}
29 | ]
30 | }
31 | view: {
32 | steps:[
33 | {do:"if", condition:"{{eq `` (.QueryParam `date`)}}", then:[
34 | {do:"set-query-param", date:"next-30-days"}
35 | ]}
36 | {do:"view-html"}
37 | ]
38 | }
39 |
40 | feed: {
41 | steps: [
42 | {do:"view-feed", search-types:["Event"]}
43 | ]
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/bandwagon-search-events/view-results.html:
--------------------------------------------------------------------------------
1 | {{- $host := .Host -}}
2 | {{- $token := .Token -}}
3 | {{- $query := .QueryParam "q" -}}
4 | {{- $escapedQuery := queryEscape $query -}}
5 |
6 | {{- $results := .Search.Top120.ByDate.WhereType "Event" -}}
7 | {{- $results := $results.Slice -}}
8 |
9 |
10 | {{- if $results.IsEmpty -}}
11 |
12 | {{- $tags := .FeaturedSearchTags.Slice -}}
13 |
14 |
15 | There are no events that match "{{$query}}" .
16 |
17 |
18 | {{- else -}}
19 |
20 | {{- $token := .Token -}}
21 | {{- $group := groupie -}}
22 |
23 |
33 |
34 |
75 |
76 | {{- end -}}
--------------------------------------------------------------------------------
/bandwagon-search-events/view-sidebar.html:
--------------------------------------------------------------------------------
1 | {{- $query := .QueryParam "q" -}}
2 | {{- $date := .QueryParam "date" -}}
3 | {{- if eq "" $date -}}
4 | {{- $date := "next-30-days" -}}
5 | {{- end -}}
6 |
7 |
14 |
15 |
16 |
17 |
18 | DATE
19 |
20 | Next 30 Days
21 | Next 90 Days
22 | Next Year
23 | Previous Year
24 |
25 |
26 |
27 |
28 | VENUE
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/bandwagon-search-news/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-search-news
3 | templateRole: search
4 | extends: ["bandwagon-search"]
5 | model: Stream
6 | containedBy: ["top"]
7 | icon: home
8 | label: Bandwagon News Search
9 | description: Search Engine for News
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string"}
14 | summary: {type:"string"}
15 | data: {
16 | type:object
17 | properties: {
18 | searchText: {type:"string"}
19 | content: {type:"string"}
20 | }
21 | }
22 | }
23 | }
24 |
25 | tagPaths: ["data.tags"]
26 |
27 | actions: {
28 | feed: {
29 | steps: [
30 | {do:"view-feed", search-types:["Person"]}
31 | ]
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/bandwagon-search-news/view-results.html:
--------------------------------------------------------------------------------
1 | {{- $token := .Token -}}
2 | {{- $query := .QueryParam "q" -}}
3 |
4 | {{- $results := .Search.Top60.ByCreateDate.Reverse -}}
5 | {{- $results = $results.WhereType "Article" -}}
6 | {{- $before := .QueryParam "before" -}}
7 |
8 | {{- if ne "" $before -}}
9 | {{- $results = $results.WhereLT "publishDate" (int64 $before) -}}
10 | {{- end -}}
11 |
12 | {{- $results := $results.Slice -}}
13 | {{- $last := $results.Last -}}
14 |
15 |
16 | {{- if $results.NotEmpty -}}
17 |
18 | {{- if eq "" $before -}}
19 |
29 | {{- end -}}
30 |
31 |
32 | {{- $token := .Token -}}
33 |
34 |
59 |
60 | {{- else if eq "" $before -}}
61 |
62 |
63 | There are no news items that match "{{$query}}" .
64 |
65 |
66 | {{- end -}}
--------------------------------------------------------------------------------
/bandwagon-search-news/view-sidebar.html:
--------------------------------------------------------------------------------
1 | {{- $query := .QueryParam "q" -}}
2 |
3 |
10 |
--------------------------------------------------------------------------------
/bandwagon-search-tracks/json.html:
--------------------------------------------------------------------------------
1 | {{- $host := .Host -}}
2 | {{- $token := .Token -}}
3 | {{- $syndication := .QueryParam "syndication" -}}
4 | {{- $syndicationSlice := array $syndication "ALL-PARTNERS" -}}
5 |
6 | {{- $streams := .Streams.Top600.ByPublishDate.Reverse -}}
7 | {{- $streams := $streams.Where "templateId" "bandwagon-album" -}}
8 | {{- $streams := $streams.WhereIN "syndication.values" $syndicationSlice -}}
9 | {{- $streams := $streams.Slice -}}
10 |
11 | {
12 | "syndication": "{{$syndication}}",
13 | "orderedItems": [
14 | {{- range $index, $stream := $streams -}}
15 | {{- if gt $index 0 -}}
16 | ,
17 | {{- end -}}
18 | {
19 | "url": "{{$host}}/{{$stream.StreamID}}",
20 | "export": "{{$host}}/{{$stream.StreamID}}/zip9348103752",
21 | "name": {{$stream.Label | json}},
22 | "icon": {{$stream.IconURL | json}},
23 | "artist": {{$stream.AttributedTo.ProfileURL | json}},
24 | "publishDate": {{$stream.PublishDate}}
25 | }
26 | {{- end -}}
27 | ]
28 | }
--------------------------------------------------------------------------------
/bandwagon-search-tracks/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-search-tracks
3 | templateRole: search
4 | extends: ["bandwagon-search"]
5 | model: Stream
6 | containedBy: ["top"]
7 | icon: book
8 | label: Bandwagon Track Search
9 | description: Search Engine for Tracks
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string"}
14 | summary: {type:"string"}
15 | data: {
16 | type:object
17 | properties: {
18 | searchText: {type:"string"}
19 | content: {type:"string"}
20 | }
21 | }
22 | }
23 | }
24 |
25 | actions: {
26 |
27 | view-albums-list: {
28 | steps: [{do:"view-html"}]
29 | }
30 |
31 | feed: {
32 | steps: [
33 | {do:"view-feed", search-types:["Audio"]}
34 | ]
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/bandwagon-search-tracks/view-results.html:
--------------------------------------------------------------------------------
1 | {{- $token := .Token -}}
2 | {{- $query := .QueryParam "q" -}}
3 | {{- $tags := .QueryParam "tags" -}}
4 | {{- $empty := and (eq "" $query) (eq "" $tags) -}}
5 |
6 |
7 | {{- $results := .Search.Top30.ByShuffle.WhereType "Audio" -}}
8 | {{- $after := .QueryParam "after" | int64 -}}
9 |
10 | {{- if ne 0 $after -}}
11 | {{- $results = $results.AfterShuffle $after -}}
12 | {{- end -}}
13 |
14 | {{- $results := $results.Slice -}}
15 | {{- $last := $results.Last -}}
16 | {{- $results = $results.Shuffle -}}
17 |
18 | {{- if $results.NotEmpty -}}
19 |
20 | {{- if eq 0 $after -}}
21 |
31 |
32 | {{- end -}}
33 |
34 |
57 |
58 | {{- else if eq 0 $after -}}
59 |
60 | {{- $tags := .FeaturedSearchTags.Slice -}}
61 |
62 |
63 | There are no tracks that match
64 | {{if ne "" $query}}
65 | "{{$query}}" .
66 | {{- else -}}
67 | your search.
68 | {{- end -}}
69 |
70 | Please try some of these search terms instead...
71 |
72 |
73 |
82 |
83 | {{- end -}}
84 |
--------------------------------------------------------------------------------
/bandwagon-search-tracks/view-sidebar.html:
--------------------------------------------------------------------------------
1 | {{- $query := .QueryParam "q" -}}
2 | {{- $escapedQuery := queryEscape $query -}}
3 |
4 |
11 |
--------------------------------------------------------------------------------
/bandwagon-search-tracks/view-tags.html:
--------------------------------------------------------------------------------
1 | {{- $token := .Token -}}
2 | {{- $tags := .FeaturedSearchTags.ByRank.Slice -}}
3 |
4 |
10 |
11 |
12 | {{- range $index, $tag := $tags -}}
13 |
14 | {{- $background00 := $tag.Colors.At 0 | parseColor -}}
15 | {{- $background01 := $tag.Colors.At 1 | parseColor -}}
16 | {{- $backgroundImage := concat "linear-gradient(120deg, " $background00.RGBA ", " $background01.RGBA ")" -}}
17 | {{- $textColor := $background00.Text -}}
18 | {{- $shadowColor := $textColor.Text -}}
19 |
20 |
21 |
22 | {{- if ne "" $tag.ImageURL }}
23 |
24 | {{- end -}}
25 |
{{$tag.Name}}
26 |
27 |
28 | {{- end -}}
29 |
30 |
--------------------------------------------------------------------------------
/bandwagon-search-users/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-search-users
3 | templateRole: search
4 | extends: ["bandwagon-search"]
5 | model: Stream
6 | containedBy: ["top"]
7 | icon: home
8 | label: Bandwagon User Search
9 | description: Search Engine for Users
10 | schema: {
11 | type:object
12 | properties: {
13 | label: {type:"string"}
14 | summary: {type:"string"}
15 | data: {
16 | type:object
17 | properties: {
18 | searchText: {type:"string"}
19 | content: {type:"string"}
20 | }
21 | }
22 | }
23 | }
24 |
25 | actions: {
26 | feed: {
27 | steps: [
28 | {do:"view-feed", search-types:["Person"]}
29 | ]
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/bandwagon-search-users/view-results.html:
--------------------------------------------------------------------------------
1 | {{- $token := .Token -}}
2 | {{- $query := .QueryParam "q" -}}
3 |
4 | {{- $results := .Search.Top60.ByShuffle.WhereType "Person" -}}
5 | {{- $after := .QueryParam "after" | int64 -}}
6 |
7 | {{- if ne 0 $after -}}
8 | {{- $results = $results.AfterShuffle $after -}}
9 | {{- end -}}
10 |
11 | {{- $results := $results.Slice -}}
12 | {{- $last := $results.Last -}}
13 | {{- $results = $results.Shuffle -}}
14 |
15 |
16 | {{- if $results.NotEmpty -}}
17 |
18 | {{- $token := .Token -}}
19 |
20 |
26 |
27 | {{- range $index, $result := $results -}}
28 |
42 | {{- end -}}
43 |
44 |
45 | {{- if ne 0 $after -}}
46 |
53 | {{- end -}}
54 |
55 | {{- else if eq 0 $after -}}
56 |
57 | {{- $tags := .FeaturedSearchTags.Slice -}}
58 |
59 |
60 | There are no artists that match "{{$query}}" .
61 |
62 | Please try some of these search terms instead...
63 |
64 |
65 |
74 |
75 | {{- end -}}
--------------------------------------------------------------------------------
/bandwagon-search-users/view-sidebar.html:
--------------------------------------------------------------------------------
1 | {{- $query := .QueryParam "q" -}}
2 |
3 |
10 |
--------------------------------------------------------------------------------
/bandwagon-search/hyperscript/listbox._hs:
--------------------------------------------------------------------------------
1 | behavior Listbox
2 |
3 | init
4 | add .list-box
5 | add [@role="listbox"]
6 | end
7 |
8 | on ArrowDown
9 |
10 | set selection to the first <[role=option].selected /> in me
11 |
12 | if selection is null then
13 | take .selected from my children for the first <[role=option] /> in me
14 | exit
15 | end
16 |
17 | if selection is the last <[role=option] /> in me then
18 | exit
19 | end
20 |
21 | take .selected from my children for the next <[role=option] /> from selection
22 | end
23 |
24 | on ArrowUp
25 | set selection to the first <[role=option].selected /> in me
26 |
27 | if selection is the first <[role=option] /> in me then
28 | send hide to me
29 | remove .selected from selection
30 | exit
31 | end
32 |
33 | if selection is null then
34 | send hide to me
35 | exit
36 | end
37 |
38 | take .selected from my children for the previous <[role=option] /> from selection
39 | end
40 |
41 | end
--------------------------------------------------------------------------------
/bandwagon-search/hyperscript/popUp._hs:
--------------------------------------------------------------------------------
1 | behavior Popup
2 |
3 | init
4 | add .pop-up
5 | end
6 |
7 | on show
8 | set visibility to my *visibility
9 |
10 | if visibility is "hidden" then
11 | log "set visible"
12 | set my *visibility to "visible"
13 | end
14 | end
15 |
16 | on hide
17 | set visibility to my *visibility
18 | if visibility is "visible" then
19 | log "set hidden"
20 | set my *visibility to "hidden"
21 | end
22 | end
23 |
24 | end
--------------------------------------------------------------------------------
/bandwagon-search/hyperscript/searchSuggestion._hs:
--------------------------------------------------------------------------------
1 | behavior SearchSuggestion
2 |
3 | on mouseover
4 | take .selected from my siblings
5 | end
6 |
7 | on mousedown
8 | useSuggestion()
9 | end
10 | end
--------------------------------------------------------------------------------
/bandwagon-search/hyperscript/searchTag._hs:
--------------------------------------------------------------------------------
1 | behavior SearchTag
2 |
3 | init
4 | add .tag
5 | add .nowrap
6 | add .margin-sm
7 | add .margin-right-none
8 | add .clickable
9 | add .flex-align-self-center
10 | add [@tabIndex=0]
11 |
12 | set my innerHTML to my @data-tag + ` `
13 | end
14 |
15 | on click or keydown[key=="Backspace"]
16 | set newTag to the previous <[data-tag]/> within #search-tags
17 | if newTag is not null then
18 | focus() the newTag
19 | else
20 | focus() the #search-input
21 | end
22 | remove me
23 | end
24 |
25 | on keydown[key=="ArrowLeft"]
26 | set newTag to the previous <[data-tag]/> within #search-tags
27 | if newTag is not null then
28 | focus() the newTag
29 | end
30 | end
31 |
32 | on keydown[key=="ArrowRight"]
33 | halt the event
34 | set newTag to the next <[data-tag]/> within #search-tags
35 | if newTag is not null then
36 | focus() the newTag
37 | else
38 | focus() the #search-input
39 | end
40 | end
41 |
42 | on keydown[key=="Enter"]
43 | halt the event
44 | send click to the #search-submit
45 | end
46 |
47 | end
--------------------------------------------------------------------------------
/bandwagon-search/hyperscript/searchWidget._hs:
--------------------------------------------------------------------------------
1 | behavior SearchWidget
2 | init
3 | send focus(initial:true) to the #search-input
4 | end
5 |
6 | -- submit the search
7 | on click from #search-submit
8 | send submit to #search-form
9 | end
10 |
11 | ---------------------------
12 | -- Search Suggestions
13 |
14 | on showSuggestions
15 | -- clean up the search value
16 | set searchValue to the #search-input's value
17 | set searchValue to lastWord(searchValue)
18 | set searchValue to searchValue.toLowerCase()
19 |
20 | -- send query and show suggestions
21 | set #search-suggestion-query.value to searchValue
22 | send search to the #search-suggestions-form
23 | send show to #search-suggestions
24 | end
25 |
26 | ---------------------------
27 | -- Keyboard Navigation
28 |
29 | on keydown[key=="Escape"] from #search-input
30 | send hide to #search-suggestions
31 | end
32 |
33 | on keydown[key=="Enter"] from #search-input
34 | if the #search-suggestions's *visibility is "visible" then
35 | useSuggestion()
36 | else
37 | send submit to #search-form
38 | end
39 | end
40 |
41 | on keydown[key=="ArrowDown"] from #search-input
42 | if the #search-suggestions's *visibility is "hidden" then
43 | trigger showSuggestions
44 | else
45 | send ArrowDown to #search-suggestions
46 | end
47 | end
48 |
49 | on keydown[key=="ArrowUp"] from #search-input
50 | halt the event
51 | send ArrowUp to #search-suggestions
52 | end
53 |
54 | on keyup from #search-input throttled at 100ms
55 | set ignoreKeys to ["ArrowDown", "ArrowUp", "Enter", "Escape"]
56 |
57 | if ignoreKeys.includes(event.key) then
58 | exit
59 | end
60 |
61 | -- handle regular keystrokes
62 | send showSuggestions
63 | end
64 |
65 | on blur from #search-input debounced at 50ms
66 | send hide to #search-suggestions
67 | end
68 |
69 | on submit from #search-form
70 | send hide to #search-suggestions
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/bandwagon-search/hyperscript/utils._hs:
--------------------------------------------------------------------------------
1 | def useSuggestion()
2 |
3 | -- do not execute if suggestions are not visible
4 | if the #search-suggestions's *visibility is "hidden" then
5 | exit
6 | end
7 |
8 | -- find the first "selected" suggestion
9 | set suggestion to the first in #search-suggestions
10 |
11 | if suggestion is undefined then
12 | exit
13 | end
14 |
15 | -- send the request to the server
16 | set the #search-input's value to "#" + suggestion.innerHTML
17 | send click to the #search-submit
18 | hide the #search-suggestions with visibility
19 | end
20 |
--------------------------------------------------------------------------------
/bandwagon-search/javascript/parseTags.js:
--------------------------------------------------------------------------------
1 | function parseTags(value) {
2 |
3 | var inTag = false
4 | var tags = []
5 | var remainder = ""
6 | var foundTag = ""
7 |
8 | for (var index = 0 ; index < value.length; index++) {
9 | var char= value[index]
10 |
11 | // This is the beginning of a tag..
12 | if (char == "#") {
13 | inTag = true
14 | foundTag = ""
15 | continue
16 | }
17 |
18 | // We're not in a tag, so just add the character to the remainder
19 | if (inTag == false) {
20 | remainder += char
21 | continue
22 | }
23 |
24 | // This is the end of a tag
25 | switch (char) {
26 |
27 | case " ":
28 | tags.push(foundTag)
29 | inTag = false
30 | break
31 |
32 | case ",":
33 | case ".":
34 | case "?":
35 | case ":":
36 | case ";":
37 | remainder += char
38 | tags.push(foundTag)
39 | inTag = false
40 | break
41 |
42 | // Otherwise, add the character to the current tag
43 | default:
44 | foundTag += char
45 |
46 | }
47 | }
48 |
49 | // Also add tags that extend to the end of the input
50 | if (inTag) {
51 | tags.push(foundTag)
52 | }
53 |
54 | return {
55 | tags: tags,
56 | remainder: remainder
57 | }
58 | }
59 |
60 | function lastWord(value) {
61 | var words = value.split(" ")
62 | return words[words.length - 1]
63 | }
--------------------------------------------------------------------------------
/bandwagon-search/search-related.html:
--------------------------------------------------------------------------------
1 | {{- $tags := .QueryParam "q" | parseTags -}}
2 | {{- if $tags.NotEmpty -}}
3 |
4 | {{- $tag := $tags.First | .SearchTag -}}
5 |
6 | {{- $related := $tag.RelatedTags -}}
7 | {{- if $related.NotEmpty -}}
8 | {{- $token := .Token -}}
9 |
10 |
Related:
11 | {{- range $tag := $related -}}
12 |
{{$tag}}
13 | {{- end -}}
14 |
15 |
16 |
39 | {{- end -}}
40 |
41 | {{- end -}}
--------------------------------------------------------------------------------
/bandwagon-search/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId:bandwagon-search
3 | templateRole:search
4 | extends:["base-intent"]
5 | containedBy: []
6 | model:stream
7 | label:Bandwagon Search page
8 | description:Search Page for Bandwagon
9 | schema: {
10 | type:object
11 | properties: {
12 | icon: {type:"string"}
13 | label: {type:"string"}
14 | summary: {type:"string"}
15 | data: {
16 | type:object
17 | properties: {
18 | searchText: {type:"string"}
19 | content: {type:"string"}
20 | }
21 | }
22 | }
23 | }
24 | states: {}
25 | roles: {}
26 |
27 | bundles: {
28 | javascript: {
29 | content-type:application/javascript
30 | }
31 | hyperscript: {
32 | content-type:text/hyperscript
33 | }
34 | }
35 |
36 | actions: {
37 |
38 | create: {
39 | roles:["owner"]
40 | steps: [
41 | {do: "set-data", defaults: {label: "New Page"}}
42 | {
43 | do:edit
44 | form: {
45 | type:layout-tabs
46 | label:Create Search Page
47 | children: [
48 | {
49 | type:layout-vertical
50 | label:Page Info
51 | children: [
52 | {type:"text", label:"Token", path:"token"}
53 | {type:"text", label:"Icon", path:"icon"}
54 | {type:"text", label:"Page Name", path:"label"}
55 | {type:"textarea", label:"Description", path:"summary"}
56 | ]
57 | },
58 | {
59 | type:layout-vertical
60 | label:Content
61 | children:[
62 | {type:"text", path:"data.searchText", label:"Search Text", description:"Prompt to display in the search box."}
63 | {type:"textarea", path:"data.content", label:"Page Heading", description:"Appears at the top of search results. Markdown is okay.", options:{rows:10}}
64 | ]
65 | }
66 | ]
67 | }
68 | }
69 | {do: "save-and-publish"}
70 | {do:"forward-to", url:"/{{.StreamID}}/edit"}
71 | ]
72 | }
73 | edit: {
74 | roles: ["owner"]
75 | steps: [
76 | {do: "as-modal", background:"view", steps: [
77 | {
78 | do: edit
79 | options: ["delete:/{{.StreamID}}/delete"]
80 | form: {
81 | type: layout-tabs
82 | label:"Edit Search Page"
83 | children: [
84 | {
85 | type:"layout-vertical",
86 | label:"Page Info",
87 | children: [
88 | {type:"text", readOnly:true, label:"Template Type", path:"templateId"}
89 | {type:"text", label:"Token", path:"token"}
90 | {type:"text", label:"Icon", path:"icon"}
91 | {type:"text", label:"Page Name", path:"label"}
92 | {type:"textarea", label:"Description", path:"summary"}
93 | ]
94 | },
95 | {
96 | type:"layout-vertical",
97 | label:"Content",
98 | children:[
99 | {type:"text", path:"data.searchText", label:"Search Text", description:"Prompt to display in the search box."}
100 | {type:"textarea", path:"data.content", label:"Page Heading", description:"Appears at the top of search results. Markdown is okay.", options:{rows:10}}
101 | ]
102 | }
103 | ]
104 | }
105 | }
106 | ]}
107 | {do: "save-and-publish", message:"Edited by {{.Author.DisplayName}}"}
108 | {do: "refresh-page"}
109 | ]
110 | }
111 | delete: {
112 | roles: ["owner"]
113 | steps: [
114 | {do:"delete", title:"Delete '{{.Label}}'?", message:"All content and comments will be lost. There is NO UNDO."}
115 | {do:"forward-to", url:"/{{.ParentID}}"}
116 | ]
117 | }
118 | sharing: {
119 | roles: ["owner"]
120 | steps: [
121 | {do:"as-modal", steps: [
122 | {do:"set-simple-sharing", roles: ["viewer"], title:"Who Can See This Article?", message:"Select who can view and comment on this article."}
123 | {do:"save", message:"Sharing updated by {{.Author}}"}
124 | ]}
125 | ]
126 | }
127 |
128 | view: {
129 | steps:[
130 | {do:"view-html"}
131 | ]
132 | }
133 | view-results: {
134 | steps:[
135 | {do:"view-html"}
136 | ]
137 | }
138 | view-suggestions: {
139 | steps:[
140 | {do:"view-html"}
141 | ]
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/bandwagon-search/view-results.html:
--------------------------------------------------------------------------------
1 | {{- $queryString := .QueryParam "q" -}}
2 | {{- $token := .Token -}}
3 |
4 | {{- if eq "" $queryString -}}
5 |
6 | {{- template "view-tags" . -}}
7 |
8 | {{- else -}}
9 |
10 | {{- $results := .Search.Slice -}}
11 |
12 |
13 | Follow for Updates
14 | {{icon "more-horizontal"}}
15 |
16 |
17 |
18 | {{- range $result := $results -}}
19 |
20 |
{{- $result | jsonIndent -}}
21 |
22 | {{- end -}}
23 |
24 |
25 | {{- end -}}
--------------------------------------------------------------------------------
/bandwagon-search/view-suggestions.html:
--------------------------------------------------------------------------------
1 | {{- $searchTags := .AllowedSearchTags.Top12.ByName.Slice -}}
2 | {{- range $searchTag := $searchTags -}}
3 | {{$searchTag.Name}}
7 | {{- end -}}
8 |
--------------------------------------------------------------------------------
/bandwagon-search/view-widget.html:
--------------------------------------------------------------------------------
1 | {{- $query := .QueryParam "q" | trim -}}
2 | {{- $placeholder := .Data "searchText" -}}
3 |
4 |
5 |
6 |
7 |
8 |
15 |
25 |
26 | {{- template "search-related" . -}}
27 |
28 |
--------------------------------------------------------------------------------
/bandwagon-search/view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{- template "view-widget" . -}}
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 | {{template "view-results" .}}
22 |
23 |
24 | {{- if .UserCan "edit" -}}
25 |
26 |
27 |
28 |
29 |
30 | Edit
31 | Sharing
32 |
33 | {{- end -}}
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/bandwagon-song/oembed.html:
--------------------------------------------------------------------------------
1 | {{- $album := .Parent "view" -}}
2 | {{- $albumColors := $album.Data "color" -}}
3 | {{- $bodyColor := first $albumColors.body "#eeeeee" -}}
4 | {{- $pageColor := parseColor (first $albumColors.page "#ffffff") -}}
5 | {{- $buttonColor := parseColor (first $albumColors.button "#0f62fe") -}}
6 | {{- $titleColor := $pageColor.Text -}}
7 | {{- $textColor := $pageColor.TextExt -}}
8 |
9 |
10 |
11 |
12 |
13 | {{- if ne "" $album.IconURL -}}
14 |
15 |
16 |
17 | {{- end -}}
18 |
19 | {{.Label}}
20 |
33 |
59 |
60 | {{- if ne "" .Summary -}}
61 |
62 | {{.Summary}}
63 |
64 | {{- end -}}
65 |
66 |
67 | {{- $license := $album.DataString "license"}}
68 | {{- if eq "COPYRIGHT" $license -}}
69 | ©
70 | Copyright. All Rights Reserved.
71 | {{- else if ne "" $license -}}
72 | {{- $licenses := .Dataset "bandwagon-album.licenses" -}}
73 | {{- $licenseInfo := $licenses.Value $license -}}
74 | {{icon $licenseInfo.Icon}}
75 | {{$licenseInfo.Label}}
76 | {{- end -}}
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/bandwagon-song/template.hjson:
--------------------------------------------------------------------------------
1 | {
2 | templateId: bandwagon-song
3 | templateRole: song
4 | label: Song
5 | extends: ["base-intent", "bandwagon-common"]
6 | containedBy: ["album"]
7 | schema: {
8 | type: object
9 | properties: {
10 | label: {type: "string", required: true}
11 | iconUrl: {type:"string", format:"url"}
12 | data: {
13 | type: object
14 | properties: {
15 | tags: {type:"string"}
16 | length: {type: "string"}
17 | genre: {type: "string"}
18 | artist: {type: "string"}
19 | composer: {type: "string"}
20 | producer: {type: "string"}
21 | publisher: {type: "string"}
22 | isrc: {type: "string"}
23 | license: {type: "string"}
24 | lyrics: {type: "string"}
25 | streamURL: {type: "string", format:"url"}
26 | featured: {type: "boolean"}
27 | explicit: {type: "boolean"}
28 | attachmentId: {type: "string"}
29 | attachmentFilename: {type: "string"}
30 | links: {
31 | type: object
32 | wildcard:{type: "string", format:"url"}
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
39 | socialRole: Audio
40 | socialRules: [
41 | {target:"@context", append:"https://funkwhale.audio/ns"}
42 | {target:"type", value:"Audio"}
43 | {target:"attributedTo.id", path:"attributedTo.profileUrl"},
44 | {target:"attributedTo.name", path:"attributedTo.name"},
45 | {target:"links", forEach:"data.links", rules:[
46 | {target:"label", path:"key"}
47 | {target:"rel", value:"alternate"}
48 | {target:"href", path:"value"}
49 | ]}
50 | {target:"track.type", value:"Track"}
51 | {target:"track.id", path:"url"}
52 | {target:"track.name", path:"label"}
53 | {target:"track.album", expression:"{{.ParentURL}}"}
54 | {target:"track.position", path:"rank"}
55 | {target:"content", path:"data.lyrics"}
56 | {target:"album", expression:"{{.ParentURL}}"}
57 | {target:"library", expression:"{{.ParentURL}}/pub/children"}
58 | ]
59 |
60 | search: {
61 | type: Audio
62 | iconUrl:"{{first .IconURL (join `/` .ParentID.Hex `/icon`)}}"
63 | summary:""
64 | text: "{{.Label}} {{.AttributedTo.Name}} {{index .Data `lyrics`}}"
65 | tags: "LIC:{{index .Data `license`}}"
66 | }
67 |
68 | tagPaths: ["data.tags"]
69 |
70 | actions: {
71 | create: {
72 | roles: ["admin", "author"]
73 | steps: [
74 | {do:"as-modal", steps:[
75 | {
76 | do:"edit",
77 | form:{
78 | type: layout-tabs
79 | label: + Add a Song
80 | children: [
81 | {
82 | type: layout-vertical
83 | label: General
84 | children: [
85 | {type: "text", path: "label", label: "Song Title"},
86 | {type: "upload", path: "data.attachmentId", label: "Audio File", options:{filename:"data.attachmentFilename", accept:"audio/*"}},
87 | {type: "upload", path: "iconUrl", label:"Cover Art", options:{accept:"image/*", filename:"data.imageFilename", delete:"/{{.StreamID}}/delete-icon", rules:{width:800, height:800, types:["webp"]}}}
88 | {type: "text", path: "data.length", label: "Length"},
89 | {type: "toggle", path: "isFeatured", options:{true-text:"Featured. Highlighted on track list", false-text:"Featured?"}}
90 | ]
91 | }
92 | {
93 | type: layout-vertical
94 | label: Description
95 | children: [
96 | {type: "textarea", path: "data.lyrics", description:"Enter lyrics or song description. Displays on this song's detail page.", options:{rows: 16}}
97 | {type: "toggle", path: "data.explicit", options:{true-text:"Explicit Lyrics", false-text:"Explicit Lyrics"}}
98 | ]
99 | }
100 | {
101 | type: layout-vertical
102 | label: Metadata
103 | children: [
104 | {type: "select", path: "data.license", label: "License", options:{provider:"bandwagon-album.licenses"}},
105 | {type: "text", path: "data.tags", label: "Tags", description:"Enter #Hashtags separated by spaces."},
106 | {type: "text", path: "data.genre", label: "Genre"},
107 | {type: "text", path: "data.artist", label: "Artist"},
108 | {type: "text", path: "data.composer", label: "Composer"},
109 | {type: "text", path: "data.producer", label: "Producer"},
110 | {type: "text", path: "data.publisher", label: "Publisher"},
111 | {type: "text", path: "data.isrc", label: "ISRC"},
112 | ]
113 | }
114 | {
115 | type: layout-vertical
116 | label: Links
117 | children: [
118 | {type: "text", path: "data.streamURL", label: "Video Stream URL"},
119 | {type:"text", path:"data.links.AMAZON", options:{placeholder:"Amazon Music"}}
120 | {type:"text", path:"data.links.APPLE", options:{placeholder:"Apple Music"}}
121 | {type:"text", path:"data.links.BANDCAMP", options:{placeholder:"Bandcamp"}}
122 | {type:"text", path:"data.links.GOOGLE", options:{placeholder:"Google Play"}}
123 | {type:"text", path:"data.links.SOUNDCLOUD", options:{placeholder:"Soundcloud"}}
124 | {type:"text", path:"data.links.SPOTIFY", options:{placeholder:"Spotify"}}
125 | {type:"text", path:"data.links.TIDAL", options:{placeholder:"Tidal"}}
126 | {type:"text", path:"data.links.YOUTUBE", options:{placeholder:"YouTube Music"}}
127 | {type:"text", path:"data.links.OTHER1", options:{placeholder:"Other"}}
128 | {type:"text", path:"data.links.OTHER2", options:{placeholder:"Other"}}
129 | {type:"text", path:"data.links.OTHER3", options:{placeholder:"Other"}}
130 | ]
131 | }
132 | ]
133 | }
134 | }
135 | ]}
136 | {do:"upload-attachments", category:"image", fieldname:"iconUrl", download-path:"iconUrl", accept-type:"image/*", maximum:1, rules:{width:800, height:800, types:["webp"]}}
137 | {do:"upload-attachments", action:"replace", category:"Audio", fieldname:"data.attachmentId", attachment-path:"data.attachmentId", filename-path:"data.attachmentFilename", accept-type:"audio/*", maximum:1}
138 | {do:"process-tags", paths:"data.tags"}
139 | {do:"save-and-publish", outbox:false}
140 | {do:"search-index"}
141 | {do:"refresh-page"}
142 | ]
143 | }
144 | view: {
145 | steps: [
146 | {do:"view-html"}
147 | ]
148 | }
149 | edit: {
150 | roles: ["admin", "author"]
151 | steps: [
152 | {
153 | do:"as-modal",
154 | steps:[
155 | {
156 | do:"edit",
157 | options: ["delete:/{{.StreamID}}/delete"]
158 | form:{
159 | type: layout-tabs
160 | label: Edit Song
161 | children: [
162 | {
163 | type: layout-vertical
164 | label: General
165 | children: [
166 | {type: "text", path: "label", label: "Song Title"},
167 | {type: "upload", path: "data.attachmentId", label: "Audio File", options:{filename:"data.attachmentFilename", accept:"audio/*"}},
168 | {type: "upload", path: "iconUrl", label:"Cover Art", options:{accept:"image/*", filename:"data.imageFilename", delete:"/{{.StreamID}}/delete-icon", rules:{width:800, height:800, types:["webp"]}}}
169 | {type: "text", path: "data.length", label: "Length"},
170 | {type: "toggle", path: "isFeatured", options:{true-text:"Featured. Highlighted on track list", false-text:"Featured?"}}
171 | ]
172 | }
173 | {
174 | type: layout-vertical
175 | label: Description
176 | children: [
177 | {type: "textarea", path: "data.lyrics", description:"Enter lyrics or song description. Displays on this song's detail page.", options:{rows: 16}}
178 | {type: "toggle", path: "data.explicit", options:{true-text:"Explicit Lyrics", false-text:"Explicit Lyrics"}}
179 | ]
180 | }
181 | {
182 | type: layout-vertical
183 | label: Metadata
184 | children: [
185 | {type: "select", path: "data.license", label: "License", options:{provider:"bandwagon-album.licenses"}},
186 | {type: "text", path: "data.tags", label: "Tags", description:"Enter #Hashtags separated by spaces."},
187 | {type: "text", path: "data.genre", label: "Genre"},
188 | {type: "text", path: "data.artist", label: "Artist"},
189 | {type: "text", path: "data.composer", label: "Composer"},
190 | {type: "text", path: "data.producer", label: "Producer"},
191 | {type: "text", path: "data.publisher", label: "Publisher"},
192 | {type: "text", path: "data.isrc", label: "ISRC"},
193 | ]
194 | }
195 | {
196 | type: layout-vertical
197 | label: Links
198 | children: [
199 | {type: "text", path: "data.streamURL", label: "Video Stream URL"},
200 | {type:"text", path:"data.links.AMAZON", options:{placeholder:"Amazon Music"}}
201 | {type:"text", path:"data.links.APPLE", options:{placeholder:"Apple Music"}}
202 | {type:"text", path:"data.links.BANDCAMP", options:{placeholder:"Bandcamp"}}
203 | {type:"text", path:"data.links.GOOGLE", options:{placeholder:"Google Play"}}
204 | {type:"text", path:"data.links.SOUNDCLOUD", options:{placeholder:"Soundcloud"}}
205 | {type:"text", path:"data.links.SPOTIFY", options:{placeholder:"Spotify"}}
206 | {type:"text", path:"data.links.TIDAL", options:{placeholder:"Tidal"}}
207 | {type:"text", path:"data.links.YOUTUBE", options:{placeholder:"YouTube Music"}}
208 | {type:"text", path:"data.links.OTHER1", options:{placeholder:"Other"}}
209 | {type:"text", path:"data.links.OTHER2", options:{placeholder:"Other"}}
210 | {type:"text", path:"data.links.OTHER3", options:{placeholder:"Other"}}
211 | ]
212 | }
213 | ]
214 | }
215 | }
216 | ]
217 | }
218 | {do:"upload-attachments", category:"image", fieldname:"iconUrl", download-path:"iconUrl", accept-type:"image/*", maximum:1, rules:{width:800, height:800, types:["webp"]}}
219 | {do:"upload-attachments", action:"replace", category:"Audio", fieldname:"data.attachmentId", attachment-path:"data.attachmentId", filename-path:"data.attachmentFilename", accept-type:"audio/*", maximum:1}
220 | {do:"process-tags", paths:"data.tags"}
221 | {do:"save-and-publish"}
222 | {do:"search-index"}
223 | {do:"refresh-page"}
224 | ]
225 | }
226 |
227 | delete: {
228 | roles: ["admin", "author"]
229 | steps: [
230 | {do:"delete", title:"Delete this Song?"}
231 | {do:"refresh-page"}
232 | ]
233 | }
234 |
235 | delete-icon: {
236 | roles: ["author"]
237 | steps: [
238 | {do: "delete-attachments"}
239 | {do: "set-data", values:{"iconUrl": ""}}
240 | {do: "save"}
241 | ]
242 | }
243 | prev: {
244 | steps:[
245 | {do:"with-prev-sibling", steps:[
246 | {do:"redirect-to", url:"/{{.StreamID}}"}
247 | ]}
248 | ]
249 | }
250 |
251 | next: {
252 | steps:[
253 | {do:"with-next-sibling", steps:[
254 | {do:"redirect-to", url:"/{{.StreamID}}"}
255 | ]}
256 | ]
257 | }
258 | }
259 | }
--------------------------------------------------------------------------------
/bandwagon-song/view.html:
--------------------------------------------------------------------------------
1 | {{- $album := .Parent "view" -}}
2 | {{- $links := .Data "links" -}}
3 | {{- $streamURL := .Data "streamURL" -}}
4 | {{- $lyrics := .Data "lyrics" -}}
5 | {{- $attachmentID := .Data "attachmentId" -}}
6 | {{- $duration := .Data "length"}}
7 | {{- $iconURL := .IconURL -}}
8 |
9 | {{- if eq "" $iconURL -}}
10 | {{- $iconURL = $album.IconURL -}}
11 | {{- end -}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Track Name:
48 | {{.Label}}
49 |
50 | {{- if eq true (.Data "explicit") -}}
51 | {{icon "explicit-fill"}}
52 | {{- end -}}
53 |
54 |
65 |
66 |
67 | {{ if ne nil ($album.Data "year") -}}
68 |
69 | Release Date:
70 | {{$album.Data "year"}}
71 |
72 | {{ if ne nil (.Data "length") -}}
73 | ·
74 | {{ end }}
75 | {{- end -}}
76 |
77 | Play Time:
78 | {{.Data "length"}}
79 |
80 |
81 |
82 |
83 |
84 |
85 |
105 |
106 |
107 | {{icon "share"}} Share
108 | {{icon "thumbs-up"}} Like
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | {{- if ne nil $lyrics -}}
121 |
{{$lyrics | text}}
122 | {{- end -}}
123 |
124 |
125 |
126 |
127 |
128 |
129 | {{- if .Tags.NotEmpty -}}
130 |
131 | {{- range $index, $tag := .Tags -}}
132 | {{- if eq "Hashtag" $tag.Type}}
133 |
#{{$tag.Name}}
134 | {{- end -}}
135 | {{- end -}}
136 |
137 | {{- end -}}
138 |
139 | {{- if $links.NotEmpty -}}
140 |
141 |
Stream Online
142 | {{- range $key, $value := $links -}}
143 |
144 | {{- if ne "" $value -}}
145 | {{- $icon := "music-note-beamed" -}}
146 | {{- $label := $value -}}
147 |
148 | {{- if eq "AMAZON" $key -}}
149 | {{- $label = "Amazon Music" -}}
150 | {{- $icon = "amazon" -}}
151 |
152 | {{- else if eq "APPLE" $key -}}
153 | {{- $icon = "apple" -}}
154 | {{- $label = "Apple Music" -}}
155 |
156 | {{- else if eq "BANDCAMP" $key -}}
157 | {{- $label = "Bandcamp" -}}
158 |
159 | {{- else if eq "GOOGLE" $key -}}
160 | {{- $label = "Google Play" -}}
161 | {{- $icon = "google" -}}
162 |
163 | {{- else if eq "IHEARTRADIO" $key -}}
164 | {{- $label = "iHeartRadio" -}}
165 |
166 | {{- else if eq "PANDORA" $key -}}
167 | {{- $label = "Pandora" -}}
168 |
169 | {{- else if eq "SPOTIFY" $key -}}
170 | {{- $label = "Spotify" -}}
171 | {{- $icon = "spotify" -}}
172 |
173 | {{- else if eq "SOUNDCLOUD" $key -}}
174 | {{- $label = "SoundCloud" -}}
175 | {{- $icon = "cloud" -}}
176 |
177 | {{- else if eq "TIDAL" $key -}}
178 | {{- $label = "Tidal" -}}
179 |
180 | {{- else if eq "YOUTUBE" $key -}}
181 | {{- $label = "YouTube Music" -}}
182 | {{- $icon = "youtube" -}}
183 |
184 | {{- else -}}
185 | {{- $label = domainOnly $value -}}
186 |
187 | {{- end -}}
188 |
189 |
190 | {{ $label }}
191 |
192 | {{- end -}}
193 |
194 | {{- end -}}
195 |
196 | {{- end -}}
197 |
198 | {{- if ne nil $streamURL -}}
199 |
200 | Music Video
201 |
202 | {{- end -}}
203 |
204 |
205 | {{- if ne nil (.Data "artist") -}}
206 |
Artist: {{.Data "artist"}}
207 | {{- end -}}
208 |
209 | {{- if ne nil (.Data "composer") -}}
210 |
Composer: {{.Data "composer"}}
211 | {{- end -}}
212 |
213 | {{- if ne nil (.Data "producer") -}}
214 |
Producer: {{.Data "producer"}}
215 | {{- end -}}
216 |
217 |
218 | {{- if ne nil (.Data "isrc") -}}
219 |
ISRC: {{.Data "isrc"}}
220 | {{- end -}}
221 |
222 | {{- $license := .DataString "license"}}
223 | {{- if eq "COPYRIGHT" $license -}}
224 |
225 |
License
226 |
227 | © Copyright
228 | {{- if ne nil (.Data "year") }}
229 | {{.Data "year"}}.
230 | {{- else -}}
231 | .
232 | {{- end }}
233 | All Rights Reserved.
234 |
235 |
236 | {{- else if ne "" $license -}}
237 | {{- $licenses := .Dataset "bandwagon-album.licenses" -}}
238 | {{- $licenseInfo := $licenses.Value $license -}}
239 |
240 |
License
241 |
250 |
251 | {{- end -}}
252 |
253 |
254 |
255 |
256 | {{- if .UserCan "edit" -}}
257 |
258 | Edit Song
259 |
260 |
261 |
264 | {{- end -}}
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------