skylab

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2020 License: MIT Imports: 46 Imported by: 0

Documentation

Overview

Package app implements the Skylab website for Orbital

Index

Constants

View Source
const (
	ProjectLevelVostok  = "vostok"
	ProjectLevelGemini  = "gemini"
	ProjectLevelApollo  = "apollo"
	ProjectLevelArtemis = "artemis"
)

ProjectLevel consts correspond to the project levels present inside the project_level_enum table in the database

View Source
const (
	RoleApplicant = "applicant"
	RoleStudent   = "student"
	RoleAdviser   = "adviser"
	RoleMentor    = "mentor"
	RoleAdmin     = "admin"
	RoleNull      = ""

	// RolePreserve is a special role that does not exist in the database, but
	// is only for facilitating the app
	RolePreserve = "preserve"
)

Role consts correspond to the roles present inside the role_enum table in the database

View Source
const (
	ApplicationStatusPending  = "pending"
	ApplicationStatusAccepted = "accepted"
	ApplicationStatusDeleted  = "deleted"
)

ApplicationStatus consts correspond to the statuses present inside the applications_status_enum table in the database

View Source
const (
	TeamStatusGood          = "good"
	TeamStatusOk            = "ok"
	TeamStatusUncontactable = "uncontactable"
)

TeamStatus consts correspond to the statuses present inside the teams_status_enum table in the database

View Source
const (
	StageApplication = "application"
	StageSubmission  = "submission"
	StageEvaluation  = "evaluation"
	StageFeedback    = "feedback"
	StageNull        = ""
)

Stage consts correspond to the stages present inside the stage_enum table in the database

View Source
const (
	Milestone1    = "milestone1"
	Milestone2    = "milestone2"
	Milestone3    = "milestone3"
	MilestoneNull = ""
)

Milestone consts correspond to the milestones present inside the milestone_enum table in the database

View Source
const (
	ApplicationSubsectionApplication = "application"
	ApplicationSubsectionApplicant   = "applicant"
)

ApplicationSubsection consts correspond to the subsection columns inside the form_schema table in the database, only applicable to application form schemas

View Source
const (
	ContextUser           skylabContext = "ContextUser"           // orbital.User
	ContextAdmin          skylabContext = "ContextAdmin"          // orbital.User
	ContextCurrentRole    skylabContext = "ContextCurrentRole"    // string
	ContextCurrentSection skylabContext = "ContextCurrentSection" // string
	ContextDumpJson       skylabContext = "ContextDumpJson"       // bool
	ContextIsProd         skylabContext = "ContextIsProd"         // bool
	ContextAllowJS        skylabContext = "ContextAllowJS"        // bool

	// Submission
	ContextCanViewSubmission skylabContext = "ContextCanViewSubmission" // bool
	ContextCanEditSubmission skylabContext = "ContextCanEditSubmission" // bool

	// Evaluation
	ContextCanViewEvaluation skylabContext = "ContextCanViewEvaluation" // bool
	ContextCanEditEvaluation skylabContext = "ContextCanEditEvaluation" // bool

	// User
	ContextCanViewUser skylabContext = "ContextCanViewUser" // bool
	ContextCanEditUser skylabContext = "ContextCanEditUser" // bool

	// Team
	ContextCanViewTeam skylabContext = "ContextCanViewTeam" // bool
	ContextCanEditTeam skylabContext = "ContextCanEditTeam" // bool

	// Form
	ContextCanViewForm skylabContext = "ContextCanViewForm" // bool
	ContextCanEditForm skylabContext = "ContextCanEditForm" // bool

	// Application
	ContextCanViewApplication skylabContext = "ContextCanViewApplication" // bool
	ContextCanEditApplication skylabContext = "ContextCanEditApplication" // bool
)

The Context consts are used as keys to access various objects from the request context. Every app handler that sets/gets an object from the context must use one of these keys, so that all objects that are accessible from the context are effectively documented by this list here

View Source
const (
	// Roles
	ErrNoRoles        erro.BaseError = "OLAJQ User %+v has no roles"
	ErrNotAnApplicant erro.BaseError = "OC8FY User %+v is not an applicant"
	ErrNotAStudent    erro.BaseError = "ONXIU User %+v is not a student"
	ErrNotAMentor     erro.BaseError = "OQVPW User %+v is not a mentor"
	ErrNotAnAdviser   erro.BaseError = "OKDRA User %+v is not an adviser"
	ErrNotAnAdmin     erro.BaseError = "OD6HR User %+v is not an admin"
	ErrNoPeerTeams    erro.BaseError = "OO6BX Team %+v has no peer teams"

	// Skylab Enums
	ErrCohortInvalid    erro.BaseError = "OLALE Cohort '%s' is not a valid Skylab cohort"
	ErrStageInvalid     erro.BaseError = "OLASP Stage '%s' is not a valid Skylab stage"
	ErrMilestoneInvalid erro.BaseError = "OLADN Milestone '%s' is not a valid Skylab milestone"
	ErrRoleInvalid      erro.BaseError = "OLAZN Role '%s' is not a valid Skylab role"

	// Forms
	ErrSubmissionFormNotExist  erro.BaseError = "OYOE8 Submission form doesn't exist"
	ErrApplicationFormNotExist erro.BaseError = "OC8BK Application Form doesn't exist"

	// APPLICATION C8
	ErrApplicationNotExist                  erro.BaseError = "OC8U9 Application doesn't exist"
	ErrApplicationPeriodNotFound            erro.BaseError = "OC8EX Application period not found"
	ErrApplicationMoreThanTwoApplicants     erro.BaseError = "OC8VX Application [apnid:%d] has more than two applicants"
	ErrApplicationMagicstringNotExist       erro.BaseError = "OC8UM Applicant [apnid:%d] tried joining an application with an invalid magicstring"
	ErrApplicantJoinedOwnApplication        erro.BaseError = "OC8JK Applicant [uid:%d] tried joining an application created by himself"
	ErrApplicationAlreadyFull               erro.BaseError = "OC8FB Application [apnid:%d] is already full"
	ErrApplicantLeaveNonExistentApplication erro.BaseError = "OC8EN Applicant [uid:%d] tried leaving an application when he isn't in one"
	ErrApplicantLeaveAcceptedApplication    erro.BaseError = "OC8A4 Applicant [uid:%d] tried leaving an application [apnid:%d] that was already accepted"
	ErrApplicationDeleted                   erro.BaseError = "OC8W6 Application [apnid:%d] already accepted/deleted"
	ErrApplicationIncomplete                erro.BaseError = "OC8KH Tried accepting an incomplete application"
	ErrApplicationNoTeam                    erro.BaseError = "OC8R1 Tried un-accepting an application that had never been accepted"

	// Misc
	ErrStudentNoTeam           erro.BaseError = "ONXDI Student [uid:%d] does not belong to any team"
	ErrTeamMoreThanTwoStudents erro.BaseError = "OYSGQ Team [tid:%d] has more than two students"
	ErrEmailNotAuthorized      erro.BaseError = "OLALP Email '%s' is not authorized to signup for any role"
	ErrEmailEmpty              erro.BaseError = "OLAR9 Email must be non-empty"

	// Not exist
	ErrUserNotExist           erro.BaseError = "OLAMC User does not exist: %s"
	ErrTeamNotExist           erro.BaseError = "OWHZT Team does not exist: %s"
	ErrSubmissionNotExist     erro.BaseError = "OQ7WT Submission does not exist: %s"
	ErrFormdataNotExist       erro.BaseError = "ON354 Formdata [fdid:%d] does not exist"
	ErrTeamEvaluationNotExist erro.BaseError = "OSF99 Team Evaluation does not exist: %s"
	ErrUserEvaluationNotExist erro.BaseError = "OR2KO User Evaluation does not exist: %s"
	ErrTeamFeedbackNotExist   erro.BaseError = "ONDHA Team Feedback does not exist: %s"
	ErrUserFeedbackNotExist   erro.BaseError = "OXZW4 User Feedback does not exist: %s"
	ErrFormNotExist           erro.BaseError = "OLAJX Form does not exist: %s"
	ErrPeriodNotExist         erro.BaseError = "OLAEE Period does not exist: %s"
)

Each error starts with a 5 letter error code, intended to represent the custom error codes that can be returned from postgres stored procedures.

How to name custom error codes https://stackoverflow.com/a/22600394 • Start with a capital letter but not F (predefined config file errors), H (fdw), P (PL/pgSQL) or X (internal). • Do not use 0 (zero) or P in the 3rd column. Predefined error codes use these commonly. • Use a capital letter in the 4th position. No predefined error codes have this. 'As an example, start with a character for your app: "T". Then a two-char error class: "3G". Then a sequential code "A0"-"A9", "B0"-"B9", etc. Yields T3GA0, T3GA1, etc.'

View Source
const (
	SectionPreserve = "preserve"

	StudentDashboard      = "/student/dashboard"
	StudentSubmission     = "/student/submission"
	StudentUserEvaluation = "/student/user-evaluation"
	StudentTeamEvaluation = "/student/team-evaluation"
	StudentM1Submission   = "/student/milestone1/submission"
	StudentM1Evaluation   = "/student/milestone1/evaluation"
	StudentM2Submission   = "/student/milestone2/submission"
	StudentM2Evaluation   = "/student/milestone2/evaluation"
	StudentM3Submission   = "/student/milestone3/submission"
	StudentM3Evaluation   = "/student/milestone3/evaluation"
	StudentTeamFeedback   = "/student/feedback/team"
	StudentUserFeedback   = "/student/feedback/user"
	StudentListFeedbacks  = "/student/feedbacks"
	StudentTeam           = "/student/teams"

	AdviserDashboard           = "/adviser/dashboard"
	AdviserTeams               = "/adviser/teams"
	AdviserEvaluateeEvaluators = "/adviser/evaluatee-evaluators"
	AdviserEvaluatorEvaluatees = "/adviser/evaluator-evaluatees"
	AdviserSubmission          = "/adviser/submission"
	AdviserUserEvaluation      = "/adviser/user-evaluation"
	AdviserTeamEvaluation      = "/adviser/team-evaluation"
	AdviserM1MakeEvaluation    = "/adviser/milestone1/evaluation"
	AdviserM1ViewEvaluation    = "/adviser/milestone1/evaluations"
	AdviserM2MakeEvaluation    = "/adviser/milestone2/evaluation"
	AdviserM2ViewEvaluation    = "/adviser/milestone2/evaluations"
	AdviserM3MakeEvaluation    = "/adviser/milestone3/evaluation"
	AdviserM3ViewEvaluation    = "/adviser/milestone3/evaluations"

	MentorDashboard = "/mentor/dashboard"

	AdminDashboard         = "/admin/dashboard"
	AdminCreateUser        = "/admin/create-user"
	AdminCreateUserConfirm = "/admin/create-user/confirm"
	AdminCreateTeam        = "/admin/create-team"
	AdminCreateTeamConfirm = "/admin/create-team/confirm"
	AdminListCohorts       = "/admin/cohorts"
	AdminListUsers         = "/admin/users"
	AdminUser              = "/admin/user"
	AdminListPeriods       = "/admin/periods"
	AdminListForms         = "/admin/forms"
	AdminForm              = "/admin/form"
	AdminListTeams         = "/admin/teams"
	AdminTeam              = "/admin/team"
	AdminListApplications  = "/admin/applications"
	AdminApplication       = "/admin/application"
	AdminListFeedbacks     = "/admin/feedbacks"
	AdminDebugMode         = "/debug-mode"
	AdminDumpJson          = "/dump-json"
	AdminTestmail          = "/testmail" // experimental
)
View Source
const (
	SessionCookieName      = "_skylab_session"       // The name of the cookie that stores the user's session
	AdminSessionCookieName = "_skylab_session_admin" // The name of the cookie that stores the admin's session
)
View Source
const GorillaCsrfToken = "gorilla.csrf.Token" // will eventually be deleted, not really used
View Source
const LastRoleCookieName = "_skylab_role"

Variables

View Source
var (
	_,// SkylabDotGo is the filepath to the source file skylab.go
	SkylabDotGo, _, _ = runtime.Caller(0)
	// RootDir will point to wherever this project's root directory is located
	// on the user's computer.  It is calculated by an offset of two
	// directories up from the skylab.go file
	RootDir = filepath.Join(filepath.Dir(SkylabDotGo), ".."+string(os.PathSeparator)+"..") + string(os.PathSeparator)
)

Functions

func AddSections

func AddSections(funcs template.FuncMap) template.FuncMap

func ApplicationStatuses

func ApplicationStatuses() []string

func ApplicationSubsections

func ApplicationSubsections() []string

func Contains

func Contains(values []string, target string) bool

func EnsureDatabasePopulated

func EnsureDatabasePopulated(databaseURL, migrationDir string) error

func InputSelect

func InputSelect(vds []ValueDisplay, name, defaultValue, class string) template.HTML

func InputSelectMilestoneOptional

func InputSelectMilestoneOptional(name, defaultValue, class string) template.HTML

func InputSelectProjectLevel

func InputSelectProjectLevel(name, defaultValue, class string) template.HTML

func InputSelectRole

func InputSelectRole(name, defaultValue, class string) template.HTML

func InputSelectStageOptional

func InputSelectStageOptional(name, defaultValue, class string) template.HTML

func IsValidSection

func IsValidSection(section string) bool

func JSONifyResponse

func JSONifyResponse(next http.HandlerFunc) http.HandlerFunc

JSONifyResponse will set the value of ContextDumpJson to true, effectively signalling to (*Skylab).Render that it should render its output as JSON, not as HTML

func LastSectionCookieName

func LastSectionCookieName(role string) string

func LoadDotenv

func LoadDotenv()

LoadDotenv will read load the environment variables from the .env file in the project root directory. The environment variable can then be accessed normally with os.Getenv.

If the .env file is not found, it will read from the .env.default file instead. This is the case when carrying out the automated tests in Github Actions. It is therefore important to ensure that the .env.default file has sane defaults set in it.

func MilestoneName

func MilestoneName(milestone string) string

MilestoneName pretty prints the name of the milestone

func MilestoneNameAbbrev

func MilestoneNameAbbrev(milestone string) string

MilestoneNameAbbrev

func Milestones

func Milestones() []string

func PadUid

func PadUid(users []User, userArrayArray ...[]User) func(int) string

PadUid pads a uid up to the max number of digits in the highest uid in a given slice of users

func ProjectLevels

func ProjectLevels() []string

func Roles

func Roles() []string

func SGTime

func SGTime(t sql.NullTime) string

func SanitizeHTML

func SanitizeHTML(policy *bluemonday.Policy) func(string) template.HTML

func Stages

func Stages() []string

func TeamStatuses

func TeamStatuses() []string

func UserViewFuncs

func UserViewFuncs(funcs map[string]interface{}, data UserView) map[string]interface{}

Types

type Application

type Application struct {
	Valid              bool
	Apnid              int
	Tid                int
	Cohort             string
	ProjectLevel       string
	Status             string
	Submitted          bool
	Magicstring        sql.NullString
	Creator            int
	Applicant1         User
	Applicant2         User
	Formdata           Formdata
	Applicant1Formdata Formdata
	Applicant2Formdata Formdata
}

type ApplicationEdit

type ApplicationEdit struct {
	Application Application
}

type ApplicationView

type ApplicationView struct {
	Application Application
}

type Config

type Config struct {
	BaseURL      string // optional
	Port         string // optional
	DatabaseURL  string // !REQUIRED
	MigrationDir string // !REQUIRED
	IsProd       string // optional
	DebugMode    string // optional
	SecretKey    string // optional
	DisableCsrf  string // optional

	// Experimental
	MailerEnabled string
	SmtpHost      string
	SmtpPort      string
	SmtpUsername  string
	SmtpPassword  string
}

Config contains all the parameters needed to start an instance of the Skylab application

type DatabaseOp

type DatabaseOp struct {
	Skylb        Skylab
	Err          error
	RowsAffected int64
}

func (*DatabaseOp) CreateRole

func (db *DatabaseOp) CreateRole(cohort, role, email string)

func (*DatabaseOp) CreateUser

func (db *DatabaseOp) CreateUser(displayname, email string)

func (*DatabaseOp) UpdateDisplayname

func (db *DatabaseOp) UpdateDisplayname(displayname, email string)

type Diagnosis

type Diagnosis struct {
	Valid bool
	HTML  template.HTML
}

type FormEdit

type FormEdit struct {
	Formdata   Formdata
	PreviewURL string
	UpdateURL  string
}

type FormView

type FormView struct {
	Formdata  Formdata
	EditURL   string
	UpdateURL string
}

type Form_Old

type Form_Old struct {
	Cohort     string          `db:"cohort"`
	Stage      string          `db:"stage"`
	Milestone  string          `db:"milestone"`
	Name       string          `db:"name"`
	Subsection string          `db:"subsection"`
	Fsid       int             `db:"fsid"`
	Questions  formx.Questions `db:"questions"`
	StartAt    sql.NullTime    `db:"start_at"`
	EndAt      sql.NullTime    `db:"end_at"`
}

type Formdata

type Formdata struct {
	Valid      bool
	Period     Period
	Fdid       int
	Fsid       int
	Name       string
	Subsection string
	Data       []formx.QuestionAnswer
}

func (Formdata) Title

func (fd Formdata) Title() string

type Period

type Period struct {
	Valid      bool         `db:"-"`
	Pid        int          `db:"pid"`
	Cohort     string       `db:"cohort"`
	Stage      string       `db:"stage"`
	Milestone  string       `db:"milestone"`
	StartAt    sql.NullTime `db:"start_at"`
	EndAt      sql.NullTime `db:"end_at"`
	Timestatus Timestatus   `db:"-"`
}

type SidebarItem

type SidebarItem struct {
	Section string
	Display string
	Link    string
	Icon    string
}

type Skylab

type Skylab struct {
	BaseURL string

	IsProd      bool
	SecretKey   string
	Policy      *bluemonday.Policy
	DB          *sqlx.DB
	Mux         *chi.Mux
	Bufpool     *bpool.BufferPool
	Log         *logutil.Logger
	DisableCsrf bool

	// Mailer
	MailerEnabled bool
	SmtpHost      string
	SmtpPort      int
	SmtpUsername  string
	SmtpPassword  string

	// These fields below are not safe for concurrent access and access must be
	// synchronized with a mutex.
	RW *sync.RWMutex
	// contains filtered or unexported fields
}

Skylab represents a common backend object that handles anything web related, but is blind to the database. It parses HTTP requests into Go variables to be handed to the Orbital object for database persistence/processing. The Apt & Stu & Adv & Mnt & Adm & Shr structs will embed the Skylab struct, hence inheriting any methods and fields exported by Skylab

func New

func New(config Config) (Skylab, error)

New creates a new instance of Skylab

func NewTestDefault

func NewTestDefault(t *testing.T) Skylab

NewTestDefault is the same as NewWithTestDB, except it uses a default skylab.Config instead of requiring the caller to pass one

func NewWithTestDB

func NewWithTestDB(t *testing.T, config Config, txdbIdentifier string) Skylab

NewWithTestDB creates a new instance of Skylab with a test database. The test database reuses the same DATABASE_URL as the actual database, except all changes are rolled back once the test database is closed i.e. DB.Close(). This makes it fantastic for running database tests on, without affecting the actual state of the database. You can optionally provide a txdbIdentifier string to id the test database being created, or you can leave it blank for a random identifier to be used.

func NewWithoutDB

func NewWithoutDB(config Config) Skylab

NewWithoutDB creates a new instance of Skylab that initializes everything except the database connection

func (Skylab) AddInputSelects

func (skylb Skylab) AddInputSelects(funcs map[string]interface{}) map[string]interface{}

func (Skylab) AddProdContext

func (skylb Skylab) AddProdContext(next http.Handler) http.Handler

AddProdContext will inject Skylab's environment (production or development) into the request context for handlers down the chain to pick up on

func (Skylab) AllowIfDevelopment

func (skylb Skylab) AllowIfDevelopment(next http.Handler) http.Handler

func (Skylab) BadRequest

func (skylb Skylab) BadRequest(w http.ResponseWriter, r *http.Request, msg string)

func (Skylab) BaseURLWithProtocol

func (skylb Skylab) BaseURLWithProtocol() string

BaseURL returns the base URL of the website e.g. localhost or nusskylab-dev.comp.nus.edu.sg.

This is purely for display purposes as the actual application is always running on localhost, even in production. On the production server, Nginx will reverse proxy the localhost application to the outside world.

This greatly simplifies deploying the app in production as everything is exactly the same as in the development environment. The application also need not concern itself with a HTTPS certificate as that is handled on the Nginx layer.

func (Skylab) ChooseProvider

func (skylb Skylab) ChooseProvider(next http.Handler) http.Handler

ChooseProvider will check if there is a valid authentication provider (either openid or oauth) in the current request's queryparams and if there isn't, it will render a page for the user to choose their preferred provider. The page will contain the same links that it was called with, effectively redirecting to itself (except this time with a valid provider)

func (Skylab) Cohorts

func (skylb Skylab) Cohorts() []string

Cohorts returns all cohorts (excluding the empty cohort).

func (Skylab) CreateTeam_Old

func (skylb Skylab) CreateTeam_Old(team Team_Old) (tid int, err error)

func (Skylab) CreateUser

func (skylb Skylab) CreateUser(user User) (uid int, err error)

CreateUser creates a new user in the database, returning the uid of the user.

The only fields it needs to read from are user.Displayname and user.Email.

It is idempotent and safe to call on the same user multiple times.

func (Skylab) CreateUserWithRoles

func (skylb Skylab) CreateUserWithRoles(user User, cohort string) (uid int, roles map[string]int, err error)

CreateUserWithRoles will create a user like Skylab.CreateUser, but it will also create new roles for every role specified in user.Roles. It returns the uid of the created user as well as the urids for each user role that was created.

The fields it reads from are user.Displayname, user.Email and user.Roles.

It is idempotent and safe to call on a user multiple times.

func (Skylab) CsrfTokenInvalid

func (skylb Skylab) CsrfTokenInvalid() http.Handler

func (Skylab) CurrentCohort

func (skylb Skylab) CurrentCohort() string

CurrentCohort returns the current cohort.

The CurrentCohort() is not the same as the LatestCohort(), as CurrentCohort() only points to the current calendar year. LatestCohort() points to the latest created cohort in the database, which may not be the same as cohorts may be created ahead of the current calendar year.

func (Skylab) DecodeVariableFromCookie

func (skylb Skylab) DecodeVariableFromCookie(r *http.Request, cookiename string, variablePtr interface{}) error

DecodeVariableFromCookie will retrieve a go variable from a client side cookie that was previously set by EncodeVariableInCookie. If the named cookie does not exist, it will fail with a cookies.ErrNoCookie error. Note that you need to pass a pointer to the variable you wish to decode the cookie value into, not the variable itself.

The cookie's digital hash signature will be checked for any tampering. If the signature is wrong, DecodeVariableFromCookie will fail with an auth.ErrDeserializeOutputInvalid error.

func (Skylab) EncodeVariableInCookie

func (skylb Skylab) EncodeVariableInCookie(w http.ResponseWriter, cookiename string, variable interface{}, opts ...cookies.CookieOpt) error

EncodeVariableInCookie will serialize any go variable into a string and store it in a client side cookie, where it can later be retrieved from the cookie with DecodeVariableFromCookie.

The cookie will be securely signed with a digital hash signature to prevent anyone from tampering with the cookie's contents.

func (Skylab) EnsureRole

func (skylb Skylab) EnsureRole(role string) func(http.Handler) http.Handler

GetSession gets a User and an Admin from the database using their corresponding cookie session IDs, and injects them into the current context. If the User or Admin does not have the required roles, the user will be redirected to "app/skylab/403.html" instead

Calling EnsureRole(RoleNull) is equivalent to calling GetSession() directly

func (Skylab) GetFlashMsgs

func (skylb Skylab) GetFlashMsgs(w http.ResponseWriter, r *http.Request) (map[string][]flash.FlashMsg, error)

GetFlashMsgs will get all flash messages from both the cookie and request context, after which it will delete the cookie (hence a 'flash' message).

Usually you won't call this function directly, instead you will include the "helpers/flash/flash.html" template in your html file (and the skylb.Render call) which will automatically pick up the flash messages and display them if any exist. Make sure to include the flash message functions (flash.Funcs) into the skylb.Render call so that "helpers/flash/flash.html" can call this function:

var funcs template.FuncMap
funcs = flash.Funcs(funcs)
skylb.Render(w, r, data, funcs, "some_file.html", "helpers/flash/flash.html")

func (Skylab) GetPeriodTimestatus

func (skylb Skylab) GetPeriodTimestatus(cohort, stage, milestone string) (status Timestatus, err error)

func (Skylab) GetSession

func (skylb Skylab) GetSession(next http.Handler) http.Handler

GetSession gets a User and an Admin from the database using their corresponding cookie session IDs, and injects them into the current context

func (Skylab) GetTeam_Old

func (skylb Skylab) GetTeam_Old(tid int) (Team_Old, error)

func (Skylab) GetUserFromCookie

func (skylb Skylab) GetUserFromCookie(r *http.Request, cookieName string) (user User, err error)

GetUserFromCookie gets a User from the database using a cookie's session ID

func (Skylab) GetUserFromSessionID

func (skylb Skylab) GetUserFromSessionID(sessionID string) (user User, err error)

GetUserFromSessionID retrieves a User by sessionID

func (Skylab) HasValidRole

func (skylb Skylab) HasValidRole(next http.Handler) http.Handler

func (Skylab) Hash

func (skylb Skylab) Hash(input []byte) (output string)

Hash will hash input byte slice and output a string

func (Skylab) InputSelectCohort

func (skylb Skylab) InputSelectCohort(name, defaultValue, class string) template.HTML

func (Skylab) InputSelectCohortOptional

func (skylb Skylab) InputSelectCohortOptional(name, defaultValue, class string) template.HTML

func (Skylab) InternalServerError

func (skylb Skylab) InternalServerError(w http.ResponseWriter, r *http.Request, err error)

InternalServerError is a catch-all handler that will direct the user to a generic error page indicating 500 Internal Server Error.

InternalServerError must not depend on any function that calls InternalServerError on error (such as (*Skylab).Render), otherwise it will spin into an infinite loop

func (Skylab) LatestCohort

func (skylb Skylab) LatestCohort() string

LatestCohort returns the latest cohort in the database.

The LatestCohort() is not the same as the CurrentCohort(), as LatestCohort() points to the latest cohort created in the database. CurrentCohort() points only to the current calendar year, which may not be the same as cohorts may be created ahead of the current calendar year.

func (Skylab) MethodNotAllowed

func (skylb Skylab) MethodNotAllowed(w http.ResponseWriter, r *http.Request)

func (Skylab) NavbarFuncs

func (skylb Skylab) NavbarFuncs(funcs map[string]interface{}, w http.ResponseWriter, r *http.Request) map[string]interface{}

NavbarFuncs contain the Template Functions required for rendering the navbar "app/skylab/navbar.html"

func (Skylab) NotAMentor

func (skylb Skylab) NotAMentor(w http.ResponseWriter, r *http.Request)

func (Skylab) NotARole

func (skylb Skylab) NotARole(role string) func(http.ResponseWriter, *http.Request)

func (Skylab) NotAStudent

func (skylb Skylab) NotAStudent(w http.ResponseWriter, r *http.Request)

func (Skylab) NotAnAdmin

func (skylb Skylab) NotAnAdmin(w http.ResponseWriter, r *http.Request)

func (Skylab) NotAnAdviser

func (skylb Skylab) NotAnAdviser(w http.ResponseWriter, r *http.Request)

func (Skylab) NotAnApplicant

func (skylb Skylab) NotAnApplicant(w http.ResponseWriter, r *http.Request)

func (Skylab) NotAuthorized

func (skylb Skylab) NotAuthorized(w http.ResponseWriter, r *http.Request)

Authentication != Authorization Not authenticated means the user is not logged in. Not authorized means user is logged in but not allowed to carry out the action e.g. student trying to access an admin page

401 Unauthorized == Authentication error 403 Forbidden == Authorization error

It seems contradictory that an Authorization error is actually 403 Forbidden and not 401 Unauthorized, but it's true. The people who designed the spec made a mistake and now we're all suffering for it. https://stackoverflow.com/a/6937030

func (Skylab) NotFound

func (skylb Skylab) NotFound(w http.ResponseWriter, r *http.Request)

func (Skylab) NotLoggedIn

func (skylb Skylab) NotLoggedIn(w http.ResponseWriter, r *http.Request)

func (Skylab) Port

func (skylab Skylab) Port() string

Port returns the port that the application is currently running on.

func (Skylab) Redirect

func (skylb Skylab) Redirect(url string) http.HandlerFunc

Redirect returns a http.HandlerFunc that redirects to the given url

func (Skylab) RedirectAfterLogout

func (skylb Skylab) RedirectAfterLogout(w http.ResponseWriter, r *http.Request)

func (Skylab) RedirectToLastSection

func (skylb Skylab) RedirectToLastSection(role string) func(http.Handler) http.Handler

func (Skylab) RedirectUserrole

func (skylb Skylab) RedirectUserrole(w http.ResponseWriter, r *http.Request)

func (*Skylab) RefreshCohorts

func (skylb *Skylab) RefreshCohorts() error

RefreshCohorts refreshes the current list of cohorts (kept in memory) by refreshing it from the database.

The reason why a list of cohorts is kept in-memory instead of querying it from the database on demand is because the cohort list rarely changes (once every year). That makes it inefficient to request it from database all the time. By keeping it in-memory, cohort retrieval is much faster but we have to refresh the cohort list everytime we add or remove a cohort from the database.

This may be a premature optimization.

func (Skylab) Render

func (skylb Skylab) Render(w http.ResponseWriter, r *http.Request, data interface{}, funcs template.FuncMap, filename string, filenames ...string)

Render will render one or more html templates together with the given data and funcs. Crucially, this function is where all the global template functions (prefixed by "Skylab") and templates (such as the global navbar template "app/skylab/navbar.html") are injected. If you want to add any globally available template functions or templates files, this is the place to do it

func (Skylab) RevokeSession

func (skylb Skylab) RevokeSession(next http.Handler) http.Handler

RevokeSession will revoke the user's or admin's (or both) sessions depending on whether the 'user' and 'admin' query params were provided. If neither was provided, both sessions will be revoked.

If only the user's session is revoked and the admin's session is not, the admin's existing session ID will be copied over as the new user session cookie. This ensures that the user session cookie is always present if someone is logged in.

func (Skylab) RevokeSessionCookie

func (skylb Skylab) RevokeSessionCookie(w http.ResponseWriter, r *http.Request, cookieName string) (err error)

RevokeSessionCookie will revoke a cookie's session ID from the database, followed by deleting the cookie

func (Skylab) SessionIdIsValidRole

func (skylb Skylab) SessionIdIsValidRole(sessionID string, role string) (valid bool, err error)

SessionIdIsValidRole checks if the given sessionID (hashed into a sessionHash) exists in the database together with the given role

func (Skylab) SetFlashMsgs

func (skylb Skylab) SetFlashMsgs(w http.ResponseWriter, r *http.Request, msgs map[string][]string) error

SetFlashMsgs will set a bunch of flash messages into a cookie that can be read later by GetFlashMsgs. See also: SetFlashMsgsCtx.

NOTE: IF YOU ARE RUNNING INTO ISSUES setting flash messages with SetFlashMsgs and being unable to read flash messages with GetFlashMsgs, it is very likely you're setting the flash messages and reading it in the same request. This will not work because SetFlashMsgs writes the data into a cookie, and you won't be able to read from that cookie until the next request (such as a redirect). To set a flash message in a handler and read it later in another handler under the same request, use SetFlashMsgsCtx instead. It takes all the same arguments except you can leave out the `w http.ResponseWriter`.

TL;DR if this not work use SetFlashMsgsCtx: From:

skylb.SetFlashMsgs(w, r, msgs)
skylb.Render(w, r, nil, nil, "some_file.html")

To:

r = r.WithContext(skylb.SetFlashMsgsCtx(r, msgs))
skylb.Render(w, r, nil, nil, "some_file.html")

func (Skylab) SetFlashMsgsCtx added in v0.2.0

func (skylb Skylab) SetFlashMsgsCtx(r *http.Request, msgs map[string][]string) context.Context

SetFlashMsgsCtx will set a bunch of flash messages into the request context that can be read later by GetFlashMsgs. See also: SetFlashMsgs.

NOTE: this returns a context.Context, which you must explicitly include into your current request context with:

r = r.WithContext(skylb.SetFlashMsgsCtx(r, msgs))

NOTE: IF YOU ARE RUNNING INTO ISSUES setting flash messages with SetFlashMsgsCtx and being unable to read flash messages with GetFlashMsgs, it is likely you're setting the flash messages and reading it after a redirect. This will not work bacuse SetFlashMsgsCtx writes the data into the request context, which is gone at the start of a new request. A redirect counts as a new request. To set a flash message in a handler and read it after a redirect, use SetFlashMsgs instead.

TL;DR if this not work use SetFlashMsgs: From:

r = r.WithContext(skylb.SetFlashMsgsCtx(r, msgs))
skylb.Render(w, r, nil, nil, "some_file.html")

To:

skylb.SetFlashMsgs(w, r, msgs)
skylb.Render(w, r, nil, nil, "some_file.html")

func (Skylab) SetRoleSectionCtx

func (skylb Skylab) SetRoleSectionCtx(w http.ResponseWriter, r *http.Request, role, section string) context.Context

func (Skylab) SetRoleSectionCtxHandler

func (skylb Skylab) SetRoleSectionCtxHandler(role, section string) func(http.Handler) http.Handler

func (Skylab) SetSession

func (skylb Skylab) SetSession(next http.Handler) http.Handler

SetSession sets the session for the user

func (Skylab) SetSessionUid

func (skylb Skylab) SetSessionUid(uid int) (sessionID string, sessionHash string, err error)

SetSessionUid sets the session for the given uid and returns the sessionID as well as the sessionHash. Prefer using SetSession over SetSessionUid if possible, as uid and sessionHash are considered more low level implementation details that are only needed in specific situations

func (Skylab) ValidateCohortStageMilestone

func (skylb Skylab) ValidateCohortStageMilestone(cohort, stage, milestone string) error

type Submission

type Submission struct {
	Valid        bool
	Tsid         int
	Team         Team
	Formdata     Formdata
	OverrideOpen bool
	Submitted    bool
	UpdatedAt    time.Time
}

type SubmissionEdit

type SubmissionEdit struct {
	Submission        Submission
	PeerEvaluations   []TeamEvaluation
	AdviserEvaluation UserEvaluation
	MentorEvaluation  UserEvaluation
	PreviewURL        string
	UpdateURL         string
	SubmitURL         string
}

type SubmissionView

type SubmissionView struct {
	Submission Submission
	EditURL    string
	SubmitURL  string
}

type Team

type Team struct {
	Valid        bool
	Tid          int
	Cohort       string
	TeamName     string
	ProjectLevel string
	Status       string

	Student1 User
	Student2 User
	Adviser  User
	Mentor   User
}

type TeamEdit

type TeamEdit struct {
	Team      Team
	UpdateURL string
}

type TeamEvaluation

type TeamEvaluation struct {
	Valid        bool
	Tesid        int
	Evaluator    Team
	Evaluatee    Submission
	Formdata     Formdata
	Submitted    bool
	OverrideOpen bool
}

An Evaluation carried out *by a Team*, to another Team

type TeamEvaluationEdit

type TeamEvaluationEdit struct {
	TeamEvaluation TeamEvaluation
	UpdateURL      string
	SubmitURL      string
}

type TeamEvaluationView

type TeamEvaluationView struct {
	TeamEvaluation TeamEvaluation
	SubmitURL      string
}

type TeamEvaluation_Old

type TeamEvaluation_Old struct {
	Cohort    string `db:"cohort"`
	Milestone string `db:"milestone"`

	// Submission
	Tsid                      sql.NullInt64          `db:"tsid"`
	EvaluateeTid              int                    `db:"evaluatee_tid"`
	EvaluateeTeamName         string                 `db:"evaluatee_team_name"`
	EvaluateeProjectLevel     string                 `db:"evaluatee_project_level"`
	SubmissionQuestions       formx.Questions        `db:"submission_questions"`
	SubmissionAnswers         formx.Answers          `db:"submission_answers"`
	SubmissionQuestionAnswers []formx.QuestionAnswer `db:"-"`
	SubmissionStartAt         sql.NullTime           `db:"submission_start_at"`
	SubmissionEndAt           sql.NullTime           `db:"submission_end_at"`
	SubmissionSubmitted       sql.NullBool           `db:"submission_submitted"`
	SubmissionUpdatedAt       sql.NullTime           `db:"submission_updated_at"`
	SubmissionOverrideOpen    sql.NullBool           `db:"submission_override_open"`

	// Evaluation
	Tesid                     sql.NullInt64          `db:"tesid"`
	EvaluatorTid              int                    `db:"evaluator_tid"`
	EvaluatorTeamName         string                 `db:"evaluator_team_name"`
	EvaluatorProjectLevel     string                 `db:"evaluator_project_level"`
	EvaluationQuestions       formx.Questions        `db:"evaluation_questions"`
	EvaluationAnswers         formx.Answers          `db:"evaluation_answers"`
	EvaluationQuestionAnswers []formx.QuestionAnswer `db:"-"`
	EvaluationStartAt         sql.NullTime           `db:"evaluation_start_at"`
	EvaluationEndAt           sql.NullTime           `db:"evaluation_end_at"`
	EvaluationSubmitted       sql.NullBool           `db:"evaluation_submitted"`
	EvaluationUpdatedAt       sql.NullTime           `db:"evaluation_updated_at"`
	EvaluationOverrideOpen    sql.NullBool           `db:"evaluation_override_open"`
}

type TeamFeedback

type TeamFeedback struct {
	Valid        bool
	Tftid        int
	Evaluator    Team
	Evaluatee    Team
	Formdata     Formdata
	Submitted    bool
	OverrideOpen bool
}

Feedback given *to a Team*, by a Team

type TeamView

type TeamView struct {
	Team        Team
	UserBaseURL string
}

type Team_Old

type Team_Old struct {
	Tid          int    `db:"tid"`
	Cohort       string `db:"cohort"`
	TeamName     string `db:"team_name"`
	ProjectLevel string `db:"project_level"`
	Status       string `db:"status"`

	Student1Uid         sql.NullInt64  `db:"stu1_uid"`
	Student1Displayname sql.NullString `db:"stu1_displayname"`
	Student1Email       sql.NullString `db:"stu1_email"`

	Student2Uid         sql.NullInt64  `db:"stu2_uid"`
	Student2Displayname sql.NullString `db:"stu2_displayname"`
	Student2Email       sql.NullString `db:"stu2_email"`

	AdviserUid         sql.NullInt64  `db:"adv_uid"`
	AdviserDisplayname sql.NullString `db:"adv_displayname"`
	AdviserEmail       sql.NullString `db:"adv_email"`

	MentorUid         sql.NullInt64  `db:"mnt_uid"`
	MentorDisplayname sql.NullString `db:"mnt_displayname"`
	MentorEmail       sql.NullString `db:"mnt_email"`

	CreatedAt time.Time    `db:"created_at"`
	UpdatedAt time.Time    `db:"updated_at"`
	DeletedAt sql.NullTime `db:"deleted_at"`
}

type Timestatus

type Timestatus struct {
	Start           sql.NullTime
	End             sql.NullTime
	IsOpen          bool
	NotYetOpen      bool
	AlreadyClosed   bool
	InvalidStartEnd bool
}

func ResolveTimestatus

func ResolveTimestatus(start, end sql.NullTime) (status Timestatus)

type User

type User struct {
	Valid       bool           `db:"-"`
	Uid         int            `db:"uid" json:"Uid"`
	Displayname string         `db:"displayname" json:"Displayname"`
	Email       string         `db:"email" json:"Email"`
	Roles       map[string]int `db:"-" json:"Roles"`
}

type UserEdit

type UserEdit struct {
	User           User
	Team           Team
	AdvisingTeams  []Team
	MentoringTeams []Team
	UserBaseURL    string
	TeamBaseURL    string
}

type UserEvaluation

type UserEvaluation struct {
	Valid     bool
	Uesid     int
	Evaluator User
	Role      string
	Evaluatee Submission
	Formdata  Formdata
	Submitted bool
}

An Evaluation carried out *by a User*, to a Team

type UserEvaluation_Old

type UserEvaluation_Old struct {
	Cohort    string `db:"cohort"`
	Milestone string `db:"milestone"`

	// Submission
	Tsid                      sql.NullInt64          `db:"tsid"`
	EvaluateeTid              int                    `db:"evaluatee_tid"`
	EvaluateeTeamName         string                 `db:"evaluatee_team_name"`
	EvaluateeProjectLevel     string                 `db:"evaluatee_project_level"`
	SubmissionQuestions       formx.Questions        `db:"submission_questions"`
	SubmissionAnswers         formx.Answers          `db:"submission_answers"`
	SubmissionQuestionAnswers []formx.QuestionAnswer `db:"-"`
	SubmissionStartAt         sql.NullTime           `db:"submission_start_at"`
	SubmissionEndAt           sql.NullTime           `db:"submission_end_at"`
	SubmissionSubmitted       sql.NullBool           `db:"submission_submitted"`
	SubmissionUpdatedAt       sql.NullTime           `db:"submission_updated_at"`
	SubmissionOverrideOpen    sql.NullBool           `db:"submission_override_open"`

	// Evaluation
	Uesid                     sql.NullInt64          `db:"uesid"`
	EvaluatorUrid             int                    `db:"evaluator_urid"`
	EvaluatorDisplayname      string                 `db:"evaluator_displayname"`
	EvaluationQuestions       formx.Questions        `db:"evaluation_questions"`
	EvaluationAnswers         formx.Answers          `db:"evaluation_answers"`
	EvaluationSubmitted       sql.NullBool           `db:"evaluation_submitted"`
	EvaluationQuestionAnswers []formx.QuestionAnswer `db:"-"`
	EvaluationStartAt         sql.NullTime           `db:"evaluation_start_at"`
	EvaluationEndAt           sql.NullTime           `db:"evaluation_end_at"`
	EvaluationUpdatedAt       sql.NullTime           `db:"evaluation_updated_at"`
}

type UserFeedback

type UserFeedback struct {
	Valid        bool
	Tfuid        int
	Evaluator    Team
	Evaluatee    User
	Role         string
	Formdata     Formdata
	Submitted    bool
	OverrideOpen bool
}

Feedback given *to a User*, by a Team

type UserView

type UserView struct {
	User           User
	Team           Team
	AdvisingTeams  []Team
	MentoringTeams []Team
	PreviewURL     string
	UserBaseURL    string
	TeamBaseURL    string
}

type ValueDisplay

type ValueDisplay struct {
	Value   string
	Display string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL