Skip to content

Commit 9fed834

Browse files
Initial server-side "continue topic" code working
1 parent 233c245 commit 9fed834

File tree

3 files changed

+144
-10
lines changed

3 files changed

+144
-10
lines changed

OurUmbraco.Site/Views/Partials/Forum/Thread.cshtml

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@
121121
<div>
122122
<p style="font-size: 1.2em; font-weight: bold;">This forum is in read-only mode [LINK TO EXPLANATION].</p>
123123
</div>
124-
@if (Model.DiscourseTopic != null && Model.DiscourseTopic.Visible && Model.DiscourseTopic.ReplyCount > 0)
124+
@if (Model.DiscourseTopic != null)
125125
{
126126
<p style="font-size: 1.2em; font-weight: bold;">You can <a href="@Model.DiscourseTopic.RedirectUrl">continue this topic on the new forum</a> by tapping the "Continue discussion" button below.</p>
127127
<div id='discourse-comments'>
128128
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
129129
<script type="text/javascript">
130130
DiscourseEmbed = {
131131
discourseUrl: '@(System.Configuration.ConfigurationManager.AppSettings["DiscourseApiBaseUrl"])',
132-
discourseEmbedUrl: '@Request.Url.AbsoluteUri',
132+
topicId: @Model.DiscourseTopic.Id,
133133
};
134134
135135
(function () {
@@ -144,8 +144,44 @@
144144
{
145145
<p style="font-size: 1.2em; font-weight: bold;">You can continue this topic on the <a href="https://forum.umbraco.com">new forum</a> by tapping the "Continue discussion" link below.</p>
146146
<p>
147-
<a href="#" class="create-topic">Continue Discussion</a>
147+
<a href="#" id="create-topic">Continue Discussion</a>
148148
</p>
149+
150+
<style type="text/css">
151+
.ready {
152+
background-color: #4CAF50;
153+
color: white;
154+
padding: 10px 24px;
155+
text-align: center;
156+
text-decoration: none;
157+
display: inline-block;
158+
font-size: 16px;
159+
margin: 4px 2px;
160+
cursor: pointer;
161+
border-radius: 8px;
162+
}
163+
</style>
164+
<script type="text/javascript">
165+
$('#create-topic').click(function() {
166+
(async () => {
167+
const rawResponse = await fetch('/umbraco/api/Discourse/Post', {
168+
method: 'POST',
169+
headers: {
170+
'Accept': 'application/json',
171+
'Content-Type': 'application/json'
172+
},
173+
body: JSON.stringify({ "TopicId": @Model.Id })
174+
});
175+
const content = await rawResponse.json();
176+
const linkElement = $('#create-topic');
177+
linkElement.attr('href', content);
178+
linkElement.attr('id', '');
179+
linkElement.html('Continue to the new topic');
180+
linkElement.addClass('ready')
181+
linkElement.unbind('click');
182+
})();
183+
});
184+
</script>
149185
}
150186
</div>
151187
</div>

OurUmbraco.Site/web.vsts.config

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,11 @@
165165
<add key ="EmailFrom" value="robot@umbraco.org" />
166166

167167
<!-- Discourse -->
168-
<add key="DiscourseApiBaseUrl" value="https://forum.westeurope.cloudapp.azure.com" />
168+
<add key="DiscourseEnabled" value="false" />
169+
<add key="DiscourseApiBaseUrl" value="https://forum.umbraco.com/" />
169170
<add key="DiscourseApiUsername" value="system" />
170171
<add key="DiscourseApiKey" value="#{DiscourseApiKey}#" />
171-
<add key="DiscourseEnabled" value="false" />
172+
<add key="OurUmbracoUrl" value="https://our.umbraco.com/" />
172173
</appSettings>
173174
<connectionStrings>
174175
<remove name="umbracoDbDSN" />
Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
using Newtonsoft.Json;
22
using OurUmbraco.Forum.Models;
33
using OurUmbraco.Forum.Services;
4+
using System.Net.Http;
5+
using System;
46
using System.Web.Http;
57
using Umbraco.Core;
68
using Umbraco.Web.WebApi;
9+
using System.Collections.Generic;
10+
using System.Linq;
711

812
namespace OurUmbraco.Forum.Controllers
913
{
@@ -14,26 +18,119 @@ public IHttpActionResult Post(TopicModel topicModel)
1418
{
1519
var topicService = new TopicService(ApplicationContext.DatabaseContext);
1620
var topic = topicService.GetById(topicModel.TopicId);
17-
var body = topic.Body.Replace(": /media/upload/", ": https://our.umbraco.com/media/upload/");
18-
body = body + "\n<hr>\n<small>This is a companion discussion topic for the original entry at <a href=\"https://cultiv.nl/blog/released-search-engine-sitemap-package\">https://cultiv.nl/blog/released-search-engine-sitemap-package</a></small>";
21+
var forumUrl = System.Configuration.ConfigurationManager.AppSettings["OurUmbracoUrl"];
22+
23+
var body = topic.Body.Replace("/media/upload/", $"{forumUrl}/media/upload/");
24+
var topicUrl = $"{forumUrl}/forum/{topic.Id}-{topic.Title.ToUrlSegment()}";
25+
body = body + $"\n<hr>\n<small>This is a companion discussion topic for the original entry at <a href=\"{topicUrl}\">{topicUrl}</a></small>";
1926
var discourseCreateTopic = new DiscourseCreateTopic
2027
{
2128
Title = topic.Title,
2229
Raw = body,
2330
Category = 5,
2431
CreatedAt = topic.Created,
25-
EmbedUrl = $"https://our.umbraco.com/forum/using-umbraco/{topic.Id}-{topic.Title.ToUrlSegment()}",
32+
EmbedUrl = topicUrl,
2633
ExternalId = topic.Id
2734
};
35+
var redirectUrl = CreateTopicOnDiscourse(discourseCreateTopic);
36+
return Ok(redirectUrl);
37+
}
38+
39+
private string CreateTopicOnDiscourse(DiscourseCreateTopic createTopic)
40+
{
41+
var forumBaseUrl = System.Configuration.ConfigurationManager.AppSettings["DiscourseApiBaseUrl"];
42+
using (var client = new HttpClient())
43+
{
44+
client.BaseAddress = new Uri(forumBaseUrl);
45+
client.DefaultRequestHeaders.Add("Api-Key", $"{System.Configuration.ConfigurationManager.AppSettings["DiscourseApiKey"]}");
46+
client.DefaultRequestHeaders.Add("Api-Username", $"{System.Configuration.ConfigurationManager.AppSettings["DiscourseApiUsername"]}");
47+
48+
var result = client.PostAsJsonAsync("posts.json", createTopic).Result;
49+
if (result.IsSuccessStatusCode == false)
50+
{
51+
var resultContent = result.Content.ReadAsStringAsync().Result;
52+
var errorModel = JsonConvert.DeserializeObject<ErrorModel>(resultContent);
53+
var firstError = errorModel.Errors.FirstOrDefault();
54+
if (firstError != null && firstError == "External has already been taken")
55+
{
56+
// the topic exists, see if we can find the URL for it
57+
var topicUrl = GetTopicByExternalId(createTopic.ExternalId);
58+
return topicUrl;
59+
}
60+
return null;
61+
}
62+
else
63+
{
64+
var resultContent = result.Content.ReadAsStringAsync().Result;
65+
var discourseTopic = JsonConvert.DeserializeObject<DiscourseTopic>(resultContent);
66+
return forumBaseUrl + discourseTopic.PostUrl;
67+
}
68+
}
69+
}
70+
71+
private string GetTopicByExternalId(int id)
72+
{
73+
var forumBaseUrl = System.Configuration.ConfigurationManager.AppSettings["DiscourseApiBaseUrl"];
74+
using (var client = new HttpClient())
75+
{
76+
client.BaseAddress = new Uri(forumBaseUrl);
77+
client.DefaultRequestHeaders.Add("Api-Key", $"{System.Configuration.ConfigurationManager.AppSettings["DiscourseApiKey"]}");
78+
client.DefaultRequestHeaders.Add("Api-Username", $"{System.Configuration.ConfigurationManager.AppSettings["DiscourseApiUsername"]}");
2879

29-
var x = discourseCreateTopic;
30-
return Ok(x);
80+
var result = client.GetAsync($"t/external_id/{id}.json").Result;
81+
if (result.IsSuccessStatusCode == false)
82+
{
83+
return null;
84+
}
85+
else
86+
{
87+
var resultContent = result.Content.ReadAsStringAsync().Result;
88+
var discourseTopic = JsonConvert.DeserializeObject<DiscoursePostStream>(resultContent);
89+
var firstPost = discourseTopic.PostStream.Posts.FirstOrDefault();
90+
if(firstPost == null)
91+
{
92+
return null;
93+
}
94+
return forumBaseUrl + firstPost.PostUrl;
95+
}
96+
}
3197
}
3298

3399
public class TopicModel
34100
{
35101
public int TopicId { get; set; }
36102
}
37103

104+
internal class DiscourseTopic
105+
{
106+
[JsonProperty("post_url")]
107+
public string PostUrl { get; set; }
108+
}
109+
110+
internal class ErrorModel
111+
{
112+
[JsonProperty("action")]
113+
public string Action { get; set; }
114+
115+
[JsonProperty("errors")]
116+
public List<string> Errors { get; set; }
117+
}
118+
119+
internal class DiscoursePostStream
120+
{
121+
[JsonProperty("post_stream")]
122+
public PostStream PostStream { get; set; }
123+
}
124+
125+
internal class PostStream
126+
{
127+
[JsonProperty("posts")]
128+
public List<DiscoursePost> Posts { get; set; }
129+
}
130+
internal class DiscoursePost
131+
{
132+
[JsonProperty("post_url")]
133+
public string PostUrl { get; set; }
134+
}
38135
}
39136
}

0 commit comments

Comments
 (0)