Document System Tutorial Part 17 - Wrapping Up Your Application (Current Progress)

Back to Previous Page

Structure:

models

channel.go

Back to Top

package models

import (
    "github.com/uadmin/uadmin"
)

// Channel !
type Channel struct {
    uadmin.Model
    Name string `uadmin:"required"`
}

document_group.go

Back to Top

package models

import (
    "github.com/uadmin/uadmin"
)

// DocumentGroup !
type DocumentGroup struct {
    uadmin.Model
    Group      uadmin.UserGroup
    GroupID    uint
    Document   Document
    DocumentID uint
    Read       bool
    Add        bool
    Edit       bool
    Delete     bool
}

// HideInDashboard !
func (DocumentGroup) HideInDashboard() bool {
    return true
}

// DocumentGroup function that returns string value
func (d *DocumentGroup) String() string {

    // Gives access to the fields in another model
    uadmin.Preload(d)

    // Returns the GroupName from the Group model
    return d.Group.GroupName
}

document_user.go

Back to Top

package models

import (
    "github.com/uadmin/uadmin"
)

// DocumentUser !
type DocumentUser struct {
    uadmin.Model
    User       uadmin.User
    UserID     uint
    Document   Document
    DocumentID uint
    Read       bool
    Add        bool
    Edit       bool
    Delete     bool
}

// DocumentUser function that returns string value
func (d *DocumentUser) String() string {

    // Gives access to the fields in another model
    uadmin.Preload(d)

    // Returns the full name from the User model
    return d.User.String()
}

// HideInDashboard !
func (DocumentUser) HideInDashboard() bool {
    return true
}

document_version.go

Back to Top

package models

import (
    "fmt"
    "time"

    "github.com/uadmin/uadmin"
)

// DocumentVersion !
type DocumentVersion struct {
    uadmin.Model
    Document   Document
    DocumentID uint
    File       string `uadmin:"file"`
    Number     int    `uadmin:"help:version number"`
    Date       time.Time
    Format     Format
}

// Returns the version number
func (d DocumentVersion) String() string {
    return fmt.Sprint(d.Number)
}

// HideInDashboard !
func (DocumentVersion) HideInDashboard() bool {
    return true
}

document.go

Back to Top

package models

import (
    "time"

    "github.com/uadmin/uadmin"
)

// Document !
type Document struct {
    uadmin.Model
    Name        string
    File        string `uadmin:"file"`
    Description string `uadmin:"html"`
    RawText     string `uadmin:"list_exclude"`
    Format      Format `uadmin:"list_exclude"`
    Folder      Folder `uadmin:"filter"`
    FolderID    uint
    CreatedDate time.Time
    Channel     Channel `uadmin:"list_exclude"`
    ChannelID   uint
    CreatedBy   string
}

// Save !
func (d *Document) Save() {
    // Initialized variables
    docChange := false
    newDoc := false
    // Checks whether the document record is new or existing
    if d.ID != 0 {
        // Initializes the Document model
        oldDoc := Document{}

        // Gets the ID of the old Document
        uadmin.Get(&oldDoc, "id = ?", d.ID)

        // Checks if the file is changed or updated
        if d.File != oldDoc.File {
            docChange = true
        }
    } else {
        // New document record
        docChange = true
        newDoc = true
    }

    // Save the document
    uadmin.Save(d)

    // Checks whether the document record has changed
    if docChange {
        // Prints the result
        uadmin.Trail(uadmin.DEBUG, "The document has changed.")

        // Sets the document value to the DocumentVersion
        ver := DocumentVersion{}
        ver.Date = time.Now()
        ver.DocumentID = d.ID
        ver.File = d.File
        ver.Format = d.Format

        // Counts the version number by DocumentID and increment it by 1
        ver.Number = uadmin.Count([]DocumentVersion{}, "document_id = ?", d.ID) + 1

        // Save the document version
        uadmin.Save(&ver)

        // Checks whether the document is a new record
        if newDoc {
            // Initializes the User model
            user := uadmin.User{}

            // Gets the username of the user to display in CreatedBy
            uadmin.Get(&user, "username = ?", d.CreatedBy)

            // Sets values to the DocumentUser model fields
            creator := DocumentUser{
                UserID:     user.ID,
                DocumentID: d.ID,
                Read:       true,
                Edit:       true,
                Add:        true,
                Delete:     true,
            }

            // Save the document user
            uadmin.Save(&creator)
        }
    }
}

// GetPermissions !
func (d Document) GetPermissions(user uadmin.User) (Read bool, Add bool, Edit bool, Delete bool) {
    // Check whether the user is an admin
    if user.Admin {
        // Set all permissions to true
        Read = true
        Add = true
        Edit = true
        Delete = true
    }

    // Since Folder is a foreign key to the Document model, we need to check
    // whether there is a Folder specified in the Document model.
    // We will check for folder permissions first
    // Then we will check for document permissions after that
    if d.FolderID != 0 {
        // Initialize the FolderGroup model
        folderGroup := FolderGroup{}

        // Get data by GroupID and FolderID
        uadmin.Get(&folderGroup, "group_id = ? AND folder_id = ?", user.UserGroupID, d.FolderID)

        // Check whether there is a FolderGroup recird
        if folderGroup.ID != 0 {
            // Assign FolderGroup permission values to the variables
            Read = folderGroup.Read
            Add = folderGroup.Add
            Edit = folderGroup.Edit
            Delete = folderGroup.Delete
        }

        // Initialize the FolderUser model
        folderUser := FolderUser{}

        // Get data by UserID and FolderID
        uadmin.Get(&folderUser, "user_id = ? AND folder_id = ?", user.ID, d.FolderID)

        // Check whether there is a FolderUser record
        if folderUser.ID != 0 {
            // Assign FolderUser permission values to the variables
            Read = folderUser.Read
            Add = folderUser.Add
            Edit = folderUser.Edit
            Delete = folderUser.Delete
        }
    }

    // Document Permissions
    // Initialize the DocumentGroup model
    documentGroup := DocumentGroup{}

    // Get data by GroupID and DocumentID
    uadmin.Get(&documentGroup, "group_id = ? AND document_id = ?", user.UserGroupID, d.ID)

    // Check whether there is a DocumentGroup record
    if documentGroup.ID != 0 {
        // Assign DocumentGroup permission values to the variables
        Read = documentGroup.Read
        Add = documentGroup.Add
        Edit = documentGroup.Edit
        Delete = documentGroup.Delete
    }

    // Initialize the DocumentUser model
    documentUser := DocumentUser{}

    // // Get data by UserID and DocumentID
    uadmin.Get(&documentUser, "user_id = ? AND document_id = ?", user.ID, d.ID)

    // Check whether there is a DocumentUser record
    if documentUser.ID != 0 {
        // Assign DocumentUser permission values to the variables
        Read = documentUser.Read
        Add = documentUser.Add
        Edit = documentUser.Edit
        Delete = documentUser.Delete
    }

    // Return Read, Add, Edit, and Delete values
    return
}

// Count !
func (d Document) Count(a interface{}, query interface{}, args ...interface{}) int {
    // Converts the query into a string
    Q := fmt.Sprint(query)

    // Checks whether the string contains a query and a UserID
    if strings.Contains(Q, "user_id = ?") {
        // Split the query part by part
        qParts := strings.Split(Q, " AND ")

        // Initialize tempArgs as an interface and tempQuery as a
        // string
        tempArgs := []interface{}{}
        tempQuery := []string{}

        // Loop the query every part
        for i := range qParts {
            // Checks whether the specific query part is not
            // equal to the UserID value
            if qParts[i] != "user_id = ?" {
                // Append the arguments into the tempArgs
                // variable
                tempArgs = append(tempArgs, args[i])

                // Append the specific query part into the
                // tempQuery variable
                tempQuery = append(tempQuery, qParts[i])
            }
        }
        // Concatenate the query to create a single string
        query = strings.Join(tempQuery, " AND ")

        // Assign tempArgs object into the args variable
        args = tempArgs
    }

    // Return the a, query, and args... inside the Count function
    // parameters
    return uadmin.Count(a, query, args...)
}

// AdminPage !
func (d Document) AdminPage(order string, asc bool, offset int, limit int, a interface{}, query interface{}, args ...interface{}) (err error) {
    // Checks whether the starting point is less than 0
    if offset < 0 {
        offset = 0
    }

    // Converts the userID into uint because SQL Database reads the model ID
    // as uint
    userID := uint(0)

    // Converts the query into a string
    Q := fmt.Sprint(query)

    // Checks whether the string contains a query and a UserID
    if strings.Contains(Q, "user_id = ?") {
        // Prints the result for debugging
        uadmin.Trail(uadmin.DEBUG, "1")

        // Split the query part by part
        qParts := strings.Split(Q, " AND ")

        // Initialize tempArgs as an interface and tempQuery as a string
        tempArgs := []interface{}{}
        tempQuery := []string{}

        // Loop the query every part
        for i := range qParts {
            // Checks whether the specific query part is not equal to the
            // UserID value
            if qParts[i] != "user_id = ?" {
                // Append the arguments into the tempArgs variable
                tempArgs = append(tempArgs, args[i])

                // Append the specific query part into the tempQuery variable
                tempQuery = append(tempQuery, qParts[i])
            } else {
                // Prints the result for debugging
                uadmin.Trail(uadmin.DEBUG, "UserID: %d", args[i])

                // A type assertion that provides access to an interface
                // value's (args[i]) underlying concrete value (uint).
                userID, _ = (args[i]).(uint)
            }
        }
        // Concatenate the query to create a single string
        query = strings.Join(tempQuery, " AND ")

        // Assign tempArgs object into the args variable
        args = tempArgs
    }

    // Checks whether the userID is equal to 0
    if userID == 0 {
        // Prints the result for debugging
        uadmin.Trail(uadmin.DEBUG, "2")

        // Fetch the error by using AdminPage function
        err = uadmin.AdminPage(order, asc, offset, limit, a, query, args...)

        // Returns an error
        return err
    }

    // Initialize the user variable that calls the User model
    user := uadmin.User{}

    // Gets the ID of the user
    uadmin.Get(&user, "id = ?", userID)

    // Initialize docList and tempList that calls the Document model
    docList := []Document{}
    tempList := []Document{}

    // Loop execution
    for {
        // Fetch the error by using AdminPage function
        err = uadmin.AdminPage(order, asc, offset, limit, &tempList, query, args)
        uadmin.Trail(uadmin.DEBUG, "8: offset:%d, limit:%d", offset, limit)

        // Checks whether an error is not equal to nil
        if err != nil {
            // Prints the result for debugging
            uadmin.Trail(uadmin.DEBUG, "3")

            // Cast a model of interface as an array of Document then assigns
            // the docList object
            *a.(*[]Document) = docList

            // Return an error
            return err
        }

        // Checks whether the length of tempList is equal to 0
        if len(tempList) == 0 {
            // Prints the result for debugging
            uadmin.Trail(uadmin.DEBUG, "4")

            // Cast a model of interface as an array of Document then assigns
            // the docList object
            *a.(*[]Document) = docList

            // Prints the result for debugging
            uadmin.Trail(uadmin.DEBUG, "a: %#v", a)

            // Returns nothing
            return nil
        }

        // Loop the tempList values
        for i := range tempList {
            // Initialize p variable as Read permission
            p, _, _, _ := tempList[i].GetPermissions(user)

            // Checks whether the Document has read permission access
            if p {
                // Prints the result for debugging
                uadmin.Trail(uadmin.DEBUG, "5")

                // Append the tempList (Document) object to the docList
                // variable
                docList = append(docList, tempList[i])
            }

            // Checks whether the length of docList is equal to the limit
            if len(docList) == limit {
                // Prints the result for debugging
                uadmin.Trail(uadmin.DEBUG, "6")

                // Cast a model of interface as an array of Document then
                // assigns the docList object
                *a.(*[]Document) = docList

                // Returns nothing
                return nil
            }
        }

        // Add limit values to the offset variable
        offset += limit
    }
    // Cast a model of interface as an array of Document then assigns the
    // docList object
    *a.(*[]Document) = docList

    // Prints the result for debugging
    uadmin.Trail(uadmin.DEBUG, "7")

    // Returns nothing
    return nil
}

// Permissions__Form creates a new field named Permissions !
func (d Document) Permissions__Form() string {
    // Initialize u variable that calls the User model
    u := uadmin.User{}

    // Get the user record based on an assigned ID
    uadmin.Get(&u, "id = ?", 1)

    // Initialize read, add, edit and delete that gets the permission for a
    // specific user based on an assigned ID
    r, a, e, del := d.GetPermissions(u)

    // Returns the permission status
    return fmt.Sprintf("Read: %v Add: %v, Edit: %v, Delete: %v", r, a, e, del)
}

folder_group.go

Back to Top

package models

import (
    "github.com/uadmin/uadmin"
)

// FolderGroup !
type FolderGroup struct {
    uadmin.Model
    Group    uadmin.UserGroup
    GroupID  uint
    Folder   Folder
    FolderID uint
    Read     bool
    Add      bool
    Edit     bool
    Delete   bool
}

// FolderGroup function that returns string value
func (f *FolderGroup) String() string {

    // Gives access to the fields in another model
    uadmin.Preload(f)

    // Returns the GroupName from the Group model
    return f.Group.GroupName
}

// HideInDashboard !
func (FolderGroup) HideInDashboard() bool {
    return true
}

folder_user.go

Back to Top

package models

import (
    "github.com/uadmin/uadmin"
)

// FolderUser !
type FolderUser struct {
    uadmin.Model
    User     uadmin.User
    UserID   uint
    Folder   Folder
    FolderID uint
    Read     bool
    Add      bool
    Edit     bool
    Delete   bool
}

// FolderUser function that returns string value
func (f *FolderUser) String() string {

    // Gives access to the fields in another model
    uadmin.Preload(f)

    // Returns the full name from the User model
    return f.User.String()
}

// HideInDashboard !
func (FolderUser) HideInDashboard() bool {
    return true
}

folder.go

Back to Top

package models

import (
    "github.com/uadmin/uadmin"
)

// Folder !
type Folder struct {
    uadmin.Model
    Name     string
    Parent   *Folder
    ParentID uint
}

format.go

Back to Top

package models

// Format is the name of the drop down list ...
type Format int

// PDF is the name of the drop down list value ...
func (Format) PDF() Format {
    return 1
}

// TXT is the name of the drop down list value ...
func (Format) TXT() Format {
    return 2
}

// Others is the name of the drop down list value ...
func (Format) Others() Format {
    return 3
}

main.go

Back to Top

package main

import (
    // Specify the username that you used inside github.com folder
    "github.com/username/document_system/models"
    "github.com/uadmin/uadmin"
)

func main() {
    // Register models to uAdmin
    uadmin.Register(
        models.Folder{},
        models.FolderGroup{},
        models.FolderUser{},
        models.Channel{},
        models.Document{},
        models.DocumentGroup{},
        models.DocumentUser{},
        models.DocumentVersion{},
    )

    // Register FolderGroup and FolderUser to Folder model
    uadmin.RegisterInlines(
        models.Folder{},
        map[string]string{
            "foldergroup": "FolderID",
            "folderuser":  "FolderID",
        },
    )

    // Register DocumentVersion, DocumentGroup, and DocumentUser to Document
    // model
    uadmin.RegisterInlines(
        models.Document{},
        map[string]string{
            "documentgroup":   "DocumentID",
            "documentuser":    "DocumentID",
            "documentversion": "DocumentID",
        },
    )

    // Initialize docS variable that calls the document model in the schema
    docS := uadmin.Schema["document"]

    // FormModifier makes CreatedBy read only if the user is not an admin
    // and the CreatedBy is not an empty string.
    docS.FormModifier = func(s *uadmin.ModelSchema, m interface{}, u *uadmin.User) {
        // Casts an interface to the Document model
        d, _ := m.(*models.Document)

        // Check whether the user is not an admin and the CreatedBy Field of
        // Document model is not an empty string
        if !u.Admin && d.CreatedBy != "" {
            // Set the CreatedBy Field to read only
            s.FieldByName("CreatedBy").ReadOnly = "true"
        }
    }

    // ListModifier is based on the user ID where the admin status is active
    // or not. If the user is not an admin, he has limited access to the
    // models and its records.
    docS.ListModifier = func(s *uadmin.ModelSchema, u *uadmin.User) (string, []interface{}) {
        // Checks whether the user is not an admin
        if !u.Admin {
            // Returns the user ID
            return "user_id = ?", []interface{}{u.ID}
        }
        // Returns nothing
        return "", []interface{}{}
    }

    // Pass back to the schema of document model
    uadmin.Schema["document"] = docS

    // Assign Site Name value as "Document System"
    // NOTE: This code works only if database does not exist yet.
    uadmin.SiteName = "Document System"

    // Activates a uAdmin server
    uadmin.StartServer()
}

uadmin.db

Channels

Back to Top

../../../_images/channelmodelupdate.png

Documents

Back to Top

../../../_images/documentmodelupdate3.png

Document Groups

Back to Top

../../../_images/documentgroupmodelupdate2.png

Document Users

Back to Top

../../../_images/documentusermodelupdate2.png

Document Versions

Back to Top

../../../_images/documentversionmodelupdate3.png

Folder Groups

Back to Top

../../../_images/foldergroupmodelupdate.png

Folder Users

Back to Top

../../../_images/folderusermodelupdate.png

Folders

Back to Top

../../../_images/foldermodelupdate.png

Group Permissions

Back to Top

../../../_images/grouppermissionmodelupdate.png