aboutsummaryrefslogtreecommitdiffstats
path: root/src/documentation/content/xdocs/examples.xml
blob: c8d4a5ddec74d1974e016040f6985e3fefc818b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?xml version="1.0" standalone="no"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- $Id$ -->
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN"
    "http://svn.apache.org/viewvc/forrest/trunk/main/webapp/resources/schema/dtd/document-v12.dtd?view=co">

<document>
  <header>
    <title>Examples</title>
    <version>$Revision$</version>
  </header>
  <body>
    <section>
      <title>Example Documents Using FOP</title>
      <p>
        These examples have been rendered using FOP:
      </p>
    <table>
      <caption>Generated examples</caption>
      <tr>
        <th>Description</th>
        <th>XSL-FO file</th>
        <th>PDF result</th>
      </tr>
      <tr>
        <td>default font characters</td>
        <td><link href="fo/fonts.fo">fonts.fo</link></td>
        <td><link href="fo/fonts.fo.pdf">fonts.fo.pdf</link></td>
      </tr>
    </table>
    <p>Other basic examples on the use of XSL-FO can be found in the FOP distribution in
       the subdirectory xml-fop/examples/fo. You can start transformation of all fo files into pdf
       files by starting xml-fop/examples/fo/runtests (only source distribution). The resulting test
       files can be found in xml-fop/examples/fo/tests
    </p>
    <p>At the moment the following files are part of the distribution:</p>
    <ul>
      <li>simple.fo - a very simple file which gives use a first impression of the structure of an XSL-FO file
      </li>
      <li>normal.fo - a simple file showing the use of a 2 level of headings, normal text and a header.
      </li>
      <li>table.fo - some table examples
      </li>
      <li>list.fo - a short tutorial how to use list fo's and properties
      </li>
      <li>images.fo - shows how to embed GIF and JPEG images into the XSL-FO file using external-graphic.
      </li>
      <li>border.fo - a not so simple example how to use borders in tables
      </li>
      <li>extensive.fo - a longer test file containing a lot of different flow objects and properties.
          A good candidate to test your bugfix or new FOP code.
      </li>
      <li>leader.fo - shows different uses of fo:leader, p.e. as rule or in a table of content
      </li>
      <li>normalex.fo - shows the use of computed property values
      </li>
      <li>inhprop.fo - shows the use of inherited property values
      </li>
      <li>instream.fo - shows the use of fo:instream-foreign-object together with SVG
      </li>
      <li>textdeko.fo - shows the use of the property text-decoration
      </li>
      <li>readme.fo - uses an old version of FOP documentation for a longer example
      </li>
    </ul>
      <p>Also, in the directory examples/fo/paginationpre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	git_model "code.gitea.io/gitea/models/git"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/unit"
	"code.gitea.io/gitea/modules/base"
	"code.gitea.io/gitea/modules/charset"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/markup"
	"code.gitea.io/gitea/modules/markup/markdown"
	"code.gitea.io/gitea/modules/notification"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/timeutil"
	"code.gitea.io/gitea/modules/util"
	"code.gitea.io/gitea/modules/web"
	"code.gitea.io/gitea/routers/common"
	"code.gitea.io/gitea/services/forms"
	wiki_service "code.gitea.io/gitea/services/wiki"
)

const (
	
n class="w"> tplWikiView base.TplName = "repo/wiki/view" tplWikiRevision base.TplName = "repo/wiki/revision" tplWikiNew base.TplName = "repo/wiki/new" tplWikiPages base.TplName = "repo/wiki/pages" ) // MustEnableWiki check if wiki is enabled, if external then redirect func MustEnableWiki(ctx *context.Context) { if !ctx.Repo.CanRead(unit.TypeWiki) && !ctx.Repo.CanRead(unit.TypeExternalWiki) { if log.IsTrace() { log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+ "User in repo has Permissions: %-+v", ctx.Doer, unit.TypeWiki, unit.TypeExternalWiki, ctx.Repo.Repository, ctx.Repo.Permission) } ctx.NotFound("MustEnableWiki", nil) return } unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalWiki) if err == nil { ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL) return } } // PageMeta wiki page meta information type PageMeta struct { Name string SubURL string UpdatedUnix timeutil.TimeStamp } // findEntryForFile finds the tree entry for a target filepath. func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) { entry, err := commit.GetTreeEntryByPath(target) if err != nil && !git.IsErrNotExist(err) { return nil, err } if entry != nil { return entry, nil } // Then the unescaped, shortest alternative var unescapedTarget string if unescapedTarget, err = url.QueryUnescape(target); err != nil { return nil, err } return commit.GetTreeEntryByPath(unescapedTarget) } func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) { wikiRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.WikiPath()) if err != nil { ctx.ServerError("OpenRepository", err) return nil, nil, err } commit, err := wikiRepo.GetBranchCommit(wiki_service.DefaultBranch) if err != nil { return wikiRepo, nil, err } return wikiRepo, commit, nil } // wikiContentsByEntry returns the contents of the wiki page referenced by the // given tree entry. Writes to ctx if an error occurs. func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte { reader, err := entry.Blob().DataAsync() if err != nil { ctx.ServerError("Blob.Data", err) return nil } defer reader.Close() content, err := io.ReadAll(reader) if err != nil { ctx.ServerError("ReadAll", err) return nil } return content } // wikiContentsByName returns the contents of a wiki page, along with a boolean // indicating whether the page exists. Writes to ctx if an error occurs. func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) { pageFilename := wiki_service.NameToFilename(wikiName) entry, err := findEntryForFile(commit, pageFilename) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findEntryForFile", err) return nil, nil, "", false } else if entry == nil { return nil, nil, "", true } return wikiContentsByEntry(ctx, entry), entry, pageFilename, false } func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { wikiRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if wikiRepo != nil { wikiRepo.Close() } if !git.IsErrNotExist(err) { ctx.ServerError("GetBranchCommit", err) } return nil, nil } // Get page list. entries, err := commit.ListEntries() if err != nil { if wikiRepo != nil { wikiRepo.Close() } ctx.ServerError("ListEntries", err) return nil, nil } pages := make([]PageMeta, 0, len(entries)) for _, entry := range entries { if !entry.IsRegular() { continue } wikiName, err := wiki_service.FilenameToName(entry.Name()) if err != nil { if repo_model.IsErrWikiInvalidFileName(err) { continue } if wikiRepo != nil { wikiRepo.Close() } ctx.ServerError("WikiFilenameToName", err) return nil, nil } else if wikiName == "_Sidebar" || wikiName == "_Footer" { continue } pages = append(pages, PageMeta{ Name: wikiName, SubURL: wiki_service.NameToSubURL(wikiName), }) } ctx.Data["Pages"] = pages // get requested pagename pageName := wiki_service.NormalizeWikiName(ctx.Params("*")) if len(pageName) == 0 { pageName = "Home" } ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName) ctx.Data["old_title"] = pageName ctx.Data["Title"] = pageName ctx.Data["title"] = pageName isSideBar := pageName == "_Sidebar" isFooter := pageName == "_Footer" // lookup filename in wiki - get filecontent, gitTree entry , real filename data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } if entry == nil || ctx.Written() { if wikiRepo != nil { wikiRepo.Close() } return nil, nil } var sidebarContent []byte if !isSideBar { sidebarContent, _, _, _ = wikiContentsByName(ctx, commit, "_Sidebar") if ctx.Written() { if wikiRepo != nil { wikiRepo.Close() } return nil, nil } } else { sidebarContent = data } var footerContent []byte if !isFooter { footerContent, _, _, _ = wikiContentsByName(ctx, commit, "_Footer") if ctx.Written() { if wikiRepo != nil { wikiRepo.Close() } return nil, nil } } else { footerContent = data } rctx := &markup.RenderContext{ Ctx: ctx, URLPrefix: ctx.Repo.RepoLink, Metas: ctx.Repo.Repository.ComposeDocumentMetas(), IsWiki: true, } buf := &strings.Builder{} renderFn := func(data []byte) (escaped *charset.EscapeStatus, output string, err error) { markupRd, markupWr := io.Pipe() defer markupWr.Close() done := make(chan struct{}) go func() { // We allow NBSP here this is rendered escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.RuneNBSP) output = buf.String() buf.Reset() close(done) }() err = markdown.Render(rctx, bytes.NewReader(data), markupWr) _ = markupWr.CloseWithError(err) <-done return escaped, output, err } ctx.Data["EscapeStatus"], ctx.Data["content"], err = renderFn(data) if err != nil { if wikiRepo != nil { wikiRepo.Close() } ctx.ServerError("Render", err) return nil, nil } if !isSideBar { buf.Reset() ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"], err = renderFn(sidebarContent) if err != nil { if wikiRepo != nil { wikiRepo.Close() } ctx.ServerError("Render", err) return nil, nil } ctx.Data["sidebarPresent"] = sidebarContent != nil } else { ctx.Data["sidebarPresent"] = false } if !isFooter { buf.Reset() ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"], err = renderFn(footerContent) if err != nil { if wikiRepo != nil { wikiRepo.Close() } ctx.ServerError("Render", err) return nil, nil } ctx.Data["footerPresent"] = footerContent != nil } else { ctx.Data["footerPresent"] = false } ctx.Data["toc"] = rctx.TableOfContents // get commit count - wiki revisions commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename) ctx.Data["CommitCount"] = commitsCount return wikiRepo, entry } func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { wikiRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if wikiRepo != nil { wikiRepo.Close() } if !git.IsErrNotExist(err) { ctx.ServerError("GetBranchCommit", err) } return nil, nil } // get requested pagename pageName := wiki_service.NormalizeWikiName(ctx.Params("*")) if len(pageName) == 0 { pageName = "Home" } ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName) ctx.Data["old_title"] = pageName ctx.Data["Title"] = pageName ctx.Data["title"] = pageName ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name // lookup filename in wiki - get filecontent, gitTree entry , real filename data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } if entry == nil || ctx.Written() { if wikiRepo != nil { wikiRepo.Close() } return nil, nil } ctx.Data["content"] = string(data) ctx.Data["sidebarPresent"] = false ctx.Data["sidebarContent"] = "" ctx.Data["footerPresent"] = false ctx.Data["footerContent"] = "" // get commit count - wiki revisions commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename) ctx.Data["CommitCount"] = commitsCount // get page page := ctx.FormInt("page") if page <= 1 { page = 1 } // get Commit Count commitsHistory, err := wikiRepo.CommitsByFileAndRange(wiki_service.DefaultBranch, pageFilename, page) if err != nil { if wikiRepo != nil { wikiRepo.Close() } ctx.ServerError("CommitsByFileAndRange", err) return nil, nil } ctx.Data["Commits"] = git_model.ConvertFromGitCommit(commitsHistory, ctx.Repo.Repository) pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager return wikiRepo, entry } func renderEditPage(ctx *context.Context) { wikiRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if wikiRepo != nil { wikiRepo.Close() } if !git.IsErrNotExist(err) { ctx.ServerError("GetBranchCommit", err) } return } defer func() { if wikiRepo != nil { wikiRepo.Close() } }() // get requested pagename pageName := wiki_service.NormalizeWikiName(ctx.Params("*")) if len(pageName) == 0 { pageName = "Home" } ctx.Data["PageURL"] = wiki_service.NameToSubURL(pageName) ctx.Data["old_title"] = pageName ctx.Data["Title"] = pageName ctx.Data["title"] = pageName // lookup filename in wiki - get filecontent, gitTree entry , real filename data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName) if noEntry { ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages") } if entry == nil || ctx.Written() { return } ctx.Data["content"] = string(data) ctx.Data["sidebarPresent"] = false ctx.Data["sidebarContent"] = "" ctx.Data["footerPresent"] = false ctx.Data["footerContent"] = "" } // WikiPost renders post of wiki page func WikiPost(ctx *context.Context) { switch ctx.FormString("action") { case "_new": if !ctx.Repo.CanWrite(unit.TypeWiki) { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } NewWikiPost(ctx) return case "_delete": if !ctx.Repo.CanWrite(unit.TypeWiki) { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } DeleteWikiPagePost(ctx) return } if !ctx.Repo.CanWrite(unit.TypeWiki) { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } EditWikiPost(ctx) } // Wiki renders single wiki page func Wiki(ctx *context.Context) { ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived switch ctx.FormString("action") { case "_pages": WikiPages(ctx) return case "_revision": WikiRevision(ctx) return case "_edit": if !ctx.Repo.CanWrite(unit.TypeWiki) { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } EditWiki(ctx) return case "_new": if !ctx.Repo.CanWrite(unit.TypeWiki) { ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } NewWiki(ctx) return } if !ctx.Repo.Repository.HasWiki() { ctx.Data["Title"] = ctx.Tr("repo.wiki") ctx.HTML(http.StatusOK, tplWikiStart) return } wikiRepo, entry := renderViewPage(ctx) defer func() { if wikiRepo != nil { wikiRepo.Close() } }() if ctx.Written() { return } if entry == nil { ctx.Data["Title"] = ctx.Tr("repo.wiki") ctx.HTML(http.StatusOK, tplWikiStart) return } wikiPath := entry.Name() if markup.Type(wikiPath) != markdown.MarkupName { ext := strings.ToUpper(filepath.Ext(wikiPath)) ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext) } // Get last change information. lastCommit, err := wikiRepo.GetCommitByPath(wikiPath) if err != nil { ctx.ServerError("GetCommitByPath", err) return } ctx.Data["Author"] = lastCommit.Author ctx.HTML(http.StatusOK, tplWikiView) } // WikiRevision renders file revision list of wiki page func WikiRevision(ctx *context.Context) { ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived if !ctx.Repo.Repository.HasWiki() { ctx.Data["Title"] = ctx.Tr("repo.wiki") ctx.HTML(http.StatusOK, tplWikiStart) return } wikiRepo, entry := renderRevisionPage(ctx) defer func() { if wikiRepo != nil { wikiRepo.Close() } }() if ctx.Written() { return } if entry == nil { ctx.Data["Title"] = ctx.Tr("repo.wiki") ctx.HTML(http.StatusOK, tplWikiStart) return } // Get last change information. wikiPath := entry.Name() lastCommit, err := wikiRepo.GetCommitByPath(wikiPath) if err != nil { ctx.ServerError("GetCommitByPath", err) return } ctx.Data["Author"] = lastCommit.Author ctx.HTML(http.StatusOK, tplWikiRevision) } // WikiPages render wiki pages list page func WikiPages(ctx *context.Context) { if !ctx.Repo.Repository.HasWiki() { ctx.Redirect(ctx.Repo.RepoLink + "/wiki") return } ctx.Data["Title"] = ctx.Tr("repo.wiki.pages") ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived wikiRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { if wikiRepo != nil { wikiRepo.Close() } return } defer func() { if wikiRepo != nil { wikiRepo.Close() } }() entries, err := commit.ListEntries() if err != nil { ctx.ServerError("ListEntries", err) return } pages := make([]PageMeta, 0, len(entries)) for _, entry := range entries { if !entry.IsRegular() { continue } c, err := wikiRepo.GetCommitByPath(entry.Name()) if err != nil { ctx.ServerError("GetCommit", err) return } wikiName, err := wiki_service.FilenameToName(entry.Name()) if err != nil { if repo_model.IsErrWikiInvalidFileName(err) { continue } ctx.ServerError("WikiFilenameToName", err) return } pages = append(pages, PageMeta{ Name: wikiName, SubURL: wiki_service.NameToSubURL(wikiName), UpdatedUnix: timeutil.TimeStamp(c.Author.When.Unix()), }) } ctx.Data["Pages"] = pages ctx.HTML(http.StatusOK, tplWikiPages) } // WikiRaw outputs raw blob requested by user (image for example) func WikiRaw(ctx *context.Context) { wikiRepo, commit, err := findWikiRepoCommit(ctx) defer func() { if wikiRepo != nil { wikiRepo.Close() } }() if err != nil { if git.IsErrNotExist(err) { ctx.NotFound("findEntryForFile", nil) return } ctx.ServerError("findEntryForfile", err) return } providedPath := ctx.Params("*") var entry *git.TreeEntry if commit != nil { // Try to find a file with that name entry, err = findEntryForFile(commit, providedPath) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findFile", err) return } if entry == nil { // Try to find a wiki page with that name providedPath = strings.TrimSuffix(providedPath, ".md") wikiPath := wiki_service.NameToFilename(providedPath) entry, err = findEntryForFile(commit, wikiPath) if err != nil && !git.IsErrNotExist(err) { ctx.ServerError("findFile", err) return } } } if entry != nil { if err = common.ServeBlob(ctx, entry.Blob(), time.Time{}); err != nil { ctx.ServerError("ServeBlob", err) } return } ctx.NotFound("findEntryForFile", nil) } // NewWiki render wiki create page func NewWiki(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") if !ctx.Repo.Repository.HasWiki() { ctx.Data["title"] = "Home" } if ctx.FormString("title") != "" { ctx.Data["title"] = ctx.FormString("title") } ctx.HTML(http.StatusOK, tplWikiNew) } // NewWikiPost response for wiki create request func NewWikiPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.NewWikiForm) ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") if ctx.HasError() { ctx.HTML(http.StatusOK, tplWikiNew) return } if util.IsEmptyString(form.Title) { ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplWikiNew, form) return } wikiName := wiki_service.NormalizeWikiName(form.Title) if len(form.Message) == 0 { form.Message = ctx.Tr("repo.editor.add", form.Title) } if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil { if repo_model.IsErrWikiReservedName(err) { ctx.Data["Err_Title"] = true ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form) } else if repo_model.IsErrWikiAlreadyExist(err) { ctx.Data["Err_Title"] = true ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form) } else { ctx.ServerError("AddWikiPage", err) } return } notification.NotifyNewWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Message) ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(wikiName)) } // EditWiki render wiki modify page func EditWiki(ctx *context.Context) { ctx.Data["PageIsWikiEdit"] = true if !ctx.Repo.Repository.HasWiki() { ctx.Redirect(ctx.Repo.RepoLink + "/wiki") return } renderEditPage(ctx) if ctx.Written() { return } ctx.HTML(http.StatusOK, tplWikiNew) } // EditWikiPost response for wiki modify request func EditWikiPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.NewWikiForm) ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") if ctx.HasError() { ctx.HTML(http.StatusOK, tplWikiNew) return } oldWikiName := wiki_service.NormalizeWikiName(ctx.Params("*")) newWikiName := wiki_service.NormalizeWikiName(form.Title) if len(form.Message) == 0 { form.Message = ctx.Tr("repo.editor.update", form.Title) } if err := wiki_service.EditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, oldWikiName, newWikiName, form.Content, form.Message); err != nil { ctx.ServerError("EditWikiPage", err) return } notification.NotifyEditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, newWikiName, form.Message) ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(newWikiName)) } // DeleteWikiPagePost delete wiki page func DeleteWikiPagePost(ctx *context.Context) { wikiName := wiki_service.NormalizeWikiName(ctx.Params("*")) if len(wikiName) == 0 { wikiName = "Home" } if err := wiki_service.DeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName); err != nil { ctx.ServerError("DeleteWikiPage", err) return } notification.NotifyDeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName) ctx.JSON(http.StatusOK, map[string]interface{}{ "redirect": ctx.Repo.RepoLink + "/wiki/", }) }