mirror of
https://github.com/foomo/neoscontentrepository.git
synced 2025-10-16 12:35:40 +00:00
initial commit
This commit is contained in:
commit
21e9ffeee8
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.*
|
||||
/.vscode
|
||||
!.git*
|
||||
!.*ignore
|
||||
/vendor
|
||||
30
exporter/export.go
Normal file
30
exporter/export.go
Normal file
@ -0,0 +1,30 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/foomo/contentserver/content"
|
||||
"github.com/foomo/neoscontentrepository/model"
|
||||
)
|
||||
|
||||
type NodeType string
|
||||
|
||||
type NodeExporter interface {
|
||||
GetRepoNode(node *model.NodeData) (repoNode *content.RepoNode, export bool, err error)
|
||||
}
|
||||
|
||||
var nodeExporterMap = map[string]NodeExporter{}
|
||||
var ErrorUnsupportedNodeType = errors.New("unsupported node type: no exporter registered for given node type")
|
||||
|
||||
func RegisterExporter(nodeType NodeType, exporter NodeExporter) {
|
||||
nodeExporterMap[string(nodeType)] = exporter
|
||||
}
|
||||
|
||||
func Export(node *model.NodeData) (repoNode *content.RepoNode, ok bool, err error) {
|
||||
nodeExporter, nodeExporterOK := nodeExporterMap[node.NodeType]
|
||||
if !nodeExporterOK {
|
||||
err = ErrorUnsupportedNodeType
|
||||
return
|
||||
}
|
||||
return nodeExporter.GetRepoNode(node)
|
||||
}
|
||||
54
exporter/page.go
Normal file
54
exporter/page.go
Normal file
@ -0,0 +1,54 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"github.com/foomo/contentserver/content"
|
||||
"github.com/foomo/neoscontentrepository/model"
|
||||
)
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// ~ Constants
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
const NodeTypePage NodeType = "Neos.NodeTypes:Page"
|
||||
const MimeTypePage = "application/neos+page"
|
||||
|
||||
type Page struct{}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
func (p *Page) GetRepoNode(node *model.NodeData) (repoNode *content.RepoNode, export bool, err error) {
|
||||
|
||||
repoNode, properties, errRepoNode := createRepoNode(node)
|
||||
if errRepoNode != nil {
|
||||
err = errRepoNode
|
||||
return
|
||||
}
|
||||
|
||||
// is not visible if node is marked as "hidden" or "hiddenBeforeDateTime" and "hiddenAfterDateTime" did not match the current time
|
||||
// if(!$node->isVisible()) {
|
||||
// return false;
|
||||
// }
|
||||
// if(!parent::map($node)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if ('' != $prop = $node->getProperty('layout')) {
|
||||
// $layout = $prop;
|
||||
// } else if ('' != $prop = RepoNode::getParentProperty($node, 'subpageLayout')) {
|
||||
// $layout = $prop;
|
||||
// } else {
|
||||
// $layout = 'default';
|
||||
// }
|
||||
// $this->addData('layout', $layout);
|
||||
|
||||
// add data: youtubeID
|
||||
youtubeID := GetPropertyString("youtubeId", properties)
|
||||
if youtubeID != "" {
|
||||
repoNode.Data["youtubeId"] = youtubeID
|
||||
}
|
||||
|
||||
export = true
|
||||
return
|
||||
}
|
||||
48
exporter/tree.go
Normal file
48
exporter/tree.go
Normal file
@ -0,0 +1,48 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/foomo/contentserver/content"
|
||||
"github.com/foomo/neoscontentrepository/model"
|
||||
)
|
||||
|
||||
func BuildTree(node *model.NodeData, tree map[string][]*model.NodeData) (repoNode *content.RepoNode, err error) {
|
||||
repoNode, exportRootNodeOk, errRepoNode := Export(node)
|
||||
if errRepoNode != nil {
|
||||
err = errRepoNode
|
||||
return
|
||||
}
|
||||
if !exportRootNodeOk {
|
||||
err = errors.New("root node export not ok")
|
||||
return
|
||||
}
|
||||
|
||||
// childNodes, childNodesOk := tree[node.Path]
|
||||
// if !childNodesOk {
|
||||
// err = errors.New("no nodes for path found: " + node.Path)
|
||||
// return
|
||||
// }
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
lock := &sync.RWMutex{}
|
||||
for _, childNode := range tree[node.Path] {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
childRepoNode, errChildRepoNode := BuildTree(childNode, tree)
|
||||
if errChildRepoNode != nil {
|
||||
err = errChildRepoNode
|
||||
return
|
||||
}
|
||||
// @todo keep sorting index order
|
||||
lock.Lock()
|
||||
repoNode.Index = append(repoNode.Index, childRepoNode.ID)
|
||||
repoNode.AddNode(childRepoNode.ID, childRepoNode)
|
||||
lock.Unlock()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return
|
||||
}
|
||||
68
exporter/utils.go
Normal file
68
exporter/utils.go
Normal file
@ -0,0 +1,68 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/foomo/contentserver/content"
|
||||
"github.com/foomo/neoscontentrepository/model"
|
||||
)
|
||||
|
||||
const RepoNodeDefaultLayout = "default"
|
||||
|
||||
func createRepoNode(node *model.NodeData) (repoNode *content.RepoNode, properties map[string]interface{}, err error) {
|
||||
|
||||
var errProperties error
|
||||
properties, errProperties = parseProperties(node.Properties)
|
||||
|
||||
repoNode = content.NewRepoNode()
|
||||
now := time.Now()
|
||||
|
||||
// set base data
|
||||
repoNode.ID = node.Identifier
|
||||
repoNode.MimeType = MimeTypePage
|
||||
repoNode.Data["createdAt"] = node.CreationDatetime.Unix()
|
||||
repoNode.Data["updatedAt"] = node.LastModificationDatetime.Unix()
|
||||
|
||||
repoNode.Hidden = node.HiddenInIndex || node.Hidden ||
|
||||
(node.HiddenBeforeDatetime != nil && node.HiddenBeforeDatetime.Unix() > 0 && now.Unix() < node.HiddenBeforeDatetime.Unix()) ||
|
||||
(node.HiddenAfterDatetime != nil && node.HiddenAfterDatetime.Unix() > 0 && now.Unix() > node.HiddenAfterDatetime.Unix())
|
||||
|
||||
// add localized properties
|
||||
// $this->URI = $this->getNodeUri($node);
|
||||
|
||||
// handle property errors
|
||||
if errProperties != nil {
|
||||
err = errProperties
|
||||
return
|
||||
}
|
||||
|
||||
// node name
|
||||
repoNode.Name = GetPropertyString("title", properties)
|
||||
|
||||
// layout
|
||||
repoNode.Data["layout"] = RepoNodeDefaultLayout
|
||||
layout := GetPropertyString("layout", properties)
|
||||
if layout != "" {
|
||||
repoNode.Data["layout"] = layout
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetPropertyString will return properties value for given key or an empty string
|
||||
func GetPropertyString(key string, properties map[string]interface{}) string {
|
||||
if value, ok := properties[key]; ok {
|
||||
return value.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseProperties(props string) (properties map[string]interface{}, err error) {
|
||||
errUnmarshall := json.Unmarshal([]byte(props), &properties)
|
||||
if errUnmarshall != nil {
|
||||
err = errUnmarshall
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
14
glide.lock
generated
Normal file
14
glide.lock
generated
Normal file
@ -0,0 +1,14 @@
|
||||
hash: 196ea20faad04f34c72ee4ec0ca7a36c2737374b60efb187cf3c673e1a2e156c
|
||||
updated: 2018-12-26T19:15:19.205432+01:00
|
||||
imports:
|
||||
- name: github.com/foomo/contentserver
|
||||
version: 8f7c23ff4a3c4137e0f3159a2e707553f00c5448
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: 72cd26f257d44c1114970e19afddcd812016007e
|
||||
- name: github.com/stretchr/testify
|
||||
version: f35b8ab0b5a2cef36673838d662e249dd9c94686
|
||||
- name: google.golang.org/appengine
|
||||
version: e9657d882bb81064595ca3b56cbe2546bbabf7b1
|
||||
subpackages:
|
||||
- cloudsql
|
||||
testImports: []
|
||||
8
glide.yaml
Normal file
8
glide.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
package: github.com/foomo/neoscontentrepository
|
||||
import:
|
||||
- package: github.com/foomo/contentserver
|
||||
version: ~1.4.2
|
||||
- package: github.com/go-sql-driver/mysql
|
||||
version: ~1.4.1
|
||||
- package: github.com/stretchr/testify
|
||||
version: ~1.2.2
|
||||
85
model/node.go
Normal file
85
model/node.go
Normal file
@ -0,0 +1,85 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
+-------------------------------+---------------+------+-----+---------+-------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+-------------------------------+---------------+------+-----+---------+-------+
|
||||
| persistence_object_identifier | varchar(40) | NO | PRI | NULL | |
|
||||
| workspace | varchar(255) | YES | MUL | NULL | |
|
||||
| contentobjectproxy | varchar(40) | YES | MUL | NULL | |
|
||||
| movedto | varchar(40) | YES | UNI | NULL | |
|
||||
| version | int(11) | NO | | 1 | |
|
||||
| pathhash | varchar(32) | NO | MUL | NULL | |
|
||||
| path | varchar(4000) | NO | | NULL | |
|
||||
| parentpathhash | varchar(32) | NO | MUL | NULL | |
|
||||
| parentpath | varchar(4000) | NO | MUL | NULL | |
|
||||
| identifier | varchar(255) | NO | MUL | NULL | |
|
||||
| sortingindex | int(11) | YES | | NULL | |
|
||||
| removed | tinyint(1) | NO | | NULL | |
|
||||
| dimensionshash | varchar(32) | NO | | NULL | |
|
||||
| lastmodificationdatetime | datetime | NO | | NULL | |
|
||||
| lastpublicationdatetime | datetime | YES | | NULL | |
|
||||
| hiddenbeforedatetime | datetime | YES | | NULL | |
|
||||
| hiddenafterdatetime | datetime | YES | | NULL | |
|
||||
| dimensionvalues | longtext | NO | | NULL | |
|
||||
| properties | longtext | NO | | NULL | |
|
||||
| nodetype | varchar(255) | NO | MUL | NULL | |
|
||||
| creationdatetime | datetime | NO | | NULL | |
|
||||
| hidden | tinyint(1) | NO | | NULL | |
|
||||
| hiddeninindex | tinyint(1) | NO | | NULL | |
|
||||
| accessroles | longtext | NO | | NULL | |
|
||||
+-------------------------------+---------------+------+-----+---------+-------+
|
||||
*/
|
||||
|
||||
type NodeData struct {
|
||||
PersistenceObjectIdentifier string `json:"persistence_object_identifier"`
|
||||
Identifier string `json:"identifier"`
|
||||
Version uint `json:"version"`
|
||||
Workspace *string `json:"workspace"`
|
||||
ContentObjectProxy *string `json:"contentobjectproxy"`
|
||||
MovedTo *string `json:"movedto"`
|
||||
PathHash string `json:"pathhash"`
|
||||
Path string `json:"path"`
|
||||
ParentPathHash string `json:"parentpathhash"`
|
||||
ParentPath string `json:"parentpath"`
|
||||
SortingIndex *uint `json:"sortingindex"`
|
||||
Removed bool `json:"removed"`
|
||||
DimensionsHash string `json:"dimensionshash"`
|
||||
CreationDatetime time.Time `json:"creationdatetime"`
|
||||
LastModificationDatetime time.Time `json:"lastmodificationdatetime"`
|
||||
LastPublicationDatetime *time.Time `json:"lastpublicationdatetime"`
|
||||
HiddenBeforeDatetime *time.Time `json:"hiddenbeforedatetime"`
|
||||
HiddenAfterDatetime *time.Time `json:"hiddenafterdatetime"`
|
||||
DimensionValues string `json:"dimensionvalues"`
|
||||
Properties string `json:"properties"`
|
||||
NodeType string `json:"nodetype"`
|
||||
Hidden bool `json:"hidden"`
|
||||
HiddenInIndex bool `json:"hiddeninindex"`
|
||||
AccessRoles string `json:"accessroles"`
|
||||
}
|
||||
|
||||
type NodeProperties struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
URIPathSegment string `json:"uriPathSegment"`
|
||||
|
||||
Layout string `json:"layout,omitempty"`
|
||||
SubPageLayout string `json:"subpageLayout,omitempty"`
|
||||
}
|
||||
|
||||
func (node *NodeData) IsVisible() bool {
|
||||
// @todo @implementme
|
||||
return false
|
||||
}
|
||||
|
||||
func (node *NodeData) GetHash() string {
|
||||
return strings.Join([]string{
|
||||
node.Identifier,
|
||||
*node.Workspace,
|
||||
node.DimensionsHash,
|
||||
}, "___")
|
||||
}
|
||||
134
store/loader.go
Normal file
134
store/loader.go
Normal file
@ -0,0 +1,134 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/foomo/neoscontentrepository/model"
|
||||
|
||||
// mysql driver import
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func Load(dsn string, nodeTypes []string) (nodes map[string]*model.NodeData, tree map[string][]*model.NodeData, rootPath string, err error) {
|
||||
|
||||
nodesLock := &sync.RWMutex{}
|
||||
nodes = map[string]*model.NodeData{}
|
||||
|
||||
// connect with DB
|
||||
db, errDB := sql.Open("mysql", dsn+"?parseTime=true")
|
||||
if errDB != nil {
|
||||
err = errDB
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
nodeTypesSlice := []string{}
|
||||
for _, nodeType := range nodeTypes {
|
||||
nodeTypesSlice = append(nodeTypesSlice, `"`+nodeType+`"`)
|
||||
}
|
||||
nodeTypesString := strings.Join(nodeTypesSlice, ",")
|
||||
|
||||
// sqlStatement, errStatement := db.Prepare(`SELECT persistence_object_identifier, identifier, version, workspace, contentobjectproxy, movedto, pathhash, path, parentpathhash, parentpath, sortingindex, removed, dimensionshash, creationdatetime, lastmodificationdatetime, lastpublicationdatetime, hiddenbeforedatetime, hiddenafterdatetime, dimensionvalues, properties, nodetype, hidden, hiddeninindex, accessroles FROM neos_contentrepository_domain_model_nodedata WHERE nodetype IN (?` + strings.Repeat(`,?`, len(nodeTypes)-1) + `)`)
|
||||
sqlStatement, errStatement := db.Prepare(fmt.Sprintf(`SELECT persistence_object_identifier, identifier, version, workspace, contentobjectproxy, movedto, pathhash, path, parentpathhash, parentpath, sortingindex, removed, dimensionshash, creationdatetime, lastmodificationdatetime, lastpublicationdatetime, hiddenbeforedatetime, hiddenafterdatetime, dimensionvalues, properties, nodetype, hidden, hiddeninindex, accessroles FROM neos_contentrepository_domain_model_nodedata WHERE nodetype IN (%s)`, nodeTypesString))
|
||||
if errStatement != nil {
|
||||
err = errStatement
|
||||
return
|
||||
}
|
||||
|
||||
// execute sql query
|
||||
rows, errRows := sqlStatement.Query()
|
||||
if errRows != nil {
|
||||
err = errRows
|
||||
return
|
||||
}
|
||||
|
||||
idsLock := &sync.RWMutex{}
|
||||
ids := map[string]bool{}
|
||||
tree = map[string][]*model.NodeData{}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for rows.Next() {
|
||||
wg.Add(1)
|
||||
|
||||
nodeData := &model.NodeData{}
|
||||
// for each row, scan the result into a node data object
|
||||
errScan := rows.Scan(
|
||||
&nodeData.PersistenceObjectIdentifier,
|
||||
&nodeData.Identifier,
|
||||
&nodeData.Version,
|
||||
&nodeData.Workspace,
|
||||
&nodeData.ContentObjectProxy,
|
||||
&nodeData.MovedTo,
|
||||
&nodeData.PathHash,
|
||||
&nodeData.Path,
|
||||
&nodeData.ParentPathHash,
|
||||
&nodeData.ParentPath,
|
||||
&nodeData.SortingIndex,
|
||||
&nodeData.Removed,
|
||||
&nodeData.DimensionsHash,
|
||||
&nodeData.CreationDatetime,
|
||||
&nodeData.LastModificationDatetime,
|
||||
&nodeData.LastPublicationDatetime,
|
||||
&nodeData.HiddenBeforeDatetime,
|
||||
&nodeData.HiddenAfterDatetime,
|
||||
&nodeData.DimensionValues,
|
||||
&nodeData.Properties,
|
||||
&nodeData.NodeType,
|
||||
&nodeData.Hidden,
|
||||
&nodeData.HiddenInIndex,
|
||||
&nodeData.AccessRoles,
|
||||
)
|
||||
if errScan != nil {
|
||||
err = errScan
|
||||
return
|
||||
}
|
||||
|
||||
// find shortest path
|
||||
if rootPath == "" || len(rootPath) > len(nodeData.Path) {
|
||||
rootPath = nodeData.ParentPath
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
// skip other workspaces
|
||||
if *nodeData.Workspace != "live" && *nodeData.Workspace != "stage" {
|
||||
return
|
||||
}
|
||||
|
||||
// skip removed nodes
|
||||
if nodeData.Removed {
|
||||
return
|
||||
}
|
||||
|
||||
hash := nodeData.GetHash()
|
||||
|
||||
// collect nodes
|
||||
nodesLock.Lock()
|
||||
nodes[hash] = nodeData
|
||||
if _, ok := tree[nodeData.ParentPath]; !ok {
|
||||
tree[nodeData.ParentPath] = []*model.NodeData{}
|
||||
}
|
||||
tree[nodeData.ParentPath] = append(tree[nodeData.ParentPath], nodeData)
|
||||
nodesLock.Unlock()
|
||||
|
||||
idsLock.RLock()
|
||||
_, ok := ids[hash]
|
||||
idsLock.RUnlock()
|
||||
if !ok {
|
||||
idsLock.Lock()
|
||||
ids[hash] = true
|
||||
idsLock.Unlock()
|
||||
} else {
|
||||
fmt.Println("duplicate ID", nodeData.GetHash())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user