diff --git a/client/src/Main.tsx b/client/src/Main.tsx
index b999a9775f21bbc999dd1bbd00ca773bdb1ea02c..b1f0675d7129f755eb70ff9506c4c145a9b5a83c 100644
--- a/client/src/Main.tsx
+++ b/client/src/Main.tsx
@@ -7,8 +7,8 @@ import LoginPage from './pages/login/LoginPage'
 import PresentationEditorPage from './pages/presentationEditor/PresentationEditorPage'
 import AudienceViewPage from './pages/views/AudienceViewPage'
 import JudgeViewPage from './pages/views/JudgeViewPage'
-import ParticipantViewPage from './pages/views/ParticipantViewPage'
-import PresenterViewPage from './pages/views/PresenterViewPage'
+import TeamViewPage from './pages/views/TeamViewPage'
+import OperatorViewPage from './pages/views/OperatorViewPage'
 import ViewSelectPage from './pages/views/ViewSelectPage'
 import SecureRoute from './utils/SecureRoute'
 
@@ -24,8 +24,8 @@ const Main: React.FC = () => {
         <SecureRoute path="/admin" component={AdminPage} />
         <SecureRoute path="/editor/competition-id=:competitionId" component={PresentationEditorPage} />
         <Route exact path="/:code" component={ViewSelectPage} />
-        <Route exact path="/participant/id=:id&code=:code" component={ParticipantViewPage} />
-        <SecureRoute exact path="/presenter/id=:id&code=:code" component={PresenterViewPage} />
+        <Route exact path="/team/id=:id&code=:code" component={TeamViewPage} />
+        <SecureRoute exact path="/operator/id=:id&code=:code" component={OperatorViewPage} />
         <Route exact path="/judge/id=:id&code=:code" component={JudgeViewPage} />
         <Route exact path="/audience/id=:id&code=:code" component={AudienceViewPage} />
       </Switch>
diff --git a/client/src/actions/editor.ts b/client/src/actions/editor.ts
index db688dcb59f3732c6bda801b0aada1b3063a1238..526ad9ff6ae1698644e32ea02806c7a6e8e2cd78 100644
--- a/client/src/actions/editor.ts
+++ b/client/src/actions/editor.ts
@@ -18,16 +18,28 @@ export const getEditorCompetition = (id: string) => async (dispatch: AppDispatch
       if (getState().editor.activeSlideId === -1 && res.data.slides[0]) {
         setEditorSlideId(res.data.slides[0].id)(dispatch)
       }
+      const defaultViewType = getState().types.viewTypes.find((viewType) => viewType.name === 'Audience')
+      if (getState().editor.activeViewTypeId === -1 && defaultViewType) {
+        setEditorViewId(defaultViewType.id)(dispatch)
+      }
     })
     .catch((err) => {
       console.log(err)
     })
 }
 
-// Set currentSlideId in editor state
+// Set activeSlideId in editor state
 export const setEditorSlideId = (id: number) => (dispatch: AppDispatch) => {
   dispatch({
     type: Types.SET_EDITOR_SLIDE_ID,
     payload: id,
   })
 }
+
+// Set activeViewTypeId in editor state
+export const setEditorViewId = (id: number) => (dispatch: AppDispatch) => {
+  dispatch({
+    type: Types.SET_EDITOR_VIEW_ID,
+    payload: id,
+  })
+}
diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts
index 445a8d86eadbcdcdd698838fcc78aeee3d307f6e..2e9fc776f4c1828427df8424ef93d9588f20afea 100644
--- a/client/src/actions/types.ts
+++ b/client/src/actions/types.ts
@@ -24,6 +24,7 @@ export default {
   SET_COMPETITIONS_COUNT: 'SET_COMPETITIONS_COUNT',
   SET_EDITOR_COMPETITION: 'SET_EDITOR_COMPETITION',
   SET_EDITOR_SLIDE_ID: 'SET_EDITOR_SLIDE_ID',
+  SET_EDITOR_VIEW_ID: 'SET_EDITOR_VIEW_ID',
   SET_PRESENTATION_COMPETITION: 'SET_PRESENTATION_COMPETITION',
   SET_PRESENTATION_SLIDE: 'SET_PRESENTATION_SLIDE',
   SET_PRESENTATION_SLIDE_PREVIOUS: 'SET_PRESENTATION_SLIDE_PREVIOUS',
diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts
index 06fdee0afa3b467c5d0fe21f525b9ca7ec5e6a6a..a6fa68a0e9b58dcc63ad67d29884bbf3e781a64a 100644
--- a/client/src/interfaces/ApiModels.ts
+++ b/client/src/interfaces/ApiModels.ts
@@ -80,6 +80,8 @@ export interface Component {
   w: number
   h: number
   type_id: number
+  view_type_id: number
+  slide_id: number
 }
 
 export interface ImageComponent extends Component {
diff --git a/client/src/pages/admin/competitions/CompetitionManager.tsx b/client/src/pages/admin/competitions/CompetitionManager.tsx
index 83f399b95a0c083727333c2f842a1c61a8eca2d0..8ec3ee2981277a3f150642410581d472602e65ed 100644
--- a/client/src/pages/admin/competitions/CompetitionManager.tsx
+++ b/client/src/pages/admin/competitions/CompetitionManager.tsx
@@ -94,7 +94,7 @@ const CompetitionManager: React.FC = (props: any) => {
   }
 
   const handleStartCompetition = () => {
-    history.push(`/presenter/id=${activeId}&code=123123`)
+    history.push(`/operator/id=${activeId}&code=123123`)
     console.log('GLHF!')
   }
 
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
index c645724e3c7fabda69a2ad2434f4bd7b587c8c2e..5426152ff33783745f8dc1181237cd2926b0a16a 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
@@ -27,8 +27,19 @@ it('renders presentation editor', () => {
       ],
     },
   }
+  const typesRes: any = {
+    data: {
+      view_types: [
+        {
+          name: '',
+          id: 0,
+        },
+      ],
+    },
+  }
   ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
     if (path.startsWith('/api/competitions')) return Promise.resolve(competitionRes)
+    if (path.startsWith('/api/misc/types')) return Promise.resolve(typesRes)
     return Promise.resolve(citiesRes)
   })
   render(
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
index cd9d22c2055af4b4ddded3260db8b124b9124e6d..80d35c5f38c8b879976b91204f9572ac9cf5735a 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
@@ -10,7 +10,7 @@ import axios from 'axios'
 import React, { useEffect, useState } from 'react'
 import { Link, useParams } from 'react-router-dom'
 import { getCities } from '../../actions/cities'
-import { getEditorCompetition, setEditorSlideId } from '../../actions/editor'
+import { getEditorCompetition, setEditorSlideId, setEditorViewId } from '../../actions/editor'
 import { getTypes } from '../../actions/typesAction'
 import { useAppDispatch, useAppSelector } from '../../hooks'
 import { RichSlide } from '../../interfaces/ApiRichModels'
@@ -27,6 +27,7 @@ import {
   SlideListItem,
   ToolBarContainer,
   ViewButton,
+  ViewButtonClicked,
   ViewButtonGroup,
 } from './styled'
 
@@ -88,12 +89,13 @@ const PresentationEditorPage: React.FC = () => {
   const { competitionId }: CompetitionParams = useParams()
   const dispatch = useAppDispatch()
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
+  const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
   const competition = useAppSelector((state) => state.editor.competition)
   const competitionLoading = useAppSelector((state) => state.editor.loading)
   useEffect(() => {
+    dispatch(getTypes())
     dispatch(getEditorCompetition(competitionId))
     dispatch(getCities())
-    dispatch(getTypes())
   }, [])
 
   const setActiveSlideId = (id: number) => {
@@ -147,6 +149,16 @@ const PresentationEditorPage: React.FC = () => {
   })((props: CheckboxProps) => <Checkbox color="default" {...props} />)
   const [checkbox, setCheckbox] = useState(false)
 
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const [activeViewTypeName, setActiveViewTypeName] = useState('')
+  const changeView = (clickedViewTypeName: string) => {
+    setActiveViewTypeName(clickedViewTypeName)
+    const clickedViewTypeId = viewTypes.find((viewType) => viewType.name === clickedViewTypeName)?.id
+    if (clickedViewTypeId) {
+      dispatch(setEditorViewId(clickedViewTypeId))
+    }
+  }
+
   return (
     <PresentationEditorContainer>
       <CssBaseline />
@@ -164,10 +176,20 @@ const PresentationEditorPage: React.FC = () => {
             <Typography className={classes.alignCheckboxText} variant="button">
               Applicera ändringar på samtliga vyer
             </Typography>
-            <ViewButton variant="contained" color="secondary">
+            <ViewButton
+              $activeView={activeViewTypeName === 'Audience'}
+              variant="contained"
+              color="secondary"
+              onClick={() => changeView('Audience')}
+            >
               Åskådarvy
             </ViewButton>
-            <ViewButton variant="contained" color="secondary">
+            <ViewButton
+              $activeView={activeViewTypeName === 'Team'}
+              variant="contained"
+              color="secondary"
+              onClick={() => changeView('Team')}
+            >
               Deltagarvy
             </ViewButton>
           </ViewButtonGroup>
@@ -229,7 +251,7 @@ const PresentationEditorPage: React.FC = () => {
 
       <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
         <InnerContent>
-          <SlideDisplay editor />
+          <SlideDisplay variant="editor" activeViewTypeId={activeViewTypeId} />
         </InnerContent>
       </Content>
       <Menu
diff --git a/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx b/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
index 8d3e9c20b8c6818594269defa6c4490ad50eccf0..cfe10e380bdbb478ee061a1946c0efaf0d4c2ee6 100644
--- a/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
+++ b/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
@@ -54,14 +54,14 @@ const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
     await axios.delete(`/api/media/images/${backgroundImage?.id}`).catch(console.log)
     if (variant === 'competition') {
       await axios
-        .put(`/api/competitions/${competitionId}`, { background_image_id: -1 })
+        .put(`/api/competitions/${competitionId}`, { background_image_id: null })
         .then(() => {
           dispatch(getEditorCompetition(competitionId.toString()))
         })
         .catch(console.log)
     } else {
       await axios
-        .put(`/api/competitions/${competitionId}/slides/${activeSlideId}`, { background_image_id: -1 })
+        .put(`/api/competitions/${competitionId}/slides/${activeSlideId}`, { background_image_id: null })
         .then(() => {
           dispatch(getEditorCompetition(competitionId.toString()))
         })
diff --git a/client/src/pages/presentationEditor/components/SlideDisplay.tsx b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
index b4e4488a8132d25e4b993e5743b1160b4b380de0..aef4ca7c48d29bbfc47cac8f29b214ec6866f360 100644
--- a/client/src/pages/presentationEditor/components/SlideDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
@@ -1,4 +1,3 @@
-import { Button, Typography } from '@material-ui/core'
 import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
 import { getTypes } from '../../../actions/typesAction'
 import { useAppDispatch, useAppSelector } from '../../../hooks'
@@ -8,22 +7,23 @@ import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } fro
 
 type SlideDisplayProps = {
   //Prop to distinguish between editor and active competition
-  editor?: boolean | undefined
+  variant: 'editor' | 'presentation'
+  activeViewTypeId: number
 }
 
-const SlideDisplay = ({ editor }: SlideDisplayProps) => {
+const SlideDisplay = ({ variant, activeViewTypeId }: SlideDisplayProps) => {
   const components = useAppSelector((state) => {
-    if (editor)
+    if (variant === 'editor')
       return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.components
     return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide?.id)?.components
   })
   const competitionBackgroundImage = useAppSelector((state) => {
-    if (editor) return state.editor.competition.background_image
+    if (variant === 'editor') return state.editor.competition.background_image
     return state.presentation.competition.background_image
   })
 
   const slideBackgroundImage = useAppSelector((state) => {
-    if (editor)
+    if (variant === 'editor')
       return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)?.background_image
     return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.slide.id)
       ?.background_image
@@ -64,21 +64,29 @@ const SlideDisplay = ({ editor }: SlideDisplayProps) => {
             />
           )}
           {components &&
-            components.map((component) => {
-              if (editor)
+            components
+              .filter((component) => component.view_type_id === activeViewTypeId)
+              .map((component) => {
+                if (variant === 'editor')
+                  return (
+                    <RndComponent
+                      height={height}
+                      width={width}
+                      key={component.id}
+                      component={component}
+                      scale={scale}
+                    />
+                  )
                 return (
-                  <RndComponent height={height} width={width} key={component.id} component={component} scale={scale} />
+                  <PresentationComponent
+                    height={height}
+                    width={width}
+                    key={component.id}
+                    component={component}
+                    scale={scale}
+                  />
                 )
-              return (
-                <PresentationComponent
-                  height={height}
-                  width={width}
-                  key={component.id}
-                  component={component}
-                  scale={scale}
-                />
-              )
-            })}
+              })}
         </SlideEditorPaper>
       </SlideEditorContainerRatio>
     </SlideEditorContainer>
diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index 0d07a1796c4fa711cb2c0112a663fc22baf0c9a3..d48b4565712d043d52d3b1e93e3633ec6b927d38 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -25,6 +25,7 @@ const SlideSettings: React.FC = () => {
     // Gets the slide with id=activeSlideId from the database.
     state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId)
   )
+  const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
 
   return (
     <PanelContainer>
@@ -48,9 +49,13 @@ const SlideSettings: React.FC = () => {
         <MultipleChoiceAlternatives activeSlide={activeSlide} competitionId={competitionId} />
       )}
 
-      {activeSlide && <Texts activeSlide={activeSlide} competitionId={competitionId} />}
+      {activeSlide && (
+        <Texts activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
+      )}
 
-      {activeSlide && <Images activeSlide={activeSlide} competitionId={competitionId} />}
+      {activeSlide && (
+        <Images activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
+      )}
 
       <BackgroundImageSelect variant="slide" />
     </PanelContainer>
diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
index b03696f37987a83347f57fb81c6b25febcb84824..f6cda5760ec8aff5bce211a8caf7d2435a8d5c61 100644
--- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
@@ -20,6 +20,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
   const [content, setContent] = useState('')
   const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
+  const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
   const dispatch = useAppDispatch()
 
   useEffect(() => {
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx
index 01c52e61266580d8c39ca485e6930f508f85f4a7..49f41e7f87fa6c53dac59704540ca3c186ef708b 100644
--- a/client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Images.tsx
@@ -11,11 +11,12 @@ import { ImageComponent, Media } from '../../../../interfaces/ApiModels'
 import { useAppDispatch, useAppSelector } from '../../../../hooks'
 
 type ImagesProps = {
+  activeViewTypeId: number
   activeSlide: RichSlide
   competitionId: string
 }
 
-const Images = ({ activeSlide, competitionId }: ImagesProps) => {
+const Images = ({ activeViewTypeId, activeSlide, competitionId }: ImagesProps) => {
   const dispatch = useAppDispatch()
 
   const uploadFile = async (formData: FormData) => {
@@ -37,6 +38,7 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
       y: 0,
       media_id: media.id,
       type_id: 2,
+      view_type_id: activeViewTypeId,
     }
     await axios
       .post(`/api/competitions/${competitionId}/slides/${activeSlide?.id}/components`, imageData)
@@ -94,17 +96,19 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
         </Center>
       </ListItem>
       {images &&
-        images.map((image) => (
-          <div key={image.id}>
-            <ListItem divider button>
-              <ImportedImage src={`http://localhost:5000/static/images/thumbnail_${image.media?.filename}`} />
-              <Center>
-                <ListItemText primary={image.media?.filename} />
-              </Center>
-              <CloseIcon onClick={() => handleCloseimageClick(image)} />
-            </ListItem>
-          </div>
-        ))}
+        images
+          .filter((image) => image.view_type_id === activeViewTypeId)
+          .map((image) => (
+            <div key={image.id}>
+              <ListItem divider button>
+                <ImportedImage src={`http://localhost:5000/static/images/thumbnail_${image.media?.filename}`} />
+                <Center>
+                  <ListItemText primary={image.media?.filename} />
+                </Center>
+                <CloseIcon onClick={() => handleCloseimageClick(image)} />
+              </ListItem>
+            </div>
+          ))}
 
       <ListItem button style={{ padding: 0 }}>
         <HiddenInput accept="image/*" id="contained-button-file" multiple type="file" onChange={handleFileSelected} />
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
index 39cf09af3ca31b73f30c258570e28365117a4282..03656614e3076e6048984e70b7ddd0ae711a492d 100644
--- a/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
@@ -9,11 +9,12 @@ import axios from 'axios'
 import { getEditorCompetition } from '../../../../actions/editor'
 
 type TextsProps = {
+  activeViewTypeId: number
   activeSlide: RichSlide
   competitionId: string
 }
 
-const Texts = ({ activeSlide, competitionId }: TextsProps) => {
+const Texts = ({ activeViewTypeId, activeSlide, competitionId }: TextsProps) => {
   const texts = useAppSelector(
     (state) =>
       state.editor.competition.slides
@@ -29,6 +30,7 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => {
         text: 'Ny text',
         w: 315,
         h: 50,
+        view_type_id: activeViewTypeId,
       })
       dispatch(getEditorCompetition(competitionId))
     }
@@ -42,12 +44,14 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => {
         </Center>
       </ListItem>
       {texts &&
-        texts.map((text) => (
-          <TextCard elevation={4} key={text.id}>
-            <TextComponentEdit component={text} />
-            <Divider />
-          </TextCard>
-        ))}
+        texts
+          .filter((text) => text.view_type_id === activeViewTypeId)
+          .map((text) => (
+            <TextCard elevation={4} key={text.id}>
+              <TextComponentEdit component={text} />
+              <Divider />
+            </TextCard>
+          ))}
       <ListItem button onClick={handleAddText}>
         <Center>
           <AddButton variant="button">Lägg till text</AddButton>
diff --git a/client/src/pages/presentationEditor/styled.tsx b/client/src/pages/presentationEditor/styled.tsx
index d1f05d6bf18de2eccc6b486fb6e94701acce3b5c..f68eb166043c970cb5f6e69bc57b090790b1d607 100644
--- a/client/src/pages/presentationEditor/styled.tsx
+++ b/client/src/pages/presentationEditor/styled.tsx
@@ -7,8 +7,18 @@ export const ToolBarContainer = styled(Toolbar)`
   padding-left: 0;
 `
 
-export const ViewButton = styled(Button)`
+interface ViewButtonProps {
+  $activeView: boolean
+}
+
+export const ViewButton = styled(Button)<ViewButtonProps>`
+  margin-right: 8px;
+  background: ${(props) => (props.$activeView ? '#5a0017' : undefined)};
+`
+
+export const ViewButtonClicked = styled(Button)`
   margin-right: 8px;
+  background: #5a0017;
 `
 
 export const ViewButtonGroup = styled.div`
diff --git a/client/src/pages/views/AudienceViewPage.tsx b/client/src/pages/views/AudienceViewPage.tsx
index 8d58a364e6d4688da1b0dcff5290a60486250de1..d03f3367499b9820ad35d646feedda3821928dcc 100644
--- a/client/src/pages/views/AudienceViewPage.tsx
+++ b/client/src/pages/views/AudienceViewPage.tsx
@@ -1,16 +1,15 @@
+import { Typography } from '@material-ui/core'
 import React from 'react'
+import { useAppSelector } from '../../hooks'
 import SlideDisplay from '../presentationEditor/components/SlideDisplay'
-import PresentationComponent from './components/PresentationComponent'
-import mockedAxios from 'axios'
 
 const AudienceViewPage: React.FC = () => {
-  const res = {
-    data: {},
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Audience')?.id
+  if (activeViewTypeId) {
+    return <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />
   }
-  ;(mockedAxios.get as jest.Mock).mockImplementation(() => {
-    return Promise.resolve(res)
-  })
-  return <SlideDisplay />
+  return <Typography>Error: Åskådarvyn kunde inte laddas</Typography>
 }
 
 export default AudienceViewPage
diff --git a/client/src/pages/views/JudgeViewPage.tsx b/client/src/pages/views/JudgeViewPage.tsx
index 5a806d237f190d6483bc8265ec0eac61c58e9151..677a080d04ab34e8232120daae018b6d1a2b29c4 100644
--- a/client/src/pages/views/JudgeViewPage.tsx
+++ b/client/src/pages/views/JudgeViewPage.tsx
@@ -51,6 +51,8 @@ const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
   const history = useHistory()
   const dispatch = useAppDispatch()
   const [activeSlideIndex, setActiveSlideIndex] = useState<number>(0)
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Judge')?.id
   const teams = useAppSelector((state) => state.presentation.competition.teams)
   const slides = useAppSelector((state) => state.presentation.competition.slides)
   const currentQuestion = slides[activeSlideIndex]?.questions[0]
@@ -128,7 +130,7 @@ const JudgeViewPage = ({ competitionId, code }: JudgeViewPageProps) => {
       <div className={classes.toolbar} />
       <Content leftDrawerWidth={leftDrawerWidth} rightDrawerWidth={rightDrawerWidth}>
         <InnerContent>
-          <SlideDisplay />
+          {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
         </InnerContent>
       </Content>
     </div>
diff --git a/client/src/pages/views/PresenterViewPage.test.tsx b/client/src/pages/views/OperatorViewPage.test.tsx
similarity index 91%
rename from client/src/pages/views/PresenterViewPage.test.tsx
rename to client/src/pages/views/OperatorViewPage.test.tsx
index fd7b0a9692e08354330b3e4db98c046649c0d10a..e658fe3b386899b9ab2972243c5d4ceeefc6dec4 100644
--- a/client/src/pages/views/PresenterViewPage.test.tsx
+++ b/client/src/pages/views/OperatorViewPage.test.tsx
@@ -4,7 +4,7 @@ import React from 'react'
 import { Provider } from 'react-redux'
 import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
-import PresenterViewPage from './PresenterViewPage'
+import OperatorViewPage from './OperatorViewPage'
 
 it('renders presenter view page', () => {
   const compRes: any = {
@@ -36,7 +36,7 @@ it('renders presenter view page', () => {
   render(
     <BrowserRouter>
       <Provider store={store}>
-        <PresenterViewPage />
+        <OperatorViewPage />
       </Provider>
     </BrowserRouter>
   )
diff --git a/client/src/pages/views/PresenterViewPage.tsx b/client/src/pages/views/OperatorViewPage.tsx
similarity index 50%
rename from client/src/pages/views/PresenterViewPage.tsx
rename to client/src/pages/views/OperatorViewPage.tsx
index f221f5a9b6e612243f626e0fafd490ee02d619e8..e965a3ec9f60b77b1919525f75ba2a925a81e84b 100644
--- a/client/src/pages/views/PresenterViewPage.tsx
+++ b/client/src/pages/views/OperatorViewPage.tsx
@@ -7,6 +7,7 @@ import {
   DialogTitle,
   List,
   ListItem,
+  ListItemText,
   Popover,
   Tooltip,
   Typography,
@@ -14,11 +15,13 @@ import {
   useTheme,
 } from '@material-ui/core'
 import AssignmentIcon from '@material-ui/icons/Assignment'
+import FileCopyIcon from '@material-ui/icons/FileCopy'
+import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount'
 import BackspaceIcon from '@material-ui/icons/Backspace'
 import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
 import ChevronRightIcon from '@material-ui/icons/ChevronRight'
 import TimerIcon from '@material-ui/icons/Timer'
-import React, { useEffect } from 'react'
+import React, { useEffect, useState } from 'react'
 import { useHistory, useParams } from 'react-router-dom'
 import { getPresentationCompetition, setPresentationCode } from '../../actions/presentation'
 import { useAppDispatch, useAppSelector } from '../../hooks'
@@ -36,23 +39,39 @@ import SlideDisplay from '../presentationEditor/components/SlideDisplay'
 import PresentationComponent from './components/PresentationComponent'
 import Timer from './components/Timer'
 import {
-  PresenterButton,
-  PresenterContainer,
-  PresenterContent,
-  PresenterFooter,
-  PresenterHeader,
-  PresenterInnerContent,
+  OperatorButton,
+  OperatorContainer,
+  OperatorFooter,
+  OperatorHeader,
+  OperatorContent,
+  OperatorInnerContent,
   SlideCounter,
   ToolBarContainer,
 } from './styled'
 
 /**
+ *  Description:
+ *
  *  Presentation is an active competition
+ *
+ *
+ *  ===========================================
+ *  TODO:
+ *  - Instead of copying code for others to join the competition, copy URL.
+ *
+ *  - Make code popup less code by using .map instead
+ *
+ *  - Fix scoreboard
+ *
+ *  - When two userers are connected to the same Localhost:5000 and updates/starts/end competition it
+ *    creates a bug where the competition can't be started.
+ * ===========================================
  */
 
-const PresenterViewPage: React.FC = () => {
+const OperatorViewPage: React.FC = () => {
   // for dialog alert
   const [openAlert, setOpen] = React.useState(false)
+  const [openAlertCode, setOpenCode] = React.useState(true)
   const theme = useTheme()
   const fullScreen = useMediaQuery(theme.breakpoints.down('sm'))
   const teams = useAppSelector((state) => state.presentation.competition.teams)
@@ -61,22 +80,31 @@ const PresenterViewPage: React.FC = () => {
   const presentation = useAppSelector((state) => state.presentation)
   const history = useHistory()
   const dispatch = useAppDispatch()
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Operator')?.id
 
   useEffect(() => {
     dispatch(getPresentationCompetition(id))
     dispatch(setPresentationCode(code))
     socket_connect()
     socketSetSlide // Behövs denna?
-    setTimeout(startCompetition, 500) // Ghetto, wait for everything to load
+    setTimeout(startCompetition, 1000) // Ghetto, wait for everything to load
     // console.log(id)
   }, [])
 
+  window.onpopstate = () => {
+    //Handle browser back arrow
+    alert('Tävlingen avslutas för alla')
+    endCompetition()
+  }
+
   const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => {
     setAnchorEl(event.currentTarget)
   }
 
   const handleClose = () => {
     setOpen(false)
+    setOpenCode(false)
     setAnchorEl(null)
   }
 
@@ -90,6 +118,14 @@ const PresenterViewPage: React.FC = () => {
     setOpen(true)
   }
 
+  const handleOpenCodes = () => {
+    setOpenCode(true)
+  }
+
+  const handleCopy = () => {
+    console.log('copied code to clipboard')
+  }
+
   const endCompetition = () => {
     setOpen(false)
     socketEndPresentation()
@@ -98,12 +134,64 @@ const PresenterViewPage: React.FC = () => {
   }
 
   return (
-    <PresenterContainer>
-      <PresenterHeader>
+    <OperatorContainer>
+      <Dialog
+        fullScreen={fullScreen}
+        open={openAlertCode}
+        onClose={handleClose}
+        aria-labelledby="responsive-dialog-title"
+      >
+        <DialogTitle id="responsive-dialog-title">{'Koder för tävlingen'}</DialogTitle>
+        <DialogContent>
+          <ListItem>
+            <ListItemText primary={`Domare: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+          <ListItem>
+            <ListItemText primary={`Tävlande: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+          <ListItem>
+            <ListItemText primary={`Publik: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+        </DialogContent>
+        <DialogActions>
+          <Button autoFocus onClick={handleClose} color="primary">
+            Ok
+          </Button>
+        </DialogActions>
+      </Dialog>
+
+      <OperatorHeader>
         <Tooltip title="Avsluta tävling" arrow>
-          <PresenterButton onClick={handleVerifyExit} variant="contained" color="secondary">
+          <OperatorButton onClick={handleVerifyExit} variant="contained" color="secondary">
             <BackspaceIcon fontSize="large" />
-          </PresenterButton>
+          </OperatorButton>
         </Tooltip>
 
         <Dialog
@@ -133,67 +221,73 @@ const PresenterViewPage: React.FC = () => {
             {presentation.slide.order + 1} / {presentation.competition.slides.length}
           </Typography>
         </SlideCounter>
-      </PresenterHeader>
+      </OperatorHeader>
       <div style={{ height: 0, paddingTop: 120 }} />
-      <PresenterContent>
-        <PresenterInnerContent>
-          <SlideDisplay />
-        </PresenterInnerContent>
-      </PresenterContent>
+      <OperatorContent>
+        <OperatorInnerContent>
+          {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
+        </OperatorInnerContent>
+      </OperatorContent>
       <div style={{ height: 0, paddingTop: 140 }} />
-      <PresenterFooter>
+      <OperatorFooter>
         <ToolBarContainer>
-          <Tooltip title="Previous Slide" arrow>
-            <PresenterButton onClick={socketSetSlidePrev} variant="contained">
+          <Tooltip title="Föregående" arrow>
+            <OperatorButton onClick={socketSetSlidePrev} variant="contained">
               <ChevronLeftIcon fontSize="large" />
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
 
           {/* 
           // Manual start button
           <Tooltip title="Start Presentation" arrow>
-            <PresenterButton onClick={startCompetition} variant="contained">
+            <OperatorButton onClick={startCompetition} variant="contained">
               start
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
 
           
-          // This creates a join button, but presenter should not join others, others should join presenter
+          // This creates a join button, but Operator should not join others, others should join Operator
           <Tooltip title="Join Presentation" arrow>
-            <PresenterButton onClick={socketJoinPresentation} variant="contained">
+            <OperatorButton onClick={socketJoinPresentation} variant="contained">
               <GroupAddIcon fontSize="large" />
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
           
 
           // This creates another end button, it might not be needed since we already have one
           <Tooltip title="End Presentation" arrow>
-            <PresenterButton onClick={socketEndPresentation} variant="contained">
+            <OperatorButton onClick={socketEndPresentation} variant="contained">
               <CancelIcon fontSize="large" />
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
           */}
 
-          <Tooltip title="Start Timer" arrow>
-            <PresenterButton onClick={socketStartTimer} variant="contained">
+          <Tooltip title="Starta Timer" arrow>
+            <OperatorButton onClick={socketStartTimer} variant="contained">
               <TimerIcon fontSize="large" />
               <Timer></Timer>
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
 
-          <Tooltip title="Scoreboard" arrow>
-            <PresenterButton onClick={handleOpenPopover} variant="contained">
+          <Tooltip title="Ställning" arrow>
+            <OperatorButton onClick={handleOpenPopover} variant="contained">
               <AssignmentIcon fontSize="large" />
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
 
-          <Tooltip title="Next Slide" arrow>
-            <PresenterButton onClick={socketSetSlideNext} variant="contained">
+          <Tooltip title="Koder" arrow>
+            <OperatorButton onClick={handleOpenCodes} variant="contained">
+              <SupervisorAccountIcon fontSize="large" />
+            </OperatorButton>
+          </Tooltip>
+
+          <Tooltip title="Nästa" arrow>
+            <OperatorButton onClick={socketSetSlideNext} variant="contained">
               <ChevronRightIcon fontSize="large" />
-            </PresenterButton>
+            </OperatorButton>
           </Tooltip>
         </ToolBarContainer>
-      </PresenterFooter>
+      </OperatorFooter>
       <Popover
         open={Boolean(anchorEl)}
         anchorEl={anchorEl}
@@ -208,17 +302,16 @@ const PresenterViewPage: React.FC = () => {
         }}
       >
         <List>
-          {/**  TODO:
-           *    Fix scoreboard
-           */}
-          {teams && teams.map((team) => <ListItem key={team.id}>{team.name} score: 20</ListItem>)}
+          {teams &&
+            teams.map((team) => (
+              <ListItem key={team.id}>
+                {team.name} score: {team.question_answers}{' '}
+              </ListItem>
+            ))}
         </List>
       </Popover>
-    </PresenterContainer>
+    </OperatorContainer>
   )
 }
 
-export default PresenterViewPage
-function componentDidMount() {
-  throw new Error('Function not implemented.')
-}
+export default OperatorViewPage
diff --git a/client/src/pages/views/ParticipantViewPage.test.tsx b/client/src/pages/views/TeamViewPage.test.tsx
similarity index 85%
rename from client/src/pages/views/ParticipantViewPage.test.tsx
rename to client/src/pages/views/TeamViewPage.test.tsx
index e25ab6b9ec8f6309ce60812d296c37ffab3042e6..10574f7e51df7dabf9f07754e9d8595d1c489559 100644
--- a/client/src/pages/views/ParticipantViewPage.test.tsx
+++ b/client/src/pages/views/TeamViewPage.test.tsx
@@ -3,7 +3,7 @@ import React from 'react'
 import { Provider } from 'react-redux'
 import { BrowserRouter } from 'react-router-dom'
 import store from '../../store'
-import ParticipantViewPage from './ParticipantViewPage'
+import TeamViewPage from './TeamViewPage'
 import mockedAxios from 'axios'
 
 it('renders participant view page', () => {
@@ -16,7 +16,7 @@ it('renders participant view page', () => {
   render(
     <BrowserRouter>
       <Provider store={store}>
-        <ParticipantViewPage />
+        <TeamViewPage />
       </Provider>
     </BrowserRouter>
   )
diff --git a/client/src/pages/views/ParticipantViewPage.tsx b/client/src/pages/views/TeamViewPage.tsx
similarity index 59%
rename from client/src/pages/views/ParticipantViewPage.tsx
rename to client/src/pages/views/TeamViewPage.tsx
index ffee1ee11857e875314f847fbd3aca58d6bdd9db..32eef28b9f7acd350195571719ea0449aeb19610 100644
--- a/client/src/pages/views/ParticipantViewPage.tsx
+++ b/client/src/pages/views/TeamViewPage.tsx
@@ -2,26 +2,28 @@ import React, { useEffect } from 'react'
 import PresentationComponent from './components/PresentationComponent'
 import { useHistory } from 'react-router-dom'
 import SlideDisplay from '../presentationEditor/components/SlideDisplay'
-import { ParticipantContainer } from './styled'
+import { TeamContainer } from './styled'
 import { socketJoinPresentation, socket_connect } from '../../sockets'
 import { useAppSelector } from '../../hooks'
 
-const ParticipantViewPage: React.FC = () => {
+const TeamViewPage: React.FC = () => {
   const history = useHistory()
   const code = useAppSelector((state) => state.presentation.code)
+  const viewTypes = useAppSelector((state) => state.types.viewTypes)
+  const activeViewTypeId = viewTypes.find((viewType) => viewType.name === 'Team')?.id
   useEffect(() => {
     //hides the url so people can't sneak peak
-    history.push('participant')
+    history.push('team')
     if (code && code !== '') {
       socket_connect()
       socketJoinPresentation()
     }
   }, [])
   return (
-    <ParticipantContainer>
-      <SlideDisplay />
-    </ParticipantContainer>
+    <TeamContainer>
+      {activeViewTypeId && <SlideDisplay variant="presentation" activeViewTypeId={activeViewTypeId} />}
+    </TeamContainer>
   )
 }
 
-export default ParticipantViewPage
+export default TeamViewPage
diff --git a/client/src/pages/views/ViewSelectPage.tsx b/client/src/pages/views/ViewSelectPage.tsx
index 686ce4f1e2d7465a4d8e8ba3ac75d1dee75914ff..845ba21c3edc1e5f499f6aae817e195af4d9bc21 100644
--- a/client/src/pages/views/ViewSelectPage.tsx
+++ b/client/src/pages/views/ViewSelectPage.tsx
@@ -4,9 +4,9 @@ import { Link, useRouteMatch } from 'react-router-dom'
 import { ViewSelectButtonGroup, ViewSelectContainer } from './styled'
 import { useParams } from 'react-router-dom'
 import { CircularProgress, Typography } from '@material-ui/core'
-import ParticipantViewPage from './ParticipantViewPage'
+import TeamViewPage from './TeamViewPage'
 import axios from 'axios'
-import PresenterViewPage from './PresenterViewPage'
+import OperatorViewPage from './OperatorViewPage'
 import JudgeViewPage from './JudgeViewPage'
 import AudienceViewPage from './AudienceViewPage'
 import { useAppDispatch, useAppSelector } from '../../hooks'
@@ -29,7 +29,7 @@ const ViewSelectPage: React.FC = () => {
     if (competitionId) {
       switch (viewType) {
         case 'Team':
-          return <ParticipantViewPage />
+          return <TeamViewPage />
         case 'Judge':
           return <JudgeViewPage code={code} competitionId={competitionId} />
         case 'Audience':
diff --git a/client/src/pages/views/styled.tsx b/client/src/pages/views/styled.tsx
index 8f63892ade7928842c12d4dc73df988a505f4cea..9699902727b2352fec07b8781a9d8c43ad20663e 100644
--- a/client/src/pages/views/styled.tsx
+++ b/client/src/pages/views/styled.tsx
@@ -35,7 +35,7 @@ export const ViewSelectButtonGroup = styled.div`
   margin-right: auto;
 `
 
-export const PresenterHeader = styled.div`
+export const OperatorHeader = styled.div`
   display: flex;
   justify-content: space-between;
   height: 120px;
@@ -43,7 +43,7 @@ export const PresenterHeader = styled.div`
   position: absolute;
 `
 
-export const PresenterFooter = styled.div`
+export const OperatorFooter = styled.div`
   display: flex;
   justify-content: space-between;
   height: 140px;
@@ -52,7 +52,7 @@ export const PresenterFooter = styled.div`
   width: 100%;
 `
 
-export const PresenterButton = styled(Button)`
+export const OperatorButton = styled(Button)`
   width: 100px;
   height: 100px;
   margin-left: 16px;
@@ -66,7 +66,7 @@ export const SlideCounter = styled(Button)`
   margin-top: 16px;
 `
 
-export const PresenterContainer = styled.div`
+export const OperatorContainer = styled.div`
   display: flex;
   flex-direction: column;
   justify-content: space-between;
@@ -127,7 +127,7 @@ export const InnerContent = styled.div`
   max-width: calc(((100vh - 64px) / 9) * 16);
 `
 
-export const PresenterContent = styled.div`
+export const OperatorContent = styled.div`
   height: 100%;
   width: 100%;
   display: flex;
@@ -135,7 +135,7 @@ export const PresenterContent = styled.div`
   background-color: rgba(0, 0, 0, 0.08);
 `
 
-export const PresenterInnerContent = styled.div`
+export const OperatorInnerContent = styled.div`
   height: 100%;
   width: 100%;
   /* Makes sure width is not bigger than where a 16:9 display can fit 
@@ -143,7 +143,7 @@ export const PresenterInnerContent = styled.div`
   max-width: calc(((100vh - 260px) / 9) * 16);
 `
 
-export const ParticipantContainer = styled.div`
+export const TeamContainer = styled.div`
   max-width: calc((100vh / 9) * 16);
 `
 
diff --git a/client/src/reducers/editorReducer.ts b/client/src/reducers/editorReducer.ts
index 88e6e3687ea6f4348ebd5956e677ef26319e33c2..4f245d1d05638cc059cf8d26921642d8239c169a 100644
--- a/client/src/reducers/editorReducer.ts
+++ b/client/src/reducers/editorReducer.ts
@@ -5,6 +5,7 @@ import { RichCompetition } from '../interfaces/ApiRichModels'
 interface EditorState {
   competition: RichCompetition
   activeSlideId: number
+  activeViewTypeId: number
   loading: boolean
 }
 
@@ -19,6 +20,7 @@ const initialState: EditorState = {
     background_image: undefined,
   },
   activeSlideId: -1,
+  activeViewTypeId: -1,
   loading: true,
 }
 
@@ -35,6 +37,11 @@ export default function (state = initialState, action: AnyAction) {
         ...state,
         activeSlideId: action.payload as number,
       }
+    case Types.SET_EDITOR_VIEW_ID:
+      return {
+        ...state,
+        activeViewTypeId: action.payload as number,
+      }
     default:
       return state
   }
diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py
index 5eab3f829ae81e17ecc269d98f568eeacc45a68c..e5ec3d2ca1e1d9de6d4c5cca5ec1e3fba40f33ee 100644
--- a/server/app/apis/__init__.py
+++ b/server/app/apis/__init__.py
@@ -13,24 +13,62 @@ def validate_editor(db_item, *views):
         abort(http_codes.UNAUTHORIZED)
 
 
-def check_jwt(editor=False, *views):
-    def wrapper(fn):
-        @wraps(fn)
-        def decorator(*args, **kwargs):
+def _is_allowed(allowed, actual):
+    return actual and "*" in allowed or actual in allowed
+
+
+def _has_access(in_claim, in_route):
+    in_route = int(in_route) if in_route else None
+    return not in_route or in_claim and in_claim == in_route
+
+
+def protect_route(allowed_roles=None, allowed_views=None):
+    def wrapper(func):
+        def inner(*args, **kwargs):
             verify_jwt_in_request()
             claims = get_jwt_claims()
+
+            # Authorize request if roles has access to the route #
+
+            nonlocal allowed_roles
+            allowed_roles = allowed_roles or []
             role = claims.get("role")
+            if _is_allowed(allowed_roles, role):
+                return func(*args, **kwargs)
+
+            # Authorize request if view has access and is trying to access the
+            # competition its in. Also check team if client is a team.
+            # Allow request if route doesn't belong to any competition.
+
+            nonlocal allowed_views
+            allowed_views = allowed_views or []
             view = claims.get("view")
-            if role == "Admin":
-                return fn(*args, **kwargs)
-            elif editor and role == "Editor":
-                return fn(*args, **kwargs)
-            elif view in views:
-                return fn(*args, **kwargs)
-            else:
-                abort(http_codes.UNAUTHORIZED)
-
-        return decorator
+            if not _is_allowed(allowed_views, view):
+                abort(
+                    http_codes.UNAUTHORIZED,
+                    f"Client with view '{view}' is not allowed to access route with allowed views {allowed_views}.",
+                )
+
+            claim_competition_id = claims.get("competition_id")
+            route_competition_id = kwargs.get("competition_id")
+            if not _has_access(claim_competition_id, route_competition_id):
+                abort(
+                    http_codes.UNAUTHORIZED,
+                    f"Client in competition '{claim_competition_id}' is not allowed to access competition '{route_competition_id}'.",
+                )
+
+            if view == "Team":
+                claim_team_id = claims.get("team_id")
+                route_team_id = kwargs.get("team_id")
+                if not _has_access(claim_team_id, route_team_id):
+                    abort(
+                        http_codes.UNAUTHORIZED,
+                        f"Client in team '{claim_team_id}' is not allowed to access team '{route_team_id}'.",
+                    )
+
+            return func(*args, **kwargs)
+
+        return inner
 
     return wrapper
 
diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py
index 2adad553ddb34bbd43e1405223c7c45962d66e19..94f6d6b1725106a3cf5994570e4d3d5829ba2dbe 100644
--- a/server/app/apis/alternatives.py
+++ b/server/app/apis/alternatives.py
@@ -1,30 +1,35 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAlternativeDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionAlternativeDTO.api
 schema = QuestionAlternativeDTO.schema
 list_schema = QuestionAlternativeDTO.list_schema
 
-question_alternative_parser = reqparse.RequestParser()
-question_alternative_parser.add_argument("text", type=str, default=None, location="json")
-question_alternative_parser.add_argument("value", type=int, default=None, location="json")
+alternative_parser_add = reqparse.RequestParser()
+alternative_parser_add.add_argument("text", type=str, required=True, location="json")
+alternative_parser_add.add_argument("value", type=int, required=True, location="json")
+
+alternative_parser_edit = reqparse.RequestParser()
+alternative_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
+alternative_parser_edit.add_argument("value", type=int, default=sentinel, location="json")
 
 
 @api.route("")
 @api.param("competition_id, slide_id, question_id")
 class QuestionAlternativeList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, question_id):
         items = dbc.get.question_alternative_list(competition_id, slide_id, question_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id, question_id):
-        args = question_alternative_parser.parse_args(strict=True)
+        args = alternative_parser_add.parse_args(strict=True)
         item = dbc.add.question_alternative(**args, question_id=question_id)
         return item_response(schema.dump(item))
 
@@ -32,19 +37,19 @@ class QuestionAlternativeList(Resource):
 @api.route("/<alternative_id>")
 @api.param("competition_id, slide_id, question_id, alternative_id")
 class QuestionAlternatives(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, question_id, alternative_id):
         items = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         return item_response(schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id, alternative_id):
-        args = question_alternative_parser.parse_args(strict=True)
+        args = alternative_parser_edit.parse_args(strict=True)
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, question_id, alternative_id):
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         dbc.delete.default(item)
diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py
index 79a9c7a9080ae50e6353894ec6780987597c929f..3990e4e1d7b75fa35d13cc592906e4c4aa921024 100644
--- a/server/app/apis/answers.py
+++ b/server/app/apis/answers.py
@@ -1,35 +1,36 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAnswerDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionAnswerDTO.api
 schema = QuestionAnswerDTO.schema
 list_schema = QuestionAnswerDTO.list_schema
 
-question_answer_parser = reqparse.RequestParser()
-question_answer_parser.add_argument("answer", type=str, required=True, location="json")
-question_answer_parser.add_argument("score", type=int, required=True, location="json")
-question_answer_parser.add_argument("question_id", type=int, required=True, location="json")
+answer_parser_add = reqparse.RequestParser()
+answer_parser_add.add_argument("answer", type=str, required=True, location="json")
+answer_parser_add.add_argument("score", type=int, required=True, location="json")
+answer_parser_add.add_argument("question_id", type=int, required=True, location="json")
 
-question_answer_edit_parser = reqparse.RequestParser()
-question_answer_edit_parser.add_argument("answer", type=str, default=None, location="json")
-question_answer_edit_parser.add_argument("score", type=int, default=None, location="json")
+answer_parser_edit = reqparse.RequestParser()
+answer_parser_edit.add_argument("answer", type=str, default=sentinel, location="json")
+answer_parser_edit.add_argument("score", type=int, default=sentinel, location="json")
 
 
 @api.route("")
 @api.param("competition_id, team_id")
 class QuestionAnswerList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, team_id):
         items = dbc.get.question_answer_list(competition_id, team_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self, competition_id, team_id):
-        args = question_answer_parser.parse_args(strict=True)
+        args = answer_parser_add.parse_args(strict=True)
         item = dbc.add.question_answer(**args, team_id=team_id)
         return item_response(schema.dump(item))
 
@@ -37,13 +38,14 @@ class QuestionAnswerList(Resource):
 @api.route("/<answer_id>")
 @api.param("competition_id, team_id, answer_id")
 class QuestionAnswers(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, team_id, answer_id):
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
         return item_response(schema.dump(item))
 
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def put(self, competition_id, team_id, answer_id):
-        args = question_answer_edit_parser.parse_args(strict=True)
+        args = answer_parser_edit.parse_args(strict=True)
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py
index 37da9e98d4b5970cd82b47dd54ad940cc53b6736..d97eab2af78deb39f5b5efdcc43e063373cca7e9 100644
--- a/server/app/apis/auth.py
+++ b/server/app/apis/auth.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, text_response
+from app.apis import item_response, protect_route, text_response
 from app.core.codes import verify_code
 from app.core.dto import AuthDTO, CodeDTO
 from flask_jwt_extended import (
@@ -12,6 +12,8 @@ from flask_jwt_extended import (
 )
 from flask_restx import Resource
 from flask_restx import inputs, reqparse
+from datetime import timedelta
+from app.core import sockets
 
 api = AuthDTO.api
 schema = AuthDTO.schema
@@ -19,23 +21,27 @@ list_schema = AuthDTO.list_schema
 
 login_parser = reqparse.RequestParser()
 login_parser.add_argument("email", type=inputs.email(), required=True, location="json")
-login_parser.add_argument("password", required=True, location="json")
+login_parser.add_argument("password", type=str, required=True, location="json")
 
 create_user_parser = login_parser.copy()
 create_user_parser.add_argument("city_id", type=int, required=True, location="json")
 create_user_parser.add_argument("role_id", type=int, required=True, location="json")
 
 login_code_parser = reqparse.RequestParser()
-login_code_parser.add_argument("code", type=str, location="json")
+login_code_parser.add_argument("code", type=str, required=True, location="json")
 
 
 def get_user_claims(item_user):
     return {"role": item_user.role.name, "city_id": item_user.city_id}
 
 
+def get_code_claims(item_code):
+    return {"view": item_code.view_type.name, "competition_id": item_code.competition_id, "team_id": item_code.team_id}
+
+
 @api.route("/signup")
 class AuthSignup(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def post(self):
         args = create_user_parser.parse_args(strict=True)
         email = args.get("email")
@@ -50,7 +56,7 @@ class AuthSignup(Resource):
 @api.route("/delete/<ID>")
 @api.param("ID")
 class AuthDelete(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def delete(self, ID):
         item_user = dbc.get.user(ID)
 
@@ -86,24 +92,38 @@ class AuthLoginCode(Resource):
         code = args["code"]
 
         if not verify_code(code):
-            api.abort(codes.BAD_REQUEST, "Invalid code")
+            api.abort(codes.UNAUTHORIZED, "Invalid code")
 
         item_code = dbc.get.code_by_code(code)
-        return item_response(CodeDTO.schema.dump(item_code))
+
+        if item_code.competition_id not in sockets.presentations:
+            api.abort(codes.UNAUTHORIZED, "Competition not active")
+
+        access_token = create_access_token(
+            item_code.id, user_claims=get_code_claims(item_code), expires_delta=timedelta(hours=8)
+        )
+
+        response = {
+            "competition_id": item_code.competition_id,
+            "view_type_id": item_code.view_type_id,
+            "team_id": item_code.team_id,
+            "access_token": access_token,
+        }
+        return response
 
 
 @api.route("/logout")
 class AuthLogout(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self):
         jti = get_raw_jwt()["jti"]
         dbc.add.blacklist(jti)
-        return text_response("User logout")
+        return text_response("Logout")
 
 
 @api.route("/refresh")
 class AuthRefresh(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     @jwt_refresh_token_required
     def post(self):
         old_jti = get_raw_jwt()["jti"]
diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py
index 8a4105d24a77f07b04da1e1e2ed477746ea7a932..d07e17435aed31417254acfd83ed9315f1477ba3 100644
--- a/server/app/apis/codes.py
+++ b/server/app/apis/codes.py
@@ -1,5 +1,5 @@
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core import http_codes as codes
 from app.core.dto import CodeDTO
 from app.database.models import Code
@@ -13,7 +13,7 @@ list_schema = CodeDTO.list_schema
 @api.route("")
 @api.param("competition_id")
 class CodesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.code_list(competition_id)
         return list_response(list_schema.dump(items), len(items))
@@ -22,7 +22,7 @@ class CodesList(Resource):
 @api.route("/<code_id>")
 @api.param("competition_id, code_id")
 class CodesById(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, code_id):
         item = dbc.get.one(Code, code_id)
         item.code = dbc.utils.generate_unique_code()
diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py
index fded52ec97df7d72acaf531eb5f0033c25227669..c5ff37c9131a7a9ec4bc1f394a9e1f5b5727460e 100644
--- a/server/app/apis/competitions.py
+++ b/server/app/apis/competitions.py
@@ -1,37 +1,40 @@
 import time
 
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import CompetitionDTO
 from app.database.models import Competition
 from flask_restx import Resource
 from flask_restx import reqparse
-from app.core.parsers import search_parser
+from app.core.parsers import search_parser, sentinel
 
 api = CompetitionDTO.api
 schema = CompetitionDTO.schema
 rich_schema = CompetitionDTO.rich_schema
 list_schema = CompetitionDTO.list_schema
 
-competition_parser = reqparse.RequestParser()
-competition_parser.add_argument("name", type=str, location="json")
-competition_parser.add_argument("year", type=int, location="json")
-competition_parser.add_argument("city_id", type=int, location="json")
+competition_parser_add = reqparse.RequestParser()
+competition_parser_add.add_argument("name", type=str, required=True, location="json")
+competition_parser_add.add_argument("year", type=int, required=True, location="json")
+competition_parser_add.add_argument("city_id", type=int, required=True, location="json")
 
-competition_edit_parser = competition_parser.copy()
-competition_edit_parser.add_argument("background_image_id", default=None, type=int, location="json")
+competition_parser_edit = reqparse.RequestParser()
+competition_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+competition_parser_edit.add_argument("year", type=int, default=sentinel, location="json")
+competition_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json")
+competition_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json")
 
-competition_search_parser = search_parser.copy()
-competition_search_parser.add_argument("name", type=str, default=None, location="args")
-competition_search_parser.add_argument("year", type=int, default=None, location="args")
-competition_search_parser.add_argument("city_id", type=int, default=None, location="args")
+competition_parser_search = search_parser.copy()
+competition_parser_search.add_argument("name", type=str, default=sentinel, location="args")
+competition_parser_search.add_argument("year", type=int, default=sentinel, location="args")
+competition_parser_search.add_argument("city_id", type=int, default=sentinel, location="args")
 
 
 @api.route("")
 class CompetitionsList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self):
-        args = competition_parser.parse_args(strict=True)
+        args = competition_parser_add.parse_args(strict=True)
 
         # Add competition
         item = dbc.add.competition(**args)
@@ -44,20 +47,21 @@ class CompetitionsList(Resource):
 @api.route("/<competition_id>")
 @api.param("competition_id")
 class Competitions(Resource):
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id):
         item = dbc.get.competition(competition_id)
 
         return item_response(rich_schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id):
-        args = competition_edit_parser.parse_args(strict=True)
+        args = competition_parser_edit.parse_args(strict=True)
         item = dbc.get.one(Competition, competition_id)
         item = dbc.edit.default(item, **args)
 
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id):
         item = dbc.get.one(Competition, competition_id)
         dbc.delete.competition(item)
@@ -67,9 +71,9 @@ class Competitions(Resource):
 
 @api.route("/search")
 class CompetitionSearch(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
-        args = competition_search_parser.parse_args(strict=True)
+        args = competition_parser_search.parse_args(strict=True)
         items, total = dbc.search.competition(**args)
         return list_response(list_schema.dump(items), total)
 
@@ -77,7 +81,7 @@ class CompetitionSearch(Resource):
 @api.route("/<competition_id>/copy")
 @api.param("competition_id")
 class SlidesOrder(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         item_competition = dbc.get.competition(competition_id)
 
diff --git a/server/app/apis/components.py b/server/app/apis/components.py
index 8ab67b8cb8bd001c9641b6331325f328639b581e..32ab03c7be6e1c76934dd47a59917a6a93082a3c 100644
--- a/server/app/apis/components.py
+++ b/server/app/apis/components.py
@@ -1,46 +1,51 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import ComponentDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = ComponentDTO.api
 schema = ComponentDTO.schema
 list_schema = ComponentDTO.list_schema
 
+component_parser_add = reqparse.RequestParser()
+component_parser_add.add_argument("x", type=str, default=0, location="json")
+component_parser_add.add_argument("y", type=int, default=0, location="json")
+component_parser_add.add_argument("w", type=int, default=1, location="json")
+component_parser_add.add_argument("h", type=int, default=1, location="json")
+component_parser_add.add_argument("type_id", type=int, required=True, location="json")
+component_parser_add.add_argument("view_type_id", type=int, required=True, location="json")
+component_parser_add.add_argument("text", type=str, default=None, location="json")
+component_parser_add.add_argument("media_id", type=str, default=None, location="json")
 
-component_parser = reqparse.RequestParser()
-component_parser.add_argument("x", type=str, default=None, location="json")
-component_parser.add_argument("y", type=int, default=None, location="json")
-component_parser.add_argument("w", type=int, default=None, location="json")
-component_parser.add_argument("h", type=int, default=None, location="json")
-
-component_edit_parser = component_parser.copy()
-component_edit_parser.add_argument("text", type=str, location="json")
-component_edit_parser.add_argument("media_id", type=str, location="json")
-
-component_create_parser = component_edit_parser.copy()
-component_create_parser.add_argument("type_id", type=int, required=True, location="json")
+component_parser_edit = reqparse.RequestParser()
+component_parser_edit.add_argument("x", type=str, default=sentinel, location="json")
+component_parser_edit.add_argument("y", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("w", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("h", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
+component_parser_edit.add_argument("media_id", type=str, default=sentinel, location="json")
 
 
 @api.route("/<component_id>")
 @api.param("competition_id, slide_id, component_id")
 class ComponentByID(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id, component_id):
         item = dbc.get.component(competition_id, slide_id, component_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, component_id):
-        args = component_edit_parser.parse_args(strict=True)
+        args = component_parser_edit.parse_args(strict=True)
         item = dbc.get.component(competition_id, slide_id, component_id)
-        args_without_none = {key: value for key, value in args.items() if value is not None}
-        item = dbc.edit.default(item, **args_without_none)
+        args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel}
+        item = dbc.edit.default(item, **args_without_sentinel)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, component_id):
         item = dbc.get.component(competition_id, slide_id, component_id)
         dbc.delete.component(item)
@@ -50,13 +55,13 @@ class ComponentByID(Resource):
 @api.route("")
 @api.param("competition_id, slide_id")
 class ComponentList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, competition_id, slide_id):
         items = dbc.get.component_list(competition_id, slide_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
-        args = component_create_parser.parse_args()
+        args = component_parser_add.parse_args()
         item = dbc.add.component(slide_id=slide_id, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/media.py b/server/app/apis/media.py
index 5d89550ac19aaff78643a34ae0a3059c011aed23..49d20608840e320ef3d73d9df848748e6da33617 100644
--- a/server/app/apis/media.py
+++ b/server/app/apis/media.py
@@ -1,6 +1,6 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import MediaDTO
 from app.core.parsers import search_parser
 from app.database.models import Media
@@ -10,6 +10,7 @@ from flask_restx import Resource
 from flask_uploads import UploadNotAllowed
 from sqlalchemy import exc
 import app.core.files as files
+from app.core.parsers import sentinel
 
 api = MediaDTO.api
 image_set = MediaDTO.image_set
@@ -17,18 +18,18 @@ schema = MediaDTO.schema
 list_schema = MediaDTO.list_schema
 
 media_parser_search = search_parser.copy()
-media_parser_search.add_argument("filename", type=str, default=None, location="args")
+media_parser_search.add_argument("filename", type=str, default=sentinel, location="args")
 
 
 @api.route("/images")
 class ImageList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = media_parser_search.parse_args(strict=True)
         items, total = dbc.search.image(**args)
         return list_response(list_schema.dump(items), total)
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self):
         if "image" not in request.files:
             api.abort(codes.BAD_REQUEST, "Missing image in request.files")
@@ -48,12 +49,12 @@ class ImageList(Resource):
 @api.route("/images/<ID>")
 @api.param("ID")
 class ImageList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self, ID):
         item = dbc.get.one(Media, ID)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, ID):
         item = dbc.get.one(Media, ID)
         try:
diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py
index ab52362d46d026d8483c52f5c4d8384f32de755d..20a84e4c17c138b3a94ea6e3902e67154036cabc 100644
--- a/server/app/apis/misc.py
+++ b/server/app/apis/misc.py
@@ -1,5 +1,5 @@
 import app.database.controller as dbc
-from app.apis import check_jwt, list_response
+from app.apis import list_response, protect_route
 from app.core import http_codes
 from app.core.dto import MiscDTO
 from app.database.models import City, Competition, ComponentType, MediaType, QuestionType, Role, User, ViewType
@@ -23,6 +23,7 @@ name_parser.add_argument("name", type=str, required=True, location="json")
 
 @api.route("/types")
 class TypesList(Resource):
+    @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def get(self):
         result = {}
         result["media_types"] = media_type_schema.dump(dbc.get.all(MediaType))
@@ -34,7 +35,7 @@ class TypesList(Resource):
 
 @api.route("/roles")
 class RoleList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         items = dbc.get.all(Role)
         return list_response(role_schema.dump(items))
@@ -42,12 +43,12 @@ class RoleList(Resource):
 
 @api.route("/cities")
 class CitiesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def post(self):
         args = name_parser.parse_args(strict=True)
         dbc.add.city(args["name"])
@@ -58,7 +59,7 @@ class CitiesList(Resource):
 @api.route("/cities/<ID>")
 @api.param("ID")
 class Cities(Resource):
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
         item = dbc.get.one(City, ID)
         args = name_parser.parse_args(strict=True)
@@ -67,7 +68,7 @@ class Cities(Resource):
         items = dbc.get.all(City)
         return list_response(city_schema.dump(items))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def delete(self, ID):
         item = dbc.get.one(City, ID)
         dbc.delete.default(item)
@@ -77,7 +78,7 @@ class Cities(Resource):
 
 @api.route("/statistics")
 class Statistics(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         user_count = User.query.count()
         competition_count = Competition.query.count()
diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py
index aaefafc40608d14bf3a4e8a2a3b7fdfbda559f2c..b14849d224501e48f9cce8a108bfd24f29657e99 100644
--- a/server/app/apis/questions.py
+++ b/server/app/apis/questions.py
@@ -1,24 +1,30 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionDTO.api
 schema = QuestionDTO.schema
 list_schema = QuestionDTO.list_schema
 
-question_parser = reqparse.RequestParser()
-question_parser.add_argument("name", type=str, default=None, location="json")
-question_parser.add_argument("total_score", type=int, default=None, location="json")
-question_parser.add_argument("type_id", type=int, default=None, location="json")
+question_parser_add = reqparse.RequestParser()
+question_parser_add.add_argument("name", type=str, default=None, location="json")
+question_parser_add.add_argument("total_score", type=int, default=None, location="json")
+question_parser_add.add_argument("type_id", type=int, required=True, location="json")
+
+question_parser_edit = reqparse.RequestParser()
+question_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+question_parser_edit.add_argument("total_score", type=int, default=sentinel, location="json")
+question_parser_edit.add_argument("type_id", type=int, default=sentinel, location="json")
 
 
 @api.route("/questions")
 @api.param("competition_id")
 class QuestionList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.question_list_for_competition(competition_id)
         return list_response(list_schema.dump(items))
@@ -27,14 +33,14 @@ class QuestionList(Resource):
 @api.route("/slides/<slide_id>/questions")
 @api.param("competition_id, slide_id")
 class QuestionListForSlide(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id):
         items = dbc.get.question_list(competition_id, slide_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
-        args = question_parser.parse_args(strict=True)
+        args = question_parser_add.parse_args(strict=True)
         item = dbc.add.question(slide_id=slide_id, **args)
         return item_response(schema.dump(item))
 
@@ -42,21 +48,21 @@ class QuestionListForSlide(Resource):
 @api.route("/slides/<slide_id>/questions/<question_id>")
 @api.param("competition_id, slide_id, question_id")
 class QuestionById(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id, question_id):
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         return item_response(schema.dump(item_question))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id):
-        args = question_parser.parse_args(strict=True)
+        args = question_parser_edit.parse_args(strict=True)
 
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         item_question = dbc.edit.default(item_question, **args)
 
         return item_response(schema.dump(item_question))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id, question_id):
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         dbc.delete.question(item_question)
diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py
index da29633a794cdb1262d2c307b8c80a94c794830e..52ce4cce1d20fc277c3ee9ec46ea3566a51ec633 100644
--- a/server/app/apis/slides.py
+++ b/server/app/apis/slides.py
@@ -1,30 +1,31 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import SlideDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = SlideDTO.api
 schema = SlideDTO.schema
 list_schema = SlideDTO.list_schema
 
-slide_parser = reqparse.RequestParser()
-slide_parser.add_argument("order", type=int, default=None, location="json")
-slide_parser.add_argument("title", type=str, default=None, location="json")
-slide_parser.add_argument("timer", type=int, default=None, location="json")
-slide_parser.add_argument("background_image_id", default=None, type=int, location="json")
+slide_parser_edit = reqparse.RequestParser()
+slide_parser_edit.add_argument("order", type=int, default=sentinel, location="json")
+slide_parser_edit.add_argument("title", type=str, default=sentinel, location="json")
+slide_parser_edit.add_argument("timer", type=int, default=sentinel, location="json")
+slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json")
 
 
 @api.route("")
 @api.param("competition_id")
 class SlidesList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.slide_list(competition_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
         item_slide = dbc.add.slide(competition_id)
         return item_response(schema.dump(item_slide))
@@ -33,21 +34,21 @@ class SlidesList(Resource):
 @api.route("/<slide_id>")
 @api.param("competition_id,slide_id")
 class Slides(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
         return item_response(schema.dump(item_slide))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
-        args = slide_parser.parse_args(strict=True)
+        args = slide_parser_edit.parse_args(strict=True)
 
         item_slide = dbc.get.slide(competition_id, slide_id)
         item_slide = dbc.edit.default(item_slide, **args)
 
         return item_response(schema.dump(item_slide))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
 
@@ -58,9 +59,9 @@ class Slides(Resource):
 @api.route("/<slide_id>/order")
 @api.param("competition_id,slide_id")
 class SlideOrder(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
-        args = slide_parser.parse_args(strict=True)
+        args = slide_parser_edit.parse_args(strict=True)
         order = args.get("order")
 
         item_slide = dbc.get.slide(competition_id, slide_id)
@@ -87,7 +88,7 @@ class SlideOrder(Resource):
 @api.route("/<slide_id>/copy")
 @api.param("competition_id,slide_id")
 class SlideCopy(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
         item_slide = dbc.get.slide(competition_id, slide_id)
 
diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py
index 3fcb276c959c8880be30713bf08869d6b30b8dbb..71ca715d55d21de75f07db4241e5ef0bb14e1050 100644
--- a/server/app/apis/teams.py
+++ b/server/app/apis/teams.py
@@ -1,29 +1,32 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import TeamDTO
 from flask_restx import Resource, reqparse
-from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = TeamDTO.api
 schema = TeamDTO.schema
 list_schema = TeamDTO.list_schema
 
-team_parser = reqparse.RequestParser()
-team_parser.add_argument("name", type=str, location="json")
+team_parser_add = reqparse.RequestParser()
+team_parser_add.add_argument("name", type=str, required=True, location="json")
+
+team_parser_edit = reqparse.RequestParser()
+team_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
 
 
 @api.route("")
 @api.param("competition_id")
 class TeamsList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id):
         items = dbc.get.team_list(competition_id)
         return list_response(list_schema.dump(items))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
-        args = team_parser.parse_args(strict=True)
+        args = team_parser_add.parse_args(strict=True)
         item_team = dbc.add.team(args["name"], competition_id)
         return item_response(schema.dump(item_team))
 
@@ -31,21 +34,21 @@ class TeamsList(Resource):
 @api.route("/<team_id>")
 @api.param("competition_id,team_id")
 class Teams(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, competition_id, team_id):
         item = dbc.get.team(competition_id, team_id)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def delete(self, competition_id, team_id):
         item_team = dbc.get.team(competition_id, team_id)
 
         dbc.delete.team(item_team)
         return {}, codes.NO_CONTENT
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self, competition_id, team_id):
-        args = team_parser.parse_args(strict=True)
+        args = team_parser_edit.parse_args(strict=True)
         name = args.get("name")
 
         item_team = dbc.get.team(competition_id, team_id)
diff --git a/server/app/apis/users.py b/server/app/apis/users.py
index a5b2d58b01f9ce8931fd29cba3af9d1d8b825cc0..dc26ac5b64e9a4270215374de84f3c71cae66f9e 100644
--- a/server/app/apis/users.py
+++ b/server/app/apis/users.py
@@ -1,27 +1,27 @@
 import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import check_jwt, item_response, list_response
+from app.apis import item_response, list_response, protect_route
 from app.core.dto import UserDTO
 from flask_jwt_extended import get_jwt_identity
 from flask_restx import Resource
 from flask_restx import inputs, reqparse
-from app.core.parsers import search_parser
+from app.core.parsers import search_parser, sentinel
 
 api = UserDTO.api
 schema = UserDTO.schema
 list_schema = UserDTO.list_schema
 
-user_parser = reqparse.RequestParser()
-user_parser.add_argument("email", type=inputs.email(), location="json")
-user_parser.add_argument("name", type=str, location="json")
-user_parser.add_argument("city_id", type=int, location="json")
-user_parser.add_argument("role_id", type=int, location="json")
+user_parser_edit = reqparse.RequestParser()
+user_parser_edit.add_argument("email", type=inputs.email(), default=sentinel, location="json")
+user_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+user_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json")
+user_parser_edit.add_argument("role_id", type=int, default=sentinel, location="json")
 
 user_search_parser = search_parser.copy()
-user_search_parser.add_argument("name", type=str, default=None, location="args")
-user_search_parser.add_argument("email", type=str, default=None, location="args")
-user_search_parser.add_argument("city_id", type=int, default=None, location="args")
-user_search_parser.add_argument("role_id", type=int, default=None, location="args")
+user_search_parser.add_argument("name", type=str, default=sentinel, location="args")
+user_search_parser.add_argument("email", type=str, default=sentinel, location="args")
+user_search_parser.add_argument("city_id", type=int, default=sentinel, location="args")
+user_search_parser.add_argument("role_id", type=int, default=sentinel, location="args")
 
 
 def _edit_user(item_user, args):
@@ -40,14 +40,14 @@ def _edit_user(item_user, args):
 
 @api.route("")
 class UsersList(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         item = dbc.get.user(get_jwt_identity())
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def put(self):
-        args = user_parser.parse_args(strict=True)
+        args = user_parser_edit.parse_args(strict=True)
         item = dbc.get.user(get_jwt_identity())
         item = _edit_user(item, args)
         return item_response(schema.dump(item))
@@ -56,14 +56,14 @@ class UsersList(Resource):
 @api.route("/<ID>")
 @api.param("ID")
 class Users(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self, ID):
         item = dbc.get.user(ID)
         return item_response(schema.dump(item))
 
-    @check_jwt(editor=False)
+    @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
-        args = user_parser.parse_args(strict=True)
+        args = user_parser_edit.parse_args(strict=True)
         item = dbc.get.user(ID)
         item = _edit_user(item, args)
         return item_response(schema.dump(item))
@@ -71,7 +71,7 @@ class Users(Resource):
 
 @api.route("/search")
 class UserSearch(Resource):
-    @check_jwt(editor=True)
+    @protect_route(allowed_roles=["*"])
     def get(self):
         args = user_search_parser.parse_args(strict=True)
         items, total = dbc.search.user(**args)
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index c695dd230b208d95fff4eaba14f12c9732042376..160d67a0af7b58483202c00c1ec6b97097de0633 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -1,5 +1,22 @@
 from flask_restx import inputs, reqparse
 
+
+class Sentinel:
+    """
+    Sentinel is used as default argument to parsers if it isn't necessary to
+    supply a value. This is used instead of None so that None can be supplied
+    as value.
+    """
+
+    def __repr__(self):
+        return "Sentinel"
+
+    def __bool__(self):
+        return False
+
+
+sentinel = Sentinel()
+
 ###SEARCH####
 search_parser = reqparse.RequestParser()
 search_parser.add_argument("page", type=int, default=0, location="args")
diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py
index 0abe02526ef28041e7c0722fc5aa0b0bc5e7f55d..31e79e69a8c181a27bf0c9ee79f8b3ce62c19ecd 100644
--- a/server/app/core/schemas.py
+++ b/server/app/core/schemas.py
@@ -161,6 +161,7 @@ class ComponentSchema(BaseSchema):
     h = ma.auto_field()
     slide_id = ma.auto_field()
     type_id = ma.auto_field()
+    view_type_id = ma.auto_field()
 
     text = fields.fields.String()
     media = fields.Nested(MediaSchema, many=False)
diff --git a/server/app/core/sockets.py b/server/app/core/sockets.py
index 00b2ddf5cc9313aed0e3eded03dc05d6548a55f8..b62f82c9365818ac76faa0cd05e09bc795d0c071 100644
--- a/server/app/core/sockets.py
+++ b/server/app/core/sockets.py
@@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
 logger.propagate = False
 logger.setLevel(logging.INFO)
 
-formatter = logging.Formatter('[%(levelname)s] %(funcName)s: %(message)s')
+formatter = logging.Formatter("[%(levelname)s] %(funcName)s: %(message)s")
 stream_handler = logging.StreamHandler()
 stream_handler.setFormatter(formatter)
 logger.addHandler(stream_handler)
@@ -44,7 +44,9 @@ def start_presentation(data):
     competition_id = data["competition_id"]
 
     if competition_id in presentations:
-        logger.error(f"Client '{request.sid}' failed to start competition '{competition_id}', presentation already active")
+        logger.error(
+            f"Client '{request.sid}' failed to start competition '{competition_id}', presentation already active"
+        )
         return
 
     presentations[competition_id] = {
@@ -58,16 +60,21 @@ def start_presentation(data):
 
     logger.info(f"Client '{request.sid}' started competition '{competition_id}'")
 
+
 @sio.on("end_presentation")
 def end_presentation(data):
     competition_id = data["competition_id"]
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to end presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid not in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', client not in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to end presentation '{competition_id}', client not in presentation"
+        )
         return
 
     if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator":
@@ -96,11 +103,15 @@ def join_presentation(data):
     competition_id = item_code.competition_id
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to join presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', client already in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to join presentation '{competition_id}', client already in presentation"
+        )
         return
 
     # TODO: Write function in database controller to do this
@@ -120,21 +131,29 @@ def set_slide(data):
     slide_order = data["slide_order"]
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid not in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation"
+        )
         return
 
     if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator":
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator"
+        )
         return
 
     num_slides = db.session.query(Slide).filter(Slide.competition_id == competition_id).count()
 
     if not (0 <= slide_order < num_slides):
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', slide number {slide_order} does not exist")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', slide number {slide_order} does not exist"
+        )
         return
 
     presentations[competition_id]["slide"] = slide_order
@@ -151,15 +170,21 @@ def set_timer(data):
     timer = data["timer"]
 
     if competition_id not in presentations:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists"
+        )
         return
 
     if request.sid not in presentations[competition_id]["clients"]:
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation"
+        )
         return
 
     if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator":
-        logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator")
+        logger.error(
+            f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator"
+        )
         return
 
     # TODO: Save timer in presentation, maybe?
@@ -168,4 +193,3 @@ def set_timer(data):
     logger.debug(f"Emitting event 'set_timer' to room {competition_id} including self")
 
     logger.info(f"Client '{request.sid}' set timer '{timer}' in presentation '{competition_id}'")
-
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index a83f5b95e8988a3d0024ca6d2f3b9601426147cc..50321626d0164fb01ef61d11e642bb679e729ead 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -59,7 +59,7 @@ def db_add(item):
     return item
 
 
-def component(type_id, slide_id, x=0, y=0, w=0, h=0, **data):
+def component(type_id, slide_id, view_type_id, x=0, y=0, w=0, h=0, **data):
     """
     Adds a component to the slide at the specified coordinates with the
     provided size and data .
@@ -80,10 +80,10 @@ def component(type_id, slide_id, x=0, y=0, w=0, h=0, **data):
         h *= ratio
 
     if type_id == 1:
-        item = db_add(TextComponent(slide_id, type_id, x, y, w, h))
+        item = db_add(TextComponent(slide_id, type_id, view_type_id, x, y, w, h))
         item.text = data.get("text")
     elif type_id == 2:
-        item = db_add(ImageComponent(slide_id, type_id, x, y, w, h))
+        item = db_add(ImageComponent(slide_id, type_id, view_type_id, x, y, w, h))
         item.media_id = data.get("media_id")
     else:
         abort(codes.BAD_REQUEST, f"Invalid type_id{type_id}")
@@ -151,9 +151,13 @@ def competition(name, year, city_id):
 
     # Add code for Judge view
     code(2, item_competition.id)
+
     # Add code for Audience view
     code(3, item_competition.id)
 
+    # Add code for Operator view
+    code(4, item_competition.id)
+
     item_competition = utils.refresh(item_competition)
     return item_competition
 
diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py
index 7e9d28d02381dfe36e2a0c7792745618b595588e..1874b5dc548be39b1965fb2d4b041a957e26fe47 100644
--- a/server/app/database/controller/copy.py
+++ b/server/app/database/controller/copy.py
@@ -45,6 +45,7 @@ def _component(item_component, item_slide_new):
     add.component(
         item_component.type_id,
         item_slide_new.id,
+        item_component.view_type_id,
         item_component.x,
         item_component.y,
         item_component.w,
diff --git a/server/app/database/controller/edit.py b/server/app/database/controller/edit.py
index b1f5bc91e045e43c5f69618a685a39f86c7b081d..efb4909696cf2ae911a2451e1427b60b460f1153 100644
--- a/server/app/database/controller/edit.py
+++ b/server/app/database/controller/edit.py
@@ -3,6 +3,7 @@ This file contains functionality to get data from the database.
 """
 
 from app.core import db
+from app.core.parsers import sentinel
 
 
 def switch_order(item1, item2):
@@ -46,7 +47,7 @@ def default(item, **kwargs):
     for key, value in kwargs.items():
         if not hasattr(item, key):
             raise AttributeError(f"Item of type {type(item)} has no attribute '{key}'")
-        if value is not None:
+        if value is not sentinel:
             setattr(item, key, value)
     db.session.commit()
     db.session.refresh(item)
diff --git a/server/app/database/models.py b/server/app/database/models.py
index 3012938955f5c9e4fe1e285a0d95478ca7f7b8dd..c56aa3f0051ba6033e7376b0912cbf4f45928252 100644
--- a/server/app/database/models.py
+++ b/server/app/database/models.py
@@ -199,13 +199,14 @@ class Component(db.Model):
 
     __mapper_args__ = {"polymorphic_on": type_id}
 
-    def __init__(self, slide_id, type_id, x=0, y=0, w=1, h=1):
+    def __init__(self, slide_id, type_id, view_type_id, x=0, y=0, w=1, h=1):
         self.x = x
         self.y = y
         self.w = w
         self.h = h
         self.slide_id = slide_id
         self.type_id = type_id
+        self.view_type_id = view_type_id
 
 
 class TextComponent(Component):
@@ -230,6 +231,8 @@ class Code(db.Model):
     competition_id = db.Column(db.Integer, db.ForeignKey("competition.id"), nullable=False)
     team_id = db.Column(db.Integer, db.ForeignKey("team.id"), nullable=True)
 
+    view_type = db.relationship("ViewType", uselist=False)
+
     def __init__(self, code, view_type_id, competition_id=None, team_id=None):
         self.code = code
         self.view_type_id = view_type_id
@@ -240,7 +243,6 @@ class Code(db.Model):
 class ViewType(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     name = db.Column(db.String(STRING_SIZE), unique=True)
-    codes = db.relationship("Code", backref="view_type")
 
     def __init__(self, name):
         self.name = name
diff --git a/server/populate.py b/server/populate.py
index 096aa40907a9259b8db637b94bde185a919d8dd3..e0bb0bd94faf1b210069d7507d48e3f740157209 100644
--- a/server/populate.py
+++ b/server/populate.py
@@ -86,13 +86,13 @@ def _add_items():
                 y = random.randrange(1, 500)
                 w = random.randrange(150, 400)
                 h = random.randrange(150, 400)
-                dbc.add.component(1, item_slide.id, x, y, w, h,view_type_id=1, text=f"hej{k}")
+                dbc.add.component(1, item_slide.id, 1, x, y, w, h, text=f"hej{k}")
             for k in range(3):
                 x = random.randrange(1, 500)
                 y = random.randrange(1, 500)
                 w = random.randrange(150, 400)
                 h = random.randrange(150, 400)
-                dbc.add.component(1, item_slide.id, x, y, w, h,view_type_id=3, text=f"hej{k}")
+                dbc.add.component(1, item_slide.id, 3, x, y, w, h, text=f"hej{k}")
 
         # item_slide = dbc.add.slide(item_comp)
         # item_slide.title = f"Slide {len(item_comp.slides)}"
diff --git a/server/tests/test_app.py b/server/tests/test_app.py
index 199df387e878fdbbde759a80e6aa3d9ea486ea7d..d59428a6380b74abe8853c6c09c4df0bafc031e0 100644
--- a/server/tests/test_app.py
+++ b/server/tests/test_app.py
@@ -3,7 +3,9 @@ This file tests the api function calls.
 """
 
 import app.core.http_codes as codes
+from app.database.controller.add import competition
 from app.database.models import Slide
+from app.core import sockets
 
 from tests import app, client, db
 from tests.test_helpers import add_default_values, change_order_test, delete, get, post, put
@@ -342,7 +344,7 @@ def test_question_api(client):
     slide_order = 1
     response, body = get(client, f"/api/competitions/{CID}/questions", headers=headers)
     assert response.status_code == codes.OK
-    assert body["count"] == 1
+    assert body["count"] == 2
 
     # Get questions from another competition that should have some questions
     CID = 3
@@ -385,3 +387,67 @@ def test_question_api(client):
     response, _ = delete(client, f"/api/competitions/{CID}/slides/{NEW_slide_order}/questions/{QID}", headers=headers)
     assert response.status_code == codes.NOT_FOUND
     """
+
+
+def test_authorization(client):
+    add_default_values()
+
+    # Fake that competition 1 is active
+    sockets.presentations[1] = {}
+
+    #### TEAM ####
+    # Login in with team code
+    response, body = post(client, "/api/auth/login/code", {"code": "111111"})
+    assert response.status_code == codes.OK
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    competition_id = body["competition_id"]
+    team_id = body["team_id"]
+
+    # Get competition team is in
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to delete competition team is in
+    response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Try to get a different competition
+    response, body = get(client, f"/api/competitions/{competition_id+1}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Get own answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id}/answers", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to get another teams answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id+1}/answers", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    #### JUDGE ####
+    # Login in with judge code
+    response, body = post(client, "/api/auth/login/code", {"code": "222222"})
+    assert response.status_code == codes.OK
+    headers = {"Authorization": "Bearer " + body["access_token"]}
+
+    competition_id = body["competition_id"]
+
+    # Get competition judge is in
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Try to delete competition judge is in
+    response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Try to get a different competition
+    response, body = get(client, f"/api/competitions/{competition_id+1}", headers=headers)
+    assert response.status_code == codes.UNAUTHORIZED
+
+    # Get team answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id}/answers", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Also get antoher teams answers
+    response, body = get(client, f"/api/competitions/{competition_id}/teams/{team_id+1}/answers", headers=headers)
+    assert response.status_code == codes.OK
\ No newline at end of file
diff --git a/server/tests/test_helpers.py b/server/tests/test_helpers.py
index b5f1e54e136b759d9924a534acf2f37e6f7b08cd..f55aa68251a5a3b9ea73411c959939acc93f1bc6 100644
--- a/server/tests/test_helpers.py
+++ b/server/tests/test_helpers.py
@@ -3,14 +3,14 @@ import json
 import app.core.http_codes as codes
 import app.database.controller as dbc
 from app.core import db
-from app.database.models import City, Role
+from app.database.models import City, Code, Role
 
 
 def add_default_values():
     media_types = ["Image", "Video"]
     question_types = ["Boolean", "Multiple", "Text"]
     component_types = ["Text", "Image"]
-    view_types = ["Team", "Judge", "Audience"]
+    view_types = ["Team", "Judge", "Audience", "Operator"]
 
     roles = ["Admin", "Editor"]
     cities = ["Linköping", "Testköping"]
@@ -40,6 +40,20 @@ def add_default_values():
 
     # Add competitions
     item_competition = dbc.add.competition("Tom tävling", 2012, item_city.id)
+
+    item_question = dbc.add.question("hej", 5, 1, item_competition.slides[0].id)
+
+    item_team1 = dbc.add.team("Hej lag 3", item_competition.id)
+    item_team2 = dbc.add.team("Hej lag 4", item_competition.id)
+
+    db.session.add(Code("111111", 1, item_competition.id, item_team1.id))  # Team
+    db.session.add(Code("222222", 2, item_competition.id))  # Judge
+
+    dbc.add.QuestionAnswer("hej", 5, item_question.id, item_team1)
+    dbc.add.QuestionAnswer("då", 5, item_question.id, item_team2)
+
+    db.session.commit()
+
     for j in range(2):
         item_comp = dbc.add.competition(f"Tävling {j}", 2012, item_city.id)
         # Add two more slides to competition
@@ -59,7 +73,7 @@ def add_default_values():
             # dbc.add.question(name=f"Q{i+1}", total_score=i + 1, type_id=1, slide_id=item_slide.id)
 
             # Add text component
-            dbc.add.component(1, item_slide.id, i, 2 * i, 3 * i, 4 * i, text="Text")
+            dbc.add.component(1, item_slide.id, 1, i, 2 * i, 3 * i, 4 * i, text="Text")
 
 
 def get_body(response):