Merge branch 'master' of git.workaround.org:chaas/debshots
This commit is contained in:
commit
181ea83540
29 changed files with 678 additions and 386 deletions
37
Dockerfile
37
Dockerfile
|
|
@ -1,37 +0,0 @@
|
||||||
FROM debian:8
|
|
||||||
MAINTAINER email@christoph-haas.de
|
|
||||||
|
|
||||||
# Install apt based dependencies required to run Rails as
|
|
||||||
# well as RubyGems. As the Ruby image itself is based on a
|
|
||||||
# Debian image, we use apt-get to install those.
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
build-essential ruby
|
|
||||||
|
|
||||||
RUN apt-get install -y git
|
|
||||||
RUN apt-get install -y rbenv
|
|
||||||
|
|
||||||
# Configure the main working directory. This is the base
|
|
||||||
# directory used in any further RUN, COPY, and ENTRYPOINT
|
|
||||||
# commands.
|
|
||||||
RUN mkdir -p /app
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy the Gemfile as well as the Gemfile.lock and install
|
|
||||||
# the RubyGems. This is a separate step so the dependencies
|
|
||||||
# will be cached unless changes to one of those two files
|
|
||||||
# are made.
|
|
||||||
COPY Gemfile Gemfile.lock ./
|
|
||||||
RUN gem install bundler && bundle install --jobs 20 --retry 5
|
|
||||||
|
|
||||||
# Copy the main application.
|
|
||||||
COPY . ./
|
|
||||||
|
|
||||||
# Expose port 3000 to the Docker host, so we can access it
|
|
||||||
# from the outside.
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
# The main command to run when the container starts. Also
|
|
||||||
# tell the Rails dev server to bind to all interfaces by
|
|
||||||
# default.
|
|
||||||
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
|
|
||||||
|
|
||||||
|
|
@ -17,5 +17,3 @@ Install the dependencies:
|
||||||
Install a PostgreSQL server:
|
Install a PostgreSQL server:
|
||||||
|
|
||||||
apt install postgresql
|
apt install postgresql
|
||||||
|
|
||||||
C
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
//= require foundation
|
//= require foundation
|
||||||
//= require fancybox
|
//= require fancybox
|
||||||
//= require cookies_eu
|
//= require cookies_eu
|
||||||
//= require jquery.amaran
|
|
||||||
//= require_tree .
|
//= require_tree .
|
||||||
|
|
||||||
$(function(){ $(document).foundation(); });
|
$(function(){ $(document).foundation(); });
|
||||||
|
|
|
||||||
|
|
@ -21,25 +21,44 @@
|
||||||
// 16. Drilldown
|
// 16. Drilldown
|
||||||
// 17. Dropdown
|
// 17. Dropdown
|
||||||
// 18. Dropdown Menu
|
// 18. Dropdown Menu
|
||||||
// 19. Forms
|
// 19. Flexbox Utilities
|
||||||
// 20. Label
|
// 20. Forms
|
||||||
// 21. Media Object
|
// 21. Label
|
||||||
// 22. Menu
|
// 22. Media Object
|
||||||
// 23. Meter
|
// 23. Menu
|
||||||
// 24. Off-canvas
|
// 24. Meter
|
||||||
// 25. Orbit
|
// 25. Off-canvas
|
||||||
// 26. Pagination
|
// 26. Orbit
|
||||||
// 27. Progress Bar
|
// 27. Pagination
|
||||||
// 28. Responsive Embed
|
// 28. Progress Bar
|
||||||
// 29. Reveal
|
// 29. Prototype Arrow
|
||||||
// 30. Slider
|
// 30. Prototype Border-Box
|
||||||
// 31. Switch
|
// 31. Prototype Border-None
|
||||||
// 32. Table
|
// 32. Prototype Bordered
|
||||||
// 33. Tabs
|
// 33. Prototype Display
|
||||||
// 34. Thumbnail
|
// 34. Prototype Font-Styling
|
||||||
// 35. Title Bar
|
// 35. Prototype List-Style-Type
|
||||||
// 36. Tooltip
|
// 36. Prototype Overflow
|
||||||
// 37. Top Bar
|
// 37. Prototype Position
|
||||||
|
// 38. Prototype Rounded
|
||||||
|
// 39. Prototype Separator
|
||||||
|
// 40. Prototype Shadow
|
||||||
|
// 41. Prototype Sizing
|
||||||
|
// 42. Prototype Spacing
|
||||||
|
// 43. Prototype Text-Decoration
|
||||||
|
// 44. Prototype Text-Transformation
|
||||||
|
// 45. Prototype Text-Utilities
|
||||||
|
// 46. Responsive Embed
|
||||||
|
// 47. Reveal
|
||||||
|
// 48. Slider
|
||||||
|
// 49. Switch
|
||||||
|
// 50. Table
|
||||||
|
// 51. Tabs
|
||||||
|
// 52. Thumbnail
|
||||||
|
// 53. Title Bar
|
||||||
|
// 54. Tooltip
|
||||||
|
// 55. Top Bar
|
||||||
|
// 56. Xy Grid
|
||||||
|
|
||||||
@import 'util/util';
|
@import 'util/util';
|
||||||
|
|
||||||
|
|
@ -67,11 +86,16 @@ $body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;
|
||||||
$body-antialiased: true;
|
$body-antialiased: true;
|
||||||
$global-margin: 1rem;
|
$global-margin: 1rem;
|
||||||
$global-padding: 1rem;
|
$global-padding: 1rem;
|
||||||
|
$global-position: 1rem;
|
||||||
$global-weight-normal: normal;
|
$global-weight-normal: normal;
|
||||||
$global-weight-bold: bold;
|
$global-weight-bold: bold;
|
||||||
$global-radius: 0;
|
$global-radius: 0;
|
||||||
|
$global-menu-padding: 0 1rem;
|
||||||
|
$global-menu-nested-margin: 1rem;
|
||||||
$global-text-direction: ltr;
|
$global-text-direction: ltr;
|
||||||
$global-flexbox: false;
|
$global-flexbox: true;
|
||||||
|
$global-prototype-breakpoints: false;
|
||||||
|
$global-color-pick-contrast-tolerance: 0;
|
||||||
$print-transparent-backgrounds: true;
|
$print-transparent-backgrounds: true;
|
||||||
|
|
||||||
@include add-foundation-colors;
|
@include add-foundation-colors;
|
||||||
|
|
@ -99,6 +123,7 @@ $grid-column-gutter: (
|
||||||
medium: 30px,
|
medium: 30px,
|
||||||
);
|
);
|
||||||
$grid-column-align-edge: true;
|
$grid-column-align-edge: true;
|
||||||
|
$grid-column-alias: 'columns';
|
||||||
$block-grid-max: 8;
|
$block-grid-max: 8;
|
||||||
|
|
||||||
// 4. Base Typography
|
// 4. Base Typography
|
||||||
|
|
@ -210,8 +235,17 @@ $accordion-content-padding: 1rem;
|
||||||
// 8. Accordion Menu
|
// 8. Accordion Menu
|
||||||
// -----------------
|
// -----------------
|
||||||
|
|
||||||
|
$accordionmenu-padding: $global-menu-padding;
|
||||||
|
$accordionmenu-nested-margin: $global-menu-nested-margin;
|
||||||
|
$accordionmenu-submenu-padding: $accordionmenu-padding;
|
||||||
$accordionmenu-arrows: true;
|
$accordionmenu-arrows: true;
|
||||||
$accordionmenu-arrow-color: $primary-color;
|
$accordionmenu-arrow-color: $primary-color;
|
||||||
|
$accordionmenu-item-background: null;
|
||||||
|
$accordionmenu-border: null;
|
||||||
|
$accordionmenu-submenu-toggle-background: null;
|
||||||
|
$accordion-submenu-toggle-border: $accordionmenu-border;
|
||||||
|
$accordionmenu-submenu-toggle-width: 40px;
|
||||||
|
$accordionmenu-submenu-toggle-height: $accordionmenu-submenu-toggle-width;
|
||||||
$accordionmenu-arrow-size: 6px;
|
$accordionmenu-arrow-size: 6px;
|
||||||
|
|
||||||
// 9. Badge
|
// 9. Badge
|
||||||
|
|
@ -235,11 +269,15 @@ $breadcrumbs-item-color-current: $black;
|
||||||
$breadcrumbs-item-color-disabled: $medium-gray;
|
$breadcrumbs-item-color-disabled: $medium-gray;
|
||||||
$breadcrumbs-item-margin: 0.75rem;
|
$breadcrumbs-item-margin: 0.75rem;
|
||||||
$breadcrumbs-item-uppercase: true;
|
$breadcrumbs-item-uppercase: true;
|
||||||
$breadcrumbs-item-slash: true;
|
$breadcrumbs-item-separator: true;
|
||||||
|
$breadcrumbs-item-separator-item: '/';
|
||||||
|
$breadcrumbs-item-separator-item-rtl: '\\';
|
||||||
|
$breadcrumbs-item-separator-color: $medium-gray;
|
||||||
|
|
||||||
// 11. Button
|
// 11. Button
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
|
$button-font-family: inherit;
|
||||||
$button-padding: 0.85em 1em;
|
$button-padding: 0.85em 1em;
|
||||||
$button-margin: 0 0 $global-margin 0;
|
$button-margin: 0 0 $global-margin 0;
|
||||||
$button-fill: solid;
|
$button-fill: solid;
|
||||||
|
|
@ -248,6 +286,7 @@ $button-background-hover: scale-color($button-background, $lightness: -15%);
|
||||||
$button-color: $white;
|
$button-color: $white;
|
||||||
$button-color-alt: $black;
|
$button-color-alt: $black;
|
||||||
$button-radius: $global-radius;
|
$button-radius: $global-radius;
|
||||||
|
$button-hollow-border-width: 1px;
|
||||||
$button-sizes: (
|
$button-sizes: (
|
||||||
tiny: 0.6rem,
|
tiny: 0.6rem,
|
||||||
small: 0.75rem,
|
small: 0.75rem,
|
||||||
|
|
@ -292,7 +331,7 @@ $card-border: 1px solid $light-gray;
|
||||||
$card-shadow: none;
|
$card-shadow: none;
|
||||||
$card-border-radius: $global-radius;
|
$card-border-radius: $global-radius;
|
||||||
$card-padding: $global-padding;
|
$card-padding: $global-padding;
|
||||||
$card-margin: $global-margin;
|
$card-margin-bottom: $global-margin;
|
||||||
|
|
||||||
// 15. Close Button
|
// 15. Close Button
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
@ -319,9 +358,13 @@ $closebutton-color-hover: $black;
|
||||||
|
|
||||||
$drilldown-transition: transform 0.15s linear;
|
$drilldown-transition: transform 0.15s linear;
|
||||||
$drilldown-arrows: true;
|
$drilldown-arrows: true;
|
||||||
|
$drilldown-padding: $global-menu-padding;
|
||||||
|
$drilldown-nested-margin: 0;
|
||||||
|
$drilldown-background: $white;
|
||||||
|
$drilldown-submenu-padding: $drilldown-padding;
|
||||||
|
$drilldown-submenu-background: $white;
|
||||||
$drilldown-arrow-color: $primary-color;
|
$drilldown-arrow-color: $primary-color;
|
||||||
$drilldown-arrow-size: 6px;
|
$drilldown-arrow-size: 6px;
|
||||||
$drilldown-background: $white;
|
|
||||||
|
|
||||||
// 17. Dropdown
|
// 17. Dropdown
|
||||||
// ------------
|
// ------------
|
||||||
|
|
@ -344,11 +387,24 @@ $dropdown-sizes: (
|
||||||
$dropdownmenu-arrows: true;
|
$dropdownmenu-arrows: true;
|
||||||
$dropdownmenu-arrow-color: $anchor-color;
|
$dropdownmenu-arrow-color: $anchor-color;
|
||||||
$dropdownmenu-arrow-size: 6px;
|
$dropdownmenu-arrow-size: 6px;
|
||||||
|
$dropdownmenu-arrow-padding: 1.5rem;
|
||||||
$dropdownmenu-min-width: 200px;
|
$dropdownmenu-min-width: 200px;
|
||||||
$dropdownmenu-background: $white;
|
$dropdownmenu-background: $white;
|
||||||
|
$dropdownmenu-submenu-background: $dropdownmenu-background;
|
||||||
|
$dropdownmenu-padding: $global-menu-padding;
|
||||||
|
$dropdownmenu-nested-margin: 0;
|
||||||
|
$dropdownmenu-submenu-padding: $dropdownmenu-padding;
|
||||||
$dropdownmenu-border: 1px solid $medium-gray;
|
$dropdownmenu-border: 1px solid $medium-gray;
|
||||||
|
$dropdown-menu-item-color-active: get-color(primary);
|
||||||
|
$dropdown-menu-item-background-active: transparent;
|
||||||
|
|
||||||
// 19. Forms
|
// 19. Flexbox Utilities
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
$flex-source-ordering-count: 6;
|
||||||
|
$flexbox-responsive-breakpoints: true;
|
||||||
|
|
||||||
|
// 20. Forms
|
||||||
// ---------
|
// ---------
|
||||||
|
|
||||||
$fieldset-border: 1px solid $medium-gray;
|
$fieldset-border: 1px solid $medium-gray;
|
||||||
|
|
@ -375,11 +431,13 @@ $input-placeholder-color: $medium-gray;
|
||||||
$input-font-family: inherit;
|
$input-font-family: inherit;
|
||||||
$input-font-size: rem-calc(16);
|
$input-font-size: rem-calc(16);
|
||||||
$input-font-weight: $global-weight-normal;
|
$input-font-weight: $global-weight-normal;
|
||||||
|
$input-line-height: $global-lineheight;
|
||||||
$input-background: $white;
|
$input-background: $white;
|
||||||
$input-background-focus: $white;
|
$input-background-focus: $white;
|
||||||
$input-background-disabled: $light-gray;
|
$input-background-disabled: $light-gray;
|
||||||
$input-border: 1px solid $medium-gray;
|
$input-border: 1px solid $medium-gray;
|
||||||
$input-border-focus: 1px solid $dark-gray;
|
$input-border-focus: 1px solid $dark-gray;
|
||||||
|
$input-padding: $form-spacing / 2;
|
||||||
$input-shadow: inset 0 1px 2px rgba($black, 0.1);
|
$input-shadow: inset 0 1px 2px rgba($black, 0.1);
|
||||||
$input-shadow-focus: 0 0 5px $medium-gray;
|
$input-shadow-focus: 0 0 5px $medium-gray;
|
||||||
$input-cursor-disabled: not-allowed;
|
$input-cursor-disabled: not-allowed;
|
||||||
|
|
@ -388,7 +446,7 @@ $input-number-spinners: true;
|
||||||
$input-radius: $global-radius;
|
$input-radius: $global-radius;
|
||||||
$form-button-radius: $global-radius;
|
$form-button-radius: $global-radius;
|
||||||
|
|
||||||
// 20. Label
|
// 21. Label
|
||||||
// ---------
|
// ---------
|
||||||
|
|
||||||
$label-background: $primary-color;
|
$label-background: $primary-color;
|
||||||
|
|
@ -399,26 +457,28 @@ $label-font-size: 0.8rem;
|
||||||
$label-padding: 0.33333rem 0.5rem;
|
$label-padding: 0.33333rem 0.5rem;
|
||||||
$label-radius: $global-radius;
|
$label-radius: $global-radius;
|
||||||
|
|
||||||
// 21. Media Object
|
// 22. Media Object
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
||||||
$mediaobject-margin-bottom: $global-margin;
|
$mediaobject-margin-bottom: $global-margin;
|
||||||
$mediaobject-section-padding: $global-padding;
|
$mediaobject-section-padding: $global-padding;
|
||||||
$mediaobject-image-width-stacked: 100%;
|
$mediaobject-image-width-stacked: 100%;
|
||||||
|
|
||||||
// 22. Menu
|
// 23. Menu
|
||||||
// --------
|
// --------
|
||||||
|
|
||||||
$menu-margin: 0;
|
$menu-margin: 0;
|
||||||
$menu-margin-nested: 1rem;
|
$menu-nested-margin: $global-menu-nested-margin;
|
||||||
$menu-item-padding: 0.7rem 1rem;
|
$menu-items-padding: $global-menu-padding;
|
||||||
|
$menu-simple-margin: 1rem;
|
||||||
$menu-item-color-active: $white;
|
$menu-item-color-active: $white;
|
||||||
$menu-item-background-active: get-color(primary);
|
$menu-item-background-active: get-color(primary);
|
||||||
$menu-icon-spacing: 0.25rem;
|
$menu-icon-spacing: 0.25rem;
|
||||||
$menu-item-background-hover: $light-gray;
|
$menu-item-background-hover: $light-gray;
|
||||||
$menu-border: $light-gray;
|
$menu-state-back-compat: true;
|
||||||
|
$menu-centered-back-compat: true;
|
||||||
|
|
||||||
// 23. Meter
|
// 24. Meter
|
||||||
// ---------
|
// ---------
|
||||||
|
|
||||||
$meter-height: 1rem;
|
$meter-height: 1rem;
|
||||||
|
|
@ -428,23 +488,26 @@ $meter-fill-good: $success-color;
|
||||||
$meter-fill-medium: $warning-color;
|
$meter-fill-medium: $warning-color;
|
||||||
$meter-fill-bad: $alert-color;
|
$meter-fill-bad: $alert-color;
|
||||||
|
|
||||||
// 24. Off-canvas
|
// 25. Off-canvas
|
||||||
// --------------
|
// --------------
|
||||||
|
|
||||||
$offcanvas-size: 250px;
|
$offcanvas-size: 250px;
|
||||||
$offcanvas-vertical-size: 250px;
|
$offcanvas-vertical-size: 250px;
|
||||||
$offcanvas-background: $light-gray;
|
$offcanvas-background: $light-gray;
|
||||||
$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
|
$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
|
||||||
$offcanvas-push-zindex: 1;
|
$offcanvas-inner-shadow-size: 20px;
|
||||||
$offcanvas-overlap-zindex: 10;
|
$offcanvas-inner-shadow-color: rgba($black, 0.25);
|
||||||
$offcanvas-reveal-zindex: 1;
|
$offcanvas-overlay-zindex: 11;
|
||||||
|
$offcanvas-push-zindex: 12;
|
||||||
|
$offcanvas-overlap-zindex: 13;
|
||||||
|
$offcanvas-reveal-zindex: 12;
|
||||||
$offcanvas-transition-length: 0.5s;
|
$offcanvas-transition-length: 0.5s;
|
||||||
$offcanvas-transition-timing: ease;
|
$offcanvas-transition-timing: ease;
|
||||||
$offcanvas-fixed-reveal: true;
|
$offcanvas-fixed-reveal: true;
|
||||||
$offcanvas-exit-background: rgba($white, 0.25);
|
$offcanvas-exit-background: rgba($white, 0.25);
|
||||||
$maincontent-class: 'off-canvas-content';
|
$maincontent-class: 'off-canvas-content';
|
||||||
|
|
||||||
// 25. Orbit
|
// 26. Orbit
|
||||||
// ---------
|
// ---------
|
||||||
|
|
||||||
$orbit-bullet-background: $medium-gray;
|
$orbit-bullet-background: $medium-gray;
|
||||||
|
|
@ -459,7 +522,7 @@ $orbit-control-background-hover: rgba($black, 0.5);
|
||||||
$orbit-control-padding: 1rem;
|
$orbit-control-padding: 1rem;
|
||||||
$orbit-control-zindex: 10;
|
$orbit-control-zindex: 10;
|
||||||
|
|
||||||
// 26. Pagination
|
// 27. Pagination
|
||||||
// --------------
|
// --------------
|
||||||
|
|
||||||
$pagination-font-size: rem-calc(14);
|
$pagination-font-size: rem-calc(14);
|
||||||
|
|
@ -477,7 +540,7 @@ $pagination-mobile-items: false;
|
||||||
$pagination-mobile-current-item: false;
|
$pagination-mobile-current-item: false;
|
||||||
$pagination-arrows: true;
|
$pagination-arrows: true;
|
||||||
|
|
||||||
// 27. Progress Bar
|
// 28. Progress Bar
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
||||||
$progress-height: 1rem;
|
$progress-height: 1rem;
|
||||||
|
|
@ -486,7 +549,168 @@ $progress-margin-bottom: $global-margin;
|
||||||
$progress-meter-background: $primary-color;
|
$progress-meter-background: $primary-color;
|
||||||
$progress-radius: $global-radius;
|
$progress-radius: $global-radius;
|
||||||
|
|
||||||
// 28. Responsive Embed
|
// 29. Prototype Arrow
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
$prototype-arrow-directions: (
|
||||||
|
down,
|
||||||
|
up,
|
||||||
|
right,
|
||||||
|
left
|
||||||
|
);
|
||||||
|
$prototype-arrow-size: 0.4375rem;
|
||||||
|
$prototype-arrow-color: $black;
|
||||||
|
|
||||||
|
// 30. Prototype Border-Box
|
||||||
|
// ------------------------
|
||||||
|
|
||||||
|
$prototype-border-box-breakpoints: $global-prototype-breakpoints;
|
||||||
|
|
||||||
|
// 31. Prototype Border-None
|
||||||
|
// -------------------------
|
||||||
|
|
||||||
|
$prototype-border-none-breakpoints: $global-prototype-breakpoints;
|
||||||
|
|
||||||
|
// 32. Prototype Bordered
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
$prototype-bordered-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-border-width: rem-calc(1);
|
||||||
|
$prototype-border-type: solid;
|
||||||
|
$prototype-border-color: $medium-gray;
|
||||||
|
|
||||||
|
// 33. Prototype Display
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
$prototype-display-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-display: (
|
||||||
|
inline,
|
||||||
|
inline-block,
|
||||||
|
block,
|
||||||
|
table,
|
||||||
|
table-cell
|
||||||
|
);
|
||||||
|
|
||||||
|
// 34. Prototype Font-Styling
|
||||||
|
// --------------------------
|
||||||
|
|
||||||
|
$prototype-font-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-wide-letter-spacing: rem-calc(4);
|
||||||
|
$prototype-font-normal: $global-weight-normal;
|
||||||
|
$prototype-font-bold: $global-weight-bold;
|
||||||
|
|
||||||
|
// 35. Prototype List-Style-Type
|
||||||
|
// -----------------------------
|
||||||
|
|
||||||
|
$prototype-list-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-style-type-unordered: (
|
||||||
|
disc,
|
||||||
|
circle,
|
||||||
|
square
|
||||||
|
);
|
||||||
|
$prototype-style-type-ordered: (
|
||||||
|
decimal,
|
||||||
|
lower-alpha,
|
||||||
|
lower-latin,
|
||||||
|
lower-roman,
|
||||||
|
upper-alpha,
|
||||||
|
upper-latin,
|
||||||
|
upper-roman
|
||||||
|
);
|
||||||
|
|
||||||
|
// 36. Prototype Overflow
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
$prototype-overflow-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-overflow: (
|
||||||
|
visible,
|
||||||
|
hidden,
|
||||||
|
scroll
|
||||||
|
);
|
||||||
|
|
||||||
|
// 37. Prototype Position
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
$prototype-position-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-position: (
|
||||||
|
static,
|
||||||
|
relative,
|
||||||
|
absolute,
|
||||||
|
fixed
|
||||||
|
);
|
||||||
|
$prototype-position-z-index: 975;
|
||||||
|
|
||||||
|
// 38. Prototype Rounded
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
$prototype-rounded-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-border-radius: rem-calc(3);
|
||||||
|
|
||||||
|
// 39. Prototype Separator
|
||||||
|
// -----------------------
|
||||||
|
|
||||||
|
$prototype-separator-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-separator-align: center;
|
||||||
|
$prototype-separator-height: rem-calc(2);
|
||||||
|
$prototype-separator-width: 3rem;
|
||||||
|
$prototype-separator-background: $primary-color;
|
||||||
|
$prototype-separator-margin-top: $global-margin;
|
||||||
|
|
||||||
|
// 40. Prototype Shadow
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
$prototype-shadow-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),
|
||||||
|
0 2px 10px 0 rgba(0,0,0,.12);
|
||||||
|
|
||||||
|
// 41. Prototype Sizing
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
$prototype-sizing-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-sizing: (
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
);
|
||||||
|
$prototype-sizes: (
|
||||||
|
25: 25%,
|
||||||
|
50: 50%,
|
||||||
|
75: 75%,
|
||||||
|
100: 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
// 42. Prototype Spacing
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
$prototype-spacing-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-spacers-count: 3;
|
||||||
|
|
||||||
|
// 43. Prototype Text-Decoration
|
||||||
|
// -----------------------------
|
||||||
|
|
||||||
|
$prototype-decoration-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-text-decoration: (
|
||||||
|
overline,
|
||||||
|
underline,
|
||||||
|
line-through,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 44. Prototype Text-Transformation
|
||||||
|
// ---------------------------------
|
||||||
|
|
||||||
|
$prototype-transformation-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-text-transformation: (
|
||||||
|
lowercase,
|
||||||
|
uppercase,
|
||||||
|
capitalize
|
||||||
|
);
|
||||||
|
|
||||||
|
// 45. Prototype Text-Utilities
|
||||||
|
// ----------------------------
|
||||||
|
|
||||||
|
$prototype-utilities-breakpoints: $global-prototype-breakpoints;
|
||||||
|
$prototype-text-overflow: ellipsis;
|
||||||
|
|
||||||
|
// 46. Responsive Embed
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
$responsive-embed-margin-bottom: rem-calc(16);
|
$responsive-embed-margin-bottom: rem-calc(16);
|
||||||
|
|
@ -495,7 +719,7 @@ $responsive-embed-ratios: (
|
||||||
widescreen: 16 by 9,
|
widescreen: 16 by 9,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 29. Reveal
|
// 47. Reveal
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
$reveal-background: $white;
|
$reveal-background: $white;
|
||||||
|
|
@ -507,7 +731,7 @@ $reveal-radius: $global-radius;
|
||||||
$reveal-zindex: 1005;
|
$reveal-zindex: 1005;
|
||||||
$reveal-overlay-background: rgba($black, 0.45);
|
$reveal-overlay-background: rgba($black, 0.45);
|
||||||
|
|
||||||
// 30. Slider
|
// 48. Slider
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
$slider-width-vertical: 0.5rem;
|
$slider-width-vertical: 0.5rem;
|
||||||
|
|
@ -521,7 +745,7 @@ $slider-handle-background: $primary-color;
|
||||||
$slider-opacity-disabled: 0.25;
|
$slider-opacity-disabled: 0.25;
|
||||||
$slider-radius: $global-radius;
|
$slider-radius: $global-radius;
|
||||||
|
|
||||||
// 31. Switch
|
// 49. Switch
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
$switch-background: $medium-gray;
|
$switch-background: $medium-gray;
|
||||||
|
|
@ -537,7 +761,7 @@ $switch-paddle-offset: 0.25rem;
|
||||||
$switch-paddle-radius: $global-radius;
|
$switch-paddle-radius: $global-radius;
|
||||||
$switch-paddle-transition: all 0.25s ease-out;
|
$switch-paddle-transition: all 0.25s ease-out;
|
||||||
|
|
||||||
// 32. Table
|
// 50. Table
|
||||||
// ---------
|
// ---------
|
||||||
|
|
||||||
$table-background: $white;
|
$table-background: $white;
|
||||||
|
|
@ -557,8 +781,9 @@ $table-foot-row-hover: darken($table-foot-background, $table-hover-scale);
|
||||||
$table-head-font-color: $body-font-color;
|
$table-head-font-color: $body-font-color;
|
||||||
$table-foot-font-color: $body-font-color;
|
$table-foot-font-color: $body-font-color;
|
||||||
$show-header-for-stacked: false;
|
$show-header-for-stacked: false;
|
||||||
|
$table-stack-breakpoint: medium;
|
||||||
|
|
||||||
// 33. Tabs
|
// 51. Tabs
|
||||||
// --------
|
// --------
|
||||||
|
|
||||||
$tab-margin: 0;
|
$tab-margin: 0;
|
||||||
|
|
@ -575,7 +800,7 @@ $tab-content-border: $light-gray;
|
||||||
$tab-content-color: $body-font-color;
|
$tab-content-color: $body-font-color;
|
||||||
$tab-content-padding: 1rem;
|
$tab-content-padding: 1rem;
|
||||||
|
|
||||||
// 34. Thumbnail
|
// 52. Thumbnail
|
||||||
// -------------
|
// -------------
|
||||||
|
|
||||||
$thumbnail-border: solid 4px $white;
|
$thumbnail-border: solid 4px $white;
|
||||||
|
|
@ -585,7 +810,7 @@ $thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
|
||||||
$thumbnail-transition: box-shadow 200ms ease-out;
|
$thumbnail-transition: box-shadow 200ms ease-out;
|
||||||
$thumbnail-radius: $global-radius;
|
$thumbnail-radius: $global-radius;
|
||||||
|
|
||||||
// 35. Title Bar
|
// 53. Title Bar
|
||||||
// -------------
|
// -------------
|
||||||
|
|
||||||
$titlebar-background: $black;
|
$titlebar-background: $black;
|
||||||
|
|
@ -596,20 +821,22 @@ $titlebar-icon-color: $white;
|
||||||
$titlebar-icon-color-hover: $medium-gray;
|
$titlebar-icon-color-hover: $medium-gray;
|
||||||
$titlebar-icon-spacing: 0.25rem;
|
$titlebar-icon-spacing: 0.25rem;
|
||||||
|
|
||||||
// 36. Tooltip
|
// 54. Tooltip
|
||||||
// -----------
|
// -----------
|
||||||
|
|
||||||
|
$has-tip-cursor: help;
|
||||||
$has-tip-font-weight: $global-weight-bold;
|
$has-tip-font-weight: $global-weight-bold;
|
||||||
$has-tip-border-bottom: dotted 1px $dark-gray;
|
$has-tip-border-bottom: dotted 1px $dark-gray;
|
||||||
$tooltip-background-color: $black;
|
$tooltip-background-color: $black;
|
||||||
$tooltip-color: $white;
|
$tooltip-color: $white;
|
||||||
$tooltip-padding: 0.75rem;
|
$tooltip-padding: 0.75rem;
|
||||||
|
$tooltip-max-width: 10rem;
|
||||||
$tooltip-font-size: $small-font-size;
|
$tooltip-font-size: $small-font-size;
|
||||||
$tooltip-pip-width: 0.75rem;
|
$tooltip-pip-width: 0.75rem;
|
||||||
$tooltip-pip-height: $tooltip-pip-width * 0.866;
|
$tooltip-pip-height: $tooltip-pip-width * 0.866;
|
||||||
$tooltip-radius: $global-radius;
|
$tooltip-radius: $global-radius;
|
||||||
|
|
||||||
// 37. Top Bar
|
// 55. Top Bar
|
||||||
// -----------
|
// -----------
|
||||||
|
|
||||||
$topbar-padding: 0.5rem;
|
$topbar-padding: 0.5rem;
|
||||||
|
|
@ -618,3 +845,18 @@ $topbar-submenu-background: $topbar-background;
|
||||||
$topbar-title-spacing: 0.5rem 1rem 0.5rem 0;
|
$topbar-title-spacing: 0.5rem 1rem 0.5rem 0;
|
||||||
$topbar-input-width: 200px;
|
$topbar-input-width: 200px;
|
||||||
$topbar-unstack-breakpoint: medium;
|
$topbar-unstack-breakpoint: medium;
|
||||||
|
|
||||||
|
// 56. Xy Grid
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
$xy-grid: true;
|
||||||
|
$grid-container: $global-width;
|
||||||
|
$grid-columns: 12;
|
||||||
|
$grid-margin-gutters: (
|
||||||
|
small: 20px,
|
||||||
|
medium: 30px
|
||||||
|
);
|
||||||
|
$grid-padding-gutters: $grid-margin-gutters;
|
||||||
|
$grid-container-padding: $grid-padding-gutters;
|
||||||
|
$grid-container-max: $global-width;
|
||||||
|
$block-grid-max: 8;
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,5 @@
|
||||||
*= require foundation_and_overrides
|
*= require foundation_and_overrides
|
||||||
*= require cookies_eu
|
*= require cookies_eu
|
||||||
*= require fancybox
|
*= require fancybox
|
||||||
* Flash/notifications using http://www.amaranjs.com/
|
|
||||||
* https://github.com/hakanersu/AmaranJS
|
|
||||||
*= require amaran
|
|
||||||
*= require font-awesome
|
*= require font-awesome
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -370,3 +370,26 @@ a.black
|
||||||
// font-weight: bold;
|
// font-weight: bold;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
box-shadow: 0px 0px 5px #000000;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple animation for FontAwesome icons in callouts at the top.
|
||||||
|
.fa-wobble {
|
||||||
|
animation-name: fa-wobble;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-direction: alternate;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fa-wobble {
|
||||||
|
0% { opacity: 0.2; }
|
||||||
|
50% { opacity: 0.8; }
|
||||||
|
100% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu .menu-text {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,15 @@ class ApplicationController < ActionController::Base
|
||||||
# filter_parameter_logging :password
|
# filter_parameter_logging :password
|
||||||
|
|
||||||
before_action :get_current_users_screenshots, :debian_sso
|
before_action :get_current_users_screenshots, :debian_sso
|
||||||
|
# TODO: Deprecation. What is the replacement for Rails.env.development? ?
|
||||||
|
before_action :better_errors_hack, if: -> { Rails.env.development? }
|
||||||
|
|
||||||
# Query for packages that were uploaded by the current user.
|
# Query for packages that were uploaded by the current user.
|
||||||
# As AAA is not yet implemented it means looking for uploads
|
# If the user is anonymous then find the uploads by the cookie token.
|
||||||
# that correspond to the user's cookie token.
|
# If the user is logged in then find the uploads by matching the user id.
|
||||||
def get_current_users_screenshots
|
def get_current_users_screenshots
|
||||||
if session[:token]
|
if user_signed_in?
|
||||||
@current_users_screenshots = Screenshot.uploaded_by(session[:token])
|
@current_users_screenshots = current_user.screenshots
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -50,4 +52,11 @@ class ApplicationController < ActionController::Base
|
||||||
def after_sign_in_path_for(resource)
|
def after_sign_in_path_for(resource)
|
||||||
my_welcome_path
|
my_welcome_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# better_errors gets extremely slow without this hack
|
||||||
|
# see: https://github.com/charliesome/better_errors/issues/341
|
||||||
|
def better_errors_hack
|
||||||
|
env['puma.config'].options.user_options.delete :app
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ class PackagesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def details
|
def details
|
||||||
|
# TODO: Get only screenshots visible to the user (admin or owner or approved)
|
||||||
@package = Package.find_by(name: params[:name])
|
@package = Package.find_by(name: params[:name])
|
||||||
|
|
||||||
unless @package
|
unless @package
|
||||||
|
|
@ -26,15 +27,19 @@ class PackagesController < ApplicationController
|
||||||
|
|
||||||
# POST target of the screenshots upload form.
|
# POST target of the screenshots upload form.
|
||||||
# Receives uploaded images. Checks if they are valid. Asks for description.
|
# Receives uploaded images. Checks if they are valid. Asks for description.
|
||||||
|
# This action saves the screenshots already if they are valid. The user is
|
||||||
|
# then given the chance to comment on and delete the screenshots again.
|
||||||
def upload_receive
|
def upload_receive
|
||||||
# Send a cookie to remember the user by the cookie session.
|
# Create a pseudo user account for the user.
|
||||||
create_user_token
|
# The user won't know that an account is created.
|
||||||
|
# But this makes it easier to track who screenshots belong to.
|
||||||
|
create_pseudo_user unless user_signed_in?
|
||||||
|
|
||||||
@package = Package.find_by!(name: params[:name])
|
@package = Package.find_by!(name: params[:name])
|
||||||
@valid_images = []
|
@valid_images = []
|
||||||
@invalid_images = []
|
@invalid_images = []
|
||||||
|
|
||||||
params[:file].each do |img|
|
params[:file].each do |img|
|
||||||
new_screenshot = @package.screenshots.new(image: img)
|
new_screenshot = @package.screenshots.new(image: img)
|
||||||
|
|
||||||
# Check if the image was valid
|
# Check if the image was valid
|
||||||
|
|
@ -42,19 +47,48 @@ class PackagesController < ApplicationController
|
||||||
new_screenshot.uploaderhash = session[:token]
|
new_screenshot.uploaderhash = session[:token]
|
||||||
new_screenshot.uploaderip = session[:ip]
|
new_screenshot.uploaderip = session[:ip]
|
||||||
new_screenshot.version = @package.version
|
new_screenshot.version = @package.version
|
||||||
|
new_screenshot.user = current_user
|
||||||
|
|
||||||
|
# TODO: must it be moderated first?
|
||||||
|
|
||||||
new_screenshot.save
|
new_screenshot.save
|
||||||
Log.log "Screenshot #{new_screenshot.id} uploaded successfully from #{session[:ip]}. User has token #{session[:token]}"
|
Log.log "Screenshot #{new_screenshot.id} uploaded successfully from #{session[:ip]}. User has token #{session[:token]} or is #{current_user}"
|
||||||
|
|
||||||
|
if current_user.is_admin?
|
||||||
|
Log.log "Admin upload is automatically approved."
|
||||||
|
new_screenshot.approve_screenshot!
|
||||||
|
end
|
||||||
|
|
||||||
@valid_images.push new_screenshot
|
@valid_images.push new_screenshot
|
||||||
else
|
else
|
||||||
|
Log.log "Screenshot #{new_screenshot.image_file_name} invalid (#{new_screenshot.errors[:image]})."
|
||||||
@invalid_images.push new_screenshot
|
@invalid_images.push new_screenshot
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
@invalid_images.each do |image|
|
||||||
|
# errors << "The image #{image.image_file_name} #{image.errors[:image].join(' and ')}."
|
||||||
|
errors << "The image #{image.image_file_name} is not valid."
|
||||||
|
flash[:error] = errors.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Redirect back to upload form if all uploads were invalid
|
||||||
|
unless @valid_images.any?
|
||||||
|
Log.log "No valid images uploaded. Back to upload form."
|
||||||
|
redirect_to(upload_path, error: errors) and return
|
||||||
|
end
|
||||||
|
|
||||||
|
# Show a list of invalid uploads by default. Or redirect to the review page
|
||||||
|
# if all uploads were okay.
|
||||||
|
# redirect_to upload_review_path unless @invalid_images
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# if @invalid_images…
|
# if @invalid_images…
|
||||||
# ' #{image.image_file_name} (#{image.errors[:image].join(' and ')})
|
# ' #{image.image_file_name} (#{image.errors[:image].join(' and ')})
|
||||||
|
|
||||||
# redirect_to package_path
|
redirect_to package_path
|
||||||
|
# render :details
|
||||||
end
|
end
|
||||||
|
|
||||||
# Legacy action to upload an image along with metadata.
|
# Legacy action to upload an image along with metadata.
|
||||||
|
|
@ -90,10 +124,13 @@ class PackagesController < ApplicationController
|
||||||
# Is the user allowed to delete the screenshot?
|
# Is the user allowed to delete the screenshot?
|
||||||
@screenshot = Screenshot.find(params[:id])
|
@screenshot = Screenshot.find(params[:id])
|
||||||
|
|
||||||
if user_can_alter_screenshot?
|
# Check if the user is allowed to change this screenshot
|
||||||
|
# - Is this the user's own screenshot? (anonymous)
|
||||||
|
if @screenshot.user == current_user or current_user.is_admin?
|
||||||
|
logger.debug "User #{current_user} deletes screenshot #{@screenshot}"
|
||||||
@screenshot.destroy
|
@screenshot.destroy
|
||||||
flash['notice'] = "Screenshot deleted."
|
flash['notice'] = "Screenshot deleted."
|
||||||
redirect_to :back
|
redirect_back(fallback_location: package_path(name: @screenshot.package.name))
|
||||||
else
|
else
|
||||||
head :forbidden
|
head :forbidden
|
||||||
end
|
end
|
||||||
|
|
@ -103,7 +140,7 @@ class PackagesController < ApplicationController
|
||||||
@screenshot = Screenshot.find(params[:id])
|
@screenshot = Screenshot.find(params[:id])
|
||||||
@screenshot.approve_screenshot!
|
@screenshot.approve_screenshot!
|
||||||
flash['notice'] = "Screenshot approved."
|
flash['notice'] = "Screenshot approved."
|
||||||
redirect_to :back
|
redirect_back(fallback_location: package_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a 160x120 thumbnail image if posssible.
|
# Returns a 160x120 thumbnail image if posssible.
|
||||||
|
|
@ -120,7 +157,7 @@ class PackagesController < ApplicationController
|
||||||
# Called as /thumbnail-with-version/:name/:version
|
# Called as /thumbnail-with-version/:name/:version
|
||||||
if params[:version]
|
if params[:version]
|
||||||
@screenshot = @package.best_screenshot_for_version(params[:version])
|
@screenshot = @package.best_screenshot_for_version(params[:version])
|
||||||
# Called as /thumbnail/:name
|
# Called as /thumbnail/:name
|
||||||
else
|
else
|
||||||
@screenshot = @package.screenshots.first
|
@screenshot = @package.screenshots.first
|
||||||
end
|
end
|
||||||
|
|
@ -149,7 +186,7 @@ class PackagesController < ApplicationController
|
||||||
# Called as /thumbnail-with-version/:name/:version
|
# Called as /thumbnail-with-version/:name/:version
|
||||||
if params[:version]
|
if params[:version]
|
||||||
@screenshot = @package.best_screenshot_for_version(params[:version])
|
@screenshot = @package.best_screenshot_for_version(params[:version])
|
||||||
# Called as /thumbnail/:name
|
# Called as /thumbnail/:name
|
||||||
else
|
else
|
||||||
@screenshot = @package.screenshots.first
|
@screenshot = @package.screenshots.first
|
||||||
end
|
end
|
||||||
|
|
@ -167,17 +204,19 @@ class PackagesController < ApplicationController
|
||||||
# Receives a form with a simple text field 'description' so that users can update
|
# Receives a form with a simple text field 'description' so that users can update
|
||||||
# the description of their screenshot.
|
# the description of their screenshot.
|
||||||
def update_screenshot_description
|
def update_screenshot_description
|
||||||
@screenshot = Screenshot.find(params[:id])
|
# TODO: Check permissions to do that
|
||||||
@screenshot.description = params[:description]
|
@screenshot = Screenshot.find(params[:id])
|
||||||
@screenshot.save!
|
# @screenshot.description = params[:description]
|
||||||
flash['notice'] = "Description updated."
|
@screenshot.update params_screenshot_description
|
||||||
redirect_to :back
|
@screenshot.save!
|
||||||
end
|
flash['notice'] = "Description updated."
|
||||||
|
redirect_back(fallback_location: package_path(name: @screenshot.package.name))
|
||||||
|
end
|
||||||
|
|
||||||
# Receive an anonymous report from a user to have a screenshot removed.
|
# Receive an anonymous report from a user to have a screenshot removed.
|
||||||
def report_screenshot
|
def report_screenshot
|
||||||
@screenshot = Screenshot.find(params[:id])
|
@screenshot = Screenshot.find(params[:id])
|
||||||
# if verify_recaptcha
|
# if verify_recaptcha
|
||||||
@screenshot.delete_reason = params[:delete_reason]
|
@screenshot.delete_reason = params[:delete_reason]
|
||||||
@screenshot.markedfordelete = true
|
@screenshot.markedfordelete = true
|
||||||
if @screenshot.valid?
|
if @screenshot.valid?
|
||||||
|
|
@ -187,46 +226,57 @@ class PackagesController < ApplicationController
|
||||||
errors = @screenshot.errors.to_a.join(' and ')
|
errors = @screenshot.errors.to_a.join(' and ')
|
||||||
flash['alert'] = "Sorry. #{errors}"
|
flash['alert'] = "Sorry. #{errors}"
|
||||||
end
|
end
|
||||||
# end
|
# end
|
||||||
redirect_to :back
|
redirect_back(fallback_location: package_path)
|
||||||
end
|
|
||||||
|
|
||||||
# Show an HTML partial with reviews of this package from the Ubuntu API
|
|
||||||
def reviews
|
|
||||||
expires_in 1.day, public: true
|
|
||||||
# @reviews = Package.find_by_name!(params[:name]).ubuntu_reviews
|
|
||||||
@reviews = get_ubuntu_reviews params[:name]
|
|
||||||
render '_reviews', layout: false
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Send a dummy thumbnail reading "No screenshot available. Sorry."
|
|
||||||
def thumbnail404
|
|
||||||
send_file Rails.root.join('public/images/dummy/thumbnail404.png'),
|
|
||||||
type: "image/png",
|
|
||||||
disposition: 'inline',
|
|
||||||
status: 404
|
|
||||||
end
|
|
||||||
|
|
||||||
def screenshot404
|
|
||||||
send_file Rails.root.join('public/images/dummy/screenshot404.png'),
|
|
||||||
type: "image/png",
|
|
||||||
disposition: 'inline',
|
|
||||||
status: 404
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return packages matching the criteria given by parameters
|
|
||||||
def query_packages
|
|
||||||
packages = Package.includes(:screenshots).order(visits: :desc)
|
|
||||||
|
|
||||||
# text search
|
|
||||||
if params[:search].present?
|
|
||||||
logger.debug "Searching for #{params[:search]}"
|
|
||||||
packages = packages.general_search(params[:search])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
case params[:show]
|
# Show an HTML partial with reviews of this package from the Ubuntu API
|
||||||
|
def reviews
|
||||||
|
expires_in 1.day, public: true
|
||||||
|
# @reviews = Package.find_by_name!(params[:name]).ubuntu_reviews
|
||||||
|
@reviews = get_ubuntu_reviews params[:name]
|
||||||
|
render '_reviews', layout: false
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Seamlessly create a user account for the current client.
|
||||||
|
# It helps track uploads.
|
||||||
|
def create_pseudo_user
|
||||||
|
generated_password = Devise.friendly_token.first(8)
|
||||||
|
new_user = User.create(
|
||||||
|
name: 'Anonymous',
|
||||||
|
password: generated_password)
|
||||||
|
Log.log "New pseudo user for anonymous upload created: #{new_user}"
|
||||||
|
sign_in(new_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Send a dummy thumbnail reading "No screenshot available. Sorry."
|
||||||
|
def thumbnail404
|
||||||
|
send_file Rails.root.join('public/images/dummy/thumbnail404.png'),
|
||||||
|
type: "image/png",
|
||||||
|
disposition: 'inline',
|
||||||
|
status: 404
|
||||||
|
end
|
||||||
|
|
||||||
|
def screenshot404
|
||||||
|
send_file Rails.root.join('public/images/dummy/screenshot404.png'),
|
||||||
|
type: "image/png",
|
||||||
|
disposition: 'inline',
|
||||||
|
status: 404
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return packages matching the criteria given by parameters
|
||||||
|
def query_packages
|
||||||
|
packages = Package.includes(:screenshots).order(visits: :desc)
|
||||||
|
|
||||||
|
# text search
|
||||||
|
if params[:search].present?
|
||||||
|
logger.debug "Searching for #{params[:search]}"
|
||||||
|
packages = packages.general_search(params[:search])
|
||||||
|
end
|
||||||
|
|
||||||
|
case params[:show]
|
||||||
when 'with'
|
when 'with'
|
||||||
packages = packages.with_screenshots
|
packages = packages.with_screenshots
|
||||||
logger.debug 'Limiting packages to those with screenshots'
|
logger.debug 'Limiting packages to those with screenshots'
|
||||||
|
|
@ -238,13 +288,6 @@ class PackagesController < ApplicationController
|
||||||
return packages
|
return packages
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the user is allowed to do changed to a screenshot
|
|
||||||
def user_can_alter_screenshot?
|
|
||||||
# - Is this the user's own screenshot?
|
|
||||||
# - Is the user an admin (=logged in)?
|
|
||||||
@screenshot.uploaderhash == session[:token] or user_signed_in?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Store a random identifier and the client's IP address in the session
|
# Store a random identifier and the client's IP address in the session
|
||||||
# for later identification.
|
# for later identification.
|
||||||
def create_user_token
|
def create_user_token
|
||||||
|
|
@ -252,7 +295,6 @@ class PackagesController < ApplicationController
|
||||||
session[:ip] ||= request.remote_ip
|
session[:ip] ||= request.remote_ip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Get reviews of this package from the Ubuntu API
|
# Get reviews of this package from the Ubuntu API
|
||||||
def get_ubuntu_reviews(packagename)
|
def get_ubuntu_reviews(packagename)
|
||||||
# Use the URL defined in the configuration to get a JSON string
|
# Use the URL defined in the configuration to get a JSON string
|
||||||
|
|
@ -270,4 +312,7 @@ class PackagesController < ApplicationController
|
||||||
return json
|
return json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def params_screenshot_description
|
||||||
|
params.require(:screenshot).permit(:description)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@ module PackagesHelper
|
||||||
# Return a query of all screenshots that the current user may see
|
# Return a query of all screenshots that the current user may see
|
||||||
# Consists of:
|
# Consists of:
|
||||||
# - approved (public) screenshots
|
# - approved (public) screenshots
|
||||||
# - screenshots uploaded by the user (determined by cookie session)
|
# - screenshots uploaded by the user
|
||||||
# - all screenshots if the user is logged in
|
# - all screenshots if the user is an admin
|
||||||
def screenshots_visible_to_user(package)
|
def screenshots_visible_to_user(package)
|
||||||
if user_signed_in?
|
if user_signed_in? and current_user.is_admin?
|
||||||
# User is an admin
|
# User is an admin
|
||||||
package.screenshots.order('created_at DESC')
|
package.screenshots
|
||||||
|
elsif user_signed_in?
|
||||||
|
current_user.screenshots
|
||||||
else
|
else
|
||||||
package.screenshots.where(
|
package.screenshots.where(approved: true).order('created_at DESC')
|
||||||
"approved=true OR uploaderhash=?", session[:token]
|
|
||||||
).order('created_at DESC')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,6 @@ class Log < ApplicationRecord
|
||||||
logger.error "Could not log to database - validation errors: #{log.errors.to_a}" unless log.valid?
|
logger.error "Could not log to database - validation errors: #{log.errors.to_a}" unless log.valid?
|
||||||
log.save
|
log.save
|
||||||
# log
|
# log
|
||||||
|
logger.info "Log.log - #{log.section} - #{log.message}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class Package < ApplicationRecord
|
||||||
|
|
||||||
# Return a query of all approved/public screenshots of this package
|
# Return a query of all approved/public screenshots of this package
|
||||||
def screenshots_approved
|
def screenshots_approved
|
||||||
self.screenshots.find_by(approved: true)
|
self.screenshots.where(approved: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a query of all approved/public screenshots of this package
|
# Return a query of all approved/public screenshots of this package
|
||||||
|
|
|
||||||
|
|
@ -90,33 +90,8 @@ class Screenshot < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the user is allowed to change this screenshot
|
|
||||||
def user_can_alter?
|
|
||||||
# - Is this the user's own screenshot? (anonymous)
|
|
||||||
if self.uploaderhash == session[:token]
|
|
||||||
logger.debug "Screenshot belongs to current anonymous user"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# - Is this the user's own screenshot? (anonymous)
|
|
||||||
if self.user = current_user
|
|
||||||
logger.debug "Screenshot belongs to user who is currently logged in"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# - Is the user an admin (=logged in)?
|
|
||||||
if current_user.can_admin?
|
|
||||||
logger.debug "User is an administrator and has super powers"
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Publish a screenshot from the moderation queue
|
# Publish a screenshot from the moderation queue
|
||||||
def approve_screenshot!
|
def approve_screenshot!
|
||||||
|
|
||||||
|
|
||||||
self.delete_reason = nil
|
self.delete_reason = nil
|
||||||
self.markedfordelete = false
|
self.markedfordelete = false
|
||||||
self.approved = true
|
self.approved = true
|
||||||
|
|
@ -131,11 +106,6 @@ class Screenshot < ApplicationRecord
|
||||||
self.find_by(approved: true)
|
self.find_by(approved: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Query for screenshots being uploaded by a certain user (by their token)
|
|
||||||
def self.uploaded_by(token)
|
|
||||||
self.where(approved: false, uploaderhash: token)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check whether the user has administrative permissions
|
# Check whether the user has administrative permissions
|
||||||
def can_admin?
|
def can_admin?
|
||||||
self.admin == 1
|
self.admin == 1
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ class User < ApplicationRecord
|
||||||
'Amazon'
|
'Amazon'
|
||||||
when 'github'
|
when 'github'
|
||||||
'GitHub'
|
'GitHub'
|
||||||
|
else
|
||||||
|
'local authentication'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,18 @@
|
||||||
- if flash
|
- if flash
|
||||||
- flash.each do |name,msg|
|
- flash.each do |name,msg|
|
||||||
- case name
|
- case name
|
||||||
- when 'notice'
|
- when 'timedout'
|
||||||
- flash_color = 'white'
|
/ Workaround for Devise sometimes sending "true"
|
||||||
- flash_bgcolor = 'green'
|
- next
|
||||||
- when 'alert'
|
- when 'notice'
|
||||||
- flash_color = 'white'
|
.callout.warning data-closable=true
|
||||||
- flash_bgcolor = 'red'
|
= fa_icon 'info-circle wobble 2x'
|
||||||
- else
|
strong< = msg
|
||||||
- flash_color = 'black'
|
- when 'error'
|
||||||
- flash_bgcolor = 'gray'
|
.callout.alert data-closable=true
|
||||||
|
= fa_icon 'meh-o wobble 2x'
|
||||||
javascript:
|
strong< = msg
|
||||||
$.amaran({
|
- else
|
||||||
'content': {
|
.callout.primary data-closable=true
|
||||||
'message' :'#{msg}',
|
= fa_icon 'info-circle wobble 2x'
|
||||||
'color' : '#{flash_color}',
|
strong< = msg
|
||||||
'bgcolor' : '#{flash_bgcolor}'
|
|
||||||
},
|
|
||||||
'position' :'bottom right',
|
|
||||||
'theme' : 'colorful',
|
|
||||||
'delay' : 7000
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
nav.top-bar
|
.top-bar
|
||||||
.row
|
.top-bar-left
|
||||||
.top-bar-left
|
ul.menu
|
||||||
.top-bar-title
|
li.menu-text
|
||||||
a href='/'
|
a href='/'
|
||||||
// TODO: Make the logo depend on the virtual host
|
// TODO: Make the logo depend on the virtual host
|
||||||
img src="/images/logos/debian.svg" width="35" alt="Debian logo"
|
img src="/images/logos/debian.svg" width="35" alt="Debian logo"
|
||||||
// TODO: Use proper distribution name
|
// TODO: Use proper distribution name
|
||||||
' Debian Screenshots >
|
' Debian Screenshots >
|
||||||
|
|
||||||
.top-bar-right
|
.top-bar-right
|
||||||
ul.dropdown.menu data-dropdown-menu=true
|
ul.dropdown.menu data-dropdown-menu=true
|
||||||
li class=('active' if controller_name=='welcome' and action_name=='home')
|
li class=('active' if controller_name=='welcome' and action_name=='home')
|
||||||
a href="/" Home
|
a href="/" Home
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
.small-6.columns
|
.small-6.columns
|
||||||
p Please moderate:
|
p Please moderate:
|
||||||
- @pending_screenshots.each do |screenshot|
|
- @pending_screenshots.each do |screenshot|
|
||||||
.container.listview.text-center
|
.container.listview
|
||||||
/ = screenshot.status
|
/ = screenshot.status
|
||||||
a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption
|
a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption
|
||||||
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,16 @@
|
||||||
|
|
||||||
/ TODO: submit - save
|
/ TODO: submit - save
|
||||||
|
|
||||||
h2 Your email address
|
- unless current_user.email.blank?
|
||||||
p = current_user.email
|
h2 Your email address
|
||||||
p.help-text
|
p = current_user.email
|
||||||
' This web site recognizes you by your email address.
|
p.help-text
|
||||||
' Don't worry - it will not be shown or given to anyone.
|
' This web site recognizes you by your email address.
|
||||||
|
' Don't worry - it will not be shown or given to anyone.
|
||||||
|
|
||||||
h2 Single-sign-on provider
|
- unless current_user.provider.blank?
|
||||||
p You logged in using #{current_user.pretty_provider}.
|
h2 Single-sign-on provider
|
||||||
|
p You logged in using #{current_user.pretty_provider}.
|
||||||
|
|
||||||
h2 Number of screenshots you uploaded
|
h2 Number of screenshots you uploaded
|
||||||
p = current_user.screenshots.count
|
p = current_user.screenshots.count
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,33 @@
|
||||||
// Button that reveals a dropdown/modal for moderation/reporting
|
// Button that reveals a dropdown/modal for moderation/reporting
|
||||||
.button-bar
|
button-group
|
||||||
ul.button-group
|
- if user_signed_in?
|
||||||
- if not screenshot.approved
|
a.button.small.alert[
|
||||||
li
|
href=delete_screenshot_path(screenshot.id)
|
||||||
a.button.small.success[
|
onclick="return confirm('Really delete the screenshot?');"
|
||||||
href=approve_screenshot_path(screenshot.id)
|
] Delete screenshot
|
||||||
method='post'
|
|
||||||
] Approve screenshot
|
|
||||||
- elsif screenshot.markedfordelete
|
|
||||||
li
|
|
||||||
a.button.small.success[
|
|
||||||
href=approve_screenshot_path(screenshot.id)
|
|
||||||
method='post'
|
|
||||||
] Keep screenshot
|
|
||||||
|
|
||||||
- if user_signed_in?
|
|
||||||
li
|
|
||||||
a.button.small.alert[
|
|
||||||
href=delete_screenshot_path(screenshot.id)
|
|
||||||
onclick="return confirm('Really delete the screenshot?');"
|
|
||||||
] Delete screenshot
|
|
||||||
|
|
||||||
/ Display additional information to admins
|
- if not screenshot.approved
|
||||||
p Status: #{screenshot.adminstatus}
|
a.button.small.success[
|
||||||
p Uploader IP=#{screenshot.uploaderip}
|
href=approve_screenshot_path(screenshot.id)
|
||||||
p Uploader Token=#{session[:token]}
|
method='post'
|
||||||
p Uploaded #{screenshot.age_days} (#{screenshot.created_at})
|
] Approve screenshot
|
||||||
|
- elsif screenshot.markedfordelete
|
||||||
|
a.button.small.success[
|
||||||
|
href=approve_screenshot_path(screenshot.id)
|
||||||
|
method='post'
|
||||||
|
] Keep screenshot
|
||||||
|
|
||||||
/ TODO: Move reporting screenshots to an extra page with a form and captcha
|
|
||||||
/ - else
|
/ Display additional information to admins
|
||||||
/ / Allow anonymous users to report inappropriate screenshots
|
p Status: #{screenshot.adminstatus}
|
||||||
/ = form_tag(report_screenshot_path(screenshot.id))
|
p Uploader IP=#{screenshot.uploaderip}
|
||||||
/ = text_area_tag 'delete_reason', nil, class: 'input-group-field', maxlength: 100, rows: 3, cols: 50
|
p Uploader Token=#{session[:token]}
|
||||||
/ = submit_tag 'Request removal', class: 'button alert'
|
p Uploaded #{screenshot.age_days} (#{screenshot.created_at})
|
||||||
|
|
||||||
|
/ TODO: Move reporting screenshots to an extra page with a form and captcha
|
||||||
|
/ - else
|
||||||
|
/ / Allow anonymous users to report inappropriate screenshots
|
||||||
|
/ = form_tag(report_screenshot_path(screenshot.id))
|
||||||
|
/ = text_area_tag 'delete_reason', nil, class: 'input-group-field', maxlength: 100, rows: 3, cols: 50
|
||||||
|
/ = submit_tag 'Request removal', class: 'button alert'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// TODO: Handle packages with multiple screenshots
|
// TODO: Handle packages with multiple screenshots
|
||||||
a.black href=package_path(name: pkg.name)
|
a.black href=package_path(name: pkg.name)
|
||||||
div.grid-thumbnail
|
div.grid-thumbnail
|
||||||
- if pkg.screenshots.any?
|
- if pkg.screenshots_approved.any?
|
||||||
// TODO: smarter selection of the most useful screenshot instead of taking the first one
|
// TODO: smarter selection of the most useful screenshot instead of taking the first one
|
||||||
- screenshot = pkg.screenshots.first
|
- screenshot = pkg.screenshots.first
|
||||||
= image_tag(screenshot.image.url(:thumb, timestamp: false), alt: screenshot.caption, class: 'thumbnail')
|
= image_tag(screenshot.image.url(:thumb, timestamp: false), alt: screenshot.caption, class: 'thumbnail')
|
||||||
|
|
|
||||||
22
app/views/packages/_receive_upload.slim
Normal file
22
app/views/packages/_receive_upload.slim
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
/ Render this callout on top of the package's details page
|
||||||
|
/ if images have just been uploaded.
|
||||||
|
- if (@valid_images and @valid_images.any?) or (@invalid_images and @invalid_images.any?)
|
||||||
|
.row
|
||||||
|
.callout.warning
|
||||||
|
|
||||||
|
- if @invalid_images.any?
|
||||||
|
= fa_icon 'exclamation-circle 2x pull-left'
|
||||||
|
p These images were not accepted:
|
||||||
|
|
||||||
|
ul
|
||||||
|
- @invalid_images.each do |image|
|
||||||
|
li
|
||||||
|
' #{image.image_file_name} (#{image.errors[:image].join(' and the image ')})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- if @valid_images.any?
|
||||||
|
= fa_icon 'check-circle 2x pull-left'
|
||||||
|
p
|
||||||
|
' #{pluralize(@valid_images.length, 'screenshot')} received successfully.
|
||||||
|
' Please review the images and add a description for each image. Thanks.
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
// TODO: Can be deleted?
|
/ Ask the user to add a description if not yet done
|
||||||
//
|
- if screenshot.description.blank?
|
||||||
// Button that reveals a dropdown/modal for users (for their own screenshots)
|
/ TODO: Allow the user to edit the screenshot's description again?
|
||||||
.text-right
|
div
|
||||||
button.small.dropdown.warning.button type="button" data-toggle="admin-info-#{screenshot.id}"
|
= form_for screenshot, url: update_screenshot_description_path(screenshot) do |f|
|
||||||
'Manage your screenshot
|
label for='description' Please add a short description of this screenshot:
|
||||||
.dropdown-pane data-dropdown=true id="admin-info-#{screenshot.id}"
|
.input-group
|
||||||
a.button.small.alert[
|
span.input-group-label = fa_icon 'edit wobble'
|
||||||
href=delete_screenshot_path(screenshot.id)
|
= f.text_field 'description', class: 'input-group-field', maxlength: 80
|
||||||
onclick="return confirm('Really delete the screenshot?');"
|
.input-group-button
|
||||||
] Delete screenshot
|
= f.submit 'Save', class: 'button success'
|
||||||
p
|
|
||||||
i =screenshot.status
|
/ Offer to delete the screenshot
|
||||||
|
a.button.small.alert[
|
||||||
|
href=delete_screenshot_path(screenshot.id)
|
||||||
|
onclick="return confirm('Really delete your screenshot again?');"
|
||||||
|
] Delete your screenshot
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
/ = link_to 'Back', :back, class: 'button small'
|
/ If images were just uploaded then show upload success/errors
|
||||||
|
/ in a callout.
|
||||||
|
= render 'receive_upload'
|
||||||
|
|
||||||
.row.packagepage
|
.row.packagepage
|
||||||
.small-12.columns
|
.small-12.columns
|
||||||
|
|
@ -10,7 +12,7 @@
|
||||||
- if screenshots_visible_to_user(@package).count > 0
|
- if screenshots_visible_to_user(@package).count > 0
|
||||||
- screenshots_visible_to_user(@package).each do |screenshot|
|
- screenshots_visible_to_user(@package).each do |screenshot|
|
||||||
.row.listview
|
.row.listview
|
||||||
.text-center
|
/ .text-center
|
||||||
a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption
|
a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption
|
||||||
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
||||||
- if screenshot.description
|
- if screenshot.description
|
||||||
|
|
@ -26,22 +28,9 @@
|
||||||
- if user_signed_in? and current_user.is_admin?
|
- if user_signed_in? and current_user.is_admin?
|
||||||
= render(partial: 'admin_dropdown', locals: {screenshot: screenshot})
|
= render(partial: 'admin_dropdown', locals: {screenshot: screenshot})
|
||||||
// or does the screenshot belong to the user (determined by session cookie)
|
// or does the screenshot belong to the user (determined by session cookie)
|
||||||
- elsif screenshot.uploaderhash == session[:token]
|
- elsif user_signed_in? and screenshot.user == current_user
|
||||||
/ = render(partial: 'user_dropdown', locals: {screenshot: screenshot})
|
= render(partial: 'user_dropdown', locals: {screenshot: screenshot})
|
||||||
/ Ask the user to add a description if not yet done
|
|
||||||
- if screenshot.description.blank?
|
|
||||||
.imgcaption
|
|
||||||
p Please add a brief description of this screenshot:
|
|
||||||
= form_tag(update_screenshot_description_path(screenshot.package.name, screenshot.id))
|
|
||||||
.input-group
|
|
||||||
= text_field_tag 'description', nil, class: 'input-group-field', maxlength: 80
|
|
||||||
.input-group-button
|
|
||||||
=submit_tag 'Save', class: 'button success'
|
|
||||||
/ Offer to delete the screenshot
|
|
||||||
a.button.small.alert[
|
|
||||||
href=delete_screenshot_path(screenshot.id)
|
|
||||||
onclick="return confirm('Really delete your screenshot again?');"
|
|
||||||
] Delete your screenshot
|
|
||||||
|
|
||||||
// or is the user not related to the screenshot and the screenshot is public?
|
// or is the user not related to the screenshot and the screenshot is public?
|
||||||
// TODO: Move to a seperate page with a form
|
// TODO: Move to a seperate page with a form
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,14 @@
|
||||||
.pkgname
|
.pkgname
|
||||||
=pkg.name
|
=pkg.name
|
||||||
' >
|
' >
|
||||||
- if pkg.screenshots.any?
|
- if pkg.screenshots_approved.any?
|
||||||
// TODO: smarter search for the best screenshot instead of taking the first one
|
// TODO: smarter search for the best screenshot instead of taking the first one
|
||||||
- screenshot = pkg.screenshots.first
|
- screenshot = pkg.screenshots.first
|
||||||
a.black title=screenshot.caption href=package_path(name: pkg.name)
|
a.black title=screenshot.caption href=package_path(name: pkg.name)
|
||||||
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
||||||
- if pkg.screenshots.length > 1
|
- if pkg.screenshots.length > 1
|
||||||
a.black href=package_path(name: pkg.name)
|
a.black href=package_path(name: pkg.name)
|
||||||
.text-center and #{pluralize(pkg.screenshots.length-1, 'screenshot')} more…
|
'and #{pluralize(pkg.screenshots.length-1, 'screenshot')} more…
|
||||||
- else
|
- else
|
||||||
a href=package_path(name: pkg.name)
|
a href=package_path(name: pkg.name)
|
||||||
img.screenshot src="/images/dummy/no-screenshots-upload-one.svg"
|
img.screenshot src="/images/dummy/no-screenshots-upload-one.svg"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
= render 'receive_upload'
|
||||||
|
|
||||||
.row
|
.row
|
||||||
h1 = "Upload screenshots for #{@package.name}"
|
h1 = "Upload screenshots for #{@package.name}"
|
||||||
|
|
||||||
|
|
|
||||||
23
app/views/packages/upload_receive.slim
Normal file
23
app/views/packages/upload_receive.slim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
h1 Nothing to see here!
|
||||||
|
|
||||||
|
/ / Show upload success/errors and load details view.
|
||||||
|
/ .row
|
||||||
|
/ .callout
|
||||||
|
/
|
||||||
|
/ - if @invalid_images.any?
|
||||||
|
/ p These images were not accepted:
|
||||||
|
/
|
||||||
|
/ ul
|
||||||
|
/ - @invalid_images.each do |image|
|
||||||
|
/ li
|
||||||
|
/ ' #{image.image_file_name} (#{image.errors[:image].join(' and the image ')})
|
||||||
|
/
|
||||||
|
/
|
||||||
|
/
|
||||||
|
/ - if @valid_images.any?
|
||||||
|
/ .row
|
||||||
|
/ p
|
||||||
|
/ ' #{pluralize(@valid_images.length, 'screenshot')} received successfully.
|
||||||
|
/ ' Please review the images and add a description for each image. Thanks.
|
||||||
|
/
|
||||||
|
/ = render 'packages/details.slim'
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
.row
|
|
||||||
h1 Your uploaded images
|
|
||||||
|
|
||||||
.row
|
|
||||||
p
|
|
||||||
' Thanks for your upload. Please check the images and add
|
|
||||||
' short one-line descriptions that describe each of the images.
|
|
||||||
|
|
||||||
- if @invalid_images.any?
|
|
||||||
.callout.warning
|
|
||||||
p
|
|
||||||
' However the following screenshots were not accepted:
|
|
||||||
ul
|
|
||||||
- @invalid_images.each do |image|
|
|
||||||
li
|
|
||||||
' #{image.image_file_name} (#{image.errors[:image].join(' and ')})
|
|
||||||
|
|
||||||
|
|
||||||
- if @valid_images.any?
|
|
||||||
|
|
||||||
= form_for :screenshot, url: upload_review2_path do |f|
|
|
||||||
|
|
||||||
- @valid_images.each do |screenshot|
|
|
||||||
|
|
||||||
.row
|
|
||||||
|
|
||||||
.small-4.columns
|
|
||||||
a.black.fancybox href=screenshot.image.url(:large, timestamp: false) title='Uploaded image'
|
|
||||||
= image_tag(screenshot.image.url(:large, timestamp: false), alt: 'Uploaded image')
|
|
||||||
.small-8.columns
|
|
||||||
em Your file name:
|
|
||||||
p
|
|
||||||
= screenshot.image_file_name
|
|
||||||
|
|
||||||
em Description:
|
|
||||||
p
|
|
||||||
= text_field_tag "description-#{screenshot.id}"
|
|
||||||
|
|
||||||
em Delete this image?
|
|
||||||
p
|
|
||||||
= check_box "delete-#{screenshot.id}", "Delete this image"
|
|
||||||
|
|
||||||
.row
|
|
||||||
button.button type="submit" id="file-submit"
|
|
||||||
= fa_stacked_icon "check", base: "circle-thin"
|
|
||||||
' Done
|
|
||||||
|
|
||||||
- else
|
|
||||||
.row
|
|
||||||
a.button href=upload_path Back to upload form
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
input.button.postfix type="submit" value="Search"
|
input.button.postfix type="submit" value="Search"
|
||||||
|
|
||||||
div.row
|
div.row
|
||||||
hr
|
|
||||||
div.large-4.medium-12.small-12.columns.text-center
|
div.large-4.medium-12.small-12.columns.text-center
|
||||||
h2 Newest upload
|
h2 Newest upload
|
||||||
a.black href=package_path(@newest_upload.package.name)
|
a.black href=package_path(@newest_upload.package.name)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ Rails.application.routes.draw do
|
||||||
post 'upload/:name' => 'packages#upload_receive', as: :upload_receive, name: /[^\/]+/
|
post 'upload/:name' => 'packages#upload_receive', as: :upload_receive, name: /[^\/]+/
|
||||||
# TODO: "get" is probably the wrong method to delete a screenshot
|
# TODO: "get" is probably the wrong method to delete a screenshot
|
||||||
get 'delete_screenshot/:id' => 'packages#delete_screenshot', as: :delete_screenshot
|
get 'delete_screenshot/:id' => 'packages#delete_screenshot', as: :delete_screenshot
|
||||||
post 'update_screenshot_description/:name/:id' => 'packages#update_screenshot_description', as: :update_screenshot_description
|
patch 'update_screenshot_description/:id' => 'packages#update_screenshot_description', as: :update_screenshot_description
|
||||||
#post 'report_screenshot/:id' => 'packages#report_screenshot', as: :report_screenshot
|
#post 'report_screenshot/:id' => 'packages#report_screenshot', as: :report_screenshot
|
||||||
# TODO: "get" is probably the wrong method to delete a screenshot
|
# TODO: "get" is probably the wrong method to delete a screenshot
|
||||||
get 'approve_screenshot/:id' => 'packages#approve_screenshot', as: :approve_screenshot
|
get 'approve_screenshot/:id' => 'packages#approve_screenshot', as: :approve_screenshot
|
||||||
|
|
|
||||||
60
doc/TODO
Normal file
60
doc/TODO
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
Next tasks
|
||||||
|
|
||||||
|
> Before next deployment.
|
||||||
|
|
||||||
|
FIXED Auto-approve admin uploads
|
||||||
|
|
||||||
|
FIXED Why does weboob-qt show an uploaded image in grid mode if no screenshot is approved?
|
||||||
|
|
||||||
|
FIXED Redirect back to upload form if all uploads are invalid
|
||||||
|
|
||||||
|
FIXED Improve callout design. No javascript. Better readability. At the top.
|
||||||
|
|
||||||
|
Main text on details page is not shown
|
||||||
|
|
||||||
|
Remove appearances of "token" everywhere.
|
||||||
|
|
||||||
|
Hint the user to add missing descriptions.
|
||||||
|
|
||||||
|
Fix upload process.
|
||||||
|
|
||||||
|
Fix my/my uploads.
|
||||||
|
|
||||||
|
Fix admin menu.
|
||||||
|
|
||||||
|
Make sure that the moderation workflow works.
|
||||||
|
|
||||||
|
Any upload will trigger the creation of an ad-hoc account.
|
||||||
|
Screenshots will always be assigned to a user.
|
||||||
|
|
||||||
|
After an upload ask the user to add descriptions to the
|
||||||
|
screenshots. And allow them to delete the screenshots.
|
||||||
|
|
||||||
|
Check all code with "current_user" and "user_signed_in?" to
|
||||||
|
make sure it matches the new concept.
|
||||||
|
|
||||||
|
Create a user account called "Anonymous" - via a migration.
|
||||||
|
All existing screenshots will become uploaded by that user.
|
||||||
|
|
||||||
|
Turn moderation into a seperate page showing just the one
|
||||||
|
screenshot and aks the user for a deletion reason.
|
||||||
|
|
||||||
|
Make sure that users can delete their own screenshots but
|
||||||
|
no other.
|
||||||
|
|
||||||
|
Fix formatting of http://localhost:3000/packages/list?search=weboob&show=
|
||||||
|
(Enumerations etc.)
|
||||||
|
|
||||||
|
> After next deployment
|
||||||
|
|
||||||
|
Make sure that ad-hoc users are recognized when they visit again.
|
||||||
|
Devise should recognize them by their session cookie.
|
||||||
|
|
||||||
|
Offer anonymous users to become real users to track their uploads.
|
||||||
|
The screenshots will have to be moved to the new account
|
||||||
|
and the ad-hoc account can be deleted.
|
||||||
|
|
||||||
|
Move ad-hoc/anonymous uploads to the main anonymous user after
|
||||||
|
a week.
|
||||||
|
|
||||||
|
Add tests.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue