func DeleteByCID(ctx context.Context, cid int, contenttype string, userId int) error {
	content, err := query.FetchByCID(ctx, contenttype, cid)
	if err != nil {
		return errors.New("[handler.delete]Content doesn't exist with cid: " + strconv.Itoa(cid))
	}
	err = DeleteByContent(ctx, content, userId, false)
	return err
}
Delete content by content id
package handler
import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
	"github.com/digimakergo/digimaker/core/contenttype"
	"github.com/digimakergo/digimaker/core/db"
	"github.com/digimakergo/digimaker/core/definition"
	"github.com/digimakergo/digimaker/core/fieldtype"
	"github.com/digimakergo/digimaker/core/log"
	"github.com/digimakergo/digimaker/core/permission"
	"github.com/digimakergo/digimaker/core/query"
	"github.com/digimakergo/digimaker/core/util"
)
type InputMap map[string]interface{}
var ErrorNoPermission = errors.New("The user doesn't have access to the action.")
func Validate(contentType string, fieldsDef map[string]definition.FieldDef, inputs InputMap, checkAllRequired bool) (bool, ValidationResult) {
	result := ValidationResult{Fields: map[string]string{}}
	for identifier, fieldDef := range fieldsDef {
		input, fieldExists := inputs[identifier]
		fieldResult := ""
		if fieldExists {
			isEmpty := fieldtype.IsEmptyInput(input)
			if fieldDef.Required && isEmpty {
				fieldResult = "1"
			} else {
				fieldtypeDef := fieldtype.GetFieldtype(fieldDef.FieldType)
				handler := fieldtypeDef.NewHandler(fieldDef)
				if handler != nil {
					_, err := handler.LoadInput(input, "")
					if _, ok := err.(fieldtype.ValidationError); ok {
						fieldResult = err.Error()
					}
					if _, ok := err.(fieldtype.EmptyError); ok {
						fieldResult = "1"
					}
				}
			}
		} else if fieldDef.Required && checkAllRequired {
			fieldResult = "1"
		}
		if fieldResult != "" {
			result.Fields[identifier] = fieldResult
		}
	}
	return result.Passed(), result
}
func storeCreatedContent(ctx context.Context, content contenttype.ContentTyper, userId int, tx *sql.Tx, parentID int) error {
	if content.GetCID() == 0 {
		log.Debug("Content is new.", "contenthandler.StoreCreatedContent", ctx)
	}
	contentDefinition := content.Definition()
	if !contentDefinition.HasLocation && contentDefinition.HasDataField("location_id") {
		content.SetValue("location_id", parentID)
	}
	for identifier, fieldDef := range contentDefinition.FieldMap {
		handler := fieldtype.GethHandler(fieldDef)
		if storeHandler, ok := handler.(fieldtype.StoreHandler); ok {
			err := storeHandler.Store(ctx, content.Value(identifier), content.ContentType(), content.GetCID(), tx)
			if err != nil {
				return err
			}
		}
	}
	err := content.Store(ctx, tx)
	if err != nil {
		return err
	}
	log.Debug("Content is saved. id :"+strconv.Itoa(content.GetCID())+". ", "contenthandler.StoreCreatedContent", ctx)
	if contentDefinition.HasLocation {
		parent, err := contenttype.GetLocationByID(parentID)
		if err != nil {
			return fmt.Errorf("Can not get parent location with %v: %w", parentID, err)
		}
		location := content.GetLocation()
		location.ParentID = parentID
		location.ContentID = content.GetCID()
		location.ContentType = content.ContentType()
		location.UID = util.GenerateUID()
		contentName := GenerateName(content)
		location.IdentifierPath = parent.IdentifierPath + "/" + util.NameToIdentifier(contentName)
		location.Depth = parent.Depth + 1
		location.Section = parent.Section
		location.Name = contentName
		err = location.Store(ctx, tx)
		if err != nil {
			return fmt.Errorf("Transaction failed in location when saving new location: %w", err)
		}
		location.Hierarchy = parent.Hierarchy + "/" + strconv.Itoa(location.ID)
		location.MainID = location.ID
		err = location.Store(ctx, tx)
		if err != nil {
			return fmt.Errorf("Transaction failed in location when saving location for main_id and hierarchy: %w", err)
		}
		log.Debug("Location is saved. location id :"+strconv.Itoa(location.ID)+". ", "contenthandler.StoreCreatedContent", ctx)
	}
	return nil
}
func Create(ctx context.Context, userID int, contentType string, inputs InputMap, parentID int) (contenttype.ContentTyper, error) {
	contentDefinition, _ := definition.GetDefinition(contentType)
	fieldsDefinition := contentDefinition.FieldMap
	var parent contenttype.ContentTyper
	if contentDefinition.HasLocation {
		if parentID == 0 {
			return nil, errors.New("Parent id cann't be 0")
		} else {
			parent, _ = query.FetchByID(ctx, parentID)
			if parent == nil {
				return nil, errors.New("parent doesn't exist. parent id: " + strconv.Itoa(parentID))
			}
		}
	}
	if !permission.CanCreate(ctx, parent, contentType, userID) {
		return nil, errors.New("User doesn't have access to create")
	}
	valid, validationResult := Validate(contentType, fieldsDefinition, inputs, true)
	if !valid {
		return nil, validationResult
	}
	contentTypeHandler := GetContentTypeHandler(contentType)
	if validator, ok := contentTypeHandler.(ContentTypeHandlerValidate); ok {
		log.Debug("Validating from content type handler", "contenthandler.validate", ctx)
		valid, validationResult = validator.ValidateCreate(ctx, inputs, parentID)
		if !valid {
			return nil, validationResult
		}
	}
	content := contenttype.NewInstance(contentType)
	for identifier, fieldDef := range fieldsDefinition {
		if input, ok := inputs[identifier]; ok {
			handler := fieldtype.GethHandler(fieldDef)
			value, _ := handler.LoadInput(input, "")
			var err error
			if fieldtypeEvent, ok := handler.(fieldtype.Event); ok {
				log.Debug("Invoking before storing on field: "+identifier, "handler", ctx)
				value, err = fieldtypeEvent.BeforeStore(value, nil, "create")
				if err != nil {
					return nil, err
				}
			}
			content.SetValue(identifier, value)
		}
	}
	now := int(time.Now().Unix())
	if contentDefinition.HasLocation || contentDefinition.HasDataField("published") {
		content.SetValue("published", now)
	}
	if contentDefinition.HasLocation || contentDefinition.HasDataField("modified") {
		content.SetValue("modified", now)
	}
	if contentDefinition.HasLocation || contentDefinition.HasDataField("author") {
		content.SetValue("author", userID)
	}
	if contentDefinition.HasLocation || contentDefinition.HasDataField("cuid") {
		content.SetValue("cuid", util.GenerateUID())
	}
	log.StartTiming(ctx, "contenthandler_create.database")
	log.Debug("Validation passed. Start saving content.", "contenthandler.Create", ctx)
	tx, err := db.CreateTx()
	if err != nil {
		return nil, errors.New("Can't get transaction")
	}
	versionIfNeeded := 1
	if contentDefinition.HasVersion {
		content.SetValue("version", versionIfNeeded)
	}
	err = storeCreatedContent(ctx, content, userID, tx, parentID)
	if err != nil {
		tx.Rollback()
		log.Error(err.Error(), "contenthandler.Create", ctx)
		return nil, fmt.Errorf("Create content error: %w", err)
	}
	if contentDefinition.HasVersion {
		log.Debug("creating version", "contenthandler.Create", ctx)
		_, err = CreateVersion(ctx, content, versionIfNeeded, tx)
		if err != nil {
			log.Error(err.Error(), "contenthandler.Create", ctx)
			return nil, fmt.Errorf("Create version error. %w", err)
		}
		log.Debug("Created version: "+strconv.Itoa(versionIfNeeded), "contenthandler.Create", ctx)
	}
	if creater, ok := contentTypeHandler.(ContentTypeHandlerCreate); ok {
		log.Debug("Calling handler for "+contentType, "contenthandler.create", ctx)
		err := creater.Create(ctx, content, inputs, parentID)
		if err != nil {
			tx.Rollback()
			log.Error("Error from callback: "+err.Error(), "contenthandler.create", ctx)
			return nil, err
		}
	}
	matchData := map[string]interface{}{"parent_id": parentID,
		"content_type": contentType}
	if contentDefinition.HasLocation {
		hierachy := content.GetLocation().Hierarchy
		matchData["under"] = strings.Split(hierachy, "/")
	}
	err = InvokeCallback(ctx, "create", true, matchData, content, tx, inputs)
	if err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("Invoking callback error: %w", err)
	}
	err = tx.Commit()
	if err != nil {
		tx.Rollback()
		log.Error("Commit error: "+err.Error(), "contenthandler.create", ctx)
		return nil, fmt.Errorf("Commit error: %w", err)
	}
	log.Debug("Data committed.", "contenthandler.create", ctx)
	log.EndTiming(ctx, "contenthandler_create.database")
	return content, nil
}
func InvokeCallback(ctx context.Context, event string, stopOnError bool, matchData map[string]interface{}, content contenttype.ContentTyper, params ...interface{}) error {
	operationHandlerList, matchInfo := GetOperationHandlerByCondition(event, matchData)
	count := len(operationHandlerList)
	if count > 0 {
		identifierList := []string{}
		for _, operationHandler := range operationHandlerList {
			identifierList = append(identifierList, operationHandler.Identifier)
		}
		log.Debug("Matched callbacks: "+strings.Join(identifierList, ","), "contenthandler.invoke_callback", ctx)
	} else {
		log.Debug("No callbacks matched.", "contenthandler.invoke_callback", ctx)
	}
	for _, info := range matchInfo {
		log.Debug(info, "callback_match", ctx)
	}
	for i, operationHandler := range operationHandlerList {
		log.Debug(strconv.Itoa(i+1)+"/"+strconv.Itoa(count)+
			" Invoking operation "+operationHandler.Identifier+" on "+event, "contehandler.invoke_callback", ctx)
		err := operationHandler.Execute(ctx, event, content, params...)
		if err != nil && stopOnError {
			log.Error("Error when invoking operation handler "+operationHandler.Identifier+":"+err.Error(), "contehandler.invoke_callback", ctx)
			return err
		}
		log.Debug("Invoking ended on "+operationHandler.Identifier, "contehandler.invoke_callback", ctx)
	}
	return nil
}
func CreateVersion(ctx context.Context, content contenttype.ContentTyper, versionNumber int, tx *sql.Tx) (int, error) {
	log.Debug("Creating version: "+strconv.Itoa(versionNumber), "contenthandler.CreateVersion", ctx)
	id := content.GetCID()
	data, err := contenttype.ContentToJson(content)
	if err != nil {
		return 0, fmt.Errorf("Can not create version data on content id %v: %w", id, err)
	}
	version := contenttype.Version{}
	version.ContentType = content.ContentType()
	version.ContentID = content.GetCID()
	version.Author = content.GetAuthor()
	version.Version = versionNumber
	version.Data = data
	err = version.Store(ctx, tx)
	if err != nil {
		tx.Rollback()
		return 0, fmt.Errorf("Can not save version on contetent id %v: %w ", id, err)
	}
	log.Debug("Version created.", "contenthandler.CreateVersion", ctx)
	return version.ID, nil
}
func UpdateByContentID(ctx context.Context, contentType string, contentID int, inputs InputMap, userId int) (bool, error) {
	content, err := query.FetchByCID(ctx, contentType, contentID)
	if err != nil {
		return false, fmt.Errorf("Failed to get content via content id: %w", err)
	}
	if content.GetCID() == 0 {
		return false, fmt.Errorf("Got empty content: %w", err)
	}
	return Update(ctx, content, inputs, userId)
}
func UpdateByID(ctx context.Context, id int, inputs InputMap, userId int) (bool, error) {
	content, err := query.FetchByID(ctx, id)
	if err != nil {
		return false, fmt.Errorf("Failed to get content via id: %w", err)
	}
	if content.GetCID() == 0 {
		return false, errors.New("Got empty content")
	}
	return Update(ctx, content, inputs, userId)
}
func Update(ctx context.Context, content contenttype.ContentTyper, inputs InputMap, userId int) (bool, error) {
	contentType := content.ContentType()
	contentDef, _ := definition.GetDefinition(contentType)
	fieldsDefinition := contentDef.FieldMap
	if !permission.CanUpdate(ctx, content, userId) {
		return false, errors.New("User " + strconv.Itoa(userId) + " doesn't have access to update")
	}
	allowedFields, err := permission.GetUpdateFields(ctx, content, userId)
	if err != nil {
		return false, err
	}
	if len(allowedFields) > 0 && allowedFields[0] != "*" {
		for field, _ := range inputs {
			if _, ok := fieldsDefinition[field]; ok && !util.Contains(allowedFields, field) {
				return false, errors.New("User doesn't have permission to update field " + field + ".")
			}
		}
	}
	log.Debug("Validating", "contenthandler.update", ctx)
	valid, validationResult := Validate(contentType, fieldsDefinition, inputs, false)
	if !valid {
		return false, validationResult
	}
	contentTypeHandler := GetContentTypeHandler(contentType)
	if validator, ok := contentTypeHandler.(ContentTypeHandlerValidate); ok {
		log.Debug("Validating from update handler", "contenthandler.validate", ctx)
		valid, validationResult = validator.ValidateUpdate(ctx, inputs, content)
		if !valid {
			return false, validationResult
		}
	}
	for identifier, fieldDef := range fieldsDefinition {
		if input, ok := inputs[identifier]; ok {
			handler := fieldtype.GethHandler(fieldDef)
			value, _ := handler.LoadInput(input, "")
			existing := content.Value(identifier)
			if fieldWithEvent, ok := handler.(fieldtype.Event); ok {
				value, err = fieldWithEvent.BeforeStore(value, existing, "")
				if err != nil {
					return false, err
				}
			}
			content.SetValue(identifier, value)
		}
	}
	tx, err := db.CreateTx()
	if err != nil {
		return false, fmt.Errorf("Create transaction error: %w", err)
	}
	if contentDef.HasVersion {
		version := content.Value("version").(int) + 1
		log.Debug("Creating new version: "+strconv.Itoa(version), "contenthandler.update", ctx)
		_, err := CreateVersion(ctx, content, version, tx)
		if err != nil {
			log.Debug("Create new version failed: "+err.Error(), "contenthandler.update", ctx)
			return false, fmt.Errorf("Can not save version: %w", err)
		}
		log.Debug("New version created", "contenthandler.update", ctx)
		content.SetValue("version", version)
	}
	if contentDef.HasLocation || contentDef.HasDataField("modified") {
		now := int(time.Now().Unix())
		content.SetValue("modified", now)
	}
	log.Debug("Saving content", "contenthandler.update", ctx)
	for identifier, fieldDef := range contentDef.FieldMap {
		handler := fieldtype.GethHandler(fieldDef)
		if storeHandler, ok := handler.(fieldtype.StoreHandler); ok {
			err := storeHandler.Store(ctx, content.Value(identifier), content.ContentType(), content.GetCID(), tx)
			if err != nil {
				tx.Rollback()
				return false, err
			}
		}
	}
	err = content.Store(ctx, tx)
	if err != nil {
		tx.Rollback()
		log.Debug("Saving content failed: "+err.Error(), "contenthandler.update", ctx)
		return false, fmt.Errorf("Saving content error: %w", err)
	}
	if contentDef.HasLocation {
		location := content.GetLocation()
		location.Name = GenerateName(content)
		log.Debug("Updating location info", "contenthandler.update", ctx)
		err := location.Store(ctx, tx)
		if err != nil {
			tx.Rollback()
			log.Debug("Updating location failed: "+err.Error(), "contenthandler.update", ctx)
			return false, fmt.Errorf("Updating location info error: %w", err)
		}
		log.Debug("Location updated", "contenthandler.update", ctx)
	}
	if updater, ok := contentTypeHandler.(ContentTypeHandlerUpdate); ok {
		log.Debug("Calling handler for "+contentType, "contenthandler.update", ctx)
		err := updater.Update(ctx, content, inputs)
		if err != nil {
			tx.Rollback()
			log.Error("Error from callback: "+err.Error(), "contenthandler.update", ctx)
			return false, err
		}
	}
	matchData := map[string]interface{}{"content_type": contentType}
	if content.Definition().HasLocation {
		hierachy := content.GetLocation().Hierarchy
		matchData["under"] = strings.Split(hierachy, "/")
	}
	err = InvokeCallback(ctx, "update", true, matchData, content, tx, inputs)
	if err != nil {
		tx.Rollback()
		return false, fmt.Errorf("Invoking callback error: %w", err)
	}
	log.Debug("All done. Commitinng.", "contenthandler.update", ctx)
	err = tx.Commit()
	if err != nil {
		tx.Rollback()
		log.Error("Commit error: "+err.Error(), "contenthandler.update", ctx)
		return false, fmt.Errorf("Commit error: %w", err)
	}
	return true, nil
}
func Move(ctx context.Context, contentIds []int, targetId int, userId int) error {
	target, err := query.FetchByID(ctx, targetId)
	targetLocation := target.GetLocation()
	if err != nil {
		log.Error(err.Error(), "")
		return errors.New("Target not found")
	}
	contents := []contenttype.ContentTyper{}
	for _, id := range contentIds {
		content, err := query.FetchByID(ctx, id)
		if err != nil {
			log.Error(err.Error(), "")
			return errors.New("Content id " + strconv.Itoa(id) + " is not found for this user.")
		}
		contents = append(contents, content)
	}
	tx, err := db.CreateTx(ctx)
	if err != nil {
		return errors.New("Internal error")
	}
	for _, content := range contents {
		location := content.GetLocation()
		if !permission.CanDelete(ctx, content, userId) {
			log.Warning("No permission to delete when moving "+strconv.Itoa(location.ID), "")
			tx.Rollback()  
			return ErrorNoPermission
		}
		if !permission.CanCreate(ctx, target, content.ContentType(), userId) {
			log.Warning("No permission to create when moving "+strconv.Itoa(location.ID), "")
			tx.Rollback()  
			return ErrorNoPermission
		}
		location.ParentID = targetId
		oldHiearachy := location.Hierarchy
		newHiearachy := targetLocation.Hierarchy + "/" + strconv.Itoa(location.ID)
		location.Hierarchy = newHiearachy
		oldPath := location.IdentifierPath
		newPath := targetLocation.IdentifierPath + "/" + util.NameToIdentifier(location.Name)
		location.IdentifierPath = newPath
		location.Depth = len(strings.Split(newHiearachy, "/"))
		location.Store(ctx, tx)
		subLocations := []contenttype.Location{}
		db.BindEntity(ctx, &subLocations, "dm_location", db.Cond("hierarchy like", oldHiearachy+"/%"))
		for _, subLocation := range subLocations {
			subContent, _ := query.FetchByID(ctx, subLocation.ID)
			if !permission.CanDelete(ctx, subContent, userId) {
				tx.Rollback()
				log.Warning("No permission to delete "+strconv.Itoa(location.ID), "")
				return ErrorNoPermission
			}
			subLocation.Hierarchy = newHiearachy + strings.TrimPrefix(subLocation.Hierarchy, oldHiearachy)
			subLocation.IdentifierPath = newPath + strings.TrimPrefix(subLocation.IdentifierPath, oldPath)
			subLocation.Depth = len(strings.Split(subLocation.Hierarchy, "/"))
			subLocation.Store(ctx, tx)
		}
	}
	tx.Commit()
	return nil
}