You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo.go 13 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "github.com/gogits/git"
  10. "path"
  11. "path/filepath"
  12. "strings"
  13. "github.com/go-martini/martini"
  14. "github.com/gogits/gogs/models"
  15. "github.com/gogits/gogs/modules/auth"
  16. "github.com/gogits/gogs/modules/base"
  17. "github.com/gogits/gogs/modules/log"
  18. "github.com/gogits/gogs/modules/middleware"
  19. )
  20. func Create(ctx *middleware.Context) {
  21. ctx.Data["Title"] = "Create repository"
  22. ctx.Data["PageIsNewRepo"] = true
  23. ctx.Data["LanguageIgns"] = models.LanguageIgns
  24. ctx.Data["Licenses"] = models.Licenses
  25. ctx.HTML(200, "repo/create")
  26. }
  27. func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
  28. ctx.Data["Title"] = "Create repository"
  29. ctx.Data["PageIsNewRepo"] = true
  30. ctx.Data["LanguageIgns"] = models.LanguageIgns
  31. ctx.Data["Licenses"] = models.Licenses
  32. if ctx.HasError() {
  33. ctx.HTML(200, "repo/create")
  34. return
  35. }
  36. repo, err := models.CreateRepository(ctx.User, form.RepoName, form.Description,
  37. form.Language, form.License, form.Private, false, form.InitReadme)
  38. if err == nil {
  39. log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName)
  40. ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName)
  41. return
  42. } else if err == models.ErrRepoAlreadyExist {
  43. ctx.RenderWithErr("Repository name has already been used", "repo/create", &form)
  44. return
  45. } else if err == models.ErrRepoNameIllegal {
  46. ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form)
  47. return
  48. }
  49. if repo != nil {
  50. if errDelete := models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); errDelete != nil {
  51. log.Error("repo.MigratePost(CreatePost): %v", errDelete)
  52. }
  53. }
  54. ctx.Handle(500, "repo.Create", err)
  55. }
  56. func Migrate(ctx *middleware.Context) {
  57. ctx.Data["Title"] = "Migrate repository"
  58. ctx.Data["PageIsNewRepo"] = true
  59. ctx.HTML(200, "repo/migrate")
  60. }
  61. func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
  62. ctx.Data["Title"] = "Migrate repository"
  63. ctx.Data["PageIsNewRepo"] = true
  64. if ctx.HasError() {
  65. ctx.HTML(200, "repo/migrate")
  66. return
  67. }
  68. url := strings.Replace(form.Url, "://", fmt.Sprintf("://%s:%s@", form.AuthUserName, form.AuthPasswd), 1)
  69. repo, err := models.MigrateRepository(ctx.User, form.RepoName, form.Description, form.Private,
  70. form.Mirror, url)
  71. if err == nil {
  72. log.Trace("%s Repository migrated: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName)
  73. ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName)
  74. return
  75. } else if err == models.ErrRepoAlreadyExist {
  76. ctx.RenderWithErr("Repository name has already been used", "repo/migrate", &form)
  77. return
  78. } else if err == models.ErrRepoNameIllegal {
  79. ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/migrate", &form)
  80. return
  81. }
  82. if repo != nil {
  83. if errDelete := models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); errDelete != nil {
  84. log.Error("repo.MigratePost(DeleteRepository): %v", errDelete)
  85. }
  86. }
  87. if strings.Contains(err.Error(), "Authentication failed") {
  88. ctx.RenderWithErr(err.Error(), "repo/migrate", &form)
  89. return
  90. }
  91. ctx.Handle(500, "repo.Migrate", err)
  92. }
  93. func Single(ctx *middleware.Context, params martini.Params) {
  94. branchName := ctx.Repo.BranchName
  95. userName := ctx.Repo.Owner.Name
  96. repoName := ctx.Repo.Repository.Name
  97. repoLink := ctx.Repo.RepoLink
  98. branchLink := ctx.Repo.RepoLink + "/src/" + branchName
  99. rawLink := ctx.Repo.RepoLink + "/raw/" + branchName
  100. // Get tree path
  101. treename := params["_1"]
  102. if len(treename) > 0 && treename[len(treename)-1] == '/' {
  103. ctx.Redirect(repoLink + "/src/" + branchName + "/" + treename[:len(treename)-1])
  104. return
  105. }
  106. ctx.Data["IsRepoToolbarSource"] = true
  107. isViewBranch := ctx.Repo.IsBranch
  108. ctx.Data["IsViewBranch"] = isViewBranch
  109. treePath := treename
  110. if len(treePath) != 0 {
  111. treePath = treePath + "/"
  112. }
  113. entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treename)
  114. if err != nil && err != git.ErrNotExist {
  115. ctx.Handle(404, "repo.Single(GetTreeEntryByPath)", err)
  116. return
  117. }
  118. if len(treename) != 0 && entry == nil {
  119. ctx.Handle(404, "repo.Single", nil)
  120. return
  121. }
  122. if entry != nil && entry.IsFile() {
  123. blob := entry.Blob()
  124. if data, err := blob.Data(); err != nil {
  125. ctx.Handle(404, "repo.Single(blob.Data)", err)
  126. } else {
  127. ctx.Data["FileSize"] = blob.Size()
  128. ctx.Data["IsFile"] = true
  129. ctx.Data["FileName"] = blob.Name
  130. ext := path.Ext(blob.Name)
  131. if len(ext) > 0 {
  132. ext = ext[1:]
  133. }
  134. ctx.Data["FileExt"] = ext
  135. ctx.Data["FileLink"] = rawLink + "/" + treename
  136. _, isTextFile := base.IsTextFile(data)
  137. _, isImageFile := base.IsImageFile(data)
  138. ctx.Data["FileIsText"] = isTextFile
  139. if isImageFile {
  140. ctx.Data["IsImageFile"] = true
  141. } else {
  142. readmeExist := base.IsMarkdownFile(blob.Name) || base.IsReadmeFile(blob.Name)
  143. ctx.Data["ReadmeExist"] = readmeExist
  144. if readmeExist {
  145. ctx.Data["FileContent"] = string(base.RenderMarkdown(data, ""))
  146. } else {
  147. if isTextFile {
  148. ctx.Data["FileContent"] = string(data)
  149. }
  150. }
  151. }
  152. }
  153. } else {
  154. // Directory and file list.
  155. tree, err := ctx.Repo.Commit.SubTree(treename)
  156. if err != nil {
  157. ctx.Handle(404, "repo.Single(SubTree)", err)
  158. return
  159. }
  160. entries := tree.ListEntries()
  161. entries.Sort()
  162. files := make([][]interface{}, 0, len(entries))
  163. for _, te := range entries {
  164. c, err := ctx.Repo.Commit.GetCommitOfRelPath(filepath.Join(treePath, te.Name))
  165. if err != nil {
  166. ctx.Handle(404, "repo.Single(SubTree)", err)
  167. return
  168. }
  169. files = append(files, []interface{}{te, c})
  170. }
  171. ctx.Data["Files"] = files
  172. var readmeFile *git.Blob
  173. for _, f := range entries {
  174. if !f.IsFile() || !base.IsReadmeFile(f.Name) {
  175. continue
  176. } else {
  177. readmeFile = f.Blob()
  178. break
  179. }
  180. }
  181. if readmeFile != nil {
  182. ctx.Data["ReadmeInSingle"] = true
  183. ctx.Data["ReadmeExist"] = true
  184. if data, err := readmeFile.Data(); err != nil {
  185. ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err)
  186. return
  187. } else {
  188. ctx.Data["FileSize"] = readmeFile.Size
  189. ctx.Data["FileLink"] = rawLink + "/" + treename
  190. _, isTextFile := base.IsTextFile(data)
  191. ctx.Data["FileIsText"] = isTextFile
  192. ctx.Data["FileName"] = readmeFile.Name
  193. if isTextFile {
  194. ctx.Data["FileContent"] = string(base.RenderMarkdown(data, branchLink))
  195. }
  196. }
  197. }
  198. }
  199. ctx.Data["Username"] = userName
  200. ctx.Data["Reponame"] = repoName
  201. var treenames []string
  202. Paths := make([]string, 0)
  203. if len(treename) > 0 {
  204. treenames = strings.Split(treename, "/")
  205. for i, _ := range treenames {
  206. Paths = append(Paths, strings.Join(treenames[0:i+1], "/"))
  207. }
  208. ctx.Data["HasParentPath"] = true
  209. if len(Paths)-2 >= 0 {
  210. ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2]
  211. }
  212. }
  213. ctx.Data["LastCommit"] = ctx.Repo.Commit
  214. ctx.Data["Paths"] = Paths
  215. ctx.Data["Treenames"] = treenames
  216. ctx.Data["TreePath"] = treePath
  217. ctx.Data["BranchLink"] = branchLink
  218. ctx.HTML(200, "repo/single")
  219. }
  220. func SingleDownload(ctx *middleware.Context, params martini.Params) {
  221. // Get tree path
  222. treename := params["_1"]
  223. blob, err := ctx.Repo.Commit.GetBlobByPath(treename)
  224. if err != nil {
  225. ctx.Handle(404, "repo.SingleDownload(GetBlobByPath)", err)
  226. return
  227. }
  228. data, err := blob.Data()
  229. if err != nil {
  230. ctx.Handle(404, "repo.SingleDownload(Data)", err)
  231. return
  232. }
  233. contentType, isTextFile := base.IsTextFile(data)
  234. _, isImageFile := base.IsImageFile(data)
  235. ctx.Res.Header().Set("Content-Type", contentType)
  236. if !isTextFile && !isImageFile {
  237. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename))
  238. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  239. }
  240. ctx.Res.Write(data)
  241. }
  242. func basicEncode(username, password string) string {
  243. auth := username + ":" + password
  244. return base64.StdEncoding.EncodeToString([]byte(auth))
  245. }
  246. func basicDecode(encoded string) (user string, name string, err error) {
  247. var s []byte
  248. s, err = base64.StdEncoding.DecodeString(encoded)
  249. if err != nil {
  250. return
  251. }
  252. a := strings.Split(string(s), ":")
  253. if len(a) == 2 {
  254. user, name = a[0], a[1]
  255. } else {
  256. err = errors.New("decode failed")
  257. }
  258. return
  259. }
  260. func authRequired(ctx *middleware.Context) {
  261. ctx.ResponseWriter.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
  262. ctx.Data["ErrorMsg"] = "no basic auth and digit auth"
  263. ctx.HTML(401, fmt.Sprintf("status/401"))
  264. }
  265. func Setting(ctx *middleware.Context, params martini.Params) {
  266. if !ctx.Repo.IsOwner {
  267. ctx.Handle(404, "repo.Setting", nil)
  268. return
  269. }
  270. ctx.Data["IsRepoToolbarSetting"] = true
  271. var title string
  272. if t, ok := ctx.Data["Title"].(string); ok {
  273. title = t
  274. }
  275. ctx.Data["Title"] = title + " - settings"
  276. ctx.HTML(200, "repo/setting")
  277. }
  278. func SettingPost(ctx *middleware.Context) {
  279. if !ctx.Repo.IsOwner {
  280. ctx.Error(404)
  281. return
  282. }
  283. ctx.Data["IsRepoToolbarSetting"] = true
  284. switch ctx.Query("action") {
  285. case "update":
  286. newRepoName := ctx.Query("name")
  287. // Check if repository name has been changed.
  288. if ctx.Repo.Repository.Name != newRepoName {
  289. isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName)
  290. if err != nil {
  291. ctx.Handle(500, "repo.SettingPost(update: check existence)", err)
  292. return
  293. } else if isExist {
  294. ctx.RenderWithErr("Repository name has been taken in your repositories.", "repo/setting", nil)
  295. return
  296. } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
  297. ctx.Handle(500, "repo.SettingPost(change repository name)", err)
  298. return
  299. }
  300. log.Trace("%s Repository name changed: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newRepoName)
  301. ctx.Repo.Repository.Name = newRepoName
  302. }
  303. br := ctx.Query("branch")
  304. if git.IsBranchExist(models.RepoPath(ctx.User.Name, ctx.Repo.Repository.Name), br) {
  305. ctx.Repo.Repository.DefaultBranch = br
  306. }
  307. ctx.Repo.Repository.Description = ctx.Query("desc")
  308. ctx.Repo.Repository.Website = ctx.Query("site")
  309. ctx.Repo.Repository.IsGoget = ctx.Query("goget") == "on"
  310. if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
  311. ctx.Handle(404, "repo.SettingPost(update)", err)
  312. return
  313. }
  314. log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
  315. if ctx.Repo.Repository.IsMirror {
  316. if len(ctx.Query("interval")) > 0 {
  317. var err error
  318. ctx.Repo.Mirror.Interval, err = base.StrTo(ctx.Query("interval")).Int()
  319. if err != nil {
  320. log.Error("repo.SettingPost(get mirror interval): %v", err)
  321. } else if err = models.UpdateMirror(ctx.Repo.Mirror); err != nil {
  322. log.Error("repo.SettingPost(UpdateMirror): %v", err)
  323. }
  324. }
  325. }
  326. ctx.Flash.Success("Repository options has been successfully updated.")
  327. ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name))
  328. case "transfer":
  329. if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
  330. ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil)
  331. return
  332. }
  333. newOwner := ctx.Query("owner")
  334. // Check if new owner exists.
  335. isExist, err := models.IsUserExist(newOwner)
  336. if err != nil {
  337. ctx.Handle(500, "repo.SettingPost(transfer: check existence)", err)
  338. return
  339. } else if !isExist {
  340. ctx.RenderWithErr("Please make sure you entered owner name is correct.", "repo/setting", nil)
  341. return
  342. } else if err = models.TransferOwnership(ctx.User, newOwner, ctx.Repo.Repository); err != nil {
  343. ctx.Handle(500, "repo.SettingPost(transfer repository)", err)
  344. return
  345. }
  346. log.Trace("%s Repository transfered: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newOwner)
  347. ctx.Redirect("/")
  348. case "delete":
  349. if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
  350. ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil)
  351. return
  352. }
  353. if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
  354. ctx.Handle(500, "repo.Delete", err)
  355. return
  356. }
  357. log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
  358. ctx.Redirect("/")
  359. }
  360. }
  361. func Action(ctx *middleware.Context, params martini.Params) {
  362. var err error
  363. switch params["action"] {
  364. case "watch":
  365. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  366. case "unwatch":
  367. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  368. case "desc":
  369. if !ctx.Repo.IsOwner {
  370. ctx.Error(404)
  371. return
  372. }
  373. ctx.Repo.Repository.Description = ctx.Query("desc")
  374. ctx.Repo.Repository.Website = ctx.Query("site")
  375. err = models.UpdateRepository(ctx.Repo.Repository)
  376. }
  377. if err != nil {
  378. log.Error("repo.Action(%s): %v", params["action"], err)
  379. ctx.JSON(200, map[string]interface{}{
  380. "ok": false,
  381. "err": err.Error(),
  382. })
  383. return
  384. }
  385. ctx.JSON(200, map[string]interface{}{
  386. "ok": true,
  387. })
  388. }