Browse Source

repo: update repository description field to contain more than 256 symbols (#5219)

* Update repository description field to contain more than 256 symbols

- update repository model - description field now is `TEXT` and limited by 4000 symbols
- new migration
- add description to html forms - repo creation and repo settings
- add translation for description

* Update for description field, new features

- add autosize (height) for description textarea, new plugin
- set max description length to 512 symbols
- update locales

* Fix migration - typo in var

* Update repo description behaviour

- add textarea autosize for /repo/create
- add symbols counter under description testarea (create/edit)

* Fix function definition - it a var

* Revert ru-RU locale

* Update by review

- Use type `varchar(512)` in migration
- Remove unused files from autosize plugin

* Fix migration - new project paths

* Fixes after review 2

- copyright year
- format includes
- use switch instead of multi-if

* Remove unused `default:` option.
pull/5293/head
Sergey Dryabzhinsky 7 years ago committed by 无闻
parent
commit
57897cc8c2
  1. 4
      conf/locale/locale_en-US.ini
  2. 2
      models/migrations/migrations.go
  3. 34
      models/migrations/v18.go
  4. 6
      models/repo.go
  5. 6
      pkg/form/repo.go
  6. 21
      public/js/gogs.js
  7. 6
      public/plugins/autosize-4.0.2/dist/autosize.min.js
  8. 1
      routes/repo/repo.go
  9. 1
      routes/repo/setting.go
  10. 4
      templates/base/head.tmpl
  11. 14
      templates/repo/create.tmpl
  12. 13
      templates/repo/settings/options.tmpl

4
conf/locale/locale_en-US.ini

@ -421,6 +421,8 @@ mirror_last_synced = Last Synced
watchers = Watchers watchers = Watchers
stargazers = Stargazers stargazers = Stargazers
forks = Forks forks = Forks
repo_description_helper = Description of repository. Maximum 512 characters length.
repo_description_length = Available characters
form.reach_limit_of_creation = The owner has reached maximum creation limit of %d repositories. form.reach_limit_of_creation = The owner has reached maximum creation limit of %d repositories.
form.name_reserved = Repository name '%s' is reserved. form.name_reserved = Repository name '%s' is reserved.
@ -856,6 +858,8 @@ settings.add_key_success = New deploy key '%s' has been added successfully!
settings.deploy_key_deletion = Delete Deploy Key settings.deploy_key_deletion = Delete Deploy Key
settings.deploy_key_deletion_desc = Deleting this deploy key will remove all related accesses for this repository. Do you want to continue? settings.deploy_key_deletion_desc = Deleting this deploy key will remove all related accesses for this repository. Do you want to continue?
settings.deploy_key_deletion_success = Deploy key has been deleted successfully! settings.deploy_key_deletion_success = Deploy key has been deleted successfully!
settings.description_desc = Description of repository. Maximum 512 characters length.
settings.description_length = Available characters
diff.browse_source = Browse Source diff.browse_source = Browse Source
diff.parent = parent diff.parent = parent

2
models/migrations/migrations.go

@ -64,6 +64,8 @@ var migrations = []Migration{
NewMigration("update repository sizes", updateRepositorySizes), NewMigration("update repository sizes", updateRepositorySizes),
// v16 -> v17:v0.10.31 // v16 -> v17:v0.10.31
NewMigration("remove invalid protect branch whitelist", removeInvalidProtectBranchWhitelist), NewMigration("remove invalid protect branch whitelist", removeInvalidProtectBranchWhitelist),
// v17 -> v18:v0.11.48
NewMigration("store long text in repository description field", updateRepositoryDescriptionField),
} }
// Migrate database to current version // Migrate database to current version

34
models/migrations/v18.go

@ -0,0 +1,34 @@
// Copyright 2018 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"fmt"
"github.com/go-xorm/xorm"
"github.com/gogs/gogs/pkg/setting"
)
func updateRepositoryDescriptionField(x *xorm.Engine) error {
exist, err := x.IsTableExist("repository")
if err != nil {
return fmt.Errorf("IsTableExist: %v", err)
} else if !exist {
return nil
}
switch {
case setting.UseMySQL:
_, err = x.Exec("ALTER TABLE `repository` MODIFY `description` VARCHAR(512);")
case setting.UseMSSQL:
_, err = x.Exec("ALTER TABLE `repository` ALTER COLUMN `description` VARCHAR(512);")
case setting.UsePostgreSQL:
_, err = x.Exec("ALTER TABLE `repository` ALTER COLUMN `description` TYPE VARCHAR(512);")
case setting.UseSQLite3:
// Sqlite3 uses TEXT type by default for any string type field.
// Keep this comment to mention that we don't missed any option.
}
return err
}

6
models/repo.go

@ -146,7 +146,7 @@ type Repository struct {
Owner *User `xorm:"-" json:"-"` Owner *User `xorm:"-" json:"-"`
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"INDEX NOT NULL"` Name string `xorm:"INDEX NOT NULL"`
Description string Description string `xorm:"VARCHAR(512)"`
Website string Website string
DefaultBranch string DefaultBranch string
Size int64 `xorm:"NOT NULL DEFAULT 0"` Size int64 `xorm:"NOT NULL DEFAULT 0"`
@ -1331,8 +1331,8 @@ func GetRepositoriesByForkID(forkID int64) ([]*Repository, error) {
func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err error) { func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err error) {
repo.LowerName = strings.ToLower(repo.Name) repo.LowerName = strings.ToLower(repo.Name)
if len(repo.Description) > 255 { if len(repo.Description) > 512 {
repo.Description = repo.Description[:255] repo.Description = repo.Description[:512]
} }
if len(repo.Website) > 255 { if len(repo.Website) > 255 {
repo.Website = repo.Website[:255] repo.Website = repo.Website[:255]

6
pkg/form/repo.go

@ -26,7 +26,7 @@ type CreateRepo struct {
UserID int64 `binding:"Required"` UserID int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool Private bool
Description string `binding:"MaxSize(255)"` Description string `binding:"MaxSize(512)"`
AutoInit bool AutoInit bool
Gitignores string Gitignores string
License string License string
@ -45,7 +45,7 @@ type MigrateRepo struct {
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
Mirror bool `json:"mirror"` Mirror bool `json:"mirror"`
Private bool `json:"private"` Private bool `json:"private"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(512)"`
} }
func (f *MigrateRepo) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *MigrateRepo) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
@ -82,7 +82,7 @@ func (f MigrateRepo) ParseRemoteAddr(user *models.User) (string, error) {
type RepoSetting struct { type RepoSetting struct {
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Description string `binding:"MaxSize(255)"` Description string `binding:"MaxSize(512)"`
Website string `binding:"Url;MaxSize(100)"` Website string `binding:"Url;MaxSize(100)"`
Branch string Branch string
Interval int Interval int

21
public/js/gogs.js

@ -1443,3 +1443,24 @@ $(function () {
if ($('.user.signin').length > 0) return; if ($('.user.signin').length > 0) return;
$('form').areYouSure(); $('form').areYouSure();
}); });
function showMessageMaxLength(maxLen, textElemId, counterId) {
var $msg = $('#'+textElemId); //text message
$('#'+counterId).html(maxLen - $msg.val().length); //symbols count
var onMessageKey = function (e) {
var $msg = $(this);
var text = $msg.val();
var len = text.length;
var remainder = maxLen - len;
if (len >= maxLen) {
$msg.val($msg.val().substr(0, maxLen));
remainder = 0;
}
$('#'+counterId).html(remainder);
};
$msg.keyup(onMessageKey).keydown(onMessageKey);
}

6
public/plugins/autosize-4.0.2/dist/autosize.min.js vendored

@ -0,0 +1,6 @@
/*!
autosize 4.0.2
license: MIT
http://www.jacklmoore.com/autosize
*/
!function(e,t){if("function"==typeof define&&define.amd)define(["module","exports"],t);else if("undefined"!=typeof exports)t(module,exports);else{var n={exports:{}};t(n,n.exports),e.autosize=n.exports}}(this,function(e,t){"use strict";var n,o,p="function"==typeof Map?new Map:(n=[],o=[],{has:function(e){return-1<n.indexOf(e)},get:function(e){return o[n.indexOf(e)]},set:function(e,t){-1===n.indexOf(e)&&(n.push(e),o.push(t))},delete:function(e){var t=n.indexOf(e);-1<t&&(n.splice(t,1),o.splice(t,1))}}),c=function(e){return new Event(e,{bubbles:!0})};try{new Event("test")}catch(e){c=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!1),t}}function r(r){if(r&&r.nodeName&&"TEXTAREA"===r.nodeName&&!p.has(r)){var e,n=null,o=null,i=null,d=function(){r.clientWidth!==o&&a()},l=function(t){window.removeEventListener("resize",d,!1),r.removeEventListener("input",a,!1),r.removeEventListener("keyup",a,!1),r.removeEventListener("autosize:destroy",l,!1),r.removeEventListener("autosize:update",a,!1),Object.keys(t).forEach(function(e){r.style[e]=t[e]}),p.delete(r)}.bind(r,{height:r.style.height,resize:r.style.resize,overflowY:r.style.overflowY,overflowX:r.style.overflowX,wordWrap:r.style.wordWrap});r.addEventListener("autosize:destroy",l,!1),"onpropertychange"in r&&"oninput"in r&&r.addEventListener("keyup",a,!1),window.addEventListener("resize",d,!1),r.addEventListener("input",a,!1),r.addEventListener("autosize:update",a,!1),r.style.overflowX="hidden",r.style.wordWrap="break-word",p.set(r,{destroy:l,update:a}),"vertical"===(e=window.getComputedStyle(r,null)).resize?r.style.resize="none":"both"===e.resize&&(r.style.resize="horizontal"),n="content-box"===e.boxSizing?-(parseFloat(e.paddingTop)+parseFloat(e.paddingBottom)):parseFloat(e.borderTopWidth)+parseFloat(e.borderBottomWidth),isNaN(n)&&(n=0),a()}function s(e){var t=r.style.width;r.style.width="0px",r.offsetWidth,r.style.width=t,r.style.overflowY=e}function u(){if(0!==r.scrollHeight){var e=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push({node:e.parentNode,scrollTop:e.parentNode.scrollTop}),e=e.parentNode;return t}(r),t=document.documentElement&&document.documentElement.scrollTop;r.style.height="",r.style.height=r.scrollHeight+n+"px",o=r.clientWidth,e.forEach(function(e){e.node.scrollTop=e.scrollTop}),t&&(document.documentElement.scrollTop=t)}}function a(){u();var e=Math.round(parseFloat(r.style.height)),t=window.getComputedStyle(r,null),n="content-box"===t.boxSizing?Math.round(parseFloat(t.height)):r.offsetHeight;if(n<e?"hidden"===t.overflowY&&(s("scroll"),u(),n="content-box"===t.boxSizing?Math.round(parseFloat(window.getComputedStyle(r,null).height)):r.offsetHeight):"hidden"!==t.overflowY&&(s("hidden"),u(),n="content-box"===t.boxSizing?Math.round(parseFloat(window.getComputedStyle(r,null).height)):r.offsetHeight),i!==n){i=n;var o=c("autosize:resized");try{r.dispatchEvent(o)}catch(e){}}}}function i(e){var t=p.get(e);t&&t.destroy()}function d(e){var t=p.get(e);t&&t.update()}var l=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?((l=function(e){return e}).destroy=function(e){return e},l.update=function(e){return e}):((l=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],function(e){return r(e)}),e}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],i),e},l.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],d),e}),t.default=l,e.exports=t.default});

1
routes/repo/repo.go

@ -75,6 +75,7 @@ func Create(c *context.Context) {
c.Data["readme"] = "Default" c.Data["readme"] = "Default"
c.Data["private"] = c.User.LastRepoVisibility c.Data["private"] = c.User.LastRepoVisibility
c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
c.Data["RequireAutosize"] = true
ctxUser := checkContextUser(c, c.QueryInt64("org")) ctxUser := checkContextUser(c, c.QueryInt64("org"))
if c.Written() { if c.Written() {

1
routes/repo/setting.go

@ -34,6 +34,7 @@ const (
func Settings(c *context.Context) { func Settings(c *context.Context) {
c.Title("repo.settings") c.Title("repo.settings")
c.PageIs("SettingsOptions") c.PageIs("SettingsOptions")
c.Data["RequireAutosize"] = true
c.Success(SETTINGS_OPTIONS) c.Success(SETTINGS_OPTIONS)
} }

4
templates/base/head.tmpl

@ -41,7 +41,9 @@
<script src="{{AppSubURL}}/js/libs/jquery.are-you-sure.js"></script> <script src="{{AppSubURL}}/js/libs/jquery.are-you-sure.js"></script>
<link rel="stylesheet" href="{{AppSubURL}}/assets/font-awesome-4.6.3/css/font-awesome.min.css"> <link rel="stylesheet" href="{{AppSubURL}}/assets/font-awesome-4.6.3/css/font-awesome.min.css">
<link rel="stylesheet" href="{{AppSubURL}}/assets/octicons-4.3.0/octicons.min.css"> <link rel="stylesheet" href="{{AppSubURL}}/assets/octicons-4.3.0/octicons.min.css">
{{if .RequireAutosize}}
<script src="{{AppSubURL}}/plugins/autosize-4.0.2/dist/autosize.min.js"></script>
{{end}}
<!-- notebook.js for rendering ipython notebooks and marked.js for rendering markdown in notebooks --> <!-- notebook.js for rendering ipython notebooks and marked.js for rendering markdown in notebooks -->
{{if .IsIPythonNotebook}} {{if .IsIPythonNotebook}}
<script src="{{AppSubURL}}/plugins/notebookjs-0.3.0/notebook.min.js"></script> <script src="{{AppSubURL}}/plugins/notebookjs-0.3.0/notebook.min.js"></script>

14
templates/repo/create.tmpl

@ -52,7 +52,9 @@
</div> </div>
<div class="inline field {{if .Err_Description}}error{{end}}"> <div class="inline field {{if .Err_Description}}error{{end}}">
<label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label>
<textarea id="description" name="description">{{.description}}</textarea> <textarea id="description" name="description" rows="3">{{.description}}</textarea>
<span class="help">{{.i18n.Tr "repo.repo_description_helper" | Safe}}</span>
<span class="help">{{.i18n.Tr "repo.repo_description_length"}}: <span id="descLength"></span></span>
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
@ -113,4 +115,14 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript">
$(document).ready(function(){
if (typeof window.autosize !== "undefined") {
autosize($('#description'));
}
showMessageMaxLength(512, 'description', 'descLength');
});
</script>
{{template "base/footer" .}} {{template "base/footer" .}}

13
templates/repo/settings/options.tmpl

@ -19,7 +19,9 @@
</div> </div>
<div class="field {{if .Err_Description}}error{{end}}"> <div class="field {{if .Err_Description}}error{{end}}">
<label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label> <label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label>
<textarea id="description" name="description" rows="2">{{.Repository.Description}}</textarea> <textarea id="description" name="description" rows="3">{{.Repository.Description}}</textarea>
<p class="help">{{.i18n.Tr "repo.settings.description_desc"}}</p>
<p class="help">{{.i18n.Tr "repo.settings.description_length"}}: <span id="descLength"></span></p>
</div> </div>
<div class="field {{if .Err_Website}}error{{end}}"> <div class="field {{if .Err_Website}}error{{end}}">
<label for="website">{{.i18n.Tr "repo.settings.site"}}</label> <label for="website">{{.i18n.Tr "repo.settings.site"}}</label>
@ -415,4 +417,13 @@
{{end}} {{end}}
{{end}} {{end}}
<script type="text/javascript">
$(document).ready(function(){
if (typeof window.autosize !== "undefined") {
autosize($('#description'));
}
showMessageMaxLength(512, 'description', 'descLength');
});
</script>
{{template "base/footer" .}} {{template "base/footer" .}}

Loading…
Cancel
Save