A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

235 lines
8.6 KiB

  1. {{define "collection"}}<!DOCTYPE HTML>
  2. <html {{if .Language}}lang="{{.Language}}"{{end}} dir="{{.Direction}}">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>{{.DisplayTitle}}{{if not .SingleUser}} &mdash; {{.SiteName}}{{end}}</title>
  6. <link rel="stylesheet" type="text/css" href="/css/write.css" />
  7. <link rel="shortcut icon" href="/favicon.ico" />
  8. <link rel="canonical" href="{{.CanonicalURL}}">
  9. {{if gt .CurrentPage 1}}<link rel="prev" href="{{.PrevPageURL .Prefix .CurrentPage .IsTopLevel}}">{{end}}
  10. {{if lt .CurrentPage .TotalPages}}<link rel="next" href="{{.NextPageURL .Prefix .CurrentPage .IsTopLevel}}">{{end}}
  11. {{if not .IsPrivate}}<link rel="alternate" type="application/rss+xml" title="{{.DisplayTitle}} &raquo; Feed" href="{{.CanonicalURL}}feed/" />{{end}}
  12. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  13. <meta name="generator" content="WriteFreely">
  14. <meta name="description" content="{{.Description}}">
  15. <meta itemprop="name" content="{{.DisplayTitle}}">
  16. <meta itemprop="description" content="{{.Description}}">
  17. <meta name="twitter:card" content="summary">
  18. <meta name="twitter:title" content="{{.DisplayTitle}}">
  19. <meta name="twitter:image" content="{{.AvatarURL}}">
  20. <meta name="twitter:description" content="{{.Description}}">
  21. <meta property="og:title" content="{{.DisplayTitle}}" />
  22. <meta property="og:site_name" content="{{.DisplayTitle}}" />
  23. <meta property="og:type" content="article" />
  24. <meta property="og:url" content="{{.CanonicalURL}}" />
  25. <meta property="og:description" content="{{.Description}}" />
  26. <meta property="og:image" content="{{.AvatarURL}}">
  27. {{if .StyleSheet}}<style type="text/css">{{.StyleSheetDisplay}}</style>{{end}}
  28. <style type="text/css">
  29. body#collection header {
  30. max-width: 40em;
  31. margin: 1em auto;
  32. text-align: left;
  33. padding: 0;
  34. }
  35. body#collection header.multiuser {
  36. max-width: 100%;
  37. margin: 1em;
  38. }
  39. body#collection header nav:not(.pinned-posts) {
  40. display: inline;
  41. }
  42. body#collection header nav.dropdown-nav,
  43. body#collection header nav.tabs,
  44. body#collection header nav.tabs a:first-child {
  45. margin: 0 0 0 1em;
  46. }
  47. </style>
  48. {{if .RenderMathJax}}
  49. <!-- Add mathjax logic -->
  50. {{template "mathjax" .}}
  51. {{end}}
  52. <!-- Add highlighting logic -->
  53. {{template "highlighting" . }}
  54. </head>
  55. <body id="collection" itemscope itemtype="http://schema.org/WebPage">
  56. {{template "user-navigation" .}}
  57. {{if .Silenced}}
  58. {{template "user-silenced"}}
  59. {{end}}
  60. <header>
  61. <h1 dir="{{.Direction}}" id="blog-title"><a href="/{{if .IsTopLevel}}{{else}}{{.Prefix}}{{.Alias}}/{{end}}" class="h-card p-author u-url" rel="me author">{{.DisplayTitle}}</a></h1>
  62. {{if .Description}}<p class="description p-note">{{.Description}}</p>{{end}}
  63. {{/*if not .Public/*}}
  64. <!--p class="meta-note"><span>Private collection</span>. Only you can see this page.</p-->
  65. {{/*end*/}}
  66. {{if .PinnedPosts}}<nav class="pinned-posts">
  67. {{range .PinnedPosts}}<a class="pinned" href="{{if not $.SingleUser}}/{{$.Alias}}/{{.Slug.String}}{{else}}{{.CanonicalURL $.Host}}{{end}}">{{.PlainDisplayTitle}}</a>{{end}}</nav>
  68. {{end}}
  69. </header>
  70. {{if .Posts}}<section id="wrapper" itemscope itemtype="http://schema.org/Blog">{{else}}<div id="wrapper">{{end}}
  71. {{if .IsWelcome}}
  72. <div id="welcome">
  73. <h2>Welcome, <strong>{{.Username}}</strong>!</h2>
  74. <p>This is your new blog.</p>
  75. <p><a class="simple-cta" href="/#{{.Alias}}">Start writing</a>, or <a class="simple-cta" href="/me/c/{{.Alias}}">customize</a> your blog.</p>
  76. <p>Check out our <a class="simple-cta" href="https://guides.write.as/writing/?pk_campaign=welcome">writing guide</a> to see what else you can do, and <a class="simple-cta" href="/contact">get in touch</a> anytime with questions or feedback.</p>
  77. </div>
  78. {{end}}
  79. {{template "posts" .}}
  80. {{if gt .TotalPages 1}}<nav id="paging" class="content-container clearfix">
  81. {{if or (and .Format.Ascending (lt .CurrentPage .TotalPages)) (isRTL .Direction)}}
  82. {{if gt .CurrentPage 1}}<a href="{{.PrevPageURL .Prefix .CurrentPage .IsTopLevel}}">&#8672; {{if and .Format.Ascending (lt .CurrentPage .TotalPages)}}Previous{{else}}Newer{{end}}</a>{{end}}
  83. {{if lt .CurrentPage .TotalPages}}<a style="float:right;" href="{{.NextPageURL .Prefix .CurrentPage .IsTopLevel}}">{{if and .Format.Ascending (lt .CurrentPage .TotalPages)}}Next{{else}}Older{{end}} &#8674;</a>{{end}}
  84. {{else}}
  85. {{if lt .CurrentPage .TotalPages}}<a href="{{.NextPageURL .Prefix .CurrentPage .IsTopLevel}}">&#8672; Older</a>{{end}}
  86. {{if gt .CurrentPage 1}}<a style="float:right;" href="{{.PrevPageURL .Prefix .CurrentPage .IsTopLevel}}">Newer &#8674;</a>{{end}}
  87. {{end}}
  88. </nav>{{end}}
  89. {{if .Posts}}</section>{{else}}</div>{{end}}
  90. {{if .ShowFooterBranding }}
  91. <footer>
  92. <hr />
  93. <nav dir="ltr">
  94. {{if not .SingleUser}}<a class="home pubd" href="/">{{.SiteName}}</a> &middot; {{end}}powered by <a style="margin-left:0" href="https://writefreely.org">writefreely</a>
  95. </nav>
  96. </footer>
  97. {{ end }}
  98. </body>
  99. {{if .CanShowScript}}
  100. {{range .ExternalScripts}}<script type="text/javascript" src="{{.}}" async></script>{{end}}
  101. {{if .Script}}<script type="text/javascript">{{.ScriptDisplay}}</script>{{end}}
  102. {{end}}
  103. <script src="/js/h.js"></script>
  104. <script src="/js/localdate.js"></script>
  105. <script src="/js/postactions.js"></script>
  106. <script type="text/javascript">
  107. var deleting = false;
  108. function delPost(e, id, owned) {
  109. e.preventDefault();
  110. if (deleting) {
  111. return;
  112. }
  113. // TODO: UNDO!
  114. if (window.confirm('Are you sure you want to delete this post?')) {
  115. // AJAX
  116. deletePost(id, "", function() {
  117. // Remove post from list
  118. var $postEl = document.getElementById('post-' + id);
  119. $postEl.parentNode.removeChild($postEl);
  120. // TODO: add next post from this collection at the bottom
  121. });
  122. }
  123. }
  124. var deletePost = function(postID, token, callback) {
  125. deleting = true;
  126. var $delBtn = document.getElementById('post-' + postID).getElementsByClassName('delete action')[0];
  127. $delBtn.innerHTML = '...';
  128. var http = new XMLHttpRequest();
  129. var url = "/api/posts/" + postID;
  130. http.open("DELETE", url, true);
  131. http.onreadystatechange = function() {
  132. if (http.readyState == 4) {
  133. deleting = false;
  134. if (http.status == 204) {
  135. callback();
  136. } else if (http.status == 409) {
  137. $delBtn.innerHTML = 'delete';
  138. alert("Post is synced to another account. Delete the post from that account instead.");
  139. // TODO: show "remove" button instead of "delete" now
  140. // Persist that state.
  141. // Have it remove the post locally only.
  142. } else {
  143. $delBtn.innerHTML = 'delete';
  144. alert("Failed to delete." + (http.status>=500?" Please try again.":""));
  145. }
  146. }
  147. }
  148. http.send();
  149. };
  150. var pinning = false;
  151. function pinPost(e, postID, slug, title) {
  152. e.preventDefault();
  153. if (pinning) {
  154. return;
  155. }
  156. pinning = true;
  157. var callback = function() {
  158. // Visibly remove post from collection
  159. var $postEl = document.getElementById('post-' + postID);
  160. $postEl.parentNode.removeChild($postEl);
  161. var $header = document.querySelector('header:not(.multiuser)');
  162. var $pinnedNavs = $header.getElementsByTagName('nav');
  163. // Add link to nav
  164. var link = '<a class="pinned" href="/{{.Alias}}/'+slug+'">'+title+'</a>';
  165. if ($pinnedNavs.length == 0) {
  166. $header.insertAdjacentHTML("beforeend", '<nav>'+link+'</nav>');
  167. } else {
  168. $pinnedNavs[0].insertAdjacentHTML("beforeend", link);
  169. }
  170. };
  171. var $pinBtn = document.getElementById('post-' + postID).getElementsByClassName('pin action')[0];
  172. $pinBtn.innerHTML = '...';
  173. var http = new XMLHttpRequest();
  174. var url = "/api/collections/{{.Alias}}/pin";
  175. var params = [ { "id": postID } ];
  176. http.open("POST", url, true);
  177. http.setRequestHeader("Content-type", "application/json");
  178. http.onreadystatechange = function() {
  179. if (http.readyState == 4) {
  180. pinning = false;
  181. if (http.status == 200) {
  182. callback();
  183. } else if (http.status == 409) {
  184. $pinBtn.innerHTML = 'pin';
  185. alert("Post is synced to another account. Delete the post from that account instead.");
  186. // TODO: show "remove" button instead of "delete" now
  187. // Persist that state.
  188. // Have it remove the post locally only.
  189. } else {
  190. $pinBtn.innerHTML = 'pin';
  191. alert("Failed to pin." + (http.status>=500?" Please try again.":""));
  192. }
  193. }
  194. }
  195. http.send(JSON.stringify(params));
  196. };
  197. try {
  198. WebFontConfig = {
  199. custom: { families: [ 'Lora:400,700:latin', 'Open+Sans:400,700:latin' ], urls: [ '/css/fonts.css' ] }
  200. };
  201. (function() {
  202. var wf = document.createElement('script');
  203. wf.src = '/js/webfont.js';
  204. wf.type = 'text/javascript';
  205. wf.async = 'true';
  206. var s = document.getElementsByTagName('script')[0];
  207. s.parentNode.insertBefore(wf, s);
  208. })();
  209. } catch (e) {}
  210. </script>
  211. </html>{{end}}