skylab

package
v0.0.0-...-baf4ea5 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2021 License: MIT Imports: 48 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"             // skylab.User
	ContextAdmin            skylabContext = "ContextAdmin"            // skylab.User
	ContextCurrentRole      skylabContext = "ContextCurrentRole"      // string
	ContextCurrentSection   skylabContext = "ContextCurrentSection"   // string
	ContextCurrentMilestone skylabContext = "ContextCurrentMilestone" // string
	ContextDumpJson         skylabContext = "ContextDumpJson"         // bool
	ContextIsProd           skylabContext = "ContextIsProd"           // 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"
	ErrProjectLevelInvalid erro.BaseError = "OEHGC Project Level '%s' is not a valid Skylab Project Level"

	// 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"
	ErrApplicationMagicstringNotExist       erro.BaseError = "OC8UM Applicant {application_id:%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 {application_id:%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 [application_id:%d] that was already accepted"
	ErrApplicationDeleted                   erro.BaseError = "OC8W6 Application {application_id:%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"
	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"
	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 LastRoleCookieName = "_skylab_role"
View Source
const MultipartMaxSize = 32 << 20

Variables

View Source
var (

	// ProjectRootDir will point to wherever this project's root directory is located
	// on the user's computer. It is calculated as an offset of two
	// directories up from the skylab.go file
	ProjectRootDir = filepath.Join(filepath.Dir(sourcefile), ".."+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

Contains is general purpose string function that checks if a slice of values contains the target string.

func EnsureDatabasePopulated

func EnsureDatabasePopulated(databaseURL, migrationDir string) error

TODO: instead of being a custom function, EnsureDatabasePopulated should utilise loadsql's functions with the configuration -clean. So running `go run main.go` is enough to bootstrap everything (tables, triggers, views, functions, data). loadsql should accept an io.Writer to write to instead of taking control of stdout, and EnsureDatabasePopulated simply has to pass in os.Stdout to print to stdout as usual.

func GetVars

func GetVars(ctx context.Context) map[string]interface{}

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. Such is the case when carrying out the automated tests in Github Actions. It is therefore important to ensure that the .env.default file has useful 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 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 SetVars

func SetVars(namespace string, ctx context.Context) context.Context

func SetVarsHandler

func SetVarsHandler(namespace string) func(http.Handler) http.Handler

func Stages

func Stages() []string

func TeamStatuses

func TeamStatuses() []string

func UserViewFuncs

func UserViewFuncs(funcs template.FuncMap, data UserView) template.FuncMap

Types

type Application

type Application struct {
	Valid              bool
	ApplicationID      int
	Cohort             string
	ProjectLevel       string
	Status             string
	Submitted          bool
	Magicstring        sql.NullString
	Applicant1         User
	Applicant2         User
	ApplicationForm    Form
	ApplicationAnswers formx.Answers
	ApplicantForm      Form
	Applicant1Answers  formx.Answers
	Applicant2Answers  formx.Answers
}

func (*Application) RowMapper

func (a *Application) RowMapper(tbl tables.VIEW_V_APPLICATIONS) func(*sq.Row)

type ApplicationEdit

type ApplicationEdit struct {
	ApplicantUserID int
	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 Diagnosis

type Diagnosis struct {
	Valid bool
	HTML  template.HTML
}

type Form

type Form struct {
	Valid      bool
	FormID     int
	Period     Period
	Name       string
	Subsection string
	Questions  formx.Questions
}

func (Form) Title

func (fs Form) Title() string

type FormEdit

type FormEdit struct {
	Title            string
	Form             Form
	QuestionsAnswers []formx.QuestionAnswer
	PreviewURL       string
	UpdateURL        string
}

type FormView

type FormView struct {
	Title            string
	Form             Form
	QuestionsAnswers []formx.QuestionAnswer
	EditURL          string
	UpdateURL        string
}

type Period

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

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
	Templates   *template.Template

	// Mailer
	// NOTE: not used
	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 is the server struct.

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 easy for running database-related tests, 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. I assume databases with the same txdbIdentifier will share the same state, making it possible to share the same database between two tests by using the same txdbIdentifier.

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 template.FuncMap) template.FuncMap

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 nusskylabx.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) 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(). 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) EnsureIsUser

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

EnsureIsUser ensures that the email from auth.Authenticate is a valid user in the system

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 which will automatically pick up the flash messages and display them if any exist.

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) 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.

Even if the user cannot be found in the database, this function will return without error. It is up to you to check the if the returned user's 'Valid' field is set to true. This makes it easier to distinguish between whether a user is valid (the user's Valid field is false) or whether an actual error occurred while querying the database (the returned error is non-nil).

Alternatively you can also check if the returned user has a particular role that is required to view the resource.

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(). 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 template.FuncMap, w http.ResponseWriter, r *http.Request) template.FuncMap

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) NotAUser

func (skylb Skylab) NotAUser(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 is not 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) Render2

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

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) (*http.Request, error)

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

NOTE: This returns a new request with the flash messages in the context updated, so make sure to re-assign the request:

r, _ = skylb.SetFlashMsgs(w, r, msgs)

func (Skylab) SetRoleSection

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

func (Skylab) SetRoleSectionHandler

func (skylb Skylab) SetRoleSectionHandler(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) SetSessionForUserID

func (skylb Skylab) SetSessionForUserID(userID int) (sessionID string, sessionHash string, err error)

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

func (Skylab) SubmissionEdit

func (skylb Skylab) SubmissionEdit(role string) http.HandlerFunc

func (Skylab) SubmissionView

func (skylb Skylab) SubmissionView(role string) http.HandlerFunc

func (Skylab) TeamEvaluationView

func (skylb Skylab) TeamEvaluationView(role string) http.HandlerFunc

func (Skylab) UserEvaluationEdit

func (skylb Skylab) UserEvaluationEdit(role string) http.HandlerFunc

func (Skylab) UserEvaluationView

func (skylb Skylab) UserEvaluationView(role string) http.HandlerFunc

func (Skylab) ValidateCohortStageMilestone

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

type Submission

type Submission struct {
	Valid             bool
	SubmissionID      int
	Team              Team
	SubmissionForm    Form
	SubmissionAnswers formx.Answers
	OverrideOpen      bool
	Submitted         bool
	UpdatedAt         sql.NullTime
}

func (*Submission) RowMapper

func (s *Submission) RowMapper(tbl tables.VIEW_V_SUBMISSIONS) func(*sq.Row)

type SubmissionEditData

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

SubmissionEditData is the data struct that targets the "app/skylab/submission_edit.html" template

type SubmissionViewData

type SubmissionViewData struct {
	Submission Submission
	EditURL    string
	SubmitURL  string
}

SubmissionViewData is the data struct that targets the "app/skylab/submission_view.html" template

type Team

type Team struct {
	Valid        bool
	TeamID       int
	Cohort       string
	TeamName     string
	ProjectLevel string
	Status       string
	Student1     User
	Student2     User
	Adviser      User
	Mentor       User
}

func (*Team) RowMapper

func (t *Team) RowMapper(tbl tables.VIEW_V_TEAMS) func(*sq.Row)

type TeamEdit

type TeamEdit struct {
	Team      Team
	UpdateURL string
}

type TeamEvaluation

type TeamEvaluation struct {
	Valid             bool
	TeamEvaluationID  int
	Evaluator         Team
	Evaluatee         Submission
	EvaluationForm    Form
	EvaluationAnswers formx.Answers
	OverrideOpen      bool
	Submitted         bool
	UpdatedAt         sql.NullTime
}

A TeamEvaluation is carried out by a Team on a Team

func (*TeamEvaluation) RowMapper

func (e *TeamEvaluation) RowMapper(tbl tables.VIEW_V_TEAM_EVALUATIONS) func(*sq.Row)

type TeamEvaluationEdit

type TeamEvaluationEdit struct {
	TeamEvaluation TeamEvaluation
	UpdateURL      string
	SubmitURL      string
	SubmissionURL  string
	PreviewURL     string
}

type TeamEvaluationView

type TeamEvaluationView struct {
	TeamEvaluation TeamEvaluation
	SubmitURL      string
	SubmissionURL  string
	EditURL        string
}

type TeamFeedback

type TeamFeedback struct {
	Valid            bool
	FeedbackIDOnTeam int
	Evaluator        Team
	Evaluatee        Team
	FeedbackForm     Form
	FeedbackAnswers  formx.Answers
	Submitted        bool
	OverrideOpen     bool
}

Feedback given to a Team, by a Team

type TeamView

type TeamView struct {
	Team        Team
	UserBaseURL string
}

type User

type User struct {
	Valid       bool           `db:"-"`
	UserID      int            `db:"user_id"`
	Displayname string         `db:"displayname"`
	Email       string         `db:"email"`
	Roles       map[string]int `db:"-" json:"Roles"` // map of the user's role to the user_role_id
}

type UserEdit

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

type UserEvaluation

type UserEvaluation struct {
	Valid             bool
	UserEvaluationID  int
	Evaluator         User
	Role              string
	Evaluatee         Submission
	EvaluationAnswers formx.Answers
	EvaluationForm    Form
	Submitted         bool
	OverrideOpen      bool
	UpdatedAt         sql.NullTime
}

An UserEvaluation is carried out by a User on a Team

func (*UserEvaluation) RowMapper

func (e *UserEvaluation) RowMapper(tbl tables.VIEW_V_USER_EVALUATIONS) func(*sq.Row)

type UserEvaluationEdit

type UserEvaluationEdit struct {
	Evaluation    UserEvaluation
	SubmissionURL string
	SubmitURL     string
	PreviewURL    string
	UpdateURL     string
}

type UserEvaluationView

type UserEvaluationView struct {
	Evaluation    UserEvaluation
	SubmissionURL string
	SubmitURL     string
	EditURL       string
}

type UserFeedback

type UserFeedback struct {
	Valid            bool
	FeedbackIDOnUser int
	Evaluator        Team
	Evaluatee        User
	Role             string
	FeedbackForm     Form
	FeedbackAnswers  formx.Answers
	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