Merge branch 'main' of github.com:writeas/web-core into main
This commit is contained in:
@@ -14,20 +14,34 @@ type Attachment struct {
|
|||||||
|
|
||||||
type AttachmentType string
|
type AttachmentType string
|
||||||
|
|
||||||
const AttachImage AttachmentType = "Image"
|
const (
|
||||||
|
AttachImage AttachmentType = "Image"
|
||||||
|
AttachDocument AttachmentType = "Document"
|
||||||
|
)
|
||||||
|
|
||||||
// NewImageAttachment creates a new Attachment from the given URL, setting the
|
// NewImageAttachment creates a new Attachment from the given URL, setting the
|
||||||
// correct type and automatically detecting the MediaType based on the file
|
// correct type and automatically detecting the MediaType based on the file
|
||||||
// extension.
|
// extension.
|
||||||
func NewImageAttachment(url string) Attachment {
|
func NewImageAttachment(url string) Attachment {
|
||||||
var imgType string
|
return newAttachment(url, AttachImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDocumentAttachment creates a new Attachment from the given URL, setting the
|
||||||
|
// correct type and automatically detecting the MediaType based on the file
|
||||||
|
// extension.
|
||||||
|
func NewDocumentAttachment(url string) Attachment {
|
||||||
|
return newAttachment(url, AttachDocument)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAttachment(url string, attachType AttachmentType) Attachment {
|
||||||
|
var fileType string
|
||||||
extIdx := strings.LastIndexByte(url, '.')
|
extIdx := strings.LastIndexByte(url, '.')
|
||||||
if extIdx > -1 {
|
if extIdx > -1 {
|
||||||
imgType = mime.TypeByExtension(url[extIdx:])
|
fileType = mime.TypeByExtension(url[extIdx:])
|
||||||
}
|
}
|
||||||
return Attachment{
|
return Attachment{
|
||||||
Type: AttachImage,
|
Type: attachType,
|
||||||
URL: url,
|
URL: url,
|
||||||
MediaType: imgType,
|
MediaType: fileType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ func TestNewImageAttachment(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want *Attachment
|
want Attachment
|
||||||
}{
|
}{
|
||||||
{name: "good svg", args: args{"https://writefreely.org/img/writefreely.svg"}, want: &Attachment{
|
{name: "good svg", args: args{"https://writefreely.org/img/writefreely.svg"}, want: Attachment{
|
||||||
Type: "Image",
|
Type: "Image",
|
||||||
URL: "https://writefreely.org/img/writefreely.svg",
|
URL: "https://writefreely.org/img/writefreely.svg",
|
||||||
MediaType: "image/svg+xml",
|
MediaType: "image/svg+xml",
|
||||||
}},
|
}},
|
||||||
{name: "good png", args: args{"https://i.snap.as/12345678.png"}, want: &Attachment{
|
{name: "good png", args: args{"https://i.snap.as/12345678.png"}, want: Attachment{
|
||||||
Type: "Image",
|
Type: "Image",
|
||||||
URL: "https://i.snap.as/12345678.png",
|
URL: "https://i.snap.as/12345678.png",
|
||||||
MediaType: "image/png",
|
MediaType: "image/png",
|
||||||
}},
|
}},
|
||||||
{name: "no extension", args: args{"https://i.snap.as/12345678"}, want: &Attachment{
|
{name: "no extension", args: args{"https://i.snap.as/12345678"}, want: Attachment{
|
||||||
Type: "Image",
|
Type: "Image",
|
||||||
URL: "https://i.snap.as/12345678",
|
URL: "https://i.snap.as/12345678",
|
||||||
MediaType: "",
|
MediaType: "",
|
||||||
@@ -38,3 +38,27 @@ func TestNewImageAttachment(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewDocumentAttachment(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want Attachment
|
||||||
|
}{
|
||||||
|
{name: "mp3", args: args{"https://listen.as/matt/abc.mp3"}, want: Attachment{
|
||||||
|
Type: "Document",
|
||||||
|
URL: "https://listen.as/matt/abc.mp3",
|
||||||
|
MediaType: "audio/mpeg",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := NewDocumentAttachment(tt.args.url); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("NewDocumentAttachment() = %+v, want %+v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Package category supports post categories
|
||||||
|
package category
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/writeas/slug"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotFound = errors.New("category doesn't exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Category represents a post tag with additional metadata, like a title and slug.
|
||||||
|
type Category struct {
|
||||||
|
ID int64 `json:"-"`
|
||||||
|
Hashtag string `json:"hashtag"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCategory creates a Category you can insert into the database, based on a hashtag. It automatically breaks up the
|
||||||
|
// hashtag by words, based on capitalization, for both the title and a URL-friendly slug.
|
||||||
|
func NewCategory(hashtag string) *Category {
|
||||||
|
title := titleFromHashtag(hashtag)
|
||||||
|
return &Category{
|
||||||
|
Hashtag: hashtag,
|
||||||
|
Slug: slug.Make(title),
|
||||||
|
Title: title,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package category
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// titleFromHashtag generates an all-lowercase title, with spaces inserted based on initial capitalization -- e.g.
|
||||||
|
// "MyWordyTag" becomes "my wordy tag".
|
||||||
|
func titleFromHashtag(hashtag string) string {
|
||||||
|
var t strings.Builder
|
||||||
|
var prev rune
|
||||||
|
for i, c := range hashtag {
|
||||||
|
if unicode.IsUpper(c) {
|
||||||
|
if i > 0 && !unicode.IsUpper(prev) {
|
||||||
|
// Insert space if previous rune wasn't also uppercase (e.g. an abbreviation)
|
||||||
|
t.WriteRune(' ')
|
||||||
|
}
|
||||||
|
t.WriteRune(unicode.ToLower(c))
|
||||||
|
} else {
|
||||||
|
t.WriteRune(c)
|
||||||
|
}
|
||||||
|
prev = c
|
||||||
|
}
|
||||||
|
return t.String()
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package category
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTitleFromHashtag(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hashtag string
|
||||||
|
expTitle string
|
||||||
|
}{
|
||||||
|
{"proper noun", "Jane", "jane"},
|
||||||
|
{"full name", "JaneDoe", "jane doe"},
|
||||||
|
{"us words", "unitedStates", "united states"},
|
||||||
|
{"usa", "USA", "usa"},
|
||||||
|
{"us monoword", "unitedstates", "unitedstates"},
|
||||||
|
{"100dto", "100DaysToOffload", "100 days to offload"},
|
||||||
|
{"iphone", "iPhone", "iphone"},
|
||||||
|
{"ilike", "iLikeThis", "i like this"},
|
||||||
|
{"abird", "aBird", "a bird"},
|
||||||
|
{"all caps", "URGENT", "urgent"},
|
||||||
|
{"smartphone", "スマートフォン", "スマートフォン"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
res := titleFromHashtag(test.hashtag)
|
||||||
|
if res != test.expTitle {
|
||||||
|
t.Fatalf("#%s: got '%s' expected '%s'", test.hashtag, res, test.expTitle)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,14 @@ go 1.10
|
|||||||
require (
|
require (
|
||||||
github.com/gofrs/uuid v3.3.0+incompatible
|
github.com/gofrs/uuid v3.3.0+incompatible
|
||||||
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec
|
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2
|
github.com/microcosm-cc/bluemonday v1.0.5
|
||||||
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
||||||
github.com/writeas/impart v1.1.1
|
github.com/writeas/impart v1.1.1
|
||||||
github.com/writeas/openssl-go v1.0.0
|
github.com/writeas/openssl-go v1.0.0
|
||||||
github.com/writeas/saturday v1.7.1
|
github.com/writeas/saturday v1.7.1
|
||||||
|
github.com/writeas/slug v1.2.0
|
||||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17
|
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect
|
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
|
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
|
||||||
|
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
|
||||||
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
|
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
|
||||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||||
|
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
@@ -7,8 +13,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec h1:ZXWuspqypleMuJy4bzYEqlMhJnGAYpLrWe5p7W3CdvI=
|
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec h1:ZXWuspqypleMuJy4bzYEqlMhJnGAYpLrWe5p7W3CdvI=
|
||||||
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec/go.mod h1:voECJzdraJmolzPBgL9Z7ANwXf4oMXaTCsIkdiPpR/g=
|
github.com/kylemcc/twitter-text-go v0.0.0-20180726194232-7f582f6736ec/go.mod h1:voECJzdraJmolzPBgL9Z7ANwXf4oMXaTCsIkdiPpR/g=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
|
github.com/microcosm-cc/bluemonday v1.0.5 h1:cF59UCKMmmUgqN1baLvqU/B1ZsMori+duLVTLpgiG3w=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
github.com/microcosm-cc/bluemonday v1.0.5/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
|
||||||
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||||
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
|
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
|
||||||
@@ -19,10 +27,11 @@ github.com/writeas/openssl-go v1.0.0 h1:YXM1tDXeYOlTyJjoMlYLQH1xOloUimSR1WMF8kjF
|
|||||||
github.com/writeas/openssl-go v1.0.0/go.mod h1:WsKeK5jYl0B5y8ggOmtVjbmb+3rEGqSD25TppjJnETA=
|
github.com/writeas/openssl-go v1.0.0/go.mod h1:WsKeK5jYl0B5y8ggOmtVjbmb+3rEGqSD25TppjJnETA=
|
||||||
github.com/writeas/saturday v1.7.1 h1:lYo1EH6CYyrFObQoA9RNWHVlpZA5iYL5Opxo7PYAnZE=
|
github.com/writeas/saturday v1.7.1 h1:lYo1EH6CYyrFObQoA9RNWHVlpZA5iYL5Opxo7PYAnZE=
|
||||||
github.com/writeas/saturday v1.7.1/go.mod h1:ETE1EK6ogxptJpAgUbcJD0prAtX48bSloie80+tvnzQ=
|
github.com/writeas/saturday v1.7.1/go.mod h1:ETE1EK6ogxptJpAgUbcJD0prAtX48bSloie80+tvnzQ=
|
||||||
|
github.com/writeas/slug v1.2.0 h1:EMQ+cwLiOcA6EtFwUgyw3Ge18x9uflUnOnR6bp/J+/g=
|
||||||
|
github.com/writeas/slug v1.2.0/go.mod h1:RE8shOqQP3YhsfsQe0L3RnuejfQ4Mk+JjY5YJQFubfQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
|
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
|
||||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
|||||||
Reference in New Issue
Block a user