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
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. if ctx.Query("go-get") == "1" {
  95. base.GoGetMetas[strings.TrimSuffix(ctx.Repo.CloneLink.HTTPS, ".git")] = true
  96. }
  97. branchName := ctx.Repo.BranchName
  98. userName := ctx.Repo.Owner.Name
  99. repoName := ctx.Repo.Repository.Name
  100. repoLink := ctx.Repo.RepoLink
  101. branchLink := ctx.Repo.RepoLink + "/src/" + branchName
  102. rawLink := ctx.Repo.RepoLink + "/raw/" + branchName
  103. // Get tree path
  104. treename := params["_1"]
  105. if len(treename) > 0 && treename[len(treename)-1] == '/' {
  106. ctx.Redirect(repoLink + "/src/" + branchName + "/" + treename[:len(treename)-1])
  107. return
  108. }
  109. ctx.Data["IsRepoToolbarSource"] = true
  110. isViewBranch := ctx.Repo.IsBranch
  111. ctx.Data["IsViewBranch"] = isViewBranch
  112. treePath := treename
  113. if len(treePath) != 0 {
  114. treePath = treePath + "/"
  115. }
  116. entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treename)
  117. if err != nil && err != git.ErrNotExist {
  118. ctx.Handle(404, "repo.Single(GetTreeEntryByPath)", err)
  119. return
  120. }
  121. if len(treename) != 0 && entry == nil {
  122. ctx.Handle(404, "repo.Single", nil)
  123. return
  124. }
  125. if entry != nil && entry.IsFile() {
  126. blob := entry.Blob()
  127. if data, err := blob.Data(); err != nil {
  128. ctx.Handle(404, "repo.Single(blob.Data)", err)
  129. } else {
  130. ctx.Data["FileSize"] = blob.Size()
  131. ctx.Data["IsFile"] = true
  132. ctx.Data["FileName"] = blob.Name
  133. ext := path.Ext(blob.Name)
  134. if len(ext) > 0 {
  135. ext = ext[1:]
  136. }
  137. ctx.Data["FileExt"] = ext
  138. ctx.Data["FileLink"] = rawLink + "/" + treename
  139. _, isTextFile := base.IsTextFile(data)
  140. _, isImageFile := base.IsImageFile(data)
  141. ctx.Data["FileIsText"] = isTextFile
  142. if isImageFile {
  143. ctx.Data["IsImageFile"] = true
  144. } else {
  145. readmeExist := base.IsMarkdownFile(blob.Name) || base.IsReadmeFile(blob.Name)
  146. ctx.Data["ReadmeExist"] = readmeExist
  147. if readmeExist {
  148. ctx.Data["FileContent"] = string(base.RenderMarkdown(data, ""))
  149. } else {
  150. if isTextFile {
  151. ctx.Data["FileContent"] = string(data)
  152. }
  153. }
  154. }
  155. }
  156. } else {
  157. // Directory and file list.
  158. tree, err := ctx.Repo.Commit.SubTree(treename)
  159. if err != nil {
  160. ctx.Handle(404, "repo.Single(SubTree)", err)
  161. return
  162. }
  163. entries := tree.ListEntries()
  164. entries.Sort()
  165. files := make([][]interface{}, 0, len(entries))
  166. for _, te := range entries {
  167. c, err := ctx.Repo.Commit.GetCommitOfRelPath(filepath.Join(treePath, te.Name))
  168. if err != nil {
  169. ctx.Handle(404, "repo.Single(SubTree)", err)
  170. return
  171. }
  172. files = append(files, []interface{}{te, c})
  173. }
  174. ctx.Data["Files"] = files
  175. var readmeFile *git.Blob
  176. for _, f := range entries {
  177. if !f.IsFile() || !base.IsReadmeFile(f.Name) {
  178. continue
  179. } else {
  180. readmeFile = f.Blob()
  181. break
  182. }
  183. }
  184. if readmeFile != nil {
  185. ctx.Data["ReadmeInSingle"] = true
  186. ctx.Data["ReadmeExist"] = true
  187. if data, err := readmeFile.Data(); err != nil {
  188. ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err)
  189. return
  190. } else {
  191. ctx.Data["FileSize"] = readmeFile.Size
  192. ctx.Data["FileLink"] = rawLink + "/" + treename
  193. _, isTextFile := base.IsTextFile(data)
  194. ctx.Data["FileIsText"] = isTextFile
  195. ctx.Data["FileName"] = readmeFile.Name
  196. if isTextFile {
  197. ctx.Data["FileContent"] = string(base.RenderMarkdown(data, branchLink))
  198. }
  199. }
  200. }
  201. }
  202. ctx.Data["Username"] = userName
  203. ctx.Data["Reponame"] = repoName
  204. var treenames []string
  205. Paths := make([]string, 0)
  206. if len(treename) > 0 {
  207. treenames = strings.Split(treename, "/")
  208. for i, _ := range treenames {
  209. Paths = append(Paths, strings.Join(treenames[0:i+1], "/"))
  210. }
  211. ctx.Data["HasParentPath"] = true
  212. if len(Paths)-2 >= 0 {
  213. ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2]
  214. }
  215. }
  216. ctx.Data["LastCommit"] = ctx.Repo.Commit
  217. ctx.Data["Paths"] = Paths
  218. ctx.Data["Treenames"] = treenames
  219. ctx.Data["TreePath"] = treePath
  220. ctx.Data["BranchLink"] = branchLink
  221. ctx.HTML(200, "repo/single")
  222. }
  223. func SingleDownload(ctx *middleware.Context, params martini.Params) {
  224. // Get tree path
  225. treename := params["_1"]
  226. blob, err := ctx.Repo.Commit.GetBlobByPath(treename)
  227. if err != nil {
  228. ctx.Handle(404, "repo.SingleDownload(GetBlobByPath)", err)
  229. return
  230. }
  231. data, err := blob.Data()
  232. if err != nil {
  233. ctx.Handle(404, "repo.SingleDownload(Data)", err)
  234. return
  235. }
  236. contentType, isTextFile := base.IsTextFile(data)
  237. _, isImageFile := base.IsImageFile(data)
  238. ctx.Res.Header().Set("Content-Type", contentType)
  239. if !isTextFile && !isImageFile {
  240. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename))
  241. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  242. }
  243. ctx.Res.Write(data)
  244. }
  245. func basicEncode(username, password string) string {
  246. auth := username + ":" + password
  247. return base64.StdEncoding.EncodeToString([]byte(auth))
  248. }
  249. func basicDecode(encoded string) (user string, name string, err error) {
  250. var s []byte
  251. s, err = base64.StdEncoding.DecodeString(encoded)
  252. if err != nil {
  253. return
  254. }
  255. a := strings.Split(string(s), ":")
  256. if len(a) == 2 {
  257. user, name = a[0], a[1]
  258. } else {
  259. err = errors.New("decode failed")
  260. }
  261. return
  262. }
  263. func authRequired(ctx *middleware.Context) {
  264. ctx.ResponseWriter.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
  265. ctx.Data["ErrorMsg"] = "no basic auth and digit auth"
  266. ctx.HTML(401, fmt.Sprintf("status/401"))
  267. }
  268. func Setting(ctx *middleware.Context, params martini.Params) {
  269. if !ctx.Repo.IsOwner {
  270. ctx.Handle(404, "repo.Setting", nil)
  271. return
  272. }
  273. ctx.Data["IsRepoToolbarSetting"] = true
  274. var title string
  275. if t, ok := ctx.Data["Title"].(string); ok {
  276. title = t
  277. }
  278. ctx.Data["Title"] = title + " - settings"
  279. ctx.HTML(200, "repo/setting")
  280. }
  281. func SettingPost(ctx *middleware.Context) {
  282. if !ctx.Repo.IsOwner {
  283. ctx.Error(404)
  284. return
  285. }
  286. ctx.Data["IsRepoToolbarSetting"] = true
  287. switch ctx.Query("action") {
  288. case "update":
  289. newRepoName := ctx.Query("name")
  290. // Check if repository name has been changed.
  291. if ctx.Repo.Repository.Name != newRepoName {
  292. isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName)
  293. if err != nil {
  294. ctx.Handle(500, "repo.SettingPost(update: check existence)", err)
  295. return
  296. } else if isExist {
  297. ctx.RenderWithErr("Repository name has been taken in your repositories.", "repo/setting", nil)
  298. return
  299. } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
  300. ctx.Handle(500, "repo.SettingPost(change repository name)", err)
  301. return
  302. }
  303. log.Trace("%s Repository name changed: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newRepoName)
  304. ctx.Repo.Repository.Name = newRepoName
  305. }
  306. br := ctx.Query("branch")
  307. if git.IsBranchExist(models.RepoPath(ctx.User.Name, ctx.Repo.Repository.Name), br) {
  308. ctx.Repo.Repository.DefaultBranch = br
  309. }
  310. ctx.Repo.Repository.Description = ctx.Query("desc")
  311. ctx.Repo.Repository.Website = ctx.Query("site")
  312. ctx.Repo.Repository.IsGoget = ctx.Query("goget") == "on"
  313. if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
  314. ctx.Handle(404, "repo.SettingPost(update)", err)
  315. return
  316. }
  317. log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
  318. if ctx.Repo.Repository.IsMirror {
  319. if len(ctx.Query("interval")) > 0 {
  320. var err error
  321. ctx.Repo.Mirror.Interval, err = base.StrTo(ctx.Query("interval")).Int()
  322. if err != nil {
  323. log.Error("repo.SettingPost(get mirror interval): %v", err)
  324. } else if err = models.UpdateMirror(ctx.Repo.Mirror); err != nil {
  325. log.Error("repo.SettingPost(UpdateMirror): %v", err)
  326. }
  327. }
  328. }
  329. ctx.Flash.Success("Repository options has been successfully updated.")
  330. ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name))
  331. case "transfer":
  332. if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
  333. ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil)
  334. return
  335. }
  336. newOwner := ctx.Query("owner")
  337. // Check if new owner exists.
  338. isExist, err := models.IsUserExist(newOwner)
  339. if err != nil {
  340. ctx.Handle(500, "repo.SettingPost(transfer: check existence)", err)
  341. return
  342. } else if !isExist {
  343. ctx.RenderWithErr("Please make sure you entered owner name is correct.", "repo/setting", nil)
  344. return
  345. } else if err = models.TransferOwnership(ctx.User, newOwner, ctx.Repo.Repository); err != nil {
  346. ctx.Handle(500, "repo.SettingPost(transfer repository)", err)
  347. return
  348. }
  349. log.Trace("%s Repository transfered: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newOwner)
  350. ctx.Redirect("/")
  351. case "delete":
  352. if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
  353. ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil)
  354. return
  355. }
  356. if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
  357. ctx.Handle(500, "repo.Delete", err)
  358. return
  359. }
  360. log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
  361. ctx.Redirect("/")
  362. }
  363. }
  364. func Action(ctx *middleware.Context, params martini.Params) {
  365. var err error
  366. switch params["action"] {
  367. case "watch":
  368. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  369. case "unwatch":
  370. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  371. case "desc":
  372. if !ctx.Repo.IsOwner {
  373. ctx.Error(404)
  374. return
  375. }
  376. ctx.Repo.Repository.Description = ctx.Query("desc")
  377. ctx.Repo.Repository.Website = ctx.Query("site")
  378. err = models.UpdateRepository(ctx.Repo.Repository)
  379. }
  380. if err != nil {
  381. log.Error("repo.Action(%s): %v", params["action"], err)
  382. ctx.JSON(200, map[string]interface{}{
  383. "ok": false,
  384. "err": err.Error(),
  385. })
  386. return
  387. }
  388. ctx.JSON(200, map[string]interface{}{
  389. "ok": true,
  390. })
  391. }