This is an automated email from the git hooks/post-receive script. New commit to branch bow-v2-go in repository bow. See https://gitlab.nuiton.org/chorem/bow.git commit a4fa61db4e4f1afe0ec53fb61b4ef5dfa20814e6 Author: Benjamin <poussin@codelutin.com> Date: Sun Apr 19 23:00:55 2020 +0200 debut implantation opensearch - sauvegarde ok - moteur de recherche ok - recherche par tags ok Amelioration des erreurs pour avoir une stack d'appel --- pkg/constant/const.go | 49 +++++++++++++++++++++- pkg/http/actionResource.go | 80 +++++++++++++++++++++++++++++++++++- pkg/http/bookmarkResource.go | 51 ++++++++++++----------- pkg/http/router.go | 4 +- pkg/repository/bookmarkRepository.go | 9 ++-- pkg/repository/database.go | 14 ++++++- pkg/repository/userRepository.go | 6 ++- pkg/utils/error.go | 36 ++++++++++------ pkg/utils/utils.go | 17 ++++++++ web/public/index.html | 1 + web/public/opensearch.xml | 11 +++++ web/src/components/SearchInput.vue | 6 +-- web/src/views/BookmarkEdit.vue | 49 +++++++++++++++++++--- 13 files changed, 275 insertions(+), 58 deletions(-) diff --git a/pkg/constant/const.go b/pkg/constant/const.go index 5f3ce80..df4069b 100644 --- a/pkg/constant/const.go +++ b/pkg/constant/const.go @@ -36,6 +36,11 @@ WithCount indique de retourner le nombre d'occurrence de ce tag */ const WithCount = "with-count" +/* +Action query parameter name for tags search +*/ +const Action = "action" + /* Tags query parameter name for tags search */ @@ -47,7 +52,7 @@ Fulltext query parameter name for fulltext search const Fulltext = "fulltext" /* -Fulltext query parameter name for fulltext search +Query query parameter name for fulltext search */ const Query = "query" @@ -56,6 +61,11 @@ ID query parameter name for id search */ const ID = "id" +/* +URI query parameter name for uri search +*/ +const URI = "uri" + /* OrderBy query parameter name for select order sorting */ @@ -69,4 +79,39 @@ const OrderDesc = "orderdesc" /* First query parameter name first result */ -const First = "first" \ No newline at end of file +const First = "first" + +/* +Save string action to save bookmark +*/ +const Save = "search.add" + +/* +RedirectAlias string action to redirect to alias (private or public) +*/ +const RedirectAlias = "search.alias" + +/* +SuggestionAlias string action to suggest alias (private) +*/ +const SuggestionAlias = "suggestion.alias" + +/* +SearchFulltext string action to fulltext search in all bookmarks +*/ +const SearchFulltext = "search.fulltext" + +/* +SuggestionFulltext string action to suggest url from fulltext search in all alias +*/ +const SuggestionFulltext = "suggestion.fulltext" + +/* +SearchTag string action to search on tag in all bookmarks +*/ +const SearchTag = "search.tag" + +/* +SuggestionTag string action to suggest url from tag in all alias +*/ +const SuggestionTag = "suggestion.tag" diff --git a/pkg/http/actionResource.go b/pkg/http/actionResource.go index 470e4d9..631f9be 100644 --- a/pkg/http/actionResource.go +++ b/pkg/http/actionResource.go @@ -1,9 +1,87 @@ package http import ( + "fmt" "net/http" + "strings" + + "gitlab.chorem.org/chorem/bow/pkg/constant" + "gitlab.chorem.org/chorem/bow/pkg/model" ) +func getOrEmpty(args []string, i int) string { + result := "" + if len(args) > i { + result = args[i] + } + + return result +} + +var actions = map [string]func(w http.ResponseWriter, r *http.Request, ask string) { + constant.Save: func(w http.ResponseWriter, r *http.Request, ask string) { + args := strings.Split(ask, "|") + uri := getOrEmpty(args, 0) + description := getOrEmpty(args, 1) + tags := getOrEmpty(args, 2) + privatealias := getOrEmpty(args, 3) + publicalias := getOrEmpty(args, 4) + lang := getOrEmpty(args, 5) + + // FIXME parametrer l'url + url := fmt.Sprintf("http://localhost:8080/edit/new?uri=%s&description=%s&tags=%s&privatealias=%s&publicalias=%s&lang=%s", uri, description, tags, privatealias, publicalias, lang) + http.Redirect(w, r, url, http.StatusFound) + }, + constant.RedirectAlias: func(w http.ResponseWriter, r *http.Request, ask string) { + // TODO recherche dans les alias prives + http.Redirect(w, r, "TODO", http.StatusFound) + }, + constant.SuggestionAlias: func(w http.ResponseWriter, r *http.Request, ask string) { + // renvoyer les couple alias/uri/description? pour l'affichage en suggest + //http.Redirect(w, r, uri, http.StatusFound) + }, + constant.SearchFulltext: func(w http.ResponseWriter, r *http.Request, ask string) { + url := fmt.Sprintf("http://localhost:8080/?fulltext=%s", ask) + http.Redirect(w, r, url, http.StatusFound) + }, + constant.SuggestionFulltext: func(w http.ResponseWriter, r *http.Request, ask string) { + // renvoyer les couple uri/description? pour l'affichage en suggest + //http.Redirect(w, r, uri, http.StatusFound) + }, + constant.SearchTag: func(w http.ResponseWriter, r *http.Request, ask string) { + url := fmt.Sprintf("http://localhost:8080/?tags=%s", ask) + http.Redirect(w, r, url, http.StatusFound) + }, + constant.SuggestionTag: func(w http.ResponseWriter, r *http.Request, ask string) { + // renvoyer les couple uri/description? pour l'affichage en suggest + //http.Redirect(w, r, uri, http.StatusFound) + }} + func doActions(w http.ResponseWriter, r *http.Request) { - + currentUser := r.Context().Value(constant.User).(model.BowUser) + + query := r.URL.Query() + ask := query.Get(constant.Action) + + // l'action a faire + var todo string + for _, a := range currentUser.Actions { + prefix := a.Prefix + if prefix == "" { + todo = a.Action + } else if strings.HasPrefix(ask, prefix) { + todo = a.Action + ask = ask[len(prefix):] + break + } + } + + toCall := actions[todo] + if toCall != nil { + toCall(w, r, ask) + } else { + uri := strings.Replace(todo, "{searchTerms}", ask, -1) + http.Redirect(w, r, uri, http.StatusFound) + } + } diff --git a/pkg/http/bookmarkResource.go b/pkg/http/bookmarkResource.go index df8c0a5..0e19afe 100644 --- a/pkg/http/bookmarkResource.go +++ b/pkg/http/bookmarkResource.go @@ -16,24 +16,25 @@ import ( ) func getBookmarks(w http.ResponseWriter, r *http.Request) { - currentUserID := r.Context().Value(constant.User).(model.BowUser) - query := r.URL.Query() - id := query.Get(constant.ID) - tags := query.Get(constant.Tags) - fulltext := query.Get(constant.Fulltext) - - log.Println("query: ", query) - orderBy := query.Get(constant.OrderBy) - orderDesc, err := strconv.ParseBool(query.Get(constant.OrderDesc)) + currentUser := r.Context().Value(constant.User).(model.BowUser) + queryParams := r.URL.Query() + id := queryParams.Get(constant.ID) + uri := queryParams.Get(constant.URI) + tags := queryParams.Get(constant.Tags) + fulltext := queryParams.Get(constant.Fulltext) + + log.Println("query: ", queryParams) + orderBy := queryParams.Get(constant.OrderBy) + orderDesc, err := strconv.ParseBool(queryParams.Get(constant.OrderDesc)) if err != nil { orderDesc = false } - first, err := strconv.ParseInt(query.Get(constant.First), 10, 16) + first, err := strconv.ParseInt(queryParams.Get(constant.First), 10, 16) if err != nil { first = 0 } - json, err := repository.BookmarkJSON(currentUserID, id, tags, fulltext, orderBy, orderDesc, int(first)) + json, err := repository.BookmarkJSON(currentUser, id, uri, tags, fulltext, orderBy, orderDesc, int(first)) if err != nil { if utils.Is404(err) { // on a rien retrouve, on renvoie une collection vide @@ -49,10 +50,10 @@ func getBookmarks(w http.ResponseWriter, r *http.Request) { } func getBookmark(w http.ResponseWriter, r *http.Request) { - currentUserID := r.Context().Value(constant.User).(model.BowUser) + currentUser := r.Context().Value(constant.User).(model.BowUser) id := mux.Vars(r)["id"] - json, err := repository.BookmarkJSON(currentUserID, id, "", "", "", false, 0) + json, err := repository.BookmarkJSON(currentUser, id, "", "", "", "", false, 0) if err != nil { utils.Throw(w, err) return @@ -66,7 +67,7 @@ func getBookmark(w http.ResponseWriter, r *http.Request) { } func getTags(w http.ResponseWriter, r *http.Request) { - currentUserID := r.Context().Value(constant.User).(model.BowUser) + currentUser := r.Context().Value(constant.User).(model.BowUser) query := r.URL.Query() filter := query.Get(constant.Filter) @@ -75,7 +76,7 @@ func getTags(w http.ResponseWriter, r *http.Request) { withCount = false } - json, err := repository.TagsJSON(currentUserID, filter, withCount) + json, err := repository.TagsJSON(currentUser, filter, withCount) if err != nil { utils.Throw(w, err) return @@ -86,10 +87,10 @@ func getTags(w http.ResponseWriter, r *http.Request) { } func addOneVisit(w http.ResponseWriter, r *http.Request) { - currentUserID := r.Context().Value(constant.User).(model.BowUser) + currentUser := r.Context().Value(constant.User).(model.BowUser) id := mux.Vars(r)["id"] - uri, err := repository.AddOneVisit(currentUserID, id) + uri, err := repository.AddOneVisit(currentUser, id) if err != nil { utils.Throw(w, err) return @@ -99,18 +100,18 @@ func addOneVisit(w http.ResponseWriter, r *http.Request) { } func addBookmark(w http.ResponseWriter, r *http.Request) { - currentUserID := r.Context().Value(constant.User).(model.BowUser) + currentUser := r.Context().Value(constant.User).(model.BowUser) var bookmark model.Bookmark err := json.NewDecoder(r.Body).Decode(&bookmark) if err != nil { - utils.Throw(w, utils.NewHTTPError400(err, currentUserID)) + utils.Throw(w, utils.NewHTTPError400(err, currentUser)) return } - id, err := repository.CreateBookmark(currentUserID, bookmark) + id, err := repository.CreateBookmark(currentUser, bookmark) if err != nil { - utils.Throw(w, utils.NewHTTPError500(err, currentUserID)) + utils.Throw(w, utils.NewHTTPError500(err, currentUser)) return } @@ -133,20 +134,20 @@ func deleteBookmark(w http.ResponseWriter, r *http.Request) { updateBookmark save bookmark withour AuthenticationInfo */ func updateBookmark(w http.ResponseWriter, r *http.Request) { - currentUserID := r.Context().Value(constant.User).(model.BowUser) + currentUser := r.Context().Value(constant.User).(model.BowUser) id := mux.Vars(r)["id"] var bookmark model.Bookmark err := json.NewDecoder(r.Body).Decode(&bookmark) if err != nil { - utils.Throw(w, utils.NewHTTPError400(err, currentUserID)) + utils.Throw(w, utils.NewHTTPError400(err, currentUser)) return } bookmark.ID = id - err = repository.UpdateBookmark(currentUserID, bookmark) + err = repository.UpdateBookmark(currentUser, bookmark) if err != nil { - utils.Throw(w, utils.NewHTTPError500(err, currentUserID)) + utils.Throw(w, utils.NewHTTPError500(err, currentUser)) return } } diff --git a/pkg/http/router.go b/pkg/http/router.go index 8eb71c4..da1f5f7 100644 --- a/pkg/http/router.go +++ b/pkg/http/router.go @@ -54,7 +54,7 @@ func Start(addr string) { s.HandleFunc("/bookmarks/{id}", updateBookmark).Methods(http.MethodPut, http.MethodOptions) s.HandleFunc("/bookmarks/{id}/visit", addOneVisit).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) s.HandleFunc("/bookmarks/{id}/authenticationinfo", updateBookmarkAuthenticationInfo).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/actions", doActions).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) + s.HandleFunc("/action", doActions).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) srv := &http.Server{ Handler: router, @@ -156,7 +156,7 @@ func authentication(next http.Handler) http.Handler { // try as application token user, err = repository.UserFromToken(token, "id", "login", "creationdate", "maxtagincloud", "maxresult", "actions") if err != nil { - utils.Throw(w, err) + utils.Throw(w, utils.NewHTTPError500(err, constant.Nobody)) return } } else if err != nil { diff --git a/pkg/repository/bookmarkRepository.go b/pkg/repository/bookmarkRepository.go index 1ea4e0a..6afaef2 100644 --- a/pkg/repository/bookmarkRepository.go +++ b/pkg/repository/bookmarkRepository.go @@ -16,7 +16,7 @@ import ( /* BookmarkJSON retourne le bookmark au format json */ -func BookmarkJSON(currentUser model.BowUser, id string, tags string, fulltext string, orderBy string, orderDesc bool, first int) (string, error) { +func BookmarkJSON(currentUser model.BowUser, id string, uri string, tags string, fulltext string, orderBy string, orderDesc bool, first int) (string, error) { var result string var err error @@ -36,10 +36,13 @@ func BookmarkJSON(currentUser model.BowUser, id string, tags string, fulltext st orderDirection = "desc" } - log.Printf("search bookmark id: %v, tags: '%v', fulltext: '%v', orderBy: %v, orderDesc: %v, first:%v", id, tags, fulltext, orderBy, orderDesc, first) + log.Printf("search bookmark id: %v, uri: %v, tags: '%v', fulltext: '%v', orderBy: %v, orderDesc: %v, first:%v", id, uri, tags, fulltext, orderBy, orderDesc, first) if id != "" { q := &query{sql: `WITH __all AS (select * from bookmark where id=$1) SELECT json_agg(__all.*) as j FROM __all`} result, err = q.QueryString(currentUser, id) + } else if uri != "" { + q := &query{sql: `WITH __all AS (select * from bookmark where uri=$1) SELECT json_agg(__all.*) as j FROM __all`} + result, err = q.QueryString(currentUser, uri) } else { tagsJSON := "{" + strings.Join(strings.Fields(tags), ",") + "}" q := &query{sql: fmt.Sprintf(` @@ -65,7 +68,7 @@ func TagsJSON(currentUser model.BowUser, filter string, withCount bool) (string, } else { q = &query{sql: `WITH __all AS (select distinct unnest(tags) as tag from bookmark order by 1), __some AS (select * from __all where tag ilike $1) select json_agg(tag) from __some;`} } - substring := fmt.Sprintf("%%%s%%", filter) + substring := fmt.Sprintf("%%%s%%", filter) // to do like, with must have many % :) result, err := q.QueryString(currentUser, substring) if err != nil { return "", utils.NewHTTPError500(err, currentUser) diff --git a/pkg/repository/database.go b/pkg/repository/database.go index 8f1e3e7..092f846 100644 --- a/pkg/repository/database.go +++ b/pkg/repository/database.go @@ -269,7 +269,7 @@ func (q *query) QueryString(currentUser model.BowUser, arguments ...interface{}) var pgjson pgtype.JSON err = row.Scan(&pgjson) if err != nil { - return "", utils.NewHTTPError500(err, currentUser) + return "", utils.NewHTTPError(fmt.Sprintf("Can't execute query '%s' %s (%v)", utils.RemoveTagReturn(q.sql), argToString(arguments...), err), currentUser, 500) } if q.postsql != "" { @@ -296,3 +296,15 @@ func (q *query) QueryString(currentUser model.BowUser, arguments ...interface{}) return result, nil } + +func argToString(arguments ...interface{}) string { + result := "" + if len(arguments) > 0 { + var argstring = make([]string, len(arguments)) + for i, arg := range arguments { + argstring[i] = fmt.Sprintf("'%v'", arg) + } + result = "with arguments: " + strings.Join(argstring, ", ") + } + return result +} \ No newline at end of file diff --git a/pkg/repository/userRepository.go b/pkg/repository/userRepository.go index 6dd0b21..fb6a7ec 100644 --- a/pkg/repository/userRepository.go +++ b/pkg/repository/userRepository.go @@ -60,9 +60,11 @@ func UserFromToken(token string, fields ...string) (model.BowUser, error) { tokenJSON := fmt.Sprintf(`{"token": "%s"}`, token) q := &query{sql: fmt.Sprintf(` - select $1 from bowuser b + WITH __all AS (select %s from bowuser b where exists (select * from jsonb_array_elements(tokens) as x - where x @> $1);`, allFields)} + where x @> $1)) + SELECT json_agg(__all) as j + FROM __all;`, allFields)} result, err := q.QueryString(currentUser, tokenJSON) if err != nil { return user, utils.NewHTTPError500(err, currentUser) diff --git a/pkg/utils/error.go b/pkg/utils/error.go index 99be5d6..ddaea58 100644 --- a/pkg/utils/error.go +++ b/pkg/utils/error.go @@ -10,6 +10,12 @@ import ( "gitlab.chorem.org/chorem/bow/pkg/model" ) +type codeRef struct { + file string + fn string + line int +} + /* httpError erreur permettant de porter le code HTTP souhaite */ @@ -18,15 +24,21 @@ type httpError struct { Msg string CurrentUserID string StatusCode int - file string - fn string - line int + stack []codeRef } func (e *httpError) Error() string { return fmt.Sprintf(`{"ID": "%s", "currentUser": "%s", "StatusCode": %v, "Msg": %q}`, e.ID, e.CurrentUserID, e.StatusCode, e.Msg) } +func addCodeRef(err *httpError) { + pc, file, line, _ := runtime.Caller(3) + fn := runtime.FuncForPC(pc).Name() + pos := strings.LastIndex(fn, "/") + pos = strings.Index(fn[pos:], ".") + pos + 1 + err.stack = append(err.stack, codeRef{file: file, fn:fn[pos:], line: line}) +} + func createHTTPError(msg string, currentUser model.BowUser, statusCode int) *httpError { e := httpError{} e.ID, _ = GenUUID() @@ -37,15 +49,8 @@ func createHTTPError(msg string, currentUser model.BowUser, statusCode int) *htt } else { e.StatusCode = 500 } - - pc, file, line, _ := runtime.Caller(2) - fn := runtime.FuncForPC(pc).Name() - pos := strings.LastIndex(fn, "/") - pos = strings.Index(fn[pos:], ".") + pos + 1 - e.file = file - e.fn = fn[pos:] - e.line = line - + e.stack = make([]codeRef, 0, 1) + addCodeRef(&e) return &e } @@ -55,6 +60,7 @@ func NewHTTPError(msg string, currentUser model.BowUser, statusCode int) *httpEr func NewHTTPError400(err error, currentUser model.BowUser) *httpError { if err, ok := err.(*httpError); ok { + addCodeRef(err) return err } @@ -63,6 +69,7 @@ func NewHTTPError400(err error, currentUser model.BowUser) *httpError { func NewHTTPError500(err error, currentUser model.BowUser) *httpError { if err, ok := err.(*httpError); ok { + addCodeRef(err) return err } @@ -79,7 +86,10 @@ func Is404(err error) bool { func Throw(w http.ResponseWriter, err error) { if err, ok := err.(*httpError); ok { if err.StatusCode >= 500 || true { // FIXME faire mode debug - log.Printf("[error] in [%s#%s:%d] %v", err.file, err.fn, err.line, err) + log.Printf("[error] %v\n", err) + for _, ref := range err.stack { + log.Printf("\tin [%s#%s:%d] ", ref.file, ref.fn, ref.line) + } } http.Error(w, fmt.Sprintf("%s", err), err.StatusCode) } else { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..d7dad83 --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,17 @@ +package utils + +import ( + "strings" +) + +/* +RemoveTagReturn remove all \n and \t in the string +*/ +func RemoveTagReturn(s string) string { + return strings.Map(func(c rune) rune { + if c == '\n' || c == '\t' { + return -1 + } + return c + }, s) +} diff --git a/web/public/index.html b/web/public/index.html index c053282..4703ad2 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -5,6 +5,7 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.png"> + <link title="bow" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml"> <title>Bow - <%= htmlWebpackPlugin.options.title %></title> </head> <body> diff --git a/web/public/opensearch.xml b/web/public/opensearch.xml new file mode 100644 index 0000000..d51563f --- /dev/null +++ b/web/public/opensearch.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/"> + <ShortName>Bow</ShortName> + <Description>Search with Bow</Description> + <InputEncoding>UTF-8</InputEncoding> + <LongName>Bow Search</LongName> + <Image height="16" width="16">data:image/x-icon;base64,R0lGODlhEAAQAMMCAAAAAP97AP////+9hP/nxv+cQsbGxoSEhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAUAAAgALAAAAAAQABAAAARHEIE5kbWUhr0rFmA2ccAFhJIJdNJwlqnZEaUXYwB97Xe+wryUbxUA8nwY1lEnM7ZqsiKuAKxKTwYn7Ic9OIXFiTeIu353lQgAOw==</Image> + <Url type="text/html" method="get" template="https://localhost:8000/api/v1/action?action={searchTerms}"/> + <Url type="application/x-suggestions+json" template="https://localhost:8000/api/v1/action?suggestion={searchTerms}"/> + <moz:SearchForm>https://bow.chorem.org/bow/</moz:SearchForm> +</OpenSearchDescription> diff --git a/web/src/components/SearchInput.vue b/web/src/components/SearchInput.vue index ebabd94..487e01b 100644 --- a/web/src/components/SearchInput.vue +++ b/web/src/components/SearchInput.vue @@ -61,9 +61,9 @@ class SearchInput extends Vue { !this.sameValue(this.mFulltext, this.first) ) { let query = {} - this.mTags && (query.t = this.mTags) - this.mFulltext && (query.f = this.mFulltext) - this.mQuery && (query.q = this.mQuery) + this.mTags && (query.tags = this.mTags) + this.mFulltext && (query.fulltext = this.mFulltext) + this.mQuery && (query.query = this.mQuery) this.mOrderby && (query.orderBy = this.mOrderby) this.mOrderdesc && (query.orderDesc = this.mOrderdesc) this.mFulltext && (query.first = this.mFirst) diff --git a/web/src/views/BookmarkEdit.vue b/web/src/views/BookmarkEdit.vue index d94ecd4..cfdca4e 100644 --- a/web/src/views/BookmarkEdit.vue +++ b/web/src/views/BookmarkEdit.vue @@ -48,9 +48,9 @@ class BookmarkEdit extends Vue { bookmark = { uri: this.uri, description: this.description, - tags: this.tags, - privatealias: this.privatealias, - publicalias: this.publicalias, + tags: this.stringToArray(this.tags), + privatealias: this.stringToArray(this.privatealias), + publicalias: this.stringToArray(this.publicalias), lang: this.lang } @@ -64,6 +64,25 @@ class BookmarkEdit extends Vue { this.errorMsg = err.cause } ) + } else if (this.bookmark.uri) { + let searchParams = new URLSearchParams() + this.uri && searchParams.append('uri', this.bookmark.uri) + this.$fetch.get(`/bookmarks?${searchParams.toString()}`).then( + data => { + if (data.length > 0) { + let b = data[0] + this.bookmark.id = b.id + this.bookmark.description = b.description + '\n' + new Date().toISOString().slice(0, 10) + '\n' + this.bookmark.description + this.bookmark.tags = [...new Set(this.stringToArray(b.tags).concat(this.bookmark.tags))] + this.bookmark.privatealias = [...new Set(this.stringToArray(b.privatealias).concat(this.bookmark.privatealias))] + this.bookmark.publicalias = [...new Set(this.stringToArray(b.publicalias).concat(this.bookmark.publicalias))] + this.bookmark.lang = b.lang || this.bookmark.lang + } + }, + err => { + this.errorMsg = err.cause + } + ) } } @@ -71,15 +90,33 @@ class BookmarkEdit extends Vue { this.$router.go(-1) } + stringToArray(s) { + if (!s) { + return [] + } + + if (!Array.isArray(s)) { + return s.split(/[,\s*]/) + } + + return s + } + save() { let promise - if (this.bookmark.tags && !Array.isArray(this.bookmark.tags)) { + if (!this.bookmark.tags) { + this.bookmark.tags = [] + } else if (!Array.isArray(this.bookmark.tags)) { this.bookmark.tags = this.bookmark.tags.split(/[,\s*]/) } - if (this.bookmark.privatealias && !Array.isArray(this.bookmark.privatealias)) { + if (!this.bookmark.privatealias) { + this.bookmark.privatealias = [] + } else if (!Array.isArray(this.bookmark.privatealias)) { this.bookmark.privatealias = this.bookmark.privatealias.split(/[,\s*]/) } - if (this.bookmark.publicalias && !Array.isArray(this.bookmark.publicalias)) { + if (!this.bookmark.publicalias) { + this.bookmark.publicalias = [] + } else if (!Array.isArray(this.bookmark.publicalias)) { this.bookmark.publicalias = this.bookmark.publicalias.split(/[,\s*]/) } -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.