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.
 
 
 
 
 

156 lines
6.6 KiB

  1. {{define "post"}}<!DOCTYPE HTML>
  2. <html {{if .Language.Valid}}lang="{{.Language.String}}"{{end}} dir="{{.Direction}}">
  3. <head prefix="og: http://ogp.me/ns# article: http://ogp.me/ns/article#">
  4. <meta charset="utf-8">
  5. <title>{{.PlainDisplayTitle}} {{localhtml "title dash" .Language.String}} {{.Collection.DisplayTitle}}</title>
  6. <link rel="stylesheet" type="text/css" href="/css/write.css" />
  7. {{if .CustomCSS}}<link rel="stylesheet" type="text/css" href="/local/custom.css" />{{end}}
  8. <link rel="shortcut icon" href="/favicon.ico" />
  9. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  10. <link rel="canonical" href="{{.CanonicalURL .Host}}" />
  11. <meta name="generator" content="WriteFreely">
  12. <meta name="title" content="{{.PlainDisplayTitle}} {{localhtml "title dash" .Language.String}} {{if .Collection.Title}}{{.Collection.Title}}{{else}}{{.Collection.Alias}}{{end}}">
  13. <meta name="description" content="{{.Summary}}">
  14. {{if gt .Views 1}}<meta name="twitter:label1" value="Views">
  15. <meta name="twitter:data1" value="{{largeNumFmt .Views}}">{{end}}
  16. <meta name="author" content="{{.Collection.Title}}" />
  17. <meta itemprop="description" content="{{.Summary}}">
  18. <meta itemprop="datePublished" content="{{.CreatedDate}}" />
  19. <meta name="twitter:card" content="summary">
  20. <meta name="twitter:description" content="{{.Summary}}">
  21. <meta name="twitter:title" content="{{.PlainDisplayTitle}} {{localhtml "title dash" .Language.String}} {{if .Collection.Title}}{{.Collection.Title}}{{else}}{{.Collection.Alias}}{{end}}">
  22. {{if gt (len .Images) 0}}<meta name="twitter:image" content="{{index .Images 0}}">{{else}}<meta name="twitter:image" content="{{.Collection.AvatarURL}}">{{end}}
  23. <meta property="og:title" content="{{.PlainDisplayTitle}}" />
  24. <meta property="og:description" content="{{.Summary}}" />
  25. <meta property="og:site_name" content="{{.Collection.DisplayTitle}}" />
  26. <meta property="og:type" content="article" />
  27. <meta property="og:url" content="{{.CanonicalURL .Host}}" />
  28. <meta property="og:updated_time" content="{{.Created8601}}" />
  29. {{range .Images}}<meta property="og:image" content="{{.}}" />{{else}}<meta property="og:image" content="{{.Collection.AvatarURL}}">{{end}}
  30. <meta property="article:published_time" content="{{.Created8601}}">
  31. {{template "collection-meta" .}}
  32. {{if .Collection.StyleSheet}}<style type="text/css">{{.Collection.StyleSheetDisplay}}</style>{{end}}
  33. <style type="text/css">
  34. body footer {
  35. max-width: 40rem;
  36. margin: 0 auto;
  37. }
  38. body#post header {
  39. padding: 1em 1rem;
  40. }
  41. </style>
  42. {{if .Collection.RenderMathJax}}
  43. <!-- Add mathjax logic -->
  44. {{template "mathjax" . }}
  45. {{end}}
  46. <!-- Add highlighting logic -->
  47. {{template "highlighting" .}}
  48. </head>
  49. <body id="post">
  50. <div id="overlay"></div>
  51. {{template "user-navigation" .}}
  52. {{if .Silenced}}
  53. {{template "user-silenced"}}
  54. {{end}}
  55. <article id="post-body" class="{{.Font}} h-entry">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name{{if $.Collection.Format.ShowDates}} dated{{end}}">{{.FormattedDisplayTitle}}</h2>{{end}}{{if and $.Collection.Format.ShowDates (not .IsPinned)}}<time class="dt-published" datetime="{{.Created8601}}" pubdate itemprop="datePublished" content="{{.Created}}">{{.DisplayDate}}</time>{{end}}<div class="e-content">{{.HTMLContent}}</div></article>
  56. {{ if .Collection.ShowFooterBranding }}
  57. <footer dir="ltr">
  58. <p style="text-align: left">Published by <a rel="author" href="{{if .IsTopLevel}}/{{else}}/{{.Collection.Alias}}/{{end}}" class="h-card p-author">{{.Collection.DisplayTitle}}</a>
  59. {{ if .IsOwner }} &middot; <span class="views" dir="ltr"><strong>{{largeNumFmt .Views}}</strong> {{pluralize "view" "views" .Views}}</span>
  60. &middot; <a class="xtra-feature" href="/{{if not .SingleUser}}{{.Collection.Alias}}/{{end}}{{.Slug.String}}/edit" dir="{{.Direction}}">Edit</a>
  61. {{if .IsPinned}} &middot; <a class="xtra-feature unpin" href="/{{.Collection.Alias}}/{{.Slug.String}}/unpin" dir="{{.Direction}}" onclick="unpinPost(event, '{{.ID}}')">Unpin</a>{{end}}
  62. {{ end }}
  63. </p>
  64. <nav>
  65. {{if .PinnedPosts}}
  66. {{range .PinnedPosts}}<a class="pinned{{if eq .Slug.String $.Slug.String}} selected{{end}}" href="{{if not $.SingleUser}}/{{$.Collection.Alias}}/{{.Slug.String}}{{else}}{{.CanonicalURL $.Host}}{{end}}">{{.PlainDisplayTitle}}</a>{{end}}
  67. {{end}}
  68. </nav>
  69. <hr>
  70. <nav><p style="font-size: 0.9em">{{localhtml "published with write.as" .Language.String}}</p></nav>
  71. </footer>
  72. {{ end }}
  73. </body>
  74. {{if .Collection.CanShowScript}}
  75. {{range .Collection.ExternalScripts}}<script type="text/javascript" src="{{.}}" async></script>{{end}}
  76. {{if .Collection.Script}}<script type="text/javascript">{{.Collection.ScriptDisplay}}</script>{{end}}
  77. {{end}}
  78. <script src="/js/localdate.js"></script>
  79. <script type="text/javascript">
  80. var pinning = false;
  81. function unpinPost(e, postID) {
  82. e.preventDefault();
  83. if (pinning) {
  84. return;
  85. }
  86. pinning = true;
  87. var $footer = document.getElementsByTagName('footer')[0];
  88. var callback = function() {
  89. // Hide current page
  90. var $pinnedNavLink = $footer.getElementsByTagName('nav')[0].querySelector('.pinned.selected');
  91. $pinnedNavLink.style.display = 'none';
  92. };
  93. var $pinBtn = $footer.getElementsByClassName('unpin')[0];
  94. $pinBtn.innerHTML = '...';
  95. var http = new XMLHttpRequest();
  96. var url = "/api/collections/{{.Collection.Alias}}/unpin";
  97. var params = [ { "id": postID } ];
  98. http.open("POST", url, true);
  99. http.setRequestHeader("Content-type", "application/json");
  100. http.onreadystatechange = function() {
  101. if (http.readyState == 4) {
  102. pinning = false;
  103. if (http.status == 200) {
  104. callback();
  105. $pinBtn.style.display = 'none';
  106. $pinBtn.innerHTML = 'Pin';
  107. } else if (http.status == 409) {
  108. $pinBtn.innerHTML = 'Unpin';
  109. } else {
  110. $pinBtn.innerHTML = 'Unpin';
  111. alert("Failed to unpin." + (http.status>=500?" Please try again.":""));
  112. }
  113. }
  114. }
  115. http.send(JSON.stringify(params));
  116. };
  117. try { // Fonts
  118. WebFontConfig = {
  119. custom: { families: [ 'Lora:400,700:latin', 'Open+Sans:400,700:latin' ], urls: [ '/css/fonts.css' ] }
  120. };
  121. (function() {
  122. var wf = document.createElement('script');
  123. wf.src = '/js/webfont.js';
  124. wf.type = 'text/javascript';
  125. wf.async = 'true';
  126. var s = document.getElementsByTagName('script')[0];
  127. s.parentNode.insertBefore(wf, s);
  128. })();
  129. } catch (e) { /* ¯\_(ツ)_/¯ */ }
  130. </script>
  131. {{if and .Monetization (not .IsOwner)}}
  132. <script src="/js/webmonetization.js"></script>
  133. <script>
  134. window.collAlias = '{{.Collection.Alias}}'
  135. window.postSlug = '{{.Slug.String}}'
  136. initMonetization()
  137. </script>
  138. {{end}}
  139. </html>{{end}}