Merge pull request 'Add shortcut links to edit page' (#883) from shortcut-links into main
Reviewed-on: https://git.joinplu.me/Plume/Plume/pulls/883
This commit is contained in:
		
						commit
						76f7b5e7ac
					
				@ -21,6 +21,7 @@ executors:
 | 
				
			|||||||
      RUST_TEST_THREADS: 1
 | 
					      RUST_TEST_THREADS: 1
 | 
				
			||||||
      FEATURES: <<#parameters.postgres>>postgres<</ parameters.postgres>><<^parameters.postgres>>sqlite<</parameters.postgres>>
 | 
					      FEATURES: <<#parameters.postgres>>postgres<</ parameters.postgres>><<^parameters.postgres>>sqlite<</parameters.postgres>>
 | 
				
			||||||
      DATABASE_URL: <<#parameters.postgres>>postgres://postgres@localhost/plume<</parameters.postgres>><<^parameters.postgres>>plume.sqlite<</parameters.postgres>>
 | 
					      DATABASE_URL: <<#parameters.postgres>>postgres://postgres@localhost/plume<</parameters.postgres>><<^parameters.postgres>>plume.sqlite<</parameters.postgres>>
 | 
				
			||||||
 | 
					      ROCKET_SECRET_KEY: VN5xV1DN7XdpATadOCYcuGeR/dV0hHfgx9mx9TarLdM=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
commands:
 | 
					commands:
 | 
				
			||||||
@ -143,12 +144,14 @@ jobs:
 | 
				
			|||||||
        cache: <<#parameters.postgres>>postgres<</ parameters.postgres>><<^parameters.postgres>>sqlite<</parameters.postgres>>
 | 
					        cache: <<#parameters.postgres>>postgres<</ parameters.postgres>><<^parameters.postgres>>sqlite<</parameters.postgres>>
 | 
				
			||||||
    - run_with_coverage:
 | 
					    - run_with_coverage:
 | 
				
			||||||
        cmd: |
 | 
					        cmd: |
 | 
				
			||||||
          cargo run -p plume-cli --no-default-features --features=${FEATURES} -- migration run
 | 
					          cargo build -p plume-cli --no-default-features --features=${FEATURES} -j1
 | 
				
			||||||
 | 
					          ./target/debug/plm migration run
 | 
				
			||||||
 | 
					          ./target/debug/plm search init
 | 
				
			||||||
          cmd="cargo test --all --exclude plume-front --exclude plume-macro --no-run --no-default-features --features=${FEATURES} -j"
 | 
					          cmd="cargo test --all --exclude plume-front --exclude plume-macro --no-run --no-default-features --features=${FEATURES} -j"
 | 
				
			||||||
          for i in 36 4 2 1 1; do
 | 
					          for i in 36 4 2 1 1; do
 | 
				
			||||||
              $cmd $i && break
 | 
					              $cmd $i && break
 | 
				
			||||||
          done
 | 
					          done
 | 
				
			||||||
          cargo test --all --exclude plume-front --exclude plume-macro --no-default-features --features="${FEATURES}" -j1 -- --test-threads=1
 | 
					          cargo test --all --exclude plume-front --exclude plume-macro --no-default-features --features="${FEATURES}" -j1
 | 
				
			||||||
    - upload_coverage:
 | 
					    - upload_coverage:
 | 
				
			||||||
        type: unit
 | 
					        type: unit
 | 
				
			||||||
    - cache:
 | 
					    - cache:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,3 @@
 | 
				
			|||||||
* {
 | 
					* {
 | 
				
			||||||
	font-family: monospace;
 | 
					  font-family: monospace;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -64,37 +64,37 @@ main header.article {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
main .article-info {
 | 
					main .article-info {
 | 
				
			||||||
 	margin: 0 auto 3em;
 | 
					  margin: 0 auto 3em;
 | 
				
			||||||
 	font-size: 0.95em;
 | 
					  font-size: 0.95em;
 | 
				
			||||||
 	font-weight: 400;
 | 
					  font-weight: 400;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	.author, .author a {
 | 
					  .author, .author a {
 | 
				
			||||||
 	  font-weight: 600;
 | 
					    font-weight: 600;
 | 
				
			||||||
 	}
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* The article itself */
 | 
					/* The article itself */
 | 
				
			||||||
main article {
 | 
					main article {
 | 
				
			||||||
  max-width: $article-width;
 | 
					  max-width: $article-width;
 | 
				
			||||||
 	margin: 2.5em auto;
 | 
					  margin: 2.5em auto;
 | 
				
			||||||
 	font-family: $lora;
 | 
					  font-family: $lora;
 | 
				
			||||||
 	font-size: 1.2em;
 | 
					  font-size: 1.2em;
 | 
				
			||||||
 	line-height: 1.7;
 | 
					  line-height: 1.7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	a:hover {
 | 
					  a:hover {
 | 
				
			||||||
 	  text-decoration: underline;
 | 
					    text-decoration: underline;
 | 
				
			||||||
 	}
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	img {
 | 
					  img {
 | 
				
			||||||
   	display: block;
 | 
					    display: block;
 | 
				
			||||||
   	margin: 3em auto;
 | 
					    margin: 3em auto;
 | 
				
			||||||
   	max-width: 100%;
 | 
					    max-width: 100%;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pre {
 | 
					  pre {
 | 
				
			||||||
   	padding: 1em;
 | 
					    padding: 1em;
 | 
				
			||||||
   	background: $gray;
 | 
					    background: $gray;
 | 
				
			||||||
   	overflow: auto;
 | 
					    overflow: auto;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  blockquote {
 | 
					  blockquote {
 | 
				
			||||||
@ -126,7 +126,7 @@ main .article-meta {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  > p {
 | 
					  > p {
 | 
				
			||||||
    margin: 2em $horizontal-margin;
 | 
					    margin: 2em $horizontal-margin;
 | 
				
			||||||
 	  font-size: 0.9em;
 | 
					    font-size: 0.9em;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Article Tags */
 | 
					  /* Article Tags */
 | 
				
			||||||
@ -157,15 +157,15 @@ main .article-meta {
 | 
				
			|||||||
  /* Likes & Boosts */
 | 
					  /* Likes & Boosts */
 | 
				
			||||||
  .actions {
 | 
					  .actions {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
   	flex-direction: row;
 | 
					    flex-direction: row;
 | 
				
			||||||
   	justify-content: space-around;
 | 
					    justify-content: space-around;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .likes, .reshares {
 | 
					  .likes, .reshares {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
   	flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
   	align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
   	padding: 0.5em 0;
 | 
					    padding: 0.5em 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    p {
 | 
					    p {
 | 
				
			||||||
      font-size: 1.5em;
 | 
					      font-size: 1.5em;
 | 
				
			||||||
@ -175,34 +175,34 @@ main .article-meta {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    .action {
 | 
					    .action {
 | 
				
			||||||
      display: flex;
 | 
					      display: flex;
 | 
				
			||||||
     	flex-direction: column;
 | 
					      flex-direction: column;
 | 
				
			||||||
     	align-items: center;
 | 
					      align-items: center;
 | 
				
			||||||
     	justify-content: center;
 | 
					      justify-content: center;
 | 
				
			||||||
     	margin: 0;
 | 
					      margin: 0;
 | 
				
			||||||
     	padding: 0;
 | 
					      padding: 0;
 | 
				
			||||||
     	background: none;
 | 
					      background: none;
 | 
				
			||||||
     	color: $text-color;
 | 
					      color: $text-color;
 | 
				
			||||||
     	border: none;
 | 
					      border: none;
 | 
				
			||||||
     	font-size: 1.1em;
 | 
					      font-size: 1.1em;
 | 
				
			||||||
      cursor: pointer;
 | 
					      cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     	svg.feather {
 | 
					      svg.feather {
 | 
				
			||||||
       	transition: background 0.1s ease-in;
 | 
					        transition: background 0.1s ease-in;
 | 
				
			||||||
       	display: flex;
 | 
					        display: flex;
 | 
				
			||||||
       	align-items: center;
 | 
					        align-items: center;
 | 
				
			||||||
       	justify-content: center;
 | 
					        justify-content: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       	margin: 0.5em 0;
 | 
					        margin: 0.5em 0;
 | 
				
			||||||
       	width: 2.5em;
 | 
					        width: 2.5em;
 | 
				
			||||||
       	height: 2.5em;
 | 
					        height: 2.5em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       	border-radius: 50%;
 | 
					        border-radius: 50%;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &.reshared, &.liked {
 | 
					      &.reshared, &.liked {
 | 
				
			||||||
        svg.feather {
 | 
					        svg.feather {
 | 
				
			||||||
         	color: $background;
 | 
					          color: $background;
 | 
				
			||||||
         	font-weight: 900;
 | 
					          font-weight: 900;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -213,14 +213,14 @@ main .article-meta {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    .action svg.feather {
 | 
					    .action svg.feather {
 | 
				
			||||||
      padding: 0.7em;
 | 
					      padding: 0.7em;
 | 
				
			||||||
     	box-sizing: border-box;
 | 
					      box-sizing: border-box;
 | 
				
			||||||
     	color: $red;
 | 
					      color: $red;
 | 
				
			||||||
     	fill: none;
 | 
					      fill: none;
 | 
				
			||||||
     	border: solid $red thin;
 | 
					      border: solid $red thin;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .action:hover svg.feather {
 | 
					    .action:hover svg.feather {
 | 
				
			||||||
     	background: transparentize($red, 0.85);
 | 
					      background: transparentize($red, 0.85);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .action.liked svg.feather {
 | 
					    .action.liked svg.feather {
 | 
				
			||||||
@ -238,22 +238,22 @@ main .article-meta {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    .action svg.feather {
 | 
					    .action svg.feather {
 | 
				
			||||||
      padding: 0.7em;
 | 
					      padding: 0.7em;
 | 
				
			||||||
     	box-sizing: border-box;
 | 
					      box-sizing: border-box;
 | 
				
			||||||
     	color: $primary;
 | 
					      color: $primary;
 | 
				
			||||||
     	border: solid $primary thin;
 | 
					      border: solid $primary thin;
 | 
				
			||||||
     	font-weight: 600;
 | 
					      font-weight: 600;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .action:hover svg.feather {
 | 
					    .action:hover svg.feather {
 | 
				
			||||||
     	background: transparentize($primary, 0.85);
 | 
					      background: transparentize($primary, 0.85);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .action.reshared svg.feather {
 | 
					    .action.reshared svg.feather {
 | 
				
			||||||
      background: $primary;
 | 
					      background: $primary;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    .action.reshared:hover svg.feather {
 | 
					    .action.reshared:hover svg.feather {
 | 
				
			||||||
     	background: transparentize($primary, 0.75)
 | 
					      background: transparentize($primary, 0.75)
 | 
				
			||||||
     	color: $primary;
 | 
					      color: $primary;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -262,9 +262,9 @@ main .article-meta {
 | 
				
			|||||||
    margin: 0 $horizontal-margin;
 | 
					    margin: 0 $horizontal-margin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    h2 {
 | 
					    h2 {
 | 
				
			||||||
     	color: $primary;
 | 
					      color: $primary;
 | 
				
			||||||
     	font-size: 1.5em;
 | 
					      font-size: 1.5em;
 | 
				
			||||||
     	font-weight: 600;
 | 
					      font-weight: 600;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    summary {
 | 
					    summary {
 | 
				
			||||||
@ -279,16 +279,16 @@ main .article-meta {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Respond & delete comment buttons
 | 
					    // Respond & delete comment buttons
 | 
				
			||||||
    a.button, form.inline, form.inline input {
 | 
					    a.button, form.inline, form.inline input {
 | 
				
			||||||
     	padding: 0;
 | 
					      padding: 0;
 | 
				
			||||||
     	background: none;
 | 
					      background: none;
 | 
				
			||||||
     	color: $text-color;
 | 
					      color: $text-color;
 | 
				
			||||||
     	margin-right: 2em;
 | 
					      margin-right: 2em;
 | 
				
			||||||
     	font-family: $route159;
 | 
					      font-family: $route159;
 | 
				
			||||||
      font-weight: normal;
 | 
					      font-weight: normal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     	&::before {
 | 
					      &::before {
 | 
				
			||||||
     	  color: $primary;
 | 
					        color: $primary;
 | 
				
			||||||
     	  padding-right: 0.5em;
 | 
					        padding-right: 0.5em;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      &:hover { color: $primary; }
 | 
					      &:hover { color: $primary; }
 | 
				
			||||||
@ -296,8 +296,8 @@ main .article-meta {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    .comment {
 | 
					    .comment {
 | 
				
			||||||
      margin: 1em 0;
 | 
					      margin: 1em 0;
 | 
				
			||||||
     	font-size: 1em;
 | 
					      font-size: 1em;
 | 
				
			||||||
     	border: none;
 | 
					      border: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      .content {
 | 
					      .content {
 | 
				
			||||||
        background: $gray;
 | 
					        background: $gray;
 | 
				
			||||||
@ -328,36 +328,36 @@ main .article-meta {
 | 
				
			|||||||
        color: transparentize($text-color, 0.6);
 | 
					        color: transparentize($text-color, 0.6);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     	.author {
 | 
					      .author {
 | 
				
			||||||
     	  display: flex;
 | 
					        display: flex;
 | 
				
			||||||
       	flex-direction: row;
 | 
					        flex-direction: row;
 | 
				
			||||||
       	align-items: center;
 | 
					        align-items: center;
 | 
				
			||||||
       	align-content: center;
 | 
					        align-content: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       	* {
 | 
					        * {
 | 
				
			||||||
       	  transition: all 0.1s ease-in;
 | 
					          transition: all 0.1s ease-in;
 | 
				
			||||||
       	}
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       	.display-name {
 | 
					        .display-name {
 | 
				
			||||||
         	color: $text-color;
 | 
					          color: $text-color;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        &:hover {
 | 
					        &:hover {
 | 
				
			||||||
          .display-name { color: $primary; }
 | 
					          .display-name { color: $primary; }
 | 
				
			||||||
          small { opacity: 1; }
 | 
					          small { opacity: 1; }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
     	}
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      & > .comment {
 | 
					      & > .comment {
 | 
				
			||||||
          padding-left: 2em;
 | 
					          padding-left: 2em;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     	.text {
 | 
					      .text {
 | 
				
			||||||
       	padding: 1.25em 0;
 | 
					        padding: 1.25em 0;
 | 
				
			||||||
       	font-family: $lora;
 | 
					        font-family: $lora;
 | 
				
			||||||
       	font-size: 1.1em;
 | 
					        font-size: 1.1em;
 | 
				
			||||||
       	line-height: 1.4;
 | 
					        line-height: 1.4;
 | 
				
			||||||
       	text-align: left;
 | 
					        text-align: left;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +1,27 @@
 | 
				
			|||||||
label {
 | 
					label {
 | 
				
			||||||
 	display: block;
 | 
					  display: block;
 | 
				
			||||||
 	margin: 2em auto .5em;
 | 
					  margin: 2em auto .5em;
 | 
				
			||||||
 	font-size: 1.2em;
 | 
					  font-size: 1.2em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
input, textarea, select {
 | 
					input, textarea, select {
 | 
				
			||||||
 	transition: all 0.1s ease-in;
 | 
					  transition: all 0.1s ease-in;
 | 
				
			||||||
 	display: block;
 | 
					  display: block;
 | 
				
			||||||
 	width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
 	margin: auto;
 | 
					  margin: auto;
 | 
				
			||||||
 	padding: 1em;
 | 
					  padding: 1em;
 | 
				
			||||||
 	box-sizing: border-box;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
  -webkit-appearance: textarea;
 | 
					  -webkit-appearance: textarea;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	background: $form-input-background;
 | 
					  background: $form-input-background;
 | 
				
			||||||
 	color: $text-color;
 | 
					  color: $text-color;
 | 
				
			||||||
 	border: solid $form-input-border thin;
 | 
					  border: solid $form-input-border thin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	font-size: 1.2em;
 | 
					  font-size: 1.2em;
 | 
				
			||||||
 	font-weight: 400;
 | 
					  font-weight: 400;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	&:focus {
 | 
					  &:focus {
 | 
				
			||||||
 	  border-color: $primary;
 | 
					    border-color: $primary;
 | 
				
			||||||
 	}
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
form input[type="submit"] {
 | 
					form input[type="submit"] {
 | 
				
			||||||
 margin: 2em auto;
 | 
					 margin: 2em auto;
 | 
				
			||||||
@ -29,18 +29,18 @@ form input[type="submit"] {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
textarea {
 | 
					textarea {
 | 
				
			||||||
 	resize: vertical;
 | 
					  resize: vertical;
 | 
				
			||||||
  overflow-y: scroll;
 | 
					  overflow-y: scroll;
 | 
				
			||||||
 	font-family: $lora;
 | 
					  font-family: $lora;
 | 
				
			||||||
 	font-size: 1.1em;
 | 
					  font-size: 1.1em;
 | 
				
			||||||
 	line-height: 1.5;
 | 
					  line-height: 1.5;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
input[type="checkbox"] {
 | 
					input[type="checkbox"] {
 | 
				
			||||||
 	display: inline;
 | 
					  display: inline;
 | 
				
			||||||
 	margin: initial;
 | 
					  margin: initial;
 | 
				
			||||||
 	min-width: initial;
 | 
					  min-width: initial;
 | 
				
			||||||
 	width: initial;
 | 
					  width: initial;
 | 
				
			||||||
  -webkit-appearance: checkbox;
 | 
					  -webkit-appearance: checkbox;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -71,31 +71,31 @@ form.inline {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.button, .button:visited, input[type="submit"], input[type="submit"].button {
 | 
					.button, .button:visited, input[type="submit"], input[type="submit"].button {
 | 
				
			||||||
 	transition: all 0.1s ease-in;
 | 
					  transition: all 0.1s ease-in;
 | 
				
			||||||
 	display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
  -webkit-appearance: none;
 | 
					  -webkit-appearance: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	margin: 0.5em auto;
 | 
					  margin: 0.5em auto;
 | 
				
			||||||
 	padding: 0.75em 1em;
 | 
					  padding: 0.75em 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	background: $primary;
 | 
					  background: $primary;
 | 
				
			||||||
 	color: $primary-text-color;
 | 
					  color: $primary-text-color;
 | 
				
			||||||
  font-weight: bold;
 | 
					  font-weight: bold;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	&:hover {
 | 
					  &:hover {
 | 
				
			||||||
 	  background: transparentize($primary, 0.1);
 | 
					    background: transparentize($primary, 0.1);
 | 
				
			||||||
 	}
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	&.destructive {
 | 
					  &.destructive {
 | 
				
			||||||
 	  background: $red;
 | 
					    background: $red;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:hover {
 | 
					    &:hover {
 | 
				
			||||||
      background: transparentize($red, 0.1);
 | 
					      background: transparentize($red, 0.1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 	}
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &.secondary {
 | 
					  &.secondary {
 | 
				
			||||||
    background: $gray;
 | 
					    background: $gray;
 | 
				
			||||||
@ -115,20 +115,20 @@ input[type="submit"] {
 | 
				
			|||||||
form.new-post {
 | 
					form.new-post {
 | 
				
			||||||
  max-width: 60em;
 | 
					  max-width: 60em;
 | 
				
			||||||
  .title {
 | 
					  .title {
 | 
				
			||||||
   	margin: 0 auto;
 | 
					    margin: 0 auto;
 | 
				
			||||||
   	padding: 0.75em 0;
 | 
					    padding: 0.75em 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   	background: none;
 | 
					    background: none;
 | 
				
			||||||
   	border: none;
 | 
					    border: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   	font-family: $playfair;
 | 
					    font-family: $playfair;
 | 
				
			||||||
   	font-size: 2em;
 | 
					    font-size: 2em;
 | 
				
			||||||
   	text-align: left;
 | 
					    text-align: left;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  textarea {
 | 
					  textarea {
 | 
				
			||||||
   	min-height: 20em;
 | 
					    min-height: 20em;
 | 
				
			||||||
   	overflow-y: scroll;
 | 
					    overflow-y: scroll;
 | 
				
			||||||
   	resize: none;
 | 
					    resize: none;
 | 
				
			||||||
   -webkit-appearance: textarea;
 | 
					   -webkit-appearance: textarea;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,43 +6,43 @@ html {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html, body {
 | 
					html, body {
 | 
				
			||||||
 	margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
 	padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
 	background: $background;
 | 
					  background: $background;
 | 
				
			||||||
 	color: $text-color;
 | 
					  color: $text-color;
 | 
				
			||||||
 	font-family: $route159;
 | 
					  font-family: $route159;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ::selection {
 | 
					  ::selection {
 | 
				
			||||||
    background: transparentize($primary, 0.7);
 | 
					    background: transparentize($primary, 0.7);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
	::-moz-selection {
 | 
					 ::-moz-selection {
 | 
				
			||||||
    background: transparentize($primary, 0.7);
 | 
					    background: transparentize($primary, 0.7);
 | 
				
			||||||
	}
 | 
					 }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a, a:visited {
 | 
					a, a:visited {
 | 
				
			||||||
 	color: $primary;
 | 
					  color: $primary;
 | 
				
			||||||
 	text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
a::selection {
 | 
					a::selection {
 | 
				
			||||||
	color: $background;
 | 
					 color: $background;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
a::-moz-selection {
 | 
					a::-moz-selection {
 | 
				
			||||||
	color: $background;
 | 
					 color: $background;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
small {
 | 
					small {
 | 
				
			||||||
 	margin-left: 1em;
 | 
					  margin-left: 1em;
 | 
				
			||||||
 	color: transparentize($text-color, 0.6);
 | 
					  color: transparentize($text-color, 0.6);
 | 
				
			||||||
 	font-size: 0.75em;
 | 
					  font-size: 0.75em;
 | 
				
			||||||
 	word-wrap: break-word;
 | 
					  word-wrap: break-word;
 | 
				
			||||||
 	word-break: break-all;
 | 
					  word-break: break-all;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.center {
 | 
					.center {
 | 
				
			||||||
 	text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
 	font-weight: bold;
 | 
					  font-weight: bold;
 | 
				
			||||||
 	opacity: 0.6;
 | 
					  opacity: 0.6;
 | 
				
			||||||
 	padding: 5em;
 | 
					  padding: 5em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.right {
 | 
					.right {
 | 
				
			||||||
@ -53,28 +53,28 @@ small {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.spaced {
 | 
					.spaced {
 | 
				
			||||||
 	margin: 4rem 0;
 | 
					  margin: 4rem 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.banner {
 | 
					.banner {
 | 
				
			||||||
 	background: $gray;
 | 
					  background: $gray;
 | 
				
			||||||
 	padding-top: 2em;
 | 
					  padding-top: 2em;
 | 
				
			||||||
 	padding-bottom: 1em;
 | 
					  padding-bottom: 1em;
 | 
				
			||||||
 	margin: 3em 0px;
 | 
					  margin: 3em 0px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.hidden {
 | 
					.hidden {
 | 
				
			||||||
	display: none;
 | 
					 display: none;
 | 
				
			||||||
	appearance: none;
 | 
					 appearance: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Main */
 | 
					/* Main */
 | 
				
			||||||
body > main > *, .h-feed > * {
 | 
					body > main > *, .h-feed > * {
 | 
				
			||||||
 	margin: 1em $horizontal-margin;
 | 
					  margin: 1em $horizontal-margin;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body > main > .h-entry, .h-feed {
 | 
					body > main > .h-entry, .h-feed {
 | 
				
			||||||
	margin: 0;
 | 
					 margin: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
body > main {
 | 
					body > main {
 | 
				
			||||||
@ -98,18 +98,18 @@ main {
 | 
				
			|||||||
    margin-top: 1em;
 | 
					    margin-top: 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.article {
 | 
					    &.article {
 | 
				
			||||||
     	margin: 1em auto 0.5em;
 | 
					      margin: 1em auto 0.5em;
 | 
				
			||||||
     	font-family: $playfair;
 | 
					      font-family: $playfair;
 | 
				
			||||||
     	font-size: 2.5em;
 | 
					      font-size: 2.5em;
 | 
				
			||||||
     	font-weight: normal;
 | 
					      font-weight: normal;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  h2 {
 | 
					  h2 {
 | 
				
			||||||
   	font-size: 1.75em;
 | 
					    font-size: 1.75em;
 | 
				
			||||||
   	font-weight: 300;
 | 
					    font-weight: 300;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   	&.article {
 | 
					    &.article {
 | 
				
			||||||
      font-size: 1.25em;
 | 
					      font-size: 1.25em;
 | 
				
			||||||
      margin-bottom: 0.5em;
 | 
					      margin-bottom: 0.5em;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -139,15 +139,15 @@ main {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Errors */
 | 
					/* Errors */
 | 
				
			||||||
p.error {
 | 
					p.error {
 | 
				
			||||||
 	color: $red;
 | 
					  color: $red;
 | 
				
			||||||
 	font-weight: bold;
 | 
					  font-weight: bold;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* User page */
 | 
					/* User page */
 | 
				
			||||||
.user h1 {
 | 
					.user h1 {
 | 
				
			||||||
 	display: flex;
 | 
					  display: flex;
 | 
				
			||||||
 	flex-direction: row;
 | 
					  flex-direction: row;
 | 
				
			||||||
 	align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  margin: 0px;
 | 
					  margin: 0px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -156,14 +156,14 @@ p.error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.badge {
 | 
					.badge {
 | 
				
			||||||
 	margin-right: 1em;
 | 
					  margin-right: 1em;
 | 
				
			||||||
 	padding: 0.35em 1em;
 | 
					  padding: 0.35em 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	background: $background;
 | 
					  background: $background;
 | 
				
			||||||
 	color: $primary;
 | 
					  color: $primary;
 | 
				
			||||||
 	border: 1px solid $primary;
 | 
					  border: 1px solid $primary;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	font-size: 1rem;
 | 
					  font-size: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.user-summary {
 | 
					.user-summary {
 | 
				
			||||||
@ -172,23 +172,25 @@ p.error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Cards */
 | 
					/* Cards */
 | 
				
			||||||
.cards {
 | 
					.cards {
 | 
				
			||||||
 	display: flex;
 | 
					  display: flex;
 | 
				
			||||||
 	flex-direction: row;
 | 
					  flex-direction: row;
 | 
				
			||||||
 	flex-wrap: wrap;
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 	padding: 0 5%;
 | 
					  padding: 0 5%;
 | 
				
			||||||
  margin: 1rem 0 5rem;
 | 
					  margin: 1rem 0 5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.card {
 | 
					.card {
 | 
				
			||||||
 	flex: 1;
 | 
					  flex: 1;
 | 
				
			||||||
 	display: flex;
 | 
					  display: flex;
 | 
				
			||||||
 	flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	min-width: 20em;
 | 
					  position: relative;
 | 
				
			||||||
 	min-height: 20em;
 | 
					 | 
				
			||||||
 	margin: 1em;
 | 
					 | 
				
			||||||
 	box-sizing: border-box;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	background: $gray;
 | 
					  min-width: 20em;
 | 
				
			||||||
 | 
					  min-height: 20em;
 | 
				
			||||||
 | 
					  margin: 1em;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  background: $gray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  text-overflow: ellipsis;
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -213,11 +215,11 @@ p.error {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	> * {
 | 
					  > * {
 | 
				
			||||||
 	  margin: 20px;
 | 
					    margin: 20px;
 | 
				
			||||||
 	}
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	.cover {
 | 
					  .cover {
 | 
				
			||||||
    min-height: 10em;
 | 
					    min-height: 10em;
 | 
				
			||||||
    background-position: center;
 | 
					    background-position: center;
 | 
				
			||||||
    background-size: cover;
 | 
					    background-size: cover;
 | 
				
			||||||
@ -225,26 +227,38 @@ p.error {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  h3 {
 | 
					  h3 {
 | 
				
			||||||
   	margin: 0.75em 20px;
 | 
					    flex-grow: 1;
 | 
				
			||||||
   	font-family: $playfair;
 | 
					    margin: 0;
 | 
				
			||||||
   	font-size: 1.75em;
 | 
					    font-family: $playfair;
 | 
				
			||||||
   	font-weight: normal;
 | 
					    font-size: 1.75em;
 | 
				
			||||||
   	a {
 | 
					    font-weight: normal;
 | 
				
			||||||
   	  transition: color 0.1s ease-in;
 | 
					    line-height: 1.75;
 | 
				
			||||||
   	  color: $text-color;
 | 
					    a {
 | 
				
			||||||
 | 
					      display: block;
 | 
				
			||||||
 | 
					      transition: color 0.1s ease-in;
 | 
				
			||||||
 | 
					      color: $text-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   	  &:hover { color: $primary; }
 | 
					      &:hover { color: $primary; }
 | 
				
			||||||
   	}
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .controls {
 | 
				
			||||||
 | 
					    float: right;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .button {
 | 
				
			||||||
 | 
					      margin-top: 0;
 | 
				
			||||||
 | 
					      margin-bottom: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  main {
 | 
					  main {
 | 
				
			||||||
   	flex: 1;
 | 
					    flex: 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   	font-family: $lora;
 | 
					    font-family: $lora;
 | 
				
			||||||
   	font-size: 1em;
 | 
					    font-size: 1em;
 | 
				
			||||||
   	line-height: 1.25;
 | 
					    line-height: 1.25;
 | 
				
			||||||
   	text-align: left;
 | 
					    text-align: left;
 | 
				
			||||||
   	overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -286,15 +300,15 @@ p.error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Stats */
 | 
					/* Stats */
 | 
				
			||||||
.stats {
 | 
					.stats {
 | 
				
			||||||
 	display: flex;
 | 
					  display: flex;
 | 
				
			||||||
 	justify-content: space-around;
 | 
					  justify-content: space-around;
 | 
				
			||||||
 	margin: 2em;
 | 
					  margin: 2em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 	> div {
 | 
					  > div {
 | 
				
			||||||
   	display: flex;
 | 
					    display: flex;
 | 
				
			||||||
   	flex-direction: column;
 | 
					    flex-direction: column;
 | 
				
			||||||
   	justify-content: center;
 | 
					    justify-content: center;
 | 
				
			||||||
   	align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  p {
 | 
					  p {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,8 +3,8 @@ body > header {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  #content {
 | 
					  #content {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
   	align-content: center;
 | 
					    align-content: center;
 | 
				
			||||||
   	justify-content: space-between;
 | 
					    justify-content: space-between;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  nav#menu {
 | 
					  nav#menu {
 | 
				
			||||||
@ -19,44 +19,44 @@ body > header {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    a {
 | 
					    a {
 | 
				
			||||||
      transform: skewX(15deg);
 | 
					      transform: skewX(15deg);
 | 
				
			||||||
     	display: flex;
 | 
					      display: flex;
 | 
				
			||||||
     	flex-direction: column;
 | 
					      flex-direction: column;
 | 
				
			||||||
     	align-items: center;
 | 
					      align-items: center;
 | 
				
			||||||
     	justify-content: center;
 | 
					      justify-content: center;
 | 
				
			||||||
     	width: 1.4em;
 | 
					      width: 1.4em;
 | 
				
			||||||
     	height: 1.4em;
 | 
					      height: 1.4em;
 | 
				
			||||||
     	margin: 0;
 | 
					      margin: 0;
 | 
				
			||||||
     	padding: 0;
 | 
					      padding: 0;
 | 
				
			||||||
     	color: $gray;
 | 
					      color: $gray;
 | 
				
			||||||
     	font-size: 1.33em;
 | 
					      font-size: 1.33em;
 | 
				
			||||||
   	}
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  nav {
 | 
					  nav {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
   	flex-direction: row;
 | 
					    flex-direction: row;
 | 
				
			||||||
   	align-items: center;
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   	hr {
 | 
					    hr {
 | 
				
			||||||
     	height: 100%;
 | 
					      height: 100%;
 | 
				
			||||||
     	width: 0.2em;
 | 
					      width: 0.2em;
 | 
				
			||||||
     	background: $primary;
 | 
					      background: $primary;
 | 
				
			||||||
     	border: none;
 | 
					      border: none;
 | 
				
			||||||
     	transform: skewX(-15deg);
 | 
					      transform: skewX(-15deg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    a {
 | 
					    a {
 | 
				
			||||||
     	display: flex;
 | 
					      display: flex;
 | 
				
			||||||
     	align-items: center;
 | 
					      align-items: center;
 | 
				
			||||||
     	position: relative;
 | 
					      position: relative;
 | 
				
			||||||
     	align-self: stretch;
 | 
					      align-self: stretch;
 | 
				
			||||||
     	margin: 0;
 | 
					      margin: 0;
 | 
				
			||||||
     	padding: 0 2em;
 | 
					      padding: 0 2em;
 | 
				
			||||||
     	font-size: 1em;
 | 
					      font-size: 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     	i { font-size: 1.2em; }
 | 
					      i { font-size: 1.2em; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
     	&.title {
 | 
					      &.title {
 | 
				
			||||||
     	  margin: 0;
 | 
					        margin: 0;
 | 
				
			||||||
        text-align: center;
 | 
					        text-align: center;
 | 
				
			||||||
        padding: 0.5em 1em;
 | 
					        padding: 0.5em 1em;
 | 
				
			||||||
        font-size: 1.75em;
 | 
					        font-size: 1.75em;
 | 
				
			||||||
@ -70,7 +70,7 @@ body > header {
 | 
				
			|||||||
          margin: 0;
 | 
					          margin: 0;
 | 
				
			||||||
          padding-left: 0.5em;
 | 
					          padding-left: 0.5em;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
     	}
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -205,36 +205,36 @@ body > header {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Only enable label animations on large screens */
 | 
					/* Only enable label animations on large screens */
 | 
				
			||||||
@media screen and (min-width: 600px) {
 | 
					@media screen and (min-width: 600px) {
 | 
				
			||||||
 	header nav a {
 | 
					  header nav a {
 | 
				
			||||||
 	  i {
 | 
					    i {
 | 
				
			||||||
   		transition: all 0.2s ease;
 | 
					      transition: all 0.2s ease;
 | 
				
			||||||
   		margin: 0;
 | 
					      margin: 0;
 | 
				
			||||||
   	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   	.mobile-label {
 | 
					 | 
				
			||||||
   		transition: all 0.2s ease;
 | 
					 | 
				
			||||||
   		display: block;
 | 
					 | 
				
			||||||
   		position: absolute;
 | 
					 | 
				
			||||||
   		left: 50%;
 | 
					 | 
				
			||||||
   		transform: translate(-50%, 0);
 | 
					 | 
				
			||||||
   		transform: translateZ(0);
 | 
					 | 
				
			||||||
   		-webkit-transform: none !important;
 | 
					 | 
				
			||||||
   		opacity: 0;
 | 
					 | 
				
			||||||
   		font-size: 0.9em;
 | 
					 | 
				
			||||||
   		white-space: nowrap;
 | 
					 | 
				
			||||||
   	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   	img + .mobile-label { display: none; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   	&:hover {
 | 
					 | 
				
			||||||
   	  i { margin-bottom: 0.75em; }
 | 
					 | 
				
			||||||
   	  .mobile-label {
 | 
					 | 
				
			||||||
   	    opacity: 1;
 | 
					 | 
				
			||||||
     		transform: translate(-50%, 80%);
 | 
					 | 
				
			||||||
     		-webkit-transform: translate(-50%, 80%);
 | 
					 | 
				
			||||||
   		}
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 	}
 | 
					
 | 
				
			||||||
 | 
					    .mobile-label {
 | 
				
			||||||
 | 
					      transition: all 0.2s ease;
 | 
				
			||||||
 | 
					      display: block;
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      left: 50%;
 | 
				
			||||||
 | 
					      transform: translate(-50%, 0);
 | 
				
			||||||
 | 
					      transform: translateZ(0);
 | 
				
			||||||
 | 
					      -webkit-transform: none !important;
 | 
				
			||||||
 | 
					      opacity: 0;
 | 
				
			||||||
 | 
					      font-size: 0.9em;
 | 
				
			||||||
 | 
					      white-space: nowrap;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    img + .mobile-label { display: none; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:hover {
 | 
				
			||||||
 | 
					      i { margin-bottom: 0.75em; }
 | 
				
			||||||
 | 
					      .mobile-label {
 | 
				
			||||||
 | 
					        opacity: 1;
 | 
				
			||||||
 | 
					       transform: translate(-50%, 80%);
 | 
				
			||||||
 | 
					       -webkit-transform: translate(-50%, 80%);
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Small screens
 | 
					// Small screens
 | 
				
			||||||
 | 
				
			|||||||
@ -247,6 +247,7 @@ pub(crate) mod tests {
 | 
				
			|||||||
    use diesel::Connection;
 | 
					    use diesel::Connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn fill_database(conn: &Conn) -> Vec<(NewInstance, Instance)> {
 | 
					    pub(crate) fn fill_database(conn: &Conn) -> Vec<(NewInstance, Instance)> {
 | 
				
			||||||
 | 
					        diesel::delete(instances::table).execute(conn).unwrap();
 | 
				
			||||||
        let res = vec![
 | 
					        let res = vec![
 | 
				
			||||||
            NewInstance {
 | 
					            NewInstance {
 | 
				
			||||||
                default_license: "WTFPL".to_string(),
 | 
					                default_license: "WTFPL".to_string(),
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ use activitypub::{
 | 
				
			|||||||
use chrono::{NaiveDateTime, TimeZone, Utc};
 | 
					use chrono::{NaiveDateTime, TimeZone, Utc};
 | 
				
			||||||
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
 | 
					use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
 | 
				
			||||||
use heck::KebabCase;
 | 
					use heck::KebabCase;
 | 
				
			||||||
 | 
					use once_cell::sync::Lazy;
 | 
				
			||||||
use plume_common::{
 | 
					use plume_common::{
 | 
				
			||||||
    activity_pub::{
 | 
					    activity_pub::{
 | 
				
			||||||
        inbox::{AsObject, FromId},
 | 
					        inbox::{AsObject, FromId},
 | 
				
			||||||
@ -20,11 +21,13 @@ use plume_common::{
 | 
				
			|||||||
    utils::md_to_html,
 | 
					    utils::md_to_html,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use riker::actors::{Publish, Tell};
 | 
					use riker::actors::{Publish, Tell};
 | 
				
			||||||
use std::collections::HashSet;
 | 
					use std::collections::{HashMap, HashSet};
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::{Arc, Mutex};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type LicensedArticle = CustomObject<Licensed, Article>;
 | 
					pub type LicensedArticle = CustomObject<Licensed, Article>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BLOG_FQN_CACHE: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| Mutex::new(HashMap::new()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Queryable, Identifiable, Clone, AsChangeset, Debug)]
 | 
					#[derive(Queryable, Identifiable, Clone, AsChangeset, Debug)]
 | 
				
			||||||
#[changeset_options(treat_none_as_null = "true")]
 | 
					#[changeset_options(treat_none_as_null = "true")]
 | 
				
			||||||
pub struct Post {
 | 
					pub struct Post {
 | 
				
			||||||
@ -275,6 +278,24 @@ impl Post {
 | 
				
			|||||||
            .map_err(Error::from)
 | 
					            .map_err(Error::from)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// This method exists for use in templates to reduce database access.
 | 
				
			||||||
 | 
					    /// This should not be used for other purpose.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This caches query result. The best way to cache query result is holding it in `Post`s field
 | 
				
			||||||
 | 
					    /// but Diesel doesn't allow it currently.
 | 
				
			||||||
 | 
					    /// If sometime Diesel allow it, this method should be removed.
 | 
				
			||||||
 | 
					    pub fn get_blog_fqn(&self, conn: &Connection) -> String {
 | 
				
			||||||
 | 
					        if let Some(blog_fqn) = BLOG_FQN_CACHE.lock().unwrap().get(&self.blog_id) {
 | 
				
			||||||
 | 
					            return blog_fqn.to_string();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let blog_fqn = self.get_blog(conn).unwrap().fqn;
 | 
				
			||||||
 | 
					        BLOG_FQN_CACHE
 | 
				
			||||||
 | 
					            .lock()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					            .insert(self.blog_id, blog_fqn.clone());
 | 
				
			||||||
 | 
					        blog_fqn
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn count_likes(&self, conn: &Connection) -> Result<i64> {
 | 
					    pub fn count_likes(&self, conn: &Connection) -> Result<i64> {
 | 
				
			||||||
        use crate::schema::likes;
 | 
					        use crate::schema::likes;
 | 
				
			||||||
        likes::table
 | 
					        likes::table
 | 
				
			||||||
 | 
				
			|||||||
@ -79,7 +79,6 @@ impl ActorFactoryArgs<(Arc<Searcher>, DbPool)> for SearchActor {
 | 
				
			|||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
    use crate::diesel::Connection;
 | 
					    use crate::diesel::Connection;
 | 
				
			||||||
    use crate::diesel::RunQueryDsl;
 | 
					 | 
				
			||||||
    use crate::{
 | 
					    use crate::{
 | 
				
			||||||
        blog_authors::{BlogAuthor, NewBlogAuthor},
 | 
					        blog_authors::{BlogAuthor, NewBlogAuthor},
 | 
				
			||||||
        blogs::{Blog, NewBlog},
 | 
					        blogs::{Blog, NewBlog},
 | 
				
			||||||
@ -114,7 +113,7 @@ mod tests {
 | 
				
			|||||||
        let conn = db_pool.clone().get().unwrap();
 | 
					        let conn = db_pool.clone().get().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let title = random_hex()[..8].to_owned();
 | 
					        let title = random_hex()[..8].to_owned();
 | 
				
			||||||
        let (instance, user, blog) = fill_database(&conn);
 | 
					        let (_instance, _user, blog) = fill_database(&conn);
 | 
				
			||||||
        let author = &blog.list_authors(&conn).unwrap()[0];
 | 
					        let author = &blog.list_authors(&conn).unwrap()[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let post = Post::insert(
 | 
					        let post = Post::insert(
 | 
				
			||||||
@ -151,11 +150,6 @@ mod tests {
 | 
				
			|||||||
            searcher.search_document(&conn, Query::from_str(&title).unwrap(), (0, 1))[0].id,
 | 
					            searcher.search_document(&conn, Query::from_str(&title).unwrap(), (0, 1))[0].id,
 | 
				
			||||||
            post_id
 | 
					            post_id
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        // TODO: Make sure records are deleted even when assertion failed
 | 
					 | 
				
			||||||
        post.delete(&conn).unwrap();
 | 
					 | 
				
			||||||
        blog.delete(&conn).unwrap();
 | 
					 | 
				
			||||||
        user.delete(&conn).unwrap();
 | 
					 | 
				
			||||||
        diesel::delete(&instance).execute(&conn).unwrap();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn fill_database(conn: &Conn) -> (Instance, User, Blog) {
 | 
					    fn fill_database(conn: &Conn) -> (Instance, User, Blog) {
 | 
				
			||||||
@ -164,7 +158,7 @@ mod tests {
 | 
				
			|||||||
                conn,
 | 
					                conn,
 | 
				
			||||||
                NewInstance {
 | 
					                NewInstance {
 | 
				
			||||||
                    default_license: "CC-0-BY-SA".to_string(),
 | 
					                    default_license: "CC-0-BY-SA".to_string(),
 | 
				
			||||||
                    local: true,
 | 
					                    local: false,
 | 
				
			||||||
                    long_description: SafeString::new("Good morning"),
 | 
					                    long_description: SafeString::new("Good morning"),
 | 
				
			||||||
                    long_description_html: "<p>Good morning</p>".to_string(),
 | 
					                    long_description_html: "<p>Good morning</p>".to_string(),
 | 
				
			||||||
                    short_description: SafeString::new("Hello"),
 | 
					                    short_description: SafeString::new("Hello"),
 | 
				
			||||||
@ -175,14 +169,29 @@ mod tests {
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
            let mut user = NewUser::default();
 | 
					            let user = User::insert(
 | 
				
			||||||
            user.instance_id = instance.id;
 | 
					                conn,
 | 
				
			||||||
            user.username = random_hex().to_string();
 | 
					                NewUser {
 | 
				
			||||||
            user.ap_url = random_hex().to_string();
 | 
					                    username: random_hex().to_string(),
 | 
				
			||||||
            user.inbox_url = random_hex().to_string();
 | 
					                    display_name: random_hex().to_string(),
 | 
				
			||||||
            user.outbox_url = random_hex().to_string();
 | 
					                    outbox_url: random_hex().to_string(),
 | 
				
			||||||
            user.followers_endpoint = random_hex().to_string();
 | 
					                    inbox_url: random_hex().to_string(),
 | 
				
			||||||
            let user = User::insert(conn, user).unwrap();
 | 
					                    summary: "".to_string(),
 | 
				
			||||||
 | 
					                    email: None,
 | 
				
			||||||
 | 
					                    hashed_password: None,
 | 
				
			||||||
 | 
					                    instance_id: instance.id,
 | 
				
			||||||
 | 
					                    ap_url: random_hex().to_string(),
 | 
				
			||||||
 | 
					                    private_key: None,
 | 
				
			||||||
 | 
					                    public_key: "".to_string(),
 | 
				
			||||||
 | 
					                    shared_inbox_url: None,
 | 
				
			||||||
 | 
					                    followers_endpoint: random_hex().to_string(),
 | 
				
			||||||
 | 
					                    avatar_id: None,
 | 
				
			||||||
 | 
					                    summary_html: SafeString::new(""),
 | 
				
			||||||
 | 
					                    role: 0,
 | 
				
			||||||
 | 
					                    fqn: random_hex().to_string(),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
            let mut blog = NewBlog::default();
 | 
					            let mut blog = NewBlog::default();
 | 
				
			||||||
            blog.instance_id = instance.id;
 | 
					            blog.instance_id = instance.id;
 | 
				
			||||||
            blog.actor_id = random_hex().to_string();
 | 
					            blog.actor_id = random_hex().to_string();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/main.rs
									
									
									
									
									
								
							@ -60,7 +60,7 @@ fn init_pool() -> Option<DbPool> {
 | 
				
			|||||||
    Some(pool)
 | 
					    Some(pool)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					pub(crate) fn init_rocket() -> rocket::Rocket {
 | 
				
			||||||
    match dotenv::dotenv() {
 | 
					    match dotenv::dotenv() {
 | 
				
			||||||
        Ok(path) => eprintln!("Configuration read from {}", path.display()),
 | 
					        Ok(path) => eprintln!("Configuration read from {}", path.display()),
 | 
				
			||||||
        Err(ref e) if e.not_found() => eprintln!("no .env was found"),
 | 
					        Err(ref e) if e.not_found() => eprintln!("no .env was found"),
 | 
				
			||||||
@ -126,7 +126,7 @@ Then try to restart Plume.
 | 
				
			|||||||
        warn!("Please refer to the documentation to see how to configure it.");
 | 
					        warn!("Please refer to the documentation to see how to configure it.");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let rocket = rocket::custom(CONFIG.rocket.clone().unwrap())
 | 
					    rocket::custom(CONFIG.rocket.clone().unwrap())
 | 
				
			||||||
        .mount(
 | 
					        .mount(
 | 
				
			||||||
            "/",
 | 
					            "/",
 | 
				
			||||||
            routes![
 | 
					            routes![
 | 
				
			||||||
@ -268,9 +268,14 @@ Then try to restart Plume.
 | 
				
			|||||||
                ])
 | 
					                ])
 | 
				
			||||||
                .finalize()
 | 
					                .finalize()
 | 
				
			||||||
                .expect("main: csrf fairing creation error"),
 | 
					                .expect("main: csrf fairing creation error"),
 | 
				
			||||||
        );
 | 
					        )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let rocket = init_rocket();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(feature = "test")]
 | 
					    #[cfg(feature = "test")]
 | 
				
			||||||
    let rocket = rocket.mount("/test", routes![test_routes::health,]);
 | 
					    let rocket = rocket.mount("/test", routes![test_routes::health,]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rocket.launch();
 | 
					    rocket.launch();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -371,3 +371,135 @@ pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>>
 | 
				
			|||||||
        feed.to_string(),
 | 
					        feed.to_string(),
 | 
				
			||||||
    ))
 | 
					    ))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use crate::init_rocket;
 | 
				
			||||||
 | 
					    use diesel::Connection;
 | 
				
			||||||
 | 
					    use plume_common::utils::random_hex;
 | 
				
			||||||
 | 
					    use plume_models::{
 | 
				
			||||||
 | 
					        blog_authors::{BlogAuthor, NewBlogAuthor},
 | 
				
			||||||
 | 
					        blogs::{Blog, NewBlog},
 | 
				
			||||||
 | 
					        db_conn::{DbConn, DbPool},
 | 
				
			||||||
 | 
					        instance::{Instance, NewInstance},
 | 
				
			||||||
 | 
					        post_authors::{NewPostAuthor, PostAuthor},
 | 
				
			||||||
 | 
					        posts::{NewPost, Post},
 | 
				
			||||||
 | 
					        safe_string::SafeString,
 | 
				
			||||||
 | 
					        users::{NewUser, User, AUTH_COOKIE},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    use rocket::{
 | 
				
			||||||
 | 
					        http::{Cookie, Cookies, SameSite},
 | 
				
			||||||
 | 
					        local::{Client, LocalRequest},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn edit_link_within_post_card() {
 | 
				
			||||||
 | 
					        let rocket = init_rocket();
 | 
				
			||||||
 | 
					        let client = Client::new(rocket).expect("valid rocket instance");
 | 
				
			||||||
 | 
					        let dbpool = client.rocket().state::<DbPool>().unwrap();
 | 
				
			||||||
 | 
					        let conn = &DbConn(dbpool.get().unwrap());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (_instance, user, blog, post) = create_models(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let blog_path = uri!(super::activity_details: name = &blog.fqn).to_string();
 | 
				
			||||||
 | 
					        let edit_link = uri!(
 | 
				
			||||||
 | 
					            super::super::posts::edit: blog = &blog.fqn,
 | 
				
			||||||
 | 
					            slug = &post.slug
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut response = client.get(&blog_path).dispatch();
 | 
				
			||||||
 | 
					        let body = response.body_string().unwrap();
 | 
				
			||||||
 | 
					        assert!(!body.contains(&edit_link));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let request = client.get(&blog_path);
 | 
				
			||||||
 | 
					        login(&request, &user);
 | 
				
			||||||
 | 
					        let mut response = request.dispatch();
 | 
				
			||||||
 | 
					        let body = response.body_string().unwrap();
 | 
				
			||||||
 | 
					        assert!(body.contains(&edit_link));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn create_models(conn: &DbConn) -> (Instance, User, Blog, Post) {
 | 
				
			||||||
 | 
					        conn.transaction::<(Instance, User, Blog, Post), diesel::result::Error, _>(|| {
 | 
				
			||||||
 | 
					            let instance = Instance::get_local().unwrap_or_else(|_| {
 | 
				
			||||||
 | 
					                let instance = Instance::insert(
 | 
				
			||||||
 | 
					                    conn,
 | 
				
			||||||
 | 
					                    NewInstance {
 | 
				
			||||||
 | 
					                        default_license: "CC-0-BY-SA".to_string(),
 | 
				
			||||||
 | 
					                        local: true,
 | 
				
			||||||
 | 
					                        long_description: SafeString::new("Good morning"),
 | 
				
			||||||
 | 
					                        long_description_html: "<p>Good morning</p>".to_string(),
 | 
				
			||||||
 | 
					                        short_description: SafeString::new("Hello"),
 | 
				
			||||||
 | 
					                        short_description_html: "<p>Hello</p>".to_string(),
 | 
				
			||||||
 | 
					                        name: random_hex().to_string(),
 | 
				
			||||||
 | 
					                        open_registrations: true,
 | 
				
			||||||
 | 
					                        public_domain: random_hex().to_string(),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .unwrap();
 | 
				
			||||||
 | 
					                Instance::cache_local(conn);
 | 
				
			||||||
 | 
					                instance
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            let mut user = NewUser::default();
 | 
				
			||||||
 | 
					            user.instance_id = instance.id;
 | 
				
			||||||
 | 
					            user.username = random_hex().to_string();
 | 
				
			||||||
 | 
					            user.ap_url = random_hex().to_string();
 | 
				
			||||||
 | 
					            user.inbox_url = random_hex().to_string();
 | 
				
			||||||
 | 
					            user.outbox_url = random_hex().to_string();
 | 
				
			||||||
 | 
					            user.followers_endpoint = random_hex().to_string();
 | 
				
			||||||
 | 
					            let user = User::insert(conn, user).unwrap();
 | 
				
			||||||
 | 
					            let mut blog = NewBlog::default();
 | 
				
			||||||
 | 
					            blog.instance_id = instance.id;
 | 
				
			||||||
 | 
					            blog.actor_id = random_hex().to_string();
 | 
				
			||||||
 | 
					            blog.ap_url = random_hex().to_string();
 | 
				
			||||||
 | 
					            blog.inbox_url = random_hex().to_string();
 | 
				
			||||||
 | 
					            blog.outbox_url = random_hex().to_string();
 | 
				
			||||||
 | 
					            let blog = Blog::insert(conn, blog).unwrap();
 | 
				
			||||||
 | 
					            BlogAuthor::insert(
 | 
				
			||||||
 | 
					                conn,
 | 
				
			||||||
 | 
					                NewBlogAuthor {
 | 
				
			||||||
 | 
					                    blog_id: blog.id,
 | 
				
			||||||
 | 
					                    author_id: user.id,
 | 
				
			||||||
 | 
					                    is_owner: true,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					            let post = Post::insert(
 | 
				
			||||||
 | 
					                conn,
 | 
				
			||||||
 | 
					                NewPost {
 | 
				
			||||||
 | 
					                    blog_id: blog.id,
 | 
				
			||||||
 | 
					                    slug: random_hex()[..8].to_owned(),
 | 
				
			||||||
 | 
					                    title: random_hex()[..8].to_owned(),
 | 
				
			||||||
 | 
					                    content: SafeString::new(""),
 | 
				
			||||||
 | 
					                    published: true,
 | 
				
			||||||
 | 
					                    license: "CC-By-SA".to_owned(),
 | 
				
			||||||
 | 
					                    ap_url: "".to_owned(),
 | 
				
			||||||
 | 
					                    creation_date: None,
 | 
				
			||||||
 | 
					                    subtitle: "".to_owned(),
 | 
				
			||||||
 | 
					                    source: "".to_owned(),
 | 
				
			||||||
 | 
					                    cover_id: None,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					            PostAuthor::insert(
 | 
				
			||||||
 | 
					                conn,
 | 
				
			||||||
 | 
					                NewPostAuthor {
 | 
				
			||||||
 | 
					                    post_id: post.id,
 | 
				
			||||||
 | 
					                    author_id: user.id,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok((instance, user, blog, post))
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn login(request: &LocalRequest, user: &User) {
 | 
				
			||||||
 | 
					        request.inner().guard::<Cookies>().unwrap().add_private(
 | 
				
			||||||
 | 
					            Cookie::build(AUTH_COOKIE, user.id.to_string())
 | 
				
			||||||
 | 
					                .same_site(SameSite::Lax)
 | 
				
			||||||
 | 
					                .finish(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,11 +8,18 @@
 | 
				
			|||||||
    @if article.cover_id.is_some() {
 | 
					    @if article.cover_id.is_some() {
 | 
				
			||||||
        <div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div>
 | 
					        <div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div>
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    <h3 class="p-name" dir="auto">
 | 
					    <header>
 | 
				
			||||||
        <a class="u-url" href="@uri!(posts::details: blog = article.get_blog(ctx.0).unwrap().fqn, slug = &article.slug, responding_to = _)">
 | 
					        @if ctx.2.clone().and_then(|u| article.is_author(ctx.0, u.id).ok()).unwrap_or(false) {
 | 
				
			||||||
            @article.title
 | 
					            <div class="controls">
 | 
				
			||||||
        </a>
 | 
					                <a class="button" href="@uri!(posts::edit: blog = &article.get_blog_fqn(ctx.0), slug = &article.slug)">@i18n!(ctx.1, "Edit")</a>
 | 
				
			||||||
    </h3>
 | 
					            </div>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        <h3 class="p-name" dir="auto">
 | 
				
			||||||
 | 
					            <a class="u-url" href="@uri!(posts::details: blog = article.get_blog_fqn(ctx.0), slug = &article.slug, responding_to = _)">
 | 
				
			||||||
 | 
					                @article.title
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					    </header>
 | 
				
			||||||
    <main>
 | 
					    <main>
 | 
				
			||||||
        <p class="p-summary" dir="auto">@article.subtitle</p>
 | 
					        <p class="p-summary" dir="auto">@article.subtitle</p>
 | 
				
			||||||
    </main>
 | 
					    </main>
 | 
				
			||||||
@ -26,7 +33,7 @@
 | 
				
			|||||||
            @if article.published {
 | 
					            @if article.published {
 | 
				
			||||||
                ⋅ <span class="dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span>
 | 
					                ⋅ <span class="dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ⋅ <a href="@uri!(blogs::details: name = &article.get_blog(ctx.0).unwrap().fqn, page = _)">@article.get_blog(ctx.0).unwrap().title</a>
 | 
					            ⋅ <a href="@uri!(blogs::details: name = &article.get_blog_fqn(ctx.0), page = _)">@article.get_blog(ctx.0).unwrap().title</a>
 | 
				
			||||||
            ⋅
 | 
					            ⋅
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        @if !article.published {
 | 
					        @if !article.published {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user