<%@ Page Title="File Forum" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" %>
<%@ Register Assembly="AjaxUploader" Namespace="AjaxUploader.Controls" TagPrefix="au" %>
<script runat="server">
    // A COMPLETE mini-application, not a feature mockup: post a topic with
    // attachments, see it stored, rendered (images inline), and deletable.
    // This is the classic v4 "file-based forum" sample rebuilt on v5.
    //
    // Flow worth understanding: FileUploaded fires during the SAME postback as
    // the Post button (post data is processed before click handlers), so the
    // stashed files are ready by the time PostTopic_Click runs.
    private List<object[]> _staged = new List<object[]>();

    private string ForumRoot { get { return Server.MapPath("~/App_Data/ForumDemo"); } }

    public class TopicVm
    {
        public string DirName { get; set; }
        public string TitleHtml { get; set; }
        public string BodyHtml { get; set; }
        public string AttachmentsHtml { get; set; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        // Bind once on first load; the Repeater restores its items from
        // ViewState on postback, which is what routes the Delete clicks.
        if (!IsPostBack) BindTopics();
    }

    protected void Uploader1_FileUploaded(object sender, AjaxUploader.Events.FileUploadedEventArgs e)
    {
        _staged.Add(new object[] { e.FileGuid, e.FileName ?? "file", e.FileSize });
    }

    protected void PostTopic_Click(object sender, EventArgs e)
    {
        var title = txtTitle.Text.Trim();
        if (title.Length == 0)
        {
            lblMsg.Text = "Give the topic a title first.";
            return;
        }

        var topicDir = System.IO.Path.Combine(ForumRoot,
            DateTime.UtcNow.Ticks.ToString("D19") + "_" + Guid.NewGuid().ToString("N").Substring(0, 8));
        System.IO.Directory.CreateDirectory(topicDir);
        System.IO.File.WriteAllLines(System.IO.Path.Combine(topicDir, "topic.txt"),
            new[] { title, txtBody.Text.Replace("\r", " ").Replace("\n", " ") });

        var svc = new AjaxUploader.Services.UploadService();
        foreach (var f in _staged)
        {
            // Server-generated prefix, original name kept AFTER it for display;
            // the serving handler re-encodes it, never trusts it as a path.
            var stored = Guid.NewGuid().ToString("N") + "_" +
                System.IO.Path.GetFileName((string)f[1]);
            svc.MoveFile((Guid)f[0], System.IO.Path.Combine(topicDir, stored));
        }

        lblMsg.Text = "Posted <strong>" + Server.HtmlEncode(title) + "</strong> with "
            + _staged.Count + " attachment(s).";
        txtTitle.Text = ""; txtBody.Text = "";
    }

    protected void rptTopics_ItemCommand(object source, RepeaterCommandEventArgs e)
    {
        if (e.CommandName != "del") return;
        var name = System.IO.Path.GetFileName((string)e.CommandArgument);   // defuse traversal
        var dir = System.IO.Path.Combine(ForumRoot, name);
        if (System.IO.Directory.Exists(dir)) System.IO.Directory.Delete(dir, true);
        lblMsg.Text = "Topic deleted (attachments included - one folder per topic makes cleanup trivial).";
    }

    protected void Page_PreRender(object sender, EventArgs e)
    {
        // Rebind at PreRender so posts/deletes made THIS request show immediately.
        BindTopics();
    }

    private void BindTopics()
    {
        var topics = new List<TopicVm>();
        if (System.IO.Directory.Exists(ForumRoot))
        {
            var dirs = System.IO.Directory.GetDirectories(ForumRoot);
            Array.Sort(dirs); Array.Reverse(dirs);
            foreach (var dir in dirs)
            {
                var meta = System.IO.Path.Combine(dir, "topic.txt");
                if (!System.IO.File.Exists(meta)) continue;
                var lines = System.IO.File.ReadAllLines(meta);
                var dirName = System.IO.Path.GetFileName(dir);
                var sb = new System.Text.StringBuilder();
                foreach (var f in System.IO.Directory.GetFiles(dir))
                {
                    var fn = System.IO.Path.GetFileName(f);
                    if (fn == "topic.txt") continue;
                    var display = fn.Length > 33 ? fn.Substring(33) : fn;
                    var url = "ForumAttachment.ashx?t=" + Server.UrlEncode(dirName) + "&f=" + Server.UrlEncode(fn);
                    var ext = System.IO.Path.GetExtension(display).ToLowerInvariant();
                    if (ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif" || ext == ".webp")
                        sb.Append("<img src='").Append(url).Append("&inline=1' alt='").Append(Server.HtmlEncode(display))
                          .Append("' style='max-height:90px;max-width:140px;margin:0 6px 6px 0;border-radius:6px' /> ");
                    else
                        sb.Append("<a class='me-3' href='").Append(url).Append("'>&#128206; ")
                          .Append(Server.HtmlEncode(display)).Append("</a> ");
                }
                topics.Add(new TopicVm
                {
                    DirName = dirName,
                    TitleHtml = Server.HtmlEncode(lines.Length > 0 ? lines[0] : "(untitled)"),
                    BodyHtml = Server.HtmlEncode(lines.Length > 1 ? lines[1] : ""),
                    AttachmentsHtml = sb.ToString()
                });
            }
        }
        rptTopics.DataSource = topics;
        rptTopics.DataBind();
        pnlEmpty.Visible = topics.Count == 0;
    }
</script>
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitleContent" runat="server">File Forum - AjaxUploader v5.2 Demos</asp:Content>
<asp:Content ID="HeaderContent" ContentPlaceHolderID="HeaderTitle" runat="server">File Forum (complete mini-app)</asp:Content>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<div class="demo-card"> <div class="demo-card-header">A complete workflow, not a mockup</div> <div class="demo-card-body">
 <p class="demo-description"> The classic v4 &quot;file-based forum&quot; rebuilt on v5: write a topic, attach files,
 post - the attachments are <em>persisted</em> (one folder per topic under <code>App_Data</code>), <em>rendered back</em>
 (images inline through a safe serving handler), and <em>deleted with their topic</em>. Everything a real integration
 does, in one page you can copy from. </p>
 <div class="demo-expect"><strong>What you should see:</strong> attach files, click Post, and your topic appears below with image attachments displayed inline and other files as download links. The staged temp files are MOVED into the topic's folder during the same postback as the Post click - <code>FileUploaded</code> runs first, then the click handler.</div>

 <div class="mb-2"><asp:TextBox ID="txtTitle" runat="server" CssClass="form-control" placeholder="Topic title" /></div>
 <div class="mb-2"><asp:TextBox ID="txtBody" runat="server" TextMode="MultiLine" Rows="2" CssClass="form-control" placeholder="Say something..." /></div>
 <au:AjaxFileUpload ID="Uploader1" runat="server"
     AllowMultiple="true"
     AutoUpload="true"
     ShowProgress="true"
     ButtonText="Attach files"
     OnFileUploaded="Uploader1_FileUploaded" />
 <p class="mt-2"><asp:Button ID="BtnPost" runat="server" Text="Post Topic" CssClass="btn btn-primary btn-sm" OnClick="PostTopic_Click" /></p>
 <div class="small text-muted mb-2"><asp:Label ID="lblMsg" runat="server" /></div>

 <div class="fw-bold mb-2 mt-3">Topics</div>
 <asp:Repeater ID="rptTopics" runat="server" OnItemCommand="rptTopics_ItemCommand">
  <ItemTemplate>
   <div class="border rounded p-3 mb-3">
    <div class="d-flex justify-content-between">
     <strong><%# Eval("TitleHtml") %></strong>
     <asp:LinkButton runat="server" CssClass="small text-danger" CommandName="del"
         CommandArgument='<%# Eval("DirName") %>' Text="Delete" />
    </div>
    <div class="text-muted small mb-2"><%# Eval("BodyHtml") %></div>
    <%# Eval("AttachmentsHtml") %>
   </div>
  </ItemTemplate>
 </asp:Repeater>
 <asp:Panel ID="pnlEmpty" runat="server" Visible="false"><em class="text-muted">No topics yet - post the first one above.</em></asp:Panel>

 <div class="code-panel"> <pre><code>// FileUploaded runs BEFORE the button's click handler on the same postback,
// so stage the files in the event and persist them in the click:
private List&lt;object[]&gt; _staged = new List&lt;object[]&gt;();

protected void Uploader1_FileUploaded(object sender, FileUploadedEventArgs e)
{
    _staged.Add(new object[] { e.FileGuid, e.FileName, e.FileSize });
}

protected void PostTopic_Click(object sender, EventArgs e)
{
    var topicDir = CreateTopicFolder(txtTitle.Text);
    foreach (var f in _staged)
        new UploadService().MoveFile((Guid)f[0],
            Path.Combine(topicDir, Guid.NewGuid().ToString("N") + "_" + Path.GetFileName((string)f[1])));
}</code></pre> </div>
</div> </div>
</asp:Content>
